SwitchedShuntCompensatorConverter.java
/**
* Copyright (c) 2021, 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.psse.converter;
import java.util.*;
import java.util.stream.Collectors;
import com.powsybl.iidm.network.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.powsybl.iidm.network.util.ContainersMapping;
import com.powsybl.psse.model.PsseVersion;
import com.powsybl.psse.model.pf.PssePowerFlowModel;
import com.powsybl.psse.model.pf.PsseSwitchedShunt;
import static com.powsybl.psse.converter.AbstractConverter.PsseEquipmentType.PSSE_SWITCHED_SHUNT;
import static com.powsybl.psse.model.PsseVersion.Major.V35;
/**
* @author Luma Zamarre��o {@literal <zamarrenolm at aia.es>}
* @author Jos�� Antonio Marqu��s {@literal <marquesja at aia.es>}
*/
class SwitchedShuntCompensatorConverter extends AbstractConverter {
SwitchedShuntCompensatorConverter(PsseSwitchedShunt psseSwitchedShunt, ContainersMapping containerMapping, Network network, PsseVersion version, NodeBreakerImport nodeBreakerImport) {
super(containerMapping, network);
this.psseSwitchedShunt = Objects.requireNonNull(psseSwitchedShunt);
this.version = Objects.requireNonNull(version);
this.nodeBreakerImport = Objects.requireNonNull(nodeBreakerImport);
}
void create() {
if (!getContainersMapping().isBusDefined(psseSwitchedShunt.getI())) {
return;
}
List<ShuntBlock> shuntBlocks = defineShuntBlocks(psseSwitchedShunt, version);
if (shuntBlocks.isEmpty()) {
return;
}
String id = defineShuntId(psseSwitchedShunt, version);
VoltageLevel voltageLevel = getNetwork().getVoltageLevel(getContainersMapping().getVoltageLevelId(psseSwitchedShunt.getI()));
ShuntCompensatorAdder adder = voltageLevel.newShuntCompensator()
.setId(getSwitchedShuntId(psseSwitchedShunt.getI(), id))
.setSectionCount(defineSectionCount(psseSwitchedShunt.getBinit(), shuntBlocks));
String equipmentId = getNodeBreakerEquipmentId(PSSE_SWITCHED_SHUNT, psseSwitchedShunt.getI(), defineShuntId(psseSwitchedShunt, version));
OptionalInt node = nodeBreakerImport.getNode(getNodeBreakerEquipmentIdBus(equipmentId, psseSwitchedShunt.getI(), 0, 0, psseSwitchedShunt.getI(), "I"));
if (node.isPresent()) {
adder.setNode(node.getAsInt());
} else {
String busId = getBusId(psseSwitchedShunt.getI());
adder.setConnectableBus(busId);
adder.setBus(psseSwitchedShunt.getStat() == 1 ? busId : null);
}
ShuntCompensatorNonLinearModelAdder modelAdder = adder.newNonLinearModel();
shuntBlocks.forEach(shuntBlock -> {
for (int i = 0; i < shuntBlock.getN(); i++) {
modelAdder.beginSection()
.setG(0.0)
.setB(powerToShuntAdmittance(shuntBlock.getB(), voltageLevel.getNominalV()))
.endSection();
}
});
modelAdder.add();
adder.add();
}
void addControl() {
String id = defineShuntId(psseSwitchedShunt, version);
ShuntCompensator shunt = getNetwork().getShuntCompensator(getSwitchedShuntId(psseSwitchedShunt.getI(), id));
// Add control only if shunt has been created
if (shunt == null) {
return;
}
Terminal regulatingTerminal = defineRegulatingTerminal(psseSwitchedShunt, getNetwork(), shunt, version, nodeBreakerImport);
// Discard control if the switchedShunt is controlling an isolated bus
if (regulatingTerminal == null) {
return;
}
if (!isControllingVoltage(psseSwitchedShunt)) {
return;
}
boolean psseVoltageRegulatorOn = true;
double vnom = regulatingTerminal.getVoltageLevel().getNominalV();
double vLow = psseSwitchedShunt.getVswlo() * vnom;
double vHigh = psseSwitchedShunt.getVswhi() * vnom;
double targetV = 0.5 * (vLow + vHigh);
boolean voltageRegulatorOn = false;
double targetDeadband = 0.0;
if (targetV != 0.0) {
targetDeadband = vHigh - vLow;
voltageRegulatorOn = psseVoltageRegulatorOn;
}
shunt.setTargetV(targetV)
.setTargetDeadband(targetDeadband)
.setVoltageRegulatorOn(voltageRegulatorOn)
.setRegulatingTerminal(regulatingTerminal);
}
private static boolean isControllingVoltage(PsseSwitchedShunt psseSwitchedShunt) {
return psseSwitchedShunt.getModsw() == 1 || psseSwitchedShunt.getModsw() == 2;
}
// Nreg (version 35) is not yet considered
private static Terminal defineRegulatingTerminal(PsseSwitchedShunt psseSwitchedShunt, Network network, ShuntCompensator shunt, PsseVersion version, NodeBreakerImport nodeBreakerImport) {
Terminal regulatingTerminal = null;
if (switchedShuntRegulatingBus(psseSwitchedShunt, version) == 0) {
regulatingTerminal = shunt.getTerminal();
} else {
Optional<NodeBreakerImport.ControlR> control = nodeBreakerImport.getControl(switchedShuntRegulatingBus(psseSwitchedShunt, version));
if (control.isPresent()) {
int controlledNode = psseSwitchedShunt.getNreg() != 0 ? psseSwitchedShunt.getNreg() : control.get().node();
regulatingTerminal = findTerminalNode(network, control.get().voltageLevelId(), controlledNode);
} else {
String regulatingBusId = getBusId(switchedShuntRegulatingBus(psseSwitchedShunt, version));
Bus bus = network.getBusBreakerView().getBus(regulatingBusId);
if (bus != null) {
regulatingTerminal = bus.getConnectedTerminalStream().findFirst().orElse(null);
}
}
}
if (regulatingTerminal == null) {
String shuntId = defineShuntId(psseSwitchedShunt, version);
LOGGER.warn("SwitchedShunt {}. Regulating terminal is not assigned", shuntId);
}
return regulatingTerminal;
}
private static int switchedShuntRegulatingBus(PsseSwitchedShunt switchedShunt, PsseVersion psseVersion) {
if (psseVersion.major() == V35) {
return switchedShunt.getSwreg();
} else {
return switchedShunt.getSwrem();
}
}
private static int defineSectionCount(double binit, List<ShuntBlock> shuntBlocks) {
double maxDistance = Double.MAX_VALUE;
int sectionCount = 0;
for (int i = 0; i < shuntBlocks.size(); i++) {
double d = Math.abs(binit - shuntBlocks.get(i).getB());
if (d < maxDistance) {
maxDistance = d;
sectionCount = i + 1; // index + 1 (count) is expected as sectionCount
}
}
return sectionCount;
}
// IIDM only considers consecutive sections
private static List<ShuntBlock> defineShuntBlocks(PsseSwitchedShunt psseSwitchedShunt, PsseVersion version) {
List<ShuntBlock> psseBlocks = collectShuntBlocks(psseSwitchedShunt, version);
List<ShuntBlock> psseReactorBlocks = psseBlocks.stream().filter(sb -> sb.getB() < 0.0)
.collect(Collectors.toList());
List<ShuntBlock> psseCapacitorBlocks = psseBlocks.stream().filter(sb -> sb.getB() > 0.0)
.collect(Collectors.toList());
// In that case we do not consider any switched combination
// blocks are sorted and switched on in input order
// When Adjm is zero the input order is considered
if (psseSwitchedShunt.getAdjm() == 1) {
psseReactorBlocks.sort(Comparator.comparing(ShuntBlock::getB).reversed());
psseCapacitorBlocks.sort(Comparator.comparing(ShuntBlock::getB));
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Switched combination not exactly supported ({})",
getSwitchedShuntId(psseSwitchedShunt.getI(), defineShuntId(psseSwitchedShunt, version)));
}
}
double bAdd = 0.0;
List<ShuntBlock> shuntBlocks = new ArrayList<>();
bAdd = addShuntBlocks(psseReactorBlocks, bAdd, shuntBlocks);
if (psseSwitchedShunt.getAdjm() == 1) {
bAdd = 0.0;
}
addShuntBlocks(psseCapacitorBlocks, bAdd, shuntBlocks);
// Add the zero block, shunt disconnected
shuntBlocks.add(new ShuntBlock(1, 1, 0.0));
shuntBlocks.sort(Comparator.comparing(ShuntBlock::getB));
return shuntBlocks;
}
private static double addShuntBlocks(List<ShuntBlock> psseShuntBlocks, double bAddInitial, List<ShuntBlock> shuntBlocks) {
double bAdd = bAddInitial;
if (!psseShuntBlocks.isEmpty()) {
for (ShuntBlock psseCapacitorBlock : psseShuntBlocks) {
for (int j = 0; j < psseCapacitorBlock.getN(); j++) {
bAdd = bAdd + psseCapacitorBlock.getB();
shuntBlocks.add(new ShuntBlock(1, 1, bAdd));
}
}
}
return bAdd;
}
// defined blocks can be reactors (< 0) or / and capacitors ( > 0)
private static List<ShuntBlock> collectShuntBlocks(PsseSwitchedShunt psseSwitchedShunt, PsseVersion version) {
List<ShuntBlock> shuntBlocks = new ArrayList<>();
if (version.major() == V35) {
addShuntBlock(shuntBlocks, psseSwitchedShunt.getS1(), psseSwitchedShunt.getN1(), psseSwitchedShunt.getB1());
addShuntBlock(shuntBlocks, psseSwitchedShunt.getS2(), psseSwitchedShunt.getN2(), psseSwitchedShunt.getB2());
addShuntBlock(shuntBlocks, psseSwitchedShunt.getS3(), psseSwitchedShunt.getN3(), psseSwitchedShunt.getB3());
addShuntBlock(shuntBlocks, psseSwitchedShunt.getS4(), psseSwitchedShunt.getN4(), psseSwitchedShunt.getB4());
addShuntBlock(shuntBlocks, psseSwitchedShunt.getS5(), psseSwitchedShunt.getN5(), psseSwitchedShunt.getB5());
addShuntBlock(shuntBlocks, psseSwitchedShunt.getS6(), psseSwitchedShunt.getN6(), psseSwitchedShunt.getB6());
addShuntBlock(shuntBlocks, psseSwitchedShunt.getS7(), psseSwitchedShunt.getN7(), psseSwitchedShunt.getB7());
addShuntBlock(shuntBlocks, psseSwitchedShunt.getS8(), psseSwitchedShunt.getN8(), psseSwitchedShunt.getB8());
} else {
addShuntBlock(shuntBlocks, 1, psseSwitchedShunt.getN1(), psseSwitchedShunt.getB1());
addShuntBlock(shuntBlocks, 1, psseSwitchedShunt.getN2(), psseSwitchedShunt.getB2());
addShuntBlock(shuntBlocks, 1, psseSwitchedShunt.getN3(), psseSwitchedShunt.getB3());
addShuntBlock(shuntBlocks, 1, psseSwitchedShunt.getN4(), psseSwitchedShunt.getB4());
addShuntBlock(shuntBlocks, 1, psseSwitchedShunt.getN5(), psseSwitchedShunt.getB5());
addShuntBlock(shuntBlocks, 1, psseSwitchedShunt.getN6(), psseSwitchedShunt.getB6());
addShuntBlock(shuntBlocks, 1, psseSwitchedShunt.getN7(), psseSwitchedShunt.getB7());
addShuntBlock(shuntBlocks, 1, psseSwitchedShunt.getN8(), psseSwitchedShunt.getB8());
}
return shuntBlocks;
}
// Only in-service blocks are included (in-service s = 1 and out-of-service s = 0)
private static void addShuntBlock(List<ShuntBlock> shuntBlocks, int s, int n, double b) {
if (s == 0 || n == 0 || b == 0.0) {
return;
}
shuntBlocks.add(new ShuntBlock(s, n, b));
}
static class ShuntBlock {
int s;
int n;
double b;
ShuntBlock(int s, int n, double b) {
this.s = s;
this.n = n;
this.b = b;
}
int getS() {
return s;
}
int getN() {
return n;
}
double getB() {
return b;
}
}
private static String defineShuntId(PsseSwitchedShunt psseSwitchedShunt, PsseVersion version) {
if (version.major() == V35) {
return psseSwitchedShunt.getId();
} else {
return "1";
}
}
static void create(Network network, PssePowerFlowModel psseModel, ContextExport contextExport) {
PsseVersion version = PsseVersion.fromRevision(psseModel.getCaseIdentification().getRev());
List<PsseSwitchedShunt> switchedShunts = new ArrayList<>();
network.getShuntCompensators().forEach(shuntCompensator -> {
if (!isFixedShunt(shuntCompensator)) {
switchedShunts.add(createSwitchedShunt(shuntCompensator, version, contextExport));
}
});
psseModel.addSwitchedShunts(switchedShunts);
psseModel.replaceAllSwitchedShunts(psseModel.getSwitchedShunts().stream().sorted(Comparator.comparingInt(PsseSwitchedShunt::getI).thenComparing(PsseSwitchedShunt::getId)).toList());
}
static PsseSwitchedShunt createSwitchedShunt(ShuntCompensator shuntCompensator, PsseVersion version, ContextExport contextExport) {
PsseSwitchedShunt psseSwitchedShunt = createDefaultSwitchedShunt();
int busI = getTerminalBusI(shuntCompensator.getTerminal(), contextExport);
int regulatingBus = getRegulatingTerminalBusI(shuntCompensator.getRegulatingTerminal(), busI, switchedShuntRegulatingBus(psseSwitchedShunt, version), contextExport);
psseSwitchedShunt.setI(busI);
psseSwitchedShunt.setId(contextExport.getFullExport().getEquipmentCkt(shuntCompensator.getId(), PSSE_SWITCHED_SHUNT.getTextCode(), busI));
psseSwitchedShunt.setModsw(getModsw(shuntCompensator));
psseSwitchedShunt.setStat(getStatus(shuntCompensator.getTerminal(), contextExport));
psseSwitchedShunt.setVswhi(getVswhi(shuntCompensator));
psseSwitchedShunt.setVswlo(getVswlo(shuntCompensator));
psseSwitchedShunt.setSwreg(regulatingBus);
psseSwitchedShunt.setNreg(getRegulatingTerminalNode(shuntCompensator.getRegulatingTerminal(), contextExport));
psseSwitchedShunt.setBinit(shuntAdmittanceToPower(shuntCompensator.getB(), shuntCompensator.getTerminal().getVoltageLevel().getNominalV()));
setShuntBlocks(shuntCompensator, psseSwitchedShunt);
return psseSwitchedShunt;
}
private static int getModsw(ShuntCompensator shuntCompensator) {
return shuntCompensator.isVoltageRegulatorOn() ? 1 : 0;
}
private static double getVswhi(ShuntCompensator shuntCompensator) {
double targetV = shuntCompensator.getTargetV() + shuntCompensator.getTargetDeadband() * 0.5;
double nominalV = getRegulatingTerminalNominalV(shuntCompensator);
return Double.isFinite(targetV) && Double.isFinite(nominalV) && targetV > 0 && nominalV > 0 ? targetV / nominalV : 1.0;
}
private static double getVswlo(ShuntCompensator shuntCompensator) {
double targetV = shuntCompensator.getTargetV() - shuntCompensator.getTargetDeadband() * 0.5;
double nominalV = getRegulatingTerminalNominalV(shuntCompensator);
return Double.isFinite(targetV) && Double.isFinite(nominalV) && targetV > 0 && nominalV > 0 ? targetV / nominalV : 1.0;
}
private static double getRegulatingTerminalNominalV(ShuntCompensator shuntCompensator) {
return shuntCompensator.getRegulatingTerminal() != null ? shuntCompensator.getRegulatingTerminal().getVoltageLevel().getNominalV() : shuntCompensator.getTerminal().getVoltageLevel().getNominalV();
}
private static void setShuntBlocks(ShuntCompensator shuntCompensator, PsseSwitchedShunt psseSwitchedShunt) {
if (shuntCompensator.getModelType() == ShuntCompensatorModelType.LINEAR) {
ShuntCompensatorLinearModel linearModel = (ShuntCompensatorLinearModel) shuntCompensator.getModel();
setShuntBlocksForLinearModel(linearModel, shuntCompensator.getMaximumSectionCount(), shuntCompensator.getTerminal().getVoltageLevel().getNominalV(), psseSwitchedShunt);
} else {
ShuntCompensatorNonLinearModel nonLinearModel = (ShuntCompensatorNonLinearModel) shuntCompensator.getModel();
setShuntBlocksForNonLinearModel(nonLinearModel, shuntCompensator.getTerminal().getVoltageLevel().getNominalV(), psseSwitchedShunt);
}
}
private static void setShuntBlocksForLinearModel(ShuntCompensatorLinearModel linearModel, int maximumSectionCount, double nominalV, PsseSwitchedShunt psseSwitchedShunt) {
psseSwitchedShunt.setN1(maximumSectionCount);
psseSwitchedShunt.setB1(shuntAdmittanceToPower(linearModel.getBPerSection(), nominalV));
}
private static void setShuntBlocksForNonLinearModel(ShuntCompensatorNonLinearModel nonLinearModel, double nominalV, PsseSwitchedShunt psseSwitchedShunt) {
List<ShuntCompensatorNonLinearModel.Section> sections = nonLinearModel.getAllSections().stream().filter(section -> section.getB() != 0.0).toList();
psseSwitchedShunt.setN1(getN(sections, 0));
psseSwitchedShunt.setB1(shuntAdmittanceToPower(getB(sections, 0), nominalV));
psseSwitchedShunt.setN2(getN(sections, 1));
psseSwitchedShunt.setB2(shuntAdmittanceToPower(getB(sections, 1), nominalV));
psseSwitchedShunt.setN3(getN(sections, 2));
psseSwitchedShunt.setB3(shuntAdmittanceToPower(getB(sections, 2), nominalV));
psseSwitchedShunt.setN4(getN(sections, 3));
psseSwitchedShunt.setB4(shuntAdmittanceToPower(getB(sections, 3), nominalV));
psseSwitchedShunt.setN5(getN(sections, 4));
psseSwitchedShunt.setB5(shuntAdmittanceToPower(getB(sections, 4), nominalV));
psseSwitchedShunt.setN6(getN(sections, 5));
psseSwitchedShunt.setB6(shuntAdmittanceToPower(getB(sections, 5), nominalV));
psseSwitchedShunt.setN7(getN(sections, 6));
psseSwitchedShunt.setB7(shuntAdmittanceToPower(getB(sections, 6), nominalV));
psseSwitchedShunt.setN8(getRemainderN(sections, 7));
psseSwitchedShunt.setB8(shuntAdmittanceToPower(getRemainderB(sections, 7), nominalV));
}
private static int getN(List<ShuntCompensatorNonLinearModel.Section> sections, int index) {
return sections.size() > index ? 1 : 0;
}
private static double getB(List<ShuntCompensatorNonLinearModel.Section> sections, int index) {
return sections.size() > index ? sections.get(index).getB() : 0.0;
}
private static int getRemainderN(List<ShuntCompensatorNonLinearModel.Section> sections, int index) {
return sections.size() > index ? sections.size() - index : 0;
}
private static double getRemainderB(List<ShuntCompensatorNonLinearModel.Section> sections, int index) {
int n = getRemainderN(sections, index);
if (n <= 0) {
return 0.0;
}
double remainderB = 0.0;
for (int i = index; i < sections.size(); i++) {
remainderB += sections.get(i).getB();
}
return remainderB / n;
}
private static PsseSwitchedShunt createDefaultSwitchedShunt() {
PsseSwitchedShunt psseSwitchedShunt = new PsseSwitchedShunt();
psseSwitchedShunt.setI(0);
psseSwitchedShunt.setId("1");
psseSwitchedShunt.setModsw(1);
psseSwitchedShunt.setAdjm(0);
psseSwitchedShunt.setStat(1);
psseSwitchedShunt.setVswhi(1.0);
psseSwitchedShunt.setVswlo(1.0);
psseSwitchedShunt.setSwreg(0);
psseSwitchedShunt.setNreg(0);
psseSwitchedShunt.setRmpct(100.0);
psseSwitchedShunt.setRmidnt("");
psseSwitchedShunt.setBinit(0.0);
psseSwitchedShunt.setS1(1);
psseSwitchedShunt.setN1(0);
psseSwitchedShunt.setB1(0.0);
psseSwitchedShunt.setS2(1);
psseSwitchedShunt.setN2(0);
psseSwitchedShunt.setB2(0.0);
psseSwitchedShunt.setS3(1);
psseSwitchedShunt.setN3(0);
psseSwitchedShunt.setB3(0.0);
psseSwitchedShunt.setS4(1);
psseSwitchedShunt.setN4(0);
psseSwitchedShunt.setB4(0.0);
psseSwitchedShunt.setS5(1);
psseSwitchedShunt.setN5(0);
psseSwitchedShunt.setB5(0.0);
psseSwitchedShunt.setS6(1);
psseSwitchedShunt.setN6(0);
psseSwitchedShunt.setB6(0.0);
psseSwitchedShunt.setS7(1);
psseSwitchedShunt.setN7(0);
psseSwitchedShunt.setB7(0.0);
psseSwitchedShunt.setS8(1);
psseSwitchedShunt.setN8(0);
psseSwitchedShunt.setB8(0.0);
return psseSwitchedShunt;
}
static void update(Network network, PssePowerFlowModel psseModel) {
PsseVersion version = PsseVersion.fromRevision(psseModel.getCaseIdentification().getRev());
psseModel.getSwitchedShunts().forEach(psseSwitchedShunt -> {
String switchedShuntId = getSwitchedShuntId(psseSwitchedShunt.getI(), defineShuntId(psseSwitchedShunt, version));
ShuntCompensator switchedShunt = network.getShuntCompensator(switchedShuntId);
if (switchedShunt == null) {
psseSwitchedShunt.setStat(0);
} else {
psseSwitchedShunt.setStat(getUpdatedStatus(switchedShunt.getTerminal()));
psseSwitchedShunt.setBinit(getQ(switchedShunt));
}
});
}
private static double getQ(ShuntCompensator switchedShunt) {
return shuntAdmittanceToPower(switchedShunt.getB(switchedShunt.getSectionCount()),
switchedShunt.getTerminal().getVoltageLevel().getNominalV());
}
private final PsseSwitchedShunt psseSwitchedShunt;
private final PsseVersion version;
private final NodeBreakerImport nodeBreakerImport;
private static final Logger LOGGER = LoggerFactory.getLogger(SwitchedShuntCompensatorConverter.class);
}