VoltageLevelSerDe.java
/**
* Copyright (c) 2016, 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.iidm.serde;
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.util.Networks;
import com.powsybl.iidm.serde.util.IidmSerDeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.powsybl.iidm.serde.PropertiesSerDe.NAME;
import static com.powsybl.iidm.serde.PropertiesSerDe.VALUE;
/**
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
*/
class VoltageLevelSerDe extends AbstractSimpleIdentifiableSerDe<VoltageLevel, VoltageLevelAdder, Container<? extends Identifiable<?>>> {
static final VoltageLevelSerDe INSTANCE = new VoltageLevelSerDe();
static final String ROOT_ELEMENT_NAME = "voltageLevel";
static final String ARRAY_ELEMENT_NAME = "voltageLevels";
private static final Logger LOGGER = LoggerFactory.getLogger(VoltageLevelSerDe.class);
private static final String NODE_BREAKER_TOPOLOGY_ELEMENT_NAME = "nodeBreakerTopology";
private static final String BUS_BREAKER_TOPOLOGY_ELEMENT_NAME = "busBreakerTopology";
private static final String NODE_COUNT = "nodeCount";
static final String INJ_ROOT_ELEMENT_NAME = "inj";
static final String INJ_ARRAY_ELEMENT_NAME = "fictitiousInjections";
@Override
protected String getRootElementName() {
return ROOT_ELEMENT_NAME;
}
@Override
protected void writeRootElementAttributes(VoltageLevel vl, Container<? extends Identifiable<?>> c, NetworkSerializerContext context) {
context.getWriter().writeDoubleAttribute("nominalV", vl.getNominalV());
context.getWriter().writeDoubleAttribute("lowVoltageLimit", vl.getLowVoltageLimit());
context.getWriter().writeDoubleAttribute("highVoltageLimit", vl.getHighVoltageLimit());
TopologyLevel topologyLevel = TopologyLevel.min(vl.getTopologyKind(), context.getOptions().getTopologyLevel());
context.getWriter().writeEnumAttribute("topologyKind", topologyLevel.getTopologyKind());
}
@Override
protected void writeSubElements(VoltageLevel vl, Container<? extends Identifiable<?>> c, NetworkSerializerContext context) {
TopologyLevel topologyLevel = TopologyLevel.min(vl.getTopologyKind(), context.getOptions().getTopologyLevel());
switch (topologyLevel) {
case NODE_BREAKER -> writeNodeBreakerTopology(vl, context);
case BUS_BREAKER -> writeBusBreakerTopology(vl, context);
case BUS_BRANCH -> writeBusBranchTopology(vl, context);
default -> throw new IllegalStateException("Unexpected TopologyLevel value: " + topologyLevel);
}
writeGenerators(vl, context);
writeBatteries(vl, context);
writeLoads(vl, context);
writeShuntCompensators(vl, context);
writeDanglingLines(vl, context);
writeStaticVarCompensators(vl, context);
writeVscConverterStations(vl, context);
writeLccConverterStations(vl, context);
writeGrounds(vl, context);
}
private void writeNodeBreakerTopology(VoltageLevel vl, NetworkSerializerContext context) {
context.getWriter().writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), NODE_BREAKER_TOPOLOGY_ELEMENT_NAME);
IidmSerDeUtil.writeIntAttributeUntilMaximumVersion(NODE_COUNT, vl.getNodeBreakerView().getMaximumNodeIndex() + 1, IidmVersion.V_1_1, context);
context.getWriter().writeStartNodes();
for (BusbarSection bs : IidmSerDeUtil.sorted(vl.getNodeBreakerView().getBusbarSections(), context.getOptions())) {
BusbarSectionSerDe.INSTANCE.write(bs, null, context);
}
context.getWriter().writeEndNodes();
context.getWriter().writeStartNodes();
for (Switch sw : IidmSerDeUtil.sorted(vl.getNodeBreakerView().getSwitches(), context.getOptions())) {
NodeBreakerViewSwitchSerDe.INSTANCE.write(sw, vl, context);
}
context.getWriter().writeEndNodes();
writeNodeBreakerTopologyInternalConnections(vl, context);
IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_1, context, () -> {
Map<String, Set<Integer>> nodesByBus = Networks.getNodesByBus(vl);
context.getWriter().writeStartNodes();
IidmSerDeUtil.sorted(vl.getBusView().getBusStream(), context.getOptions())
.filter(bus -> !Double.isNaN(bus.getV()) || !Double.isNaN(bus.getAngle()))
.forEach(bus -> {
Set<Integer> nodes = nodesByBus.get(bus.getId());
writeCalculatedBus(bus, nodes, context);
});
context.getWriter().writeEndNodes();
});
IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_8, context, () -> {
context.getWriter().writeStartNodes();
for (int node : vl.getNodeBreakerView().getNodes()) {
double fictP0 = vl.getNodeBreakerView().getFictitiousP0(node);
double fictQ0 = vl.getNodeBreakerView().getFictitiousQ0(node);
if (fictP0 != 0.0 || fictQ0 != 0.0) {
context.getWriter().writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), INJ_ROOT_ELEMENT_NAME);
context.getWriter().writeIntAttribute("node", node);
context.getWriter().writeDoubleAttribute("fictitiousP0", fictP0, 0.0);
context.getWriter().writeDoubleAttribute("fictitiousQ0", fictQ0, 0.0);
context.getWriter().writeEndNode();
}
}
context.getWriter().writeEndNodes();
});
context.getWriter().writeEndNode();
}
private static void writeCalculatedBus(Bus bus, Set<Integer> nodes, NetworkSerializerContext context) {
context.getWriter().writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), "bus");
context.getWriter().writeDoubleAttribute("v", bus.getV());
context.getWriter().writeDoubleAttribute("angle", bus.getAngle());
context.getWriter().writeIntArrayAttribute("nodes", nodes);
if (context.getVersion().compareTo(IidmVersion.V_1_11) >= 0 && bus.hasProperty()) {
PropertiesSerDe.write(bus, context);
}
context.getWriter().writeEndNode();
}
private void writeNodeBreakerTopologyInternalConnections(VoltageLevel vl, NetworkSerializerContext context) {
context.getWriter().writeStartNodes();
for (VoltageLevel.NodeBreakerView.InternalConnection ic : IidmSerDeUtil.sortedInternalConnections(vl.getNodeBreakerView().getInternalConnections(), context.getOptions())) {
NodeBreakerViewInternalConnectionSerDe.INSTANCE.write(ic.getNode1(), ic.getNode2(), context);
}
context.getWriter().writeEndNodes();
}
private void writeBusBreakerTopology(VoltageLevel vl, NetworkSerializerContext context) {
context.getWriter().writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), BUS_BREAKER_TOPOLOGY_ELEMENT_NAME);
context.getWriter().writeStartNodes();
for (Bus b : IidmSerDeUtil.sorted(vl.getBusBreakerView().getBuses(), context.getOptions())) {
if (!context.getFilter().test(b)) {
continue;
}
BusSerDe.INSTANCE.write(b, null, context);
}
context.getWriter().writeEndNodes();
context.getWriter().writeStartNodes();
for (Switch sw : IidmSerDeUtil.sorted(vl.getBusBreakerView().getSwitches(), context.getOptions())) {
Bus b1 = vl.getBusBreakerView().getBus1(sw.getId());
Bus b2 = vl.getBusBreakerView().getBus2(sw.getId());
if (!context.getFilter().test(b1) || !context.getFilter().test(b2)) {
continue;
}
BusBreakerViewSwitchSerDe.INSTANCE.write(sw, vl, context);
}
context.getWriter().writeEndNodes();
context.getWriter().writeEndNode();
}
private void writeBusBranchTopology(VoltageLevel vl, NetworkSerializerContext context) {
context.getWriter().writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), BUS_BREAKER_TOPOLOGY_ELEMENT_NAME);
context.getWriter().writeStartNodes();
for (Bus b : IidmSerDeUtil.sorted(vl.getBusView().getBuses(), context.getOptions())) {
if (!context.getFilter().test(b)) {
continue;
}
BusSerDe.INSTANCE.write(b, null, context);
}
context.getWriter().writeEndNodes();
context.getWriter().writeEndNode();
}
private void writeGenerators(VoltageLevel vl, NetworkSerializerContext context) {
context.getWriter().writeStartNodes();
for (Generator g : IidmSerDeUtil.sorted(vl.getGenerators(), context.getOptions())) {
if (!context.getFilter().test(g)) {
continue;
}
GeneratorSerDe.INSTANCE.write(g, vl, context);
}
context.getWriter().writeEndNodes();
}
private void writeBatteries(VoltageLevel vl, NetworkSerializerContext context) {
context.getWriter().writeStartNodes();
for (Battery b : IidmSerDeUtil.sorted(vl.getBatteries(), context.getOptions())) {
if (!context.getFilter().test(b)) {
continue;
}
BatterySerDe.INSTANCE.write(b, vl, context);
}
context.getWriter().writeEndNodes();
}
private void writeLoads(VoltageLevel vl, NetworkSerializerContext context) {
context.getWriter().writeStartNodes();
for (Load l : IidmSerDeUtil.sorted(vl.getLoads(), context.getOptions())) {
if (!context.getFilter().test(l)) {
continue;
}
LoadSerDe.INSTANCE.write(l, vl, context);
}
context.getWriter().writeEndNodes();
}
private void writeShuntCompensators(VoltageLevel vl, NetworkSerializerContext context) {
context.getWriter().writeStartNodes();
for (ShuntCompensator sc : IidmSerDeUtil.sorted(vl.getShuntCompensators(), context.getOptions())) {
if (!context.getFilter().test(sc)) {
continue;
}
ShuntSerDe.INSTANCE.write(sc, vl, context);
}
context.getWriter().writeEndNodes();
}
private void writeDanglingLines(VoltageLevel vl, NetworkSerializerContext context) {
context.getWriter().writeStartNodes();
for (DanglingLine dl : IidmSerDeUtil.sorted(vl.getDanglingLines(DanglingLineFilter.ALL), context.getOptions())) {
if (!context.getFilter().test(dl) || context.getVersion().compareTo(IidmVersion.V_1_10) < 0 && dl.isPaired()) {
continue;
}
DanglingLineSerDe.INSTANCE.write(dl, vl, context);
}
context.getWriter().writeEndNodes();
}
private void writeStaticVarCompensators(VoltageLevel vl, NetworkSerializerContext context) {
context.getWriter().writeStartNodes();
for (StaticVarCompensator svc : IidmSerDeUtil.sorted(vl.getStaticVarCompensators(), context.getOptions())) {
if (!context.getFilter().test(svc)) {
continue;
}
StaticVarCompensatorSerDe.INSTANCE.write(svc, vl, context);
}
context.getWriter().writeEndNodes();
}
private void writeVscConverterStations(VoltageLevel vl, NetworkSerializerContext context) {
context.getWriter().writeStartNodes();
for (VscConverterStation cs : IidmSerDeUtil.sorted(vl.getVscConverterStations(), context.getOptions())) {
if (!context.getFilter().test(cs)) {
continue;
}
VscConverterStationSerDe.INSTANCE.write(cs, vl, context);
}
context.getWriter().writeEndNodes();
}
private void writeLccConverterStations(VoltageLevel vl, NetworkSerializerContext context) {
context.getWriter().writeStartNodes();
for (LccConverterStation cs : IidmSerDeUtil.sorted(vl.getLccConverterStations(), context.getOptions())) {
if (!context.getFilter().test(cs)) {
continue;
}
LccConverterStationSerDe.INSTANCE.write(cs, vl, context);
}
context.getWriter().writeEndNodes();
}
private void writeGrounds(VoltageLevel vl, NetworkSerializerContext context) {
context.getWriter().writeStartNodes();
for (Ground g : IidmSerDeUtil.sorted(vl.getGrounds(), context.getOptions())) {
if (!context.getFilter().test(g)) {
continue;
}
GroundSerDe.INSTANCE.write(g, vl, context);
}
context.getWriter().writeEndNodes();
}
@Override
protected VoltageLevelAdder createAdder(Container<? extends Identifiable<?>> c) {
if (c instanceof Network network) {
return network.newVoltageLevel();
}
if (c instanceof Substation substation) {
return substation.newVoltageLevel();
}
throw new IllegalStateException();
}
@Override
protected VoltageLevel readRootElementAttributes(VoltageLevelAdder adder, Container<? extends Identifiable<?>> c, NetworkDeserializerContext context) {
double nominalV = context.getReader().readDoubleAttribute("nominalV");
double lowVoltageLimit = context.getReader().readDoubleAttribute("lowVoltageLimit");
double highVoltageLimit = context.getReader().readDoubleAttribute("highVoltageLimit");
TopologyKind topologyKind = context.getReader().readEnumAttribute("topologyKind", TopologyKind.class);
return adder
.setNominalV(nominalV)
.setLowVoltageLimit(lowVoltageLimit)
.setHighVoltageLimit(highVoltageLimit)
.setTopologyKind(topologyKind)
.add();
}
@Override
protected void readSubElements(VoltageLevel vl, NetworkDeserializerContext context) {
context.getReader().readChildNodes(elementName -> {
switch (elementName) {
case NODE_BREAKER_TOPOLOGY_ELEMENT_NAME -> readNodeBreakerTopology(vl, context);
case BUS_BREAKER_TOPOLOGY_ELEMENT_NAME -> readBusBreakerTopology(vl, context);
case GeneratorSerDe.ROOT_ELEMENT_NAME -> GeneratorSerDe.INSTANCE.read(vl, context);
case BatterySerDe.ROOT_ELEMENT_NAME -> BatterySerDe.INSTANCE.read(vl, context);
case LoadSerDe.ROOT_ELEMENT_NAME -> LoadSerDe.INSTANCE.read(vl, context);
case ShuntSerDe.ROOT_ELEMENT_NAME -> ShuntSerDe.INSTANCE.read(vl, context);
case DanglingLineSerDe.ROOT_ELEMENT_NAME -> DanglingLineSerDe.INSTANCE.read(vl, context);
case StaticVarCompensatorSerDe.ROOT_ELEMENT_NAME -> StaticVarCompensatorSerDe.INSTANCE.read(vl, context);
case VscConverterStationSerDe.ROOT_ELEMENT_NAME -> VscConverterStationSerDe.INSTANCE.read(vl, context);
case LccConverterStationSerDe.ROOT_ELEMENT_NAME -> LccConverterStationSerDe.INSTANCE.read(vl, context);
case GroundSerDe.ROOT_ELEMENT_NAME -> GroundSerDe.INSTANCE.read(vl, context);
default -> readSubElement(elementName, vl, context);
}
});
}
private void readNodeBreakerTopology(VoltageLevel vl, NetworkDeserializerContext context) {
IidmSerDeUtil.runUntilMaximumVersion(IidmVersion.V_1_1, context, () -> {
context.getReader().readIntAttribute(NODE_COUNT);
LOGGER.trace("attribute " + NODE_BREAKER_TOPOLOGY_ELEMENT_NAME + ".nodeCount is ignored.");
});
context.getReader().readChildNodes(elementName -> {
switch (elementName) {
case BusbarSectionSerDe.ROOT_ELEMENT_NAME -> BusbarSectionSerDe.INSTANCE.read(vl, context);
case AbstractSwitchSerDe.ROOT_ELEMENT_NAME -> NodeBreakerViewSwitchSerDe.INSTANCE.read(vl, context);
case NodeBreakerViewInternalConnectionSerDe.ROOT_ELEMENT_NAME -> NodeBreakerViewInternalConnectionSerDe.INSTANCE.read(vl, context);
case BusSerDe.ROOT_ELEMENT_NAME -> readCalculatedBus(vl, context);
case INJ_ROOT_ELEMENT_NAME -> readFictitiousInjection(vl, context);
default -> throw new PowsyblException(String.format("Unknown element name '%s' in 'nodeBreakerTopology'", elementName));
}
});
}
private void readCalculatedBus(VoltageLevel vl, NetworkDeserializerContext context) {
IidmSerDeUtil.assertMinimumVersion(ROOT_ELEMENT_NAME, BusSerDe.ROOT_ELEMENT_NAME, IidmSerDeUtil.ErrorMessage.NOT_SUPPORTED, IidmVersion.V_1_1, context);
double v = context.getReader().readDoubleAttribute("v");
double angle = context.getReader().readDoubleAttribute("angle");
List<Integer> busNodes = context.getReader().readIntArrayAttribute("nodes");
Map<String, String> properties = new HashMap<>();
context.getReader().readChildNodes(elementName -> {
if (elementName.equals(PropertiesSerDe.ROOT_ELEMENT_NAME)) {
String name = context.getReader().readStringAttribute(NAME);
String value = context.getReader().readStringAttribute(VALUE);
context.getReader().readEndNode();
properties.put(name, value);
} else {
throw new PowsyblException(String.format("Unknown element name '%s' in 'bus'", elementName));
}
});
context.addEndTask(DeserializationEndTask.Step.AFTER_EXTENSIONS, () -> {
for (int node : busNodes) {
Terminal terminal = vl.getNodeBreakerView().getTerminal(node);
if (terminal != null) {
Bus b = terminal.getBusView().getBus();
if (b != null) {
b.setV(v).setAngle(angle);
properties.forEach(b::setProperty);
break;
}
}
}
});
}
private void readFictitiousInjection(VoltageLevel vl, NetworkDeserializerContext context) {
IidmSerDeUtil.assertMinimumVersion(ROOT_ELEMENT_NAME, INJ_ROOT_ELEMENT_NAME, IidmSerDeUtil.ErrorMessage.NOT_SUPPORTED, IidmVersion.V_1_8, context);
int node = context.getReader().readIntAttribute("node");
double p0 = context.getReader().readDoubleAttribute("fictitiousP0");
double q0 = context.getReader().readDoubleAttribute("fictitiousQ0");
context.getReader().readEndNode();
if (!Double.isNaN(p0)) {
vl.getNodeBreakerView().setFictitiousP0(node, p0);
}
if (!Double.isNaN(q0)) {
vl.getNodeBreakerView().setFictitiousQ0(node, q0);
}
}
private void readBusBreakerTopology(VoltageLevel vl, NetworkDeserializerContext context) {
context.getReader().readChildNodes(elementName -> {
switch (elementName) {
case BusSerDe.ROOT_ELEMENT_NAME -> BusSerDe.INSTANCE.read(vl, context);
case AbstractSwitchSerDe.ROOT_ELEMENT_NAME -> BusBreakerViewSwitchSerDe.INSTANCE.read(vl, context);
default -> throw new PowsyblException(String.format("Unknown element name '%s' in 'busBreakerTopology'", elementName));
}
});
}
}