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: GeneratorConverter #2061

Merged
merged 7 commits into from
Apr 8, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
*/
public abstract class AbstractConverter {

static final String TYP_ID = "typ_id";
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can more all attribute names in a dedicated utility class DataAttributeNames?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


private final ImportContext importContext;

private final Network network;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/**
* 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 java.util.OptionalInt;

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;

/**
* @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();
Optional<ReactiveLimits> reactiveLimits = ReactiveLimits.create(elmSym);
if (reactiveLimits.isPresent()) {
g.newMinMaxReactiveLimits()
.setMinQ(reactiveLimits.get().minQ)
.setMaxQ(reactiveLimits.get().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;
}
Optional<String> avMode = elmSym.findStringAttributeValue("av_mode");
if (avMode.isPresent()) {
return avMode.get().equals("constv");
}
return false;
}

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

private static double maxP(DataObject elmSym, double p) {
Optional<Float> pMaxUc = elmSym.findFloatAttributeValue("Pmax_uc");
if (pMaxUc.isPresent()) {
return pMaxUc.get();
}
if (p > 0.0) {
return p;
}
return 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 + "'");
}
Optional<DataObject> typSym = elmSym.findObjectAttributeValue(TYP_ID).flatMap(DataObjectRef::resolve);
if (typSym.isPresent()) {
return create(elmSym, typSym.get());
} else {
return Optional.empty();
}
}

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(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,34 @@ 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"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
$$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;12;1;1;0;0;1;0

$$ElmSym;ID(a:40);loc_name(a:40);fold_id(p);typ_id(p);ngnum(i);i_mot(i);chr_name(a:20);outserv(i);pgini(r);qgini(r);usetp(r);iv_mode(i);q_min(r);q_max(r);Pmin_uc(r);Pmax_uc(r);iqtype(i)
6;sym_B_1;2;13;1;0;;0;25;10;1,02;1;-1,1;1,2;0;250;0

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

$$StaSwitch;ID(a:40);loc_name(a:40);fold_id(p);iUse(i);on_off(i)
10;Switch;7;1;1
11;Switch;8;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)
12;tlne_A_B_1;;5;220;99999;1;0;3,561228;11,7311;999999;999999;0;80;80;0;3;0;60;Cu;82,54568;0

$$TypSym;ID(a:40);loc_name(a:40);fold_id(p);sgn(r);ugn(r);cosn(r);xd(r);xq(r);xdsss(r);rstr(r);xdsat(r);satur(i);Q_min(r);Q_max(r);q_min(r);q_max(r)
13;tsym_B_1;220;100;2;1;2;2;1;0;1,2;0;-100;100;-1;1
Loading