Stride Reference Manual  1.0
Simulator.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 2015, Willem L, Kuylen E, Stijven S & Broeckhove J
14  */
15 
21 #include "Simulator.h"
22 #include "AsyncSimulator.h"
23 
24 #include "calendar/Calendar.h"
26 #include "core/Infector.h"
27 #include "pop/Population.h"
28 #include "core/Cluster.h"
29 #include "util/unipar.h"
31 #include "util/etc.h"
32 
33 #include <random>
34 #include <algorithm>
35 #include <mutex>
36 
37 namespace stride {
38 
39 using namespace std;
40 using namespace boost::property_tree;
41 using namespace stride::util;
42 
44  : m_num_threads(1U), m_log_level(LogMode::Null), m_config_pt(), m_population(nullptr),
45  m_disease_profile(), m_track_index_case(false), m_next_id(0), m_next_hh_id(0) {
46  m_parallel.resources().setFunc([&]() {
47  #if UNIPAR_IMPL == UNIPAR_DUMMY
48  return m_rng.get();
49  #else
50  std::random_device rd;
51  return make_unique<Random>(rd());
52  #endif
53  });
54 }
55 
56 const shared_ptr<const Population> Simulator::getPopulation() const {
57  return m_population;
58 }
59 
60 void Simulator::setTrackIndexCase(bool track_index_case) {
61  m_track_index_case = track_index_case;
62 }
63 
64 template<LogMode log_level, bool track_index_case>
66  // Slight hack (thanks to http://stackoverflow.com/q/31724863/2678118#comment51385875_31724863)
67  // but saves us a lot of typing without resorting to macro's.
68  for (auto clusters: {&m_households, &m_school_clusters, &m_work_clusters,
70  m_parallel.for_(0, clusters->size(), [&](RandomRef& rng, size_t i) {
72  (*clusters)[i], m_disease_profile, *rng, m_calendar, *m_logger);
73  });
74  }
75 }
76 
78  // Advance the "calendar" of the districts (for the sphere of influence)
79  for (auto& district: m_districts) {
80  district.advanceInfluencesRecords();
81  }
82 
83  shared_ptr<DaysOffInterface> days_off {nullptr};
84 
85  // Logic where you compute (on the basis of input/config for initial day
86  // or on the basis of number of sick persons, duration of epidemic etc)
87  // what kind of DaysOff scheme you apply. If we want to make this cluster
88  // dependent then the days_off object has to be passed into the update function.
89  days_off = make_shared<DaysOffStandard>(m_calendar);
90  const bool is_work_off {days_off->isWorkOff()};
91  const bool is_school_off {days_off->isSchoolOff()};
92 
93  double fraction_infected = m_population->getFractionInfected();
94 
95  for (auto& p : *m_population) {
96  p.update(is_work_off, is_school_off, fraction_infected);
97  }
98 
99  if (m_track_index_case) {
100  switch (m_log_level) {
101  case LogMode::Contacts:
102  updateClusters<LogMode::Contacts, true>();
103  break;
105  updateClusters<LogMode::Transmissions, true>();
106  break;
107  case LogMode::None:
108  updateClusters<LogMode::None, true>();
109  break;
110  default:
111  throw runtime_error(std::string(__func__) + "Log mode screwed up!");
112  }
113  } else {
114  switch (m_log_level) {
115  case LogMode::Contacts:
116  updateClusters<LogMode::Contacts, false>();
117  break;
119  updateClusters<LogMode::Transmissions, false>();
120  break;
121  case LogMode::None:
122  updateClusters<LogMode::None, false>();
123  break;
124  default:
125  throw runtime_error(std::string(__func__) + "Log mode screwed up!");
126  }
127  }
128 
129  m_calendar->advanceDay();
130  this->notify(*this);
131  return SimulatorStatus(m_population->getInfectedCount(),
132  m_population->getAdoptedCount<Simulator::BeliefPolicy>());
133 }
134 
135 const vector<Cluster>& Simulator::getClusters(ClusterType cluster_type) const {
136  switch (cluster_type) {
138  return m_households;
139  case ClusterType::School:
140  return m_school_clusters;
141  case ClusterType::Work:
142  return m_work_clusters;
144  return m_primary_community;
146  return m_secondary_community;
147  default:
148  throw runtime_error(string(__func__) + "> Should not reach default.");
149  }
150 }
151 
152 vector<string> Simulator::getRngStates() const {
153  vector<string> states;
154  stringstream ss;
155  ss << *m_rng;
156  states.push_back(ss.str());
157  return states;
158 }
159 
160 void Simulator::setRngStates(vector<string> states) {
161  m_rng->setState(states.at(0));
162 }
163 
164 uint Simulator::chooseCluster(const GeoCoordinate& coordinate, const vector<Cluster>& clusters, double influence) {
165  double current_influence = influence;
166 
167  if (clusters.size() == 0) {
168  throw runtime_error(string(__func__) + string("> Empty cluster vector."));
169  }
170 
171  while (true) {
172  vector<uint> available_clusters;
173  const auto& calc = GeoCoordCalculator::getInstance();
174 
175  for (uint i = 1; i < clusters.size(); ++i) {
176 
177  const auto& cluster = clusters.at(i);
178 
179  if (calc.getDistance(coordinate, cluster.getLocation()) <= current_influence) {
180  available_clusters.push_back(i);
181  }
182  }
183 
184  if (available_clusters.size() != 0) {
185  uint chosen_index = m_rng->operator()(available_clusters.size());
186  return available_clusters[chosen_index];
187 
188  } else {
189  // Couldn't find cluster within influence range
190  current_influence *= 2.0;
191  }
192  }
193 }
194 
195 bool Simulator::hostForeignTravellers(const vector<Simulator::TravellerType>& travellers, uint days,
196  string destination_district, string destination_facility) {
197  GeoCoordinate facility_location;
198  double influence = 0.0;
199  bool found_airport = false;
200 
201  for (auto& district: this->m_districts) {
202  if (district.getName() == destination_district && district.hasFacility(destination_facility)) {
203  found_airport = true;
204  facility_location = district.getLocation();
205  influence = district.getFacilityInfluence(destination_facility);
206  district.visitFacility(destination_facility, travellers.size());
207  break;
208  }
209  }
210 
211  if (!found_airport) {
212  cerr << "\nWarning: facility " << destination_facility << " not found in district " << destination_district
213  << endl;
214  return false;
215  }
216 
217  // So that the addresses don't break, reserve the space needed in the vector
218  this->m_population.get()->m_visitors.getDay(days);
219  this->m_population.get()->m_visitors.getModifiableDay(days)->reserve(travellers.size());
220 
221  for (const Simulator::TravellerType& traveller: travellers) {
222  // Choose the clusters the traveller will reside in
223  uint work_index = this->chooseCluster(facility_location, this->m_work_clusters, influence);
224  uint prim_comm_index = this->chooseCluster(facility_location, this->m_primary_community, influence);
225  uint sec_comm_index = this->chooseCluster(facility_location, this->m_secondary_community, influence);
226 
227  if (work_index == this->m_work_clusters.size()
228  || prim_comm_index == this->m_primary_community.size()
229  || sec_comm_index == this->m_secondary_community.size()) {
230 
231  throw runtime_error("Failed to find cluster for traveller");
232  }
233 
234  // Make the person
235  uint start_infectiousness = traveller.getHomePerson().getHealth().getStartInfectiousness();
236  uint start_symptomatic = traveller.getHomePerson().getHealth().getStartSymptomatic();
237 
238  // Note: the "ID" given to the constructor of a person is actually an index!
239  Simulator::PersonType new_person = Simulator::PersonType(m_next_id, traveller.getHomePerson().getAge(),
240  m_next_hh_id, 0,
241  work_index, prim_comm_index, sec_comm_index,
242  start_infectiousness, start_symptomatic,
243  traveller.getHomePerson().getHealth().getEndInfectiousness() -
244  start_infectiousness,
245  traveller.getHomePerson().getHealth().getEndSymptomatic() -
246  start_symptomatic);
247  new_person.getHealth() = traveller.getHomePerson().getHealth();
248 
249  // Add the person to the planner
250  this->m_population->m_visitors.add(days, new_person);
251 
252  // Note: the ID of a non-traveller is always the same as his index in m_population->m_original
253  Simulator::TravellerType new_traveller = Simulator::TravellerType(traveller.getHomePerson(),
254  this->m_population->m_visitors.getModifiableDay(
255  days)->back().get(),
256  traveller.getHomeSimulatorId(),
257  traveller.getDestinationSimulatorId(),
258  traveller.getHomePerson().getId());
259 
260  new_traveller.getNewPerson()->setOnVacation(false);
261  m_planner.add(days, new_traveller);
262 
263  // Get a pointer to the person you just made
264  Simulator::PersonType* person = this->m_population->m_visitors.getModifiableDay(days)->back().get();
265 
266  // Add the person to the clusters
267  this->m_work_clusters.at(work_index).addPerson(person);
268  this->m_primary_community.at(prim_comm_index).addPerson(person);
269  this->m_secondary_community.at(sec_comm_index).addPerson(person);
270 
271  ++m_next_id;
272  ++m_next_hh_id;
273  }
274 
275  return true;
276 }
277 
278 bool Simulator::welcomeHomeTravellers(const vector<uint>& travellers_indices, const vector<Health>& health_status) {
279  auto& original_population = m_population->m_original;
280  for (uint i = 0; i < travellers_indices.size(); ++i) {
281  original_population.at(travellers_indices.at(i)).setOnVacation(false);
282  original_population.at(travellers_indices.at(i)).getHealth() = health_status.at(i);
283  }
284 
285  return true;
286 }
287 
289 
290  // Get the people that return home today (according to the planner in the population of this simulator)
291  SimplePlanner<Simulator::TravellerType>::Block* returning_people = m_planner.getModifiableDay(0);
292 
293  map<string, pair<vector<uint>, vector<Health>>> result;
294 
295  for (auto it = returning_people->begin(); it != returning_people->end(); ++it) {
296  auto& traveller = **it;
297 
298  Simulator::PersonType* returning_person = traveller.getNewPerson();
299 
300  // Get the clusters
301  auto work_index = returning_person->getClusterId(ClusterType::Work);
302  auto prim_comm_index = returning_person->getClusterId(ClusterType::PrimaryCommunity);
303  auto sec_comm_index = returning_person->getClusterId(ClusterType::SecondaryCommunity);
304 
305  // Remove him from the clusters
306  m_work_clusters.at(work_index).removePerson(returning_person->getId());
307  m_primary_community.at(prim_comm_index).removePerson(returning_person->getId());
308  m_secondary_community.at(sec_comm_index).removePerson(returning_person->getId());
309 
310  string destination_sim = traveller.getHomeSimulatorId();
311 
312  // Make the output
313  result[destination_sim].first.push_back(traveller.getHomePerson().getId());
314  result.at(destination_sim).second.push_back(traveller.getNewPerson()->getHealth());
315  }
316 
317  m_planner.nextDay();
318  m_population->m_visitors.nextDay();
319 
320  // Give the data to the senders
321  for (auto it = result.begin(); it != result.end(); ++it) {
322  if (it->second.second.size() != 0) {
323  m_communication_map[it->first]->welcomeHomeTravellers(it->second);
324  }
325  }
326 }
327 
328 void Simulator::sendNewTravellers(uint amount, uint days, const string& destination_sim, string destination_district,
329  string destination_facility) {
330  list < Simulator::PersonType * > working_people;
331 
332  // Get the working people
333  Population& population = *(m_population.get());
334  for (uint i = 0; i < population.m_original.size(); ++i) {
335  Simulator::PersonType& person = population.m_original.at(i);
336  if (person.getClusterId(ClusterType::Work) != 0 && !person.isOnVacation()) {
337  working_people.push_back(&person);
338  }
339  }
340 
341  if (amount > working_people.size()) {
342  cout << "Warning, more people to send than actual people in region. Sending all people.\n";
343  amount = working_people.size();
344  }
345 
346  vector<Simulator::TravellerType> chosen_people;
347  //chosen_people.reserve(amount);
348 
349  while (chosen_people.size() != amount) {
350  // Randomly generate an index in working_people
351  unsigned int index = m_rng->operator()(working_people.size());
352 
353  // Get the person to be sent
354  Simulator::PersonType* person = *(next(working_people.begin(), index));
355  person->setOnVacation(true);
356 
357  // Make the traveller and make sure he can't be sent twice
358  Simulator::TravellerType new_traveller = Simulator::TravellerType(*person, nullptr, m_name, destination_sim,
359  person->getId());
360  chosen_people.push_back(new_traveller);
361  working_people.erase(next(working_people.begin(), index));
362 
363  }
364 
365  m_communication_map[destination_sim]->hostForeignTravellers(chosen_people, days, destination_district,
366  destination_facility);
367 }
368 
369 }
Person< BehaviourPolicy, BeliefPolicy > PersonType
Definition: Simulator.h:72
unsigned int uint
Definition: Influence.h:17
std::shared_ptr< Calendar > m_calendar
Management of calendar.
Definition: Simulator.h:172
VectorType m_original
Definition: Population.h:163
SimulatorStatus timeStep()
Run one time step, computing full simulation (default) or only index case.
Definition: Simulator.cpp:77
std::shared_ptr< spdlog::logger > m_logger
Definition: Simulator.h:177
Traveller< PersonType > TravellerType
Definition: Simulator.h:74
PersonType * getNewPerson() const
Definition: Traveller.h:35
Header for the Infector class.
std::vector< Cluster > m_school_clusters
Container with school Clusters.
Definition: Simulator.h:181
Header file for the Calendar class.
const std::vector< Cluster > & getClusters(ClusterType cluster_type) const
Get the clusters of this simulator based on the cluster type This is rather for testing purposes...
Definition: Simulator.cpp:135
Simulator()
Default constructor for empty Simulator.
Definition: Simulator.cpp:43
void sendNewTravellers(uint amount, uint days, const string &destination_sim_id, string destination_district, string destination_facility)
Definition: Simulator.cpp:328
bool hostForeignTravellers(const vector< Simulator::TravellerType > &travellers, uint days, string destination_district, string destination_facility)
Receive travellers travellers: the travellers this simulator has to host.
Definition: Simulator.cpp:195
Time Dependent Person DataType.
Definition: NoBehaviour.h:17
DiseaseProfile m_disease_profile
Profile of disease.
Definition: Simulator.h:190
Utilities for the project.
Definition: Infector.h:36
std::vector< std::string > getRngStates() const
Retrieve the states of the rng&#39;s.
Definition: Simulator.cpp:152
DaysOffStandard class.
static void execute(Cluster &cluster, DiseaseProfile disease_profile, util::Random &contact_handler, std::shared_ptr< const Calendar > calendar, spdlog::logger &logger)
Definition: Infector.cpp:109
std::shared_ptr< Population > m_population
Pointer to the Population.
Definition: Simulator.h:178
std::map< string, AsyncSimulator * > m_communication_map
Communication between the simulator and the senders.
Definition: Simulator.h:188
void setOnVacation(bool is_on_vacation)
Definition: Person.h:107
void setRngStates(std::vector< std::string > states)
Set the states of the rng&#39;s.
Definition: Simulator.cpp:160
string m_name
Name of the simulator (the region it simulates)
Definition: Simulator.h:196
bool m_track_index_case
General simulation or tracking index case.
Definition: Simulator.h:192
bool isOnVacation() const
Definition: Person.h:105
Header for the Simulator class.
vector< unique_ptr< T >> Block
Definition: SimplePlanner.h:25
Header file for the core Population class.
LogMode m_log_level
Specifies logging mode.
Definition: Simulator.h:171
Container for persons in population.
Definition: Population.h:132
Forward declaration of class Person.
Definition: ThresholdData.h:15
std::vector< Cluster > m_primary_community
Container with primary community Clusters.
Definition: Simulator.h:183
uint chooseCluster(const GeoCoordinate &coordinate, const vector< Cluster > &clusters, double influence)
Return an index to a cluster in the given vector Current policy: search for the first cluster with eq...
Definition: Simulator.cpp:164
void notify(const EventType &e)
Definition: Subject.h:57
Health & getHealth()
Return person&#39;s health status.
Definition: Person.h:79
STL namespace.
const std::shared_ptr< const Population > getPopulation() const
Get the population.
Definition: Simulator.cpp:56
void returnForeignTravellers()
Return people that are here FROM abroad.
Definition: Simulator.cpp:288
uint m_next_hh_id
The household ID of the next traveller that arrives.
Definition: Simulator.h:195
decltype(Parallel().with< RandomRef >()) m_parallel
Definition: Simulator.h:168
bool welcomeHomeTravellers(const vector< uint > &travellers_indices, const vector< Health > &health_status)
Return people that were abroad travellers_indices: contains the indices (in the m_population->m_orig...
Definition: Simulator.cpp:278
LogMode
Enum specifiying the level of logging required:
Definition: LogMode.h:32
uint m_next_id
The ID of the next traveller that arrives.
Definition: Simulator.h:194
std::vector< District > m_districts
Container with districts (villages and cities).
Definition: Simulator.h:186
std::vector< Cluster > m_work_clusters
Container with work Clusters.
Definition: Simulator.h:182
unsigned int getId() const
Get the id.
Definition: Person.h:88
unsigned int getClusterId(ClusterType cluster_type) const
Get cluster ID of cluster_type.
Definition: Person.cpp:27
ClusterType
Enumerates the cluster types.
Definition: ClusterType.h:28
static const GeoCoordCalculator & getInstance()
Singleton pattern.
SimplePlanner< Traveller< Simulator::PersonType > > m_planner
The Planner, responsible for the timing of travellers (when do they return home?).
Definition: Simulator.h:198
std::vector< Cluster > m_households
Container with household Clusters.
Definition: Simulator.h:180
Header for the core Cluster class.
void updateClusters()
Update the contacts in the given clusters.
Definition: Simulator.cpp:65
std::shared_ptr< util::Random > m_rng
Definition: Simulator.h:170
void setTrackIndexCase(bool track_index_case)
Change track_index_case setting.
Definition: Simulator.cpp:60
std::vector< Cluster > m_secondary_community
Container with secondary community Clusters.
Definition: Simulator.h:184
The random number generator.
Definition: Random.h:39