SubNetworkPredicate.java
/**
* Copyright (c) 2020, 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.reducer;
import com.powsybl.iidm.network.*;
import java.util.*;
/**
* A network reducer predicate that allow reduction based on a center voltage level and all other voltage level neighbors
* within a specified depth.
*
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
*/
public class SubNetworkPredicate implements NetworkPredicate {
private final IdentifierNetworkPredicate delegate;
public SubNetworkPredicate(VoltageLevel vl, int maxDepth) {
delegate = init(vl, maxDepth);
}
private static IdentifierNetworkPredicate init(VoltageLevel rootVl, int maxDepth) {
Objects.requireNonNull(rootVl);
if (maxDepth < 0) {
throw new IllegalArgumentException("Invalid max depth value: " + maxDepth);
}
Set<String> traversedVoltageLevelIds = new HashSet<>();
traversedVoltageLevelIds.add(rootVl.getId());
var currentDepth = Set.of(rootVl);
// BFS traversal of voltage levels
for (int i = 1; i <= maxDepth; i++) {
Set<VoltageLevel> nextDepth = i < maxDepth ? new LinkedHashSet<>() : null; // No need to calculate the last nextDepth set
for (VoltageLevel voltageLevel : currentDepth) {
findNextDepthVoltageLevels(voltageLevel, traversedVoltageLevelIds, nextDepth);
}
currentDepth = nextDepth;
if (nextDepth != null && nextDepth.isEmpty()) {
break;
}
}
return new IdentifierNetworkPredicate(traversedVoltageLevelIds);
}
private static void findNextDepthVoltageLevels(VoltageLevel vl, Set<String> traversedVoltageLevelIds, Set<VoltageLevel> nextDepth) {
vl.getLineStream().forEach(l -> visitBranch(l, traversedVoltageLevelIds, vl, nextDepth));
vl.getDanglingLineStream().forEach(dl -> dl.getTieLine().ifPresent(tl -> visitBranch(tl, traversedVoltageLevelIds, vl, nextDepth)));
vl.getTwoWindingsTransformerStream().forEach(t -> visitBranch(t, traversedVoltageLevelIds, vl, nextDepth));
vl.getThreeWindingsTransformerStream().forEach(t -> visitConnectable(t, traversedVoltageLevelIds, vl, nextDepth));
vl.getLccConverterStationStream().forEach(t -> visitHvdc(t, traversedVoltageLevelIds, nextDepth));
vl.getVscConverterStationStream().forEach(t -> visitHvdc(t, traversedVoltageLevelIds, nextDepth));
}
private static void visitBranch(Branch<?> branch, Set<String> traversedVoltageLevelIds, VoltageLevel vl, Set<VoltageLevel> nextDepth) {
visitTerminals(List.of(branch.getTerminal1(), branch.getTerminal2()), traversedVoltageLevelIds, vl, nextDepth);
}
private static void visitConnectable(Connectable<?> connectable, Set<String> traversedVoltageLevelIds, VoltageLevel voltageLevel, Set<VoltageLevel> nextDepth) {
visitTerminals(connectable.getTerminals(), traversedVoltageLevelIds, voltageLevel, nextDepth);
}
private static void visitHvdc(HvdcConverterStation<?> hvdc, Set<String> traversedVoltageLevelIds, Set<VoltageLevel> nextDepth) {
hvdc.getOtherConverterStation().ifPresent(otherHvdcCc ->
addVoltageLevel(otherHvdcCc.getTerminal().getVoltageLevel(), traversedVoltageLevelIds, nextDepth));
}
private static void visitTerminals(List<? extends Terminal> terminals, Set<String> traversedVoltageLevelIds, VoltageLevel voltageLevel, Set<VoltageLevel> nextDepth) {
terminals.stream().map(Terminal::getVoltageLevel)
.filter(vl -> !vl.getId().equals(voltageLevel.getId()))
.forEach(vl -> addVoltageLevel(vl, traversedVoltageLevelIds, nextDepth));
}
private static void addVoltageLevel(VoltageLevel vl, Set<String> traversedVoltageLevelIds, Set<VoltageLevel> nextDepth) {
if (traversedVoltageLevelIds.add(vl.getId()) && nextDepth != null) {
nextDepth.add(vl);
}
}
@Override
public boolean test(Substation substation) {
return delegate.test(substation);
}
@Override
public boolean test(VoltageLevel voltageLevel) {
return delegate.test(voltageLevel);
}
}