I’m encountering an issue when trying to loop over the fields of an RNTuple. Specifically, when I create a new RNTuple with two fields, the function reader->GetDescriptor().GetNFields() reports three fields, while PrintInfo() shows two.
Is there a default field that gets created with an RNTuple, or is something else causing this discrepancy?
Additionally, when I call reader->GetDescriptor().GetQualifiedFieldName(numberOfFields - 1), I get an std::out_of_range error. It only accepts values 0 and 1, even though GetNFields() says there are three fields. Could you clarify why this might be happening?
Here is a minimal example of the code:
#include <ROOT/RNTupleModel.hxx>
#include <ROOT/RNTupleReader.hxx>
#include <ROOT/RNTupleWriter.hxx>
using ROOT::Experimental::RNTupleModel;
using ROOT::Experimental::RNTupleReader;
using ROOT::Experimental::RNTupleWriter;
int main () {
{
// Create an RNTuple model
auto model = RNTupleModel::Create();
// Create fields for the RNTuple
auto var1Field = model->MakeField<float>("Field1");
auto var2Field = model->MakeField<float>("Field2");
// Create an RNTuple writer
auto ntuple = RNTupleWriter::Recreate(std::move(model), "Input ntuple", "InputNtuple.root");
// Fill the RNTuple with some information
*var1Field = 1;
*var2Field = 2;
ntuple->Fill();
}
auto reader = RNTupleReader::Open("Input ntuple", "InputNtuple.root");
reader->PrintInfo();
reader->LoadEntry(0);
size_t numberOfFields = reader->GetDescriptor().GetNFields();
std::cout << "Number of fields: " << numberOfFields << std::endl;
reader->GetDescriptor().GetQualifiedFieldName(numberOfFields-1);
return 0;
}
Thanks!
Nikolaj
ROOT Version: 6.33.01 Platform: Not Provided Compiler: g++ (GCC) 14.2.0
The difference in the number of events is due to the fact that the descriptor also stores the artificial/implicit “zero field”, which is the root field of the schema tree.
Note that the descriptor IDs used in the RNTupleDescriptor API are really arbitrary IDs. They are not necessarily consecutive. To get the IDs of certain fields, you’d need to query them by name, e.g.
GetDescriptor().FindFieldId("Field1")
or you can iterate all fields with GetFieldIterable.
Thanks for the response! I do need to iterate over all fields, and I’m unsure how to implement this.
From what I understand, GetFieldIterable() seems to iterate over a specific field as it requires either a fieldId or a fieldDescriptor. Could you clarify how I can use it to loop through all fields in the RNTuple?
The best way depends a bit on what you want to do. Probably you would not need to look at the RNTupleDescriptor, which gives access to the on-disk representation, but you can use the RNTupleModel, which is the in-memory representation of the fields.
Starting from the RNTupleReader, the easiest is using
for (const auto &f : reader->GetModel().GetConstFieldZero()) {
// f is of type const RFieldBase &
std::cout << f.GetQualifiedFieldName() << std::endl;
}
This will iterate the entire schema tree recursively.
If you only want to look at top-level fields, you can use
for (const auto &f : reader->GetModel().GetConstFieldZero().GetSubFields()) {
// f is of type const RFieldBase *
std::cout << f->GetFieldName() << std::endl;
}
If you are not using the latest master branch, use GetFieldZero() instead of GetConstFieldZero().
Strange how GetSubFields gets all the top fields…
Is it possible to loop over the subfields in any way, though?
Maybe there is some way to only look if the fields are more than 1 step deep or, alternatively, for a given field loop over its subfields.
I’m afraid I don’t quite understand. Could you give an example which fields you would like to iterate? Please open a new topic, since this one is marked as completed.