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')\n"
195 "columns=['A','B','C']\ncop=SimpleCSVTableProvider."
196 "simple_csv_integer_table_provider('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')\n"
231 "columns=['SQRT','EXP','LOG']\ncop=SimpleCSVTableProvider."
232 "simple_csv_double_table_provider('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 "import sys\n\nfrom LDMX.Framework "
263 "import ldmxcfg\nfrom LDMX.Conditions import SimpleCSVTableProvider\n"
264 "p=ldmxcfg.Process(\"test\")\n"
265 "columns=[\"A\",\"Q\",\"V\"]\n"
266 "cop=SimpleCSVTableProvider.simple_csv_integer_table_provider(\"test_"
267 "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 "import sys\n"
291 "from LDMX.Framework import ldmxcfg\n"
292 "from LDMX.Conditions import SimpleCSVTableProvider\n"
293 "p=ldmxcfg.Process(\"test\")\n"
294 "columns=[\"PEDESTAL_ADC\"]\n"
295 "cop=SimpleCSVTableProvider.simple_csv_double_table_provider("
296 "\"testbeam22_"
297 "pedestals\",columns)\n"
298 "cop.conditions_base_url='https://raw.githubusercontent.com/"
299 "LDMX-Software/conditions-data/refs/heads/main/'\n"
300 "cop.entries_url='${LDMX_CONDITION_BASEURL}/Hcal/testbeam04-2022/"
301 "pedestals/index_v1_0_0.csv'\n";
302
303 FILE* f = fopen("/tmp/test_cond.py", "w");
304 fputs(cfgpy, f);
305 fclose(f);
306
307 auto cfg{framework::config::run("ldmxcfg.Process.last_process",
308 "/tmp/test_cond.py", 0, 0)};
309 auto hp{std::make_unique<framework::Process>(cfg)};
311
312 hp->setEventHeader(&cxt);
313
314 unsigned int http_requests[2], http_failures[2];
315
316 conditions::urlstatistics(http_requests[0], http_failures[0]);
317
318 cxt.setRun(128);
319
320 hp->getConditions().getCondition<DoubleTableCondition>(
321 "testbeam22_pedestals");
322
323 hp->getConditions().getCondition<DoubleTableCondition>(
324 "testbeam22_pedestals");
325
326 conditions::urlstatistics(http_requests[1], http_failures[1]);
327 REQUIRE(((http_requests[1] - http_requests[0]) == 1 &&
328 (http_failures[1] - http_failures[0]) == 0));
329
330 cxt.setRun(129);
331
332 hp->getConditions().getCondition<DoubleTableCondition>(
333 "testbeam22_pedestals");
334
335 conditions::urlstatistics(http_requests[1], http_failures[1]);
336 REQUIRE(((http_requests[1] - http_requests[0]) == 1 &&
337 (http_failures[1] - http_failures[0]) == 0));
338
339 cxt.setRun(140);
340
341 hp->getConditions().getCondition<DoubleTableCondition>(
342 "testbeam22_pedestals");
343
344 conditions::urlstatistics(http_requests[1], http_failures[1]);
345 REQUIRE(((http_requests[1] - http_requests[0]) == 2 &&
346 (http_failures[1] - http_failures[0]) == 0));
347
348 cxt.setRun(10);
349
350 REQUIRE_THROWS(hp->getConditions().getCondition<DoubleTableCondition>(
351 "testbeam22_pedestals"));
352 }
353}
354
361TEST_CASE("CSVLoader", "[Conditions][CSVLoader]") {
362 std::string test_a("A,B,C\n1,2,3\n5,\"6\",7\n");
363
364 StringCSVLoader loader_a(test_a);
365
366 REQUIRE(loader_a.nextRow());
367 REQUIRE(loader_a.get("A") == "1");
368 REQUIRE(loader_a.getInteger("B") == 2);
369 REQUIRE(loader_a.get("C") == "3");
370 REQUIRE(loader_a.nextRow());
371 REQUIRE(loader_a.get("A") == "5");
372 REQUIRE(loader_a.getInteger("B") == 6);
373 REQUIRE(loader_a.get("C") == "7");
374 REQUIRE(!loader_a.nextRow());
375
376 std::string test_b("#Ignore me, dude\nA,B,C\n\n1,2,3\n5,\"6\",7");
377
378 StringCSVLoader loader_b(test_b);
379
380 REQUIRE(loader_b.nextRow());
381 REQUIRE(loader_b.get("A") == "1");
382 REQUIRE(loader_b.getInteger("B") == 2);
383 REQUIRE(loader_b.get("C") == "3");
384 REQUIRE(loader_b.nextRow());
385 REQUIRE(loader_b.get("A") == "5");
386 REQUIRE(loader_b.getInteger("B") == 6);
387 REQUIRE(loader_b.get("C") == "7");
388 REQUIRE(!loader_b.nextRow());
389
390 std::string test_c("#Ignore me, dude\nA,B,C\n\n1,2,3\n5,\"6\",7,9");
391
392 StringCSVLoader loader_c(test_c);
393
394 REQUIRE(loader_c.nextRow());
395 REQUIRE(loader_c.get("A") == "1");
396 REQUIRE(loader_c.getInteger("B") == 2);
397 REQUIRE(loader_c.get("C") == "3");
398 REQUIRE_THROWS(loader_c.nextRow());
399
400 std::ofstream fx_b("test.csv");
401 fx_b << test_b;
402 fx_b.close();
403
404 StreamCSVLoader loader_b2("test.csv");
405
406 REQUIRE(loader_b2.nextRow());
407 REQUIRE(loader_b2.get("A") == "1");
408 REQUIRE(loader_b2.getInteger("B") == 2);
409 REQUIRE(loader_b2.get("C") == "3");
410 REQUIRE(loader_b2.nextRow());
411 REQUIRE(loader_b2.get("A") == "5");
412 REQUIRE(loader_b2.getInteger("B") == 6);
413 REQUIRE(loader_b2.get("C") == "7");
414 REQUIRE(!loader_b2.nextRow());
415}
416
417} // namespace test
418} // 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