Skip to content

Commit

Permalink
births and deaths test
Browse files Browse the repository at this point in the history
  • Loading branch information
confunguido committed Oct 24, 2024
1 parent eddca43 commit 198c1cc
Show file tree
Hide file tree
Showing 9 changed files with 300 additions and 141 deletions.
32 changes: 16 additions & 16 deletions examples/births-deaths/README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Birth and death processes

This example describes the process of loading a population, adding new births and deaths. It has a simple sir model that represents infections from a constant force of infection that differs by age category (0,2m, 6m, 65y).
This example describes the process of loading a population, adding new births and deaths. It has a simple sir model that represents infections from a constant force of infection that differs by age category (0,2m, 6m, 65y).

# Features

* Adding people to the simulation with person properties that are set by default for newborns
* Looking up people based on their current age
* Introduces the concept of updated person properties. For instance, if a person's risk property depends on their age, it should change when they age.
* Introduces deaths and how they are removed from the population.
* Introduces the concept of updated person properties. For instance, if a person's risk property depends on their age, it should change when they age.
* Introduces deaths and how they are removed from the population.

# Simulation overview
The simulation loads parameters that contain a seed, population size, birth and death rates, as well as foi rates. Then, a the population manager module is initialized to set up the initial population and their person properties. This module is also in charge of scheduling new births and deaths based on the specified rates in parameters. Finally, the transmission manager module is initialized. Infection attempts are scheduled to occur based on the constant age-specific force of infection. Once an infection event is scheduled, a susceptible individual is selected to be infected. This individual is required to be alive at the time of the infection attempt. After an infection attempt is finished, the next infection event is scheduled based on a constant force of infection.
The simulation loads parameters that contain a seed, population size, birth and death rates, as well as foi rates. Then, a the population manager module is initialized to set up the initial population and their person properties. This module is also in charge of scheduling new births and deaths based on the specified rates in parameters. Finally, the transmission manager module is initialized. Infection attempts are scheduled to occur based on the constant age-specific force of infection. Once an infection event is scheduled, a susceptible individual is selected to be infected. This individual is required to be alive at the time of the infection attempt. After an infection attempt is finished, the next infection event is scheduled based on a constant force of infection.

Infected individuals schedule their recovery at time `t + infection_period`. The infection status of recovered individuals remains as recovered for the rest of the simulation.

