NetworkResultsUpdater.java
/**
* Copyright (c) 2023, 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.dynawo.commons;
import com.google.common.collect.Iterables;
import com.powsybl.commons.PowsyblException;
import com.powsybl.dynawo.commons.loadmerge.LoadPowersSigns;
import com.powsybl.dynawo.commons.loadmerge.LoadsMerger;
import com.powsybl.iidm.network.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author Guillem Jan�� Guasch {@literal <janeg at aia.es>}
*/
public final class NetworkResultsUpdater {
private static final Logger LOG = LoggerFactory.getLogger(NetworkResultsUpdater.class);
private NetworkResultsUpdater() {
}
public static void update(Network targetNetwork, Network sourceNetwork, boolean mergeLoads) {
updateLoads(targetNetwork, sourceNetwork, mergeLoads);
for (Line lineSource : sourceNetwork.getLines()) {
update(targetNetwork.getLine(lineSource.getId()).getTerminal1(), lineSource.getTerminal1());
update(targetNetwork.getLine(lineSource.getId()).getTerminal2(), lineSource.getTerminal2());
}
for (DanglingLine sourceDangling : sourceNetwork.getDanglingLines()) {
update(targetNetwork.getDanglingLine(sourceDangling.getId()).getTerminal(), sourceDangling.getTerminal());
}
updateHvdcLines(targetNetwork, sourceNetwork.getHvdcLines());
updateTwoWindingsTransformers(targetNetwork, sourceNetwork.getTwoWindingsTransformers());
updateThreeWindingsTransformers(targetNetwork, sourceNetwork.getThreeWindingsTransformers());
for (Generator sourceGenerator : sourceNetwork.getGenerators()) {
update(targetNetwork.getGenerator(sourceGenerator.getId()).getTerminal(), sourceGenerator.getTerminal());
}
for (ShuntCompensator sourceShuntCompensator : sourceNetwork.getShuntCompensators()) {
targetNetwork.getShuntCompensator(sourceShuntCompensator.getId()).setSectionCount(sourceShuntCompensator.getSectionCount());
update(targetNetwork.getShuntCompensator(sourceShuntCompensator.getId()).getTerminal(), sourceShuntCompensator.getTerminal());
}
for (StaticVarCompensator sourceStaticVarCompensator : sourceNetwork.getStaticVarCompensators()) {
update(targetNetwork.getStaticVarCompensator(sourceStaticVarCompensator.getId()).getTerminal(), sourceStaticVarCompensator.getTerminal());
targetNetwork.getStaticVarCompensator(sourceStaticVarCompensator.getId()).setRegulationMode(sourceStaticVarCompensator.getRegulationMode());
}
for (Switch sourceSwitch : sourceNetwork.getSwitches()) {
targetNetwork.getSwitch(sourceSwitch.getId()).setOpen(sourceSwitch.isOpen());
}
// We have to update the voltages AFTER all possible topology changes have been updated in the target Network,
// At this point, the buses in the BusBreakerView of target and source should match
// We choose to iterate over BusBreakerView buses instead of BusView buses because they are more stable:
// a use-case when we need to export a node/breaker network to bus/breaker to Dynawo exists,
// and reading the results from Dynawo-exported bus/breaker will end up with different ids at BusView level
Map<String, Bus> targetNetworkBusBreakerViewBusById = targetNetwork.getBusBreakerView().getBusStream()
.collect(Collectors.toMap(Identifiable::getId, Function.identity())); // it is needed to pre-index into a map as in network store n.getBusBreakerView().getBus(id) is slow
for (Bus sourceBus : sourceNetwork.getBusBreakerView().getBuses()) {
Bus targetBus = targetNetworkBusBreakerViewBusById.get(sourceBus.getId());
if (targetBus == null) {
LOG.error("Source bus {} not found in target network. Voltage not updated ({}, {})", sourceBus.getId(), sourceBus.getV(), sourceBus.getAngle());
} else {
targetBus.setV(sourceBus.getV());
targetBus.setAngle(sourceBus.getAngle());
}
}
}
private static void updateHvdcLines(Network targetNetwork, Iterable<HvdcLine> hvdcLines) {
for (HvdcLine sourceHvdcLine : hvdcLines) {
Terminal targetTerminal1 = targetNetwork.getHvdcLine(sourceHvdcLine.getId()).getConverterStation(TwoSides.ONE).getTerminal();
Terminal targetTerminal2 = targetNetwork.getHvdcLine(sourceHvdcLine.getId()).getConverterStation(TwoSides.TWO).getTerminal();
Terminal sourceTerminal1 = sourceHvdcLine.getConverterStation(TwoSides.ONE).getTerminal();
Terminal sourceTerminal2 = sourceHvdcLine.getConverterStation(TwoSides.TWO).getTerminal();
update(targetTerminal1, sourceTerminal1);
update(targetTerminal2, sourceTerminal2);
}
}
private static void updateTwoWindingsTransformers(Network targetNetwork, Iterable<TwoWindingsTransformer> twoWindingsTransformers) {
for (TwoWindingsTransformer sourceTwoWindingsTransformer : twoWindingsTransformers) {
Terminal targetTerminal1 = targetNetwork.getTwoWindingsTransformer(sourceTwoWindingsTransformer.getId()).getTerminal1();
Terminal targetTerminal2 = targetNetwork.getTwoWindingsTransformer(sourceTwoWindingsTransformer.getId()).getTerminal2();
Terminal sourceTerminal1 = sourceTwoWindingsTransformer.getTerminal1();
Terminal sourceTerminal2 = sourceTwoWindingsTransformer.getTerminal2();
update(targetTerminal1, sourceTerminal1);
update(targetTerminal2, sourceTerminal2);
PhaseTapChanger sourcePhaseTapChanger = sourceTwoWindingsTransformer.getPhaseTapChanger();
PhaseTapChanger targetPhaseTapChanger = targetNetwork.getTwoWindingsTransformer(sourceTwoWindingsTransformer.getId()).getPhaseTapChanger();
if (targetPhaseTapChanger != null) {
targetPhaseTapChanger.setTapPosition(sourcePhaseTapChanger.getTapPosition());
}
RatioTapChanger sourceRatioTapChanger = sourceTwoWindingsTransformer.getRatioTapChanger();
RatioTapChanger targetRatioTapChanger = targetNetwork.getTwoWindingsTransformer(sourceTwoWindingsTransformer.getId()).getRatioTapChanger();
if (targetRatioTapChanger != null) {
targetRatioTapChanger.setTapPosition(sourceRatioTapChanger.getTapPosition());
}
}
}
private static void updateThreeWindingsTransformers(Network targetNetwork, Iterable<ThreeWindingsTransformer> threeWindingsTransformers) {
for (ThreeWindingsTransformer sourceThreeWindingsTransformer : threeWindingsTransformers) {
ThreeWindingsTransformer targetThreeWindingsTransformer = targetNetwork.getThreeWindingsTransformer(sourceThreeWindingsTransformer.getId());
update(targetThreeWindingsTransformer.getLeg1(), sourceThreeWindingsTransformer.getLeg1());
update(targetThreeWindingsTransformer.getLeg2(), sourceThreeWindingsTransformer.getLeg2());
update(targetThreeWindingsTransformer.getLeg3(), sourceThreeWindingsTransformer.getLeg3());
}
}
private static void update(ThreeWindingsTransformer.Leg target, ThreeWindingsTransformer.Leg source) {
update(target.getTerminal(), source.getTerminal());
PhaseTapChanger sourcePhaseTapChanger = source.getPhaseTapChanger();
PhaseTapChanger targetPhaseTapChanger = target.getPhaseTapChanger();
if (targetPhaseTapChanger != null) {
targetPhaseTapChanger.setTapPosition(sourcePhaseTapChanger.getTapPosition());
}
RatioTapChanger sourceRatioTapChanger = source.getRatioTapChanger();
RatioTapChanger targetRatioTapChanger = target.getRatioTapChanger();
if (targetRatioTapChanger != null) {
targetRatioTapChanger.setTapPosition(sourceRatioTapChanger.getTapPosition());
}
}
private static void update(Terminal target, Terminal source) {
target.setP(source.getP());
target.setQ(source.getQ());
if (source.isConnected()) {
target.connect();
} else {
target.disconnect();
}
}
private static void update(Terminal target, Terminal mergedSource, double targetGroupP, double targetGroupQ) {
double pRatio = target.getP() / targetGroupP;
double qRatio = target.getQ() / targetGroupQ;
target.setP(mergedSource.getP() * pRatio);
target.setQ(mergedSource.getQ() * qRatio);
if (mergedSource.isConnected()) {
target.connect();
} else {
target.disconnect();
}
}
private static void updateLoads(Network targetNetwork, Network sourceNetwork, boolean mergeLoads) {
if (!mergeLoads) {
for (Load sourceLoad : sourceNetwork.getLoads()) {
update(targetNetwork.getLoad(sourceLoad.getId()).getTerminal(), sourceLoad.getTerminal());
}
} else {
Map<String, Bus> sourceBusesById = sourceNetwork.getBusBreakerView().getBusStream().collect(Collectors.toMap(Identifiable::getId, Function.identity()));
for (Bus busTarget : targetNetwork.getBusBreakerView().getBuses()) {
updateLoads(busTarget, sourceBusesById.get(busTarget.getId()));
}
}
}
private static void updateLoads(Bus busTarget, Bus busSource) {
Iterable<Load> loadsTarget = busTarget.getLoads();
int nbLoads = Iterables.size(loadsTarget);
if (nbLoads == 0) {
return;
}
Map<LoadPowersSigns, Terminal> mergedLoadsTerminal = busSource.getLoadStream()
.collect(Collectors.toMap(LoadsMerger::getLoadPowersSigns, Load::getTerminal));
LoadsMerger.getLoadPowersSignsGrouping(busTarget).forEach((loadPowersSigns, loadsGroup) -> {
Terminal mergedLoadTerminal = Optional.ofNullable(mergedLoadsTerminal.get(loadPowersSigns))
.orElseThrow(() -> new PowsyblException("Missing merged load in bus " + busTarget.getId()));
if (loadsGroup.size() == 1) {
update(loadsGroup.get(0).getTerminal(), mergedLoadTerminal);
} else {
double groupP = loadsGroup.stream().map(Load::getTerminal).mapToDouble(Terminal::getP).sum();
double groupQ = loadsGroup.stream().map(Load::getTerminal).mapToDouble(Terminal::getQ).sum();
loadsGroup.forEach(load -> update(load.getTerminal(), mergedLoadTerminal, groupP, groupQ));
}
});
}
}