Ejemplo n.º 1
0
def close_educ_after_april_5(paths, fixed_inputs):
    start_date = fixed_inputs["duration"]["start"]
    end_date = fixed_inputs["duration"]["end"]
    contact_models = fixed_inputs["contact_models"]
    enacted_policies = get_enacted_policies(contact_models)

    stays_same, to_change = split_policies(enacted_policies,
                                           split_date=AFTER_EASTER)
    keep = remove_educ_policies(to_change)

    block_info = {
        "prefix": "educ_closed_after_april_5",
        "start_date": AFTER_EASTER,
        "end_date": VERY_LATE,
    }
    new_educ_policies = shut_down_educ_models(contact_models=contact_models,
                                              block_info=block_info,
                                              educ_type="all")

    new_policies = combine_dictionaries([stays_same, keep, new_educ_policies])
    new_policies = shorten_policies(new_policies, start_date, end_date)

    out = {
        "vaccination_models":
        _baseline_vaccination_models(paths, fixed_inputs),
        "rapid_test_models":
        _baseline_rapid_test_models(fixed_inputs),
        "rapid_test_reaction_models":
        _baseline_rapid_test_reaction_models(fixed_inputs),
        "contact_policies":
        new_policies,
    }
    return out
Ejemplo n.º 2
0
def _get_policies_with_multiplied_work_attend_multiplier_after_date(
        enacted_policies, contact_models, multiplier, split_date, prefix):
    """Multiply the attend work multiplier with *multiplier* after **date**."""
    stays_same, to_change = split_policies(enacted_policies,
                                           split_date=split_date)
    after_split_without_work_policies = remove_work_policies(to_change)

    # get one attend multiplier and assume it was the same for all work models
    old_work_policies = filter_dictionary(lambda x: "work" in x, to_change)
    one_work_policy = list(old_work_policies.values())[0]
    old_attend_multiplier = one_work_policy["policy"].keywords[
        "attend_multiplier"]
    new_attend_multiplier = (multiplier * old_attend_multiplier).clip(0, 1)

    block_info = {
        "prefix": prefix,
        "start_date": split_date,
        "end_date": VERY_LATE,
    }
    new_work_policies = reduce_work_models(
        contact_models=contact_models,
        block_info=block_info,
        attend_multiplier=new_attend_multiplier,
        hygiene_multiplier=HYGIENE_MULTIPLIER,
    )

    new_policies = combine_dictionaries(
        [stays_same, after_split_without_work_policies, new_work_policies])
    return new_policies
Ejemplo n.º 3
0
def get_all_contact_models():
    """Create the full set of contact models.

    Returns:
        contact_models (dict): sid contact model dictionary.

    """

    to_combine = [
        get_household_contact_model(),
        # education
        get_school_contact_models(),
        get_preschool_contact_model(),
        get_nursery_contact_model(),
        # work
        get_work_non_recurrent_contact_model(),
        get_work_daily_contact_model(),
        get_work_weekly_contact_models(),
        # other
        get_other_non_recurrent_contact_model(),
        get_other_daily_contact_model(),
        get_other_weekly_contact_models(),
    ]
    contact_models = combine_dictionaries(to_combine)
    return contact_models
Ejemplo n.º 4
0
def _get_enacted_other_policies(contact_models):
    """Get enacted other policies.

    These multipliers are on top of the implemented seasonality.

    """
    specs = OTHER_MULTIPLIER_SPECS

    start_date = VERY_EARLY
    to_combine = []
    for prefix, end_date, multiplier in specs:
        block_info = {
            "start_date": start_date,
            "end_date": pd.Timestamp(end_date),
            "prefix": prefix,
        }
        to_combine.append(
            reduce_other_models(
                contact_models=contact_models,
                block_info=block_info,
                multiplier=multiplier,
            ),
        )
        start_date = pd.Timestamp(end_date) + pd.Timedelta(days=1)
    other_policies = combine_dictionaries(to_combine)
    return other_policies
Ejemplo n.º 5
0
def _get_policies_with_different_work_attend_multiplier_after_date(
        enacted_policies, contact_models, new_attend_multiplier, split_date,
        prefix):
    """Set the attend work multiplier to **new_attend_multiplier** after **date**.

    In April 2020, the mean work multiplier was 0.54. In November 2020 it was 0.83.

    """
    stays_same, to_change = split_policies(enacted_policies,
                                           split_date=split_date)
    after_split_without_work_policies = remove_work_policies(to_change)

    block_info = {
        "prefix": prefix,
        "start_date": split_date,
        "end_date": VERY_LATE,
    }
    new_work_policies = reduce_work_models(
        contact_models=contact_models,
        block_info=block_info,
        attend_multiplier=new_attend_multiplier,
        hygiene_multiplier=HYGIENE_MULTIPLIER,
    )

    new_policies = combine_dictionaries(
        [stays_same, after_split_without_work_policies, new_work_policies])
    return new_policies
