Skip to content

Configure PowerFactory contingency result recording - #89

Merged
qian-harvard merged 2 commits into
Power-Agent:mainfrom
aswinkrishnapoyil:feat/powerfactory-contingency-recording
Sep 26, 2026
Merged

qian-harvard merged 2 commits into
Power-Agent:mainfrom
aswinkrishnapoyil:feat/powerfactory-contingency-recording

Conversation

@aswinkrishnapoyil

Copy link
Copy Markdown
Contributor

Summary

Adds add_contingency_result_variables for configuring which PowerFactory objects and variables are recorded in AC or DC contingency result files.

  • Resolves calculation-relevant objects using a bounded query.
  • Adds requested object-variable pairs to the selected ElmRes.
  • Deduplicates requested variables.
  • Avoids adding columns that are already registered.
  • Reports object counts, configured variables, truncation, and result-file metadata.
  • Does not execute contingency analysis.
  • Documents the new tool in the README and functions overview.

Validation

  • PowerFactory suite: 41 passed, including 7 subtests.
  • Repository suite: 2,206 passed and 25 skipped.
  • The two remaining GenX Bash checks initially could not launch because Git Bash was absent from PATH; both passed after adding the installed Git Bash directory.
  • Live PowerFactory validation against Case 1:
    • Registered m:u and m:phiu for Bus 08.ElmTerm.
    • configured_objects: 1
    • configured_variables: 2
    • Repeated the same registration successfully.
    • Result columns remained unchanged at 840 before and after the repeated call, confirming idempotency.
  • No contingency calculation was executed during the live validation.

@aswinkrishnapoyil

Copy link
Copy Markdown
Contributor Author

Implemented and validated the contingency result-recording support in b1b871d.

What changed

Added add_contingency_result_variables, which:

  • Supports AC and DC contingency result files.
  • Resolves calculation-relevant objects using object_query.
  • Bounds object processing using max_objects.
  • Deduplicates repeated variable names.
  • Registers the requested object-variable pairs in the selected ElmRes.
  • Detects existing result columns and avoids adding duplicates.
  • Returns result-file, object, variable, count, and truncation metadata.
  • Does not execute contingency analysis.
  • Informs the caller that the analysis must be rerun before newly selected variables contain populated results.

Documentation was also updated in:

  • PowerFactory/README.md
  • PowerFactory/functions_overview.txt

PR scope

The PR contains one commit and changes only:

  • PowerFactory/MCP_PowerFactory.py
  • PowerFactory/README.md
  • PowerFactory/functions_overview.txt
  • PowerFactory/test_state_inspection.py

Diff summary:

  • 4 files changed
  • 194 insertions
  • No unrelated files included

Result contract

A successful add_contingency_result_variables call returns:

  • success
  • calculation_method
  • result_file
  • query
  • variables
  • total_objects
  • configured_objects
  • configured_variables
  • truncated
  • results
  • message

Each entry in results identifies the matched PowerFactory object and the variables configured for it.

Automated validation

PowerFactory-focused suite:

  • 41 tests passed
  • 7 subtests passed

Repository suite:

  • 2,206 tests passed
  • 25 tests skipped
  • Two GenX Bash checks initially failed because bash.exe was not available through PATH
  • After adding the installed Git Bash directory to PATH, both checks passed independently
  • The repository run produced one unrelated NumPy binary-compatibility warning from test_powerio_server.py

GitHub Actions:

  • 4 of 4 CI checks passed

Live PowerFactory validation

Environment:

  • Project: test
  • Study case: Case 1
  • Result file: Contingency Analysis AC.ElmRes

The study case was activated successfully:

{
  "success": true,
  "message": "Study case already existed and was activated: Case 1"
}

The recording tool was then called with:

{
  "object_query": "Bus 08.ElmTerm",
  "variables": [
    "m:u",
    "m:phiu"
  ],
  "calculation_method": "ac",
  "max_objects": 10
}

