Skip to content

The method, M9

M9 is a counterfactual comparison, not a classifier. For one site-day of 96 fifteen-minute readings it follows nine steps; each has its own module under pynrpf.m9, and reading them in order is reading the method.

readings (y, s) → two stories → candidate windows → bridge misfits → r(W) → W*, r* → z → p → decision

In brief

PyNRPF compares two stories for every day: the sign is right, or it is wrong inside some daytime window. For each candidate window it asks which story makes the underlying demand join up smoothly with the hours either side, and keeps the window with the strongest evidence. Two calibrated numbers turn that evidence into a probability, and the control c decides: correct automatically, keep, or send the day to a person.

Moving parts

One physical test (the bridge), no fitted scoring parameters, four fixed settings (the 06:00 to 18:00 scan, the floor φ, the edge rule, the tie order), two calibration numbers, one control.

Show details

Notation

symbol meaning
t fifteen-minute slot of the day, 0 to 95
y_t recorded net load, MW; positive means import as stored
s_t solar generation estimate, MW
W = [a, b] a candidate window from slot a to slot b inclusive
L = b − a + 1 slots in the window
U0_t, UW_t underlying demand if the sign is kept, or flipped inside W
ℓ, r anchor slots: the nearest finite readings before a and after b
g_W(t) the bridge, a straight line from U0_ℓ to U0_r
RSS_u, RSS_c misfit of the kept and flipped stories against the bridge, MW²
φ the evidence floor, MW
r(W), r* evidence for window W; the winner's evidence
z, p, c compressed evidence; probability of a wrong sign; the control

Step 1, the two stories (stories)

Underlying demand is the solar generation plus the net load the meter recorded. If the sign is right that is U0_t = s_t + y_t everywhere. If the sign is wrong inside W, the export there was stored as an import, and demand inside W is s_t − y_t.

Story A, keep the sign:     U0_t = s_t + y_t
Story B, flip inside W:     UW_t = s_t − y_t  for t in W,  s_t + y_t elsewhere

On a real wrong-sign day Story A shows a solar-shaped hump (demand would have to rise and fall with the sun); Story B gives an ordinary smooth day.

Step 2, every window (windows)

M9 does not know where the error starts or ends, so it tries every contiguous window between 06:00 and 18:00: 48 slots, hence 48 × 49 / 2 = 1,176 start-end pairs, plus "no correction". A window is admissible when every reading inside it is finite and a finite reading exists somewhere before its start and after its end. Missing readings disqualify the windows that touch them, not the day.

Step 3, edges at kinks (edges)

A wrong sign reflects the true trace about zero, so where the error starts and ends the recorded net load touches its reflection: a cusp, a local minimum. On the Beta dataset 84% of reviewer-labelled edges sit exactly on such a minimum and 95% within one slot.

m_t = [y_t ≤ y_{t−1}] and [y_t ≤ y_{t+1}]         (plateaus count)
a start a is valid when m_a or m_{a−1};  an end b is valid when m_b or m_{b+1}

The one-slot tolerance points inward: outward tolerance would re-admit the over-wide windows the rule exists to remove. An edge beside a missing reading is exempt.

Step 4, the straight bridge (bridge)

g_W(t) = U0_ℓ + (t − ℓ) / (r − ℓ) · (U0_r − U0_ℓ)

Both stories share the bridge, because they agree outside the window. The bridge does not claim demand is linear; it asks which story looks like a smooth transition between the surrounding demand levels and which like a hump.

Step 5, the misfit (misfit)

RSS_u(W) = Σ_{t∈W} (U0_t − g_W(t))²        RSS_c(W) = Σ_{t∈W} (UW_t − g_W(t))²

Smaller means closer to the bridge. On a genuine wrong-sign window RSS_c ≪ RSS_u.

Step 6, the evidence (evidence)

r(W) = (L / 2) · log( (RSS_u(W) + λ_L) / (RSS_c(W) + λ_L) ),      λ_L = L · φ²

r > 0: flipping fits better; r = 0: the stories tie; r < 0: keeping fits better.

Why a ratio: the same 100 MW² of improvement is nothing on a window whose misfit is 1,000 and everything on one whose misfit is 110; the ratio sees that, and it has no units, so no external noise scale is needed. Why the logarithm: a two-fold improvement and a two-fold deterioration become +0.69 and −0.69. Why L/2: treat the residuals as Gaussian noise with unknown variance, fit the variance under each story, and the log-likelihood difference is exactly (L/2) log(RSS_u / RSS_c); evidence sustained over more slots counts for more. Why the floor: two perfect fits must tie (log 1 = 0), and a floor on one side only would send the ratio to infinity. φ is the smallest non-zero overnight demand step over the release population; it only guards perfectly flat readings, and it is fixed (RELEASE_PHI) so a day scores the same in any batch.

Step 7, the winner (winner)

W* = argmax_W r(W),   r* = r(W*),   with "no correction" at r = 0

Ties: no correction over a window, a shorter window over a longer one, an earlier over a later. The best window that does not overlap the winner is kept as the runner-up. r* is the best window's evidence even when no correction wins, so a weak negative day stays distinguishable from a day with no candidate at all. A positive r* does not by itself cause a correction; the next two steps decide.

Step 8, from evidence to probability (calibration)

z = sign(r*) · log(1 + |r*|)
p = 1 / (1 + exp(−(a + b · z)))

The signed log compresses winning scores that span orders of magnitude, so a few near-perfect days do not dominate the fit. a and b are two numbers fitted by logistic regression on reviewed days; see Calibration for the release values and why b carries over between populations while a does not.

Step 9, the decision (decision)

AUTO_CORRECT if p ≥ c,   AUTO_KEEP if p ≤ 1 − c,   UNCERTAIN otherwise,   c = 0.7

Only AUTO_CORRECT flips the sign, inside W*, into a separate corrected series. UNCERTAIN leaves the day alone and lists it for review with the window and the runner-up. Three different statements: r(W) > 0 says flipping inside W fits better; p > 0.5 says an error is more likely than not; p ≥ 0.7 says the confidence set for acting automatically has been reached.

One day end to end

Beta station B, 20 September 2024, from the reference run:

step value
admissible windows 78
best window W* slots 33 to 63 (08:15 to 15:45); the reviewers' span was the same
r* 55.9; runner-up −27.1
z 4.04
p with the release calibration logistic(−4.59 + 2.16 × 4.04) = 0.984
outcome at c = 0.7 AUTO_CORRECT, 86 MWh flipped, 86 MWh in the reference