Skip to content

C3: Failure inputs in the export (#30, #31) - #34

Draft
yaniv-kwb wants to merge 21 commits into
mainfrom
c3-failure-inputs-in-export
Draft

yaniv-kwb wants to merge 21 commits into
mainfrom
c3-failure-inputs-in-export

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 C2 (#29), so the diff also shows C1 and C2. Review only these commits:

What changes

The export package records which failures a result is based on.

  • Reference export test (C3: Reference test for the export package #30): a fixed benchmark assessment is exported through the application and compared with a reference stored in qmra/risk_assessment/tests/reference_export/:

    • the same files;
    • identical CSV tables and HTML report;
    • both plots present. They aren't compared byte for byte, because rendering can differ between machines.

    UPDATE_REFERENCE_EXPORT=1 rewrites the reference on purpose; the README next to it explains how. This is the check the "Export stability" Quality goal asks for.

  • Failure inputs in the export (C3: Failure inputs in the treatment table of the export #31): treatments.csv gets two columns, "Failure frequency (days per year)" and "Failure duration (minutes)", with each step's values repeated on its three pathogen-group rows. The reference export is updated on purpose; only treatments.csv changed.

  • HTML report unchanged: it shows no inputs at all, so it doesn't show failure inputs either (decided 2026-09-25). The roadmap's C3 entry is corrected accordingly.

C3 is a Small change without a Spec; the Tickets follow the roadmap entry and the decisions of 2026-09-25.

For the release notes

  • The export package's treatments.csv has two new columns at the end: "Failure frequency (days per year)" and "Failure duration (minutes)".

Spec and Tickets

Checks

  • Mock-data check (Controlled server):
    • The full suite passes: 65 tests, including the reference export test and a new HTTP test.
    • An assessment with failure inputs exports exactly its values per step, and a step without them exports 0 and 30.
    • The reference test fails on any changed file, column or value and names the file (checked by changing one stored value).
    • Still to do: the manual check on dev (see below).
  • Real-data check (Workstation): not done yet.
  • Quality goals checked:
    • Export stability: the export package is now compared with a stored reference in every test run. This PR changes it on purpose (two new columns), with the reference updated in the same commit.
    • Traceability: the export now records each step's failure inputs, so a reader can see which failures a result is based on. Origin and reference columns stay out of scope.

Tolerance in the reference test: the first CI run failed. 30 best-case values differed from the reference by up to 1.2·10⁻⁵ relative, because tiny probabilities (down to 10⁻¹³) are computed as 1 − exp(−k·dose), where most digits cancel, and CI's processor and math library differ. That difference is about 10¹⁰ times smaller than the health-based reference level. The test now requires identical text (names, columns, labels, layout) and each number within 0.1 % relative; a 1 % change or a renamed column still fails. Making the calculation itself numerically more precise (expm1/log1p) would be a separate change to the calculation.

Manual check on dev

  • Save an assessment with failure inputs on at least one step, download its export package, and check exposure-assessment/treatments.csv: two new columns with each step's values on all its rows, 0 and 30 for steps without failures.
  • The HTML report in the package looks as before (no inputs).
  • An assessment saved before C1 exports 0 and 30 for every step.

This deploy replaces C1 + C2 on dev with C1 + C2 + C3, 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 on dev is 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 (none affected; the vision's MVP item 4 already says the export records the failure inputs)
  • Relevant Quality goals checked
  • I can explain this Change in my own words

🤖 Generated with Claude Code

ma-z-am and others added 15 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>
C3 and C4 are Small changes without a Spec; the tickets follow their
roadmap entries and the decisions of 2026-09-25: the HTML report shows
no inputs and stays so, the failure columns repeat on each step's rows,
a reference export test comes first, the guided tour's treatment step is
extended, and a new Sphinx page explains the method.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Quality goal Export stability: the content and layout of the export
package change only on purpose. A fixed benchmark assessment is exported
through the application and compared with the reference stored in
tests/reference_export/: the same ZIP entries, identical CSV tables and
HTML report, and both plots present. The plots are only checked for
presence, and masked where the report embeds them, because their
rendering can differ between machines.

The benchmark's inputs are fixed values rather than bundled data. One
step fails 10 days per year, so the failure calculation is part of the
reference. UPDATE_REFERENCE_EXPORT=1 rewrites the reference on purpose;
the README next to it explains how. The export itself is unchanged.

Refs #30

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The export package records which failures a result is based on:
treatments.csv gets the columns "Failure frequency (days per year)" and
"Failure duration (minutes)", with each step's values on all three of
its pathogen-group rows, so the file keeps one row per step and group.

The HTML report shows no inputs at all and stays so (decided
2026-09-25); the roadmap's C3 entry says so now. The reference export is
updated on purpose: only treatments.csv changed. The C1 test for failure
frequency 0 now allows the treatment table to show the new durations,
while results and every other exported file stay identical.

Release note: treatments.csv in the export package has two new columns
at the end.

Refs #31

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The reference export test failed in CI: CI calculates with Python 3.11
and numpy 2.4.6, a developer machine with Python 3.12 and numpy 2.5.3,
and the Monte Carlo results differ in their last digits. Decimal numbers
in the compared files are now rounded to 10 significant digits before
the comparison, and the reference is stored the same way. Every real
change to a value, column or file is still caught. Checked with numpy
2.4.6 and 2.5.3.

Refs #30

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Rounding to 10 significant digits was not enough: CI still differed in
30 of the 60 result values, all best-case, by up to 1.2e-5 relative.
Tiny best-case probabilities (down to 1e-13 here) are computed as
1 - exp(-k * dose), where most digits cancel, so processors and math
libraries differ far beyond the last digit. The difference is about
1e10 times smaller than the health-based reference level.

The text of each compared file (names, columns, labels, layout) must
stay identical, and each number must match within 0.1 % relative, about
100 times the largest deviation seen. CI's deviating value passes, and
a value changed by 1 % or a renamed column still fails. The reference
stores the full values again.

Refs #30

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
yaniv-kwb and others added 4 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>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
yaniv-kwb and others added 2 commits September 28, 2026 09:25
Co-Authored-By: Claude Opus 5.5 (1M context) <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 — 27b52687 Deployed Sep 25, 2026 by yaniv-kwb via deploy-dev / deploy #92
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