Skip to content

Commit d170c48

Browse files
committed
A concept for configurable parameters
This commit introduces a concept for configurable parameters, which means that they are class members which can be, fully-automatic, influenced from a config file or by other means (command-line, distributed key-value stores). The commit focuses on providing an automatic mechanism, so that the developer/user merely needs to declare a parameter class in a special way: ``` class FooParam : public o2::conf::ConfigurableParamHelper<FooParam> { double getDensity() const { return mDensity; } private: double mDensity = 1.1; int mNPlanes = 2; O2ParamDef(FooParam, "Foo") }; ``` After this, parameters Foo.mDensity, Foo.mNPlaces are automatically part of a global parameter database (using boost property_tree) by means of an introspection mechanism (using ROOT dictionaries). Parameters can then be changed (textually) using the parameter database. Changes are furthermore propagated to the actual C++ singletons of type FooParam. In particular, the following works: ``` o2::conf::ConfigurableParam::setValue<double>("Foo.mDensity", 2.); // will now return 2. instead of default value 1.1 auto d = FooParam::Instance().getDensity(); ``` This mechanism is used to set/change these parameters from the command line. As a first example, we provide the simulation cuts of the Hall volume as such parameters and demonstrate that they can be changed from the simulation command line ``` o2sim -m HALL -n 1 --configKeyValue "HallSim.mCUTELE=10" ``` which should be useful for systematic studies. Basic fixed size vector types ``` double m[4] ``` are supported.
1 parent e5ac981 commit d170c48

File tree

15 files changed

+936
-12
lines changed

15 files changed

+936
-12
lines changed

Common/SimConfig/CMakeLists.txt

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ O2_SETUP(NAME ${MODULE_NAME})
44

55
set(SRCS
66
src/SimConfig.cxx
7+
src/ConfigurableParam.cxx
8+
src/ConfigurableParamHelper.cxx
79
)
810

911
set(HEADERS
1012
include/${MODULE_NAME}/SimConfig.h
13+
include/${MODULE_NAME}/ConfigurableParam.h
14+
include/${MODULE_NAME}/ConfigurableParamHelper.h
1115
)
1216

1317
set(LINKDEF src/SimConfigLinkDef.h)
@@ -17,9 +21,16 @@ set(BUCKET_NAME configuration_bucket)
1721
O2_GENERATE_LIBRARY()
1822

