-
Notifications
You must be signed in to change notification settings - Fork 14
[BUG] ContingencyAnalysisCPP converges while the powerflow diverge #129
Copy link
Copy link
Open
Labels
bugSomething isn't workingSomething isn't working
Description
Environment
- Grid2op version:
1.12.3 - lightsim version:
0.12.1 - System: Oracle Linux Server 8.10
uv venv --python 3.12.9
uv pip install grid2op==1.12.3 lightsim2grid==0.12.1
Bug description
When doing the contingency analysis using ContingencyAnalysisCPP, we obtain powerflow result that seems valid although the powerflow didn't converge. However, when using grid2op env with lightsim backend, the init_state with the disconnected line (CHALOL61CPVAN, l_id = 42) seems to lead to powerflow divergence.
In the example bellow, we intentionally use a 'bad' N-1 init state, that diverges. At N, the powerflow is not diverging however when we create N-1, it diverges. The ContingencyAnalysisCPP returned valid flows with overflow on line CHALOL31LOUHA therefore we saved this N-1 init state although it is faulty.
How to reproduce
Code snippet
import json
from pathlib import Path
from typing import Any
import grid2op
import structlog
from grid2op.Chronics import ChangeNothing
from grid2op.Parameters import Parameters as Grid2OpParameters
from lightsim2grid import LightSimBackend
from lightsim2grid_cpp import ContingencyAnalysisCPP
def build_env() -> Any:
"""Build a grid2op environment from a user-provided env path.
"""
path_env = "/DSIA/devilderali/env_dijon_v2_training"
params = Grid2OpParameters()
params.init_from_dict(dict_={"ENV_DOES_REDISPATCHING": False})
gen_slack_id = [
"N.SE17GROUP.1",
"N.SE27GROUP.2",
"BUGEY7G2",
"BUGEY7G3",
"BUGEY7G4",
"BUGEY7G5",
]
lightsim2grid_loader_kwargs = {
"use_buses_for_sub": False,
"use_grid2op_default_names": False,
"reconnect_disco_gen": False,
"reconnect_disco_load": False,
"sort_index": True,
"n_busbar_per_sub": 6,
}
backend_arguments = {
"loader_method": "pypowsybl",
"loader_kwargs": lightsim2grid_loader_kwargs,
"max_iter": 1000,
"tol": 1e-8,
"turned_off_pv": False,
"dist_slack_non_renew": False,
"gen_slack_id": gen_slack_id,
"use_static_gen": False,
"stop_if_load_disco": None,
"stop_if_gen_disco": None,
"stop_if_storage_disco": None,
"automatically_disconnect": False,
}
n_busbar = backend_arguments["loader_kwargs"]["n_busbar_per_sub"]
backend = LightSimBackend(
**backend_arguments,
)
env = grid2op.make(
path_env,
allow_detachment=True,
backend=backend,
n_busbar=n_busbar,
chronics_class=ChangeNothing,
param=params,
)
return env
def load_init_state(json_path: Path) -> dict:
with open(json_path, "rt", encoding="utf-8") as f:
data = json.load(f)
return data
def write_init_state(json_path: Path, content: dict) -> None:
with open(json_path, "wt", encoding="utf-8") as f:
json.dump(content, f, indent=2, ensure_ascii=False)
def set_line_bus(init_state: dict, line_id: str, bus_value: int) -> None:
"""Set both OR / EX bus for a given line."""
init_state["init state"]["set_bus"]["lines_or_id"][line_id] = bus_value
init_state["init state"]["set_bus"]["lines_ex_id"][line_id] = bus_value
def main() -> None:
"""Run the N / N-1 check and contingency analysis on a given env path.
"""
logger = structlog.get_logger()
base_json_path = Path("_20221112-0115_CHALOL61CPVAN.json")
# Load original N-1 problematic state
init_state_json = load_init_state(base_json_path)
line_id = "CHALOL61CPVAN"
# 1) Change to N state (line back in service) and try to reset the env
set_line_bus(init_state_json, line_id=line_id, bus_value=1)
env = build_env()
logger.info("Resetting env with N-state (line in service)...")
try:
obs = env.reset(options={"init state": init_state_json["init state"]})
logger.info("N-state reset converged", obs_type=type(obs))
except Exception as exc: # noqa: BLE001
logger.warning("N-state reset FAILED to converge:", exc=repr(exc))
# 2) Put the line back to -1 (out of service) and reset again
set_line_bus(init_state_json, line_id=line_id, bus_value=-1)
logger.info("Resetting env with N-1 state (line out of service)...")
try:
obs = env.reset(options={"init state": init_state_json["init state"]})
logger.info("N-1 reset converged UNEXPECTEDLY", obs_type=type(obs))
except Exception as exc: # noqa: BLE001
logger.warning("N-1 reset failed to converge as expected:", exc=repr(exc))
# 3) ContingencyAnalysisCPP comparison on backend grid
logger.info("Running ContingencyAnalysisCPP on backend grid...")
backend_grid = env.backend._grid # type: ignore[attr-defined]
computer = ContingencyAnalysisCPP(backend_grid)
# l_id=42 is assumed to be CHALOL61CPVAN according to your remark
computer.add_n1(42)
v_init = 1.0 * env.backend.V # type: ignore[attr-defined]
max_it = getattr(env.backend, "max_it", 20)
tol = getattr(env.backend, "tol", 1e-8)
computer.compute(v_init, max_it, tol)
_ = computer.get_voltages()
computer.compute_flows()
_amps = 1e3 * computer.get_flows()
logger.info("Contingency analysis completed.", _amps=_amps)
if __name__ == "__main__":
print("Launching n1_state_check main()...")
main()Current output
The current output is:
2026-03-27 12:37:52 [info ] Resetting env with N-state (line in service)...
2026-03-27 12:37:52 [info ] N-state reset converged obs_type=<class 'grid2op.Space.GridObjects.ObservationAF2024_env_dijon_v2_trainingLightSimBackend_6_allowDetach'>
2026-03-27 12:37:52 [info ] Resetting env with N-1 state (line out of service)...
2026-03-27 12:37:52 [warning ] N-1 reset failed to converge as expected: exc='Grid2OpException Grid2OpException("Impossible to initialize the powergrid, the powerflow diverge at iteration 0. Available information are: {\'disc_lines\': array([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\\n -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\\n -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\\n -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\\n -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\\n -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\\n -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\\n -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\\n -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\\n -1, -1, -1, -1, -1, -1, -1], dtype=int32), \'is_illegal\': False, \'is_ambiguous\': False, \'failed_redispatching\': False, \'is_illegal_reco\': False, \'reason_alarm_illegal\': None, \'reason_alert_illegal\': None, \'opponent_attack_line\': None, \'opponent_attack_sub\': None, \'opponent_attack_duration\': 0, \'exception\': [Grid2OpException BackendError BackendError(\'Divergence of AC powerflow. Detailed error: ErrorType.TooManyIterations\')], \'time_series_id\': None, \'rewards\': {}}")'
2026-03-27 12:37:52 [info ] Running ContingencyAnalysisCPP on backend grid...
2026-03-27 12:37:52 [info ] Contingency analysis completed. _amps=array([[1.17727103e+01, 3.94480422e+01, 6.48559203e+01, 6.80139859e+01,
2.08241015e-02, 7.87608265e+01, 8.28089463e+01, 8.29597174e+01,
8.29597174e+01, 3.94280573e+01, 4.97021740e+01, 1.24445688e+03,
1.87252329e+03, 1.24434261e+03, 1.85859200e+03, 0.00000000e+00,
1.77459483e+02, 1.31646964e+03, 1.22632106e+03, 5.64175887e+01,
1.20197632e+03, 5.15018600e+01, 0.00000000e+00, 0.00000000e+00,
2.37228176e+02, 2.05270877e+02, 2.27772449e+02, 2.38915696e+02,
1.34832449e+02, 7.66473311e+01, 7.72293572e+01, 2.31952087e+02,
2.29944313e+02, 2.16770016e+02, 1.14483151e+02, 7.75370603e+01,
2.62266046e+02, 2.71985343e+02, 3.96841915e+02, 3.89465406e+02,
5.82506909e+02, 3.35093319e+02, 0.00000000e+00, 2.85185469e+02,
2.85190037e+02, 6.20648167e+01, 5.97891048e+01, 6.10794335e+01,
3.72156441e+00, 2.91862969e-12, 0.00000000e+00, 2.20399881e+02,
7.71261631e+01, 1.98464367e+02, 1.43031645e+02, 5.24962355e+01,
2.30534202e+02, 2.16281316e+02, 7.19097379e+01, 1.53260021e+02,
1.04782409e+02, 1.53285668e+02, 5.08151497e+02, 6.10907179e+02,
8.27218102e+02, 1.62768541e+03, 8.26051380e+02, 1.62869076e+03,
1.03428410e+02, 6.66275791e+01, 6.29111248e+00, 0.00000000e+00,
8.09099180e+01, 1.61417576e+02, 1.59781912e+02, 3.35180262e+02,
0.00000000e+00, 1.15373222e+02, 2.86819059e+01, 0.00000000e+00,
3.60419434e-01, 3.60261754e-01, 3.95578479e-01, 0.00000000e+00,
0.00000000e+00, 6.99603466e+02, 6.08111161e+02, 7.44851645e+01,
1.50041360e+02, 1.21315647e+02, 3.77451254e+01, 1.83378982e+02,
3.41376747e+01, 4.83199241e+02, 2.80524388e+01, 1.85431893e+02,
2.26169128e+02, 2.26169128e+02, 6.66901431e+01, 7.79735433e+01,
2.04829522e+02, 1.35432582e+02, 6.69994239e+02, 6.70387506e+02,
1.24380324e+02, 7.69856742e+01, 1.12603091e+02, 3.21264742e+02,
7.21588120e+01, 1.70927661e+01, 6.57526850e+01, 8.54097806e+01,
5.96526758e+01, 1.11411063e+02, 5.08825360e+01, 1.31646964e+03,
1.22632106e+03, 5.64175887e+01, 1.20197632e+03, 8.67573338e+01,
8.67495980e+01, 1.89952063e+02, 1.91465486e+02, 1.92455027e+02,
2.04887537e+01, 5.77660769e+01, 4.81745829e+01, 6.99861519e+01,
4.67452146e+01, 7.36204071e+01, 4.74323274e+02, 0.00000000e+00,
0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
0.00000000e+00, 0.00000000e+00, 2.96925080e+02, 2.45329917e+02,
2.72628806e+02, 9.52325838e+01, 0.00000000e+00, 7.69856742e+01,
1.87279537e+03, 1.85877491e+03, 4.05956919e+01, 5.70278127e+01,
9.20613143e+01, 0.00000000e+00, 3.18176001e+01, 3.19592476e+01,
8.42168676e+01, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
1.10254511e+00, 1.19707929e+01, 1.00932223e+02, 6.73117091e+01]])
Expected output
We want the contingency analysis to fail due to powerflow divergence.
_amps=array([[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]])
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working