Warning, /acts/docs/examples/howto/add_new_algorithm.md is written in an unsupported language. File is not indexed.
0001 # Add a new algorithm
0002
0003 ## Purpose
0004
0005 The main part of ACTS code is located in the `Core` packages.
0006 To use this code in the ACTS examples framework, an algorithm is needed.
0007 Before doing so, the ideas explored in Examples are typically first developed as algorithms.
0008 In a second step, the essential parts of this code are then moved to the `Core` packages,
0009 which the algorithm then executes.
0010
0011 ## Code Organisation
0012
0013 Algorithms reside in `Examples/Algorithms` of the repository.
0014 The code is split into a header with the algorithm class declaration
0015 and a source file containing the implementation.
0016
0017 Assuming that you want to experiment with a new seeding algorithm the files to add would be:
0018 `Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/MySeedingAlgorithm.h`
0019 and
0020 `Examples/Algorithms/TrackFinding/src/MySeedingAlgorithm.cpp`
0021
0022 The `CMakeLists.txt` file needs to be updated as well.
0023
0024 ## Algorithm Class
0025
0026 The algorithm class has to inherit from `ActsExamples::IAlgorithm`
0027 and thus implement this single method:
0028
0029 ```cpp
0030 ProcessCode execute(const AlgorithmContext& ctx) const final;
0031 ```
0032
0033 Optionally, they can also override the following lifecycle methods which are
0034 called at the beginning and end of the event loop:
0035
0036 ```cpp
0037 ProcessCode initialize() final;
0038 ProcessCode finalize() final;
0039 ```
0040
0041 Note that these methods are not `const`, meaning they can manipulate members of
0042 the algorithm object. This is safe because these methods are only called from a
0043 single thread.
0044
0045 :::{hint}
0046 There are other types of algorithms for reading inputs from files
0047 or for saving data to files. All these interfaces can be found in
0048 `Examples/Framework/include/ActsExamples/Framework`.
0049 In particular, there are base classes for algorithms for IO operations.
0050 :::
0051
0052 :::{important}
0053 The constructor should ideally follow certain rules. See section on configuration below.
0054 :::
0055
0056 The `execute` method will be called once for each event.
0057 Other methods can also be added to your class.
0058 It is good to remember that the algorithmic code in ACTS is typically stateless,
0059 and if the state needs to be passed between the calls it should be done explicitly.
0060 An algorithm is stateless if it does not modify its own attributes while executing.
0061 This way it becomes reentrant and in consequence thread safe.
0062 For instance if the event processing is best organized with such methods:
0063
0064 ```cpp
0065 void prepareBuffers();
0066 void fillBuffers( const SpacePoint& );
0067 void extractInfo();
0068 ```
0069
0070 that need to pass the data between each other these methods should rather look like this:
0071
0072 ```cpp
0073 struct SeedingBuffers{ ... some buffers ... };
0074 void prepareBuffers(SeedingBuffers& buffers);
0075 void fillBuffers(SeedingBuffers& buffers, const SpacePoint& );
0076 void extractInfo(const SeedingBuffers& buffers);
0077 ```
0078
0079 :::{tip}
0080 It is common practice to put the algorithm code in the `ActsExamples` namespace.
0081 :::
0082
0083 ## Input and Output
0084
0085 The algorithm would be typically part of some processing sequence and thus
0086 consume and produce some event data. In the hypothetical example discussed
0087 here, space-points are the input and track seeds an output.
0088
0089 Data is passed between algorithms through a central *event store*, basically a
0090 dictionary type that can store arbitrary value types in memory.
0091
0092 The data can be retrieved using special *handle* objects that encode an
0093 object type and the name with which they are associated in the event store.
0094
0095 To add an input and output to your algorithm, in the class declaration add handles like
0096
0097 ```cpp
0098 ReadDataHandle<SimSpacePointContainer> m_inputSpacePoints{this, "InputSpacePoints"};
0099 WriteDataHandle<SimSeedContainer> m_outputSeeds{this, "OutputSeeds"};
0100 ```
0101
0102 The first argument is needed to register the handles with the owning algorithm,
0103 while the second argument is the *handle name* that is used in debug printouts
0104 to identify the handle.
0105
0106 In your algorithm constructor, you can then *initialize* the handles with a
0107 key, that is used to read and write the data objects to and from the event
0108 store. This key can be hard-coded, or you can make it configurable by making it
0109 a property of the algorithm `Config` nested struct.
0110
0111 This initialization can look something like this:
0112
0113 ```cpp
0114 m_inputSpacePoints.initialize(m_cfg.inputSpacePoints);
0115 m_outputSeeds.initialize(m_cfg.outputSeeds);
0116 ```
0117
0118 where both `m_cfg.inputSpacePoints` and `m_cfg.outputSeeds` are simple strings.
0119
0120 To read data inside your `execute` function, you can use these
0121 handles with the event context given as an argument:
0122
0123 ```cpp
0124 const auto& inputSpacePoints = m_inputSpacePoints(ctx);
0125 ```
0126
0127 The data object (or objects) produced by an algorithm can be placed in the
0128 store by calling the output handle like this:
0129
0130 ```cpp
0131 auto mydata = makeData();
0132 m_outputSeeds(ctx, std::move(mydata));
0133 ```
0134
0135 The ownership of the object is transferred to the store. That is, the
0136 destruction of this object at the end of event processing is taken care of.
0137
0138 ## Configurability
0139
0140 It is customary that an algorithm requires configuration parameters.
0141 For example, in a seeding algorithm these parameters could include which detector layers should be used.
0142 The configuration can be provided to an algorithm through an additional class/structure.
0143 For algorithm, it is typically an inner class named `Config`.
0144
0145 That is how the configuration object could look like for `MySeedingAlgorithm`:
0146
0147 ```cpp
0148 class MySeedingAlgoritm : ...
0149 public:
0150 struct Config {
0151 std::vector<int> layers; // layers to use by the seeder
0152 float deltaZ; // the maximum allowed deviation in r-z plane
0153 };
0154 ...
0155 ```
0156
0157 :::{tip}
0158 It is customary to put the config structures in the ``Acts`` namespace.
0159 :::
0160
0161 The algorithm constructor would take a `MySeedingAlgorithm::Config` object during
0162 the construction in addition to an argument controlling verbosity of diagnostic messages.
0163
0164 ```cpp
0165 MySeedingAlgorithm::MySeedingAlgorithm( Config cfg, Acts::Logging::Level lvl):
0166 ActsExamples::BareAlgortihm("MySeedingAlgorithm", lvl),
0167 m_cfg(std::move(cfg)){...}
0168 ```
0169
0170 ## Python bindings
0171
0172 In order to use an algorithm in standalone ACTS the algorithm
0173 and the associated config structure need to be accessible from python.
0174 For that, python bindings need to be created using the pybind11 library.
0175 The binding is defined in C++ code in `Examples/Python/src/` directory.
0176 There is one source file per category,
0177 in this particular case the file to edit would be `TrackFinding.cpp`.
0178
0179 The algorithm class and associated config class can be made known to python via such binding definition:
0180
0181 ```cpp
0182 ACTS_PYTHON_DECLARE_ALGORITHM(
0183 ActsExamples::MySeedingAlgorithm,
0184 "MySeedingAlgorithm",
0185 layers, deltaZ);
0186 ```
0187
0188 The bindings can be tested in a standalone python session:
0189
0190 ```python
0191 from acts .examples import *
0192 help(MySeedingAlgorithm)
0193 help(MySeedingConfig)
0194 ```
0195 An info about the class and config structure should be printed.
0196
0197 ## Example empty algorithm
0198
0199 A complete example of an algorithm called `UserAlgorithm` can be found in these two branches/locations:
0200
0201 [Algorithm definition](https://github.com/asalzburger/acts/tree/ws-add-user-algorithm/Examples/Algorithms/Tutorial)
0202
0203 [Python bindings definition](https://github.com/asalzburger/acts/blob/ws-add-user-algorithm-python-bindings/Examples/Python/src/Tutorial.cpp)