Skip to content

Commit f116f31

Browse files
authored
Adapt and export shared parameters for DMC - Functional implementation (#157)
Shared parameters with DMC : Dynamic model evaluated from a mapping and a network Dynawo parameters which contains solver params, network params, parameters sets --------- Signed-off-by: Thang PHAM <phamthang37@gmail.com>
1 parent a1a0e4e commit f116f31

File tree

16 files changed

+818
-76
lines changed

16 files changed

+818
-76
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* Copyright (c) 2026, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
package com.powsybl.dynawo.suppliers.dynamicmodels;
9+
10+
import com.fasterxml.jackson.databind.ObjectMapper;
11+
import com.fasterxml.jackson.databind.module.SimpleModule;
12+
import com.powsybl.commons.json.JsonUtil;
13+
14+
import java.util.List;
15+
16+
/**
17+
* @author Thang PHAM <quyet-thang.pham at rte-france.com>
18+
*/
19+
public final class DynamicModelConfigJsonUtils {
20+
21+
private DynamicModelConfigJsonUtils() {
22+
throw new AssertionError("Utility class should not be instantiated");
23+
}
24+
25+
public static ObjectMapper createObjectMapper() {
26+
ObjectMapper mapper = JsonUtil.createObjectMapper();
27+
28+
SimpleModule module = new SimpleModule("dynamic-model-configs");
29+
module.addSerializer(new DynamicModelConfigsJsonSerializer());
30+
module.addDeserializer(List.class, new DynamicModelConfigsJsonDeserializer());
31+
32+
mapper.registerModule(module);
33+
return mapper;
34+
}
35+
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/**
2+
* Copyright (c) 2026, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
package com.powsybl.dynawo.suppliers.dynamicmodels;
9+
10+
import com.fasterxml.jackson.core.JsonGenerator;
11+
import com.fasterxml.jackson.databind.SerializerProvider;
12+
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
13+
import com.powsybl.dynawo.suppliers.Property;
14+
import com.powsybl.dynawo.suppliers.PropertyType;
15+
import com.powsybl.iidm.network.TwoSides;
16+
17+
import java.io.IOException;
18+
import java.util.List;
19+
20+
/**
21+
* Serialize a List<DynamicModelConfig> with "models" as root:
22+
*
23+
* {
24+
* "models": [
25+
* {
26+
* "model": "...",
27+
* "group": "...",
28+
* "groupType": "FIXED",
29+
* "properties": [ ... ]
30+
* }
31+
* ]
32+
* }
33+
*
34+
* This matches {@link DynamicModelConfigsJsonDeserializer}.
35+
*
36+
* TODO : to remove when available at powsybl-dynawo
37+
*
38+
* @author Thang PHAM <quyet-thang.pham at rte-france.com>
39+
*/
40+
public class DynamicModelConfigsJsonSerializer extends StdSerializer<List<DynamicModelConfig>> {
41+
42+
public DynamicModelConfigsJsonSerializer() {
43+
super((Class<List<DynamicModelConfig>>) (Class<?>) List.class);
44+
}
45+
46+
@Override
47+
public void serialize(List<DynamicModelConfig> configs,
48+
JsonGenerator gen,
49+
SerializerProvider provider) throws IOException {
50+
51+
gen.writeStartObject();
52+
53+
gen.writeFieldName("models");
54+
writeDynamicModelConfigs(configs, gen);
55+
56+
gen.writeEndObject();
57+
}
58+
59+
private static void writeDynamicModelConfigs(List<DynamicModelConfig> configs, JsonGenerator gen) throws IOException {
60+
gen.writeStartArray();
61+
if (configs != null) {
62+
for (DynamicModelConfig cfg : configs) {
63+
writeDynamicModelConfig(cfg, gen);
64+
}
65+
}
66+
gen.writeEndArray();
67+
}
68+
69+
private static void writeDynamicModelConfig(DynamicModelConfig cfg,
70+
JsonGenerator gen) throws IOException {
71+
72+
gen.writeStartObject();
73+
74+
gen.writeStringField("model", cfg.model());
75+
gen.writeStringField("group", cfg.group());
76+
77+
if (cfg.groupType() != null) {
78+
gen.writeStringField("groupType", cfg.groupType().name());
79+
}
80+
81+
gen.writeFieldName("properties");
82+
writeProperties(cfg.properties(), gen);
83+
84+
gen.writeEndObject();
85+
}
86+
87+
private static void writeProperties(List<Property> properties, JsonGenerator gen) throws IOException {
88+
gen.writeStartArray();
89+
if (properties != null) {
90+
for (Property p : properties) {
91+
writeProperty(p, gen);
92+
}
93+
}
94+
gen.writeEndArray();
95+
}
96+
97+
/**
98+
* See {@code PropertyParserUtils.parseProperty}:
99+
* - always writes "name"
100+
* - writes exactly one of: "value" (string value), "values" (string array), "arrays" (array of string arrays)
101+
* - writes "type" when it can be inferred from propertyClass
102+
*/
103+
private static void writeProperty(Property property, JsonGenerator gen) throws IOException {
104+
gen.writeStartObject();
105+
106+
gen.writeStringField("name", property.name());
107+
108+
Object value = property.value();
109+
if (value instanceof List<?> list) {
110+
if (!list.isEmpty()) {
111+
// lists: List<String|int|double|boolean|TwoSides>
112+
gen.writeFieldName("values");
113+
gen.writeStartArray();
114+
for (Object v : list) {
115+
gen.writeString(String.valueOf(v));
116+
}
117+
gen.writeEndArray();
118+
writeOptionalType(gen, property);
119+
}
120+
} else if (value != null && value.getClass().isArray()) {
121+
// arrays: List<List<String|integer|double|boolean|TwoSides>>
122+
gen.writeFieldName("arrays");
123+
gen.writeStartArray();
124+
for (List<?> row : (List<?>[]) value) {
125+
gen.writeStartArray();
126+
for (Object v : row) {
127+
gen.writeString(String.valueOf(v));
128+
}
129+
gen.writeEndArray();
130+
}
131+
gen.writeEndArray();
132+
writeOptionalType(gen, property);
133+
} else {
134+
if (value instanceof TwoSides ts) {
135+
gen.writeStringField("value", ts.name());
136+
writeOptionalType(gen, property);
137+
} else if (value != null) {
138+
gen.writeStringField("value", String.valueOf(value));
139+
writeOptionalType(gen, property);
140+
}
141+
}
142+
143+
gen.writeEndObject();
144+
}
145+
146+
/**
147+
* Writes the "type" field if we can (or if Property already carries an explicit type via propertyClass()).
148+
*
149+
* This keeps compatibility with PropertyParserUtils:
150+
* case "type" -> builder.type(PropertyType.valueOf(parser.nextTextValue()));
151+
*/
152+
private static void writeOptionalType(JsonGenerator gen, Property property) throws IOException {
153+
PropertyType inferredType = PropertyType.STRING;
154+
155+
Class<?> propertyClass = property.propertyClass();
156+
if (propertyClass != null) {
157+
if (propertyClass == double.class) {
158+
inferredType = PropertyType.DOUBLE;
159+
} else if (propertyClass == int.class) {
160+
inferredType = PropertyType.INTEGER;
161+
} else if (propertyClass == boolean.class) {
162+
inferredType = PropertyType.BOOLEAN;
163+
} else if (propertyClass == TwoSides.class) {
164+
inferredType = PropertyType.TWO_SIDES;
165+
}
166+
}
167+
168+
gen.writeStringField("type", inferredType.name());
169+
}
170+
}

src/main/java/org/gridsuite/ds/server/controller/DynamicSimulationController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ public ResponseEntity<Resource> downloadDebugFile(@Parameter(description = "Resu
205205
public ResponseEntity<List<DynamicModelConfig>> exportDynamicModel(@PathVariable("networkUuid") UUID networkUuid,
206206
@RequestParam(name = "variantId", required = false) String variantId,
207207
@RequestParam(name = "mappingName") String mappingName) {
208-
List<DynamicModelConfig> dynamicModelConfigList = dynamicSimulationService.exportDynamicModel(networkUuid, variantId, mappingName);
208+
List<DynamicModelConfig> dynamicModelConfigList = parametersService.getDynamicModel(mappingName, networkUuid, variantId);
209209
return CollectionUtils.isNotEmpty(dynamicModelConfigList) ?
210210
ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(dynamicModelConfigList) :
211211
ResponseEntity.noContent().build();
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Copyright (c) 2026, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
package org.gridsuite.ds.server.controller;
8+
9+
import io.swagger.v3.oas.annotations.Operation;
10+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
11+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
12+
import io.swagger.v3.oas.annotations.tags.Tag;
13+
import org.gridsuite.ds.server.DynamicSimulationApi;
14+
import org.gridsuite.ds.server.dto.DynamicSimulationParametersInfos;
15+
import org.gridsuite.ds.server.dto.DynamicSimulationParametersValues;
16+
import org.gridsuite.ds.server.service.parameters.ParametersService;
17+
import org.springframework.http.MediaType;
18+
import org.springframework.http.ResponseEntity;
19+
import org.springframework.web.bind.annotation.*;
20+
21+
import java.util.Optional;
22+
import java.util.UUID;
23+
24+
25+
/**
26+
* @author Thang PHAM <quyet-thang.pham at rte-france.com>
27+
*/
28+
@RestController
29+
@RequestMapping(value = "/" + DynamicSimulationApi.API_VERSION + "/parameters")
30+
@Tag(name = "Dynamic simulation server - Parameters")
31+
public class DynamicSimulationParametersController {
32+
33+
private final ParametersService parametersService;
34+
35+
public DynamicSimulationParametersController(ParametersService parametersService) {
36+
this.parametersService = parametersService;
37+
}
38+
39+
@PostMapping(value = "/values", produces = MediaType.APPLICATION_JSON_VALUE)
40+
@Operation(summary = "Get the dynamic simulation parameters values")
41+
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "The dynamic simulation parameters values"),
42+
@ApiResponse(responseCode = "404", description = "The dynamic simulation parameters has not been found")})
43+
public ResponseEntity<DynamicSimulationParametersValues> getParametersValues(@RequestParam(name = "networkUuid") UUID networkUuid,
44+
@RequestParam(name = "variantId", required = false) String variantId,
45+
@RequestBody DynamicSimulationParametersInfos parameters) {
46+
DynamicSimulationParametersValues parametersValues = parametersService.getParametersValues(parameters, networkUuid, variantId);
47+
return ResponseEntity.of(Optional.ofNullable(parametersValues));
48+
}
49+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) 2026, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
package org.gridsuite.ds.server.dto;
8+
9+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
10+
import com.fasterxml.jackson.annotation.JsonInclude;
11+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
12+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
13+
import com.powsybl.dynawo.DynawoSimulationParameters;
14+
import com.powsybl.dynawo.suppliers.dynamicmodels.DynamicModelConfig;
15+
import com.powsybl.dynawo.suppliers.dynamicmodels.DynamicModelConfigsJsonDeserializer;
16+
import com.powsybl.dynawo.suppliers.dynamicmodels.DynamicModelConfigsJsonSerializer;
17+
import lombok.*;
18+
19+
import java.util.List;
20+
21+
/**
22+
* @author Thang PHAM <quyet-thang.pham at rte-france.com>
23+
*/
24+
@NoArgsConstructor
25+
@AllArgsConstructor
26+
@Getter
27+
@Setter
28+
@Builder
29+
@JsonIgnoreProperties(ignoreUnknown = true)
30+
public class DynamicSimulationParametersValues {
31+
@JsonSerialize(using = DynamicModelConfigsJsonSerializer.class)
32+
@JsonDeserialize(using = DynamicModelConfigsJsonDeserializer.class)
33+
@JsonInclude(JsonInclude.Include.NON_EMPTY)
34+
List<DynamicModelConfig> dynamicModel;
35+
36+
@JsonInclude(JsonInclude.Include.NON_EMPTY)
37+
DynawoSimulationParameters dynawoParameters;
38+
}

src/main/java/org/gridsuite/ds/server/service/DynamicSimulationService.java

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,13 @@
88

99
import com.fasterxml.jackson.databind.ObjectMapper;
1010
import com.powsybl.dynamicsimulation.DynamicSimulationProvider;
11-
import com.powsybl.dynawo.suppliers.dynamicmodels.DynamicModelConfig;
12-
import com.powsybl.iidm.network.Network;
13-
import com.powsybl.iidm.network.VariantManagerConstants;
14-
import com.powsybl.network.store.client.NetworkStoreService;
15-
import com.powsybl.network.store.client.PreloadingStrategy;
16-
import org.apache.commons.lang3.StringUtils;
1711
import org.gridsuite.computation.s3.ComputationS3Service;
1812
import org.gridsuite.computation.service.AbstractComputationService;
1913
import org.gridsuite.computation.service.NotificationService;
2014
import org.gridsuite.computation.service.UuidGeneratorService;
2115
import org.gridsuite.ds.server.dto.DynamicSimulationStatus;
22-
import org.gridsuite.ds.server.dto.dynamicmapping.InputMapping;
23-
import org.gridsuite.ds.server.service.client.dynamicmapping.DynamicMappingClient;
2416
import org.gridsuite.ds.server.service.contexts.DynamicSimulationResultContext;
2517
import org.gridsuite.ds.server.service.contexts.DynamicSimulationRunContext;
26-
import org.gridsuite.ds.server.service.parameters.ParametersService;
2718
import org.springframework.beans.factory.annotation.Value;
2819
import org.springframework.messaging.Message;
2920
import org.springframework.stereotype.Service;
@@ -38,24 +29,14 @@
3829
public class DynamicSimulationService extends AbstractComputationService<DynamicSimulationRunContext, DynamicSimulationResultService, DynamicSimulationStatus> {
3930
public static final String COMPUTATION_TYPE = "dynamic simulation";
4031

41-
private final ParametersService parametersService;
42-
private final DynamicMappingClient dynamicMappingClient;
43-
private final NetworkStoreService networkStoreService;
44-
4532
public DynamicSimulationService(
4633
NotificationService notificationService,
4734
ObjectMapper objectMapper,
4835
UuidGeneratorService uuidGeneratorService,
4936
DynamicSimulationResultService dynamicSimulationResultService,
5037
ComputationS3Service computationS3Service,
51-
@Value("${dynamic-simulation.default-provider}") String defaultProvider,
52-
ParametersService parametersService,
53-
DynamicMappingClient dynamicMappingClient,
54-
NetworkStoreService networkStoreService) {
38+
@Value("${dynamic-simulation.default-provider}") String defaultProvider) {
5539
super(notificationService, dynamicSimulationResultService, computationS3Service, objectMapper, uuidGeneratorService, defaultProvider);
56-
this.parametersService = parametersService;
57-
this.dynamicMappingClient = dynamicMappingClient;
58-
this.networkStoreService = networkStoreService;
5940
}
6041

6142
@Override
@@ -76,13 +57,4 @@ public List<String> getProviders() {
7657
.toList();
7758
}
7859

79-
public List<DynamicModelConfig> exportDynamicModel(UUID networkUuid, String variantId, String mappingName) {
80-
81-
InputMapping inputMapping = dynamicMappingClient.getMapping(mappingName);
82-
Network network = networkStoreService.getNetwork(networkUuid, PreloadingStrategy.COLLECTION);
83-
String variant = StringUtils.isBlank(variantId) ? VariantManagerConstants.INITIAL_VARIANT_ID : variantId;
84-
network.getVariantManager().setWorkingVariant(variant);
85-
86-
return parametersService.getDynamicModel(inputMapping, network);
87-
}
8860
}

src/main/java/org/gridsuite/ds/server/service/DynamicSimulationWorkerService.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.powsybl.dynawo.DynawoSimulationParameters;
2121
import com.powsybl.dynawo.DynawoSimulationProvider;
2222
import com.powsybl.dynawo.suppliers.dynamicmodels.DynamicModelConfig;
23+
import com.powsybl.dynawo.suppliers.dynamicmodels.DynamicModelConfigJsonUtils;
2324
import com.powsybl.dynawo.suppliers.dynamicmodels.DynawoModelsSupplier;
2425
import com.powsybl.dynawo.suppliers.events.DynawoEventModelsSupplier;
2526
import com.powsybl.dynawo.suppliers.events.EventModelConfig;
@@ -331,7 +332,8 @@ private byte[] zipParameters(DynamicSimulationParameters parameters) {
331332
private byte[] zipDynamicModel(List<DynamicModelConfig> dynamicModelContent) {
332333
byte[] zippedJsonDynamicModelContent;
333334
try {
334-
String jsonDynamicModelContent = objectMapper.writeValueAsString(dynamicModelContent);
335+
// cannot use global shared object mapper for List<DynamicModelConfig> because the Deserializer/Serializer write as an object with "models" root
336+
String jsonDynamicModelContent = DynamicModelConfigJsonUtils.createObjectMapper().writeValueAsString(dynamicModelContent);
335337
zippedJsonDynamicModelContent = Utils.zip(jsonDynamicModelContent);
336338
} catch (IOException e) {
337339
throw new UncheckedIOException("Error occurred while zipping the dynamic model", e);

0 commit comments

Comments
 (0)