example_flu.py

  1"""An illustration of the use of the workbench for a model specified in Python itself.
  2
  3The example is based on `Pruyt & Hamarat <https://www.systemdynamics.org/conferences/2010/proceed/papers/P1253.pdf>`_.
  4For comparison, run both this model and example_flu_vensim_no_policy.py.
  5
  6"""
  7
  8import matplotlib.pyplot as plt
  9import numpy as np
 10from numpy import exp, min, sin
 11
 12from ema_workbench import (
 13    Model,
 14    RealParameter,
 15    SequentialEvaluator,
 16    TimeSeriesOutcome,
 17    ema_logging,
 18    perform_experiments,
 19)
 20from ema_workbench.analysis import Density, lines
 21
 22# Created on 20 dec. 2010
 23# .. codeauthor:: jhkwakkel <j.h.kwakkel (at) tudelft (dot) nl>
 24#                 chamarat <c.hamarat  (at) tudelft (dot) nl>
 25
 26
 27# =============================================================================
 28#
 29#    the model itself
 30#
 31# =============================================================================
 32
 33FINAL_TIME = 48
 34INITIAL_TIME = 0
 35TIME_STEP = 0.0078125
 36
 37switch_regions = 1.0
 38switch_immunity = 1.0
 39switch_deaths = 1.0
 40switch_immunity_cap = 1.0
 41
 42
 43def lookup_function_x(variable, start, end, step, skew, growth, v=0.5):
 44    """A basic lookup function."""
 45    return start + (
 46        (end - start) / ((1 + skew * exp(-growth * (variable - step))) ** (1 / v))
 47    )
 48
 49
 50def flu_model(
 51    x11=0,
 52    x12=0,
 53    x21=0,
 54    x22=0,
 55    x31=0,
 56    x32=0,
 57    x41=0,
 58    x51=0,
 59    x52=0,
 60    x61=0,
 61    x62=0,
 62    x81=0,
 63    x82=0,
 64    x91=0,
 65    x92=0,
 66    x101=0,
 67    x102=0,
 68):
 69    """The overall model."""
 70    # Assigning initial values
 71    additional_seasonal_immune_population_fraction_r1 = float(x11)
 72    additional_seasonal_immune_population_fraction_r2 = float(x12)
 73
 74    fatality_rate_region_1 = float(x21)
 75    fatality_rate_region_2 = float(x22)
 76
 77    initial_immune_fraction_of_the_population_of_region_1 = float(x31)
 78    initial_immune_fraction_of_the_population_of_region_2 = float(x32)
 79
 80    normal_interregional_contact_rate = float(x41)
 81    interregional_contact_rate = switch_regions * normal_interregional_contact_rate
 82
 83    permanent_immune_population_fraction_r1 = float(x51)
 84    permanent_immune_population_fraction_r2 = float(x52)
 85
 86    recovery_time_region_1 = float(x61)
 87    recovery_time_region_2 = float(x62)
 88
 89    susceptible_to_immune_population_delay_time_region_1 = 1
 90    susceptible_to_immune_population_delay_time_region_2 = 1
 91
 92    root_contact_rate_region_1 = float(x81)
 93    root_contact_rate_region_2 = float(x82)
 94
 95    infection_rate_region_1 = float(x91)
 96    infection_rate_region_2 = float(x92)
 97
 98    normal_contact_rate_region_1 = float(x101)
 99    normal_contact_rate_region_2 = float(x102)
100
101    ######
102    susceptible_to_immune_population_flow_region_1 = 0.0
103    susceptible_to_immune_population_flow_region_2 = 0.0
104    ######
105
106    initial_value_population_region_1 = 6.0 * 10**8
107    initial_value_population_region_2 = 3.0 * 10**9
108
109    initial_value_infected_population_region_1 = 10.0
110    initial_value_infected_population_region_2 = 10.0
111
112    initial_value_immune_population_region_1 = (
113        switch_immunity
114        * initial_immune_fraction_of_the_population_of_region_1
115        * initial_value_population_region_1
116    )
117    initial_value_immune_population_region_2 = (
118        switch_immunity
119        * initial_immune_fraction_of_the_population_of_region_2
120        * initial_value_population_region_2
121    )
122
123    initial_value_susceptible_population_region_1 = (
124        initial_value_population_region_1 - initial_value_immune_population_region_1
125    )
126    initial_value_susceptible_population_region_2 = (
127        initial_value_population_region_2 - initial_value_immune_population_region_2
128    )
129
130    recovered_population_region_1 = 0.0
131    recovered_population_region_2 = 0.0
132
133    infected_population_region_1 = initial_value_infected_population_region_1
134    infected_population_region_2 = initial_value_infected_population_region_2
135
136    susceptible_population_region_1 = initial_value_susceptible_population_region_1
137    susceptible_population_region_2 = initial_value_susceptible_population_region_2
138
139    immune_population_region_1 = initial_value_immune_population_region_1
140    immune_population_region_2 = initial_value_immune_population_region_2
141
142    deceased_population_region_1 = [0.0]
143    deceased_population_region_2 = [0.0]
144    run_time = [INITIAL_TIME]
145
146    # --End of Initialization--
147
148    max_infected = 0.0
149
150    for time in range(int(INITIAL_TIME / TIME_STEP), int(FINAL_TIME / TIME_STEP)):
151        run_time.append(run_time[-1] + TIME_STEP)
152        total_population_region_1 = (
153            infected_population_region_1
154            + recovered_population_region_1
155            + susceptible_population_region_1
156            + immune_population_region_1
157        )
158        total_population_region_2 = (
159            infected_population_region_2
160            + recovered_population_region_2
161            + susceptible_population_region_2
162            + immune_population_region_2
163        )
164
165        infected_population_region_1 = max(0, infected_population_region_1)
166        infected_population_region_2 = max(0, infected_population_region_2)
167
168        infected_fraction_region_1 = (
169            infected_population_region_1 / total_population_region_1
170        )
171        infected_fraction_region_2 = (
172            infected_population_region_2 / total_population_region_2
173        )
174
175        impact_infected_population_on_contact_rate_region_1 = 1 - (
176            infected_fraction_region_1 ** (1 / root_contact_rate_region_1)
177        )
178        impact_infected_population_on_contact_rate_region_2 = 1 - (
179            infected_fraction_region_2 ** (1 / root_contact_rate_region_2)
180        )
181
182        #        if ((time*TIME_STEP) >= 4) and ((time*TIME_STEP)<=10):
183        #            normal_contact_rate_region_1 = float(x101)*(1 - 0.5)
184        #        else:normal_contact_rate_region_1 = float(x101)
185
186        normal_contact_rate_region_1 = float(x101) * (
187            1 - lookup_function_x(infected_fraction_region_1, 0, 1, 0.15, 0.75, 15)
188        )
189
190        contact_rate_region_1 = (
191            normal_contact_rate_region_1
192            * impact_infected_population_on_contact_rate_region_1
193        )
194        contact_rate_region_2 = (
195            normal_contact_rate_region_2
196            * impact_infected_population_on_contact_rate_region_2
197        )
198
199        recoveries_region_1 = (
200            (1 - (fatality_rate_region_1 * switch_deaths))
201            * infected_population_region_1
202            / recovery_time_region_1
203        )
204        recoveries_region_2 = (
205            (1 - (fatality_rate_region_2 * switch_deaths))
206            * infected_population_region_2
207            / recovery_time_region_2
208        )
209
210        flu_deaths_region_1 = (
211            fatality_rate_region_1
212            * switch_deaths
213            * infected_population_region_1
214            / recovery_time_region_1
215        )
216        flu_deaths_region_2 = (
217            fatality_rate_region_2
218            * switch_deaths
219            * infected_population_region_2
220            / recovery_time_region_2
221        )
222
223        infections_region_1 = (
224            susceptible_population_region_1
225            * contact_rate_region_1
226            * infection_rate_region_1
227            * infected_fraction_region_1
228        ) + (
229            susceptible_population_region_1
230            * interregional_contact_rate
231            * infection_rate_region_1
232            * infected_fraction_region_2
233        )
234        infections_region_2 = (
235            susceptible_population_region_2
236            * contact_rate_region_2
237            * infection_rate_region_2
238            * infected_fraction_region_2
239        ) + (
240            susceptible_population_region_2
241            * interregional_contact_rate
242            * infection_rate_region_2
243            * infected_fraction_region_1
244        )
245
246        infected_population_region_1_next = infected_population_region_1 + (
247            TIME_STEP
248            * (infections_region_1 - flu_deaths_region_1 - recoveries_region_1)
249        )
250        infected_population_region_2_next = infected_population_region_2 + (
251            TIME_STEP
252            * (infections_region_2 - flu_deaths_region_2 - recoveries_region_2)
253        )
254
255        if (
256            infected_population_region_1_next < 0
257            or infected_population_region_2_next < 0
258        ):
259            pass
260
261        recovered_population_region_1_next = recovered_population_region_1 + (
262            TIME_STEP * recoveries_region_1
263        )
264        recovered_population_region_2_next = recovered_population_region_2 + (
265            TIME_STEP * recoveries_region_2
266        )
267
268        if fatality_rate_region_1 >= 0.025:
269            qw = 1.0
270        elif fatality_rate_region_1 >= 0.01:
271            qw = 0.8
272        elif fatality_rate_region_1 >= 0.001:
273            qw = 0.6
274        elif fatality_rate_region_1 >= 0.0001:
275            qw = 0.4
276        else:
277            qw = 0.2
278
279        if (time * TIME_STEP) <= 10:
280            normal_immune_population_fraction_region_1 = (
281                additional_seasonal_immune_population_fraction_r1 / 2
282            ) * sin(4.5 + (time * TIME_STEP / 2)) + (
283                (
284                    (2 * permanent_immune_population_fraction_r1)
285                    + additional_seasonal_immune_population_fraction_r1
286                )
287                / 2
288            )
289        else:
290            normal_immune_population_fraction_region_1 = max(
291                (
292                    float(qw),
293                    (additional_seasonal_immune_population_fraction_r1 / 2)
294                    * sin(4.5 + (time * TIME_STEP / 2))
295                    + (
296                        (
297                            (2 * permanent_immune_population_fraction_r1)
298                            + additional_seasonal_immune_population_fraction_r1
299                        )
300                        / 2
301                    ),
302                )
303            )
304
305        normal_immune_population_fraction_region_2 = switch_immunity_cap * min(
306            (
307                (
308                    sin((time * TIME_STEP / 2) + 1.5)
309                    * additional_seasonal_immune_population_fraction_r2
310                    / 2
311                )
312                + (
313                    (
314                        (2 * permanent_immune_population_fraction_r2)
315                        + additional_seasonal_immune_population_fraction_r2
316                    )
317                    / 2
318                ),
319                (
320                    permanent_immune_population_fraction_r1
321                    + additional_seasonal_immune_population_fraction_r1
322                ),
323            ),
324        ) + (
325            (1 - switch_immunity_cap)
326            * (
327                (
328                    sin((time * TIME_STEP / 2) + 1.5)
329                    * additional_seasonal_immune_population_fraction_r2
330                    / 2
331                )
332                + (
333                    (
334                        (2 * permanent_immune_population_fraction_r2)
335                        + additional_seasonal_immune_population_fraction_r2
336                    )
337                    / 2
338                )
339            )
340        )
341
342        normal_immune_population_region_1 = (
343            normal_immune_population_fraction_region_1 * total_population_region_1
344        )
345        normal_immune_population_region_2 = (
346            normal_immune_population_fraction_region_2 * total_population_region_2
347        )
348
349        if switch_immunity == 1:
350            susminreg1_1 = (
351                normal_immune_population_region_1 - immune_population_region_1
352            ) / susceptible_to_immune_population_delay_time_region_1
353            susminreg1_2 = (
354                susceptible_population_region_1
355                / susceptible_to_immune_population_delay_time_region_1
356            )
357            susmaxreg1 = -(
358                immune_population_region_1
359                / susceptible_to_immune_population_delay_time_region_1
360            )
361            if (susmaxreg1 >= susminreg1_1) or (susmaxreg1 >= susminreg1_2):
362                susceptible_to_immune_population_flow_region_1 = susmaxreg1
363            elif (susminreg1_1 < susminreg1_2) and (susminreg1_1 > susmaxreg1):
364                susceptible_to_immune_population_flow_region_1 = susminreg1_1
365            elif (susminreg1_2 < susminreg1_1) and (susminreg1_2 > susmaxreg1):
366                susceptible_to_immune_population_flow_region_1 = susminreg1_2
367        else:
368            susceptible_to_immune_population_flow_region_1 = 0
369
370        if switch_immunity == 1:
371            susminreg2_1 = (
372                normal_immune_population_region_2 - immune_population_region_2
373            ) / susceptible_to_immune_population_delay_time_region_2
374            susminreg2_2 = (
375                susceptible_population_region_2
376                / susceptible_to_immune_population_delay_time_region_2
377            )
378            susmaxreg2 = -(
379                immune_population_region_2
380                / susceptible_to_immune_population_delay_time_region_2
381            )
382            if (susmaxreg2 >= susminreg2_1) or (susmaxreg2 >= susminreg2_2):
383                susceptible_to_immune_population_flow_region_2 = susmaxreg2
384            elif (susminreg2_1 < susminreg2_2) and (susminreg2_1 > susmaxreg2):
385                susceptible_to_immune_population_flow_region_2 = susminreg2_1
386            elif (susminreg2_2 < susminreg2_1) and (susminreg2_2 > susmaxreg2):
387                susceptible_to_immune_population_flow_region_2 = susminreg2_2
388        else:
389            susceptible_to_immune_population_flow_region_2 = 0
390
391        susceptible_population_region_1_next = susceptible_population_region_1 - (
392            TIME_STEP
393            * (infections_region_1 + susceptible_to_immune_population_flow_region_1)
394        )
395        susceptible_population_region_2_next = susceptible_population_region_2 - (
396            TIME_STEP
397            * (infections_region_2 + susceptible_to_immune_population_flow_region_2)
398        )
399
400        immune_population_region_1_next = immune_population_region_1 + (
401            TIME_STEP * susceptible_to_immune_population_flow_region_1
402        )
403        immune_population_region_2_next = immune_population_region_2 + (
404            TIME_STEP * susceptible_to_immune_population_flow_region_2
405        )
406
407        deceased_population_region_1_next = deceased_population_region_1[-1] + (
408            TIME_STEP * flu_deaths_region_1
409        )
410        deceased_population_region_2_next = deceased_population_region_2[-1] + (
411            TIME_STEP * flu_deaths_region_2
412        )
413
414        # Updating integral values
415        if max_infected < (
416            infected_population_region_1_next
417            / (
418                infected_population_region_1_next
419                + recovered_population_region_1_next
420                + susceptible_population_region_1_next
421                + immune_population_region_1_next
422            )
423        ):
424            max_infected = infected_population_region_1_next / (
425                infected_population_region_1_next
426                + recovered_population_region_1_next
427                + susceptible_population_region_1_next
428                + immune_population_region_1_next
429            )
430
431        recovered_population_region_1 = recovered_population_region_1_next
432        recovered_population_region_2 = recovered_population_region_2_next
433
434        infected_population_region_1 = infected_population_region_1_next
435        infected_population_region_2 = infected_population_region_2_next
436
437        susceptible_population_region_1 = susceptible_population_region_1_next
438        susceptible_population_region_2 = susceptible_population_region_2_next
439
440        immune_population_region_1 = immune_population_region_1_next
441        immune_population_region_2 = immune_population_region_2_next
442
443        deceased_population_region_1.append(deceased_population_region_1_next)
444        deceased_population_region_2.append(deceased_population_region_2_next)
445
446        # End of main code
447
448    return {
449        "TIME": run_time,
450        "deceased_population_region_1": deceased_population_region_1,
451    }
452
453
454if __name__ == "__main__":
455    ema_logging.log_to_stderr(ema_logging.INFO)
456
457    model = Model("mexicanFlu", function=flu_model)
458    model.uncertainties = [
459        RealParameter("x11", 0, 0.5),
460        RealParameter("x12", 0, 0.5),
461        RealParameter("x21", 0.0001, 0.1),
462        RealParameter("x22", 0.0001, 0.1),
463        RealParameter("x31", 0, 0.5),
464        RealParameter("x32", 0, 0.5),
465        RealParameter("x41", 0, 0.9),
466        RealParameter("x51", 0, 0.5),
467        RealParameter("x52", 0, 0.5),
468        RealParameter("x61", 0, 0.8),
469        RealParameter("x62", 0, 0.8),
470        RealParameter("x81", 1, 10),
471        RealParameter("x82", 1, 10),
472        RealParameter("x91", 0, 0.1),
473        RealParameter("x92", 0, 0.1),
474        RealParameter("x101", 0, 200),
475        RealParameter("x102", 0, 200),
476    ]
477
478    model.outcomes = [
479        TimeSeriesOutcome("TIME"),
480        TimeSeriesOutcome("deceased_population_region_1"),
481    ]
482
483    nr_experiments = 500
484
485    with SequentialEvaluator(model) as evaluator:
486        experiments, outcomes = perform_experiments(
487            model, nr_experiments, evaluator=evaluator
488        )
489
490    lines(
491        experiments,
492        outcomes,
493        outcomes_to_show="deceased_population_region_1",
494        show_envelope=True,
495        density=Density.KDE,
496        titles=None,
497        experiments_to_show=np.arange(0, nr_experiments, 10),
498    )
499    plt.show()