@@ -35,6 +35,15 @@ class FileRAII {
3535 std::string GetPath () const { return fPath ; }
3636};
3737
38+ template <typename T>
39+ void expect_vec_eq (const ROOT::RVec<T> &v1, const ROOT::RVec<T> &v2)
40+ {
41+ ASSERT_EQ (v1.size (), v2.size ()) << " Vectors 'v1' and 'v2' are of unequal length" ;
42+ for (std::size_t i = 0ull ; i < v1.size (); ++i) {
43+ EXPECT_EQ (v1[i], v2[i]) << " Vectors 'v1' and 'v2' differ at index " << i;
44+ }
45+ }
46+
3847TEST (RDFSnapshotRNTuple, FromScratch)
3948{
4049 FileRAII fileGuard{" RDFSnapshotRNTuple_from_scratch.root" };
@@ -444,16 +453,36 @@ void WriteTestTree(const std::string &tname, const std::string &fname)
444453{
445454 TFile file (fname.c_str (), " RECREATE" );
446455 TTree t (tname.c_str (), tname.c_str ());
447- float pt;
456+
457+ float pt = 42 .f ;
458+ std::vector<float > photons{1 .f , 2 .f , 3 .f };
459+ Electron electron{137 .f };
460+ Jet jets;
461+ jets.electrons .emplace_back (Electron{122 .f });
462+ jets.electrons .emplace_back (Electron{125 .f });
463+ jets.electrons .emplace_back (Electron{129 .f });
464+
465+ Int_t nmuons = 1 ;
466+ float muon_pt[3 ] = {10 .f , 20 .f , 30 .f };
467+
468+ struct {
469+ Int_t x = 1 ;
470+ Int_t y = 2 ;
471+ } point;
472+
448473 t.Branch (" pt" , &pt);
474+ t.Branch (" photons" , &photons);
475+ t.Branch (" electron" , &electron);
476+ t.Branch (" jets" , &jets);
477+ t.Branch (" nmuons" , &nmuons);
478+ t.Branch (" muon_pt" , muon_pt, " muon_pt[nmuons]" );
479+ t.Branch (" point" , &point, " x/I:y/I" );
449480
450- pt = 42.0 ;
451481 t.Fill ();
452-
453482 t.Write ();
454483}
455484
456- TEST (RDFSnapshotRNTuple, DisallowFromTTree )
485+ TEST (RDFSnapshotRNTuple, FromTTree )
457486{
458487 const auto treename = " tree" ;
459488 FileRAII fileGuard{" RDFSnapshotRNTuple_disallow_from_ttree.root" };
@@ -465,13 +494,68 @@ TEST(RDFSnapshotRNTuple, DisallowFromTTree)
465494 RSnapshotOptions opts;
466495 opts.fOutputFormat = ROOT::RDF::ESnapshotOutputFormat::kRNTuple ;
467496
468- try {
469- auto sdf = df.Define (" x" , [] { return 10 ; }).Snapshot (" ntuple" , fileGuard.GetPath (), {" pt" , " x" }, opts);
470- FAIL () << " snapshotting from RNTuple to TTree is not (yet) possible" ;
471- } catch (const std::runtime_error &err) {
472- EXPECT_STREQ (err.what (), " Snapshotting from TTree to RNTuple is not yet supported. The current recommended way "
473- " to convert TTrees to RNTuple is through the RNTupleImporter." );
497+ {
498+ // FIXME(fdegeus): snapshotting leaflist branches as-is (i.e. without explicitly providing their leafs) is not
499+ // supported, because we have no way of reconstructing the memory layout of the branch itself from only the
500+ // TTree's on-disk information without JITting. For RNTuple, we would be able to do this using anonymous record
501+ // fields, however. Once this is implemented, this test should be changed to check the result of snapshotting
502+ // "point" fully.
503+ auto sdf = df.Define (" x" , [] { return 10 ; })
504+ .Snapshot (" ntuple" , fileGuard.GetPath (),
505+ {" x" , " pt" , " photons" , " electron" , " jets" , " muon_pt" , " point.x" , " point.y" }, opts);
506+
507+ auto x = sdf->Take <int >(" x" );
508+ auto pt = sdf->Take <float >(" pt" );
509+ auto photons = sdf->Take <ROOT::RVec<float >>(" photons" );
510+ auto electron = sdf->Take <Electron>(" electron" );
511+ auto jet_electrons = sdf->Take <ROOT::RVec<Electron>>(" jets.electrons" );
512+ auto nMuons = sdf->Take <int >(" nmuons" );
513+ auto muonPt = sdf->Take <ROOT::RVec<float >>(" muon_pt" );
514+ auto pointX = sdf->Take <int >(" point_x" );
515+ auto pointY = sdf->Take <int >(" point_y" );
516+
517+ ASSERT_EQ (1UL , x->size ());
518+ ASSERT_EQ (1UL , pt->size ());
519+ ASSERT_EQ (1UL , photons->size ());
520+ ASSERT_EQ (1UL , electron->size ());
521+ ASSERT_EQ (1UL , jet_electrons->size ());
522+ ASSERT_EQ (1UL , nMuons->size ());
523+ ASSERT_EQ (1UL , muonPt->size ());
524+ ASSERT_EQ (1UL , pointX->size ());
525+ ASSERT_EQ (1UL , pointY->size ());
526+
527+ EXPECT_EQ (10 , x->front ());
528+ EXPECT_EQ (42 .f , pt->front ());
529+ expect_vec_eq<float >({1 .f , 2 .f , 3 .f }, photons->front ());
530+ EXPECT_EQ (Electron{137 .f }, electron->front ());
531+ expect_vec_eq ({Electron{122 .f }, Electron{125 .f }, Electron{129 .f }}, jet_electrons->front ());
532+ EXPECT_EQ (1 , nMuons->front ());
533+ expect_vec_eq ({10 .f }, muonPt->front ());
534+ EXPECT_EQ (1 , pointX->front ());
535+ EXPECT_EQ (2 , pointY->front ());
474536 }
537+
538+ auto reader = RNTupleReader::Open (" ntuple" , fileGuard.GetPath ());
539+
540+ auto x = reader->GetView <int >(" x" );
541+ auto pt = reader->GetView <float >(" pt" );
542+ auto photons = reader->GetView <ROOT::RVec<float >>(" photons" );
543+ auto electron = reader->GetView <Electron>(" electron" );
544+ auto jet_electrons = reader->GetView <ROOT::RVec<Electron>>(" jets.electrons" );
545+ auto nMuons = reader->GetView <int >(" nmuons" );
546+ auto muonPt = reader->GetView <ROOT::RVec<float >>(" muon_pt" );
547+ auto pointX = reader->GetView <int >(" point_x" );
548+ auto pointY = reader->GetView <int >(" point_y" );
549+
550+ EXPECT_EQ (10 , x (0 ));
551+ EXPECT_EQ (42 .f , pt (0 ));
552+ expect_vec_eq<float >({1 .f , 2 .f , 3 .f }, photons (0 ));
553+ EXPECT_EQ (Electron{137 .f }, electron (0 ));
554+ expect_vec_eq ({Electron{122 .f }, Electron{125 .f }, Electron{129 .f }}, jet_electrons (0 ));
555+ EXPECT_EQ (1 , nMuons (0 ));
556+ expect_vec_eq ({10 .f }, muonPt (0 ));
557+ EXPECT_EQ (1 , pointX (0 ));
558+ EXPECT_EQ (2 , pointY (0 ));
475559}
476560
477561#ifdef R__USE_IMT
0 commit comments