Dynamic Simulation¶
This module provides the time-domain (dynamic) data generation pipeline. See the Dynamic Simulation manual page for the configuration and output reference.
Entry point¶
generate_dynamic_data¶
Generate dynamic simulation data from a YAML config. Accepted format includes: a path to the YAML file (str or os.PathLike), a dictionnary or a NestedNamespace.
Runs the full pipeline: 1. Validate config. 2. Prepare network + load scenarios. 3. Load and prepare Dynawo mappings. 4. Build solver parameters. 5. Run distributed dynamic simulations. 6. Save static (Parquet) + dynamic (Zarr) outputs.
Args¶
config : str | os.PathLike | dict | NestedNamespace Path to a YAML config file, a plain dict, or a NestedNamespace.
Returns¶
dict
Paths to all generated artifacts, all rooted at settings.data_dir:
the static keys (bus_data, branch_data, gen_data,
y_bus_data, runtime_data, error_log, args_log,
solver_log_dir, scenarios) plus dynamic_results (Zarr store)
and metadata. dynamic_reports_dir is present unless
dynamic.logging.save_reports is off, and final_state_values only
when the variables table declares FinalStateValue rows.
Raises¶
TypeError
If config is none of the accepted forms.
ValueError
If network.reader != "powsybl", the dynamic block or
dynamic.dynamic_solver is missing, load.scenarios is below 1, or
the removed dynamic.output_dir key is still present.
RuntimeError
If no sample survived, i.e. every scenario failed.
Source code in gridfm_datakit/dynamic/generate_dynamic.py
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | |
Inputs¶
DynamicInputs¶
Solver-agnostic container for dynamic simulation inputs.
All three attributes are pandas DataFrames or list of pandas DataFrames so they remain compatible with pypowsybl.dynamic's native input format and are easy to inspect or serialize for debugging.
Attributes¶
dynamic_models : list[pd.DataFrame] List of 2 pandas DataFrames. First one is for the dynamic models that equip static elements: One row per network element to be equipped with a dynamic model. Required columns: category_name, static_id, parameter_set_id, model_name Note: unequipped static element will be given a default model Second one is for the automation systems: One row per automation system Required columns: category_name, dynamic_model_id, parameter_set_id, params, model_name events : pd.DataFrame One row per event in the simulation sequence. Required columns: event_name, static_id, start_time, params variables : pd.DataFrame One row per monitored output variables (curve or final state value). Required columns: type, model_id, variables
Source code in gridfm_datakit/dynamic/__init__.py
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | |
DynamicResults¶
Solver-agnostic container for dynamic simulation outputs.
Attributes¶
dynamic_results : pandas.DataFrame Curves for one sample, indexed by time: shape (n_timesteps, n_variables), one column per output variable. This is the solver's natural orientation and is kept as-is through the pipeline.
Note the persistent store uses the *transposed* orientation: the writer in
generate_dynamic transposes each sample to (n_variables, n_timesteps) and
stacks them into a Zarr array of shape
(n_scenarios, n_variables, n_timesteps).
report : Any Dynamic simulation report including model build-up and problem resolution. final_state_values : Any, optional Values of the variables declared as "FinalStateValue" in the variables input table: one scalar per variable at the end of the simulation, rather than a trajectory. pypowsybl returns a DataFrame indexed by the flattened variable name with a single "values" column. None when the run monitors no such variable, in which case no final_state_values.parquet is written.
Source code in gridfm_datakit/dynamic/__init__.py
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | |
load_raw_inputs¶
Load dynamic simulation inputs from CSV files declared in the config.
Reads the four CSV (or Parquet) files listed under config.dynamic and returns a DynamicInputs instance. When dynamic_solver == "dynawo", the minimum required columns for each DataFrame are validated.
Args¶
args : NestedNamespace
Configuration object. args.dynamic.input_files must carry:
- static_element_dynamic_models_file : path to the models CSV
- automation_systems_file : path to the automation systems CSV
- events_file : path to the events CSV
- variables_file : path to the variables CSV
and args.dynamic.dynamic_solver the solver name ("dynawo" or future
alternatives); it defaults to "dynawo" when absent.
Returns¶
DynamicInputs
Raises¶
FileNotFoundError If any of the four input files is missing. ValueError If required columns are absent from a DataFrame, if a key column holds an unsupported value, or if the variables table declares no "Curve" row (Dynawo solver only). TypeError If any of the input files is not of CSV or Parquet format.
Source code in gridfm_datakit/dynamic/__init__.py
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | |
Processing¶
iter_dynamic_simulations¶
Distributed outer loop for dynamic simulation data generation.
Splits scenarios into chunks and dispatches each chunk to a worker process. Each worker initialises a Julia instance and a local copy of the pypowsybl network once, then reuses them for all scenarios in the chunk.
Yields one large chunk's samples at a time so the caller can persist and drop
them: holding every sample would make peak memory scale with the whole dataset
rather than with settings.large_chunk_size, and dynamic curves are far
larger than static snapshots. Use process_dynamic_simulations for the
collected list.
Args¶
network_path : str Path to the network. scenarios : np.ndarray Load scenarios array, shape (n_loads, n_scenarios, 2). dynamic_inputs: DynamicInputs Dynamics inputs. dynamic_solver : str Solver name ("dynawo" or future alternatives). config : Full NestedNamespace config. error_log_file : str Path to error log. seed : int Global seed. Deterministically derived per-chunk seeds are computed from this value.
Yields¶
list of dict
One list per large chunk, holding one dict per successfully processed
(scenario, topology-perturbation) sample, each with keys "pf_data",
"dynamic_results", "scenario_index", "perturbation_index".
A chunk whose scenarios all failed yields an empty list.
Source code in gridfm_datakit/dynamic/process_dynamic.py
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | |
process_dynamic_simulations¶
Collect every sample from :func:iter_dynamic_simulations into one list.
Convenience for callers that want the whole run in memory (tests, ad-hoc scripts). The pipeline streams instead; see generate_dynamic.generate_dynamic_data.
Source code in gridfm_datakit/dynamic/process_dynamic.py
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | |
process_single_dynamic_simulation¶
Process one load scenario, expanded over topology perturbations.
The load scenario is applied, then generation and admittance perturbations
(before OPF). Each resulting topology perturbation is processed independently:
the balanced initial state is computed on the perturbed network (so OPF adapts
the set-points to the topology and Dynawo initialises from a converged
operating point), then the dynamic simulation is run. One sample is produced
per (scenario_index, perturbation_index).
Absent generators default to identity, so a scenario yields exactly one sample, the pre-perturbation behaviour.
Returns a list of result dicts (possibly empty if every perturbation failed).
Source code in gridfm_datakit/dynamic/process_dynamic.py
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 | |
Dynawo backend¶
DynawoMappings¶
Dynawo-ready simulation inputs derived from DynamicInputs. 3 mappings: - dynamic model mapping -> for both static equipments and automation systems - event mapping -> for events - variable mapping -> for the output variables
Attributes¶
dynamic_model_mapping : pypowsybl.dynamic.ModelMapping event_mapping : pypowsybl.dynamic.EventMapping variable_mapping : pypowsybl.dynamic.OutputVariableMapping
Source code in gridfm_datakit/dynamic/dynawo/__init__.py
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | |
generate_dynawo_mappings¶
Convert generic DynamicInputs into Dynawo-compatible DynawoMappings.
The conversion builds the Dynawo-specific mapping objects by parsing the generic DynamicInputs dataframes.
Args dynamic_inputs: DynamicInputs, generic inputs loaded by load_raw_inputs().
Returns DynawoMappings: simulation-ready Dynawo mappings
Source code in gridfm_datakit/dynamic/dynawo/__init__.py
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | |
get_dynawo_simulation_parameters¶
Prepares the parameters for Dynawo simulation.
Raises¶
ValueError If a required key is missing, or an unsupported one is present.
Source code in gridfm_datakit/dynamic/dynawo/__init__.py
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | |
get_dynawo_loadflow_parameters¶
Build the AC load flow parameters for the balanced initial state.
The defaults deliberately differ from powsybl.get_default_lf_params(),
which the static pipeline uses. Dynawo initialises each synchronous machine
from the power flow solution, so the slack must sit on a machine that carries
a dynamic model: slackBusSelectionMode=LARGEST_GENERATOR puts it there,
and read_slack_bus/write_slack_bus keep that choice explicit and
visible in the network. OpenLoadFlow's own default (MOST_MESHED) can select a
bus with no generator at all, leaving Dynawo to initialise from a state its
machine models cannot reproduce.
Every default is overridable through the optional dynamic.loadflow_parameters
config block.
Raises¶
ValueError If the config block contains an unsupported key.
Source code in gridfm_datakit/dynamic/dynawo/__init__.py
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | |
compute_balanced_static_state_dynawo¶
Compute the balanced initial conditions for a dynamic simulation.
Runs the four-step sequence required to produce a consistent initial state for Dynawo:
- OPF via Julia/PowerModels on the randomised gfm network → optimal dispatch for the current scenario.
- update_powsybl (powsybl submodule) → applies OPF results (Pg, Vm setpoints) onto the pypowsybl object with correct per-unit conventions.
- AC-PF via pypowsybl OpenLoadFlow → verifies convergence and produces the balanced initial state.
- get_pf_res / pf_post_processing (powsybl submodule) → formats pypowsybl PF results in the gridfm column schema with ID-based bus index assignment.
Args¶
pp_net:
pypowsybl network.
The caller must pass a per-worker clone/variant to avoid
cross-scenario contamination
gfm_net:
randomised gridfm network for the current scenario
(with applied load scenario and perturbations)
julia:
Initialised Julia interface (from "init_julia")
p2g_maps:
Pypowsybl-to-gridfm index maps for pp_net (from
powsybl.build_p2g_maps), passed in rather than rebuilt per scenario
scenario_index: int
Used to label the results row (matches pf_post_processing's
scenario_index argument)
lf_params:
pypowsybl.loadflow.Parameters for step 3 (from
get_dynawo_loadflow_parameters). Defaults to the dynamic-appropriate
settings in LOADFLOW_PARAMETERS_DEFAULTS when None.
Returns¶
pp_net:
The updated pypowsybl network, balanced and ready for dynamic
simulation.
pf_data: dict
Power flow results in gridfm column schema with keys:
"bus", "gen", "branch", "Y_bus", "runtime"
Raises¶
RuntimeError If OPF fails to converge. ValueError If the AC power flow does not converge.
Source code in gridfm_datakit/dynamic/dynawo/simulate.py
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |
run_dynawo_simulation¶
Apply Dynawo mappings to a balanced pypowsybl network and run the simulation.
Args¶
pp_net :
Balanced pypowsybl network (output of compute_balanced_static_state_dynawo).
dynawo_mapping : DynawoMappings
Validated Dynawo-ready mappings (models, events, variables).
parameters :
pypowsybl.dynamic.Parameters object (from get_dynawo_simulation_parameters).
drop_duplicate_timestep :
Whether drop duplicate timestep in the output timeseries, True by default.
Returns¶
DynamicResults Solver-agnostic container holding the curves as a pandas DataFrame indexed by time with shape (n_timesteps, n_variables), plus the solver status report string and the final state values. The transpose to (n_variables, n_timesteps) happens only when writing the Zarr store.
Raises¶
RuntimeError If the status is not SUCCESS, or if a dynamic model was not instantiated.
Source code in gridfm_datakit/dynamic/dynawo/simulate.py
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | |
Availability checks¶
is_dynawo_available¶
Return True if pypowsybl.dynamic AND a local Dynawo installation are usable.
Source code in gridfm_datakit/dynamic/dynawo/api.py
163 164 165 166 167 | |
check_dynawo_available¶
Raise if the Dynawo backend cannot run, explaining exactly what is missing.
Called before launching simulations so a missing installation surfaces as an actionable message instead of an opaque "DynawoSimulationProvider could not be instantiated" from deep inside the solver.
Raises¶
ImportError If pypowsybl.dynamic is not installed. RuntimeError If no usable local Dynawo installation is declared to powsybl.
Source code in gridfm_datakit/dynamic/dynawo/api.py
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | |
Validation¶
validate_dynamic_data¶
Run the static-data validation suite on the dynamic pipeline's PF snapshot.
The dynamic pipeline stores the same physical quantities as the static one, but lays them out differently, so validate_generated_data cannot read them directly:
- flat single-file parquet, not partitioned directories (no
n_scenarios.txt); - a sample is keyed by the pair (scenario_index, perturbation_index), because a
topology perturbation expands one load scenario into several samples, whereas
the static schema has a single
scenariocolumn.
This loader bridges the two: it reads the flat files and adds a dense
scenario column by ranking the distinct (scenario_index, perturbation_index)
pairs, so each dynamic sample becomes one "scenario" from the checks' point of
view. The checks themselves are shared verbatim with the static pipeline.
Note this validates the static snapshot (the initial operating point Dynawo starts from): the bus/branch/gen/Y-bus/runtime tables. It does not validate the time-series curves in the Zarr store.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file_paths
|
Dict[str, str]
|
Paths as returned by generate_dynamic_data (needs "bus_data", "branch_data", "gen_data", "y_bus_data", optionally "runtime_data"). |
required |
mode
|
str
|
Operating mode ("opf" or "pf"). The dynamic pipeline balances with an OPF but stores an AC-PF solution, so the default is "pf". |
'pf'
|
sn_mva
|
float
|
Base MVA used to scale power quantities. |
100.0
|
Returns:
| Type | Description |
|---|---|
bool
|
True if all validations pass. |
Raises:
| Type | Description |
|---|---|
AssertionError
|
If any validation fails. |
Source code in gridfm_datakit/validation.py
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 | |