Skip to content

Commit 0f1f27e

Browse files
authored
Merge pull request #1 from hpvsim/modernize-v2.3.0
Modernize hpvsim_tanzania for hpvsim v2.3.0
2 parents 9b271f9 + ab42496 commit 0f1f27e

25 files changed

Lines changed: 686 additions & 205 deletions

.github/workflows/ci.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
name: tests
2+
on: [push, pull_request]
3+
jobs:
4+
test:
5+
runs-on: ubuntu-latest
6+
steps:
7+
- uses: actions/checkout@v4
8+
- uses: actions/setup-python@v5
9+
with:
10+
python-version: '3.11'
11+
- run: pip install -r requirements.txt
12+
- run: python -m pytest tests/ -v # incl. full-resolution ASR validation (~1-2 min)

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
__pycache__/
2+
*.pyc
3+
*.sim
4+
*.obj
5+
!results/tanzania_pars.obj
6+
raw_results/*
7+
!raw_results/.gitkeep
8+
figures/*
9+
!figures/.gitkeep
10+
.DS_Store

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Changelog
2+
3+
## 2026-06-05 — v2.3.0 modernization
4+
- Rebuilt onto hpvsim v2.3.0; Tanzania parameters folded into `make_sim` (single-country
5+
convention).
6+
- Ported data and calibration framework from `hpvsim_1dose`; replaced the dead
7+
Nov-2023 parameter object.
8+
- Validated the existing calibration under v2.3.0 (baseline ASR ≈ 60.7 vs Globocan 62.5)
9+
— no recalibration required.
10+
- Added illustrative VIA-vs-HPV screen-&-treat scenarios, smoke + validation tests, CI,
11+
README, and packaging.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023-2026 Gates Foundation
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,64 @@
11
# hpvsim_tanzania
2-
HPVsim analyses in Tanzania
2+
3+
An [HPVsim](https://hpvsim.org) model of cervical cancer for Tanzania, calibrated to
4+
Globocan/IARC incidence data. Built on **hpvsim v2.3.0**.
5+
6+
## Install
7+
8+
```bash
9+
pip install -r requirements.txt
10+
```
11+
12+
Requires `hpvsim==2.3.0`.
13+
14+
## What's here
15+
16+
| File | Purpose |
17+
|------|---------|
18+
| `run_sim.py` | Defines the Tanzania simulation (`make_sim`, `run_sim`). Tanzania-specific parameters (sexual behaviour, mixing, layer probabilities) are inlined here. |
19+
| `run_calibration.py` | Calibrates the model to Tanzania data (`hpv.Calibration`). |
20+
| `run_scenarios.py` | Illustrative screen-&-treat scenarios (VIA vs HPV-test). |
21+
| `utils.py` | Fonts, sexual-behaviour data, calibration datafiles. |
22+
| `analyzers.py` | `cohort_cancers` analyzer for projection runs. |
23+
| `data/` | Calibration targets (cancer cases, ASR, genotype distributions) + sexual-behaviour parameters. |
24+
| `results/tanzania_pars.obj` | Calibrated parameter set (validated under v2.3.0). |
25+
| `tests/` | Smoke + validation tests. |
26+
27+
## Data provenance
28+
29+
- `tanzania_asr_cancer_incidence.csv`, `tanzania_cancer_cases.csv` — Globocan/IARC
30+
cervical cancer incidence (ASR ≈ 62.5 per 100,000, 2020).
31+
- `tanzania_cancer_types.csv`, `tanzania_cin3_types.csv` — HPV genotype distribution in
32+
cancers / CIN3 (ICO/IARC HPV Information Centre).
33+
- `sb_pars_{women,men}_lognormal.csv` — DHS-fitted sexual-debut distributions.
34+
35+
Data and the calibration framework were ported from `hpvsim_1dose`.
36+
37+
## How to run
38+
39+
```bash
40+
python run_sim.py # single baseline run + plot (local)
41+
42+
# Calibration — RUN only on a multi-core VM (edit `to_run` in the file):
43+
python run_calibration.py # 'plot_calibration' extracts/plots locally;
44+
# 'run_calibration' fits (VM only)
45+
46+
python run_scenarios.py # VIA vs HPV-test screen-&-treat comparison
47+
```
48+
49+
> **Calibration compute:** the calibration is only fast on multi-core machines. Run the
50+
> `run_calibration` step on a VM; use the `plot_calibration` step locally to extract the
51+
> best parameters into `results/tanzania_pars.obj`.
52+
53+
## Calibration status
54+
55+
The parameter set in `results/tanzania_pars.obj` reproduces the Globocan 2020 ASR target
56+
(model ≈ 60.7 vs target 62.5 per 100,000) under hpvsim v2.3.0 — see
57+
`tests/test_baseline.py`.
58+
59+
## Testing
60+
61+
```bash
62+
pytest tests/ -k "debug or load or scenarios" # fast subset (also run in CI)
63+
pytest tests/ # full, incl. ~1-2 min ASR validation
64+
```

analyzers.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"""Custom analyzers for hpvsim_tanzania."""
2+
import numpy as np
3+
import sciris as sc
4+
import hpvsim as hpv
5+
import hpvsim.utils as hpu
6+
7+
8+
class cohort_cancers(hpv.Analyzer):
9+
"""Track scaled cancer cases in an ageing cohort from a given start year."""
10+
11+
def __init__(self, cohort_age=None, start=None, **kwargs):
12+
super().__init__(**kwargs)
13+
self.start = start or 2024
14+
self.cohort_age = cohort_age or [9, 16]
15+
self.years = None
16+
self.results = None
17+
return
18+
19+
def initialize(self, sim):
20+
super().initialize()
21+
self.si = sc.findfirst(sim.res_yearvec, self.start)
22+
self.npts = len(sim.res_yearvec[self.si:])
23+
self.years = sim.res_yearvec[self.si:]
24+
self.results = np.zeros(self.npts)
25+
return
26+
27+
def apply(self, sim):
28+
if sim.yearvec[sim.t] >= self.start:
29+
li = np.floor(sim.yearvec[sim.t])
30+
idx = sc.findfirst(self.years, li)
31+
ppl = sim.people
32+
time_elapsed = sim.yearvec[sim.t] - self.start
33+
current_age_range = [self.cohort_age[0] + time_elapsed, self.cohort_age[1] + time_elapsed]
34+
cic = (ppl.date_cancerous == sim.t) & (ppl.age >= current_age_range[0]) & (ppl.age <= current_age_range[1])
35+
if cic.any():
36+
self.results[idx] += sum(ppl.scale[hpu.true(cic)])
37+
return
38+
39+
@staticmethod
40+
def reduce(analyzers, use_mean=False, quantiles=None):
41+
if quantiles is None:
42+
quantiles = {'low': 0.1, 'high': 0.9}
43+
if not isinstance(quantiles, dict):
44+
try:
45+
quantiles = {'low': float(quantiles[0]), 'high': float(quantiles[1])}
46+
except Exception as E:
47+
errormsg = (f'Could not convert {quantiles} into a quantiles object: must be a dict '
48+
f'with keys low, high or a 2-element array ({str(E)})')
49+
raise ValueError(errormsg)
50+
51+
base_analyzer = analyzers[0]
52+
reduced_analyzer = sc.dcp(base_analyzer)
53+
ashape = base_analyzer.results.shape
54+
new_ashape = ashape + (len(analyzers),)
55+
raw = np.zeros(new_ashape)
56+
for a, analyzer in enumerate(analyzers):
57+
raw[:, a] = analyzer.results
58+
59+
reduced_analyzer.raw = raw
60+
reduced_analyzer.results = np.quantile(raw, q=0.5, axis=-1)
61+
reduced_analyzer.low = np.quantile(raw, q=quantiles['low'], axis=-1)
62+
reduced_analyzer.high = np.quantile(raw, q=quantiles['high'], axis=-1)
63+
64+
sums = raw.sum(axis=0)
65+
reduced_analyzer.cum_cancers_best = np.quantile(sums, q=0.5)
66+
reduced_analyzer.cum_cancers_low = np.quantile(sums, q=quantiles['low'])
67+
reduced_analyzer.cum_cancers_high = np.quantile(sums, q=quantiles['high'])
68+
69+
return reduced_analyzer

assets/LibertinusSans-Regular.otf

251 KB
Binary file not shown.

data/sb_pars_men_lognormal.csv

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
,location,par1,par2,dist
2+
0,Bangladesh,,,lognormal
3+
1,Burkina Faso,,,lognormal
4+
2,Cambodia,,,lognormal
5+
3,Cameroon,19.465022867081586,3.245297380451358,lognormal
6+
4,Ethiopia,,,lognormal
7+
5,Gambia,,,lognormal
8+
6,Malawi,18.661550897370137,3.157692331110352,lognormal
9+
7,Mali,,,lognormal
10+
8,Mozambique,17.691420976753562,2.623842932154142,lognormal
11+
9,Myanmar,,,lognormal
12+
10,Nepal,,,lognormal
13+
11,Nigeria,,,lognormal
14+
12,Sierra Leone,18.6896536067683,2.6830029097813672,lognormal
15+
13,Tanzania,18.39559348476703,2.7041700923084426,lognormal
16+
14,Togo,19.687920637220614,3.4045193073473548,lognormal
17+
15,Uganda,18.616848509079883,2.8665946291840183,lognormal
18+
16,Zambia,18.6459911760071,3.059130649059651,lognormal

data/sb_pars_women_lognormal.csv

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
,location,par1,par2,dist
2+
0,Bangladesh,16.882058104942626,3.6539509187811747,lognormal
3+
1,Burkina Faso,17.841716818965278,2.2622639478972144,lognormal
4+
2,Cambodia,21.234633427297076,3.183132964759674,lognormal
5+
3,Cameroon,17.16236273422492,2.3776355376509564,lognormal
6+
4,Ethiopia,16.771792032805386,2.419149200119716,lognormal
7+
5,Gambia,19.094106290622094,3.390622798455199,lognormal
8+
6,Malawi,16.94175754928015,2.20549526176653,lognormal
9+
7,Mali,16.602597271302887,1.8543471003865675,lognormal
10+
8,Mozambique,16.993535086928812,2.5868492413358113,lognormal
11+
9,Myanmar,23.50358554987862,4.945225886687883,lognormal
12+
10,Nepal,18.736225980455835,3.003703400415313,lognormal
13+
11,Nigeria,17.413867713187244,2.7546376031680846,lognormal
14+
12,Sierra Leone,16.087238093512617,1.6822121223258502,lognormal
15+
13,Tanzania,17.33401630838652,2.1680691217833727,lognormal
16+
14,Togo,18.43159845947176,2.949764744803772,lognormal
17+
15,Uganda,17.070537516937275,2.431205495785502,lognormal
18+
16,Zambia,16.694696425114053,1.7857668639582425,lognormal
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
year,name,genotype,value
2+
2020,asr_cancer_incidence,total,62.5

0 commit comments

Comments
 (0)