IeeeCdfImporter.java
/**
* Copyright (c) 2019, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.ieeecdf.converter;
import com.google.auto.service.AutoService;
import com.google.common.io.ByteStreams;
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.datasource.DataSource;
import com.powsybl.commons.datasource.ReadOnlyDataSource;
import com.powsybl.commons.parameters.ConfiguredParameter;
import com.powsybl.commons.parameters.Parameter;
import com.powsybl.commons.parameters.ParameterDefaultValueConfig;
import com.powsybl.commons.parameters.ParameterType;
import com.powsybl.ieeecdf.model.*;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.extensions.SlackTerminal;
import com.powsybl.iidm.network.util.ContainersMapping;
import org.apache.commons.math3.complex.Complex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.function.ToDoubleFunction;
import java.util.stream.Collectors;
/**
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
*/
@AutoService(Importer.class)
public class IeeeCdfImporter implements Importer {
private static final Logger LOGGER = LoggerFactory.getLogger(IeeeCdfImporter.class);
private static final String FORMAT = "IEEE-CDF";
private static final String EXT = "txt";
private static final Parameter IGNORE_BASE_VOLTAGE_PARAMETER = new Parameter("ignore-base-voltage",
ParameterType.BOOLEAN,
"Ignore base voltage specified in the file",
Boolean.FALSE);
private static final double DEFAULT_ACTIVE_POWER_LIMIT = 9999d;
static final ToDoubleFunction<IeeeCdfBus> DEFAULT_NOMINAL_VOLTAGE_PROVIDER = ieeeCdfBus -> ieeeCdfBus.getBaseVoltage() == 0 ? 1 : ieeeCdfBus.getBaseVoltage();
private final ToDoubleFunction<IeeeCdfBus> nominalVoltageProvider;
public IeeeCdfImporter() {
this(DEFAULT_NOMINAL_VOLTAGE_PROVIDER);
}
public IeeeCdfImporter(ToDoubleFunction<IeeeCdfBus> nominalVoltageProvider) {
this.nominalVoltageProvider = Objects.requireNonNull(nominalVoltageProvider);
}
@Override
public String getFormat() {
return FORMAT;
}
@Override
public List<String> getSupportedExtensions() {
return List.of(EXT);
}
@Override
public List<Parameter> getParameters() {
return ConfiguredParameter.load(Collections.singletonList(IGNORE_BASE_VOLTAGE_PARAMETER), getFormat(), ParameterDefaultValueConfig.INSTANCE);
}
@Override
public String getComment() {
return "IEEE Common Data Format to IIDM converter";
}
@Override
public boolean exists(ReadOnlyDataSource dataSource) {
try {
if (dataSource.isDataExtension(EXT) && dataSource.exists(null, EXT)) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(dataSource.newInputStream(null, EXT)))) {
String titleLine = reader.readLine();
if (titleLine != null) {
return titleLine.length() >= 44
&& titleLine.charAt(3) == '/'
&& titleLine.charAt(6) == '/'
&& (titleLine.charAt(43) == 'S' || titleLine.charAt(43) == 'W');
}
}
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return false;
}
@Override
public void copy(ReadOnlyDataSource fromDataSource, DataSource toDataSource) {
Objects.requireNonNull(fromDataSource);
Objects.requireNonNull(toDataSource);
try {
try (InputStream is = fromDataSource.newInputStream(null, EXT);
OutputStream os = toDataSource.newOutputStream(null, EXT, false)) {
ByteStreams.copy(is, os);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private static final class PerUnitContext {
private final double sb; // base apparent power
private final boolean ignoreBaseVoltage;
private PerUnitContext(double sb, boolean ignoreBaseVoltage) {
this.sb = sb;
this.ignoreBaseVoltage = ignoreBaseVoltage;
}
private double getSb() {
return sb;
}
public boolean isIgnoreBaseVoltage() {
return ignoreBaseVoltage;
}
}
private static boolean isTransformer(IeeeCdfBranch ieeeCdfBranch) {
return ieeeCdfBranch.getType() != null && (ieeeCdfBranch.getType() != IeeeCdfBranch.Type.TRANSMISSION_LINE || ieeeCdfBranch.getFinalTurnsRatio() != 0);
}
private static String getBusId(int busNum) {
return "B" + busNum;
}
private void createBuses(IeeeCdfModel ieeeCdfModel, ContainersMapping containerMapping, PerUnitContext perUnitContext,
Network network) {
for (IeeeCdfBus ieeeCdfBus : ieeeCdfModel.getBuses()) {
String voltageLevelId = containerMapping.getVoltageLevelId(ieeeCdfBus.getNumber());
String substationId = containerMapping.getSubstationId(voltageLevelId);
// create substation
Substation substation = createSubstation(network, substationId);
// create voltage level
VoltageLevel voltageLevel = createVoltageLevel(ieeeCdfBus, perUnitContext, voltageLevelId, substation, network);
// create bus
Bus bus = createBus(ieeeCdfBus, voltageLevel);
// create load
createLoad(ieeeCdfBus, voltageLevel);
// create shunt compensator
createShuntCompensator(ieeeCdfBus, perUnitContext, voltageLevel);
// create generator
switch (ieeeCdfBus.getType()) {
case UNREGULATED:
// nothing to do
break;
case HOLD_MVAR_GENERATION_WITHIN_VOLTAGE_LIMITS:
newGeneratorAdder(ieeeCdfBus, voltageLevel)
.setTargetQ(ieeeCdfBus.getReactiveGeneration())
.setVoltageRegulatorOn(false)
.add();
break;
case HOLD_VOLTAGE_WITHIN_VAR_LIMITS:
case HOLD_VOLTAGE_AND_ANGLE:
Generator generator = newGeneratorAdder(ieeeCdfBus, voltageLevel)
.setTargetV(ieeeCdfBus.getDesiredVoltage() * voltageLevel.getNominalV())
.setVoltageRegulatorOn(true)
.add();
if (ieeeCdfBus.getMinReactivePowerOrVoltageLimit() != 0 || ieeeCdfBus.getMaxReactivePowerOrVoltageLimit() != 0) {
generator.newMinMaxReactiveLimits()
.setMinQ(ieeeCdfBus.getMinReactivePowerOrVoltageLimit())
.setMaxQ(ieeeCdfBus.getMaxReactivePowerOrVoltageLimit())
.add();
}
// Keep the given value for reactive output
// It is relevant if we want to load a solved case and validate it
// Another option would be to store given p, q values at terminal
generator.setTargetQ(ieeeCdfBus.getReactiveGeneration());
break;
default:
throw new IllegalStateException("Unexpected bus type: " + ieeeCdfBus.getType());
}
// Attach a slack bus
if (ieeeCdfBus.getType() == IeeeCdfBus.Type.HOLD_VOLTAGE_AND_ANGLE) {
SlackTerminal.attach(bus);
}
}
}
private static Bus createBus(IeeeCdfBus ieeeCdfBus, VoltageLevel voltageLevel) {
String busId = getBusId(ieeeCdfBus.getNumber());
Bus bus = voltageLevel.getBusBreakerView().newBus()
.setId(busId)
.setName(ieeeCdfBus.getName())
.add();
bus.setV(ieeeCdfBus.getFinalVoltage() * voltageLevel.getNominalV())
.setAngle(ieeeCdfBus.getFinalAngle());
return bus;
}
private static Substation createSubstation(Network network, String substationId) {
Substation substation = network.getSubstation(substationId);
if (substation == null) {
substation = network.newSubstation()
.setId(substationId)
.add();
}
return substation;
}
private double getNominalV(IeeeCdfBus ieeeCdfBus, PerUnitContext perUnitContext) {
if (perUnitContext.isIgnoreBaseVoltage()) {
return 1;
}
return nominalVoltageProvider.applyAsDouble(ieeeCdfBus);
}
private VoltageLevel createVoltageLevel(IeeeCdfBus ieeeCdfBus, PerUnitContext perUnitContext,
String voltageLevelId, Substation substation, Network network) {
double nominalV = getNominalV(ieeeCdfBus, perUnitContext);
VoltageLevel voltageLevel = network.getVoltageLevel(voltageLevelId);
if (voltageLevel == null) {
voltageLevel = substation.newVoltageLevel()
.setId(voltageLevelId)
.setNominalV(nominalV)
.setTopologyKind(TopologyKind.BUS_BREAKER)
.add();
}
return voltageLevel;
}
private static void createLoad(IeeeCdfBus ieeeCdfBus, VoltageLevel voltageLevel) {
if (ieeeCdfBus.getActiveLoad() != 0 || ieeeCdfBus.getReactiveLoad() != 0) {
String busId = getBusId(ieeeCdfBus.getNumber());
voltageLevel.newLoad()
.setId(busId + "-L")
.setConnectableBus(busId)
.setBus(busId)
.setP0(ieeeCdfBus.getActiveLoad())
.setQ0(ieeeCdfBus.getReactiveLoad())
.add();
}
}
private static GeneratorAdder newGeneratorAdder(IeeeCdfBus ieeeCdfBus, VoltageLevel voltageLevel) {
String busId = getBusId(ieeeCdfBus.getNumber());
return voltageLevel.newGenerator()
.setId(busId + "-G")
.setConnectableBus(busId)
.setBus(busId)
.setTargetP(ieeeCdfBus.getActiveGeneration())
.setMaxP(DEFAULT_ACTIVE_POWER_LIMIT)
.setMinP(-DEFAULT_ACTIVE_POWER_LIMIT);
}
private static void createShuntCompensator(IeeeCdfBus ieeeCdfBus, PerUnitContext perUnitContext, VoltageLevel voltageLevel) {
if (ieeeCdfBus.getShuntSusceptance() != 0) {
String busId = getBusId(ieeeCdfBus.getNumber());
double zb = Math.pow(voltageLevel.getNominalV(), 2) / perUnitContext.getSb();
voltageLevel.newShuntCompensator()
.setId(busId + "-SH")
.setConnectableBus(busId)
.setBus(busId)
.setSectionCount(1)
.newLinearModel()
.setMaximumSectionCount(1)
.setBPerSection(ieeeCdfBus.getShuntSusceptance() / zb)
.add()
.add();
}
}
private static String getBranchId(char type, int tapBusNumber, int zBusNumber, int circuit, Network network) {
int uniqueCircuit = circuit;
String id;
do {
id = "" + type + tapBusNumber + "-" + zBusNumber + "-" + uniqueCircuit++;
} while (network.getIdentifiable(id) != null);
return id;
}
private static void createLine(IeeeCdfBranch ieeeCdfBranch, ContainersMapping containerMapping, PerUnitContext perUnitContext, Network network) {
String id = getBranchId('L', ieeeCdfBranch.getTapBusNumber(), ieeeCdfBranch.getzBusNumber(), ieeeCdfBranch.getCircuit(), network);
String bus1Id = getBusId(ieeeCdfBranch.getTapBusNumber());
String bus2Id = getBusId(ieeeCdfBranch.getzBusNumber());
String voltageLevel1Id = containerMapping.getVoltageLevelId(ieeeCdfBranch.getTapBusNumber());
String voltageLevel2Id = containerMapping.getVoltageLevelId(ieeeCdfBranch.getzBusNumber());
VoltageLevel voltageLevel1 = network.getVoltageLevel(voltageLevel1Id);
VoltageLevel voltageLevel2 = network.getVoltageLevel(voltageLevel2Id);
double nominalV1 = voltageLevel1.getNominalV();
double nominalV2 = voltageLevel2.getNominalV();
double sBase = perUnitContext.getSb();
double r = impedanceToEngineeringUnitsForLine(ieeeCdfBranch.getResistance(), nominalV1, nominalV2, sBase);
double x = impedanceToEngineeringUnitsForLine(ieeeCdfBranch.getReactance(), nominalV1, nominalV2, sBase);
Complex ytr = impedanceToAdmittance(r, x);
double g1 = admittanceEndToEngineeringUnitsForLine(ytr.getReal(), 0.0, nominalV1, nominalV2, sBase);
double b1 = admittanceEndToEngineeringUnitsForLine(ytr.getImaginary(), ieeeCdfBranch.getChargingSusceptance() * 0.5, nominalV1, nominalV2, sBase);
double g2 = admittanceEndToEngineeringUnitsForLine(ytr.getReal(), 0.0, nominalV2, nominalV1, sBase);
double b2 = admittanceEndToEngineeringUnitsForLine(ytr.getImaginary(), ieeeCdfBranch.getChargingSusceptance() * 0.5, nominalV2, nominalV1, sBase);
network.newLine()
.setId(id)
.setBus1(bus1Id)
.setConnectableBus1(bus1Id)
.setVoltageLevel1(voltageLevel1Id)
.setBus2(bus2Id)
.setConnectableBus2(bus2Id)
.setVoltageLevel2(voltageLevel2Id)
.setR(r)
.setX(x)
.setG1(g1)
.setB1(b1)
.setG2(g2)
.setB2(b2)
.add();
}
// avoid NaN when r and x, both are 0.0
private static Complex impedanceToAdmittance(double r, double x) {
return r == 0.0 && x == 0.0 ? new Complex(0.0, 0.0) : new Complex(r, x).reciprocal();
}
private static double impedanceToEngineeringUnitsForLine(double impedance, double nominalVoltageAtEnd, double nominalVoltageAtOtherEnd, double sBase) {
// this method handles also line with different nominal voltage at ends
return impedance * nominalVoltageAtEnd * nominalVoltageAtOtherEnd / sBase;
}
private static double admittanceEndToEngineeringUnitsForLine(double transmissionAdmittance, double shuntAdmittanceAtEnd,
double nominalVoltageAtEnd, double nominalVoltageAtOtherEnd, double sBase) {
// this method handles also line with different nominal voltage at ends
// note that ytr is already in engineering units
return shuntAdmittanceAtEnd * sBase / (nominalVoltageAtEnd * nominalVoltageAtEnd) - (1 - nominalVoltageAtOtherEnd / nominalVoltageAtEnd) * transmissionAdmittance;
}
private static TwoWindingsTransformer createTransformer(IeeeCdfBranch ieeeCdfBranch, ContainersMapping containerMapping, PerUnitContext perUnitContext, Network network) {
String id = getBranchId('T', ieeeCdfBranch.getTapBusNumber(), ieeeCdfBranch.getzBusNumber(), ieeeCdfBranch.getCircuit(), network);
String bus1Id = getBusId(ieeeCdfBranch.getTapBusNumber());
String bus2Id = getBusId(ieeeCdfBranch.getzBusNumber());
String voltageLevel1Id = containerMapping.getVoltageLevelId(ieeeCdfBranch.getTapBusNumber());
String voltageLevel2Id = containerMapping.getVoltageLevelId(ieeeCdfBranch.getzBusNumber());
VoltageLevel voltageLevel1 = network.getVoltageLevel(voltageLevel1Id);
VoltageLevel voltageLevel2 = network.getVoltageLevel(voltageLevel2Id);
double zb = Math.pow(voltageLevel2.getNominalV(), 2) / perUnitContext.getSb();
return voltageLevel2.getSubstation().map(Substation::newTwoWindingsTransformer)
.orElseThrow(() -> new PowsyblException("Substation null! Transformer must be within a substation"))
.setId(id)
.setBus1(bus1Id)
.setConnectableBus1(bus1Id)
.setVoltageLevel1(voltageLevel1Id)
.setBus2(bus2Id)
.setConnectableBus2(bus2Id)
.setVoltageLevel2(voltageLevel2Id)
.setRatedU1(voltageLevel1.getNominalV() * ieeeCdfBranch.getFinalTurnsRatio())
.setRatedU2(voltageLevel2.getNominalV())
.setR(ieeeCdfBranch.getResistance() * zb)
.setX(ieeeCdfBranch.getReactance() * zb)
.setG(0)
.setB(ieeeCdfBranch.getChargingSusceptance() / zb)
.add();
}
private static Terminal getRegulatingTerminal(IeeeCdfBranch ieeeCdfBranch, TwoWindingsTransformer transformer) {
Terminal regulatingTerminal = null;
if (ieeeCdfBranch.getSide() != null) {
switch (ieeeCdfBranch.getSide()) {
case CONTROLLED_BUS_IS_ONE_OF_THE_TERMINALS:
int controlBusNum = ieeeCdfBranch.getControlBusNumber();
if (controlBusNum != 0) {
String controlBusId = getBusId(controlBusNum);
if (controlBusId.equals(transformer.getTerminal1().getBusBreakerView().getBus().getId())) {
regulatingTerminal = transformer.getTerminal1();
} else if (controlBusId.equals(transformer.getTerminal2().getBusBreakerView().getBus().getId())) {
regulatingTerminal = transformer.getTerminal2();
} else {
throw new UnsupportedOperationException("Remote control bus not yet supported: " + transformer.getId());
}
}
break;
case CONTROLLED_BUS_IS_NEAR_THE_TAP_SIDE:
regulatingTerminal = transformer.getTerminal1();
break;
case CONTROLLED_BUS_IS_NEAR_THE_IMPEDANCE_SIDE:
regulatingTerminal = transformer.getTerminal2();
break;
default:
throw new IllegalStateException("Unknown branch side: " + ieeeCdfBranch.getSide());
}
}
return regulatingTerminal;
}
private static void createTransformerWithVoltageControl(IeeeCdfBranch ieeeCdfBranch, ContainersMapping containerMapping, PerUnitContext perUnitContext, Network network) {
TwoWindingsTransformer transformer = createTransformer(ieeeCdfBranch, containerMapping, perUnitContext, network);
boolean regulating = false;
double targetV = Double.NaN;
Terminal regulatingTerminal = getRegulatingTerminal(ieeeCdfBranch, transformer);
if (regulatingTerminal != null) {
Bus regulatingBus = regulatingTerminal.getBusView().getBus();
if (regulatingBus != null) {
regulating = true;
targetV = regulatingBus.getV();
}
}
List<Double> rhos = new ArrayList<>();
rhos.add(1.0); // TODO create full table
if (ieeeCdfBranch.getMinTapOrPhaseShift() != 0 && ieeeCdfBranch.getMaxTapOrPhaseShift() != 0) {
LOGGER.warn("Tap steps are not yet supported ({})", transformer.getId());
}
RatioTapChangerAdder ratioTapChangerAdder = transformer.newRatioTapChanger()
.setLoadTapChangingCapabilities(true)
.setRegulating(regulating)
.setRegulationTerminal(regulatingTerminal)
.setTargetV(targetV)
.setTargetDeadband(regulating ? 0.0 : Double.NaN)
.setTapPosition(0);
for (double rho : rhos) {
ratioTapChangerAdder.beginStep()
.setRho(rho)
.setR(0)
.setX(0)
.setG(0)
.setB(0)
.endStep();
}
ratioTapChangerAdder.add();
}
private static void createTransformerWithActivePowerControl(IeeeCdfBranch ieeeCdfBranch, ContainersMapping containerMapping, PerUnitContext perUnitContext, Network network) {
TwoWindingsTransformer transformer = createTransformer(ieeeCdfBranch, containerMapping, perUnitContext, network);
// As there is no active power or current setpoint in IEEE data model there is no way to have regulating phase
// shifter and so on we always set it to fixed tap.
PhaseTapChangerAdder phaseTapChangerAdder = transformer.newPhaseTapChanger()
.setRegulationMode(PhaseTapChanger.RegulationMode.FIXED_TAP)
.setRegulating(false)
.setTapPosition(0);
List<Double> alphas = new ArrayList<>();
alphas.add(-ieeeCdfBranch.getFinalAngle()); // TODO create full table
if (ieeeCdfBranch.getMinTapOrPhaseShift() != 0 && ieeeCdfBranch.getMaxTapOrPhaseShift() != 0) {
LOGGER.warn("Phase shift steps are not yet supported ({})", transformer.getId());
}
for (double alpha : alphas) {
phaseTapChangerAdder.beginStep()
.setAlpha(alpha)
.setRho(1)
.setR(0)
.setX(0)
.setG(0)
.setB(0)
.endStep();
}
phaseTapChangerAdder.add();
}
private static void createBranches(IeeeCdfModel ieeeCdfModel, ContainersMapping containerMapping, PerUnitContext perUnitContext, Network network) {
for (IeeeCdfBranch ieeeCdfBranch : ieeeCdfModel.getBranches()) {
if (ieeeCdfBranch.getType() == null) {
createLine(ieeeCdfBranch, containerMapping, perUnitContext, network);
} else {
switch (ieeeCdfBranch.getType()) {
case TRANSMISSION_LINE:
if (ieeeCdfBranch.getFinalTurnsRatio() == 0) {
createLine(ieeeCdfBranch, containerMapping, perUnitContext, network);
} else {
createTransformer(ieeeCdfBranch, containerMapping, perUnitContext, network);
}
break;
case FIXED_TAP:
createTransformer(ieeeCdfBranch, containerMapping, perUnitContext, network);
break;
case VARIABLE_TAP_FOR_VOLTAVE_CONTROL:
createTransformerWithVoltageControl(ieeeCdfBranch, containerMapping, perUnitContext, network);
break;
case VARIABLE_TAP_FOR_REACTIVE_POWER_CONTROL:
throw new UnsupportedOperationException("Transformers not yet implemented: " + ieeeCdfBranch.getType());
case VARIABLE_PHASE_ANGLE_FOR_ACTIVE_POWER_CONTROL:
createTransformerWithActivePowerControl(ieeeCdfBranch, containerMapping, perUnitContext, network);
break;
default:
throw new IllegalStateException("Unexpected branch type: " + ieeeCdfBranch.getType());
}
}
// TODO create current limits
}
}
@Override
public Network importData(ReadOnlyDataSource dataSource, NetworkFactory networkFactory, Properties parameters) {
Objects.requireNonNull(dataSource);
Objects.requireNonNull(networkFactory);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(dataSource.newInputStream(null, EXT)))) {
// parse file
IeeeCdfModel ieeeCdfModel = new IeeeCdfReader().read(reader);
boolean ignoreBaseVoltage = Parameter.readBoolean(FORMAT, parameters, IGNORE_BASE_VOLTAGE_PARAMETER,
ParameterDefaultValueConfig.INSTANCE);
return convert(ieeeCdfModel, networkFactory, dataSource.getBaseName(), ignoreBaseVoltage);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
Network convert(IeeeCdfModel ieeeCdfModel, NetworkFactory networkFactory, String networkId, boolean ignoreBaseVoltage) {
PerUnitContext perUnitContext = new PerUnitContext(ieeeCdfModel.getTitle().getMvaBase(), ignoreBaseVoltage);
Network network = networkFactory.createNetwork(networkId, FORMAT);
// set date and time
IeeeCdfTitle ieeeCdfTitle = ieeeCdfModel.getTitle();
if (ieeeCdfTitle.getDate() != null) {
ZonedDateTime caseDateTime = ieeeCdfTitle.getDate().atStartOfDay(ZoneOffset.UTC.normalized());
network.setCaseDate(ZonedDateTime.ofInstant(caseDateTime.toInstant(), ZoneOffset.UTC));
}
// build container to fit IIDM requirements
Map<Integer, IeeeCdfBus> busNumToIeeeCdfBus = ieeeCdfModel.getBuses().stream().collect(Collectors.toMap(IeeeCdfBus::getNumber, Function.identity()));
ContainersMapping containerMapping = ContainersMapping.create(ieeeCdfModel.getBuses(), ieeeCdfModel.getBranches(),
IeeeCdfBus::getNumber,
IeeeCdfBranch::getTapBusNumber,
IeeeCdfBranch::getzBusNumber,
branch -> branch.getResistance() == 0.0 && branch.getReactance() == 0.0,
IeeeCdfImporter::isTransformer,
busNumber -> getNominalVFromBusNumber(busNumToIeeeCdfBus, busNumber, perUnitContext),
busNums -> "VL" + busNums.stream().sorted().findFirst().orElseThrow(() -> new PowsyblException("Unexpected empty busNums")),
substationNums -> "S" + substationNums.stream().sorted().findFirst().orElseThrow(() -> new PowsyblException("Unexpected empty substationNums")));
// create objects
createBuses(ieeeCdfModel, containerMapping, perUnitContext, network);
createBranches(ieeeCdfModel, containerMapping, perUnitContext, network);
return network;
}
private double getNominalVFromBusNumber(Map<Integer, IeeeCdfBus> busNumToIeeeCdfBus, int busNumber, PerUnitContext perUnitContext) {
if (!busNumToIeeeCdfBus.containsKey(busNumber)) { // never should happen
throw new PowsyblException("busId without IeeeCdfBus" + busNumber);
}
return getNominalV(busNumToIeeeCdfBus.get(busNumber), perUnitContext);
}
}