fix(treatment): province beds, occupancy and admissions on one capped basis - #969
seabbs-bot wants to merge 22 commits into
Conversation
…scale Admissions are scored as a plain negative binomial in the fit, its predictive checks and the forecast. The free-bed headroom censor never bound a fitted day but capped the predictive draws (#918). The cut-off occupancy is the reported-scale mean, the demand plus the reclassification offset, and the bed shortfall is the demand above the modelled capacity (#640). Province occupied beds are the national occupancy split on the demand shares, as the split likelihood and the forecast use, rather than the demand capped at the printed beds (#920).
The fit scores admissions uncensored. The forecast censors them at the modelled capacity less the previous day's occupancy, so forecast admissions cannot exceed the beds available. Guard the province occupancy split against a zero demand total and test that province occupied beds sum to the national occupancy. Co-authored-by: Sam Abbott <contact@samabbott.co.uk> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Forecast admissions are capped at the capacity less the previous day's occupancy plus the day's modelled deaths, recoveries, rule-outs and absconds, since a bed freed during the day can be refilled. Co-authored-by: Sam Abbott <contact@samabbott.co.uk> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e 100% Move the forecast admissions ceiling into `_admission_ceilings` and test that it follows the previous day's occupancy, adds the day's exits and floors at half a patient. State that the national utilisation can exceed 100% now the cut-off occupancy is on the reported scale. Co-authored-by: Sam Abbott <contact@samabbott.co.uk> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each province bed entry is the largest of the printed beds, the beds implied by the printed occupancy rate and the patients held (#958). The CSVs keep the printed counts. Co-authored-by: Sam Abbott <contact@samabbott.co.uk> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The cut-off occupancy is the reported-scale mean, demand plus the reclassification offset, capped at the modelled capacity. Patients above the beds are unmet demand, carried by the bed shortfall, so utilisation stays at or below 100% as the national data do. Co-authored-by: Sam Abbott <contact@samabbott.co.uk> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd utilisation The cut-off beds are the modelled capacity floored at the recorded cap on the last occupancy day, the bound the forecast starts from. The cut-off occupancy is capped at them, and the shortfall and utilisation are taken against them, so the occupancy never sits below an observed count. Co-authored-by: Sam Abbott <contact@samabbott.co.uk> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ince's free beds With more than one patch, forecast admissions are drawn per province at its modelled admissions, censored at its beds less its previous day's occupancy plus its exits that day, and the national forecast is their sum (#958). Province beds are the modelled capacity floored at the last recorded effective beds. The national cap adds the day's exits. Co-authored-by: Sam Abbott <contact@samabbott.co.uk> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The forecast reports the floored beds its caps use, the shortfall is on the reported scale the occupancy is, and the last recorded cap is computed once in the treatment state. Co-authored-by: Sam Abbott <contact@samabbott.co.uk> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ons-forecast # Conflicts: # docs/pages/methods.jl # src/models/joint.jl # src/models/observations.jl # test/test_mooncake_rules.jl
…ove the printed beds The implied beds count only when the patients held exceed the printed beds, so a rounded rate no longer inflates them (#958). The three occupancy rates the scan took from a ward (SitReps 092, 093, 095) are settled against the PDFs and recorded in the scanned CSV, and the manifest reconciles the rates like the other cells. Co-authored-by: Sam Abbott <contact@samabbott.co.uk> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`province_admissions_history` holds each province's 24h admissions, reconciled from the two reads by the manifest builder and read by `load_observations` (#958). The scan's Nord-Kivu counts for SitReps 081-083 took the confirmed admissions alone; the totals are settled against the PDFs and recorded in the scanned CSV. Co-authored-by: Sam Abbott <contact@samabbott.co.uk> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…f and in the forecast Each patch's beds are its modelled capacity floored at its recorded beds. At the cut-off each patch holds its demand share of the reported occupancy up to its beds, the rest its shortfall, and the national figures are the sums. The forecast runs each patch as a stock capped at its beds from that cut-off occupancy: it admits up to its free beds and loses its share of the in-care exits, each scaled by the occupied beds over the demand. Occupancy and admissions are drawn by patch and summed to national, and the in-care deaths and rule-outs are the scaled flows (#958). Co-authored-by: Sam Abbott <contact@samabbott.co.uk> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Benchmark comparison vs
|
| Group | 🟢 <50% | 🟢 50–75% | 🟢 75–80% | ⚪ 80–120% | 🔴 120–125% | 🔴 125–150% | 🔴 >150% |
|---|---|---|---|---|---|---|---|
| Log density | · | · | · | 17 | · | · | · |
| Mooncake | · | · | · | 17 | · | · | · |
Across all 34 benchmarks the ratio runs 0.99× to 1.09×, median 1.0×.
Log density — 17 benchmarks (by time change)
| Benchmark | main | PR | time | spread | memory |
|---|---|---|---|---|---|
| Log density / Latent / patch_infection_model (uncoupled) | 29.46 μs | 30.77 μs | ⚪ 1.04× | 8.3% | ⚪ 1.0× |
| Log density / Latent / latent (infection + onset staging) | 15.26 μs | 15.06 μs | ⚪ 0.99× | 10.0% | ⚪ 1.0× |
| Log density / Composer / confirmed_only_model | 29.52 μs | 29.19 μs | ⚪ 0.99× | 6.3% | ⚪ 1.0× |
| Log density / Joint / bvd_joint | 80.94 μs | 81.73 μs | ⚪ 1.01× | 6.0% | ⚪ 1.0× |
| Log density / Composer / treatment_only_model | 42.56 μs | 42.88 μs | ⚪ 1.01× | 10.1% | ⚪ 1.0× |
| Log density / Latent / patch_infection_model (coupled) | 24.81 μs | 24.93 μs | ⚪ 1.0× | 28.3% | ⚪ 1.0× |
| Log density / Composer / onsets_only_model | 23.3 μs | 23.18 μs | ⚪ 1.0× | 7.8% | ⚪ 1.0× |
| Log density / Submodel / treatment_flow_model | 17.53 μs | 17.59 μs | ⚪ 1.0× | 9.2% | ⚪ 1.0× |
| Log density / Submodel / onset_reporting_model | 6.78 μs | 6.8 μs | ⚪ 1.0× | 8.7% | ⚪ 1.0× |
| Log density / Composer / cases_only_model | 22.62 μs | 22.68 μs | ⚪ 1.0× | 15.0% | ⚪ 1.0× |
| Log density / Composer / deaths_only_model | 37.23 μs | 37.32 μs | ⚪ 1.0× | 6.0% | ⚪ 1.0× |
| Log density / Submodel / exports_model | 4.79 μs | 4.78 μs | ⚪ 1.0× | 35.5% | ⚪ 1.0× |
| Log density / Submodel / deaths_model | 15.17 μs | 15.14 μs | ⚪ 1.0× | 10.9% | ⚪ 1.0× |
| Log density / Composer / exports_only_model | 23.5 μs | 23.47 μs | ⚪ 1.0× | 6.4% | ⚪ 1.0× |
| Log density / Submodel / province_composition_model | 1.26 μs | 1.26 μs | ⚪ 1.0× | 11.4% | ⚪ 1.0× |
| Log density / Submodel / confirmed_cases_model | 4.02 μs | 4.02 μs | ⚪ 1.0× | 13.3% | ⚪ 1.0× |
| Log density / Submodel / reported_cases_model | 4.68 μs | 4.68 μs | ⚪ 1.0× | 5.0% | ⚪ 1.0× |
AD gradients — 17 benchmarks (by time change)
| Benchmark | main | PR | time | spread | memory |
|---|---|---|---|---|---|
| AD gradients / Composer / treatment_only_model / Mooncake | 317.01 μs | 345.23 μs | ⚪ 1.09× | 20.3% | 🔴 1.37× |
| AD gradients / Joint / bvd_joint / Mooncake | 656.6 μs | 697.36 μs | ⚪ 1.06× | 10.8% | 🟢 0.88× |
| AD gradients / Submodel / onset_reporting_model / Mooncake | 69.92 μs | 72.71 μs | ⚪ 1.04× | 21.5% | ⚪ 1.0× |
| AD gradients / Submodel / province_composition_model / Mooncake | 5.02 μs | 5.16 μs | ⚪ 1.03× | 9.4% | ⚪ 1.0× |
| AD gradients / Submodel / treatment_flow_model / Mooncake | 119.9 μs | 123.07 μs | ⚪ 1.03× | 9.3% | 🔴 1.01× |
| AD gradients / Composer / confirmed_only_model / Mooncake | 137.28 μs | 140.27 μs | ⚪ 1.02× | 9.9% | ⚪ 1.0× |
| AD gradients / Latent / patch_infection_model (coupled) / Mooncake | 115.28 μs | 117.29 μs | ⚪ 1.02× | 5.9% | ⚪ 1.0× |
| AD gradients / Latent / latent (infection + onset staging) / Mooncake | 50.98 μs | 51.85 μs | ⚪ 1.02× | 12.2% | ⚪ 1.0× |
| AD gradients / Submodel / confirmed_cases_model / Mooncake | 39.81 μs | 40.27 μs | ⚪ 1.01× | 8.0% | ⚪ 1.0× |
| AD gradients / Composer / cases_only_model / Mooncake | 153.45 μs | 151.85 μs | ⚪ 0.99× | 11.8% | ⚪ 1.0× |
| AD gradients / Submodel / reported_cases_model / Mooncake | 15.86 μs | 15.71 μs | ⚪ 0.99× | 4.5% | ⚪ 1.0× |
| AD gradients / Submodel / exports_model / Mooncake | 17.92 μs | 17.77 μs | ⚪ 0.99× | 5.0% | ⚪ 1.0× |
| AD gradients / Latent / patch_infection_model (uncoupled) / Mooncake | 107.65 μs | 108.54 μs | ⚪ 1.01× | 5.5% | ⚪ 1.0× |
| AD gradients / Composer / exports_only_model / Mooncake | 151.47 μs | 150.29 μs | ⚪ 0.99× | 12.3% | ⚪ 1.0× |
| AD gradients / Composer / deaths_only_model / Mooncake | 179.78 μs | 180.9 μs | ⚪ 1.01× | 11.4% | ⚪ 1.0× |
| AD gradients / Composer / onsets_only_model / Mooncake | 138.84 μs | 139.32 μs | ⚪ 1.0× | 8.1% | ⚪ 1.0× |
| AD gradients / Submodel / deaths_model / Mooncake | 45.53 μs | 45.57 μs | ⚪ 1.0× | 4.0% | ⚪ 1.0× |
spread is the interquartile range of that benchmark's own samples, as a fraction of its median.
…ional admissions Each day's printed province admissions are scored as a split of their sum on each patch's modelled admissions, with their own overdispersion, and the recovery generator simulates them (#958). The methods, news and the in-sample province page cover the split and the capped cut-off and forecast. Co-authored-by: Sam Abbott <contact@samabbott.co.uk> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parameter recovery: unconverged (3 seeds)1 of 3 fits converged (R-hat at most 1.1, bulk ESS at least 30). National
Province
Fit per seed
How the verdict is reachedA seed fails when more quantities lie outside their 99% interval than the 99th percentile of a Binomial with a 1% chance each (2 of 32), or fewer than 60% lie inside their 90% interval. This was opened by a bot. Please ping @seabbs for any questions. |
Fit convergence:
|
| fit | max R-hat | min ESS bulk | min ESS tail | divergences | draws |
|---|---|---|---|---|---|
joint |
1.06 | 49 | 32 | 2 | 2000 |
local |
1.03 | 146 | 109 | 0 | 1600 |
joint: ⚠️ passed with warnings
- warn — max R-hat is 1.06, past the warning threshold 1.05
- warn — min bulk ESS is 49, past the warning threshold 100
- warn — min tail ESS is 32, past the warning threshold 100
Worst-mixing parameters of `joint`
| parameter | rhat | ess_bulk | ess_tail |
|---|---|---|---|
| cases_state.positivity | 1.062 | 49 | 149 |
| confirmed_state.s_test | 1.056 | 63 | 171 |
| deaths_state.cfr_state.CFR | 1.021 | 74 | 125 |
| treatment_state.sev_state.δ_iso | 1.047 | 76 | 109 |
| recovered_state.rec_state.recovery_offset | 1.021 | 79 | 131 |
| background_total | 1.04 | 79 | 181 |
| treatment_state.isolation_bvd_admission | 1.045 | 84 | 141 |
| treatment_state.recovery_los_state.delay_sd | 1.028 | 86 | 32 |
| onset_report_state.σ_h0 | 1.032 | 89 | 132 |
| treatment_state.β_iso | 1.015 | 89 | 125 |
The same parameters grouped, one row per parameter:
| parameter | elements | max_rhat | min_ess_bulk | above_1.05 |
|---|---|---|---|---|
| cases_state.positivity | 1 | 1.062 | 49 | 1 |
| confirmed_state.s_test | 1 | 1.056 | 63 | 1 |
| deaths_state.cfr_state.CFR | 1 | 1.021 | 74 | 0 |
| treatment_state.sev_state.δ_iso | 1 | 1.047 | 76 | 0 |
| recovered_state.rec_state.recovery_offset | 1 | 1.021 | 79 | 0 |
| background_total | 1 | 1.04 | 79 | 0 |
| treatment_state.isolation_bvd_admission | 1 | 1.045 | 84 | 0 |
| treatment_state.recovery_los_state.delay_sd | 1 | 1.028 | 86 | 0 |
| onset_report_state.σ_h0 | 1 | 1.032 | 89 | 0 |
| treatment_state.β_iso | 1 | 1.015 | 89 | 0 |
Where the divergent transitions sit, in standard deviations of the full posterior:
| parameter | all_draws | divergent_draws | separation |
|---|---|---|---|
| growth_state.m | 0.099–2.21 | 2.19–3.79 | 3.14 |
| growth_state.T | 1.32–29.1 | 27.6–47.1 | 2.94 |
| T | 182.0–210.0 | 209.0–228.0 | 2.94 |
| growth_state.τ | 8.09–20.1 | 17.5–29.6 | 2.92 |
| rt_state.sigma_rw | 0.0681–0.237 | 0.211–0.304 | 2.2 |
| expected_onset_reported_T | 5740.0–6060.0 | 6030.0–6130.0 | 1.92 |
| deaths_state.asc_state.p_death | 0.798–0.953 | 0.729–0.891 | -1.72 |
| confirmed_state.ρ | 0.0368–0.0594 | 0.0542–0.0605 | 1.54 |
| rt_state.log_R0 | 0.407–0.944 | 0.276–0.454 | -1.53 |
| growth_state.r | 0.0345–0.0857 | 0.0238–0.0403 | -1.43 |
Thresholds — fail: R-hat above 1.1, bulk ESS below 25, tail ESS below 25, divergences above 0.05 of draws; warn: 1.05, 100, 100, 0.01.
The full per-parameter breakdown is on the sensitivity page, under Fit diagnostics by parameter.
…and the national sums Co-authored-by: Sam Abbott <contact@samabbott.co.uk> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
The PR caps each province's occupancy at its beds at the cut-off and in the forecast. It drops the admissions censoring from the fit and adds a province admissions split. The logic reads as sound and the AD-relevant code uses min/max only. The gaps are one untested forecast quantity, a weak assertion, an untaped data helper and over-long docstrings.
Automated first pass by seabbs-review-bot (Claude sonnet), triggered by: re-review requested by seabbs-bot. Not a human review. Comment @seabbs-review-bot to ask for another pass: @seabbs any time, the author's agent once it has pushed changes. Add the no-review label to opt this PR out. Ping @seabbs with any questions.
…dmissions-forecast The province blocks are regenerated with scripts/province_care_manifest.jl from the merged CSVs, which adds SitReps 131-135 to the admissions block and the effective Nord-Kivu beds for 26 September. Co-authored-by: Sam Abbott <contact@samabbott.co.uk>
…ds fall The forecast scaled the in-care exits by the occupied beds over the latent demand, which carries no reclassification offset, so the scale was below one even with no province at its beds. The in-care deaths and rule-outs were scaled down and the stock drifted off the fitted occupancy mean. The scale is now over the uncapped occupancy mean, so it is one below the beds. Forecast beds are a running maximum, so the stock stays within them. Also from review: a zero-derivative rule for `_province_bed_floors`, the forecast shortfall computed directly, property tests for the cut-off occupancy and shortfall, tests of the forecast shortfall and the forecast beds, and the bed fixture read from its rows. Co-authored-by: Sam Abbott <contact@samabbott.co.uk>
Co-authored-by: Sam Abbott <contact@samabbott.co.uk>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #969 +/- ##
==========================================
+ Coverage 91.02% 91.05% +0.02%
==========================================
Files 26 26
Lines 9183 9212 +29
==========================================
+ Hits 8359 8388 +29
Misses 824 824 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
📖 Documentation preview is ready! View the docs for this PR at: http://epiforecasts.io/BVDOutbreakSize/previews/PR969/ This preview will be updated automatically when you push new commits. |
|
@seabbs-review-bot the findings from review 5345882617 were fixed in 301fc5f and 7d8225b, and each inline comment now has a reply. Please take another pass. Generated by Claude Code |
There was a problem hiding this comment.
The PR rebuilds the treatment forecast as one capped stock per province, with beds that never fall and exits scaled to the occupied beds. It also adds a province admissions split and removes the old forecast_province_isolation split. The design is coherent, but the forecast stock logic is dense and only partly tested, and there are a few history-style comments and doc problems. I did not run anything, so I have not tied the failing fit-convergence check to a specific line.
Automated first pass by seabbs-review-bot (Claude sonnet), triggered by: re-review requested by seabbs-bot. Not a human review. Comment @seabbs-review-bot to ask for another pass: @seabbs any time, the author's agent once it has pushed changes. Add the no-review label to opt this PR out. Ping @seabbs with any questions.
src/models/joint.jl:121— suggestioncapped_stock_forecastsets the exit scale toheld / uncapped[t]and readsuncappedfromstate.occupancy_mean[fd .- 1].occupancy_meanincludes the reclassification offset and can be negative, soris 1 whenever the stock is zero or the mean is non-positive. That is a silent fallback. Nothing tests a zero or negativeuncapped, orheld > uncapped(r > 1, exits above the fitted flows). Add cases for both. Say in the docstring thatrcan exceed 1. Rather than allowing exits above the modelled flows, caprat 1 or state why it must not be capped.src/models/joint.jl:197— suggestionforecast_bed_shortfallrecomputes_held_by_patch(...) .- bedsper day inside a comprehension. This is the same share-and-cap logic ascutoff_occupancy, written a second time. The forecast version also uses the uncappedoccupancy_meanrather than the stock, so it will not matchpath.occupancy. Callcutoff_occupancy(state.occupancy_mean[t], state.demand_patch[:, t], beds[:, j], zeros(np)).shortfalland sum it, so there is one definition. No test coversforecast_bed_shortfallor thebed_shortfallcolumn offorecast_reported, although the PR changes how that column is computed.src/models/joint.jl:163— suggestion The docstring oftreatment_forecast_modelsays 'With no recorded capacity the future counts are uncensored'. The code also switchesoccupiedtooccupancy_patch_T + shortfall_patch_Tin that case, andnocap = 1.0e6ceilings feedmax.(path.free, 0.5). State that in the docstring. Add a test for thehave_cap == falsepath: today the horizon test only asserts the capped invariants. The docstring also cross-references three internal helpers and restates the body. Cut it to two or three sentences.src/forecast.jl:393— suggestion Thedailyclosure returnsnothingon a length mismatch. This covers a fit with no province rows, but it also hides a real mismatch betweenn_patchesand the draws, whereasinffails loudly a few lines up. Alsoisoandadmcome fromforecast_isolation.province.obsandforecast_admissions.province.obs, which always exist. Onlybedsis conditional onnp > 1. A single-patch fit withn_patches = 1therefore silently gets national counts labelled as a province, and that is the case the comment describes. Either throw when the length is neitherHnorn_patches * H, or drop the 'nationally, as one patch' comment. Add aforecast_provincestest that checksadmissions_newandisolation_levelsum to the national forecast, which the docstring claims.src/models/observations.jl:2563— suggestion Whenby_patchis false the floor is an inlinecensoring_capcall wrapped inisemptyguards inside the model body.censoring_capalready handles missing capacity (see its test 'no recorded capacity'). Move this into a data-only helper next to_province_bed_floors, with azero_derivativerule and a test, rather than adding a branch to an already long model.src/models/fit_args.jl:115— suggestion Comments that describe the decision are fine, but several added comments narrate the change rather than the code. Examples: 'the 24h admissions are a flow, so each day is a fresh split' and the 'Unmet demand: the reported-scale demand above each patch's beds' rewrites inobservations.jl. Trim them to constraints that are not visible in the code. The same applies to the docstring rewrites insrc/plots.jlandsrc/summaries.jl, which also carry very long lines. Fix the sentence inplots.jlthat lost its comma ("the isolation occupancy ... and the beds ... the 24h admissions").src/models/observation_distributions.jl:241— suggestion The comment 'the forecast caps at the modelled capacity and the free beds' now points at callers rather than the reason. The reason is that a draw at the ceiling returns the ceiling, which need not be whole. Say only that, and remove the reference to callers, which will rot.test/test_province_care.jl:1146— suggestion This testincludesscripts/province_effective_beds.jlfrom the package tests. That makes a script a test dependency and couples the tests to a data-preparation tool. The spot-check numbers (141/171/118.8 and so on) assert the script's implementation against hand-picked dates. Ifeffective_bedsdefines a data rule the package relies on, move it intosrc/with a docstring. If it does not, keep the data-derived assertion (every bed entry holds that day's patients) and drop the include.
…dmissions-forecast Co-authored-by: Sam Abbott <contact@samabbott.co.uk>
- Cap the exit scale at one and test it above the uncapped occupancy and at a non-positive one. - Build the forecast shortfall with `cutoff_occupancy`, and test the `bed_shortfall` column of `forecast_reported`. - Test the forecast with no recorded capacity. - `forecast_provinces` throws on a draw length that is neither one national row nor one row per patch. - Move the one-patch bed floor into the data-only `_national_bed_floor`, with a zero-derivative rule and tests. - Trim narration comments and reflow docstrings. Co-authored-by: Sam Abbott <contact@samabbott.co.uk>
Province beds, occupancy and admissions now use one capped basis. Each province quantity sums to the national one. This PR carries the changes from #942.
What
Fit
admission_headroomis removed. The old free-bed censor never bound on a fitted day.A_bvd_p + w_p·A_bgafter the admission delay, with its own ρ (province_admissions).Cut-off beds and occupancy
cutoff_occupancy).Forecast
capped_stock_forecast), starting from its cut-off occupancy.forecast_isolation.province.obs,forecast_admissions.province.obs,forecast_province_beds).Data
province_admissions_historyblock and loader, regenerated withscripts/province_care_manifest.jlfrom main's CSVs to SitRep 135.Why
Checks
4eb46ae7(2773 pass, 0 fail): all oftest_forecast_provinces.jlandtest_province_care.jl, plus the touched items intest_forecast_horizon.jl,test_isolation.jlandtest_mooncake_rules.jl. No fits were run locally.126d07fbare taken in301fc5fe.test_zone_plots.jllegend test and the province page'sprovince_capacity_share_sdKeyError, came from the old base and are fixed on main. The Windows job lost its runner.Review findings
First review (5345882617, on
126d07fb): all seven findings fixed in301fc5fe.Second review (5361409245, on
7d8225b1), all in4eb46ae7unless stated:forecast_bed_shortfallduplicatingcutoff_occupancy: fixed. It now callscutoff_occupancy, and a test covers thebed_shortfallcolumn offorecast_reported. The shortfall stays on the uncapped occupancy by design, since it measures unmet demand, not the stock.have_cap == falseuntested and underdocumented: fixed. The docstring is cut and a test covers that path.nothinginforecast_provinces: fixed. It throws on any length other than one national row or one row per patch. The existing test already checks that the province isolation and admissions sum to the national forecast._national_bed_floor, with a zero-derivative rule and tests.plot_province_split_ppc: fixed.scripts/province_effective_beds.jl: rejected. Ten other test files include scripts the same way (for exampletest_scoring.jlandtest_province_lab_parser.jl). The rule prepares data and is not model code, so it stays inscripts/.Fit comparison with main
The current CI fit on
4eb46ae7(run 36674507618) passes the convergence gate with warnings.This head also includes #1013, which changes the onset reporting walk.
jointzoneFor context on the slow capacity-step mixing (#1016), the earlier CI fit on
7d8225b1(run 36615565487) is set against main's fit on73b1b398(run 36603384866), the commit merged in there.7d8225b173b1b398cap_state.steps[3])cap_state.steps[3])cap_share_stateτ_cap and z_cap bulk ESSThe PR does not change the national bed data or the walk.
steps[3]is split between chains by about 0.3 sd, and the chains agree on the log density.Main's fit on
442b0592mixed the same knot at ESS 168, so its mixing depends on the draw.The effective Nord-Kivu beds move the shares (τ_cap 0.75 against 0.98), and one chain visits the neck of the non-centred τ·z funnel.
Closes #640
Closes #918
Closes #920
Closes #958
This was opened by a bot. Please ping @seabbs for any questions.