Adding medical workers and hospital interactions#120
Open
debog wants to merge 77 commits into
Open
Conversation
…Us and CPUs, as well as various number of MPI ranks
…edical workers NAICS from worker interaction model
…s for each community based on number of available medical workers
…multiple components; added the option to write this out as plotfiles
…ffect probability of death/recovery
…s to screen; writing hospital data with the same plotting interval as plt files
…ing of overcrowded hospitals and underserved patients
…development branch
Brings PRs #144-#152 into the medical-workers branch: Weather module (#149), UrbanPop IO optimization (#144), multi-disease hospital-stats fix (#146), disease-period CDF sampling / EpiCast comparison (#152), worker-assignment fix for phantom transcontinental commutes (#147), and random/air-travel fixes (#151). (#145 chkdiff fix was already present.) Resolved one conflict in src/HospitalModel.H (treatAgents): combined development's corrected multi-disease hospital-stat bookkeeping (exit_*/died_today/rm_from arrays, immune_length_loc on recovery) with the medical-workers treatment-quality / capacity-limited mortality, indexing the baseline severity by status_d. Builds clean (make -j2). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reformat the merged treatAgents() p_death expression in HospitalModel.H to satisfy the repo clang-format (v19) check. Whitespace only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hospital patient capacity was computed from all active medical workers (n_mw_arr component 1), so acute_medworkers_proportion had no effect on capacity, load, or mortality -- it only fed the n_t_mw>0 gate and a diagnostic. Capacity must reflect the medical workers treating the modeled disease: use n_a_mw (= acute_medworkers_proportion * active). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a master switch agent.model_medical_workers (default false). When off, medical workers are ordinary workers, there is no hospital capacity limit or in-hospital transmission, and hospitalized agents die at the baseline rate, so the run reproduces development exactly for both census and UrbanPop initialization. Gate interactDay, treatAgents, WorkCandidate, and the census medical-worker assignment on the flag; remove an over-strict naics-equality assert in BinaryInteractionWork. Redesign hospital capacity (Option 2): capacity = bed_supply x (available/baseline), bed_supply = (staffed_beds_per_1000/1000) x residential population. This replaces the degenerate num_patients_per_doctor x acute_medworkers_proportion pair with a single staffed_beds_per_1000 knob (default 2.4, AHA). Bed supply is recorded once at init from residential population (initHospitalCapacityModel/initBedSupply); the workforce enters only through the availability fraction. Rename acute -> frontline. Set realistic, model-off-inert defaults (med_workers_proportion 0.13, staffed_beds_per_1000 2.4, nonzero xmit_hosp_*). Update how_to_run.rst. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add hospital_model.use_HIFLD_HHS_data (default false): set each community's staffed-bed supply from real per-county or per-tract hospital data instead of the uniform staffed_beds_per_1000 density. County-level data apportions a county's beds to its communities by population; tract-level data places beds at the tracts that have hospitals and routes each community's patients to the nearest hospital tract, reusing the home/work agent-movement machinery (hosp_i/hosp_j set from a per-community assignment built at init from the global demographic data and the domain raster). Add utilities/build_hospital_data.py to build the data files from public sources (HHS facility capacity, HIFLD hospitals), tied to a year, for a state, metro area (--counties), or the whole US. Commit county-level examples for CA, BayArea, NM, MA, US. Document in docs/source/usage/data.rst, data/HospitalData/README.md, and how_to_run.rst. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…o hospitals Route the medical workforce to hospitals in tract mode (analog of the patient routing): retarget each medical worker's work_i/work_j to its assigned hospital community, so staff, beds, and patients all aggregate at the real hospital cells. Capacity becomes beds x catchment workforce availability; the per-cell-workforce mismatch and the n_baseline==0 silent-disable are gone, and non-hospital cells are inert. Justified because the worker-flow data only resolves home/work to census tracts. HIFLD Open is being deprecated, so source hospital data from HHS instead: the HHS facility dataset carries geocoded_hospital_address points, providing both bed counts and locations for county and tract placement (no HIFLD needed). HHS is now the default --source; HIFLD remains a legacy local-CSV option. Rename input hospital_model.use_HIFLD_HHS_data -> use_HHS_data. Commit real tract-level data for CA, BayArea, NM, MA, US (HHS geocoded points joined to Census tracts, week 2021-12-26), validated end-to-end. Update docs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e hospital data HHS has no pre-COVID (Jan 2020) data -- the facility series starts ~April 2020, comprehensive from ~late July 2020 (ICU beds only from then). Use 2020-09-27, the earliest clean low-COVID lull with full reporting, so the staffed-bed counts represent baseline (full-strength) capacity rather than surge-affected staffing. Replace the 2021-12-26 (Omicron peak) county and tract example files for CA, BayArea, NM, MA, US with the 2020-09-27 versions; update docs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…CUDA Release crash) The medical-workers init/reroute device lambdas shared a translation unit with generateCellData. On the matrix build (CUDA + RDC + -maxrregcount=255 + fast-math + -O3) the added device code perturbed the Release register allocation of generateCellData's kernel, faulting in writePlotFile -- only on matrix, only in Release (Debug was clean), and only after these gated-off additions (35cdfd3 was clean). Move readHospital* + initHospitalCapacityModel + the reroute (now rerouteHospitalizedToHospital) into AgentContainerHospital.cpp so generateCellData's compilation is no longer affected. No functional change; builds clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reflow the lines flagged by the clang-format CI (v19): collapse short blocks/declarations that fit within the 130-column limit, align trailing comments, and drop a stray blank line. No functional change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The bed-supply initialization device kernels lived in the HospitalModel class template (initBedSupply, setBedSupply), so they were instantiated in every translation unit that includes the template. Following the earlier fix in 3dda948 ("creating hospital data MultiFab in AgentContainer instead of HospitalModel to avoid weird CUDA error"), write the per-community bed supply directly into AgentContainer's m_hosp_data MultiFab (which the hospital model already aliases) from the concrete AgentContainerHospital.cpp translation unit instead. This removes initBedSupply/setBedSupply from the template (replaced by a host bedsPerThousand() accessor) and drops the temporary bed_supply MultiFabs and the setBedSupply copy kernel. No functional change: the same component (HospMod::bed_supply) gets the same values; the per-capita, county-apportioned, and tract-placed paths are unchanged. Builds clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Encapsulate the hospital bed-supply/patient-routing feature in a new HospitalData class held by AgentContainer through a std::unique_ptr (with an out-of-line dtor), replacing the by-value iMultiFab m_hosp_assignment and bool m_tract_routing members. Those by-value members changed AgentContainer's layout, which re-instantiated ParticleToMesh<AgentContainer> and tipped the marginal -O3 codegen of its inner FabArray::PC_local_gpu copy kernel into an "invalid device function" miscompile in generateCellData (Release only). Keeping the iMultiFab and all device/STL code in HospitalData.cpp restores AgentContainer's translation unit to the working shape. No functional change; AgentContainerHospital.cpp is superseded by HospitalData.cpp. Co-authored-by: Cursor Agent <cursor-agent@cursor.sh>
Write medical_workers.dat (one file per disease, only when the medical-workers model is on) with daily medical-worker vs other-worker counts: total, susceptible, and newly infected for each group. This provides the in-hospital transmission (xmit_hosp_*) calibration target -- the HCW infection hazard relative to other workers. AgentContainer::getMedicalWorkerCounts is a ParticleReduce kept in HospitalData.cpp (not AgentContainer.cpp) so the new device kernel stays out of the AgentContainer translation unit. main.cpp writes the file alongside output.dat; output.dat and the regtest baselines are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…inimum) The capacity-limited treatment-quality multiplier q that scales hospitalized mortality (p_death = 1 - q*(1 - p_death_baseline)) was accumulated by compounding the daily community score over the whole stay (q = product of daily scores). Over a multi-day stay this drives q toward zero under sustained overload, so even a moderate ~3x load produced an unrealistic ~5-6x mortality multiplier. Add hospital_model.treatment_score_type (AMREX_ENUM): minimum q = min of the daily scores (the worst day of the stay) [default] cumulative q = product of the daily scores (the previous behavior) The minimum model makes the overall mortality multiplier linear in the baseline, so it calibrates cleanly to the ~2x-at-peak-strain target from caseload-surge studies. Inert when the medical-worker model is off (regression tests unchanged). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
For load > 1 the score used (e^{L-1})/(e^{L_half}+e^{L-1}), which at L=1 equals
1/(e^{L_half}+1) != 0 -- so the score stepped down from the L<=1 branch (about
0.969 at floor 0.1, L_half 3.34) instead of leaving 1 continuously. Use
(e^{L-1}-1)/((e^{L_half}-1)+(e^{L-1}-1)): the numerator vanishes at L=1 so the
score joins the within-capacity branch continuously, while the half-score load
and the floor are preserved exactly. Recalibrating M(2.5)=2x shifts the
calibrated halfscore_load from 3.34 to 3.13 (decks updated separately).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
readHospitalDataLevel now returns county only when the data-file header marks level=county; otherwise it defaults to tract-level placement (real hospital locations + patient/workforce routing). The existing county files carry an explicit level=county header, so they are unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
rerouteHospitalized indexed the per-cell hospital-assignment MultiFab at each agent's home cell using the current tile's array. Once an admitted agent is moved to its hospital cell, its home cell lies outside that tile, so the lookup read out of bounds and produced a garbage hospital cell -- tripping the patient-cell assertion in computeHospitalScores a few weeks into a run. Route only just-admitted agents (hosp still equals home, set by assignHospital, so the agent is at its home cell and the lookup is in-tile); already-routed agents keep their fixed hospital cell. Found by a local 4-rank Bay Area validation run. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
treatAgents deposited the per-community death and hospitalization-exit statistics at each agent's home cell using the current tile's array. With tract-level routing a hospitalized agent sits in its hospital cell, so the home cell is outside the tile and the deposit was an out-of-bounds write -- dropping deaths (mesh vs agent death counts diverged and tripped the main.cpp consistency assertion) and unbalancing the hospitalization counts. Capture each agent's in-tile cell at the start of treatAgents and deposit there; the per-disease totals (summed over cells) are unchanged, so the output and the consistency checks are correct. Also move the deceased back to their home community so plt-based mortality is attributed by residence, matching the no-routing baseline. Validated by a local 4-rank Bay Area run completing past the admission/death onset that previously crashed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
BayArea.dat is 2000-vintage (1405 tracts; San Francisco has 176, not the 197 of 2010), but the tract bed file was built on 2020 TIGER tracts. 20 of 60 hospitals fell in post-2000 tract splits the demographic file does not have, so a third of the beds (7,240 of 10,876 at 39 of 58 tracts) never placed. Rejoin the HHS facility points to the 2000 Bay Area tracts: all 10,876 staffed beds at 58 hospital tracts now place. Bundle the 2000 tract shapefile and fix the build command + add a vintage note in the README so the file stays reproducible. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rker-death diagnostic - Patient transfer: a just-admitted patient whose nearest hospital is over capacity is routed to the lowest-load hospital in the same county (none in a one-hospital county), from the one-day-lagged per-hospital load. On by default under tract routing (hospital_model.patient_transfer). Optional per-day transfer log (hospital_model.transfer_output_file): day, from/to FIPS+tract, n_patients. - Hospital workgroups: split each hospital's medical workforce into groups of ~workgroup_size instead of one group per hospital, bounding worker-to-worker in-hospital transmission to realistic team sizes (med/surg units ~30 beds). hospital_model.workgroup_size overrides agent.workgroup_size. - Diagnostics: medical_workers.dat gains cumulative MW_dead/OW_dead columns (appended; the first six columns are unchanged). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…n private methods Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirrors the workplace exclusion. Without it, tract routing pools a hospital's whole medical workforce (same work cell, work_nborhood=0) into one neighborhood group and over-infects them (~94% even with in-hospital channels off). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… fallback path, matching the tract-data path
Add a per-disease DiseaseStats::hospital_acquired field that counts in-hospital cross-disease infections: a patient admitted for one disease who acquires another while hospitalized. Hospitalized agents are cut off from community/household/work/school contact, so any new infection of a patient is necessarily hospital-acquired. The count is deposited at the hospital cell and written to the plotfiles per disease; the disease_stats MultiFab gains one component (5 -> 6). Also fix a pre-existing out-of-bounds write in infectAgents. new_cases was deposited at the agent's home cell, but a hospitalized agent sits in its hospital cell, which with tract-level routing can be in a different box. A hospitalized agent acquiring a second disease therefore wrote outside the tile's FAB and corrupted the disease-stats mesh, surfacing as a spurious death-count assertion failure. This was latent until now: it requires a multi-disease run with in-hospital patient transmission, where an already-infected hospitalized agent becomes newly infected with another disease. Deposit at the agent's current (in-box) cell instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…missions The hospital occupancy counts (ward, ICU, ventilator) are per-agent nested levels (ward superset ICU superset ventilator), deposited once at admission and removed as the patient steps down. The removal combined per-disease exit flags: for a patient hospitalized for two diseases at once (from community co-infection), it removed the patient from the ward as soon as one disease's stay ended, even though the other still kept the patient hospitalized. This tripped the discharge-consistency assertion (a not-yet-discharged patient flagged for ward removal) and, more generally, double-decremented the occupancy counts. The bug was latent until now: it requires a multi-disease run with the hospital model and a patient admitted for two diseases simultaneously. Replace the per-disease exit flags with the patient's nested hospital level (the most intensive level across its diseases), captured before and after the per-disease treatment update. Each occupancy count is decremented exactly when the patient drops below that level, so a patient hospitalized for two diseases leaves a level only when neither disease keeps it there, and is discharged only when no disease keeps it hospitalized. For a single disease this reproduces the previous behavior bit-for-bit (verified: identical output over a 60-day Bay Area COVID-19 run with the hospital model) and adds no random-number draws. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…kers # Conflicts: # data/.gitignore
p_death = min(1, M*p0) with M = 1 + (M_max-1)(1-q)/(1-s_min), capped at 1 and identical across baseline-risk groups, replacing 1 - q(1-p0) which drove low-risk patients toward certain death at extreme overload. New input hospital_model.mortality_multiplier_max (default 3.0). The score floor s_min now only normalizes the score field. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Added medical workers and a capacity-limited hospital model to
HospitalModel:developmentbranch exactly — for both census and UrbanPop initialization:so capacity falls as medical workers fall ill, withdraw, or die. The staffed-bed density (at full workforce strength) is specified by:
The treatment-quality decay under overload is controlled by:
utilities/build_hospital_data.py(county or census-tract level, tied to a year), and used via:County-level data scales each community's bed supply to its county's real supply; tract-level data places hospitals at the tracts that have them and routes patients and staff to the nearest hospital (reusing the home<->work agent movement). County and tract example files for CA, the SF Bay Area, NM, MA, and the whole US are included under
data/HospitalData/.Note: All the new input parameters are optional. The model is off by default (
agent.model_medical_workers = false), in which case the code behaves exactly the same as thedevelopmentbranch for both census and UrbanPop initialization.