Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: QADB algorithm #321

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
48 changes: 37 additions & 11 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ rcdb_subproj = subproject('rcdb', required: get_option('z_require_rcdb'))

# resolve dependencies
prog_minver = find_program('meson' / 'minimum-version.sh')
rapidjson_ver = '1.1.0'
# NOTE: those that are typically installed by package managers should use `meson/minimum-version.sh`
fmt_dep = dependency(
'fmt',
Expand All @@ -44,6 +45,11 @@ yamlcpp_dep = dependency(
method: 'pkg-config',
version: '>=' + run_command(prog_minver, 'yaml-cpp', check: true).stdout().strip(),
)
rapidjson_dep = dependency(
'RapidJSON',
method: 'pkg-config',
version: f'>=@rapidjson_ver@',
)
hipo_dep = dependency(
'hipo4',
method: 'pkg-config',
Expand Down Expand Up @@ -76,41 +82,61 @@ thread_dep = dependency(
# themselves are listed last (see FIXME in meson/this_iguana.sh.in)
# NOTE: omit ROOT (handled differently, and most ROOT users already have it in their environment)
dep_list = []
foreach dep : [ hipo_dep, fmt_dep, yamlcpp_dep, rcdb_dep ]
foreach dep : [ hipo_dep, fmt_dep, yamlcpp_dep, rapidjson_dep, rcdb_dep ]
if dep.found()
dep_list += dep
endif
endforeach

# list of 'requires' for generated pkg-config file
# FIXME: depenencies built from wrapdb subprojects (type_name()=='internal') need to
# be handled differently:
# - add to `pkgconfig_reqs` by text
# - dep.name() does not give the actual name
pkgconfig_reqs = []
foreach dep : dep_list
if dep.type_name() != 'internal'
pkgconfig_reqs += dep
endif
endforeach
foreach dep_name, dep : { 'RapidJSON': rapidjson_dep, }
if dep.type_name() == 'internal'
pkgconfig_reqs += f'@dep_name@ >= @rapidjson_ver@'
endif
endforeach

# pkgconfig configuration: make a list of dependency library and include directories
dep_lib_dirs = []
dep_include_dirs = []
dep_pkgconfig_dirs = []
foreach dep : dep_list

# get library and include dirs
libdirs = []
incdirs = []
if dep.type_name() == 'pkgconfig'
libdirs = [ dep.get_variable(pkgconfig: 'libdir') ]
incdirs = [ dep.get_variable(pkgconfig: 'includedir') ]
libdirs += dep.get_variable(pkgconfig: 'libdir', default_value: 'not_defined')
incdirs += dep.get_variable(pkgconfig: 'includedir', default_value: 'not_defined')
elif dep.type_name() == 'cmake'
libdirs = []
foreach lib : dep.get_variable(cmake: 'PACKAGE_LIBRARIES').split(';')
libdirs += run_command('dirname', lib, check: true).stdout().strip()
endforeach
incdirs = ROOT_dep.get_variable(cmake: 'PACKAGE_INCLUDE_DIRS').split(';')
else
elif dep.type_name() == 'internal'
name = dep.get_variable(internal: 'name', default_value: dep.name())
if name == 'rcdb'
incdirs = [ dep.get_variable(internal: 'includedir') ]
incdirs += dep.get_variable(internal: 'includedir')
else
warning(f'Unknown dependency "@name@"')
# warning(f'Unknown dependency "@name@"')
continue
endif
else
error('Dependency type "' + dep.type_name() + '" is unknown')
endif

# append to `dep_*_dirs` arrays, uniquely
# append to `dep_*` arrays, uniquely
foreach libdir : libdirs
if not dep_lib_dirs.contains(libdir)
if not dep_lib_dirs.contains(libdir) and libdir != 'not_defined'
dep_lib_dirs += libdir
endif
if dep.type_name() == 'pkgconfig'
Expand All @@ -121,7 +147,7 @@ foreach dep : dep_list
endif
endforeach
foreach incdir : incdirs
if not dep_include_dirs.contains(incdir)
if not dep_include_dirs.contains(incdir) and incdir != 'not_defined'
dep_include_dirs += incdir
endif
endforeach
Expand Down Expand Up @@ -241,7 +267,7 @@ pkg.generate(
name: meson.project_name(),
description: project_description,
libraries: project_libs,
requires: [ fmt_dep, yamlcpp_dep, hipo_dep ], # pkg-config dependencies only
requires: pkgconfig_reqs,
variables: project_pkg_vars_nonempty,
)

Expand Down
20 changes: 20 additions & 0 deletions src/iguana/algorithms/clas12/QADB/Action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
algorithm:
name: 'clas12::QADB'

actions:
- name: PrepareEvent
type: creator
inputs:
- name: runnum
type: int
- name: evnum
type: int
outputs:
- type: concurrent_key_t
cast: int
- name: Filter
type: filter
inputs:
- name: key
type: concurrent_key_t
cast: int
75 changes: 75 additions & 0 deletions src/iguana/algorithms/clas12/QADB/Algorithm.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include "Algorithm.h"

namespace iguana::clas12 {

REGISTER_IGUANA_ALGORITHM(QADB);

void QADB::Start(hipo::banklist& banks)
{

// get configuration
ParseYAMLConfig();
o_datasets = GetOptionVector<std::string>("datasets");
o_qadb_dir = GetOptionScalar<std::string>("qadb_dir");
o_create_bank = GetOptionScalar<bool>("create_bank");
o_runnum = ConcurrentParamFactory::Create<int>();
o_binnum = ConcurrentParamFactory::Create<int>();

// get expected bank indices
b_config = GetBankIndex(banks, "RUN::config");
}

void QADB::Run(hipo::banklist& banks) const
{

// get the banks
auto& configBank = GetBank(banks, b_config, "RUN::config");

// prepare the event, reloading configuration parameters, if necessary
auto key = PrepareEvent(configBank.getInt("run", 0), configBank.getInt("event", 0));

Filter(key); // FIXME: do something with this
}

concurrent_key_t QADB::PrepareEvent(int const runnum, int const evnum) const {
m_log->Trace("calling PrepareEvent({}, {})", runnum, evnum);
if(o_runnum->NeedsHashing()) {
std::hash<int> hash_ftn;
auto hash_key = hash_ftn(runnum); // FIXME: need to hash both runnum and evnum
if(!o_runnum->HasKey(hash_key))
Reload(runnum, evnum, hash_key);
return hash_key;
} else {
if(o_runnum->IsEmpty() || o_runnum->Load(0) != runnum) // FIXME: bound check evnum here
Reload(runnum, evnum, 0);
return 0;
}
}

void QADB::Reload(int const runnum, int const evnum, 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);
// FIXME: query QADB here
}

bool QADB::Filter(concurrent_key_t key) const
{
return true; // FIXME
}

int QADB::GetRunNum(concurrent_key_t const key) const
{
return o_runnum->Load(key);
}

int QADB::GetBinNum(concurrent_key_t const key) const
{
return o_binnum->Load(key);
}

void QADB::Stop()
{
}

}
86 changes: 86 additions & 0 deletions src/iguana/algorithms/clas12/QADB/Algorithm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#pragma once

#include "iguana/algorithms/Algorithm.h"
#include "iguana/services/ConcurrentParam.h"

namespace iguana::clas12 {

/// @brief_algo Filter using Quality Assurance Database (QADB)
///
/// @begin_doc_algo{clas12::QADB | EventFilter}
/// @input_banks{RUN::config}
/// @output_banks{REC::Particle}
/// @end_doc
///
/// @begin_doc_config
/// @config_param{datasets | list[string] | the list of QADB datasets to include (see below)}
/// @config_param{qadb_dir | string | custom QADB directory; if not set, defaults to environment variable `$QADB`}
/// @config_param{create_bank | bool | if `true`, create the output bank with QADB information for this event}
/// @end_doc
///
/// This algorithm is an "EventFilter" type, which uses the return value of `iguana::Algorithm::Run` to
/// indicate whether the whole event is filtered or not.
///
/// This algorithm requires the QADB to be installed. The environment variable `$QADB` is assumed to point
/// to the QADB installation. Alternatively, use the configuration variable `qadb_dir` if you do not
/// want to use `$QADB`.
///
/// The QADB is defined for various datasets, and you must choose which dataset(s) to load by setting the
/// `datasets` configuration parameter; it is a list of `string`s, where each can be either:
/// - a dataset name, where the list of datasets is found in the QADB documentation
/// - in this case, the QADB files should be within `$QADB` (or `qadb_dir`) within `qadb/<dataset>/`
/// - the full path to a QADB `json` file
///
/// This algorithm has the option `create_bank` to control whether or not an output bank is created,
/// which contains information from the QADB about the QA bin that contains the current event.
class QADB : public Algorithm
{

DEFINE_IGUANA_ALGORITHM(QADB, clas12::QADB)

public:

void Start(hipo::banklist& banks) override;
void Run(hipo::banklist& banks) const override;
void Stop() override;

/// @action_function{reload} prepare the event
/// @when_to_call{for each event}
/// @param runnum the run number
/// @param evnum the event number
/// @returns the key to be used in `::Filter`
concurrent_key_t PrepareEvent(int const runnum, int const evnum) const;

/// @action_function{scalar filter} checks if the Z Vertex is within specified bounds if pid is one for which the filter should be applied to.;
/// Cuts applied to particles in FD or CD (ie not in FT).
/// @when_to_call{for each particle}
/// @param key the return value of `::PrepareEvent`
/// @returns `true` if `zvertex` is within specified bounds
bool Filter(concurrent_key_t const key) const;

/// @param key the return value of `::PrepareEvent`
/// @returns the current run number
int GetRunNum(concurrent_key_t const key) const;

/// @param key the return value of `::PrepareEvent`
/// @returns the current QA bin number
int GetBinNum(concurrent_key_t const key) const;

private:
hipo::banklist::size_type b_particle, b_config;

// Reload function
void Reload(int const runnum, int const evnum, concurrent_key_t key) const;

// configuration options
std::vector<std::string> o_datasets;
std::string o_qadb_dir;
bool o_create_bank{false};

// concurrent params
mutable std::unique_ptr<ConcurrentParam<int>> o_runnum;
mutable std::unique_ptr<ConcurrentParam<int>> o_binnum;

};

}
14 changes: 14 additions & 0 deletions src/iguana/algorithms/clas12/QADB/Config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
clas12::QADB

# choose which datasets to include; see QADB documentation
# for the full list
datasets:
- latest/rga_fa18_inbending
- latest/rga_fa18_outbending
- latest/rga_sp19

# location of QADB; if set to 'env', uses `$QADB`
qadb_dir: env

# whether or not to create a bank with QADB results
create_bank: false
5 changes: 5 additions & 0 deletions src/iguana/algorithms/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ algo_dict = [
'has_action_yaml': false,
'test_args': {'banks': [ 'REC::Particle', 'REC::Calorimeter', 'RUN::config' ]},
},
{
'name': 'clas12::QADB',
'has_validator': false,
'test_args': {'banks': [ 'RUN::config' ]},
},
]

# make lists of objects to build; inclusion depends on whether ROOT is needed or not, and if we have ROOT
Expand Down
2 changes: 2 additions & 0 deletions subprojects/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*/
!/rcdb
13 changes: 13 additions & 0 deletions subprojects/rapidjson.wrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[wrap-file]
directory = rapidjson-1.1.0
source_url = https://github.com/Tencent/rapidjson/archive/v1.1.0.tar.gz
source_filename = rapidjson-1.1.0.tar.gz
source_hash = bf7ced29704a1e696fbccf2a2b4ea068e7774fa37f6d7dd4039d0787f8bed98e
patch_filename = rapidjson_1.1.0-2_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/rapidjson_1.1.0-2/get_patch
patch_hash = c1480d0ecef09dbaa4b4d85d86090205386fb2c7e87f4f158b20dbbda14c9afc
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/rapidjson_1.1.0-2/rapidjson-1.1.0.tar.gz
wrapdb_version = 1.1.0-2

[provide]
rapidjson = rapidjson_dep
Loading