ValidationUtil.java
/**
* Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium)
* 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;
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.commons.report.TypedValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.ZonedDateTime;
import java.util.Collection;
import java.util.Objects;
import java.util.Set;
/**
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
*/
public final class ValidationUtil {
public enum ActionOnError {
THROW_EXCEPTION,
LOG_ERROR,
IGNORE,
}
private static final Logger LOGGER = LoggerFactory.getLogger(ValidationUtil.class);
private static final String ACTIVE_POWER_SETPOINT = "active power setpoint";
private static final String MAXIMUM_P = "maximum P";
private static final String UNIQUE_REGULATING_TAP_CHANGER_MSG = "Only one regulating control enabled is allowed";
private static final String VOLTAGE_REGULATOR_ON = "voltage regulator is on";
private static final String VOLTAGE_SETPOINT = "voltage setpoint";
private ValidationUtil() {
}
public static PowsyblException createUndefinedValueGetterException() {
return new PowsyblException("This getter cannot be used if the value is not defined");
}
public static PowsyblException createUnsetMethodException() {
return new PowsyblException("Unset method is not defined. Implement SCADA mode in order to use it");
}
private static ValidationException createInvalidValueException(Validable validable, double value, String valueName) {
return createInvalidValueException(validable, value, valueName, null);
}
private static ValidationException createInvalidValueException(Validable validable, double value, String valueName, String reason) {
String r = reason == null ? "" : " (" + reason + ")";
return new ValidationException(validable, "invalid value (" + value + ") for " + valueName + r);
}
private static String createInvalidValueMessage(double value, String valueName, String reason) {
return "invalid value (" + value + ") for " + valueName + (reason == null ? "" : " (" + reason + ")");
}
private static void logError(Validable validable, String message, ReportNode reportNode) {
reportNode.newReportNode()
.withMessageTemplate(validable.getMessageHeader(), message)
.withSeverity(TypedValue.ERROR_SEVERITY)
.add();
LOGGER.error("{}{}", validable.getMessageHeader(), message);
}
private static void throwExceptionOrLogError(Validable validable, String message, ActionOnError actionOnError, ReportNode reportNode) {
if (actionOnError == ActionOnError.THROW_EXCEPTION) {
throw new ValidationException(validable, message);
}
if (actionOnError == ActionOnError.LOG_ERROR) {
logError(validable, message, reportNode);
}
}
private static void throwExceptionOrIgnore(Validable validable, String message, ActionOnError actionOnError) {
if (actionOnError == ActionOnError.THROW_EXCEPTION) {
throw new ValidationException(validable, message);
}
}
public static void throwExceptionOrIgnore(Validable validable, String message, ValidationLevel validationLevel) {
throwExceptionOrIgnore(validable, message, checkValidationActionOnError(validationLevel));
}
public static void throwExceptionOrLogError(Validable validable, String message, ValidationLevel validationLevel, ReportNode reportNode) {
throwExceptionOrLogError(validable, message, validationLevel == ValidationLevel.STEADY_STATE_HYPOTHESIS ? ActionOnError.THROW_EXCEPTION : ActionOnError.LOG_ERROR, reportNode);
}
private static void throwExceptionOrLogErrorForInvalidValue(Validable validable, double value, String valueName, ActionOnError actionOnError, ReportNode reportNode) {
throwExceptionOrLogErrorForInvalidValue(validable, value, valueName, null, actionOnError, reportNode);
}
private static void throwExceptionOrLogErrorForInvalidValue(Validable validable, double value, String valueName, String reason, ActionOnError actionOnError, ReportNode reportNode) {
if (actionOnError == ActionOnError.THROW_EXCEPTION) {
throw createInvalidValueException(validable, value, valueName, reason);
}
if (actionOnError == ActionOnError.LOG_ERROR) {
logError(validable, createInvalidValueMessage(value, valueName, reason), reportNode);
}
}
public static ValidationLevel checkActivePowerSetpoint(Validable validable, double activePowerSetpoint, ValidationLevel validationLevel, ReportNode reportNode) {
return checkActivePowerSetpoint(validable, activePowerSetpoint, checkValidationActionOnError(validationLevel), reportNode);
}
private static ValidationLevel checkActivePowerSetpoint(Validable validable, double activePowerSetpoint, ActionOnError actionOnError, ReportNode reportNode) {
if (Double.isNaN(activePowerSetpoint)) {
throwExceptionOrLogErrorForInvalidValue(validable, activePowerSetpoint, ACTIVE_POWER_SETPOINT, actionOnError, reportNode);
return ValidationLevel.EQUIPMENT;
}
return ValidationLevel.STEADY_STATE_HYPOTHESIS;
}
public static ValidationLevel checkHvdcActivePowerSetpoint(Validable validable, double activePowerSetpoint, ValidationLevel validationLevel, ReportNode reportNode) {
return checkHvdcActivePowerSetpoint(validable, activePowerSetpoint, checkValidationActionOnError(validationLevel), reportNode);
}
private static ValidationLevel checkHvdcActivePowerSetpoint(Validable validable, double activePowerSetpoint, ActionOnError actionOnError, ReportNode reportNode) {
if (Double.isNaN(activePowerSetpoint)) {
throwExceptionOrLogErrorForInvalidValue(validable, activePowerSetpoint, ACTIVE_POWER_SETPOINT, actionOnError, reportNode);
return ValidationLevel.EQUIPMENT;
} else if (activePowerSetpoint < 0) {
throw createInvalidValueException(validable, activePowerSetpoint, ACTIVE_POWER_SETPOINT, "active power setpoint should not be negative");
}
return ValidationLevel.STEADY_STATE_HYPOTHESIS;
}
public static void checkActivePowerLimits(Validable validable, double minP, double maxP) {
if (minP > maxP) {
throw new ValidationException(validable, "invalid active limits [" + minP + ", " + maxP + "]");
}
}
public static ValidationLevel checkTargetDeadband(Validable validable, String validableType, boolean regulating, double targetDeadband, ValidationLevel validationLevel, ReportNode reportNode) {
return checkTargetDeadband(validable, validableType, regulating, targetDeadband, checkValidationActionOnError(validationLevel), reportNode);
}
private static ValidationLevel checkTargetDeadband(Validable validable, String validableType, boolean regulating, double targetDeadband, ActionOnError actionOnError, ReportNode reportNode) {
if (regulating && Double.isNaN(targetDeadband)) {
throwExceptionOrLogError(validable, "Undefined value for target deadband of regulating " + validableType, actionOnError, reportNode);
return ValidationLevel.EQUIPMENT;
}
if (targetDeadband < 0) {
throw new ValidationException(validable, "Unexpected value for target deadband of " + validableType + ": " + targetDeadband + " < 0");
}
return ValidationLevel.STEADY_STATE_HYPOTHESIS;
}
public static ValidationLevel checkVoltageControl(Validable validable, boolean voltageRegulatorOn, double voltageSetpoint, ValidationLevel validationLevel, ReportNode reportNode) {
return checkVoltageControl(validable, voltageRegulatorOn, voltageSetpoint, checkValidationActionOnError(validationLevel), reportNode);
}
private static ValidationLevel checkVoltageControl(Validable validable, boolean voltageRegulatorOn, double voltageSetpoint, ActionOnError actionOnError, ReportNode reportNode) {
if (voltageRegulatorOn) {
if (Double.isNaN(voltageSetpoint)) {
throwExceptionOrLogErrorForInvalidValue(validable, voltageSetpoint, VOLTAGE_SETPOINT, VOLTAGE_REGULATOR_ON, actionOnError, reportNode);
return ValidationLevel.EQUIPMENT;
}
if (voltageSetpoint <= 0) {
throw createInvalidValueException(validable, voltageSetpoint, VOLTAGE_SETPOINT, VOLTAGE_REGULATOR_ON);
}
}
return ValidationLevel.STEADY_STATE_HYPOTHESIS;
}
public static ValidationLevel checkVoltageControl(Validable validable, Boolean voltageRegulatorOn, double voltageSetpoint, double reactivePowerSetpoint, ValidationLevel validationLevel, ReportNode reportNode) {
return checkVoltageControl(validable, voltageRegulatorOn, voltageSetpoint, reactivePowerSetpoint, checkValidationActionOnError(validationLevel), reportNode);
}
private static ValidationLevel checkVoltageControl(Validable validable, Boolean voltageRegulatorOn, double voltageSetpoint, double reactivePowerSetpoint, ActionOnError actionOnError, ReportNode reportNode) {
if (voltageRegulatorOn == null) {
throw new ValidationException(validable, "voltage regulator status is not set");
}
if (voltageRegulatorOn) {
if (Double.isNaN(voltageSetpoint)) {
throwExceptionOrLogErrorForInvalidValue(validable, voltageSetpoint, VOLTAGE_SETPOINT, VOLTAGE_REGULATOR_ON, actionOnError, reportNode);
return ValidationLevel.EQUIPMENT;
}
if (voltageSetpoint <= 0) {
throw createInvalidValueException(validable, voltageSetpoint, VOLTAGE_SETPOINT, VOLTAGE_REGULATOR_ON);
}
} else if (Double.isNaN(reactivePowerSetpoint)) {
throwExceptionOrLogErrorForInvalidValue(validable, reactivePowerSetpoint, "reactive power setpoint", "voltage regulator is off", actionOnError, reportNode);
return ValidationLevel.EQUIPMENT;
}
return ValidationLevel.STEADY_STATE_HYPOTHESIS;
}
public static void checkRatedS(Validable validable, double ratedS) {
if (!Double.isNaN(ratedS) && ratedS <= 0) {
throw new ValidationException(validable, "Invalid value of rated S " + ratedS);
}
}
public static void checkEnergySource(Validable validable, EnergySource energySource) {
if (energySource == null) {
throw new ValidationException(validable, "energy source is not set");
}
}
public static void checkMinP(Validable validable, double minP) {
if (Double.isNaN(minP)) {
throw createInvalidValueException(validable, minP, "minimum P");
}
}
public static void checkMaxP(Validable validable, double maxP) {
if (Double.isNaN(maxP)) {
throw createInvalidValueException(validable, maxP, MAXIMUM_P);
}
}
public static void checkHvdcMaxP(Validable validable, double maxP) {
if (Double.isNaN(maxP)) {
throw createInvalidValueException(validable, maxP, MAXIMUM_P);
} else if (maxP < 0) {
throw createInvalidValueException(validable, maxP, MAXIMUM_P, "maximum P should not be negative");
}
}
public static void checkRegulatingTerminal(Validable validable, Terminal regulatingTerminal, Network network) {
if (regulatingTerminal != null && regulatingTerminal.getVoltageLevel().getNetwork() != network) {
throw new ValidationException(validable, "regulating terminal is not part of the network");
}
}
public static void checkLoadType(Validable validable, LoadType loadType) {
if (loadType == null) {
throw new ValidationException(validable, "load type is null");
}
}
public static ValidationLevel checkP0(Validable validable, double p0, ValidationLevel validationLevel, ReportNode reportNode) {
return checkP0(validable, p0, checkValidationActionOnError(validationLevel), reportNode);
}
private static ValidationLevel checkP0(Validable validable, double p0, ActionOnError actionOnError, ReportNode reportNode) {
if (Double.isNaN(p0)) {
throwExceptionOrLogError(validable, "p0 is invalid", actionOnError, reportNode);
return ValidationLevel.EQUIPMENT;
}
return ValidationLevel.STEADY_STATE_HYPOTHESIS;
}
public static ValidationLevel checkQ0(Validable validable, double q0, ValidationLevel validationLevel, ReportNode reportNode) {
return checkQ0(validable, q0, checkValidationActionOnError(validationLevel), reportNode);
}
private static ValidationLevel checkQ0(Validable validable, double q0, ActionOnError actionOnError, ReportNode reportNode) {
if (Double.isNaN(q0)) {
throwExceptionOrLogError(validable, "q0 is invalid", actionOnError, reportNode);
return ValidationLevel.EQUIPMENT;
}
return ValidationLevel.STEADY_STATE_HYPOTHESIS;
}
public static void checkR(Validable validable, double r) {
if (Double.isNaN(r)) {
throw new ValidationException(validable, "r is invalid");
}
}
public static void checkX(Validable validable, double x) {
if (Double.isNaN(x)) {
throw new ValidationException(validable, "x is invalid");
}
}
public static void checkG1(Validable validable, double g1) {
if (Double.isNaN(g1)) {
throw new ValidationException(validable, "g1 is invalid");
}
}
public static void checkG2(Validable validable, double g2) {
if (Double.isNaN(g2)) {
throw new ValidationException(validable, "g2 is invalid");
}
}
public static void checkB1(Validable validable, double b1) {
if (Double.isNaN(b1)) {
throw new ValidationException(validable, "b1 is invalid");
}
}
public static void checkB2(Validable validable, double b2) {
if (Double.isNaN(b2)) {
throw new ValidationException(validable, "b2 is invalid");
}
}
public static void checkG(Validable validable, double g) {
if (Double.isNaN(g)) {
throw new ValidationException(validable, "g is invalid");
}
}
public static void checkB(Validable validable, double b) {
if (Double.isNaN(b)) {
throw new ValidationException(validable, "b is invalid");
}
}
public static void checkNominalV(Validable validable, double nominalV) {
if (Double.isNaN(nominalV) || nominalV <= 0) {
throw new ValidationException(validable, "nominal voltage is invalid");
}
}
public static void checkVoltageLimits(Validable validable, double lowVoltageLimit, double highVoltageLimit) {
if (lowVoltageLimit < 0) {
throw new ValidationException(validable, "low voltage limit is < 0");
}
if (highVoltageLimit < 0) {
throw new ValidationException(validable, "high voltage limit is < 0");
}
if (lowVoltageLimit > highVoltageLimit) {
throw new ValidationException(validable, "Inconsistent voltage limit range ["
+ lowVoltageLimit + ", " + highVoltageLimit + "]");
}
}
public static void checkTopologyKind(Validable validable, TopologyKind topologyKind) {
if (topologyKind == null) {
throw new ValidationException(validable, "topology kind is invalid");
}
}
public static void checkCaseDate(Validable validable, ZonedDateTime caseDate) {
if (caseDate == null) {
throw new ValidationException(validable, "case date is invalid");
}
}
public static void checkForecastDistance(Validable validable, int forecastDistance) {
if (forecastDistance < 0) {
throw new ValidationException(validable, "forecast distance < 0");
}
}
public static void checkBPerSection(Validable validable, double sectionB) {
if (Double.isNaN(sectionB)) {
throw new ValidationException(validable, "section susceptance is invalid");
}
}
public static void checkMaximumSectionCount(Validable validable, int maximumSectionCount) {
if (maximumSectionCount <= 0) {
throw new ValidationException(validable, "the maximum number of section (" + maximumSectionCount
+ ") should be greater than 0");
}
}
public static ValidationLevel checkSections(Validable validable, Integer currentSectionCount, int maximumSectionCount, ValidationLevel validationLevel, ReportNode reportNode) {
return checkSections(validable, currentSectionCount, maximumSectionCount, checkValidationActionOnError(validationLevel), reportNode);
}
private static ValidationLevel checkSections(Validable validable, Integer currentSectionCount, int maximumSectionCount, ActionOnError actionOnError, ReportNode reportNode) {
checkMaximumSectionCount(validable, maximumSectionCount);
if (currentSectionCount == null) {
throwExceptionOrLogError(validable, "the current number of section is undefined", actionOnError, reportNode);
return ValidationLevel.EQUIPMENT;
} else {
if (currentSectionCount < 0) {
throw new ValidationException(validable, "the current number of section (" + currentSectionCount
+ ") should be greater than or equal to 0");
}
if (currentSectionCount > maximumSectionCount) {
throw new ValidationException(validable, "the current number (" + currentSectionCount
+ ") of section should be lesser than the maximum number of section ("
+ maximumSectionCount + ")");
}
}
return ValidationLevel.STEADY_STATE_HYPOTHESIS;
}
public static void checkRatedU(Validable validable, double ratedU, String num) {
if (Double.isNaN(ratedU)) {
throw new ValidationException(validable, "rated U" + num + " is invalid");
}
}
public static void checkRatedU1(Validable validable, double ratedU1) {
checkRatedU(validable, ratedU1, "1");
}
public static void checkRatedU2(Validable validable, double ratedU2) {
checkRatedU(validable, ratedU2, "2");
}
public static ValidationLevel checkSvcRegulator(Validable validable, double voltageSetpoint, double reactivePowerSetpoint,
StaticVarCompensator.RegulationMode regulationMode, ValidationLevel validationLevel, ReportNode reportNode) {
return checkSvcRegulator(validable, voltageSetpoint, reactivePowerSetpoint, regulationMode, checkValidationActionOnError(validationLevel), reportNode);
}
private static ValidationLevel checkSvcRegulator(Validable validable, double voltageSetpoint, double reactivePowerSetpoint,
StaticVarCompensator.RegulationMode regulationMode, ActionOnError actionOnError, ReportNode reportNode) {
if (regulationMode == null) {
throwExceptionOrLogError(validable, "Regulation mode is invalid", actionOnError, reportNode);
return ValidationLevel.EQUIPMENT;
}
switch (regulationMode) {
case VOLTAGE -> {
if (Double.isNaN(voltageSetpoint)) {
throwExceptionOrLogErrorForInvalidValue(validable, voltageSetpoint, VOLTAGE_SETPOINT, actionOnError, reportNode);
return ValidationLevel.EQUIPMENT;
}
}
case REACTIVE_POWER -> {
if (Double.isNaN(reactivePowerSetpoint)) {
throwExceptionOrLogErrorForInvalidValue(validable, reactivePowerSetpoint, "reactive power setpoint", actionOnError, reportNode);
return ValidationLevel.EQUIPMENT;
}
}
case OFF -> {
// nothing to check
}
}
return ValidationLevel.STEADY_STATE_HYPOTHESIS;
}
public static void checkBmin(Validable validable, double bMin) {
if (Double.isNaN(bMin)) {
throw new ValidationException(validable, "bmin is invalid");
}
}
public static void checkBmax(Validable validable, double bMax) {
if (Double.isNaN(bMax)) {
throw new ValidationException(validable, "bmax is invalid");
}
}
private static ValidationLevel errorOrWarningForRtc(Validable validable, boolean loadTapChangingCapabilities, String message, ActionOnError actionOnError, ReportNode reportNode) {
if (loadTapChangingCapabilities) {
throwExceptionOrLogError(validable, message, actionOnError, reportNode);
return ValidationLevel.EQUIPMENT;
}
reportNode.newReportNode()
.withMessageTemplate(validable.getMessageHeader(), message)
.withSeverity(TypedValue.WARN_SEVERITY)
.add();
LOGGER.warn("{}{}", validable.getMessageHeader(), message);
return ValidationLevel.STEADY_STATE_HYPOTHESIS;
}
public static ValidationLevel checkRatioTapChangerRegulation(Validable validable, boolean regulating, boolean loadTapChangingCapabilities,
Terminal regulationTerminal, RatioTapChanger.RegulationMode regulationMode,
double regulationValue, Network network, ValidationLevel validationLevel, ReportNode reportNode) {
return checkRatioTapChangerRegulation(validable, regulating, loadTapChangingCapabilities, regulationTerminal, regulationMode, regulationValue, network, checkValidationActionOnError(validationLevel), reportNode);
}
private static ValidationLevel checkRatioTapChangerRegulation(Validable validable, boolean regulating, boolean loadTapChangingCapabilities,
Terminal regulationTerminal, RatioTapChanger.RegulationMode regulationMode,
double regulationValue, Network network, ActionOnError actionOnError,
ReportNode reportNode) {
ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
if (regulating) {
if (Objects.isNull(regulationMode)) {
validationLevel = ValidationLevel.min(validationLevel, errorOrWarningForRtc(validable, loadTapChangingCapabilities, "regulation mode of regulating ratio tap changer must be given", actionOnError, reportNode));
}
if (Double.isNaN(regulationValue)) {
validationLevel = ValidationLevel.min(validationLevel, errorOrWarningForRtc(validable, loadTapChangingCapabilities, "a regulation value has to be set for a regulating ratio tap changer", actionOnError, reportNode));
}
if (regulationMode == RatioTapChanger.RegulationMode.VOLTAGE && regulationValue <= 0) {
throw new ValidationException(validable, "bad target voltage " + regulationValue);
}
if (regulationTerminal == null) {
validationLevel = ValidationLevel.min(validationLevel, errorOrWarningForRtc(validable, loadTapChangingCapabilities, "a regulation terminal has to be set for a regulating ratio tap changer", actionOnError, reportNode));
}
}
if (regulationTerminal != null && regulationTerminal.getVoltageLevel().getNetwork() != network) {
throw new ValidationException(validable, "regulation terminal is not part of the network");
}
return validationLevel;
}
public static ValidationLevel checkPhaseTapChangerRegulation(Validable validable, PhaseTapChanger.RegulationMode regulationMode,
double regulationValue, boolean regulating, Terminal regulationTerminal,
Network network, ValidationLevel validationLevel, ReportNode reportNode) {
return checkPhaseTapChangerRegulation(validable, regulationMode, regulationValue, regulating, regulationTerminal,
network, checkValidationActionOnError(validationLevel), reportNode);
}
private static ValidationLevel checkPhaseTapChangerRegulation(Validable validable, PhaseTapChanger.RegulationMode regulationMode,
double regulationValue, boolean regulating, Terminal regulationTerminal,
Network network, ActionOnError actionOnError, ReportNode reportNode) {
ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
if (regulationMode == null) {
throwExceptionOrLogError(validable, "phase regulation mode is not set", actionOnError, reportNode);
validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT);
}
if (regulating && regulationMode != null) {
if (regulationMode != PhaseTapChanger.RegulationMode.FIXED_TAP && Double.isNaN(regulationValue)) {
throwExceptionOrLogError(validable, "phase regulation is on and threshold/setpoint value is not set", actionOnError, reportNode);
validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT);
}
if (regulationMode != PhaseTapChanger.RegulationMode.FIXED_TAP && regulationTerminal == null) {
throwExceptionOrLogError(validable, "phase regulation is on and regulated terminal is not set", actionOnError, reportNode);
validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT);
}
if (regulationMode == PhaseTapChanger.RegulationMode.FIXED_TAP) {
throwExceptionOrLogError(validable, "phase regulation cannot be on if mode is FIXED", actionOnError, reportNode);
validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT);
}
}
if (regulationTerminal != null && regulationTerminal.getVoltageLevel().getNetwork() != network) {
throw new ValidationException(validable, "phase regulation terminal is not part of the network");
}
return validationLevel;
}
public static ValidationLevel checkOnlyOneTapChangerRegulatingEnabled(Validable validable, Set<TapChanger<?, ?, ?, ?>> tapChangersNotIncludingTheModified,
boolean regulating, ValidationLevel validationLevel, ReportNode reportNode) {
return checkOnlyOneTapChangerRegulatingEnabled(validable, tapChangersNotIncludingTheModified, regulating, checkValidationActionOnError(validationLevel), reportNode);
}
private static ValidationLevel checkOnlyOneTapChangerRegulatingEnabled(Validable validable, Set<TapChanger<?, ?, ?, ?>> tapChangersNotIncludingTheModified,
boolean regulating, ActionOnError actionOnError, ReportNode reportNode) {
if (regulating && tapChangersNotIncludingTheModified.stream().anyMatch(TapChanger::isRegulating)) {
throwExceptionOrLogError(validable, UNIQUE_REGULATING_TAP_CHANGER_MSG, actionOnError, reportNode);
return ValidationLevel.EQUIPMENT;
}
return ValidationLevel.STEADY_STATE_HYPOTHESIS;
}
public static ValidationLevel checkConvertersMode(Validable validable, HvdcLine.ConvertersMode converterMode,
ValidationLevel validationLevel, ReportNode reportNode) {
return checkConvertersMode(validable, converterMode, checkValidationActionOnError(validationLevel), reportNode);
}
private static ValidationLevel checkConvertersMode(Validable validable, HvdcLine.ConvertersMode converterMode,
ActionOnError actionOnError, ReportNode reportNode) {
if (converterMode == null) {
throwExceptionOrLogError(validable, "converter mode is invalid", actionOnError, reportNode);
return ValidationLevel.EQUIPMENT;
}
return ValidationLevel.STEADY_STATE_HYPOTHESIS;
}
public static void checkPowerFactor(Validable validable, double powerFactor) {
if (Double.isNaN(powerFactor)) {
throw new ValidationException(validable, "power factor is invalid");
} else if (Math.abs(powerFactor) > 1) {
throw new ValidationException(validable, "power factor is invalid, it should be between -1 and 1");
}
}
public static ValidationLevel checkLoadingLimits(Validable validable, double permanentLimit, Collection<LoadingLimits.TemporaryLimit> temporaryLimits,
ValidationLevel validationLevel, ReportNode reportNode) {
return checkLoadingLimits(validable, permanentLimit, temporaryLimits, checkValidationActionOnError(validationLevel), reportNode);
}
private static ValidationLevel checkLoadingLimits(Validable validable, double permanentLimit, Collection<LoadingLimits.TemporaryLimit> temporaryLimits,
ActionOnError actionOnError, ReportNode reportNode) {
ValidationLevel validationLevel = ValidationUtil.checkPermanentLimit(validable, permanentLimit, temporaryLimits, actionOnError, reportNode);
ValidationUtil.checkTemporaryLimits(validable, permanentLimit, temporaryLimits);
return validationLevel;
}
public static ValidationLevel checkPermanentLimit(Validable validable, double permanentLimit, Collection<LoadingLimits.TemporaryLimit> temporaryLimits,
ValidationLevel validationLevel, ReportNode reportNode) {
return checkPermanentLimit(validable, permanentLimit, temporaryLimits, checkValidationActionOnError(validationLevel), reportNode);
}
private static ValidationLevel checkPermanentLimit(Validable validable, double permanentLimit, Collection<LoadingLimits.TemporaryLimit> temporaryLimits,
ActionOnError actionOnError, ReportNode reportNode) {
ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
if (Double.isNaN(permanentLimit) && !temporaryLimits.isEmpty()) {
throwExceptionOrLogError(validable, "permanent limit must be defined if temporary limits are present", actionOnError, reportNode);
validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT);
}
if (permanentLimit < 0) {
// because it is forbidden for SSH and EQ validation levels.
throw new ValidationException(validable, "permanent limit must be >= 0");
}
if (permanentLimit == 0) {
// log if zero
LOGGER.info("{}permanent limit is set to 0", validable.getMessageHeader());
}
return validationLevel;
}
private static void checkTemporaryLimits(Validable validable, double permanentLimit, Collection<LoadingLimits.TemporaryLimit> temporaryLimits) {
// check temporary limits are consistent with permanent
if (LOGGER.isDebugEnabled()) {
double previousLimit = Double.NaN;
boolean wrongOrderMessageAlreadyLogged = false;
for (LoadingLimits.TemporaryLimit tl : temporaryLimits) { // iterate in ascending order
if (tl.getValue() <= permanentLimit) {
LOGGER.debug("{}, temporary limit should be greater than permanent limit", validable.getMessageHeader());
}
if (!wrongOrderMessageAlreadyLogged && !Double.isNaN(previousLimit) && tl.getValue() <= previousLimit) {
LOGGER.debug("{} : temporary limits should be in ascending value order", validable.getMessageHeader());
wrongOrderMessageAlreadyLogged = true;
}
previousLimit = tl.getValue();
}
}
}
public static ValidationLevel checkLossFactor(Validable validable, float lossFactor, ValidationLevel validationLevel, ReportNode reportNode) {
return checkLossFactor(validable, lossFactor, checkValidationActionOnError(validationLevel), reportNode);
}
private static ValidationLevel checkLossFactor(Validable validable, float lossFactor, ActionOnError actionOnError, ReportNode reportNode) {
ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
if (Double.isNaN(lossFactor)) {
throwExceptionOrLogError(validable, "loss factor is invalid is undefined", actionOnError, reportNode);
validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT);
} else if (lossFactor < 0 || lossFactor > 100) {
throw new ValidationException(validable, "loss factor must be >= 0 and <= 100");
}
return validationLevel;
}
private static ValidationLevel checkRtc(Validable validable, RatioTapChanger rtc, Network network, ActionOnError actionOnError, ReportNode reportNode) {
ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
if (rtc.findTapPosition().isEmpty()) {
throwExceptionOrLogError(validable, "tap position is not set", actionOnError, reportNode);
validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT);
}
validationLevel = ValidationLevel.min(validationLevel, checkRatioTapChangerRegulation(validable, rtc.isRegulating(), rtc.hasLoadTapChangingCapabilities(), rtc.getRegulationTerminal(), rtc.getRegulationMode(), rtc.getRegulationValue(), network, actionOnError, reportNode));
validationLevel = ValidationLevel.min(validationLevel, checkTargetDeadband(validable, "ratio tap changer", rtc.isRegulating(), rtc.getTargetDeadband(), actionOnError, reportNode));
return validationLevel;
}
private static ValidationLevel checkPtc(Validable validable, PhaseTapChanger ptc, Network network, ActionOnError actionOnError, ReportNode reportNode) {
ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
if (ptc.findTapPosition().isEmpty()) {
throwExceptionOrLogError(validable, "tap position is not set", actionOnError, reportNode);
validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT);
}
validationLevel = ValidationLevel.min(validationLevel, checkPhaseTapChangerRegulation(validable, ptc.getRegulationMode(), ptc.getRegulationValue(), ptc.isRegulating(), ptc.getRegulationTerminal(), network, actionOnError, reportNode));
validationLevel = ValidationLevel.min(validationLevel, checkTargetDeadband(validable, "phase tap changer", ptc.isRegulating(), ptc.getTargetDeadband(), actionOnError, reportNode));
return validationLevel;
}
private static ValidationLevel checkThreeWindingsTransformer(Validable validable, ThreeWindingsTransformer twt, ActionOnError actionOnError, ReportNode reportNode) {
ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
for (ThreeWindingsTransformer.Leg leg : twt.getLegs()) {
if (leg.hasRatioTapChanger()) {
validationLevel = ValidationLevel.min(validationLevel, checkRtc(validable, leg.getRatioTapChanger(), twt.getNetwork(), actionOnError, reportNode));
}
if (leg.hasPhaseTapChanger()) {
validationLevel = ValidationLevel.min(validationLevel, checkPtc(validable, leg.getPhaseTapChanger(), twt.getNetwork(), actionOnError, reportNode));
}
}
long regulatingTc = twt.getLegStream()
.map(ThreeWindingsTransformer.Leg::getRatioTapChanger)
.filter(Objects::nonNull)
.filter(TapChanger::isRegulating)
.count()
+ twt.getLegStream()
.map(ThreeWindingsTransformer.Leg::getPhaseTapChanger)
.filter(Objects::nonNull)
.filter(TapChanger::isRegulating)
.count();
if (regulatingTc > 1) {
throw new ValidationException(validable, UNIQUE_REGULATING_TAP_CHANGER_MSG);
}
validationLevel = checkOperationalLimitsGroups(validable, twt.getLeg1().getOperationalLimitsGroups(), validationLevel, actionOnError, reportNode);
validationLevel = checkOperationalLimitsGroups(validable, twt.getLeg2().getOperationalLimitsGroups(), validationLevel, actionOnError, reportNode);
validationLevel = checkOperationalLimitsGroups(validable, twt.getLeg3().getOperationalLimitsGroups(), validationLevel, actionOnError, reportNode);
return validationLevel;
}
private static ValidationLevel checkTwoWindingsTransformer(Validable validable, TwoWindingsTransformer twt, ActionOnError actionOnError, ReportNode reportNode) {
ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
if (twt.hasRatioTapChanger()) {
validationLevel = ValidationLevel.min(validationLevel, checkRtc(validable, twt.getRatioTapChanger(), twt.getNetwork(), actionOnError, reportNode));
}
if (twt.hasPhaseTapChanger()) {
validationLevel = ValidationLevel.min(validationLevel, checkPtc(validable, twt.getPhaseTapChanger(), twt.getNetwork(), actionOnError, reportNode));
}
if (twt.getOptionalRatioTapChanger().map(TapChanger::isRegulating).orElse(false)
&& twt.getOptionalPhaseTapChanger().map(TapChanger::isRegulating).orElse(false)) {
throw new ValidationException(validable, UNIQUE_REGULATING_TAP_CHANGER_MSG);
}
validationLevel = checkOperationalLimitsGroups(validable, twt.getOperationalLimitsGroups1(), validationLevel, actionOnError, reportNode);
validationLevel = checkOperationalLimitsGroups(validable, twt.getOperationalLimitsGroups2(), validationLevel, actionOnError, reportNode);
return validationLevel;
}
private static ValidationLevel checkIdentifiable(Identifiable<?> identifiable, ValidationLevel previous, ActionOnError actionOnError, ReportNode reportNode) {
ValidationLevel validationLevel = previous;
if (identifiable instanceof Validable validable) {
if (identifiable instanceof Battery battery) {
validationLevel = ValidationLevel.min(validationLevel, checkP0(validable, battery.getTargetP(), actionOnError, reportNode));
validationLevel = ValidationLevel.min(validationLevel, checkQ0(validable, battery.getTargetQ(), actionOnError, reportNode));
} else if (identifiable instanceof DanglingLine danglingLine) {
validationLevel = ValidationLevel.min(validationLevel, checkP0(validable, danglingLine.getP0(), actionOnError, reportNode));
validationLevel = ValidationLevel.min(validationLevel, checkQ0(validable, danglingLine.getQ0(), actionOnError, reportNode));
validationLevel = checkGenerationOnDanglingLine(validationLevel, validable, danglingLine, actionOnError, reportNode);
validationLevel = checkOperationalLimitsGroups(validable, danglingLine.getOperationalLimitsGroups(), validationLevel, actionOnError, reportNode);
} else if (identifiable instanceof Generator generator) {
validationLevel = ValidationLevel.min(validationLevel, checkActivePowerSetpoint(validable, generator.getTargetP(), actionOnError, reportNode));
validationLevel = ValidationLevel.min(validationLevel, checkVoltageControl(validable, generator.isVoltageRegulatorOn(), generator.getTargetV(), generator.getTargetQ(), actionOnError, reportNode));
} else if (identifiable instanceof HvdcLine hvdcLine) {
validationLevel = ValidationLevel.min(validationLevel, checkConvertersMode(validable, hvdcLine.getConvertersMode(), actionOnError, reportNode));
validationLevel = ValidationLevel.min(validationLevel, checkHvdcActivePowerSetpoint(validable, hvdcLine.getActivePowerSetpoint(), actionOnError, reportNode));
} else if (identifiable instanceof Load load) {
validationLevel = ValidationLevel.min(validationLevel, checkP0(validable, load.getP0(), actionOnError, reportNode));
validationLevel = ValidationLevel.min(validationLevel, checkQ0(validable, load.getQ0(), actionOnError, reportNode));
} else if (identifiable instanceof ShuntCompensator shunt) {
validationLevel = ValidationLevel.min(validationLevel, checkVoltageControl(validable, shunt.isVoltageRegulatorOn(), shunt.getTargetV(), actionOnError, reportNode));
validationLevel = ValidationLevel.min(validationLevel, checkTargetDeadband(validable, "shunt compensator", shunt.isVoltageRegulatorOn(), shunt.getTargetDeadband(), actionOnError, reportNode));
validationLevel = ValidationLevel.min(validationLevel, checkSections(validable, getSectionCount(shunt), shunt.getMaximumSectionCount(), actionOnError, reportNode));
} else if (identifiable instanceof StaticVarCompensator svc) {
validationLevel = ValidationLevel.min(validationLevel, checkSvcRegulator(validable, svc.getVoltageSetpoint(), svc.getReactivePowerSetpoint(), svc.getRegulationMode(), actionOnError, reportNode));
} else if (identifiable instanceof ThreeWindingsTransformer twt) {
validationLevel = ValidationLevel.min(validationLevel, checkThreeWindingsTransformer(validable, twt, actionOnError, reportNode));
} else if (identifiable instanceof TwoWindingsTransformer twt) {
validationLevel = ValidationLevel.min(validationLevel, checkTwoWindingsTransformer(validable, twt, actionOnError, reportNode));
} else if (identifiable instanceof VscConverterStation converterStation) {
validationLevel = ValidationLevel.min(validationLevel, checkVoltageControl(validable, converterStation.isVoltageRegulatorOn(), converterStation.getVoltageSetpoint(), converterStation.getReactivePowerSetpoint(), actionOnError, reportNode));
} else if (identifiable instanceof Branch<?> branch) {
validationLevel = checkOperationalLimitsGroups(validable, branch.getOperationalLimitsGroups1(), validationLevel, actionOnError, reportNode);
validationLevel = checkOperationalLimitsGroups(validable, branch.getOperationalLimitsGroups2(), validationLevel, actionOnError, reportNode);
}
}
return validationLevel;
}
private static ValidationLevel checkGenerationOnDanglingLine(ValidationLevel previous, Validable validable, DanglingLine danglingLine, ActionOnError actionOnError, ReportNode reportNode) {
ValidationLevel validationLevel = previous;
DanglingLine.Generation generation = danglingLine.getGeneration();
if (generation != null) {
validationLevel = ValidationLevel.min(validationLevel, checkActivePowerSetpoint(validable, generation.getTargetP(), actionOnError, reportNode));
validationLevel = ValidationLevel.min(validationLevel, checkVoltageControl(validable, generation.isVoltageRegulationOn(), generation.getTargetV(), generation.getTargetQ(), actionOnError, reportNode));
}
return validationLevel;
}
private static Integer getSectionCount(ShuntCompensator shunt) {
return shunt.findSectionCount().isPresent() ? shunt.getSectionCount() : null;
}
private static ValidationLevel checkOperationalLimitsGroups(Validable validable, Collection<OperationalLimitsGroup> operationalLimitsGroupCollection, ValidationLevel previous, ActionOnError actionOnError, ReportNode reportNode) {
ValidationLevel validationLevel = previous;
for (OperationalLimitsGroup group : operationalLimitsGroupCollection) {
validationLevel = checkOperationalLimitsGroup(validable, group, validationLevel, actionOnError, reportNode);
}
return validationLevel;
}
private static ValidationLevel checkOperationalLimitsGroup(Validable validable, OperationalLimitsGroup operationalLimitsGroup, ValidationLevel previous, ActionOnError actionOnError, ReportNode reportNode) {
ValidationLevel[] validationLevel = new ValidationLevel[1];
validationLevel[0] = previous;
operationalLimitsGroup.getCurrentLimits().ifPresent(l -> validationLevel[0] = checkLoadingLimits(validable, l, validationLevel[0], actionOnError, reportNode));
operationalLimitsGroup.getApparentPowerLimits().ifPresent(l -> validationLevel[0] = checkLoadingLimits(validable, l, validationLevel[0], actionOnError, reportNode));
operationalLimitsGroup.getActivePowerLimits().ifPresent(l -> validationLevel[0] = checkLoadingLimits(validable, l, validationLevel[0], actionOnError, reportNode));
return validationLevel[0];
}
private static ValidationLevel checkLoadingLimits(Validable validable, LoadingLimits limits, ValidationLevel validationLevel, ActionOnError actionOnError, ReportNode reportNode) {
return ValidationLevel.min(validationLevel, checkLoadingLimits(validable, limits.getPermanentLimit(), limits.getTemporaryLimits(), actionOnError, reportNode));
}
public static ValidationLevel validate(Collection<Identifiable<?>> identifiables, boolean allChecks, ActionOnError actionOnError, ValidationLevel previous, ReportNode reportNode) {
Objects.requireNonNull(identifiables);
Objects.requireNonNull(previous);
Objects.requireNonNull(reportNode);
if (checkValidationLevel(previous)) {
return previous;
}
ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
for (Identifiable<?> identifiable : identifiables) {
validationLevel = checkIdentifiable(identifiable, validationLevel, actionOnError, reportNode);
if (!allChecks && validationLevel == ValidationLevel.MINIMUM_VALUE) {
return validationLevel;
}
}
return validationLevel;
}
private static boolean checkValidationLevel(ValidationLevel validationLevel) {
return validationLevel.compareTo(ValidationLevel.STEADY_STATE_HYPOTHESIS) >= 0;
}
private static ActionOnError checkValidationActionOnError(ValidationLevel validationLevel) {
return validationLevel.compareTo(ValidationLevel.STEADY_STATE_HYPOTHESIS) >= 0 ? ActionOnError.THROW_EXCEPTION : ActionOnError.IGNORE;
}
}