Relevant response:

{
  "success": true,
  "calculation_method": "ac",
  "result_file": {
    "name": "Contingency Analysis AC",
    "class_name": "ElmRes"
  },
  "query": "Bus 08.ElmTerm",
  "variables": [
    "m:u",
    "m:phiu"
  ],
  "total_objects": 1,
  "configured_objects": 1,
  "configured_variables": 2,
  "truncated": false,
  "results": [
    {
      "object": {
        "name": "Bus 08",
        "class_name": "ElmTerm"
      },
      "variables": [
        "m:u",
        "m:phiu"
      ]
    }
  ],
  "message": "Result recording selection updated; rerun contingency analysis to populate the added variables"
}

Idempotency validation

The result-file metadata was read before and after repeating the identical registration:

  • total_columns before the repeated call: 840
  • Repeated registration returned success: true
  • Repeated registration reported configured_variables: 2
  • total_columns after the repeated call: 840

The unchanged column count confirms that repeating the request does not create duplicate result columns.

No contingency calculation was executed during the live validation.

@qian-harvard qian-harvard left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good, focused addition. It's rebased on main, green on all four interpreters, and 41 pass in the PowerFactory suite locally. The core is right: dedup of repeated variable names, the FindColumn check so a repeat doesn't add duplicate columns, Release() in a finally, no Execute, and bounded object processing with a truncated flag. The test pins the skip-if-already-recorded path (AddVariable called once for two names when one exists), which is the part most likely to regress.

There's a nice cross-check in your live evidence, too. #78's run reported the AC result file at 838 columns; this one reports 840 after registering two variables for one bus. That's consistent with the first call genuinely adding columns, not just the repeat being a no-op. As before, I have no PowerFactory here, so AddVariable / FindColumn semantics rest on your live run. Everything below is from the fakes.

Unlike the mode issue on #84, the persistence here is the tool's stated purpose, and the docstring says so. No concern there.

1. A repeat is indistinguishable from a first call — and tells the caller to rerun every time

first : columns actually added=2  success=True configured_variables=2
        message='Result recording selection updated; rerun contingency analysis to populate the added variables'
repeat: columns actually added=0  success=True configured_variables=2
        message='Result recording selection updated; rerun contingency analysis to populate the added variables'
responses identical: True

added includes variables that were already recorded, so configured_variables and results can't say whether anything changed. The message always instructs a rerun. A model that calls this defensively before each analysis — the natural pattern for an idempotent setup tool — will rerun ComSimoutage every time, which on a real grid is seconds to minutes. It also can't tell a user whether their project was modified.

Splitting the two would fix both: per object, added and already_recorded; totals added_variables and already_recorded_variables; and the rerun instruction only when added_variables > 0. For example, "Already recorded; no change" otherwise.

2. An exception after writes have landed is reported as a failed read

This is the only mutating tool routed through _read_only_result; every other write goes through the agent via _agent_result. So anything that escapes _impl is labelled "PowerFactory read failed", including after AddVariable has already changed the project:

columns actually added: [('Bus 1', 'm:u'), ('Bus 3', 'm:u')]
reported: {'success': False, 'message': 'PowerFactory read failed: COM object detached'}

In this case the trigger was _object_summary(obj) raising for an object whose AddVariable had already succeeded; it runs after the adds, in both the configured.append and the errors.append paths. A caller reads "read failed" and concludes nothing was written, and the record of what was written is gone. The trigger is narrow, but the misreport is exactly the kind a model can't recover from.

Containing exceptions per object in the write loop would keep the partial record: append to errors and carry on, so results still lists what landed. Alternatively, move the write into the agent like create_contingency. Either way, the message shouldn't say "read".

The ordinary partial case is handled well, for what it's worth. A rejected AddVariable gives success: false, lists the rejected pair in errors, and keeps the applied pairs in results. Since additions are idempotent, a retry converges, so no rollback is needed.

