Skip to content

Commit

Permalink
Dynamic simulation results (#2788)
Browse files Browse the repository at this point in the history
* Add a reporter to dynamic simulation and its models suppliers
* Add reporter export
* Add unit tests
* Refactor DynamicSimulationResult: 
  - add status enum
  - add status text
  - remove logs
  - replace StringTimesSeries with TimelineEvent
  - replace TimeSeries with DoubleTimeSeries for curves

Signed-off-by: lisrte <[email protected]>
  • Loading branch information
Lisrte authored Dec 5, 2023
1 parent 37389f0 commit df8015f
Show file tree
Hide file tree
Showing 15 changed files with 201 additions and 170 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,53 @@
*/
package com.powsybl.dynamicsimulation;

import java.util.Collections;
import java.util.List;
import java.util.Map;

import com.powsybl.timeseries.RegularTimeSeriesIndex;
import com.powsybl.timeseries.StringTimeSeries;
import com.powsybl.timeseries.TimeSeries;
import com.powsybl.timeseries.DoubleTimeSeries;

import static com.powsybl.dynamicsimulation.DynamicSimulationResult.Status.SUCCESS;

/**
* @author Marcos de Miguel {@literal <demiguelm at aia.es>}
* @author Laurent Issertial {@literal <laurent.issertial at rte-france.com>}
*/
public interface DynamicSimulationResult {

boolean isOk();
enum Status {
SUCCESS,
FAILURE
}

Status getStatus();

String getStatusText();

String getLogs();
/**
* @deprecated use DynamicSimulationResult.Status instead
*/
@Deprecated(since = "6.1.0")
default boolean isOk() {
return SUCCESS == getStatus();
}

Map<String, TimeSeries> getCurves();
Map<String, DoubleTimeSeries> getCurves();

TimeSeries getCurve(String curve);
default DoubleTimeSeries getCurve(String curve) {
return getCurves().get(curve);
}

/**
* The Timeline contains information about relevant events that may have happened during the time domain simulation.
*/
StringTimeSeries getTimeLine();
List<TimelineEvent> getTimeLine();

/**
* Returns an empty timeline.
* @return an empty timeline
*/
static StringTimeSeries emptyTimeLine() {
return TimeSeries.createString("timeLine", new RegularTimeSeriesIndex(0, 0, 0));
static List<TimelineEvent> emptyTimeLine() {
return Collections.emptyList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,56 @@
package com.powsybl.dynamicsimulation;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import com.powsybl.timeseries.StringTimeSeries;
import com.powsybl.timeseries.TimeSeries;
import com.powsybl.timeseries.DoubleTimeSeries;

/**
* @author Marcos de Miguel {@literal <demiguelm at aia.es>}
* @author Laurent Issertial {@literal <laurent.issertial at rte-france.com>}
*/
public class DynamicSimulationResultImpl implements DynamicSimulationResult {

private final boolean status;
private final String logs;
private final Map<String, TimeSeries> curves;
private final StringTimeSeries timeLine;
private final Status status;
private final String statusText;
private final Map<String, DoubleTimeSeries> curves;
private final List<TimelineEvent> timeLine;

public DynamicSimulationResultImpl(boolean status, String logs, Map<String, TimeSeries> curves, StringTimeSeries timeLine) {
this.status = status;
this.logs = logs;
public DynamicSimulationResultImpl(Status status, String statusText, Map<String, DoubleTimeSeries> curves, List<TimelineEvent> timeLine) {
this.status = Objects.requireNonNull(status);
this.statusText = Objects.requireNonNull(statusText);
this.curves = Objects.requireNonNull(curves);
this.timeLine = Objects.requireNonNull(timeLine);
timeLine.forEach(Objects::requireNonNull);
}

@Override
public boolean isOk() {
return status;
public DynamicSimulationResultImpl(Status status, Map<String, DoubleTimeSeries> curves, List<TimelineEvent> timeLine) {
this(status, "", curves, timeLine);
}

public static DynamicSimulationResultImpl createSuccessResult(Map<String, DoubleTimeSeries> curves, List<TimelineEvent> timeLine) {
return new DynamicSimulationResultImpl(Status.SUCCESS, curves, timeLine);
}

@Override
public String getLogs() {
return logs;
public Status getStatus() {
return status;
}

@Override
public Map<String, TimeSeries> getCurves() {
return Collections.unmodifiableMap(curves);
public String getStatusText() {
return statusText;
}

@Override
public TimeSeries getCurve(String curve) {
return curves.get(curve);
public Map<String, DoubleTimeSeries> getCurves() {
return Collections.unmodifiableMap(curves);
}

@Override
public StringTimeSeries getTimeLine() {
public List<TimelineEvent> getTimeLine() {
return timeLine;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Copyright (c) 2023, 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.dynamicsimulation;

/**
* @author Florian Dupuy {@literal <florian.dupuy at rte-france.com>}
* @author Laurent Issertial {@literal <laurent.issertial at rte-france.com>}
*/
public record TimelineEvent(double time, String modelName, String message) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,6 @@
*/
package com.powsybl.dynamicsimulation.json;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
Expand All @@ -25,38 +15,49 @@
import com.powsybl.commons.json.JsonUtil;
import com.powsybl.dynamicsimulation.DynamicSimulationResult;
import com.powsybl.dynamicsimulation.DynamicSimulationResultImpl;
import com.powsybl.timeseries.StringTimeSeries;
import com.powsybl.timeseries.TimeSeries;
import com.powsybl.dynamicsimulation.TimelineEvent;
import com.powsybl.timeseries.DoubleTimeSeries;
import com.powsybl.timeseries.json.DoubleTimeSeriesJsonDeserializer;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;

/**
* @author Marcos de Miguel {@literal <demiguelm at aia.es>}
*/
public class DynamicSimulationResultDeserializer extends StdDeserializer<DynamicSimulationResult> {

private final DoubleTimeSeriesJsonDeserializer doubleTimeSeriesJsonDeserializer = new DoubleTimeSeriesJsonDeserializer();

DynamicSimulationResultDeserializer() {
super(DynamicSimulationResult.class);
}

@Override
public DynamicSimulationResult deserialize(JsonParser parser, DeserializationContext ctx) throws IOException {
boolean isOK = false;
String logs = null;
Map<String, TimeSeries> curves = new HashMap<>();
StringTimeSeries timeLine = null;
DynamicSimulationResult.Status status = null;
String error = "";
Map<String, DoubleTimeSeries> curves = new HashMap<>();
List<TimelineEvent> timeLine = new ArrayList<>();

while (parser.nextToken() != JsonToken.END_OBJECT) {
switch (parser.getCurrentName()) {
case "version":
parser.nextToken(); // skip
break;

case "isOK":
case "status":
parser.nextToken();
isOK = parser.readValueAs(Boolean.class);
status = parser.readValueAs(DynamicSimulationResult.Status.class);
break;

case "logs":
logs = parser.nextTextValue();
case "error":
parser.nextToken();
error = parser.readValueAs(String.class);
break;

case "curves":
Expand All @@ -66,36 +67,53 @@ public DynamicSimulationResult deserialize(JsonParser parser, DeserializationCon

case "timeLine":
parser.nextToken();
timeLine = (StringTimeSeries) deserializeTimeSeries(parser);
if (timeLine == null) {
timeLine = DynamicSimulationResult.emptyTimeLine();
}
deserializeTimeline(parser, timeLine);
break;

default:
throw new IllegalStateException("Unexpected field: " + parser.getCurrentName());
}
}

return new DynamicSimulationResultImpl(isOK, logs, curves, timeLine);
return new DynamicSimulationResultImpl(status, error, curves, timeLine);
}

private void deserializeCurves(JsonParser parser, Map<String, TimeSeries> curves) throws IOException {
TimeSeries curve = null;
private void deserializeCurves(JsonParser parser, Map<String, DoubleTimeSeries> curves) throws IOException {
DoubleTimeSeries curve;
while (parser.nextToken() != JsonToken.END_ARRAY) {
curve = deserializeTimeSeries(parser);
curve = doubleTimeSeriesJsonDeserializer.deserialize(parser, null);
if (curve != null) {
curves.put(curve.getMetadata().getName(), curve);
}
}
}

private TimeSeries deserializeTimeSeries(JsonParser parser) {
List<TimeSeries> timeseries = TimeSeries.parseJson(parser, true);
if (!timeseries.isEmpty()) {
return timeseries.get(0);
private void deserializeTimeline(JsonParser parser, List<TimelineEvent> timelineEvents) throws IOException {
while (parser.nextToken() != JsonToken.END_ARRAY) {
timelineEvents.add(deserializeTimelineEvent(parser));
}
}

private TimelineEvent deserializeTimelineEvent(JsonParser parser) throws IOException {
double time = 0;
String modelName = null;
String message = null;
while (parser.nextToken() != JsonToken.END_OBJECT) {
switch (parser.getCurrentName()) {
case "time":
time = parser.getValueAsDouble();
break;
case "modelName":
modelName = parser.getValueAsString();
break;
case "message":
message = parser.getValueAsString();
break;
default:
throw new IllegalStateException("Unexpected field: " + parser.getCurrentName());
}
}
return null;
return new TimelineEvent(time, modelName, message);
}

public static DynamicSimulationResult read(InputStream is) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.powsybl.commons.json.JsonUtil;
import com.powsybl.dynamicsimulation.DynamicSimulationResult;
import com.powsybl.timeseries.TimeSeries;
import com.powsybl.dynamicsimulation.TimelineEvent;
import com.powsybl.timeseries.DoubleTimeSeries;

/**
* @author Marcos de Miguel {@literal <demiguelm at aia.es>}
Expand All @@ -39,16 +40,26 @@ public class DynamicSimulationResultSerializer extends StdSerializer<DynamicSimu
public void serialize(DynamicSimulationResult result, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("version", VERSION);
jsonGenerator.writeBooleanField("isOK", result.isOk());
jsonGenerator.writeStringField("logs", result.getLogs());
jsonGenerator.writeStringField("status", result.getStatus().name());
if (!result.getStatusText().isEmpty()) {
jsonGenerator.writeStringField("error", result.getStatusText());
}
jsonGenerator.writeFieldName("curves");
jsonGenerator.writeStartArray();
for (Entry<String, TimeSeries> entry : result.getCurves().entrySet()) {
for (Entry<String, DoubleTimeSeries> entry : result.getCurves().entrySet()) {
entry.getValue().writeJson(jsonGenerator);
}
jsonGenerator.writeEndArray();
jsonGenerator.writeFieldName("timeLine");
result.getTimeLine().writeJson(jsonGenerator);
jsonGenerator.writeStartArray();
for (TimelineEvent event : result.getTimeLine()) {
jsonGenerator.writeStartObject();
jsonGenerator.writeNumberField("time", event.time());
jsonGenerator.writeStringField("modelName", event.modelName());
jsonGenerator.writeStringField("message", event.message());
jsonGenerator.writeEndObject();
}
jsonGenerator.writeEndArray();
jsonGenerator.writeEndObject();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public String getVersion() {
@Override
public CompletableFuture<DynamicSimulationResult> run(Network network, DynamicModelsSupplier dynamicModelsSupplier, EventModelsSupplier eventModelsSupplier,
CurvesSupplier curvesSupplier, String workingVariantId, ComputationManager computationManager, DynamicSimulationParameters parameters, Reporter reporter) {
return CompletableFuture.completedFuture(new DynamicSimulationResultImpl(true, null, Collections.emptyMap(), DynamicSimulationResult.emptyTimeLine()));
return CompletableFuture.completedFuture(DynamicSimulationResultImpl.createSuccessResult(Collections.emptyMap(), DynamicSimulationResult.emptyTimeLine()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public String getVersion() {
@Override
public CompletableFuture<DynamicSimulationResult> run(Network network, DynamicModelsSupplier dynamicModelsSupplier, EventModelsSupplier eventModelsSupplier, CurvesSupplier curvesSupplier,
String workingVariantId, ComputationManager computationManager, DynamicSimulationParameters parameters, Reporter reporter) {
return CompletableFuture.completedFuture(new DynamicSimulationResultImpl(true, null, Collections.emptyMap(), DynamicSimulationResult.emptyTimeLine()));
return CompletableFuture.completedFuture(DynamicSimulationResultImpl.createSuccessResult(Collections.emptyMap(), DynamicSimulationResult.emptyTimeLine()));
}

}
Loading

0 comments on commit df8015f

Please sign in to comment.