From 20af2c85f63e9e5316012829cf4354a15ce78e3e Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Fri, 5 Jan 2024 12:12:11 -0500 Subject: [PATCH] feat: generate a template algorithm (#67) --- README.md | 1 + .../algorithms/example/ExampleAlgorithm.cc | 111 ++++++++++++++++++ .../algorithms/example/ExampleAlgorithm.h | 98 ++++++++++++++++ src/iguana/algorithms/example/README.md | 25 ++++ .../algorithms/example/make_template.sh | 49 ++++++++ src/iguana/algorithms/meson.build | 2 + 6 files changed, 286 insertions(+) create mode 100644 src/iguana/algorithms/example/ExampleAlgorithm.cc create mode 100644 src/iguana/algorithms/example/ExampleAlgorithm.h create mode 100644 src/iguana/algorithms/example/README.md create mode 100755 src/iguana/algorithms/example/make_template.sh diff --git a/README.md b/README.md index fc3bcc87..7527d5b7 100644 --- a/README.md +++ b/README.md @@ -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] diff --git a/src/iguana/algorithms/example/ExampleAlgorithm.cc b/src/iguana/algorithms/example/ExampleAlgorithm.cc new file mode 100644 index 00000000..a59f738a --- /dev/null +++ b/src/iguana/algorithms/example/ExampleAlgorithm.cc @@ -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) { + } + +} diff --git a/src/iguana/algorithms/example/ExampleAlgorithm.h b/src/iguana/algorithms/example/ExampleAlgorithm.h new file mode 100644 index 00000000..795a0db4 --- /dev/null +++ b/src/iguana/algorithms/example/ExampleAlgorithm.h @@ -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; + + }; + +} diff --git a/src/iguana/algorithms/example/README.md b/src/iguana/algorithms/example/README.md new file mode 100644 index 00000000..08521c95 --- /dev/null +++ b/src/iguana/algorithms/example/README.md @@ -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! diff --git a/src/iguana/algorithms/example/make_template.sh b/src/iguana/algorithms/example/make_template.sh new file mode 100755 index 00000000..2ff8f078 --- /dev/null +++ b/src/iguana/algorithms/example/make_template.sh @@ -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 +""" diff --git a/src/iguana/algorithms/meson.build b/src/iguana/algorithms/meson.build index c49ea640..becff3f4 100644 --- a/src/iguana/algorithms/meson.build +++ b/src/iguana/algorithms/meson.build @@ -2,6 +2,7 @@ algo_sources = [ 'Algorithm.cc', 'AlgorithmFactory.cc', 'AlgorithmSequence.cc', + 'example/ExampleAlgorithm.cc', 'clas12/EventBuilderFilter.cc', 'clas12/LorentzTransformer.cc', ] @@ -11,6 +12,7 @@ algo_public_headers = [ 'AlgorithmBoilerplate.h', 'AlgorithmFactory.h', 'AlgorithmSequence.h', + 'example/ExampleAlgorithm.h', 'clas12/EventBuilderFilter.h', 'clas12/LorentzTransformer.h', ]