Skip to content

Commit f0e6538

Browse files
authored
feat: algorithm validators (#123)
1 parent 82b2911 commit f0e6538

28 files changed

+846
-214
lines changed

.github/workflows/ci.yml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,6 @@ defaults:
4747
env:
4848
hipo_version: f40da676bbd1745398e9fdf233ff213ff98798f1
4949
num_events: 1000
50-
# sanitizer options; NOTE: suppression path assumes the build directory is a subdirectory of the top-level repo directory
51-
UBSAN_OPTIONS: halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1:suppressions=../iguana_src/.github/UBSAN.supp
52-
ASAN_OPTIONS: halt_on_error=1:abort_on_error=1:print_summary=1
53-
5450

5551
jobs:
5652

@@ -364,9 +360,10 @@ jobs:
364360
run: meson setup build-iguana iguana_src --wipe
365361
- name: meson configure
366362
run: |
367-
meson configure \
368-
-Dtest_data_file=$(pwd)/test_data.hipo \
369-
-Dtest_num_events=${{ env.num_events }} \
363+
meson configure \
364+
-Dtest_data_file=$(pwd)/test_data.hipo \
365+
-Dtest_num_events=${{ env.num_events }} \
366+
-Dtest_output_dir=$(pwd)/validation_results \
370367
build-iguana
371368
case "${{ matrix.mode }}" in
372369
coverage)
@@ -417,6 +414,12 @@ jobs:
417414
name: coverage-report
418415
retention-days: 7
419416
path: coverage-report
417+
- uses: actions/upload-artifact@v4
418+
if: ${{ matrix.mode == 'coverage' }} # select one job-matrix element, since we only need one copy of this artifact
419+
with:
420+
name: _validation_results
421+
retention-days: 7
422+
path: validation_results
420423

421424
# run examples
422425
# - a bit redundant, with `meson test` from `test_iguana`, but this ensures
@@ -522,7 +525,7 @@ jobs:
522525
env:
523526
CC: gcc
524527
CXX: g++
525-
build_id: cpp-gcc-release
528+
build_id: cpp-gcc-release-noROOT # FIXME: ROOT libs sporadically can't be found on some runners; for now use `noROOT` build
526529
steps:
527530
### dependencies and test data
528531
- uses: actions/checkout@v4

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@
99

1010
# data files
1111
*.hipo
12+
*.root
13+
*.png

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
1. [Design Notes](doc/design.md)
77
1. [API documentation](https://jeffersonlab.github.io/iguana/doxygen)
88
1. [Developing a new Algorithm](src/iguana/algorithms/example/README.md)
9+
1. [Algorithm Tests and Validators](doc/testing.md)
910
1. [Repository Maintenance](doc/maintenance.md)
1011

1112
## Status

bind/python/iguana-example-00-basic.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@
1313
numEvents = int(sys.argv[2]) if len(sys.argv)>2 else 3
1414

1515
reader = hipo.reader(inFile)
16-
banks = reader.getBanks(["REC::Particle", "RUN::config"]);
16+
banks = reader.getBanks([
17+
"RUN::config",
18+
"REC::Particle",
19+
"REC::Calorimeter",
20+
"REC::Track",
21+
"REC::Scintillator"])
1722

1823
seq = iguana.AlgorithmSequence('pyiguana')
1924
seq.Add('clas12::EventBuilderFilter')
@@ -33,8 +38,8 @@ def prettyPrint(message, bank):
3338
seq.Start(banks)
3439
while(reader.next(banks) and (numEvents==0 or iEvent < numEvents)):
3540
iEvent += 1
36-
prettyPrint("BEFORE", banks[0])
41+
prettyPrint("BEFORE", banks[1])
3742
seq.Run(banks)
38-
prettyPrint("AFTER", banks[0])
43+
prettyPrint("AFTER", banks[1])
3944

4045
seq.Stop()

bind/python/iguana-example-01-bank-rows.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@
4242
pid = particleBank.getInt('pid', row)
4343
if(algo_eventbuilder_filter.Filter(pid)):
4444

45-
sector = 1 # FIXME: get the sector number
45+
sector = 1 # FIXME: get the sector number. The algorithm `clas12::SectorFinder` can do this, however
46+
# it requires reading full `hipo::bank` objects, whereas this example is meant to demonstrate
47+
# `iguana` usage operating _only_ on bank row elements
4648

4749
px, py, pz, = algo_momentum_correction.Transform(
4850
particleBank.getFloat("px", row),

doc/testing.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Testing and Validating Algorithms
2+
3+
## Running a Single Test
4+
5+
The installed executable `iguana-test` is used for running single tests. Run
6+
it with no arguments for a usage guide. For example:
7+
```bash
8+
iguana/bin/iguana-test validator -f data.hipo -n 0 -a clas12::MomentumCorrectionValidator -o validation_plots
9+
```
10+
will run the validator `MomentumCorrectionValidator` and write its output to `./validation_plots/`, assuming:
11+
- your installation `prefix` is `./iguana`
12+
- your data are found in `./data.hipo`
13+
14+
## Running All of the Tests
15+
16+
While in your build directory, you may run
17+
```bash
18+
meson test
19+
```
20+
to run all available tests. However, depending on your build options, not all of them may run. For example,
21+
since the `validator` tests need input data, you need to set the build option `test_data_file` to a sample input
22+
file, otherwise tests which need input data will not run.
23+
24+
For further usage of `meson test`, for example how to run a single test or control verbosity, run
25+
```bash
26+
meson test --help
27+
```
28+
> [!TIP]
29+
> - if you are testing on a _large_ data set, you may need to increase the timeout with the `-t` option
30+
> and parallelize with the `-j` option
31+
> - if a test is failing, use `--print-errorlogs` to see the `stderr` stream and add `--no-stdsplit` if you need to see the `stdout` stream;
32+
> if the streams' outputs are mixed up or not appearing, try prepending `stdbuf` as:
33+
> ```bash
34+
> stdbuf -o0 meson test [OPTIONS]...
35+
> ```
36+
37+
> [!NOTE]
38+
> All of the available tests run on the Continuous Integration (CI), so if you are developing and you submit a pull
39+
> requests, you may rely on the CI logs to check if the tests were successful

examples/iguana-example-00-basic.cc

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@ int main(int argc, char** argv)
2020
hipo::reader reader(inFileName);
2121

2222
// set banks
23-
hipo::banklist banks = reader.getBanks({"REC::Particle", "RUN::config"});
24-
enum banks_enum { b_particle,
25-
b_config }; // TODO: users shouldn't have to do this
23+
hipo::banklist banks = reader.getBanks({"RUN::config",
24+
"REC::Particle",
25+
"REC::Calorimeter",
26+
"REC::Track",
27+
"REC::Scintillator"});
28+
enum banks_enum { b_config,
29+
b_particle }; // TODO: users shouldn't have to do this
2630

2731
// iguana algorithm sequence
2832
iguana::AlgorithmSequence seq;

examples/iguana-example-01-bank-rows.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ int main(int argc, char** argv)
4949
auto pid = particleBank.getInt("pid", row);
5050
if(algo_eventbuilder_filter.Filter(pid)) {
5151

52-
int sector = 1; // FIXME: get the sector number
52+
int sector = 1; // FIXME: get the sector number. The algorithm `clas12::SectorFinder` can do this, however
53+
// it requires reading full `hipo::bank` objects, whereas this example is meant to demonstrate
54+
// `iguana` usage operating _only_ on bank row elements
5355

5456
// if accepted PID, correct its momentum
5557
auto [px, py, pz] = algo_momentum_correction.Transform(

examples/meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ foreach example, info : example_sources
2727
link_args: ROOT_dep_link_args,
2828
install: true,
2929
install_rpath: ':'.join(project_exe_rpath),
30+
build_rpath: ROOT_dep_rpath,
3031
)
3132
if fs.is_file(get_option('test_data_file'))
3233
test(
@@ -36,6 +37,7 @@ foreach example, info : example_sources
3637
'test_args',
3738
[ get_option('test_data_file'), get_option('test_num_events') ]
3839
),
40+
env: project_test_env,
3941
)
4042
endif
4143
endforeach

meson.build

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,25 @@ message('Dependency include dirs = [', ', '.join(dep_inc_paths), ']')
9494
# handle ROOT
9595
ROOT_dep_inc_dirs = []
9696
ROOT_dep_link_args = []
97+
ROOT_dep_link_args_for_validators = []
98+
ROOT_dep_rpath = ''
9799
if ROOT_dep.found()
98100
ROOT_dep_inc_dirs += include_directories(run_command('root-config', '--incdir', check: true).stdout().strip())
101+
ROOT_libdir = run_command('root-config', '--libdir', check: true).stdout().strip()
99102
ROOT_dep_link_args += [
100-
'-L' + run_command('root-config', '--libdir', check: true).stdout().strip(),
101103
# ROOT libraries that we need (safer than `root-config --libs`)
104+
'-L' + ROOT_libdir,
102105
'-lCore',
103106
'-lGenVector',
104107
]
108+
ROOT_dep_link_args_for_validators = [
109+
# additional ROOT libraries for validators (namely, graphics libraries)
110+
'-L' + ROOT_libdir,
111+
'-lRIO',
112+
'-lHist',
113+
'-lGpad',
114+
]
115+
ROOT_dep_rpath = ROOT_libdir
105116
endif
106117

107118
# general project vars
@@ -110,11 +121,32 @@ project_inc = include_directories('src')
110121
project_libs = []
111122
project_deps = declare_dependency(dependencies: [ fmt_dep, yamlcpp_dep, hipo_dep ] ) # do NOT include ROOT here
112123
project_etc = get_option('sysconfdir') / meson.project_name()
124+
project_test_env = environment()
113125
project_pkg_vars = [
114126
'dep_pkgconfigdirs=' + ':'.join(get_option('pkg_config_path')),
115127
'dep_libdirs=' + ':'.join(dep_lib_paths),
116128
]
117129

130+
# sanitizer settings
131+
project_test_env.set(
132+
'UBSAN_OPTIONS',
133+
'halt_on_error=1',
134+
'abort_on_error=1',
135+
'print_summary=1',
136+
'print_stacktrace=1',
137+
'suppressions=' + meson.project_source_root() / 'meson' / 'ubsan.supp',
138+
)
139+
project_test_env.set(
140+
'ASAN_OPTIONS',
141+
'halt_on_error=1',
142+
'abort_on_error=1',
143+
'print_summary=1',
144+
)
145+
project_test_env.set(
146+
'LSAN_OPTIONS',
147+
'suppressions=' + meson.project_source_root() / 'meson' / 'lsan.supp',
148+
)
149+
118150
# executables' rpath
119151
project_exe_rpath = []
120152
if host_machine.system() != 'darwin'

0 commit comments

Comments
 (0)