Ejemplo n.º 6
0
def test_combine_dictionaries_unproblematic():
    d1 = {"a": 0}
    d2 = {"b": 1}
    d3 = {"c": 2}
    res = combine_dictionaries([d1, d2, d3])
    assert d1 == {"a": 0}
    assert d2 == {"b": 1}
    assert d3 == {"c": 2}
    assert res == {"a": 0, "b": 1, "c": 2}
Ejemplo n.º 7
0
def get_enacted_policies(contact_models):
    """Get enacted policies."""
    work_policies = _get_enacted_work_policies(contact_models)
    other_policies = _get_enacted_other_policies(contact_models)
    young_educ_policies = _get_enacted_young_educ_policies(contact_models)
    school_policies = _get_enacted_school_policies(contact_models)
    policies = combine_dictionaries(
        [work_policies, school_policies, young_educ_policies, other_policies]
    )
    return policies
def create_rapid_test_statistics(demand_by_channel, states, date, params):
    """Calculate the rapid test statistics.

    Args:
        demand_by_channel (pandas.DataFrame): same index as states. Each column is one
            channel through which rapid tests can be demanded.
        states (pandas.DataFrame): sid states DataFrame.
        date (pandas.Timestamp or str): date
        params (pandas.DataFrame): parameter DataFrame that contains the sensitivity
            and specificity of the rapid tests

    Returns:
        statistics (pandas.DataFrame): DataFrame with just one column named 0. The
            index contains the date, the number of individuals and for each channel
            the share of the population that demand a test through this channel, the
            share of tests in each channel that are demanded by infected individuals.

    """
    statistics = {
        "date": date,
    }

    demand_by_channel = demand_by_channel.copy()
    demand_by_channel["overall"] = demand_by_channel.any(axis=1)

    for channel in demand_by_channel.columns:
        # because we don't know the seed with which sample_test_outcome will be called
        # with, these results will not be exactly equal to the test outcomes in sid but
        # most channels easily exceed the number of tests for randomness to be relevant
        rapid_test_results = _sample_test_outcome(
            states=states,
            receives_rapid_test=demand_by_channel[channel],
            params=params,
            seed=itertools.count(93894),
        )

        channel_statistics = _calculate_rapid_test_statistics_by_channel(
            states=states,
            rapid_test_results=rapid_test_results,
            receives_rapid_test=demand_by_channel[channel],
            channel_name=channel,
        )
        statistics = combine_dictionaries([statistics, channel_statistics])

    statistics = pd.Series(statistics).to_frame()
    statistics.index.name = "index"
    return statistics
Ejemplo n.º 9
0
def get_school_contact_models():
    model_list = []
    for i in range(3):
        model = {
            f"educ_school_{i}": {
                "is_recurrent": True,
                "model": partial(
                    cm_funcs.attends_educational_facility,
                    id_column=f"school_group_id_{i}",
                ),
                "assort_by": [f"school_group_id_{i}"],
                "is_factorized": True,
            },
        }
        model_list.append(model)
    school_contact_models = combine_dictionaries(model_list)
    return school_contact_models
Ejemplo n.º 10
0
def only_strict_emergency_care_after_april_5(paths, fixed_inputs):
    start_date = fixed_inputs["duration"]["start"]
    end_date = fixed_inputs["duration"]["end"]
    contact_models = fixed_inputs["contact_models"]
    enacted_policies = get_enacted_policies(contact_models)

    stays_same, to_change = split_policies(enacted_policies,
                                           split_date=AFTER_EASTER)
    keep = remove_educ_policies(to_change)

    block_info = {
        "prefix": "emergency_care_after_april_5",
        "start_date": AFTER_EASTER,
        "end_date": VERY_LATE,
    }
    new_young_educ_policies = apply_emergency_care_policies(
        contact_models=contact_models,
        block_info=block_info,
        educ_type="young_educ",
        attend_multiplier=0.25,
        hygiene_multiplier=HYGIENE_MULTIPLIER,
    )
    school_options = get_school_options_for_strict_emergency_care()
    new_school_policies = apply_mixed_educ_policies(
        contact_models=contact_models,
        block_info=block_info,
        educ_type="school",
        **school_options,
    )

    new_policies = combine_dictionaries(
        [stays_same, keep, new_young_educ_policies, new_school_policies])
    new_policies = shorten_policies(new_policies, start_date, end_date)

    out = {
        "vaccination_models":
        _baseline_vaccination_models(paths, fixed_inputs),
        "rapid_test_models":
        _baseline_rapid_test_models(fixed_inputs),
        "rapid_test_reaction_models":
        _baseline_rapid_test_reaction_models(fixed_inputs),
        "contact_policies":
        new_policies,
    }
    return out
