Skip to content

Commit 73986f1

Browse files
Merge branch 'develop' into dash
2 parents 1847842 + 649307d commit 73986f1

File tree

8 files changed

+81
-26
lines changed

8 files changed

+81
-26
lines changed

k8s/app.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ spec:
2525
app: chime
2626
spec:
2727
containers:
28-
- image: docker.pkg.github.com/codeforphilly/chime/penn-chime:1.1.1
28+
- image: docker.pkg.github.com/codeforphilly/chime/penn-chime:1.1.2
2929
name: chime
3030
ports:
3131
- containerPort: 8000

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22
"""Setup file for chime
33
"""
4-
__version__ = "1.1.1"
4+
__version__ = "1.1.2"
55
__author__ = "Predictive Healthcare @ Penn Medicine"
66

77
from os import path

src/penn_chime/models.py

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from datetime import date, datetime, timedelta
1010
from logging import INFO, basicConfig, getLogger
1111
from sys import stdout
12-
from typing import Dict, Generator, Tuple, Sequence,Optional
12+
from typing import Dict, Generator, Tuple, Sequence, Optional
1313

1414
import numpy as np
1515
import pandas as pd
@@ -66,14 +66,13 @@ def __init__(self, p: Parameters):
6666
intrinsic_growth_rate = get_growth_rate(p.doubling_time)
6767

6868
self.beta = get_beta(intrinsic_growth_rate, gamma, self.susceptible, 0.0)
69+
self.beta_t = get_beta(intrinsic_growth_rate, self.gamma, self.susceptible, p.relative_contact_rate)
6970

7071
self.i_day = 0 # seed to the full length
71-
self.beta_t = self.beta
72-
self.run_projection(p)
72+
self.run_projection(p, [(self.beta, p.n_days)])
7373
self.i_day = i_day = int(get_argmin_ds(self.census_df, p.current_hospitalized))
7474

75-
self.beta_t = get_beta(intrinsic_growth_rate, self.gamma, self.susceptible, p.relative_contact_rate)
76-
self.run_projection(p)
75+
self.run_projection(p, self.gen_policy(p))
7776

7877
logger.info('Set i_day = %s', i_day)
7978
p.date_first_hospitalized = p.current_date - timedelta(days=i_day)
@@ -100,7 +99,7 @@ def __init__(self, p: Parameters):
10099
self.beta = get_beta(intrinsic_growth_rate, self.gamma, self.susceptible, 0.0)
101100
self.beta_t = get_beta(intrinsic_growth_rate, self.gamma, self.susceptible, p.relative_contact_rate)
102101

103-
self.run_projection(p)
102+
self.run_projection(p, self.gen_policy(p))
104103
loss = self.get_loss()
105104
losses[i] = loss
106105

@@ -109,7 +108,7 @@ def __init__(self, p: Parameters):
109108
intrinsic_growth_rate = get_growth_rate(p.doubling_time)
110109
self.beta = get_beta(intrinsic_growth_rate, self.gamma, self.susceptible, 0.0)
111110
self.beta_t = get_beta(intrinsic_growth_rate, self.gamma, self.susceptible, p.relative_contact_rate)
112-
self.run_projection(p)
111+
self.run_projection(p, self.gen_policy(p))
113112

114113
self.population = p.population
115114
else:
@@ -146,18 +145,35 @@ def __init__(self, p: Parameters):
146145
self.daily_growth_rate = get_growth_rate(p.doubling_time)
147146
self.daily_growth_rate_t = get_growth_rate(self.doubling_time_t)
148147

