Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Powerfactory lines: shunt admittance calculation. #2055

Merged
merged 10 commits into from
Apr 5, 2022
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* 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/.
*/
package com.powsybl.powerfactory.converter;

import java.util.List;
import java.util.Objects;

import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.Network;
import com.powsybl.powerfactory.converter.PowerFactoryImporter.ImportContext;
import com.powsybl.powerfactory.converter.PowerFactoryImporter.NodeRef;
import com.powsybl.powerfactory.model.DataObject;

/**
* @author Luma Zamarreño <zamarrenolm at aia.es>
* @author José Antonio Marqués <marquesja at aia.es>
*/
public abstract class AbstractConverter {

private final ImportContext importContext;

private final Network network;

AbstractConverter(ImportContext importContext, Network network) {
this.importContext = Objects.requireNonNull(importContext);
this.network = Objects.requireNonNull(network);
}

Network getNetwork() {
return network;
}

static double microFaradToSiemens(double frnom, double capacitance) {
return 2 * Math.PI * frnom * capacitance * 1.0e-6;
}

static double microSiemensToSiemens(double susceptance) {
return susceptance * 1.0e-6;
}

TwoNodeRefs checkAndGetTwoNodeRefs(DataObject obj) {
List<NodeRef> nodeRefs = importContext.objIdToNode.get(obj.getId());
if (nodeRefs == null || nodeRefs.size() != 2) {
throw new PowsyblException("Inconsistent number (" + (nodeRefs != null ? nodeRefs.size() : 0)
+ ") of connection for '" + obj + "'");
}
return new TwoNodeRefs(nodeRefs.get(0), nodeRefs.get(1));
}

static class TwoNodeRefs {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we do not need to create this class. We could keep the method checkNodes(obj, int connections) returning directly a List<NodeRef>. For 3-winding transformers we would need to extend it anyway

private final NodeRef end1;
private final NodeRef end2;

TwoNodeRefs(NodeRef end1, NodeRef end2) {
this.end1 = end1;
this.end2 = end2;
}

NodeRef getEnd1() {
return end1;
}

NodeRef getEnd2() {
return end2;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* 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/.
*/
package com.powsybl.powerfactory.converter;

import java.util.Optional;

import com.powsybl.iidm.network.Network;
import com.powsybl.powerfactory.converter.PowerFactoryImporter.ImportContext;
import com.powsybl.powerfactory.model.DataObject;

/**
* @author Luma Zamarreño <zamarrenolm at aia.es>
* @author José Antonio Marqués <marquesja at aia.es>
*/

class LineConverter extends AbstractConverter {

private static final String TYP_ID = "typ_id";

LineConverter(ImportContext importContext, Network network) {
super(importContext, network);
}

void createFromElmLne(DataObject elmLne) {
TwoNodeRefs nodeRefs = checkAndGetTwoNodeRefs(elmLne);
Optional<LineModel> lineModel = LineModel.createFromTypLne(elmLne);
if (lineModel.isEmpty()) {
return;
}

getNetwork().newLine()
.setId(elmLne.getLocName())
.setEnsureIdUnicity(true)
.setVoltageLevel1(nodeRefs.getEnd1().voltageLevelId)
.setVoltageLevel2(nodeRefs.getEnd2().voltageLevelId)
.setNode1(nodeRefs.getEnd1().node)
.setNode2(nodeRefs.getEnd2().node)
.setR(lineModel.get().r)
.setX(lineModel.get().x)
.setG1(lineModel.get().g1)
.setB1(lineModel.get().b1)
.setG2(lineModel.get().g2)
.setB2(lineModel.get().b2)
.add();
}

static class LineModel {
private final double r;
private final double x;
private final double g1;
private final double b1;
private final double g2;
private final double b2;

LineModel(double r, double x, double g1, double b1, double g2, double b2) {
this.r = r;
this.x = x;
this.g1 = g1;
this.b1 = b1;
this.g2 = g2;
this.b2 = b2;
}

static Optional<LineModel> createFromTypLne(DataObject elmLne) {
Optional<DataObject> typLne = elmLne.getObjectAttributeValue(TYP_ID).resolve();
if (typLne.isPresent()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use of isPresent, Optional.of and empty can be simplified using map

return Optional.of(typeLneModel(elmLne, typLne.get()));
}
return Optional.empty();
}

private static LineModel typeLneModel(DataObject elmLne, DataObject typLne) {
float dline = elmLne.getFloatAttributeValue("dline");
float rline = typLne.getFloatAttributeValue("rline");
float xline = typLne.getFloatAttributeValue("xline");
double r = rline * dline;
double x = xline * dline;

double g = typeLneModelShuntConductance(typLne) * dline;
double b = typeLneModelShuntSusceptance(typLne) * dline;

return new LineModel(r, x, g * 0.5, b * 0.5, g * 0.5, b * 0.5);
}

private static double typeLneModelShuntConductance(DataObject typLne) {
Optional<Float> gline = typLne.findFloatAttributeValue("gline");
if (gline.isPresent()) {
return microSiemensToSiemens(gline.get());
}
Optional<Float> tline = typLne.findFloatAttributeValue("tline");
Optional<Float> bline = typLne.findFloatAttributeValue("bline");
if (tline.isPresent() && bline.isPresent()) {
return microSiemensToSiemens(bline.get() * tline.get());
}
return 0.0;
}

private static double typeLneModelShuntSusceptance(DataObject typLne) {
Optional<Float> bline = typLne.findFloatAttributeValue("bline");
if (bline.isPresent()) {
return microSiemensToSiemens(bline.get());
}
Optional<Float> frnom = typLne.findFloatAttributeValue("frnom");
Optional<Float> cline = typLne.findFloatAttributeValue("cline");
if (frnom.isPresent() && cline.isPresent()) {
return microFaradToSiemens(frnom.get(), cline.get());
}
return 0.0;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ private Network createNetwork(StudyCase studyCase, NetworkFactory networkFactory
break;

case "ElmLne":
createLine(network, importContext, obj);
new LineConverter(importContext, network).createFromElmLne(obj);
break;

case "ElmTr2":
Expand Down Expand Up @@ -353,43 +353,6 @@ private void createReactiveLimits(DataObject elmSym, DataObject typSym, Generato
}
}

private void createLine(Network network, ImportContext importContext, DataObject elmLne) {
Collection<NodeRef> nodeRefs = checkNodes(elmLne, importContext.objIdToNode, 2);
Iterator<NodeRef> it = nodeRefs.iterator();
NodeRef nodeRef1 = it.next();
NodeRef nodeRef2 = it.next();
float dline = elmLne.getFloatAttributeValue("dline");
DataObject typLne = elmLne.getObjectAttributeValue(TYP_ID)
.resolve()
.orElseThrow();
float rline = typLne.getFloatAttributeValue("rline");
float xline = typLne.getFloatAttributeValue("xline");
float bline = typLne.getFloatAttributeValue("bline");
float tline = typLne.getFloatAttributeValue("tline");
double r = rline * dline;
double x = xline * dline;
double g = tline * dline * 10e-6;
double b = bline * dline * 10e-6;
double g1 = g / 2;
double g2 = g / 2;
double b1 = b / 2;
double b2 = b / 2;
network.newLine()
.setId(elmLne.getLocName())
.setEnsureIdUnicity(true)
.setVoltageLevel1(nodeRef1.voltageLevelId)
.setVoltageLevel2(nodeRef2.voltageLevelId)
.setNode1(nodeRef1.node)
.setNode2(nodeRef2.node)
.setR(r)
.setX(x)
.setG1(g1)
.setG2(g2)
.setB1(b1)
.setB2(b2)
.add();
}

private void create2wTransformer(Network network, ImportContext importContext, DataObject elmTr2) {
Collection<NodeRef> nodeRefs = checkNodes(elmTr2, importContext.objIdToNode, 2);
Iterator<NodeRef> it = nodeRefs.iterator();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

/**
* @author Geoffroy Jamgotchian <geoffroy.jamgotchian at rte-france.com>
* @author Luma Zamarreño <zamarrenolm at aia.es>
* @author José Antonio Marqués <marquesja at aia.es>
*/
public class PowerFactoryImporterTest extends AbstractConverterTest {

Expand Down Expand Up @@ -59,7 +61,7 @@ public void testExistsAndCopy() throws IOException {
assertTrue(Files.exists(fileSystem.getPath("/work/ieee14-copy.dgs")));
}

private void importAndCompareXml(String id) {
private boolean importAndCompareXml(String id) {
Network network = new PowerFactoryImporter()
.importData(new ResourceDataSource(id, new ResourceSet("/", id + ".dgs")),
NetworkFactory.findDefault(),
Expand All @@ -72,10 +74,31 @@ private void importAndCompareXml(String id) {
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return true;
}

@Test
public void ieee14Test() {
importAndCompareXml("ieee14");
assertTrue(importAndCompareXml("ieee14"));
}

@Test
public void twoBusesLineWithBTest() {
assertTrue(importAndCompareXml("TwoBusesLineWithB"));
}

@Test
public void twoBusesLineWithGandBTest() {
assertTrue(importAndCompareXml("TwoBusesLineWithGandB"));
}

@Test
public void twoBusesLineWithTandBTest() {
assertTrue(importAndCompareXml("TwoBusesLineWithTandB"));
}

@Test
public void twoBusesLineWithCTest() {
assertTrue(importAndCompareXml("TwoBusesLineWithC"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
$$General;ID(a:40);Descr(a:40);Val(a:40)
1;Version;5.0

$$ElmNet;ID(a:40);loc_name(a:40);fold_id(p);frnom(r)
2;MS;;50

$$ElmTerm;ID(a:40);loc_name(a:40);fold_id(p);iUsage(i);uknom(r)
3;Busbar-A;2;0;220
4;Busbar-B;2;0;220

$$ElmLne;ID(a:40);loc_name(a:40);for_name(a:50);fold_id(p);typ_id(p);dline(r);fline(r);GPScoords:SIZEROW(i);GPScoords:SIZECOL(i);nlnum(i);inAir(i)
5;lne_A_B_1;;2;10;1;1;0;0;1;0

$$StaCubic;ID(a:40);loc_name(a:40);fold_id(p);chr_name(a:20);obj_id(p);obj_bus(i)
6;Cub_1;3;;5;0
7;Cub_1;4;;5;1

$$StaSwitch;ID(a:40);loc_name(a:40);fold_id(p);iUse(i);on_off(i)
8;Switch;6;1;1
9;Switch;7;1;1

$$TypLne;ID(a:40);loc_name(a:40);for_name(a:50);fold_id(p);uline(r);sline(r);InomAir(r);cohl_(i);rline(r);xline(r);rline0(r);xline0(r);Ithr(r);tmax(r);rtemp(r);systp(i);nlnph(i);nneutral(i);frnom(r);mlei(a:2);bline(r);bline0(r)
10;tlne_A_B_1;;5;138;99999;1;0;3,561228;11,7311;999999;999999;0;80;80;0;3;0;60;Cu;82,54568;0

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<iidm:network xmlns:iidm="http://www.powsybl.org/schema/iidm/1_7" id="TwoBusesLineWithB" caseDate="2021-01-01T10:00:00.000+02:00" forecastDistance="0" sourceFormat="POWER-FACTORY" minimumValidationLevel="STEADY_STATE_HYPOTHESIS">
<iidm:substation id="S2">
<iidm:voltageLevel id="VL0" nominalV="220.0" topologyKind="NODE_BREAKER">
<iidm:nodeBreakerTopology>
<iidm:busbarSection id="VL0_Busbar-A" node="0"/>
<iidm:switch id="VL0_Switch" kind="BREAKER" retained="false" open="false" node1="0" node2="1"/>
</iidm:nodeBreakerTopology>
</iidm:voltageLevel>
</iidm:substation>
<iidm:substation id="S1">
<iidm:voltageLevel id="VL1" nominalV="220.0" topologyKind="NODE_BREAKER">
<iidm:nodeBreakerTopology>
<iidm:busbarSection id="VL1_Busbar-B" node="0"/>
<iidm:switch id="VL1_Switch" kind="BREAKER" retained="false" open="false" node1="0" node2="1"/>
</iidm:nodeBreakerTopology>
</iidm:voltageLevel>
</iidm:substation>
<iidm:line id="lne_A_B_1" r="3.561228036880493" x="11.731100082397461" g1="0.0" b1="4.1272838592529294E-5" g2="0.0" b2="4.1272838592529294E-5" node1="1" voltageLevelId1="VL0" node2="1" voltageLevelId2="VL1"/>
</iidm:network>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
$$General;ID(a:40);Descr(a:40);Val(a:40)
1;Version;5.0

$$ElmNet;ID(a:40);loc_name(a:40);fold_id(p);frnom(r)
2;MS;;50

$$ElmTerm;ID(a:40);loc_name(a:40);fold_id(p);iUsage(i);uknom(r)
3;Busbar-A;2;0;220
4;Busbar-B;2;0;220

$$ElmLne;ID(a:40);loc_name(a:40);for_name(a:50);fold_id(p);typ_id(p);dline(r);fline(r);GPScoords:SIZEROW(i);GPScoords:SIZECOL(i);nlnum(i);inAir(i)
5;lne_A_B_1;;2;10;1;1;0;0;1;0

$$StaCubic;ID(a:40);loc_name(a:40);fold_id(p);chr_name(a:20);obj_id(p);obj_bus(i)
6;Cub_1;3;;5;0
7;Cub_1;4;;5;1

$$StaSwitch;ID(a:40);loc_name(a:40);fold_id(p);iUse(i);on_off(i)
8;Switch;6;1;1
9;Switch;7;1;1

$$TypLne;ID(a:40);loc_name(a:40);for_name(a:50);fold_id(p);uline(r);sline(r);InomAir(r);cohl_(i);rline(r);xline(r);rline0(r);xline0(r);Ithr(r);tmax(r);rtemp(r);systp(i);nlnph(i);nneutral(i);frnom(r);mlei(a:2);cline(r)
10;tlne_A_B_1;;5;138;99999;1;0;3,561228;11,7311;999999;999999;0;80;80;0;3;0;60;Cu;0,218959

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<iidm:network xmlns:iidm="http://www.powsybl.org/schema/iidm/1_7" id="TwoBusesLineWithC" caseDate="2021-01-01T10:00:00.000+02:00" forecastDistance="0" sourceFormat="POWER-FACTORY" minimumValidationLevel="STEADY_STATE_HYPOTHESIS">
<iidm:substation id="S2">
<iidm:voltageLevel id="VL0" nominalV="220.0" topologyKind="NODE_BREAKER">
<iidm:nodeBreakerTopology>
<iidm:busbarSection id="VL0_Busbar-A" node="0"/>
<iidm:switch id="VL0_Switch" kind="BREAKER" retained="false" open="false" node1="0" node2="1"/>
</iidm:nodeBreakerTopology>
</iidm:voltageLevel>
</iidm:substation>
<iidm:substation id="S1">
<iidm:voltageLevel id="VL1" nominalV="220.0" topologyKind="NODE_BREAKER">
<iidm:nodeBreakerTopology>
<iidm:busbarSection id="VL1_Busbar-B" node="0"/>
<iidm:switch id="VL1_Switch" kind="BREAKER" retained="false" open="false" node1="0" node2="1"/>
</iidm:nodeBreakerTopology>
</iidm:voltageLevel>
</iidm:substation>
<iidm:line id="lne_A_B_1" r="3.561228036880493" x="11.731100082397461" g1="0.0" b1="4.127279984520725E-5" g2="0.0" b2="4.127279984520725E-5" node1="1" voltageLevelId1="VL0" node2="1" voltageLevelId2="VL1"/>
</iidm:network>
Loading