Ejemplo n.º 11
0
def _get_enacted_work_policies(contact_models):
    """Get the enacted work policies.

    This uses the google mobility data as proxy for the share of individuals
    working in home office. The last value is extrapolated into the future.

    """
    multiplier_path = BLD / "policies" / "work_multiplier.csv"
    attend_multiplier = pd.read_csv(multiplier_path, parse_dates=["date"])
    attend_multiplier = attend_multiplier.set_index("date")
    first_2_weeks_value = attend_multiplier[:14].mean()
    last_2_weeks_value = attend_multiplier[-14:].mean()
    extended_attend_multiplier = attend_multiplier.reindex(
        pd.date_range(VERY_EARLY, attend_multiplier.index.max())
    )
    extended_attend_multiplier = extended_attend_multiplier.fillna(first_2_weeks_value)
    full_attend_multiplier = extended_attend_multiplier.reindex(
        pd.date_range(VERY_EARLY, VERY_LATE)
    )
    full_attend_multiplier = full_attend_multiplier.fillna(last_2_weeks_value)
    before_november_policies = reduce_work_models(
        contact_models=contact_models,
        block_info={
            "start_date": VERY_EARLY,
            "end_date": "2020-11-01",
            "prefix": "before_november_2020",
        },
        attend_multiplier=full_attend_multiplier,
        hygiene_multiplier=1.0,
    )

    after_november_policies = reduce_work_models(
        contact_models=contact_models,
        block_info={
            "start_date": "2020-11-02",
            "end_date": VERY_LATE,
            "prefix": "after_november_2020",
        },
        attend_multiplier=full_attend_multiplier,
        hygiene_multiplier=HYGIENE_MULTIPLIER,
    )
    work_policies = combine_dictionaries(
        [before_november_policies, after_november_policies]
    )
    return work_policies
Ejemplo n.º 12
0
def _create_deps(scenarios, groupby):
    available_scenarios = get_available_scenarios(get_named_scenarios())

    py_dependencies = {
        "config.py": SRC / "config.py",
        "scenario_config.py": SRC / "simulation" / "scenario_config.py",
        "plotting.py": SRC / "plotting" / "plotting.py",
    }

    data_dependencies = {}
    scenarios_to_compare = [s for s in scenarios if s in available_scenarios]
    for scenario in scenarios_to_compare:
        outcome = ("newly_infected"
                   if groupby is None else f"newly_infected_by_{groupby}")
        data_dependencies[
            scenario] = create_path_to_scenario_outcome_time_series(
                scenario, outcome)
    dependencies = combine_dictionaries([py_dependencies, data_dependencies])
    return dependencies
Ejemplo n.º 13
0
def _open_all_educ_after_date(paths,
                              fixed_inputs,
                              split_date,
                              multiplier=HYGIENE_MULTIPLIER):
    start_date = fixed_inputs["duration"]["start"]
    end_date = fixed_inputs["duration"]["end"]
    contact_models = fixed_inputs["contact_models"]
    enacted_policies = get_enacted_policies(contact_models)

    stays_same, to_change = split_policies(enacted_policies,
                                           split_date=split_date)
    after_split_without_educ_policies = remove_educ_policies(to_change)
    block_info = {
        "prefix": f"open_all_educ_after_{pd.Timestamp(split_date).date()}",
        "start_date": split_date,
        "end_date": VERY_LATE,
    }

    new_educ_policies = reduce_educ_models(
        contact_models=contact_models,
        block_info=block_info,
        educ_type="all",
        multiplier=multiplier,
    )
    new_policies = combine_dictionaries(
        [stays_same, after_split_without_educ_policies, new_educ_policies])
    new_policies = shorten_policies(new_policies, start_date, end_date)

    out = {
        "contact_policies":
        new_policies,
        "vaccination_models":
        _baseline_vaccination_models(paths, fixed_inputs),
        "rapid_test_models":
        _baseline_rapid_test_models(fixed_inputs),
        "rapid_test_reaction_models":
        _baseline_rapid_test_reaction_models(fixed_inputs),
    }
    return out