Expand All @@ -21,9 +21,9 @@ fn main() {
let current_dir = Path::new(file!()).parent().unwrap();
let file_path = current_dir
.join("input.json");

parameters_loader::init();

let parameters = context.get_global_property_value(Parameters).clone();
context.init_random(parameters.seed);
population_manager::init(&mut context);
Expand All @@ -33,9 +33,9 @@ fn main() {
```

# People and person properties
When the `Population manager` module initializes, a number of persons are created and given a unique person id (from `0` to `population_size`). This functionality is provided by an `create_person` method from the `people` module in `ixa`, which adds them to a `People` data container. This function also defines a special person property that determines people's life status in the simulation `define_person_property!(Alive, bool, true)`.
When the `Population manager` module initializes, a number of persons are created and given a unique person id (from `0` to `population_size`). This functionality is provided by an `create_person` method from the `people` module in `ixa`, which adds them to a `People` data container. This function also defines a special person property that determines people's life status in the simulation `define_person_property!(Alive, bool, true)`.

The population manager also defines an infection status person property and an age property, which is assigned randomly based on a uniform distribution.
The population manager also defines an infection status person property and an age property, which is assigned randomly based on a uniform distribution.

```rust
InfectionStatus = enum(
Expand All @@ -50,7 +50,7 @@ pub enum RiskCategory {
}

define_person_property!(Age, u8); // Age in days
define_person_property!(RiskCategoryType, RiskCategory); // This is a derived property that depends on age.
define_person_property!(RiskCategoryType, RiskCategory); // This is a derived property that depends on age.
define_person_property!(InfectionStatusType, InfectionStatus, InfectionStatus::S);

for (person_id in 0..parameters.get_parameter(population_size)) {
Expand All @@ -64,22 +64,22 @@ for (person_id in 0..parameters.get_parameter(population_size)) {
## Births and deaths

### Births
Some requirements are necessary to include births in the simulation. Namely,
Some requirements are necessary to include births in the simulation. Namely,
* Newborns increase the population,
* Person properties are set for newborns at the time of creation, including derived properties,
* Newborns become available to look up and should be considered alive after their time of birth,
* A person created events should be emitted.
* A person created events should be emitted.

### Deaths
Requirements for deaths include removing from simulation and canceling any plans for the `person_id`.
Requirements for deaths include removing from simulation and canceling any plans for the `person_id`.
* Deaths reduce current population, but not for the purposes of the person id for next new born. This means that a counter for total population is required as well as newborns and deaths,
* Alive person property should be set to `false`,
* All plans should be canceled for `person_id` when they are removed from population. This should happen inside `people.rs` so that modules aren't required to continuously observe for death events,
* Death people should not be counted for the force of infection or other transmission events.
* Death people should not be counted for the force of infection or other transmission events.


# Transmission manager
Infections are spread throughout the population based on a constant force of infection, which differs for age groups 0-12m, 1-65, and 65+. Infection attempts are scheduled based on each age group force of infection. This requires the implementation of an Ixa functionality to look up individuals based on their current age.
Infections are spread throughout the population based on a constant force of infection, which differs for age groups 0-12m, 1-65, and 65+. Infection attempts are scheduled based on each age group force of infection. This requires the implementation of an Ixa functionality to look up individuals based on their current age.

```rust transmission_manager.rs
fn schedule_infection(context, age_group, foi_age) {
Expand Down
51 changes: 22 additions & 29 deletions examples/births-deaths/demographics_report.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use crate::population_manager::{AgeGroupRisk, Alive, ContextPopulationExt};
use crate::Parameters;
use ixa::{
context::Context, create_report_trait, global_properties::ContextGlobalPropertiesExt, people::{ContextPeopleExt, PersonCreatedEvent, PersonPropertyChangeEvent}, report::{ContextReportExt, Report}
context::Context,
create_report_trait,
global_properties::ContextGlobalPropertiesExt,
people::{PersonCreatedEvent, PersonPropertyChangeEvent},
report::{ContextReportExt, Report},
};
use std::path::PathBuf;
use std::path::Path;
use serde::{Deserialize, Serialize};
use crate::population_manager::{Birth, AgeGroupRisk, ContextPopulationExt, Alive};
use crate::Parameters;
use std::path::Path;
use std::path::PathBuf;

#[derive(Serialize, Deserialize, Clone)]
struct PersonReportItem {
Expand All @@ -18,41 +22,34 @@ struct PersonReportItem {

create_report_trait!(PersonReportItem);

fn handle_person_created(
context: &mut Context,
event: PersonCreatedEvent,
) {
fn handle_person_created(context: &mut Context, event: PersonCreatedEvent) {
let person = event.person_id;
let age_person = context.get_person_age(person);
let age_group_person = context.get_person_age_group(person).clone();
let age_group_person = context.get_person_age_group(person);
context.send_report(PersonReportItem {
time: context.get_current_time(),
person_id: format!("{}", person),
person_id: format!("{person}"),
age: age_person,
age_group: age_group_person,
event: "Created".to_string(),
});
}

fn handle_death_events(
context: &mut Context,
event: PersonPropertyChangeEvent<Alive>,
) {
if event.current == false {
fn handle_death_events(context: &mut Context, event: PersonPropertyChangeEvent<Alive>) {
if !event.current {
let person = event.person_id;
let age_person = context.get_person_age(person);
let age_group_person = context.get_person_age_group(person).clone();
let age_group_person = context.get_person_age_group(person);
context.send_report(PersonReportItem {
time: context.get_current_time(),
person_id: format!("{}", person),
person_id: format!("{person}"),
age: age_person,
age_group: age_group_person,
event: "Dead".to_string(),
});
}
}


pub fn init(context: &mut Context) {
let parameters = context.get_global_property_value(Parameters).clone();

Expand All @@ -62,14 +59,10 @@ pub fn init(context: &mut Context) {
.directory(PathBuf::from(current_dir));

context.add_report::<PersonReportItem>(&parameters.demographic_output_file);
context.subscribe_to_event(
|context, event: PersonCreatedEvent| {
handle_person_created(context, event);
},
);
context.subscribe_to_event(
|context, event: PersonPropertyChangeEvent<Alive> | {
handle_death_events(context, event);
},
);
context.subscribe_to_event(|context, event: PersonCreatedEvent| {
handle_person_created(context, event);
});
context.subscribe_to_event(|context, event: PersonPropertyChangeEvent<Alive>| {
handle_death_events(context, event);
});
}
8 changes: 4 additions & 4 deletions examples/births-deaths/incidence_report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ use ixa::global_properties::ContextGlobalPropertiesExt;
use ixa::people::PersonPropertyChangeEvent;
use ixa::report::ContextReportExt;
use ixa::{create_report_trait, report::Report};
use std::path::PathBuf;
use std::path::Path;
use std::path::PathBuf;

use crate::population_manager::ContextPopulationExt;
use crate::population_manager::AgeGroupRisk;
use crate::population_manager::InfectionStatusType;
use crate::population_manager::ContextPopulationExt;
use crate::population_manager::InfectionStatus;
use crate::population_manager::InfectionStatusType;

use serde::{Deserialize, Serialize};

Expand All @@ -31,7 +31,7 @@ fn handle_infection_status_change(
event: PersonPropertyChangeEvent<InfectionStatusType>,
) {
let age_person = context.get_person_age(event.person_id);
let age_group_person = context.get_person_age_group(event.person_id).clone();
let age_group_person = context.get_person_age_group(event.person_id);
context.send_report(IncidenceReportItem {
time: context.get_current_time(),
person_id: format!("{}", event.person_id),
Expand Down
4 changes: 2 additions & 2 deletions examples/births-deaths/infection_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use ixa::people::{ContextPeopleExt, PersonId, PersonPropertyChangeEvent};
use ixa::random::ContextRandomExt;
use rand_distr::Exp;

use crate::population_manager::Alive;
use crate::population_manager::InfectionStatus;
use crate::population_manager::InfectionStatusType;
use crate::population_manager::Alive;
use crate::Parameters;

define_rng!(InfectionRng);
Expand All @@ -17,7 +17,7 @@ fn schedule_recovery(context: &mut Context, person_id: PersonId) {
let infection_duration = parameters.infection_duration;
let recovery_time = context.get_current_time()
+ context.sample_distr(InfectionRng, Exp::new(1.0 / infection_duration).unwrap());
if context.get_person_property(person_id, Alive) {
if context.get_person_property(person_id, Alive) {
context.add_plan(recovery_time, move |context| {
context.set_person_property(person_id, InfectionStatusType, InfectionStatus::R);
});
Expand Down
2 changes: 1 addition & 1 deletion examples/births-deaths/input.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"population": 100,
"max_time": 380.0,
"seed": 123,
"birth_rate": 0.0,
"birth_rate": 0.1,
"death_rate": 0.1,
"foi_groups": [
{"group_name": "NewBorn", "foi": 0.2},
Expand Down
19 changes: 7 additions & 12 deletions examples/births-deaths/main.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,33 @@
use ixa::random::ContextRandomExt;
use ixa::{
context::Context,
global_properties::ContextGlobalPropertiesExt,
};
use ixa::{context::Context, global_properties::ContextGlobalPropertiesExt};
use std::path::Path;

mod parameters_loader;
mod population_manager;
mod demographics_report;
mod transmission_manager;
mod incidence_report;
mod infection_manager;
mod parameters_loader;
mod population_manager;
mod transmission_manager;

use crate::parameters_loader::Parameters;

fn main() {
let mut context = Context::new();
let current_dir = Path::new(file!()).parent().unwrap();
let file_path = current_dir
.join("input.json");
let file_path = current_dir.join("input.json");

match parameters_loader::init_parameters(&mut context, &file_path) {
Ok(()) => {
let parameters = context.get_global_property_value(Parameters).clone();
context.init_random(parameters.seed);

demographics_report::init(&mut context);
incidence_report::init(&mut context);

population_manager::init(&mut context);
transmission_manager::init(&mut context);
infection_manager::init(&mut context);

context.add_plan(parameters.max_time, |context| {
context.shutdown();
});
Expand All @@ -43,4 +39,3 @@ fn main() {
}
}
}

7 changes: 3 additions & 4 deletions examples/births-deaths/parameters_loader.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use ixa::context::Context;
use ixa::global_properties::ContextGlobalPropertiesExt;
use std::{fmt::Debug, vec};
use std::fmt::Debug;
use std::path::Path;

use ixa::define_global_property;
Expand Down Expand Up @@ -28,10 +28,9 @@ pub struct ParametersValues {
pub demographic_output_file: String,
}
define_global_property!(Parameters, ParametersValues);
//define_global_property!(Foi, HashMap);

pub fn init_parameters(context: &mut Context, file_path: &Path) -> Result<(), IxaError> {
let parameters_json = context.load_parameters_from_json::<ParametersValues>(file_path)?;
context.set_global_property_value(Parameters, parameters_json.clone());
context.set_global_property_value(Parameters, parameters_json.clone());
Ok(())
}

Loading

0 comments on commit 198c1cc

Please sign in to comment.