{ "cells": [ { "cell_type": "markdown", "id": "41d5d126", "metadata": {}, "source": [ "# ntpl019_attributes\n", "Example using RNTuple attributes\n", "\n", "RNTuple Attributes are the way to store custom metadata in RNTuple.\n", "\n", "They work by associating rows of user-defined metadata to ranges of entries in their parent RNTuple\n", "(called the \"main RNTuple\"). These rows of metadata, called \"attribute entries\", are\n", "defined much like a regular RNTuple by an RNTupleModel and they belong to an \"attribute set\".\n", "\n", "An attribute set is a standalone collection of attributes which is linked to one and only one RNTuple.\n", "Attribute sets are identified by their name and, similarly to RNTuples, they are created with\n", "an associated Model and can be written and read via bespoke classes (RNTupleAttrSetWriter/Reader).\n", "These classes are never used by themselves but are created from an existing RNTupleWriter or Reader.\n", "\n", "Each main RNTuple can have an arbitrary number of associated attribute sets, though usually one is\n", "enough for most purposes.\n", "This tutorial shows how to create, write and read back attributes from an RNTuple.\n", "\n", "NOTE: The RNTuple attributes are experimental at this point.\n", "Functionality and interface are still subject to changes.\n", "\n", "\n", "\n", "\n", "**Author:** The ROOT Team \n", "This notebook tutorial was automatically generated with ROOTBOOK-izer from the macro found in the ROOT repository on Tuesday, May 19, 2026 at 08:15 PM." ] }, { "cell_type": "code", "execution_count": 1, "id": "52676ab8", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:46.969715Z", "iopub.status.busy": "2026-05-19T20:15:46.969587Z", "iopub.status.idle": "2026-05-19T20:15:46.972758Z", "shell.execute_reply": "2026-05-19T20:15:46.972227Z" } }, "outputs": [], "source": [ "%%cpp -d\n", "\n", "#include \n", "#include \n", "#include \n", "\n", "#include \n", "#include \n", "\n", "#include \n", "#include \n", "#include \n", "\n", "constexpr const char *kFileName = \"ntpl019_attributes.root\";\n", "constexpr const char *kNTupleName = \"ntpl\";\n", "\n", "struct Event {\n", " // ... some event data here ...\n", " double pt = 0.;\n", "};" ] }, { "cell_type": "markdown", "id": "00f06bfe", "metadata": {}, "source": [ " Definition of a helper function: " ] }, { "cell_type": "code", "execution_count": 2, "id": "d5129dfa", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:46.973962Z", "iopub.status.busy": "2026-05-19T20:15:46.973851Z", "iopub.status.idle": "2026-05-19T20:15:47.055966Z", "shell.execute_reply": "2026-05-19T20:15:47.055325Z" } }, "outputs": [], "source": [ "%%cpp -d\n", "\n", "static void Write()\n", "{\n", " // Define the model of our main RNTuple.\n", " auto model = ROOT::RNTupleModel::Create();\n", " auto pEvent = model->MakeField(\"event\");\n", "\n", " // Create our main RNTuple.\n", " // NOTE: currently attributes are only supported in TFile-based RNTuples, so we must\n", " // create the RNTupleWriter via Append(), not Recreate(). This will be relaxed in the future.\n", " auto file = std::unique_ptr(TFile::Open(kFileName, \"RECREATE\"));\n", " auto writer = ROOT::RNTupleWriter::Append(std::move(model), kNTupleName, *file);\n", "\n", " // Define the model for the attribute set we want to create.\n", " auto attrModel = ROOT::RNTupleModel::Create();\n", " attrModel->SetDescription(\"Metadata containing the events' provenance\"); // (this is optional)\n", " auto pRunNumber = attrModel->MakeField(\"runNumber\");\n", "\n", " // Create the attribute set from the main RNTupleWriter. We name it \"Provenance\".\n", " auto attrSet = writer->CreateAttributeSet(std::move(attrModel), \"Provenance\");\n", "\n", " // Writing attributes works like this:\n", " // 1. begin an attribute range\n", " // 2. fill your main RNTuple data as usual\n", " // 3. commit the attribute range.\n", " //\n", " // Between the beginning and committing of each attribute range you can set the values of all the\n", " // fields in the attribute model; all the main RNTuple data you filled in before committing the range\n", " // will have those values as metadata associated to it.\n", " //\n", " // Beginning an attribute range is done like this:\n", " auto attrRange = attrSet->BeginRange();\n", "\n", " // Here you can assign values to your attributes. In this case we only have 1 attribute (the integer \"runNumber\"),\n", " // so we only fill that.\n", " // Note that attribute values can be assigned anywhere between BeginRange() and CommitRange(), so we could do\n", " // this step even after the main data filling.\n", " *pRunNumber = 0;\n", "\n", " // Now fill in the main RNTuple data\n", " TRandom3 rng;\n", " for (int i = 0; i < 100; ++i) {\n", " *pEvent = {rng.Rndm()};\n", " writer->Fill();\n", "\n", " // For explanatory purpose, let's say every 10 entries we have a new run number.\n", " if (i % 10 == 0 && i > 0) {\n", " // Close the attribute range. To do this you need to move the attribute range into the CommitRange\n", " // method, so you cannot reuse `attrRange`.\n", " attrSet->CommitRange(std::move(attrRange));\n", " // If you need to open a new range, call BeginRange again.\n", " attrRange = attrSet->BeginRange();\n", " // Then we can assign the new run number.\n", " *pRunNumber += 1;\n", " }\n", " }\n", "\n", " // IMPORTANT: attributes are not written to storage until you call CommitRange and, differently from regular data,\n", " // they are NOT automatically written on destruction. If you don't call CommitRange, the attribute data will not\n", " // be stored.\n", " // In general, you can check if the attribute range was not committed via its operator bool():\n", " if (attrRange) {\n", " attrSet->CommitRange(std::move(attrRange));\n", " }\n", "}" ] }, { "cell_type": "markdown", "id": "d48a18fb", "metadata": {}, "source": [ " Definition of a helper function: " ] }, { "cell_type": "code", "execution_count": 3, "id": "80072de7", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:47.057312Z", "iopub.status.busy": "2026-05-19T20:15:47.057186Z", "iopub.status.idle": "2026-05-19T20:15:47.059401Z", "shell.execute_reply": "2026-05-19T20:15:47.058903Z" } }, "outputs": [], "source": [ "%%cpp -d\n", "static bool IsGoodRunNumber(std::int32_t runNo)\n", "{\n", " // For illustratory purposes, let's pretend we know that runNumber 4 is \"good\" for our analysis.\n", " return runNo == 4;\n", "}" ] }, { "cell_type": "markdown", "id": "63d9e4a2", "metadata": {}, "source": [ " Definition of a helper function: " ] }, { "cell_type": "code", "execution_count": 4, "id": "e319e0ce", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:47.060599Z", "iopub.status.busy": "2026-05-19T20:15:47.060404Z", "iopub.status.idle": "2026-05-19T20:15:47.098039Z", "shell.execute_reply": "2026-05-19T20:15:47.097407Z" } }, "outputs": [], "source": [ "%%cpp -d\n", "static void Read()\n", "{\n", " // Open the main RNTuple for reading\n", " auto reader = ROOT::RNTupleReader::Open(kNTupleName, kFileName);\n", "\n", " // To read back the list of available attribute sets:\n", " std::cout << \"Here are the attribute sets linked to the RNTuple '\" << kNTupleName << \"':\\n\";\n", " for (const auto &attrSetDesc : reader->GetDescriptor().GetAttrSetIterable()) {\n", " std::cout << \" \" << attrSetDesc.GetName() << \"\\n\";\n", " }\n", "\n", " // Open a specific attribute set\n", " auto attrSet = reader->OpenAttributeSet(\"Provenance\");\n", " // Fetch pointers to all attributes you want to read.\n", " auto pRunNumber = attrSet->GetModel().GetDefaultEntry().GetPtr(\"runNumber\");\n", "\n", " std::cout << \"\\nOpened attribute set '\" << attrSet->GetDescriptor().GetName() << \"' with description: \\\"\"\n", " << attrSet->GetDescriptor().GetDescription() << \"\\\"\\n\";\n", " // Loop over the main entries and, for each, print its associated run number\n", " for (auto mainIdx : reader->GetEntryRange()) {\n", " // There are various ways to access attributes from a main entry index (see the RNTupleAttrSetReader's\n", " // documentation). Here we use GetAttributes to get all attributes associated to the main entry `mainIdx`:\n", " std::cout << \"Entry \" << mainIdx << \" has the following attributes associated to it:\\n\";\n", " for (auto attrIdx : attrSet->GetAttributes(mainIdx)) {\n", " auto range = attrSet->LoadEntry(attrIdx);\n", " std::cout << \" runNumber = \" << *pRunNumber << \" (valid for range [\" << *range.GetFirst() << \", \"\n", " << *range.GetLast() << \"])\\n\";\n", " }\n", " }\n", "\n", " // You can also do the opposite lookup, by looping over all attributes first...\n", " auto pEvent = reader->GetModel().GetDefaultEntry().GetPtr(\"event\");\n", " for (auto attrIdx : attrSet->GetAttributes()) {\n", " auto range = attrSet->LoadEntry(attrIdx);\n", " // ...and then deciding whether you want to load the corresponding range or not.\n", " if (IsGoodRunNumber(*pRunNumber)) {\n", " std::cout << \"\\nRun \" << *pRunNumber << \" is good. Events:\\n\";\n", " for (auto mainIdx = range.GetStart(); mainIdx < range.GetEnd(); ++mainIdx) {\n", " reader->LoadEntry(mainIdx);\n", " std::cout << \" Event \" << mainIdx << \" with pt = \" << pEvent->pt << \"\\n\";\n", " }\n", " }\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 5, "id": "108a8d16", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:47.099284Z", "iopub.status.busy": "2026-05-19T20:15:47.099154Z", "iopub.status.idle": "2026-05-19T20:15:48.048789Z", "shell.execute_reply": "2026-05-19T20:15:48.048109Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Here are the attribute sets linked to the RNTuple 'ntpl':\n", " Provenance\n", "\n", "Opened attribute set 'Provenance' with description: \"Metadata containing the events' provenance\"\n", "Entry 0 has the following attributes associated to it:\n", " runNumber = 0 (valid for range [0, 10])\n", "Entry 1 has the following attributes associated to it:\n", " runNumber = 0 (valid for range [0, 10])\n", "Entry 2 has the following attributes associated to it:\n", " runNumber = 0 (valid for range [0, 10])\n", "Entry 3 has the following attributes associated to it:\n", " runNumber = 0 (valid for range [0, 10])\n", "Entry 4 has the following attributes associated to it:\n", " runNumber = 0 (valid for range [0, 10])\n", "Entry 5 has the following attributes associated to it:\n", " runNumber = 0 (valid for range [0, 10])\n", "Entry 6 has the following attributes associated to it:\n", " runNumber = 0 (valid for range [0, 10])\n", "Entry 7 has the following attributes associated to it:\n", " runNumber = 0 (valid for range [0, 10])\n", "Entry 8 has the following attributes associated to it:\n", " runNumber = 0 (valid for range [0, 10])\n", "Entry 9 has the following attributes associated to it:\n", " runNumber = 0 (valid for range [0, 10])\n", "Entry 10 has the following attributes associated to it:\n", " runNumber = 0 (valid for range [0, 10])\n", "Entry 11 has the following attributes associated to it:\n", " runNumber = 1 (valid for range [11, 20])\n", "Entry 12 has the following attributes associated to it:\n", " runNumber = 1 (valid for range [11, 20])\n", "Entry 13 has the following attributes associated to it:\n", " runNumber = 1 (valid for range [11, 20])\n", "Entry 14 has the following attributes associated to it:\n", " runNumber = 1 (valid for range [11, 20])\n", "Entry 15 has the following attributes associated to it:\n", " runNumber = 1 (valid for range [11, 20])\n", "Entry 16 has the following attributes associated to it:\n", " runNumber = 1 (valid for range [11, 20])\n", "Entry 17 has the following attributes associated to it:\n", " runNumber = 1 (valid for range [11, 20])\n", "Entry 18 has the following attributes associated to it:\n", " runNumber = 1 (valid for range [11, 20])\n", "Entry 19 has the following attributes associated to it:\n", " runNumber = 1 (valid for range [11, 20])\n", "Entry 20 has the following attributes associated to it:\n", " runNumber = 1 (valid for range [11, 20])\n", "Entry 21 has the following attributes associated to it:\n", " runNumber = 2 (valid for range [21, 30])\n", "Entry 22 has the following attributes associated to it:\n", " runNumber = 2 (valid for range [21, 30])\n", "Entry 23 has the following attributes associated to it:\n", " runNumber = 2 (valid for range [21, 30])\n", "Entry 24 has the following attributes associated to it:\n", " runNumber = 2 (valid for range [21, 30])\n", "Entry 25 has the following attributes associated to it:\n", " runNumber = 2 (valid for range [21, 30])\n", "Entry 26 has the following attributes associated to it:\n", " runNumber = 2 (valid for range [21, 30])\n", "Entry 27 has the following attributes associated to it:\n", " runNumber = 2 (valid for range [21, 30])\n", "Entry 28 has the following attributes associated to it:\n", " runNumber = 2 (valid for range [21, 30])\n", "Entry 29 has the following attributes associated to it:\n", " runNumber = 2 (valid for range [21, 30])\n", "Entry 30 has the following attributes associated to it:\n", " runNumber = 2 (valid for range [21, 30])\n", "Entry 31 has the following attributes associated to it:\n", " runNumber = 3 (valid for range [31, 40])\n", "Entry 32 has the following attributes associated to it:\n", " runNumber = 3 (valid for range [31, 40])\n", "Entry 33 has the following attributes associated to it:\n", " runNumber = 3 (valid for range [31, 40])\n", "Entry 34 has the following attributes associated to it:\n", " runNumber = 3 (valid for range [31, 40])\n", "Entry 35 has the following attributes associated to it:\n", " runNumber = 3 (valid for range [31, 40])\n", "Entry 36 has the following attributes associated to it:\n", " runNumber = 3 (valid for range [31, 40])\n", "Entry 37 has the following attributes associated to it:\n", " runNumber = 3 (valid for range [31, 40])\n", "Entry 38 has the following attributes associated to it:\n", " runNumber = 3 (valid for range [31, 40])\n", "Entry 39 has the following attributes associated to it:\n", " runNumber = 3 (valid for range [31, 40])\n", "Entry 40 has the following attributes associated to it:\n", " runNumber = 3 (valid for range [31, 40])\n", "Entry 41 has the following attributes associated to it:\n", " runNumber = 4 (valid for range [41, 50])\n", "Entry 42 has the following attributes associated to it:\n", " runNumber = 4 (valid for range [41, 50])\n", "Entry 43 has the following attributes associated to it:\n", " runNumber = 4 (valid for range [41, 50])\n", "Entry 44 has the following attributes associated to it:\n", " runNumber = 4 (valid for range [41, 50])\n", "Entry 45 has the following attributes associated to it:\n", " runNumber = 4 (valid for range [41, 50])\n", "Entry 46 has the following attributes associated to it:\n", " runNumber = 4 (valid for range [41, 50])\n", "Entry 47 has the following attributes associated to it:\n", " runNumber = 4 (valid for range [41, 50])\n", "Entry 48 has the following attributes associated to it:\n", " runNumber = 4 (valid for range [41, 50])\n", "Entry 49 has the following attributes associated to it:\n", " runNumber = 4 (valid for range [41, 50])\n", "Entry 50 has the following attributes associated to it:\n", " runNumber = 4 (valid for range [41, 50])\n", "Entry 51 has the following attributes associated to it:\n", " runNumber = 5 (valid for range [51, 60])\n", "Entry 52 has the following attributes associated to it:\n", " runNumber = 5 (valid for range [51, 60])\n", "Entry 53 has the following attributes associated to it:\n", " runNumber = 5 (valid for range [51, 60])\n", "Entry 54 has the following attributes associated to it:\n", " runNumber = 5 (valid for range [51, 60])\n", "Entry 55 has the following attributes associated to it:\n", " runNumber = 5 (valid for range [51, 60])\n", "Entry 56 has the following attributes associated to it:\n", " runNumber = 5 (valid for range [51, 60])\n", "Entry 57 has the following attributes associated to it:\n", " runNumber = 5 (valid for range [51, 60])\n", "Entry 58 has the following attributes associated to it:\n", " runNumber = 5 (valid for range [51, 60])\n", "Entry 59 has the following attributes associated to it:\n", " runNumber = 5 (valid for range [51, 60])\n", "Entry 60 has the following attributes associated to it:\n", " runNumber = 5 (valid for range [51, 60])\n", "Entry 61 has the following attributes associated to it:\n", " runNumber = 6 (valid for range [61, 70])\n", "Entry 62 has the following attributes associated to it:\n", " runNumber = 6 (valid for range [61, 70])\n", "Entry 63 has the following attributes associated to it:\n", " runNumber = 6 (valid for range [61, 70])\n", "Entry 64 has the following attributes associated to it:\n", " runNumber = 6 (valid for range [61, 70])\n", "Entry 65 has the following attributes associated to it:\n", " runNumber = 6 (valid for range [61, 70])\n", "Entry 66 has the following attributes associated to it:\n", " runNumber = 6 (valid for range [61, 70])\n", "Entry 67 has the following attributes associated to it:\n", " runNumber = 6 (valid for range [61, 70])\n", "Entry 68 has the following attributes associated to it:\n", " runNumber = 6 (valid for range [61, 70])\n", "Entry 69 has the following attributes associated to it:\n", " runNumber = 6 (valid for range [61, 70])\n", "Entry 70 has the following attributes associated to it:\n", " runNumber = 6 (valid for range [61, 70])\n", "Entry 71 has the following attributes associated to it:\n", " runNumber = 7 (valid for range [71, 80])\n", "Entry 72 has the following attributes associated to it:\n", " runNumber = 7 (valid for range [71, 80])\n", "Entry 73 has the following attributes associated to it:\n", " runNumber = 7 (valid for range [71, 80])\n", "Entry 74 has the following attributes associated to it:\n", " runNumber = 7 (valid for range [71, 80])\n", "Entry 75 has the following attributes associated to it:\n", " runNumber = 7 (valid for range [71, 80])\n", "Entry 76 has the following attributes associated to it:\n", " runNumber = 7 (valid for range [71, 80])\n", "Entry 77 has the following attributes associated to it:\n", " runNumber = 7 (valid for range [71, 80])\n", "Entry 78 has the following attributes associated to it:\n", " runNumber = 7 (valid for range [71, 80])\n", "Entry 79 has the following attributes associated to it:\n", " runNumber = 7 (valid for range [71, 80])\n", "Entry 80 has the following attributes associated to it:\n", " runNumber = 7 (valid for range [71, 80])\n", "Entry 81 has the following attributes associated to it:\n", " runNumber = 8 (valid for range [81, 90])\n", "Entry 82 has the following attributes associated to it:\n", " runNumber = 8 (valid for range [81, 90])\n", "Entry 83 has the following attributes associated to it:\n", " runNumber = 8 (valid for range [81, 90])\n", "Entry 84 has the following attributes associated to it:\n", " runNumber = 8 (valid for range [81, 90])\n", "Entry 85 has the following attributes associated to it:\n", " runNumber = 8 (valid for range [81, 90])\n", "Entry 86 has the following attributes associated to it:\n", " runNumber = 8 (valid for range [81, 90])\n", "Entry 87 has the following attributes associated to it:\n", " runNumber = 8 (valid for range [81, 90])\n", "Entry 88 has the following attributes associated to it:\n", " runNumber = 8 (valid for range [81, 90])\n", "Entry 89 has the following attributes associated to it:\n", " runNumber = 8 (valid for range [81, 90])\n", "Entry 90 has the following attributes associated to it:\n", " runNumber = 8 (valid for range [81, 90])\n", "Entry 91 has the following attributes associated to it:\n", " runNumber = 9 (valid for range [91, 99])\n", "Entry 92 has the following attributes associated to it:\n", " runNumber = 9 (valid for range [91, 99])\n", "Entry 93 has the following attributes associated to it:\n", " runNumber = 9 (valid for range [91, 99])\n", "Entry 94 has the following attributes associated to it:\n", " runNumber = 9 (valid for range [91, 99])\n", "Entry 95 has the following attributes associated to it:\n", " runNumber = 9 (valid for range [91, 99])\n", "Entry 96 has the following attributes associated to it:\n", " runNumber = 9 (valid for range [91, 99])\n", "Entry 97 has the following attributes associated to it:\n", " runNumber = 9 (valid for range [91, 99])\n", "Entry 98 has the following attributes associated to it:\n", " runNumber = 9 (valid for range [91, 99])\n", "Entry 99 has the following attributes associated to it:\n", " runNumber = 9 (valid for range [91, 99])\n", "\n", "Run 4 is good. Events:\n", " Event 41 with pt = 0.502403\n", " Event 42 with pt = 0.20778\n", " Event 43 with pt = 0.28891\n", " Event 44 with pt = 0.0831748\n", " Event 45 with pt = 0.128124\n", " Event 46 with pt = 0.547371\n", " Event 47 with pt = 0.0823196\n", " Event 48 with pt = 0.292141\n", " Event 49 with pt = 0.891624\n", " Event 50 with pt = 0.227118\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Warning in <[ROOT.NTuple] Warning /github/home/ROOT-CI/src/tree/ntuple/src/RNTupleSerialize.cxx:1815 in static ROOT::RResult ROOT::Internal::RNTupleSerializer::SerializeFooter(void*, const ROOT::RNTupleDescriptor&, const RContext&)>: RNTuple Attributes are experimental. They are not guaranteed to be readable back in the future (but your main data is)\n", "Warning in <[ROOT.NTuple] Warning /github/home/ROOT-CI/src/tree/ntuple/src/RNTupleSerialize.cxx:1815 in static ROOT::RResult ROOT::Internal::RNTupleSerializer::SerializeFooter(void*, const ROOT::RNTupleDescriptor&, const RContext&)>: RNTuple Attributes are experimental. They are not guaranteed to be readable back in the future (but your main data is)\n", "Warning in <[ROOT.NTuple] Warning /github/home/ROOT-CI/src/tree/ntuple/src/RNTupleSerialize.cxx:2025 in static ROOT::RResult ROOT::Internal::RNTupleSerializer::DeserializeFooter(const void*, uint64_t, ROOT::Internal::RNTupleDescriptorBuilder&)>: RNTuple Attributes are experimental. They are not guaranteed to be readable back in the future (but your main data is)\n" ] } ], "source": [ "Write();\n", "Read();" ] }, { "cell_type": "markdown", "id": "e7976eaf", "metadata": {}, "source": [ "Draw all canvases " ] }, { "cell_type": "code", "execution_count": 6, "id": "96df4daa", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:48.050032Z", "iopub.status.busy": "2026-05-19T20:15:48.049915Z", "iopub.status.idle": "2026-05-19T20:15:48.267101Z", "shell.execute_reply": "2026-05-19T20:15:48.258268Z" } }, "outputs": [], "source": [ "gROOT->GetListOfCanvases()->Draw()" ] } ], "metadata": { "kernelspec": { "display_name": "ROOT C++", "language": "c++", "name": "root" }, "language_info": { "codemirror_mode": "text/x-c++src", "file_extension": ".C", "mimetype": " text/x-c++src", "name": "c++" } }, "nbformat": 4, "nbformat_minor": 5 }