JsonVoltageCnecsCreationParameters.java
/*
* Copyright (c) 2022, 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.crac.io.cim.parameters;
import com.powsybl.openrao.commons.OpenRaoException;
import com.powsybl.openrao.commons.Unit;
import com.powsybl.openrao.data.crac.api.InstantKind;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.type.TypeReference;
import java.io.IOException;
import java.util.*;
/**
* @author Peter Mitri {@literal <peter.mitri at rte-france.com>}
*/
public final class JsonVoltageCnecsCreationParameters {
public static final String MONITORED_STATES_AND_THRESHOLDS = "monitored-states-and-thresholds";
public static final String MONITORED_NETWORK_ELEMENTS = "monitored-network-elements";
public static final String INSTANT = "instant";
public static final String CONTINGENCY_NAMES = "contingency-names";
public static final String THRESHOLDS_PER_NOMINAL_V = "thresholds-per-nominal-v";
public static final String NOMINAL_V = "nominalV";
public static final String UNIT = "unit";
public static final String MIN = "min";
public static final String MAX = "max";
public static final String KILOVOLT = "kilovolt";
private JsonVoltageCnecsCreationParameters() {
// should not be used
}
static VoltageCnecsCreationParameters deserialize(JsonParser jsonParser) throws IOException {
Map<String, VoltageMonitoredContingenciesAndThresholds> voltageMonitoringStatesAndThresholds = new TreeMap<>();
Set<String> monitoredNetworkElements = new HashSet<>();
while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
switch (jsonParser.getCurrentName()) {
case MONITORED_STATES_AND_THRESHOLDS:
jsonParser.nextToken();
try {
voltageMonitoringStatesAndThresholds = deserializeStatesAndThresholds(jsonParser);
} catch (NoSuchFieldException e) {
throw new OpenRaoException(String.format("Could not deserialize %s", MONITORED_STATES_AND_THRESHOLDS), e);
}
break;
case MONITORED_NETWORK_ELEMENTS:
jsonParser.nextToken();
monitoredNetworkElements = jsonParser.readValueAs(new TypeReference<HashSet<String>>() {
});
break;
default:
throw new OpenRaoException("Unexpected field in voltage-cnecs-creation-parameters: " + jsonParser.getCurrentName());
}
}
return new VoltageCnecsCreationParameters(voltageMonitoringStatesAndThresholds, monitoredNetworkElements);
}
private static Map<String, VoltageMonitoredContingenciesAndThresholds> deserializeStatesAndThresholds(JsonParser jsonParser) throws IOException, NoSuchFieldException {
Map<String, VoltageMonitoredContingenciesAndThresholds> statesAndThresholds = new HashMap<>();
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
String instantId = null;
Set<String> contingencyNames = null;
Map<Double, VoltageThreshold> thresholdPerNominalV = new TreeMap<>();
while (!jsonParser.nextToken().isStructEnd()) {
switch (jsonParser.getCurrentName()) {
case INSTANT:
instantId = jsonParser.nextTextValue();
break;
case CONTINGENCY_NAMES:
jsonParser.nextToken();
contingencyNames = jsonParser.readValueAs(new TypeReference<HashSet<String>>() {
});
break;
case THRESHOLDS_PER_NOMINAL_V:
jsonParser.nextToken();
thresholdPerNominalV = deserializeThresholdsPerNominalV(jsonParser);
break;
default:
throw new NoSuchFieldException(String.format("Unexpected field in %s: ", MONITORED_STATES_AND_THRESHOLDS) + jsonParser.getCurrentName());
}
}
Objects.requireNonNull(instantId);
if (instantId.equals(InstantKind.PREVENTIVE.toString().toLowerCase()) && !Objects.isNull(contingencyNames) && !contingencyNames.isEmpty()) {
throw new OpenRaoException("When monitoring the preventive instant, no contingency can be defined.");
}
if (statesAndThresholds.containsKey(instantId)) {
throw new OpenRaoException(String.format("A threshold is already defined for instant %s.", instantId));
} else {
statesAndThresholds.put(instantId, new VoltageMonitoredContingenciesAndThresholds(contingencyNames, thresholdPerNominalV));
}
}
return statesAndThresholds;
}
private static Map<Double, VoltageThreshold> deserializeThresholdsPerNominalV(JsonParser jsonParser) throws IOException, NoSuchFieldException {
Map<Double, VoltageThreshold> map = new TreeMap<>();
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
Double nominalV = null;
Unit unit = null;
Double min = null;
Double max = null;
while (!jsonParser.nextToken().isStructEnd()) {
switch (jsonParser.getCurrentName()) {
case NOMINAL_V:
jsonParser.nextToken();
nominalV = jsonParser.getValueAsDouble();
break;
case UNIT:
unit = stringToUnit(jsonParser.nextTextValue());
break;
case MIN:
jsonParser.nextToken();
min = jsonParser.getValueAsDouble();
break;
case MAX:
jsonParser.nextToken();
max = jsonParser.getValueAsDouble();
break;
default:
throw new NoSuchFieldException(String.format("Unexpected field in %s: ", THRESHOLDS_PER_NOMINAL_V) + jsonParser.getCurrentName());
}
}
if (nominalV == null) {
throw new OpenRaoException(String.format("Field %s for %s should be defined.", NOMINAL_V, THRESHOLDS_PER_NOMINAL_V));
} else if (map.containsKey(nominalV)) {
throw new OpenRaoException(String.format(Locale.ENGLISH, "Multiple thresholds for same nominalV (%.1f) defined", nominalV));
} else {
Objects.requireNonNull(unit);
map.put(nominalV, new VoltageThreshold(unit, min, max));
}
}
return map;
}
static void serialize(VoltageCnecsCreationParameters voltageCnecsCreationParameters, JsonGenerator jsonGenerator) throws IOException {
jsonGenerator.writeStartObject();
serializeMonitoredStatesAndThresholds(voltageCnecsCreationParameters.getMonitoredStatesAndThresholds(), jsonGenerator);
serializeMonitoredNetworkElements(voltageCnecsCreationParameters.getMonitoredNetworkElements(), jsonGenerator);
jsonGenerator.writeEndObject();
}
private static void serializeMonitoredStatesAndThresholds(Map<String, VoltageMonitoredContingenciesAndThresholds> monitoredStatesAndThresholds, JsonGenerator jsonGenerator) throws IOException {
jsonGenerator.writeArrayFieldStart(MONITORED_STATES_AND_THRESHOLDS);
for (Map.Entry<String, VoltageMonitoredContingenciesAndThresholds> entry : monitoredStatesAndThresholds.entrySet()) {
jsonGenerator.writeStartObject();
// Instant
jsonGenerator.writeStringField(INSTANT, entry.getKey());
VoltageMonitoredContingenciesAndThresholds data = entry.getValue();
// Thresholds
jsonGenerator.writeArrayFieldStart(THRESHOLDS_PER_NOMINAL_V);
for (Map.Entry<Double, VoltageThreshold> thresholdEntry : data.getThresholdPerNominalV().entrySet()) {
jsonGenerator.writeStartObject();
jsonGenerator.writeNumberField(NOMINAL_V, thresholdEntry.getKey());
VoltageThreshold thresh = thresholdEntry.getValue();
jsonGenerator.writeStringField(UNIT, unitToString(thresh.getUnit()));
if (thresh.getMin() != null) {
jsonGenerator.writeNumberField(MIN, thresh.getMin());
}
if (thresh.getMax() != null) {
jsonGenerator.writeNumberField(MAX, thresh.getMax());
}
jsonGenerator.writeEndObject();
}
jsonGenerator.writeEndArray();
// Contingency names
writeStringArray(CONTINGENCY_NAMES, data.getContingencyNames(), jsonGenerator);
jsonGenerator.writeEndObject();
}
jsonGenerator.writeEndArray();
}
private static void serializeMonitoredNetworkElements(Set<String> monitoredNetworkElements, JsonGenerator jsonGenerator) throws IOException {
writeStringArray(MONITORED_NETWORK_ELEMENTS, monitoredNetworkElements, jsonGenerator);
}
private static void writeStringArray(String fieldName, Set<String> array, JsonGenerator jsonGenerator) throws IOException {
if (array == null || array.isEmpty()) {
return;
}
List<String> sortedArray = array.stream().sorted().toList();
jsonGenerator.writeArrayFieldStart(fieldName);
for (String str : sortedArray) {
jsonGenerator.writeString(str);
}
jsonGenerator.writeEndArray();
}
private static Unit stringToUnit(String string) {
if (string.equalsIgnoreCase(KILOVOLT)) {
return Unit.KILOVOLT;
} else {
throw new OpenRaoException(String.format("Unhandled unit in voltage monitoring: %s", string));
}
}
private static String unitToString(Unit unit) {
if (unit.equals(Unit.KILOVOLT)) {
return KILOVOLT;
} else {
throw new OpenRaoException(String.format("Unhandled unit in voltage monitoring: %s", unit));
}
}
}