Skip to content

Voltage card: draw trip and cease points as timer bars (PERC2, PERC1 DLL, PERC2 DLL) - #11

Merged
TimThacker merged 2 commits into
mainfrom
feature/perc2-timer-bars
Oct 26, 2026
Merged

TimThacker merged 2 commits into
mainfrom
feature/perc2-timer-bars

Conversation

@TimThacker

Copy link
Copy Markdown
Owner

What does this change?

On the live preview's Voltage at the load bus card (Screening, Post-Commissioning and the saved HTML report, which share src/preview/page.py), each voltage trip or cease point is now a short timer bar. Before, it was a dashed line across the whole plot. This applies to PERC2 Standard, the PERC1 DLL and the PERC2 DLL.

How each point is drawn

  • The bar: a red bar at the point's voltage. It starts when the event starts and is as long as the point's delay. So the bars show which timer runs out first against the voltage trace.
  • Points with no delay: a small square at the event start, for example VHC2, VDUA1 at TDUA1 = 0, or the PERC2 DLL's momentary-cessation levels.
  • Strength: the bar that tripped or ceased the load is solid. Bars this event's voltage reaches are drawn at half strength, and the rest are faint.
  • No labels: nothing is written on the bars. The legend reads Trip thresholds (a red bar swatch) and Reconnect <CON> <level>.
  • Hover: hovering anywhere along a bar shows its details:
    VDPA1 trip threshold
    Below 0.80 pu for 0.50 s (DMA1) + 0.05 s (CMTDELAY)
    Tripped the load at 1.06 s        (or "Armed by this event" / "Not reached by this event")
    
  • Reconnect line: the green dashed reconnect line is unchanged (VLrecon for PERC2, VRPA for the DLLs).

Per model

Model Before Now
PERC2 Standard One full-width line per distinct level: the momentary checks and long-table points 1, 2 and 4 in both directions, up to 6 lines with ERCOT One bar per active cease point: VLcease/VHcease (Tcease), plus VLC1-4/VHC1-4 with their TV{L,H}C* delays. A point 3 is timed by point 1, as PSS/E 36.7.1 does.
PERC1 DLL Only stage 1 in each direction (VDPA, VDUA). Stages 2-4 were not shown. All 4 stages per direction (VDPA-VDPA3 with DMA-DMA3, VDUA-VDUA3 with TDUA-TDUA3), each as long as its delay plus CMTDELAY (when the simulator trips it)
PERC2 DLL Same as PERC1 DLL Same as PERC1 DLL, plus VLBLK/VUBLK (no delay) when momentary cessation is on (MCFLAG = 1, FCEASE > 0)
Standard (Vcease) model One Vcease line Unchanged: a single line is not cluttered

Presets pad unused stages by repeating a stage, for example ERCOT's VDUA2/VDUA3 = VDUA1. A repeated point is drawn once, under its first name. An undervoltage stage at or below 0 pu can never arm, so it is skipped.

Matching the stage that fired
The point that tripped the load comes from the simulator's own trip_events, so the solid bar is the one the simulation actually used:

  • PERC2 Standard: events name the check (VLM, VHM, VLC2, …), which is mapped to its (voltage, delay) point.
  • DLLs: events carry a stage index in the simulator's order: undervoltage stages 0-3, overvoltage stages 4-7, then the PERC2 DLL's momentary cessation (8 = VLBLK, 9 = VUBLK).

When momentary cessation is later taken over by a stage trip, both are marked: the cessation as "Ceased the load at …" and the stage as "Tripped the load at …".

Code (src/preview/page.py)

  • New voltage_cease_points(result) -> list[CeasePoint]: one list of the drawn points for PERC2 Standard and both DLLs. Each point holds its voltage, bar length, side, name, delay text, first fire time and verb. It replaces the PERC2-only perc2_fired_points that was added earlier on this branch.
  • New _timer_bars(fig, result): draws the bars. Each bar is sampled at 25 points so hovering anywhere along it works.
  • voltage_thresholds: now built from voltage_cease_points for these three models. It still sets the y-axis range, and the standard model's legend still uses it.
  • disturbance_header: the bar legend for these three models.
  • _swatch(..., bar=True): a 4 px rounded legend swatch.