def test_get_policies_with_different_work_attend_multiplier_after_date():
    contact_models = combine_dictionaries([
        get_household_contact_model(),
        get_work_non_recurrent_contact_model(),
        get_other_non_recurrent_contact_model(),
    ])

    enacted_policies = {
        "keep_work": {
            "affected_contact_model": "work_non_recurrent",
            "start": pd.Timestamp("2021-01-01"),
            "end": pd.Timestamp("2021-02-28"),
            "policy": 0.5,
        },
        "cut_work": {
            "affected_contact_model": "work_non_recurrent",
            "start": pd.Timestamp("2021-03-01"),
            "end": pd.Timestamp("2021-04-30"),
            "policy": 0.5,
        },
        "drop_work": {
            "affected_contact_model": "work_non_recurrent",
            "start": pd.Timestamp("2021-05-01"),
            "end": pd.Timestamp("2021-05-31"),
            "policy": 0.5,
        },
        "other": {
            "affected_contact_model": "other_non_recurrent",
            "start": pd.Timestamp("2021-01-01"),
            "end": pd.Timestamp("2021-05-31"),
            "policy": 0.5,
        },
    }
    res = _get_policies_with_different_work_attend_multiplier_after_date(
        enacted_policies=enacted_policies,
        contact_models=contact_models,
        new_attend_multiplier=0.0,
        split_date=pd.Timestamp("2021-04-15"),
        prefix="test",
    )
    expected_work_policy = partial(
        reduce_work_model,
        attend_multiplier=0.0,
        hygiene_multiplier=HYGIENE_MULTIPLIER,
        is_recurrent=False,
    )
    expected = {
        "keep_work": {
            "affected_contact_model": "work_non_recurrent",
            "start": pd.Timestamp("2021-01-01"),
            "end": pd.Timestamp("2021-02-28"),
            "policy": 0.5,
        },
        "cut_work_first": {
            "affected_contact_model": "work_non_recurrent",
            "start": pd.Timestamp("2021-03-01"),
            "end": pd.Timestamp("2021-04-14"),
            "policy": 0.5,
        },
        "other_first": {
            "affected_contact_model": "other_non_recurrent",
            "start": pd.Timestamp("2021-01-01"),
            "end": pd.Timestamp("2021-04-14"),
            "policy": 0.5,
        },
        "other_second": {
            "affected_contact_model": "other_non_recurrent",
            "start": pd.Timestamp("2021-04-15"),
            "end": pd.Timestamp("2021-05-31"),
            "policy": 0.5,
        },
        "test_work_non_recurrent": {
            "affected_contact_model": "work_non_recurrent",
            "start": pd.Timestamp("2021-04-15"),
            "end": VERY_LATE,
            "policy": expected_work_policy,
        },
    }

    # This is a custom comparison because the two partialed functions are not recognized
    # to be identical
    assert res.keys() == expected.keys()
    for pol_name, exp_pol in expected.items():
        res_pol = res[pol_name]
        assert res_pol.keys() == exp_pol.keys()
        if pol_name == "test_work_non_recurrent":
            for key, val in exp_pol.items():
                if key == "policy":
                    assert res_pol["policy"].func == val.func
                    assert res_pol["policy"].args == val.args
                    assert res_pol["policy"].keywords == val.keywords
                else:
                    assert res_pol[key] == val
        else:
            assert exp_pol == res_pol
