Utopia 2
Framework for studying models of complex & adaptive systems.
Loading...
Searching...
No Matches
factory.hh
Go to the documentation of this file.
1#ifndef UTOPIA_DATAIO_FACTORY_HH
2#define UTOPIA_DATAIO_FACTORY_HH
3
4// stl includes for having shared_ptr, hashmap, vector, swap
5#include <algorithm>
6#include <memory>
7#include <unordered_map>
8#include <vector>
9
10// boost includes
11#include <boost/graph/adjacency_list.hpp>
12#include <boost/graph/adjacency_matrix.hpp>
13#include <boost/graph/graph_traits.hpp>
14#include <boost/graph/properties.hpp>
15
16#include <boost/hana/ext/std/tuple.hpp>
17#include <boost/hana/integral_constant.hpp>
18#include <boost/hana/remove_at.hpp>
19#include <boost/hana/transform.hpp>
20
21// utopia includes
22#include "../../core/logging.hh"
23#include "../../core/type_traits.hh"
24#include "../cfg_utils.hh"
25#include "data_manager.hh"
26#include "defaults.hh"
27#include "utils.hh"
28
29namespace Utopia
30{
31namespace DataIO
32{
33
65{
66 std::string path;
67 std::vector< hsize_t > dataset_capacity = {};
68 std::vector< hsize_t > dataset_chunksize = {};
70};
71
91
92// Making the factory a functor allows for the separation of template parameters
93// which are user defined (model, graphtag) and automatically determined ones,
94// which are given to the call operator.
95
103template < class Model, TypeTag typetag = TypeTag::plain >
105{
106 private:
107 static std::unordered_map<
108 std::string,
109 std::function< std::string(std::string, Model&) > >
111
129 template < class Func, class AttributeHandle >
130 Func
132 {
133 using AttrType = std::decay_t< AttributeHandle >;
134 Func writer{};
135
136 if constexpr (std::is_same_v< AttrType, Nothing >)
137 {
138 // do nothing here, because nothing shall be done ;)
139 }
140 else if constexpr (Utopia::Utils::has_static_size_v< AttrType >)
141 {
142
143 using std::get;
144 using Nametype =
145 std::decay_t< std::tuple_element_t< 0, AttrType > >;
146
147 // if the first thing held by the Dataset_attribute tuplelike is
148 // not convertible to stirng, and hence cannot be used to name
149 // the thing, error is thrown.
150 static_assert(
151 Utils::is_string_v< Nametype >,
152 "Error, first entry of Dataset_attribute must be a string "
153 "naming the attribute");
154
155 // check if the $ indicating string interpolation like behavior
156 // is found. If yes, invoke path modifier, else leave as is
157 std::string name = get< 0 >(attr);
158 auto pos = name.find('$'); // find indicator
159
160 if (pos != std::string::npos)
161 {
162 auto path_builder = _modifiers[name.substr(pos + 1)];
163 std::string new_path = name.substr(0, pos);
164
166 auto&& m) -> void {
167 hdfobject->add_attribute(path_builder(new_path, m),
168 get< 1 >(attr));
169 };
170 }
171 else
172 {
173 writer = [attr](auto&& hdfobject, auto &&) -> void {
174 hdfobject->add_attribute(get< 0 >(attr),
175 get< 1 >(attr));
176 };
177 }
178 }
179 else
180 {
181 static_assert(
182 Utils::is_callable_v< AttrType >,
183 "Error, if the given attribute argument is not a tuple/pair "
184 "and not 'Nothing', it has to be a function");
185
186 writer = attr;
187 }
188
189 return writer;
190 }
191
202 {
204
205 auto pos = dataset_descriptor.path.find('$'); // find indicator
206
207 // $ indicates that string interpolation shall be used
208 if (pos != std::string::npos)
209 {
210 // get the path builder corresponding to the flag extracted after
211 // indicator
212 auto path_builder =
213 _modifiers[dataset_descriptor.path.substr(pos + 1)];
214
215 std::string new_path = dataset_descriptor.path.substr(0, pos);
216 // put the latter into the dataset builder
219 auto&& group,
220 auto&& model) -> std::shared_ptr< HDFDataset > {
221 return group->open_dataset(
223 dataset_descriptor.dataset_capacity,
224 dataset_descriptor.dataset_chunksize,
225 dataset_descriptor.dataset_compression);
226 };
227 }
228 else // no string to interpolate something
229 {
230
233 std::shared_ptr< HDFGroup >& group,
234 Model&) -> std::shared_ptr< HDFDataset > {
235
236 return group->open_dataset(
238 dataset_descriptor.dataset_capacity,
239 dataset_descriptor.dataset_chunksize,
240 dataset_descriptor.dataset_compression);
241 };
242 }
243 return dataset_builder;
244 }
245
259 template < class SourceGetter, class Getter >
262 {
264
265 using GraphType = std::decay_t<
266 std::invoke_result_t< std::decay_t< SourceGetter >, Model& > >;
267
268 if constexpr (typetag == TypeTag::vertex_property)
269 {
270 writer = [&getter, &get_source](
271 std::shared_ptr< HDFDataset >& dataset,
272 Model& m) -> void {
273 // Collect some information on the graph
274 auto& graph = get_source(m);
275
276 // Make vertex iterators
277 typename GraphType::vertex_iterator v, v_end;
278 boost::tie(v, v_end) = boost::vertices(graph);
279
280 dataset->write(v, v_end, [&getter, &graph](auto&& vd) {
281 return getter(graph[vd]);
282 });
283 };
284 }
285 else if constexpr (typetag == TypeTag::edge_property)
286 {
287 writer = [&getter, &get_source](
288 std::shared_ptr< HDFDataset >& dataset,
289 Model& m) -> void {
290 auto& graph = get_source(m);
291
292 // Make edge iterators
293 typename GraphType::edge_iterator v, v_end;
294 boost::tie(v, v_end) = boost::edges(graph);
295
296 dataset->write(v, v_end, [&getter, &graph](auto&& vd) {
297 return getter(graph[vd]);
298 });
299 };
300 }
301 else if constexpr (typetag == TypeTag::vertex_descriptor and
302 not std::is_same_v< std::decay_t< Getter >,
303 std::function< void() > >)
304 {
305
306 writer = [&getter, &get_source](
307 std::shared_ptr< HDFDataset >& dataset,
308 Model& m) -> void {
309 auto& graph = get_source(m);
310
311 // Make edge iterators
312 typename GraphType::vertex_iterator v, v_end;
313 boost::tie(v, v_end) = boost::vertices(graph);
314 dataset->write(v, v_end, [&getter, &graph](auto&& vd) {
315 return getter(graph, vd);
316 });
317 };
318 }
319 else if constexpr (typetag == TypeTag::edge_descriptor and
320 not std::is_same_v< std::decay_t< Getter >,
321 std::function< void() > >)
322 {
323 writer = [&getter, &get_source](
324 std::shared_ptr< HDFDataset >& dataset,
325 Model& m) -> void {
326 auto& graph = get_source(m);
327
328 // Make edge iterators
329 typename GraphType::edge_iterator v, v_end;
330 boost::tie(v, v_end) = boost::edges(graph);
331
332 dataset->write(v, v_end, [&getter, &graph](auto&& vd) {
333 return getter(graph, vd);
334 });
335 };
336 }
337 // for writing pure graphs
338
339 // vertex
340 else if constexpr (std::is_same_v< std::decay_t< Getter >,
341 std::function< void() > > and
343 {
344 writer = [&get_source](
345 std::shared_ptr< HDFDataset >& dataset,
346 Model& m) -> void {
347 auto& graph = get_source(m);
348
349 auto [v, v_end] = boost::vertices(graph);
350 dataset->write(v, v_end, [&](auto&& vd) {
351 return boost::get(boost::vertex_index_t(), graph, vd);
352 });
353 };
354 }
355 // edges
356 else if constexpr (std::is_same_v< std::decay_t< Getter >,
357 std::function< void() > > and
359 {
360 writer =
361 [get_source](std::shared_ptr< HDFDataset >& dataset,
362 Model& m) -> void {
363 auto& graph = get_source(m);
364
365 auto [e, e_end] = boost::edges(graph);
366
367 dataset->write(e, e_end, [&](auto&& ed) {
368 return boost::get(boost::vertex_index_t(),
369 graph,
370 boost::source(ed, graph));
371 });
372
373 dataset->write(e, e_end, [&](auto&& ed) {
374 return boost::get(boost::vertex_index_t(),
375 graph,
376 boost::target(ed, graph));
377 });
378 };
379 }
380 else
381 {
382 // user fucked up
383 throw std::invalid_argument("Unknown ObjectType.");
384 }
385
386 return writer;
387 }
388
389 public:
390
448 template < class SourceGetter,
449 class Getter,
450 class Group_attribute = Nothing,
451 class Dataset_attribute = Nothing >
452 std::pair< std::string,
453 std::shared_ptr< Default::DefaultWriteTask< Model > > >
455 // is in config
456 std::string name,
457 std::string basegroup_path,
459 // has to be given
461 Getter&& getter,
464 {
465 // return type of get_source when used with model
466 using Container =
467 std::decay_t< decltype(get_source(std::declval< Model& >())) >;
468
469 // asserts that the SourceGetter-type is really a container type.
470 static_assert(
471 Utils::is_container_v< std::decay_t< Container > > or
472 Utils::is_graph_v< std::decay_t< Container > >,
473 "Error, the argument 'get_source' must return a container "
474 "type or graph, i.e., "
475 "a type with an iterator");
476
477 // make basegroup builder and attribute builders
478
479 Default::DefaultBuilder< Model > dataset_builder =
481
483 Default::DefaultAttributeWriterGroup< Model > >(
484 std::forward< Group_attribute >(group_attribute));
485
487 Default::DefaultAttributeWriterDataset< Model > >(
488 std::forward< Dataset_attribute >(dataset_attribute));
489
490 Default::DefaultDataWriter< Model > datawriter;
491
492 // assert that the given template argument combination is valid:
493 // A graph cannot be written with TypeTag::plain
494
495 static_assert(not(Utils::is_graph_v< std::decay_t< Container > > and
497 "Error in WriteTask factory:, a graph cannot be written "
498 "with TypeTag::plain, see documentation of TypeTag enum");
499
500 if constexpr (Utils::is_graph_v< std::decay_t< Container > > and
502 {
503 datawriter =
504 _adapt_graph_writer(std::forward< SourceGetter >(get_source),
505 std::forward< Getter >(getter));
506 }
507 else
508 {
509
511 std::shared_ptr< HDFDataset >& dataset,
512 Model& m) -> void {
513 dataset->write(
514 get_source(m).begin(), get_source(m).end(), getter);
515 };
516 }
517
518 // build a defaultwriteTask
519 return std::make_pair(
520 name,
521 std::make_shared< Default::DefaultWriteTask< Model > >(
522 // builds the basegroup to write datasets in
524 [basegroup_path](std::shared_ptr< HDFGroup > parent) {
525 return parent->open_group(basegroup_path);
526 }),
527 // writes out data
529 // builds datasets as needed
531 // writes attributes to base group
533 // writes attributes to dataset
535 }
536
552 std::pair< std::string,
553 std::shared_ptr< Default::DefaultWriteTask< Model > > >
566};
567
574template < typename Model, TypeTag typetag >
575std::unordered_map< std::string,
576 std::function< std::string(std::string, Model&) > >
578 std::unordered_map< std::string,
579 std::function< std::string(std::string, Model&) > >{
580 { "time",
581 [](std::string path, Model& m) {
582 return path + "_" + std::to_string(m.get_time());
583 } } // for time addition
584 };
585
597template < class Model >
599{
600
611 template < typename ArgTpl >
612 std::pair< std::string,
613 std::shared_ptr< Default::DefaultWriteTask< Model > > >
615 {
616
617 if (typetag == "plain")
618 {
620
621 return std::apply(factory, std::forward< ArgTpl >(arg_tpl));
622 }
623 else if (typetag == "edge_property")
624 {
626
627 return std::apply(factory, std::forward< ArgTpl >(arg_tpl));
628 }
629 else if (typetag == "vertex_descriptor")
630 {
632
633 return std::apply(factory, std::forward< ArgTpl >(arg_tpl));
634 }
635 else if (typetag == "vertex_property")
636 {
638
639 return std::apply(factory, std::forward< ArgTpl >(arg_tpl));
640 }
641 else
642 {
644
645 return std::apply(factory, std::forward< ArgTpl >(arg_tpl));
646 }
647 }
648
649 public:
673 template < typename... Args >
674 auto
676 const std::tuple< Args... >& args,
678 Default::default_deciders<Model>,
680 Default::default_triggers<Model>)
681 {
682 // Get the global data manager logger
683 const auto _log = spdlog::get("data_mngr");
684
685 if constexpr (sizeof...(Args) == 0)
686 {
687 _log->info("Empty argument tuple for DataManager factory, "
688 "building default ...");
690 }
691 else
692 {
693 // read the tasks from the config into a map
694 // has the consequence that the ordering need to match
695 std::map< std::string, Config > tasknodes;
696 for (auto&& node : conf["tasks"])
697 {
698 _log->info("Name of current task: {}",
699 node.first.as< std::string >());
700
701 tasknodes[node.first.as< std::string >()] = node.second;
702 }
703
704 // this transforms the tuple of argument tuples std::tuple< Args...
705 // >
706 std::unordered_map<
707 std::string,
708 std::shared_ptr< Default::DefaultWriteTask< Model > > >
709 tasks;
710
711 boost::hana::for_each(
712 args,
713 // function gets a tuple representing arguments for the
714 // writetask extracts config-supplied arguments from the
715 // respective config node if the latter is found, puts them in
716 // their correct place, then forwards the rest to the
717 // taksfactory. If no config node for a given taskname is found,
718 // exception is thrown
719 [&](const auto& arg_tpl) {
720 using std::get;
721 std::string name = "";
722 std::string name_in_tpl = get< 0 >(arg_tpl);
724
725 // find the current task in the config, if not found throw
726 if (tasknode_iter != tasknodes.end())
727 {
728 name = tasknode_iter->first;
729 }
730 else
731 {
732 // .. and throw if it is not
733 throw std::invalid_argument(
734 "A task with name '" + name_in_tpl +
735 "' was not found in the config!");
736 }
737
738 // read out the typetag from the config
739 std::string typetag = "";
740 if (not tasknode_iter->second["typetag"])
741 {
742 typetag = "plain";
743 }
744 else
745 {
746 get_as< std::string >("typetag", tasknode_iter->second);
747 }
748
749 // make a tuple of types from the argtuple except the first
750 // element which is the sentinel name
751 auto type_tuple = boost::hana::transform(
752 boost::hana::remove_at(arg_tpl,
753 boost::hana::size_t< 0 >{}),
754 [](auto&& t) {
755 return boost::hana::type_c<
756 std::decay_t< decltype(t) > >;
757 });
758
759 constexpr bool is_all_callable =
760 decltype(boost::hana::unpack(
762 boost::hana::template_<
763 _DMUtils::all_callable >))::type::value;
764
765 // all arguments are callables
766 if constexpr (is_all_callable)
767 {
768 _log->info("Building write task '{}' via factory ...",
769 name);
771 }
772 // not all-callable arguments
773 else
774 {
775
776 // extract arguments from the configfile
777 auto config_args = std::make_tuple(
778 // name
780 // basegroup_path
781 get_as< std::string >("basegroup_path",
782 tasknode_iter->second),
783 // dataset_descriptor
785 get_as< std::string >("dataset_path",
786 tasknode_iter->second),
787 (tasknode_iter->second["capacity"]
788 ? get_as< std::vector< hsize_t > >(
789 "capacity", tasknode_iter->second)
790 : std::vector< hsize_t >{}),
791 (tasknode_iter->second["chunksize"]
793 "chunksize", tasknode_iter->second)
794 : std::vector< hsize_t >{}),
795 (tasknode_iter->second["compression"]
796 ? get_as< int >("compression",
797 tasknode_iter->second)
798 : 0) });
799
800 // remove the sentinel name and concat the tuples, which
801 // then is forwarded to args
802 auto full_arg_tpl = std::tuple_cat(
804 boost::hana::remove_at(arg_tpl,
805 boost::hana::size_t< 0 >{}));
806
807 _log->info("Building write task '{}' via factory ...",
808 name);
809
811 }
812 });
813
814 _log->info("Forwarding arguments to DataManager constructor ...");
815 // then produce default datamanager with all default deciders,
816 // triggers etc
817
819 conf,
820 tasks,
824 }
825 }
826};
829} // namespace DataIO
830} // namespace Utopia
831
832#endif
Factory function which produces a Datamanager of type Default::DefaultDataManager<Model> from a confi...
Definition factory.hh:599
std::pair< std::string, std::shared_ptr< Default::DefaultWriteTask< Model > > > _call_taskfactory(std::string typetag, ArgTpl &&arg_tpl)
Function which calls the taskfactory with argument tuples, and takes care of using the correct type-t...
Definition factory.hh:614
auto operator()(const Config &conf, const std::tuple< Args... > &args, const Default::DefaultDecidermap< Model > &deciderfactories=Default::default_deciders< Model >, const Default::DefaultTriggermap< Model > &triggerfactories=Default::default_triggers< Model >)
Builds a new datamanager from a config file and a tuple of tuples of arguments.
Definition factory.hh:675
Manage different tasks of writing out data from a source in a uniform yet flexible way....
Definition data_manager.hh:131
Functor for building a writetask from arguments.
Definition factory.hh:105
std::pair< std::string, std::shared_ptr< Default::DefaultWriteTask< Model > > > operator()(std::string name, std::string basegroup_path, DatasetDescriptor dataset_descriptor, SourceGetter &&get_source, Getter &&getter, Group_attribute &&group_attribute=Nothing{}, Dataset_attribute &&dataset_attribute=Nothing{})
Basic factory function producing Default::DefaultWriteTask<Model> intstances, for writing out data....
Definition factory.hh:454
Func _make_attribute_writer(AttributeHandle &&attr)
Function which produces functions for writing attributes to a dataset or group, and which are used by...
Definition factory.hh:131
std::pair< std::string, std::shared_ptr< Default::DefaultWriteTask< Model > > > operator()(std::string name, Default::DefaultBaseGroupBuilder group_builder, Default::DefaultDataWriter< Model > writer, Default::DefaultBuilder< Model > dataset_builder, Default::DefaultAttributeWriterGroup< Model > group_attr, Default::DefaultAttributeWriterDataset< Model > dset_attr)
Thin wrapper around the writetask constructor which allows to construct a writetask via the factory b...
Definition factory.hh:554
Default::DefaultBuilder< Model > _make_dataset_builder(DatasetDescriptor dataset_descriptor)
Function producing a dataset builder function of type Default::DefaultBuilder<Model>,...
Definition factory.hh:201
Default::DefaultDataWriter< Model > _adapt_graph_writer(SourceGetter &&get_source, Getter &&getter)
Function which adapts getter functions for the correct graph accessor type, i.e., vertex_descriptor e...
Definition factory.hh:261
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::function< void(std::shared_ptr< HDFDataset > &, Model &) > DefaultAttributeWriterDataset
Type of the default attribute writer for datasets.
Definition defaults.hh:54
std::function< std::shared_ptr< HDFGroup >(std::shared_ptr< HDFGroup > &&) > DefaultBaseGroupBuilder
Type of the default group builder.
Definition defaults.hh:34
std::unordered_map< std::string, std::function< std::shared_ptr< Decider< Model > >() > > DefaultDecidermap
Definition defaults.hh:406
std::function< std::shared_ptr< HDFDataset >(std::shared_ptr< HDFGroup > &, Model &) > DefaultBuilder
Type of the default dataset builder.
Definition defaults.hh:44
std::function< void(std::shared_ptr< HDFGroup > &, Model &) > DefaultAttributeWriterGroup
Type of the default attribute writer for groups.
Definition defaults.hh:49
DefaultDecidermap< Model > DefaultTriggermap
Definition defaults.hh:454
std::function< void(std::shared_ptr< HDFDataset > &, Model &) > DefaultDataWriter
Type of the default data writer.
Definition defaults.hh:39
static std::unordered_map< std::string, std::function< std::string(std::string, Model &) > > _modifiers
Initialization of the modifier map.
Definition factory.hh:110
TypeTag
TypeTag enumerates the kind of access which is used to write data. It became necessary after integrat...
Definition factory.hh:84
Container select_entities(const Manager &mngr, const DataIO::Config &sel_cfg)
Select entities according to parameters specified in a configuration.
Definition select.hh:213
constexpr bool is_graph_v
Shorthand for is_graph<T>::value.
Definition type_traits.hh:418
Definition agent.hh:11
Metafunction for use with boost hana, check if all T are callable types.
Definition utils.hh:39
Descriptor for a dataset. Contains: path: string giving the path of the dataset in its group or file ...
Definition factory.hh:65
std::vector< hsize_t > dataset_chunksize
Definition factory.hh:68
std::string path
Definition factory.hh:66
int dataset_compression
Definition factory.hh:69
std::vector< hsize_t > dataset_capacity
Definition factory.hh:67
Functor representing what is considered the most widely used execution process for writing data.
Definition defaults.hh:84
Encapsulate a task for writing data to a destination. Containes a callable 'writer' responisible for ...
Definition write_task.hh:50
Represent a type that does nothing and represents nothing, hence can be used in metaprogramming whene...
Definition type_traits.hh:742