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()));
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 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(getStatus(switchedShunt));
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);
}