NodeConverter.java
/**
* Copyright (c) 2022, 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.powerfactory.converter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.apache.commons.lang3.mutable.MutableInt;
import com.google.common.primitives.Ints;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.Substation;
import com.powsybl.iidm.network.TopologyKind;
import com.powsybl.iidm.network.VoltageLevel;
import com.powsybl.powerfactory.converter.PowerFactoryImporter.ImportContext;
import com.powsybl.powerfactory.model.DataObject;
import com.powsybl.powerfactory.model.DataObjectRef;
import com.powsybl.powerfactory.model.PowerFactoryException;
/**
* @author Luma Zamarre��o {@literal <zamarrenolm at aia.es>}
* @author Jos�� Antonio Marqu��s {@literal <marquesja at aia.es>}
*/
class NodeConverter extends AbstractConverter {
NodeConverter(ImportContext importContext, Network network) {
super(importContext, network);
}
/**
* A cubicle defines the connection between a node and a side of an element.
* A staSwitch object is automatically created inside the cubicle when an element is connected to a node.
* An ElmCoup is also connected to a node through a cubicle, but without any additional StaSwitch.
*
* An iidm node is created by each elmTerm and by each staSwitch connected to an element.
*/
void createAndMapConnectedObjs(DataObject elmTerm) {
VoltageLevel vl = findVoltageLevel(elmTerm);
// Create the node defined by elmTerm
int node = createNode(vl);
getImportContext().elmTermIdToNode.putIfAbsent(elmTerm.getId(), new NodeRef(vl.getId(), node, 0));
NodeModel nodeModel = NodeModel.create(vl, elmTerm);
createBusbarSectionIfNodeHasBeenDefinedAsSuch(vl, node, nodeModel);
// Create additional nodes associated to switches (staSwitch) included in cubicles
createAdditionalNodesAndMapConnectedObjs(vl, node, elmTerm);
}
private VoltageLevel findVoltageLevel(DataObject elmTerm) {
String voltageLevelId = getImportContext().containerMapping.getVoltageLevelId(Ints.checkedCast(elmTerm.getId()));
Objects.requireNonNull(voltageLevelId);
VoltageLevel vl = getNetwork().getVoltageLevel(voltageLevelId);
if (vl == null) {
vl = createVoltageLevel(voltageLevelId, getNominalVoltage(elmTerm));
}
return vl;
}
private VoltageLevel createVoltageLevel(String voltageLevelId, double nominalV) {
String substationId = getImportContext().containerMapping.getSubstationId(voltageLevelId);
Substation s = getNetwork().getSubstation(substationId);
if (s == null) {
s = getNetwork().newSubstation()
.setId(substationId)
.add();
}
return s.newVoltageLevel()
.setId(voltageLevelId)
.setNominalV(nominalV)
.setTopologyKind(TopologyKind.NODE_BREAKER)
.add();
}
static double getNominalVoltage(DataObject elmTerm) {
return elmTerm.getFloatAttributeValue("uknom");
}
private int createNode(VoltageLevel vl) {
MutableInt nodeCount = getImportContext().nodeCountByVoltageLevelId.computeIfAbsent(vl.getId(), k -> new MutableInt());
int node = nodeCount.intValue();
nodeCount.increment();
return node;
}
private void createBusbarSectionIfNodeHasBeenDefinedAsSuch(VoltageLevel vl, int node, NodeModel nodeModel) {
if (!nodeModel.isBusbarSection) {
return;
}
vl.getNodeBreakerView().newBusbarSection()
.setId(nodeModel.busbarId)
.setEnsureIdUnicity(true)
.setNode(node)
.add();
}
private void createAdditionalNodesAndMapConnectedObjs(VoltageLevel vl, int node, DataObject elmTerm) {
for (DataObject staCubic : elmTerm.getChildrenByClass("StaCubic")) {
DataObject connectedObj = staCubic.findObjectAttributeValue("obj_id")
.flatMap(DataObjectRef::resolve)
.orElse(null);
int busIndexIn = staCubic.getIntAttributeValue("obj_bus");
if (connectedObj == null) {
getImportContext().cubiclesObjectNotFound.add(staCubic);
} else {
int outOfService = connectedObj.findIntAttributeValue("outserv").orElse(0);
int referenceNode = createNodeSwitchAndInternalConnection(vl, node, staCubic, connectedObj.getDataClassName().equals("ElmCoup"),
connectedObj.getLocName(), outOfService == 1);
mapConnectedObjToNode(vl, referenceNode, busIndexIn, connectedObj);
}
}
}
private int createNodeSwitchAndInternalConnection(VoltageLevel vl, int node, DataObject staCubic,
boolean isConnectedObjSwitch, String connectedObjId, boolean outOfService) {
List<DataObject> staSwitches = staCubic.getChildrenByClass("StaSwitch");
int referenceNode = node;
// An internalConnection is created, as in iidm only one element can be connected to a node,
// except when the element is a ElmCoup (breaker).
if (staSwitches.isEmpty()) {
if (!isConnectedObjSwitch) {
int additionalNode = createNode(vl);
createInternalConnection(vl, node, additionalNode);
referenceNode = additionalNode;
}
} else if (staSwitches.size() == 1) {
int additionalNode = createNode(vl);
new SwitchConverter(getImportContext(), getNetwork()).createFromStaSwitch(vl, node, additionalNode, staSwitches.get(0));
referenceNode = additionalNode;
} else {
throw new PowerFactoryException("Only one staSwitch is allowed inside a cubicle");
}
// we have decided to create fictitious switches to map the outOfService attribute to IIDM
if (outOfService) {
int additionalNode = createNode(vl);
new SwitchConverter(getImportContext(), getNetwork()).createFictitiousSwitch(vl, referenceNode, additionalNode, connectedObjId);
referenceNode = additionalNode;
}
return referenceNode;
}
private void mapConnectedObjToNode(VoltageLevel vl, int node, int busIndexIn, DataObject connectedObj) {
getImportContext().objIdToNode.computeIfAbsent(connectedObj.getId(), k -> new ArrayList<>())
.add(new NodeRef(vl.getId(), node, busIndexIn));
}
private static final class NodeModel {
private final boolean isBusbarSection;
private final String busbarId;
private NodeModel(boolean isBusbarSection, String busbarId) {
this.isBusbarSection = isBusbarSection;
this.busbarId = busbarId;
}
// Usage:0=Busbar:1=Junction Node:2=Internal Node
private static NodeModel create(VoltageLevel vl, DataObject elmTerm) {
int iUsage = elmTerm.getIntAttributeValue("iUsage");
String busbarId = vl.getId() + "_" + elmTerm.getLocName();
return new NodeModel(iUsage == 0, busbarId);
}
}
}