Skip to content
33 changes: 33 additions & 0 deletions docs/source/usage/how_to_run.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ The following are inputs for the overall simulation:
* ``agent.air_travel_int`` (`integer`, default ``-1``)
The number of time steps between air travel events. Set to -1 to disable all air travel events. Currently this is implemented
only for ``ic_type = census``.
* ``agent.weather_int`` (`integer`, default ``-1``)
The number of time steps between weather data updates. Set to -1 to disable the weather-dependent transmission model.
When enabled, ``agent.weather_filename`` and ``agent.startdate`` must also be provided.
* ``agent.weather_filename`` (`string`)
Path to the CSV file containing weather data (temperature and humidity by county and week).
Required when ``agent.weather_int > 0``. Example data files are provided in ``ExaEpi/data/``.
* ``agent.startdate`` (`string`)
Start date of the simulation in ``YYYY-MM-DD`` format (e.g. ``2020-01-01``).
Used to align the weather data with the simulation timeline.
Required when ``agent.weather_int > 0``.
* ``agent.aggregated_diag_int`` (`integer`, default ``-1``)
The number of time steps between writing aggregated data, for example wastewater data. Set to -1 to disable writing.
* ``agent.aggregated_diag_prefix`` (`string`, default ``cases``)
Expand Down Expand Up @@ -254,6 +264,29 @@ can be specified as follows:
where ``[disease name]`` is any of the names specified in ``agent.disease_names`` (or the
default value), and ``[key]`` is any of the parameters listed above.

The following inputs configure the weather-dependent transmission model. These parameters are only
used when ``agent.weather_int > 0``. The model computes a weather-based scale factor for the
transmission probability as a function of near-surface air temperature :math:`T` (°C) and absolute
humidity :math:`AH` (g/m³):

.. math::

p(T,\,AH) = p_{\max} \exp\!\bigl(-\beta_{AH}\,AH - \alpha_T\,\max(0,\,T - T_0)\bigr)

* ``weather_transmission.p_max`` (`float`, default ``1.0``)
Maximum transmission scale factor (dimensionless). At low temperature and low humidity the
scale factor approaches this value. It is multiplied by the baseline transmission probability
``disease.p_trans`` to give the effective transmission probability.
* ``weather_transmission.beta_AH`` (`float`, default ``0.18``)
Absolute-humidity sensitivity coefficient in units of m³/g. Larger values cause transmission
to decrease more rapidly as humidity increases.
* ``weather_transmission.T0`` (`float`, default ``5.0``)
Temperature threshold in °C above which the temperature penalty is applied. Below this
temperature the model applies no temperature-dependent reduction.
* ``weather_transmission.alpha_T`` (`float`, default ``0.015``)
Temperature sensitivity coefficient in units of °C⁻¹. Larger values cause transmission
to decrease more rapidly above ``T0``.

In addition to the ExaEpi inputs, there are also a number of runtime options that can be configured for AMReX itself.
Please see <https://amrex-codes.github.io/amrex/docs_html/GPU.html#inputs-parameters>`__ for more information on these options.

Expand Down
6 changes: 6 additions & 0 deletions src/AgentContainer.H
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "InteractionModelLibrary.H"
#include "Utils.H"
#include "WeatherData.H"
#include "WeatherTransmissionModel.H"

