Skip to content

Commit

Permalink
Add reporters for suppliers
Browse files Browse the repository at this point in the history
Add dataframes for TCB measurement points

Signed-off-by: lisrte <laurent.issertial@rte-france.com>
  • Loading branch information
Lisrte committed Nov 6, 2024
1 parent 8c8d1b1 commit cc4e2db
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@
import com.powsybl.dataframe.update.StringSeries;
import com.powsybl.dataframe.update.UpdatingDataframe;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.Consumer;

/**
* @author Laurent Issertial {@literal <laurent.issertial at rte-france.com>}
*/
public final class DynamicModelSeriesUtils {

private DynamicModelSeriesUtils(){
private DynamicModelSeriesUtils() {
}

public static Map<String, List<String>> createIdMap(UpdatingDataframe dataframe, String indexColumn, String idColumn) {
if (dataframe.getRowCount() == 0) {
return Collections.emptyMap();
}
StringSeries joinIds = dataframe.getStrings(indexColumn);
if (joinIds == null) {
throw new PowsyblException("Join dataframe: %s column is not set".formatted(indexColumn));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public final class DynamicModelDataframeConstants {
public static final String GENERATOR = "generator";
public static final String TRANSFORMER = "transformer";
public static final String SIDE = "side";
public static final String U_MEASUREMENTS = "u_measurements";
public static final String MEASUREMENT_POINT_ID = "measurement_point_id";
public static final String TRANSFORMER_ID = "transformer_id";
public static final String START_TIME = "start_time";
public static final String DISCONNECT_ONLY = "disconnect_only";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@

import com.powsybl.commons.report.ReportNode;
import com.powsybl.dataframe.SeriesMetadata;
import com.powsybl.dataframe.dynamic.PersistentStringSeries;
import com.powsybl.dataframe.network.adders.SeriesUtils;
import com.powsybl.dataframe.update.StringSeries;
import com.powsybl.dataframe.update.UpdatingDataframe;
import com.powsybl.dynawo.builders.ModelInfo;
import com.powsybl.dynawo.models.automationsystems.TapChangerBlockingAutomationSystemBuilder;
Expand All @@ -32,14 +29,25 @@ public class TapChangerBlockingAutomationSystemAdder implements DynamicMappingAd
private static final List<SeriesMetadata> METADATA = List.of(
SeriesMetadata.stringIndex(DYNAMIC_MODEL_ID),
SeriesMetadata.strings(PARAMETER_SET_ID),
SeriesMetadata.strings(MODEL_NAME),
SeriesMetadata.strings(U_MEASUREMENTS));
SeriesMetadata.strings(MODEL_NAME));

private static final List<SeriesMetadata> TRANSFORMER_METADATA = List.of(
SeriesMetadata.stringIndex(DYNAMIC_MODEL_ID),
SeriesMetadata.strings(TRANSFORMER_ID));

private static final List<List<SeriesMetadata>> METADATA_LIST = List.of(METADATA, TRANSFORMER_METADATA);
private static final List<SeriesMetadata> U_MEASUREMENT_METADATA = List.of(
SeriesMetadata.stringIndex(DYNAMIC_MODEL_ID),
SeriesMetadata.strings(MEASUREMENT_POINT_ID));

private static final List<List<SeriesMetadata>> METADATA_LIST = List.of(METADATA,
TRANSFORMER_METADATA,
U_MEASUREMENT_METADATA,
U_MEASUREMENT_METADATA,
U_MEASUREMENT_METADATA,
U_MEASUREMENT_METADATA,
U_MEASUREMENT_METADATA);

private static final int MAX_MEASUREMENTS = 5;

@Override
public List<List<SeriesMetadata>> getMetadata() {
Expand All @@ -53,34 +61,43 @@ public Collection<ModelInfo> getSupportedModels() {

@Override
public void addElements(PythonDynamicModelsSupplier modelMapping, List<UpdatingDataframe> dataframes) {
if (dataframes.size() != 2) {
throw new IllegalArgumentException("Expected 2 dataframes: one for TCB, one for transformers.");
if (dataframes.size() < 3) {
throw new IllegalArgumentException("Expected at least 3 dataframes: one for TCB, one for transformers and one for measurement points.");
}
UpdatingDataframe dataframe = dataframes.get(0);
UpdatingDataframe tfo_dataframe = dataframes.get(1);
DynamicModelSeries series = new TapChangerBlockingSeries(dataframe, tfo_dataframe);
UpdatingDataframe tfoDataframe = dataframes.get(1);
List<UpdatingDataframe> measurementDataframe = new ArrayList<>(MAX_MEASUREMENTS);
for (int i = 2; i < dataframes.size(); i++) {
measurementDataframe.add(dataframes.get(i));
}

DynamicModelSeries series = new TapChangerBlockingSeries(dataframe, tfoDataframe, measurementDataframe);
for (int row = 0; row < dataframe.getRowCount(); row++) {
modelMapping.addModel(series.getModelSupplier(row));
}
}

private static class TapChangerBlockingSeries extends AbstractAutomationSystemSeries<TapChangerBlockingAutomationSystemBuilder> {

private final StringSeries uMeasurements;
private final Map<String, List<String>> transformers;
private final List<Map<String, List<String>>> uMeasurements = new ArrayList<>(MAX_MEASUREMENTS);

TapChangerBlockingSeries(UpdatingDataframe tcbDataframe, UpdatingDataframe tfoDataframe) {
TapChangerBlockingSeries(UpdatingDataframe tcbDataframe, UpdatingDataframe tfoDataframe, List<UpdatingDataframe> measurementDataframe) {
super(tcbDataframe);
this.uMeasurements = PersistentStringSeries.copyOf(tcbDataframe, U_MEASUREMENTS);
this.transformers = createIdMap(tfoDataframe, DYNAMIC_MODEL_ID, TRANSFORMER_ID);
measurementDataframe.forEach(mdf -> uMeasurements.add(createIdMap(mdf, DYNAMIC_MODEL_ID, MEASUREMENT_POINT_ID)));
}

@Override
protected void applyOnBuilder(int row, TapChangerBlockingAutomationSystemBuilder builder) {
super.applyOnBuilder(row, builder);
SeriesUtils.applyIfPresent(uMeasurements, row, builder::uMeasurements);
String tcbId = dynamicModelIds.get(row);
applyIfPresent(transformers, tcbId, builder::transformers);
List<Collection<String>> id2dList = new ArrayList<>();
uMeasurements.forEach(idMap -> applyIfPresent(idMap, tcbId, id2dList::add));
if (!id2dList.isEmpty()) {
builder.uMeasurements(id2dList.toArray(new Collection[0]));
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
import com.powsybl.dataframe.update.TestStringSeries;
import com.powsybl.dynawo.models.AbstractPureDynamicBlackBoxModel;
import com.powsybl.dynawo.models.TransformerSide;
import com.powsybl.dynawo.models.automationsystems.TapChangerBlockingAutomationSystem;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.TwoSides;
import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory;
import com.powsybl.iidm.network.test.HvdcTestNetwork;
import com.powsybl.iidm.network.test.SvcTestCaseFactory;
import com.powsybl.python.commons.PyPowsyblApiHeader;
import com.powsybl.python.dynamic.PythonDynamicModelsSupplier;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
Expand Down Expand Up @@ -77,7 +79,8 @@ void testAutomationSystemAdders(PyPowsyblApiHeader.DynamicMappingType mappingTyp
assertThat(dynamicModelsSupplier.get(network)).satisfiesExactly(
model1 -> assertThat(model1).hasFieldOrPropertyWithValue("dynamicModelId", dynamicModelId)
.isInstanceOf(AbstractPureDynamicBlackBoxModel.class),
model2 -> assertThat(model2).hasFieldOrPropertyWithValue("dynamicModelId", dynamicModelId + DEFAULT_SUFFIX));
model2 -> assertThat(model2).hasFieldOrPropertyWithValue("dynamicModelId", dynamicModelId + DEFAULT_SUFFIX)
.isInstanceOf(AbstractPureDynamicBlackBoxModel.class));
}

@Test
Expand All @@ -88,17 +91,30 @@ void testTapChangerBlockingAdders() {
String defaultDynamicModelId = dynamicModelId + DEFAULT_SUFFIX;
// Setup Tcb df
setupDataFrame(dataframe, dynamicModelId, expectedModelName);
dataframe.addSeries(U_MEASUREMENTS, false, createTwoRowsSeries("NHV1"));
// Setup Tfo df
DefaultUpdatingDataframe tfoDataFrame = new DefaultUpdatingDataframe(3);
tfoDataFrame.addSeries(DYNAMIC_MODEL_ID, true, new TestStringSeries(dynamicModelId, dynamicModelId, defaultDynamicModelId));
tfoDataFrame.addSeries(TRANSFORMER_ID, false, new TestStringSeries("NGEN_NHV1", "NHV2_NLOAD", "NHV2_NLOAD"));
DynamicMappingHandler.addElements(TAP_CHANGER_BLOCKING, dynamicModelsSupplier, List.of(dataframe, tfoDataFrame));
// Setup measurement points df
DefaultUpdatingDataframe m1DataFrame = new DefaultUpdatingDataframe(3);
m1DataFrame.addSeries(DYNAMIC_MODEL_ID, true, new TestStringSeries(dynamicModelId, dynamicModelId, defaultDynamicModelId));
m1DataFrame.addSeries(MEASUREMENT_POINT_ID, false, new TestStringSeries("BBS_NGEN", "NGEN", "NHV1"));
DefaultUpdatingDataframe m2DataFrame = new DefaultUpdatingDataframe(2);
m2DataFrame.addSeries(DYNAMIC_MODEL_ID, true, new TestStringSeries(dynamicModelId, dynamicModelId));
m2DataFrame.addSeries(MEASUREMENT_POINT_ID, false, new TestStringSeries("OLD_NLOAD_ID", "NLOAD"));
DynamicMappingHandler.addElements(TAP_CHANGER_BLOCKING, dynamicModelsSupplier, List.of(dataframe, tfoDataFrame, m1DataFrame, m2DataFrame));

assertThat(dynamicModelsSupplier.get(network)).satisfiesExactly(
model1 -> assertThat(model1).hasFieldOrPropertyWithValue("dynamicModelId", dynamicModelId)
.isInstanceOf(AbstractPureDynamicBlackBoxModel.class),
model2 -> assertThat(model2).hasFieldOrPropertyWithValue("dynamicModelId", defaultDynamicModelId));
.isInstanceOf(TapChangerBlockingAutomationSystem.class)
.extracting("uMeasurements")
.asInstanceOf(InstanceOfAssertFactories.LIST)
.containsExactly(network.getBusBreakerView().getBus("NGEN"), network.getBusBreakerView().getBus("NLOAD")),
model2 -> assertThat(model2).hasFieldOrPropertyWithValue("dynamicModelId", defaultDynamicModelId)
.isInstanceOf(TapChangerBlockingAutomationSystem.class)
.extracting("uMeasurements")
.asInstanceOf(InstanceOfAssertFactories.LIST)
.containsExactly(network.getBusBreakerView().getBus("NHV1")));
}

@Test
Expand Down
2 changes: 1 addition & 1 deletion pypowsybl/dynamic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
from .impl.event_mapping import EventMapping, EventMappingType
from .impl.simulation_result import SimulationResult
from .impl.simulation import Simulation
from .impl.model_mapping import ModelMapping, DynamicMappingType
from .impl.model_mapping import ModelMapping, DynamicMappingType
2 changes: 1 addition & 1 deletion pypowsybl/dynamic/impl/event_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# SPDX-License-Identifier: MPL-2.0
#
from typing import Optional
from numpy.typing import ArrayLike
from pandas import DataFrame
from typing import Optional
from pypowsybl import _pypowsybl as _pp
from pypowsybl._pypowsybl import EventMappingType # pylint: disable=protected-access
from pypowsybl.utils import _add_index_to_kwargs, \
Expand Down
30 changes: 24 additions & 6 deletions pypowsybl/dynamic/impl/model_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -837,27 +837,35 @@ def add_tap_changer_automation_system(self, df: DataFrame = None, **kwargs: Arra
"""
self._add_all_dynamic_mappings(DynamicMappingType.TAP_CHANGER, [df], **kwargs)

def add_tap_changer_blocking_automation_system(self, df: DataFrame, tfo_df: DataFrame) -> None:
def add_tap_changer_blocking_automation_system(self, df: DataFrame, tfo_df: DataFrame, mp1_df: DataFrame,
mp2_df: DataFrame = None, mp3_df: DataFrame = None,
mp5_df: DataFrame = None, mp4_df: DataFrame = None) -> None:
"""
Add a dynamic tap changer blocking automation system (not link to a network element)
:Args:
df: Primary attributes as a dataframe.
tfo_df: Dataframe for transformer data.
mpN_df: Dataframes for a measurement point data, the automation system can handle up to 5 measurement points,
at least 1 measurement point is expected. For each measurement point dataframe, alternative points can be input
(for example bus or busbar section) the first energized element found in the network will be used
Notes:
Valid attributes for thr primary dataframes are:
Valid attributes for the primary dataframes are:
- **dynamic_model_id**: id of the tap changer blocking automation system
- **parameter_set_id**: id of the parameter for this model given in the dynawo configuration
- **u_measurements**: id of the bus or busbar section used for the voltage measurement
- **model_name**: name of the model used for the mapping (if none the default model will be used)
Valid attributes for thr primary dataframes are:
Valid attributes for the transformer dataframes are:
- **dynamic_model_id**: id of the tap changer blocking automation system
- **transformer_id**: id of a transformer controlled by the automation system
Valid attributes for the measurement point dataframes are:
- **dynamic_model_id**: id of the tap changer blocking automation system
- **measurement_point_id**: id of the bus or busbar section used for the voltage measurement
Examples:
We need to provide 2 dataframes, 1 for tap changer blocking automation system basic data, and one for transformer data:
Expand All @@ -874,9 +882,19 @@ def add_tap_changer_blocking_automation_system(self, df: DataFrame, tfo_df: Data
data=[('DM_TCB', 'TFO1'),
('DM_TCB', 'TFO2'),
('DM_TCB', 'TFO3')])
model_mapping.add_tap_changer_blocking_automation_system(df, tfo_df)
measurement1_df = pd.DataFrame.from_records(
index='dynamic_model_id',
columns=['dynamic_model_id', 'measurement_point_id'],
data=[('DM_TCB', 'B1'),
('DM_TCB', 'BS1')])
measurement2_df = pd.DataFrame.from_records(
index='dynamic_model_id',
columns=['dynamic_model_id', 'measurement_point_id'],
data=[('DM_TCB', 'B4')])
model_mapping.add_tap_changer_blocking_automation_system(df, tfo_df, measurement1_df, measurement2_df)
"""
self._add_all_dynamic_mappings(DynamicMappingType.TAP_CHANGER_BLOCKING, [df, tfo_df])
dfs = [df, tfo_df, mp1_df, mp2_df, mp3_df, mp4_df, mp5_df]
self._add_all_dynamic_mappings(DynamicMappingType.TAP_CHANGER_BLOCKING, [DataFrame() if df is None else df for df in dfs])

def _add_all_dynamic_mappings(self, mapping_type: DynamicMappingType, mapping_dfs: List[Optional[DataFrame]], **kwargs: ArrayLike) -> None:
metadata = _pp.get_dynamic_mappings_meta_data(mapping_type)
Expand Down
21 changes: 12 additions & 9 deletions tests/test_dynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ def set_up():


def test_add_mapping():
static_id = 'test_id'
dynamic_id = 'test_dynamic_id'
parameter_id = 'test_parameter'
model_mapping = dyn.ModelMapping()
# Equipments
model_mapping.add_base_load(static_id='LOAD', parameter_set_id='lab', dynamic_model_id='DM_LOAD', model_name='LoadPQ')
Expand Down Expand Up @@ -57,9 +54,6 @@ def test_add_mapping():
phase_shifter_id='PSI', model_name='PhaseShifterBlockingI')
model_mapping.add_tap_changer_automation_system(dynamic_model_id='DM_TC', parameter_set_id='tc', static_id='LOAD',
side='HIGH_VOLTAGE', model_name='TapChangerAutomaton')
model_mapping.add_tap_changer_blocking_automation_system(dynamic_model_id='DM_TCB', parameter_set_id='tcb',
transformers='TRA', u_measurements='BUS',
model_name='TapChangerBlockingAutomaton')
# Equipment with default model name and dynamic id
model_mapping.add_base_load(static_id='LOAD', parameter_set_id='lab')
# Equipment model from Supported models
Expand Down Expand Up @@ -91,15 +85,24 @@ def test_dynamic_dataframe():

tcb_df = pd.DataFrame.from_records(
index='dynamic_model_id',
columns=['dynamic_model_id', 'parameter_set_id', 'u_measurements', 'model_name'],
data=[('DM_TCB', 'tcb', 'BUS', 'TapChangerBlockingAutomaton')])
columns=['dynamic_model_id', 'parameter_set_id', 'model_name'],
data=[('DM_TCB', 'tcb', 'TapChangerBlockingAutomaton')])
tfo_df = pd.DataFrame.from_records(
index='dynamic_model_id',
columns=['dynamic_model_id', 'transformer_id'],
data=[('DM_TCB', 'TFO1'),
('DM_TCB', 'TFO2'),
('DM_TCB', 'TFO3')])
model_mapping.add_tap_changer_blocking_automation_system(tcb_df, tfo_df)
measurement1_df = pd.DataFrame.from_records(
index='dynamic_model_id',
columns=['dynamic_model_id', 'measurement_point_id'],
data=[('DM_TCB', 'B1'),
('DM_TCB', 'BS1')])
measurement2_df = pd.DataFrame.from_records(
index='dynamic_model_id',
columns=['dynamic_model_id', 'measurement_point_id'],
data=[('DM_TCB', 'B4')])
model_mapping.add_tap_changer_blocking_automation_system(tcb_df, tfo_df, measurement1_df, measurement2_df)


def test_add_event():
Expand Down

0 comments on commit cc4e2db

Please sign in to comment.