Skip to content

Commit

Permalink
feat: generate a template algorithm (#67)
Browse files Browse the repository at this point in the history
  • Loading branch information
c-dilks committed Jan 5, 2024
1 parent 4edc4f5 commit 20af2c8
Show file tree
Hide file tree
Showing 6 changed files with 286 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
1. [Troubleshooting](doc/troubleshooting.md)
1. [Design Notes](doc/design.md)
1. [API documentation](https://jeffersonlab.github.io/iguana/)
1. [Developing a new Algorithm](src/iguana/algorithms/example/README.md)
1. [Repository Maintenance](doc/maintenance.md)

> [!CAUTION]
Expand Down
111 changes: 111 additions & 0 deletions src/iguana/algorithms/example/ExampleAlgorithm.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//############################################################################
//# include the algorithm header
//############################################################################
#include "ExampleAlgorithm.h"

//############################################################################
//# namespace must match that in the header
//############################################################################
namespace iguana::example {

//############################################################################
//# boilerplate to register an algorithm, so `AlgorithmFactory` knows about it
//# - this is a preprocessor macro call (see `../AlgorithmBoilerplate.h`)
//# - this must be done here in the source file, not in the header
//# - the argument is the class name; do not surround it with quotes
//# - usage of a semicolon at the end is optional, but recommended
//############################################################################
REGISTER_IGUANA_ALGORITHM(ExampleAlgorithm);

//############################################################################
//# define `ExampleAlgorithm::Start()`
//# - again, a preprocessor macro is used here
//# - this overrides the virtual function `Algorithm::Start`; see `Algorithm.h`
//# for its parameters and return type
//############################################################################
START_IGUANA_ALGORITHM(ExampleAlgorithm) {
//############################################################################
//# define configuration options, their default values, and cache them
//# - see `Algorithm.h` for more details
//# - this function "caches" the option values into the `o_*` members, to
//# avoid looking them up in the `Algorithm::Run` method
//############################################################################
CacheOption("testInt", 8, o_testInt);
CacheOption("testFloat", 7.0, o_testFloat);
//############################################################################
//# cache expected bank indices
//# - here we make sure that parameter `banks` includes the banks that are
//# required to run this algorithm
//# - this function "caches" the bank index values into the `b_*` members, to
//# avoid looking them up in the `Algorithm::Run` method
//############################################################################
CacheBankIndex(banks, "REC::Particle", b_particle);
}


//############################################################################
//# define `ExampleAlgorithm::Run()`
//# - this overrides the virtual function `Algorithm::Run`; see `Algorithm.h`
//# for its parameters and return type
//# - note that this method must be _thread safe_, for example, you cannot modify
//# class instance objects
//# - try to avoid expensive operations here; instead, put them in the `Start` method
//# if it is reasonable to do so
//############################################################################
RUN_IGUANA_ALGORITHM(ExampleAlgorithm) {
//############################################################################
//# get the banks; here we just need `REC::Particle`
//############################################################################
auto& particleBank = GetBank(banks, b_particle, "REC::Particle");
//############################################################################
//# dump the bank
//# - this will only happen if the log level for this algorithm is set low enough
//# - this provides a look at the bank _before_ the algorithm runs
//# - this is optional
//############################################################################
ShowBank(particleBank, Logger::Header("INPUT PARTICLES"));

//############################################################################
//# loop over the bank rows
//############################################################################
for(int row = 0; row < particleBank.getRows(); row++) {
//############################################################################
//# get the `pid` and feed it to the `Filter` action function; if the row
//# is not acceptable, mask it out
//############################################################################
auto pid = particleBank.getInt("pid", row);
auto accept = Filter(pid);
if(!accept)
MaskRow(particleBank, row);
//############################################################################
//# print a useful debugging method (see `Logger.h` for details, or other
//# algorithms for examples of how to use the logger)
//############################################################################
m_log->Debug("input PID {} -- accept = {}", pid, accept);
}

//############################################################################
//# dump the modified bank (only if the log level is low enough); this is also optional
//############################################################################
ShowBank(particleBank, Logger::Header("OUTPUT PARTICLES"));
}


//############################################################################
//# define the action function
//############################################################################
bool ExampleAlgorithm::Filter(const int pid) const {
return pid > 0;
}


//############################################################################
//# define `ExampleAlgorithm::Stop()`
//# - this overrides the virtual function `Algorithm::Stop`; see `Algorithm.h`
//# for its parameters and return type
//# - in this example, there is nothing to do
//############################################################################
STOP_IGUANA_ALGORITHM(ExampleAlgorithm) {
}

}
98 changes: 98 additions & 0 deletions src/iguana/algorithms/example/ExampleAlgorithm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//############################################################################
//# allow this header to be included only once
//############################################################################
#pragma once

//############################################################################
//# include `AlgorithmFactory`, so that we may register this algorithm for factory creation
//############################################################################
#include "iguana/algorithms/AlgorithmFactory.h"

//############################################################################
//# define the namespace
//# - all `iguana` code should be within the `iguana` namespace
//# - algorithms specific to an experiment or analysis may be put in a namespace within the `iguana` namespace
//# - here we use the `iguana::example` namespace
//# - for CLAS12-specific algorithms, use `iguana::clas12`
//############################################################################
namespace iguana::example {

//############################################################################
//# define the algorithm class
//# - it must inherit from `Algorithm`, which includes common methods and objects used by each algorithm
//# - it must also include a Doxygen docstring, typically defined by 3 forward slashes `///`;
//# see Doxygen documentation for more details, or see other algorithms
//############################################################################
/// @brief This is a template algorithm; provide a brief, one-line description of your algorithm here.
///
/// Provide a more detailed description of your algorithm here, if you want.
class ExampleAlgorithm : public Algorithm {

//############################################################################
//# this is a preprocessor macro call which generates boilerplate for the algorithm definition
//# - the arguments are:
//# - the class name, `ExampleAlgorithm`
//# - a unique, "full" name of the algorithm, used by `AlgorithmFactory`; typically is the
//# namespace with the class name, excluding the `iguana::` part, but you are free to choose any name
//# - NOTE: quotes are not used, and there is no need for a semicolon at the end of this call
//# - see `../AlgorithmBoilerplate.h` for details
//# - the macros are relatively modular, so if you want to use your own constructor or destructor, you may
//# do so, and use other preprocessor macros called within `DEFINE_IGUANA_ALGORITHM` to complete
//# the boilerplate public and private functions and members
//############################################################################
DEFINE_IGUANA_ALGORITHM(ExampleAlgorithm, example::ExampleAlgorithm)

public:

//############################################################################
//# public functions go here
//# - typically these are "action functions", which expose the primary operation of an algorithm
//# - these functions are _unique_ to each algorithm, and therefore are not defined in the
//# `Algorithm` base class
//# - their purpose is to allow the usage of the algorithm for users who _don't_ process full banks,
//# but rather process bank rows, or from some other data source
//# - try to keep the parameters and return types _friendly_ to language bindings; for example,
//# avoid "complicated" types and lvalue references
//# - don't forget the Doxygen docstrings
//# - the action function here is trivial, just to show an example
//# - you do not have to name it as `Filter`, but take a look at other algorithms and try to
//# keep some consistency, for example:
//# - `bool Filter` for a filtering type algorithm, such as fiducial cuts
//# - `Transform` for a transformation type algorithm, such as momentum corrections
//# - `Create` for a creation type algorithm, such as inclusive kinematic (x, Q2, etc.) reconstruction
//############################################################################
/// **Action function**: checks if the PDG `pid` is positive;
/// this is an example action function, please replace it with your own
/// @param pid the particle PDG to check
/// @returns `true` if `pid` is positive
bool Filter(const int pid) const;

private:

//############################################################################
//# indices for the banks needed for this algorithm
//# - see `Algorithm::CacheBankIndex` for details
//# - here, we just define one for the `REC::Particle` bank
//# - convention: they should start with `b_`
//############################################################################
/// `hipo::banklist` index for the particle bank (as an example)
hipo::banklist::size_type b_particle;

//############################################################################
//# configuration options
//# - their type may be:
//# - one of the allowed types in `option_t`, which is a `std::variant`
//# - `std::set`, used by `Algorithm::CacheOptionToSet`, which converts
//# a user's `std::vector` option to a `std::set`
//# - your own type, but you will have to set it in the `Start()` method
//# - here we show example `int` and `float` options
//# - convention: they should start with `o_`
//############################################################################
/// Example integer configuration option
int o_testInt;
/// Example float configuration option
double o_testFloat;

};

}
25 changes: 25 additions & 0 deletions src/iguana/algorithms/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Example Algorithm Template

This directory includes a fully documented, simple example algorithm:
- [`ExampleAlgorithm.h`](ExampleAlgorithm.h)
- [`ExampleAlgorithm.cc`](ExampleAlgorithm.cc)

In this code, the following comment styles are used:
- `//#`: this is a comment which describes in detail what each line of the code does; you probably
don't want this much detail in your own algorithm
- `///`: this is a docstring for [Doxygen](https://www.doxygen.nl/), for automated documentation generation;
you'll need to use these when documenting your algorithm

To generate a template algorithm (without the `//#` comments) so you may get
started coding, run the `make_template.sh` script here:
```bash
# run with no arguments to see usage
src/iguana/algorithms/example/make_template.sh

# example: make a CLAS12 algorithm called AwesomeAlgorithm
src/iguana/algorithms/example/make_template.sh AwesomeAlgorithm clas12 src/iguana/algorithms/clas12
```

Once you have generated your new algorithm, add it to the appropriate
`meson.build` file (likely [`src/iguana/algorithms/meson.build`](../meson.build)),
and get started coding!
49 changes: 49 additions & 0 deletions src/iguana/algorithms/example/make_template.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/bin/bash
# generate a template algorithm from the example algorithm

set -e

namespace=clas12
installDir=src/iguana/algorithms/clas12

if [ $# -lt 1 ]; then
echo """USAGE: $0 [algorithm class name] [namespace] [directory]
[algorithm class name]: the name of your new algorithm
(required)
[namespace]: the namespace (within the \`iguana\` namespace) of your new algorithm
(default: '$namespace')
- if you want the namespace to be \`iguana\` alone, set this to 'iguana'
[directory]: the directory where your algorithm will be created
(default: $installDir)
""" >&2
exit 2
fi

algo=$1
[ $# -ge 2 ] && namespace=$2
[ $# -ge 3 ] && installDir=$3

thisFile=${BASH_SOURCE[0]:-$0}
thisDir=$(cd $(dirname $thisFile) && pwd -P)
exName=ExampleAlgorithm

mkdir -p $installDir

for ext in h cc; do
outFile=$installDir/$algo.$ext
cat $thisDir/$exName.$ext |\
sed "s;$exName;$algo;g" |\
sed "s;example::;$namespace::;g" |\
sed "s;::example;::$namespace;g" |\
sed "s;iguana::iguana;iguana;g" |\
grep -v '//#' \
> $outFile
echo Created $outFile
done

echo """
Done. Now add '$algo.h' and '$algo.cc' to the appropriate meson.build file
"""
2 changes: 2 additions & 0 deletions src/iguana/algorithms/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ algo_sources = [
'Algorithm.cc',
'AlgorithmFactory.cc',
'AlgorithmSequence.cc',
'example/ExampleAlgorithm.cc',
'clas12/EventBuilderFilter.cc',
'clas12/LorentzTransformer.cc',
]
Expand All @@ -11,6 +12,7 @@ algo_public_headers = [
'AlgorithmBoilerplate.h',
'AlgorithmFactory.h',
'AlgorithmSequence.h',
'example/ExampleAlgorithm.h',
'clas12/EventBuilderFilter.h',
'clas12/LorentzTransformer.h',
]
Expand Down

0 comments on commit 20af2c8

Please sign in to comment.