Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-04-19 09:13:37

0001 // -*- C++ -*-
0002 //
0003 // This file is part of YODA -- Yet more Objects for Data Analysis
0004 // Copyright (C) 2008-2024 The YODA collaboration (see AUTHORS for details)
0005 //
0006 #ifndef YODA_AnalysisObject_h
0007 #define YODA_AnalysisObject_h
0008 
0009 #include "YODA/Exceptions.h"
0010 #include "YODA/Utils/StringUtils.h"
0011 #include "YODA/Config/BuildConfig.h"
0012 #include <iomanip>
0013 #include <string>
0014 #include <limits>
0015 #include <map>
0016 
0017 namespace YODA {
0018 
0019 
0020   /// AnalysisObject is the base class for histograms and scatters
0021   class AnalysisObject {
0022 
0023   public:
0024 
0025     /// Collection type for annotations, as a string-string map.
0026     typedef std::map<std::string, std::string> Annotations;
0027 
0028 
0029     /// @name Creation and destruction
0030     /// @{
0031 
0032     /// Default constructor
0033     AnalysisObject() { }
0034 
0035     /// Constructor giving a type, a path and an optional title
0036     AnalysisObject(const std::string& type, const std::string& path, const std::string& title="") {
0037       setAnnotation("Type", type);
0038       setPath(path);
0039       setTitle(title);
0040     }
0041 
0042     /// Constructor giving a type, a path, another AO to copy annotation from, and an optional title
0043     AnalysisObject(const std::string& type, const std::string& path,
0044                    const AnalysisObject& ao, const std::string& title="") {
0045       AnalysisObject::operator = (ao);
0046       setAnnotation("Type", type); // might override the copied ones
0047       setPath(path);
0048       setTitle(title);
0049     }
0050 
0051     // /// Default copy constructor
0052     // AnalysisObject(const AnalysisObject& ao) {
0053     //   if (ao.path().length() > 0) setPath(ao.path());
0054     //   if (ao.title().length() > 0) setTitle(ao.title());
0055     // }
0056 
0057     /// Default destructor
0058     virtual ~AnalysisObject() { }
0059 
0060     /// Default copy assignment operator
0061     virtual AnalysisObject& operator = (const AnalysisObject& ao) noexcept {
0062       for (const std::string& a : ao.annotations()) {
0063         if (a == "Type")  continue;
0064         if (a == "Path"  && !ao.path().length())  continue;
0065         if (a == "Title" && !ao.title().length())  continue;
0066         setAnnotation(a, ao.annotation(a));
0067       }
0068       return *this;
0069     }
0070 
0071     /// Make a copy on the heap, via 'new'
0072     virtual AnalysisObject* newclone() const = 0;
0073 
0074     /// @}
0075 
0076 
0077 
0078     /// @name Modifiers
0079     /// @{
0080 
0081     /// Reset this analysis object
0082     virtual void reset() = 0;
0083 
0084     /// @}
0085 
0086     /// @name I/O methods
0087     /// @{
0088 
0089     /// Render information about this analysis object
0090     virtual void _renderYODA(std::ostream& os, const int width = 13) const noexcept = 0;
0091 
0092     /// Render scatter-like information about this analysis object
0093     virtual void _renderFLAT(std::ostream& os, const int width = 13) const noexcept = 0;
0094 
0095     /// Return axis configuration (where applicable)
0096     virtual std::string _config() const noexcept { return ""; }
0097 
0098     /// @}
0099 
0100 
0101 
0102     ///@name Annotations
0103     /// @{
0104 
0105     /// Get all the annotation names
0106     /// @todo Change this to return the str->str map, with a separate annotationKeys, etc.
0107     std::vector<std::string> annotations() const {
0108       std::vector<std::string> rtn;
0109       rtn.reserve(_annotations.size());
0110       for (const Annotations::value_type& kv : _annotations) rtn.push_back(kv.first);
0111       return rtn;
0112     }
0113 
0114 
0115     /// Check if an annotation is defined
0116     bool hasAnnotation(const std::string& name) const {
0117       return _annotations.find(name) != _annotations.end();
0118     }
0119 
0120 
0121     /// Get an annotation by name (as a string)
0122     const std::string& annotation(const std::string& name) const {
0123       Annotations::const_iterator v = _annotations.find(name);
0124       // If not found... written this way round on purpose
0125       if (v == _annotations.end()) {
0126         std::string missing = "YODA::AnalysisObject: No annotation named " + name;
0127         throw AnnotationError(missing);
0128       }
0129       return v->second;
0130     }
0131 
0132 
0133     /// Get an annotation by name (as a string) with a default in case the annotation is not found
0134     const std::string& annotation(const std::string& name, const std::string& defaultreturn) const {
0135       Annotations::const_iterator v = _annotations.find(name);
0136       if (v != _annotations.end()) return v->second;
0137       return defaultreturn;
0138     }
0139 
0140 
0141     /// @brief Get an annotation by name (copied to another type)
0142     ///
0143     /// @note Templated on return type
0144     template <typename T>
0145     const T annotation(const std::string& name) const {
0146       std::string s = annotation(name);
0147       return Utils::lexical_cast<T>(s);
0148     }
0149 
0150 
0151     /// @brief Get an annotation by name (copied to another type) with a default in case the annotation is not found
0152     ///
0153     /// @note Templated on return type
0154     template <typename T>
0155     const T annotation(const std::string& name, const T& defaultreturn) const {
0156       Annotations::const_iterator v = _annotations.find(name);
0157       if (v != _annotations.end()) return Utils::lexical_cast<T>(v->second);
0158       return defaultreturn;
0159     }
0160 
0161 
0162     /// @brief Add or set an annotation by name (templated for remaining types)
0163     ///
0164     /// @note Templated on arg type, but stored as a string.
0165     template <typename T>
0166     void setAnnotation(const std::string& name, const T& value) {
0167       if constexpr( std::is_floating_point<T>::value ) {
0168         // Recipe from Boost docs
0169         std::stringstream ss;
0170         ss << std::setprecision(std::numeric_limits<double>::max_digits10) << std::scientific << value;
0171         setAnnotation(name, ss.str());
0172       }
0173       else if constexpr( std::is_same<T, std::string>::value ) {
0174         _annotations[name] = value;
0175       }
0176       else {
0177         _annotations[name] = Utils::lexical_cast<std::string>(value);
0178       }
0179     }
0180 
0181 
0182     /// Set all annotations at once
0183     void setAnnotations(const Annotations& anns) {
0184       _annotations = anns;
0185     }
0186 
0187 
0188     /// @brief Add or set an annotation by name
0189     ///
0190     /// Note: Templated on arg type, but stored as a string. This is just a synonym for setAnnotation.
0191     template <typename T>
0192     void addAnnotation(const std::string& name, const T& value) {
0193       setAnnotation(name, value);
0194     }
0195 
0196 
0197     /// Delete an annotation by name
0198     void rmAnnotation(const std::string& name) {
0199       _annotations.erase(name);
0200     }
0201 
0202 
0203     /// Delete an annotation by name
0204     void clearAnnotations() {
0205       _annotations.clear();
0206     }
0207 
0208     /// @}
0209 
0210 
0211     /// @name Standard annotations
0212     /// @{
0213 
0214     /// @brief Get the AO title.
0215     ///
0216     /// Returns a null string if undefined, rather than throwing an exception cf. the annotation("Title").
0217     const std::string title() const {
0218       return annotation("Title", "");
0219     }
0220 
0221     /// Set the AO title
0222     void setTitle(const std::string& title) {
0223       setAnnotation("Title", title);
0224     }
0225 
0226     /// @brief Get the AO path.
0227     ///
0228     /// Returns a null string if undefined, rather than throwing an exception cf. annotation("Path").
0229     /// @note A leading / will be prepended if not already set.
0230     const std::string path() const {
0231       const std::string p = annotation("Path", "");
0232       // If not set at all, return an empty string
0233       if (p.empty()) return p;
0234       // If missing a leading slash, one will be prepended
0235       return p.find("/") == 0 ? p : ("/"+p);
0236     }
0237 
0238     /// Set the AO path
0239     ///
0240     /// @note A leading / will be prepended if not already given.
0241     void setPath(const std::string& path) {
0242       const std::string p = (path.find("/") == 0) ? path : "/"+path;
0243       // if (path.length() > 0 && path.find("/") != 0) {
0244       //   throw AnnotationError("Histo paths must start with a slash (/) character.");
0245       // }
0246       setAnnotation("Path", p);
0247     }
0248 
0249 
0250     /// Get the AO name -- the last part of the path.
0251     /// Returns a null string if path is undefined
0252     const std::string name() const {
0253       const std::string p = path();
0254       const size_t lastslash = p.rfind("/");
0255       if (lastslash == std::string::npos) return p;
0256       return p.substr(lastslash+1);
0257     }
0258 
0259     /// @}
0260 
0261 
0262   public:
0263 
0264     /// @name Persistency hooks / object type info
0265     /// @{
0266 
0267     /// Get name of the analysis object type
0268     virtual std::string type() const {
0269       return annotation("Type");
0270     }
0271 
0272     /// @brief Get the dimension of the analysis object type
0273     ///
0274     /// @note For fillable types this is the dimension of the fill space (e.g. Histo1D -> dim=1).
0275     ///    For scatter types, it is the total dimension of the points (e.g. Scatter3D -> dim=3).
0276     virtual size_t dim() const noexcept = 0;
0277 
0278     /// @brief Return an inert version of the analysis object (e.g. scatter, estimate)
0279     virtual AnalysisObject* mkInert(const std::string& path = "",
0280                                     const std::string& source = "") const noexcept {
0281       (void)source;
0282       AnalysisObject* rtn = this->newclone();
0283       rtn->setPath(path);
0284       return rtn;
0285     }
0286 
0287     /// @}
0288 
0289     /// @name MPI serialisation
0290     ///@{
0291 
0292     /// @brief Length of serialized content vector for MPI reduce operations
0293     virtual size_t lengthContent(bool fixed_length = false) const noexcept = 0;
0294 
0295     /// @brief Content serialisation for MPI reduce operations
0296     virtual std::vector<double> serializeContent(bool fixed_length = false) const noexcept = 0;
0297 
0298     /// @brief Content deserialisation for MPI reduce operations
0299     virtual void deserializeContent(const std::vector<double>& data) = 0;
0300 
0301     /// @brief Length of serialized meta-data vector for MPI reduce operations
0302     size_t lengthMeta(const bool skipPath = true,
0303                       const bool skipTitle = true) const noexcept {
0304       return 2*(_annotations.size() - skipPath - skipTitle);
0305     }
0306 
0307     /// @brief Mate-data serialisation for MPI reduce operations
0308     std::vector<std::string> serializeMeta(const bool skipPath = true,
0309                                            const bool skipTitle = true) const noexcept {
0310 
0311       // Assemble annotations
0312       std::vector<std::string> rtn;
0313       rtn.reserve(2*(_annotations.size() - skipPath - skipTitle));
0314       for (const auto& item : _annotations) {
0315         if (item.first == "Type")  continue;
0316         if (skipPath && item.first == "Path")  continue;
0317         if (skipTitle && item.first == "Title")  continue;
0318         rtn.push_back(item.first);
0319         rtn.push_back(item.second);
0320       }
0321       return rtn;
0322     }
0323 
0324     /// @brief Mate-data deserialisation for MPI reduce operations
0325     virtual void deserializeMeta(const std::vector<std::string>& data,
0326                                  const bool resetPath = false, const bool resetTitle = false) {
0327 
0328       if (data.empty())  return;
0329       if (data.size() % 2)
0330         throw  UserError("Expected even number of annotation elements (key-value pairs)!");
0331 
0332       const std::string path = annotation("Path");
0333       const std::string type = annotation("Type");
0334       const std::string title = annotation("Title");
0335       _annotations.clear();
0336       _annotations["Type"] = type;
0337       if (!resetPath)   _annotations["Path"] = path;
0338       if (!resetTitle)  _annotations["Title"] = title;
0339 
0340       auto itr = data.cbegin();
0341       const auto itrEnd = data.cend();
0342       while (itr != itrEnd) {
0343         const std::string key = *itr; ++itr;
0344         const std::string val = *itr; ++itr;
0345         _annotations[key] = val;
0346       }
0347     }
0348 
0349     /// @}
0350 
0351   private:
0352 
0353     /// The annotations indexed by name
0354     std::map<std::string,std::string> _annotations;
0355 
0356   };
0357 
0358 
0359   /// Convenience alias
0360   using AO = AnalysisObject;
0361 
0362 
0363 }
0364 
0365 #endif // YODA_AnalysisObject_h