Skip to content

Commit

Permalink
PowerFactory: GeneratorConverter (#2061)
Browse files Browse the repository at this point in the history
Signed-off-by: José Antonio Marqués <marquesja@aia.es>
Signed-off-by: Luma <zamarrenolm@aia.es>
  • Loading branch information
marqueslanauja authored Apr 8, 2022
1 parent 5ed18db commit 6151595
Show file tree
Hide file tree
Showing 28 changed files with 742 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,8 @@ public String getVoltageLevelId(Set<Integer> ids) {
}
}

if (voltageLevelId == null) { // automatic naming
return "VL" + noNameVoltageLevelCount++;
}

return voltageLevelId;
// automatic naming
return Objects.requireNonNullElseGet(voltageLevelId, () -> "VL" + noNameVoltageLevelCount++);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* 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;

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

final class DataAttributeNames {

private DataAttributeNames() {
}

static final String TYP_ID = "typ_id";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/**
* 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 com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.Generator;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.VoltageLevel;
import com.powsybl.powerfactory.converter.PowerFactoryImporter.ImportContext;
import com.powsybl.powerfactory.converter.PowerFactoryImporter.NodeRef;
import com.powsybl.powerfactory.model.DataObject;
import com.powsybl.powerfactory.model.DataObjectRef;

import java.util.Optional;
import java.util.OptionalInt;

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

class GeneratorConverter extends AbstractConverter {

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

void create(DataObject elmSym) {
NodeRef nodeRef = checkNodes(elmSym, 1).get(0);
GeneratorModel generatorModel = GeneratorModel.create(elmSym);

VoltageLevel vl = getNetwork().getVoltageLevel(nodeRef.voltageLevelId);
Generator g = vl.newGenerator()
.setId(elmSym.getLocName())
.setEnsureIdUnicity(true)
.setNode(nodeRef.node)
.setTargetP(generatorModel.targetP)
.setTargetQ(generatorModel.targetQ)
.setTargetV(generatorModel.targetVpu * vl.getNominalV())
.setVoltageRegulatorOn(generatorModel.voltageRegulatorOn)
.setMinP(generatorModel.minP)
.setMaxP(generatorModel.maxP)
.add();
ReactiveLimits.create(elmSym).ifPresent(reactiveLimits ->
g.newMinMaxReactiveLimits()
.setMinQ(reactiveLimits.minQ)
.setMaxQ(reactiveLimits.maxQ)
.add());
}

static class GeneratorModel {
private final double targetP;
private final double targetQ;
private final double targetVpu;
private final boolean voltageRegulatorOn;
private final double minP;
private final double maxP;

GeneratorModel(double targetP, double targetQ, double targetVpu, boolean voltageRegulatorOn, double minP, double maxP) {
this.targetP = targetP;
this.targetQ = targetQ;
this.targetVpu = targetVpu;
this.voltageRegulatorOn = voltageRegulatorOn;
this.minP = minP;
this.maxP = maxP;
}

static GeneratorModel create(DataObject elmSym) {
boolean voltageRegulatorOn = voltageRegulatorOn(elmSym);

float pgini = elmSym.getFloatAttributeValue("pgini");
float qgini = elmSym.getFloatAttributeValue("qgini");
double usetp = elmSym.getFloatAttributeValue("usetp");
double pMinUc = minP(elmSym, pgini);
double pMaxUc = maxP(elmSym, pgini);

return new GeneratorModel(pgini, qgini, usetp, voltageRegulatorOn, pMinUc, pMaxUc);
}

private static boolean voltageRegulatorOn(DataObject elmSym) {
OptionalInt ivMode = elmSym.findIntAttributeValue("iv_mode");
if (ivMode.isPresent()) {
return ivMode.getAsInt() == 1;
}
return elmSym.findStringAttributeValue("av_mode").map(s -> s.equals("constv")).orElse(false);
}

private static double minP(DataObject elmSym, double p) {
Optional<Float> pMinUc = elmSym.findFloatAttributeValue("Pmin_uc");
if (pMinUc.isPresent()) {
return pMinUc.get();
}
return Math.min(p, 0.0);
}

private static double maxP(DataObject elmSym, double p) {
Optional<Float> pMaxUc = elmSym.findFloatAttributeValue("Pmax_uc");
if (pMaxUc.isPresent()) {
return pMaxUc.get();
}
return Math.max(p, 0.0);
}
}

static class ReactiveLimits {
private final double minQ;
private final double maxQ;

ReactiveLimits(double minQ, double maxQ) {
this.minQ = minQ;
this.maxQ = maxQ;
}

static Optional<ReactiveLimits> create(DataObject elmSym) {
Optional<DataObject> pQlimType = elmSym.findObjectAttributeValue("pQlimType").flatMap(DataObjectRef::resolve);
if (pQlimType.isPresent()) {
throw new PowsyblException("Reactive capability curve not supported: '" + elmSym + "'");
}
return elmSym
.findObjectAttributeValue(DataAttributeNames.TYP_ID)
.flatMap(DataObjectRef::resolve)
.flatMap(typSym -> create(elmSym, typSym));
}

private static Optional<ReactiveLimits> create(DataObject elmSym, DataObject typSym) {

OptionalInt iqtype = elmSym.findIntAttributeValue("iqtype");
Optional<Float> qMinPuElm = elmSym.findFloatAttributeValue("q_min");
Optional<Float> qMaxPuElm = elmSym.findFloatAttributeValue("q_max");
Optional<Float> qMinPuTyp = typSym.findFloatAttributeValue("q_min");
Optional<Float> qMaxPuTyp = typSym.findFloatAttributeValue("q_max");
Optional<Float> sgn = typSym.findFloatAttributeValue("sgn");
Optional<Float> qMinMvar = typSym.findFloatAttributeValue("Q_min");
Optional<Float> qMaxMvar = typSym.findFloatAttributeValue("Q_max");

// Reactive limits form Elm
double qMinElm = Double.NaN;
double qMaxElm = Double.NaN;
if (qMinPuElm.isPresent() && qMaxPuElm.isPresent() && sgn.isPresent()) {
qMinElm = qMinPuElm.get() * sgn.get();
qMaxElm = qMaxPuElm.get() * sgn.get();
}
// Reactive limits from Typ
double qMinTyp = Double.NaN;
double qMaxTyp = Double.NaN;
if (qMinMvar.isPresent() && qMaxMvar.isPresent()) {
qMinTyp = qMinMvar.get();
qMaxTyp = qMaxMvar.get();
} else if (qMinPuTyp.isPresent() && qMaxPuTyp.isPresent() && sgn.isPresent()) {
qMinTyp = qMinPuTyp.get() * sgn.get();
qMaxTyp = qMaxPuTyp.get() * sgn.get();
}

if (iqtype.isPresent() && iqtype.getAsInt() == 0 && !Double.isNaN(qMinElm) && !Double.isNaN(qMaxElm)) {
return Optional.of(new ReactiveLimits(qMinElm, qMaxElm));
}
if (iqtype.isPresent() && iqtype.getAsInt() != 0 && !Double.isNaN(qMinTyp) && !Double.isNaN(qMaxTyp)) {
return Optional.of(new ReactiveLimits(qMinTyp, qMaxTyp));
}
if (!Double.isNaN(qMinElm) && !Double.isNaN(qMaxElm)) {
return Optional.of(new ReactiveLimits(qMinElm, qMaxElm));
}
if (!Double.isNaN(qMinTyp) && !Double.isNaN(qMaxTyp)) {
return Optional.of(new ReactiveLimits(qMinTyp, qMaxTyp));
}
return Optional.empty();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.powsybl.powerfactory.converter.PowerFactoryImporter.ImportContext;
import com.powsybl.powerfactory.converter.PowerFactoryImporter.NodeRef;
import com.powsybl.powerfactory.model.DataObject;
import com.powsybl.powerfactory.model.DataObjectRef;

import java.util.List;
import java.util.Optional;
Expand All @@ -21,13 +22,11 @@

class LineConverter extends AbstractConverter {

private static final String TYP_ID = "typ_id";

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

void createFromElmLne(DataObject elmLne) {
void create(DataObject elmLne) {
List<NodeRef> nodeRefs = checkNodes(elmLne, 2);
Optional<LineModel> lineModel = LineModel.createFromTypLne(elmLne);
if (lineModel.isEmpty()) {
Expand Down Expand Up @@ -70,7 +69,7 @@ static class LineModel {
}

static Optional<LineModel> createFromTypLne(DataObject elmLne) {
return elmLne.getObjectAttributeValue(TYP_ID).resolve().map(typLne -> typeLneModel(elmLne, typLne));
return elmLne.findObjectAttributeValue(DataAttributeNames.TYP_ID).flatMap(DataObjectRef::resolve).map(typLne -> typeLneModel(elmLne, typLne));
}

private static LineModel typeLneModel(DataObject elmLne, DataObject typLne) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ private Network createNetwork(StudyCase studyCase, NetworkFactory networkFactory
case "ElmSym":
case "ElmAsm":
case "ElmGenstat":
createGenerator(network, importContext, obj);
new GeneratorConverter(importContext, network).create(obj);
break;

case "ElmLod":
Expand All @@ -200,7 +200,7 @@ private Network createNetwork(StudyCase studyCase, NetworkFactory networkFactory
break;

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

case "ElmTr2":
Expand All @@ -220,6 +220,13 @@ private Network createNetwork(StudyCase studyCase, NetworkFactory networkFactory
// already processed
break;

case "TypLne":
case "TypSym":
case "TypLod":
case "TypTr2":
// Referenced by other objects
break;

case "ElmDsl":
case "ElmComp":
case "ElmStactrl":
Expand Down Expand Up @@ -296,63 +303,6 @@ private void createShunt(Network network, ImportContext importContext, DataObjec
.add();
}

private void createGenerator(Network network, ImportContext importContext, DataObject elmSym) {
NodeRef nodeRef = checkNodes(elmSym, importContext.objIdToNode, 1).iterator().next();
VoltageLevel vl = network.getVoltageLevel(nodeRef.voltageLevelId);
int ivMode = elmSym.getIntAttributeValue("iv_mode");
float pgini = elmSym.getFloatAttributeValue("pgini");
float qgini = elmSym.getFloatAttributeValue("qgini");
double usetp = elmSym.getFloatAttributeValue("usetp") * vl.getNominalV();
double pMinUc = elmSym.getFloatAttributeValue("Pmin_uc");
double pMaxUc = elmSym.getFloatAttributeValue("Pmax_uc");
Generator g = vl.newGenerator()
.setId(elmSym.getLocName())
.setEnsureIdUnicity(true)
.setNode(nodeRef.node)
.setTargetP(pgini)
.setTargetQ(qgini)
.setTargetV(usetp)
.setVoltageRegulatorOn(ivMode == 1)
.setMinP(pMinUc)
.setMaxP(pMaxUc)
.add();
elmSym.findObjectAttributeValue(TYP_ID)
.flatMap(DataObjectRef::resolve)
.ifPresent(typSym -> createReactiveLimits(elmSym, typSym, g));
}

private void createReactiveLimits(DataObject elmSym, DataObject typSym, Generator g) {
if (typSym.getDataClassName().equals("TypSym")) {
DataObject pQlimType = elmSym.findObjectAttributeValue("pQlimType")
.flatMap(DataObjectRef::resolve)
.orElse(null);
if (pQlimType != null) {
throw new PowsyblException("Reactive capability curve not supported: '" + elmSym + "'");
} else {
int iqtype = elmSym.getIntAttributeValue("iqtype");
float qMinPu;
float qMaxPu;
if (iqtype == 0) { // use limits specified in element
qMinPu = elmSym.getFloatAttributeValue("q_min");
qMaxPu = elmSym.getFloatAttributeValue("q_max");
} else { // use limits specified in type
qMinPu = typSym.getFloatAttributeValue("q_min");
qMaxPu = typSym.getFloatAttributeValue("q_max");
}
float qMinTyp = typSym.getFloatAttributeValue("Q_min");
float qMaxTyp = typSym.getFloatAttributeValue("Q_max");
double minQ = -1 * qMinPu * qMinTyp;
double maxQ = qMaxPu * qMaxTyp;
g.newMinMaxReactiveLimits()
.setMinQ(minQ)
.setMaxQ(maxQ)
.add();
}
} else {
// TODO
}
}

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 @@ -101,4 +101,49 @@ public void twoBusesLineWithTandBTest() {
public void twoBusesLineWithCTest() {
assertTrue(importAndCompareXml("TwoBusesLineWithC"));
}

@Test
public void twoBusesGeneratorTest() {
assertTrue(importAndCompareXml("TwoBusesGenerator"));
}

@Test
public void twoBusesGeneratorWithoutIvmodeTest() {
assertTrue(importAndCompareXml("TwoBusesGeneratorWithoutIvmode"));
}

@Test
public void twoBusesGeneratorAvmodeTest() {
assertTrue(importAndCompareXml("TwoBusesGeneratorAvmode"));
}

@Test
public void twoBusesGeneratorWithoutActiveLimitsTest() {
assertTrue(importAndCompareXml("TwoBusesGeneratorWithoutActiveLimits"));
}

@Test
public void twoBusesGeneratorIqtypeTest() {
assertTrue(importAndCompareXml("TwoBusesGeneratorIqtype"));
}

@Test
public void twoBusesGeneratorWithoutIqtypeTest() {
assertTrue(importAndCompareXml("TwoBusesGeneratorWithoutIqtype"));
}

@Test
public void twoBusesGeneratorElmReactiveLimits() {
assertTrue(importAndCompareXml("TwoBusesGeneratorElmReactiveLimits"));
}

@Test
public void twoBusesGeneratorTypReactiveLimits() {
assertTrue(importAndCompareXml("TwoBusesGeneratorTypReactiveLimits"));
}

@Test
public void twoBusesGeneratorTypMvarReactiveLimits() {
assertTrue(importAndCompareXml("TwoBusesGeneratorTypMvarReactiveLimits"));
}
}
Loading

0 comments on commit 6151595

Please sign in to comment.