com.powsybl.metrix.mapping.TimeSeriesMapperChecker Maven / Gradle / Ivy
/*
* 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.metrix.mapping;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.extensions.HvdcOperatorActivePowerRange;
import com.powsybl.metrix.mapping.log.*;
import com.powsybl.metrix.mapping.timeseries.*;
import com.powsybl.timeseries.TimeSeriesIndex;
import java.util.*;
import static com.powsybl.metrix.mapping.TimeSeriesMapper.addActivePowerRangeExtension;
/**
* @author Paul Bui-Quang {@literal }
*/
public class TimeSeriesMapperChecker extends MultipleTimeSeriesMapperObserver {
private static final String UNHANDLED_SCALING_OPERATION_ERROR = "Unhandled scaling operation %s";
private int version;
private TimeSeriesIndex index;
private final TimeSeriesMappingLogger timeSeriesMappingLogger;
private final float toleranceThreshold;
private final Map hvdcLineToActivePowerRange = new HashMap<>();
private final Map, MappedPower> identifiableToConstantMappedPowers = new LinkedHashMap<>();
private final Map, MappedPower> identifiableToMappedPower = new LinkedHashMap<>();
private final Map targetPTimeSeriesToEquipments = new HashMap<>();
private final Map setpointTimeSeriesToEquipments = new HashMap<>();
private final Map> targetPTimeSeriesToScalingDownPowerChangeSynthesis = new HashMap<>();
private final Map> setpointTimeSeriesToScalingDownPowerChangeSynthesis = new HashMap<>();
private final Map> targetPTimeSeriesToScalingDownLimitViolationSynthesis = new HashMap<>();
private final Map> setpointTimeSeriesToScalingDownLimitViolationSynthesis = new HashMap<>();
private final Map, LimitChange> generatorToMaxValues = new HashMap<>();
private final Map, LimitChange> generatorToMinValues = new HashMap<>();
private final Map, LimitChange> hvdcLineToMaxValues = new HashMap<>();
private final Map, LimitChange> hvdcLineToMinValues = new HashMap<>();
private final Map, LimitChange> hvdcLineToCS1toCS2Values = new HashMap<>();
private final Map, LimitChange> hvdcLineToCS2toCS1Values = new HashMap<>();
private static final String MAX_P_VARIABLE_NAME = EquipmentVariable.MAX_P.getVariableName();
private static final String MIN_P_VARIABLE_NAME = EquipmentVariable.MIN_P.getVariableName();
private static final String TARGET_P_VARIABLE_NAME = EquipmentVariable.TARGET_P.getVariableName();
private static final String SET_POINT_VARIABLE_NAME = EquipmentVariable.ACTIVE_POWER_SETPOINT.getVariableName();
private String timeSeriesName;
private String id;
private boolean isMappedMinP;
private boolean isMappedMaxP;
private double minP;
private double maxP;
private boolean isOkMinP;
private boolean isOkMaxP;
boolean ignoreLimits;
@Override
public void versionStart(int version) {
this.version = version;
super.versionStart(version);
}
@Override
public void timeSeriesMappingStart(int point, TimeSeriesIndex index) {
// With ignore limits option, restore previous extended limits (potentially overwritten by NetworkPointWriter)
generatorToMaxValues.entrySet().stream().filter(e -> !Double.isNaN(e.getValue().getLimit())).forEach(e -> ((Generator) (e.getKey())).setMaxP(e.getValue().getLimit()));
generatorToMinValues.entrySet().stream().filter(e -> !Double.isNaN(e.getValue().getLimit())).forEach(e -> ((Generator) (e.getKey())).setMinP(e.getValue().getLimit()));
hvdcLineToMaxValues.entrySet().stream().filter(e -> !Double.isNaN(e.getValue().getLimit())).forEach(e -> ((HvdcLine) (e.getKey())).setMaxP(e.getValue().getLimit()));
hvdcLineToMinValues.entrySet().stream().filter(e -> !Double.isNaN(e.getValue().getLimit())).forEach(e -> ((HvdcLine) (e.getKey())).setMaxP(Math.abs(e.getValue().getLimit())));
hvdcLineToCS1toCS2Values.entrySet().stream().filter(e -> !Double.isNaN(e.getValue().getLimit())).forEach(e -> ((HvdcLine) (e.getKey())).getExtension(HvdcOperatorActivePowerRange.class).setOprFromCS1toCS2((float) e.getValue().getLimit()));
hvdcLineToCS2toCS1Values.entrySet().stream().filter(e -> !Double.isNaN(e.getValue().getLimit())).forEach(e -> ((HvdcLine) (e.getKey())).getExtension(HvdcOperatorActivePowerRange.class).setOprFromCS2toCS1((float) Math.abs(e.getValue().getLimit())));
super.timeSeriesMappingStart(point, index);
this.index = index;
if (point != TimeSeriesMapper.CONSTANT_VARIANT_ID) {
identifiableToConstantMappedPowers.forEach((key, value) -> identifiableToMappedPower.put(key, new MappedPower(value)));
}
}
@Override
public void timeSeriesMappingEnd(int point, TimeSeriesIndex index, double balance) {
// Correct each mapped power value if necessary and notify observers
identifiableToMappedPower.forEach((key, value) -> correctAndNotifyMappedPowers(point, key, value));
// Add scaling down logs to logger
targetPTimeSeriesToEquipments.forEach((key, value) -> addScalingDownLogs(index, point, key, value,
EquipmentVariable.TARGET_P,
targetPTimeSeriesToScalingDownPowerChangeSynthesis,
targetPTimeSeriesToScalingDownLimitViolationSynthesis));
setpointTimeSeriesToEquipments.forEach((key, value) -> addScalingDownLogs(index, point, key, value,
EquipmentVariable.ACTIVE_POWER_SETPOINT,
setpointTimeSeriesToScalingDownPowerChangeSynthesis,
setpointTimeSeriesToScalingDownLimitViolationSynthesis));
identifiableToMappedPower.clear();
targetPTimeSeriesToEquipments.clear();
setpointTimeSeriesToEquipments.clear();
super.timeSeriesMappingEnd(point, index, balance);
}
@Override
public void versionEnd(int version) {
// Add limit change warnings to logger
addLimitChangeLog(generatorToMinValues, MappingLimitType.MIN, version, MIN_P_VARIABLE_NAME, EquipmentVariable.TARGET_P.getVariableName());
addLimitChangeLog(generatorToMaxValues, MappingLimitType.MAX, version, MAX_P_VARIABLE_NAME, EquipmentVariable.TARGET_P.getVariableName());
addLimitChangeLog(hvdcLineToMinValues, MappingLimitType.MIN, version, TimeSeriesConstants.MINUS_MAXP, EquipmentVariable.ACTIVE_POWER_SETPOINT.getVariableName());
addLimitChangeLog(hvdcLineToMaxValues, MappingLimitType.MAX, version, MAX_P_VARIABLE_NAME, EquipmentVariable.ACTIVE_POWER_SETPOINT.getVariableName());
addLimitChangeLog(hvdcLineToCS2toCS1Values, MappingLimitType.MIN, version, TimeSeriesConstants.MINUS_CS21, EquipmentVariable.ACTIVE_POWER_SETPOINT.getVariableName());
addLimitChangeLog(hvdcLineToCS1toCS2Values, MappingLimitType.MAX, version, TimeSeriesConstants.CS12, EquipmentVariable.ACTIVE_POWER_SETPOINT.getVariableName());
// Add scaling down logs synthesis to logger
targetPTimeSeriesToScalingDownPowerChangeSynthesis.forEach((key, value) -> value.forEach(change -> addScalingDownLogSynthesis(EquipmentVariable.TARGET_P.getVariableName(), change, version, key)));
targetPTimeSeriesToScalingDownLimitViolationSynthesis.forEach((key, value) -> value.forEach(change -> addScalingDownLimitViolationLogSynthesis(change, version, key)));
setpointTimeSeriesToScalingDownPowerChangeSynthesis.forEach((key, value) -> value.forEach(change -> addScalingDownLogSynthesis(EquipmentVariable.ACTIVE_POWER_SETPOINT.getVariableName(), change, version, key)));
setpointTimeSeriesToScalingDownLimitViolationSynthesis.forEach((key, value) -> value.forEach(change -> addScalingDownLimitViolationLogSynthesis(change, version, key)));
identifiableToConstantMappedPowers.clear();
generatorToMinValues.clear();
generatorToMaxValues.clear();
hvdcLineToMinValues.clear();
hvdcLineToMaxValues.clear();
hvdcLineToCS1toCS2Values.clear();
hvdcLineToCS2toCS1Values.clear();
targetPTimeSeriesToScalingDownPowerChangeSynthesis.clear();
targetPTimeSeriesToScalingDownLimitViolationSynthesis.clear();
setpointTimeSeriesToScalingDownPowerChangeSynthesis.clear();
setpointTimeSeriesToScalingDownLimitViolationSynthesis.clear();
super.versionEnd(version);
}
public static boolean isTwoWindingsTransformerWithOutOfBoundsPhaseTapPosition(Identifiable> identifiable, MappingVariable variable, int value) {
return identifiable instanceof TwoWindingsTransformer twoWindingsTransformer
&& twoWindingsTransformer.hasPhaseTapChanger()
&& variable == EquipmentVariable.PHASE_TAP_POSITION
&& isPhaseTapPositionOutOfBounds(twoWindingsTransformer, value);
}
private static boolean isPhaseTapPositionOutOfBounds(TwoWindingsTransformer twoWindingsTransformer, int value) {
int lowTapPosition = twoWindingsTransformer.getPhaseTapChanger().getLowTapPosition();
int highTapPosition = twoWindingsTransformer.getPhaseTapChanger().getHighTapPosition();
return value < lowTapPosition || value > highTapPosition;
}
public TimeSeriesMapperChecker(List observers, TimeSeriesMappingLogger timeSeriesMappingLogger, TimeSeriesMapperParameters parameters) {
super(observers);
this.timeSeriesMappingLogger = Objects.requireNonNull(timeSeriesMappingLogger);
this.toleranceThreshold = parameters.getToleranceThreshold();
}
public void timeSeriesMappedToEquipments(int point, String timeSeriesName, double timeSeriesValue, List> identifiables, MappingVariable variable, double[] equipmentValues, boolean ignoreLimits) {
if (variable == EquipmentVariable.TARGET_P) {
targetPTimeSeriesToEquipments.put(timeSeriesName, new MappedEquipments(timeSeriesValue, new HashSet<>(identifiables)));
} else if (variable == EquipmentVariable.ACTIVE_POWER_SETPOINT) {
setpointTimeSeriesToEquipments.put(timeSeriesName, new MappedEquipments(timeSeriesValue, new HashSet<>(identifiables)));
}
for (int i = 0; i < identifiables.size(); i++) {
Identifiable> identifiable = identifiables.get(i);
double equipmentValue = equipmentValues[i];
if (TimeSeriesMapper.isPowerOrLimitVariable(variable)) {
// Store mapped power values and limits in order to correct power values not included in limits
addTimeSeriesMappedToEquipments(point, timeSeriesName, identifiable, variable, equipmentValue, ignoreLimits);
}
if (identifiable instanceof HvdcLine hvdcLine) {
hvdcLineToActivePowerRange.computeIfAbsent(identifiable.getId(), e -> hvdcLine.getExtension(HvdcOperatorActivePowerRange.class) != null);
}
if (isTwoWindingsTransformerWithOutOfBoundsPhaseTapPosition(identifiable, variable, (int) equipmentValue)) {
// fix equipmentValue in case it is out of [lowTapPosition, highTapPosition] bounds
equipmentValue = fixPhaseTapChangerTapPosition(point, (TwoWindingsTransformer) identifiable, equipmentValue);
}
if (!TimeSeriesMapper.isPowerVariable(variable)) {
// For power values, observers will be notified later after correction
// For other values, observers are notified immediately
super.timeSeriesMappedToEquipment(point, timeSeriesName, identifiable, variable, equipmentValue);
}
}
}
private void addTimeSeriesMappedToEquipments(int point, String timeSeriesName, Identifiable> identifiable, MappingVariable variable, double equipmentValue, boolean ignoreLimits) {
MappedPower mappedPower;
if (point == TimeSeriesMapper.CONSTANT_VARIANT_ID) {
identifiableToConstantMappedPowers.computeIfAbsent(identifiable, e -> new MappedPower());
mappedPower = identifiableToConstantMappedPowers.get(identifiable);
} else {
identifiableToMappedPower.computeIfAbsent(identifiable, e -> new MappedPower());
mappedPower = identifiableToMappedPower.get(identifiable);
}
mappedPower.setIgnoreLimits(ignoreLimits);
if (variable == EquipmentVariable.MIN_P) {
mappedPower.setMinP(equipmentValue);
} else if (TimeSeriesMapper.isPowerVariable(variable)) {
mappedPower.setTimeSeriesNameP(timeSeriesName);
mappedPower.setP(equipmentValue);
} else if (variable == EquipmentVariable.MAX_P) {
mappedPower.setMaxP(equipmentValue);
}
}
private double fixPhaseTapChangerTapPosition(int point, TwoWindingsTransformer twoWindingsTransformer, double value) {
int lowTapPosition = twoWindingsTransformer.getPhaseTapChanger().getLowTapPosition();
int highTapPosition = twoWindingsTransformer.getPhaseTapChanger().getHighTapPosition();
double newValue = value < lowTapPosition ? lowTapPosition : highTapPosition;
LogBuilder logbuilder = new LogBuilder().level(System.Logger.Level.WARNING).version(version).index(index).point(point);
RangeLogPhaseTapPosition rangeLogPhaseTapPosition = new RangeLogPhaseTapPosition()
.value(value)
.minValue(lowTapPosition)
.maxValue(highTapPosition)
.id(twoWindingsTransformer.getId())
.toVariable(value < lowTapPosition ? "lowTapPosition" : "highTapPosition")
.newValue(newValue);
LogContent logContent = rangeLogPhaseTapPosition.build();
Log log = logbuilder.logDescription(logContent).build();
timeSeriesMappingLogger.addLog(log);
return newValue;
}
private void correctAndNotifyMappedPowers(int point, Identifiable> identifiable, MappedPower mappedPower) {
double value;
if (identifiable instanceof Generator generator) {
value = correctMappedPowerGenerator(point, generator, mappedPower);
} else if (identifiable instanceof HvdcLine hvdcLine) {
value = correctMappedPowerHvdcLine(point, hvdcLine, mappedPower);
} else {
throw new AssertionError("Unsupported equipment type for id " + identifiable.getId());
}
mappedPower.setP(value);
super.timeSeriesMappedToEquipment(point, timeSeriesName != null ? timeSeriesName : "", identifiable, TimeSeriesMapper.getPowerVariable(identifiable), value);
}
private double correctMappedPowerGenerator(int point, Generator generator, MappedPower mappedPower) {
initCorrector(mappedPower);
id = generator.getId();
minP = isMappedMinP ? mappedPower.getMinP() : generator.getMinP();
maxP = isMappedMaxP ? mappedPower.getMaxP() : generator.getMaxP();
if (minP > maxP) {
throw new AssertionError(String.format("Equipment '%s' : invalid active limits [%s, %s] at point %s", id, minP, maxP, point));
}
final boolean isMappedTargetP = mappedPower.getP() != null;
double targetP = isMappedTargetP ? mappedPower.getP() : TimeSeriesMapper.getP(generator);
isOkMinP = targetP >= minP - toleranceThreshold;
isOkMaxP = targetP <= maxP + toleranceThreshold;
targetP = applyToleranceThresholdOnTargetP(isMappedTargetP, targetP);
addGeneratorLimitValue(generator, isMappedTargetP, targetP);
if (!isMappedTargetP) {
double result = correctMappedPowerGeneratorWhenTargetPIsNotMapped(targetP, point, TARGET_P_VARIABLE_NAME, false);
if (!Double.isNaN(result)) {
return result;
}
}
if (ignoreLimits) {
double result = correctMappedPowerGeneratorWithIgnoreLimits(targetP, generator);
if (!Double.isNaN(result)) {
return result;
}
}
return correctMappedPowerGeneratorIsTargetP(targetP, isMappedTargetP);
}
private double correctMappedPowerGeneratorIsTargetP(double targetP, boolean isMappedTargetP) {
double newValue = Double.NaN;
ScalingDownPowerChange change = null;
if (!isOkMaxP) {
change = isMappedMaxP ? ScalingDownPowerChange.MAPPED_MAXP : ScalingDownPowerChange.BASE_CASE_MAXP;
newValue = maxP;
} else if (!isOkMinP && minP <= 0) {
change = isMappedMinP ? ScalingDownPowerChange.MAPPED_MINP : ScalingDownPowerChange.BASE_CASE_MINP;
newValue = minP;
} else if (!isOkMinP && targetP < 0) {
change = ScalingDownPowerChange.ZERO;
newValue = 0;
}
if (!Double.isNaN(newValue)) {
targetPTimeSeriesToEquipments.get(timeSeriesName).getScalingDownPowerChange().add(change);
return newValue;
}
if (!isOkMinP) {
ScalingDownLimitViolation changes = isMappedMinP ? ScalingDownLimitViolation.MAPPED_MINP_BY_TARGETP : ScalingDownLimitViolation.BASE_CASE_MINP_BY_TARGETP;
targetPTimeSeriesToEquipments.get(timeSeriesName).getScalingDownLimitViolation().add(changes);
return targetP;
}
if (isMappedTargetP) {
return targetP;
}
return newValue;
}
private double correctMappedPowerGeneratorWithIgnoreLimits(double targetP, Generator generator) {
MappedEquipments mappedEquipments = targetPTimeSeriesToEquipments.get(timeSeriesName);
if (!isOkMaxP && !isMappedMaxP) {
generator.setMaxP(Math.round(targetP + 0.5f));
mappedEquipments.getScalingDownLimitViolation().add(ScalingDownLimitViolation.MAXP_BY_TARGETP);
return targetP;
} else if (!isOkMaxP) {
mappedEquipments.getScalingDownPowerChange().add(ScalingDownPowerChange.MAPPED_MAXP_DISABLED);
return maxP;
} else if (!isOkMinP && minP <= 0 && !isMappedMinP) {
// targetP is mapped, minP is not mapped -> reduce base case minP to targetP
generator.setMinP(Math.floor(targetP - 0.5f));
mappedEquipments.getScalingDownLimitViolation().add(ScalingDownLimitViolation.MINP_BY_TARGETP);
return targetP;
} else if (!isOkMinP && minP <= 0) {
mappedEquipments.getScalingDownPowerChange().add(ScalingDownPowerChange.MAPPED_MINP_DISABLED);
return minP;
} else if (!isOkMinP && targetP < 0) {
mappedEquipments.getScalingDownPowerChange().add(ScalingDownPowerChange.ZERO_DISABLED);
return 0;
}
return Double.NaN;
}
private double correctMappedPowerHvdcLinesWithIgnoreLimits(double setpoint, HvdcLine hvdcLine, boolean isActivePowerRange) {
final long round = Math.round(Math.abs(setpoint) + 0.5f);
if (!isOkMaxP && !isMappedMaxP) {
// setpoint is mapped, maxP is not mapped -> increase base case maxP to setpoint
addActivePowerRangeExtension(hvdcLine);
if (!isActivePowerRange) {
hvdcLine.getExtension(HvdcOperatorActivePowerRange.class).setOprFromCS1toCS2((float) Math.abs(hvdcLineToMinValues.get(hvdcLine).getBaseCaseLimit()));
}
TimeSeriesMapper.setHvdcMax(hvdcLine, round);
setpointTimeSeriesToEquipments.get(timeSeriesName).getScalingDownLimitViolation().add(isActivePowerRange ? ScalingDownLimitViolation.CS1TOCS2_BY_ACTIVEPOWER : ScalingDownLimitViolation.MAXP_BY_ACTIVEPOWER);
return setpoint;
} else if (!isOkMaxP) {
setpointTimeSeriesToEquipments.get(timeSeriesName).getScalingDownPowerChange().add(ScalingDownPowerChange.MAPPED_MAXP_DISABLED);
return maxP;
} else if (!isOkMinP && !isMappedMinP) {
addActivePowerRangeExtension(hvdcLine);
if (!isActivePowerRange) {
hvdcLine.getExtension(HvdcOperatorActivePowerRange.class).setOprFromCS2toCS1((float) Math.abs(hvdcLineToMaxValues.get(hvdcLine).getBaseCaseLimit()));
}
TimeSeriesMapper.setHvdcMin(hvdcLine, round);
setpointTimeSeriesToEquipments.get(timeSeriesName).getScalingDownLimitViolation().add(isActivePowerRange ? ScalingDownLimitViolation.CS2TOCS1_BY_ACTIVEPOWER : ScalingDownLimitViolation.MINP_BY_ACTIVEPOWER);
return setpoint;
} else if (!isOkMinP) {
setpointTimeSeriesToEquipments.get(timeSeriesName).getScalingDownPowerChange().add(ScalingDownPowerChange.MAPPED_MINP_DISABLED);
return minP;
}
return Double.NaN;
}
private double correctMappedPowerHvdcLinesWithNotIgnoreLimits(boolean isActivePowerRange) {
MappedEquipments mappedEquipments = setpointTimeSeriesToEquipments.get(timeSeriesName);
if (!isOkMaxP && isMappedMaxP) {
mappedEquipments.getScalingDownPowerChange().add(ScalingDownPowerChange.MAPPED_MAXP);
return maxP;
} else if (!isOkMaxP) {
ScalingDownPowerChange change = isActivePowerRange ? ScalingDownPowerChange.BASE_CASE_CS1TOCS2 : ScalingDownPowerChange.BASE_CASE_MAXP;
mappedEquipments.getScalingDownPowerChange().add(change);
return maxP;
} else if (!isOkMinP && isMappedMinP) {
mappedEquipments.getScalingDownPowerChange().add(ScalingDownPowerChange.MAPPED_MINP);
return minP;
} else if (!isOkMinP) {
ScalingDownPowerChange change = isActivePowerRange ? ScalingDownPowerChange.BASE_CASE_CS2TOCS1 : ScalingDownPowerChange.BASE_CASE_MINUS_MAXP;
mappedEquipments.getScalingDownPowerChange().add(change);
return minP;
}
return Double.NaN;
}
private double correctMappedPowerGeneratorWhenTargetPIsNotMapped(double value, int point, String variableName, boolean isHvdc) {
RangeLogWithVariableChanged rangeLogWithVariableChanged = new RangeLogWithVariableChanged()
.notIncludedVariable(variableName).id(id).minValue(minP).maxValue(maxP)
.value(value).oldValue(variableName).isMapping();
LogBuilder logbuilder = new LogBuilder().level(System.Logger.Level.WARNING).version(version).index(index).point(point);
double newValue = Double.NaN;
if (ignoreLimits) {
rangeLogWithVariableChanged.disabled(true);
}
if (!isOkMaxP) {
newValue = maxP;
rangeLogWithVariableChanged.toVariable(MAX_P_VARIABLE_NAME).newValue(newValue);
} else if (!isOkMinP && minP <= 0 || isHvdc && !isOkMinP) {
newValue = minP;
rangeLogWithVariableChanged.toVariable(MIN_P_VARIABLE_NAME).newValue(newValue);
} else if (!isOkMinP && value < 0) {
newValue = 0;
rangeLogWithVariableChanged.toVariable("").newValue(newValue);
} else if (!isOkMinP) {
LogContent logContent = new RangeWithMinPViolatedByTargetP()
.notIncludedVariable(TARGET_P_VARIABLE_NAME).id(id).minValue(minP)
.maxValue(maxP).value(value).mapped().build();
Log log = logbuilder.level(System.Logger.Level.INFO).logDescription(logContent).build();
timeSeriesMappingLogger.addLog(log);
return value;
}
if (!Double.isNaN(newValue)) {
Log log = logbuilder.logDescription(rangeLogWithVariableChanged.build()).build();
timeSeriesMappingLogger.addLog(log);
}
return newValue;
}
private double applyToleranceThresholdOnTargetP(boolean isMappedTargetP, double targetP) {
if (!ignoreLimits && isMappedTargetP) {
if (isOkMaxP && targetP >= maxP - toleranceThreshold) {
return maxP - toleranceThreshold;
} else if (isOkMinP && targetP <= minP + toleranceThreshold) {
return minP + toleranceThreshold;
}
}
return targetP;
}
private void initCorrector(MappedPower mappedPower) {
timeSeriesName = mappedPower.getTimeSeriesNameP();
isMappedMinP = mappedPower.getMinP() != null;
isMappedMaxP = mappedPower.getMaxP() != null;
ignoreLimits = mappedPower.isIgnoreLimits();
}
private void addGeneratorLimitValue(Generator generator, boolean isMappedTargetP, double targetP) {
if (ignoreLimits && isMappedTargetP) {
if (!isMappedMaxP) {
addLimitValueChange(MappingLimitType.MAX, generatorToMaxValues, generator, generator.getMaxP(), targetP);
}
if (!isMappedMinP && minP <= 0) {
addLimitValueChange(MappingLimitType.MIN, generatorToMinValues, generator, generator.getMinP(), targetP);
}
}
}
private void addHvdcLineLimitValue(HvdcLine hvdcLine, boolean isMappedSetpoint, boolean isActivePowerRange, double setpoint) {
if (ignoreLimits && isMappedSetpoint) {
if (!isMappedMaxP) {
addLimitValueChange(MappingLimitType.MAX, isActivePowerRange ? hvdcLineToCS1toCS2Values : hvdcLineToMaxValues, hvdcLine, TimeSeriesMapper.getMax(hvdcLine), setpoint);
}
if (!isMappedMinP) {
addLimitValueChange(MappingLimitType.MIN, isActivePowerRange ? hvdcLineToCS2toCS1Values : hvdcLineToMinValues, hvdcLine, TimeSeriesMapper.getMin(hvdcLine), setpoint);
}
}
}
private double correctMappedPowerHvdcLine(int point, HvdcLine hvdcLine, MappedPower mappedPower) {
initCorrector(mappedPower);
id = hvdcLine.getId();
minP = isMappedMinP ? mappedPower.getMinP() : TimeSeriesMapper.getMin(hvdcLine);
maxP = isMappedMaxP ? mappedPower.getMaxP() : TimeSeriesMapper.getMax(hvdcLine);
final boolean isMappedSetpoint = mappedPower.getP() != null;
final boolean isActivePowerRange = hvdcLineToActivePowerRange.get(id);
double setpoint = isMappedSetpoint ? mappedPower.getP() : TimeSeriesMapper.getHvdcLineSetPoint(hvdcLine);
isOkMinP = setpoint >= minP - toleranceThreshold;
isOkMaxP = setpoint <= maxP + toleranceThreshold;
setpoint = applyToleranceThresholdOnTargetP(isMappedSetpoint, setpoint);
if (hvdcLine.getMaxP() < 0) {
throw new AssertionError(String.format("Equipment '%s' : invalid active limit maxP %s at point %s", id, hvdcLine.getMaxP(), point));
} else if (isActivePowerRange && (minP > 0 || maxP < 0)) {
throw new AssertionError(String.format("Equipment '%s' : invalid active limits [%s, %s] at point %s", id, minP, maxP, point));
}
addHvdcLineLimitValue(hvdcLine, isMappedSetpoint, isActivePowerRange, setpoint);
if (!isMappedSetpoint) {
double result = correctMappedPowerGeneratorWhenTargetPIsNotMapped(setpoint, point, SET_POINT_VARIABLE_NAME, true);
if (!Double.isNaN(result)) {
return result;
}
}
if (ignoreLimits) {
double result = correctMappedPowerHvdcLinesWithIgnoreLimits(setpoint, hvdcLine, isActivePowerRange);
if (!Double.isNaN(result)) {
return result;
}
}
double result = correctMappedPowerHvdcLinesWithNotIgnoreLimits(isActivePowerRange);
if (!Double.isNaN(result)) {
return result;
}
if (isMappedSetpoint) {
return setpoint;
}
return Double.NaN;
}
private void addLimitValueChange(MappingLimitType limitType, Map, LimitChange> equipmentToLimitValues, Identifiable> identifiable, double oldLimit, double newLimit) {
if (limitType == MappingLimitType.MAX || limitType == MappingLimitType.MIN) {
equipmentToLimitValues.computeIfAbsent(identifiable, e -> new LimitChange(oldLimit, Double.NaN));
if (limitType == MappingLimitType.MAX && newLimit > oldLimit + toleranceThreshold
|| limitType == MappingLimitType.MIN && newLimit < oldLimit - toleranceThreshold) {
LimitChange limitChange = equipmentToLimitValues.get(identifiable);
limitChange.setLimit(newLimit);
}
}
equipmentToLimitValues.computeIfPresent(identifiable, (k, v) -> {
LimitChange limitChange = equipmentToLimitValues.get(identifiable);
if (!Double.isNaN(limitChange.getLimit()) && limitType == MappingLimitType.MAX && newLimit > limitChange.getBaseCaseLimit()
|| limitType == MappingLimitType.MIN && newLimit < limitChange.getBaseCaseLimit()) {
limitChange.setBaseCaseLimitNbOfViolation(limitChange.getBaseCaseLimitNbOfViolation() + 1);
}
return v;
});
}
private void addScalingDownLogs(TimeSeriesIndex index, int point, String timeSeriesName, MappedEquipments mappedEquipments,
MappingVariable variable,
Map> timeSeriesToPowerChange,
Map> timeSeriesToLimitViolation) {
if (!mappedEquipments.getScalingDownPowerChange().isEmpty()) {
double value = mappedEquipments.getTimeSeriesValue();
Set> identifiables = mappedEquipments.getIdentifiables();
double sum = identifiables.stream()
.mapToDouble(e -> identifiableToMappedPower.get(e).getP() != null ? identifiableToMappedPower.get(e).getP() : TimeSeriesMapper.getP(e))
.sum();
mappedEquipments.getScalingDownPowerChange().forEach(e -> addScalingDownLog(variable.getVariableName(), e, index, timeSeriesName, value, sum, point));
timeSeriesToPowerChange.computeIfAbsent(timeSeriesName, e -> new HashSet<>());
Set powerChange = timeSeriesToPowerChange.get(timeSeriesName);
powerChange.addAll(mappedEquipments.getScalingDownPowerChange());
}
if (!mappedEquipments.getScalingDownLimitViolation().isEmpty()) {
timeSeriesToLimitViolation.computeIfAbsent(timeSeriesName, e -> new HashSet<>());
Set scalingDownLimitViolation = timeSeriesToLimitViolation.get(timeSeriesName);
scalingDownLimitViolation.addAll(mappedEquipments.getScalingDownLimitViolation());
}
}
private void addScalingDownLog(String changedVariable, ScalingDownPowerChange change, TimeSeriesIndex index, String timeSeriesName, double value, double sum, int point) {
ScalingDownChangeToVariable logBuilder = new ScalingDownChangeToVariable().changedVariable(changedVariable)
.timeSeriesName(timeSeriesName).timeSeriesValue(value).sum(sum);
if (ScalingDownPowerChange.MAPPED_MAXP_DISABLED.equals(change)
|| ScalingDownPowerChange.MAPPED_MINP_DISABLED.equals(change)
|| ScalingDownPowerChange.ZERO_DISABLED.equals(change)) {
logBuilder.disabled(true);
}
LogContent logContent;
switch (change) {
case ZERO, ZERO_DISABLED -> logContent = logBuilder.toVariable("0").build();
case BASE_CASE_MINP -> logContent = logBuilder.toVariable(MIN_P_VARIABLE_NAME).basecase().build();
case BASE_CASE_MAXP -> logContent = logBuilder.toVariable(MAX_P_VARIABLE_NAME).basecase().build();
case MAPPED_MINP_DISABLED, MAPPED_MINP -> logContent = logBuilder.toVariable(MIN_P_VARIABLE_NAME).mapped().build();
case MAPPED_MAXP_DISABLED, MAPPED_MAXP -> logContent = logBuilder.toVariable(MAX_P_VARIABLE_NAME).mapped().build();
case BASE_CASE_CS1TOCS2 -> logContent = logBuilder.toVariable(TimeSeriesConstants.CS12).basecase().build();
case BASE_CASE_CS2TOCS1 -> logContent = logBuilder.toVariable(TimeSeriesConstants.MINUS_CS21).basecase().build();
case BASE_CASE_MINUS_MAXP -> logContent = logBuilder.toVariable(TimeSeriesConstants.MINUS_MAXP).basecase().build();
default -> throw new AssertionError(String.format(UNHANDLED_SCALING_OPERATION_ERROR, change.name()));
}
Log log = new LogBuilder().level(System.Logger.Level.WARNING).index(index).version(version).point(point).logDescription(logContent).build();
timeSeriesMappingLogger.addLog(log);
}
private void addScalingDownLogSynthesis(String changedVariable, ScalingDownPowerChange change, int version, String timeSeriesName) {
ScalingDownChangeToVariable scalingDownChangeToVariable = new ScalingDownChangeToVariable()
.changedVariable(changedVariable).timeSeriesName(timeSeriesName).synthesis(true);
LogContent logContent = switch (change) {
case BASE_CASE_MINP -> scalingDownChangeToVariable.toVariable(MIN_P_VARIABLE_NAME).basecase().build();
case BASE_CASE_MAXP -> scalingDownChangeToVariable.toVariable(MAX_P_VARIABLE_NAME).basecase().build();
case MAPPED_MINP -> scalingDownChangeToVariable.toVariable(MIN_P_VARIABLE_NAME).mapped().build();
case MAPPED_MAXP -> scalingDownChangeToVariable.toVariable(MAX_P_VARIABLE_NAME).mapped().build();
case ZERO -> scalingDownChangeToVariable.toVariable("0").build();
case MAPPED_MINP_DISABLED -> scalingDownChangeToVariable.toVariable(MIN_P_VARIABLE_NAME).mapped().disabled(true).build();
case MAPPED_MAXP_DISABLED -> scalingDownChangeToVariable.toVariable(MAX_P_VARIABLE_NAME).mapped().disabled(true).build();
case ZERO_DISABLED -> scalingDownChangeToVariable.toVariable("0").disabled(true).build();
case BASE_CASE_CS1TOCS2 -> scalingDownChangeToVariable.toVariable(TimeSeriesConstants.CS12).basecase().build();
case BASE_CASE_CS2TOCS1 -> scalingDownChangeToVariable.toVariable(TimeSeriesConstants.MINUS_CS21).basecase().build();
case BASE_CASE_MINUS_MAXP -> scalingDownChangeToVariable.toVariable(TimeSeriesConstants.MINUS_MAXP).basecase().build();
};
Log log = new LogBuilder().index(index).version(version).level(System.Logger.Level.WARNING).point(Integer.MAX_VALUE).logDescription(logContent).build();
timeSeriesMappingLogger.addLog(log);
}
private void addScalingDownLimitViolationLogSynthesis(ScalingDownLimitViolation change, int version, String timeSeriesName) {
ScalingDownLimitChangeSynthesis scalingDownLimitChangeSynthesis = new ScalingDownLimitChangeSynthesis().timeSeriesName(timeSeriesName);
LogContent logContent = switch (change) {
case BASE_CASE_MINP_BY_TARGETP -> scalingDownLimitChangeSynthesis.baseCase().buildNotModified();
case MAPPED_MINP_BY_TARGETP -> scalingDownLimitChangeSynthesis.mapped().buildNotModified();
case MAXP_BY_TARGETP -> scalingDownLimitChangeSynthesis.violatedVariable(MAX_P_VARIABLE_NAME).max()
.variable(EquipmentVariable.TARGET_P.getVariableName()).buildLimitChange();
case MAXP_BY_ACTIVEPOWER -> scalingDownLimitChangeSynthesis.violatedVariable(MAX_P_VARIABLE_NAME).max()
.variable(EquipmentVariable.ACTIVE_POWER_SETPOINT.getVariableName()).buildLimitChange();
case CS1TOCS2_BY_ACTIVEPOWER ->
scalingDownLimitChangeSynthesis.violatedVariable(TimeSeriesConstants.CS12).max()
.variable(EquipmentVariable.ACTIVE_POWER_SETPOINT.getVariableName()).buildLimitChange();
case MINP_BY_TARGETP -> scalingDownLimitChangeSynthesis.violatedVariable(MIN_P_VARIABLE_NAME).min()
.variable(EquipmentVariable.TARGET_P.getVariableName()).buildLimitChange();
case MINP_BY_ACTIVEPOWER ->
scalingDownLimitChangeSynthesis.violatedVariable(TimeSeriesConstants.MINUS_MAXP).min()
.variable(EquipmentVariable.ACTIVE_POWER_SETPOINT.getVariableName()).buildLimitChange();
case CS2TOCS1_BY_ACTIVEPOWER ->
scalingDownLimitChangeSynthesis.violatedVariable(TimeSeriesConstants.MINUS_CS21).min()
.variable(EquipmentVariable.ACTIVE_POWER_SETPOINT.getVariableName()).buildLimitChange();
};
Log log = new LogBuilder().index(index).version(version).level(System.Logger.Level.INFO).point(Integer.MAX_VALUE)
.logDescription(logContent).build();
timeSeriesMappingLogger.addLog(log);
}
private void addLimitChangeLog(Map, LimitChange> equipmentToValues, MappingLimitType limitType,
int version, String variableToChange, String variable) {
equipmentToValues.entrySet().stream()
.filter(e -> !Double.isNaN(e.getValue().getLimit()))
.forEach(e -> timeSeriesMappingLogger.addLog(getLimitLog(limitType, version, variableToChange, variable, e)));
}
private Log getLimitLog(MappingLimitType limitType, int version, String variableToChange,
String variable, Map.Entry, LimitChange> e) {
LimitLogBuilder limitLogBuilder = new LimitLogBuilder()
.id(e.getKey().getId())
.variable(variable)
.nbViolation(e.getValue().getBaseCaseLimitNbOfViolation())
.newValue(e.getValue().getLimit())
.oldValue(e.getValue().getBaseCaseLimit())
.variableToChange(variableToChange);
LogContent logContent;
if (limitType == MappingLimitType.MIN) {
logContent = limitLogBuilder.isMin().build();
} else {
logContent = limitLogBuilder.isMax().build();
}
return new LogBuilder().level(System.Logger.Level.INFO)
.index(index)
.version(version)
.point(Integer.MAX_VALUE)
.logDescription(logContent)
.build();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy