Skip to content

Commit cf2fb73

Browse files
committed
Add new and remove old tests for DataSource
1 parent 9683d7a commit cf2fb73

5 files changed

Lines changed: 338 additions & 22 deletions

File tree

tests/root_io/CMakeLists.txt

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,42 @@ if(ENABLE_DATASOURCE)
9090
set_tests_properties(read_with_rdatasource_root PROPERTIES FIXTURES_REQUIRED podio_write_root_fixture)
9191
endif()
9292

93+
if(ENABLE_DATASOURCE)
94+
add_test(NAME use_python_with_rdatasource_root
95+
COMMAND python3 ${PROJECT_SOURCE_DIR}/tests/root_io/use_datasource.py)
96+
PODIO_SET_TEST_ENV(use_python_with_rdatasource_root PYTHON)
97+
set_tests_properties(use_python_with_rdatasource_root PROPERTIES
98+
FIXTURES_REQUIRED podio_write_root_fixture
99+
FIXTURES_SETUP podio_datasource_snapshot_fixture
100+
)
101+
102+
add_executable(check_datasource_output check_datasource_output.cpp)
103+
target_link_libraries(check_datasource_output PRIVATE ROOT::Core ROOT::RIO ROOT::Tree)
104+
add_test(NAME check_datasource_output COMMAND check_datasource_output)
105+
PODIO_SET_TEST_ENV(check_datasource_output)
106+
set_tests_properties(check_datasource_output PROPERTIES
107+
FIXTURES_REQUIRED podio_datasource_snapshot_fixture
108+
)
109+
endif()
110+
111+
if(ENABLE_DATASOURCE AND ENABLE_RNTUPLE)
112+
add_test(NAME use_python_with_rdatasource_rntuple
113+
COMMAND python3 ${PROJECT_SOURCE_DIR}/tests/root_io/use_datasource.py
114+
example_rntuple.root datasource_snapshot_rntuple.root)
115+
PODIO_SET_TEST_ENV(use_python_with_rdatasource_rntuple PYTHON)
116+
set_tests_properties(use_python_with_rdatasource_rntuple PROPERTIES
117+
FIXTURES_REQUIRED podio_write_rntuple_fixture
118+
FIXTURES_SETUP podio_datasource_rntuple_snapshot_fixture
119+
)
120+
121+
add_test(NAME check_datasource_output_rntuple
122+
COMMAND check_datasource_output datasource_snapshot_rntuple.root)
123+
PODIO_SET_TEST_ENV(check_datasource_output_rntuple)
124+
set_tests_properties(check_datasource_output_rntuple PROPERTIES
125+
FIXTURES_REQUIRED podio_datasource_rntuple_snapshot_fixture
126+
)
127+
endif()
128+
93129
add_executable(read_frame_legacy_root read_frame_legacy_root.cpp)
94130
target_link_libraries(read_frame_legacy_root PRIVATE "${root_libs}")
95131

