Repository navigation
Cosimulation that passes information from ExaGO to GridKit - #144
Conversation
… 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 <[email protected]>
superwhiskers
left a comment
There was a problem hiding this comment.
ran test suite, all passed. replicated live run on my machine, same fault clear duration of ~.104s, simulated 15s total. same overall statistics (angle spread, damping)
samimk
left a comment
There was a problem hiding this comment.
Test suite passes, with GridKit linked locally. I also ran AgentiGrid with GridKit end to end from the console and it works.
A few comments:
- Launcher output folders
Launcher is not updated, and launcher/session_manager.py still uses plain load_config, so launcher studies still write to workdir/. That will be corrected when we update launcher and make it use exago_output().
- -save_output on every solve
OPFLOW/PFLOW now save the solved case on every run, including sweep and contingency solves. On large cases that’s a full .m file per solve. Could it be limited to single solves, or made configurable?
We do want to solve .m files for each solve because they contain the needed information. I agree it would be helpful to have ability to switch output off at instances. |
Answers the #144 review: OPFLOW/PFLOW saved a solved case on every solve, including sweep and contingency solves. New config key exago.save_solutions (also --set exago.save_solutions=<value>): - best (default): single solves keep their solution; an action with many solves keeps only its best (cheapest feasible sweep candidate or best by the sweep's metric, largest hosting capacity for boundary, Pareto-best explore variant), or the most recent if it has no best (contingency, reserve) - newest: multi-solve actions keep only the most recent solve - all: every solution kept (previous behaviour) - none: OPFLOW/PFLOW write no solution (-save_output not passed) Only solution files are removed; run folders and input cases stay. After an explore, pruning waits for the next action so a selected variant keeps its solution. New tests: tests/runner/test_save_solutions.py. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Here is what we are doing. config now has a flag what to keep ("all", "best", "newest", "none"), the normal flag is "best". If there is no best solution, it keeps the most recent. If you are reluctant to save any outputs, then you can set "none". Alternatively, tell the agent not to save any data outputs. |
* 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 <[email protected]> * AgentiGrid: exago.save_solutions limits the solved cases kept per action Answers the #144 review: OPFLOW/PFLOW saved a solved case on every solve, including sweep and contingency solves. New config key exago.save_solutions (also --set exago.save_solutions=<value>): - best (default): single solves keep their solution; an action with many solves keeps only its best (cheapest feasible sweep candidate or best by the sweep's metric, largest hosting capacity for boundary, Pareto-best explore variant), or the most recent if it has no best (contingency, reserve) - newest: multi-solve actions keep only the most recent solve - all: every solution kept (previous behaviour) - none: OPFLOW/PFLOW write no solution (-save_output not passed) Only solution files are removed; run folders and input cases stay. After an explore, pruning waits for the next action so a selected variant keeps its solution. New tests: tests/runner/test_save_solutions.py. Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: tsybinae <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]>
* AgentiGrid: substation-based N-1/N-2 contingency screen, verdict, fast-only relief (#134) * AgentiGrid: GridKit transient-stability engine, first build (#135) * Cosimulation that passes information from ExaGO to GridKit (#144) * AgentiGrid: one config for ExaGO and GridKit, GridKit from the latest ExaGO steady state * AgentiGrid: exago.save_solutions limits the solved cases kept per action --------- Co-authored-by: tsybinae <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: tsybinae <[email protected]> Co-authored-by: tsybinae <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]>
Summary
After #135 GridKit ran only from the operating point stored in its own case files. If something was solved by ExaGO, it would not be transferred to GridKit. This PR completes the folder strucutre and adds a GridKit fault screen start from the most recent ExaGO steady state. A transient study can run on top of the latest ExaGO result. A screen can also run all of its faults in one GridKit
ContingencyAnalysisprocess.Old approach
New approach
Changes
ExaGO folders and output
engine/→exago_engine/,parsers/→exago_parsers/(files unchanged). Imports,patch("agentigrid.engine...")strings in tests and logger names updated in 76 files; logger names followagentigrid.exago_engine..., as GridKit's do. Docs updated:README.md,tests/README.md,agentigrid_architecture.md,launcher/ARCHITECTURE.md.cli.py:exago_output()puts ExaGO runs inworkdir/exago/and logs inlogs/exago/(GridKit already usedgridkit/subfolders). The steeringsavecommand (was hard-coded toworkdir/) andregenerate-reportfollow it.exago_engine/executor.py: OPFLOW and PFLOW run with-save_output, which writes the solved case (opflowout.m/pflowout.m) into the run folder. Other applications are unchanged.One config for both tools (
config.py,configs/*.template)gridkit:section:binary_dir,dynamicsimulation_binary,timeout;study:(tmax,dt_monitor,rel_tol,abs_tol,dt_fixed,max_steps,max_order), written one-to-one into each run's solver.json;fault: start. LLM, iteration limit, worker count and output folders stay shared.tmax15 s,timeout600 s, fault start 1 s), so behaviour and the LLM prompt text are unchanged with defaults.gridkit_engine/criteria.pywith their sources.max_orderis outside 1-5,dt_monitor <= 0, or the fault starts at or aftertmax.gridkit_engine/settings.py,executor.py,agent_loop.py,commands.py,schema_description.pyread the configured values.Start from the latest ExaGO steady state (new
gridkit_engine/from_exago.py)find_latest_steady_state: newestjournal_*.jsoninworkdir/exago/, its last simulation, the run folder from the recorded-netfile. Error if that simulation did not converge, was a sweep, was not OPFLOW/PFLOW, or has no saved solution.apply_steady_statematches the solved MATPOWER case to the GridKit case:init Vr, Vifrom the solvedVm, Vap0, q0from the solvedPg, Qg; machines of generators that are off in ExaGO are removed with every device on their signals (governor, exciter, stabilizer), and unused signals are removedLoadZIPper bus fromPd, Qd; shunts: a separate constant-impedanceLoadZIPfromGs, Bsconverged = 0(ExaGO writes the OPFLOW flag there), so convergence is read from the journal.commands.py: new fault-screen fieldstart_from("case"default,"latest_steady_state"). Each start is built once per session and must pass its own no-fault run first; the converted case is saved asfrom_exago.case.jsonin the session folder.agent_loop.py: if the goal asks for the most recent steady state and the LLM sends"start_from": "case", the screen is rejected and the LLM is told to set it. The journal, verdict line and final summary name the ExaGO iteration and file used.cli.py:--from-exagouses the ExaGO start for the whole session.ContingencyAnalysis (
gridkit_engine/executor.py,modifier.py,gridkit_parsers/status_parser.py)application:"DynamicSimulation"(default) or"ContingencyAnalysis".run_contingencyruns all faults of a screen in one process, writesoutput_<fault index>.csvper fault in the screen's folder, and splits the shared console output into a status per fault. Timeout = per-run timeout × number of faults.ContingencyAnalysisruns every fault device in the file, so its case copy keeps only the requested faults (fault devices shipped with the case, such as the demo fault at bus 149 in ACTIVSg200, are left out). WithDynamicSimulationthey stay in the copy but are never switched on.Complete in 1.91939s).Prompt (
prompts/gridkit/system_prompt.py)"buses": [<bus>]). The POI screen with neighbours ("poi","hops") is used only when the goal asks for nearby buses, a depth or the PJM POI screen.start_fromandapplicationare set only when the goal asks for them; the answer states the operating point used.Tests
tests/gridkit/:test_config.py(gridkit section, defaults, warnings, solver keys in the.json),test_from_exago.py(journal lookup, no fallback, conversion on ACTIVSg200, generator without model, bus mismatch,-save_outputonly for OPFLOW/PFLOW),test_screen_options.py(start_fromandapplication, live on IEEE39 and ACTIVSg200 when GridKit is linked).test_status_parser.pycovers the ContingencyAnalysis completion line.Testing
python -m pytest -m "not slow"). The 46 new GridKit tests pass, including the live runs.gpt-4o, OPFLOW with Ipopt/MUMPS, GridKit at cdb77263. Fault: bus short circuit to ground at 1 s, cleared after 6.25 cycles (0.104 s), 15 s simulated. The ExaGO study removed the generator at the bus; the GridKit study then faulted the same bus.RelWithDebInfo(as in GridKit's CI); the build is selected withgridkit.binary_dir.Known issues
final_voltagefails a bus that the OPF holds exactly at its 1.1 pu limit when GridKit ends 1e-6 pu above it (bus 100 in the ACTIVSg200 runs: 1.100000 → 1.100001 pu). This is a false failure; a small tolerance would fix it. Not changed here.BusFault(replaced byBus::setFault); AgentiGrid's fault runs useBusFaultdevices and need adapting before moving past cdb77263.Release(-O3):ReecbEnzyme.cppfails with Enzyme errors;RelWithDebInfo(-O2) builds and passesctest(70/70).configs/env_setup.sh; one SOPFLOW test needs output from a real EMPAR run; two backend tests needANTHROPIC_API_KEY/OPENAI_API_KEY.Produced with help of Claude Code, but not exclusively by Claude.