Utopia 2
Framework for studying models of complex & adaptive systems.
Loading...
Searching...
No Matches
data_manager.hh
Go to the documentation of this file.
1#ifndef UTOPIA_DATAIO_DATA_MANAGER_HH
2#define UTOPIA_DATAIO_DATA_MANAGER_HH
3
4// stl includes for having shared_ptr, hashmap, vector, swap
5#include <algorithm>
6#include <memory>
7#include <stdexcept>
8#include <type_traits>
9#include <unordered_map>
10#include <vector>
11
12// utopia includes
13#include "../../core/logging.hh"
14#include "../../core/type_traits.hh"
15#include "../cfg_utils.hh"
16#include "defaults.hh"
17#include "utils.hh"
18
19namespace Utopia
20{
21namespace DataIO
22{
23
24using namespace Utopia::Utils;
25
26// Doxygen group for DataManager ++++++++++++++++++++++++++++++++++++++++++++++
68template < class TaskType,
69 class DeciderType,
70 class TriggerType,
79
129template < class Traits >
131{
132 public:
135
137 using Task = typename Traits::Task;
138
140 using Decider = typename Traits::Decider;
141
143 using Trigger = typename Traits::Trigger;
144
146 using ExecutionProcess = typename Traits::ExecutionProcess;
147
149 using TaskMap = std::unordered_map< std::string, std::shared_ptr< Task > >;
150
152 using OrderedTaskMap = std::map< std::string, std::shared_ptr< Task > >;
153
156 std::unordered_map< std::string, std::shared_ptr< Decider > >;
157
160 std::map< std::string, std::shared_ptr< Decider > >;
161
164 std::unordered_map< std::string, std::shared_ptr< Trigger > >;
165
168 std::map< std::string, std::shared_ptr< Trigger > >;
169
171 using AssocsMap =
172 std::unordered_map< std::string, std::vector< std::string > >;
173
174 protected:
178 std::shared_ptr< spdlog::logger > _log;
179
184
189
194
200
206
212
213 // .. Construction Helpers ................................................
214
215 template < typename ObjMap, typename KnownObjectsMap >
216 ObjMap
218 {
219 _log->debug("Setting up name -> object map from config node ...");
220
221 // Check whether the given configuration is valid
222 if (not cfg)
223 {
224 throw std::invalid_argument("Received a zombie node for the setup "
225 "of DataManager objects!");
226 }
227 else if (not cfg.IsMap())
228 {
229 throw std::invalid_argument("Expected a mapping for DataManager "
230 "object setup, got:\n" +
231 to_string(cfg));
232 }
233
234 // The name -> object map that is to be populated
235 ObjMap map;
236 _log->debug("Configuring DataManager objects ... (container size: {})",
237 known_objects.size());
238
239 // Go over the known objects and decide whether to retain them
240 // as they are or whether new objects need to be constructed from the
241 // known ones
242 // Depending on the name of the object, the name given in the
243 // configuration, and the configuration itself, decide on
244 // whether a new object needs to be constructed or can be
245 // retained from the known objects.
246 // If the configuration specifies a type, use that information
247 // to either construct a new object from given arguments or
248 // copy-construct one from an existing object
249 for (const auto& node_pair : cfg)
250 {
251 // Unpack the (key node, value node) pair, i.e. the name of the
252 // object that is to be configured and the corresponding
253 // configuration node
254 const auto cfg_name = node_pair.first.as< std::string >();
255 const auto& obj_cfg = node_pair.second;
256
257 const auto type_name = get_as< std::string >("type", obj_cfg);
258
259 _log->debug("Attempting to build {} of type {} from config",
260 cfg_name,
261 type_name);
262
263 if (known_objects.find(type_name) == known_objects.end())
264 {
265 throw std::invalid_argument("Error for node " + cfg_name +
266 ": No 'type' node given");
267 }
268 else
269 {
270 if (obj_cfg["args"])
271 {
272 _log->debug(" ... using given arguments from config ...");
273 map[cfg_name] =
274 known_objects[type_name](); // default construct
275 map[cfg_name]->set_from_cfg(obj_cfg["args"]);
276 }
277 else
278 {
279
280 // if no args is given, do a default build because this
281 // not all of the deciders/triggers need an args node
282 _log->debug("... constructing {} of type {} without "
283 "config args because no node 'args' is "
284 "given for it in the config.",
285 cfg_name,
286 type_name);
287
289 }
290 }
291 }
292
293 return map;
294 }
295
304 TaskMap
306 {
307 // map to use in the end
308 TaskMap map;
309
310 if (not task_cfg)
311 {
312 throw std::invalid_argument(
313 "Error: data_manager config node needs to contain a node "
314 "'tasks' which it apparently is missing ");
315 }
316 if (not task_cfg.IsMap())
317 {
318 throw std::invalid_argument("Expected a mapping for DataManager "
319 "task filtering, got:\n" +
321 }
322
323 for (const auto& node_pair : task_cfg)
324 {
325 // Unpack the (key node, value node) pair, i.e. the name of the
326 // object that is to be configured and the corresponding
327 // configuration node
328 const auto cfg_name = node_pair.first.as< std::string >();
329 const auto& obj_cfg = node_pair.second;
330
331 _log->debug("Investigating task {} and checking if it is active ",
332 cfg_name);
333
334 if (get_as< bool >("active", obj_cfg))
335 {
336
337 _log->debug("Task '{}' was marked as active; will be kept.",
338 cfg_name);
339
340 if (tasks.find(cfg_name) == tasks.end())
341 {
342 throw std::invalid_argument(
343 "Error, no task supplied to the datamanager is named " +
344 cfg_name);
345 }
346 else
347 {
349 }
350 }
351 else
352 {
353 // skip inactive tasks
354 _log->debug("Task '{}' was marked as not active; skipping.",
355 cfg_name);
356 continue;
357 }
358 }
359
360 return map;
361 }
362
378 template < typename DTMap >
381 DTMap&& dt_map,
382 std::string lookup_key)
383 {
385
386 // error checking regarding the task_cfg node is done in the
387 // _filter_tasks function, and hence does not need to be repeated here
388
389 _log->debug("Building task to {} associations from given config ...",
390 lookup_key);
391
392 // Iterate over the given configuration node, pull out the name of the
393 // task and the name of the associated decider/trigger, and put the
394 // AssocsMap together from this
395 for (const auto& node_pair : task_cfg)
396 {
397 // Unpack the (key node, value node) pair
398 const auto task_name = node_pair.first.as< std::string >();
399 const auto& task_cfg = node_pair.second;
400
401 // Find out if active; true by default
402 const auto active = get_as< bool >("active", task_cfg, true);
403
404 // Associate only if active
405 if (not active)
406 {
407 _log->debug("Task '{}' was marked as not active; skipping.",
408 task_name);
409 continue;
410 }
411
412 // Get the name of the trigger or decider to associate to
413
414 const auto dt_to_associate_to =
416
417 // Find erroneous config namings for deciders/triggers
418 if (dt_map.find(dt_to_associate_to) == dt_map.end())
419 {
420 this->_log->info(" Error for decider/trigger: {}",
422 throw std::invalid_argument(
423 "Error when trying to associate tasks to deciders or "
424 "triggers: "
425 "Name in config does not match the name of a "
426 "decider/trigger known to the datamanager");
427 }
428 else
429 {
430 // dt_to_associate_to exists in the dt_map, we're good
431 map[dt_to_associate_to].push_back(task_name);
432
433 _log->debug("Associating task '{}' to {} '{}'.",
434 task_name,
437 }
438 }
439
440 // Use the helper function to build the actual association map
441 return map;
442 }
443
444 public:
445 // -- Public interface ----------------------------------------------------
446
455 template < class Model, typename... Args >
456 void
458 {
460 *this, std::forward< Model >(model), std::forward< Args >(args)...);
461 }
462
463 // .. Getters .............................................................
464
472 {
473 return _deciders;
474 }
475
481 TaskMap&
483 {
484 return _tasks;
485 }
486
494 {
495 return _triggers;
496 }
497
503 AssocsMap&
505 {
506 return _decider_task_map;
507 }
508
514 AssocsMap&
516 {
517 return _trigger_task_map;
518 }
519
525 const std::shared_ptr< spdlog::logger >&
527 {
528 return _log;
529 }
530
531 // .. Rule of Five ........................................................
532
537 DataManager() = default;
538
544 DataManager(const DataManager& other) = default;
545
552
560 operator=(const DataManager& other) = default;
561
570
575 virtual ~DataManager() = default;
576
577 // .. Constructors ........................................................
578
601 const Config& cfg,
603 std::unordered_map< std::string,
604 std::function< std::shared_ptr< Decider >() > >
605 deciders,
606 std::unordered_map< std::string,
607 std::function< std::shared_ptr< Trigger >() > >
608 triggers,
610 // Get the global data manager logger
611 _log(spdlog::get("data_mngr")),
612 _tasks(_filter_tasks_from_config(cfg["tasks"], tasks)),
613 _deciders(_setup_from_config< DeciderMap >(cfg["deciders"], deciders)),
614 _triggers(_setup_from_config< TriggerMap >(cfg["triggers"], triggers)),
615 // Create maps: decider/trigger -> vector of task names
617 _associate_from_config(cfg["tasks"], _deciders, "decider")),
619 _associate_from_config(cfg["tasks"], _triggers, "trigger")),
621 {
622 // nothing remains to be done here
623 }
624
647 std::map< std::string, std::string > decider_task_assocs = {},
648 std::map< std::string, std::string > trigger_task_assocs = {}) :
649 // Get the global data manager logger
650 _log(spdlog::get("data_mngr")),
651 _tasks(TaskMap(tasks.begin(), tasks.end())),
652 _deciders(DeciderMap(deciders.begin(), deciders.end())),
653 _triggers(TriggerMap(triggers.begin(), triggers.end())),
654 // Create maps: decider/trigger -> vector of task names
655 _decider_task_map(_DMUtils::build_task_association_map< AssocsMap >(
657 _trigger_task_map(_DMUtils::build_task_association_map< AssocsMap >(
660 {
661 _log->info("DataManager setup with {} task(s), {} decider(s), and "
662 "{} trigger(s).",
663 _tasks.size(),
664 _decider_task_map.size(),
665 _trigger_task_map.size());
666 }
667
668 // .. Helper Methods ......................................................
669
675 void
677 {
678 if (this == &other)
679 {
680 return;
681 }
682
683 using std::swap;
684 swap(_log, other._log);
685 swap(_tasks, other._tasks);
686 swap(_deciders, other._deciders);
687 swap(_triggers, other._triggers);
688 swap(_decider_task_map, other._decider_task_map);
689 swap(_trigger_task_map, other._trigger_task_map);
690 }
691};
692
697// ++ Default DataManager +++++++++++++++++++++++++++++++++++++++++++++++++++++
698
705namespace Default
706{
715template < typename Model >
721} // namespace Default
722
727} // namespace DataIO
728} // namespace Utopia
729
730#endif // UTOPIA_DATAIO_DATAMANAGER_HH
Manage different tasks of writing out data from a source in a uniform yet flexible way....
Definition data_manager.hh:131
AssocsMap & get_trigger_task_map()
Get the trigger task map object.
Definition data_manager.hh:515
ObjMap _setup_from_config(const Config &cfg, KnownObjectsMap &&known_objects)
Definition data_manager.hh:217
std::unordered_map< std::string, std::shared_ptr< Decider > > DeciderMap
Map of decider names to decider functions.
Definition data_manager.hh:156
typename Traits::Task Task
Task type, as defined in the traits.
Definition data_manager.hh:137
DataManager()=default
Construct a new Data Manager object.
virtual ~DataManager()=default
Destroy the Data Manager object.
AssocsMap & get_decider_task_map()
Get the decider task map object.
Definition data_manager.hh:504
TriggerMap & get_triggers()
Get the container of trigger objects.
Definition data_manager.hh:493
DataManager(DataManager &&other)=default
Construct a new Data Manager object.
TaskMap _filter_tasks_from_config(const Config &task_cfg, TaskMap &tasks)
Check which tasks supplied to the datamanager are active and shall be retained, using the config node...
Definition data_manager.hh:305
typename Traits::Trigger Trigger
Trigger type, as defined in the traits.
Definition data_manager.hh:143
void operator()(Model &&model, Args &&... args)
Invoke the execution process.
Definition data_manager.hh:457
void swap(DataManager &other)
Exchange the state of the caller with the argument 'other'.
Definition data_manager.hh:676
std::map< std::string, std::shared_ptr< Task > > OrderedTaskMap
Same as TaskMap, but using std::map such that ordering is preserved.
Definition data_manager.hh:152
ExecutionProcess _execution_process
Callable which tells how to utilize triggers, deciders, tasks to write data.
Definition data_manager.hh:211
AssocsMap _trigger_task_map
Mapping from trigger names to containers of names of tasks that use those triggers.
Definition data_manager.hh:205
std::map< std::string, std::shared_ptr< Decider > > OrderedDeciderMap
Same as DeciderMap, but using std::map such that ordering is preserved.
Definition data_manager.hh:160
DataManager(const DataManager &other)=default
Construct a new Data Manager object.
DataManager(const Config &cfg, TaskMap tasks, std::unordered_map< std::string, std::function< std::shared_ptr< Decider >() > > deciders, std::unordered_map< std::string, std::function< std::shared_ptr< Trigger >() > > triggers, ExecutionProcess execproc)
Construct DataManager using a config node.
Definition data_manager.hh:600
DataManager & operator=(const DataManager &other)=default
Copy assignment.
DeciderMap & get_deciders()
Get the container of decider objects.
Definition data_manager.hh:471
typename Traits::ExecutionProcess ExecutionProcess
Execution process type, as defined in the traits.
Definition data_manager.hh:146
TaskMap & get_tasks()
Get the container of task objects.
Definition data_manager.hh:482
TaskMap _tasks
Stores (name, task) pairs in an unordered map.
Definition data_manager.hh:183
std::shared_ptr< spdlog::logger > _log
Used to inform about progress of DataManager operations.
Definition data_manager.hh:178
std::unordered_map< std::string, std::vector< std::string > > AssocsMap
Map of decider/task names to a collection of task names.
Definition data_manager.hh:172
AssocsMap _associate_from_config(const Config &task_cfg, DTMap &&dt_map, std::string lookup_key)
Given a configuration, builds an association map.
Definition data_manager.hh:380
std::unordered_map< std::string, std::shared_ptr< Task > > TaskMap
Map of task names to shared pointers of Tasks; supporting polymorphism.
Definition data_manager.hh:149
AssocsMap _decider_task_map
Mapping from deciders names to containers of names of tasks that use those deciders.
Definition data_manager.hh:199
DataManager & operator=(DataManager &&other)=default
Move assignment.
typename Traits::Decider Decider
Decider type, as defined in the traits.
Definition data_manager.hh:140
DataManager(OrderedTaskMap tasks, OrderedDeciderMap deciders, OrderedTriggerMap triggers, ExecutionProcess execproc, std::map< std::string, std::string > decider_task_assocs={}, std::map< std::string, std::string > trigger_task_assocs={})
Construct DataManager without config node from passed mappings only. If the last two arguments are no...
Definition data_manager.hh:643
const std::shared_ptr< spdlog::logger > & get_logger() const
Get the logger used in this DataManager.
Definition data_manager.hh:526
TriggerMap _triggers
Stores (name, triggerfunction) pairs in an unordered map.
Definition data_manager.hh:193
DeciderMap _deciders
Stores (name, deciderfunction) pairs in an unordered map.
Definition data_manager.hh:188
std::unordered_map< std::string, std::shared_ptr< Trigger > > TriggerMap
Map of trigger names to trigger functions.
Definition data_manager.hh:164
std::map< std::string, std::shared_ptr< Trigger > > OrderedTriggerMap
Same as TriggerMap, but using std::map such that ordering is preserved.
Definition data_manager.hh:168
Base class interface for Models using the CRT Pattern.
Definition model.hh:112
YAML::Node Config
Type of a variadic dictionary-like data structure used throughout Utopia.
Definition types.hh:71
std::string to_string(const Config &node)
Given a config node, returns a string representation of it.
Definition cfg_utils.hh:110
Container select_entities(const Manager &mngr, const DataIO::Config &sel_cfg)
Select entities according to parameters specified in a configuration.
Definition select.hh:213
Definition metaprogramming.hh:43
Definition agent.hh:11
Type traits for the DataManager This allows to specify custom types for the DataManager....
Definition data_manager.hh:73
ExecutionProcessType ExecutionProcess
Definition data_manager.hh:77
TaskType Task
Definition data_manager.hh:74
DeciderType Decider
Definition data_manager.hh:75
TriggerType Trigger
Definition data_manager.hh:76
Common interface for all deciders (and triggers, for that matter). Every decider/Trigger must inherit...
Definition defaults.hh:166
Functor representing what is considered the most widely used execution process for writing data.
Definition defaults.hh:84