From 929fd277ccf33a1d1bead943cdd4040df2a0b837 Mon Sep 17 00:00:00 2001 From: tsybinae <186317713+tsybinae@users.noreply.github.com> Date: Thu, 8 Oct 2026 13:29:55 -0700 Subject: [PATCH 1/2] AgentiGrid: one config for ExaGO and GridKit, GridKit from the latest ExaGO steady state - ExaGO code moved to exago_engine/ and exago_parsers/ (imports, test patch targets and logger names updated); ExaGO output in workdir/exago/ and logs/exago/; OPFLOW and PFLOW save their solved case (-save_output). - One shared config: new gridkit: section (binary, timeout, solver .json values, default fault start), read by the GridKit engine; warnings for unknown solver keys and invalid values. Defaults keep the old behaviour. - New gridkit_engine/from_exago.py: a fault screen with "start_from": "latest_steady_state" takes voltages, angles, dispatch, generator outages, loads, shunts and branches from the last simulation of the newest ExaGO journal and applies them to the GridKit case's dynamic models; error for online generators without a machine model. --from-exago forces it for a session. - Fault screens can run with GridKit ContingencyAnalysis (one process for all faults of a screen). - GridKit prompt: a fault at a bus means that bus only, unless the goal asks for nearby buses. - New tests: tests/gridkit/test_config.py, test_from_exago.py, test_screen_options.py. Co-Authored-By: Claude Opus 5.5 --- agentigrid/README.md | 31 +- agentigrid/agentigrid/cli.py | 52 +++- agentigrid/agentigrid/config.py | 89 +++++- .../{engine => exago_engine}/__init__.py | 26 +- .../{engine => exago_engine}/agent_loop.py | 64 ++-- .../{engine => exago_engine}/benchmark.py | 16 +- .../{engine => exago_engine}/commands.py | 2 +- .../{engine => exago_engine}/contingency.py | 8 +- .../{engine => exago_engine}/executor.py | 21 +- .../{engine => exago_engine}/explore.py | 18 +- .../goal_classifier.py | 2 +- .../{engine => exago_engine}/journal.py | 4 +- .../metric_extractor.py | 4 +- .../{engine => exago_engine}/modifier.py | 8 +- .../objective_parser.py | 2 +- .../{engine => exago_engine}/pareto.py | 4 +- .../regenerate_report.py | 8 +- .../{engine => exago_engine}/relief.py | 8 +- .../{engine => exago_engine}/reserve.py | 0 .../schema_description.py | 0 .../{engine => exago_engine}/session_io.py | 10 +- .../{engine => exago_engine}/sweep_metrics.py | 0 .../{engine => exago_engine}/topology.py | 2 +- .../{engine => exago_engine}/validation.py | 6 +- .../{parsers => exago_parsers}/__init__.py | 38 +-- .../dcopflow_summary.py | 2 +- .../matpower_model.py | 0 .../matpower_parser.py | 4 +- .../matpower_writer.py | 4 +- .../network_metadata.py | 2 +- .../network_summary.py | 2 +- .../opflow_parser.py | 4 +- .../opflow_results.py | 0 .../pflow_parser.py | 6 +- .../pflow_summary.py | 2 +- .../results_summary.py | 2 +- .../scopflow_parser.py | 6 +- .../scopflow_summary.py | 2 +- .../sopflow_dispatch.py | 4 +- .../sopflow_parser.py | 8 +- .../sopflow_summary.py | 2 +- .../tcopflow_parser.py | 8 +- .../tcopflow_summary.py | 2 +- .../agentigrid/gridkit_engine/agent_loop.py | 165 ++++++++-- .../agentigrid/gridkit_engine/commands.py | 25 +- .../agentigrid/gridkit_engine/executor.py | 83 ++++- .../agentigrid/gridkit_engine/from_exago.py | 288 ++++++++++++++++++ .../agentigrid/gridkit_engine/modifier.py | 17 +- .../gridkit_engine/schema_description.py | 22 +- .../agentigrid/gridkit_engine/settings.py | 43 ++- .../gridkit_parsers/status_parser.py | 3 +- .../prompts/gridkit/system_prompt.py | 13 +- agentigrid/agentigrid_architecture.md | 16 +- .../configs/default_config.yaml.template | 25 ++ agentigrid/configs/local_config.yaml.template | 25 ++ agentigrid/launcher/ARCHITECTURE.md | 6 +- agentigrid/launcher/app.py | 4 +- agentigrid/launcher/charts.py | 4 +- agentigrid/launcher/report_generator.py | 10 +- agentigrid/launcher/session_manager.py | 10 +- agentigrid/tests/README.md | 10 +- .../tests/agent_loop/test_agent_loop.py | 42 +-- agentigrid/tests/agent_loop/test_steering.py | 6 +- .../tests/analyses/test_boundary_sweep.py | 18 +- agentigrid/tests/analyses/test_contingency.py | 36 +-- agentigrid/tests/analyses/test_explore.py | 116 +++---- agentigrid/tests/analyses/test_relief.py | 28 +- agentigrid/tests/analyses/test_reserve.py | 26 +- agentigrid/tests/analyses/test_reserve_min.py | 20 +- .../tests/analyses/test_sweep_corrections.py | 22 +- .../tests/analyses/test_sweep_llm_view.py | 2 +- .../analyses/test_sweep_metrics_dispatch.py | 30 +- .../tests/analyses/test_sweep_reporting.py | 2 +- agentigrid/tests/analyses/test_topology.py | 14 +- .../tests/commands/test_cow_modifier.py | 24 +- agentigrid/tests/commands/test_engine.py | 12 +- agentigrid/tests/e2e/test_e2e.py | 6 +- agentigrid/tests/exago/test_benchmark.py | 22 +- agentigrid/tests/exago/test_dcopflow.py | 24 +- agentigrid/tests/exago/test_pflow.py | 34 +-- agentigrid/tests/exago/test_scopflow.py | 8 +- agentigrid/tests/exago/test_sopflow.py | 34 +-- .../tests/exago/test_sopflow_empar_masking.py | 22 +- .../exago/test_sopflow_report_absorption.py | 18 +- agentigrid/tests/exago/test_tcopflow.py | 24 +- agentigrid/tests/grid_files/test_parsers.py | 4 +- agentigrid/tests/gridkit/test_config.py | 117 +++++++ agentigrid/tests/gridkit/test_from_exago.py | 196 ++++++++++++ .../tests/gridkit/test_screen_options.py | 177 +++++++++++ .../tests/gridkit/test_status_parser.py | 6 + agentigrid/tests/journal/test_journal.py | 10 +- .../tests/journal/test_multi_objective.py | 8 +- agentigrid/tests/journal/test_pareto.py | 4 +- .../tests/journal/test_regenerate_report.py | 14 +- agentigrid/tests/journal/test_session_io.py | 4 +- .../journal/test_solve_iteration_counting.py | 6 +- .../tests/results/test_opflow_parser.py | 6 +- .../tests/results/test_prompt_size_bounded.py | 16 +- .../results/test_scenario_voltage_spread.py | 10 +- .../runner/test_exago_command_logging.py | 16 +- agentigrid/tests/runner/test_executor.py | 18 +- 101 files changed, 1862 insertions(+), 592 deletions(-) rename agentigrid/agentigrid/{engine => exago_engine}/__init__.py (63%) rename agentigrid/agentigrid/{engine => exago_engine}/agent_loop.py (98%) rename agentigrid/agentigrid/{engine => exago_engine}/benchmark.py (95%) rename agentigrid/agentigrid/{engine => exago_engine}/commands.py (99%) rename agentigrid/agentigrid/{engine => exago_engine}/contingency.py (98%) rename agentigrid/agentigrid/{engine => exago_engine}/executor.py (96%) rename agentigrid/agentigrid/{engine => exago_engine}/explore.py (96%) rename agentigrid/agentigrid/{engine => exago_engine}/goal_classifier.py (99%) rename agentigrid/agentigrid/{engine => exago_engine}/journal.py (99%) rename agentigrid/agentigrid/{engine => exago_engine}/metric_extractor.py (97%) rename agentigrid/agentigrid/{engine => exago_engine}/modifier.py (99%) rename agentigrid/agentigrid/{engine => exago_engine}/objective_parser.py (98%) rename agentigrid/agentigrid/{engine => exago_engine}/pareto.py (96%) rename agentigrid/agentigrid/{engine => exago_engine}/regenerate_report.py (97%) rename agentigrid/agentigrid/{engine => exago_engine}/relief.py (98%) rename agentigrid/agentigrid/{engine => exago_engine}/reserve.py (100%) rename agentigrid/agentigrid/{engine => exago_engine}/schema_description.py (100%) rename agentigrid/agentigrid/{engine => exago_engine}/session_io.py (97%) rename agentigrid/agentigrid/{engine => exago_engine}/sweep_metrics.py (100%) rename agentigrid/agentigrid/{engine => exago_engine}/topology.py (99%) rename agentigrid/agentigrid/{engine => exago_engine}/validation.py (98%) rename agentigrid/agentigrid/{parsers => exago_parsers}/__init__.py (86%) rename agentigrid/agentigrid/{parsers => exago_parsers}/dcopflow_summary.py (98%) rename agentigrid/agentigrid/{parsers => exago_parsers}/matpower_model.py (100%) rename agentigrid/agentigrid/{parsers => exago_parsers}/matpower_parser.py (98%) rename agentigrid/agentigrid/{parsers => exago_parsers}/matpower_writer.py (97%) rename agentigrid/agentigrid/{parsers => exago_parsers}/network_metadata.py (98%) rename agentigrid/agentigrid/{parsers => exago_parsers}/network_summary.py (98%) rename agentigrid/agentigrid/{parsers => exago_parsers}/opflow_parser.py (99%) rename agentigrid/agentigrid/{parsers => exago_parsers}/opflow_results.py (100%) rename agentigrid/agentigrid/{parsers => exago_parsers}/pflow_parser.py (98%) rename agentigrid/agentigrid/{parsers => exago_parsers}/pflow_summary.py (98%) rename agentigrid/agentigrid/{parsers => exago_parsers}/results_summary.py (97%) rename agentigrid/agentigrid/{parsers => exago_parsers}/scopflow_parser.py (94%) rename agentigrid/agentigrid/{parsers => exago_parsers}/scopflow_summary.py (98%) rename agentigrid/agentigrid/{parsers => exago_parsers}/sopflow_dispatch.py (98%) rename agentigrid/agentigrid/{parsers => exago_parsers}/sopflow_parser.py (97%) rename agentigrid/agentigrid/{parsers => exago_parsers}/sopflow_summary.py (98%) rename agentigrid/agentigrid/{parsers => exago_parsers}/tcopflow_parser.py (97%) rename agentigrid/agentigrid/{parsers => exago_parsers}/tcopflow_summary.py (99%) create mode 100644 agentigrid/agentigrid/gridkit_engine/from_exago.py create mode 100644 agentigrid/tests/gridkit/test_config.py create mode 100644 agentigrid/tests/gridkit/test_from_exago.py create mode 100644 agentigrid/tests/gridkit/test_screen_options.py diff --git a/agentigrid/README.md b/agentigrid/README.md index 2a417db7..5d5c7212 100644 --- a/agentigrid/README.md +++ b/agentigrid/README.md @@ -56,7 +56,7 @@ Adding a generator supports two modes, chosen from the goal's wording (and named ### Custom Sweep Metric / Predicate (named registry) -Beyond the default cost metric and standard V-band/loading feasibility, a sweep can select a **named, verified primitive** (`agentigrid/engine/sweep_metrics.py`) — not free-form code. Selection is by name; the registry is extensible (`register_metric` / `register_predicate`) for later capabilities. +Beyond the default cost metric and standard V-band/loading feasibility, a sweep can select a **named, verified primitive** (`agentigrid/exago_engine/sweep_metrics.py`) — not free-form code. Selection is by name; the registry is extensible (`register_metric` / `register_predicate`) for later capabilities. - **`metric: "max_delta_v"`** — ranks buses by the worst *system-wide* voltage step `max_b |V_candidate[b] − V_base[b]|` caused by switching in a load block (`search.switched_load_mw`) at the candidate bus. A power-quality flag; the largest step need not be at the switched bus. Requires (and triggers) a one-time base-case solve for the reference voltages. - **`feasibility_predicate: "reactive_adequacy"`** — replaces standard feasibility with a reactive-headroom test: a feasible OPF must exist with the added unit forced to `(P = Pmax, Q = Qmax)` (the sweep pins `Qmin = Qmax`). Returns the limiting quantity when it fails. @@ -78,6 +78,28 @@ agentigrid --tool gridkit --config configs/local_config.yaml \ "Assume a short circuit fault at each bus of the system, tell me if the system can survive without blackout." ``` +**Starting from the latest ExaGO steady state.** A fault screen with +`"start_from": "latest_steady_state"` (the LLM sets it when the goal asks for "the most +recent steady state"; `--from-exago` forces it for the whole session) uses the GridKit +case only for the dynamic models; the operating point and network come from the last +simulation of the newest ExaGO journal in `workdir/exago/` (OPFLOW/PFLOW save their +solved case, `opflowout.m` / `pflowout.m`, in the iteration folder). Bus voltages, +machine `p0`/`q0`, loads, shunts and branches are taken from ExaGO; machines of +generators that are off in ExaGO are removed with their controls. That start gets its +own no-fault run before any fault. The screen is not run if that simulation did not +converge, was a sweep, or has an online generator without a dynamic model in the +GridKit case. The converted case is saved as `from_exago.case.json` in the session +folder. + +**ContingencyAnalysis.** With `"application": "ContingencyAnalysis"` (the LLM sets it +when the goal asks for contingency analysis), one GridKit `ContingencyAnalysis` process +runs all bus faults of the screen, writing `output_.csv` per fault in the +screen's `contingency/` folder; the checks and verdict are the same as with +`DynamicSimulation`. `ContingencyAnalysis` runs every fault device in the case file, so +its case copy holds only the requested faults (fault devices shipped with the case, +such as a demo fault, are left out of the copy). With `DynamicSimulation` they stay in +the copy but are never switched on. + Link the GridKit binaries and cases first (see [applications/gridkit/README.md](applications/gridkit/README.md) and [data/gridkit/README.md](data/gridkit/README.md)). Sessions are written to `workdir/gridkit/session_