GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 84.4% 27 / 0 / 32
Functions: 77.8% 7 / 0 / 9
Branches: 29.2% 14 / 0 / 48

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