Sharing model between multiple RNTuples

Following the example from RNTuple documentation: ROOT: RNTuple-related classes, that:

tree->Branch("px", &Category, "px/F");

becomes

auto px = model->MakeField<float>("px"); // px is std::shared_ptr<float>

This however makes a problem with sharing leafs between trees.

With tree I could

tree1->Branch("px", &Category, "px/F");
tree2->Branch("px", &Category, "px/F");

and both trees would read from the same variable. However, this is not possible anymore with the RNTuple:

{
    auto model = ROOT::RNTupleModel::Create();

    auto fldAge = model->MakeField<int>("Age");

    auto writer1 = ROOT::RNTupleWriter::Recreate(model->Clone(), "Staff1", "out1.root");
    auto writer2 = ROOT::RNTupleWriter::Recreate(std::move(model), "Staff2", "out2.root");

    *fldAge = 25.;

    writer1->Fill();
    writer2->Fill();
}

Here, as the model is unique pointer, I must e.g. clone the model but then fldAge is not connected to the model any more. The writer2 will have Age = 25 whereas writer1 will have Age = 0.

I think that the writer takes ownership of the model’s unique_ptr was perhaps unfortunate design choice. Could the RNTuple Writer/Reader take the model as non-owning (model.get()) or model is of shared_ptr type? Then I should be able to do:

// non-owning pointer
auto writer1 = ROOT::RNTupleWriter::Recreate(model.get(), "Staff1", "out1.root");
auto writer2 = ROOT::RNTupleWriter::Recreate(model.get(), "Staff2", "out2.root");

or

// shared_ptr
auto writer1 = ROOT::RNTupleWriter::Recreate(model, "Staff1", "out1.root");
auto writer2 = ROOT::RNTupleWriter::Recreate(model, "Staff2", "out2.root");

I cannot imagine any reason forcing model to be unique - it could be shared between different RNTuples. I also think about an application where we have:

// shared_ptr
auto reader1 = ROOT::RNTupleReader::Open(model, "Staff", "input1.root");
auto writer1 = ROOT::RNTupleWriter::Recreate(model, "Staff", "out2.root");

where we read a field, play with it and store it back to another tuple.

How much the RNTuple interface is fixed atm?

Hi Rafal,

Thanks for this post.
I am adding in the loop @vpadulan, @florine and @silverweed who could help out.

Cheers,
Danilo

Dear @rlalik,

Thank you for your interest in RNTuple and reaching out to us with your question.

With the first production API release of RNTuple in ROOT 6.36 earlier this month, the interfaces that exist now have become fixed. However, we take user feedback like yours seriously and I will bring this use case up with the rest of the RNTuple development team.

For the time being, one way to share pointers to field values between two models, is to bind[1] the value pointers from the default entry (which is used by RNTupleWriter::Fill() if no entry has been explicitly passed) of the first model to that of the default entry of the second model:

{
  // Create the first model as desired
  auto model1 = ROOT::RNTupleModel::Create();

  auto fldAge = model1->MakeField<int>("Age");

  // Clone the first model
  auto model2 = model1->Clone();

  // Bind the values of model1's default entry to model2's default entry
  for (auto &value : model2->GetDefaultEntry()) {
    model2->GetDefaultEntry().BindValue<void>(value.GetField().GetFieldName(),
                                              value.GetPtr<void>());
  }

  // Pass model1 to writer1
  auto writer1 =
      ROOT::RNTupleWriter::Recreate(std::move(model1), "Staff1", "out1.root");
  // Pass model2 to writer2
  auto writer2 =
      ROOT::RNTupleWriter::Recreate(std::move(model2), "Staff2", "out2.root");

  *fldAge = 25.;

  writer1->Fill();
  writer2->Fill();
}

I hope this is useful, don’t hestitate to reach out again if you have more questions!

Best,
Florine


  1. Also see the REntry::BindValue docs. ↩︎

@florine thanks for the answer and the code example. It is definitely useful.

I was wondering about Clone() method which makes an deep copy of the object. What about introducing Copy() method which makes shallow copy, exactly what you showed above?

auto writer1 = ROOT::RNTupleWriter::Recreate(model->Copy(), "Staff1", "out1.root");
auto writer2 = ROOT::RNTupleWriter::Recreate(model->Copy(), "Staff2", "out2.root");

Regards,
Rafał