diff --git a/docs/dynamic_simulation/curves-dsl.md b/docs/dynamic_simulation/curves-dsl.md
index 96c4e0c54..216d70b5c 100644
--- a/docs/dynamic_simulation/curves-dsl.md
+++ b/docs/dynamic_simulation/curves-dsl.md
@@ -1,7 +1,7 @@
# Curves DSL
-The curves domain specific language allow a user to configure the curves Dynawo will export at the end of the simulation. This DSL defines the `curve` and the `curves` keywords.
+The curves domain specific language allow a user to configure the curves Dynawo will export at the end of the simulation. This DSL defines the `curve` keywords.
-The `curve` keyword create a single curve for a dynamic model. One identifies a dynamic model by its ID, the same as the one used in the [Dynamic Models DSL](dynamic-models-dsl). The variable to plot is identified by its name.
+The `curve` keyword combined with the `variable` field create a single curve for a dynamic model. One identifies a dynamic model by its ID, the same as the one used in the [Dynamic Models DSL](dynamic-models-dsl). The variable to plot is identified by its name.
```groovy
curve {
dynamicModelId load.id
@@ -17,7 +17,7 @@ curve {
}
```
-If you want to plot several variables of the same dynamic model, you can use the `curves` keyword that permit limiting boilerplate code in the script.
+If you want to plot several variables of the same dynamic model, you can use the `variables` field that permit limiting boilerplate code in the script.
```
// This:
curve {
@@ -30,7 +30,7 @@ curve {
}
// is equivalent to:
-curves {
+curve {
dynamicModelId load.id
variables "load_PPu", "load_QPu"
}
diff --git a/dynawaltz-dsl/src/main/groovy/com/powsybl/dynawaltz/dsl/DynaWaltzCurveGroovyExtension.groovy b/dynawaltz-dsl/src/main/groovy/com/powsybl/dynawaltz/dsl/DynaWaltzCurveGroovyExtension.groovy
index 36b0f4fc1..855c9902a 100644
--- a/dynawaltz-dsl/src/main/groovy/com/powsybl/dynawaltz/dsl/DynaWaltzCurveGroovyExtension.groovy
+++ b/dynawaltz-dsl/src/main/groovy/com/powsybl/dynawaltz/dsl/DynaWaltzCurveGroovyExtension.groovy
@@ -8,15 +8,12 @@ package com.powsybl.dynawaltz.dsl
import com.google.auto.service.AutoService
import com.powsybl.commons.report.ReportNode
-import com.powsybl.dsl.DslException
import com.powsybl.dynamicsimulation.Curve
import com.powsybl.dynamicsimulation.groovy.CurveGroovyExtension
-
-import com.powsybl.dynawaltz.DynaWaltzCurve
import com.powsybl.dynawaltz.DynaWaltzProvider
+import com.powsybl.dynawaltz.curves.DynawoCurvesBuilder
import java.util.function.Consumer
-
/**
* An implementation of {@link CurveGroovyExtension} that adds the
curve
keyword to the DSL
*
@@ -25,80 +22,20 @@ import java.util.function.Consumer
@AutoService(CurveGroovyExtension.class)
class DynaWaltzCurveGroovyExtension implements CurveGroovyExtension {
- /**
- * A curve for DynaWaltz
can be defined in DSL using {@code staticId} and {@code variable} or {@code dynamicModelId} and {@code variable}.
- * Definition with {@code staticId} and {@code variable} are used when no explicit dynamic component exists (buses).
- * DynaWaltz
expects {@code dynamicModelId} = “NETWORK” for these variables.
- */
- static class CurvesSpec {
- String dynamicModelId
- String staticId
- String[] variables
-
- void dynamicModelId(String dynamicModelId) {
- this.dynamicModelId = dynamicModelId
- }
-
- void staticId(String staticId) {
- this.staticId = staticId
- }
-
- void variables(String[] variables) {
- this.variables = variables
- }
-
- void variable(String variable) {
- this.variables = [variable]
- }
- }
-
@Override
String getName() {
DynaWaltzProvider.NAME
}
- DynaWaltzCurve dynawoCurve(CurvesSpec curveSpec, Consumer consumer) {
-
- if (curveSpec.staticId && curveSpec.dynamicModelId) {
- throw new DslException("Both staticId and dynamicModelId are defined")
- }
- if (!curveSpec.variables) {
- throw new DslException("'variables' field is not set")
- }
- if (curveSpec.variables.length == 0) {
- throw new DslException("'variables' field is empty")
- }
-
- for (String variable : curveSpec.variables) {
- if (curveSpec.staticId) {
- consumer.accept(new DynaWaltzCurve("NETWORK", curveSpec.staticId + "_" + variable))
- } else {
- consumer.accept(new DynaWaltzCurve(curveSpec.dynamicModelId, variable))
- }
- }
- }
-
@Override
void load(Binding binding, Consumer consumer, ReportNode reportNode) {
- binding.curve = { Closure closure ->
- def cloned = closure.clone()
- CurvesSpec curveSpec = new CurvesSpec()
-
- cloned.delegate = curveSpec
- cloned()
-
- dynawoCurve(curveSpec, consumer)
- }
-
- binding.curves = { Closure closure ->
+ Closure closure = { Closure closure ->
def cloned = closure.clone()
- CurvesSpec curvesSpec = new CurvesSpec()
-
- cloned.delegate = curvesSpec
+ DynawoCurvesBuilder curvesBuilder = new DynawoCurvesBuilder(reportNode)
+ cloned.delegate = curvesBuilder
cloned()
-
- dynawoCurve(curvesSpec, consumer)
+ curvesBuilder.add(consumer)
}
+ binding.curve = closure
}
-
}
diff --git a/dynawaltz-dsl/src/test/java/com/powsybl/dynawaltz/dsl/DynaWaltzGroovyCurvesSupplierTest.java b/dynawaltz-dsl/src/test/java/com/powsybl/dynawaltz/dsl/DynaWaltzGroovyCurvesSupplierTest.java
index cd04ca96e..2b540613b 100644
--- a/dynawaltz-dsl/src/test/java/com/powsybl/dynawaltz/dsl/DynaWaltzGroovyCurvesSupplierTest.java
+++ b/dynawaltz-dsl/src/test/java/com/powsybl/dynawaltz/dsl/DynaWaltzGroovyCurvesSupplierTest.java
@@ -8,13 +8,12 @@
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
-import com.powsybl.dsl.DslException;
import com.powsybl.dynamicsimulation.Curve;
import com.powsybl.dynamicsimulation.CurvesSupplier;
import com.powsybl.dynamicsimulation.groovy.CurveGroovyExtension;
import com.powsybl.dynamicsimulation.groovy.GroovyCurvesSupplier;
import com.powsybl.dynamicsimulation.groovy.GroovyExtension;
-import com.powsybl.dynawaltz.DynaWaltzCurve;
+import com.powsybl.dynawaltz.curves.DynawoCurve;
import com.powsybl.dynawaltz.DynaWaltzProvider;
import com.powsybl.iidm.network.Generator;
import com.powsybl.iidm.network.Load;
@@ -23,9 +22,6 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
import java.nio.file.FileSystem;
@@ -33,14 +29,13 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
-import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author Marcos de Miguel {@literal }
*/
-class DynaWaltzGroovyCurvesSupplierTest {
+class DynaWaltzGroovyCurvesSupplierTest extends AbstractModelSupplierTest {
private FileSystem fileSystem;
private Network network;
@@ -51,9 +46,6 @@ void setup() throws IOException {
network = EurostagTutorialExample1Factory.create();
Files.copy(Objects.requireNonNull(getClass().getResourceAsStream("/curves.groovy")), fileSystem.getPath("/curves.groovy"));
- Files.copy(Objects.requireNonNull(getClass().getResourceAsStream("/curves_dynamicModelId_staticId.groovy")), fileSystem.getPath("/curves_dynamicModelId_staticId.groovy"));
- Files.copy(Objects.requireNonNull(getClass().getResourceAsStream("/curves_variable.groovy")), fileSystem.getPath("/curves_variable.groovy"));
- Files.copy(Objects.requireNonNull(getClass().getResourceAsStream("/curves_variables.groovy")), fileSystem.getPath("/curves_variables.groovy"));
}
@AfterEach
@@ -70,23 +62,6 @@ void test() {
curves.forEach(this::validateCurve);
}
- @ParameterizedTest(name = "{1}")
- @MethodSource("provideFileError")
- void testScriptError(String fileName, String error) {
- List extensions = validateGroovyExtension();
- CurvesSupplier supplier = new GroovyCurvesSupplier(fileSystem.getPath(fileName), extensions);
- DslException exception = assertThrows(DslException.class, () -> supplier.get(network));
- assertEquals(error, exception.getMessage());
- }
-
- private static Stream provideFileError() {
- return Stream.of(
- Arguments.of("/curves_dynamicModelId_staticId.groovy", "Both staticId and dynamicModelId are defined"),
- Arguments.of("/curves_variable.groovy", "'variables' field is not set"),
- Arguments.of("/curves_variables.groovy", "'variables' field is not set")
- );
- }
-
private List validateGroovyExtension() {
List extensions = GroovyExtension.find(CurveGroovyExtension.class, DynaWaltzProvider.NAME);
assertEquals(1, extensions.size());
@@ -95,8 +70,8 @@ private List validateGroovyExtension() {
}
private void validateCurve(Curve curve) {
- assertEquals(DynaWaltzCurve.class, curve.getClass());
- DynaWaltzCurve curveImpl = (DynaWaltzCurve) curve;
+ assertEquals(DynawoCurve.class, curve.getClass());
+ DynawoCurve curveImpl = (DynawoCurve) curve;
if (curveImpl.getModelId().equals("NETWORK")) {
assertTrue(Arrays.asList("NGEN_Upu_value", "NHV1_Upu_value", "NHV2_Upu_value", "NLOAD_Upu_value").contains(curveImpl.getVariable()));
} else if (network.getIdentifiable(curveImpl.getModelId()) instanceof Generator) {
diff --git a/dynawaltz-dsl/src/test/resources/curves.groovy b/dynawaltz-dsl/src/test/resources/curves.groovy
index 9e4de3021..49c11a42d 100644
--- a/dynawaltz-dsl/src/test/resources/curves.groovy
+++ b/dynawaltz-dsl/src/test/resources/curves.groovy
@@ -17,7 +17,7 @@ for (Bus bus : network.busBreakerView.buses) {
}
for (Generator gen : network.generators) {
- curves {
+ curve {
dynamicModelId gen.id
variables "generator_omegaPu", "generator_PGen", "generator_UStatorPU", "voltageRegulator_UcEfdP", "voltageRegulator_EfdPu"
}
diff --git a/dynawaltz-dsl/src/test/resources/curves_dynamicModelId_staticId.groovy b/dynawaltz-dsl/src/test/resources/curves_dynamicModelId_staticId.groovy
deleted file mode 100644
index 24d5c61f5..000000000
--- a/dynawaltz-dsl/src/test/resources/curves_dynamicModelId_staticId.groovy
+++ /dev/null
@@ -1,16 +0,0 @@
-/**
- * Copyright (c) 2020, RTE (http://www.rte-france.com)
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-import com.powsybl.iidm.network.Bus
-
-for (Bus bus : network.busBreakerView.buses) {
- curve {
- dynamicModelId "NETWORK"
- staticId bus.id
- variable "Upu_value"
- }
-}
diff --git a/dynawaltz-dsl/src/test/resources/curves_variable.groovy b/dynawaltz-dsl/src/test/resources/curves_variable.groovy
deleted file mode 100644
index a37d35c66..000000000
--- a/dynawaltz-dsl/src/test/resources/curves_variable.groovy
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * Copyright (c) 2020, RTE (http://www.rte-france.com)
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-import com.powsybl.iidm.network.Bus
-
-for (Bus bus : network.busBreakerView.buses) {
- curve {
- staticId bus.id
- }
-}
diff --git a/dynawaltz-dsl/src/test/resources/curves_variables.groovy b/dynawaltz-dsl/src/test/resources/curves_variables.groovy
deleted file mode 100644
index 7c293e4df..000000000
--- a/dynawaltz-dsl/src/test/resources/curves_variables.groovy
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * Copyright (c) 2020, RTE (http://www.rte-france.com)
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-import com.powsybl.iidm.network.Generator
-
-for (Generator gen : network.generators) {
- curves {
- dynamicModelId gen.id
- }
-}
diff --git a/dynawaltz-dsl/src/test/resources/ieee14-disconnectline/powsybl-inputs/curves.groovy b/dynawaltz-dsl/src/test/resources/ieee14-disconnectline/powsybl-inputs/curves.groovy
index 7bf0f8979..e7acd795b 100644
--- a/dynawaltz-dsl/src/test/resources/ieee14-disconnectline/powsybl-inputs/curves.groovy
+++ b/dynawaltz-dsl/src/test/resources/ieee14-disconnectline/powsybl-inputs/curves.groovy
@@ -17,7 +17,7 @@ for (Bus bus : network.busBreakerView.buses) {
}
for (Generator gen : network.generators) {
- curves {
+ curve {
dynamicModelId gen.id
variables "generator_omegaPu", "generator_PGen", "generator_QGen", "generator_UStatorPu", "voltageRegulator_EfdPu"
}
diff --git a/dynawaltz-dsl/src/test/resources/ieee14-macroconnects/powsybl-inputs/curves.groovy b/dynawaltz-dsl/src/test/resources/ieee14-macroconnects/powsybl-inputs/curves.groovy
index ae2e1aadd..a0e03268a 100644
--- a/dynawaltz-dsl/src/test/resources/ieee14-macroconnects/powsybl-inputs/curves.groovy
+++ b/dynawaltz-dsl/src/test/resources/ieee14-macroconnects/powsybl-inputs/curves.groovy
@@ -17,7 +17,7 @@ for (Bus bus : network.busBreakerView.buses) {
}
for (Generator gen : network.generators) {
- curves {
+ curve {
dynamicModelId gen.id
variables "generator_omegaPu", "generator_PGen", "generator_QGen", "generator_UStatorPu", "voltageRegulator_EfdPu"
}
diff --git a/dynawaltz-dsl/src/test/resources/ieee14-overloadmanagementsystem/powsybl-inputs/curves.groovy b/dynawaltz-dsl/src/test/resources/ieee14-overloadmanagementsystem/powsybl-inputs/curves.groovy
index 2bfb3a04a..bc49f3dfa 100644
--- a/dynawaltz-dsl/src/test/resources/ieee14-overloadmanagementsystem/powsybl-inputs/curves.groovy
+++ b/dynawaltz-dsl/src/test/resources/ieee14-overloadmanagementsystem/powsybl-inputs/curves.groovy
@@ -17,38 +17,38 @@ for (Bus bus : network.busBreakerView.buses) {
}
for (Generator gen : network.generators) {
- curves {
+ curve {
dynamicModelId gen.id
variables "generator_omegaPu", "generator_PGen", "generator_QGen", "generator_UStatorPu", "voltageRegulator_EfdPu"
}
}
-curves {
+curve {
dynamicModelId "_LOAD___2_EC"
variables "load_PPu", "load_QPu"
}
-curves {
+curve {
staticId "_BUS____2-BUS____4-1_AC"
variables "iSide2", "state"
}
-curves {
+curve {
dynamicModelId "CLA_2_4"
variables "currentLimitAutomaton_order", "currentLimitAutomaton_IMax"
}
-curves {
+curve {
staticId "_BUS____1-BUS____5-1_AC"
variables "iSide2", "state"
}
-curves {
+curve {
staticId "_BUS____2-BUS____5-1_AC"
variables "iSide2", "state"
}
-curves {
+curve {
dynamicModelId "CLA_2_5"
variables "currentLimitAutomaton_order", "currentLimitAutomaton_IMax"
}
diff --git a/dynawaltz-dsl/src/test/resources/ieee57-disconnectgenerator/powsybl-inputs/curves.groovy b/dynawaltz-dsl/src/test/resources/ieee57-disconnectgenerator/powsybl-inputs/curves.groovy
index fcec6a943..8d5dee563 100644
--- a/dynawaltz-dsl/src/test/resources/ieee57-disconnectgenerator/powsybl-inputs/curves.groovy
+++ b/dynawaltz-dsl/src/test/resources/ieee57-disconnectgenerator/powsybl-inputs/curves.groovy
@@ -19,7 +19,7 @@ for (Bus bus : network.busBreakerView.buses) {
}
for (Generator gen : network.generators) {
- curves {
+ curve {
dynamicModelId gen.id
variables "generator_omegaPu", "generator_PGen", "generator_QGen", "generator_UStatorPu"
}
diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzContext.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzContext.java
index 7db4b9572..96290715f 100644
--- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzContext.java
+++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzContext.java
@@ -10,6 +10,7 @@
import com.powsybl.commons.report.ReportNode;
import com.powsybl.dynamicsimulation.Curve;
import com.powsybl.dynamicsimulation.DynamicSimulationParameters;
+import com.powsybl.dynawaltz.curves.DynawoCurve;
import com.powsybl.dynawaltz.models.AbstractPureDynamicBlackBoxModel;
import com.powsybl.dynawaltz.models.BlackBoxModel;
import com.powsybl.dynawaltz.models.EquipmentBlackBoxModel;
@@ -54,7 +55,7 @@ public class DynaWaltzContext {
private final List dynamicModels;
private final List eventModels;
private final Map staticIdBlackBoxModelMap;
- private final List curves;
+ private final List curves;
private final Map macroStaticReferences = new LinkedHashMap<>();
private final List macroConnectList = new ArrayList<>();
private final Map macroConnectorsMap = new LinkedHashMap<>();
@@ -97,7 +98,10 @@ public DynaWaltzContext(Network network, String workingVariantId, List e.setEquipmentHasDynamicModel(this));
- this.curves = Objects.requireNonNull(curves);
+ this.curves = Objects.requireNonNull(curves).stream()
+ .filter(DynawoCurve.class::isInstance)
+ .map(DynawoCurve.class::cast)
+ .toList();
this.frequencySynchronizer = setupFrequencySynchronizer(dynamicModels.stream().anyMatch(AbstractBus.class::isInstance) ? SetPoint::new : OmegaRef::new);
this.macroConnectionsAdder = new MacroConnectionsAdder(this::getDynamicModel,
this::getPureDynamicModel,
@@ -256,8 +260,8 @@ public List getBlackBoxEventModels() {
return eventModels;
}
- public List getCurves() {
- return Collections.unmodifiableList(curves);
+ public List getCurves() {
+ return curves;
}
public boolean withCurves() {
diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/builders/BuilderReports.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/builders/BuilderReports.java
index 0e399d6fc..a9f210f25 100644
--- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/builders/BuilderReports.java
+++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/builders/BuilderReports.java
@@ -54,6 +54,14 @@ public static void reportModelInstantiationFailure(ReportNode reportNode, String
.add();
}
+ public static void reportCurveInstantiationFailure(ReportNode reportNode, String id) {
+ reportNode.newReportNode()
+ .withMessageTemplate("curveInstantiationError", "Curve ${id} cannot be instantiated")
+ .withUntypedValue("id", id)
+ .withSeverity(TypedValue.WARN_SEVERITY)
+ .add();
+ }
+
public static void reportFieldReplacement(ReportNode reportNode, String fieldName, String replacementName, String replacement) {
reportNode.newReportNode()
.withMessageTemplate("fieldReplacement", "'${fieldName}' field is not set, ${replacementName} ${replacement} will be used instead")
@@ -129,4 +137,12 @@ public static void reportFieldSetWithWrongEquipment(ReportNode reportNode, Strin
.add();
}
+ public static void reportFieldConflict(ReportNode reportNode, String firstFieldName, String secondFieldName) {
+ reportNode.newReportNode()
+ .withMessageTemplate("fieldConflict", "Both '${firstFieldName}' and '${secondFieldName}' are defined, '${firstFieldName}' will be used")
+ .withUntypedValue("firstFieldName", firstFieldName)
+ .withUntypedValue("secondFieldName", secondFieldName)
+ .withSeverity(TypedValue.TRACE_SEVERITY)
+ .add();
+ }
}
diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzCurve.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/curves/DynawoCurve.java
similarity index 83%
rename from dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzCurve.java
rename to dynawaltz/src/main/java/com/powsybl/dynawaltz/curves/DynawoCurve.java
index ebcc7bd2e..82e955b0b 100644
--- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzCurve.java
+++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/curves/DynawoCurve.java
@@ -4,8 +4,7 @@
* 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.dynawaltz;
+package com.powsybl.dynawaltz.curves;
import com.powsybl.dynamicsimulation.Curve;
@@ -14,12 +13,12 @@
/**
* @author Mathieu Bague {@literal }
*/
-public class DynaWaltzCurve implements Curve {
+public class DynawoCurve implements Curve {
private final String dynamicModelId;
private final String variable;
- public DynaWaltzCurve(String dynamicModelId, String variable) {
+ DynawoCurve(String dynamicModelId, String variable) {
this.dynamicModelId = Objects.requireNonNull(dynamicModelId);
this.variable = Objects.requireNonNull(variable);
}
diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/curves/DynawoCurvesBuilder.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/curves/DynawoCurvesBuilder.java
new file mode 100644
index 000000000..26d9437a4
--- /dev/null
+++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/curves/DynawoCurvesBuilder.java
@@ -0,0 +1,104 @@
+/**
+ * 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.curves;
+
+import com.powsybl.commons.report.ReportNode;
+import com.powsybl.dynamicsimulation.Curve;
+import com.powsybl.dynawaltz.builders.BuilderReports;
+
+import java.util.*;
+import java.util.function.Consumer;
+
+/**
+ * A curve for Dynawo
can be defined using {@code staticId} and {@code variable} or {@code dynamicModelId} and {@code variable}.
+ * Definition with {@code staticId} and {@code variable} are used when no explicit dynamic component exists.
+ * Dynawo
expects {@code dynamicModelId} = “NETWORK” for these variables.
+ * @author Laurent Issertial {@literal }
+ */
+public class DynawoCurvesBuilder {
+
+ private static final String DEFAULT_DYNAMIC_MODEL_ID = "NETWORK";
+
+ private final ReportNode reportNode;
+ private boolean isInstantiable = true;
+ private String dynamicModelId;
+ private String staticId;
+ private List variables;
+
+ public DynawoCurvesBuilder(ReportNode reportNode) {
+ this.reportNode = reportNode;
+ }
+
+ public DynawoCurvesBuilder() {
+ this(ReportNode.NO_OP);
+ }
+
+ public DynawoCurvesBuilder dynamicModelId(String dynamicModelId) {
+ this.dynamicModelId = dynamicModelId;
+ return this;
+ }
+
+ public DynawoCurvesBuilder staticId(String staticId) {
+ this.staticId = staticId;
+ return this;
+ }
+
+ public DynawoCurvesBuilder variables(String... variables) {
+ this.variables = List.of(variables);
+ return this;
+ }
+
+ public DynawoCurvesBuilder variables(List variables) {
+ this.variables = variables;
+ return this;
+ }
+
+ public DynawoCurvesBuilder variable(String variable) {
+ this.variables = List.of(variable);
+ return this;
+ }
+
+ private String getId() {
+ return dynamicModelId != null ? dynamicModelId : staticId;
+ }
+
+ private void checkData() {
+ if (staticId != null && dynamicModelId != null) {
+ BuilderReports.reportFieldConflict(reportNode, "dynamicModelId", "staticId");
+ }
+ if (variables == null) {
+ BuilderReports.reportFieldNotSet(reportNode, "variables");
+ isInstantiable = false;
+ } else if (variables.isEmpty()) {
+ BuilderReports.reportEmptyList(reportNode, "variables");
+ isInstantiable = false;
+ }
+ }
+
+ private boolean isInstantiable() {
+ checkData();
+ if (!isInstantiable) {
+ BuilderReports.reportCurveInstantiationFailure(reportNode, getId());
+ }
+ return isInstantiable;
+ }
+
+ public void add(Consumer curveConsumer) {
+ if (isInstantiable()) {
+ boolean hasDynamicModelId = dynamicModelId != null;
+ String id = hasDynamicModelId ? dynamicModelId : DEFAULT_DYNAMIC_MODEL_ID;
+ variables.forEach(v -> curveConsumer.accept(new DynawoCurve(id, hasDynamicModelId ? v : staticId + "_" + v)));
+ }
+ }
+
+ public List build() {
+ List curves = new ArrayList<>();
+ add(curves::add);
+ return curves;
+ }
+}
diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/curves/CurvesJsonDeserializer.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/curves/CurvesJsonDeserializer.java
new file mode 100644
index 000000000..a1b3d63e8
--- /dev/null
+++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/suppliers/curves/CurvesJsonDeserializer.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.curves;
+
+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.dynamicsimulation.Curve;
+import com.powsybl.dynawaltz.curves.DynawoCurvesBuilder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+/**
+ * @author Laurent Issertial {@literal }
+ */
+public class CurvesJsonDeserializer extends StdDeserializer> {
+
+ private final transient Supplier builderConstructor;
+
+ public CurvesJsonDeserializer(Supplier builderConstructor) {
+ super(List.class);
+ this.builderConstructor = builderConstructor;
+ }
+
+ public CurvesJsonDeserializer() {
+ this(DynawoCurvesBuilder::new);
+ }
+
+ @Override
+ public List deserialize(JsonParser parser, DeserializationContext context) {
+ List modelConfigList = new ArrayList<>();
+ JsonUtil.parseObject(parser, name -> {
+ if (name.equals("curves")) {
+ JsonUtil.parseObjectArray(parser, modelConfigList::add, this::parseCurvesBuilder);
+ return true;
+ }
+ return false;
+ });
+ return modelConfigList.stream()
+ .flatMap(b -> b.build().stream())
+ .filter(Objects::nonNull)
+ .toList();
+ }
+
+ private DynawoCurvesBuilder parseCurvesBuilder(JsonParser parser) {
+ DynawoCurvesBuilder curvesBuilder = builderConstructor.get();
+ JsonUtil.parseObject(parser, name -> {
+ boolean handled = true;
+ switch (name) {
+ case "dynamicModelId" -> curvesBuilder.dynamicModelId(parser.nextTextValue());
+ case "staticId" -> curvesBuilder.staticId(parser.nextTextValue());
+ case "variable" -> curvesBuilder.variable(parser.nextTextValue());
+ case "variables" -> curvesBuilder.variables(JsonUtil.parseStringArray(parser));
+ default -> handled = false;
+ }
+ return handled;
+ });
+ return curvesBuilder;
+ }
+}
diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/xml/CurvesXml.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/xml/CurvesXml.java
index 3fe6aec83..29d22f363 100644
--- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/xml/CurvesXml.java
+++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/xml/CurvesXml.java
@@ -6,9 +6,8 @@
*/
package com.powsybl.dynawaltz.xml;
-import com.powsybl.dynamicsimulation.Curve;
import com.powsybl.dynawaltz.DynaWaltzContext;
-import com.powsybl.dynawaltz.DynaWaltzCurve;
+import com.powsybl.dynawaltz.curves.DynawoCurve;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
@@ -33,8 +32,7 @@ public static void write(Path workingDir, DynaWaltzContext context) throws IOExc
@Override
public void write(XMLStreamWriter writer, DynaWaltzContext context) throws XMLStreamException {
- for (Curve curve : context.getCurves()) {
- DynaWaltzCurve dynCurve = (DynaWaltzCurve) curve;
+ for (DynawoCurve dynCurve : context.getCurves()) {
writer.writeEmptyElement(DYN_URI, "curve");
writer.writeAttribute("model", dynCurve.getModelId());
writer.writeAttribute("variable", dynCurve.getVariable());
diff --git a/dynawaltz/src/test/java/com/powsybl/dynawaltz/DynaWaltzCurveTest.java b/dynawaltz/src/test/java/com/powsybl/dynawaltz/DynaWaltzCurveTest.java
deleted file mode 100644
index 24c3a9fb3..000000000
--- a/dynawaltz/src/test/java/com/powsybl/dynawaltz/DynaWaltzCurveTest.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * Copyright (c) 2020, RTE (http://www.rte-france.com)
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-package com.powsybl.dynawaltz;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * @author Marcos de Miguel {@literal }
- */
-class DynaWaltzCurveTest {
-
- @Test
- void test() {
- DynaWaltzCurve curve = new DynaWaltzCurve("busId", "variable");
-
- assertEquals("busId", curve.getModelId());
- assertEquals("variable", curve.getVariable());
- }
-}
diff --git a/dynawaltz/src/test/java/com/powsybl/dynawaltz/DynaWaltzProviderTest.java b/dynawaltz/src/test/java/com/powsybl/dynawaltz/DynaWaltzProviderTest.java
index 68e334a32..31ecd3ccc 100644
--- a/dynawaltz/src/test/java/com/powsybl/dynawaltz/DynaWaltzProviderTest.java
+++ b/dynawaltz/src/test/java/com/powsybl/dynawaltz/DynaWaltzProviderTest.java
@@ -14,6 +14,7 @@
import com.powsybl.computation.local.LocalComputationConfig;
import com.powsybl.computation.local.LocalComputationManager;
import com.powsybl.dynamicsimulation.*;
+import com.powsybl.dynawaltz.curves.DynawoCurvesBuilder;
import com.powsybl.dynawo.commons.DynawoConstants;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.Substation;
@@ -46,6 +47,7 @@ class DynaWaltzProviderTest extends AbstractSerDeTest {
private DynaWaltzConfig config;
@BeforeEach
+ @Override
public void setUp() throws IOException {
super.setUp();
config = DynaWaltzConfig.load();
@@ -54,7 +56,7 @@ public void setUp() throws IOException {
public static class CurvesSupplierMock implements CurvesSupplier {
@Override
public List get(Network network, ReportNode reportNode) {
- return Collections.singletonList(new DynaWaltzCurve("bus", "uPu"));
+ return new DynawoCurvesBuilder().dynamicModelId("bus").variable("uPu").build();
}
}
diff --git a/dynawaltz/src/test/java/com/powsybl/dynawaltz/builders/CurvesBuilderTest.java b/dynawaltz/src/test/java/com/powsybl/dynawaltz/builders/CurvesBuilderTest.java
new file mode 100644
index 000000000..189862a8e
--- /dev/null
+++ b/dynawaltz/src/test/java/com/powsybl/dynawaltz/builders/CurvesBuilderTest.java
@@ -0,0 +1,102 @@
+package com.powsybl.dynawaltz.builders;
+
+import com.powsybl.commons.report.ReportNode;
+import com.powsybl.commons.test.TestUtil;
+import com.powsybl.dynamicsimulation.Curve;
+import com.powsybl.dynawaltz.curves.DynawoCurve;
+import com.powsybl.dynawaltz.curves.DynawoCurvesBuilder;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class CurvesBuilderTest {
+
+ private ReportNode reporter;
+
+ @BeforeEach
+ void setup() {
+ reporter = ReportNode.newRootReportNode().withMessageTemplate("builderTests", "Builder tests").build();
+ }
+
+ @Test
+ void buildFromDynamicId() {
+ List curveList = new DynawoCurvesBuilder()
+ .dynamicModelId("BBM_GEN")
+ .variable("generator_omegaPu")
+ .build();
+ assertEquals(1, curveList.size());
+ DynawoCurve curve = (DynawoCurve) curveList.get(0);
+ assertEquals("BBM_GEN", curve.getModelId());
+ assertEquals("generator_omegaPu", curve.getVariable());
+ }
+
+ @Test
+ void buildFromStaticId() {
+ List curveList = new DynawoCurvesBuilder()
+ .staticId("GEN")
+ .variables("generator_omegaPu", "generator_PGen")
+ .build();
+ assertEquals(2, curveList.size());
+ DynawoCurve curve = (DynawoCurve) curveList.get(0);
+ assertEquals("NETWORK", curve.getModelId());
+ assertEquals("GEN_generator_omegaPu", curve.getVariable());
+ }
+
+ @ParameterizedTest(name = "{1}")
+ @MethodSource("provideBuilderError")
+ void testScriptError(Function builderFunction, boolean isInstantiable, String report) throws IOException {
+ boolean hasInstance = !builderFunction.apply(reporter).build().isEmpty();
+ assertEquals(isInstantiable, hasInstance);
+ checkReportNode(report);
+ }
+
+ private static Stream provideBuilderError() {
+ return Stream.of(
+ Arguments.of((Function) r ->
+ new DynawoCurvesBuilder(r)
+ .staticId("GEN")
+ .dynamicModelId("BBM_GEN")
+ .variable("uPu"),
+ true,
+ """
+ + Builder tests
+ Both 'dynamicModelId' and 'staticId' are defined, 'dynamicModelId' will be used
+ """),
+ Arguments.of((Function) r ->
+ new DynawoCurvesBuilder(r)
+ .staticId("GEN"),
+ false,
+ """
+ + Builder tests
+ 'variables' field is not set
+ Curve GEN cannot be instantiated
+ """),
+ Arguments.of((Function) r ->
+ new DynawoCurvesBuilder(r)
+ .staticId("GEN")
+ .variables(),
+ false,
+ """
+ + Builder tests
+ 'variables' list is empty
+ Curve GEN cannot be instantiated
+ """)
+ );
+ }
+
+ private void checkReportNode(String report) throws IOException {
+ StringWriter sw = new StringWriter();
+ reporter.print(sw);
+ assertEquals(report, TestUtil.normalizeLineSeparator(sw.toString()));
+ }
+}
diff --git a/dynawaltz/src/test/java/com/powsybl/dynawaltz/suppliers/DynawoCurvesJsonDeserializerTest.java b/dynawaltz/src/test/java/com/powsybl/dynawaltz/suppliers/DynawoCurvesJsonDeserializerTest.java
new file mode 100644
index 000000000..83f3cdd58
--- /dev/null
+++ b/dynawaltz/src/test/java/com/powsybl/dynawaltz/suppliers/DynawoCurvesJsonDeserializerTest.java
@@ -0,0 +1,44 @@
+/**
+ * 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.dynamicsimulation.Curve;
+import com.powsybl.dynawaltz.curves.DynawoCurvesBuilder;
+import com.powsybl.dynawaltz.suppliers.curves.CurvesJsonDeserializer;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Laurent Issertial {@literal }
+ */
+class DynawoCurvesJsonDeserializerTest {
+
+ @Test
+ void testCurvesSupplier() throws IOException {
+ try (InputStream is = getClass().getResourceAsStream("/suppliers/curves.json")) {
+ List curves = new SupplierJsonDeserializer<>(new CurvesJsonDeserializer()).deserialize(is);
+ assertThat(curves).usingRecursiveFieldByFieldElementComparatorOnFields()
+ .containsExactlyInAnyOrderElementsOf(getExpectedCurves());
+ }
+ }
+
+ private static List getExpectedCurves() {
+ List curves = new ArrayList<>();
+ new DynawoCurvesBuilder().dynamicModelId("BBM_GEN").variables("voltageRegulator_EfdPu").add(curves::add);
+ new DynawoCurvesBuilder().staticId("BUS").variables("Upu_value").add(curves::add);
+ new DynawoCurvesBuilder().dynamicModelId("BBM_GEN2").variables("generator_omegaPu", "generator_PGen", "generator_UStatorPU").add(curves::add);
+ new DynawoCurvesBuilder().staticId("LOAD").variables("load_PPu", "load_QPu").add(curves::add);
+ return curves;
+ }
+}
diff --git a/dynawaltz/src/test/java/com/powsybl/dynawaltz/xml/CurvesXmlTest.java b/dynawaltz/src/test/java/com/powsybl/dynawaltz/xml/CurvesXmlTest.java
index e3b3e9559..eecc62f9e 100644
--- a/dynawaltz/src/test/java/com/powsybl/dynawaltz/xml/CurvesXmlTest.java
+++ b/dynawaltz/src/test/java/com/powsybl/dynawaltz/xml/CurvesXmlTest.java
@@ -12,7 +12,6 @@
import org.junit.jupiter.api.Test;
import org.xml.sax.SAXException;
-import javax.xml.stream.XMLStreamException;
import java.io.IOException;
/**
@@ -21,7 +20,7 @@
class CurvesXmlTest extends DynaWaltzTestUtil {
@Test
- void writeCurve() throws SAXException, IOException, XMLStreamException {
+ void writeCurve() throws SAXException, IOException {
DynamicSimulationParameters parameters = DynamicSimulationParameters.load();
DynaWaltzParameters dynawoParameters = DynaWaltzParameters.load();
DynaWaltzContext context = new DynaWaltzContext(network, network.getVariantManager().getWorkingVariantId(), dynamicModels, eventModels, curves, parameters, dynawoParameters);
diff --git a/dynawaltz/src/test/java/com/powsybl/dynawaltz/xml/DynaWaltzTestUtil.java b/dynawaltz/src/test/java/com/powsybl/dynawaltz/xml/DynaWaltzTestUtil.java
index b57d27a61..47fe34d6c 100644
--- a/dynawaltz/src/test/java/com/powsybl/dynawaltz/xml/DynaWaltzTestUtil.java
+++ b/dynawaltz/src/test/java/com/powsybl/dynawaltz/xml/DynaWaltzTestUtil.java
@@ -8,7 +8,7 @@
import com.powsybl.commons.test.AbstractSerDeTest;
import com.powsybl.dynamicsimulation.Curve;
-import com.powsybl.dynawaltz.DynaWaltzCurve;
+import com.powsybl.dynawaltz.curves.DynawoCurvesBuilder;
import com.powsybl.dynawaltz.models.BlackBoxModel;
import com.powsybl.dynawaltz.models.automationsystems.overloadmanagments.DynamicOverloadManagementSystemBuilder;
import com.powsybl.dynawaltz.models.events.EventDisconnectionBuilder;
@@ -55,17 +55,17 @@ void setup() {
network = createEurostagTutorialExample1WithMoreLoads();
curves = new ArrayList<>();
- network.getBusBreakerView().getBusStream().forEach(b -> curves.add(new DynaWaltzCurve("NETWORK", b.getId() + "_Upu_value")));
+ network.getBusBreakerView().getBusStream().forEach(b -> new DynawoCurvesBuilder()
+ .staticId(b.getId())
+ .variables("Upu_value")
+ .add(curves::add));
// A curve is made up of the id of the dynamic model and the variable to plot.
// The static id of the generator is used as the id of the dynamic model (dynamicModelId).
- network.getGeneratorStream().forEach(g -> {
- curves.add(new DynaWaltzCurve(g.getId(), "generator_omegaPu"));
- curves.add(new DynaWaltzCurve(g.getId(), "generator_PGen"));
- curves.add(new DynaWaltzCurve(g.getId(), "generator_UStatorPu"));
- curves.add(new DynaWaltzCurve(g.getId(), "voltageRegulator_UcEfdP"));
- curves.add(new DynaWaltzCurve(g.getId(), "voltageRegulator_EfdPu"));
- });
+ network.getGeneratorStream().forEach(g -> new DynawoCurvesBuilder()
+ .dynamicModelId(g.getId())
+ .variables("generator_omegaPu", "generator_PGen", "generator_UStatorPu", "voltageRegulator_UcEfdP", "voltageRegulator_EfdPu")
+ .add(curves::add));
// Dynamic Models
dynamicModels = new ArrayList<>();
diff --git a/dynawaltz/src/test/resources/suppliers/curves.json b/dynawaltz/src/test/resources/suppliers/curves.json
new file mode 100644
index 000000000..3b89f124c
--- /dev/null
+++ b/dynawaltz/src/test/resources/suppliers/curves.json
@@ -0,0 +1,20 @@
+{
+ "curves":[
+ {
+ "dynamicModelId": "BBM_GEN",
+ "variable": "voltageRegulator_EfdPu"
+ },
+ {
+ "staticId": "BUS",
+ "variable": "Upu_value"
+ },
+ {
+ "dynamicModelId": "BBM_GEN2",
+ "variables": ["generator_omegaPu", "generator_PGen", "generator_UStatorPU"]
+ },
+ {
+ "staticId": "LOAD",
+ "variables": ["load_PPu", "load_QPu"]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/dynawo-integration-tests/src/test/resources/ieee14/disconnectline/curves.groovy b/dynawo-integration-tests/src/test/resources/ieee14/disconnectline/curves.groovy
index 7bf0f8979..e7acd795b 100644
--- a/dynawo-integration-tests/src/test/resources/ieee14/disconnectline/curves.groovy
+++ b/dynawo-integration-tests/src/test/resources/ieee14/disconnectline/curves.groovy
@@ -17,7 +17,7 @@ for (Bus bus : network.busBreakerView.buses) {
}
for (Generator gen : network.generators) {
- curves {
+ curve {
dynamicModelId gen.id
variables "generator_omegaPu", "generator_PGen", "generator_QGen", "generator_UStatorPu", "voltageRegulator_EfdPu"
}
diff --git a/dynawo-integration-tests/src/test/resources/smib/curves.groovy b/dynawo-integration-tests/src/test/resources/smib/curves.groovy
index b74db84ac..f6989320f 100644
--- a/dynawo-integration-tests/src/test/resources/smib/curves.groovy
+++ b/dynawo-integration-tests/src/test/resources/smib/curves.groovy
@@ -8,19 +8,19 @@
import com.powsybl.iidm.network.Line
-curves {
+curve {
dynamicModelId "sm"
variables "generator_cePu", "generator_PePu", "generator_cmPu", "generator_PmPu", "generator_ufPu", "generator_efdPu", "generator_omegaPu", "generator_theta", "generator_PGenPu", "generator_QGenPu", "generator_PGen", "generator_QGen", "generator_UPu", "generator_IStatorPu", "generator_IRotorPu", "generator_UStatorPu", "generator_QStatorPu", "generator_thetaInternal", "governor_PmRefPu"
}
for (Line line : network.lines) {
- curves {
+ curve {
dynamicModelId line.id
variables "line_P1Pu", "line_P2Pu", "line_Q1Pu", "line_Q2Pu"
}
}
-curves {
+curve {
dynamicModelId "tfo"
variables "transformer_terminal1_V_re", "transformer_terminal1_V_im", "transformer_terminal2_V_re", "transformer_terminal2_V_im", "transformer_terminal1_i_re", "transformer_terminal1_i_im", "transformer_terminal2_i_re", "transformer_terminal2_i_im"
}
\ No newline at end of file