Skip to content

Support new power reduction format (loads_p/gens_p) for load shedding and curtailment - #73

Merged
marota merged 14 commits into
mainfrom
claude/update-cos-load-shedding-MtPs0
Apr 8, 2026
Merged

marota merged 14 commits into
mainfrom
claude/update-cos-load-shedding-MtPs0

Conversation

@marota

@marota marota commented Apr 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR adds support for the new power reduction format (loads_p/gens_p) used by the Expert_op4grid_recommender library, while maintaining full backward compatibility with the legacy bus disconnection format (bus=-1). Load shedding and curtailment actions now use active power setpoint changes instead of bus disconnection.

Key Changes

Backend (Python)

RecommenderService (recommender_service.py)

  • Updated _enrich_actions() to extract loads_p and gens_p fields from action objects alongside legacy topology fields
  • Enhanced _compute_load_shedding_details() to detect load reductions from both:
    • Legacy format: loads_bus with bus = -1
    • New format: loads_p attribute or content.set_load_p
  • Enhanced _compute_curtailment_details() to detect generator curtailment from both:
    • Legacy format: gens_bus with bus = -1
    • New format: gens_p attribute or content.set_gen_p
  • Updated _mw_start_load_shedding() to prioritize set_load_p over legacy set_bus.loads_id format
  • Updated _mw_start_curtailment() to prioritize set_gen_p over legacy set_bus.generators_id format
  • Modified _build_action_entry_from_topology() to map loads_p → set_load_p and gens_p → set_gen_p in action content
  • Updated simulate_manual_action() to generate dynamic actions using new format:
    • load_shedding_* actions now use loads_p topology
    • curtail_* actions now use gens_p topology

Frontend (TypeScript/React)

Type Definitions (types.ts)

  • Added loads_p?: Record<string, number> and gens_p?: Record<string, number> to ActionTopology interface

SVG Utilities (svgUtils.ts)

  • Updated getActionTargetLines() to exclude lines from visualization when loads_p or gens_p are present (power reduction actions, not line topology changes)
  • Updated getActionTargetVoltageLevels() to handle power reduction actions correctly

Components

  • ActionFeed.tsx: Updated to include loads_p and gens_p keys when collecting equipment names for action details
  • VisualizationPanel.tsx: Updated SLD overlay logic to recognize power reduction topology fields

Tests

New Test Suite (test_power_reduction_format.py)

  • Comprehensive test coverage for new power reduction format with 553 lines of tests
  • Tests for _compute_load_shedding_details() with loads_p format
  • Tests for _compute_curtailment_details() with gens_p format
  • Tests for _mw_start_load_shedding() and _mw_start_curtailment() with new format
  • Tests for _build_action_entry_from_topology() mapping logic
  • Tests for dynamic action creation with new format
  • Backward compatibility tests ensuring legacy format still works

Updated Existing Tests

  • test_manual_action_enrichment.py: Added loads_p and gens_p to MockAction class
  • test_dynamic_actions.py: Added loads_p and gens_p to MockAction class
  • test_recommender_regressions.py: Updated mock action setup
  • ActionFeed.test.tsx: Added tests for new format display in action cards
  • svgUtils.test.ts: Added tests for power reduction action visualization exclusion

Documentation

  • Added CLAUDE.md with comprehensive test structure documentation covering both backend and frontend test organization

Implementation Details

  • Dual format support: Both legacy (bus=-1) and new

https://claude.ai/code/session_01NLhamrxroa2pNuKHDfEuUM

claude and others added 14 commits April 7, 2026 08:08
… and curtailment actions

