test: add test suite for ac validation after dc solver multi outage - #728
Open
spetznick-elia wants to merge 21 commits into
Open
spetznick-elia wants to merge 21 commits into
spetznick-elia wants to merge 21 commits into
Conversation
… ones
An imported multi-outage that contains a bridging branch, or that is a cut
set in its own right, made the MODF denominator singular for every candidate
topology. The BSDF/LODF action filter then rejected every substation split, so
the topology optimizer was left with no search space at all. Reproduces on the
PandaPower oberrhein fixture with no complex import involved.
exclude_bridges_from_outage_masks now acts on whole contingencies: an imported
group is dropped and reported at warning level, while a synthesised trafo3w or
bus group -- which islands its own star node or busbar by construction -- is
repaired by sparing branches instead. This replaces the type-keyed
_zero_out_first_branch hack and its -1 sentinel.
Also removes multi_outage_nodes end to end. It was produced, serialised and
shape-asserted but never read by any computation; DC already models busbar
outages through preprocess_bb_outages. This drops the multi_outage_nodes_{idx}
datasets from static_information.hdf5: new code reads old files, but older code
cannot read new ones.
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Add load_nminus1_definition_from_file(), which resolves a business N-1 contingency list (grouped InterruptedComponents/OpenedSwitches outages plus ClosedSwitches SPPS actions) against a Powsybl network into a shared Nminus1Definition. A converted three-winding transformer referenced in InterruptedComponents is expanded to its -Leg1/-Leg2/-Leg3 elements. This is not wired into the production import pipeline (preprocessing.py / powsybl_masks.py) yet, so convert_file() behavior, the DC solver, contingency analysis and the topology optimizer are all unchanged: the parser is a fully tested, standalone capability that a follow-up change will connect to create_nminus1_definition() together with the DC/CA-side handling for grouped outages and SPPS rules. Adds the minimal Nminus1Definition/spps_parameters additions the parser needs: - Nminus1Definition.source_schema to tag complex-imported definitions - Condition.condition_limit_value now also accepts the string switch targets used by SPPS switching-state conditions - a model validator enforcing that every SPPS scheme_name matches exactly one contingency id - SppsConditionType.SWITCHING_STATE - HVDC_LINE added to the shared GridElementType literal, needed because get_all_element_names() now inventories HVDC lines for element resolution Fixture: data/complex_grid/contingency_list_complex.json (7 cases, 6 genuine multi-outages) for the parser's own tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
… solver
Cut the Powsybl backend's dependency on *_for_nminus1 masks: outages are now
taken exclusively from nminus1_definition.json (or the DC-written
dc_nminus1_definition.json fallback for folders without an importer run), so
grouped/multi-element contingencies written by an importer (including the
complex parser added in a previous commit) reach the DC solver unchanged.
_get_definition_mask lost its mask fallback and the uses_dc_definition switch;
all six call sites now take outages from the definition only.
get_busbar_outage_map is built from kind=="bus" contingencies instead of the
busbar_for_nminus1 mask.
Because producers must now write the definition before load_grid() reads it,
save_nminus1_definition_from_masks() derives one from the masks a fixture
already wrote and is wired into the Powsybl example-grid builders and into
write_aux_data() as a non-overwriting provisioning step.
DC's own projection of the canonical definition -- exactly the contingencies
DC computes, in solver order -- is now written to dc_nminus1_definition.json
as an output only; it is never read back as input, which used to shrink the
busbar set on every run. NetworkData.contingency_ids now labels single
outages by their source contingency id
(BackendInterface.get_contingency_id_by_element_id, defaults to {} so
PandaPower is unaffected) instead of by element id, and the single-outage vs.
multi-outage split now keys off the *projected* arity instead of the source
arity, so a line plus its two breakers collapses to one branch outage instead
of a degenerate multi-outage row.
Fixes a multi-outage bug found while wiring this up: a live multi-outage that
islands the unsplit grid made the BSDF/LODF action filter reject every split
of every substation, because modf_success is conjoined over all multi-outages
and the MODF denominator is singular for any group that islands the base
grid. exclude_bridges_from_outage_masks now runs an islanding test over whole
multi-outage groups (find_islanding_branch_groups), not just single branches:
an *imported* group that contains a bridge or is a cut set in its own right is
dropped whole and reported at warning level (a curated contingency is
computed as declared or not at all); a *synthesised* group (trafo3w, bus -
one physical element that islands its own star node/busbar by construction)
is repaired instead by sparing the minimal set of branches that resolves the
islanding (find_branches_to_spare_from_groups), replacing the previous
unconditional "blank the first branch" hack. The gap between a declared and
a computed synthesised group is now recorded explicitly in
NetworkData.multi_outage_spared_branch_mask.
Also removes multi_outage_nodes end to end (BackendInterface method, both
backend implementations, NetworkData/DynamicInformation fields, HDF5
datasets): it was produced, reduced, padded, serialized and shape-asserted
but never read by any computation, since build_modf_matrices and
apply_modf_matrices only ever took branches.
Note: static_information.hdf5 no longer contains multi_outage_nodes_{idx}.
Old code reads new files fine (lookup by key), but older code cannot read
files written after this change.
New end-to-end regression:
tests/preprocessing/test_complex_contingency_end_to_end.py covers
contingency_list_complex.json -> convert_file -> canonical -> projection ->
PowsyblBackend -> NetworkData/StaticInformation and pins the switch-collapse
table (a line + its breakers projects to a single branch outage; a converted
3W transformer's three legs project to a 3-branch multi-outage; a group with
no DC-supported element is dropped).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
…pipeline create_nminus1_definition() now dispatches on importer_parameters.schema_format: for "ContingencyImportSchemaComplex" it calls load_nminus1_definition_from_file() (added in a previous commit) instead of deriving the definition from masks, using the mask-derived definition only for its monitored_elements and base_case. make_masks() becomes a no-op for the complex schema, since a mask cannot express grouped N-k outages -- it stays the importer's internal derivation step for every other input, never a transport to DC. save_shared_nminus1_definition() replaces the direct save_pydantic_model_fs() call in convert_file() / compute_network_masks_and_n_1_definition(): it persists the canonical definition and immediately reloads it, asserting the round trip is lossless, so a serialization gap can never silently change what DC and CA read back. BaseImporterParameters.schema_format gains the "ContingencyImportSchemaComplex" literal alongside the existing PowerFactory and CSV schemas. New regression: test_convert_file_complex_contingencies_persists_grouped_definition drives convert_file() over data/complex_grid/contingency_list_complex.json end to end and asserts the persisted definition keeps every grouped contingency, its SPPS rules, and source_schema == "complex". Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
…evaluation Neither AC backend may execute a complex list's SPPS rules in this work: Pandapower would feed them to its SPPS engine and Powsybl has no engine for them at all. get_ac_loadflow_results() now strips spps_rules from an in-memory copy (copy_without_spps_rules) whenever n_minus_1_definition.source_schema == "complex"; the canonical definition on disk keeps its rules untouched. For every other input, a mask-derived definition auto-generates one contingency per switch in the N-1 area, so a node-breaker grid yields a contingency for every disconnector -- these outage only the equipment behind them, cannot converge, and used to make CA's ">len/2" non-convergence guard abort every run once DC stopped silently shrinking the definition it forwarded. get_ac_loadflow_results() and the AC optimizer's definition load now both apply copy_without_switch_only_contingencies() to drop these, gated on source_schema != "complex" so a curated list's deliberate switch-only case is kept. Both call sites need the filter independently: the topology optimizer reaches Powsybl CA through PowsyblRunner and bypasses get_ac_loadflow_results() entirely. Nminus1Definition gains the two copy helper functions used above (copy_without_spps_rules, copy_without_switch_only_contingencies); the source_schema field, the SPPS scheme-name validator and the widened condition_limit_value type were added by the parser commit. BaseGrid gains a sanitize_spps_rules flag for message-level callers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
…notebook Update docs/dc_solver/preprocessing.md, docs/dc_solver/quickstart.md and docs/usage.md to describe nminus1_definition.json as the DC solver's outage source instead of the *_for_nminus1 masks. Add notebooks/example4_data_contracts.ipynb, walking through importer -> canonical definition -> DC projection -> CA for a complex grouped-contingency import. Remove the unreferenced data/test_station.json fixture. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Signed-off-by: Sascha Petznick <229719644+spetznick-elia@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The AC coverage assertion can pass when unsupported contingencies are skipped.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds end-to-end tests for AC validation after DC multi-outage support.
Changes:
- Adds a two-line imported outage fixture.
- Verifies DC scope and multi-outage metadata.
- Compares AC security results with brute-force AC loadflows.
File summaries
| File | Description |
|---|---|
packages/dc_solver_pkg/tests/postprocessing/test_ac_dc_multi_outage.py |
Adds multi-outage preprocessing, scope, and AC validation tests. |
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+181
to
+182
| ac_converged = ac_results.converged.filter(pl.col("timestep") == 0).select("contingency").unique().collect() | ||
| ac_ids = set(ac_converged["contingency"].to_list()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds a test for AC validation after introducing proper multi-outage support in DC in #723
No changes in AC validation are required as the Powsybl backend already supports multi-outages.
Checklist
Please check if the PR fulfills these requirements:
Does this PR already have an issue describing the problem?
Fixes #
What is the new behavior (if this is a feature change)?
Does this PR introduce a breaking change?