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& iTable =
211 hp->getConditions().getCondition<IntegerTableCondition>(
212 "test_table_python");
213
214 CHECK(iTable.getByName(292, "A") == 10);
215 CHECK(iTable.getByName(2928184, "B") == 45);
216 CHECK(iTable.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& fTable1 =
248 hp->getConditions().getCondition<conditions::DoubleTableCondition>(
249 "test_table_file");
250 cxt.setRun(119);
251 const conditions::DoubleTableCondition& fTable2 =
252 hp->getConditions().getCondition<conditions::DoubleTableCondition>(
253 "test_table_file");
254 matchesAll(dtable, fTable1);
255 matchesAll(dtable, fTable2);
256 }
257
258 SECTION("Testing HTTP loading") {
259 const char* cfgpy =
260 "#!/usr/bin/python3\n\nimport sys\n\nfrom LDMX.Framework "
261 "import ldmxcfg\nfrom LDMX.Conditions import SimpleCSVTableProvider\n"
262 "p=ldmxcfg.Process(\"test\")\n"
263 "p.testMode=True\n"
264 "columns=[\"A\",\"Q\",\"V\"]\n"
265 "cop=SimpleCSVTableProvider.SimpleCSVIntegerTableProvider(\"test_table_"
266 "http\",columns)\n"
267 "cop.validForever(\"http://www-users.cse.umn.edu/~jmmans/"
268 "test_table.csv\")\n";
269
270 FILE* f = fopen("/tmp/test_cond.py", "w");
271 fputs(cfgpy, f);
272 fclose(f);
273
274 auto cfg{framework::config::run("ldmxcfg.Process.lastProcess",
275 "/tmp/test_cond.py", 0, 0)};
276 auto hp{std::make_unique<framework::Process>(cfg)};
278 hp->setEventHeader(&cxt);
279
280 const IntegerTableCondition& httpTable =
281 hp->getConditions().getCondition<IntegerTableCondition>(
282 "test_table_http");
283 matchesAll(httpTable, itable);
284 }
285
286 SECTION("Testing CSV metatable") {
287 const char* cfgpy =
288 "#!/usr/bin/python3\n\nimport sys\n\nfrom LDMX.Framework "
289 "import ldmxcfg\nfrom LDMX.Conditions import SimpleCSVTableProvider\n"
290 "p=ldmxcfg.Process(\"test\")\n"
291 "p.testMode=True\n"
292 "columns=[\"PEDESTAL_ADC\"]\n"
293 "cop=SimpleCSVTableProvider.SimpleCSVDoubleTableProvider(\"testbeam22_"
294 "pedestals\",columns)\n"
295 "cop.conditions_baseURL='http://www-users.cse.umn.edu/~jmmans/ldmx/"
296 "condtest/'\n"
297 "cop.entriesURL='${LDMX_CONDITION_BASEURL}/testbeam22_pedestals.csv'\n";
298
299 FILE* f = fopen("/tmp/test_cond.py", "w");
300 fputs(cfgpy, f);
301 fclose(f);
302
303 auto cfg{framework::config::run("ldmxcfg.Process.lastProcess",
304 "/tmp/test_cond.py", 0, 0)};
305 auto hp{std::make_unique<framework::Process>(cfg)};
307
308 hp->setEventHeader(&cxt);
309
310 unsigned int http_requests[2], http_failures[2];
311
312 conditions::urlstatistics(http_requests[0], http_failures[0]);
313
314 cxt.setRun(128);
315
316 hp->getConditions().getCondition<DoubleTableCondition>(
317 "testbeam22_pedestals");
318
319 hp->getConditions().getCondition<DoubleTableCondition>(
320 "testbeam22_pedestals");
321
322 conditions::urlstatistics(http_requests[1], http_failures[1]);
323 REQUIRE(((http_requests[1] - http_requests[0]) == 1 &&
324 (http_failures[1] - http_failures[0]) == 0));
325
326 cxt.setRun(129);
327
328 hp->getConditions().getCondition<DoubleTableCondition>(
329 "testbeam22_pedestals");
330
331 conditions::urlstatistics(http_requests[1], http_failures[1]);
332 REQUIRE(((http_requests[1] - http_requests[0]) == 1 &&
333 (http_failures[1] - http_failures[0]) == 0));
334
335 cxt.setRun(140);
336
337 hp->getConditions().getCondition<DoubleTableCondition>(
338 "testbeam22_pedestals");
339
340 conditions::urlstatistics(http_requests[1], http_failures[1]);
341 REQUIRE(((http_requests[1] - http_requests[0]) == 2 &&
342 (http_failures[1] - http_failures[0]) == 0));
343
344 cxt.setRun(10);
345
346 REQUIRE_THROWS(hp->getConditions().getCondition<DoubleTableCondition>(
347 "testbeam22_pedestals"));
348 }
349}
350
357TEST_CASE("CSVLoader", "[Conditions][CSVLoader]") {
358 std::string testA("A,B,C\n1,2,3\n5,\"6\",7\n");
359
360 StringCSVLoader loaderA(testA);
361
362 REQUIRE(loaderA.nextRow());
363 REQUIRE(loaderA.get("A") == "1");
364 REQUIRE(loaderA.getInteger("B") == 2);
365 REQUIRE(loaderA.get("C") == "3");
366 REQUIRE(loaderA.nextRow());
367 REQUIRE(loaderA.get("A") == "5");
368 REQUIRE(loaderA.getInteger("B") == 6);
369 REQUIRE(loaderA.get("C") == "7");
370 REQUIRE(!loaderA.nextRow());
371
372 std::string testB("#Ignore me, dude\nA,B,C\n\n1,2,3\n5,\"6\",7");
373
374 StringCSVLoader loaderB(testB);
375
376 REQUIRE(loaderB.nextRow());
377 REQUIRE(loaderB.get("A") == "1");
378 REQUIRE(loaderB.getInteger("B") == 2);
379 REQUIRE(loaderB.get("C") == "3");
380 REQUIRE(loaderB.nextRow());
381 REQUIRE(loaderB.get("A") == "5");
382 REQUIRE(loaderB.getInteger("B") == 6);
383 REQUIRE(loaderB.get("C") == "7");
384 REQUIRE(!loaderB.nextRow());
385
386 std::string testC("#Ignore me, dude\nA,B,C\n\n1,2,3\n5,\"6\",7,9");
387
388 StringCSVLoader loaderC(testC);
389
390 REQUIRE(loaderC.nextRow());
391 REQUIRE(loaderC.get("A") == "1");
392 REQUIRE(loaderC.getInteger("B") == 2);
393 REQUIRE(loaderC.get("C") == "3");
394 REQUIRE_THROWS(loaderC.nextRow());
395
396 std::ofstream fxB("test.csv");
397 fxB << testB;
398 fxB.close();
399
400 StreamCSVLoader loaderB2("test.csv");
401
402 REQUIRE(loaderB2.nextRow());
403 REQUIRE(loaderB2.get("A") == "1");
404 REQUIRE(loaderB2.getInteger("B") == 2);
405 REQUIRE(loaderB2.get("C") == "3");
406 REQUIRE(loaderB2.nextRow());
407 REQUIRE(loaderB2.get("A") == "5");
408 REQUIRE(loaderB2.getInteger("B") == 6);
409 REQUIRE(loaderB2.get("C") == "7");
410 REQUIRE(!loaderB2.nextRow());
411}
412
413} // namespace test
414} // 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:292