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 <cmath>
6#include <fstream>
7#include <sstream>
8
9#include "Conditions/GeneralCSVLoader.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
19namespace conditions {
20namespace test {
21
22template <class T>
23void matchesMeta(const T& a, const T& b) {
24 REQUIRE(a.getColumnCount() == b.getColumnCount());
25 REQUIRE(a.getRowCount() == b.getRowCount());
26 REQUIRE(a.getColumnNames() == b.getColumnNames());
27}
28
29void matchesAll(const conditions::DoubleTableCondition& a,
31 matchesMeta(a, b);
32 for (unsigned int i = 0; i < a.getRowCount(); i++) {
33 std::pair<unsigned int, std::vector<double>> ar = a.getRow(i);
34 std::pair<unsigned int, std::vector<double>> br = b.getRow(i);
35 REQUIRE(ar.first == br.first);
36 for (unsigned int ic = 0; ic < a.getColumnCount(); ic++) {
37 // std::cout << i << ',' << ic << "=>" << ar.second[ic] << " " <<
38 // br.second[ic] << std::endl;
39 REQUIRE(fabs(ar.second[ic] - br.second[ic]) /
40 std::max(1e-5, (ar.second[ic] + br.second[ic]) / 2) <
41 1e-5); // limited precision of course
42 }
43 }
44}
45
46void matchesAll(const conditions::IntegerTableCondition& a,
48 matchesMeta(a, b);
49 for (unsigned int i = 0; i < a.getRowCount(); i++)
50 REQUIRE(a.getRow(i) == b.getRow(i));
51}
52
53using Catch::Matchers::ContainsSubstring;
54
62TEST_CASE("Conditions", "[Conditions]") {
63 // create a simple table
64
65 std::vector<std::string> columns({"A", "Q", "V"});
66
67 IntegerTableCondition itable("ITable", columns);
68
69 for (int key = 100; key > 0; key -= 10) {
70 ldmx::EcalID id(1, 1, key);
71 std::vector<int> vals;
72 vals.push_back(key * 2);
73 vals.push_back(key / 2);
74 vals.push_back(key * key);
75 itable.add(id.raw(), vals);
76 }
77
78 std::vector<std::string> columnsd({"SQRT", "EXP", "LOG"});
79 conditions::DoubleTableCondition dtable("DTable", columnsd);
80
81 for (int key = 1; key < 8; key += 2) {
82 ldmx::HcalID id(1, 1, key);
83 std::vector<double> vals;
84 vals.push_back(sqrt(key));
85 vals.push_back(exp(key * 5));
86 vals.push_back(log(key));
87 dtable.add(id.raw(), vals);
88 }
89
90 SECTION("Testing simple table construction") {
91 REQUIRE(itable.getRowCount() == 10);
92
93 REQUIRE(itable.getColumnCount() == 3);
94
95 ldmx::EcalID id(1, 1, 20);
96 REQUIRE(itable.get(id.raw(), 1) == 10);
97
98 REQUIRE_THROWS_WITH(itable.add(2, std::vector<int>(2)),
99 ContainsSubstring("columns into a table"));
100
101 REQUIRE_THROWS_WITH(itable.add(id.raw(), std::vector<int>(3)),
102 ContainsSubstring("existing id"));
103
104 std::pair<unsigned int, std::vector<int>> row = itable.getRow(4);
105
106 CHECK(row.first == 0x14021032);
107
108 CHECK(row.second.size() == 3);
109
110 CHECK(row.second.at(2) == (50 * 50));
111
112 ldmx::EcalID id2(1, 1, 60);
113
114 CHECK(itable.getByName(id2.raw(), "Q") == 30);
115
116 const char* expected =
117 "[ TableCondition: ITable\n"
118 " DetID,id:\"A\",id:\"Q\",id:\"V\"\n"
119 " 335679498,20,5,100\n"
120 " 335679508,40,10,400\n"
121 " 335679518,60,15,900\n"
122 " 335679528,80,20,1600\n"
123 " 335679538,100,25,2500\n"
124 " 335679548,120,30,3600\n"
125 " 335679558,140,35,4900\n"
126 " 335679568,160,40,6400\n"
127 " 335679578,180,45,8100\n"
128 " 335679588,200,50,10000\n"
129 "]";
130 std::stringstream ss;
131 ss << itable;
132 std::string image = ss.str();
133 CHECK(image == expected);
134 }
135
136 SECTION("Testing CSV IO") {
137 std::stringstream ss;
139
140 std::string image1 = ss.str();
141 const char* expected1 =
142 "\"DetID\",id:\"subdetector\",id:\"layer\",id:\"module\",id:\"cell\","
143 "\"A\",\"Q\",\"V\"\n0x1402100a,5,1,1,10,20,5,100\n0x14021014,5,1,1,20,"
144 "40,10,400\n0x1402101e,5,1,1,30,60,15,900\n0x14021028,5,1,1,40,80,20,"
145 "1600\n0x14021032,5,1,1,50,100,25,2500\n0x1402103c,5,1,1,60,120,30,"
146 "3600\n0x14021046,5,1,1,70,140,35,4900\n0x14021050,5,1,1,80,160,40,"
147 "6400\n0x1402105a,5,1,1,90,180,45,8100\n0x14021064,5,1,1,100,200,50,"
148 "10000\n";
149
150 CHECK(image1 == expected1);
151
152 // std::cout << image1;
153
154 std::stringstream ss_read1(image1);
155 IntegerTableCondition itable2("ITable", columns);
157
158 matchesAll(itable, itable2);
159
160 // missing id column example
161 std::string image2("B,A,Q,V\n0,40,50,100\n0,60,70,90\n");
162 std::stringstream ss_read2(image2);
163 REQUIRE_THROWS_WITH(
165 ContainsSubstring(
166 "Malformed CSV file with no DetId or subdetector column"));
167
168 // missing column example
169 std::string image3(
170 "DetID,A,Q\n0x1402100a,20,5\n0x14021014,40,10\n0x1402101e,60,"
171 "15\n0x14021028,80,20\n0x14021032,100,25\n0x1402103c,120,"
172 "30\n0x14021046,140,35\n0x14021050,160,40\n0x1402105a,180,"
173 "45\n0x14021064,200,50\n");
174 std::stringstream ss_read3(image3);
175 REQUIRE_THROWS_WITH(
177 ContainsSubstring("Missing column"));
178
179 // varying line lengths example
180 std::string image4(
181 "DetID,A,Q,V\n0x1402100a,20,5,100\n0x14021014,40,10\n0x1402101e,60,15,"
182 "900\n");
183 std::stringstream ss_read4(image4);
184 REQUIRE_THROWS_WITH(
186 ContainsSubstring("Mismatched number of columns (3!=4) on line 3"));
187 }
188
189 SECTION("Testing python static") {
190 const char* cfgpy =
191 "#!/usr/bin/python3\n\nimport sys\n\nfrom LDMX.Framework import "
192 "ldmxcfg\nfrom LDMX.Conditions import "
193 "SimpleCSVTableProvider\n\np=ldmxcfg.Process('test')\n"
194 "columns=['A','B','C']\ncop=SimpleCSVTableProvider."
195 "simple_csv_integer_table_provider('test_table_python',columns)\ncop."
196 "valid_for_all_rows([10,45,129])";
197
198 FILE* f = fopen("/tmp/test_cond.py", "w");
199 fputs(cfgpy, f);
200 fclose(f);
201
202 auto cfg{framework::config::run("ldmxcfg.Process.last_process",
203 "/tmp/test_cond.py", 0, 0)};
204 auto hp{std::make_unique<framework::Process>(cfg)};
206 hp->setEventHeader(&cxt);
207
208 cxt.setRun(10);
209 const IntegerTableCondition& i_table =
210 hp->getConditions().getCondition<IntegerTableCondition>(
211 "test_table_python");
212
213 CHECK(i_table.getByName(292, "A") == 10);
214 CHECK(i_table.getByName(2928184, "B") == 45);
215 CHECK(i_table.getByName(82910, "C") == 129);
216 }
217
218 SECTION("Testing file loading") {
219 std::ofstream fs("/tmp/dump_double.csv");
220 std::stringstream ss;
223 fs.close();
224 // std::cout << "Step 1" << std::endl << ss.str();
225
226 const char* cfgpy =
227 "#!/usr/bin/python3\n\nimport sys\n\nfrom LDMX.Framework import "
228 "ldmxcfg\nfrom LDMX.Conditions import "
229 "SimpleCSVTableProvider\n\np=ldmxcfg.Process('test')\n"
230 "columns=['SQRT','EXP','LOG']\ncop=SimpleCSVTableProvider."
231 "simple_csv_double_table_provider('test_table_file',columns)\ncop."
232 "valid_for_runs('file:///tmp/"
233 "dump_double.csv',0,100)\ncop.valid_for_runs('/"
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.last_process",
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 "import sys\n\nfrom LDMX.Framework "
262 "import ldmxcfg\nfrom LDMX.Conditions import SimpleCSVTableProvider\n"
263 "p=ldmxcfg.Process(\"test\")\n"
264 "columns=[\"A\",\"Q\",\"V\"]\n"
265 "cop=SimpleCSVTableProvider.simple_csv_integer_table_provider(\"test_"
266 "table_"
267 "http\",columns)\n"
268 "cop.valid_forever(\"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.last_process",
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 "import sys\n"
290 "from LDMX.Framework import ldmxcfg\n"
291 "from LDMX.Conditions import SimpleCSVTableProvider\n"
292 "p=ldmxcfg.Process(\"test\")\n"
293 "columns=[\"PEDESTAL_ADC\"]\n"
294 "cop=SimpleCSVTableProvider.simple_csv_double_table_provider("
295 "\"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