GCC Code Coverage Report


Directory: ./
File: src/iguana/algorithms/Algorithm.h
Date: 2025-11-25 17:57:04
Coverage Exec Excl Total
Lines: 88.6% 31 0 35
Functions: 100.0% 7 0 7
Branches: 28.6% 16 0 56

Line Branch Exec Source
1 #pragma once
2
3 #include <mutex>
4 #include <optional>
5 #include <set>
6
7 #include <hipo4/bank.h>
8
9 #include "AlgorithmBoilerplate.h"
10 #include "iguana/bankdefs/BankDefs.h"
11 #include "iguana/services/RCDBReader.h"
12 #include "iguana/services/YAMLReader.h"
13 #include <iguana/services/GlobalParam.h>
14
15 namespace iguana {
16
17 //////////////////////////////////////////////////////////////////////////////
18 // ALGORITHM TOOLS
19 //////////////////////////////////////////////////////////////////////////////
20
21 /// general algorithm tools
22 namespace tools {
23 /// Get the index of a bank from a `hipo::banklist`.
24 /// @note this function is preferred over `hipo::getBanklistIndex`, since it handles the case where there are more than one bank
25 /// in the banklist with the same name
26 /// @param banks the `hipo::banklist` from which to get the specified bank
27 /// @param bank_name the name of the bank
28 /// @param variant if 0, the _first_ bank named `bank_name` in `banks` will be returned; if 1, the 2nd such bank will be returned, _etc_.;
29 /// note, you can call `Algorithm::GetCreatedBankVariant` to get the created-bank variant number for a specific algorithm
30 /// @returns the `hipo::banklist` index of the bank
31 /// @see Algorithm::GetCreatedBankIndex or AlgorithmSequence::GetCreatedBankIndex to get the index of a bank created by a creator algorithm.
32 hipo::banklist::size_type GetBankIndex(
33 hipo::banklist& banks,
34 std::string const& bank_name,
35 unsigned int const& variant = 0) noexcept(false);
36 }
37
38 //////////////////////////////////////////////////////////////////////////////
39 // BASE CLASS ALGORITHM
40 //////////////////////////////////////////////////////////////////////////////
41
42 /// Option value variant type
43 /* NOTE: if you modify this, you also must modify:
44 * - [ ] `PrintOptionValue`
45 * - [ ] Template specializations in this class
46 * - [ ] Template specializations in `YAMLReader` or `ConfigFileReader`, and `ConcurrentParam`
47 * - [ ] Add new tests, if you added new types
48 * - FIXME: adding `bool` type may be tricky, see https://github.com/JeffersonLab/iguana/issues/347
49 */
50 using option_t = std::variant<
51 int,
52 double,
53 std::string,
54 std::vector<int>,
55 std::vector<double>,
56 std::vector<std::string>>;
57
58 /// @brief Base class for all algorithms to inherit from
59 ///
60 /// This is the base class for all algorithms. It provides common members, such as
61 /// a logger instance and options data structure. Algorithm implementations must:
62 /// - inherit from this base class
63 /// - override the methods `Algorithm::Start`, `Algorithm::Run` and `Algorithm::Stop`
64 ///
65 /// See existing algorithms for examples.
66 class Algorithm : public Object
67 {
68
69 public:
70
71 /// @param name the unique name for a derived class instance
72 100 Algorithm(std::string_view name)
73 100 : Object(name)
74 100 , m_rows_only(false)
75
1/4
✓ Branch 3 → 4 taken 100 times.
✗ Branch 3 → 19 not taken.
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 22 not taken.
100 , m_default_config_file("")
76
1/4
✓ Branch 4 → 5 taken 100 times.
✗ Branch 4 → 13 not taken.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 16 not taken.
100 , o_user_config_file("")
77
2/6
✓ Branch 3 → 4 taken 100 times.
✗ Branch 3 → 19 not taken.
✓ Branch 5 → 6 taken 100 times.
✗ Branch 5 → 7 not taken.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 10 not taken.
200 , o_user_config_dir("")
78 100 {}
79 300 virtual ~Algorithm() {}
80
81 /// @brief Initialize this algorithm before any events are processed, with the intent to process _banks_
82 ///
83 /// use this method if you intend to use `Algorithm::Run`.
84 /// @param banks the list of banks this algorithm will use, so that `Algorithm::Run` can cache the indices
85 /// of the banks that it needs
86 virtual void Start(hipo::banklist& banks) = 0;
87
88 /// @brief Initialize this algorithm before any events are processed, with the intent to process _bank rows_ rather than full banks;
89 ///
90 /// use this method if you intend to use "action functions" instead of `Algorithm::Run`.
91 void Start();
92
93 /// @brief **Run Function:** Process an event's `hipo::banklist`
94 /// @param banks the list of banks to process
95 /// @returns a boolean value, which is typically used to decide whether or not to continue analyzing an event, _i.e._, it can be used
96 /// as an _event-level_ filter; not all algorithms use or need this feature; see the algorithm's more specialized `Run` functions,
97 /// which have `hipo::bank` parameters
98 /// @see Specialized `%Run` function(s) above/below; they take individual `hipo::bank` objects as parameters, and their documentation explains which banks are used by this algorithm and how.
99 virtual bool Run(hipo::banklist& banks) const = 0;
100
101 /// @brief Finalize this algorithm after all events are processed.
102 virtual void Stop() = 0;
103
104 /// Set an option specified by the user. If the option name is `"log"`, the log level of the `Logger`
105 /// owned by this algorithm will be changed to the specified value.
106 /// @param key the name of the option
107 /// @param val the value to set
108 /// @returns the value that has been set (if needed, _e.g._, when `val` is an rvalue)
109 template <typename OPTION_TYPE>
110 68 OPTION_TYPE SetOption(std::string const& key, const OPTION_TYPE val)
111 {
112 // FIXME: this template is not specialized, to be friendlier to python `cppyy` bindings
113
5/10
std::vector<int, std::allocator<int> > iguana::Algorithm::SetOption<std::vector<int, std::allocator<int> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::vector<int, std::allocator<int> >):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 13 taken 11 times.
char const* iguana::Algorithm::SetOption<char const*>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*):
✓ Branch 2 → 3 taken 38 times.
✗ Branch 2 → 5 not taken.
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > iguana::Algorithm::SetOption<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 14 times.
std::vector<double, std::allocator<double> > iguana::Algorithm::SetOption<std::vector<double, std::allocator<double> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::vector<double, std::allocator<double> >):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 13 taken 1 time.
iguana::Logger::Level iguana::Algorithm::SetOption<iguana::Logger::Level>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, iguana::Logger::Level):
✓ Branch 2 → 3 taken 4 times.
✗ Branch 2 → 4 not taken.
68 if(key == "log") {
114 if constexpr(std::disjunction<
115 std::is_same<OPTION_TYPE, std::string>,
116 std::is_same<OPTION_TYPE, char const*>,
117 std::is_same<OPTION_TYPE, Logger::Level>>::value)
118 42 m_log->SetLevel(val);
119 else
120 m_log->Error("Option '{}' must be a string or a Logger::Level", key);
121 }
122 64 m_option_cache[key] = val;
123 68 return val;
124 }
125
126 /// Get the value of a scalar option
127 /// @param key the unique key name of this option, for caching; if empty, the option will not be cached
128 /// @param node_path the `YAML::Node` identifier path to search for this option in the config files; if empty, it will just use `key`
129 /// @returns the scalar option
130 template <typename OPTION_TYPE>
131 OPTION_TYPE GetOptionScalar(std::string const& key, YAMLReader::node_path_t node_path = {}) const;
132
133 /// Get the value of a vector option
134 /// @param key the unique key name of this option, for caching; if empty, the option will not be cached
135 /// @param node_path the `YAML::Node` identifier path to search for this option in the config files; if empty, it will just use `key`
136 /// @returns the vector option
137 template <typename OPTION_TYPE>
138 std::vector<OPTION_TYPE> GetOptionVector(std::string const& key, YAMLReader::node_path_t node_path = {}) const;
139
140 /// Get the value of a vector option, and convert it to `std::set`
141 /// @param key the unique key name of this option
142 /// @param node_path the `YAML::Node` identifier path to search for this option in the config files; if empty, it will just use `key`
143 /// @returns the vector option converted to `std::set`
144 template <typename OPTION_TYPE>
145 std::set<OPTION_TYPE> GetOptionSet(std::string const& key, YAMLReader::node_path_t node_path = {}) const;
146
147 /// Set the name of this algorithm
148 /// @param name the new name
149 void SetName(std::string_view name);
150
151 /// Get a reference to this algorithm's configuration (`YAMLReader`)
152 /// @returns the configuration
153 std::unique_ptr<YAMLReader> const& GetConfig() const;
154
155 /// Set a custom `YAMLReader` to use for this algorithm
156 /// @param yaml_config the custom `YAMLReader` instance
157 void SetConfig(std::unique_ptr<YAMLReader>&& yaml_config);
158
159 /// Set a custom configuration file for this algorithm
160 /// @see `Algorithm::SetConfigDirectory`
161 /// @param name the configuration file name
162 void SetConfigFile(std::string const& name);
163
164 /// Set a custom configuration file directory for this algorithm
165 /// @see `Algorithm::SetConfigFile`
166 /// @param name the directory name
167 void SetConfigDirectory(std::string const& name);
168
169 /// Get the index of a bank in a `hipo::banklist`; throws an exception if the bank is not found
170 /// @param banks the list of banks this algorithm will use
171 /// @param bank_name the name of the bank
172 /// @returns the `hipo::banklist` index of the bank
173 /// @see tools::GetBankIndex for a function that is independent of algorithm
174 /// @see GetCreatedBankIndex, a convenience method for _Iguana-created_ banks
175 hipo::banklist::size_type GetBankIndex(hipo::banklist& banks, std::string const& bank_name) const noexcept(false);
176
177 /// Get the index of an _Iguana-created_ bank in a `hipo::banklist`; throws an exception if the bank is not found, or if the algorithm
178 /// creates more than one bank
179 /// @param banks the list of banks this algorithm will use
180 /// @returns the `hipo::banklist` index of the bank
181 /// @see GetBankIndex for a more general method
182 hipo::banklist::size_type GetCreatedBankIndex(hipo::banklist& banks) const noexcept(false);
183
184 /// Get the list of created bank names, for creator-type algorithms
185 /// @see `Algorithm::GetCreatedBankName` for algorithms which create only one bank
186 /// @returns the list of new bank names
187 std::vector<std::string> GetCreatedBankNames() const noexcept(false);
188
189 /// Get the created bank name, for creator-type algorithms which create only one new bank
190 /// @see `Algorithm::GetCreatedBankNames` for algorithms which create more than one new bank
191 /// @returns the new bank name
192 std::string GetCreatedBankName() const noexcept(false);
193
194 /// Get a bank created by a creator-type algorithm. The bank must be defined in `src/iguana/bankdefs/iguana.json`.
195 /// Use this function if you intend to use specialized `Run(hipo::bank&, ...)` functions, where one of its parameters
196 /// is a (reference to) a created bank.
197 /// @param [in] bank_name the created bank name, which is only needed if the algorithm creates more than one bank
198 /// @returns the new bank
199
2/4
✓ Branch 69 → 70 taken 1 time.
✗ Branch 69 → 183 not taken.
✓ Branch 76 → 114 taken 1 time.
✗ Branch 76 → 181 not taken.
2 hipo::bank GetCreatedBank(std::string const& bank_name = "") const noexcept(false);
200
201 /// Get a bank schema created by a creator-type algorithm. The bank must be defined in `src/iguana/bankdefs/iguana.json`.
202 /// @see `Algorithm::GetCreatedBank`
203 /// @param [in] bank_name the created bank name, which is only needed if the algorithm creates more than one bank
204 /// @returns the new bank schema
205 hipo::schema GetCreatedBankSchema(std::string const& bank_name = "") const noexcept(false);
206
207 /// @returns the variant number of a created bank
208 /// @see tools::GetBankIndex for details
209 unsigned int GetCreatedBankVariant() const;
210
211 /// @returns the RCDB reader instance
212 std::unique_ptr<RCDBReader>& GetRCDBReader();
213
214 protected: // methods
215
216 /// Parse YAML configuration files. Sets `m_yaml_config`.
217 void ParseYAMLConfig();
218
219 /// Instantiate the `RCDBReader` instance for this algorithm
220 void StartRCDBReader();
221
222 /// Get the reference to a bank from a `hipo::banklist`; optionally checks if the bank name matches the expectation
223 /// @param banks the `hipo::banklist` from which to get the specified bank
224 /// @param idx the index of `banks` of the specified bank
225 /// @param expected_bank_name if specified, checks that the specified bank has this name
226 /// @return a reference to the bank
227
5/16
✗ Branch 8 → 9 not taken.
✗ Branch 8 → 116 not taken.
✓ Branch 11 → 12 taken 882 times.
✗ Branch 11 → 107 not taken.
✓ Branch 50 → 51 taken 882 times.
✓ Branch 50 → 57 taken 2961 times.
✗ Branch 57 → 58 not taken.
✓ Branch 57 → 64 taken 3843 times.
✗ Branch 64 → 65 not taken.
✓ Branch 64 → 67 taken 3843 times.
✗ Branch 100 → 101 not taken.
✗ Branch 100 → 107 not taken.
✗ Branch 108 → 109 not taken.
✗ Branch 108 → 115 not taken.
✗ Branch 116 → 117 not taken.
✗ Branch 116 → 119 not taken.
5607 hipo::bank& GetBank(hipo::banklist& banks, hipo::banklist::size_type const idx, std::string const& expected_bank_name = "") const noexcept(false);
228
229 /// Create a new bank and push it to the bank list. The bank must be defined in `src/iguana/bankdefs/iguana.json`.
230 /// @param [out] banks the `hipo::banklist` onto which the new bank will be pushed
231 /// @param [out] bank_idx will be set to the `hipo::banklist` index of the new bank
232 /// @param [in] bank_name the new bank name
233 /// @returns the bank's schema
234 hipo::schema CreateBank(
235 hipo::banklist& banks,
236 hipo::banklist::size_type& bank_idx,
237 std::string const& bank_name) noexcept(false);
238
239 /// Dump all banks in a `hipo::banklist`
240 /// @param banks the banks to show
241 /// @param message if specified, print a header message
242 /// @param level the log level
243 void ShowBanks(hipo::banklist const& banks, std::string_view message = "", Logger::Level const level = Logger::trace) const;
244
245 /// Dump a single bank
246 /// @param bank the bank to show
247 /// @param message if specified, print a header message
248 /// @param level the log level
249 void ShowBank(hipo::bank const& bank, std::string_view message = "", Logger::Level const level = Logger::trace) const;
250
251 /// Get an option from the option cache
252 /// @param key the key name associated with this option
253 /// @returns the option value, if found (using `std::optional`)
254 template <typename OPTION_TYPE>
255 std::optional<OPTION_TYPE> GetCachedOption(std::string const& key) const;
256
257 /// Throw a runtime exception since this algorithm has been renamed.
258 /// Guidance will be printed for the user.
259 /// @param new_name the new name of the algorithm
260 /// @param version the first software version where this change applies
261 void ThrowSinceRenamed(std::string const& new_name, std::string const& version) const noexcept(false);
262
263 private: // methods
264
265 /// Prepend `node_path` with the full algorithm name. If `node_path` is empty, set it to `{key}`.
266 /// @param key the key name for this option
267 /// @param node_path the `YAMLReader::node_path_t` to prepend
268 void CompleteOptionNodePath(std::string const& key, YAMLReader::node_path_t& node_path) const;
269
270 // PrintOptionValue: overloaded for different value types
271 void PrintOptionValue(std::string const& key, int const& val, Logger::Level const level = Logger::debug, std::string_view prefix = "OPTION") const;
272 void PrintOptionValue(std::string const& key, double const& val, Logger::Level const level = Logger::debug, std::string_view prefix = "OPTION") const;
273 void PrintOptionValue(std::string const& key, std::string const& val, Logger::Level const level = Logger::debug, std::string_view prefix = "OPTION") const;
274 void PrintOptionValue(std::string const& key, std::vector<int> const& val, Logger::Level const level = Logger::debug, std::string_view prefix = "OPTION") const;
275 void PrintOptionValue(std::string const& key, std::vector<double> const& val, Logger::Level const level = Logger::debug, std::string_view prefix = "OPTION") const;
276 void PrintOptionValue(std::string const& key, std::vector<std::string> const& val, Logger::Level const level = Logger::debug, std::string_view prefix = "OPTION") const;
277
278 protected: // members
279
280 /// Class name of this algorithm
281 std::string m_class_name;
282
283 /// If true, algorithm can only operate on bank _rows_; `Algorithm::GetBank`, and therefore `Algorithm::Run`, cannot be called
284 bool m_rows_only;
285
286 /// Default configuration file name
287 std::string m_default_config_file;
288
289 /// User's configuration file name, which may override the default configuration file, `m_default_config_file`.
290 /// Set it with `Algorithm::SetConfigFile`
291 std::string o_user_config_file;
292
293 /// User's configuration file directory.
294 /// Set it with `Algorithm::SetConfigDirectory`
295 std::string o_user_config_dir;
296
297 /// A mutex for this algorithm
298 mutable std::mutex m_mutex;
299
300 /// Unique created-bank variant number, to handle the case where a user creates duplicate banks, _e.g._, with two creator algorithm
301 /// instances that are configured differently
302 unsigned int m_created_bank_variant{0};
303
304 /// RCDB reader
305 std::unique_ptr<RCDBReader> m_rcdb;
306
307 private: // members
308
309 /// YAML reader
310 std::unique_ptr<YAMLReader> m_yaml_config;
311
312 /// Data structure to hold configuration options set by `Algorithm::SetOption`
313 std::unordered_map<std::string, option_t> m_option_cache;
314 };
315
316 //////////////////////////////////////////////////////////////////////////////
317 // ALGORITHM FACTORY
318 //////////////////////////////////////////////////////////////////////////////
319
320 /// Algorithm pointer type
321 using algo_t = std::unique_ptr<Algorithm>;
322
323 /// @brief Factory to create an algorithm.
324 class AlgorithmFactory
325 {
326
327 public:
328
329 /// Algorithm creator function type
330 using algo_creator_t = std::function<algo_t()>;
331
332 AlgorithmFactory() = delete;
333
334 /// Register an algorithm with a unique name. Algorithms register themselves by calling this function.
335 /// @param algo_name the name of the algorithm (not equivalent to `Object::m_name`)
336 /// @param creator the creator function
337 /// @param new_banks if this algorithm creates *new* banks, list them here
338 /// @returns true if the algorithm has not yet been registered
339 static bool Register(std::string const& algo_name, algo_creator_t creator, std::vector<std::string> const new_banks = {}) noexcept;
340
341 /// Create an algorithm. Throws an exception if the algorithm cannot be created
342 /// @param algo_name the name of the algorithm, which was used as an argument in the `AlgorithmFactory::Register` call
343 /// @returns the algorithm instance
344 static algo_t Create(std::string const& algo_name) noexcept(false);
345
346 /// Get list of creator-type algorithms which create a particular bank
347 /// @param bank_name the bank name
348 /// @returns the list of algorithms which create the bank, if any
349 static std::optional<std::vector<std::string>> GetCreatorAlgorithms(std::string const& bank_name) noexcept;
350
351 /// Get list of banks which are created by a particular creator-type algorithm
352 /// @param algo_name the algorithm name
353 /// @returns the list of banks which are created by the algorithm, if any
354 static std::optional<std::vector<std::string>> GetCreatedBanks(std::string const& algo_name) noexcept(false);
355
356 private:
357
358 /// Association between the algorithm names and their creators
359 static std::unordered_map<std::string, algo_creator_t> s_creators;
360
361 /// Association from a created bank to the creator-type algorithms that create it
362 static std::unordered_map<std::string, std::vector<std::string>> s_bank_to_algos;
363
364 /// Association from a creator-type algorithm to the banks it creates
365 static std::unordered_map<std::string, std::vector<std::string>> s_algo_to_banks;
366 };
367 }
368