The Expert_op4grid_recommender library (PR #74) now uses active power setpoint
changes (set_load_p/set_gen_p) instead of bus disconnection (bus=-1) for load
shedding and curtailment actions. This update adds support for both the new
and legacy formats across the full stack:

Backend (recommender_service.py):
- _compute_load_shedding_details: detect loads_p in addition to loads_bus
- _compute_curtailment_details: detect gens_p in addition to gens_bus
- _mw_start_load_shedding: check set_load_p in content
- _mw_start_curtailment: check set_gen_p in content
- Dynamic action creation: use loads_p/gens_p topology format
- simulate_manual_action: detect affected assets from both formats
- _build_action_entry_from_topology: map loads_p→set_load_p, gens_p→set_gen_p
- _enrich_actions: extract loads_p/gens_p from action objects

Frontend:
- ActionTopology type: add optional loads_p/gens_p fields
- ActionFeed: include loads_p/gens_p equipment IDs in badge display
- VisualizationPanel: include loads_p/gens_p in target equipment IDs
- svgUtils: account for loads_p/gens_p in target detection and reconnection check

https://claude.ai/code/session_01NLhamrxroa2pNuKHDfEuUM
…w topology

Backend (23 new tests in test_power_reduction_format.py):
- _compute_load_shedding_details with loads_p attribute and set_load_p content
- _compute_curtailment_details with gens_p attribute and set_gen_p content
- _mw_start_load_shedding with set_load_p content format
- _mw_start_curtailment with set_gen_p content format
- _build_action_entry_from_topology mapping loads_p/gens_p
- Dynamic action creation with new power reduction format
- Backwards compatibility with legacy bus=-1 format

Updated existing backend tests:
- test_dynamic_actions: expect set_load_p instead of set_bus.loads_id
- test_manual_action_enrichment: expect loads_p/gens_p in topology
- test_recommender_regressions: update mock actions for new format

Frontend (6 new tests):
- ActionFeed: load shedding/curtailment details with loads_p/gens_p topology
- svgUtils: target line detection excludes power reduction actions
- svgUtils: reconnection check accounts for loads_p/gens_p presence

Added CLAUDE.md documenting the full test structure for both suites.

https://claude.ai/code/session_01NLhamrxroa2pNuKHDfEuUM
Rewrites the spec document to reflect the current implementation state:
- Documents the new set_load_p/set_gen_p format from recommender PR #74
- Describes dual-format detection (legacy bus=-1 + new power reduction)
- Documents dynamic action creation, MW start computation, and topology mapping
- Lists all relevant tests covering the new format
- Adds planned configurable MW slider section

https://claude.ai/code/session_01NLhamrxroa2pNuKHDfEuUM
Users can now choose partial MW reduction instead of full shedding/curtailment:
- Backend: target_mw parameter in simulate_manual_action computes setpoint
  from N-1 observation (remaining = current_mw - target_mw, clamped >= 0)
- Frontend: Target MW input column in score table for LS/RC action types
- Frontend: Editable MW + Re-simulate button in action card detail boxes
- 8 new backend tests, 6 new frontend tests covering the feature
- Updated curtailment-actions.md documentation

https://claude.ai/code/session_01NLhamrxroa2pNuKHDfEuUM
Mirror the React frontend changes:
- Score table: Target MW input column for load_shedding/renewable_curtailment
- Action cards: Editable Reduction MW input + Re-simulate button
- handleAddManualAction accepts targetMw param, passes to API
- handleResimulate function for re-simulating with new MW values

https://claude.ai/code/session_01NLhamrxroa2pNuKHDfEuUM
Merge description and editable MW into a single compact line:
- "Load shedding on BEON3 TR311 in MW: [input] [Re-simulate]"
- "Renewable curtailment on VIELMIN3 in MW: [input] [Re-simulate]"

Removes the verbose two-line format that had separate description text
and "Reduction MW" row. Both React frontend and standalone_interface.html
updated. Tests adapted for new format.

https://claude.ai/code/session_01NLhamrxroa2pNuKHDfEuUM
- Shorten "Load shedding" to "Shedding", "Renewable curtailment" to "Curtailment"
- Score table: clicking a computed LS/RC row with a Target MW value
  now triggers re-simulation instead of being blocked
- Both React frontend and standalone_interface.html updated

https://claude.ai/code/session_01NLhamrxroa2pNuKHDfEuUM
- Add onClick+onMouseDown stopPropagation on the detail box container
  so clicks on input/button don't trigger card selection first
- Clear cardEditMw state after successful re-simulation so the input
  shows the updated shedded/curtailed MW from the new results
- Both React frontend and standalone_interface.html updated

https://claude.ai/code/session_01NLhamrxroa2pNuKHDfEuUM
…Prevented

The card onClick was still firing despite stopPropagation on the detail
box. Added e.preventDefault() in the detail box onClick handler and
check e.defaultPrevented in the card onClick handler as belt-and-
suspenders to prevent handleActionSelect from triggering when clicking
inside load shedding / curtailment interactive elements.

https://claude.ai/code/session_01NLhamrxroa2pNuKHDfEuUM
- Implemented promotion of heuristic actions to the registry in simulate_manual_action.
- Added curtailment_details and load_shedding_details to simulation response.
- Sanitized simulation response for JSON serialization (fixed JavaHandle TypeError).
- Refined frontend card state management to prevent premature input clearing.
- Updated unit tests to verify MW persistence and response structure.
…ixes

- test_heuristic_action_promotion: Verifies Bug 5 fix (action promotion).
- test_response_sanitization: Verifies Bug 4 fix (JSON safe response).
- test_curtailment_details_computation: Verifies Bug 3 fix (consistent details).
Updated simulateManualAction test expectation to include mandatory target_mw field.
…nd islanding reporting

- Restructured simulate_manual_action to restore get_obs() call sequence (Base N then Contingency N-1) for mock compatibility.
- Restored missing action_id, lines_overloaded, disconnected_mw, and n_components fields to simulation response.
- Cleaned up duplicate and scrambled logic in recommender_service.py.
- Updated test_resimulate_regression.py mock sequence to match 2-call sequence.
@marota
marota merged commit 1d0632a into main Apr 8, 2026
2 checks passed
@marota marota mentioned this pull request Apr 8, 2026
9 of 17 tasks
marota pushed a commit that referenced this pull request Apr 14, 2026
Session reload no longer loses data introduced by PRs #73/#78/#83/#88:

- handleRestoreSession now restores lines_overloaded_after,
  load_shedding_details, curtailment_details and pst_details on each
  ActionDetail. Previously these were dropped on reload, so the PST /
  load-shedding / curtailment editor cards rendered empty and the
  Remedial Action tab lost its post-action overload halos until the
  user re-ran analysis.
- buildSessionResult persists the sticky-header rho arrays
  (n_overloads_rho / n1_overloads_rho) alongside the overload name
  lists, guarded on matching length so misaligned legacy data is
  omitted instead of saved.
- committedNetworkPathRef is now updated on session restore so the
  "Change Network?" confirmation dialog no longer misfires (or
  silently drops the study) after a reload.

Interaction logging now captures every user gesture the replay
contract needs to faithfully reproduce a session:

- config_loaded and settings_applied include the full settings
  payload (all paths, every recommender threshold including
  min_load_shedding and min_renewable_curtailment_actions, monitoring,
  pre-existing overload threshold, ignore_reconnections,
  pypowsybl_fast_mode).
- settings_tab_changed emits { from_tab, to_tab } and skips no-op
  clicks on the already-active tab.
- New event types action_mw_resimulated and pst_tap_resimulated are
  logged from ActionFeed.handleResimulate / handleResimulateTap with
  the raw user-entered target_mw / target_tap. useActions no longer
  logs manual_action_simulated from handleActionResimulated, which
  conflated the two flows and made replay impossible.

docs/interaction-logging.md is rewritten to reflect all of the above:
documented tab_detached / tab_reattached / tab_tied / tab_untied
visualisation events (previously in types.ts but undocumented),
corrected the details shape for view_mode_changed / asset_clicked /
inspect_query_changed / sld_overlay_* / session_* to match the actual
emitted payloads, documented the applySettings / loadStudy /
changeNetwork cases on contingency_confirmed, and added a new
"Session reload fidelity" section listing exactly which fields are
persisted / restored and which are intentionally ephemeral.

Tests: 695 frontend tests still pass; sessionUtils.test.ts gains
coverage for rho persistence guards and useActions.test.ts now
asserts that handleActionResimulated does not log from the hook.

https://claude.ai/code/session_013qJjLFQWMR91ZfPTRCLFiu
marota pushed a commit that referenced this pull request Apr 14, 2026
Adds 20 new tests across three files, guarding every fix from the
previous commit.

frontend/src/hooks/useSession.test.ts (+10 tests)
  New describe block "handleRestoreSession" with a reusable
  makeCtx() / makeSession() fixture pair. Covers:
  - Full configuration restore (every field, including new
    min_load_shedding / min_renewable_curtailment_actions
    thresholds from PRs #73 / #78).
  - Legacy session fallback to 0.0 when the two new thresholds
    are absent from older JSON dumps.
  - committedNetworkPathRef.current update on success — the
    regression for the "Change Network?" dialog misfire fix.
  - api.updateConfig payload shape, including the new thresholds.
  - session_reloaded interaction event emission.
  - Empty outputFolderPath short-circuit (no API call, ref
    untouched).
  - Backend error surfacing via ctx.setError with no ref mutation.
  - Enrichment field round-trip: captures the setResult updater
    via a captureRestoredResult() helper and asserts
    load_shedding_details, curtailment_details, pst_details and
    lines_overloaded_after all land on the restored ActionDetail.
  - Action status flag restoration into selected / suggested /
    rejected / manually-simulated sets.
  - Legacy action shape (enrichment fields absent) doesn't crash.
  - Estimation-only combined entries are filtered out of top-level
    actions but survive under combined_actions.

frontend/src/components/ActionFeed.test.tsx (+4 tests)
  New "Re-simulation interaction logging" describe block:
  - action_mw_resimulated is recorded with target_mw equal to
    parseFloat(user input) when re-simulating a load-shedding
    card.
  - manual_action_simulated is NOT emitted on LS re-simulation
    (regression guard for the mistyped event in useActions).
  - pst_tap_resimulated is recorded with target_tap equal to the
    user-entered integer on PST re-simulation, and neither
    manual_action_simulated nor action_mw_resimulated leak into
    the log.
  - target_mw is parsed as a float even when the user types
    trailing zeros (5.400 → 5.4).

frontend/src/components/modals/SettingsModal.test.tsx (+5 tests)
  New "settings_tab_changed interaction log shape" describe block:
  - paths → recommender logs { from_tab: 'paths',
    to_tab: 'recommender' }.
  - paths → configurations logs the matching transition.
  - from_tab tracks the currently-active tab, not the initial one
    (rendering the modal already on 'recommender' and clicking
    'configurations' yields from_tab: 'recommender').
  - Clicking the already-active tab does NOT log a
    settings_tab_changed entry (no-op skip).
  - setSettingsTab is still called unconditionally on no-op
    clicks — pins the "setter always, logger only on transition"
    split behaviour.

Results: 39 test files, 715 tests passing (was 695). tsc -b and
eslint both clean.

https://claude.ai/code/session_013qJjLFQWMR91ZfPTRCLFiu
marota pushed a commit that referenced this pull request Apr 30, 2026
Reconciliation of section 2 (0.5.0):
- Drop misattributed PRs that were actually pre-rebrand: save/reload
  (#49/#52), MW Start (#62), interaction-logging (#64), SLD highlights
  (#63), load shedding initial integration (#61). All now properly
  cited in section 1.5–1.6.
- Disambiguate App.tsx refactor history: PR #56 (hooks, 2100 → 800,
  pre-rebrand) vs PR #74 (components, 1000 → 650, 0.5.0) vs PR #75
  (memoization Phase 2, same LoC).
- Add accurate 0.5.0 PRs: #66 (vectorization w/ benchmark table),
  #69/#70/#71 (UI polish), #72 (curtailment), #73 (loads_p/gens_p
  format + configurable MW), #74/#75 (App.tsx decomposition),
  #78 (PST tap re-simulation), #84/#86/#87/#90 (detachable tabs).
- Add a recap table summarizing what's truly new in 0.5.0.

Diagrams added (Mermaid, GitHub-rendered):
- Gantt timeline of all 4 phases (top of doc).
- High-level architecture (frontend / backend / data).
- Two-step analysis sequence diagram (section 1.6).
- App.tsx LoC evolution flow (section 2.4).
- Backend mixin decomposition before/after PR #104/#106 (section 3).
- PyPSA-EUR pipeline flowchart (section 4).

https://claude.ai/code/session_01Pg7fuCUG2edfm5PyHS6SbN
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