Stride Reference Manual  1.0
PopulationBuilder.cpp
Go to the documentation of this file.
1 /*
2  * This is free software: you can redistribute it and/or modify it
3  * under the terms of the GNU General Public License as published by
4  * the Free Software Foundation, either version 3 of the License, or
5  * any later version.
6  * The software is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9  * GNU General Public License for more details.
10  * You should have received a copy of the GNU General Public License
11  * along with the software. If not, see <http://www.gnu.org/licenses/>.
12  *
13  * Copyright 2017, Willem L, Kuylen E, Stijven S & Broeckhove J
14  */
15 
21 #include "PopulationBuilder.h"
22 
23 #include "util/InstallDirs.h"
24 #include "util/StringUtils.h"
25 
26 #include <boost/property_tree/xml_parser.hpp>
27 
28 namespace stride {
29 
30 using namespace std;
31 using namespace boost::filesystem;
32 using namespace boost::property_tree;
33 using namespace stride::util;
34 
35 shared_ptr<Population> PopulationBuilder::build(
36  const boost::property_tree::ptree& pt_config,
37  const boost::property_tree::ptree& pt_disease,
38  const boost::property_tree::ptree& pt_pop,
39  util::Random& rng) {
40  //------------------------------------------------
41  // Setup.
42  //------------------------------------------------
43  // TODO first determine how many people are needed (by scanning the file twice)
44  const auto pop = make_shared<Population>();
45  Population::VectorType& population = pop->m_original;
46  const double seeding_rate = pt_config.get<double>("run.disease.seeding_rate");
47  const double immunity_rate = pt_config.get<double>("run.disease.immunity_rate");
48  const string disease_config_file = pt_config.get<string>("run.disease.config");
49 
50  //------------------------------------------------
51  // check input.
52  //------------------------------------------------
53  bool status = (seeding_rate <= 1) && (immunity_rate <= 1) && ((seeding_rate + immunity_rate) <= 1);
54  if (!status) {
55  throw runtime_error(string(__func__) + "> Bad input data.");
56  }
57 
58  //------------------------------------------------
59  // Add persons to population.
60  //------------------------------------------------
61  const auto file_name = pt_pop.get<string>("population.people");
62  const auto file_path = InstallDirs::getDataDir() /= file_name;
63  if (!is_regular_file(file_path)) {
64  throw runtime_error(string(__func__)
65  + "> Population (people) file " + file_path.string() + " not present.");
66  }
67 
68  std::ifstream pop_file;
69  pop_file.open(file_path.string());
70  if (!pop_file.is_open()) {
71  throw runtime_error(string(__func__)
72  + "> Error opening population file " + file_path.string());
73  }
74 
75  const auto distrib_start_infectiousness = getDistribution(pt_disease, "disease.start_infectiousness");
76  const auto distrib_start_symptomatic = getDistribution(pt_disease, "disease.start_symptomatic");
77  const auto distrib_time_infectious = getDistribution(pt_disease, "disease.time_infectious");
78  const auto distrib_time_symptomatic = getDistribution(pt_disease, "disease.time_symptomatic");
79 
80  string line;
81  getline(pop_file, line); // step over file header
82  unsigned int person_id = 0U;
83  while (getline(pop_file, line)) {
84  // Make use of stochastic disease characteristics.
85  const auto start_infectiousness = sample(rng, distrib_start_infectiousness);
86  const auto start_symptomatic = sample(rng, distrib_start_symptomatic);
87  const auto time_infectious = sample(rng, distrib_time_infectious);
88  const auto time_symptomatic = sample(rng, distrib_time_symptomatic);
89  const auto values = StringUtils::split(line, ",");
90  auto risk_averseness = 0.0;
91  if (values.size() > 6) {
92  risk_averseness = StringUtils::fromString<double>(values[6]);
93  }
94  population.emplace_back(Simulator::PersonType(person_id,
95  StringUtils::fromString<unsigned int>(values[0]),
96  StringUtils::fromString<unsigned int>(values[1]),
97  StringUtils::fromString<unsigned int>(values[2]),
98  StringUtils::fromString<unsigned int>(values[3]),
99  StringUtils::fromString<unsigned int>(values[4]),
100  StringUtils::fromString<unsigned int>(values[5]),
101  start_infectiousness, start_symptomatic, time_infectious,
102  time_symptomatic, risk_averseness));
103  ++person_id;
104  }
105 
106  pop_file.close();
107 
108  //------------------------------------------------
109  // Customize the population.
110  //------------------------------------------------
111 
112  const unsigned int max_population_index = population.size() - 1;
113  if (max_population_index <= 1U) {
114  throw runtime_error(string(__func__) + "> Problem with population size.");
115  }
116  //------------------------------------------------
117  // Set participants in social contact survey.
118  //------------------------------------------------
119  const string log_level = pt_config.get<string>("run.log_level", "None");
120  if (log_level == "Contacts") {
121  const unsigned int num_participants = pt_config.get<double>("run.outputs.participants_survey.<xmlattr>.num");
122 
123  // use a while-loop to obtain 'num_participant' unique participants (default sampling is with replacement)
124  // A for loop will not do because we might draw the same person twice.
125  // TODO: getting a hold of a proper logger here is very difficult
126  unsigned int num_samples = 0;
127  while (num_samples < num_participants) {
128  Simulator::PersonType& p = population[rng(max_population_index)];
129  if (!p.isParticipatingInSurvey()) {
131  //logger.info("[PART] {} {} {}", p.getId(), p.getAge(), p.getGender());
132  num_samples++;
133  }
134  }
135  }
136 
137  //------------------------------------------------
138  // Set population immunity.
139  //------------------------------------------------
140  unsigned int num_immune = floor(static_cast<double>(population.size()) * immunity_rate);
141  while (num_immune > 0) {
142  Simulator::PersonType& p = population[rng(max_population_index)];
143  if (p.getHealth().isSusceptible()) {
144  p.getHealth().setImmune();
145  num_immune--;
146  }
147  }
148 
149  //------------------------------------------------
150  // Seed infected persons.
151  //------------------------------------------------
152  unsigned int num_infected = floor(static_cast<double> (population.size()) * seeding_rate);
153  while (num_infected > 0) {
154  Simulator::PersonType& p = population[rng(max_population_index)];
155  if (p.getHealth().isSusceptible()) {
157  num_infected--;
158  }
159  }
160 
161  return pop;
162 }
163 
164 
165 vector<double> PopulationBuilder::getDistribution(const boost::property_tree::ptree& pt_root, const string& xml_tag) {
166  vector<double> values;
167  boost::property_tree::ptree subtree = pt_root.get_child(xml_tag);
168  for (const auto& tree : subtree) {
169  values.push_back(tree.second.get<double>(""));
170  }
171  return values;
172 }
173 
174 unsigned int PopulationBuilder::sample(Random& rng, const vector<double>& distribution) {
175  double random_value = rng.nextDouble();
176  for (unsigned int i = 0; i < distribution.size(); i++) {
177  if (random_value <= distribution[i]) {
178  return i;
179  }
180  }
181  cerr << "WARNING: PROBLEM WITH DISEASE DISTRIBUTION [PopulationBuilder]" << endl;
182  return distribution.size();
183 }
184 
185 }
bool isParticipatingInSurvey() const
Does this person participates in the social contact study?
Definition: Person.h:94
Interface for install directory queries.
Time Dependent Person DataType.
Definition: NoBehaviour.h:17
Conversion from or to string.
static boost::filesystem::path getDataDir()
Utility method: get path to the directory for data files.
Utilities for the project.
Definition: Infector.h:36
static unsigned int sample(util::Random &rng, const std::vector< double > &distribution)
Sample from the distribution.
Forward declaration of class Person.
Definition: ThresholdData.h:15
static std::shared_ptr< Population > build(const boost::property_tree::ptree &pt_config, const boost::property_tree::ptree &pt_disease, const boost::property_tree::ptree &pt_pop, util::Random &rng)
Initializes a Population: add persons, set immunity, seed infection.
Health & getHealth()
Return person&#39;s health status.
Definition: Person.h:79
void participateInSurvey()
Participate in social contact study and log person details.
Definition: Person.h:97
STL namespace.
vector< PersonType > VectorType
Definition: Population.h:138
double nextDouble()
Definition: Random.h:46
static std::vector< double > getDistribution(const boost::property_tree::ptree &pt_root, const std::string &xml_tag)
Get distribution associateed with tag values.
bool isSusceptible() const
Is this person susceptible?
Definition: Health.h:67
static std::vector< std::string > split(const std::string &str, const std::string &delimiters)
Split a string (in order of occurence) by splitting it on the given delimiters.
Definition: StringUtils.h:49
void startInfection()
Start the infection.
Definition: Health.cpp:39
Initialize populations.
The random number generator.
Definition: Random.h:39
void setImmune()
Set immune to true.
Definition: Health.cpp:31