feat: implement non linear pst#615
Conversation
Signed-off-by: Benjamin Petrick <170433522+BenjPetr@users.noreply.github.com>
Signed-off-by: Benjamin Petrick <170433522+BenjPetr@users.noreply.github.com>
…ic_flows() as part of an action Signed-off-by: Benjamin Petrick <170433522+BenjPetr@users.noreply.github.com>
… anything anymore Signed-off-by: Benjamin Petrick <170433522+BenjPetr@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds runtime support for non-linear phase-shifting transformers (PSTs) in the DC solver by introducing per-tap branch-parameter (susceptance) handling, along with a small terminology cleanup in the topology optimizer where PST-related results are renamed for clarity.
Changes:
- Add backend + preprocessing support for per-tap PST susceptance and keep non-linear PSTs controllable through preprocessing.
- Introduce JAX-side PST helpers and a low-rank PTDF update path for tap-dependent branch-parameter changes.
- Rename
nodal_injections_optimized→pst_tap_resultsacross topology optimizer + solver outputs and update tests accordingly.
Reviewed changes
Copilot reviewed 29 out of 29 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/topology_optimizer_pkg/tests/dc/repertoire/test_discrete_me_repertoire.py | Updates genotype construction to use pst_tap_results. |
| packages/topology_optimizer_pkg/tests/dc/repertoire/test_discrete_map_elites.py | Updates PST optimization assertions/accessors to pst_tap_results. |
| packages/topology_optimizer_pkg/tests/dc/genetic_functions/test_initialization.py | Adjusts test genotypes to use pst_tap_results. |
| packages/topology_optimizer_pkg/tests/dc/genetic_functions/test_evolution_functions.py | Updates deduplication-related assertions for PST results. |
| packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/scoring_functions.py | Propagates PST tap results through scoring + topology conversion. |
| packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/mutation/mutate.py | Mutations now thread through pst_tap_results. |
| packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/genotype.py | Renames genotype field and keeps PST taps in deduplication. |
| packages/topology_optimizer_pkg/src/toop_engine_topology_optimizer/dc/genetic_functions/crossover.py | Carries PST tap results through crossover. |
| packages/interfaces_pkg/src/toop_engine_interfaces/backend.py | Adds default interface method to provide per-tap susceptance values. |
| packages/dc_solver_pkg/tests/preprocessing/test_preprocess.py | Updates expectations: non-linear PSTs remain controllable. |
| packages/dc_solver_pkg/tests/postprocessing/test_validate_loadflow_results.py | Adds validation coverage for non-linear PST tap scenarios. |
| packages/dc_solver_pkg/tests/postprocessing/test_postprocess_powsybl.py | Expands PST validation to cover non-linear PST grids and scenarios. |
| packages/dc_solver_pkg/tests/jax/test_pst.py | New focused tests for PST helper logic (linear, parallel, non-linear). |
| packages/dc_solver_pkg/tests/jax/test_nodal_inj_optim.py | Removes legacy tests tied to the old nodal-injection optimization path. |
| packages/dc_solver_pkg/tests/jax/test_aggregate_results.py | Renames loadflow result field usage to pst_tap_results. |
| packages/dc_solver_pkg/src/toop_engine_dc_solver/preprocess/preprocess.py | Preserves non-linear PST controllability; reduces PST susceptance tap lists with branch reduction. |
| packages/dc_solver_pkg/src/toop_engine_dc_solver/preprocess/powsybl/powsybl_backend.py | Uses has_pst_tap for controllable mask; computes per-tap susceptance for Powsybl PSTs. |
| packages/dc_solver_pkg/src/toop_engine_dc_solver/preprocess/pandapower/pandapower_backend.py | Adds per-tap susceptance values (constant across taps for PandaPower path). |
| packages/dc_solver_pkg/src/toop_engine_dc_solver/preprocess/network_data.py | Adds phase_shift_susceptance_taps to NetworkData and validates alignment. |
| packages/dc_solver_pkg/src/toop_engine_dc_solver/preprocess/convert_to_jax.py | Carries per-tap susceptance + linearity metadata into JAX NodalInjectionInformation. |
| packages/dc_solver_pkg/src/toop_engine_dc_solver/postprocess/validate_loadflow_results.py | Tightens default validation tolerances and exposes parameter model in tests. |
| packages/dc_solver_pkg/src/toop_engine_dc_solver/jax/types.py | Extends NodalInjectionInformation and renames solver output field to pst_tap_results. |
| packages/dc_solver_pkg/src/toop_engine_dc_solver/jax/pst.py | New PST helper module to prepare taps, write angles, and update N-0 flows. |
| packages/dc_solver_pkg/src/toop_engine_dc_solver/jax/nodal_inj_optim.py | Reduces module scope to start-option helper(s) only. |
| packages/dc_solver_pkg/src/toop_engine_dc_solver/jax/inputs.py | Persists/loads new PST metadata fields and validates shapes. |
| packages/dc_solver_pkg/src/toop_engine_dc_solver/jax/compute_batch.py | Applies PST tap state during batch solve and updates PTDF for non-linear PST susceptance changes. |
| packages/dc_solver_pkg/src/toop_engine_dc_solver/jax/branch_parameter_changes.py | New low-rank PTDF update for simultaneous branch-parameter changes (e.g., PST tap-dependent susceptance). |
| packages/dc_solver_pkg/src/toop_engine_dc_solver/jax/aggregate_results.py | Updates PST metric aggregation to use pst_tap_results. |
| benchmarks/benchmark_run_single_device_epoch.py | Updates benchmark plumbing for renamed PST result field. |
|
changes:
tldr: no injections are optimized, psts are now better represented as an action and include branch parameter updates |
| representation. | ||
| """ | ||
|
|
||
| phase_shift_susceptance: Float[Array, " n_phase_shifters"] |
There was a problem hiding this comment.
Should we rename this to pst_susceptance
| class NodalInjectionInformation(eqx.Module): | ||
| """Holds the nodal injection optimization data required by the DC solver.""" | ||
|
|
||
| phase_shift_branch_indices: Int[Array, " n_phase_shifters"] |
There was a problem hiding this comment.
What is the difference between this and the controllable_pst_indices if now all PSTs should be controllable?
spetznick-elia
left a comment
There was a problem hiding this comment.
Overall incredible! I think the data structures need some refactoring: We introduce the nonlinear PSTs into the set of controllable ones. We do no need to distinguish between them anymore.
|
|
||
|
|
||
| def test_exclude_nonlinear_psts_from_controllable_drops_nonlinear_group_member( | ||
| def test_exclude_nonlinear_psts_from_controllable_keeps_nonlinear_group_member( |
There was a problem hiding this comment.
this should be renamed I guess
Signed-off-by: Benjamin Petrick <170433522+BenjPetr@users.noreply.github.com>
There was a problem hiding this comment.
somewhat similar to modf, but a different file, as it allows for updated_susceptance instead of a full outage delta
There was a problem hiding this comment.
this is part one of a pst change
- Paramter change -> non linear pst
- injection change -> PSDF
There was a problem hiding this comment.
nothing is optimized anymore -> move to pst file
There was a problem hiding this comment.
all nodal pst functions
| @@ -1286,10 +1294,10 @@ def compute_separation_set_for_stations( | |||
|
|
|||
|
|
|||
| def exclude_nonlinear_psts_from_controllable(network_data: NetworkData) -> NetworkData: | |||
There was a problem hiding this comment.
this can be removed?
There was a problem hiding this comment.
I think in my latest fix i also excluded them directly in the backend. So this willhave t be adjusted
| The list order matches phase_shift_taps and controllable_phase_shift_mask. | ||
| """ | ||
|
|
||
| phase_shift_linearity: Bool[np.ndarray, " n_controllable_pst"] |
There was a problem hiding this comment.
can stay for now, if we want to do nodal optimization later on again for linear + HVDC
Signed-off-by: Benjamin Petrick <170433522+BenjPetr@users.noreply.github.com>
…_battery_hvdc_svc_3w_trafo() Signed-off-by: Benjamin Petrick <170433522+BenjPetr@users.noreply.github.com>
| alphas = np.linspace(alpha_min, alpha_max, len(taps)) | ||
| x_vals = np.abs(np.linspace(x_min, x_max, len(taps))) | ||
| r_vals = np.abs(np.linspace(r_min, r_max, len(taps))) | ||
| rho_vals = np.ones(len(taps)) if is_linear else np.linspace(rho_min, rho_max, len(taps)) |
There was a problem hiding this comment.
Note: This addition makes sure asymmetrical PST work as well.
The way the taps are imported make this change automatically possible with no further changes
see get_phase_shift_susceptance_taps()
effective_x_taps
| @@ -57,17 +57,14 @@ def __getitem__(self, key: Union[slice, int, jnp.ndarray]) -> "MODFMatrix": | |||
|
|
|||
|
|
|||
| class NodalInjectionInformation(eqx.Module): | |||
There was a problem hiding this comment.
This is now rather a PstInformation?
Signed-off-by: Benjamin Petrick <170433522+BenjPetr@users.noreply.github.com>
|




Signed-off-by: Benjamin Petrick 170433522+BenjPetr@users.noreply.github.com
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?