Skip to content

Commit

Permalink
Refactor: repeated code per interaction type to SIER.h
Browse files Browse the repository at this point in the history
  • Loading branch information
ptheywood committed Dec 18, 2024
1 parent 7f7c034 commit 420b290
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 53 deletions.
41 changes: 41 additions & 0 deletions src/exateppabm/disease/SEIR.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <cstdint>
#include "flamegpu/flamegpu.h"
#include "exateppabm/input.h"
#include "exateppabm/person.h"

namespace exateppabm {
namespace disease {
Expand Down Expand Up @@ -53,6 +54,46 @@ void define(flamegpu::ModelDescription& model, const exateppabm::input::config&

void appendLayers(flamegpu::ModelDescription& model);

/**
* Device utility function to get an individuals current infection status from global agent memory
*
* Templated for due to the templated DeviceAPI object
* *
* @param FLAMEGPU flame gpu device API object
* @return individuals current infection state
*/
template<typename MsgIn, typename MsgOut>
FLAMEGPU_DEVICE_FUNCTION disease::SEIR::InfectionStateUnderlyingType getCurrentInfectionStatus(flamegpu::DeviceAPI<MsgIn, MsgOut>* FLAMEGPU) {
return FLAMEGPU->template getVariable<disease::SEIR::InfectionStateUnderlyingType>(person::v::INFECTION_STATE);
}


/**
* Device utility function for when an individual is exposed, moving from susceptible to exposed
*
* Templated for due to the templated DeviceAPI object
*
* @param FLAMEGPU flamegpu device api object
* @param current infection status for the individual to be mutated in-place
*/
template<typename MsgIn, typename MsgOut>
FLAMEGPU_DEVICE_FUNCTION void susceptibleToExposed(flamegpu::DeviceAPI<MsgIn, MsgOut>* FLAMEGPU, disease::SEIR::InfectionStateUnderlyingType& infectionStatus) {
// Generate how long the individual will be in the exposed for.
float mean = FLAMEGPU->environment.template getProperty<float>("mean_time_to_infected");
float sd = FLAMEGPU->environment.template getProperty<float>("sd_time_to_infected");
float stateDuration = (FLAMEGPU->random.template normal<float>() * sd) + mean;

// Update the referenced value containing the individuals current infections status, used to reduce branching within a device for loop.
infectionStatus = disease::SEIR::InfectionState::Exposed;
// Update individuals infection state in global agent memory
FLAMEGPU->template setVariable<disease::SEIR::InfectionStateUnderlyingType>(person::v::INFECTION_STATE, infectionStatus);
// Update the individual infection state duration in global agent memory
FLAMEGPU->template setVariable<float>(person::v::INFECTION_STATE_DURATION, stateDuration);

// Increment the infection counter for this individual
person::incrementInfectionCounter(FLAMEGPU);
}

} // namespace SEIR

} // namespace disease
Expand Down
29 changes: 11 additions & 18 deletions src/exateppabm/household.cu
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,12 @@ FLAMEGPU_AGENT_FUNCTION(interactHousehold, flamegpu::MessageBucket, flamegpu::Me
p_s2e *= relativeSusceptibility;

// Check if the current individual is susceptible to being infected
auto infectionState = FLAMEGPU->getVariable<disease::SEIR::InfectionStateUnderlyingType>(person::v::INFECTION_STATE);
auto infectionState = disease::SEIR::getCurrentInfectionStatus(FLAMEGPU);

// @todo - this will need to change for contact tracing, the message interaction will need to occur regardless.
// Only check interactions from this individual if they are susceptible. @todo - this will need to change for contact tracing.
if (infectionState == disease::SEIR::Susceptible) {
// Variable to store the duration of the exposed phase (if exposed)
float stateDuration = 0.f;

// Bool to track if individual newly exposed - used to move expensive operations outside the message iteration loop.
bool newlyExposed = false;
// Iterate messages from anyone within the household
for (const auto &message : FLAMEGPU->message_in(householdIdx)) {
// Ignore self messages (can't infect oneself)
Expand All @@ -91,25 +90,19 @@ FLAMEGPU_AGENT_FUNCTION(interactHousehold, flamegpu::MessageBucket, flamegpu::Me
// Roll a dice
float r = FLAMEGPU->random.uniform<float>();
if (r < p_s2e) {
// I have been exposed
infectionState = disease::SEIR::InfectionState::Exposed;
// Generate how long until I am infected
float mean = FLAMEGPU->environment.getProperty<float>("mean_time_to_infected");
float sd = FLAMEGPU->environment.getProperty<float>("sd_time_to_infected");
stateDuration = (FLAMEGPU->random.normal<float>() * sd) + mean;
// @todo - for now only any exposure matters. This may want to change when quantity of exposure is important?
// Increment the infection counter for this individual
FLAMEGPU->setVariable<std::uint32_t>(person::v::INFECTION_COUNT, FLAMEGPU->getVariable<std::uint32_t>(person::v::INFECTION_COUNT) + 1);
// set a flag indicating that the individual has been exposed in this message iteration loop
newlyExposed = true;
// break out of the message iteration loop, currently no need to check for multiple exposures on the same day.
break;
}
}
}
}
}
// If newly exposed, store the value in global device memory.
if (infectionState == disease::SEIR::InfectionState::Exposed) {
FLAMEGPU->setVariable<disease::SEIR::InfectionStateUnderlyingType>(person::v::INFECTION_STATE, infectionState);
FLAMEGPU->setVariable<float>(person::v::INFECTION_STATE_DURATION, stateDuration);
// If newly exposed, update agent data and generate new seir state information. This is done outside the message iteration loop to be more GPU-shaped.
if (newlyExposed) {
// Transition from susceptible to exposed in SEIR
disease::SEIR::susceptibleToExposed(FLAMEGPU, infectionState);
}
}

Expand Down
12 changes: 12 additions & 0 deletions src/exateppabm/person.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ void define(flamegpu::ModelDescription& model, const exateppabm::input::config&
*/
void appendLayers(flamegpu::ModelDescription& model);

/**
* Device utility function for Increasing an individuals infection counter, for more legible agent code
*
* Templated for due to the templated DeviceAPI object
*
* @todo - consider moving to the disease namespace?
*/
template<typename MsgIn, typename MsgOut>
FLAMEGPU_DEVICE_FUNCTION void incrementInfectionCounter(flamegpu::DeviceAPI<MsgIn, MsgOut>* FLAMEGPU) {
FLAMEGPU->template setVariable<std::uint32_t>(v::INFECTION_COUNT, FLAMEGPU->template getVariable<std::uint32_t>(v::INFECTION_COUNT) + 1);
}


// Undefine the host device macro to avoid potential macro collisions
#undef DEVICE_CONSTEXPR_STRING
Expand Down
29 changes: 11 additions & 18 deletions src/exateppabm/random_interactions.cu
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,12 @@ FLAMEGPU_AGENT_FUNCTION(interactRandomDailyNetwork, flamegpu::MessageArray, flam
p_s2e *= relativeSusceptibility;

// Check if the current individual is susceptible to being infected
auto infectionState = FLAMEGPU->getVariable<disease::SEIR::InfectionStateUnderlyingType>(person::v::INFECTION_STATE);
auto infectionState = disease::SEIR::getCurrentInfectionStatus(FLAMEGPU);

// @todo - this will need to change for contact tracing, the message interaction will need to occur regardless.
// Only check interactions from this individual if they are susceptible. @todo - this will need to change for contact tracing.
if (infectionState == disease::SEIR::Susceptible) {
// Variable to store the duration of the exposed phase (if exposed)
float stateDuration = 0.f;

// Bool to track if individual newly exposed - used to move expensive operations outside the message iteration loop.
bool newlyExposed = false;
// For each interaction this agent is set to perform
const std::uint32_t randomInteractionCount = FLAMEGPU->getVariable<std::uint32_t>(person::v::RANDOM_INTERACTION_COUNT);
for (std::uint32_t randomInteractionIdx = 0; randomInteractionIdx < randomInteractionCount; ++randomInteractionIdx) {
Expand All @@ -200,24 +199,18 @@ FLAMEGPU_AGENT_FUNCTION(interactRandomDailyNetwork, flamegpu::MessageArray, flam
// Roll a dice
float r = FLAMEGPU->random.uniform<float>();
if (r < p_s2e) {
// I have been exposed
infectionState = disease::SEIR::InfectionState::Exposed;
// Generate how long until I am infected
float mean = FLAMEGPU->environment.getProperty<float>("mean_time_to_infected");
float sd = FLAMEGPU->environment.getProperty<float>("sd_time_to_infected");
stateDuration = (FLAMEGPU->random.normal<float>() * sd) + mean;
// @todo - for now only any exposure matters. This may want to change when quantity of exposure is important?
// Increment the infection counter for this individual
FLAMEGPU->setVariable<std::uint32_t>(person::v::INFECTION_COUNT, FLAMEGPU->getVariable<std::uint32_t>(person::v::INFECTION_COUNT) + 1);
// set a flag indicating that the individual has been exposed in this message iteration loop
newlyExposed = true;
// break out of the loop over today's random interactions - can only be exposed once
break;
}
}
}
}
// If newly exposed, store the value in global device memory.
if (infectionState == disease::SEIR::InfectionState::Exposed) {
FLAMEGPU->setVariable<disease::SEIR::InfectionStateUnderlyingType>(person::v::INFECTION_STATE, infectionState);
FLAMEGPU->setVariable<float>(person::v::INFECTION_STATE_DURATION, stateDuration);
// If newly exposed, update agent data and generate new seir state information. This is done outside the message iteration loop to be more GPU-shaped.
if (newlyExposed) {
// Transition from susceptible to exposed in SEIR
disease::SEIR::susceptibleToExposed(FLAMEGPU, infectionState);
}
}

Expand Down
28 changes: 11 additions & 17 deletions src/exateppabm/workplace.cu
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ FLAMEGPU_AGENT_FUNCTION(interactWorkplace, flamegpu::MessageArray, flamegpu::Mes
p_s2e *= relativeSusceptibility;

// Check if the current individual is susceptible to being infected
auto infectionState = FLAMEGPU->getVariable<disease::SEIR::InfectionStateUnderlyingType>(person::v::INFECTION_STATE);
auto infectionState = disease::SEIR::getCurrentInfectionStatus(FLAMEGPU);

// @todo - this will need to change for contact tracing, the message interaction will need to occur regardless.
// Only check interactions from this individual if they are susceptible. @todo - this will need to change for contact tracing.
if (infectionState == disease::SEIR::Susceptible) {
// Variable to store the duration of the exposed phase (if exposed)
float stateDuration = 0.f;
// Bool to track if individual newly exposed - used to move expensive operations outside the message iteration loop.
bool newlyExposed = false;
// Iterate my downstream neighbours (the graph is undirected, so no need to iterate in and out
auto workplaceGraph = FLAMEGPU->environment.getDirectedGraph("WORKPLACE_DIGRAPH");
std::uint32_t myVertexIndex = workplaceGraph.getVertexIndex(id);
Expand All @@ -93,25 +93,19 @@ FLAMEGPU_AGENT_FUNCTION(interactWorkplace, flamegpu::MessageArray, flamegpu::Mes
// Roll a dice to determine if exposure occurred
float r = FLAMEGPU->random.uniform<float>();
if (r < p_s2e) {
// I have been exposed
infectionState = disease::SEIR::InfectionState::Exposed;
// Generate how long until I am infected
float mean = FLAMEGPU->environment.getProperty<float>("mean_time_to_infected");
float sd = FLAMEGPU->environment.getProperty<float>("sd_time_to_infected");
stateDuration = (FLAMEGPU->random.normal<float>() * sd) + mean;
// @todo - for now only any exposure matters. This may want to change when quantity of exposure is important?
// Increment the infection counter for this individual
FLAMEGPU->setVariable<std::uint32_t>(person::v::INFECTION_COUNT, FLAMEGPU->getVariable<std::uint32_t>(person::v::INFECTION_COUNT) + 1);
// set a flag indicating that the individual has been exposed in this message iteration loop
newlyExposed = true;
// break out of the message iteration loop, currently no need to check for multiple exposures on the same day.
break;
}
}
}
}
}
// If newly exposed, store the value in global device memory.
if (infectionState == disease::SEIR::InfectionState::Exposed) {
FLAMEGPU->setVariable<disease::SEIR::InfectionStateUnderlyingType>(person::v::INFECTION_STATE, infectionState);
FLAMEGPU->setVariable<float>(person::v::INFECTION_STATE_DURATION, stateDuration);
// If newly exposed, update agent data and generate new seir state information. This is done outside the message iteration loop to be more GPU-shaped.
if (newlyExposed) {
// Transition from susceptible to exposed in SEIR
disease::SEIR::susceptibleToExposed(FLAMEGPU, infectionState);
}
}

Expand Down

0 comments on commit 420b290

Please sign in to comment.