GeneratorConverter.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 com.powsybl.iidm.network.Generator;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.ReactiveCapabilityCurveAdder;
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 java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
/**
* @author Luma Zamarre��o {@literal <zamarrenolm at aia.es>}
* @author Jos�� Antonio Marqu��s {@literal <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(getId(elmSym))
.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<List<CapabilityCurvePoint>> capabitlityCurve = CapabilityCurvePoint.create(elmSym);
if (capabitlityCurve.isPresent()) {
ReactiveCapabilityCurveAdder adder = g.newReactiveCapabilityCurve();
capabitlityCurve.get().forEach(capabitlityCurvePoint -> adder.beginPoint()
.setP(capabitlityCurvePoint.p)
.setMinQ(capabitlityCurvePoint.qMin)
.setMaxQ(capabitlityCurvePoint.qMax)
.endPoint());
adder.add();
} else {
ReactiveLimits.create(elmSym).ifPresent(reactiveLimits -> g.newMinMaxReactiveLimits()
.setMinQ(reactiveLimits.minQ)
.setMaxQ(reactiveLimits.maxQ)
.add());
}
}
static boolean isSlack(DataObject elmSym) {
OptionalInt ipCtrl = elmSym.findIntAttributeValue("ip_ctrl");
if (ipCtrl.isPresent() && ipCtrl.getAsInt() == 1) {
return true;
}
Optional<String> bustp = elmSym.findStringAttributeValue("bustp");
return bustp.isPresent() && bustp.get().equals("SL");
}
private static final 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;
private 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;
}
private static GeneratorModel create(DataObject elmSym) {
boolean voltageRegulatorOn = voltageRegulatorOn(elmSym);
float pgini = elmSym.findFloatAttributeValue("pgini_a").orElse(elmSym.getFloatAttributeValue("pgini"));
float qgini = elmSym.findFloatAttributeValue("qgini_a").orElse(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);
}
}
private static final class ReactiveLimits {
private final double minQ;
private final double maxQ;
private ReactiveLimits(double minQ, double maxQ) {
this.minQ = minQ;
this.maxQ = maxQ;
}
private static Optional<ReactiveLimits> create(DataObject elmSym) {
Optional<DataObject> pQlimType = elmSym.findObjectAttributeValue("pQlimType").flatMap(DataObjectRef::resolve);
if (pQlimType.isPresent()) {
return Optional.empty();
}
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();
}
}
private static final class CapabilityCurvePoint {
private final double p;
private final double qMin;
private final double qMax;
private CapabilityCurvePoint(double p, double qMin, double qMax) {
this.p = p;
this.qMin = qMin;
this.qMax = qMax;
}
private static Optional<List<CapabilityCurvePoint>> create(DataObject elmSym) {
Optional<DataObject> pQlimType = elmSym.findObjectAttributeValue("pQlimType")
.flatMap(DataObjectRef::resolve);
if (pQlimType.isPresent()) {
Optional<List<Double>> capP = pQlimType.get().findDoubleVectorAttributeValue("cap_P");
Optional<List<Double>> capQmn = pQlimType.get().findDoubleVectorAttributeValue("cap_Qmn");
Optional<List<Double>> capQmx = pQlimType.get().findDoubleVectorAttributeValue("cap_Qmx");
if (capP.isPresent() && capQmn.isPresent() && capQmx.isPresent()
&& !capP.get().isEmpty()
&& capP.get().size() == capQmn.get().size() && capP.get().size() == capQmx.get().size()) {
List<CapabilityCurvePoint> capabilityCurve = new ArrayList<>();
for (int i = 0; i < capP.get().size(); i++) {
capabilityCurve.add(new CapabilityCurvePoint(capP.get().get(i), capQmn.get().get(i), capQmx.get().get(i)));
}
return Optional.of(capabilityCurve);
}
}
return Optional.empty();
}
}
static String getId(DataObject elmSym) {
return elmSym.getLocName();
}
}