Ejemplo n.º 15
0
def load_simulation_inputs(
    scenario,
    start_date,
    end_date,
    debug,
    group_share_known_case_path=None,
    period_outputs=False,
    return_last_states=False,
    initial_states_path=None,
    is_resumed=False,
    rapid_test_statistics_path=None,
):
    """Load the simulation inputs.

    Does **not** include: params, path, seed.

    Args:
        scenario (str): string specifying the scenario. A function with the
            same name must exist in src.simulation.scenario_simulation_inputs.
        start_date (str): date on which the simulation starts. Data must be available
            for at least a month before the start date for the burn in period.
        end_date (str): date on which the simulation ends.
        debug (bool): Whether to use the debug or the full initial states.
        group_share_known_case_path (pathlib.Path, str or None): if not None, the group
            share known cases are loaded from this path and used for the creation of the
            initial conditions.
        period_outputs (bool, optional): whether to use period_outputs instead of saving
            the time series. Default is False.
        return_last_states (bool, optional): if True, the last states are returned as
            part of the simulation result.
        initial_states_path (pathlib.Path, optional): Path to the initial states.
            If not given the standard initial states are used.
        is_resumed (bool, optional): if True, the initial_states_path must be given. In
            that case no initial conditions are created
        rapid_test_statistics_path (Path, optional): where to save rapid test
            statistics.


    Returns:
        dict: Dictionary with most arguments of get_simulate_func. Keys are:
            - initial_states
            - contact_models
            - duration
            - events
            - saved_columns
            - virus_strains
            - derived_state_variables
            - seasonality_factor_model
            - initial_conditions
            - susceptibility_factor_model
            - testing_demand_models
            - testing_allocation_models
            - testing_processing_models
            - period_outputs
            - return_last_states
            - return_time_series

            - contact_policies
            - vaccination_models
            - rapid_test_models
            - rapid_test_reaction_models

    """
    if is_resumed:
        assert (
            initial_states_path is not None
        ), "You must specify the path to the initial states if you resume a simulation."

    start_date = pd.Timestamp(start_date)
    end_date = pd.Timestamp(end_date)

    paths = get_simulation_dependencies(
        debug=debug,
        is_resumed=is_resumed,
    )
    if rapid_test_statistics_path is not None:
        paths["rapid_test_statistics_path"] = rapid_test_statistics_path

    if initial_states_path is None:
        initial_states_path = paths["initial_states"]

    if initial_states_path.suffix == ".pkl":
        initial_states = pd.read_pickle(initial_states_path)
    elif initial_states_path.suffix == ".parquet":
        initial_states = pd.read_parquet(paths["initial_states"])

    contact_models = get_all_contact_models()

    # process dates
    one_day = pd.Timedelta(1, unit="D")
    init_start = start_date - pd.Timedelta(31, unit="D")
    init_end = start_date - one_day
    duration = {"start": start_date, "end": end_date}

    # testing models
    share_of_tests_for_symptomatics_series = pd.read_pickle(
        paths["share_of_tests_for_symptomatics_series"]
    )
    test_start = init_start - one_day
    test_end = end_date + one_day
    test_demand_func = partial(
        demand_test,
        share_of_tests_for_symptomatics_series=share_of_tests_for_symptomatics_series,
    )
    testing_demand_models = {
        "symptoms": {
            "model": test_demand_func,
            "start": test_start,
            "end": test_end,
        }
    }
    testing_allocation_models = {
        "direct_allocation": {
            "model": allocate_tests,
            "start": test_start,
            "end": test_end,
        }
    }
    testing_processing_models = {
        "direct_processing": {
            "model": process_tests,
            "start": test_start,
            "end": test_end,
        }
    }

    saved_columns = {
        "initial_states": ["age_group_rki"],
        "disease_states": ["newly_infected", "newly_deceased", "ever_infected"],
        "time": ["date"],
        "other": [
            "new_known_case",
            "virus_strain",
            "n_has_infected",
            "channel_infected_by_contact",
            "state",
            "knows_currently_infected",
            "currently_infected",
        ],
    }

    if not is_resumed:
        virus_shares = pd.read_pickle(paths["virus_shares"])
        rki_infections = pd.read_pickle(paths["rki"])

        group_weights = pd.read_pickle(paths["rki_age_groups"])["weight"]
        if group_share_known_case_path is not None:
            group_share_known_cases = pd.read_pickle(group_share_known_case_path)
        else:
            group_share_known_cases = None

        params = pd.read_pickle(paths["params"])
        with warnings.catch_warnings():
            warnings.filterwarnings(
                "ignore", message="indexing past lexsort depth may impact performance."
            )
            params_slice = params.loc[("share_known_cases", "share_known_cases")]
        overall_share_known_cases = get_piecewise_linear_interpolation(params_slice)

        initial_conditions = create_initial_conditions(
            start=init_start,
            end=init_end,
            seed=3930,
            reporting_delay=5,
            synthetic_data=initial_states[["county", "age_group_rki"]],
            empirical_infections=rki_infections,
            virus_shares=virus_shares,
            overall_share_known_cases=overall_share_known_cases,
            group_share_known_cases=group_share_known_cases,
            group_weights=group_weights,
        )
    else:
        initial_conditions = None

    seasonality_factor_model = partial(seasonality_model, contact_models=contact_models)

    def _currently_infected(df):
        return df["infectious"] | df["symptomatic"] | (df["cd_infectious_true"] >= 0)

    def _knows_currently_infected(df):
        return df["knows_immune"] & df["currently_infected"]

    derived_state_variables = {
        "currently_infected": _currently_infected,
        "knows_currently_infected": _knows_currently_infected,
    }

    events = {
        "introduce_b117": {"model": introduce_b117},
        "introduce_delta": {"model": introduce_delta},
    }
    fixed_inputs = {
        "initial_states": initial_states,
        "contact_models": contact_models,
        "duration": duration,
        "events": events,
        "testing_demand_models": testing_demand_models,
        "testing_allocation_models": testing_allocation_models,
        "testing_processing_models": testing_processing_models,
        "saved_columns": saved_columns,
        "initial_conditions": initial_conditions,
        "susceptibility_factor_model": calculate_susceptibility,
        "virus_strains": ["base_strain", "b117", "delta"],
        "seasonality_factor_model": seasonality_factor_model,
        "derived_state_variables": derived_state_variables,
        "return_last_states": return_last_states,
    }

    if period_outputs:
        fixed_inputs["period_outputs"] = create_period_outputs()
        fixed_inputs["return_time_series"] = False

    scenario_func = getattr(scenario_simulation_inputs, scenario)
    scenario_inputs = scenario_func(paths, fixed_inputs)
    simulation_inputs = combine_dictionaries([fixed_inputs, scenario_inputs])
    return simulation_inputs
