DanglingLineConverter.java
/**
* Copyright (c) 2024, 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 com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.util.DanglingLineData;
import com.powsybl.psse.model.pf.*;
import org.apache.commons.math3.complex.Complex;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import static com.powsybl.psse.converter.AbstractConverter.PsseEquipmentType.*;
/**
* @author Luma Zamarre��o {@literal <zamarrenolm at aia.es>}
* @author Jos�� Antonio Marqu��s {@literal <marquesja at aia.es>}
*/
class DanglingLineConverter extends AbstractConverter {
DanglingLineConverter(Network network) {
super(network);
}
static void create(Network network, PssePowerFlowModel psseModel, ContextExport contextExport, PsseExporter.PerUnitContext perUnitContext) {
List<DanglingLine> unPairedDanglingLines = network.getDanglingLineStream().filter(danglingLine -> !danglingLine.isPaired()).toList();
if (!unPairedDanglingLines.isEmpty()) {
createUnpairedDanglingLines(unPairedDanglingLines, psseModel, contextExport, perUnitContext);
}
}
private static void createUnpairedDanglingLines(List<DanglingLine> unPairedDanglingLines, PssePowerFlowModel psseModel, ContextExport contextExport, PsseExporter.PerUnitContext perUnitContext) {
unPairedDanglingLines.forEach(danglingLine -> {
PsseBus psseBus = createDanglingLineBus(danglingLine, contextExport);
psseModel.addBuses(Collections.singletonList(psseBus));
psseModel.addNonTransformerBranches(Collections.singletonList(createDanglingLineBranch(danglingLine, psseBus.getI(), contextExport, perUnitContext)));
createDanglingLineLoad(danglingLine, psseBus.getI(), contextExport).ifPresent(psseLoad -> psseModel.addLoads(Collections.singletonList(psseLoad)));
createDanglingLineGenerator(danglingLine, psseBus.getI(), contextExport, perUnitContext).ifPresent(psseGenerator -> psseModel.addGenerators(Collections.singletonList(psseGenerator)));
});
psseModel.replaceAllBuses(psseModel.getBuses().stream().sorted(Comparator.comparingInt(PsseBus::getI)).toList());
psseModel.replaceAllNonTransformerBranches(psseModel.getNonTransformerBranches().stream().sorted(Comparator.comparingInt(PsseNonTransformerBranch::getI).thenComparingInt(PsseNonTransformerBranch::getJ).thenComparing(PsseNonTransformerBranch::getCkt)).toList());
psseModel.replaceAllLoads(psseModel.getLoads().stream().sorted(Comparator.comparingInt(PsseLoad::getI).thenComparing(PsseLoad::getId)).toList());
psseModel.replaceAllGenerators(psseModel.getGenerators().stream().sorted(Comparator.comparingInt(PsseGenerator::getI).thenComparing(PsseGenerator::getId)).toList());
}
// Boundary bus will always be exported as busBranch
private static PsseBus createDanglingLineBus(DanglingLine danglingLine, ContextExport contextExport) {
Bus networkBusView = getTerminalConnectableBusView(danglingLine.getTerminal());
PsseBus psseBus = createDefaultBus();
psseBus.setI(contextExport.getFullExport().getBusI(danglingLine).orElseThrow());
psseBus.setName(fixBusName(danglingLine.getNameOrId()));
psseBus.setBaskv(danglingLine.getTerminal().getVoltageLevel().getNominalV());
psseBus.setIde(findType(danglingLine, contextExport));
psseBus.setVm(getVm(new DanglingLineData(danglingLine).getBoundaryBusU()));
psseBus.setVa(getVa(Math.toDegrees(new DanglingLineData(danglingLine).getBoundaryBusTheta())));
psseBus.setNvhi(getHighVm(networkBusView));
psseBus.setNvlo(getLowVm(networkBusView));
psseBus.setEvhi(getHighVm(networkBusView));
psseBus.setEvlo(getLowVm(networkBusView));
return psseBus;
}
private static int findType(DanglingLine danglingLine, ContextExport contextExport) {
if (getStatus(danglingLine.getTerminal(), contextExport) == 1) {
return isVoltageRegulationOn(danglingLine) ? 2 : 1;
} else {
return 4;
}
}
private static PsseNonTransformerBranch createDanglingLineBranch(DanglingLine danglingLine, int busJ, ContextExport contextExport, PsseExporter.PerUnitContext perUnitContext) {
PsseNonTransformerBranch psseLine = createDefaultNonTransformerBranch();
int busI = getTerminalBusI(danglingLine.getTerminal(), contextExport);
double vNom1 = danglingLine.getTerminal().getVoltageLevel().getNominalV();
Complex transmissionAdmittance = new Complex(danglingLine.getR(), danglingLine.getX()).reciprocal();
psseLine.setI(busI);
psseLine.setJ(busJ);
psseLine.setCkt(contextExport.getFullExport().getEquipmentCkt(danglingLine.getId(), PSSE_BRANCH.getTextCode(), busI, busJ));
psseLine.setR(impedanceToPerUnitForLinesWithDifferentNominalVoltageAtEnds(danglingLine.getR(), vNom1, vNom1, perUnitContext.sBase()));
psseLine.setX(impedanceToPerUnitForLinesWithDifferentNominalVoltageAtEnds(danglingLine.getX(), vNom1, vNom1, perUnitContext.sBase()));
psseLine.setName(fixNonTransformerBranchName(danglingLine.getNameOrId()));
psseLine.setRates(createRates(danglingLine, vNom1));
psseLine.setGi(admittanceEnd1ToPerUnitForLinesWithDifferentNominalVoltageAtEnds(transmissionAdmittance.getReal(), danglingLine.getG(), vNom1, vNom1, perUnitContext.sBase()));
psseLine.setBi(admittanceEnd1ToPerUnitForLinesWithDifferentNominalVoltageAtEnds(transmissionAdmittance.getImaginary(), danglingLine.getB(), vNom1, vNom1, perUnitContext.sBase()));
psseLine.setSt(getStatus(danglingLine.getTerminal(), contextExport));
return psseLine;
}
private static PsseRates createRates(DanglingLine danglingLine, double vNominal1) {
PsseRates windingRates = createDefaultRates();
danglingLine.getApparentPowerLimits().ifPresent(apparentPowerLimits -> setSortedRatesToPsseRates(getSortedRates(apparentPowerLimits), windingRates));
if (danglingLine.getApparentPowerLimits().isEmpty()) {
danglingLine.getCurrentLimits().ifPresent(currentLimits -> setSortedRatesToPsseRates(getSortedRates(currentLimits, vNominal1), windingRates));
}
if (danglingLine.getApparentPowerLimits().isEmpty() && danglingLine.getCurrentLimits().isEmpty()) {
danglingLine.getActivePowerLimits().ifPresent(activePowerLimits -> setSortedRatesToPsseRates(getSortedRates(activePowerLimits), windingRates));
}
return windingRates;
}
private static Optional<PsseLoad> createDanglingLineLoad(DanglingLine danglingLine, int busJ, ContextExport contextExport) {
if (!isLoadDefined(danglingLine)) {
return Optional.empty();
} else {
PsseLoad psseLoad = createDefaultLoad();
psseLoad.setI(busJ);
psseLoad.setId(contextExport.getFullExport().getEquipmentCkt(danglingLine.getId(), PSSE_LOAD.getTextCode(), busJ));
psseLoad.setStatus(1); // always connected
psseLoad.setPl(danglingLine.getP0());
psseLoad.setQl(danglingLine.getQ0());
return Optional.of(psseLoad);
}
}
private static boolean isLoadDefined(DanglingLine danglingLine) {
return Double.isFinite(danglingLine.getP0()) && Double.isFinite(danglingLine.getQ0()) &&
(danglingLine.getP0() != 0.0 || danglingLine.getQ0() != 0.0);
}
private static Optional<PsseGenerator> createDanglingLineGenerator(DanglingLine danglingLine, int busJ, ContextExport contextExport, PsseExporter.PerUnitContext perUnitContext) {
if (isGeneratorDefined(danglingLine)) {
return Optional.of(createGenerator(danglingLine, danglingLine.getGeneration(), busJ, contextExport, perUnitContext));
} else {
return Optional.empty();
}
}
private static PsseGenerator createGenerator(DanglingLine danglingLine, DanglingLine.Generation generation, int busJ, ContextExport contextExport, PsseExporter.PerUnitContext perUnitContext) {
PsseGenerator psseGenerator = createDefaultGenerator();
psseGenerator.setI(busJ);
psseGenerator.setId(contextExport.getFullExport().getEquipmentCkt(danglingLine.getId(), PSSE_GENERATOR.getTextCode(), busJ));
psseGenerator.setPg(generation.getTargetP());
psseGenerator.setQg(generation.getTargetQ());
psseGenerator.setQt(Math.max(getMaxQ(generation), getMinQ(generation)));
psseGenerator.setQb(getMinQ(generation));
psseGenerator.setVs(getTargetV(danglingLine, generation));
psseGenerator.setIreg(busJ);
psseGenerator.setNreg(0);
psseGenerator.setMbase(perUnitContext.sBase());
psseGenerator.setStat(1);
psseGenerator.setPt(getMaxP(generation));
psseGenerator.setPb(getMinP(generation));
return psseGenerator;
}
private static boolean isGeneratorDefined(DanglingLine danglingLine) {
return danglingLine.getGeneration() != null;
}
private static boolean isVoltageRegulationOn(DanglingLine danglingLine) {
return danglingLine.getGeneration() != null && danglingLine.getGeneration().isVoltageRegulationOn();
}
private static double getMaxP(DanglingLine.Generation generation) {
return Double.isNaN(generation.getMaxP()) ? 9999.0 : generation.getMaxP();
}
private static double getMinP(DanglingLine.Generation generation) {
return Double.isNaN(generation.getMinP()) ? -9999.0 : generation.getMinP();
}
private static double getMaxQ(DanglingLine.Generation generation) {
return generation.getReactiveLimits() != null ? generation.getReactiveLimits().getMaxQ(generation.getTargetP()) : 9999.0;
}
private static double getMinQ(DanglingLine.Generation generation) {
return generation.getReactiveLimits() != null ? generation.getReactiveLimits().getMinQ(generation.getTargetP()) : -9999.0;
}
private static double getTargetV(DanglingLine danglingLine, DanglingLine.Generation generation) {
if (Double.isNaN(generation.getTargetV()) || generation.getTargetV() <= 0.0) {
return 1.0;
} else {
return generation.getTargetV() / danglingLine.getTerminal().getVoltageLevel().getNominalV();
}
}
}