Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9579714
Added LeptonID .c
MarianaT27 Jun 6, 2024
d6abc5d
Add LeptonID
MarianaT27 Jun 13, 2024
5d82b06
Added algorithm to folder, added weight file
MarianaT27 Jun 27, 2024
089b655
Removed extra LeptonID files
MarianaT27 Jun 27, 2024
1b33921
Added config variables
MarianaT27 Jun 27, 2024
d6a9fea
renamed: LeptonIDFilter/config.yaml -> LeptonIDFilter/Config.yaml
c-dilks Jun 28, 2024
52eac3d
fix: compilation fixes
c-dilks Jun 28, 2024
e069eb1
feat: install and use algorithm data files (viz., weight files)
c-dilks Jul 1, 2024
ae7e1f2
doc: some doxygen fixes
c-dilks Jul 1, 2024
cf3ec1d
Update src/iguana/algorithms/clas12/LeptonIDFilter/Algorithm.cc
MarianaT27 Jul 1, 2024
9d34a08
Added: documentation descriptions
MarianaT27 Jul 2, 2024
2ae3b9d
fix: increase `algorithm` test timeout
c-dilks Jul 2, 2024
7ee9977
Moved declaration of TMVAReader to header; Added documentation
MarianaT27 Jul 2, 2024
efb276d
Fixed some errors
MarianaT27 Jul 2, 2024
d900c1d
Fixed more errors
MarianaT27 Jul 2, 2024
40a84e0
Fix typo in meson.build
Jan 14, 2025
0175215
Remove line in src/iguana/services/meson.build
Jan 14, 2025
befabef
Fix error in src/iguana/algorithms/Algorithm.h
Jan 14, 2025
6b0fe8f
I fix the declaration of the TMVA reader
Jan 23, 2025
a47415f
fix: don't look for `Action.yaml`
c-dilks Jan 29, 2025
861f037
fix: RCDB inclusion and linking
c-dilks Jan 29, 2025
13b3119
doc: clarify environment variables and consuming
c-dilks Jan 29, 2025
dfda3ce
build: minor changes
c-dilks Jan 29, 2025
f7605ad
Merge branch 'main' into dev_Mariana
c-dilks Jan 29, 2025
127946d
Merge remote-tracking branch 'origin/main' into lepton-pid-redux
c-dilks Dec 11, 2025
791c714
Merge remote-tracking branch 'origin/main' into lepton-pid-redux
c-dilks Dec 11, 2025
7a2f98b
fix: `project_etcdir -> project_etc_dir`
c-dilks Dec 11, 2025
b446aff
fix: remove stray conflict marker
c-dilks Dec 11, 2025
8d3f61d
style: clang auto-format
c-dilks Dec 11, 2025
315a985
fix: docstrings, `Run` function, and particle bank config param
c-dilks Dec 11, 2025
ee1b99b
fix: CODEOWNERS
c-dilks Dec 11, 2025
8e6f824
fix: book the model and mutex-lock the `Run` function
c-dilks Dec 17, 2025
e8a3e4e
fix: suppress `TMVA` leak
c-dilks Dec 17, 2025
ace50c5
feat: add `Algorithm::GetOptionNode` and support multiple weights files
c-dilks Dec 20, 2025
122b2c0
style: `-DOPT` -> `-D OPT`
c-dilks Dec 21, 2025
f290cb3
refactor: improve multithreading support
c-dilks Dec 21, 2025
77d474a
feat: apply to multiple PIDs
c-dilks Dec 21, 2025
ee6b735
fix: use run number to choose appropriate weights files
c-dilks Dec 21, 2025
9a61b56
fix: add `default` dataset to config
c-dilks Dec 21, 2025
d9ff0b9
Merge branch 'main' into lepton-pid-redux
c-dilks Dec 21, 2025
6480e89
Merge branch 'main' into lepton-pid-redux
c-dilks Jan 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
# @RichCap Richard Capobianco
# @rtysonCLAS12 Richard Tyson
# @Gregtom3 Gregory Matousek
# @MarianaT27 Mariana Tenorio Pita
# @tbhayward Timothy Hayward
#################################################################################

