CountryArea.java
/*
* Copyright (c) 2019, 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/.
*/
package com.powsybl.balances_adjustment.util;
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.util.TieLineUtil;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author Ameni Walha {@literal <ameni.walha at rte-france.com>}
* @author Sebastien Murgey {@literal <sebastien.murgey at rte-france.com>}
* @author Mathieu Bague {@literal <mathieu.bague at rte-france.com>}
*/
public class CountryArea implements NetworkArea {
private final List<Country> countries = new ArrayList<>();
// We should consider all dangling lines now, either paired or unpaired.
// The computation is more clean because we have the real value at boundary
// for a tie line now.
private final List<DanglingLine> danglingLineBordersCache;
private final List<Line> lineBordersCache;
private final List<HvdcLine> hvdcLineBordersCache;
private final Set<Bus> busesCache;
public CountryArea(Network network, List<Country> countries) {
this.countries.addAll(countries);
danglingLineBordersCache = network.getDanglingLineStream()
.filter(this::isAreaBorder)
.toList();
lineBordersCache = network.getLineStream()
.filter(this::isAreaBorder)
.toList();
hvdcLineBordersCache = network.getHvdcLineStream()
.filter(this::isAreaBorder)
.toList();
busesCache = network.getBusView().getBusStream()
.filter(bus -> bus.getVoltageLevel().getSubstation().flatMap(Substation::getCountry).map(countries::contains).orElse(false))
.collect(Collectors.toSet());
}
public List<Country> getCountries() {
return countries;
}
@Override
public double getNetPosition() {
return danglingLineBordersCache.parallelStream().mapToDouble(this::getLeavingFlow).sum()
+ lineBordersCache.parallelStream().mapToDouble(this::getLeavingFlow).sum()
+ hvdcLineBordersCache.parallelStream().mapToDouble(this::getLeavingFlow).sum();
}
@Override
public Collection<Bus> getContainedBusViewBuses() {
return Collections.unmodifiableCollection(busesCache);
}
public double getLeavingFlowToCountry(CountryArea otherCountryArea) {
otherCountryArea.getCountries().forEach(country -> {
if (countries.contains(country)) {
throw new PowsyblException("The leaving flow to the country area cannot be computed. " +
"The country " + country.getName() + " is contained in both control areas.");
}
});
double sum = 0;
for (DanglingLine danglingLine : danglingLineBordersCache) {
if (isOtherSideInArea(danglingLine, otherCountryArea)) {
sum += getLeavingFlow(danglingLine);
}
}
for (Line line : lineBordersCache) {
if (otherCountryArea.isAreaBorder(line)) {
sum += getLeavingFlow(line);
}
}
for (HvdcLine line : hvdcLineBordersCache) {
if (otherCountryArea.isAreaBorder(line)) {
sum += getLeavingFlow(line);
}
}
return sum;
}
private boolean isOtherSideInArea(DanglingLine danglingLine, CountryArea countryArea) {
return TieLineUtil.getPairedDanglingLine(danglingLine).filter(countryArea::isAreaBorder).isPresent();
}
private boolean isAreaBorder(DanglingLine danglingLine) {
Country country = danglingLine.getTerminal().getVoltageLevel().getSubstation().map(Substation::getNullableCountry).orElse(null);
return countries.contains(country);
}
private boolean isAreaBorder(Line line) {
Country countrySide1 = line.getTerminal1().getVoltageLevel().getSubstation().map(Substation::getNullableCountry).orElse(null);
Country countrySide2 = line.getTerminal2().getVoltageLevel().getSubstation().map(Substation::getNullableCountry).orElse(null);
if (countrySide1 == null || countrySide2 == null) {
return false;
}
return countries.contains(countrySide1) && !countries.contains(countrySide2) ||
!countries.contains(countrySide1) && countries.contains(countrySide2);
}
private boolean isAreaBorder(HvdcLine hvdcLine) {
Country countrySide1 = hvdcLine.getConverterStation1().getTerminal().getVoltageLevel().getSubstation().map(Substation::getNullableCountry).orElse(null);
Country countrySide2 = hvdcLine.getConverterStation2().getTerminal().getVoltageLevel().getSubstation().map(Substation::getNullableCountry).orElse(null);
if (countrySide1 == null || countrySide2 == null) {
return false;
}
return countries.contains(countrySide1) && !countries.contains(countrySide2) ||
!countries.contains(countrySide1) && countries.contains(countrySide2);
}
private double getLeavingFlow(DanglingLine danglingLine) {
return danglingLine.getTerminal().isConnected() ? zeroIfNan(-danglingLine.getBoundary().getP()) : 0;
}
private double getLeavingFlow(Line line) {
double flowSide1 = line.getTerminal1().isConnected() ? zeroIfNan(line.getTerminal1().getP()) : 0;
double flowSide2 = line.getTerminal2().isConnected() ? zeroIfNan(line.getTerminal2().getP()) : 0;
double directFlow = (flowSide1 - flowSide2) / 2;
return countries.contains(line.getTerminal1().getVoltageLevel().getSubstation().map(Substation::getNullableCountry).orElse(null)) ? directFlow : -directFlow;
}
private double getLeavingFlow(HvdcLine hvdcLine) {
double flowSide1 = hvdcLine.getConverterStation1().getTerminal().isConnected() ? zeroIfNan(hvdcLine.getConverterStation1().getTerminal().getP()) : 0;
double flowSide2 = hvdcLine.getConverterStation2().getTerminal().isConnected() ? zeroIfNan(hvdcLine.getConverterStation2().getTerminal().getP()) : 0;
double directFlow = (flowSide1 - flowSide2) / 2;
return countries.contains(hvdcLine.getConverterStation1().getTerminal().getVoltageLevel().getSubstation().map(Substation::getNullableCountry).orElse(null)) ? directFlow : -directFlow;
}
private static double zeroIfNan(double aPossiblyNanValue) {
return Double.isNaN(aPossiblyNanValue) ? 0 : aPossiblyNanValue;
}
}