pops-core  0.9
PoPS (Pest or Pathogen Spread) Model Core C++ library
scheduling.hpp
Go to the documentation of this file.
1 /*
2  * PoPS model - scheduling simulation steps
3  *
4  * Copyright (C) 2015-2020 by the authors.
5  *
6  * Authors: Anna Petrasova
7  * Vaclav Petras
8  *
9  * The code contained herein is licensed under the GNU General Public
10  * License. You may obtain a copy of the GNU General Public License
11  * Version 2 or later at the following locations:
12  *
13  * http://www.opensource.org/licenses/gpl-license.html
14  * http://www.gnu.org/copyleft/gpl.html
15  */
16 
17 #ifndef POPS_SCHEDULING_HPP
18 #define POPS_SCHEDULING_HPP
19 
20 #include <iostream>
21 #include <vector>
22 #include <map>
23 #include <tuple>
24 #include <string>
25 #include <algorithm>
26 
27 #include "date.hpp"
28 
29 namespace pops {
30 
38 class Step
39 {
40 public:
41  Step(Date start_date, Date end_date) : start_date_(start_date), end_date_(end_date)
42  {}
43  inline Date start_date() const
44  {
45  return start_date_;
46  }
47  inline Date end_date() const
48  {
49  return end_date_;
50  }
51  inline friend std::ostream& operator<<(std::ostream& os, const Step& step);
52 
53 private:
54  Date start_date_;
55  Date end_date_;
56 };
57 std::ostream& operator<<(std::ostream& os, const Step& step)
58 {
59  os << step.start_date_ << " - " << step.end_date_;
60  return os;
61 }
62 
66 enum class StepUnit
67 {
68  Day,
69  Week,
70  Month
71 };
72 
79 inline StepUnit step_unit_enum_from_string(const std::string& text)
80 {
81  std::map<std::string, StepUnit> mapping{
82  {"day", StepUnit::Day}, {"week", StepUnit::Week}, {"month", StepUnit::Month}};
83  try {
84  return mapping.at(text);
85  }
86  catch (const std::out_of_range&) {
87  throw std::invalid_argument(
88  "step_unit_enum_from_string:"
89  " Invalid value '"
90  + text + "' provided");
91  }
92 }
93 
94 class Scheduler
95 {
96 public:
112  const Date& start,
113  const Date& end,
114  StepUnit simulation_unit,
115  unsigned simulation_num_units)
116  : start_(start),
117  end_(end),
118  simulation_unit_(simulation_unit),
119  simulation_num_units_(simulation_num_units)
120  {
121  if (start >= end)
122  throw std::invalid_argument("Start date must be before end date");
123  if (simulation_num_units <= 0)
124  throw std::invalid_argument(
125  "Number of simulation units must be higher than zero");
126  Date d(start);
127  increase_date(d);
128  if (d > end)
129  throw std::invalid_argument(
130  "There must be at least one step between start and end date");
131  if (simulation_unit == StepUnit::Month && start.day() != 1)
132  throw std::invalid_argument(
133  "If step unit is month, start date must start the first day of a "
134  "month");
135 
136  Date date(start_);
137  unsigned step = 0;
138  while (date <= end_) {
139  Date start(date);
140  increase_date(date);
141  Date end(date);
142  end.subtract_day();
143  steps.push_back(Step(start, end));
144  step++;
145  }
146  num_steps = step;
147  }
148 
152  unsigned get_num_steps() const
153  {
154  return num_steps;
155  }
156 
160  Step get_step(unsigned index) const
161  {
162  return steps.at(index);
163  }
164 
168  std::tuple<unsigned, StepUnit> get_step_length() const
169  {
170  return std::make_tuple(simulation_num_units_, simulation_unit_);
171  }
172 
178  std::vector<bool> schedule_spread(const Season& season) const
179  {
180  std::vector<bool> schedule;
181  schedule.reserve(num_steps);
182  for (Step step : steps) {
183  if (season.month_in_season(step.start_date().month())
184  || season.month_in_season(step.end_date().month()))
185  schedule.push_back(true);
186  else
187  schedule.push_back(false);
188  }
189  return schedule;
190  }
191 
203  std::vector<bool> schedule_action_yearly(int month, int day) const
204  {
205  std::vector<bool> schedule;
206  schedule.reserve(num_steps);
207  for (Step step : steps) {
208  Date st = step.start_date();
209  Date end = step.end_date();
210  Date test(st.year(), month, day);
211  if ((test >= st && test <= end))
212  schedule.push_back(true);
213  else
214  schedule.push_back(false);
215  }
216  return schedule;
217  }
218 
224  std::vector<bool> schedule_action_end_of_year() const
225  {
226  std::vector<bool> schedule;
227  schedule.reserve(num_steps);
228  for (Step step : steps) {
229  if (step.end_date().is_last_day_of_year())
230  schedule.push_back(true);
231  else
232  schedule.push_back(false);
233  }
234  return schedule;
235  }
236 
242  std::vector<bool> schedule_action_end_of_simulation() const
243  {
244  std::vector<bool> schedule(num_steps, false);
245  if (num_steps > 0)
246  schedule[num_steps - 1] = true;
247  return schedule;
248  }
249 
258  std::vector<bool> schedule_action_nsteps(unsigned n_steps) const
259  {
260  std::vector<bool> schedule;
261  schedule.reserve(num_steps);
262  for (unsigned i = 0; i < num_steps; i++) {
263  if ((i + 1) % n_steps == 0)
264  schedule.push_back(true);
265  else
266  schedule.push_back(false);
267  }
268  return schedule;
269  }
270 
278  std::vector<bool> schedule_action_monthly() const
279  {
280  std::vector<bool> schedule;
281  schedule.reserve(num_steps);
282  for (Step step : steps) {
283  Date st = step.start_date();
284  Date end = step.end_date();
285  if (st.month() != end.month() || end.is_last_day_of_month())
286  schedule.push_back(true);
287  else
288  schedule.push_back(false);
289  }
290  return schedule;
291  }
292 
301  unsigned schedule_action_date(const Date& date) const
302  {
303  for (unsigned i = 0; i < num_steps; i++) {
304  if (date >= steps[i].start_date() && date <= steps[i].end_date())
305  return i;
306  }
307  throw std::invalid_argument("Date is outside of schedule");
308  }
313  void debug_schedule(std::vector<bool>& schedule) const
314  {
315  for (unsigned i = 0; i < num_steps; i++)
316  std::cout << steps[i] << ": " << (schedule.at(i) ? "true" : "false")
317  << std::endl;
318  }
319  void debug_schedule(unsigned n) const
320  {
321  for (unsigned i = 0; i < num_steps; i++)
322  std::cout << steps[i] << ": " << (n == i ? "true" : "false") << std::endl;
323  }
324  void debug_schedule() const
325  {
326  for (unsigned i = 0; i < num_steps; i++)
327  std::cout << steps[i] << std::endl;
328  }
329 
330 private:
331  Date start_;
332  Date end_;
333  StepUnit simulation_unit_;
334  unsigned simulation_num_units_;
335  std::vector<Step> steps;
336  unsigned num_steps;
337 
342  void increase_date(Date& date)
343  {
344  if (simulation_unit_ == StepUnit::Day) {
345  date.increased_by_days(simulation_num_units_);
346  }
347  else if (simulation_unit_ == StepUnit::Week) {
348  for (unsigned i = 0; i < simulation_num_units_; i++) {
349  date.increased_by_week();
350  }
351  }
352  else if (simulation_unit_ == StepUnit::Month) {
353  for (unsigned i = 0; i < simulation_num_units_; i++) {
354  date.increased_by_month();
355  }
356  }
357  }
358 };
359 
372 unsigned
373 simulation_step_to_action_step(const std::vector<bool>& action_schedule, unsigned step)
374 {
375  std::vector<unsigned> indices(action_schedule.size());
376  unsigned idx = 0;
377  for (unsigned i = 0; i < action_schedule.size(); i++) {
378  indices[i] = idx;
379  if (action_schedule[i])
380  ++idx;
381  }
382  return indices.at(step);
383 }
384 
390 unsigned get_number_of_scheduled_actions(const std::vector<bool>& action_schedule)
391 {
392  return std::count(action_schedule.begin(), action_schedule.end(), true);
393 }
394 
406 inline std::vector<bool> schedule_from_string(
407  const Scheduler& scheduler, const std::string& frequency, unsigned n = 0)
408 {
409  StepUnit sim_unit;
410  unsigned sim_n;
411  std::invalid_argument exception(
412  "Output frequency and simulation step are incompatible");
413  std::tie(sim_n, sim_unit) = scheduler.get_step_length();
414  if (!frequency.empty()) {
415  if (frequency == "final_step")
416  return scheduler.schedule_action_end_of_simulation();
417  else if (frequency == "year" || frequency == "yearly")
418  return scheduler.schedule_action_end_of_year();
419  else if (frequency == "month" || frequency == "monthly")
420  return scheduler.schedule_action_monthly();
421  else if (frequency == "week" || frequency == "weekly") {
422  if (sim_unit == StepUnit::Day) {
423  if (sim_n == 1)
424  return scheduler.schedule_action_nsteps(7);
425  else if (sim_n == 7)
426  return scheduler.schedule_action_nsteps(1);
427  else
428  throw exception;
429  }
430  else if (sim_unit == StepUnit::Week) {
431  if (sim_n == 1)
432  return scheduler.schedule_action_nsteps(1);
433  else
434  throw exception;
435  }
436  throw exception;
437  }
438  else if (frequency == "day" || frequency == "daily") {
439  if (sim_unit == StepUnit::Day && sim_n == 1)
440  return scheduler.schedule_action_nsteps(1);
441  else
442  throw exception;
443  }
444  else if (frequency == "every_n_steps" && n > 0)
445  return scheduler.schedule_action_nsteps(n);
446  else
447  throw std::invalid_argument("Invalid value of output frequency");
448  }
449  else {
450  std::vector<bool> v(scheduler.get_num_steps(), false);
451  return v;
452  }
453 }
454 
455 } // namespace pops
456 
457 #endif // POPS_SCHEDULING_HPP
pops::Date
Representation and manipulation of a date for the simulation.
Definition: date.hpp:32
pops::Scheduler::schedule_action_end_of_simulation
std::vector< bool > schedule_action_end_of_simulation() const
Schedule an action at the end of simulation.
Definition: scheduling.hpp:242
pops::StepUnit::Week
@ Week
pops::Date::month
int month() const
Definition: date.hpp:66
pops::Step::end_date
Date end_date() const
Definition: scheduling.hpp:47
pops::step_unit_enum_from_string
StepUnit step_unit_enum_from_string(const std::string &text)
Get step enum from string.
Definition: scheduling.hpp:79
indices
Definition: utils.hpp:97
pops::schedule_from_string
std::vector< bool > schedule_from_string(const Scheduler &scheduler, const std::string &frequency, unsigned n=0)
Get output (export) schedule based on frequency string ("year", "month", "week", "day",...
Definition: scheduling.hpp:406
pops::Scheduler::schedule_action_nsteps
std::vector< bool > schedule_action_nsteps(unsigned n_steps) const
Schedule action every N simulation steps,.
Definition: scheduling.hpp:258
date.hpp
pops::Step::operator<<
friend std::ostream & operator<<(std::ostream &os, const Step &step)
Definition: scheduling.hpp:57
pops::Scheduler::schedule_spread
std::vector< bool > schedule_spread(const Season &season) const
Schedule spread.
Definition: scheduling.hpp:178
pops::Scheduler::debug_schedule
void debug_schedule(std::vector< bool > &schedule) const
Prints schedule for debugging purposes.
Definition: scheduling.hpp:313
pops::get_number_of_scheduled_actions
unsigned get_number_of_scheduled_actions(const std::vector< bool > &action_schedule)
Returns how many actions are scheduled.
Definition: scheduling.hpp:390
pops::Scheduler::debug_schedule
void debug_schedule(unsigned n) const
Definition: scheduling.hpp:319
pops::Scheduler
Definition: scheduling.hpp:94
pops::Season
Holds beginning and end of a season and decides what is in season.
Definition: date.hpp:473
pops::Date::increased_by_month
void increased_by_month()
Definition: date.hpp:362
pops::Scheduler::get_step
Step get_step(unsigned index) const
Get step based on index.
Definition: scheduling.hpp:160
pops::Date::increased_by_days
void increased_by_days(int num_days)
Increases the date by the num_days (specified by the user) except on the last timestep of the year,...
Definition: date.hpp:287
pops::Scheduler::Scheduler
Scheduler(const Date &start, const Date &end, StepUnit simulation_unit, unsigned simulation_num_units)
Scheduler creates a vector of simulation steps based on start, end date, unit and number of units.
Definition: scheduling.hpp:111
pops::Date::is_last_day_of_month
bool is_last_day_of_month()
Definition: date.hpp:186
pops::Date::day
int day() const
Definition: date.hpp:74
pops
Definition: cauchy_kernel.hpp:25
pops::Date::subtract_day
void subtract_day()
Subtract 1 day from a date.
Definition: date.hpp:398
pops::simulation_step_to_action_step
unsigned simulation_step_to_action_step(const std::vector< bool > &action_schedule, unsigned step)
Converts simulation step to step of actions.
Definition: scheduling.hpp:373
pops::Scheduler::schedule_action_date
unsigned schedule_action_date(const Date &date) const
Schedule action at a specific date (not repeated action).
Definition: scheduling.hpp:301
pops::Step::Step
Step(Date start_date, Date end_date)
Definition: scheduling.hpp:41
pops::Scheduler::schedule_action_monthly
std::vector< bool > schedule_action_monthly() const
Schedule action at the end of each month.
Definition: scheduling.hpp:278
pops::Scheduler::schedule_action_end_of_year
std::vector< bool > schedule_action_end_of_year() const
Schedule an action at the end of each year, e.g.
Definition: scheduling.hpp:224
pops::Date::increased_by_week
void increased_by_week()
Increases the date by one week (7 days) except on the last week of the year, which is increased by 8 ...
Definition: date.hpp:327
pops::Scheduler::get_step_length
std::tuple< unsigned, StepUnit > get_step_length() const
Get length of simulation step as number of units and unit type.
Definition: scheduling.hpp:168
pops::Date::year
int year() const
Definition: date.hpp:70
pops::Scheduler::schedule_action_yearly
std::vector< bool > schedule_action_yearly(int month, int day) const
Schedule an action at certain date each year, e.g.
Definition: scheduling.hpp:203
pops::StepUnit::Month
@ Month
pops::Step::start_date
Date start_date() const
Definition: scheduling.hpp:43
pops::StepUnit
StepUnit
Enum for step unit.
Definition: scheduling.hpp:66
pops::StepUnit::Day
@ Day
pops::Step
Representation and manipulation of a date for the simulation.
Definition: scheduling.hpp:38
pops::operator<<
std::ostream & operator<<(std::ostream &os, const Date &d)
Definition: date.hpp:109
pops::Scheduler::get_num_steps
unsigned get_num_steps() const
Get number of simulation steps.
Definition: scheduling.hpp:152
pops::Season::month_in_season
bool month_in_season(int month) const
Decides if a month is in season or not.
Definition: date.hpp:482
pops::Scheduler::debug_schedule
void debug_schedule() const
Definition: scheduling.hpp:324