From f1d3fb0ed7ecf43c7e1b98d529b153ccde9a2448 Mon Sep 17 00:00:00 2001 From: lisrte Date: Wed, 10 Apr 2024 16:17:08 +0200 Subject: [PATCH] Add dynamic and event models suppliers Signed-off-by: lisrte --- .../models/buses/StandardBusBuilder.java | 2 +- .../powsybl/dynawaltz/suppliers/Property.java | 14 ++ .../dynawaltz/suppliers/PropertyBuilder.java | 49 +++++++ .../suppliers/PropertyParserUtils.java | 80 +++++++++++ .../dynawaltz/suppliers/PropertyType.java | 51 +++++++ .../dynawaltz/suppliers/SetGroupType.java | 17 +++ .../dynamicmodels/DynamicModelConfig.java | 69 +++++++++ .../DynamicModelConfigsJsonDeserializer.java | 71 +++++++++ .../DynawoDynamicModelSupplier.java | 70 +++++++++ .../events/DynawoEventModelsSupplier.java | 59 ++++++++ .../suppliers/events/EventModelConfig.java | 46 ++++++ .../EventModelConfigsJsonDeserializer.java | 56 ++++++++ .../DynawoDynamicModelsSuppliersTest.java | 136 ++++++++++++++++++ .../DynawoEventModelsSuppliersTest.java | 129 +++++++++++++++++ .../suppliers/mappingDynamicModel.json | 16 +++ .../resources/suppliers/mappingEvent.json | 24 ++++ 16 files changed, 888 insertions(+), 1 deletion(-) create mode 100644 dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/Property.java create mode 100644 dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/PropertyBuilder.java create mode 100644 dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/PropertyParserUtils.java create mode 100644 dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/PropertyType.java create mode 100644 dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/SetGroupType.java create mode 100644 dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/dynamicmodels/DynamicModelConfig.java create mode 100644 dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/dynamicmodels/DynamicModelConfigsJsonDeserializer.java create mode 100644 dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/dynamicmodels/DynawoDynamicModelSupplier.java create mode 100644 dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/events/DynawoEventModelsSupplier.java create mode 100644 dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/events/EventModelConfig.java create mode 100644 dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/events/EventModelConfigsJsonDeserializer.java create mode 100644 dynawaltz/src/test/java/com/powsybl/dynawaltz/suppliers/DynawoDynamicModelsSuppliersTest.java create mode 100644 dynawaltz/src/test/java/com/powsybl/dynawaltz/suppliers/DynawoEventModelsSuppliersTest.java create mode 100644 dynawaltz/src/test/resources/suppliers/mappingDynamicModel.json create mode 100644 dynawaltz/src/test/resources/suppliers/mappingEvent.json diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/buses/StandardBusBuilder.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/buses/StandardBusBuilder.java index bb63615c9..e8529ba80 100644 --- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/buses/StandardBusBuilder.java +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/buses/StandardBusBuilder.java @@ -21,7 +21,7 @@ */ public class StandardBusBuilder extends AbstractBusBuilder { - public static final String CATEGORY = "INFINITE_BUS"; + public static final String CATEGORY = "BASE_BUS"; private static final ModelConfigs MODEL_CONFIGS = ModelConfigsHandler.getInstance().getModelConfigs(CATEGORY); public static StandardBusBuilder of(Network network) { diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/Property.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/Property.java new file mode 100644 index 000000000..b22ebeb4e --- /dev/null +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/Property.java @@ -0,0 +1,14 @@ +/** + * 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.dynawaltz.suppliers; + +/** + * @author Laurent Issertial {@literal } + */ +public record Property(String name, Object value, Class propertyClass) { +} diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/PropertyBuilder.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/PropertyBuilder.java new file mode 100644 index 000000000..d79be5d1a --- /dev/null +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/PropertyBuilder.java @@ -0,0 +1,49 @@ +/** + * 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.dynawaltz.suppliers; + +import java.util.List; + +/** + * @author Laurent Issertial {@literal } + */ +public class PropertyBuilder { + + private String name; + private Object value; + private PropertyType type; + + public Property build() { + return new Property(name, type.isConversionFree() ? value : type.convertValue((String) value), type.getPropertyClass()); + } + + public PropertyBuilder name(String name) { + this.name = name; + return this; + } + + public PropertyBuilder value(String value) { + this.value = value; + return this; + } + + public PropertyBuilder values(List values) { + this.value = values; + return this; + } + + public PropertyBuilder arrays(List> arrays) { + this.value = arrays.toArray(new List[0]); + return this; + } + + public PropertyBuilder type(PropertyType type) { + this.type = type; + return this; + } +} diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/PropertyParserUtils.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/PropertyParserUtils.java new file mode 100644 index 000000000..bae35d87f --- /dev/null +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/PropertyParserUtils.java @@ -0,0 +1,80 @@ +/** + * 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.dynawaltz.suppliers; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.json.JsonUtil; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Laurent Issertial {@literal } + */ +public final class PropertyParserUtils { + + private PropertyParserUtils() { + } + + public static Property parseProperty(JsonParser parser) { + PropertyBuilder builder = new PropertyBuilder(); + JsonUtil.parseObject(parser, name -> switch (name) { + case "name" -> { + builder.name(parser.nextTextValue()); + yield true; + } + case "value" -> { + builder.value(parser.nextTextValue()); + yield true; + } + case "values" -> { + builder.values(JsonUtil.parseStringArray(parser)); + yield true; + } + case "arrays" -> { + parseArrays(parser, builder); + yield true; + } + case "type" -> { + builder.type(PropertyType.valueOf(parser.nextTextValue())); + yield true; + } + default -> false; + }); + return builder.build(); + } + + private static void parseArrays(JsonParser parser, PropertyBuilder builder) throws IOException { + parser.nextToken(); + parser.nextToken(); + JsonToken token; + List> arrays = new ArrayList<>(); + List values = new ArrayList<>(); + while ((token = parser.nextToken()) != null) { + if (token == JsonToken.VALUE_STRING) { + values.add(parser.getText()); + } else if (token == JsonToken.END_ARRAY) { + arrays.add(values); + JsonToken next = parser.nextToken(); + if (next == JsonToken.END_ARRAY) { + builder.arrays(arrays); + break; + } else if (next == JsonToken.START_ARRAY) { + values = new ArrayList<>(); + } else { + throw new PowsyblException("Unexpected token " + next); + } + } else { + throw new PowsyblException("Unexpected token " + token); + } + } + } +} diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/PropertyType.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/PropertyType.java new file mode 100644 index 000000000..0c0a0e3d5 --- /dev/null +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/PropertyType.java @@ -0,0 +1,51 @@ +/** + * 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.dynawaltz.suppliers; + +import com.powsybl.iidm.network.TwoSides; + +import java.util.Collection; +import java.util.EnumSet; +import java.util.List; +import java.util.function.Function; + +/** + * @author Laurent Issertial {@literal } + */ +public enum PropertyType { + + TWO_SIDES(TwoSides.class, TwoSides::valueOf), + BOOLEAN(boolean.class, Boolean::parseBoolean), + INTEGER(int.class, Integer::parseInt), + DOUBLE(double.class, Double::parseDouble), + STRING(String.class, value -> value), + STRINGS(Collection.class, List::of), + STRINGS_ARRAYS(Collection[].class, s -> new Collection[]{List.of(s)}); + + private static final EnumSet CONVERSION_FREE_TYPES = EnumSet.of(STRING, STRINGS, STRINGS_ARRAYS); + + private final Class propertyClass; + private final Function valueConvertor; + + PropertyType(Class propertyClass, Function valueConvertor) { + this.propertyClass = propertyClass; + this.valueConvertor = valueConvertor; + } + + public Class getPropertyClass() { + return propertyClass; + } + + public Object convertValue(String value) { + return valueConvertor.apply(value); + } + + public boolean isConversionFree() { + return CONVERSION_FREE_TYPES.contains(this); + } +} diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/SetGroupType.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/SetGroupType.java new file mode 100644 index 000000000..0c706bda0 --- /dev/null +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/SetGroupType.java @@ -0,0 +1,17 @@ +/** + * 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.dynawaltz.suppliers; + +/** + * @author Laurent Issertial {@literal } + */ +public enum SetGroupType { + FIXED, + PREFIX, + SUFFIX +} diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/dynamicmodels/DynamicModelConfig.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/dynamicmodels/DynamicModelConfig.java new file mode 100644 index 000000000..f3b946bef --- /dev/null +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/dynamicmodels/DynamicModelConfig.java @@ -0,0 +1,69 @@ +/** + * 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.dynawaltz.suppliers.dynamicmodels; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.dynawaltz.suppliers.Property; +import com.powsybl.dynawaltz.suppliers.SetGroupType; + +import java.util.List; + +/** + * @author Laurent Issertial {@literal } + */ +public class DynamicModelConfig { + + private final String model; + private final String group; + private final List properties; + + public DynamicModelConfig(String model, String group, SetGroupType groupType, List properties) { + this.model = model; + this.group = switch (groupType) { + case FIXED -> group; + case PREFIX -> group + getDynamicModelIdProperty(properties); + case SUFFIX -> getDynamicModelIdProperty(properties) + group; + }; + this.properties = properties; + } + + public DynamicModelConfig(String model, String group, List properties) { + this.model = model; + this.group = group; + this.properties = properties; + } + + public String getModel() { + return model; + } + + public String getGroup() { + return group; + } + + public List getProperties() { + return properties; + } + + private static String getDynamicModelIdProperty(List properties) { + return properties.stream() + .filter(p -> p.name().equalsIgnoreCase("dynamicModelId")) + .map(p -> (String) p.value()) + .findFirst() + .orElseGet(() -> DynamicModelConfig.getStaticIdProperty(properties)); + } + + private static String getStaticIdProperty(List properties) { + return properties.stream() + .filter(p -> p.name().equalsIgnoreCase("staticId")) + .map(p -> (String) p.value()) + .findFirst() + .orElseThrow(() -> new PowsyblException("No ID found for parameter set id")); + } +} + diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/dynamicmodels/DynamicModelConfigsJsonDeserializer.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/dynamicmodels/DynamicModelConfigsJsonDeserializer.java new file mode 100644 index 000000000..d2ca86589 --- /dev/null +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/dynamicmodels/DynamicModelConfigsJsonDeserializer.java @@ -0,0 +1,71 @@ +/** + * 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.dynawaltz.suppliers.dynamicmodels; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.powsybl.commons.json.JsonUtil; +import com.powsybl.dynawaltz.suppliers.Property; +import com.powsybl.dynawaltz.suppliers.PropertyParserUtils; +import com.powsybl.dynawaltz.suppliers.SetGroupType; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Laurent Issertial {@literal } + */ +public class DynamicModelConfigsJsonDeserializer extends StdDeserializer> { + + public DynamicModelConfigsJsonDeserializer() { + super(List.class); + } + + @Override + public List deserialize(JsonParser parser, DeserializationContext context) { + List modelConfigList = new ArrayList<>(); + JsonUtil.parseObject(parser, name -> { + if (name.equals("models")) { + JsonUtil.parseObjectArray(parser, modelConfigList::add, DynamicModelConfigsJsonDeserializer::parseModelConfig); + return true; + } + return false; + }); + return modelConfigList; + } + + private static DynamicModelConfig parseModelConfig(JsonParser parser) { + var parsingContext = new Object() { + String model = null; + String group = null; + SetGroupType groupType = null; + final List properties = new ArrayList<>(); + }; + JsonUtil.parseObject(parser, name -> switch (name) { + case "model" -> { + parsingContext.model = parser.nextTextValue(); + yield true; + } + case "group" -> { + parsingContext.group = parser.nextTextValue(); + yield true; + } + case "groupType" -> { + parsingContext.groupType = SetGroupType.valueOf(parser.nextTextValue()); + yield true; + } + case "properties" -> { + JsonUtil.parseObjectArray(parser, parsingContext.properties::add, PropertyParserUtils::parseProperty); + yield true; + } + default -> false; + }); + return new DynamicModelConfig(parsingContext.model, parsingContext.group, parsingContext.groupType, parsingContext.properties); + } +} diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/dynamicmodels/DynawoDynamicModelSupplier.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/dynamicmodels/DynawoDynamicModelSupplier.java new file mode 100644 index 000000000..c5b305e2a --- /dev/null +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/dynamicmodels/DynawoDynamicModelSupplier.java @@ -0,0 +1,70 @@ +/** + * 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.dynawaltz.suppliers.dynamicmodels; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dynamicsimulation.DynamicModel; +import com.powsybl.dynamicsimulation.DynamicModelsSupplier; +import com.powsybl.dynawaltz.builders.ModelBuilder; +import com.powsybl.dynawaltz.builders.ModelConfigsHandler; +import com.powsybl.dynawaltz.suppliers.Property; +import com.powsybl.iidm.network.Network; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.Objects; + +/** + * @author Laurent Issertial {@literal } + */ +public class DynawoDynamicModelSupplier implements DynamicModelsSupplier { + + private static final String PARAMETER_ID_FIELD = "parameterSetId"; + + private final List dynamicModelConfigs; + + public DynawoDynamicModelSupplier(List dynamicModelConfigs) { + this.dynamicModelConfigs = dynamicModelConfigs; + } + + @Override + public List get(Network network, ReportNode reportNode) { + return dynamicModelConfigs.stream() + .map(dynamicModelConfig -> buildDynamicModel(dynamicModelConfig, network, reportNode)) + .filter(Objects::nonNull) + .toList(); + } + + private static DynamicModel buildDynamicModel(DynamicModelConfig dynamicModelConfig, Network network, ReportNode reportNode) { + ModelBuilder builder = ModelConfigsHandler.getInstance().getModelBuilder(network, dynamicModelConfig.getModel(), reportNode); + if (builder != null) { + Class builderClass = builder.getClass(); + invokeParameterIdMethod(builderClass, builder, dynamicModelConfig.getGroup()); + dynamicModelConfig.getProperties().forEach(p -> invokeMethod(builderClass, builder, p)); + return builder.build(); + } + return null; + } + + private static void invokeMethod(Class builderClass, ModelBuilder builder, Property property) { + try { + builderClass.getMethod(property.name(), property.propertyClass()).invoke(builder, property.value()); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new PowsyblException(String.format("Method %s not found for parameter %s on builder %s", property.name(), property.value(), builderClass.getSimpleName()), e); + } + } + + private static void invokeParameterIdMethod(Class builderClass, ModelBuilder builder, String value) { + try { + builderClass.getMethod(DynawoDynamicModelSupplier.PARAMETER_ID_FIELD, String.class).invoke(builder, value); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new PowsyblException(String.format("Method %s not found for parameter %s on builder %s", DynawoDynamicModelSupplier.PARAMETER_ID_FIELD, value, builderClass.getSimpleName()), e); + } + } +} diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/events/DynawoEventModelsSupplier.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/events/DynawoEventModelsSupplier.java new file mode 100644 index 000000000..f92180e7b --- /dev/null +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/events/DynawoEventModelsSupplier.java @@ -0,0 +1,59 @@ +/** + * 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.dynawaltz.suppliers.events; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dynamicsimulation.EventModel; +import com.powsybl.dynamicsimulation.EventModelsSupplier; +import com.powsybl.dynawaltz.builders.ModelBuilder; +import com.powsybl.dynawaltz.builders.ModelConfigsHandler; +import com.powsybl.dynawaltz.suppliers.Property; +import com.powsybl.iidm.network.Network; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.Objects; + +/** + * @author Laurent Issertial {@literal } + */ +public class DynawoEventModelsSupplier implements EventModelsSupplier { + + private final List eventModelConfigs; + + public DynawoEventModelsSupplier(List eventModelConfigs) { + this.eventModelConfigs = eventModelConfigs; + } + + @Override + public List get(Network network, ReportNode reportNode) { + return eventModelConfigs.stream() + .map(eventModelConfig -> buildEventModel(eventModelConfig, network, reportNode)) + .filter(Objects::nonNull) + .toList(); + } + + private static EventModel buildEventModel(EventModelConfig eventModelConfig, Network network, ReportNode reportNode) { + ModelBuilder builder = ModelConfigsHandler.getInstance().getEventModelBuilder(network, eventModelConfig.getModel(), reportNode); + if (builder != null) { + Class builderClass = builder.getClass(); + eventModelConfig.getProperties().forEach(p -> invokeMethod(builderClass, builder, p)); + return builder.build(); + } + return null; + } + + private static void invokeMethod(Class builderClass, ModelBuilder builder, Property property) { + try { + builderClass.getMethod(property.name(), property.propertyClass()).invoke(builder, property.value()); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new PowsyblException(String.format("Method %s not found for parameter %s on builder %s", property.name(), property.value(), builderClass.getSimpleName()), e); + } + } +} diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/events/EventModelConfig.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/events/EventModelConfig.java new file mode 100644 index 000000000..a2da6a995 --- /dev/null +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/events/EventModelConfig.java @@ -0,0 +1,46 @@ +/** + * 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.dynawaltz.suppliers.events; + +import com.powsybl.dynawaltz.suppliers.Property; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Laurent Issertial {@literal } + */ +public class EventModelConfig { + + private String model; + private List properties = new ArrayList<>(); + + public EventModelConfig(String model, List properties) { + this.model = model; + this.properties = properties; + } + + public EventModelConfig() { + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public List getProperties() { + return properties; + } + + public void setProperties(List properties) { + this.properties = properties; + } +} diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/events/EventModelConfigsJsonDeserializer.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/events/EventModelConfigsJsonDeserializer.java new file mode 100644 index 000000000..d30e0ed0a --- /dev/null +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/events/EventModelConfigsJsonDeserializer.java @@ -0,0 +1,56 @@ +/** + * 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.dynawaltz.suppliers.events; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.powsybl.commons.json.JsonUtil; +import com.powsybl.dynawaltz.suppliers.PropertyParserUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Laurent Issertial {@literal } + */ +public class EventModelConfigsJsonDeserializer extends StdDeserializer> { + + public EventModelConfigsJsonDeserializer() { + super(List.class); + } + + @Override + public List deserialize(JsonParser parser, DeserializationContext context) { + List modelConfigList = new ArrayList<>(); + JsonUtil.parseObject(parser, name -> { + if (name.equals("events")) { + JsonUtil.parseObjectArray(parser, modelConfigList::add, EventModelConfigsJsonDeserializer::parseModelConfig); + return true; + } + return false; + }); + return modelConfigList; + } + + private static EventModelConfig parseModelConfig(JsonParser parser) { + EventModelConfig modelConfig = new EventModelConfig(); + JsonUtil.parseObject(parser, name -> switch (name) { + case "model" -> { + modelConfig.setModel(parser.nextTextValue()); + yield true; + } + case "properties" -> { + JsonUtil.parseObjectArray(parser, modelConfig.getProperties()::add, PropertyParserUtils::parseProperty); + yield true; + } + default -> false; + }); + return modelConfig; + } +} diff --git a/dynawaltz/src/test/java/com/powsybl/dynawaltz/suppliers/DynawoDynamicModelsSuppliersTest.java b/dynawaltz/src/test/java/com/powsybl/dynawaltz/suppliers/DynawoDynamicModelsSuppliersTest.java new file mode 100644 index 000000000..a27682ea7 --- /dev/null +++ b/dynawaltz/src/test/java/com/powsybl/dynawaltz/suppliers/DynawoDynamicModelsSuppliersTest.java @@ -0,0 +1,136 @@ +/** + * 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.dynawaltz.suppliers; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dynamicsimulation.DynamicModel; +import com.powsybl.dynawaltz.models.automationsystems.TapChangerBlockingAutomationSystemBuilder; +import com.powsybl.dynawaltz.models.generators.SynchronizedGeneratorBuilder; +import com.powsybl.dynawaltz.suppliers.dynamicmodels.DynamicModelConfig; +import com.powsybl.dynawaltz.suppliers.dynamicmodels.*; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Laurent Issertial {@literal } + */ +class DynawoDynamicModelsSuppliersTest { + + @Test + void testDynamicModelSupplier() { + Network network = EurostagTutorialExample1Factory.createWithLFResults(); + List modelConfigList = getModelConfigs(); + List models = new DynawoDynamicModelSupplier(modelConfigList).get(network, ReportNode.NO_OP); + + DynamicModel gen = SynchronizedGeneratorBuilder.of(network, "GeneratorPQ") + .staticId("GEN") + .parameterSetId("DM_GEN") + .build(); + DynamicModel tcb = TapChangerBlockingAutomationSystemBuilder.of(network) + .dynamicModelId("TCB1") + .parameterSetId("tcb_par") + .transformers("NGEN_NHV1", "NHV2_NLOAD") + .uMeasurements(new Collection[]{List.of("OldNGen", "NGEN"), List.of("NHV1", "NHV2")}) + .build(); + + assertEquals(2, models.size()); + assertThat(models.get(0)).usingRecursiveComparison().isEqualTo(gen); + assertThat(models.get(1)).usingRecursiveComparison().isEqualTo(tcb); + } + + @Test + void testWrongNameBuilder() { + Network network = EurostagTutorialExample1Factory.create(); + List modelConfigList = List.of( + new DynamicModelConfig("WrongName", "param", Collections.emptyList()) + ); + List models = new DynawoDynamicModelSupplier(modelConfigList).get(network, ReportNode.NO_OP); + assertTrue(models.isEmpty()); + } + + @Test + void testEventModelConfigDeserializer() throws IOException { + ObjectMapper objectMapper = setupObjectMapper(); + try (InputStream is = getClass().getResourceAsStream("/suppliers/mappingDynamicModel.json")) { + List configs = objectMapper.readValue(is, new TypeReference<>() { + }); + assertEquals(1, configs.size()); + assertThat(configs.get(0)).usingRecursiveComparison().isEqualTo(getLoadConfig()); + } + } + + @Test + void groupTypeException() { + PowsyblException e = assertThrows(PowsyblException.class, () -> new DynamicModelConfig("LoadAlphaBeta", "_DM", SetGroupType.SUFFIX, List.of( + new PropertyBuilder() + .name("propertyName") + .value("LOAD") + .type(PropertyType.STRING) + .build()))); + assertEquals("No ID found for parameter set id", e.getMessage()); + } + + private static List getModelConfigs() { + return List.of( + new DynamicModelConfig("GeneratorPQ", "DM_", SetGroupType.PREFIX, List.of( + new PropertyBuilder() + .name("staticId") + .value("GEN") + .type(PropertyType.STRING) + .build())), + new DynamicModelConfig("TapChangerBlockingAutomaton", "tcb_par", List.of( + new PropertyBuilder() + .name("dynamicModelId") + .value("TCB1") + .type(PropertyType.STRING) + .build(), + new PropertyBuilder() + .name("transformers") + .values(List.of("NGEN_NHV1", "NHV2_NLOAD")) + .type(PropertyType.STRINGS) + .build(), + new PropertyBuilder() + .name("uMeasurements") + .arrays(List.of(List.of("OldNGen", "NGEN"), List.of("NHV1", "NHV2"))) + .type(PropertyType.STRINGS_ARRAYS) + .build() + )) + ); + } + + private static DynamicModelConfig getLoadConfig() { + return new DynamicModelConfig("LoadAlphaBeta", "_DM", SetGroupType.SUFFIX, List.of( + new PropertyBuilder() + .name("staticId") + .value("LOAD") + .type(PropertyType.STRING) + .build())); + } + + private static ObjectMapper setupObjectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addDeserializer(List.class, new DynamicModelConfigsJsonDeserializer()); + objectMapper.registerModule(module); + return objectMapper; + } +} diff --git a/dynawaltz/src/test/java/com/powsybl/dynawaltz/suppliers/DynawoEventModelsSuppliersTest.java b/dynawaltz/src/test/java/com/powsybl/dynawaltz/suppliers/DynawoEventModelsSuppliersTest.java new file mode 100644 index 000000000..3639f68f8 --- /dev/null +++ b/dynawaltz/src/test/java/com/powsybl/dynawaltz/suppliers/DynawoEventModelsSuppliersTest.java @@ -0,0 +1,129 @@ +/** + * 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.dynawaltz.suppliers; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dynamicsimulation.EventModel; +import com.powsybl.dynawaltz.models.events.EventActivePowerVariationBuilder; +import com.powsybl.dynawaltz.models.events.EventDisconnectionBuilder; +import com.powsybl.dynawaltz.suppliers.events.*; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.TwoSides; +import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Laurent Issertial {@literal } + */ +class DynawoEventModelsSuppliersTest { + + @Test + void testEventSupplier() { + Network network = EurostagTutorialExample1Factory.create(); + List eventModelConfigList = getEventConfigs(); + List events = new DynawoEventModelsSupplier(eventModelConfigList).get(network, ReportNode.NO_OP); + + EventModel disconnection = EventDisconnectionBuilder.of(network) + .staticId("NHV1_NHV2_2") + .startTime(1) + .disconnectOnly(TwoSides.TWO) + .build(); + EventModel step = EventActivePowerVariationBuilder.of(network) + .staticId("LOAD") + .startTime(1) + .deltaP(20) + .build(); + + assertEquals(2, events.size()); + assertThat(events.get(0)).usingRecursiveComparison().isEqualTo(disconnection); + assertThat(events.get(1)).usingRecursiveComparison().isEqualTo(step); + } + + @Test + void testWrongNameBuilder() { + Network network = EurostagTutorialExample1Factory.create(); + List eventModelConfigList = List.of( + new EventModelConfig("WrongName", Collections.emptyList()) + ); + List events = new DynawoEventModelsSupplier(eventModelConfigList).get(network, ReportNode.NO_OP); + assertTrue(events.isEmpty()); + } + + @Test + void testEventModelConfigDeserializer() throws IOException { + ObjectMapper objectMapper = setupObjectMapper(); + try (InputStream is = getClass().getResourceAsStream("/suppliers/mappingEvent.json")) { + List configs = objectMapper.readValue(is, new TypeReference<>() { + }); + assertEquals(1, configs.size()); + assertThat(configs.get(0)).usingRecursiveComparison().isEqualTo(getActivePowerVariationConfig()); + } + } + + private static List getEventConfigs() { + return List.of( + new EventModelConfig("Disconnect", List.of( + new PropertyBuilder() + .name("staticId") + .value("NHV1_NHV2_2") + .type(PropertyType.STRING) + .build(), + new PropertyBuilder() + .name("startTime") + .value("1") + .type(PropertyType.DOUBLE) + .build(), + new PropertyBuilder() + .name("disconnectOnly") + .value("TWO") + .type(PropertyType.TWO_SIDES) + .build() + )), + getActivePowerVariationConfig() + ); + } + + private static EventModelConfig getActivePowerVariationConfig() { + return new EventModelConfig("Step", List.of( + new PropertyBuilder() + .name("staticId") + .value("LOAD") + .type(PropertyType.STRING) + .build(), + new PropertyBuilder() + .name("startTime") + .value("1") + .type(PropertyType.DOUBLE) + .build(), + new PropertyBuilder() + .name("deltaP") + .value("20") + .type(PropertyType.DOUBLE) + .build())); + } + + private static ObjectMapper setupObjectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addDeserializer(List.class, new EventModelConfigsJsonDeserializer()); + objectMapper.registerModule(module); + return objectMapper; + } +} diff --git a/dynawaltz/src/test/resources/suppliers/mappingDynamicModel.json b/dynawaltz/src/test/resources/suppliers/mappingDynamicModel.json new file mode 100644 index 000000000..e3f62a0b6 --- /dev/null +++ b/dynawaltz/src/test/resources/suppliers/mappingDynamicModel.json @@ -0,0 +1,16 @@ +{ + "models":[ + { + "model":"LoadAlphaBeta", + "group": "_DM", + "groupType": "SUFFIX", + "properties":[ + { + "name":"staticId", + "value":"LOAD", + "type":"STRING" + } + ] + } + ] +} \ No newline at end of file diff --git a/dynawaltz/src/test/resources/suppliers/mappingEvent.json b/dynawaltz/src/test/resources/suppliers/mappingEvent.json new file mode 100644 index 000000000..f33c3749c --- /dev/null +++ b/dynawaltz/src/test/resources/suppliers/mappingEvent.json @@ -0,0 +1,24 @@ +{ + "events":[ + { + "model":"Step", + "properties":[ + { + "name":"staticId", + "value":"LOAD", + "type":"STRING" + }, + { + "name":"startTime", + "value":"1", + "type":"DOUBLE" + }, + { + "name":"deltaP", + "value":"20", + "type":"DOUBLE" + } + ] + } + ] +} \ No newline at end of file