@@ -119,8 +155,4 @@ add_test(NAME param_reading_rdataframe COMMAND python3 ${CMAKE_CURRENT_SOURCE_DI
119155
PODIO_SET_TEST_ENV(param_reading_rdataframe PYTHON)
120156
set_tests_properties(param_reading_rdataframe PROPERTIES FIXTURES_REQUIRED podio_write_root_fixture)
121157

122-
if(ENABLE_DATASOURCE)
123-
add_test(NAME read_python_with_rdatasource_root COMMAND python3 ${PROJECT_SOURCE_DIR}/tests/root_io/read_datasource.py)
124-
PODIO_SET_TEST_ENV(read_python_with_rdatasource_root PYTHON)
125-
set_tests_properties(read_python_with_rdatasource_root PROPERTIES FIXTURES_REQUIRED podio_write_root_fixture)
126-
endif()
158+
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/**
2+
* Checker for the datasource snapshot output produced by use_datasource.py.
3+
*/
4+
5+
#include <TFile.h>
6+
#include <TTree.h>
7+
8+
#include <cmath>
9+
#include <iostream>
10+
#include <limits>
11+
#include <stdexcept>
12+
#include <string>
13+
14+
#define CHECK(condition, msg) \
15+
if (!(condition)) { \
16+
throw std::runtime_error(std::string("check_datasource_output: ") + (msg)); \
17+
}
18+
19+
#define CHECK_EQUAL(actual, expected, label) \
20+
if (std::abs((actual) - (expected)) >= std::numeric_limits<double>::min()) { \
21+
throw std::runtime_error(std::string("check_datasource_output: ") + (label) + " value mismatch: got " + \
22+
std::to_string(actual) + ", expected " + std::to_string(expected)); \
23+
}
24+
25+
int main(int argc, const char* argv[]) {
26+
std::string inputFile = "datasource_snapshot.root";
27+
if (argc == 2) {
28+
inputFile = argv[1];
29+
} else if (argc > 2) {
30+
std::cout << "Usage: " << argv[0] << " [FILE]" << std::endl;
31+
return 1;
32+
}
33+
34+
TFile f(inputFile.c_str(), "READ");
35+
if (f.IsZombie()) {
36+
std::cerr << "Could not open " << inputFile << std::endl;
37+
return EXIT_FAILURE;
38+
}
39+
40+
auto* tree = f.Get<TTree>("events");
41+
CHECK(tree != nullptr, "TTree 'events' not found in " + inputFile)
42+
CHECK(tree->GetEntries() == 10, "Expected 10 entries in snapshot, got " + std::to_string(tree->GetEntries()))
43+
44+
int event_number = 0;
45+
double hit_energy_0 = 0., hit_energy_1 = 0., hit_energy_sum = 0.;
46+
double cluster_energy_0 = 0., cluster_energy_1 = 0., cluster_energy_2 = 0.;
47+
48+
double composite_hit_energy_0 = 0., composite_hit_energy_1 = 0.;
49+
50+
double sub_cluster_energy_0 = 0., sub_cluster_energy_1 = 0.;
51+
52+
double link0_weight = 0., link0_from_energy = 0., link0_to_energy = 0.;
53+
double link1_weight = 0., link1_from_energy = 0., link1_to_energy = 0.;
54+
55+
tree->SetBranchAddress("event_number", &event_number);
56+
tree->SetBranchAddress("hit_energy_0", &hit_energy_0);
57+
tree->SetBranchAddress("hit_energy_1", &hit_energy_1);
58+
tree->SetBranchAddress("hit_energy_sum", &hit_energy_sum);
59+
tree->SetBranchAddress("cluster_energy_0", &cluster_energy_0);
60+
tree->SetBranchAddress("cluster_energy_1", &cluster_energy_1);
61+
tree->SetBranchAddress("cluster_energy_2", &cluster_energy_2);
62+
tree->SetBranchAddress("composite_hit_energy_0", &composite_hit_energy_0);
63+
tree->SetBranchAddress("composite_hit_energy_1", &composite_hit_energy_1);
64+
tree->SetBranchAddress("sub_cluster_energy_0", &sub_cluster_energy_0);
65+
tree->SetBranchAddress("sub_cluster_energy_1", &sub_cluster_energy_1);
66+
tree->SetBranchAddress("link0_weight", &link0_weight);
67+
tree->SetBranchAddress("link0_from_energy", &link0_from_energy);
68+
tree->SetBranchAddress("link0_to_energy", &link0_to_energy);
69+
tree->SetBranchAddress("link1_weight", &link1_weight);
70+
tree->SetBranchAddress("link1_from_energy", &link1_from_energy);
71+
tree->SetBranchAddress("link1_to_energy", &link1_to_energy);
72+
73+
for (Long64_t entry = 0; entry < tree->GetEntries(); ++entry) {
74+
tree->GetEntry(entry);
75+
76+
const int i = event_number; // event_number == i by construction
77+
CHECK(i >= 0 && i < 10, "event_number out of expected range [0,9]: " + std::to_string(i))
78+
const std::string ev = " (event " + std::to_string(i) + ")";
79+
80+
CHECK_EQUAL(hit_energy_0, 23. + i, "hit_energy_0" + ev)
81+
CHECK_EQUAL(hit_energy_1, 12. + i, "hit_energy_1" + ev)
82+
CHECK_EQUAL(hit_energy_sum, 35. + 2. * i, "hit_energy_sum" + ev)
83+
84+
CHECK_EQUAL(cluster_energy_0, 23. + i, "cluster_energy_0" + ev)
85+
CHECK_EQUAL(cluster_energy_1, 12. + i, "cluster_energy_1" + ev)
86+
CHECK_EQUAL(cluster_energy_2, 35. + 2. * i, "cluster_energy_2" + ev)
87+
88+
CHECK_EQUAL(hit_energy_sum, cluster_energy_2, "hit_energy_sum vs cluster_energy_2" + ev)
89+
CHECK_EQUAL(hit_energy_0 + hit_energy_1, cluster_energy_2, "hit0+hit1 vs cluster_energy_2" + ev)
90+
CHECK_EQUAL(cluster_energy_0 + cluster_energy_1, cluster_energy_2, "sub-cluster sum vs cluster_energy_2" + ev)
91+
92+
CHECK_EQUAL(composite_hit_energy_0, 23. + i, "composite_hit_energy_0 (cluster->hit relation)" + ev)
93+
CHECK_EQUAL(composite_hit_energy_1, 12. + i, "composite_hit_energy_1 (cluster->hit relation)" + ev)
94+
95+
CHECK_EQUAL(composite_hit_energy_0, hit_energy_0, "composite_hit_energy_0 vs hit_energy_0" + ev)
96+
CHECK_EQUAL(composite_hit_energy_1, hit_energy_1, "composite_hit_energy_1 vs hit_energy_1" + ev)
97+
98+
CHECK_EQUAL(sub_cluster_energy_0, 23. + i, "sub_cluster_energy_0 (cluster->cluster relation)" + ev)
99+
CHECK_EQUAL(sub_cluster_energy_1, 12. + i, "sub_cluster_energy_1 (cluster->cluster relation)" + ev)
100+
101+
CHECK_EQUAL(sub_cluster_energy_0, cluster_energy_0, "sub_cluster_energy_0 vs cluster_energy_0" + ev)
102+
CHECK_EQUAL(sub_cluster_energy_1, cluster_energy_1, "sub_cluster_energy_1 vs cluster_energy_1" + ev)
103+
104+
CHECK_EQUAL(link0_weight, 0.0, "link0_weight" + ev)
105+
CHECK_EQUAL(link0_from_energy, 23. + i, "link0_from_energy (link->hit relation)" + ev)
106+
CHECK_EQUAL(link0_to_energy, 12. + i, "link0_to_energy (link->cluster relation)" + ev)
107+
CHECK_EQUAL(link0_from_energy, hit_energy_0, "link0_from_energy vs hit_energy_0" + ev)
108+
CHECK_EQUAL(link0_to_energy, cluster_energy_1, "link0_to_energy vs cluster_energy_1" + ev)
109+
110+
CHECK_EQUAL(link1_weight, 0.5, "link1_weight" + ev)
111+
CHECK_EQUAL(link1_from_energy, 12. + i, "link1_from_energy (link->hit relation)" + ev)
112+
CHECK_EQUAL(link1_to_energy, 23. + i, "link1_to_energy (link->cluster relation)" + ev)
113+
CHECK_EQUAL(link1_from_energy, hit_energy_1, "link1_from_energy vs hit_energy_1" + ev)
114+
CHECK_EQUAL(link1_to_energy, cluster_energy_0, "link1_to_energy vs cluster_energy_0" + ev)
115+
}
116+
117+
std::cout << "check_datasource_output: all checks passed" << std::endl;
118+
return EXIT_SUCCESS;
119+
}

tests/root_io/read_datasource.py

Lines changed: 0 additions & 16 deletions
This file was deleted.

tests/root_io/use_datasource.py

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
#!/usr/bin/env python3
2+
"""Test to exercise DataSource thoroughly"""
3+
4+
import sys
5+
import ROOT
6+
from podio.data_source import CreateDataFrame # pylint: disable=import-error, no-name-in-module
7+
8+
input_file = sys.argv[1] if len(sys.argv) > 1 else "example_frame.root"
9+
snapshot_file = sys.argv[2] if len(sys.argv) > 2 else "datasource_snapshot.root"
10+
11+
if ROOT.gSystem.Load("libTestDataModelDict") < 0:
12+
raise RuntimeError("Could not load TestDataModel dictionary")
13+
14+
ROOT.gInterpreter.ProcessLine("using namespace ROOT::VecOps;")
15+
16+
ROOT.gInterpreter.Declare(
17+
"""
18+
#include <datamodel/ExampleHitCollection.h>
19+
#include <datamodel/ExampleClusterCollection.h>
20+
#include <datamodel/EventInfoCollection.h>
21+
#include <datamodel/TestLinkCollection.h>
22+
"""
23+
)
24+
25+
# Declare helpers that extract quantities from the test collections
26+
ROOT.gInterpreter.Declare(
27+
"""
28+
RVec<double> getHitEnergies(const ExampleHitCollection& hits) {
29+
RVec<double> v;
30+
v.reserve(hits.size());
31+
for (const auto& h : hits) {
32+
v.push_back(h.energy());
33+
}
34+
return v;
35+
}
36+
37+
RVec<double> getClusterEnergies(const ExampleClusterCollection& clusters) {
38+
RVec<double> v;
39+
v.reserve(clusters.size());
40+
for (const auto& c : clusters) {
41+
v.push_back(c.energy());
42+
}
43+
return v;
44+
}
45+
46+
int getEventNumber(const EventInfoCollection& info) {
47+
return info[0].Number();
48+
}
49+
50+
RVec<double> getCompositeClusterHitEnergies(const ExampleClusterCollection& clusters) {
51+
RVec<double> v;
52+
for (const auto& hit : clusters[2].Hits()) {
53+
v.push_back(hit.energy());
54+
}
55+
return v;
56+
}
57+
58+
RVec<double> getSubClusterEnergies(const ExampleClusterCollection& clusters) {
59+
RVec<double> v;
60+
for (const auto& sub : clusters[2].Clusters()) {
61+
v.push_back(sub.energy());
62+
}
63+
return v;
64+
}
65+
66+
// Follow each link From (hit) and To (cluster) relations and return:
67+
// [weight0, hit_energy0, cluster_energy0, weight1, hit_energy1, cluster_energy1]
68+
RVec<double> getLinkInfo(const TestLinkCollection& links) {
69+
RVec<double> v;
70+
for (const auto& link : links) {
71+
v.push_back(link.getWeight());
72+
v.push_back(link.getFrom().energy());
73+
v.push_back(link.getTo().energy());
74+
}
75+
return v;
76+
}
77+
"""
78+
)
79+
80+
rdf = CreateDataFrame(input_file)
81+
82+
assert rdf.Count().GetValue() == 10, f"Expected 10 events in {input_file}"
83+
84+
rdf = (
85+
rdf.Define("hit_energies", "getHitEnergies(hits)")
86+
.Define("hit_energy_0", "hit_energies[0]")
87+
.Define("hit_energy_1", "hit_energies[1]")
88+
.Define("hit_energy_sum", "Sum(hit_energies)")
89+
.Define("cluster_energies", "getClusterEnergies(clusters)")
90+
.Define("cluster_energy_0", "cluster_energies[0]")
91+
.Define("cluster_energy_1", "cluster_energies[1]")
92+
.Define("cluster_energy_2", "cluster_energies[2]")
93+
.Define("event_number", "getEventNumber(info)")
94+
.Define("composite_hit_energies", "getCompositeClusterHitEnergies(clusters)")
95+
.Define("composite_hit_energy_0", "composite_hit_energies[0]")
96+
.Define("composite_hit_energy_1", "composite_hit_energies[1]")
97+
.Define("sub_cluster_energies", "getSubClusterEnergies(clusters)")
98+
.Define("sub_cluster_energy_0", "sub_cluster_energies[0]")
99+
.Define("sub_cluster_energy_1", "sub_cluster_energies[1]")
100+
.Define("link_info", "getLinkInfo(links)")
101+
.Define("link0_weight", "link_info[0]")
102+
.Define("link0_from_energy", "link_info[1]")
103+
.Define("link0_to_energy", "link_info[2]")
104+
.Define("link1_weight", "link_info[3]")
105+
.Define("link1_from_energy", "link_info[4]")
106+
.Define("link1_to_energy", "link_info[5]")
107+
)
108+
109+
# print(rdf.Describe())
110+
# print(rdf.Display(["event_number", "hit_energy_0", "cluster_energy_2", "composite_hit_energy_0", "sub_cluster_energy_0"]).AsString())
111+
112+
# 10 events, i = 0..9
113+
# hit_energy_0 = 23+i
114+
mean_hit0 = rdf.Mean("hit_energy_0").GetValue()
115+
assert abs(mean_hit0 - 27.5) < 1e-9, f"Mean of hit_energy_0 should be 27.5, got {mean_hit0}"
116+
117+
# cluster_energy_2 = 35+2*i
118+
mean_clu2 = rdf.Mean("cluster_energy_2").GetValue()
119+
assert abs(mean_clu2 - 44.0) < 1e-9, f"Mean of cluster_energy_2 should be 44.0, got {mean_clu2}"
120+
121+
# event_number = i
122+
mean_evtnum = rdf.Mean("event_number").GetValue()
123+
assert abs(mean_evtnum - 4.5) < 1e-9, f"Mean of event_number should be 4.5, got {mean_evtnum}"
124+
125+
# composite_hit_energy_0 follows clusters[2].Hits()[0] == hits[0], energy = 23+i -> Mean = 27.5
126+
mean_chit0 = rdf.Mean("composite_hit_energy_0").GetValue()
127+
assert (
128+
abs(mean_chit0 - 27.5) < 1e-9
129+
), f"Mean of composite_hit_energy_0 (cluster->hit relation) should be 27.5, got {mean_chit0}"
130+
131+
# sub_cluster_energy_0 follows clusters[2].Clusters()[0] == clusters[0], energy = 23+i -> Mean = 27.5
132+
mean_sub0 = rdf.Mean("sub_cluster_energy_0").GetValue()
133+
assert (
134+
abs(mean_sub0 - 27.5) < 1e-9
135+
), f"Mean of sub_cluster_energy_0 (cluster->cluster relation) should be 27.5, got {mean_sub0}"
136+
137+
# link0_weight is always 0.0, link1_weight is always 0.5
138+
mean_w0 = rdf.Mean("link0_weight").GetValue()
139+
assert abs(mean_w0 - 0.0) < 1e-9, f"Mean of link0_weight should be 0.0, got {mean_w0}"
140+
mean_w1 = rdf.Mean("link1_weight").GetValue()
141+
assert abs(mean_w1 - 0.5) < 1e-9, f"Mean of link1_weight should be 0.5, got {mean_w1}"
142+
143+
# link0 goes hits[0]->clusters[1]: from-energy = 23+i -> Mean = 27.5,
144+
# to-energy = 12+i -> Mean = 16.5
145+
mean_l0from = rdf.Mean("link0_from_energy").GetValue()
146+
assert (
147+
abs(mean_l0from - 27.5) < 1e-9
148+
), f"Mean of link0_from_energy (link->hit relation) should be 27.5, got {mean_l0from}"
149+
mean_l0to = rdf.Mean("link0_to_energy").GetValue()
150+
assert (
151+
abs(mean_l0to - 16.5) < 1e-9
152+
), f"Mean of link0_to_energy (link->cluster relation) should be 16.5, got {mean_l0to}"
153+
154+
rdf_even = rdf.Filter("event_number % 2 == 0")
155+
assert rdf_even.Count().GetValue() == 5, "Expected 5 even-numbered events after filter"
156+
157+
columns = [
158+
"event_number",
159+
"hit_energy_0",
160+
"hit_energy_1",
161+
"hit_energy_sum",
162+
"cluster_energy_0",
163+
"cluster_energy_1",
164+
"cluster_energy_2",
165+
"composite_hit_energy_0",
166+
"composite_hit_energy_1",
167+
"sub_cluster_energy_0",
168+
"sub_cluster_energy_1",
169+
"link0_weight",
170+
"link0_from_energy",
171+
"link0_to_energy",
172+
"link1_weight",
173+
"link1_from_energy",
174+
"link1_to_energy",
175+
]
176+
177+
rdf.Snapshot("events", snapshot_file, columns)
178+
179+
print(f"All assertions passed, snapshot written to {snapshot_file}")

tools/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ install(PROGRAMS ${CMAKE_CURRENT_LIST_DIR}/podio-dump-legacy DESTINATION ${CMAKE
1313
install(PROGRAMS ${CMAKE_CURRENT_LIST_DIR}/json-to-yaml DESTINATION ${CMAKE_INSTALL_BINDIR})
1414
install(PROGRAMS ${CMAKE_CURRENT_LIST_DIR}/podio-vis DESTINATION ${CMAKE_INSTALL_BINDIR})
1515
if(ENABLE_RNTUPLE)
16-
install(PROGRAMS ${CMAKE_CURRENT_LIST_DIR}/podio-ttree-to-rntuple DESTINATION ${CMAKE_INSTALL_BINDIR})
16+
add_executable(podio-ttree-to-rntuple src/podio-ttree-to-rntuple.cpp)
17+
target_link_libraries(podio-ttree-to-rntuple PRIVATE podio::podio podio::podioIO)
18+
install(TARGETS podio-ttree-to-rntuple EXPORT podioTargets DESTINATION ${CMAKE_INSTALL_BINDIR})
1719
endif()
1820
install(PROGRAMS ${CMAKE_CURRENT_LIST_DIR}/podio-merge-files DESTINATION ${CMAKE_INSTALL_BINDIR})
1921

0 commit comments

Comments
 (0)