Utopia  2
Framework for studying models of complex & adaptive systems.
model.hh
Go to the documentation of this file.
1 #ifndef UTOPIA_CORE_MODEL_HH
2 #define UTOPIA_CORE_MODEL_HH
3 
4 #include <algorithm>
5 
6 #include "exceptions.hh"
7 #include "signal.hh"
8 #include "ostream.hh"
9 #include "logging.hh"
10 #include "space.hh"
11 #include "parallel.hh"
12 
13 #include "../data_io/hdffile.hh"
14 #include "../data_io/hdfgroup.hh"
15 #include "../data_io/cfg_utils.hh"
16 #include "../data_io/monitor.hh"
17 #include "../data_io/data_manager/data_manager.hh"
18 #include "../data_io/data_manager/factory.hh"
19 
20 
21 namespace Utopia {
22 
30 enum class WriteMode {
32 
38  basic,
39 
41 
48  managed,
49 
51 
57  manual,
58 
60  off
61 };
62 
65 
66 
68 
81 template<
82  typename RNGType=DefaultRNG,
83  WriteMode data_write_mode=DefaultWriteMode,
84  typename SpaceType=DefaultSpace,
85  typename ConfigType=Utopia::DataIO::Config,
86  typename DataGroupType=Utopia::DataIO::HDFGroup,
87  typename DataSetType=Utopia::DataIO::HDFDataset,
88  typename TimeType=std::size_t,
89  typename MonitorType=Utopia::DataIO::Monitor,
90  typename MonitorManagerType=Utopia::DataIO::MonitorManager
91  >
92 struct ModelTypes {
93  using RNG = RNGType;
94  static constexpr WriteMode write_mode = data_write_mode;
95  using Space = SpaceType;
96  using Config = ConfigType;
97  using DataGroup = DataGroupType;
98  using DataSet = DataSetType;
99  using Time = TimeType;
100  using Monitor = MonitorType;
101  using MonitorManager = MonitorManagerType;
102  using Level = std::size_t;
103 };
104 
105 
107 
110 template<class Derived, typename ModelTypes>
111 class Model
112 {
113 public:
114  // -- Data types uses throughout the model class --------------------------
116  using Config = typename ModelTypes::Config;
117 
120 
123 
125  using DataSet = typename ModelTypes::DataSet;
126 
128  using RNG = typename ModelTypes::RNG;
129 
131  using Space = typename ModelTypes::Space;
132 
134  using Time = typename ModelTypes::Time;
135 
137  using Monitor = typename ModelTypes::Monitor;
138 
141 
143  using Level = typename ModelTypes::Level;
144 
145 
146 protected:
147  // -- Member declarations -------------------------------------------------
149  const std::string _name;
150 
152  const std::string _full_name;
153 
155  const Level _level;
156 
158  const Config _cfg;
159 
161  const std::shared_ptr<RNG> _rng;
162 
164  const std::shared_ptr<spdlog::logger> _log;
165 
167  std::shared_ptr<Space> _space;
168 
171 
174 
176  const std::shared_ptr<DataGroup> _hdfgrp;
177 
180 
183 
186 
189 
191 
195 
196 private:
197  // .. Construction helpers ................................................
198 
200 
202  template<class Parent>
203  auto setup_logger(const Parent& parent_model) const {
204  auto log = spdlog::stdout_color_mt(
205  parent_model.get_logger()->name() + "." + _name
206  );
207 
208  // Set this model instance's log level
209  if (_cfg["log_level"]) {
210  // Via value given in configuration
211  const auto lvl = get_as<std::string>("log_level", _cfg);
212  log->debug("Setting log level to '{}' ...", lvl);
213  log->set_level(spdlog::level::from_str(lvl));
214  }
215  else {
216  // No config value given; use the level of the parent's logger
217  log->set_level(parent_model.get_logger()->level());
218  }
219 
220  log->info("Model logger initialized.");
221  return log;
222  }
223 
225  auto setup_space() const {
226  if (_cfg["space"]) {
227  auto space = std::make_shared<Space>(_cfg["space"]);
228  _log->info(
229  "Using {}periodic space with extent {}.",
230  space->periodic ? "" : "non-", Utils::str(space->extent)
231  );
232  return space;
233  }
234  else {
235  _log->debug(
236  "No model-level space configured; using default space."
237  );
238  return std::make_shared<Space>();
239  }
240  }
241 
242 
243 public:
244  // -- Constructor ---------------------------------------------------------
245 
247 
269  template < class ParentModel, class... WriterArgs >
271  const std::string& name,
272  const ParentModel& parent_model,
273  const Config& custom_cfg = {},
274  std::tuple< WriterArgs... > w_args = {},
275  const DataIO::Default::DefaultDecidermap< Derived >&
276  w_deciders = DataIO::Default::default_deciders< Derived >,
277  const DataIO::Default::DefaultTriggermap< Derived >&
278  w_triggers = DataIO::Default::default_triggers< Derived >
279  )
280  :
281  // First thing: setup name and position within model hierarchy
282  _name(name),
283  _full_name(parent_model.get_full_name() + "." + name),
284  _level(parent_model.get_level() + 1),
285 
286  // Retrieve the appropriate configuration entry from the parent model
287  _cfg(custom_cfg.size() ? custom_cfg
288  : get_as<Config>(_name, parent_model.get_cfg())),
289 
290  // Construct infrastructure objects using information from parent
291  _rng(parent_model.get_rng()),
292  _log(setup_logger(parent_model)),
293 
294  // Determine space and time
295  _space(setup_space()),
296  _time(0),
297  _time_max(_level == 1 ? parent_model.get_time_max()
298  : get_as<Time>("num_steps", _cfg,
299  std::numeric_limits<Time>::max())),
300 
301  // Extract the other information from the parent model object
302  _hdfgrp(parent_model.get_hdfgrp()->open_group(_name)),
303  _write_start(get_as<Time>("write_start", _cfg,
304  parent_model.get_write_start())),
305  _write_every(get_as<Time>("write_every", _cfg,
306  parent_model.get_write_every())),
307 
308  // Set up the monitor, using the parent model's monitor to place it in
309  // a hierarchy equivalent to the model hierarchy
310  _monitor(_name, parent_model.get_monitor()),
311 
312  // Default-construct the data maanger; only used if needed, see below.
313  _datamanager()
314  {
315  // Provide some information, also depending on write mode
316  _log->info("Model base constructor for '{}' finished.", _name);
317  _log->info(" full_name: {}", _full_name);
318  _log->info(" level: {}", _level);
319  _log->info(" time_max: {:7d}", _time_max);
320 
321  if constexpr (_write_mode == WriteMode::basic) {
322  _log->info(" write_mode: {:>7s}", "basic");
323  _log->info(" write_start: {:7d}", _write_start);
324  _log->info(" write_every: {:7d}", _write_every);
325  _log->info(" #writes: {:7d}", get_remaining_num_writes());
326 
327  // Store relevant info in base group attributes
328  _hdfgrp->add_attribute("write_mode", "basic");
329  _hdfgrp->add_attribute("write_start", _write_start);
330  _hdfgrp->add_attribute("write_every", _write_every);
331  _hdfgrp->add_attribute("time_max", _time_max);
332  }
333  else if constexpr (_write_mode == WriteMode::manual) {
334  _log->info(" write_mode: {:>7s}", "manual");
335  _hdfgrp->add_attribute("write_mode", "manual");
336  }
337  else if constexpr (_write_mode == WriteMode::off) {
338  _log->info(" write_mode: {:>7s}", "off");
339  _hdfgrp->add_attribute("write_mode", "off");
340  }
341  else if constexpr (_write_mode == WriteMode::managed) {
342  static_assert(sizeof...(WriterArgs) > 0,
343  "No arguments to construct write_tasks given!");
344 
345  _log->info(" write_mode: {:>7s}", "managed");
346 
347  // Store relevant info in base group attributes
348  _hdfgrp->add_attribute("write_mode", "managed");
349 
350  // Some convenience key checks for nicer error messages.
351  if (not _cfg["data_manager"]) {
352  throw KeyError("data_manager", _cfg,
353  "Cannot set up DataManager!");
354  }
355  const auto dm_cfg = _cfg["data_manager"];
356 
357  if (not dm_cfg["tasks"]) {
358  throw KeyError("tasks", dm_cfg,
359  "Cannot set up DataManager tasks!");
360  }
361  if (not dm_cfg["deciders"]) {
362  throw KeyError("deciders", dm_cfg,
363  "Cannot set up DataManager deciders!");
364  }
365  if (not dm_cfg["triggers"]) {
366  throw KeyError("triggers", dm_cfg,
367  "Cannot set up DataManager triggers!");
368  }
369 
370  _log->info("Invoking DataManager task factory ...");
371 
372  _datamanager = DataIO::DataManagerFactory<Derived>()(
373  dm_cfg, w_args, w_deciders, w_triggers
374  );
375 
376  _log->info("DataManager set up with {} task(s), {} decider(s), "
377  "and {} trigger(s).", _datamanager.get_tasks().size(),
378  _datamanager.get_deciders().size(),
379  _datamanager.get_triggers().size());
380  }
381  }
382 
383 
384 
385  // -- Getters -------------------------------------------------------------
386 
388  const std::shared_ptr<Space>& get_space() const {
389  return _space;
390  }
391 
393  Time get_time() const {
394  return _time;
395  }
396 
398 
403  Time get_time_max() const {
404  if (_time_max == std::numeric_limits<Time>::max()) {
405  this->_log->warn("Accessing the `time_max` of (sub-)model {} with "
406  "unlimited value!", this->get_full_name());
407  }
408  return _time_max;
409  }
410 
412  Config get_cfg() const {
413  return _cfg;
414  }
415 
417  std::string get_name() const {
418  return _name;
419  }
420 
422  std::string get_full_name() const {
423  return _full_name;
424  }
425 
427  std::shared_ptr<DataGroup> get_hdfgrp() const {
428  return _hdfgrp;
429  }
430 
433  return _write_start;
434  }
435 
438  return _write_every;
439  }
440 
443  return _datamanager;
444  }
445 
447 
452  hsize_t get_remaining_num_writes() const {
453  static_assert(
458  );
459 
460  if constexpr (_write_mode == WriteMode::basic) {
461  if (_time_max == std::numeric_limits<hsize_t>::max()) {
462  return std::numeric_limits<hsize_t>::max();
463  }
464  return ( (_time_max - std::max(_time, _write_start))
465  / _write_every) + 1;
466  }
467  else if constexpr (_write_mode == WriteMode::manual) {
468  if (_time_max - _time == std::numeric_limits<hsize_t>::max()) {
469  return std::numeric_limits<hsize_t>::max();
470  }
471  return _time_max - _time + 1;
472  }
473  else {
474  return 0;
475  }
476 
477  }
478 
480  std::shared_ptr<RNG> get_rng() const {
481  return _rng;
482  }
483 
485  std::shared_ptr<spdlog::logger> get_logger() const {
486  return _log;
487  }
488 
491  return _monitor;
492  }
493 
495  std::shared_ptr<MonitorManager> get_monitor_manager() const {
496  return _monitor.get_monitor_manager();
497  }
498 
500  Level get_level() const {
501  return _level;
502  }
503 
504 
505  // -- Simulation control --------------------------------------------------
507 
509  virtual void prolog () {
510  __prolog();
511  }
512 
514 
516  virtual void epilog () {
517  __epilog();
518  }
519 
521 
527  void iterate () {
528  // -- Perform the simulation step
529  __perform_step();
530  increment_time();
531 
532  // -- Monitoring
533  /* If the model is at the first hierarchical level, check whether the
534  * monitor entries should be collected and emitted. This leads to a
535  * flag being set in the monitor manager, such that the submodels do
536  * not have to do the check against the timer as well and that all
537  * collected data stems from the same time step.
538  */
539  if (_level == 1) {
540  _monitor.get_monitor_manager()->check_timer();
541  __monitor();
542 
543  // If enabled for this step, perform the emission of monitor data
544  // NOTE At this point, we can be sure that all submodels have
545  // already run, because their iterate functions were called
546  // in the perform_step of the level 1 model.
547  _monitor.get_monitor_manager()->emit_if_enabled();
548  }
549  else {
550  __monitor();
551  }
552 
553  // -- Data output
554  if constexpr (_write_mode == WriteMode::basic) {
555  if ( (_time >= _write_start)
556  and (_time - _write_start) % _write_every == 0) {
557  __write_data();
558  }
559  }
560  else if constexpr (_write_mode == WriteMode::manual) {
561  __write_data();
562  }
563  else if constexpr (_write_mode == WriteMode::managed) {
564  _datamanager(static_cast<Derived&>(*this));
565  }
566 
567  if (_level == 1) {
568  _log->debug("Finished iteration: {:7d} / {:d}", _time, _time_max);
569  }
570  else {
571  _log->debug("Finished iteration: {:7d}", _time);
572  }
573  }
574 
576 
581  void run () {
582  if (_level > 1 and get_time_max() == std::numeric_limits<Time>::max()) {
583  throw std::runtime_error(fmt::format("Cannot perform run on "
584  "(sub-)model {} with unlimited `time_max`. You need to provide "
585  "`num_steps` to the model's configuration in order to use the "
586  "`run` method. See the nested models guide for further "
587  "information!", get_full_name()));
588  }
589 
590  // First, attach the signal handler, such that the while loop below can
591  // be left upon receiving of a signal.
593 
594  // call the prolog of the model
595  prolog();
596 
597  // Now, let's go repeatedly iterate the model ...
598  _log->info("Running from current time {} to {} ...",
599  _time, _time_max);
600 
601  while (_time < _time_max) {
602  iterate();
603 
604  if (stop_now.load()) {
605  const auto signum = received_signum.load();
606 
607  if (signum == SIGUSR1) {
608  _log->warn("The stop condition for this run is fulfilled. "
609  "Not iterating further ...");
610  }
611  else {
612  _log->warn("Was told to stop. Not iterating further ...");
613  }
614 
615  _log->info("Invoking epilog ...");
616  epilog();
617 
618  throw GotSignal(received_signum.load());
619  }
620  }
621 
622  _log->info("Run finished. Current time: {}", _time);
623 
624  // call the epilog of the model
625  epilog();
626  }
627 
628 
629 protected:
630  // -- Functions requiring/allowing user-defined implementations -----------
631 
633  void __perform_step () {
634  impl().perform_step();
635  }
636 
638  /* The child implementation of this function will only be called if the
639  * monitor manager has determined that an emission will occur, because it
640  * only makes sense to collect data if it will be emitted in this step.
641  */
642  void __monitor () {
643  if (_monitor.get_monitor_manager()->emit_enabled()) {
644  // Perform actions that should only happen once by the monitor at
645  // the highest level of the model hierarchy.
646  if (_level == 1){
647  // Supply the global time. When reaching this point, all sub-
648  // models will also have reached this time.
649  _monitor.get_monitor_manager()->set_time_entries(_time,
650  _time_max);
651  // This method also sets the other top level entries
652  }
653 
654  // Call the child's implementation of the monitor functions.
655  impl().monitor();
656  }
657  }
658 
660  void __write_data () {
661  _log->trace("Calling write_data ...");
662  impl().write_data();
663  }
664 
667  // Select the required WriteMode
668  // Decide on whether the initial state needs to be written
669  if constexpr (_write_mode == WriteMode::basic) {
670  if (_write_start == _time) {
671  _log->info("Writing initial state ...");
672  __write_data();
673  }
674  }
675  else if constexpr (_write_mode == WriteMode::manual) {
676  __write_data();
677  }
678  else if constexpr (_write_mode == WriteMode::managed) {
679  _datamanager(static_cast<Derived&>(*this));
680  }
681 
682  }
683 
685 
687  void increment_time (const Time dt=1) {
688  _time += dt;
689  }
690 
692 
695  void __prolog () {
697 
698  this->_log->debug("Prolog finished.");
699  }
700 
702 
705  void __epilog () {
706  this->_log->debug("Epilog finished.");
707  }
708 
709 
710  // -- CRTP ----------------------------------------------------------------
712  Derived& impl () {
713  return static_cast<Derived&>(*this);
714  }
715 
717  const Derived& impl () const {
718  return static_cast<const Derived&>(*this);
719  }
720 
721 
722 public:
723  // -- Convenience functions -----------------------------------------------
724 
751  std::shared_ptr<DataSet>
752  create_dset(const std::string name,
753  const std::shared_ptr<DataGroup>& hdfgrp,
754  std::vector<hsize_t> add_write_shape,
755  const std::size_t compression_level=1,
756  const std::vector<hsize_t> chunksize={})
757  {
758  _log->debug("Creating dataset '{}' in group '{}' ...",
759  name, hdfgrp->get_path());
760 
761  // Calculate the number of time steps to be written
762  const hsize_t num_write_ops = get_remaining_num_writes();
763 
764  // Calculate the shape of the dataset
765  add_write_shape.insert(add_write_shape.begin(), num_write_ops);
766  auto capacity = add_write_shape;
767 
768  // Create the dataset and return it.
769  const auto dset = hdfgrp->open_dataset(name, capacity,
770  chunksize, compression_level);
771  _log->debug("Successfully created dataset '{}'.", name);
772 
773  // Write further attributes, if not specifically suppressed
774  if (get_as<bool>("write_dim_labels_and_coords", _cfg, true)) {
775  // We know that dimension 0 is the time dimension. Add the
776  // attributes that specify dimension names and coordinates:
777  dset->add_attribute("dim_name__0", "time");
778  dset->add_attribute("coords_mode__time", "start_and_step");
779  dset->add_attribute("coords__time",
780  std::vector<std::size_t>{_write_start,
781  _write_every});
782  _log->debug("Added time dimension labels and coordinates to "
783  "dataset '{}'.", name);
784  }
785 
786  return dset;
787  }
788 
814  std::shared_ptr<DataSet>
815  create_dset(const std::string name,
816  const std::vector<hsize_t> add_write_shape,
817  const std::size_t compression_level=1,
818  const std::vector<hsize_t> chunksize={})
819  {
820  // Forward to the main create_dset function
821  return create_dset(name,
822  _hdfgrp, // The base group for this model
823  add_write_shape, compression_level, chunksize);
824  }
825 
847  template<class CellManager>
848  std::shared_ptr<DataSet>
849  create_cm_dset(const std::string name,
850  const CellManager& cm,
851  const std::size_t compression_level=1,
852  const std::vector<hsize_t> chunksize={})
853  {
854  // Forward to the main create_dset function
855  const auto dset = create_dset(
856  name,
857  _hdfgrp,
858  {cm.cells().size()}, // -> 2D dataset
859  compression_level,
860  chunksize
861  );
862 
863  // Set attributes to mark this dataset as containing grid data
864  dset->add_attribute("content", "grid");
865 
866  // Need further attributes to denote the grid structure, size, etc.
867  const auto grid_structure = cm.grid()->structure_name();
868  dset->add_attribute("grid_structure", grid_structure);
869  dset->add_attribute("grid_shape", cm.grid()->shape());
870  dset->add_attribute("space_extent", cm.grid()->space()->extent);
871  dset->add_attribute("periodic_space", cm.grid()->space()->periodic);
872 
873  if (grid_structure == "hexagonal") {
874  // ... some additional info is needed, which is dependent on the
875  // way the HexagonalGrid maps cells
876  dset->add_attribute("coordinate_mode", "offset");
877  dset->add_attribute("offset_mode", "even");
878  dset->add_attribute("pointy_top", true);
879  }
880 
881  // The CellManager uses "column-style" index ordering, also called
882  // "Fortran-style". This is relevant for assigning the correct IDs.
883  dset->add_attribute("index_order", "F");
884 
885  _log->debug("Added attributes to dataset '{}' to mark it as storing "
886  "grid data.", name);
887 
888  // Write additional attributes, if not specifically suppressed.
889  if (get_as<bool>("write_dim_labels_and_coords", _cfg, true)) {
890  // We know that the dimensions here refer to (time, cell ids).
891  // The time information is already added in create_dset; only need
892  // to add the ID information here
893  dset->add_attribute("dim_name__1", "ids");
894 
895  // For ids, the dimensions are trivial
896  dset->add_attribute("coords_mode__ids", "range");
897  dset->add_attribute("coords__ids",
898  std::vector<std::size_t>{cm.cells().size()});
899 
900  _log->debug("Added cell ID dimension labels and coordinates to "
901  "dataset '{}'.", name);
902  }
903 
904  return dset;
905  }
906 
907 
937  template<class AgentManager>
938  std::shared_ptr<DataSet>
939  create_am_dset(const std::string name,
940  const AgentManager& am,
941  const std::size_t compression_level=1,
942  const std::vector<hsize_t> chunksize = {})
943  {
944  // Forward to the main create_dset function
945  const auto dset = create_dset(
946  name,
947  _hdfgrp,
948  {am.agents().size()}, // --> 2D: time, agents
949  compression_level,
950  chunksize
951  );
952 
953  // Set attribute to store the agent managers' extent of space
954  dset->add_attribute("space_extent", am.space()->extent);
955  _log->debug("Added attribute to dataset '{}' to store space extent",
956  name);
957 
958  // Write additional attributes, if not specifically suppressed.
959  if (get_as<bool>("write_dim_labels_and_coords", _cfg, true)) {
960  // We know that the dimensions here refer to (time, agent ids). The
961  // time information is already added in create_dset; add only ID
962  // information here. Note that this assumes trivial and constant
963  // agent IDs!
964  dset->add_attribute("dim_name__1", "ids");
965 
966  // For ids, the dimensions are trivial
967  dset->add_attribute("coords_mode__ids", "trivial");
968 
969  _log->debug("Added agent index dimension labels and coordinates "
970  "to dataset '{}'.", name);
971  }
972 
973  return dset;
974  }
975 
976 
977 private:
978  // -- Private Helper Methods ----------------------------------------------
979 
981 
990  void __attach_sig_handlers() const {
991  _log->debug("Attaching signal handlers for SIGINT and SIGTERM ...");
992 
993  attach_signal_handler(SIGINT);
994  attach_signal_handler(SIGTERM);
995 
996  _log->debug("Attaching signal handler for stop conditions, triggered "
997  "by SIGUSR1 ({:d}) ...", SIGUSR1);
998  attach_signal_handler(SIGUSR1);
999 
1000  _log->debug("Signal handlers attached.");
1001  }
1002 };
1003 
1004 
1005 
1007 
1015 template<typename RNG=DefaultRNG>
1017 {
1018 protected:
1020 
1023 
1026 
1027  using Time = std::size_t;
1028  using Level = std::size_t;
1029 
1031  const Level _level;
1032 
1034  const Config _cfg;
1035 
1037  const std::shared_ptr<HDFFile> _hdffile;
1038 
1040  const std::shared_ptr<RNG> _rng;
1041 
1043 
1046  const std::shared_ptr<spdlog::logger> _log;
1047 
1049  const std::shared_ptr<MonitorManager> _monitor_mgr;
1050 
1053 
1054 public:
1056 
1063  PseudoParent (const std::string cfg_path)
1064  :
1065  // The hierarchical level is 0
1066  _level(0),
1067  // Initialize the config node from the path to the config file
1068  _cfg(YAML::LoadFile(cfg_path)),
1069  // Create a file at the specified output path and store the shared pointer
1070  _hdffile(std::make_shared<HDFFile>(
1071  get_as<std::string>("output_path", _cfg),
1072  get_as<std::string>("output_file_mode", _cfg, "w")
1073  )),
1074  // Initialize the RNG from a seed
1075  _rng(std::make_shared<RNG>(get_as<int>("seed", _cfg))),
1076  // And initialize the root logger at warning level
1077  _log(Utopia::init_logger("root", spdlog::level::warn, false)),
1078  // Create a monitor manager and a root monitor
1079  _monitor_mgr(std::make_shared<MonitorManager>(
1080  get_as<double>("monitor_emit_interval", _cfg))
1081  ),
1083  {
1084  setup_loggers(); // global loggers
1085  set_log_level(); // this log level
1087 
1088  _log->info("Initialized PseudoParent from config file");
1089  _log->debug("cfg_path: {}", cfg_path);
1090  _log->debug("output_path: {}", get_as<std::string>("output_path",
1091  _cfg));
1092  _log->debug("RNG seed: {}", get_as<int>("seed", _cfg));
1093  _log->debug("emit_interval: {}s",
1094  get_as<double>("monitor_emit_interval", _cfg));
1095  }
1096 
1097 
1099 
1106  PseudoParent (const std::string cfg_path,
1107  const std::string output_path,
1108  const int seed=42,
1109  const std::string output_file_mode="w",
1110  const double emit_interval=5.)
1111  :
1112  // The hierarchical level is 0
1113  _level(0),
1114  // Initialize the config node from the path to the config file
1115  _cfg(YAML::LoadFile(cfg_path)),
1116  // Create a file at the specified output path
1117  _hdffile(std::make_shared<HDFFile>(output_path, output_file_mode)),
1118  // Initialize the RNG from a seed
1119  _rng(std::make_shared<RNG>(seed)),
1120  // And initialize the root logger at warning level
1121  _log(Utopia::init_logger("root", spdlog::level::warn, false)),
1122  // Create a monitor manager and a "root" monitor
1123  _monitor_mgr(std::make_shared<MonitorManager>(emit_interval)),
1125  {
1126  setup_loggers(); // global loggers
1127  set_log_level(); // this log level
1129 
1130  _log->info("Initialized PseudoParent from parameters");
1131  _log->debug("cfg_path: {}", cfg_path);
1132  _log->debug("output_path: {} (mode: {})",
1133  output_path, output_file_mode);
1134  _log->debug("RNG seed: {}", seed);
1135  _log->debug("emit_interval: {}", emit_interval);
1136  }
1137 
1138 
1139 
1140  // -- Getters -- //
1141 
1143  Level get_level() const {
1144  return _level;
1145  }
1146 
1148  std::string get_full_name() const {
1149  return "";
1150  }
1151 
1153  Config get_cfg() const {
1154  return _cfg;
1155  }
1156 
1158  std::shared_ptr<HDFFile> get_hdffile() const {
1159  return _hdffile;
1160  }
1161 
1163  std::shared_ptr<HDFGroup> get_hdfgrp() const {
1164  return _hdffile->get_basegroup();
1165  }
1166 
1169  return get_as<Time>("write_start", _cfg, 0);
1170  }
1171 
1174  return get_as<Time>("write_every", _cfg, 1);
1175  }
1176 
1178  std::shared_ptr<RNG> get_rng() const {
1179  return _rng;
1180  }
1181 
1183  std::shared_ptr<spdlog::logger> get_logger() const {
1184  return _log;
1185  }
1186 
1188 
1191  Time get_time_max() const {
1192  return get_as<Time>("num_steps", _cfg);
1193  }
1194 
1196  std::shared_ptr<MonitorManager> get_monitor_manager() const {
1197  return _monitor_mgr;
1198  }
1199 
1201  const Monitor& get_monitor() const {
1202  return _monitor;
1203  }
1204 
1205 
1206 private:
1207 
1209 
1217  void setup_loggers () const {
1219  spdlog::level::from_str(
1220  get_as<std::string>("core", _cfg["log_levels"])
1221  ),
1222  spdlog::level::from_str(
1223  get_as<std::string>("data_io", _cfg["log_levels"])
1224  ),
1225  spdlog::level::from_str(
1226  get_as<std::string>("data_mngr", _cfg["log_levels"], "warn")
1227  ),
1228  get_as<std::string>("log_pattern", _cfg, "")
1229  );
1230  }
1231 
1233  void set_log_level () const {
1234  _log->set_level(
1235  spdlog::level::from_str(get_as<std::string>("model",
1236  _cfg["log_levels"]))
1237  );
1238  }
1239 };
1240 
1241 
1242 // end group Model
1247 } // namespace Utopia
1248 
1249 #endif // UTOPIA_CORE_MODEL_HH
The agent manager manages the agents living in a model.
Definition: agent_manager.hh:31
const std::shared_ptr< Space > & space() const
Return pointer to the space, for convenience.
Definition: agent_manager.hh:179
const AgentContainer< Agent > & agents() const
Return const reference to the managed agents.
Definition: agent_manager.hh:184
Manages a physical space, its grid discretization, and cells on that grid.
Definition: cell_manager.hh:41
const CellContainer< Cell > & cells() const
Return const reference to the managed CA cells.
Definition: cell_manager.hh:219
const std::shared_ptr< GridType > & grid() const
Return const reference to the grid.
Definition: cell_manager.hh:214
Manage different tasks of writing out data from a source in a uniform yet flexible way....
Definition: data_manager.hh:131
DeciderMap & get_deciders()
Get the container of decider objects.
Definition: data_manager.hh:471
TriggerMap & get_triggers()
Get the container of trigger objects.
Definition: data_manager.hh:493
TaskMap & get_tasks()
Get the container of task objects.
Definition: data_manager.hh:482
Class representing a HDFDataset, wich reads and writes data and attributes.
Definition: hdfdataset.hh:53
Class representing a HDF5 file.
Definition: hdffile.hh:62
Class represting a HDFGroup, an object analogous to a folder for HDFFiles.
Definition: hdfgroup.hh:41
The Monitor monitors entries that are emitted if a given time has passed.
Definition: monitor.hh:323
The MonitorManager manages the monitor entries and MonitorTimer.
Definition: monitor.hh:159
An exception for when the program should end due to handling of a signal.
Definition: exceptions.hh:51
Base class interface for Models using the CRT Pattern.
Definition: model.hh:112
void __prolog()
The default prolog of a model.
Definition: model.hh:695
DataManager get_datamanager() const
return the datamanager
Definition: model.hh:442
auto setup_logger(const Parent &parent_model) const
Constructs this models logger instance from the parent.
Definition: model.hh:203
auto setup_space() const
Constructs the Space from configuration or uses the default Space.
Definition: model.hh:225
typename ModelTypes::MonitorManager MonitorManager
Data type for the monitor manager.
Definition: model.hh:140
typename ModelTypes::Level Level
Data type for the hierarchical level.
Definition: model.hh:143
const std::shared_ptr< DataGroup > _hdfgrp
The HDF group this model instance should write its data to.
Definition: model.hh:176
void __write_initial_state()
Write the initial state.
Definition: model.hh:666
DataManager _datamanager
Manager object for handling data output; see DataManager.
Definition: model.hh:194
std::shared_ptr< DataSet > create_am_dset(const std::string name, const AgentManager &am, const std::size_t compression_level=1, const std::vector< hsize_t > chunksize={})
Create a dataset storing data from a AgentManager.
Definition: model.hh:939
Monitor _monitor
The monitor.
Definition: model.hh:188
void run()
Run the model from the current time to the maximum time.
Definition: model.hh:581
void iterate()
Iterate one (time) step of this model.
Definition: model.hh:527
void __attach_sig_handlers() const
Attaches signal handlers: SIGINT, SIGTERM, SIGUSR1.
Definition: model.hh:990
std::shared_ptr< spdlog::logger > get_logger() const
Return a pointer to the logger of this model.
Definition: model.hh:485
Monitor get_monitor() const
Return the monitor of this model.
Definition: model.hh:490
std::shared_ptr< DataSet > create_dset(const std::string name, const std::vector< hsize_t > add_write_shape, const std::size_t compression_level=1, const std::vector< hsize_t > chunksize={})
Create a new dataset within the model's base data group.
Definition: model.hh:815
const Time _write_start
First time at which write_data is called.
Definition: model.hh:182
std::shared_ptr< DataGroup > get_hdfgrp() const
Return a pointer to the HDF group this model stores data in.
Definition: model.hh:427
Time get_time_max() const
Return the maximum time possible for this model.
Definition: model.hh:403
std::shared_ptr< DataSet > create_cm_dset(const std::string name, const CellManager &cm, const std::size_t compression_level=1, const std::vector< hsize_t > chunksize={})
Create a dataset storing data from a CellManager.
Definition: model.hh:849
typename ModelTypes::DataSet DataSet
Data type that is used for storing data.
Definition: model.hh:125
const Config _cfg
Config node belonging to this model instance.
Definition: model.hh:158
const Time _write_every
How often to call write_data from iterate.
Definition: model.hh:185
Time _time
Model-internal current time stamp.
Definition: model.hh:170
typename ModelTypes::Space Space
Data type of the space this model resides in.
Definition: model.hh:131
const std::string _name
Name of the model instance.
Definition: model.hh:149
Derived & impl()
cast to the derived class
Definition: model.hh:712
std::shared_ptr< DataSet > create_dset(const std::string name, const std::shared_ptr< DataGroup > &hdfgrp, std::vector< hsize_t > add_write_shape, const std::size_t compression_level=1, const std::vector< hsize_t > chunksize={})
Create a new dataset within the given group.
Definition: model.hh:752
std::string get_full_name() const
Return the full name of this model within the model hierarchy.
Definition: model.hh:422
std::string get_name() const
Return the name of this model instance.
Definition: model.hh:417
Time get_time() const
Return the current time of this model.
Definition: model.hh:393
void __monitor()
Monitor information in the terminal.
Definition: model.hh:642
const Time _time_max
Model-internal maximum time stamp.
Definition: model.hh:173
void __perform_step()
Perform the computation of a step.
Definition: model.hh:633
Model(const std::string &name, const ParentModel &parent_model, const Config &custom_cfg={}, std::tuple< WriterArgs... > w_args={}, const DataIO::Default::DefaultDecidermap< Derived > &w_deciders=DataIO::Default::default_deciders< Derived >, const DataIO::Default::DefaultTriggermap< Derived > &w_triggers=DataIO::Default::default_triggers< Derived >)
Constructs a Model instance.
Definition: model.hh:270
void __write_data()
Write data; calls the implementation's write_data method.
Definition: model.hh:660
hsize_t get_remaining_num_writes() const
Return the number of remaining write_data calls this model will make.
Definition: model.hh:452
const Derived & impl() const
const cast to the derived interface
Definition: model.hh:717
virtual void epilog()
A function that is called after the last iteration of a model.
Definition: model.hh:516
Config get_cfg() const
Return the config node of this model.
Definition: model.hh:412
Time get_write_start() const
Return the parameter that controls when write_data is called first.
Definition: model.hh:432
std::shared_ptr< RNG > get_rng() const
Return a pointer to the shared RNG.
Definition: model.hh:480
Level get_level() const
Return the hierarchical level within the model hierarchy.
Definition: model.hh:500
const Level _level
The level within the model hierarchy.
Definition: model.hh:155
typename ModelTypes::DataGroup DataGroup
Data type that is used for storing datasets.
Definition: model.hh:122
virtual void prolog()
A function that is called before starting model iteration.
Definition: model.hh:509
void __epilog()
The default epilog of a model.
Definition: model.hh:705
typename ModelTypes::Time Time
Data type for the model time.
Definition: model.hh:134
typename ModelTypes::Config Config
Data type that holds the configuration.
Definition: model.hh:116
typename ModelTypes::Monitor Monitor
Data type for the monitor.
Definition: model.hh:137
typename ModelTypes::RNG RNG
Data type of the shared RNG.
Definition: model.hh:128
Time get_write_every() const
Return the parameter that controls how often write_data is called.
Definition: model.hh:437
const std::shared_ptr< Space > & get_space() const
Return the space this model resides in.
Definition: model.hh:388
std::shared_ptr< MonitorManager > get_monitor_manager() const
Get the monitor manager of the root model.
Definition: model.hh:495
const std::string _full_name
The full name within the model hierarchy.
Definition: model.hh:152
const std::shared_ptr< spdlog::logger > _log
The (model) logger.
Definition: model.hh:164
void increment_time(const Time dt=1)
Increment time.
Definition: model.hh:687
static constexpr WriteMode _write_mode
Which data-writing mode the base model should use.
Definition: model.hh:179
const std::shared_ptr< RNG > _rng
The RNG shared between models.
Definition: model.hh:161
std::shared_ptr< Space > _space
The space this model resides in.
Definition: model.hh:167
static void init(const DataIO::Config &cfg)
Initialize parallel features based on configuration setting.
Definition: parallel.hh:113
A class to use at the top level of the model hierarchy as a mock parent.
Definition: model.hh:1017
Utopia::DataIO::Config Config
Definition: model.hh:1019
Monitor _monitor
The monitor instance of this root model.
Definition: model.hh:1052
void set_log_level() const
Set the log level for the pseudo parent from the base_cfg.
Definition: model.hh:1233
std::size_t Level
Definition: model.hh:1028
std::shared_ptr< MonitorManager > get_monitor_manager() const
Return the monitor manager of this model.
Definition: model.hh:1196
std::shared_ptr< spdlog::logger > get_logger() const
Return a pointer to the logger of this model.
Definition: model.hh:1183
std::shared_ptr< HDFGroup > get_hdfgrp() const
Return a pointer to the HDF group, which is the base group of the file.
Definition: model.hh:1163
const Config _cfg
The config node.
Definition: model.hh:1034
Level get_level() const
Return the hierarchical level within the model hierarchy.
Definition: model.hh:1143
const std::shared_ptr< RNG > _rng
Pointer to a RNG that can be shared between models.
Definition: model.hh:1040
const std::shared_ptr< spdlog::logger > _log
Pointer to the logger of this (pseudo) model.
Definition: model.hh:1046
std::shared_ptr< RNG > get_rng() const
Return a pointer to the RNG.
Definition: model.hh:1178
Time get_time_max() const
The maximum time value as it can be found in the config.
Definition: model.hh:1191
std::shared_ptr< HDFFile > get_hdffile() const
Return a pointer to the HDF data file.
Definition: model.hh:1158
const std::shared_ptr< MonitorManager > _monitor_mgr
The monitor manager.
Definition: model.hh:1049
Time get_write_start() const
Return the parameter that controls when write_data is called first.
Definition: model.hh:1168
PseudoParent(const std::string cfg_path, const std::string output_path, const int seed=42, const std::string output_file_mode="w", const double emit_interval=5.)
Constructor that allows granular control over config parameters.
Definition: model.hh:1106
const Monitor & get_monitor() const
Return the monitor of this model.
Definition: model.hh:1201
PseudoParent(const std::string cfg_path)
Constructor that only requires path to a config file.
Definition: model.hh:1063
std::size_t Time
Definition: model.hh:1027
Time get_write_every() const
Return the parameter that controls how often write_data is called.
Definition: model.hh:1173
const Level _level
The hierarchical level.
Definition: model.hh:1031
void setup_loggers() const
Set up the global loggers with levels specified in the config file.
Definition: model.hh:1217
std::string get_full_name() const
Return the full name of the PseudoParent: an empty string.
Definition: model.hh:1148
const std::shared_ptr< HDFFile > _hdffile
Pointer to the HDF5 file where data is written to.
Definition: model.hh:1037
Config get_cfg() const
Return the config node of the Pseudo model, i.e. the root node.
Definition: model.hh:1153
YAML::Node Config
Type of a variadic dictionary-like data structure used throughout Utopia.
Definition: types.hh:71
ReturnType get_as(const std::string &key, const DataIO::Config &node)
This function is a wrapper around the yaml-cpp YAML::Node::as function.
Definition: cfg_utils.hh:158
void setup_loggers(const spdlog::level::level_enum level_core=spdlog::level::warn, const spdlog::level::level_enum level_data_io=spdlog::level::warn, const spdlog::level::level_enum level_data_mngr=spdlog::level::warn, const std::string &log_pattern="")
Set up and register the global loggers and set the global log pattern.
Definition: logging.hh:66
std::shared_ptr< spdlog::logger > init_logger(const std::string name, const spdlog::level::level_enum level, const bool throw_on_exist=true)
Initialize a logger with a certain name and log level.
Definition: logging.hh:31
WriteMode
How to write data in the models.
Definition: model.hh:30
constexpr WriteMode DefaultWriteMode
Alias for the default write mode.
Definition: model.hh:64
Space< 2 > DefaultSpace
The default Space object to be used throughout Utopia.
Definition: space.hh:220
@ off
The write_data method is never called.
@ manual
Fully manual: write_data method is always called.
@ managed
Use the DataManager to handle output.
@ basic
Basic writing features: write_start, write_every.
std::string str(T &&t)
Turn any object for which operator<< exists into a string. Mostly useful for logging data via spdlog ...
Definition: ostream.hh:164
Definition: agent.hh:11
std::atomic< bool > stop_now
The flag indicating whether to stop whatever is being done right now.
Definition: signal.hh:15
std::mt19937 DefaultRNG
Type of default random number generator.
Definition: types.hh:17
std::atomic< int > received_signum
The received signal value.
Definition: signal.hh:18
void attach_signal_handler(int signum, Handler &&handler)
Attaches a signal handler for the given signal via sigaction.
Definition: signal.hh:35
Definition: parallel.hh:235
Wrapper struct for defining model class data types.
Definition: model.hh:92
ConfigType Config
Definition: model.hh:96
TimeType Time
Definition: model.hh:99
static constexpr WriteMode write_mode
Definition: model.hh:94
std::size_t Level
Definition: model.hh:102
RNGType RNG
Definition: model.hh:93
MonitorManagerType MonitorManager
Definition: model.hh:101
DataSetType DataSet
Definition: model.hh:98
MonitorType Monitor
Definition: model.hh:100
SpaceType Space
Definition: model.hh:95
DataGroupType DataGroup
Definition: model.hh:97