Ejemplo n.º 16
0
def _get_enacted_young_educ_policies(contact_models):
    """Get the educ policies for nurseries and preschools.

    The multiplier for the emergency care around the Christmas holidays is
    based on the data for the 1st half of January where many parents might
    have still had vacations.
    1 in 10 primary children attend in emergency care (source only
    for Bavaria, mid January: https://bit.ly/3sGHZbJ). We assume this is similar
    for preschools and nurseries.

    - "jan_and_feb_2021":
        - Jump from ~25% to 33% between first Jan week and later.
        - source: https://bit.ly/3uGL1Pb
        - This could be a vacation effect with more parents returning to work.

    - "feb_22_to_mid_march":
        - We assume nurseries and preschools open normally. This is what happened
          for nurseries in all states with >8 mio inhabitants (BW, BY, NRW) on
          Feb 22nd. (https://bit.ly/3uSp6Ey, https://bit.ly/3h77Cjs,
          https://bit.ly/2O3aS3h)

    - "mid_march_to_easter":
        - NRW: preschools and nurseries are open (https://bit.ly/3nSkUBM)
        - BW: emergency care after March 17 (https://bit.ly/3useyeP)
        - BY: emergency care in counties with incidences >100 starting
          March 15 (https://bit.ly/2PRtaW0), the incidence in BY was >100 for
          most of that time frame.

            => assume generous emergency care. This is errs on the side of reducing
               contacts too much.

    -"easter_until_april_25":
        - BY: emergency care in counties with incidences >100 (https://bit.ly/3h234eh)
          Incidence was >120 (up to 200) over the whole time
        - BW: open again (https://bit.ly/3h2g83e)
        - NRW: unchanged open (https://bit.ly/33kqof8)

            => assume generous emergency care. This is errs on the side of reducing
               contacts too much.

    - "summer" (last updated 2021-05-22):
        - BY (https://bit.ly/3oF6Go3): normal until 165. emergency care above.

            Over the time frame the share of counties above 165 drops from 48% to 4%.

            => assume preschools and nurseries are open normally.

    """
    strict_emergency_care_kwargs = {
        "attend_multiplier": 0.25,
        "hygiene_multiplier": HYGIENE_MULTIPLIER,
    }

    generous_emergency_care_kwargs = {
        "attend_multiplier": 0.34,
        "hygiene_multiplier": HYGIENE_MULTIPLIER,
    }

    # policies start the day after the end date of the last policy
    specs = [
        ("before_november_2020", "2020-11-01", reduce_educ_models, 1.0),
        ("until_christmas_2020", "2020-12-15", reduce_educ_models, HYGIENE_MULTIPLIER),
        (
            "christmas-lockdown",
            "2021-01-10",
            apply_emergency_care_policies,
            strict_emergency_care_kwargs,
        ),
        (
            "jan_and_feb_2021",
            "2021-02-21",
            apply_emergency_care_policies,
            generous_emergency_care_kwargs,
        ),
        ("feb_22_to_mid_march", "2021-03-16", reduce_educ_models, HYGIENE_MULTIPLIER),
        (
            "mid_march_to_easter",
            "2021-04-05",
            apply_emergency_care_policies,
            generous_emergency_care_kwargs,
        ),
        (
            "easter_until_april_25",
            "2021-04-25",
            apply_emergency_care_policies,
            generous_emergency_care_kwargs,
        ),
        (
            "summer",
            VERY_LATE,
            reduce_educ_models,
            HYGIENE_MULTIPLIER,
        ),
    ]

    start_date = VERY_EARLY
    to_combine = []
    for prefix, end_date, func, kwargs in specs:
        block_info = {
            "start_date": start_date,
            "end_date": pd.Timestamp(end_date),
            "prefix": prefix,
        }
        if isinstance(kwargs, float):
            kwargs = {"multiplier": kwargs}

        to_combine.append(
            func(
                contact_models=contact_models,
                block_info=block_info,
                educ_type="young_educ",
                **kwargs,
            )
        )
        start_date = pd.Timestamp(end_date) + pd.Timedelta(days=1)
    educ_policies = combine_dictionaries(to_combine)
    return educ_policies