149-
def run_projection(self, p):
148+
def gen_policy(self, p: Parameters) -> Sequence[Tuple[float, int]]:
149+
if p.mitigation_date is not None:
150+
mitigation_day = -(p.current_date - p.mitigation_date).days
151+
else:
152+
mitigation_day = 0
153+
154+
total_days = self.i_day + p.n_days
155+
156+
if mitigation_day < -self.i_day:
157+
mitigation_day = -self.i_day
158+
159+
pre_mitigation_days = self.i_day + mitigation_day
160+
post_mitigation_days = total_days - pre_mitigation_days
161+
162+
return [
163+
(self.beta, pre_mitigation_days),
164+
(self.beta_t, post_mitigation_days),
165+
]
166+
167+
def run_projection(self, p: Parameters, policy: Sequence[Tuple[float, int]]):
150168
self.raw_df = sim_sir_df(
151169
self.susceptible,
152170
self.infected,
153171
p.recovered,
154172
self.gamma,
155173
-self.i_day,
156-
self.beta,
157-
self.i_day,
158-
self.beta_t,
159-
p.n_days
174+
policy
160175
)
176+
161177
self.dispositions_df = build_dispositions_df(self.raw_df, self.rates, p.market_share, p.current_date)
162178
self.admits_df = build_admits_df(self.dispositions_df)
163179
self.census_df = build_census_df(self.admits_df, self.days)
@@ -221,7 +237,7 @@ def sir(
221237

222238

223239
def gen_sir(
224-
s: float, i: float, r: float, gamma: float, i_day: int, *args
240+
s: float, i: float, r: float, gamma: float, i_day: int, policies: Sequence[Tuple[float, int]]
225241
) -> Generator[Tuple[int, float, float, float], None, None]:
226242
"""Simulate SIR model forward in time yielding tuples.
227243
Parameter order has changed to allow multiple (beta, n_days)
@@ -230,8 +246,7 @@ def gen_sir(
230246
s, i, r = (float(v) for v in (s, i, r))
231247
n = s + i + r
232248
d = i_day
233-
while args:
234-
beta, n_days, *args = args
249+
for beta, n_days in policies:
235250
for _ in range(n_days):
236251
yield d, s, i, r
237252
s, i, r = sir(s, i, r, beta, gamma, n)
@@ -241,11 +256,11 @@ def gen_sir(
241256

242257
def sim_sir_df(
243258
s: float, i: float, r: float,
244-
gamma: float, i_day: int, *args
259+
gamma: float, i_day: int, policies: Sequence[Tuple[float, int]]
245260
) -> pd.DataFrame:
246261
"""Simulate the SIR model forward in time."""
247262
return pd.DataFrame(
248-
data=gen_sir(s, i, r, gamma, i_day, *args),
263+
data=gen_sir(s, i, r, gamma, i_day, policies),
249264
columns=("day", "susceptible", "infected", "recovered"),
250265
)
251266

src/penn_chime/parameters.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def __init__(
5555
hospitalized: Disposition,
5656
icu: Disposition,
5757
relative_contact_rate: float,
58+
mitigation_date: Optional[date] = None,
5859
ventilated: Disposition,
5960
current_date: date = date.today(),
6061
date_first_hospitalized: Optional[date] = None,
@@ -68,7 +69,6 @@ def __init__(
6869
region: Optional[Regions] = None,
6970
):
7071
self.current_hospitalized = Positive(value=current_hospitalized)
71-
self.relative_contact_rate = Rate(value=relative_contact_rate)
7272

7373
Rate(value=hospitalized.rate), Rate(value=icu.rate), Rate(value=ventilated.rate)
7474
StrictlyPositive(value=hospitalized.days), StrictlyPositive(value=icu.days),
@@ -92,6 +92,9 @@ def __init__(
9292
self.date_first_hospitalized = OptionalDate(value=date_first_hospitalized)
9393
self.doubling_time = OptionalStrictlyPositive(value=doubling_time)
9494

95+
self.relative_contact_rate = Rate(value=relative_contact_rate)
96+
self.mitigation_date = OptionalDate(value=mitigation_date)
97+
9598
self.infectious_days = StrictlyPositive(value=infectious_days)
9699
self.market_share = Rate(value=market_share)
97100
self.max_y_axis = OptionalStrictlyPositive(value=max_y_axis)

src/penn_chime/presentation.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
CHANGE_DATE,
1212
DATE_FORMAT,
1313
DOCS_URL,
14+
EPSILON,
1415
FLOAT_INPUT_MIN,
1516
FLOAT_INPUT_STEP,
1617
)
@@ -58,9 +59,12 @@ def display_header(st, m, p):
5859
)
5960
st.markdown(
6061
"""*This tool was developed by the [Predictive Healthcare team](http://predictivehealthcare.pennmedicine.org/) at
61-
Penn Medicine to assist hospitals and public health officials with hospital capacity planning,
62-
but can be used anywhere in the world.
63-
Customize it for your region by modifying data inputs in the left panel.*
62+
Penn Medicine to assist hospitals and public health officials with hospital capacity planning.*"""
63+
)
64+
st.markdown(
65+
"""**Notice**: *There is a high
66+
degree of uncertainty about the details of COVID-19 infection, transmission, and the effectiveness of social distancing
67+
measures. Long-term projections made using this simplified model of outbreak progression should be treated with extreme caution.*
6468
"""
6569
)
6670

@@ -204,6 +208,10 @@ def display_sidebar(st, d: Parameters) -> Parameters:
204208
st_obj, "Date of first hospitalized case - Enter this date to have chime estimate the initial doubling time",
205209
value=d.date_first_hospitalized,
206210
)
211+
mitigation_date_input = DateInput(
212+
st_obj, "Date of social distancing measures effect (may be delayed from implementation)",
213+
value=d.mitigation_date
214+
)
207215
relative_contact_pct_input = PercentInput(
208216
st_obj,
209217
"Social distancing (% reduction in social contact going forward)",
@@ -279,7 +287,7 @@ def display_sidebar(st, d: Parameters) -> Parameters:
279287

280288
# Build in desired order
281289
st.sidebar.markdown(
282-
"""**CHIME [v1.1.1](https://github.com/CodeForPhilly/chime/releases/tag/v1.1.1) ({change_date})**""".format(
290+
"""**CHIME [v1.1.2](https://github.com/CodeForPhilly/chime/releases/tag/v1.1.1) ({change_date})**""".format(
283291
change_date=CHANGE_DATE
284292
)
285293
)
@@ -309,7 +317,15 @@ def display_sidebar(st, d: Parameters) -> Parameters:
309317
doubling_time = doubling_time_input()
310318
date_first_hospitalized = None
311319

312-
relative_contact_rate = relative_contact_pct_input()
320+
if st.sidebar.checkbox(
321+
"Social distancing measures have been implemented",
322+
value=(d.relative_contact_rate > EPSILON)
323+
):
324+
mitigation_date = mitigation_date_input()
325+
relative_contact_rate = relative_contact_pct_input()
326+
else:
327+
mitigation_date = None
328+
relative_contact_rate = EPSILON
313329

314330
st.sidebar.markdown(
315331
"### Severity Parameters [ℹ]({docs_url}/what-is-chime/parameters#severity-parameters)".format(
@@ -343,6 +359,7 @@ def display_sidebar(st, d: Parameters) -> Parameters:
343359
hospitalized=Disposition(hospitalized_rate, hospitalized_days),
344360
icu=Disposition(icu_rate, icu_days),
345361
relative_contact_rate=relative_contact_rate,
362+
mitigation_date=mitigation_date,
346363
ventilated=Disposition(ventilated_rate, ventilated_days),
347364
current_date=current_date,
348365
date_first_hospitalized=date_first_hospitalized,

src/penn_chime/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def get_defaults():
1616
infectious_days=14,
1717
market_share=0.15,
1818
n_days=100,
19+
mitigation_date=date.today(),
1920
relative_contact_rate=0.3,
2021
ventilated=Disposition(0.005, 10),
2122
)

tests/conftest.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def DEFAULTS():
5151
doubling_time=4.0,
5252
n_days=60,
5353
market_share=0.15,
54+
mitigation_date=datetime(year=2020, month=3, day=28),
5455
relative_contact_rate=0.3,
5556
hospitalized=Disposition(0.025, 7),
5657
icu=Disposition(0.0075, 9),
@@ -65,6 +66,7 @@ def param():
6566
current_hospitalized=100,
6667
doubling_time=6.0,
6768
market_share=0.05,
69+
mitigation_date=datetime(year=2020, month=3, day=28),
6870
relative_contact_rate=0.15,
6971
population=500000,
7072
hospitalized=Disposition(0.05, 7),
@@ -81,6 +83,7 @@ def halving_param():
8183
current_hospitalized=100,
8284
doubling_time=6.0,
8385
market_share=0.05,
86+
mitigation_date=datetime(year=2020, month=3, day=28),
8487
relative_contact_rate=0.7,
8588
population=500000,
8689
hospitalized=Disposition(0.05, 7),

tests/penn_chime/test_models.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
import pytest
44
import pandas as pd
55
import numpy as np
6+
from datetime import timedelta
67

78
from src.penn_chime.models import (
89
sir,
910
sim_sir_df,
1011
get_growth_rate,
12+
SimSirModel,
1113
)
1214

1315
from src.penn_chime.constants import EPSILON
@@ -64,7 +66,7 @@ def test_sim_sir():
6466
Rounding to move fast past decimal place issues
6567
"""
6668
raw_df = sim_sir_df(
67-
5, 6, 7, 0.1, 0, 0.1, 40, # s # i # r # gamma # i_day # beta1 # n_days1
69+
5, 6, 7, 0.1, 0, [(0.1, 40)], # s # i # r # gamma # i_day # beta1 # n_days1
6870
)
6971

7072
first = raw_df.iloc[0, :]
@@ -100,6 +102,20 @@ def test_model(model, param):
100102
assert model.r_t == 2.307298374881539
101103
assert model.r_naught == 2.7144686763312222
102104
assert model.doubling_time_t == 7.764405988534983
105+
assert model.i_day == 43
106+
107+
108+
def test_model_first_hosp_fit(param):
109+
param.date_first_hospitalized = param.current_date - timedelta(days=43)
110+
param.doubling_time = None
111+
112+
my_model = SimSirModel(param)
113+
114+
assert my_model.intrinsic_growth_rate == 0.12246204830937302
115+
assert abs(my_model.beta - 4.21501347256401e-07) < EPSILON
116+
assert my_model.r_t == 2.307298374881539
117+
assert my_model.r_naught == 2.7144686763312222
118+
assert my_model.doubling_time_t == 7.764405988534983
103119

104120

105121
def test_model_raw_start(model, param):

0 commit comments

Comments
 (0)