Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-18 09:11:49

0001 // -*- C++ -*-
0002 //
0003 // This file is part of YODA -- Yet more Objects for Data Analysis
0004 // Copyright (C) 2008-2025 The YODA collaboration (see AUTHORS for details)
0005 //
0006 #ifndef YODA_Writer_h
0007 #define YODA_Writer_h
0008 
0009 #include "YODA/AnalysisObject.h"
0010 #include "YODA/Utils/Traits.h"
0011 
0012 #ifdef HAVE_HDF5
0013 #include <YODA/Config/BuildConfig.h>
0014 #ifdef WITH_HIGHFIVE
0015 #include <YODA/highfive/H5File.hpp>
0016 #else
0017 #include <highfive/H5File.hpp>
0018 #define YODA_H5 HighFive
0019 #endif
0020 #endif
0021 
0022 #include <type_traits>
0023 #include <fstream>
0024 #include <iostream>
0025 
0026 namespace YODA {
0027 
0028   static bool enableH5compression = Utils::getEnvParam("YODA_HDF5_COMPRESSION", true);
0029 
0030   /// Pure virtual base class for various output writers.
0031   class Writer {
0032   public:
0033 
0034     /// Virtual destructor
0035     virtual ~Writer() {}
0036 
0037 
0038     /// @name Writing a single analysis object.
0039     /// @{
0040 
0041     /// Write out object @a ao to file @a filename.
0042     void write(const std::string& filename, const AnalysisObject& ao);
0043 
0044     /// Write out object @a ao to output stream @a stream.
0045     void write(std::ostream& stream, const AnalysisObject& ao) {
0046       std::vector<const AnalysisObject*> vec{&ao};
0047       write(stream, vec);
0048     }
0049 
0050     /// Write out pointer-like object @a ao to output stream @a stream.
0051     template <typename T>
0052     typename std::enable_if_t<DerefableToAO<T>::value> //< -> void if valid
0053     write(std::ostream& stream, const T& ao) {
0054       write(stream, *ao);
0055     }
0056 
0057     /// Write out pointer-like object @a ao to file @a filename.
0058     template <typename T>
0059     typename std::enable_if_t<DerefableToAO<T>::value> //< -> void if valid
0060     write(const std::string& filename, const T& ao) {
0061       write(filename, *ao);
0062     }
0063 
0064     /// @}
0065 
0066 
0067     /// @name Writing multiple analysis objects by collection.
0068     /// @{
0069 
0070     /// Write out a vector of AO pointers (untemplated=exact type-match) to the given stream
0071     ///
0072     /// @note This is the canonical function for implementing AO writing to H5:
0073     /// all others call this.
0074     ///
0075     /// @note Among other reasons, this is non-inline to hide zstr from the public API
0076     #ifdef HAVE_HDF5
0077     void write(YODA_H5::File& file, const std::vector<const AnalysisObject*>& aos);
0078     #endif
0079 
0080     /// Write out a vector of AO pointers (untemplated=exact type-match) to the given stream
0081     ///
0082     /// @note This is the canonical function for implementing AO writing to stream:
0083     /// all others call this.
0084     ///
0085     /// @note Among other reasons, this is non-inline to hide zstr from the public API
0086     void write(std::ostream& stream, const std::vector<const AnalysisObject*>& aos, int precision = -1);
0087 
0088 
0089     /// Write out a collection of objects @a objs to output stream @a stream.
0090     ///
0091     /// @note The enable_if call checks whether RANGE is const_iterable, if yes the return
0092     ///       type is void. If not, this template will not be a candidate in the lookup
0093     template <typename RANGE>
0094     typename std::enable_if_t<CIterable<RANGE>::value>
0095     write(std::ostream& stream, const RANGE& aos) {
0096       write(stream, std::begin(aos), std::end(aos));
0097     }
0098 
0099     /// Write out a collection of objects @a objs to file @a filename.
0100     template <typename RANGE>
0101     typename std::enable_if_t<CIterable<RANGE>::value>
0102     write(const std::string& filename, const RANGE& aos) {
0103       write(filename, std::begin(aos), std::end(aos));
0104     }
0105 
0106     /// @}
0107 
0108 
0109     /// @name Writing multiple analysis objects by iterator range.
0110     /// @{
0111 
0112     /// Write out the objects specified by start iterator @a begin and end
0113     /// iterator @a end to output stream @a stream.
0114     ///
0115     /// @todo Add SFINAE trait checking for AOITER = DerefableToAO
0116     template <typename AOITER>
0117     void write(std::ostream& stream, const AOITER& begin, const AOITER& end, int precision = -1) {
0118       std::vector<const AnalysisObject*> vec;
0119       // vec.reserve(std::distance(begin, end));
0120       for (AOITER ipao = begin; ipao != end; ++ipao)  vec.push_back(&(**ipao));
0121       write(stream, vec, precision);
0122     }
0123 
0124 
0125     /// Write out the objects specified by start iterator @a begin and end
0126     /// iterator @a end to file @a filename.
0127     ///
0128     /// @todo Add SFINAE trait checking for AOITER = DerefableToAO
0129     template <typename AOITER>
0130     void write(const std::string& filename, const AOITER& begin, const AOITER& end) {
0131       std::vector<const AnalysisObject*> vec;
0132       // vec.reserve(std::distance(begin, end));
0133       for (AOITER ipao = begin; ipao != end; ++ipao)  vec.push_back(&(**ipao));
0134 
0135       if (filename != "-") {
0136         try {
0137           const size_t lastdot = filename.find_last_of(".");
0138           std::string fmt = Utils::toLower(lastdot == std::string::npos ? filename : filename.substr(lastdot+1));
0139           const bool compress = (fmt == "gz") || (fmt == "h5" && enableH5compression);
0140           useCompression(compress);
0141           #ifdef HAVE_HDF5
0142           // check if the requested format is H5
0143           if (Utils::startswith(fmt, "h5")) {
0144             try {
0145               YODA_H5::File h5(filename, YODA_H5::File::OpenOrCreate | YODA_H5::File::Truncate);
0146               write(h5, vec);
0147             } catch(...) {
0148               throw WriteError("Failed to open HDF5 file " + filename);
0149             }
0150             return;
0151           }
0152           #endif
0153           // try writing to stream
0154           std::ofstream stream;
0155           stream.exceptions(std::ofstream::failbit | std::ofstream::badbit);
0156           stream.open(filename.c_str());
0157           if (stream.fail())
0158             throw WriteError("Writing to filename " + filename + " failed");
0159           write(stream, vec);
0160         } catch (std::ofstream::failure& e) {
0161           throw WriteError("Writing to filename " + filename + " failed: " + e.what());
0162         }
0163       } else {
0164         try {
0165           write(std::cout, vec);
0166         } catch (std::runtime_error& e) {
0167           throw WriteError("Writing to stdout failed: " + std::string(e.what()));
0168         }
0169       }
0170 
0171     }
0172 
0173     /// @}
0174 
0175     /// Set precision of numerical quantities in this writer's output.
0176     void setPrecision(int precision) {
0177       _precision = precision;
0178     }
0179 
0180     /// Set precision of numerical quantities for current AO in this writer's output.
0181     void setAOPrecision(const bool needsDP = false) {
0182       if (needsDP)             _aoprecision = std::numeric_limits<double>::max_digits10;
0183       else if (_precision > 0) _aoprecision = _precision;
0184       else                     _aoprecision = 6;
0185     }
0186 
0187     /// Use libz compression?
0188     void useCompression(const bool compress=true) {
0189       _compress = compress;
0190     }
0191 
0192 
0193   protected:
0194 
0195     /// @name Main writer elements
0196     /// @{
0197 
0198     /// Write any opening boilerplate required by the format to @a stream
0199     virtual void writeHead(std::ostream&) {}
0200 
0201     /// @brief Write the body elements corresponding to AnalysisObject @a ao to @a stream
0202     virtual void writeBody(std::ostream& stream, const AnalysisObject* ao);
0203 
0204     /// @brief Write the body elements corresponding to AnalysisObject pointer @a ao to @a stream
0205     virtual void writeBody(std::ostream& stream, const AnalysisObject& ao);
0206 
0207     /// @brief Write the body elements corresponding to AnalysisObject @a ao to @a stream
0208     /// @note Requires that @a ao is dereferenceable to an AnalysisObject, via the DerefableToAO<T> trait,
0209     template <typename T>
0210     typename std::enable_if_t<DerefableToAO<T>::value> //< -> void if valid
0211     writeBody(std::ostream& stream, const T& ao) { writeBody(stream, *ao); }
0212 
0213     /// Write any closing boilerplate required by the format to @a stream
0214     virtual void writeFoot(std::ostream& stream) { stream << std::flush; }
0215 
0216     /// @}
0217 
0218 
0219     /// @name Specific AO type writer implementations, to be implemented in derived classes
0220     /// @{
0221 
0222     virtual void writeAO(std::ostream& stream, const AnalysisObject& ao) = 0;
0223 
0224     #ifdef HAVE_HDF5
0225     virtual void writeAOS(YODA_H5::File& file, const vector<const AnalysisObject*>& aos) = 0;
0226     #endif
0227 
0228     /// @}
0229 
0230 
0231     /// Output precision
0232     int _precision, _aoprecision;
0233 
0234     /// Compress the output?
0235     bool _compress;
0236 
0237   };
0238 
0239 
0240   /// Factory function to make a writer object by format name or a filename
0241   Writer& mkWriter(const std::string& format_name);
0242 
0243 
0244 }
0245 
0246 #endif