Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 10:30:01

0001 /// \file ROOT/RNTupleWriter.hxx
0002 /// \ingroup NTuple
0003 /// \author Jakob Blomer <jblomer@cern.ch>
0004 /// \date 2024-02-20
0005 
0006 /*************************************************************************
0007  * Copyright (C) 1995-2024, Rene Brun and Fons Rademakers.               *
0008  * All rights reserved.                                                  *
0009  *                                                                       *
0010  * For the licensing terms see $ROOTSYS/LICENSE.                         *
0011  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
0012  *************************************************************************/
0013 
0014 #ifndef ROOT_RNTupleWriter
0015 #define ROOT_RNTupleWriter
0016 
0017 #include <ROOT/RConfig.hxx> // for R__unlikely
0018 #include <ROOT/REntry.hxx>
0019 #include <ROOT/RError.hxx>
0020 #include <ROOT/RNTupleFillContext.hxx>
0021 #include <ROOT/RNTupleFillStatus.hxx>
0022 #include <ROOT/RNTupleMetrics.hxx>
0023 #include <ROOT/RNTupleModel.hxx>
0024 #include <ROOT/RNTupleTypes.hxx>
0025 #include <ROOT/RPageStorage.hxx>
0026 #include <ROOT/RRawPtrWriteEntry.hxx>
0027 
0028 #include <cstddef>
0029 #include <cstdint>
0030 #include <memory>
0031 #include <string_view>
0032 #include <utility>
0033 
0034 class TDirectory;
0035 
0036 namespace ROOT {
0037 
0038 class RNTupleWriteOptions;
0039 
0040 namespace Internal {
0041 // Non-public factory method for an RNTuple writer that uses an already constructed page sink
0042 std::unique_ptr<RNTupleWriter>
0043 CreateRNTupleWriter(std::unique_ptr<ROOT::RNTupleModel> model, std::unique_ptr<Internal::RPageSink> sink);
0044 } // namespace Internal
0045 
0046 // clang-format off
0047 /**
0048 \class ROOT::RNTupleWriter
0049 \ingroup NTuple
0050 \brief An RNTuple that gets filled with entries (data) and writes them to storage
0051 
0052 RNTupleWriter is an interface for writing RNTuples to storage. It can be instantiated using the static functions
0053 Append() and Recreate(), providing an RNTupleModel that defines the schema of the data to be written.
0054 
0055 An RNTuple can be thought of as a table, whose columns are defined by its schema (i.e. by its associated RNTupleModel,
0056 whose Fields map to 0 or more columns).
0057 Writing into an RNTuple happens by filling *entries* into the RNTupleWriter, which make up the rows of the table.
0058 The simplest way to do so is by:
0059 
0060 - retrieving a (shared) pointer to each Field's value;
0061 - writing a value into each pointer;
0062 - calling `writer->Fill()` to commit the entry with all the current pointer values.
0063 
0064 ~~~ {.cpp}
0065 #include <ROOT/RNTupleWriter.hxx>
0066 
0067 /// 1. Create the model.
0068 auto model = ROOT::RNTupleModel::Create();
0069 // Define the schema by adding Fields to the model.
0070 // MakeField returns a shared_ptr to the value to be written (in this case, a shared_ptr<int>)
0071 auto pFoo = model->MakeField<int>("foo");
0072 
0073 /// 2. Create writer from the model.
0074 auto writer = ROOT::RNTupleReader::Recreate(std::move(model), "myNTuple", "some/file.root");
0075 
0076 /// 3. Write into it.
0077 for (int i = 0; i < 10; ++i) {
0078    // Assign the value you want to each RNTuple Field (in this case there is only one Field "foo").
0079    *pFoo = i;
0080 
0081    // Fill() writes the entire entry to the RNTuple.
0082    // After calling Fill() you can safely write another value into `pFoo` knowing that the previous one was
0083    // already saved.
0084    writer->Fill();
0085 }
0086 
0087 // On destruction, the writer will flush the written data to disk.
0088 ~~~
0089 
0090 The caller has to make sure that the data that gets filled into an RNTuple is not modified for the time of the
0091 Fill() call. The Fill call serializes the C++ object into the column format and
0092 writes data into the corresponding column page buffers.
0093 
0094 The actual writing of the buffers to storage is deferred and can be triggered by FlushCluster() or by
0095 destructing the writer.
0096 
0097 On I/O errors, a ROOT::RException is thrown.
0098 
0099 */
0100 // clang-format on
0101 class RNTupleWriter {
0102    friend ROOT::RNTupleModel::RUpdater;
0103    friend std::unique_ptr<RNTupleWriter>
0104       Internal::CreateRNTupleWriter(std::unique_ptr<ROOT::RNTupleModel>, std::unique_ptr<Internal::RPageSink>);
0105 
0106 private:
0107    RNTupleFillContext fFillContext;
0108    Experimental::Detail::RNTupleMetrics fMetrics;
0109 
0110    ROOT::NTupleSize_t fLastCommittedClusterGroup = 0;
0111 
0112    RNTupleWriter(std::unique_ptr<ROOT::RNTupleModel> model, std::unique_ptr<Internal::RPageSink> sink);
0113 
0114    ROOT::RNTupleModel &GetUpdatableModel();
0115    Internal::RPageSink &GetSink() { return *fFillContext.fSink; }
0116 
0117    // Helper function that is called from CommitCluster() when necessary
0118    void CommitClusterGroup();
0119 
0120    /// Create a writer, potentially wrapping the sink in a RPageSinkBuf.
0121    static std::unique_ptr<RNTupleWriter> Create(std::unique_ptr<ROOT::RNTupleModel> model,
0122                                                 std::unique_ptr<Internal::RPageSink> sink,
0123                                                 const ROOT::RNTupleWriteOptions &options);
0124 
0125 public:
0126    /// Creates an RNTupleWriter backed by `storage`, overwriting it if one with the same URI exists.
0127    /// The format of the backing storage is determined by `storage`: in the simplest case it will be a local file, but
0128    /// a different backend may be selected via the URI prefix.
0129    ///
0130    /// The RNTupleWriter will create an RNTuple with the schema determined by `model` (which must not be null) and
0131    /// with name `ntupleName`. This same name can later be used to read back the RNTuple via RNTupleReader.
0132    ///
0133    /// \param model The RNTupleModel describing the schema of the RNTuple written by this writer
0134    /// \param ntupleName The name of the RNTuple to be written
0135    /// \param storage The URI where the RNTuple will be stored (usually just a file name or path)
0136    /// \param options May be passed to customize the behavior of the RNTupleWriter (see also RNTupleWriteOptions).
0137    ///
0138    /// Throws a ROOT::RException if the model is null.
0139    static std::unique_ptr<RNTupleWriter>
0140    Recreate(std::unique_ptr<ROOT::RNTupleModel> model, std::string_view ntupleName, std::string_view storage,
0141             const ROOT::RNTupleWriteOptions &options = ROOT::RNTupleWriteOptions());
0142 
0143    /// Convenience function allowing to call Recreate() with an inline-defined model.
0144    static std::unique_ptr<RNTupleWriter>
0145    Recreate(std::initializer_list<std::pair<std::string_view, std::string_view>> fields, std::string_view ntupleName,
0146             std::string_view storage, const ROOT::RNTupleWriteOptions &options = ROOT::RNTupleWriteOptions());
0147 
0148    /// Creates an RNTupleWriter that writes into an existing TFile or TDirectory, without overwriting its content.
0149    /// `fileOrDirectory` may be an empty TFile.
0150    /// \see Recreate()
0151    static std::unique_ptr<RNTupleWriter> Append(std::unique_ptr<ROOT::RNTupleModel> model, std::string_view ntupleName,
0152                                                 TDirectory &fileOrDirectory,
0153                                                 const ROOT::RNTupleWriteOptions &options = ROOT::RNTupleWriteOptions());
0154    RNTupleWriter(const RNTupleWriter &) = delete;
0155    RNTupleWriter &operator=(const RNTupleWriter &) = delete;
0156    ~RNTupleWriter();
0157 
0158    /// The simplest user interface if the default entry that comes with the ntuple model is used.
0159    /// \return The number of uncompressed bytes written.
0160    std::size_t Fill() { return fFillContext.Fill(fFillContext.fModel->GetDefaultEntry()); }
0161    /// Multiple entries can have been instantiated from the ntuple model.  This method will check the entry's model ID
0162    /// to ensure it comes from the writer's own model or throw an exception otherwise.
0163    /// \return The number of uncompressed bytes written.
0164    std::size_t Fill(ROOT::REntry &entry) { return fFillContext.Fill(entry); }
0165    /// Fill an entry into this ntuple, but don't commit the cluster. The calling code must pass an RNTupleFillStatus
0166    /// and check RNTupleFillStatus::ShouldFlushCluster.
0167    void FillNoFlush(ROOT::REntry &entry, RNTupleFillStatus &status) { fFillContext.FillNoFlush(entry, status); }
0168 
0169    /// Fill an RRawPtrWriteEntry into this ntuple.  This method will check the entry's model ID to ensure it comes from
0170    /// the writer's own model or throw an exception otherwise.
0171    /// \return The number of uncompressed bytes written.
0172    std::size_t Fill(Experimental::Detail::RRawPtrWriteEntry &entry) { return fFillContext.Fill(entry); }
0173    /// Fill an RRawPtrWriteEntry into this ntuple, but don't commit the cluster. The calling code must pass an
0174    /// RNTupleFillStatus and check RNTupleFillStatus::ShouldFlushCluster.
0175    void FillNoFlush(Experimental::Detail::RRawPtrWriteEntry &entry, RNTupleFillStatus &status)
0176    {
0177       fFillContext.FillNoFlush(entry, status);
0178    }
0179 
0180    /// Flush column data, preparing for CommitCluster or to reduce memory usage. This will trigger compression of pages,
0181    /// but not actually write to storage (unless buffered writing is turned off).
0182    void FlushColumns() { fFillContext.FlushColumns(); }
0183    /// Flush so far filled entries to storage
0184    void FlushCluster() { fFillContext.FlushCluster(); }
0185    /// Ensure that the data from the so far seen Fill calls has been written to storage
0186    void CommitCluster(bool commitClusterGroup = false)
0187    {
0188       fFillContext.FlushCluster();
0189       if (commitClusterGroup)
0190          CommitClusterGroup();
0191    }
0192    /// Closes the underlying file (page sink) and expires the model. Automatically called on destruct.
0193    /// Once the dataset is committed, calls to Fill(), [Commit|Flush]Cluster(), FlushColumns(), CreateEntry(),
0194    /// and model updating fail.
0195    void CommitDataset();
0196 
0197    std::unique_ptr<ROOT::REntry> CreateEntry() const { return fFillContext.CreateEntry(); }
0198    std::unique_ptr<Experimental::Detail::RRawPtrWriteEntry> CreateRawPtrWriteEntry() const
0199    {
0200       return fFillContext.CreateRawPtrWriteEntry();
0201    }
0202 
0203    /// Return the entry number that was last flushed in a cluster.
0204    ROOT::NTupleSize_t GetLastFlushed() const { return fFillContext.GetLastFlushed(); }
0205    /// Return the entry number that was last committed in a cluster.
0206    ROOT::NTupleSize_t GetLastCommitted() const { return fFillContext.GetLastFlushed(); }
0207    /// Return the entry number that was last committed in a cluster group.
0208    ROOT::NTupleSize_t GetLastCommittedClusterGroup() const { return fLastCommittedClusterGroup; }
0209    /// Return the number of entries filled so far.
0210    ROOT::NTupleSize_t GetNEntries() const { return fFillContext.GetNEntries(); }
0211 
0212    void EnableMetrics() { fMetrics.Enable(); }
0213    const Experimental::Detail::RNTupleMetrics &GetMetrics() const { return fMetrics; }
0214 
0215    const ROOT::RNTupleModel &GetModel() const { return *fFillContext.fModel; }
0216 
0217    /// Get a RNTupleModel::RUpdater that provides limited support for incremental updates to the underlying
0218    /// model, e.g. addition of new fields.
0219    ///
0220    /// Note that a Model may not be extended with Streamer fields.
0221    ///
0222    /// **Example: add a new field after the model has been used to construct a `RNTupleWriter` object**
0223    /// ~~~ {.cpp}
0224    /// #include <ROOT/RNTuple.hxx>
0225    ///
0226    /// auto model = ROOT::RNTupleModel::Create();
0227    /// auto fldFloat = model->MakeField<float>("fldFloat");
0228    /// auto writer = ROOT::RNTupleWriter::Recreate(std::move(model), "myNTuple", "some/file.root");
0229    /// auto updater = writer->CreateModelUpdater();
0230    /// updater->BeginUpdate();
0231    /// updater->AddField(std::make_unique<RField<float>>("pt"));
0232    /// updater->CommitUpdate();
0233    ///
0234    /// // ...
0235    /// ~~~
0236    std::unique_ptr<ROOT::RNTupleModel::RUpdater> CreateModelUpdater()
0237    {
0238       return std::make_unique<ROOT::RNTupleModel::RUpdater>(*this);
0239    }
0240 }; // class RNTupleWriter
0241 
0242 } // namespace ROOT
0243 
0244 #endif // ROOT_RNTupleWriter