Skip to content

feat: use limit violation table - #888

Draft
BenjPetr wants to merge 4 commits into
mainfrom
feat/use_limit_violation_table
Draft

BenjPetr wants to merge 4 commits into
mainfrom
feat/use_limit_violation_table

Conversation

@BenjPetr

@BenjPetr BenjPetr commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Checklist

Please check if the PR fulfills these requirements:

  • PR Title follows conventional commit messages
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)
  • All commits in this PR are DCO signed-off (see CONTRIBUTING.md)

Does this PR already have an issue describing the problem?

Fixes #

What is the new behavior and why?

  • adds pypowsybl_limit_table, which runs a SA using only the limit violation table logic

Does this PR introduce a breaking change?

  • Yes, because
  • No

Signed-off-by: BenjPetr <170433522+BenjPetr@users.noreply.github.com>
Signed-off-by: BenjPetr <170433522+BenjPetr@users.noreply.github.com>
Signed-off-by: BenjPetr <170433522+BenjPetr@users.noreply.github.com>
Signed-off-by: BenjPetr <170433522+BenjPetr@users.noreply.github.com>
@sonarqube-mccs

sonarqube-mccs Bot commented Nov 4, 2026

Copy link
Copy Markdown

"""The contingencies to run, translated one to one. A contingency without elements is the base case."""

missing_contingencies: list[Contingency] = []
"""Contingencies whose elements are (partially) not present in the network. They are not run."""

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Id say if we have a multi outage of line1/line2/line3 and line1 is not there we should outage the other two, no?

#: Maps the status names of the pre and post contingency results to the convergence status of the results.
POWSYBL_STATUS_NAME_MAP = {
"CONVERGED": ConvergenceStatus.CONVERGED.value,
"NO_IMPACT": ConvergenceStatus.CONVERGED.value,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isnt this similar to NO_CALCULATION? So maybe we should add it as a status, but consider it converged in the ac worker?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah wait, is NO_CALCULATION deprecated and replaced by NO_IMPACT in powsybl?



@runtime_checkable
class PostContingencyStatus(Protocol):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from pypowsybl._pypowsybl import PostContingencyResult
does not work?

.then(deviation / (pl.col("limit") - pl.col("nominal_v")))
.when(pl.col("limit_type") == LOW_VOLTAGE_VIOLATION)
.then(deviation / (pl.col("nominal_v") - pl.col("limit")))
.when(is_va_diff)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this not the same as for the branches?

"""Build the convergence table from the native pre and post contingency results.

Every requested contingency gets a row, whether or not powsybl computed it. Contingencies without a result are
reported as NO_CALCULATION, contingencies without impact as CONVERGED with a note in the warnings column.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Contingencies dont have a result, because no overloads occuring right? Than i would choose something else than NO_CALCULATION. Since it was calculated, I would prefer NO_IMPACT or rather a new status. NON_CRITICAL / FILTERED / DISCARDED or something.

NO_CALCULATION are usually already disconnected lines that are outaged, as far as i know. So i would not mix those up.



@dataclass
class PowsyblLimitTableRun:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could also inherit from pypowsybl.security.SecurityAnalysisResult to make the connection clearer?

"""Read the limit violation table, see get_ca_limit_violations."""
return get_ca_limit_violations(self.handle, lazy=True)

def branch_results(self) -> pl.LazyFrame:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the original these are propertys

voltage_level_ids=definition.voltage_level_ids or None,
)
# only CURRENT limit reduction is supported by powsybl
analysis.add_limit_reductions(limit_type="CURRENT", permanent=True, temporary=True, value=limit_reduction)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACTIVE_POWER and APPARENT_POWER as well right? Dont have those right now, but might as well add them?

va_diff_results=VADiffResultSchemaPolars.empty().lazy(),
converged=add_name_column_polars(converged, definition.contingency_name_mapping, index_level="contingency"),
connectivity_result=connectivity_result,
**{name: update_basename_polars(frame, basecase_id) for name, frame in violations.items()},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer splitting the violations into the separate violations that are returned. More readable imo.

get_regulating_element_results([], timestep=timestep), include_index=True, nan_to_null=False
).lazy()

def finalize(frame: pl.LazyFrame) -> pl.LazyFrame:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Id call it something else, like add_readable_names or sth in that direction. And maybe move it outside of the function

def _batch_contingencies(
definition: PowsyblLimitTableDefinition, batch_size: Optional[int]
) -> list[PowsyblLimitTableDefinition]:
"""Split the definition into sequential batches, the base case riding with the first one."""

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing arg documentation

from toop_engine_contingency_analysis.pypowsybl_limit_table.extract import NO_IMPACT_WARNING
from toop_engine_interfaces.loadflow_results_polars import LimitViolationSchemaPolars

NAN = float("nan")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somewhere else you used _NAN

def _filter_limit_violations(
loadflow_results: LoadflowResultsPolars, condition: pl.Expr
) -> dict[str, Optional[pl.LazyFrame]]:
"""Filter every limit violation frame of the results, keeping the absent ones None."""

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docs

@BenjPetr BenjPetr changed the title Feat/use limit violation table feat: use limit violation table Nov 5, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants