Skip to content

DemographicsTemplates

DemographicsTemplatesConstants

Mortality_Rates_Mod30_5yrs_Xval: Mod 30 values closest to the 5 yr age boundaries based on when EMOD actually updates individual mortality rates. The distribution is constant for about 5 years (e.g. values at 0.6 days and 1829.5 days) and linearly interpolated between the 5 yr boundaries.

Source code in emod_api/demographics/DemographicsTemplates.py
21
22
23
24
25
26
27
28
29
30
class DemographicsTemplatesConstants:
    """Mortality_Rates_Mod30_5yrs_Xval: Mod 30 values closest to the 5 yr age boundaries based on when EMOD actually updates individual mortality rates.
                                        The distribution is constant for about 5 years (e.g. values at 0.6 days and 1829.5 days) and linearly interpolated between the 5 yr boundaries. """
    Mortality_Rates_Mod30_5yrs_Xval = [0.6, 1829.5, 1829.6, 3659.5, 3659.6, 5489.5,
               5489.6, 7289.5, 7289.6, 9119.5, 9119.6, 10949.5,
               10949.6, 12779.5, 12779.6, 14609.5, 14609.6, 16439.5,
               16439.6, 18239.5, 18239.6, 20069.5, 20069.6, 21899.5,
               21899.6, 23729.5, 23729.6, 25559.5, 25559.6, 27389.5,
               27389.6, 29189.5, 29189.6, 31019.5, 31019.6, 32849.5,
               32849.6, 34679.5, 34679.6, 36509.5, 36509.6, 38339.5]

FullRisk(demog, description='')

FullRisk puts everyone at 100% risk.

Source code in emod_api/demographics/DemographicsTemplates.py
206
207
208
209
210
211
212
213
214
215
216
217
218
def FullRisk( demog, description="" ):
    """
    FullRisk puts everyone at 100% risk.
    """
    if not description:
        description = f"Setting full risk using default values"

    setting = {"RiskDist_Description": "Full risk",
            "RiskDistributionFlag": 0, # 0 = CONSTANT
            "RiskDistribution1": 1,
            "RiskDistribution2": 0,
            "RiskDistribution_Description": description}
    demog.SetDefaultFromTemplate( setting, _set_enable_demog_risk  )

InitRiskExponential(demog, mean=1.0)

InitRiskExponential puts everyone at somewhere between 0% risk and 100% risk, drawn from Exponential.

Parameters:

Name Type Description Default
mean float

Mean of exponential distribution.

1.0

Returns:

Raises:

Source code in emod_api/demographics/DemographicsTemplates.py
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
def InitRiskExponential( demog,
                         mean: float = 1.0 ):
    """
    InitRiskExponential puts everyone at somewhere between 0% risk and 100% risk, drawn from Exponential.

    Args:
        mean: Mean of exponential distribution. 

    Returns:

    Raises:

    """
    setting = {"RiskDist_Description": "Exponentially distributed risk",
            "RiskDistributionFlag": 3, # exponential
            "RiskDistribution1": mean,
            "RiskDistribution2": 0 }
    demog.SetDefaultFromTemplate( setting, _set_enable_demog_risk )

InitRiskLogNormal(demog, mean=0.0, sigma=1.0)

InitRiskLogNormal puts everyone at somewhere between 0% risk and 100% risk, drawn from LogNormal.

Parameters:

Name Type Description Default
mean float

Mean of lognormal distribution.

0.0
sigma float

Sigma of lognormal distribution.

1.0

Returns:

Raises:

Source code in emod_api/demographics/DemographicsTemplates.py
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
def InitRiskLogNormal( demog, mean=0.0, sigma=1.0 ):
    """
    InitRiskLogNormal puts everyone at somewhere between 0% risk and 100% risk, drawn from LogNormal.

    Args:
        mean (float): Mean of lognormal distribution.
        sigma (float): Sigma of lognormal distribution.

    Returns:

    Raises:

    """
    setting = {"RiskDist_Description": "LogNormal distributed risk",
            "RiskDistributionFlag": 5, # lognormal
            "RiskDistribution1": mean,
            "RiskDistribution2": sigma }
    demog.SetDefaultFromTemplate( setting, _set_enable_demog_risk )

InitRiskUniform(demog, min_lim=0, max_lim=1, description='')

InitRiskUniform puts everyone at somewhere between 0% risk and 100% risk, drawn uniformly.

Parameters:

Name Type Description Default
min_lim float

Low end of uniform distribution. Must be >=0, <1.

0
max_lim float

High end of uniform distribution. Must be >=min, <=1.

1
description str

Why were these values chosen?

''

Returns:

Raises:

Source code in emod_api/demographics/DemographicsTemplates.py
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
def InitRiskUniform(demog,
                    min_lim: float = 0,
                    max_lim: float = 1,
                    description: str = "" ):
    """
    InitRiskUniform puts everyone at somewhere between 0% risk and 100% risk, drawn uniformly.

    Args:
        min_lim: Low end of uniform distribution. Must be >=0, <1.
        max_lim: High end of uniform distribution. Must be >=min, <=1.
        description: Why were these values chosen?

    Returns:

    Raises:

    """
    if not description:
        description = f"Risk is drawn from a uniform distribution, min_lim={min_lim} and max_lim={max_lim}"

    if min_lim<0:
        raise ValueError( f"min_lim value of {min_lim} is less than 0. Not valid." )
    setting = {"RiskDist_Description": "Uniformly distributed risk",
               "RiskDistributionFlag": 1,
               "RiskDistribution1": min_lim,
               "RiskDistribution2": max_lim,
               "RiskDistribution_Description": description}
    demog.SetDefaultFromTemplate( setting, _set_enable_demog_risk  )

MortalityRateByAge(demog, age_bins, mort_rates)

Set (non-disease) mortality rates by age bins. No checks are done on input arrays.

Parameters:

Name Type Description Default
age_bins list[float]

list of age bins, with ages in years.

required
mort_rates list[float]

list of mortality rates, where mortality rate is daily probability of dying..

required

Returns:

Source code in emod_api/demographics/DemographicsTemplates.py
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
def MortalityRateByAge(demog,
                       age_bins: list[float],
                       mort_rates: list[float]):
    """
        Set (non-disease) mortality rates by age bins. No checks are done on input arrays.

        Args:
            age_bins: list of age bins, with ages in years.
            mort_rates: list of mortality rates, where mortality rate is daily probability of dying..

        Returns:

    """
    # Note that the first input axis is sex (or gender). There are two values, but the rates are applied
    # equally for both here. The second input axis is age bin, and that is much more configurable.
    mort_dist = {
        "MortalityDistribution": {
        "AxisNames": ["gender", "age"],
        "AxisUnits": ["male=0,female=1", "years"],
        "AxisScaleFactors": [1, 365],
        "PopulationGroups": [
            [0, 1],
            age_bins
        ],
        "ResultScaleFactor": 1,
        "ResultUnits": "daily probability of dying",
        "ResultValues": [
            mort_rates,
            mort_rates
        ]
    }
    }
    demog.SetDefaultFromTemplate( mort_dist, _set_mortality_age_gender )

NoInitialPrevalence(demog)

NoInitialPrevalence disables initial prevalence; outbreak seeding must be done from an Outbreak intervention (or serialized population).

Parameters:

Name Type Description Default
demog Demographics

Demographics object

required

Returns:

Raises:

Source code in emod_api/demographics/DemographicsTemplates.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
def NoInitialPrevalence( demog ):
    """
    NoInitialPrevalence disables initial prevalence; outbreak seeding must be done from an Outbreak intervention (or serialized population).

    Args:
        demog (Demographics): Demographics object

    Returns:

    Raises:

    """

    setting = {"PrevalenceDist_Description": "No initial prevalence",
               "InitialPrevalence": 0,
              }
    # why not just disable it at config?
    demog.SetDefaultFromTemplate( setting )

NoRisk()

NoRisk puts everyone at 0 risk.

Source code in emod_api/demographics/DemographicsTemplates.py
196
197
198
199
200
201
202
203
def NoRisk():
    """
    NoRisk puts everyone at 0 risk.
    """
    return {"RiskDist_Description": "No risk",
            "RiskDistributionFlag": 0, # 0 = CONSTANT
            "RiskDistribution1": 0,
            "RiskDistribution2": 0}

SimpleSusceptibilityDistribution(demog, meanAgeAtInfection=2.5)

Rough initialization to reduce burn-in and prevent huge outbreaks at sim start.
For ages 0 through 99 the susceptibility distribution is set to an exponential distribution with an average age at infection. The minimum susceptibility is 2.5% at old ages.

Parameters:

Name Type Description Default
demog Demographics

Demographics object

required
meanAgeAtInfection float

Rough average age at infection in years.

2.5

Note: Requires that config.parameters.Susceptibility_Initialization_Distribution_Type=DISTRIBUTION_COMPLEX

Source code in emod_api/demographics/DemographicsTemplates.py
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
def SimpleSusceptibilityDistribution( demog,
                                      meanAgeAtInfection: float=2.5): 
    """
    Rough initialization to reduce burn-in and prevent huge outbreaks at sim start.  
    For ages 0 through 99 the susceptibility distribution is set to an exponential distribution with an average age at infection.
    The minimum susceptibility is 2.5% at old ages.

    Args:
        demog (Demographics): Demographics object
        meanAgeAtInfection: Rough average age at infection in years.

    Note:
    Requires that ``config.parameters.Susceptibility_Initialization_Distribution_Type=DISTRIBUTION_COMPLEX``

    """
    # set config.Susceptibility_Initialization_Distribution_Type=COMPLEX
    # This function is first to be switched over to be reversed.
    # Calling code in emodpy will call this and pass the demographics instance then we
    # call SetDefaultFromTemplate on the demog object so we can also pass the setter function
    suscDist = {
        "SusceptibilityDist_Description": f"Rough initialization to reduce burn-in and prevent huge outbreaks at "
                                          f"sim start.  Exponential distribution, Average age at infection "
                                          f"~{meanAgeAtInfection} years, minimum susceptibility is 2.5% at old ages",
        "SusceptibilityDistribution": {
            "DistributionValues":  [i * 365 for i in range(100)],
            "ResultScaleFactor": 1,
            "ResultValues":  [1.0, 1.0] + [0.025 + 0.975 * math.exp(-(i - 1) / (meanAgeAtInfection / math.log(2)))
                                           for i in range(2, 100, 1)]
        }
    }
    demog.SetDefaultFromTemplate( suscDist, _set_suscept_complex )

birthrate_multiplier(pop_dat_file, base_year, start_year, max_daily_mort=0.01)

Create a birth rate multiplier from UN World Population data file. Args: pop_dat_file: path to UN World Population data file base_year: Base year/Reference year start_year: Read in the pop_dat_file starting with year 'start_year' max_daily_mort: Maximum daily mortality rate

Returns:

Type Description
tuple[ndarray, ndarray]

bith_rate_multiplier_x, birth_rate_multiplier_y

Source code in emod_api/demographics/DemographicsTemplates.py
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
def birthrate_multiplier(pop_dat_file: Path,
                         base_year: int,
                         start_year: int,
                         max_daily_mort: float = 0.01) -> tuple[np.ndarray, np.ndarray]:
    """
    Create a birth rate multiplier from UN World Population data file.
    Args:
        pop_dat_file: path to UN World Population data file
        base_year: Base year/Reference year
        start_year: Read in the pop_dat_file starting with year 'start_year'
        max_daily_mort: Maximum daily mortality rate

    Returns:
        bith_rate_multiplier_x, birth_rate_multiplier_y
    """
    # Load reference data
    year_vec, year_init, pop_mat, pop_init = _read_un_worldpop_file(pop_dat_file, base_year, start_year)

    t_delta = np.diff(year_vec)
    pow_vec = 365.0*t_delta
    mortvecs = _calculate_mortility_vectors(pop_mat, t_delta, max_daily_mort)

    tot_pop  = np.sum(pop_mat, axis=0)
    tpop_mid = (tot_pop[:-1]+tot_pop[1:])/2.0
    pop_corr = np.exp(-mortvecs[0, :]*pow_vec/2.0)

    brate_vec = np.round(pop_mat[0, 1:]/tpop_mid/t_delta*1000.0, 1)/pop_corr
    brate_val = np.interp(year_init, year_vec[:-1], brate_vec)

    # Calculate birth rate multiplier
    yrs_off = year_vec[:-1]-year_init
    yrs_dex = (yrs_off>0)

    birth_rate_mult_x_temp = np.array([0.0] + (365.0*yrs_off[yrs_dex]).tolist())
    birth_rate_mult_y_temp = np.array([1.0] + (brate_vec[yrs_dex]/brate_val).tolist())
    bith_rate_multiplier_x = np.zeros(2*len(birth_rate_mult_x_temp)-1)
    birth_rate_multiplier_y = np.zeros(2*len(birth_rate_mult_y_temp)-1)

    bith_rate_multiplier_x[0::2] = birth_rate_mult_x_temp[0:]
    birth_rate_multiplier_y[0::2] = birth_rate_mult_y_temp[0:]
    bith_rate_multiplier_x[1::2] = birth_rate_mult_x_temp[1:]-0.5
    birth_rate_multiplier_y[1::2] = birth_rate_mult_y_temp[0:-1]

    return bith_rate_multiplier_x, birth_rate_multiplier_y

demographicsBuilder(pop_dat_file, base_year, start_year=1950, max_daily_mort=0.01, mortality_rate_x_values=DemographicsTemplatesConstants.Mortality_Rates_Mod30_5yrs_Xval, years_per_age_bin=5)

Build demographics from UN World Population data. Args: pop_dat_file: path to UN World Population data file base_year: Base year/Reference year start_year: Read in the pop_dat_file starting with year 'start_year' years_per_age_bin: The number of years in one age bin, i.e. in one row of the UN World Population data file max_daily_mort: Maximum daily mortality rate mortality_rate_x_values: The distribution of non-disease mortality for a population.

Returns:

Type Description
tuple[IndividualAttributes, NodeAttributes]

IndividualAttributes, NodeAttributes

Source code in emod_api/demographics/DemographicsTemplates.py
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
def demographicsBuilder(pop_dat_file: Path,
                        base_year: int,
                        start_year: int = 1950,
                        max_daily_mort: float = 0.01,
                        mortality_rate_x_values: list = DemographicsTemplatesConstants.Mortality_Rates_Mod30_5yrs_Xval,
                        years_per_age_bin: int = 5) -> tuple[IndividualAttributes, NodeAttributes]:
    """
    Build demographics from UN World Population data.
    Args:
        pop_dat_file: path to UN World Population data file
        base_year: Base year/Reference year
        start_year: Read in the pop_dat_file starting with year 'start_year'
        years_per_age_bin: The number of years in one age bin, i.e. in one row of the UN World Population data file
        max_daily_mort: Maximum daily mortality rate
        mortality_rate_x_values: The distribution of non-disease mortality for a population.

    Returns:
        IndividualAttributes, NodeAttributes
    """
    Days_per_Year = 365

    year_vec, year_init, pop_mat, pop_init = _read_un_worldpop_file(pop_dat_file, base_year, start_year)

    # create age bins in days
    pop_age_days = [bin_index * years_per_age_bin * Days_per_Year for bin_index in range(len(pop_init))]

    # Calculate vital dynamics
    t_delta = np.diff(year_vec)
    mortvecs = _calculate_mortility_vectors(pop_mat, t_delta, max_daily_mort)
    brate_vec, brate_val = _calculate_birth_rate_vector(pop_mat, mortvecs, t_delta, year_vec, year_init)
    birth_rate = brate_val/365.0/1000.0

    na = NodeAttributes()
    na.birth_rate = birth_rate

    age_y = pop_age_days
    age_init_cdf = np.cumsum(pop_init[:-1])/np.sum(pop_init)
    age_x = [0] + age_init_cdf.tolist()

    ad = AgeDistribution()
    ad.distribution_values = age_x
    ad.result_scale_factor = 1
    ad.result_values = age_y

    mort_vec_x = mortality_rate_x_values
    mort_year = np.zeros(2*year_vec.shape[0]-3)
    mort_year[0::2] = year_vec[0:-1]
    mort_year[1::2] = year_vec[1:-1]-1e-4
    mort_year = mort_year.tolist()

    mort_mat = np.zeros((len(mort_vec_x), len(mort_year)))
    mort_mat[0:-2:2, 0::2] = mortvecs
    mort_mat[1:-2:2, 0::2] = mortvecs
    mort_mat[0:-2:2, 1::2] = mortvecs[:, :-1]
    mort_mat[1:-2:2, 1::2] = mortvecs[:, :-1]
    mort_mat[-2:, :] = max_daily_mort

    md = MortalityDistribution()
    md.axis_names = ['age', 'year']
    md.axis_scale_factors = [1, 1]
    md.population_groups = [mort_vec_x, mort_year]
    md.result_scale_factor = 1
    md.result_values = mort_mat.tolist()

    ia = IndividualAttributes()
    ia.age_distribution = ad
    ia.mortality_distribution_female = md
    ia.mortality_distribution_male = md

    return ia, na

get_fert_dist_from_rates(rates)

Create dictionary with DTK-compatible distributions from input vectors of fertility (crude) rates.

Parameters:

Name Type Description Default
rates list[float]

Array/vector of crude rates for whole population, for a range of years.

required
Source code in emod_api/demographics/DemographicsTemplates.py
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
def get_fert_dist_from_rates( rates: list[float] ):
    """
    Create dictionary with DTK-compatible distributions from input vectors of fertility (crude) rates.

    Args:
        rates: Array/vector of crude rates for whole population, for a range of years.

    """
    fert_dist = {
        "FertilityDistribution": {
            "AxisNames": ["age","year"],
            "AxisUnits": ["years","simulation_year"],
            "AxisScaleFactors": [365,1],
            "PopulationGroups": [
                [0,125],
                [x for x in range(len(rates))]
            ],
            "ResultScaleFactor": 2.73972602739726e-03,
            "ResultUnits": "annual births per 1000 individuals",
            "ResultValues": [ rates, rates ]
        }
    }
    return FertilityDistribution().from_dict( fertility_distribution=fert_dist["FertilityDistribution"] )