AbstractLimitReductionsApplier.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.security.limitreduction;
import com.powsybl.contingency.ContingencyContext;
import com.powsybl.iidm.criteria.NetworkElementCriterion;
import com.powsybl.iidm.criteria.NetworkElementVisitor;
import com.powsybl.iidm.criteria.duration.AbstractTemporaryDurationCriterion;
import com.powsybl.iidm.criteria.duration.LimitDurationCriterion;
import com.powsybl.iidm.criteria.translation.NetworkElement;
import com.powsybl.iidm.network.LimitType;
import com.powsybl.iidm.network.ThreeSides;
import com.powsybl.iidm.network.limitmodification.AbstractLimitsComputerWithCache;
import com.powsybl.iidm.network.limitmodification.LimitsComputer;
import com.powsybl.iidm.network.limitmodification.result.LimitsContainer;
import com.powsybl.iidm.network.limitmodification.result.IdenticalLimitsContainer;
import com.powsybl.security.limitreduction.computation.AbstractLimitsReducer;
import com.powsybl.security.limitreduction.computation.AbstractLimitsReducerCreator;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import static com.powsybl.contingency.ContingencyContextType.*;
/**
* Abstract class responsible for computing reduced limits using a list of {@link LimitReduction}.
*
* @author Sophie Frasnedo {@literal <sophie.frasnedo at rte-france.com>}
* @author Olivier Perrin {@literal <olivier.perrin at rte-france.com>}
*/
public abstract class AbstractLimitReductionsApplier<P, L> extends AbstractLimitsComputerWithCache<P, L> {
private final List<LimitReduction> limitReductionList;
private List<LimitReduction> reductionsForThisContingency = Collections.emptyList();
/**
* Create a new {@link AbstractLimitReductionsApplier} using a list of reductions.
* @param limitReductionList the list of the reductions to use when computing reduced limits.
*/
protected AbstractLimitReductionsApplier(List<LimitReduction> limitReductionList) {
super();
this.limitReductionList = limitReductionList;
computeReductionsForThisContingency(null);
}
@Override
protected Optional<LimitsContainer<L>> computeUncachedLimits(P processable, LimitType limitType, ThreeSides side, boolean monitoringOnly) {
OriginalLimitsGetter<P, L> originalLimitsGetter = Objects.requireNonNull(getOriginalLimitsGetter());
Optional<L> originalLimits = originalLimitsGetter.getLimits(processable, limitType, side);
if (reductionsForThisContingency.isEmpty() || originalLimits.isEmpty()) {
// No reductions to apply or no limits on which to apply them
return originalLimits.map(IdenticalLimitsContainer::new);
}
AbstractLimitsReducerCreator<L, AbstractLimitsReducer<L>> limitsReducerCreator = Objects.requireNonNull(getLimitsReducerCreator());
NetworkElement networkElement = Objects.requireNonNull(asNetworkElement(processable));
AbstractLimitsReducer<L> limitsReducer = limitsReducerCreator.create(networkElement.getId(), originalLimits.get());
updateLimitReducer(limitsReducer, networkElement, limitType, side, monitoringOnly);
LimitsContainer<L> limitsContainer = limitsReducer.getLimits();
// Cache the value to avoid recomputing it
putInCache(processable, limitType, side, monitoringOnly, limitsContainer);
return Optional.of(limitsContainer);
}
/**
* Return an {@link OriginalLimitsGetter} allowing to retrieve
* {@link L} from a network element of type {@link P}.
* @return an original limits getter
*/
protected abstract OriginalLimitsGetter<P, L> getOriginalLimitsGetter();
/**
* Return the {@link AbstractLimitsReducer} creator, which will be used to create an object of type {@link L} containing the modified limits.
* @return the creator for {@link AbstractLimitsReducer}
*/
protected abstract AbstractLimitsReducerCreator<L, AbstractLimitsReducer<L>> getLimitsReducerCreator();
/**
* <p>Return a {@link NetworkElement} representation of <code>processable</code>
* (itself if it already is a {@link NetworkElement} or an adapter).</p>
* @param processable the object which limits should be computed.
* @return a {@link NetworkElement} representation of <code>processable</code>
*/
protected abstract NetworkElement asNetworkElement(P processable);
private void updateLimitReducer(AbstractLimitsReducer<?> limitsReducer, NetworkElement networkElement,
LimitType limitType, ThreeSides side, boolean monitoringOnly) {
for (LimitReduction limitReduction : reductionsForThisContingency) {
if (limitReduction.getLimitType() == limitType
&& limitReduction.isMonitoringOnly() == monitoringOnly
&& isNetworkElementAffectedByLimitReduction(networkElement, side, limitReduction)) {
setLimitReductionsToLimitReducer(limitsReducer, limitReduction);
}
}
}
/**
* <p>Change the contingency for which the reduced limits must be computed.</p>
* @param contingencyId the ID of the new contingency, or <code>null</code> if you study the pre-contingency state.
*/
public void setWorkingContingency(String contingencyId) {
var reductionsForPreviousContingency = reductionsForThisContingency;
computeReductionsForThisContingency(contingencyId);
if (!reductionsForThisContingency.equals(reductionsForPreviousContingency)) {
// The limit reductions are not the same as for the previous contingencyId, we clear the cache.
clearCache();
}
}
private void computeReductionsForThisContingency(String contingencyId) {
reductionsForThisContingency = limitReductionList.stream()
.filter(l -> isContingencyInContingencyContext(l.getContingencyContext(), contingencyId))
.toList();
}
private void setLimitReductionsToLimitReducer(AbstractLimitsReducer<?> limitsReducer, LimitReduction limitReduction) {
if (isPermanentLimitAffectedByLimitReduction(limitReduction)) {
limitsReducer.setPermanentLimitReduction(limitReduction.getValue());
}
limitsReducer.getTemporaryLimitsAcceptableDurationStream()
.filter(acceptableDuration -> isTemporaryLimitAffectedByLimitReduction(acceptableDuration, limitReduction))
.forEach(acceptableDuration -> limitsReducer.setTemporaryLimitReduction(acceptableDuration,
limitReduction.getValue()));
}
protected static boolean isContingencyInContingencyContext(ContingencyContext contingencyContext, String contingencyId) {
return contingencyContext == null
|| contingencyContext.getContextType() == ALL
|| contingencyContext.getContextType() == NONE && contingencyId == null
|| contingencyContext.getContextType() == ONLY_CONTINGENCIES && contingencyId != null
|| contingencyContext.getContextType() == SPECIFIC && contingencyContext.getContingencyId().equals(contingencyId);
}
protected static boolean isNetworkElementAffectedByLimitReduction(NetworkElement networkElement, ThreeSides side, LimitReduction limitReduction) {
NetworkElementVisitor networkElementVisitor = new NetworkElementVisitor(networkElement, side);
List<NetworkElementCriterion> networkElementCriteria = limitReduction.getNetworkElementCriteria();
return networkElementCriteria.isEmpty()
|| networkElementCriteria.stream().anyMatch(networkElementCriterion -> networkElementCriterion.accept(networkElementVisitor));
}
protected static boolean isPermanentLimitAffectedByLimitReduction(LimitReduction limitReduction) {
return limitReduction.getDurationCriteria().isEmpty()
|| limitReduction.getDurationCriteria().stream()
.anyMatch(c -> c.getType().equals(LimitDurationCriterion.LimitDurationType.PERMANENT));
}
protected static boolean isTemporaryLimitAffectedByLimitReduction(int temporaryLimitAcceptableDuration, LimitReduction limitReduction) {
return limitReduction.getDurationCriteria().isEmpty()
|| limitReduction.getDurationCriteria().stream()
.filter(limitDurationCriterion -> limitDurationCriterion.getType().equals(LimitDurationCriterion.LimitDurationType.TEMPORARY))
.map(AbstractTemporaryDurationCriterion.class::cast)
.anyMatch(c -> c.filter(temporaryLimitAcceptableDuration));
}
/**
* Interface for objects allowing to retrieve limits (of generic type {@link L}) from an object
* manageable by the {@link LimitsComputer} (of generic type {@link P}).
*
* @param <P> Generic type for the network element for which we want to retrieve the limits
* @param <L> Generic type for the limits to retrieve
*/
protected interface OriginalLimitsGetter<P, L> {
Optional<L> getLimits(P e, LimitType limitType, ThreeSides side);
}
}