Ejemplo n.º 17
0
def test_combine_dictionaries_duplicate():
    d1 = {"a": 0}
    d2 = {"b": 1}
    d3 = {"a": 2}
    with pytest.raises(ValueError):
        combine_dictionaries([d1, d2, d3])
Ejemplo n.º 18
0
def _get_enacted_school_policies(contact_models):
    """Get the policies for schools.

    - "christmas_lockdown":
        This is based on the data for the 1st half of January where many parents
        might have still had vacations. 1 in 10 primary children attend in
        emergency care (source only for BY, mid January: https://bit.ly/3sGHZbJ)
        This abstracts from graduating classes opening early in some states,
        such as Berlin (11 of Jan, https://bit.ly/385iCZk).

    - "jan_and_feb_2021":
        In the second half of January, approx. 1 / 3 of children below secondary
        level were in emergency care.

        Sources:

            - https://bit.ly/3uGL1Pb
            - https://bit.ly/2PErr5T
            - Berlin: <40% (https://bit.ly/304R5ml)
            - Niedersachsen: 38% (https://bit.ly/2PtPdSb)

        In addition, many states opened graduating classes. We open them at the
        federal level in A / B schooling on Jan 16th. This abstracts from variety:

        - Bavaria: started on 1 Feb (https://bit.ly/3e4p1YE)
        - Baden-Württemberg started on 18 Jan (https://bit.ly/2Ofq9O7)
        - Berlin started on  11 Jan (https://bit.ly/385iCZk)

        sources:

            - https://taz.de/Schulen-in-Coronazeiten/!5753515/
            - https://tinyurl.com/2jfm4tp8

    - "feb_22_to_mid_march":
        Schools open for primary students
            and graduating classes in A/B while maintaining emergency care for children
            with high educ_contact_priority (>0.9 for secondary students <13 and
            >0.66 for primary students).

            Summary of actual policies of states with >=8 mio inhabitants:
                - BW: Primary schools open Feb 22nd. Graduating classes attend.
                - BY: Primary schools open Feb 22nd. Graduating classes attend.
                - NRW: Primary schools and graduating classes start Feb 22nd,
                  A/B or full depending on local incidecne (https://bit.ly/3uSp6Ey)

    - "mid_march_to_easter":
        - BY:
            - source: https://bit.ly/3lOZowy
            - <50 incidence: normal schooling
            - 50-100 incidence: A/B schooling
            - >100 incidence: distance for all except graduation classes

        - BW:
            - source: https://km-bw.de/Coronavirus (accessed: March 25th)
            - primaries and 5th, 6th grade open normally since 15/3
            - graduating classes open since 22/2
            - rest continues distance learning

        - NRW:
            - source: https://bit.ly/3f9O4Kp (WDR)
            - A/B schooling since March 15th

        -> We simplify to A/B schooling for everyone plus emergency care
           and graduating classes.

    - "easter_until_may":
        This covers April 6-30. Starting April 26th the Bundesnotbremse is in place.
        That means when counties have a >165 incidence schools only offer
        classes to graduating classes and emergency care. (https://bit.ly/3aUd2KC)
        Since BY has a lower cutoff and cases only fell below 165 around May 1,
        we assume generous emergency care for everyone.

       - BW:
           - source: https://bit.ly/32ABEUr, https://bit.ly/3u6Dcld
           - A/B schooling for graduating classes + 4th grade
           - closures >200 incidence

        - BY:
            - source: https://bit.ly/2QmRNu0, https://bit.ly/32FlgBQ (2021-04-22)
            - incidence <100: A/B for everyone
            - incidence >100: 4th grade and graduating classes in A/B schooling.
              emergency care for rest.
            - mean incidence >130 and increasing over the whole time

        - NRW:
            ⁻ sources: https://bit.ly/3nxGZWb
            - only graduating classes, not in  A/B mode

        => We summarize this as a return to graduating classes in A/B plus
        generous emergceny care (i.e. same as 2nd half of January).

    - "may2021":
        Starting April 26th the Bundesnotbremse is in place. That means when counties
        have a >165 incidence schools only offer classes to graduating classes and
        emergency care. (https://bit.ly/3aUd2KC)

        - BW: federal guidelines apply (https://bit.ly/3t7AIBJ, https://bit.ly/3aR4yUM)
        - BY:
            - source: https://bit.ly/2RgmsJm (accessed April 30), https://bit.ly/3wEaCsj
            - incidence <100: A/B for everyone
            - incidence >100: 4th grade and graduating classes in A/B schooling.
              emergency care for rest.
            - incidences >100 in most counties!
            - after May 10, primaries are allowed in A/B schooling in incidences
              <165 (https://bit.ly/3vQNO8e)
        - NRW:
            - sources: https://bit.ly/2QHWChG, https://bit.ly/3gPraZu,
              https://bit.ly/32Zq1q8, https://bit.ly/3nzNhEx, https://bit.ly/3vc4mYk
            - A/B schooling when incidences <165 b/c of Bundesnotbremse

        => Simplified, one could say we have A/B < 165 and emergency care >165.
           The share of the population in counties with >165 incidence falls from 50%
           to 2.5% between April 25th and May 15th. We assume A/B for everyone.

    - "june2021": (last updated May 22nd)
        - BW: normal <50, A/B above primary from 50-100, A/B from 100-165
          (source: https://bit.ly/3hYtbDt).
        - BY: normal <50, A/B from 50 to 165 (https://bit.ly/2T6uupf).
        - NRW: normal <100, A/B when btw. 100 and 165 (https://bit.ly/2SbmD9v).

            => A/B for everyone. With this we err on the side of restricting contacts
               too much.

    - "summer": (last updated May 22nd)
        - BW: normal <100, A/B from 100 to 165 (source: https://bit.ly/3hYtbDt).
        - BY: normal <50, A/B from 50 to 165 (https://bit.ly/2T6uupf).
        - NRW: normal <100, A/B from 100 ot 165 (https://bit.ly/2SbmD9v).

            => normal

    """
    strict_emergency_care_kwargs = get_school_options_for_strict_emergency_care()
    generous_emergency_care_kwargs = (
        _get_school_options_for_emergency_care_with_graduating_classes_having_a_b_schooling()  # noqa: E501
    )
    primary_and_graduating_in_ab_kwargs = (
        _get_school_options_for_a_b_schooling_for_primary_and_graduating_classes()
    )
    # A/B for everyone, graduating classes attend in full + emergency care.
    mid_march_to_easter_policy_kwargs = _get_school_options_for_a_b_for_most()

    # combine specs
    specs = [
        ("before_november_2020", "2020-11-01", reduce_educ_models, 1.0),
        ("until_christmas_2020", "2020-12-15", reduce_educ_models, HYGIENE_MULTIPLIER),
        (
            "christmas-lockdown",
            "2021-01-10",
            apply_mixed_educ_policies,
            strict_emergency_care_kwargs,
        ),
        (
            "jan_and_feb_2021",
            "2021-02-21",
            apply_mixed_educ_policies,
            generous_emergency_care_kwargs,
        ),
        (
            "feb_22_to_mid_march",
            "2021-03-14",
            apply_mixed_educ_policies,
            primary_and_graduating_in_ab_kwargs,
        ),
        (
            "mid_march_to_easter",
            "2021-04-05",
            apply_mixed_educ_policies,
            mid_march_to_easter_policy_kwargs,
        ),
        (
            "easter_until_may",
            "2021-04-30",
            apply_mixed_educ_policies,
            generous_emergency_care_kwargs,
        ),
        (
            "may2021",  # incidence fell from 150 to 35
            "2021-05-31",
            apply_mixed_educ_policies,
            mid_march_to_easter_policy_kwargs,
        ),
        (
            "june2021",  # nationwide incidence below 40
            "2021-06-30",
            reduce_educ_models,
            HYGIENE_MULTIPLIER,
        ),
        ("summer", VERY_LATE, reduce_educ_models, HYGIENE_MULTIPLIER),
    ]
    start_date = VERY_EARLY
    to_combine = []
    for prefix, end_date, func, kwargs in specs:
        block_info = {
            "start_date": start_date,
            "end_date": pd.Timestamp(end_date),
            "prefix": prefix,
        }
        if isinstance(kwargs, float):
            kwargs = {"multiplier": kwargs}

        to_combine.append(
            func(
                contact_models=contact_models,
                block_info=block_info,
                educ_type="school",
                **kwargs,
            )
        )
        start_date = pd.Timestamp(end_date) + pd.Timedelta(days=1)
    school_policies = combine_dictionaries(to_combine)
    return school_policies