@@ -35,6 +35,18 @@ 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+ if constexpr (std::is_floating_point_v<T>)
44+ EXPECT_FLOAT_EQ (v1[i], v2[i]) << " Vectors 'v1' and 'v2' differ at index " << i;
45+ else
46+ EXPECT_EQ (v1[i], v2[i]) << " Vectors 'v1' and 'v2' differ at index " << i;
47+ }
48+ }
49+
3850TEST (RDFSnapshotRNTuple, FromScratch)
3951{
4052 FileRAII fileGuard{" RDFSnapshotRNTuple_from_scratch.root" };
@@ -444,16 +456,36 @@ void WriteTestTree(const std::string &tname, const std::string &fname)
444456{
445457 TFile file (fname.c_str (), " RECREATE" );
446458 TTree t (tname.c_str (), tname.c_str ());
447- float pt;
459+
460+ float pt = 42 .f ;
461+ std::vector<float > photons{1 .f , 2 .f , 3 .f };
462+ Electron electron{137 .f };
463+ Jet jets;
464+ jets.electrons .emplace_back (Electron{122 .f });
465+ jets.electrons .emplace_back (Electron{125 .f });
466+ jets.electrons .emplace_back (Electron{129 .f });
467+
468+ Int_t nmuons = 1 ;
469+ float muon_pt[3 ] = {10 .f , 20 .f , 30 .f };
470+
471+ struct {
472+ Int_t x = 1 ;
473+ Int_t y = 2 ;
474+ } point;
475+
448476 t.Branch (" pt" , &pt);
477+ t.Branch (" photons" , &photons);
478+ t.Branch (" electron" , &electron);
479+ t.Branch (" jets" , &jets);
480+ t.Branch (" nmuons" , &nmuons);
481+ t.Branch (" muon_pt" , muon_pt, " muon_pt[nmuons]" );
482+ t.Branch (" point" , &point, " x/I:y/I" );
449483
450- pt = 42.0 ;
451484 t.Fill ();
452-
453485 t.Write ();
454486}
455487
456- TEST (RDFSnapshotRNTuple, DisallowFromTTree )
488+ TEST (RDFSnapshotRNTuple, FromTTree )
457489{
458490 const auto treename = " tree" ;
459491 FileRAII fileGuard{" RDFSnapshotRNTuple_disallow_from_ttree.root" };
@@ -465,13 +497,68 @@ TEST(RDFSnapshotRNTuple, DisallowFromTTree)
465497 RSnapshotOptions opts;
466498 opts.fOutputFormat = ROOT::RDF::ESnapshotOutputFormat::kRNTuple ;
467499
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." );
500+ {
501+ // FIXME(fdegeus): snapshotting leaflist branches as-is (i.e. without explicitly providing their leafs) is not
502+ // supported, because we have no way of reconstructing the memory layout of the branch itself from only the
503+ // TTree's on-disk information without JITting. For RNTuple, we would be able to do this using anonymous record
504+ // fields, however. Once this is implemented, this test should be changed to check the result of snapshotting
505+ // "point" fully.
506+ auto sdf = df.Define (" x" , [] { return 10 ; })
507+ .Snapshot (" ntuple" , fileGuard.GetPath (),
508+ {" x" , " pt" , " photons" , " electron" , " jets" , " muon_pt" , " point.x" , " point.y" }, opts);
509+
510+ auto x = sdf->Take <int >(" x" );
511+ auto pt = sdf->Take <float >(" pt" );
512+ auto photons = sdf->Take <ROOT::RVec<float >>(" photons" );
513+ auto electron = sdf->Take <Electron>(" electron" );
514+ auto jet_electrons = sdf->Take <ROOT::RVec<Electron>>(" jets.electrons" );
515+ auto nMuons = sdf->Take <int >(" nmuons" );
516+ auto muonPt = sdf->Take <ROOT::RVec<float >>(" muon_pt" );
517+ auto pointX = sdf->Take <int >(" point_x" );
518+ auto pointY = sdf->Take <int >(" point_y" );
519+
520+ ASSERT_EQ (1UL , x->size ());
521+ ASSERT_EQ (1UL , pt->size ());
522+ ASSERT_EQ (1UL , photons->size ());
523+ ASSERT_EQ (1UL , electron->size ());
524+ ASSERT_EQ (1UL , jet_electrons->size ());
525+ ASSERT_EQ (1UL , nMuons->size ());
526+ ASSERT_EQ (1UL , muonPt->size ());
527+ ASSERT_EQ (1UL , pointX->size ());
528+ ASSERT_EQ (1UL , pointY->size ());
529+
530+ EXPECT_EQ (10 , x->front ());
531+ EXPECT_EQ (42 .f , pt->front ());
532+ expect_vec_eq<float >({1 .f , 2 .f , 3 .f }, photons->front ());
533+ EXPECT_EQ (Electron{137 .f }, electron->front ());
534+ expect_vec_eq ({Electron{122 .f }, Electron{125 .f }, Electron{129 .f }}, jet_electrons->front ());
535+ EXPECT_EQ (1 , nMuons->front ());
536+ expect_vec_eq ({10 .f }, muonPt->front ());
537+ EXPECT_EQ (1 , pointX->front ());
538+ EXPECT_EQ (2 , pointY->front ());
474539 }
540+
541+ auto reader = RNTupleReader::Open (" ntuple" , fileGuard.GetPath ());
542+
543+ auto x = reader->GetView <int >(" x" );
544+ auto pt = reader->GetView <float >(" pt" );
545+ auto photons = reader->GetView <ROOT::RVec<float >>(" photons" );
546+ auto electron = reader->GetView <Electron>(" electron" );
547+ auto jet_electrons = reader->GetView <ROOT::RVec<Electron>>(" jets.electrons" );
548+ auto nMuons = reader->GetView <int >(" nmuons" );
549+ auto muonPt = reader->GetView <ROOT::RVec<float >>(" muon_pt" );
550+ auto pointX = reader->GetView <int >(" point_x" );
551+ auto pointY = reader->GetView <int >(" point_y" );
552+
553+ EXPECT_EQ (10 , x (0 ));
554+ EXPECT_EQ (42 .f , pt (0 ));
555+ expect_vec_eq<float >({1 .f , 2 .f , 3 .f }, photons (0 ));
556+ EXPECT_EQ (Electron{137 .f }, electron (0 ));
557+ expect_vec_eq ({Electron{122 .f }, Electron{125 .f }, Electron{129 .f }}, jet_electrons (0 ));
558+ EXPECT_EQ (1 , nMuons (0 ));
559+ expect_vec_eq ({10 .f }, muonPt (0 ));
560+ EXPECT_EQ (1 , pointX (0 ));
561+ EXPECT_EQ (2 , pointY (0 ));
475562}
476563
477564#ifdef R__USE_IMT
0 commit comments