Smaller

  • Interaction with get_contingency_summary. The summary refuses when rows × (metadata + measurements) > 20,000, and m:u columns count as measurements. Recording m:u for ~1,000 buses with ~20 contingencies crosses that line, after which the summary tool stops working on that result file. Worth a sentence in this tool's docstring, since it's the tool that creates the condition.
  • No inverse. There's no way for a caller to remove a recorded variable. Fine for now; worth noting as a follow-up, since this is a persistent project change.
  • Silent input filtering. Non-string entries in variables are dropped silently; returning them in the error, or rejecting, would be clearer.
  • Nice touch backfilling functions_overview.txt for the earlier contingency tools.

Happy to merge once 1 and 2 are sorted, or I can make both changes on the branch as with #84 — say which you'd prefer.

@aswinkrishnapoyil

Copy link
Copy Markdown
Contributor Author

Thank you for the review. If you don't mind, please go ahead and apply the suggested changes directly to the branch. I will be pushing a new feature tomorrow as well. In the meantime, if there is any other functionality you think would be worth adding, please feel free to let me know.

A first call that added two columns and an identical repeat that added
none returned byte-identical responses, both telling the caller to
rerun contingency analysis. A model calling this as an idempotent setup
step would rerun ComSimoutage every time -- seconds to minutes on a real
grid -- and could not tell a user whether the project had changed.

Each entry in results now lists the variables added by this call and
those already recorded, with added_variables and
already_recorded_variables totalling them, and the rerun instruction is
given only when something was added. A repeat says "All requested
variables were already recorded; no change". Per-object variables keeps
its request order and its meaning.

Separately, this is the one mutating tool routed through
_read_only_result, so anything raised after AddVariable had already
changed the project surfaced as "PowerFactory read failed" with the
record of what was written gone. The object is now identified before
anything is written to it, and failures stay scoped to that object:
recorded in errors with its object_index, while results keeps what
landed. An object that cannot be identified is not written to. The
remaining reads before the first write -- Load, FindColumn, the object
query -- are genuinely reads, so the read-only wrapper now labels
accurately.

The docstring states the added/already-recorded split and when to rerun,
and notes that recording many m:u or c:loading columns can push a file
past get_contingency_summary's 20,000-cell limit.

Tests were written first and fail on the previous head, one with
KeyError: 'added_variables' and one with the "read failed" message.
43 pass in the PowerFactory suite; full CI 498 passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@qian-harvard

Copy link
Copy Markdown
Contributor

Pushed both fixes as a27efba, as you asked, and merging.

Change reporting. Each results entry now lists what this call added and what was already_recorded, with added_variables and already_recorded_variables totalling them. The rerun instruction appears only when something was added. A repeat reads "All requested variables were already recorded; no change", so a model using this as a setup step won't rerun the analysis for nothing. Per-object variables keeps its request order and meaning, and your original test passes unchanged.

Failure after a write. Each object is now identified before anything is written to it, and failures stay scoped to that object. They're recorded in errors with an object_index, while results keeps what landed. An object that can't be identified isn't written to at all. What's left before the first write — Load, FindColumn, the object query — is genuinely reads, so keeping _read_only_result is now accurate rather than moving the tool into the agent.

repeat        added=0 already_recorded=2  "…already recorded; no change"
partial       success=false, rejected pair in errors, applied pairs in results
unidentified  not written to; Bus 1's column kept; no "read failed"

I also added a docstring sentence on the get_contingency_summary 20,000-cell interaction, since this is the tool that can create it.

Tests were written first and fail on your head: one with KeyError: 'added_variables', one with the "read failed" message. 43 pass in the PowerFactory suite; the full suite is 498 passed, green on all four interpreters.

Thanks — the dedup, the FindColumn idempotency check and the live column-count evidence made this easy to build on.

@qian-harvard
qian-harvard merged commit 1abddc4 into Power-Agent:main Sep 26, 2026
4 checks passed
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