RangeActionResultArrayDeserializer.java
/*
* Copyright (c) 2021, 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/.
*/
package com.powsybl.openrao.data.raoresult.io.json.deserializers;
import com.powsybl.openrao.commons.OpenRaoException;
import com.powsybl.openrao.data.crac.api.Crac;
import com.powsybl.openrao.data.crac.api.rangeaction.PstRangeAction;
import com.powsybl.openrao.data.crac.api.rangeaction.RangeAction;
import com.powsybl.openrao.data.raoresult.impl.RangeActionResult;
import com.powsybl.openrao.data.raoresult.impl.RaoResultImpl;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.powsybl.openrao.data.raoresult.io.json.RaoResultJsonConstants.*;
import static com.powsybl.openrao.data.raoresult.io.json.deserializers.Utils.*;
/**
* @author Philippe Edwards {@literal <philippe.edwards at rte-france.com>}
*/
final class RangeActionResultArrayDeserializer {
private static final Logger LOGGER = LoggerFactory.getLogger(RangeActionResultArrayDeserializer.class);
private RangeActionResultArrayDeserializer() {
}
static void deserialize(JsonParser jsonParser, RaoResultImpl raoResult, Crac crac, String jsonFileVersion) throws IOException {
Pair<Integer, Integer> version = getVersion(jsonFileVersion);
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
String firstFieldName = jsonParser.nextFieldName();
switch (firstFieldName) {
case RANGEACTION_ID:
break;
case DeprecatedRaoResultJsonConstants.HVDCRANGEACTION_ID:
// in version <= 1.1, the id field was HVDCRANGEACION_ID, it is now RANGEACTION_ID
checkDeprecatedField(DeprecatedRaoResultJsonConstants.HVDCRANGEACTION_RESULTS, RAO_RESULT_TYPE, jsonFileVersion, "1.1");
break;
case PSTRANGEACTION_ID:
// in version <= 1.2, the id field was PSTRANGEACTION_ID, it is now RANGEACTION_ID
checkDeprecatedField(PSTRANGEACTION_ID, RAO_RESULT_TYPE, jsonFileVersion, "1.2");
break;
default:
throw new OpenRaoException(String.format("Cannot deserialize RaoResult: each %s must start with an %s field", RANGEACTION_RESULTS, RANGEACTION_ID));
}
String rangeActionId = jsonParser.nextTextValue();
RangeAction<?> rangeAction = crac.getRangeAction(rangeActionId);
if (rangeAction == null) {
throw new OpenRaoException(String.format("Cannot deserialize RaoResult: cannot deserialize RaoResult: RangeAction with id %s does not exist in the Crac", rangeActionId));
}
RangeActionResult rangeActionResult = raoResult.getAndCreateIfAbsentRangeActionResult(rangeAction);
while (!jsonParser.nextToken().isStructEnd()) {
switch (jsonParser.getCurrentName()) {
case DeprecatedRaoResultJsonConstants.HVDC_NETWORKELEMENT_ID:
checkDeprecatedField(DeprecatedRaoResultJsonConstants.HVDC_NETWORKELEMENT_ID, RANGEACTION_RESULTS, jsonFileVersion, "1.1");
jsonParser.nextTextValue();
break;
case DeprecatedRaoResultJsonConstants.PST_NETWORKELEMENT_ID:
checkDeprecatedField(DeprecatedRaoResultJsonConstants.PST_NETWORKELEMENT_ID, RANGEACTION_RESULTS, jsonFileVersion, "1.2");
jsonParser.nextTextValue();
break;
case INITIAL_SETPOINT:
if ((version.getLeft() == 1 && version.getRight() >= 8 || version.getLeft() >= 2)
&& rangeAction instanceof PstRangeAction) {
throw new OpenRaoException("Since version 1.8, only the initial taps are reported for PST range actions.");
}
jsonParser.nextToken();
rangeActionResult.setInitialSetpoint(jsonParser.getDoubleValue());
break;
case STATES_ACTIVATED:
jsonParser.nextToken();
deserializeResultsPerStates(jsonParser, rangeActionResult, crac, version, rangeAction);
break;
case AFTER_PRA_SETPOINT:
checkDeprecatedField(AFTER_PRA_SETPOINT, RANGEACTION_RESULTS, jsonFileVersion, "1.2");
jsonParser.nextTextValue();
break;
case AFTER_PRA_TAP:
checkDeprecatedField(AFTER_PRA_TAP, RANGEACTION_RESULTS, jsonFileVersion, "1.2");
jsonParser.nextTextValue();
break;
case INITIAL_TAP:
if (version.getLeft() <= 1 && version.getRight() <= 7) {
// skip this, we don't need to read tap because we have the set-point
LOGGER.info("Field {} in {} is no longer used", INITIAL_TAP, RANGEACTION_RESULTS);
jsonParser.nextTextValue();
break;
} else if (rangeAction instanceof PstRangeAction pstRangeAction) {
jsonParser.nextToken();
rangeActionResult.setInitialSetpoint(pstRangeAction.getTapToAngleConversionMap().get(jsonParser.getIntValue()));
break;
} else {
throw new OpenRaoException("Initial taps can only be defined for PST range actions.");
}
default:
throw new OpenRaoException(String.format("Cannot deserialize RaoResult: unexpected field in %s (%s)", RANGEACTION_RESULTS, jsonParser.getCurrentName()));
}
}
}
}
private static void deserializeResultsPerStates(JsonParser jsonParser, RangeActionResult rangeActionResult, Crac crac, Pair<Integer, Integer> version, RangeAction<?> rangeAction) throws IOException {
String instantId = null;
String contingencyId = null;
Double setpoint = null;
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
while (!jsonParser.nextToken().isStructEnd()) {
switch (jsonParser.getCurrentName()) {
case INSTANT:
String stringValue = jsonParser.nextTextValue();
instantId = stringValue;
break;
case CONTINGENCY_ID:
contingencyId = jsonParser.nextTextValue();
break;
case SETPOINT:
if ((version.getLeft() == 1 && version.getRight() >= 8 || version.getLeft() >= 2)
&& rangeAction instanceof PstRangeAction) {
throw new OpenRaoException("Since version 1.8, only the taps are reported for PST range actions.");
}
jsonParser.nextToken();
setpoint = jsonParser.getDoubleValue();
break;
case TAP:
if (version.getLeft() <= 1 && version.getRight() <= 7) {
// Skip, we already have setpoint
LOGGER.info("Field {} in {} is no longer used", TAP, RANGEACTION_RESULTS);
jsonParser.nextFieldName();
break;
} else if (rangeAction instanceof PstRangeAction pstRangeAction) {
jsonParser.nextToken();
setpoint = pstRangeAction.getTapToAngleConversionMap().get(jsonParser.getIntValue());
break;
} else {
throw new OpenRaoException("Taps can only be defined for PST range actions.");
}
default:
throw new OpenRaoException(String.format("Cannot deserialize RaoResult: unexpected field in %s (%s)", RANGEACTION_RESULTS, jsonParser.getCurrentName()));
}
}
if (setpoint == null) {
throw new OpenRaoException(String.format("Cannot deserialize RaoResult: setpoint is required in %s", RANGEACTION_RESULTS));
}
rangeActionResult.addActivationForState(StateDeserializer.getState(instantId, contingencyId, crac, RANGEACTION_RESULTS), setpoint);
}
}
private static Pair<Integer, Integer> getVersion(String jsonFileVersion) {
Pattern pattern = Pattern.compile("([1-9]\\d*)\\.(\\d+)");
Matcher matcher = pattern.matcher(jsonFileVersion);
matcher.find();
return Pair.of(Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
}
}