SubnetworkImpl.java
/**
* Copyright (c) 2022, 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.iidm.network.impl;
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.iidm.network.*;
import com.powsybl.commons.ref.RefChain;
import com.powsybl.commons.ref.RefObj;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* @author Miora Vedelago {@literal <miora.ralambotiana at rte-france.com>}
*/
public class SubnetworkImpl extends AbstractNetwork {
private static final Logger LOGGER = LoggerFactory.getLogger(SubnetworkImpl.class);
/**
* Reference to the root network, hence the parent network in this implementation (only one level of subnetworks).
* This is used to easily update the root network in all equipments when detaching this subnetwork.
* <p>This {@link RefChain} should reference the {@code ref} attribute of the root network in order for the {@link #flatten()}
* method to work.</p>
*/
private final RefChain<NetworkImpl> rootNetworkRef;
/**
* Reference to current subnetwork. This is used to easily update the subnetwork reference in substations / voltage
* levels when detaching this subnetwork.
*/
private final RefChain<SubnetworkImpl> ref;
SubnetworkImpl(RefChain<NetworkImpl> rootNetworkRef, String id, String name, String sourceFormat) {
super(id, name, sourceFormat);
this.rootNetworkRef = Objects.requireNonNull(rootNetworkRef);
this.ref = new RefChain<>(new RefObj<>(this));
}
SubnetworkImpl(RefChain<NetworkImpl> rootNetworkRef, RefChain<SubnetworkImpl> subnetworkRef, String id, String name, String sourceFormat, ZonedDateTime caseDate) {
super(id, name, sourceFormat);
this.rootNetworkRef = Objects.requireNonNull(rootNetworkRef);
this.ref = Objects.requireNonNull(subnetworkRef);
this.ref.setRef(new RefObj<>(this));
setCaseDate(caseDate);
}
@Override
public RefChain<NetworkImpl> getRootNetworkRef() {
return rootNetworkRef;
}
protected RefChain<SubnetworkImpl> getRef() {
return ref;
}
@Override
public final Collection<Network> getSubnetworks() {
return Collections.emptyList();
}
@Override
public final Network getSubnetwork(String id) {
return null;
}
@Override
public NetworkImpl getNetwork() {
return rootNetworkRef.get();
}
@Override
public VariantManager getVariantManager() {
return getNetwork().getVariantManager();
}
@Override
public void allowReportNodeContextMultiThreadAccess(boolean allow) {
getNetwork().allowReportNodeContextMultiThreadAccess(allow);
}
@Override
public ReportNodeContext getReportNodeContext() {
return getNetwork().getReportNodeContext();
}
private boolean contains(Identifiable<?> identifiable) {
return identifiable == this ||
identifiable != null && identifiable.getParentNetwork() == this;
}
@Override
public Set<Country> getCountries() {
return getCountryStream().collect(Collectors.toSet());
}
@Override
public int getCountryCount() {
return (int) getCountryStream().count();
}
private Stream<Country> getCountryStream() {
return getNetwork().getSubstationStream()
.filter(this::contains)
.map(s -> s.getCountry().orElse(null))
.filter(Objects::nonNull)
.distinct();
}
@Override
public Iterable<String> getAreaTypes() {
return getAreaTypeStream().toList();
}
@Override
public Stream<String> getAreaTypeStream() {
return getAreaStream().map(Area::getAreaType).distinct();
}
@Override
public int getAreaTypeCount() {
return (int) getAreaTypeStream().count();
}
@Override
public AreaAdder newArea() {
return new AreaAdderImpl(rootNetworkRef, ref);
}
@Override
public Iterable<Area> getAreas() {
return getAreaStream().toList();
}
@Override
public Stream<Area> getAreaStream() {
return getNetwork().getAreaStream().filter(this::contains);
}
@Override
public Area getArea(String id) {
Area area = getNetwork().getArea(id);
return contains(area) ? area : null;
}
@Override
public int getAreaCount() {
return (int) getAreaStream().count();
}
@Override
public SubstationAdder newSubstation() {
return new SubstationAdderImpl(rootNetworkRef, ref);
}
@Override
public Iterable<Substation> getSubstations() {
return getSubstationStream().toList();
}
@Override
public Stream<Substation> getSubstationStream() {
return getNetwork().getSubstationStream().filter(this::contains);
}
@Override
public int getSubstationCount() {
return (int) getSubstationStream().count();
}
@Override
public Iterable<Substation> getSubstations(Country country, String tsoId, String... geographicalTags) {
return StreamSupport.stream(getNetwork().getSubstations(country, tsoId, geographicalTags).spliterator(), false)
.filter(this::contains)
.toList();
}
@Override
public Iterable<Substation> getSubstations(String country, String tsoId, String... geographicalTags) {
return StreamSupport.stream(getNetwork().getSubstations(country, tsoId, geographicalTags).spliterator(), false)
.filter(this::contains)
.toList();
}
@Override
public Substation getSubstation(String id) {
Substation s = getNetwork().getSubstation(id);
return contains(s) ? s : null;
}
@Override
public VoltageLevelAdder newVoltageLevel() {
return new VoltageLevelAdderImpl(rootNetworkRef, ref);
}
@Override
public Iterable<VoltageLevel> getVoltageLevels() {
return getVoltageLevelStream().toList();
}
@Override
public Stream<VoltageLevel> getVoltageLevelStream() {
return getNetwork().getVoltageLevelStream().filter(this::contains);
}
@Override
public int getVoltageLevelCount() {
return (int) getVoltageLevelStream().count();
}
@Override
public VoltageLevel getVoltageLevel(String id) {
VoltageLevel vl = getNetwork().getVoltageLevel(id);
return contains(vl) ? vl : null;
}
@Override
public LineAdder newLine() {
return getNetwork().newLine(id);
}
@Override
public LineAdder newLine(Line line) {
return getNetwork().newLine(id, line);
}
@Override
public Iterable<Line> getLines() {
return getLineStream().toList();
}
@Override
public Branch<?> getBranch(String branchId) {
Branch<?> b = getNetwork().getBranch(branchId);
return contains(b) ? b : null;
}
@Override
public Iterable<Branch> getBranches() {
return getBranchStream().toList();
}
@Override
public Stream<Branch> getBranchStream() {
return getNetwork().getBranchStream().filter(this::contains);
}
@Override
public int getBranchCount() {
return (int) getBranchStream().count();
}
@Override
public Stream<Line> getLineStream() {
return getNetwork().getLineStream().filter(this::contains);
}
@Override
public int getLineCount() {
return (int) getLineStream().count();
}
@Override
public Line getLine(String id) {
Line l = getNetwork().getLine(id);
return contains(l) ? l : null;
}
@Override
public VoltageAngleLimitAdder newVoltageAngleLimit() {
return getNetwork().newVoltageAngleLimit(id);
}
@Override
public Iterable<VoltageAngleLimit> getVoltageAngleLimits() {
return getVoltageAngleLimitsStream().toList();
}
@Override
public Stream<VoltageAngleLimit> getVoltageAngleLimitsStream() {
return getNetwork().getVoltageAngleLimitsStream()
.filter(val -> contains(val.getTerminalFrom().getVoltageLevel()) && contains(val.getTerminalTo().getVoltageLevel()));
}
@Override
public VoltageAngleLimit getVoltageAngleLimit(String id) {
VoltageAngleLimitImpl val = (VoltageAngleLimitImpl) getNetwork().getVoltageAngleLimit(id);
boolean valInSubnetwork = val != null
&& contains(val.getTerminalFrom().getVoltageLevel())
&& contains(val.getTerminalTo().getVoltageLevel());
return valInSubnetwork ? val : null;
}
@Override
public TieLineAdder newTieLine() {
return getNetwork().newTieLine(id);
}
@Override
public Iterable<TieLine> getTieLines() {
return getTieLineStream().toList();
}
@Override
public Stream<TieLine> getTieLineStream() {
return getNetwork().getTieLineStream().filter(this::contains);
}
@Override
public int getTieLineCount() {
return (int) getTieLineStream().count();
}
@Override
public TieLine getTieLine(String id) {
TieLine t = getNetwork().getTieLine(id);
return contains(t) ? t : null;
}
@Override
public Iterable<TwoWindingsTransformer> getTwoWindingsTransformers() {
return getTwoWindingsTransformerStream().toList();
}
@Override
public Stream<TwoWindingsTransformer> getTwoWindingsTransformerStream() {
return getNetwork().getTwoWindingsTransformerStream().filter(this::contains);
}
@Override
public int getTwoWindingsTransformerCount() {
return (int) getTwoWindingsTransformerStream().count();
}
@Override
public TwoWindingsTransformer getTwoWindingsTransformer(String id) {
TwoWindingsTransformer twt = getNetwork().getTwoWindingsTransformer(id);
return contains(twt) ? twt : null;
}
@Override
public Iterable<ThreeWindingsTransformer> getThreeWindingsTransformers() {
return getThreeWindingsTransformerStream().toList();
}
@Override
public Stream<ThreeWindingsTransformer> getThreeWindingsTransformerStream() {
return getNetwork().getThreeWindingsTransformerStream().filter(this::contains);
}
@Override
public int getThreeWindingsTransformerCount() {
return (int) getThreeWindingsTransformerStream().count();
}
@Override
public ThreeWindingsTransformer getThreeWindingsTransformer(String id) {
ThreeWindingsTransformer twt = getNetwork().getThreeWindingsTransformer(id);
return contains(twt) ? twt : null;
}
@Override
public Iterable<OverloadManagementSystem> getOverloadManagementSystems() {
return getOverloadManagementSystemStream().toList();
}
@Override
public Stream<OverloadManagementSystem> getOverloadManagementSystemStream() {
return getNetwork().getOverloadManagementSystemStream().filter(this::contains);
}
@Override
public int getOverloadManagementSystemCount() {
return (int) getOverloadManagementSystemStream().count();
}
@Override
public OverloadManagementSystem getOverloadManagementSystem(String id) {
OverloadManagementSystem oms = getNetwork().getOverloadManagementSystem(id);
return contains(oms) ? oms : null;
}
@Override
public Iterable<Generator> getGenerators() {
return getGeneratorStream().toList();
}
@Override
public Stream<Generator> getGeneratorStream() {
return getNetwork().getGeneratorStream().filter(this::contains);
}
@Override
public int getGeneratorCount() {
return (int) getGeneratorStream().count();
}
@Override
public Generator getGenerator(String id) {
Generator g = getNetwork().getGenerator(id);
return contains(g) ? g : null;
}
@Override
public Iterable<Battery> getBatteries() {
return getBatteryStream().toList();
}
@Override
public Stream<Battery> getBatteryStream() {
return getNetwork().getBatteryStream().filter(this::contains);
}
@Override
public int getBatteryCount() {
return (int) getBatteryStream().count();
}
@Override
public Battery getBattery(String id) {
Battery b = getNetwork().getBattery(id);
return contains(b) ? b : null;
}
@Override
public Iterable<Load> getLoads() {
return getLoadStream().toList();
}
@Override
public Stream<Load> getLoadStream() {
return getNetwork().getLoadStream().filter(this::contains);
}
@Override
public int getLoadCount() {
return (int) getLoadStream().count();
}
@Override
public Load getLoad(String id) {
Load l = getNetwork().getLoad(id);
return contains(l) ? l : null;
}
@Override
public Iterable<ShuntCompensator> getShuntCompensators() {
return getShuntCompensatorStream().toList();
}
@Override
public Stream<ShuntCompensator> getShuntCompensatorStream() {
return getNetwork().getShuntCompensatorStream().filter(this::contains);
}
@Override
public int getShuntCompensatorCount() {
return (int) getShuntCompensatorStream().count();
}
@Override
public ShuntCompensator getShuntCompensator(String id) {
ShuntCompensator s = getNetwork().getShuntCompensator(id);
return contains(s) ? s : null;
}
@Override
public Iterable<DanglingLine> getDanglingLines(DanglingLineFilter danglingLineFilter) {
return getDanglingLineStream(danglingLineFilter).toList();
}
@Override
public Stream<DanglingLine> getDanglingLineStream(DanglingLineFilter danglingLineFilter) {
return getNetwork().getDanglingLineStream().filter(this::contains).filter(danglingLineFilter.getPredicate());
}
@Override
public Iterable<DanglingLine> getDanglingLines() {
return getDanglingLines(DanglingLineFilter.ALL);
}
@Override
public Stream<DanglingLine> getDanglingLineStream() {
return getDanglingLineStream(DanglingLineFilter.ALL);
}
@Override
public int getDanglingLineCount() {
return (int) getDanglingLineStream().count();
}
@Override
public DanglingLine getDanglingLine(String id) {
DanglingLine dl = getNetwork().getDanglingLine(id);
return contains(dl) ? dl : null;
}
@Override
public Iterable<StaticVarCompensator> getStaticVarCompensators() {
return getStaticVarCompensatorStream().toList();
}
@Override
public Stream<StaticVarCompensator> getStaticVarCompensatorStream() {
return getNetwork().getStaticVarCompensatorStream().filter(this::contains);
}
@Override
public int getStaticVarCompensatorCount() {
return (int) getStaticVarCompensatorStream().count();
}
@Override
public StaticVarCompensator getStaticVarCompensator(String id) {
StaticVarCompensator s = getNetwork().getStaticVarCompensator(id);
return contains(s) ? s : null;
}
@Override
public Switch getSwitch(String id) {
Switch s = getNetwork().getSwitch(id);
return contains(s) ? s : null;
}
@Override
public Iterable<Switch> getSwitches() {
return getSwitchStream().toList();
}
@Override
public Stream<Switch> getSwitchStream() {
return getNetwork().getSwitchStream().filter(this::contains);
}
@Override
public int getSwitchCount() {
return (int) getSwitchStream().count();
}
@Override
public BusbarSection getBusbarSection(String id) {
BusbarSection b = getNetwork().getBusbarSection(id);
return contains(b) ? b : null;
}
@Override
public Iterable<BusbarSection> getBusbarSections() {
return getBusbarSectionStream().toList();
}
@Override
public Stream<BusbarSection> getBusbarSectionStream() {
return getNetwork().getBusbarSectionStream().filter(this::contains);
}
@Override
public int getBusbarSectionCount() {
return (int) getBusbarSectionStream().count();
}
@Override
public Iterable<HvdcConverterStation<?>> getHvdcConverterStations() {
return getHvdcConverterStationStream().toList();
}
@Override
public Stream<HvdcConverterStation<?>> getHvdcConverterStationStream() {
return getNetwork().getHvdcConverterStationStream().filter(this::contains);
}
@Override
public int getHvdcConverterStationCount() {
return (int) getHvdcConverterStationStream().count();
}
@Override
public HvdcConverterStation<?> getHvdcConverterStation(String id) {
HvdcConverterStation<?> s = getNetwork().getHvdcConverterStation(id);
return contains(s) ? s : null;
}
@Override
public Iterable<LccConverterStation> getLccConverterStations() {
return getLccConverterStationStream().toList();
}
@Override
public Stream<LccConverterStation> getLccConverterStationStream() {
return getNetwork().getLccConverterStationStream().filter(this::contains);
}
@Override
public int getLccConverterStationCount() {
return (int) getLccConverterStationStream().count();
}
@Override
public LccConverterStation getLccConverterStation(String id) {
LccConverterStation s = getNetwork().getLccConverterStation(id);
return contains(s) ? s : null;
}
@Override
public Iterable<VscConverterStation> getVscConverterStations() {
return getVscConverterStationStream().toList();
}
@Override
public Stream<VscConverterStation> getVscConverterStationStream() {
return getNetwork().getVscConverterStationStream().filter(this::contains);
}
@Override
public int getVscConverterStationCount() {
return (int) getVscConverterStationStream().count();
}
@Override
public VscConverterStation getVscConverterStation(String id) {
VscConverterStation s = getNetwork().getVscConverterStation(id);
return contains(s) ? s : null;
}
@Override
public Iterable<HvdcLine> getHvdcLines() {
return getHvdcLineStream().toList();
}
@Override
public Stream<HvdcLine> getHvdcLineStream() {
return getNetwork().getHvdcLineStream().filter(this::contains);
}
@Override
public int getHvdcLineCount() {
return (int) getHvdcLineStream().count();
}
@Override
public HvdcLine getHvdcLine(String id) {
HvdcLine l = getNetwork().getHvdcLine(id);
return contains(l) ? l : null;
}
@Override
public HvdcLine getHvdcLine(HvdcConverterStation converterStation) {
if (converterStation.getParentNetwork() == this) {
return getHvdcLineStream()
.filter(l -> l.getConverterStation1() == converterStation || l.getConverterStation2() == converterStation)
.findFirst()
.orElse(null);
}
return null;
}
@Override
public HvdcLineAdder newHvdcLine() {
return getNetwork().newHvdcLine(id);
}
@Override
public Ground getGround(String id) {
Ground s = getNetwork().getGround(id);
return contains(s) ? s : null;
}
@Override
public Iterable<Ground> getGrounds() {
return getGroundStream().toList();
}
@Override
public Stream<Ground> getGroundStream() {
return getNetwork().getGroundStream().filter(this::contains);
}
@Override
public int getGroundCount() {
return (int) getGroundStream().count();
}
@Override
public Identifiable<?> getIdentifiable(String id) {
Identifiable<?> i = getNetwork().getIdentifiable(id);
return contains(i) ? i : null;
}
@Override
public Collection<Identifiable<?>> getIdentifiables() {
return getNetwork().getIdentifiables().stream().filter(this::contains).toList();
}
@Override
public <C extends Connectable> Iterable<C> getConnectables(Class<C> clazz) {
return getConnectableStream(clazz).toList();
}
@Override
public <C extends Connectable> Stream<C> getConnectableStream(Class<C> clazz) {
return getNetwork().getConnectableStream(clazz).filter(this::contains);
}
@Override
public <C extends Connectable> int getConnectableCount(Class<C> clazz) {
return (int) getConnectableStream(clazz).count();
}
@Override
public Iterable<Connectable> getConnectables() {
return getConnectableStream().toList();
}
@Override
public Stream<Connectable> getConnectableStream() {
return getNetwork().getConnectableStream().filter(this::contains);
}
@Override
public Connectable<?> getConnectable(String id) {
Connectable<?> c = getNetwork().getConnectable(id);
return contains(c) ? c : null;
}
@Override
public int getConnectableCount() {
return (int) getConnectableStream().count();
}
class BusBreakerViewImpl extends AbstractNetwork.AbstractBusBreakerViewImpl {
@Override
public Bus getBus(String id) {
Bus b = getNetwork().getBusBreakerView().getBus(id);
return contains(b) ? b : null;
}
}
private final BusBreakerViewImpl busBreakerView = new BusBreakerViewImpl();
@Override
public BusBreakerView getBusBreakerView() {
return busBreakerView;
}
class BusViewImpl extends AbstractNetwork.AbstractBusViewImpl {
@Override
public Bus getBus(String id) {
Bus b = getNetwork().getBusView().getBus(id);
return contains(b) ? b : null;
}
@Override
public Collection<Component> getConnectedComponents() {
return getNetwork().getBusView().getConnectedComponents().stream()
.filter(c -> c.getBusStream().anyMatch(SubnetworkImpl.this::contains))
.map(c -> (Component) new Subcomponent(c, SubnetworkImpl.this))
.toList();
}
@Override
public Collection<Component> getSynchronousComponents() {
return getNetwork().getBusView().getSynchronousComponents().stream()
.filter(c -> c.getBusStream().anyMatch(SubnetworkImpl.this::contains))
.map(c -> (Component) new Subcomponent(c, SubnetworkImpl.this))
.toList();
}
}
private final BusViewImpl busView = new BusViewImpl();
@Override
public BusView getBusView() {
return busView;
}
/**
* {@inheritDoc}
* <p>This operation is not allowed on a subnetwork.</p>
* <p>This method throws an {@link UnsupportedOperationException}</p>
*/
@Override
public Network createSubnetwork(String subnetworkId, String name, String sourceFormat) {
throw new UnsupportedOperationException("Inner subnetworks are not yet supported");
}
@Override
public Network detach() {
Set<Identifiable<?>> boundaryElements = getBoundaryElements();
checkDetachable(boundaryElements, true);
long start = System.currentTimeMillis();
// Remove tie-lines
boundaryElements.stream()
.filter(DanglingLine.class::isInstance)
.map(DanglingLine.class::cast)
.map(DanglingLine::getTieLine)
.filter(Optional::isPresent)
.forEach(t -> t.get().remove(true));
// Create a new NetworkImpl and transfer the extensions to it
NetworkImpl detachedNetwork = new NetworkImpl(getId(), getNameOrId(), getSourceFormat());
transferExtensions(this, detachedNetwork);
transferProperties(this, detachedNetwork);
// Memorize the network identifiables/voltageAngleLimits before moving references (to use them later)
Collection<Identifiable<?>> identifiables = getIdentifiables();
Iterable<VoltageAngleLimit> vals = getVoltageAngleLimits();
// Move the substations and voltageLevels to the new network
ref.setRef(new RefObj<>(null));
// Remove the old subnetwork from the subnetworks list of the current parent network
NetworkImpl previousRootNetwork = rootNetworkRef.get();
previousRootNetwork.removeFromSubnetworks(getId());
// Change root network back reference
rootNetworkRef.setRef(detachedNetwork.getRef());
// Remove all the identifiers from the parent's index and add them to the detached network's index
for (Identifiable<?> i : identifiables) {
previousRootNetwork.getIndex().remove(i);
if (i != this) {
detachedNetwork.getIndex().checkAndAdd(i);
}
}
for (VoltageAngleLimit val : vals) {
previousRootNetwork.getVoltageAngleLimitsIndex().remove(val.getId());
detachedNetwork.getVoltageAngleLimitsIndex().put(val.getId(), val);
}
// We don't control that regulating terminals and phase/ratio regulation terminals are in the same subnetwork
// as their network elements (generators, PSTs, ...). It is unlikely that those terminals and their elements
// are in different subnetworks but nothing prevents it. For now, we ignore this case, but it may be necessary
// to handle it later. If so, note that there are 2 possible cases:
// - the element is in the subnetwork to detach and its regulating or phase/ratio regulation terminal is not
// - the terminal is in the subnetwork, but not its element (this is trickier)
LOGGER.info("Detaching of {} done in {} ms", id, System.currentTimeMillis() - start);
return detachedNetwork;
}
/**
* {@inheritDoc}
* <p>For now, only tie-lines can be split (HVDC lines may be supported later).</p>
*/
@Override
public boolean isDetachable() {
return checkDetachable(getBoundaryElements(), false);
}
private boolean checkDetachable(Set<Identifiable<?>> boundaryElements, boolean throwsException) {
if (getNetwork().getVariantManager().getVariantArraySize() != 1) {
if (throwsException) {
throw new PowsyblException("Detaching from multi-variants network is not supported");
}
return false;
}
if (boundaryElements.stream().anyMatch(Predicate.not(SubnetworkImpl::isSplittable))) {
if (throwsException) {
throw new PowsyblException("Un-splittable boundary elements prevent the subnetwork to be detached: "
+ boundaryElements.stream().filter(Predicate.not(SubnetworkImpl::isSplittable)).map(Identifiable::getId).collect(Collectors.joining(", ")));
}
return false;
}
if (getNetwork().getVoltageAngleLimitsStream().anyMatch(this::isBoundary)) {
if (throwsException) {
throw new PowsyblException("VoltageAngleLimits prevent the subnetwork to be detached: "
+ getNetwork().getVoltageAngleLimitsStream().filter(this::isBoundary).map(VoltageAngleLimit::getId).collect(Collectors.joining(", ")));
}
return false;
}
return true;
}
private static boolean isSplittable(Identifiable<?> identifiable) {
return identifiable.getType() == IdentifiableType.DANGLING_LINE;
}
@Override
public Set<Identifiable<?>> getBoundaryElements() {
// transformers cannot link different subnetworks for the moment.
Stream<Line> lines = getNetwork().getLineStream().filter(i -> i.getParentNetwork() == getNetwork());
Stream<DanglingLine> danglingLineStream = getDanglingLineStream();
Stream<HvdcLine> hvdcLineStream = getNetwork().getHvdcLineStream().filter(i -> i.getParentNetwork() == getNetwork());
return Stream.of(lines, danglingLineStream, hvdcLineStream)
.flatMap(Function.identity())
.map(o -> (Identifiable<?>) o)
.filter(this::isBoundaryElement)
.collect(Collectors.toSet());
}
@Override
public boolean isBoundaryElement(Identifiable<?> identifiable) {
return switch (identifiable.getType()) {
case LINE, TIE_LINE -> isBoundary((Branch<?>) identifiable);
case HVDC_LINE -> isBoundary((HvdcLine) identifiable);
case DANGLING_LINE -> isBoundary((DanglingLine) identifiable);
default -> false;
};
}
@Override
public void flatten() {
throw new UnsupportedOperationException("Subnetworks cannot be flattened.");
}
private boolean isBoundary(Branch<?> branch) {
return isBoundary(branch.getTerminal1(), branch.getTerminal2());
}
private boolean isBoundary(HvdcLine hvdcLine) {
return isBoundary(hvdcLine.getConverterStation1().getTerminal(),
hvdcLine.getConverterStation2().getTerminal());
}
private boolean isBoundary(DanglingLine danglingLine) {
return danglingLine.getTieLine()
.map(this::isBoundary)
.orElse(true);
}
private boolean isBoundary(VoltageAngleLimit val) {
return isBoundary(val.getTerminalFrom(), val.getTerminalTo());
}
private boolean isBoundary(Terminal terminal1, Terminal terminal2) {
boolean containsVoltageLevel1 = contains(terminal1.getVoltageLevel());
boolean containsVoltageLevel2 = contains(terminal2.getVoltageLevel());
return containsVoltageLevel1 && !containsVoltageLevel2 ||
!containsVoltageLevel1 && containsVoltageLevel2;
}
/**
* {@inheritDoc}
* <p>This method throws an {@link PowsyblException}.</p>
* <p>Motivation: The listeners apply to the whole network (root + subnetworks). Thus, in a network with several subnetworks,
* if calling <code>addListener</code> on a subnetwork registers the given listener on the root network, changes
* on another subnetwork will also be reported to the listener. This is counterintuitive and could lead to confusion.
* To avoid that, we don't allow to add or remove listeners from a subnetwork.</p>
*/
@Override
public void addListener(NetworkListener listener) {
throw new PowsyblException("Listeners are not managed at subnetwork level." +
" Add this listener to the parent network '" + getNetwork().getId() + "'");
}
/**
* {@inheritDoc}
* <p>This method throws an {@link PowsyblException}.</p>
* <p>Motivation: The listeners apply to the whole network (root + subnetworks). Thus, in a network with several subnetworks,
* if calling <code>addListener</code> on a subnetwork registers the given listener on the root network, changes
* on another subnetwork will also be reported to the listener. This is counterintuitive and could lead to confusion.
* To avoid that, we don't allow to add or remove listeners from a subnetwork.</p>
*/
@Override
public void removeListener(NetworkListener listener) {
throw new PowsyblException("Listeners are not managed at subnetwork level." +
" Remove this listener to the parent network '" + getNetwork().getId() + "'");
}
@Override
public ValidationLevel runValidationChecks() {
return getNetwork().runValidationChecks();
}
@Override
public ValidationLevel runValidationChecks(boolean throwsException) {
return getNetwork().runValidationChecks(throwsException);
}
@Override
public ValidationLevel runValidationChecks(boolean throwsException, ReportNode reportNode) {
return getNetwork().runValidationChecks(throwsException, reportNode);
}
@Override
public ValidationLevel getValidationLevel() {
return getNetwork().getValidationLevel();
}
@Override
public Network setMinimumAcceptableValidationLevel(ValidationLevel validationLevel) {
return getNetwork().setMinimumAcceptableValidationLevel(validationLevel);
}
@Override
public Stream<Identifiable<?>> getIdentifiableStream(IdentifiableType identifiableType) {
return getNetwork().getIdentifiableStream(identifiableType).filter(this::contains);
}
}