1923
O2_GENERATE_EXECUTABLE(
20-
EXE_NAME testSimConf
21-
SOURCES test/TestConfig.cxx
22-
MODULE_LIBRARY_NAME ${LIBRARY_NAME}
23-
BUCKET_NAME ${BUCKET_NAME}
24-
)
24+
EXE_NAME testSimConf
25+
SOURCES test/TestConfig.cxx
26+
MODULE_LIBRARY_NAME ${LIBRARY_NAME}
27+
BUCKET_NAME ${BUCKET_NAME}
28+
)
29+
30+
O2_GENERATE_EXECUTABLE(
31+
EXE_NAME testConfigurableParam
32+
SOURCES test/TestConfigurableParam.cxx
33+
MODULE_LIBRARY_NAME ${LIBRARY_NAME}
34+
BUCKET_NAME ${BUCKET_NAME}
35+
)
2536

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// Copyright CERN and copyright holders of ALICE O2. This software is
2+
// distributed under the terms of the GNU General Public License v3 (GPL
3+
// Version 3), copied verbatim in the file "COPYING".
4+
//
5+
// See http://alice-o2.web.cern.ch/license for full licensing information.
6+
//
7+
// In applying this license CERN does not waive the privileges and immunities
8+
// granted to it by virtue of its status as an Intergovernmental Organization
9+
// or submit itself to any jurisdiction.
10+
11+
//first version 8/2018, Sandro Wenzel
12+
13+
#ifndef COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAM_H_
14+
#define COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAM_H_
15+
16+
#include <vector>
17+
#include <map>
18+
#include <boost/property_tree/ptree.hpp>
19+
#include <typeinfo>
20+
21+
namespace o2
22+
{
23+
namespace conf
24+
{
25+
// Base class for a configurable parameter.
26+
//
27+
// A configurable parameter is a simple class, defining
28+
// a few (pod) properties/members which are registered
29+
// in a global (boost) property tree.
30+
//
31+
// The features that we provide here are:
32+
// a) Automatic translation from C++ data description to INI/JSON/XML
33+
// format via ROOT introspection and boost property trees and
34+
// the possibility to readably save the configuration
35+
// b) Automatic integration of sub-classes into a common configuration
36+
// c) Be able to query properties from high level interfaces (just knowing
37+
// d) Be able to set properties from high-level interfaces (and modifying the underlying
38+
// C++ object)
39+
// e) Automatic ability to modify parameters from the command-line
40+
//
41+
// Note that concrete parameter sub-classes **must** be implemented
42+
// by inheriting from ConfigurableParamHelper and not from this class.
43+
//
44+
// ---------------------
45+
// Example: To define a parameter class TPCGasParameters, one does the following:
46+
//
47+
// class TPCGasParamer : public ConfigurableParamHelper<TPCGasParameter>
48+
// {
49+
// public:
50+
// double getGasDensity() const { return mGasDensity; }
51+
// private: // define properties AND default values
52+
// double mGasDensity = 1.23;
53+
// int mGasMaterialID = 1;
54+
//
55+
// O2ParamDef(TPCGasParameter, TPCGas); // a macro implementing some magic
56+
// }
57+
//
58+
//
59+
// We can now query the parameters in various ways
60+
// - All parameter classes are singletons and we can say: TPCGasParameter::Instance().getGasDensity();
61+
// - We can query by key (using classname + parameter name) from the global registry:
62+
// - ConfigurableParameter::getValueAs<double>("TPCGas", "mGasDensity");
63+
//
64+
// We can modify the parameters via the global registry together with an automatic syncing
65+
// of the underlying C++ object:
66+
// - ConfigurableParameter::setValue("TPCGas.mGasDensity", "0.5");
67+
//
68+
// - TPCGasParameter::Instance().getGasParameter() will now return 0.5;
69+
//
70+
// This feature allows to easily modify parameters at run-time via a textual representation
71+
// (for example by giving strings on the command line)
72+
//
73+
// The collection of all parameter keys and values can be stored to a human/machine readable
74+
// file
75+
// - ConfigurableParameter::writeJSON("thisconfiguration.json")
76+
class ConfigurableParam
77+
{
78+
public:
79+
//
80+
virtual std::string getName() = 0; // print the name of the configurable Parameter
81+
82+
// print the current keys and values to screen
83+
virtual void printKeyValues() = 0;
84+
85+
static void printAllRegisteredParamNames();
86+
static void printAllKeyValuePairs();
87+
88+
static void writeJSON(std::string filename);
89+
static void writeINI(std::string filename);
90+
91+
// can be used instead of using API on concrete child classes
92+
template <typename T>
93+
static T getValueAs(std::string key)
94+
{
95+
if (!sIsFullyInitialized) {
96+
initialize();
97+
}
98+
return sPtree->get<T>(key);
99+
}
100+
101+
template <typename T>
102+
static void setValue(std::string mainkey, std::string subkey, T x)
103+
{
104+
auto key = mainkey + "." + subkey;
105+
if (sPtree->get_optional<std::string>(key).is_initialized()) {
106+
sPtree->put(key, x);
107+
updateThroughStorageMap(mainkey, subkey, typeid(T), (void*)&x);
108+
}
109+
}
110+
111+
// specialized for std::string
112+
// which means that the type will be converted internally
113+
static void setValue(std::string key, std::string valuestring)
114+
{
115+
if (sPtree->get_optional<std::string>(key).is_initialized()) {
116+
sPtree->put(key, valuestring);
117+
updateThroughStorageMapWithConversion(key, valuestring);
118+
}
119+
}
120+
121+
static void initialize();
122+
123+
// allows to provide a file from which to update
124+
// (certain) key-values
125+
// propagates changes down to each registered configuration
126+
static void updateFromFile(std::string filename);
127+
128+
// allows to provide a string of key-values from which to update
129+
// (certain) key-values
130+
// propagates changes down to each registered configuration
131+
// might be useful to get stuff from the command line
132+
static void updateFromString(std::string);
133+
134+
protected:
135+
// constructor is doing nothing else but
136+
// registering the concrete parameters
137+
ConfigurableParam();
138+
139+
static void updatePropertyTree();
140+
static void updateThroughStorageMap(std::string, std::string, std::type_info const&, void*);
141+
static void updateThroughStorageMapWithConversion(std::string, std::string);
142+
143+
virtual ~ConfigurableParam() = default;
144+
145+
// fill property tree with the key-values from the sub-classes
146+
virtual void putKeyValues(boost::property_tree::ptree*) = 0;
147+
148+
// static map keeping, for each configuration key, its memory location and type
149+
// (internal use to easily sync updates, this is ok since parameter classes are singletons)
150+
static std::map<std::string, std::pair<int, void*>>* sKeyToStorageMap;
151+
152+
private:
153+
// static registry for implementations of this type
154+
static std::vector<ConfigurableParam*>* sRegisteredParamClasses; //!
155+
// static property tree (stocking all key - value pairs from instances of type ConfigurableParam)
156+
static boost::property_tree::ptree* sPtree; //!
157+
static bool sIsFullyInitialized; //!
158+
};
159+
160+
} // end namespace conf
161+
} // end namespace o2
162+
163+
// a helper macro for boilerplate code in parameter classes
164+
#define O2ParamDef(classname, key) \
165+
private: \
166+
static constexpr char const* const sKey = key; \
167+
static classname sInstance; \
168+
classname() = default; \
169+
template <typename T> \
170+
friend class o2::conf::ConfigurableParamHelper;
171+
172+
// a helper macro to implement necessary symbols in source
173+
#define O2ParamImpl(classname) classname classname::sInstance;
174+
175+
#endif /* COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAM_H_ */
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright CERN and copyright holders of ALICE O2. This software is
2+
// distributed under the terms of the GNU General Public License v3 (GPL
3+
// Version 3), copied verbatim in the file "COPYING".
4+
//
5+
// See http://alice-o2.web.cern.ch/license for full licensing information.
6+
//
7+
// In applying this license CERN does not waive the privileges and immunities
8+
// granted to it by virtue of its status as an Intergovernmental Organization
9+
// or submit itself to any jurisdiction.
10+
11+
//first version 8/2018, Sandro Wenzel
12+
13+
#ifndef COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAMHELPER_H_
14+
#define COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAMHELPER_H_
15+
16+
#include "SimConfig/ConfigurableParam.h"
17+
#include "TClass.h"
18+
#include <type_traits>
19+
#include <typeinfo>
20+
21+
namespace o2
22+
{
23+
namespace conf
24+
{
25+
26+
// just a (non-templated) Helper with exclusively private functions
27+
// used by ConfigurableParamHelper
28+
class _ParamHelper
29+
{
30+
private:
31+
static void printParametersImpl(TClass* cl, void*);
32+
33+
static void fillKeyValuesImpl(std::string mainkey, TClass* cl, void*, boost::property_tree::ptree*,
34+
std::map<std::string, std::pair<int, void*>>*);
35+
36+
static void printWarning(std::type_info const&);
37+
38+
template <typename P>
39+
friend class ConfigurableParamHelper;
40+
};
41+
42+
// implementer (and checker) for concrete ConfigurableParam classes P
43+
template <typename P>
44+
class ConfigurableParamHelper : virtual public ConfigurableParam
45+
{
46+
public:
47+
using ConfigurableParam::ConfigurableParam;
48+
static P& Instance()
49+
{
50+
return P::sInstance;
51+
}
52+
53+
std::string getName() final
54+
{
55+
return P::sKey;
56+
}
57+
58+
// one of the key methods, using introspection to print itself
59+
void printKeyValues() final
60+
{
61+
// just a helper line to make sure P::sInstance is looked-up
62+
// and that compiler complains about missing static sInstance of type P
63+
// volatile void* ptr = (void*)&P::sInstance;
64+
// static assert on type of sInstance:
65+
static_assert(std::is_same<decltype(P::sInstance), P>::value, "static instance must of same type as class");
66+
67+
// obtain the TClass for P and delegate further
68+
auto cl = TClass::GetClass(typeid(P));
69+
if (!cl) {
70+
_ParamHelper::printWarning(typeid(P));
71+
return;
72+
}
73+
_ParamHelper::printParametersImpl(cl, (void*)this);
74+
}
75+
76+
void putKeyValues(boost::property_tree::ptree* tree) final
77+
{
78+
auto cl = TClass::GetClass(typeid(P));
79+
if (!cl) {
80+
_ParamHelper::printWarning(typeid(P));
81+
return;
82+
}
83+
_ParamHelper::fillKeyValuesImpl(getName(), cl, (void*)this, tree, sKeyToStorageMap);
84+
}
85+
};
86+
} // namespace conf
87+
} // namespace o2
88+
89+
#endif /* COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAMHELPER_H_ */

Common/SimConfig/include/SimConfig/SimConfig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ struct SimConfigData {
3232
std::string mOutputPrefix; // prefix to be used for output files
3333
std::string mLogSeverity; // severity for FairLogger
3434
std::string mLogVerbosity; // loglevel for FairLogger
35+
std::string mKeyValueTokens; // a string holding arbitrary sequence of key-value tokens
36+
// Foo.parameter1=x,Bar.parameter2=y,Baz.paramter3=hello
37+
// (can be used to **loosly** change any configuration parameter from
38+
// command-line)
3539
ClassDefNV(SimConfigData, 1);
3640
};
3741

@@ -84,6 +88,7 @@ class SimConfig
8488
std::string getOutPrefix() const { return mConfigData.mOutputPrefix; }
8589
std::string getLogVerbosity() const { return mConfigData.mLogVerbosity; }
8690
std::string getLogSeverity() const { return mConfigData.mLogSeverity; }
91+
std::string getKeyValueString() const { return mConfigData.mKeyValueTokens; }
8792

8893
private:
8994
SimConfigData mConfigData; //!

0 commit comments

Comments
 (0)