V1 manifest — what reproduces, and what does not
The state of the project at the end of the scheme-layer batch, 2026-08-11. Written to the same standard as archive/v0/MANIFEST.md: what is here, what reproduces, and what does not.
The state of the project at the end of the scheme-layer batch,
2026-08-11. Written to the same standard as
archive/v0/MANIFEST.md: what is here,
what reproduces, and what does not.
Numbers in this manifest are from the state before the full
rebuild_all.pypass. The runoff model has since been refitted on the 2010-start rainfall (splitf0.59 → 0.57, recharge 217 → 205 mm/yr, AET 470 → 475, rainfall 1079 → 1040 mm/yr) and every downstream chapter inherits that. Re-read the chapters, not this summary, for current figures once the rebuild has completed.
How to rebuild the whole thing
python rebuild_all.py somerset # every layer, dependency order
python rebuild_all.py somerset --dry-run # show the plan
python rebuild_all.py --datasets # what a clean machine must fetch
rebuild_all.py exists because this project previously had no single
command that regenerated itself, and that is exactly how the water
layer came to be fitted on one rainfall series while the scheme layer
consumed another. A failing stage does not stop the run; the summary
names every stale link.
It deliberately does not re-download (rude to the EA/BGS/Defra/OSM
services, and slow) and does not rebuild the geology chain by
default (driven by BGS mapping, not rainfall, and parts need manual
georeferencing). --with-aquifers includes the scriptable geology
stages.
What is new in V1
Layers
| layer | state |
|---|---|
| water | rebuilt on 2010- rainfall; adds river routing and the rhynes |
| demand | new layer — 3 chapters |
| scheme | new layer — 13 chapters |
| report | new — the front report for a lay audience |
Model changes, all in core/model/ and run_model.py
Every one is opt-in, so runs 00–06 are unaffected and the synthetic
mass-closure check (python run_model.py --check) passes throughout.
- Seasonal aquifer capacity —
seasonal_capacity_fractionmakes headroom a monthly series. It has to be: the water table peaks in February and 86% of the divertible resource arrives Nov–Mar, so a static capacity offers full headroom in the month the real aquifer has least room. February is 52% of September on the Chipley record. - Warm start —
--spin-up N,--dump-state,--warm-start. The record opens on the driest water year in seventeen, so a cold-started run met the worst drought with empty reserves. That artefact was worth 62 and 67 percentage points of demand met in 2010 and 2011. recovery_efficiencymoved from injection to recovery, and gained a buffer-zone term (recovery_efficiency_initial,buffer_volume_m3). See the correction below — this one changed answers.- Direct injection —
diversion: direct_inject: true, river straight to the wells. Without it the model could not represent a scheme with no surface storage at all: recharge was drawn only out of the reservoir, so a zero basin gave zero delivery, which was plumbing rather than physics. - Settling —
settling: residence_days: N. Water diverted today cannot go down a well today. Present for timing, but its useful consequence is a derived floor under basin size. - Cascade —
cascade: [{from, to, max_daily_m3}], aquifer to aquifer.aquifer_registry.ymlhad always carriedcan_send_to: [.., aquifer]; nothing had implemented it. - Demand as a series —
irrigation: series_csv:, replacing a flat annual volume on a fixed monthly curve.
Corrections made to earlier work
These changed numbers that had already been written down. All are recorded rather than quietly fixed.
recovery_efficiency was creating a ratchet. Charged at injection,
the unrecoverable dead pool grew with every deposit, shrank only by
leakage, and still consumed headroom. A 66-year spin-up of run 05 ended
with 75% of stated capacity dead and the recoverable pool down 86% — a
store full of water it could neither deliver nor make room around. It
is now charged at recovery, with the shortfall booked as
recovery_loss_m3 so the ledger still closes exactly.
Areal rainfall started in 2012 for want of three gauges.
build_rainfall.py picks the earliest start retaining ≥90% of the best
gauge count, which chose 2012 over 2010 by 65 gauges against 62. For a
drought study, two extra years containing the worst drought in the
record beat three gauges out of sixty-five. Rebuilt with
--start 2010-01-01; the 2010–11 areal rainfall rests on a slightly
thinner network than the rest.
The drawn_by pump ledger was overwritten, not accumulated. With a
cascade active, a store could pump beyond its daily capacity.
A demand/supply correlation was asserted before it was measured. A chart title and chapter section claimed demand peaks when supply fails. Measured: Pearson −0.28, Spearman −0.19, n=13 — right sign, too weak to claim. WY2017 and WY2022 were both severe supply droughts with demand near its minimum in one and its maximum in the other.
A pump-sizing knee was hardcoded at 150k before the data returned. It is 200k. Both text and figure titles now compute it.
The open defect
The portfolio result is not settled.
Options reported three rivers beating one by 3.9×.
The cause was found: build_scheme_options.py gave every sub-scheme a
full 150,000 m³/day intake pump, so a 3-scheme portfolio carried
450,000 against the single scheme’s 150,000. A fix was written and then
silently reverted by a later line-number edit.
With intake held equal, a single scheme given 450,000 delivers 4.57 Mm³ against the portfolio’s 4.32 — so the gain was pump capacity, not several rivers. But pump sizing then shows three standard 150,000 units capture 10% more water than one 450,000 unit, because the three rivers do not flood simultaneously.
Delivery favours concentration by ~5%; capture favours splitting by ~10%. They roughly cancel, and the decision turns on engineering rather than hydrology. Both intake modes are now reported side by side. Do not quote the 3.9× figure.
What is still assumed, and where it bites
| assumption | value | how much it matters |
|---|---|---|
leakage_to_river_fraction |
0.8, no source | swept 0→1: moves answers 1–3%, changes no ranking. No longer a risk |
recovery_efficiency shape |
0.55 → 0.90 over 8 Mm³ | mechanism well supported, values are placeholders. Nearly all quantified ASR figures are brackish aquifers; the Sherwood is fresh |
| acceptance rate | bounded by natural amplitude | nothing in 14 years of record contains an injection. Never predicted, only bounded |
| clogging | not modelled at all | the main risk to every well-led conclusion |
| costs | none anywhere | every finding is “X buys more than Y”, unpriced. This is the gap between this work and a decision |
| crop water requirement | not built | the avoid crop failure demand tier needs crop coefficients and a yield-response function |
| tidal boundary | not modelled | back in scope via the rhynes: a level-controlled system with a tide-locked outfall cannot be drawn down on demand either |
What does not reproduce, and why
- Runs 00–06 (
projects/somerset/runs/) predate every model change above. They still run, and the--checkclosure test covers the legacy path, but their outputs were produced under the old injection-timerecovery_efficiencyand a static aquifer capacity. Treat run 07 and the scheme chapters as the current state; treat 00–06 as history. papers/somerset_scheme/andpapers/somerset_v2/are the V0/V1 narrative papers and predate the scheme layer entirely. They are not regenerated byrebuild_all.py.- The geology chain is not in the default rebuild. It is driven by BGS mapping and borehole logs rather than by rainfall, so it does not go stale when the water layer is refitted.
Where to start reading
report/README.md— the summary report. Written for a general reader; every number links to the chapter behind it.pipeline/README.md— how the layers fitscheme/README.md— the findings, in the order they bindscheme/synthesis.md— single vs portfolio vs cascade, and the shape of the spacedemand/README.md— why demand is not the constraint