Skip to content

Commit

Permalink
Merge pull request #239 from ManuelHu/analysis-mt
Browse files Browse the repository at this point in the history
Analysis manager refactoring
  • Loading branch information
gipert authored Jan 29, 2025
2 parents 115a62b + 9186f46 commit 401c56e
Show file tree
Hide file tree
Showing 35 changed files with 896 additions and 176 deletions.
36 changes: 34 additions & 2 deletions docs/rmg-commands.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions examples/05-MUSUN/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,14 @@ endif()

add_executable(05-MUSUN main.cc HPGeTestStand.hh HPGeTestStand.cc)
target_link_libraries(05-MUSUN PUBLIC RMG::remage)

# collect auxiliary files
file(
GLOB _aux
RELATIVE ${PROJECT_SOURCE_DIR}
*.csv *.mac)

# copy them to the build area
foreach(_file ${_aux})
configure_file(${PROJECT_SOURCE_DIR}/${_file} ${PROJECT_BINARY_DIR}/${_file} COPYONLY)
endforeach()
4 changes: 3 additions & 1 deletion examples/05-MUSUN/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ int main(int argc, char** argv) {
manager.SetUserInit(new HPGeTestStand());
manager.SetNumberOfThreads(2);
manager.EnablePersistency();
manager.SetOutputOverwriteFiles(true);
manager.GetDetectorConstruction()->RegisterDetector(kGermanium, "HPGe1", 0);
manager.GetDetectorConstruction()->RegisterDetector(kGermanium, "HPGe2", 1);
manager.GetDetectorConstruction()->RegisterDetector(kGermanium, "HPGe3", 2);
Expand All @@ -22,5 +23,6 @@ int main(int argc, char** argv) {
manager.Initialize();
manager.Run();

return 0;
if (manager.HadError()) return 1;
return manager.HadWarning() ? 2 : 0;
}
6 changes: 2 additions & 4 deletions examples/05-MUSUN/run.mac
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@

/RMG/Output/FileName output.root



/RMG/Generator/Confine UnConfined
/RMG/Generator/Select MUSUNCosmicMuons

/RMG/Generator/MUSUNCosmicMuons/SetMUSUNFile MUSUN_10k_events.csv
/RMG/Generator/MUSUNCosmicMuons/SetMUSUNFile MUSUN_100_events.csv

/run/beamOn 1000
/run/beamOn 100
152 changes: 146 additions & 6 deletions include/RMGAnalysisReader.hh
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,114 @@
#ifndef _RMG_ANALYSIS_READER_HH_
#define _RMG_ANALYSIS_READER_HH_

#include <map>
#include <string>

#include "G4ThreeVector.hh"
#include "G4AutoLock.hh"
#include "G4VAnalysisReader.hh"

#include "RMGVVertexGenerator.hh"
#include "RMGConfig.hh"

class RMGAnalysisReader {
/**
* @brief wrapper around @ref G4VAnalysisReader instances with special handling for LH5 files.
*
* @details notes for threadsafe use:
* - opening/closing can only be performed on the master thread.
* - in a multithreaded application, all function calls are guarded by a mutex. Worker
* threads can use the reader instance after opening, but only one worker can use the reader at a
* time.
* - the reader access handles are generally thread-safe, if no other thread uses any
* @ref G4VAnalysisReader directly.
* - the reader should only be bound once to variables that are of static
* storage duration. Example: only bind to static class fields to read the values into. Do only
* unloack the reader access handle after reading/checking the read data.
*/
class RMGAnalysisReader final {

public:

/**
* @brief thread-safe access handle to the underlying reader. This handle can be used to set-up
* ntuple reading (in setup mode) or to read rows from the ntuple. */
class Access final {
friend class RMGAnalysisReader;

public:

~Access() { unlock(); }

Access(Access const&) = delete;
Access& operator=(Access&) = delete;
Access& operator=(Access const&) = delete;
Access& operator=(Access&&) = delete;

/**
* @brief unlock this access handle before it exits the scope. */
void unlock() {
fReader = nullptr;
fNtupleId = -1;
fUnits = nullptr;
if (fLock) { fLock.unlock(); }
}

/**
* @brief wraps @ref G4VAnalysisReader::GetNtupleRow. */
[[nodiscard]] auto GetNtupleRow() {
AssertSetup(false);
return fReader->GetNtupleRow(fNtupleId);
}
/**
* @brief wraps @ref G4VAnalysisReader::SetNtupleDColumn. */
auto SetNtupleDColumn(const std::string& name, G4double& value,
const std::vector<std::string>& allowed_units = {}) {
AssertSetup(true);
AssertUnit(name, allowed_units);
return fReader->SetNtupleDColumn(fNtupleId, name, value);
}
/**
* @brief wraps @ref G4VAnalysisReader::SetNtupleFColumn. */
auto SetNtupleFColumn(const std::string& name, G4float& value,
const std::vector<std::string>& allowed_units = {}) {
AssertSetup(true);
AssertUnit(name, allowed_units);
return fReader->SetNtupleFColumn(fNtupleId, name, value);
}
/**
* @brief wraps @ref G4VAnalysisReader::SetNtupleIColumn. */
auto SetNtupleIColumn(const std::string& name, G4int& value,
const std::vector<std::string>& allowed_units = {}) {
AssertSetup(true);
AssertUnit(name, allowed_units);
return fReader->SetNtupleIColumn(fNtupleId, name, value);
}

/**
* @brief get unit information for the column. an empty string means either no unit
* attached or no support by the file format. */
[[nodiscard]] std::string GetUnit(const std::string& name) const;

/**
* @brief check whether this access handle is still valid. */
operator bool() const { return fReader != nullptr && fNtupleId >= 0 && fLock; }

private:

// only allow creation or moving in parent.
inline Access(G4AutoLock lock, G4VAnalysisReader* reader, int nt,
const std::map<std::string, std::string>* u, bool setup)
: fLock(std::move(lock)), fReader(reader), fNtupleId(nt), fUnits(u), fCanSetup(setup) {};
Access(Access&&) = default;

void AssertUnit(const std::string& name, const std::vector<std::string>& allowed_units) const;
void AssertSetup(bool setup) const;

G4VAnalysisReader* fReader = nullptr;
int fNtupleId = -1;
const std::map<std::string, std::string>* fUnits;
G4AutoLock fLock;
bool fCanSetup = false;
};

RMGAnalysisReader() = default;
~RMGAnalysisReader() = default;

Expand All @@ -35,18 +132,61 @@ class RMGAnalysisReader {
RMGAnalysisReader(RMGAnalysisReader&&) = delete;
RMGAnalysisReader& operator=(RMGAnalysisReader&&) = delete;

bool OpenFile(std::string& name, std::string ntuple_dir_name, std::string ntuple_name);
/**
* @brief open an input file for reading of one specific ntuple. The return access handle can be
* used to connect to-be-read column to variables.
*
* @details This function can only be used on the master thread. This operation acquires a
* global lock across all readers that will be held until the access handle is discarded.
*
* @param file_name the input file name. the file format is determined from the file extension.
* @param ntuple_dir_name the first part of the input table name. For the table addressed by
* "dir/table" this is "dir".
* @param ntuple_name the first part of the input table name. For the table addressed by
* "dir/table" this is "table".
* @param lock a lock instance obtained by @ref RMGAnalysisReader::GetLock - optional.
* @param force_ext force a file extension/reader type. should be a lowercase file extension
* like @c lh5, @c hdf5, @c csv, @c root.
*/
[[nodiscard]] Access OpenFile(const std::string& file_name, std::string ntuple_dir_name,
std::string ntuple_name, G4AutoLock lock, std::string force_ext = "");
/**
* @overload */
[[nodiscard]] Access OpenFile(const std::string& file_name, std::string ntuple_dir_name,
std::string ntuple_name, std::string force_ext = "");

/**
* @brief if any file is open for reading, close the reader. Also clean-up temporary files.
*
* @details This function can only be used on the master thread. This operation acquires a global
* across all readers. This function will not actually free resources allocated for the reader by Geant4. */
void CloseFile();

[[nodiscard]] inline auto GetReader() { return fReader; }
[[nodiscard]] inline auto GetNtupleId() const { return fNtupleId; }
/**
* @brief get an access handle to the current underlying G4VAnalysisReader.
*
* @details The return access handle can be used to read row(s) from the ntuple. This function
* acquires a global across all readers that will be held until the access handle is discarded. */
[[nodiscard]] Access GetLockedReader() const;

/**
* @brief acquires a global lock to the analysis reader mutex. */
[[nodiscard]] G4AutoLock GetLock() const;

/**
* @brief get the file name of the current open file, or an empty string. */
[[nodiscard]] inline auto& GetFileName() const { return fFileName; }

private:

static G4Mutex fMutex;

G4VAnalysisReader* fReader = nullptr;
int fNtupleId = -1;

std::map<std::string, std::string> fUnits{};
bool fHasUnits = false;

std::string fFileName;
bool fFileIsTemp = false;
};
Expand Down
8 changes: 5 additions & 3 deletions include/RMGConvertLH5.hh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#define _RMG_CONVERT_LH5_HH

#include <filesystem>
#include <map>
#include <memory>
#include <optional>
#include <regex>
Expand All @@ -33,7 +34,8 @@ class RMGConvertLH5 {
public:

static bool ConvertToLH5(std::string, std::string, bool, bool part_of_batch = false);
static bool ConvertFromLH5(std::string, std::string, bool, bool part_of_batch = false);
static bool ConvertFromLH5(std::string, std::string, bool, bool part_of_batch,
std::map<std::string, std::map<std::string, std::string>>&);

inline static bool fIsStandalone = false;

Expand Down Expand Up @@ -77,12 +79,12 @@ class RMGConvertLH5 {
////////////////////////////////////////////////////////////////////////////////////////////
// LH5 -> HDF5 (input files):

bool ConvertFromLH5Internal();
bool ConvertFromLH5Internal(std::map<std::string, std::map<std::string, std::string>>&);

static inline const std::regex table_dtype_re = std::regex("^table\\{.*\\}$");

std::string HDFDataTypeToForm(H5::DataType);
bool ConvertTableToNTuple(H5::Group&);
bool ConvertTableToNTuple(H5::Group&, std::map<std::string, std::string>&);

////////////////////////////////////////////////////////////////////////////////////////////

Expand Down
Loading

0 comments on commit 401c56e

Please sign in to comment.