Why?

With PERC2 Standard on a preset like ERCOT, the voltage card had six dashed red lines crossing the whole plot. That was hard to read, and the lines said nothing about delays. On the DLL models the opposite was true: only stage 1 of each direction was drawn, so stages 2-4, which often decide the trip (for example VDPA1 0.80 pu / 0.5 s with ERCOT), were not shown at all. A timer bar shows a point's level and its delay together, and the hover keeps the full detail off the plot.

How was it tested?

Test counts (uv run pytest -q): 879 passed, 1 skipped (the skip needs a real PSS/E install). The suite ran headless (QT_QPA_PLATFORM=offscreen). uv run ruff check src tests is clean.

New and updated tests:

  • tests/test_perc2_preview.py::test_the_voltage_card_draws_each_cease_point_as_a_timer_bar, PERC2 Standard, ERCOT, 0.50 pu for 200 cycles:
    • only VLC2 (0.80 pu / 0.5 s) fires, at about 1.01 s (0.50 pu is not strictly below VLC4's 0.50);
    • there is one bar per active point, each from fault_start to fault_start + delay;
    • the only full-width line is the 0.95 pu reconnect line;
    • there are no on-plot labels, exactly one hover says "Ceased the load at", and the legend matches.
  • tests/test_perc2_preview.py::test_a_ride_through_names_no_cease: a 5-cycle sag to 0.30 pu marks no point as fired.
  • tests/test_preview_page.py::test_the_dll_voltage_card_draws_every_stage_as_a_timer_bar, run for both the PERC1 DLL and the PERC2 DLL, ERCOT with CMTDELAY = 0.05:
    • the bars are VDPA, VDPA1, VDPA2, VDPA3, VDUA and VDUA1, with the repeats merged;
    • only VDPA1 fires;
    • each bar ends at fault_start + delay + CMTDELAY;
    • only the VRPA line is full width;
    • the hover reads "Below 0.80 pu for 0.50 s (DMA1) + 0.05 s (CMTDELAY)".
  • tests/test_preview_page.py::test_perc2_dll_momentary_cessation_has_its_own_marks:
    • with MCFLAG = 1, VLBLK is a zero-length mark credited with the first cessation, and its hover says "Ceased the load at";
    • with MCFLAG = 0 it is not drawn.
  • tests/test_preview_page.py::test_the_voltage_card_names_its_thresholds: the DLL legend is now "Trip thresholds" plus "Reconnect VRPA 0.90".

I also printed the drawn points for the PERC1 and PERC2 DLLs with the ITIC, ERCOT and PJM presets on a trip and a ride-through. With ERCOT the fired stage is the same as PERC2 Standard's (0.80 pu / 0.5 s at 1.01 s). The PERC2 Standard card was also checked by hand in the app on this branch (uv run perc-studio).

What was not done?

  • The standard (Vcease) model: its single trip line is unchanged.
  • The HTML report's older 2×2 figures (preview/perc1_dll.py, preview/perc2.py) and the Voltage vs. standard card are unchanged. That card already plots every stage as a point against the envelope.
  • Measurement-filter lag (TVM): the bars start at the event, not at the moment the filtered voltage crosses the level. With a non-zero TVM, the fired time in the hover is slightly later than the bar's end.
  • Release: there is no CHANGELOG entry or version bump here. Those go in a separate release PR for a v1.2.1 hotfix after this merges.

Checklist

  • I have read CONTRIBUTING.md
  • I have not included secrets, credentials, or unrelated changes

All 4 stages per direction (repeats merged), each as long as its delay
plus CMTDELAY, and the PERC2 DLL's momentary-cessation levels when they
are on. One voltage_cease_points() now serves PERC2 and both DLLs.
@TimThacker
TimThacker merged commit 83bf67c into main Oct 26, 2026
1 check passed
@TimThacker
TimThacker deleted the feature/perc2-timer-bars branch October 26, 2026 13:24
@TimThacker TimThacker mentioned this pull request Oct 26, 2026
2 tasks done
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.

1 participant