forked from AMReX-Combustion/PelePhysics
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathFactory.H
165 lines (148 loc) · 5.03 KB
/
Factory.H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#ifndef FACTORY_H
#define FACTORY_H
#include <memory>
#include <unordered_map>
#include <iostream>
#include "AMReX_Print.H"
#include "AMReX.H"
namespace pele::physics {
/** Runtime selection of options based on string keyword lookup
*
* This is lifted straight from AMR-wind, <https://github.com/Exawind/amr-wind>
*
* Factory provides an automated way to register subclasses that can be
* instantiated during runtime using string based lookups. The facility mimics
* the runTimeSelectionTable in OpenFOAM and the current structure is adapted
* from http://www.nirfriedman.com/2018/04/29/unforgettable-factory/
*
* The usage is described using the following example from code. We would like
* to support creation of a turbulence model based on user inputs that are read
* from a file. We will create a base class TurbulenceModel that defines the
* API for the turbulence model and how it interacts with the rest of the code.
* Then new models, e.g., Smagorinsky, k-Omega SST/SAS, etc., are added as
* subclasses to this instance.
*
* ```
* // Create a base class that can be used to register and create instances
* //
* // The template arguments are the classname for CRTP and types of additional
* // arguments to be passed to the constructor of the object
* class TurbulenceModel : public Factory<TurbulenceModel, const CFDSim&>
* {
* // Define a base identifier string for debugging purposes
* static const std::string base_identifier() { return "TurbulenceModel";
* }
*
* // Define API as appropriate here
* };
*
* // Define a subclass
* // Instead of using the base class we use Base::Register to allow
* registration class Smagorinsky : public
* TurbulenceModel::Register<Smagorinsky>
* {
* // This is the keyword that is used to lookup and create this instance
* static const std::string identifier() { return "Smagorinsky"; }
*
* // Model implementation here
* };
*
* // To create a turbulence model instance
* auto sim = CFDSim(mesh);
* // tmodel is a std::unique_ptr<TurbulenceModel> instance
* auto tmodel = TurbulenceModel::create("Smagorinsky", sim);
* ```
*
* \tparam Base base class (e.g., Physics)
* \tparam Args Arguments types to be passed to the constructor during
* instantiation
*/
template <class Base, class... Args>
struct Factory
{
/** Create an instance of the concrete subclass based on the runtime keyword
*
* \param key Unique identifier used to lookup subclass instance
* \param args Arguments to the constructor of the subclass
*/
static std::unique_ptr<Base> create(const std::string& key, Args... args)
{
key_exists_or_error(key);
auto ptr = table().at(key)(std::forward<Args>(args)...);
amrex::Print() << "Creating " << Base::base_identifier()
<< " instance: " << key << std::endl;
return ptr;
}
/** Print a list of the valid subclasses registered to this base instance
*
* \param os Valid output stream
*/
static void print(std::ostream& os)
{
const auto& tbl = table();
os << Base::base_identifier() << " " << tbl.size() << std::endl;
for (const auto& it : tbl) {
os << " - " << it.first << std::endl;
}
}
//! Class to handle registration of subclass for runtime selection
template <class T, class DeriveFrom = Base>
struct Register : public DeriveFrom
{
friend T;
static bool registered;
static_assert(std::is_base_of_v<Base, DeriveFrom>);
static bool add_sub_type()
{
// TODO: Add checks against multiple registration
Factory::table()[T::identifier()] =
[](Args... args) -> std::unique_ptr<Base> {
return std::unique_ptr<Base>(new T(std::forward<Args>(args)...));
};
return true;
}
~Register() override
{
if (registered) {
const auto& tbl = Factory::table();
const auto& it = tbl.find(T::identifier());
registered = (it != tbl.end());
}
}
private:
Register() { (void)registered; }
};
virtual ~Factory() = default;
friend Base;
private:
using CreatorFunc = std::unique_ptr<Base> (*)(Args...);
using LookupTable = std::unordered_map<std::string, CreatorFunc>;
//! Check if the keyword exists in table or print diagnostic message and
//! abort
static void key_exists_or_error(const std::string& key)
{
const auto& tbl = table();
if (tbl.find(key) == tbl.end()) {
// Print valid options
if (amrex::ParallelDescriptor::IOProcessor()) {
print(std::cout);
}
// Quit with error
amrex::Abort(
"In " + Base::base_identifier() + " cannot find instance: " + key);
}
}
//! Lookup table containing all registered instances
static LookupTable& table()
{
static LookupTable tbl;
return tbl;
}
Factory() = default;
};
template <class Base, class... Args>
template <class T, class DeriveFrom>
bool Factory<Base, Args...>::Register<T, DeriveFrom>::registered =
Factory<Base, Args...>::Register<T, DeriveFrom>::add_sub_type();
} // namespace pele::physics
#endif /* FACTORY_H */