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 "valid_for_all_rows([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.last_process",
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 "valid_for_runs('file:///tmp/"
234 "dump_double.csv',0,100)\ncop.valid_for_runs('/"
235 "tmp/dump_double.csv',101,120)\n";
236
237 FILE* f = fopen("/tmp/test_cond.py", "w");
238 fputs(cfgpy, f);
239 fclose(f);
240
241 auto cfg{framework::config::run("ldmxcfg.Process.last_process",
242 "/tmp/test_cond.py", 0, 0)};
243 auto hp{std::make_unique<framework::Process>(cfg)};
245 hp->setEventHeader(&cxt);
246
247 cxt.setRun(10);
248 const conditions::DoubleTableCondition& f_table1 =
249 hp->getConditions().getCondition<conditions::DoubleTableCondition>(
250 "test_table_file");
251 matchesAll(dtable, f_table1);
252
253 cxt.setRun(119);
254 const conditions::DoubleTableCondition& f_table2 =
255 hp->getConditions().getCondition<conditions::DoubleTableCondition>(
256 "test_table_file");
257 matchesAll(dtable, f_table2);
258 }
259
260 SECTION("Testing HTTP loading") {
261 const char* cfgpy =
262 "#!/usr/bin/python3\n\nimport sys\n\nfrom LDMX.Framework "
263 "import ldmxcfg\nfrom LDMX.Conditions import SimpleCSVTableProvider\n"
264 "p=ldmxcfg.Process(\"test\")\n"
265 "p.testMode=True\n"
266 "columns=[\"A\",\"Q\",\"V\"]\n"
267 "cop=SimpleCSVTableProvider.SimpleCSVIntegerTableProvider(\"test_table_"
268 "http\",columns)\n"
269 "cop.valid_forever(\"https://raw.githubusercontent.com/LDMX-Software/"
270 "ci-data/refs/heads/main/conditions-test/test_table.csv\")\n";
271
272 FILE* f = fopen("/tmp/test_cond.py", "w");
273 fputs(cfgpy, f);
274 fclose(f);
275
276 auto cfg{framework::config::run("ldmxcfg.Process.last_process",
277 "/tmp/test_cond.py", 0, 0)};
278 auto hp{std::make_unique<framework::Process>(cfg)};
280 hp->setEventHeader(&cxt);
281
282 const IntegerTableCondition& http_table =
283 hp->getConditions().getCondition<IntegerTableCondition>(
284 "test_table_http");
285 matchesAll(http_table, itable);
286 }
287
288 SECTION("Testing CSV metatable") {
289 const char* cfgpy =
290 "#!/usr/bin/python3\n\nimport sys\n\nfrom LDMX.Framework "
291 "import ldmxcfg\nfrom LDMX.Conditions import SimpleCSVTableProvider\n"
292 "p=ldmxcfg.Process(\"test\")\n"
293 "p.testMode=True\n"
294 "columns=[\"PEDESTAL_ADC\"]\n"
295 "cop=SimpleCSVTableProvider.SimpleCSVDoubleTableProvider(\"testbeam22_"
296 "pedestals\",columns)\n"
297 "cop.conditions_base_url='https://raw.githubusercontent.com/"
298 "LDMX-Software/conditions-data/refs/heads/main/'\n"
299 "cop.entries_url='${LDMX_CONDITION_BASEURL}/Hcal/testbeam04-2022/"
300 "pedestals/index_v1_0_0.csv'\n";
301
302 FILE* f = fopen("/tmp/test_cond.py", "w");
303 fputs(cfgpy, f);
304 fclose(f);
305
306 auto cfg{framework::config::run("ldmxcfg.Process.last_process",
307 "/tmp/test_cond.py", 0, 0)};
308 auto hp{std::make_unique<framework::Process>(cfg)};
310
311 hp->setEventHeader(&cxt);
312
313 unsigned int http_requests[2], http_failures[2];
314
315 conditions::urlstatistics(http_requests[0], http_failures[0]);
316
317 cxt.setRun(128);
318
319 hp->getConditions().getCondition<DoubleTableCondition>(
320 "testbeam22_pedestals");
321
322 hp->getConditions().getCondition<DoubleTableCondition>(
323 "testbeam22_pedestals");
324
325 conditions::urlstatistics(http_requests[1], http_failures[1]);
326 REQUIRE(((http_requests[1] - http_requests[0]) == 1 &&
327 (http_failures[1] - http_failures[0]) == 0));
328
329 cxt.setRun(129);
330
331 hp->getConditions().getCondition<DoubleTableCondition>(
332 "testbeam22_pedestals");
333
334 conditions::urlstatistics(http_requests[1], http_failures[1]);
335 REQUIRE(((http_requests[1] - http_requests[0]) == 1 &&
336 (http_failures[1] - http_failures[0]) == 0));
337
338 cxt.setRun(140);
339
340 hp->getConditions().getCondition<DoubleTableCondition>(
341 "testbeam22_pedestals");
342
343 conditions::urlstatistics(http_requests[1], http_failures[1]);
344 REQUIRE(((http_requests[1] - http_requests[0]) == 2 &&
345 (http_failures[1] - http_failures[0]) == 0));
346
347 cxt.setRun(10);
348
349 REQUIRE_THROWS(hp->getConditions().getCondition<DoubleTableCondition>(
350 "testbeam22_pedestals"));
351 }
352}
353
360TEST_CASE("CSVLoader", "[Conditions][CSVLoader]") {
361 std::string test_a("A,B,C\n1,2,3\n5,\"6\",7\n");
362
363 StringCSVLoader loader_a(test_a);
364
365 REQUIRE(loader_a.nextRow());
366 REQUIRE(loader_a.get("A") == "1");
367 REQUIRE(loader_a.getInteger("B") == 2);
368 REQUIRE(loader_a.get("C") == "3");
369 REQUIRE(loader_a.nextRow());
370 REQUIRE(loader_a.get("A") == "5");
371 REQUIRE(loader_a.getInteger("B") == 6);
372 REQUIRE(loader_a.get("C") == "7");
373 REQUIRE(!loader_a.nextRow());
374
375 std::string test_b("#Ignore me, dude\nA,B,C\n\n1,2,3\n5,\"6\",7");
376
377 StringCSVLoader loader_b(test_b);
378
379 REQUIRE(loader_b.nextRow());
380 REQUIRE(loader_b.get("A") == "1");
381 REQUIRE(loader_b.getInteger("B") == 2);
382 REQUIRE(loader_b.get("C") == "3");
383 REQUIRE(loader_b.nextRow());
384 REQUIRE(loader_b.get("A") == "5");
385 REQUIRE(loader_b.getInteger("B") == 6);
386 REQUIRE(loader_b.get("C") == "7");
387 REQUIRE(!loader_b.nextRow());
388
389 std::string test_c("#Ignore me, dude\nA,B,C\n\n1,2,3\n5,\"6\",7,9");
390
391 StringCSVLoader loader_c(test_c);
392
393 REQUIRE(loader_c.nextRow());
394 REQUIRE(loader_c.get("A") == "1");
395 REQUIRE(loader_c.getInteger("B") == 2);
396 REQUIRE(loader_c.get("C") == "3");
397 REQUIRE_THROWS(loader_c.nextRow());
398
399 std::ofstream fx_b("test.csv");
400 fx_b << test_b;
401 fx_b.close();
402
403 StreamCSVLoader loader_b2("test.csv");
404
405 REQUIRE(loader_b2.nextRow());
406 REQUIRE(loader_b2.get("A") == "1");
407 REQUIRE(loader_b2.getInteger("B") == 2);
408 REQUIRE(loader_b2.get("C") == "3");
409 REQUIRE(loader_b2.nextRow());
410 REQUIRE(loader_b2.get("A") == "5");
411 REQUIRE(loader_b2.getInteger("B") == 6);
412 REQUIRE(loader_b2.get("C") == "7");
413 REQUIRE(!loader_b2.nextRow());
414}
415
416} // namespace test
417} // 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