Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-22 08:48:40

0001 // Copyright 2023, Jefferson Science Associates, LLC.
0002 // Subject to the terms in the LICENSE file found in the top-level directory.
0003 // Created by Nathan Brei
0004 
0005 #pragma once
0006 
0007 /**
0008  * Omnifactories are a lightweight layer connecting JANA to generic algorithms
0009  * It is assumed multiple input data (controlled by input tags)
0010  * which might be changed by user parameters.
0011  */
0012 
0013 #include <JANA/JEvent.h>
0014 #include <JANA/JMultifactory.h>
0015 #include <spdlog/spdlog.h>
0016 #include <spdlog/version.h>
0017 #if SPDLOG_VERSION >= 11400 && (!defined(SPDLOG_NO_TLS) || !SPDLOG_NO_TLS)
0018 #include <spdlog/mdc.h>
0019 #endif
0020 
0021 #include "services/log/Log_service.h"
0022 
0023 #include <string>
0024 #include <vector>
0025 
0026 // PodioTypeMap provides type traits for podio types
0027 // This mirrors the structure written by the legacy python generator,
0028 // and puts the types in the format expected by JANA2.
0029 template <typename T> struct PodioTypeMap {
0030   using collection_t = typename T::collection_type;
0031   using mutable_t    = typename T::mutable_type;
0032 };
0033 
0034 struct EmptyConfig {};
0035 
0036 template <typename AlgoT, typename ConfigT = EmptyConfig>
0037 class JOmniFactory : public JMultifactory {
0038 public:
0039   /// ========================
0040   /// Handle input collections
0041   /// ========================
0042 
0043   struct InputBase {
0044     std::string type_name;
0045     std::vector<std::string> collection_names;
0046     bool is_variadic = false;
0047 
0048     virtual void GetCollection(const JEvent& event) = 0;
0049   };
0050 
0051   template <typename T, bool IsOptional = false> class Input : public InputBase {
0052 
0053     std::vector<const T*> m_data;
0054 
0055   public:
0056     Input(JOmniFactory* owner, std::string default_tag = "") {
0057       owner->RegisterInput(this);
0058       this->collection_names.push_back(default_tag);
0059       this->type_name = JTypeInfo::demangle<T>();
0060     }
0061 
0062     const std::vector<const T*>& operator()() { return m_data; }
0063 
0064   private:
0065     friend class JOmniFactory;
0066 
0067     void GetCollection(const JEvent& event) {
0068       try {
0069         m_data = event.Get<T>(this->collection_names[0], !IsOptional);
0070       } catch (const JException& e) {
0071         if constexpr (!IsOptional) {
0072           throw JException("JOmniFactory: Failed to get collection %s: %s",
0073                            this->collection_names[0].c_str(), e.what());
0074         }
0075       }
0076     }
0077   };
0078 
0079   template <typename PodioT, bool IsOptional = false> class PodioInput : public InputBase {
0080 
0081     const typename PodioTypeMap<PodioT>::collection_t* m_data;
0082 
0083   public:
0084     PodioInput(JOmniFactory* owner, std::string default_collection_name = "") {
0085       owner->RegisterInput(this);
0086       this->collection_names.push_back(default_collection_name);
0087       this->type_name = JTypeInfo::demangle<PodioT>();
0088     }
0089 
0090     const typename PodioTypeMap<PodioT>::collection_t* operator()() { return m_data; }
0091 
0092   private:
0093     friend class JOmniFactory;
0094 
0095     void GetCollection(const JEvent& event) {
0096       try {
0097         m_data = event.GetCollection<PodioT>(this->collection_names[0], !IsOptional);
0098       } catch (const JException& e) {
0099         if constexpr (!IsOptional) {
0100           throw JException("JOmniFactory: Failed to get collection %s: %s",
0101                            this->collection_names[0].c_str(), e.what());
0102         }
0103       }
0104     }
0105   };
0106 
0107   template <typename PodioT, bool IsOptional = false> class VariadicPodioInput : public InputBase {
0108 
0109     std::vector<const typename PodioTypeMap<PodioT>::collection_t*> m_data;
0110 
0111   public:
0112     VariadicPodioInput(JOmniFactory* owner, std::vector<std::string> default_names = {}) {
0113       owner->RegisterInput(this);
0114       this->collection_names = default_names;
0115       this->type_name        = JTypeInfo::demangle<PodioT>();
0116       this->is_variadic      = true;
0117     }
0118 
0119     const std::vector<const typename PodioTypeMap<PodioT>::collection_t*> operator()() {
0120       return m_data;
0121     }
0122 
0123   private:
0124     friend class JOmniFactory;
0125 
0126     void GetCollection(const JEvent& event) {
0127       m_data.clear();
0128       for (auto& coll_name : this->collection_names) {
0129         try {
0130           m_data.push_back(event.GetCollection<PodioT>(coll_name, !IsOptional));
0131         } catch (const JException& e) {
0132           if constexpr (!IsOptional) {
0133             throw JException("JOmniFactory: Failed to get collection %s: %s", coll_name.c_str(),
0134                              e.what());
0135           }
0136         }
0137       }
0138     }
0139   };
0140 
0141   void RegisterInput(InputBase* input) { m_inputs.push_back(input); }
0142 
0143   /// =========================
0144   /// Handle output collections
0145   /// =========================
0146 
0147   struct OutputBase {
0148     std::string type_name;
0149     std::vector<std::string> collection_names;
0150     bool is_variadic = false;
0151 
0152     virtual void CreateHelperFactory(JOmniFactory& fac) = 0;
0153     virtual void SetCollection(JOmniFactory& fac)       = 0;
0154     virtual void Reset()                                = 0;
0155   };
0156 
0157   template <typename T> class Output : public OutputBase {
0158     std::vector<T*> m_data;
0159 
0160   public:
0161     Output(JOmniFactory* owner, std::string default_tag_name = "") {
0162       owner->RegisterOutput(this);
0163       this->collection_names.push_back(default_tag_name);
0164       this->type_name = JTypeInfo::demangle<T>();
0165     }
0166 
0167     std::vector<T*>& operator()() { return m_data; }
0168 
0169   private:
0170     friend class JOmniFactory;
0171 
0172     void CreateHelperFactory(JOmniFactory& fac) override {
0173       fac.DeclareOutput<T>(this->collection_names[0]);
0174     }
0175 
0176     void SetCollection(JOmniFactory& fac) override {
0177       fac.SetData<T>(this->collection_names[0], this->m_data);
0178     }
0179 
0180     void Reset() override { m_data.clear(); }
0181   };
0182 
0183   template <typename PodioT> class PodioOutput : public OutputBase {
0184 
0185     std::unique_ptr<typename PodioTypeMap<PodioT>::collection_t> m_data;
0186 
0187   public:
0188     PodioOutput(JOmniFactory* owner, std::string default_collection_name = "") {
0189       owner->RegisterOutput(this);
0190       this->collection_names.push_back(default_collection_name);
0191       this->type_name = JTypeInfo::demangle<PodioT>();
0192     }
0193 
0194     std::unique_ptr<typename PodioTypeMap<PodioT>::collection_t>& operator()() { return m_data; }
0195 
0196   private:
0197     friend class JOmniFactory;
0198 
0199     void CreateHelperFactory(JOmniFactory& fac) override {
0200       fac.DeclarePodioOutput<PodioT>(this->collection_names[0]);
0201     }
0202 
0203     void SetCollection(JOmniFactory& fac) override {
0204       if (m_data == nullptr) {
0205         throw JException("JOmniFactory: SetCollection failed due to missing output collection '%s'",
0206                          this->collection_names[0].c_str());
0207         // Otherwise this leads to a PODIO segfault
0208       }
0209       fac.SetCollection<PodioT>(this->collection_names[0], std::move(this->m_data));
0210     }
0211 
0212     void Reset() override {
0213       m_data = std::move(std::make_unique<typename PodioTypeMap<PodioT>::collection_t>());
0214     }
0215   };
0216 
0217   template <typename PodioT> class VariadicPodioOutput : public OutputBase {
0218 
0219     std::vector<std::unique_ptr<typename PodioTypeMap<PodioT>::collection_t>> m_data;
0220 
0221   public:
0222     VariadicPodioOutput(JOmniFactory* owner,
0223                         std::vector<std::string> default_collection_names = {}) {
0224       owner->RegisterOutput(this);
0225       this->collection_names = default_collection_names;
0226       this->type_name        = JTypeInfo::demangle<PodioT>();
0227       this->is_variadic      = true;
0228     }
0229 
0230     std::vector<std::unique_ptr<typename PodioTypeMap<PodioT>::collection_t>>& operator()() {
0231       return m_data;
0232     }
0233 
0234   private:
0235     friend class JOmniFactory;
0236 
0237     void CreateHelperFactory(JOmniFactory& fac) override {
0238       for (auto& coll_name : this->collection_names) {
0239         fac.DeclarePodioOutput<PodioT>(coll_name);
0240       }
0241     }
0242 
0243     void SetCollection(JOmniFactory& fac) override {
0244       if (m_data.size() != this->collection_names.size()) {
0245         throw JException("JOmniFactory: VariadicPodioOutput SetCollection failed: Declared %d "
0246                          "collections, but provided %d.",
0247                          this->collection_names.size(), m_data.size());
0248         // Otherwise this leads to a PODIO segfault
0249       }
0250       std::size_t i = 0;
0251       for (auto& coll_name : this->collection_names) {
0252         fac.SetCollection<PodioT>(coll_name, std::move(this->m_data[i++]));
0253       }
0254     }
0255 
0256     void Reset() override {
0257       m_data.clear();
0258       for (auto& coll_name [[maybe_unused]] : this->collection_names) {
0259         m_data.push_back(std::make_unique<typename PodioTypeMap<PodioT>::collection_t>());
0260       }
0261     }
0262   };
0263 
0264   void RegisterOutput(OutputBase* output) { m_outputs.push_back(output); }
0265 
0266   // =================
0267   // Handle parameters
0268   // =================
0269 
0270   struct ParameterBase {
0271     std::string m_name;
0272     std::string m_description;
0273     virtual void Configure(JParameterManager& parman, const std::string& prefix) = 0;
0274     virtual void Configure(std::map<std::string, std::string> fields)            = 0;
0275   };
0276 
0277   template <typename T> class ParameterRef : public ParameterBase {
0278 
0279     T* m_data;
0280 
0281   public:
0282     ParameterRef(JOmniFactory* owner, std::string name, T& slot, std::string description = "") {
0283       owner->RegisterParameter(this);
0284       this->m_name        = name;
0285       this->m_description = description;
0286       m_data              = &slot;
0287     }
0288 
0289     const T& operator()() { return *m_data; }
0290 
0291   private:
0292     friend class JOmniFactory;
0293 
0294     void Configure(JParameterManager& parman, const std::string& prefix) override {
0295       parman.SetDefaultParameter(prefix + ":" + this->m_name, *m_data, this->m_description);
0296     }
0297     void Configure(std::map<std::string, std::string> fields) override {
0298       auto it = fields.find(this->m_name);
0299       if (it != fields.end()) {
0300         const auto& value_str = it->second;
0301         JParameterManager::Parse(value_str, *m_data);
0302       }
0303     }
0304   };
0305 
0306   template <typename T> class Parameter : public ParameterBase {
0307 
0308     T m_data;
0309 
0310   public:
0311     Parameter(JOmniFactory* owner, std::string name, T default_value, std::string description) {
0312       owner->RegisterParameter(this);
0313       this->m_name        = name;
0314       this->m_description = description;
0315       m_data              = default_value;
0316     }
0317 
0318     const T& operator()() { return m_data; }
0319 
0320   private:
0321     friend class JOmniFactory;
0322 
0323     void Configure(JParameterManager& parman, const std::string& prefix) override {
0324       parman.SetDefaultParameter(prefix + ":" + this->m_name, m_data, this->m_description);
0325     }
0326     void Configure(std::map<std::string, std::string> fields) override {
0327       auto it = fields.find(this->m_name);
0328       if (it != fields.end()) {
0329         const auto& value_str = it->second;
0330         if constexpr (10000 * JVersion::major + 100 * JVersion::minor + 1 * JVersion::patch <
0331                       20102) {
0332           m_data = JParameterManager::Parse<T>(value_str);
0333         } else {
0334           JParameterManager::Parse(value_str, m_data);
0335         }
0336       }
0337     }
0338   };
0339 
0340   void RegisterParameter(ParameterBase* parameter) { m_parameters.push_back(parameter); }
0341 
0342   void ConfigureAllParameters(std::map<std::string, std::string> fields) {
0343     for (auto* parameter : this->m_parameters) {
0344       parameter->Configure(fields);
0345     }
0346   }
0347 
0348   // ===============
0349   // Handle services
0350   // ===============
0351 
0352   struct ServiceBase {
0353     virtual void Init(JApplication* app) = 0;
0354   };
0355 
0356   template <typename ServiceT> class Service : public ServiceBase {
0357 
0358     std::shared_ptr<ServiceT> m_data;
0359 
0360   public:
0361     Service(JOmniFactory* owner) { owner->RegisterService(this); }
0362 
0363     ServiceT& operator()() { return *m_data; }
0364 
0365   private:
0366     friend class JOmniFactory;
0367 
0368     void Init(JApplication* app) { m_data = app->GetService<ServiceT>(); }
0369   };
0370 
0371   void RegisterService(ServiceBase* service) { m_services.push_back(service); }
0372 
0373   // ================
0374   // Handle resources
0375   // ================
0376 
0377   struct ResourceBase {
0378     virtual void ChangeRun(const JEvent& event) = 0;
0379   };
0380 
0381   template <typename ServiceT, typename ResourceT, typename LambdaT>
0382   class Resource : public ResourceBase {
0383     ResourceT m_data;
0384     LambdaT m_lambda;
0385 
0386   public:
0387     Resource(JOmniFactory* owner, LambdaT lambda) : m_lambda(lambda) {
0388       owner->RegisterResource(this);
0389     };
0390 
0391     const ResourceT& operator()() { return m_data; }
0392 
0393   private:
0394     friend class JOmniFactory;
0395 
0396     void ChangeRun(const JEvent& event) {
0397       auto run_nr                       = event.GetRunNumber();
0398       std::shared_ptr<ServiceT> service = event.GetJApplication()->template GetService<ServiceT>();
0399       m_data                            = m_lambda(service, run_nr);
0400     }
0401   };
0402 
0403   void RegisterResource(ResourceBase* resource) { m_resources.push_back(resource); }
0404 
0405 public:
0406   std::vector<InputBase*> m_inputs;
0407   std::vector<OutputBase*> m_outputs;
0408   std::vector<ParameterBase*> m_parameters;
0409   std::vector<ServiceBase*> m_services;
0410   std::vector<ResourceBase*> m_resources;
0411 
0412 private:
0413   // App belongs on JMultifactory, it is just missing temporarily
0414   JApplication* m_app;
0415 
0416   // Plugin name belongs on JMultifactory, it is just missing temporarily
0417   std::string m_plugin_name;
0418 
0419   // Prefix for parameters and loggers, derived from plugin name and tag in PreInit().
0420   std::string m_prefix;
0421 
0422   /// Current logger
0423   std::shared_ptr<spdlog::logger> m_logger;
0424 
0425   /// Configuration
0426   ConfigT m_config;
0427 
0428 public:
0429   std::size_t FindVariadicCollectionCount(std::size_t total_input_count,
0430                                           std::size_t variadic_input_count,
0431                                           std::size_t total_collection_count, bool is_input) {
0432 
0433     std::size_t variadic_collection_count =
0434         total_collection_count - (total_input_count - variadic_input_count);
0435 
0436     if (variadic_input_count == 0) {
0437       // No variadic inputs: check that collection_name count matches input count exactly
0438       if (total_input_count != total_collection_count) {
0439         throw JException(
0440             "JOmniFactory '%s': Wrong number of %s collection names: %d expected, %d found.",
0441             m_prefix.c_str(), (is_input ? "input" : "output"), total_input_count,
0442             total_collection_count);
0443       }
0444     } else {
0445       // Variadic inputs: check that we have enough collection names for the non-variadic inputs
0446       if (total_input_count - variadic_input_count > total_collection_count) {
0447         throw JException("JOmniFactory '%s': Not enough %s collection names: %d needed, %d found.",
0448                          m_prefix.c_str(), (is_input ? "input" : "output"),
0449                          total_input_count - variadic_input_count, total_collection_count);
0450       }
0451 
0452       // Variadic inputs: check that the variadic collection names is evenly divided by the variadic input count
0453       if (variadic_collection_count % variadic_input_count != 0) {
0454         throw JException("JOmniFactory '%s': Wrong number of %s collection names: %d found total, "
0455                          "but %d can't be distributed among %d variadic inputs evenly.",
0456                          m_prefix.c_str(), (is_input ? "input" : "output"), total_collection_count,
0457                          variadic_collection_count, variadic_input_count);
0458       }
0459     }
0460     return variadic_collection_count;
0461   }
0462 
0463   inline void PreInit(std::string tag, std::vector<std::string> default_input_collection_names,
0464                       std::vector<std::string> default_output_collection_names) {
0465 
0466     m_prefix = (this->GetPluginName().empty()) ? tag : this->GetPluginName() + ":" + tag;
0467 
0468     // Obtain collection name overrides if provided.
0469     // Priority = [JParameterManager, JOmniFactoryGenerator]
0470     m_app->SetDefaultParameter(m_prefix + ":InputTags", default_input_collection_names,
0471                                "Input collection names");
0472     m_app->SetDefaultParameter(m_prefix + ":OutputTags", default_output_collection_names,
0473                                "Output collection names");
0474 
0475     // Figure out variadic inputs
0476     std::size_t variadic_input_count = 0;
0477     for (auto* input : m_inputs) {
0478       if (input->is_variadic) {
0479         variadic_input_count += 1;
0480       }
0481     }
0482     std::size_t variadic_input_collection_count = FindVariadicCollectionCount(
0483         m_inputs.size(), variadic_input_count, default_input_collection_names.size(), true);
0484 
0485     // Set input collection names
0486     for (std::size_t i = 0; auto* input : m_inputs) {
0487       input->collection_names.clear();
0488       if (input->is_variadic) {
0489         for (std::size_t j = 0; j < (variadic_input_collection_count / variadic_input_count); ++j) {
0490           input->collection_names.push_back(default_input_collection_names[i++]);
0491         }
0492       } else {
0493         input->collection_names.push_back(default_input_collection_names[i++]);
0494       }
0495     }
0496 
0497     // Figure out variadic outputs
0498     std::size_t variadic_output_count = 0;
0499     for (auto* output : m_outputs) {
0500       if (output->is_variadic) {
0501         variadic_output_count += 1;
0502       }
0503     }
0504     std::size_t variadic_output_collection_count = FindVariadicCollectionCount(
0505         m_outputs.size(), variadic_output_count, default_output_collection_names.size(), true);
0506 
0507     // Set output collection names and create corresponding helper factories
0508     for (std::size_t i = 0; auto* output : m_outputs) {
0509       output->collection_names.clear();
0510       if (output->is_variadic) {
0511         for (std::size_t j = 0; j < (variadic_output_collection_count / variadic_output_count);
0512              ++j) {
0513           output->collection_names.push_back(default_output_collection_names[i++]);
0514         }
0515       } else {
0516         output->collection_names.push_back(default_output_collection_names[i++]);
0517       }
0518       output->CreateHelperFactory(*this);
0519     }
0520 
0521     // Obtain logger (defines the parameter option)
0522     m_logger = m_app->GetService<Log_service>()->logger(m_prefix);
0523   }
0524 
0525   void Init() override {
0526     auto app = GetApplication();
0527     for (auto* parameter : m_parameters) {
0528       parameter->Configure(*(app->GetJParameterManager()), m_prefix);
0529     }
0530     for (auto* service : m_services) {
0531       service->Init(app);
0532     }
0533     static_cast<AlgoT*>(this)->Configure();
0534   }
0535 
0536   void BeginRun(const std::shared_ptr<const JEvent>& event) override {
0537     for (auto* resource : m_resources) {
0538       resource->ChangeRun(*event);
0539     }
0540     static_cast<AlgoT*>(this)->ChangeRun(event->GetRunNumber());
0541   }
0542 
0543   virtual void ChangeRun(int32_t /* run_number */) override {};
0544 
0545   virtual void Process(int32_t /* run_number */, uint64_t /* event_number */) {};
0546 
0547   void Process(const std::shared_ptr<const JEvent>& event) override {
0548     try {
0549       for (auto* input : m_inputs) {
0550         input->GetCollection(*event);
0551       }
0552       for (auto* output : m_outputs) {
0553         output->Reset();
0554       }
0555 #if SPDLOG_VERSION >= 11400 && (!defined(SPDLOG_NO_TLS) || !SPDLOG_NO_TLS)
0556       spdlog::mdc::put("e", std::to_string(event->GetEventNumber()));
0557 #endif
0558       static_cast<AlgoT*>(this)->Process(event->GetRunNumber(), event->GetEventNumber());
0559       for (auto* output : m_outputs) {
0560         output->SetCollection(*this);
0561       }
0562     } catch (std::exception& e) {
0563       throw JException(e.what());
0564     }
0565   }
0566 
0567   using ConfigType = ConfigT;
0568 
0569   void SetApplication(JApplication* app) { m_app = app; }
0570 
0571   JApplication* GetApplication() { return m_app; }
0572 
0573   void SetPluginName(std::string plugin_name) { m_plugin_name = plugin_name; }
0574 
0575   std::string GetPluginName() { return m_plugin_name; }
0576 
0577   inline std::string GetPrefix() { return m_prefix; }
0578 
0579   /// Retrieve reference to already-configured logger
0580   std::shared_ptr<spdlog::logger>& logger() { return m_logger; }
0581 
0582   /// Retrieve reference to embedded config object
0583   ConfigT& config() { return m_config; }
0584 };