Commit c07274a9 authored by Oleg Golosov's avatar Oleg Golosov Committed by Pierre-Alain Loizeau
Browse files

add json-based config for transport and digitization

parent 48cc7cb6
#pragma once
#include "CbmDefs.h"
#include <FairLogger.h>
#include <TSystem.h>
#include <boost/property_tree/json_parser.hpp>
#include <cassert>
#include <iostream>
#include <map>
#include <regex>
namespace pt = boost::property_tree;
template<class Config_t, class Obj_t>
class CbmConfigBase {
private:
public:
using TagSet_t = std::set<std::string>;
virtual ~CbmConfigBase() = default;
static bool Load(Obj_t& obj, const std::string& path)
{
pt::ptree tree;
LoadFromFile(path, tree);
if (!Load(obj, tree)) return false;
return true;
}
static bool Load(Obj_t& obj, const pt::ptree& tree)
{
SetLogLevel(tree);
auto moduleTree {tree.get_child_optional(Config_t::GetModuleTag())};
if (!moduleTree) {
LOG(error) << "CbmConfig: module tag is not correct!\n";
return false;
}
if (!Validate(moduleTree.get())) return false;
if (!Config_t::LoadImpl(obj, moduleTree.get())) return false;
return true;
}
static void LoadFromFile(const std::string& path, pt::ptree& tree)
{
std::string absPath = path;
if (absPath.at(0) != '/') absPath = gSystem->GetWorkingDirectory() + "/" + path;
LOG(info) << "CbmConfig: loading config from file: " << absPath << std::endl;
pt::read_json(path, tree);
}
static bool Validate(const pt::ptree& tree)
{
const auto validationSet {Config_t::GetValidationTags()};
TagSet_t treeSet;
ParseTree(tree, "", treeSet);
std::vector<std::string> diff;
std::set_difference(treeSet.begin(), treeSet.end(), validationSet.begin(), validationSet.end(),
std::inserter(diff, diff.begin()));
if (!diff.empty()) {
LOG(error) << "CbmConfig: Invalid tags: ";
for (auto s : diff)
std::cout << s << ", ";
std::cout << std::endl;
PrintAvailableTags();
return false;
}
return true;
}
static void ParseTree(const pt::ptree& pt, std::string key, TagSet_t& treeSet)
{
std::string nkey;
if (!key.empty()) {
nkey = key;
if (nkey.back() != '.') nkey += ".";
LOG(debug) << "CbmConfig: key: " << key << "\tnkey: " << nkey;
}
if (pt.empty() /* && !pt.data().empty()*/) {
if (key.back() == '.') key.pop_back();
LOG(debug) << "CbmConfig: Insert: " << key;
if (key.find("#") > key.size()) //used for comments
treeSet.insert(key);
}
for (auto node : pt) {
LOG(debug) << "CbmConfig: Try: " << nkey + node.first;
ParseTree(node.second, nkey + node.first, treeSet);
}
}
static void PrintAvailableTags()
{
auto tags = Config_t::GetValidationTags();
std::cout << "\nAvailable config tags:\n";
for (auto& tag : tags)
std::cout << tag << std::endl;
}
static ECbmModuleId stringToECbmModuleId(std::string s)
{
std::map<std::string, ECbmModuleId> stringToModuleId = {
{"cave", ECbmModuleId::kCave}, {"magnet", ECbmModuleId::kMagnet}, {"pipe", ECbmModuleId::kPipe},
{"target", ECbmModuleId::kTarget}, {"mvd", ECbmModuleId::kMvd}, {"sts", ECbmModuleId::kSts},
{"rich", ECbmModuleId::kRich}, {"much", ECbmModuleId::kMuch}, {"trd", ECbmModuleId::kTrd},
{"tof", ECbmModuleId::kTof}, {"psd", ECbmModuleId::kPsd}, {"hodo", ECbmModuleId::kHodo},
{"shield", ECbmModuleId::kShield}, {"platform", ECbmModuleId::kPlatform}};
if (stringToModuleId.find(s) == stringToModuleId.end()) {
LOG(error) << "CbmConfig: detector subsystem not recognized: " << s;
std::cout << "list of available detector subsystems:\n";
for (auto& p : stringToModuleId)
std::cout << p.first << std::endl;
assert(0);
return ECbmModuleId::kNotExist;
}
else
return stringToModuleId.at(s);
}
static std::string GetStringValue(boost::optional<std::string> opt) { return ParseString(opt.get()); }
static std::string GetStringValue(pt::ptree tree, std::string key, std::string fallback)
{
return ParseString(tree.get<std::string>(key, fallback));
}
static std::string ParseString(std::string s)
{
std::regex rgx("\\$\\{?\\w+\\}?");
std::smatch match;
while (s.find("$") < s.size()) {
std::regex_search(s, match, rgx);
std::string varString = match[0];
std::string varName = std::regex_replace(varString, std::regex("\\$|\\{|\\}"), "");
s.replace(s.find(varString), varString.size(), gSystem->Getenv(varName.c_str()));
}
return s;
}
static void SetLogLevel(const pt::ptree& moduleTree)
{
auto logScreenLevel = moduleTree.get_optional<std::string>("logScreenLevel");
if (logScreenLevel) FairLogger::GetLogger()->SetLogScreenLevel(logScreenLevel.get().c_str());
auto logVerbosityLevel = moduleTree.get_optional<std::string>("logVerbosityLevel");
if (logVerbosityLevel) FairLogger::GetLogger()->SetLogVerbosityLevel(logVerbosityLevel.get().c_str());
}
};
# ===== Generate the needed shell scripts ================================
GENERATE_ROOT_TEST_SCRIPT(${CBMROOT_SOURCE_DIR}/macro/run/run_tra_file.C)
GENERATE_ROOT_TEST_SCRIPT(${CBMROOT_SOURCE_DIR}/macro/run/run_transport_json_config.C)
GENERATE_ROOT_TEST_SCRIPT(${CBMROOT_SOURCE_DIR}/macro/run/run_digi_json_config.C)
GENERATE_ROOT_TEST_SCRIPT(${CBMROOT_SOURCE_DIR}/macro/run/run_tra_beam.C)
GENERATE_ROOT_TEST_SCRIPT(${CBMROOT_SOURCE_DIR}/macro/run/run_digi.C)
GENERATE_ROOT_TEST_SCRIPT(${CBMROOT_SOURCE_DIR}/macro/run/run_reco.C)
......@@ -108,6 +110,30 @@ foreach(setup IN LISTS cbm_setup)
RESOURCE_LOCK beamParDb_${setup}
)
# --- Test run_transport_json_config
# --- Transport run using run_transport_json_config.C
set(testname run_transport_json_config)
add_test(${testname} ${MACRO_DIR}/run_transport_json_config.sh)
set_tests_properties(${testname} PROPERTIES
TIMEOUT ${timeOutTime}
PASS_REGULAR_EXPRESSION "Macro finished successfully"
FIXTURES_REQUIRED cleanup
FIXTURES_SETUP fixt_digi_json_config
RESOURCE_LOCK json_config_ParDb
)
# --- Test run_digi_json_config
# --- Digitization run with using run_digi_json_config.C
set(testname run_digi_json_config)
add_test(${testname} ${MACRO_DIR}/run_digi_json_config.sh)
set_tests_properties(${testname} PROPERTIES
TIMEOUT ${timeOutTime}
PASS_REGULAR_EXPRESSION "Macro finished successfully"
FIXTURES_REQUIRED fixt_digi_json_config
FIXTURES_SETUP fixt_reco_json_config
RESOURCE_LOCK json_config_ParDb
)
# --- Test run_digi_ev
# --- Detector response simulation, event-by-event, using run_digi.C
set(testname run_${sname}_digi_ev)
......@@ -209,7 +235,7 @@ endforeach(setup IN LISTS cbm_setup)
Install(FILES .rootrc run_transport.C run_digi.C run_reco_event.C check_overlaps.C
Install(FILES .rootrc run_transport.C run_transport_json_config.C run_digi_json_config.C config.json run_digi.C run_reco_event.C check_overlaps.C
DESTINATION share/cbmroot/macro/run
)
Install(DIRECTORY modules DESTINATION share/cbmroot/macro/run)
......
{
"logScreenLevel": "INFO",
"logVerbosityLevel": "LOW",
"transport": {
"input": [
{"generator": "unigen",
"file": "${VMCWORKDIR}/input/urqmd.auau.10gev.centr.root"},
{"generator": "pluto",
"file": "${VMCWORKDIR}/input/pluto.auau.8gev.omega.mpmm.0001.root",
"plutoPdg": 0},
{"generator": "beam",
"beamA": 197,
"beamZ": 79,
"beamQ": 79,
"beamP": 12.0,
"beamStartZ": -1.0}
],
"output.path": "",
"target": {
"material": "Gold",
"thickness": 0.025,
"diameter": 2.5,
"position": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"rotation.y": 0.0
},
"beam": {
"position": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"sigmaX": 0.1,
"sigmaY": 0.1
},
"angle": {
"x": 0.0,
"y": 0.0,
"sigmaX": 0.001,
"sigmaY": 0.001
}
},
"randomRP": true,
"geantVersion": 4,
"geant4vmcSettings": {
"physicsLists": "QGSP_BERT_EMV+optical",
"specialProcesses": "stepLimiter",
"maxNsteps": 10000000,
"geant4commands": [""]
},
"geometry": {
"baseSetup": "sis100_electron",
"magneticField": {
"tag": "v18a",
"scale": 1.0,
"position": {
"x": 0.0,
"y": 0.0,
"z": 40.0
}
},
"subsystems": {
"magnet": "v20a",
"pipe": "v16b_1e",
"mvd": "v20c_tr",
"sts": "v19a",
"rich": "v17a_1e",
"trd": "v20b_1e",
"tof": "v20a_1e",
"psd": "v20a",
"platform": "v13a"
}
}
},
"digitization": {
"generateRunInfo": false,
"eventMode": false,
"timeSliceLength": -1.0,
"storeAllTimeSlices": false,
"startTime": 1000,
"produceNoise": true,
"input": [
{"id": 0,
"path": "test",
"rate": "1.e7",
"treeAccessMode": "regular",
"parameterSource": true},
{"id": -1,
"path": "test_emb",
"embedToId": 0,
"treeAccessMode": "regular"},
{"id": -1,
"path": "test_beam",
"rate": "1.e9",
"treeAccessMode": "random"}
],
"output": {
"path": "test",
"overwrite": false
},
"geometry": {
"deactivate": ["", ""],
"#deactivateAllBut": ""
}
}
}
/** @file run_digi_json_config.C
** @author Oleg Golosov <oleg.golosov@gmail.com>
** @since 31 March 2020
**/
// Includes needed for IDE
#if !defined(__CLING__)
#include "CbmDigitization.h"
#include "FairSystemInfo.h"
#include "TStopwatch.h"
#endif
/** @brief Macro for detector response simulation in CBM
** @author Oleg Golosov <oleg.golosov@gmail.com>
** @since 31 March 2021
** @param config Name of config file
** @param nEvents Number of events to process
**
** This macro performs the detector response simulation
** for multiple transport files using test-based config.
** Additions to the CbmDigitization settings can be made after
** the main config is applied.
**/
void run_digi_json_config(std::string config = "", int nEvents = 2)
{
if (config == "") config = Form("%s/macro/run/config.json", gSystem->Getenv("VMCWORKDIR"));
// ----- Timer --------------------------------------------------------
TStopwatch timer;
timer.Start();
// ------------------------------------------------------------------------
CbmDigitization run;
if (CbmDigitizationConfig::Load(run, config)) run.Run(nEvents);
else
return;
// ----- Finish -------------------------------------------------------
timer.Stop();
Double_t rtime = timer.RealTime();
Double_t ctime = timer.CpuTime();
std::cout << "Macro finished successfully." << std::endl;
std::cout << "Real time " << rtime << " s, CPU time " << ctime << "s" << std::endl;
FairSystemInfo sysInfo;
Float_t maxMemory = sysInfo.GetMaxMemory();
std::cout << "<DartMeasurement name=\"MaxMemory\" type=\"numeric/double\">";
std::cout << maxMemory;
std::cout << "</DartMeasurement>" << std::endl;
Float_t cpuUsage = ctime / rtime;
std::cout << "<DartMeasurement name=\"CpuLoad\" type=\"numeric/double\">";
std::cout << cpuUsage;
std::cout << "</DartMeasurement>" << std::endl;
// ------------------------------------------------------------------------
}
/** @file run_transport_json_config.C
** @author Oleg Golosov <oleg.golosov@gmail.com>
** @since 31 March 2020
**/
// Includes needed for IDE
#if !defined(__CLING__)
#include "CbmTransport.h"
#include "FairSystemInfo.h"
#include "TStopwatch.h"
#endif
/** @brief Macro for transport simulation of events from file
** @author Oleg Golosov <oleg.golosov@gmail.com>
** @since 31 March 2021
** @param config Name of config file
** @param nEvents Number of events to transport
**
** This macro performs the transport simulation of externally generated
** events from an input file using test-based config.
** Additions to the CbmTransport settings can be made after
** the main config is applied.
**/
void run_transport_json_config(std::string config = "", int nEvents = 2)
{
if (config == "") config = Form("%s/macro/run/config.json", gSystem->Getenv("VMCWORKDIR"));
// ----- Timer --------------------------------------------------------
TStopwatch timer;
timer.Start();
// ------------------------------------------------------------------------
CbmTransport run;
if (CbmTransportConfig::Load(run, config)) run.Run(nEvents);
else
return;
// ----- Finish -------------------------------------------------------
timer.Stop();
Double_t rtime = timer.RealTime();
Double_t ctime = timer.CpuTime();
std::cout << "Macro finished successfully." << std::endl;
std::cout << "Real time " << rtime << " s, CPU time " << ctime << "s" << std::endl;
FairSystemInfo sysInfo;
Float_t maxMemory = sysInfo.GetMaxMemory();
std::cout << "<DartMeasurement name=\"MaxMemory\" type=\"numeric/double\">";
std::cout << maxMemory;
std::cout << "</DartMeasurement>" << std::endl;
Float_t cpuUsage = ctime / rtime;
std::cout << "<DartMeasurement name=\"CpuLoad\" type=\"numeric/double\">";
std::cout << cpuUsage;
std::cout << "</DartMeasurement>" << std::endl;
// ------------------------------------------------------------------------
}
......@@ -14,12 +14,15 @@ base/CbmDigitizeInfo.cxx
base/CbmMCInput.cxx
base/CbmMCInputSet.cxx
base/CbmRunAna.cxx
base/CbmDigitizationConfig.cxx
)
# ---------------------------------------------------------
# ---- Include directories -------------------------------
set(INCLUDE_DIRECTORIES
${CBMROOT_SOURCE_DIR}/core/config
${CBMROOT_SOURCE_DIR}/sim/response
${CBMROOT_SOURCE_DIR}/sim/response/base
......
......@@ -6,6 +6,7 @@
// --- base
#pragma link C++ class CbmDigitization + ;
#pragma link C++ class CbmDigitizationConfig + ;
#pragma link C++ class CbmDigitizationSource + ;
#pragma link C++ class CbmDigitizeInfo + ;
#pragma link C++ class CbmMCInput + ;
......
#include "CbmDigitizationConfig.h"
#include "CbmDigitization.h"
using namespace std;
string CbmDigitizationConfig::GetModuleTag() { return "digitization"; }
CbmDigitizationConfig::TagSet_t CbmDigitizationConfig::GetValidationTags()
{
return {"logScreenLevel",
"logVerbosityLevel",
"generateRunInfo",
"storeAllTimeSlices",
"eventMode",
"startTime",
"timeSliceLength",
"produceNoise",
"input.id",
"input.path",
"input.rate",
"input.embedToId",
"input.treeAccessMode",
"input.parameterSource",
"output.overwrite",
"output.path",
"geometry.path",
"geometry.sourceId",
"geometry.deactivate",
"geometry.deactivateAllBut"};
}
bool CbmDigitizationConfig::SetIO(CbmDigitization& obj, const pt::ptree& moduleTree)
{
map<string, ECbmTreeAccess> stringToECbmTreeAccess = {{"regular", ECbmTreeAccess::kRegular},
{"repeat", ECbmTreeAccess::kRepeat},
{"random", ECbmTreeAccess::kRandom}};
bool eventMode = moduleTree.get<bool>("eventMode", false);
auto inputs = moduleTree.get_child("input");
uint inputCounter = 0;
vector<string> paths;
bool parametersSet = false;
string parametersPath = "";
ECbmTreeAccess treeAccessMode;
for (auto& input : inputs) {
pt::ptree pt_input = input.second;
int id = pt_input.get<int>("id", inputCounter);
string path = GetStringValue(pt_input, "path", "");
auto configRate = pt_input.get_optional<float>("rate");
string treeAccessModeString = pt_input.get<string>("treeAccessMode", "regular");
bool parameterSource = pt_input.get<bool>("parameterSource", false);
auto configEmbedToId = pt_input.get_optional<int>("embedToId");
if (id < 0) continue;
if (path == "") {
LOG(error) << "CbmDigitizationConfig: no path specified for input #" << id;
return false;
}
paths.push_back(path);
string traFileName = path + ".tra.root";
if (stringToECbmTreeAccess.find(treeAccessModeString) != stringToECbmTreeAccess.end())
treeAccessMode = stringToECbmTreeAccess.at(treeAccessModeString);
else {
LOG(error) << "CbmDigitizationConfig: invalid tree access mode: " << treeAccessModeString;
cout << "Available access modes:\n";
for (auto& p : stringToECbmTreeAccess)
cout << p.first << endl;
return false;
}
if (configEmbedToId) {
if (configRate) {
LOG(error) << "CbmDigitizationConfig: input.embedToId and input.rate should not be used simultaneously!";
return false;
}
else {
int embedToId = configEmbedToId.get();
LOG(info) << "CbmDigitizationConfig: Embedding input: " << traFileName;
obj.EmbedInput(id, traFileName, embedToId);
}
}
else {
if (eventMode && inputCounter > 0) {
LOG(error) << "CbmDigitizationConfig: event mixing is not possible in event-by-event mode!";
return false;
}
float rate = pt_input.get<float>("rate", -1.);
LOG(info) << "CbmDigitizationConfig: Adding input: " << traFileName;
obj.AddInput(id, path + ".tra.root", rate, treeAccessMode);
}
if (parameterSource) {
if (!parametersSet) { parametersPath = path; }
else {
LOG(error) << "CbmDigitizationConfig: only one parameter source is allowed!";
return false;
}
}
inputCounter++;
}
string outputPath = GetStringValue(moduleTree, "output.path", paths.at(0));
bool overwrite = moduleTree.get<bool>("output.overwrite", false);
if (!parametersSet) parametersPath = paths.at(0);
LOG(info) << "CbmDigitizationConfig: Parameter source:\n" << parametersPath;
obj.SetParameterRootFile(parametersPath + ".par.root");
LOG(info) << "CbmDigitizationConfig: Output path:\n" << outputPath;
obj.SetOutputFile(outputPath + ".raw.root", overwrite);
obj.SetMonitorFile((outputPath + ".moni_digi.root").c_str());
return true;
}
bool CbmDigitizationConfig::SetDigitizationParameters(CbmDigitization& obj, const pt::ptree& moduleTree)
{
auto produceNoise = moduleTree.get_optional<bool>("produceNoise");
bool eventMode = moduleTree.get<bool>("eventMode", false);
auto storeAllTimeSlices = moduleTree.get_optional<bool>("storeAllTimeSlices");
auto timeSliceLength = moduleTree.get_optional<float>("timeSliceLength");
auto startTime = moduleTree.get_optional<float>("startTime");
if (eventMode) {
if (storeAllTimeSlices || timeSliceLength || startTime) {
LOG(error) << "CbmDigitizationConfig: time slice settings should not be used in event mode!";
return false;
}
}
obj.SetEventMode(eventMode);
if (timeSliceLength) obj.SetTimeSliceLength(timeSliceLength.get());
if (startTime) obj.SetStartTime(startTime.get());
if (produceNoise) obj.SetProduceNoise(produceNoise.get());
return true;
}
bool CbmDigitizationConfig::SetGeometry(CbmDigitization& obj, const pt::ptree& moduleTree)
{
auto modulesToDeactivate = moduleTree.get_child_optional("geometry.deactivate");
auto deactivateAllBut = moduleTree.get_optional<string>("geometry.deactivateAllBut");
if (modulesToDeactivate && deactivateAllBut) {
LOG(error)
<< "CbmDigitizationConfig: geometry.deactivate and geometry.deactivateAllBut should not be used simultaneously!";
return false;