Please note that this tutorial has very simplified versions of classes that could be found in a framework, such as DataProduct, FileService, ParallelOutputter, and SerializingOutputter. They try to mimick the usage in a framework (for example, Outputters are agnostic of the data written, which is encapsulated in std::vector<DataProduct>), but are not meant for production usage!
Also note that this tutorial uses std::thread and std::mutex directly instead of a task scheduling library such as Threading Building Blocks (TBB). For that reason, turning on ROOT's implicit multithreading (IMT) would not be very efficient with the simplified code in this tutorial because a thread blocking to acquire a std::mutex cannot "help" the other thread that is currently in the critical section by executing its tasks. If that is wanted, the framework should use synchronization methods provided by TBB directly (which goes beyond the scope of this tutorial).
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <mutex>
#include <random>
#include <string>
#include <string_view>
#include <thread>
#include <utility>
#include <vector>
using ModelTokensPair = std::pair<std::unique_ptr<ROOT::RNTupleModel>, std::vector<ROOT::RFieldToken>>;
const void *address;
};
std::unique_ptr<TFile> fFile;
std::mutex fMutex;
public:
{
fFile.reset(
TFile::Open(std::string(
url).c_str(), std::string(options).c_str()));
}
TFile &GetFile() {
return *fFile; }
std::mutex &GetMutex() { return fMutex; }
};
public:
virtual void InitSlot(
unsigned slot) = 0;
virtual void Fill(
unsigned slot,
const std::vector<DataProduct> &
products) = 0;
};
std::vector<ROOT::RFieldToken>
fTokens;
std::unique_ptr<RRawPtrWriteEntry>
entry;
};
std::vector<SlotData> fSlots;
public:
{
}
void InitSlot(
unsigned slot)
final
{
if (
slot >= fSlots.size()) {
}
fSlots[
slot].entry = fSlots[
slot].fillContext->GetModel().CreateRawPtrWriteEntry();
}
void Fill(
unsigned slot,
const std::vector<DataProduct> &
products)
final
{
}
{
}
}
}
};
std::unique_ptr<ROOT::RNTupleWriter> fWriter;
std::vector<ROOT::RFieldToken>
fTokens;
std::unique_ptr<RRawPtrWriteEntry>
entry;
};
std::vector<SlotData> fSlots;
public:
{
}
void InitSlot(
unsigned slot)
final
{
if (
slot >= fSlots.size()) {
}
fSlots[
slot].entry = fWriter->GetModel().CreateRawPtrWriteEntry();
}
void Fill(
unsigned slot,
const std::vector<DataProduct> &
products)
final
{
}
{
fWriter->FillNoFlush(
entry, status);
fWriter->FlushColumns();
{
fWriter->FlushCluster();
}
}
}
}
};
float eta;
float mass;
float phi;
};
};
std::vector<ChargedTrack>
muons;
};
{
std::vector<ROOT::RFieldToken>
tokens;
tokens.push_back(model->GetToken(
"eventId"));
model->MakeField<decltype(Event::runId)>("runId");
tokens.push_back(model->GetToken(
"runId"));
model->MakeField<decltype(Event::electrons)>("electrons");
tokens.push_back(model->GetToken(
"electrons"));
model->MakeField<decltype(Event::photons)>("photons");
tokens.push_back(model->GetToken(
"photons"));
model->MakeField<decltype(Event::muons)>("muons");
tokens.push_back(model->GetToken(
"muons"));
return {std::move(model), std::move(
tokens)};
}
{
products.emplace_back(2, &event.electrons);
products.emplace_back(3, &event.photons);
}
struct Run {
std::uint32_t nEvents;
};
{
std::vector<ROOT::RFieldToken>
tokens;
model->MakeField<decltype(Run::runId)>("runId");
tokens.push_back(model->GetToken(
"runId"));
model->MakeField<decltype(Run::nEvents)>("nEvents");
tokens.push_back(model->GetToken(
"nEvents"));
return {std::move(model), std::move(
tokens)};
}
{
}
{
std::uniform_real_distribution<float>
floatDist;
std::uint32_t nEvents = 0;
nEvents =
static_cast<std::uint32_t
>(
nEventsD);
}
for (std::uint32_t eventId = 0; eventId < nEvents; eventId++) {
event.eventId = eventId;
}
}
muon.charge = (
gen() % 2 ? 1 : -1);
}
}
Run run;
run.nEvents = nEvents;
}
}
{
}
}
}
}
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
A container of const raw pointers, corresponding to a row in the data set.
A context for filling entries (data) into clusters of an RNTuple.
A writer to fill an RNTuple from multiple contexts.
A status object after filling an entry.
bool ShouldFlushCluster() const
Return true if the caller should call FlushCluster.
static std::unique_ptr< RNTupleModel > CreateBare()
Creates a "bare model", i.e. an RNTupleModel with no default entry.
Common user-tunable settings for storing RNTuples.
void SetUseBufferedWrite(bool val)
void SetApproxZippedClusterSize(std::size_t val)
static std::unique_ptr< RNTupleWriter > Append(std::unique_ptr< ROOT::RNTupleModel > model, std::string_view ntupleName, TDirectory &fileOrDirectory, const ROOT::RNTupleWriteOptions &options=ROOT::RNTupleWriteOptions())
Creates an RNTupleWriter that writes into an existing TFile or TDirectory, without overwriting its co...
A ROOT file is an on-disk file, usually with extension .root, that stores objects in a file-system-li...
static TFile * Open(const char *name, Option_t *option="", const char *ftitle="", Int_t compress=ROOT::RCompressionSetting::EDefaults::kUseCompiledDefault, Int_t netopt=0)
Create / open a file.
double product(double const *factors, std::size_t nFactors)