diff --git a/Common/Utils/include/CommonUtils/ConfigurableParam.h b/Common/Utils/include/CommonUtils/ConfigurableParam.h index 717a4c425fc82..f44d9efcaea76 100644 --- a/Common/Utils/include/CommonUtils/ConfigurableParam.h +++ b/Common/Utils/include/CommonUtils/ConfigurableParam.h @@ -321,17 +321,19 @@ class ConfigurableParam } // end namespace o2 // a helper macro for boilerplate code in parameter classes -#define O2ParamDef(classname, key) \ - public: \ - classname(TRootIOCtor*) {} \ - classname(classname const&) = delete; \ - \ - private: \ - static constexpr char const* const sKey = key; \ - static classname sInstance; \ - classname() = default; \ - template \ - friend class o2::conf::ConfigurableParamHelper; +#define O2ParamDef(classname, key) \ + public: \ + classname(TRootIOCtor*) {} \ + classname(classname const&) = delete; \ + \ + private: \ + static constexpr char const* const sKey = key; \ + static classname sInstance; \ + classname() = default; \ + template \ + friend class o2::conf::ConfigurableParamHelper; \ + template \ + friend class o2::conf::ConfigurableParamPromoter; // a helper macro to implement necessary symbols in source #define O2ParamImpl(classname) classname classname::sInstance; diff --git a/Common/Utils/include/CommonUtils/ConfigurableParamHelper.h b/Common/Utils/include/CommonUtils/ConfigurableParamHelper.h index 1dc5d5c4c38f8..7d9cb78bb9968 100644 --- a/Common/Utils/include/CommonUtils/ConfigurableParamHelper.h +++ b/Common/Utils/include/CommonUtils/ConfigurableParamHelper.h @@ -45,18 +45,18 @@ class _ParamHelper { private: static std::vector* getDataMembersImpl(std::string const& mainkey, TClass* cl, void*, - std::map const* provmap); + std::map const* provmap, size_t virtualoffset); static void fillKeyValuesImpl(std::string const& mainkey, TClass* cl, void*, boost::property_tree::ptree*, std::map>*, - EnumRegistry*); + EnumRegistry*, size_t offset); static void printWarning(std::type_info const&); static void assignmentImpl(std::string const& mainkey, TClass* cl, void* to, void* from, - std::map* provmap); + std::map* provmap, size_t offset); static void syncCCDBandRegistry(std::string const& mainkey, TClass* cl, void* to, void* from, - std::map* provmap); + std::map* provmap, size_t offset); static void outputMembersImpl(std::ostream& out, std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger); static void printMembersImpl(std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger); @@ -65,6 +65,9 @@ class _ParamHelper template friend class ConfigurableParamHelper; + + template + friend class ConfigurableParamPromoter; }; // ---------------------------------------------------------------- @@ -140,7 +143,7 @@ class ConfigurableParamHelper : virtual public ConfigurableParam return nullptr; } - return _ParamHelper::getDataMembersImpl(getName(), cl, (void*)this, sValueProvenanceMap); + return _ParamHelper::getDataMembersImpl(getName(), cl, (void*)this, sValueProvenanceMap, 0); } // ---------------------------------------------------------------- @@ -153,7 +156,7 @@ class ConfigurableParamHelper : virtual public ConfigurableParam _ParamHelper::printWarning(typeid(P)); return; } - _ParamHelper::fillKeyValuesImpl(getName(), cl, (void*)this, tree, sKeyToStorageMap, sEnumRegistry); + _ParamHelper::fillKeyValuesImpl(getName(), cl, (void*)this, tree, sKeyToStorageMap, sEnumRegistry, 0); } // ---------------------------------------------------------------- @@ -167,7 +170,7 @@ class ConfigurableParamHelper : virtual public ConfigurableParam file->GetObject(getName().c_str(), readback); if (readback != nullptr) { _ParamHelper::assignmentImpl(getName(), TClass::GetClass(typeid(P)), (void*)this, (void*)readback, - sValueProvenanceMap); + sValueProvenanceMap, 0); delete readback; } setRegisterMode(true); @@ -185,7 +188,146 @@ class ConfigurableParamHelper : virtual public ConfigurableParam // setRegisterMode(false); _ParamHelper::syncCCDBandRegistry(getName(), TClass::GetClass(typeid(P)), (void*)this, (void*)externalobj, - sValueProvenanceMap); + sValueProvenanceMap, 0); + setRegisterMode(true); + } + + // ---------------------------------------------------------------- + + void serializeTo(TFile* file) const final + { + file->WriteObjectAny((void*)this, TClass::GetClass(typeid(P)), getName().c_str()); + } +}; + +// Promotes a simple struct Base to a configurable parameter class +// Aka implements all interfaces for a ConfigurableParam P, which shares or +// takes the fields from a Base struct +template +class ConfigurableParamPromoter : public Base, virtual public ConfigurableParam +{ + public: + using ConfigurableParam::ConfigurableParam; + + static const P& Instance() + { + return P::sInstance; + } + + // extracts a copy of the underlying data struct + Base detach() const + { + static_assert(std::copyable, "Base type must be copyable."); + return static_cast(*this); + } + + // ---------------------------------------------------------------- + std::string getName() const final + { + return P::sKey; + } + + // ---------------------------------------------------------------- + // get the provenace of the member with given key + EParamProvenance getMemberProvenance(const std::string& key) const final + { + return getProvenance(getName() + '.' + key); + } + + // ---------------------------------------------------------------- + + // one of the key methods, using introspection to print itself + void printKeyValues(bool showProv = true, bool useLogger = false) const final + { + if (!isInitialized()) { + initialize(); + } + auto members = getDataMembers(); + _ParamHelper::printMembersImpl(getName(), members, showProv, useLogger); + } + + // + size_t getHash() const final + { + return _ParamHelper::getHashImpl(getName(), getDataMembers()); + } + + // ---------------------------------------------------------------- + + void output(std::ostream& out) const final + { + auto members = getDataMembers(); + _ParamHelper::outputMembersImpl(out, getName(), members, true, false); + } + + // ---------------------------------------------------------------- + + // Grab the list of ConfigurableParam data members + // Returns a nullptr if the TClass of the P template class cannot be created. + std::vector* getDataMembers() const + { + // just a helper line to make sure P::sInstance is looked-up + // and that compiler complains about missing static sInstance of type P + // volatile void* ptr = (void*)&P::sInstance; + // static assert on type of sInstance: + static_assert(std::is_same::value, + "static instance must of same type as class"); + + // obtain the TClass for the Base type and delegate further + auto cl = TClass::GetClass(typeid(Base)); + if (!cl) { + _ParamHelper::printWarning(typeid(Base)); + return nullptr; + } + + // we need to put an offset of 8 bytes since internally this is using data members of the Base class + // which doesn't account for the virtual table of P + return _ParamHelper::getDataMembersImpl(getName(), cl, (void*)this, sValueProvenanceMap, 8); + } + + // ---------------------------------------------------------------- + + // fills the data structures with the initial default values + void putKeyValues(boost::property_tree::ptree* tree) final + { + auto cl = TClass::GetClass(typeid(Base)); + if (!cl) { + _ParamHelper::printWarning(typeid(Base)); + return; + } + _ParamHelper::fillKeyValuesImpl(getName(), cl, (void*)this, tree, sKeyToStorageMap, sEnumRegistry, 8); + } + + // ---------------------------------------------------------------- + + void initFrom(TFile* file) final + { + // switch off auto registering since the readback object is + // only a "temporary" singleton + setRegisterMode(false); + P* readback = nullptr; + file->GetObject(getName().c_str(), readback); + if (readback != nullptr) { + _ParamHelper::assignmentImpl(getName(), TClass::GetClass(typeid(Base)), (void*)this, (void*)readback, + sValueProvenanceMap, 8); + delete readback; + } + setRegisterMode(true); + } + + // ---------------------------------------------------------------- + + void syncCCDBandRegistry(void* externalobj) final + { + // We may be getting an external copy from CCDB which is passed as externalobj. + // The task of this function is to + // a) update the internal registry with fields coming from CCDB + // but only if keys have not been modified via RT == command line / ini file + // b) update the external object with with fields having RT provenance + // + setRegisterMode(false); + _ParamHelper::syncCCDBandRegistry(getName(), TClass::GetClass(typeid(Base)), (void*)this, (void*)externalobj, + sValueProvenanceMap, 8); setRegisterMode(true); } diff --git a/Common/Utils/src/ConfigurableParamHelper.cxx b/Common/Utils/src/ConfigurableParamHelper.cxx index 0fb213b722e26..f217d402bcb45 100644 --- a/Common/Utils/src/ConfigurableParamHelper.cxx +++ b/Common/Utils/src/ConfigurableParamHelper.cxx @@ -182,19 +182,19 @@ std::string asString(TDataMember const& dm, char* pointer) // potentially other cases to be added here LOG(error) << "COULD NOT REPRESENT AS STRING"; - return nullptr; + return std::string(); } // ---------------------------------------------------------------------- std::vector* _ParamHelper::getDataMembersImpl(std::string const& mainkey, TClass* cl, void* obj, - std::map const* provmap) + std::map const* provmap, size_t globaloffset) { std::vector* members = new std::vector; - auto toDataMember = [&members, obj, mainkey, provmap](const TDataMember* dm, int index, int size) { + auto toDataMember = [&members, obj, mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) { auto TS = getSizeOfUnderlyingType(*dm); - char* pointer = ((char*)obj) + dm->GetOffset() + index * TS; + char* pointer = ((char*)obj) + dm->GetOffset() + index * TS + globaloffset; const std::string name = getName(dm, index, size); auto value = asString(*dm, pointer); @@ -280,14 +280,14 @@ std::type_info const& nameToTypeInfo(const char* tname, TDataType const* dt) void _ParamHelper::fillKeyValuesImpl(std::string const& mainkey, TClass* cl, void* obj, boost::property_tree::ptree* tree, std::map>* keytostoragemap, - EnumRegistry* enumRegistry) + EnumRegistry* enumRegistry, size_t globaloffset) { boost::property_tree::ptree localtree; - auto fillMap = [obj, &mainkey, &localtree, &keytostoragemap, &enumRegistry](const TDataMember* dm, int index, int size) { + auto fillMap = [obj, &mainkey, &localtree, &keytostoragemap, &enumRegistry, globaloffset](const TDataMember* dm, int index, int size) { const auto name = getName(dm, index, size); auto dt = dm->GetDataType(); auto TS = getSizeOfUnderlyingType(*dm); - char* pointer = ((char*)obj) + dm->GetOffset() + index * TS; + char* pointer = ((char*)obj) + dm->GetOffset() + index * TS + globaloffset; localtree.put(name, asString(*dm, pointer)); auto key = mainkey + "." + name; @@ -355,14 +355,14 @@ bool isMemblockDifferent(char const* block1, char const* block2, int sizeinbytes // ---------------------------------------------------------------------- void _ParamHelper::assignmentImpl(std::string const& mainkey, TClass* cl, void* to, void* from, - std::map* provmap) + std::map* provmap, size_t globaloffset) { - auto assignifchanged = [to, from, &mainkey, provmap](const TDataMember* dm, int index, int size) { + auto assignifchanged = [to, from, &mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) { const auto name = getName(dm, index, size); auto dt = dm->GetDataType(); auto TS = getSizeOfUnderlyingType(*dm); - char* pointerto = ((char*)to) + dm->GetOffset() + index * TS; - char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS; + char* pointerto = ((char*)to) + dm->GetOffset() + index * TS + globaloffset; + char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS + globaloffset; // lambda to update the provenance auto updateProv = [&mainkey, name, provmap]() { @@ -402,14 +402,14 @@ void _ParamHelper::assignmentImpl(std::string const& mainkey, TClass* cl, void* // ---------------------------------------------------------------------- void _ParamHelper::syncCCDBandRegistry(const std::string& mainkey, TClass* cl, void* to, void* from, - std::map* provmap) + std::map* provmap, size_t globaloffset) { - auto sync = [to, from, &mainkey, provmap](const TDataMember* dm, int index, int size) { + auto sync = [to, from, &mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) { const auto name = getName(dm, index, size); auto dt = dm->GetDataType(); auto TS = getSizeOfUnderlyingType(*dm); - char* pointerto = ((char*)to) + dm->GetOffset() + index * TS; - char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS; + char* pointerto = ((char*)to) + dm->GetOffset() + index * TS + globaloffset; + char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS + globaloffset; // check current provenance auto key = mainkey + "." + name; diff --git a/Generators/CMakeLists.txt b/Generators/CMakeLists.txt index d60d185817c84..d909b3e604887 100644 --- a/Generators/CMakeLists.txt +++ b/Generators/CMakeLists.txt @@ -129,8 +129,15 @@ if(doBuildSimulation) COMPONENT_NAME Generator LABELS generator PUBLIC_LINK_LIBRARIES O2::Generators) + + o2_add_test(GeneratorPythia8Param NAME test_Generator_test_GeneratorPythia8Param + SOURCES test/test_GeneratorPythia8Param.cxx + COMPONENT_NAME Generator + LABELS generator + PUBLIC_LINK_LIBRARIES O2::Generators) endif() + o2_add_test_root_macro(share/external/tgenerator.C PUBLIC_LINK_LIBRARIES O2::Generators LABELS generators) diff --git a/Generators/include/Generators/GeneratorPythia8Param.h b/Generators/include/Generators/GeneratorPythia8Param.h index 165b1622239f5..612964fca73d9 100644 --- a/Generators/include/Generators/GeneratorPythia8Param.h +++ b/Generators/include/Generators/GeneratorPythia8Param.h @@ -24,28 +24,21 @@ namespace eventgen { /** - ** a parameter class/struct to keep the settings of - ** the Pythia8 event generator and - ** allow the user to modify them + ** a parameter class/struct to configure the settings of + ** the GeneratorPythia8 event generator **/ - -struct GeneratorPythia8Param : public o2::conf::ConfigurableParamHelper { +struct Pythia8GenConfig { std::string config = ""; std::string hooksFileName = ""; std::string hooksFuncName = ""; bool includePartonEvent = false; // whether to keep the event before hadronization std::string particleFilter = ""; // user particle filter int verbose = 0; // verbose control (if > 0 may show more info messages about what is going on) - O2ParamDef(GeneratorPythia8Param, "GeneratorPythia8"); }; -struct Pythia8GenConfig { - std::string config = ""; - std::string hooksFileName = ""; - std::string hooksFuncName = ""; - bool includePartonEvent = false; // whether to keep the event before hadronization - std::string particleFilter = ""; // user particle filter - int verbose = 0; // verbose control (if > 0 may show more info messages about what is going on) +// construct a configurable param singleton out of the Pythia8GenConfig struct +struct GeneratorPythia8Param : public o2::conf::ConfigurableParamPromoter { + O2ParamDef(GeneratorPythia8Param, "GeneratorPythia8"); }; } // end namespace eventgen diff --git a/Generators/src/GeneratorPythia8.cxx b/Generators/src/GeneratorPythia8.cxx index 8c9b4fcffdff2..fef2c4d2e9a1c 100644 --- a/Generators/src/GeneratorPythia8.cxx +++ b/Generators/src/GeneratorPythia8.cxx @@ -45,26 +45,11 @@ namespace eventgen /*****************************************************************/ /*****************************************************************/ -GeneratorPythia8::GeneratorPythia8() : Generator("ALICEo2", "ALICEo2 Pythia8 Generator") +// the default construct uses the GeneratorPythia8Param singleton to extract a config and delegates +// to the proper constructor +GeneratorPythia8::GeneratorPythia8() : GeneratorPythia8(GeneratorPythia8Param::Instance().detach()) { - /** default constructor **/ - - mInterface = reinterpret_cast(&mPythia); - mInterfaceName = "pythia8"; - - auto& param = GeneratorPythia8Param::Instance(); - LOG(info) << "Default Instance \'Pythia8\' generator with following parameters"; - LOG(info) << param; - - // convert the outside singleton config to the internally used one - o2::eventgen::Pythia8GenConfig config{param.config, - param.hooksFileName, param.hooksFuncName, param.includePartonEvent, param.particleFilter, param.verbose}; - mGenConfig = config; - - setConfig(config.config); - setHooksFileName(config.hooksFileName); - setHooksFuncName(config.hooksFuncName); - // TODO: use constructor delegation to other interface + LOG(info) << "GeneratorPythia8 constructed from GeneratorPythia8Param ConfigurableParam"; } /*****************************************************************/ diff --git a/Generators/src/GeneratorPythia8Param.cxx b/Generators/src/GeneratorPythia8Param.cxx index 984680e46ad01..6b477beb16ba9 100644 --- a/Generators/src/GeneratorPythia8Param.cxx +++ b/Generators/src/GeneratorPythia8Param.cxx @@ -12,4 +12,4 @@ /// \author R+Preghenella - January 2020 #include "Generators/GeneratorPythia8Param.h" -O2ParamImpl(o2::eventgen::GeneratorPythia8Param); +O2ParamImpl(o2::eventgen::GeneratorPythia8Param); \ No newline at end of file diff --git a/Generators/src/GeneratorsLinkDef.h b/Generators/src/GeneratorsLinkDef.h index 41e14b02f18b9..fe219c6f5476c 100644 --- a/Generators/src/GeneratorsLinkDef.h +++ b/Generators/src/GeneratorsLinkDef.h @@ -48,8 +48,9 @@ #pragma link C++ class o2::eventgen::GeneratorPythia8Param + ; #pragma link C++ class o2::eventgen::Pythia8GenConfig + ; #pragma link C++ class o2::eventgen::DecayerPythia8Param + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::GeneratorPythia8Param> + ; +#pragma link C++ class o2::conf::ConfigurableParamPromoter < o2::eventgen::GeneratorPythia8Param, o2::eventgen::Pythia8GenConfig> + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::DecayerPythia8Param> + ; + #pragma link C++ class o2::eventgen::GeneratorFactory + ; #endif #if defined(GENERATORS_WITH_PYTHIA8) && defined(GENERATORS_WITH_HEPMC3) diff --git a/Generators/test/test_GeneratorPythia8Param.cxx b/Generators/test/test_GeneratorPythia8Param.cxx new file mode 100644 index 0000000000000..c735487ea293c --- /dev/null +++ b/Generators/test/test_GeneratorPythia8Param.cxx @@ -0,0 +1,80 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#define BOOST_TEST_MODULE Test GeneratorPythia8Param class +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include +#include +#include +#include +#include "CCDB/BasicCCDBManager.h" + +// Tests various aspects of the +// ConfigurableParamPromoter class, which is used to promote +// Pythia8GenConfig to a configurable param +BOOST_AUTO_TEST_CASE(pythia8_Pythia8GenConfig) +{ + o2::conf::ConfigurableParam::updateFromString( + "GeneratorPythia8.config=Foo;GeneratorPythia8.includePartonEvent=true"); + + using o2::eventgen::GeneratorPythia8Param; + + BOOST_CHECK_EQUAL(GeneratorPythia8Param::Instance().config, std::string("Foo")); + BOOST_CHECK_EQUAL(GeneratorPythia8Param::Instance().includePartonEvent, true); + + BOOST_CHECK_EQUAL(GeneratorPythia8Param::Instance().includePartonEvent, o2::conf::ConfigurableParam::getValueAs("GeneratorPythia8.includePartonEvent")); + // setValue - getValue + o2::conf::ConfigurableParam::setValue("GeneratorPythia8.config", "Baz"); + BOOST_CHECK_EQUAL(o2::conf::ConfigurableParam::getValueAs("GeneratorPythia8.config"), std::string("Baz")); + BOOST_CHECK_EQUAL(GeneratorPythia8Param::Instance().config, std::string("Baz")); + + // member provenance + BOOST_CHECK_EQUAL(GeneratorPythia8Param::Instance().getMemberProvenance("config"), o2::conf::ConfigurableParam::EParamProvenance::kRT); + BOOST_CHECK_EQUAL(GeneratorPythia8Param::Instance().getMemberProvenance("verbose"), o2::conf::ConfigurableParam::EParamProvenance::kCODE); + + // config detach + auto config_copy = GeneratorPythia8Param::Instance().detach(); + BOOST_CHECK_EQUAL(config_copy.config, std::string("Baz")); + BOOST_CHECK_EQUAL(config_copy.includePartonEvent, true); + + // file IO + TFile tmp_file("GeneratorParamConfig_tmp.root", "RECREATE"); + + GeneratorPythia8Param::Instance().serializeTo(&tmp_file); + // modify the instance to some intermediate fluent value + o2::conf::ConfigurableParam::setValue("GeneratorPythia8.includePartonEvent", "0"); + BOOST_CHECK_EQUAL(config_copy.includePartonEvent, true); + BOOST_CHECK_EQUAL(GeneratorPythia8Param::Instance().includePartonEvent, false); + tmp_file.Close(); + + // read back + TFile tmp_file2("GeneratorParamConfig_tmp.root", "READ"); + const_cast(GeneratorPythia8Param::Instance()).initFrom(&tmp_file2); + BOOST_CHECK_EQUAL(GeneratorPythia8Param::Instance().includePartonEvent, true); + tmp_file2.Close(); + + // CCDB IO + std::string ccdbUrl = "http://ccdb-test.cern.ch:8080"; + bool hostReachable = false; + o2::ccdb::CcdbApi api; + api.init(ccdbUrl); + std::string pathA = "/Generators/UnitTest/Pythia8/GeneratorPythia8Param"; + std::map md; + long start = 1000, stop = 2000; + api.storeAsTFileAny(&GeneratorPythia8Param::Instance(), pathA, md, start, stop); + + // modify the instance to some intermediate fluent value + o2::conf::ConfigurableParam::setValue("GeneratorPythia8.includePartonEvent", "0"); + + auto returnedobj = api.retrieveFromTFileAny(pathA, md, (start + stop) / 2); + GeneratorPythia8Param::Instance().printKeyValues(); +};