/*! \brief Derived class from ParticleContainer that defines agents and their functions */
class AgentContainer : public amrex::ParticleContainer<0, 0, RealIdx::nattribs, IntIdx::nattribs> {
Expand Down Expand Up @@ -131,6 +132,11 @@ class AgentContainer : public amrex::ParticleContainer<0, 0, RealIdx::nattribs,
int activeWeatherWeek;
ActiveWeather* awd;

/*! Weather-dependent transmission probability model */
WeatherTransmissionModel m_weather_model;
/*! Controls weather coupling: <= 0 disables weather scaling, > 0 enables it */
int m_weather_int = -1;

protected:
amrex::Real m_shelter_compliance = 0.95_rt; /*!< Shelter-in-place compliance rate */

Expand Down
7 changes: 7 additions & 0 deletions src/AgentContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ AgentContainer::AgentContainer (const amrex::Geometry& a_geom,

addAttributes();

awd = nullptr;
{
amrex::ParmParse pp("agent");
pp.query("weather_int", m_weather_int);
}
m_weather_model.readInputs("weather_transmission");

amrex::ParmParse pp("agent");

pp.query("shelter_compliance", m_shelter_compliance);
Expand Down
4 changes: 3 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ set(_sources
UrbanPopData.H
UrbanPopData.cpp
Utils.H
Utils.cpp)
Utils.cpp
WeatherTransmissionModel.H
WeatherTransmissionModel.cpp)

# List of input files
set(_input_files)
Expand Down
19 changes: 14 additions & 5 deletions src/InteractionModHome.H
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,10 @@ void InteractionModHome<PCType, PTDType, PType>::fastInteractHome (PCType& agent
auto prob_ptr = soa.GetRealData(RealIdx::nattribs + r0(d) + RealIdxDisease::prob).data();
auto lparm = agents.getDiseaseParameters_d(d);
auto lparm_h = agents.getDiseaseParameters_h(d);
Real scale = 1.0_rt; // TODO this should vary based on cell
auto weatherIdxPtr = soa.GetIntData(IntIdx::weatherLookup).data();
auto varVec = agents.awd != nullptr ? agents.awd->varVec.data() : nullptr;
auto weather_model = agents.m_weather_model;
int a_weather_int = agents.m_weather_int;
Real infect = (1.0_rt - lparm_h->vac_eff);
// loop to count infectious agents in each group
auto d_ptr = getCommunityIndex.comm_to_local_index_d.data();
Expand Down Expand Up @@ -235,8 +238,14 @@ void InteractionModHome<PCType, PTDType, PType>::fastInteractHome (PCType& agent
int family_i = community * max_family + family_ptr[i];
int num_infected_family = infected_family_d_ptr[family_i];
int num_infected_family_asymp = infected_family_asymp_d_ptr[family_i];
Real family_prob = 1.0_rt - infect * xmit_family_prob * scale;
Real family_prob_asymp = 1.0_rt - lparm->asymp_relative_inf * infect * xmit_family_prob * scale;
Real weather_scale = 1.0_rt;
if (a_weather_int > 0 && varVec != nullptr && weatherIdxPtr[i] >= 0) {
weatherVars wvar = varVec[weatherIdxPtr[i]];
weather_scale = weather_model(wvar);
}
Real family_prob = 1.0_rt - infect * xmit_family_prob * weather_scale;
Real family_prob_asymp =
1.0_rt - lparm->asymp_relative_inf * infect * xmit_family_prob * weather_scale;
prob_ptr[i] *= static_cast<ParticleReal>(std::pow(family_prob, num_infected_family));
prob_ptr[i] *= static_cast<ParticleReal>(std::pow(family_prob_asymp, num_infected_family_asymp));
if (!ptd.m_idata[IntIdx::withdrawn][i]) {
Expand All @@ -245,15 +254,15 @@ void InteractionModHome<PCType, PTDType, PType>::fastInteractHome (PCType& agent
int cluster_i = community * max_hcs + hh_cluster_ptr[i];
int num_infected_nc = infected_nc_d_ptr[cluster_i] - num_infected_family_not_withdrawn;
AMREX_ALWAYS_ASSERT(num_infected_nc >= 0);
Real nc_prob = 1.0_rt - infect * xmit_nc_prob * scale;
Real nc_prob = 1.0_rt - infect * xmit_nc_prob * weather_scale;
prob_ptr[i] *= static_cast<ParticleReal>(std::pow(nc_prob, num_infected_nc));
// same for asymptomatic
int num_infected_family_not_withdrawn_asymp = infected_family_not_withdrawn_asymp_d_ptr[family_i];
AMREX_ALWAYS_ASSERT(num_infected_family_asymp >= num_infected_family_not_withdrawn_asymp);
int num_infected_nc_asymp =
infected_nc_asymp_d_ptr[cluster_i] - num_infected_family_not_withdrawn_asymp;
AMREX_ALWAYS_ASSERT(num_infected_nc_asymp >= 0);
Real nc_prob_asymp = 1.0_rt - lparm->asymp_relative_inf * infect * xmit_nc_prob * scale;
Real nc_prob_asymp = 1.0_rt - lparm->asymp_relative_inf * infect * xmit_nc_prob * weather_scale;
prob_ptr[i] *= static_cast<ParticleReal>(std::pow(nc_prob_asymp, num_infected_nc_asymp));
}
}
Expand Down
21 changes: 16 additions & 5 deletions src/InteractionModHomeNborhood.H
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,10 @@ void InteractionModHomeNborhood<PCType, PTDType, PType>::fastInteractHomeNborhoo
auto prob_ptr = soa.GetRealData(RealIdx::nattribs + r0(d) + RealIdxDisease::prob).data();
auto lparm = agents.getDiseaseParameters_d(d);
auto lparm_h = agents.getDiseaseParameters_h(d);
Real scale = 1.0_rt; // TODO this should vary based on cell
auto weatherIdxPtr = soa.GetIntData(IntIdx::weatherLookup).data();
auto varVec = agents.awd != nullptr ? agents.awd->varVec.data() : nullptr;
auto weather_model = agents.m_weather_model;
int a_weather_int = agents.m_weather_int;
Real infect = 1.0_rt - lparm_h->vac_eff;
auto d_ptr = getCommunityIndex.comm_to_local_index_d.data();

Expand Down Expand Up @@ -176,15 +179,23 @@ void InteractionModHomeNborhood<PCType, PTDType, PType>::fastInteractHomeNborhoo
infected_community_asymp_d_ptr[community] - num_infected_nborhood_asymp;

AMREX_ALWAYS_ASSERT(num_infected_community >= 0);
Real comm_prob = 1.0_prt - infect * lparm->xmit_comm[ptd.m_idata[IntIdx::age_group][i]] * scale;
Real weather_scale = 1.0_rt;
if (a_weather_int > 0 && varVec != nullptr && weatherIdxPtr[i] >= 0) {
weatherVars wvar = varVec[weatherIdxPtr[i]];
weather_scale = weather_model(wvar);
}
Real comm_prob = 1.0_prt - infect * lparm->xmit_comm[ptd.m_idata[IntIdx::age_group][i]] * weather_scale;
Real comm_prob_asymp = 1.0_prt - lparm->asymp_relative_inf * infect *
lparm->xmit_comm[ptd.m_idata[IntIdx::age_group][i]] * scale;
lparm->xmit_comm[ptd.m_idata[IntIdx::age_group][i]] *
weather_scale;
prob_ptr[i] *= static_cast<ParticleReal>(std::pow(comm_prob, num_infected_community));
prob_ptr[i] *= static_cast<ParticleReal>(std::pow(comm_prob_asymp, num_infected_community_asymp));

Real nborhood_prob = 1.0_prt - infect * lparm->xmit_hood[ptd.m_idata[IntIdx::age_group][i]] * scale;
Real nborhood_prob =
1.0_prt - infect * lparm->xmit_hood[ptd.m_idata[IntIdx::age_group][i]] * weather_scale;
Real nborhood_prob_asymp = 1.0_prt - lparm->asymp_relative_inf * infect *
lparm->xmit_hood[ptd.m_idata[IntIdx::age_group][i]] * scale;
lparm->xmit_hood[ptd.m_idata[IntIdx::age_group][i]] *
weather_scale;
prob_ptr[i] *= static_cast<ParticleReal>(std::pow(nborhood_prob, num_infected_nborhood));
prob_ptr[i] *= static_cast<ParticleReal>(std::pow(nborhood_prob_asymp, num_infected_nborhood_asymp));
}
Expand Down
19 changes: 14 additions & 5 deletions src/InteractionModSchool.H
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,10 @@ void InteractionModSchool<PCType, PTDType, PType>::fastInteractSchool (PCType& a
auto prob_ptr = soa.GetRealData(RealIdx::nattribs + r0(d) + RealIdxDisease::prob).data();
auto lparm = agents.getDiseaseParameters_d(d);
auto lparm_h = agents.getDiseaseParameters_h(d);
Real scale = 1.0_rt; // TODO this should vary based on cell
auto weatherIdxPtr = soa.GetIntData(IntIdx::weatherLookup).data();
auto varVec = agents.awd != nullptr ? agents.awd->varVec.data() : nullptr;
auto weather_model = agents.m_weather_model;
int a_weather_int = agents.m_weather_int;
Real infect = (1.0_rt - lparm_h->vac_eff);
auto d_ptr = getCommunityIndex.comm_to_local_index_d.data();

Expand Down Expand Up @@ -205,17 +208,23 @@ void InteractionModSchool<PCType, PTDType, PType>::fastInteractSchool (PCType& a
auto tidx = getTileIndex(iv, valid_box, true, bin_size, tbx);
auto community = d_ptr[tidx];
int pos = (community * max_school_id + school_id_ptr[i]) * max_school_grade + school_grade_ptr[i];
Real weather_scale = 1.0_rt;
if (a_weather_int > 0 && varVec != nullptr && weatherIdxPtr[i] >= 0) {
weatherVars wvar = varVec[weatherIdxPtr[i]];
weather_scale = weather_model(wvar);
}
if (getSchoolType(school_grade_ptr[i]) == SchoolType::daycare) {
if (adults && age_group_ptr[i] > AgeGroups::a5to17) { // adults-adults interaction accounted for
// in InteractionModWork.H
// do nothing!
} else {
int num_infected_daycare = infected_daycare_d_ptr[pos];
Real daycare_prob = 1.0_rt - infect * lparm->xmit_school[SchoolType::daycare] * scale;
Real daycare_prob = 1.0_rt - infect * lparm->xmit_school[SchoolType::daycare] * weather_scale;
prob_ptr[i] *= static_cast<ParticleReal>(std::pow(daycare_prob, num_infected_daycare));
int num_infected_daycare_asymp = infected_daycare_asymp_d_ptr[pos];
Real daycare_prob_asymp = 1.0_rt - lparm->asymp_relative_inf * infect *
lparm->xmit_school[SchoolType::daycare] * scale;
lparm->xmit_school[SchoolType::daycare] *
weather_scale;
prob_ptr[i] *=
static_cast<ParticleReal>(std::pow(daycare_prob_asymp, num_infected_daycare_asymp));
}
Expand All @@ -238,9 +247,9 @@ void InteractionModSchool<PCType, PTDType, PType>::fastInteractSchool (PCType& a
xmit = lparm->xmit_school_c2a[getSchoolType(school_grade_ptr[i])];
}
}
Real school_prob = 1.0_rt - infect * xmit * scale;
Real school_prob = 1.0_rt - infect * xmit * weather_scale;
prob_ptr[i] *= static_cast<ParticleReal>(std::pow(school_prob, num_infected_school));
Real school_prob_asymp = 1.0_rt - lparm->asymp_relative_inf * infect * xmit * scale;
Real school_prob_asymp = 1.0_rt - lparm->asymp_relative_inf * infect * xmit * weather_scale;
prob_ptr[i] *= static_cast<ParticleReal>(std::pow(school_prob_asymp, num_infected_school_asymp));
}
}
Expand Down
15 changes: 12 additions & 3 deletions src/InteractionModWork.H
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,10 @@ void InteractionModWork<PCType, PTDType, PType>::fastInteractWork (PCType& agent
auto prob_ptr = soa.GetRealData(RealIdx::nattribs + r0(d) + RealIdxDisease::prob).data();
auto lparm = agents.getDiseaseParameters_d(d);
auto lparm_h = agents.getDiseaseParameters_h(d);
Real scale = 1.0_rt; // TODO this should vary based on cell
auto weatherIdxPtr = soa.GetIntData(IntIdx::weatherLookup).data();
auto varVec = agents.awd != nullptr ? agents.awd->varVec.data() : nullptr;
auto weather_model = agents.m_weather_model;
int a_weather_int = agents.m_weather_int;
Real infect = 1.0_rt - lparm_h->vac_eff;
auto d_ptr = getCommunityIndex.comm_to_local_index_d.data();

Expand Down Expand Up @@ -165,10 +168,16 @@ void InteractionModWork<PCType, PTDType, PType>::fastInteractWork (PCType& agent
auto community = d_ptr[tidx];
int wgroup_i = (community * max_workgroup + workgroup_ptr[i]) * max_naics + naics_ptr[i];
int num_infected_workgroup = infected_workgroup_d_ptr[wgroup_i];
Real workgroup_prob = 1.0_prt - infect * lparm->xmit_work * scale;
Real weather_scale = 1.0_rt;
if (a_weather_int > 0 && varVec != nullptr && weatherIdxPtr[i] >= 0) {
weatherVars wvar = varVec[weatherIdxPtr[i]];
weather_scale = weather_model(wvar);
}
Real workgroup_prob = 1.0_prt - infect * lparm->xmit_work * weather_scale;
prob_ptr[i] *= static_cast<ParticleReal>(std::pow(workgroup_prob, num_infected_workgroup));
int num_infected_workgroup_asymp = infected_workgroup_asymp_d_ptr[wgroup_i];
Real workgroup_prob_asymp = 1.0_prt - lparm->asymp_relative_inf * infect * lparm->xmit_work * scale;
Real workgroup_prob_asymp =
1.0_prt - lparm->asymp_relative_inf * infect * lparm->xmit_work * weather_scale;
prob_ptr[i] *= static_cast<ParticleReal>(std::pow(workgroup_prob_asymp, num_infected_workgroup_asymp));
}
});
Expand Down
Loading