LDMX Software
TestConditions.cxx
1#include <stdlib.h>
2
3#include <catch2/catch_test_macros.hpp>
4#include <catch2/matchers/catch_matchers_string.hpp>
5#include <fstream>
6#include <sstream>
7
8#include "Conditions/GeneralCSVLoader.h"
9#include "Conditions/SimpleCSVTableProvider.h"
10#include "Conditions/SimpleTableCondition.h"
11#include "Conditions/SimpleTableStreamers.h"
12#include "Conditions/URLStreamer.h"
13#include "DetDescr/EcalID.h"
14#include "DetDescr/HcalID.h"
15#include "Framework/Configure/Python.h"
17#include "Framework/Process.h"
18#include "Framework/RunHeader.h"
19
20namespace conditions {
21namespace test {
22
23template <class T>
24void matchesMeta(const T& a, const T& b) {
25 REQUIRE(a.getColumnCount() == b.getColumnCount());
26 REQUIRE(a.getRowCount() == b.getRowCount());
27 REQUIRE(a.getColumnNames() == b.getColumnNames());
28}
29
30void matchesAll(const conditions::DoubleTableCondition& a,
32 matchesMeta(a, b);
33 for (unsigned int i = 0; i < a.getRowCount(); i++) {
34 std::pair<unsigned int, std::vector<double>> ar = a.getRow(i);
35 std::pair<unsigned int, std::vector<double>> br = b.getRow(i);
36 REQUIRE(ar.first == br.first);
37 for (unsigned int ic = 0; ic < a.getColumnCount(); ic++) {
38 // std::cout << i << ',' << ic << "=>" << ar.second[ic] << " " <<
39 // br.second[ic] << std::endl;
40 REQUIRE(fabs(ar.second[ic] - br.second[ic]) /
41 std::max(1e-5, (ar.second[ic] + br.second[ic]) / 2) <
42 1e-5); // limited precision of course
43 }
44 }
45}
46
47void matchesAll(const conditions::IntegerTableCondition& a,
49 matchesMeta(a, b);
50 for (unsigned int i = 0; i < a.getRowCount(); i++)
51 REQUIRE(a.getRow(i) == b.getRow(i));
52}
53
54using Catch::Matchers::ContainsSubstring;
55
63TEST_CASE("Conditions", "[Conditions]") {
64 // create a simple table
65
66 std::vector<std::string> columns({"A", "Q", "V"});
67
68 IntegerTableCondition itable("ITable", columns);
69
70 for (int key = 100; key > 0; key -= 10) {
71 ldmx::EcalID id(1, 1, key);
72 std::vector<int> vals;
73 vals.push_back(key * 2);
74 vals.push_back(key / 2);
75 vals.push_back(key * key);
76 itable.add(id.raw(), vals);
77 }
78
79 std::vector<std::string> columnsd({"SQRT", "EXP", "LOG"});
80 conditions::DoubleTableCondition dtable("DTable", columnsd);
81
82 for (int key = 1; key < 8; key += 2) {
83 ldmx::HcalID id(1, 1, key);
84 std::vector<double> vals;
85 vals.push_back(sqrt(key));
86 vals.push_back(exp(key * 5));
87 vals.push_back(log(key));
88 dtable.add(id.raw(), vals);
89 }
90
91 SECTION("Testing simple table construction") {
92 REQUIRE(itable.getRowCount() == 10);
93
94 REQUIRE(itable.getColumnCount() == 3);
95
96 ldmx::EcalID id(1, 1, 20);
97 REQUIRE(itable.get(id.raw(), 1) == 10);
98
99 REQUIRE_THROWS_WITH(itable.add(2, std::vector<int>(2)),
100 ContainsSubstring("columns into a table"));
101
102 REQUIRE_THROWS_WITH(itable.add(id.raw(), std::vector<int>(3)),
103 ContainsSubstring("existing id"));
104
105 std::pair<unsigned int, std::vector<int>> row = itable.getRow(4);
106
107 CHECK(row.first == 0x14021032);
108
109 CHECK(row.second.size() == 3);
110
111 CHECK(row.second.at(2) == (50 * 50));
112
113 ldmx::EcalID id2(1, 1, 60);
114
115 CHECK(itable.getByName(id2.raw(), "Q") == 30);
116
117 const char* expected =
118 "[ TableCondition: ITable\n"
119 " DetID,id:\"A\",id:\"Q\",id:\"V\"\n"
120 " 335679498,20,5,100\n"
121 " 335679508,40,10,400\n"
122 " 335679518,60,15,900\n"
123 " 335679528,80,20,1600\n"
124 " 335679538,100,25,2500\n"
125 " 335679548,120,30,3600\n"
126 " 335679558,140,35,4900\n"
127 " 335679568,160,40,6400\n"
128 " 335679578,180,45,8100\n"
129 " 335679588,200,50,10000\n"
130 "]";
131 std::stringstream ss;
132 ss << itable;
133 std::string image = ss.str();
134 CHECK(image == expected);
135 }
136
137 SECTION("Testing CSV IO") {
138 std::stringstream ss;
140
141 std::string image1 = ss.str();
142 const char* expected1 =
143 "\"DetID\",id:\"subdetector\",id:\"layer\",id:\"module\",id:\"cell\","
144 "\"A\",\"Q\",\"V\"\n0x1402100a,5,1,1,10,20,5,100\n0x14021014,5,1,1,20,"
145 "40,10,400\n0x1402101e,5,1,1,30,60,15,900\n0x14021028,5,1,1,40,80,20,"
146 "1600\n0x14021032,5,1,1,50,100,25,2500\n0x1402103c,5,1,1,60,120,30,"
147 "3600\n0x14021046,5,1,1,70,140,35,4900\n0x14021050,5,1,1,80,160,40,"
148 "6400\n0x1402105a,5,1,1,90,180,45,8100\n0x14021064,5,1,1,100,200,50,"
149 "10000\n";
150
151 CHECK(image1 == expected1);
152
153 // std::cout << image1;
154
155 std::stringstream ss_read1(image1);
156 IntegerTableCondition itable2("ITable", columns);
158
159 matchesAll(itable, itable2);
160
161 // missing id column example
162 std::string image2("B,A,Q,V\n0,40,50,100\n0,60,70,90\n");
163 std::stringstream ss_read2(image2);
164 REQUIRE_THROWS_WITH(
166 ContainsSubstring(
167 "Malformed CSV file with no DetId or subdetector column"));
168
169 // missing column example
170 std::string image3(
171 "DetID,A,Q\n0x1402100a,20,5\n0x14021014,40,10\n0x1402101e,60,"
172 "15\n0x14021028,80,20\n0x14021032,100,25\n0x1402103c,120,"
173 "30\n0x14021046,140,35\n0x14021050,160,40\n0x1402105a,180,"
174 "45\n0x14021064,200,50\n");
175 std::stringstream ss_read3(image3);
176 REQUIRE_THROWS_WITH(
178 ContainsSubstring("Missing column"));
179
180 // varying line lengths example
181 std::string image4(
182 "DetID,A,Q,V\n0x1402100a,20,5,100\n0x14021014,40,10\n0x1402101e,60,15,"
183 "900\n");
184 std::stringstream ss_read4(image4);
185 REQUIRE_THROWS_WITH(
187 ContainsSubstring("Mismatched number of columns (3!=4) on line 3"));
188 }
189
190 SECTION("Testing python static") {
191 const char* cfgpy =
192 "#!/usr/bin/python3\n\nimport sys\n\nfrom LDMX.Framework import "
193 "ldmxcfg\nfrom LDMX.Conditions import "
194 "SimpleCSVTableProvider\n\np=ldmxcfg.Process('test')\np.testMode="
195 "True\ncolumns=['A','B','C']\ncop=SimpleCSVTableProvider."
196 "SimpleCSVIntegerTableProvider('test_table_python',columns)\ncop."
197 "validForAllRows([10,45,129])";
198
199 FILE* f = fopen("/tmp/test_cond.py", "w");
200 fputs(cfgpy, f);
201 fclose(f);
202
203 auto cfg{framework::config::run("ldmxcfg.Process.lastProcess",
204 "/tmp/test_cond.py", 0, 0)};
205 auto hp{std::make_unique<framework::Process>(cfg)};
207 hp->setEventHeader(&cxt);
208
209 cxt.setRun(10);
210 const IntegerTableCondition& i_table =
211 hp->getConditions().getCondition<IntegerTableCondition>(
212 "test_table_python");
213
214 CHECK(i_table.getByName(292, "A") == 10);
215 CHECK(i_table.getByName(2928184, "B") == 45);
216 CHECK(i_table.getByName(82910, "C") == 129);
217 }
218
219 SECTION("Testing file loading") {
220 std::ofstream fs("/tmp/dump_double.csv");
221 std::stringstream ss;
224 fs.close();
225 // std::cout << "Step 1" << std::endl << ss.str();
226
227 const char* cfgpy =
228 "#!/usr/bin/python3\n\nimport sys\n\nfrom LDMX.Framework import "
229 "ldmxcfg\nfrom LDMX.Conditions import "
230 "SimpleCSVTableProvider\n\np=ldmxcfg.Process('test')\np.testMode="
231 "True\ncolumns=['SQRT','EXP','LOG']\ncop=SimpleCSVTableProvider."
232 "SimpleCSVDoubleTableProvider('test_table_file',columns)\ncop."
233 "validForRuns('file:///tmp/dump_double.csv',0,100)\ncop.validForRuns('/"
234 "tmp/dump_double.csv',101,120)\n";
235
236 FILE* f = fopen("/tmp/test_cond.py", "w");
237 fputs(cfgpy, f);
238 fclose(f);
239
240 auto cfg{framework::config::run("ldmxcfg.Process.lastProcess",
241 "/tmp/test_cond.py", 0, 0)};
242 auto hp{std::make_unique<framework::Process>(cfg)};
244 hp->setEventHeader(&cxt);
245
246 cxt.setRun(10);
247 const conditions::DoubleTableCondition& f_table1 =
248 hp->getConditions().getCondition<conditions::DoubleTableCondition>(
249 "test_table_file");
250 matchesAll(dtable, f_table1);
251
252 cxt.setRun(119);
253 const conditions::DoubleTableCondition& f_table2 =
254 hp->getConditions().getCondition<conditions::DoubleTableCondition>(
255 "test_table_file");
256 matchesAll(dtable, f_table2);
257 }
258
259 SECTION("Testing HTTP loading") {
260 const char* cfgpy =
261 "#!/usr/bin/python3\n\nimport sys\n\nfrom LDMX.Framework "
262 "import ldmxcfg\nfrom LDMX.Conditions import SimpleCSVTableProvider\n"
263 "p=ldmxcfg.Process(\"test\")\n"
264 "p.testMode=True\n"
265 "columns=[\"A\",\"Q\",\"V\"]\n"
266 "cop=SimpleCSVTableProvider.SimpleCSVIntegerTableProvider(\"test_table_"
267 "http\",columns)\n"
268 "cop.validForever(\"https://raw.githubusercontent.com/LDMX-Software/"
269 "ci-data/refs/heads/main/conditions-test/test_table.csv\")\n";
270
271 FILE* f = fopen("/tmp/test_cond.py", "w");
272 fputs(cfgpy, f);
273 fclose(f);
274
275 auto cfg{framework::config::run("ldmxcfg.Process.lastProcess",
276 "/tmp/test_cond.py", 0, 0)};
277 auto hp{std::make_unique<framework::Process>(cfg)};
279 hp->setEventHeader(&cxt);
280
281 const IntegerTableCondition& http_table =
282 hp->getConditions().getCondition<IntegerTableCondition>(
283 "test_table_http");
284 matchesAll(http_table, itable);
285 }
286
287 SECTION("Testing CSV metatable") {
288 const char* cfgpy =
289 "#!/usr/bin/python3\n\nimport sys\n\nfrom LDMX.Framework "
290 "import ldmxcfg\nfrom LDMX.Conditions import SimpleCSVTableProvider\n"
291 "p=ldmxcfg.Process(\"test\")\n"
292 "p.testMode=True\n"
293 "columns=[\"PEDESTAL_ADC\"]\n"
294 "cop=SimpleCSVTableProvider.SimpleCSVDoubleTableProvider(\"testbeam22_"
295 "pedestals\",columns)\n"
296 "cop.conditions_baseURL='https://raw.githubusercontent.com/"
297 "LDMX-Software/conditions-data/refs/heads/main/'\n"
298 "cop.entriesURL='${LDMX_CONDITION_BASEURL}/Hcal/testbeam04-2022/"
299 "pedestals/index_v1_0_0.csv'\n";
300
301 FILE* f = fopen("/tmp/test_cond.py", "w");
302 fputs(cfgpy, f);
303 fclose(f);
304
305 auto cfg{framework::config::run("ldmxcfg.Process.lastProcess",
306 "/tmp/test_cond.py", 0, 0)};
307 auto hp{std::make_unique<framework::Process>(cfg)};
309
310 hp->setEventHeader(&cxt);
311
312 unsigned int http_requests[2], http_failures[2];
313
314 conditions::urlstatistics(http_requests[0], http_failures[0]);
315
316 cxt.setRun(128);
317
318 hp->getConditions().getCondition<DoubleTableCondition>(
319 "testbeam22_pedestals");
320
321 hp->getConditions().getCondition<DoubleTableCondition>(
322 "testbeam22_pedestals");
323
324 conditions::urlstatistics(http_requests[1], http_failures[1]);
325 REQUIRE(((http_requests[1] - http_requests[0]) == 1 &&
326 (http_failures[1] - http_failures[0]) == 0));
327
328 cxt.setRun(129);
329
330 hp->getConditions().getCondition<DoubleTableCondition>(
331 "testbeam22_pedestals");
332
333 conditions::urlstatistics(http_requests[1], http_failures[1]);
334 REQUIRE(((http_requests[1] - http_requests[0]) == 1 &&
335 (http_failures[1] - http_failures[0]) == 0));
336
337 cxt.setRun(140);
338
339 hp->getConditions().getCondition<DoubleTableCondition>(
340 "testbeam22_pedestals");
341
342 conditions::urlstatistics(http_requests[1], http_failures[1]);
343 REQUIRE(((http_requests[1] - http_requests[0]) == 2 &&
344 (http_failures[1] - http_failures[0]) == 0));
345
346 cxt.setRun(10);
347
348 REQUIRE_THROWS(hp->getConditions().getCondition<DoubleTableCondition>(
349 "testbeam22_pedestals"));
350 }
351}
352
359TEST_CASE("CSVLoader", "[Conditions][CSVLoader]") {
360 std::string test_a("A,B,C\n1,2,3\n5,\"6\",7\n");
361
362 StringCSVLoader loader_a(test_a);
363
364 REQUIRE(loader_a.nextRow());
365 REQUIRE(loader_a.get("A") == "1");
366 REQUIRE(loader_a.getInteger("B") == 2);
367 REQUIRE(loader_a.get("C") == "3");
368 REQUIRE(loader_a.nextRow());
369 REQUIRE(loader_a.get("A") == "5");
370 REQUIRE(loader_a.getInteger("B") == 6);
371 REQUIRE(loader_a.get("C") == "7");
372 REQUIRE(!loader_a.nextRow());
373
374 std::string test_b("#Ignore me, dude\nA,B,C\n\n1,2,3\n5,\"6\",7");
375
376 StringCSVLoader loader_b(test_b);
377
378 REQUIRE(loader_b.nextRow());
379 REQUIRE(loader_b.get("A") == "1");
380 REQUIRE(loader_b.getInteger("B") == 2);
381 REQUIRE(loader_b.get("C") == "3");
382 REQUIRE(loader_b.nextRow());
383 REQUIRE(loader_b.get("A") == "5");
384 REQUIRE(loader_b.getInteger("B") == 6);
385 REQUIRE(loader_b.get("C") == "7");
386 REQUIRE(!loader_b.nextRow());
387
388 std::string test_c("#Ignore me, dude\nA,B,C\n\n1,2,3\n5,\"6\",7,9");
389
390 StringCSVLoader loader_c(test_c);
391
392 REQUIRE(loader_c.nextRow());
393 REQUIRE(loader_c.get("A") == "1");
394 REQUIRE(loader_c.getInteger("B") == 2);
395 REQUIRE(loader_c.get("C") == "3");
396 REQUIRE_THROWS(loader_c.nextRow());
397
398 std::ofstream fx_b("test.csv");
399 fx_b << test_b;
400 fx_b.close();
401
402 StreamCSVLoader loader_b2("test.csv");
403
404 REQUIRE(loader_b2.nextRow());
405 REQUIRE(loader_b2.get("A") == "1");
406 REQUIRE(loader_b2.getInteger("B") == 2);
407 REQUIRE(loader_b2.get("C") == "3");
408 REQUIRE(loader_b2.nextRow());
409 REQUIRE(loader_b2.get("A") == "5");
410 REQUIRE(loader_b2.getInteger("B") == 6);
411 REQUIRE(loader_b2.get("C") == "7");
412 REQUIRE(!loader_b2.nextRow());
413}
414
415} // namespace test
416} // namespace conditions
Class that defines an ECal detector ID with a cell number.
Class that provides header information about an event such as event number and timestamp.
Class that defines an HCal sensitive detector.
Class which represents the process under execution.
unsigned int getColumnCount() const
Get the number of columns.
std::size_t getRowCount() const
Get the number of rows.
std::pair< unsigned int, std::vector< T > > getRow(unsigned int irow) const
Get a row by number Used primarily for persisting the SimpleTableCondition.
static void load(IntegerTableCondition &, std::istream &)
Load the table from a stream Columns must be defined by the user.
static void store(const IntegerTableCondition &, std::ostream &, bool expandIds=true)
Convert the table into a stream.
Extension of DetectorID providing access to ECal layers and cell numbers in a hex grid.
Definition EcalID.h:20
Provides header information an event such as event number and timestamp.
Definition EventHeader.h:44
void setRun(int run)
Set the run number.
Implements detector ids for HCal subdetector.
Definition HcalID.h:19
Parameters run(const std::string &root_object, const std::string &pythonScript, char *args[], int nargs)
run the python script and extract the parameters
Definition Python.cxx:300