Skip to content

C2: Failure calculation (#24, #25, #26) - #29

Draft
yaniv-kwb wants to merge 13 commits into
mainfrom
c2-failure-calculation
Draft

yaniv-kwb wants to merge 13 commits into
mainfrom
c2-failure-calculation

Conversation

@yaniv-kwb

@yaniv-kwb yaniv-kwb commented Sep 25, 2026 •

Copy link
Copy Markdown

Do not merge before the release. Failure work stays out of main until C1–C5 are released together (roadmap decision D5). This PR targets main only because the pipeline deploys pull requests into main to dev (the production jobs run only for main itself). It is built on C1 (#28), so the diff also shows C1's commits: review only the three C2 commits 93c8f76, 2f6efec and a8a1972.

What changes

Results now include treatment failures (ADR-0001). The calculation module is the only code that changes: the result page, the reference-level exceedance, saved results, the assessment comparison and the export's result table show whatever it returns, with unchanged layout.

The combined-failure rule changed during implementation (decided 2026-09-25). The lowest-concentration arrangement for three or more steps needed a linear program, and the standard solver failed when the failing steps lose about 16 LRV or more together. Such days are a rare exception, so they now count like worst-case. This is updated in:

  • the C2 Spec;
  • CONTEXT.md (Combined failure);
  • decision 6 of docs/QMRA_Failure_Approach.md;
  • the roadmap and the vision.

C5's independent benchmark script must implement this rule.

A C1 test that required unchanged results with failure inputs was split:

  • with failure frequency 0, results and export stay identical;
  • with failures, the export keeps its files and table layout, and only values change.

Spec and Tickets

Checks

  • Mock-data check (Controlled server):
    • The full suite passes: 63 tests, including 17 new calculation tests.
    • Without failures, results are bit-identical to before C2. This was checked with a script outside the repository on 28 scenarios (1932 values, 0 differences), because the existing regression test only compares with a tolerance.
    • Local timing, drinking water with 3 pathogens and 5 bundled steps: 0.07 s without failures, 0.13 s at 10 and 0.19 s at 100 failure days per year on every step.
    • Still to do: the manual check and the timing on dev (see below).
  • Real-data check (Workstation): not done yet.
  • Quality goals checked:
    • Scientific correctness: the internal checks from the Spec pass:

      • frequency 0 reproduces today's results;
      • an always-failing step equals the train without its positive LRVs (full day in best-case, any duration in worst-case);
      • Eq. 5 and the 900 + 900 minute overlap equal hand-computed values;
      • a higher frequency or longer duration never lowers the risk;
      • worst-case ≥ best-case;
      • LRVs of 0 or below are unaffected.

      Agreement with an independent calculation is C5.

    • Reproducibility: the same scenario gives identical results every time (fixed seeds), and assessments without failures keep their exact results.

    • Export stability: the export keeps its files and CSV layout; only the result values of assessments with failures change (HTTP test).

Manual check on dev

  • Enter a scenario with failures (for example one step at 20 days per year) and run it as a guest and as a registered user. The result is higher than with frequency 0, and worst-case is at least best-case.
  • Save it. The saved-assessments page, the assessment comparison and the export's result table show the changed results, with unchanged layout.
  • An assessment saved without failures gives the same result as before when saved again.
  • Record the calculation time of a drinking-water scenario (365 events per year) with failures, for example from the browser's network tab, in this PR.

Dev currently runs C1 (#28). This deploy replaces it with C1 + C2, so please tell the team.

Definition of Done

  • Tests pass
  • code-review findings are handled (fixed, or answered in this PR)
  • Mock-data check done (automated checks done; the manual check and the timing on dev are open)
  • Real-data check done, or marked not needed above
  • No secrets or Real data committed
  • Glossary, ADRs and Vision document updated if this Change affects them (CONTEXT.md Combined failure, vision MVP item 3, failure approach decision 6)
  • Relevant Quality goals checked
  • I can explain this Change in my own words

🤖 Generated with Claude Code

ma-z-am and others added 9 commits September 25, 2026 09:52
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ent per pathogen [skip ci]

The best-case arrangement of combined failure events longer than a day is
chosen separately for each reference pathogen; failure days stay shared.
Recorded in the C2 Spec and in CONTEXT.md (Combined failure).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Each treatment step of an assessment gets a failure frequency (failure
days per year, 0-365, default 0) and a failure duration (whole minutes,
1-1440, default 30). The configurator shows both fields with their units
below the LRVs, rejects values outside the ranges with a message, and
keeps the values with their step through save, reopen and edit. Existing
steps get the defaults via the migration. The calculation is unchanged,
so results and export packages stay the same (C2 adds the effect).

Tidy-up: the min <= max LRV check duplicated in the configurator and the
personal treatment step form is now one shared rule, which also holds the
failure ranges. The two existing treatment form tests pass the two new
required fields in their input data; their assertions are unchanged.

Adds the first HTTP-level tests (Django test client, logged-in user).

Refs #21

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…sts (#22)

A treatment step can have a failure frequency above 0 only if at least
one of its six LRVs is positive (an empty LRV counts as 0), since a
failure removes only positive LRVs (D4). The shared treatment step rule
rejects it with a message on the failure frequency field.

Guests: an invalid guest result now answers 422 with the error messages
keyed by input name instead of an empty body. The configurator marks the
affected inputs and treatment step and explains in the result pane why
nothing ran; the marks clear on the next run. Registered users keep their
path (the saved configurator re-renders with the errors). This also makes
existing LRV errors visible to guests.

Also fixes the treatments section from #21: its fixed height hid the
failure fields and their messages under the footer; it now grows with
its content.

Refs #22

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tor copies them in (#23)

A personal treatment step gets a failure frequency (failure days per
year) and a failure duration (minutes), with the same shared rule as the
configurator: ranges, whole minutes, a positive LRV for a frequency above
0 (D4), and minimum <= maximum LRV. Existing personal steps get 0 and 30
via the migration. Invalid input now shows the messages in the form (422
with the rendered form), like the duplicate-name error already did.

The owner's list of personal treatment steps includes both values, and
adding a personal step in the configurator copies them into the new step
card together with its LRVs. Bundled steps carry no failure inputs, so
their card keeps 0 and 30. The copy can be changed in the assessment
without touching the personal step.

Refs #23

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The manual check on the dev environment in 06 stays open until it is
done on the deployed pull request.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ts (#24)

Tidy-up before treatment failures enter the calculation (C2).

- The inflow concentration samples and the exposure events of the
  simulated years are drawn once per reference pathogen and shared by
  best-case and worst-case. Each event picks its sample by position,
  which uses exactly the same random numbers as picking it by value.
- Each exposure event has its own LRV. For now every event gets the
  same LRV; the dose-response is evaluated once per distinct LRV and
  concentration sample and then looked up per event.
- Summary statistics and the reference-level exceedance are computed
  in one place for both cases and both risk measures.

The public entry point keeps its interface. Results are bit-identical:
28 scenarios (3 exposures x 3 treatment trains x 3 inflow ranges, plus
a step with negative and empty LRVs) compared with exact equality
before and after, 1932 values, no difference. The existing regression
test only compares with a tolerance, so this was checked with a script
outside the repository. A new test checks that the same scenario gives
identical results.

Refs #24

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…' positive LRVs (#25)

For every simulated exposure event and every treatment step with a
failure frequency above 0, the step fails that day with probability
failure frequency / 365 (ADR-0001). The draws come from their own
fixed-seed generator, one stream per step position, and are shared by
both cases and all reference pathogens, so raising a failure frequency
only adds failure days. Without failures no draws are made and results
are exactly today's (checked on 28 scenarios, 1932 values).

On a failure day, worst-case loses the positive minimum LRVs of every
failing step for the whole day, whatever the failure duration. LRVs of
0 or below stay (D4). Best-case is unchanged until the mixed-water
assumption (#26).

The C1 test that required unchanged results with failure inputs is
split: with failure frequency 0, results and export stay identical;
with failures, the export keeps its files and table layout and only
the values change.

Refs #25

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
On a failure day, best-case uses the mixed water: water treated during
the failure events and in normal operation, in proportion to the failure
durations. Only positive maximum LRVs are lost (D4), and each combination
of failing steps is calculated once.

Combined failures of one day, counting only the steps that lose LRV for
the pathogen group:
- durations that fit into 1,440 minutes do not overlap (one step: Eq. 5);
- two steps longer than a day in total overlap by the extra minutes;
- three or more steps longer than a day in total count like worst-case:
  all these steps are down for the whole day.

The last rule replaces the lowest-concentration arrangement decided on
2026-09-24. That needed a linear program, and scipy's solver failed when
the failing steps lose about 16 LRV or more together; such days are a
rare exception. The C2 Spec, CONTEXT.md (Combined failure), decision 6
of the failure approach, the roadmap, the vision and ticket 09 / #26
are updated.

Without failures results are exactly today's (28 scenarios, 1932
values). Local timing, drinking water with 3 pathogens and 5 bundled
steps: 0.07 s without failures, 0.13 s at 10 and 0.19 s at 100 failure
days per year on every step.

Refs #26

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…[skip ci]

Tested manually on dev. The calculation time measured on dev (09)
stays open until it is recorded in the pull request.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
yaniv-kwb and others added 2 commits September 28, 2026 08:37
…n ticket 09 [skip ci]

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ng) [skip ci]

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch was successfully deployed

1 active (outdated) deployment
dev — a8a1972c Deployed Sep 25, 2026 by yaniv-kwb via deploy-dev / deploy #89
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