* @c-dilks
src/iguana/algorithms/clas12/CalorimeterLinker/* @c-dilks
src/iguana/algorithms/clas12/EventBuilderFilter/* @c-dilks
src/iguana/algorithms/clas12/FTEnergyCorrection/* @asligonulacar
src/iguana/algorithms/clas12/FiducialFilter/* @Gregtom3
src/iguana/algorithms/clas12/LeptonIDFilter/* @MarianaT27
src/iguana/algorithms/clas12/MatchParticleProximity/* @c-dilks
src/iguana/algorithms/clas12/MomentumCorrection/* @RichCap @c-dilks
src/iguana/algorithms/clas12/PhotonGBTFilter/* @Gregtom3
Expand Down
2 changes: 1 addition & 1 deletion examples/meson.build
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# install config files
example_config_files_prefix = project_etcdir / 'examples'
example_config_files_prefix = project_etc_dir / 'examples'
install_subdir('config', install_dir: example_config_files_prefix, strip_directory: true)

# example source information
Expand Down
24 changes: 20 additions & 4 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ project(
'cpp_std': 'c++17',
'buildtype': 'release',
'libdir': 'lib',
'datadir': 'share',
'licensedir': 'share/licenses/iguana',
'pkgconfig.relocatable': 'true',
'force_fallback_for': ['rcdb'],
Expand All @@ -20,6 +21,14 @@ project_description = 'Implementation Guardian of Analysis Algorithms'
add_languages('fortran', native: false, required: get_option('bind_fortran'))
use_chameleon = get_option('bind_fortran')

# check built-in build options
if get_option('libdir') != 'lib'
warning('build option "libdir" = "' + get_option('libdir') + '", which is not "lib"; if you experience any issues, please report them')
endif
if get_option('datadir') != 'share'
error('build option "datadir" must be "share", but it is set to "' + get_option('datadir') + '"')
endif

# warn that macOS is no longer tested
if host_machine.system() == 'darwin'
warning('''host machine system is darwin:
Expand Down Expand Up @@ -70,6 +79,7 @@ ROOT_dep = dependency(
'ROOT::RIO',
'ROOT::ROOTDataFrame',
'ROOT::ROOTVecOps',
'ROOT::TMVA',
'ROOT::TreePlayer',
],
)
Expand Down Expand Up @@ -154,7 +164,8 @@ hipo_dep_dataframes_found = hipo_dep.get_variable(
project_inc = include_directories('src')
project_libs = []
project_deps = declare_dependency(dependencies: dep_list)
project_etcdir = get_option('sysconfdir') / meson.project_name()
project_etc_dir = get_option('sysconfdir') / meson.project_name()
project_data_dir = get_option('datadir') / meson.project_name()
project_test_env = environment()
project_pythondir = 'python'

Expand All @@ -180,9 +191,14 @@ project_test_env.set(
)

# set preprocessor macros
add_project_arguments('-DIGUANA_ETCDIR="' + get_option('prefix') / project_etcdir + '"', language: [ 'cpp' ])
add_project_arguments(
'-D IGUANA_PREFIX="' + get_option('prefix') + '"',
'-D IGUANA_ETCDIR="' + project_etc_dir + '"',
'-D IGUANA_DATADIR="' + project_data_dir + '"',
language: ['cpp'],
)
if ROOT_dep.found()
add_project_arguments('-DIGUANA_ROOT_FOUND', language: [ 'cpp' ]) # currently only used for Validator plot styles
add_project_arguments('-D IGUANA_ROOT_FOUND', language: [ 'cpp' ]) # currently only used for Validator plot styles
endif

# start chameleon
Expand Down Expand Up @@ -236,7 +252,7 @@ if get_option('z_install_envfile')
'libdir': get_option('libdir'),
'includedir': get_option('includedir'),
'bindir': get_option('bindir'),
'etcdir': project_etcdir,
'etcdir': project_etc_dir,
'pythondir': project_pythondir,
'dep_pkgconfigdirs': ':'.join(dep_pkgconfig_dirs),
'dep_libdirs': ':'.join(dep_lib_dirs),
Expand Down
1 change: 1 addition & 0 deletions meson/lsan.supp
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
leak:libCling.so
leak:TMVA::DataSetFactory::BuildDynamicDataSet
24 changes: 24 additions & 0 deletions src/iguana/algorithms/Algorithm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,19 @@ namespace iguana {

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

YAML::Node Algorithm::GetOptionNode(YAMLReader::node_path_t node_path) const
{
CompleteOptionNodePath("", node_path);
auto node = m_yaml_config->GetNode(node_path);
if(!node.has_value()) {
m_log->Error("Algorithm::GetOptionNode failed to find node");
throw std::runtime_error("config file parsing issue");
}
return node.value();
}

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

void Algorithm::SetName(std::string_view name)
{
Object::SetName(name);
Expand Down Expand Up @@ -123,6 +136,17 @@ namespace iguana {

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

std::string Algorithm::GetDataFile(std::string const& name)
{
if(!m_datafile_reader) {
m_datafile_reader = std::make_unique<DataFileReader>(ConfigFileReader::ConvertAlgoNameToConfigDir(m_class_name), "data|" + m_name);
m_datafile_reader->SetLogLevel(m_log->GetLevel());
}
return m_datafile_reader->FindFile(name);
}

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

void Algorithm::ParseYAMLConfig()
{

Expand Down
16 changes: 15 additions & 1 deletion src/iguana/algorithms/Algorithm.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@

#include "AlgorithmBoilerplate.h"
#include "iguana/bankdefs/BankDefs.h"
#include "iguana/services/DataFileReader.h"
#include "iguana/services/GlobalParam.h"
#include "iguana/services/RCDBReader.h"
#include "iguana/services/YAMLReader.h"
#include <iguana/services/GlobalParam.h>

namespace iguana {

Expand Down Expand Up @@ -144,6 +145,11 @@ namespace iguana {
template <typename OPTION_TYPE>
std::set<OPTION_TYPE> GetOptionSet(std::string const& key, YAMLReader::node_path_t node_path = {}) const;

/// Get the `YAML::Node` for an option
/// @param node_path the `YAML::Node` identifier path to search
/// @returns the `YAML::Node`
YAML::Node GetOptionNode(YAMLReader::node_path_t node_path) const;

/// Set the name of this algorithm
/// @param name the new name
void SetName(std::string_view name);
Expand All @@ -166,6 +172,11 @@ namespace iguana {
/// @param name the directory name
void SetConfigDirectory(std::string const& name);

/// Get the full path to a data file, such as a machine-learning model
/// @param name the name of the file; if found in the user's current working directory (`./`), that will be the file that is used;
/// otherwise the _installed_ file (in `$IGUANA/share/`) will be used by default
std::string GetDataFile(std::string const& name);

/// Get the index of a bank in a `hipo::banklist`; throws an exception if the bank is not found
/// @param banks the list of banks this algorithm will use
/// @param bank_name the name of the bank
Expand Down Expand Up @@ -311,6 +322,9 @@ namespace iguana {

/// Data structure to hold configuration options set by `Algorithm::SetOption`
std::unordered_map<std::string, option_t> m_option_cache;

/// Data file reader
std::unique_ptr<DataFileReader> m_datafile_reader;
};

//////////////////////////////////////////////////////////////////////////////
Expand Down
207 changes: 207 additions & 0 deletions src/iguana/algorithms/clas12/LeptonIDFilter/Algorithm.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
#include "Algorithm.h"

#include <Math/Vector4D.h>
#include <cmath>

namespace iguana::clas12 {

REGISTER_IGUANA_ALGORITHM(LeptonIDFilter, "clas12::LeptonIDFilter");

void LeptonIDFilter::Start(hipo::banklist& banks)
{
// Get configuration
ParseYAMLConfig();
o_pids = GetOptionSet<int>("pids");
o_cut = GetOptionScalar<double>("cut");
o_tmva_reader_options = GetOptionScalar<std::string>("tmva_reader_options");
o_particle_bank = GetOptionScalar<std::string>("particle_bank");
o_runnum = ConcurrentParamFactory::Create<int>();
o_weightfile_electron = ConcurrentParamFactory::Create<std::string>();
o_weightfile_positron = ConcurrentParamFactory::Create<std::string>();

// Get Banks that we are going to use
b_particle = GetBankIndex(banks, o_particle_bank);
b_calorimeter = GetBankIndex(banks, "REC::Calorimeter");
b_config = GetBankIndex(banks, "RUN::config");

// Initialize the TMVA reader
readerTMVA = std::make_unique<TMVA::Reader>(LeptonIDVars::names, o_tmva_reader_options);

// find all the unique weights files in the configuration YAML
std::set<std::string> weightfile_list;
for(auto const& node : GetOptionNode({"weightfile"})) {
for(std::string const particle : {"electron", "positron"}) {
if(node[particle]) {
weightfile_list.insert(node[particle].as<std::string>());
}
}
}

// book the weights files
// use the name of weightfile as the method tag, for simplicity
m_log->Debug("Booking weight files:");
for(auto const& weightfile_name : weightfile_list) {
auto weightfile_path = GetDataFile(weightfile_name);
m_log->Debug(" - {}", weightfile_path);
readerTMVA->BookMVA(weightfile_name, weightfile_path);
}
}


bool LeptonIDFilter::Run(hipo::banklist& banks) const
{
return Run(
GetBank(banks, b_particle, o_particle_bank),
GetBank(banks, b_calorimeter, "REC::Calorimeter"),
GetBank(banks, b_config, "RUN::config"));
}


bool LeptonIDFilter::Run(hipo::bank& particleBank, hipo::bank const& calorimeterBank, hipo::bank const& configBank) const
{
// particle bank before filtering
ShowBank(particleBank, Logger::Header("INPUT PARTICLES"));

// prepare the event, reloading configuration parameters if the run number changed or is not yet known
auto key = PrepareEvent(configBank.getInt("run", 0));

// filter the particle bank
particleBank.getMutableRowList().filter([this, &calorimeterBank, key](auto bank, auto row) {
auto pid = bank.getInt("pid", row);
// check if this is a lepton in `o_pids`
if(o_pids.find(pid) != o_pids.end()) {
auto status = bank.getShort("status", row);
// status cut
if(std::abs(status) >= 2000 && std::abs(status) < 4000) {
m_log->Trace("Found lepton: pindex={}", row);
auto lepton_vars = GetLeptonIDVariables(row, bank, calorimeterBank);
lepton_vars.pid = pid;
lepton_vars.score = CalculateScore(lepton_vars, key);
return Filter(lepton_vars.score) ? 1 : 0;
}
else {
m_log->Trace("Lepton at pindex={} did not pass status cut", row);
return 0;
}
}
return 1; // not a lepton in `o_pids`, let it pass the filter
});

// particle bank after filtering
ShowBank(particleBank, Logger::Header("OUTPUT PARTICLES"));
return !particleBank.getRowList().empty();
}


concurrent_key_t LeptonIDFilter::PrepareEvent(int const runnum) const
{
m_log->Trace("calling PrepareEvent({})", runnum);
if(o_runnum->NeedsHashing()) {
std::hash<int> hash_ftn;
auto hash_key = hash_ftn(runnum);
if(!o_runnum->HasKey(hash_key))
Reload(runnum, hash_key);
return hash_key;
}
else {
if(o_runnum->IsEmpty() || o_runnum->Load(0) != runnum)
Reload(runnum, 0);
return 0;
}
}


void LeptonIDFilter::Reload(int const runnum, concurrent_key_t key) const
{
std::lock_guard<std::mutex> const lock(m_mutex); // NOTE: be sure to lock successive `ConcurrentParam::Save` calls !!!
m_log->Trace("-> calling Reload({}, {})", runnum, key);
o_runnum->Save(runnum, key);
o_weightfile_electron->Save(GetOptionScalar<std::string>("weightfile:electron", {"weightfile", GetConfig()->InRange("runs", runnum), "electron"}), key);
o_weightfile_positron->Save(GetOptionScalar<std::string>("weightfile:positron", {"weightfile", GetConfig()->InRange("runs", runnum), "positron"}), key);
}


LeptonIDVars LeptonIDFilter::GetLeptonIDVariables(int const plepton, hipo::bank const& particle_bank, hipo::bank const& calorimeter_bank) const
{

double px = particle_bank.getFloat("px", plepton);
double py = particle_bank.getFloat("py", plepton);
double pz = particle_bank.getFloat("pz", plepton);
double E = std::sqrt(std::pow(px, 2) + std::pow(py, 2) + std::pow(pz, 2) + std::pow(0.000511, 2));
ROOT::Math::PxPyPzMVector vec_lepton(px, py, pz, E);

LeptonIDVars lepton;

lepton.P = vec_lepton.P();
lepton.Theta = vec_lepton.Theta();
lepton.Phi = vec_lepton.Phi();

m_log->Debug("Variables obtained from particle bank");


lepton.m2pcal = -1;
lepton.m2ecin = -1;
lepton.m2ecout = -1;

for(int row = 0; row < calorimeter_bank.getRows(); row++) {
auto pindex = calorimeter_bank.getShort("pindex", row);
auto layer = calorimeter_bank.getByte("layer", row);
auto energy = calorimeter_bank.getFloat("energy", row);
auto m2u = calorimeter_bank.getFloat("m2u", row);
auto m2v = calorimeter_bank.getFloat("m2v", row);
auto m2w = calorimeter_bank.getFloat("m2w", row);

if(pindex == plepton && layer == 1) {
lepton.SFpcal = energy / vec_lepton.P();
lepton.m2pcal = (m2u + m2v + m2w) / 3;
}

if(pindex == plepton && layer == 4) {
lepton.SFecin = energy / vec_lepton.P();
lepton.m2ecin = (m2u + m2v + m2w) / 3;
}
if(pindex == plepton && layer == 7) {
lepton.SFecout = energy / vec_lepton.P();
lepton.m2ecout = (m2u + m2v + m2w) / 3;
}
}


m_log->Debug("Variables obtained from calorimeter bank");

return lepton;
}


double LeptonIDFilter::CalculateScore(LeptonIDVars lepton_vars, concurrent_key_t const key) const
{
// Assigning variables from lepton_vars for TMVA method
std::string weightsfile;
switch(lepton_vars.pid) {
case 11:
weightsfile = o_weightfile_electron->Load(key);
break;
case -11:
weightsfile = o_weightfile_positron->Load(key);
break;
default:
throw std::runtime_error(fmt::format("unknown lepton PDG code {}", lepton_vars.pid));
}
return readerTMVA->EvaluateMVA(lepton_vars.GetValues(), weightsfile);
}


bool LeptonIDFilter::Filter(double score) const
{
if(score >= o_cut)
return true;
else
return false;
}


void LeptonIDFilter::Stop()
{
}

}
Loading