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/ConfigurePython.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* cfg =
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(cfg, f);
201 fclose(f);
202
203 framework::ConfigurePython cp("/tmp/test_cond.py", 0, 0);
204 framework::ProcessHandle hp = cp.makeProcess();
206 hp->setEventHeader(&cxt);
207
208 cxt.setRun(10);
209 const IntegerTableCondition& iTable =
210 hp->getConditions().getCondition<IntegerTableCondition>(
211 "test_table_python");
212
213 CHECK(iTable.getByName(292, "A") == 10);
214 CHECK(iTable.getByName(2928184, "B") == 45);
215 CHECK(iTable.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* cfg =
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')\np.testMode="
230 "True\ncolumns=['SQRT','EXP','LOG']\ncop=SimpleCSVTableProvider."
231 "SimpleCSVDoubleTableProvider('test_table_file',columns)\ncop."
232 "validForRuns('file:///tmp/dump_double.csv',0,100)\ncop.validForRuns('/"
233 "tmp/dump_double.csv',101,120)\n";
234
235 FILE* f = fopen("/tmp/test_cond.py", "w");
236 fputs(cfg, f);
237 fclose(f);
238
239 framework::ConfigurePython cp("/tmp/test_cond.py", 0, 0);
240 framework::ProcessHandle hp = cp.makeProcess();
242 hp->setEventHeader(&cxt);
243
244 cxt.setRun(10);
245 const conditions::DoubleTableCondition& fTable1 =
246 hp->getConditions().getCondition<conditions::DoubleTableCondition>(
247 "test_table_file");
248 cxt.setRun(119);
249 const conditions::DoubleTableCondition& fTable2 =
250 hp->getConditions().getCondition<conditions::DoubleTableCondition>(
251 "test_table_file");
252 matchesAll(dtable, fTable1);
253 matchesAll(dtable, fTable2);
254 }
255
256 SECTION("Testing HTTP loading") {
257 const char* cfg =
258 "#!/usr/bin/python3\n\nimport sys\n\nfrom LDMX.Framework "
259 "import ldmxcfg\nfrom LDMX.Conditions import SimpleCSVTableProvider\n"
260 "p=ldmxcfg.Process(\"test\")\n"
261 "p.testMode=True\n"
262 "columns=[\"A\",\"Q\",\"V\"]\n"
263 "cop=SimpleCSVTableProvider.SimpleCSVIntegerTableProvider(\"test_table_"
264 "http\",columns)\n"
265 "cop.validForever(\"http://www-users.cse.umn.edu/~jmmans/"
266 "test_table.csv\")\n";
267
268 FILE* f = fopen("/tmp/test_cond.py", "w");
269 fputs(cfg, f);
270 fclose(f);
271
272 framework::ConfigurePython cp("/tmp/test_cond.py", 0, 0);
273 framework::ProcessHandle hp = cp.makeProcess();
275 hp->setEventHeader(&cxt);
276
277 const IntegerTableCondition& httpTable =
278 hp->getConditions().getCondition<IntegerTableCondition>(
279 "test_table_http");
280 matchesAll(httpTable, itable);
281 }
282
283 SECTION("Testing CSV metatable") {
284 const char* cfg =
285 "#!/usr/bin/python3\n\nimport sys\n\nfrom LDMX.Framework "
286 "import ldmxcfg\nfrom LDMX.Conditions import SimpleCSVTableProvider\n"
287 "p=ldmxcfg.Process(\"test\")\n"
288 "p.testMode=True\n"
289 "columns=[\"PEDESTAL_ADC\"]\n"
290 "cop=SimpleCSVTableProvider.SimpleCSVDoubleTableProvider(\"testbeam22_"
291 "pedestals\",columns)\n"
292 "cop.conditions_baseURL='http://www-users.cse.umn.edu/~jmmans/ldmx/"
293 "condtest/'\n"
294 "cop.entriesURL='${LDMX_CONDITION_BASEURL}/testbeam22_pedestals.csv'\n";
295
296 FILE* f = fopen("/tmp/test_cond.py", "w");
297 fputs(cfg, f);
298 fclose(f);
299
300 framework::ConfigurePython cp("/tmp/test_cond.py", 0, 0);
301 framework::ProcessHandle hp = cp.makeProcess();
303
304 hp->setEventHeader(&cxt);
305
306 unsigned int http_requests[2], http_failures[2];
307
308 conditions::urlstatistics(http_requests[0], http_failures[0]);
309
310 cxt.setRun(128);
311
312 hp->getConditions().getCondition<DoubleTableCondition>(
313 "testbeam22_pedestals");
314
315 hp->getConditions().getCondition<DoubleTableCondition>(
316 "testbeam22_pedestals");
317
318 conditions::urlstatistics(http_requests[1], http_failures[1]);
319 REQUIRE(((http_requests[1] - http_requests[0]) == 1 &&
320 (http_failures[1] - http_failures[0]) == 0));
321
322 cxt.setRun(129);
323
324 hp->getConditions().getCondition<DoubleTableCondition>(
325 "testbeam22_pedestals");
326
327 conditions::urlstatistics(http_requests[1], http_failures[1]);
328 REQUIRE(((http_requests[1] - http_requests[0]) == 1 &&
329 (http_failures[1] - http_failures[0]) == 0));
330
331 cxt.setRun(140);
332
333 hp->getConditions().getCondition<DoubleTableCondition>(
334 "testbeam22_pedestals");
335
336 conditions::urlstatistics(http_requests[1], http_failures[1]);
337 REQUIRE(((http_requests[1] - http_requests[0]) == 2 &&
338 (http_failures[1] - http_failures[0]) == 0));
339
340 cxt.setRun(10);
341
342 REQUIRE_THROWS(hp->getConditions().getCondition<DoubleTableCondition>(
343 "testbeam22_pedestals"));
344 }
345}
346
353TEST_CASE("CSVLoader", "[Conditions][CSVLoader]") {
354 std::string testA("A,B,C\n1,2,3\n5,\"6\",7\n");
355
356 StringCSVLoader loaderA(testA);
357
358 REQUIRE(loaderA.nextRow());
359 REQUIRE(loaderA.get("A") == "1");
360 REQUIRE(loaderA.getInteger("B") == 2);
361 REQUIRE(loaderA.get("C") == "3");
362 REQUIRE(loaderA.nextRow());
363 REQUIRE(loaderA.get("A") == "5");
364 REQUIRE(loaderA.getInteger("B") == 6);
365 REQUIRE(loaderA.get("C") == "7");
366 REQUIRE(!loaderA.nextRow());
367
368 std::string testB("#Ignore me, dude\nA,B,C\n\n1,2,3\n5,\"6\",7");
369
370 StringCSVLoader loaderB(testB);
371
372 REQUIRE(loaderB.nextRow());
373 REQUIRE(loaderB.get("A") == "1");
374 REQUIRE(loaderB.getInteger("B") == 2);
375 REQUIRE(loaderB.get("C") == "3");
376 REQUIRE(loaderB.nextRow());
377 REQUIRE(loaderB.get("A") == "5");
378 REQUIRE(loaderB.getInteger("B") == 6);
379 REQUIRE(loaderB.get("C") == "7");
380 REQUIRE(!loaderB.nextRow());
381
382 std::string testC("#Ignore me, dude\nA,B,C\n\n1,2,3\n5,\"6\",7,9");
383
384 StringCSVLoader loaderC(testC);
385
386 REQUIRE(loaderC.nextRow());
387 REQUIRE(loaderC.get("A") == "1");
388 REQUIRE(loaderC.getInteger("B") == 2);
389 REQUIRE(loaderC.get("C") == "3");
390 REQUIRE_THROWS(loaderC.nextRow());
391
392 std::ofstream fxB("test.csv");
393 fxB << testB;
394 fxB.close();
395
396 StreamCSVLoader loaderB2("test.csv");
397
398 REQUIRE(loaderB2.nextRow());
399 REQUIRE(loaderB2.get("A") == "1");
400 REQUIRE(loaderB2.getInteger("B") == 2);
401 REQUIRE(loaderB2.get("C") == "3");
402 REQUIRE(loaderB2.nextRow());
403 REQUIRE(loaderB2.get("A") == "5");
404 REQUIRE(loaderB2.getInteger("B") == 6);
405 REQUIRE(loaderB2.get("C") == "7");
406 REQUIRE(!loaderB2.nextRow());
407}
408
409} // namespace test
410} // 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.
Utility class which reads/executes a python script and creates a Process object based on the input.
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
std::unique_ptr< Process > ProcessHandle
A handle to the current process Used to pass a process from ConfigurePython to fire....
Definition Process.h:248