BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
Compare.cc
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) Royal Holloway,
3University of London 2001 - 2023.
4
5This file is part of BDSIM.
6
7BDSIM is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published
9by the Free Software Foundation version 3 of the License.
10
11BDSIM is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with BDSIM. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "Compare.hh"
20#include "Result.hh"
21#include "ResultEvent.hh"
22#include "ResultEventTree.hh"
23#include "ResultHistogram.hh"
24#include "ResultHistogram2D.hh"
25#include "ResultSampler.hh"
26#include "ResultTree.hh"
27
28#include "analysis/Event.hh"
29#include "analysis/Model.hh"
30#include "analysis/Options.hh"
31
32#include "BDSDebug.hh"
33#include "BDSOutputROOTEventOptions.hh"
34#include "BDSOutputROOTEventSampler.hh"
35#include "BDSVersionData.hh"
36
37#include <algorithm>
38#include <cmath>
39#include <iomanip>
40#include <iostream>
41#include <map>
42#include <string>
43#include <vector>
44
45#include "TBranch.h"
46#include "TChain.h"
47#include "TDirectory.h"
48#include "TFile.h"
49#include "TH1.h"
50#include "TObjArray.h"
51#include "TTree.h"
52
53std::vector<Result*> Compare::Files(TFile* f1, TFile* f2)
54{
55 std::vector<Result*> results;
56 // A TFile inherits TDirectory, so we simply use the TDirectory function.
57 Compare::Directories((TDirectory*)f1, (TDirectory*)f2, results);
58 return results;
59}
60
61void Compare::Directories(TDirectory* d1,
62 TDirectory* d2,
63 std::vector<Result*>& results)
64{
65 // record original directory in file.
66 TDirectory* originalDirectory = TDirectory::CurrentDirectory();
67
68 d1->cd(); // change into the directory
69
70 // get list of keys for file 1 & loop over
71 TList* d1k = d1->GetListOfKeys();
72 for (int i = 0; i < d1k->GetEntries(); ++i)
73 {
74 TObject* keyObject = d1k->At(i); // key object in list of keys
75 TObject* d1o = d1->Get(keyObject->GetName()); // object in file
76
77 std::string objectName = std::string(keyObject->GetName());
78 std::string className = std::string(d1o->ClassName());
79
80 // get storePrimaries from options tree.
81 if (objectName == "Options" && className == "TTree")
82 {
83 std::vector<const char*> names;
84 TTree* options = (TTree*) d1->Get(objectName.c_str());
85 Options* optLocal = new Options();
86 optLocal->SetBranchAddress(options);
87 options->GetEntry(0);
88 hasPrimaries = optLocal->options->storePrimaries;
89 delete optLocal; // delete - no need to store in memory.
90 }
91
92 if (className == "TDirectory" || className == "TDirectoryFile") // recursion!
93 {
94 TDirectory* subD1 = (TDirectory*)d1->Get(objectName.c_str());
95 TDirectory* subD2 = (TDirectory*)d2->Get(objectName.c_str());
96 if (!subD2)
97 {
98 Compare::PrintNoMatching(className, objectName);
99 continue;
100 }
101 Compare::Directories(subD1, subD2, results);
102 }
103 else if(className == "TH1D")
104 {
105 TH1* d1h = (TH1*)d1->Get(objectName.c_str());
106 TH1* d2h = (TH1*)d2->Get(objectName.c_str());
107 if (!d2h)
108 {
109 Compare::PrintNoMatching(className, objectName);
110 continue;
111 }
112 Compare::Histograms(d1h, d2h, results);
113 }
114 else if(className == "TTree")
115 {
116 TTree* d1t = (TTree*)d1->Get(objectName.c_str());
117 TTree* d2t = (TTree*)d2->Get(objectName.c_str());
118 if (!d2t)
119 {
120 Compare::PrintNoMatching(className, objectName);
121 continue;
122 }
123 Compare::Trees(d1t, d2t, results);
124 }
125 else
126 {
127 std::cout << "No comparison written for object named " << objectName
128 << " of type " << className << std::endl;
129 }
130 }
131 originalDirectory->cd();
132}
133
134void Compare::Histograms(TH1* h1, TH1* h2, std::vector<Result*>& results)
135{
136 // Take difference between histograms
138 c->name = h1->GetName();
139 c->tolerance = Compare::Chi2Tolerance;
140 c->objtype = "TH1";
141 c->h1Entries = h1->GetEntries();
142 c->h2Entries = h2->GetEntries();
143 c->h1NXBins = h1->GetNbinsX();
144 c->h2NXBins = h2->GetNbinsX();
145 c->h1XMean = h1->GetMean();
146 c->h2XMean = h2->GetMean();
147 c->h1XRms = h1->GetRMS();
148 c->h2XRms = h2->GetRMS();
149 c->h1Integral= h1->Integral();
150 c->h2Integral= h2->Integral();
151
152 // check for a bad comparison
153 if (c->h1NXBins != c->h2NXBins)
154 {
155 c->passed = false;
156 results.push_back(c);
157 return;
158 }
159
160 c->chi2 = 0.0;
161 int ndof = 0;
162 for (int i=0;i < h1->GetNbinsX(); i++)
163 {
164 // std::cout << h1->GetBinContent(i) << " " << h2->GetBinContent(i) << " " << h1->GetBinError(i) << std::endl;
165 if (h1->GetBinError(i) > 0)
166 {
167 c->chi2 += std::pow(h1->GetBinContent(i)-h2->GetBinContent(i),2)/(std::pow(h1->GetBinError(i),2)+std::pow(h2->GetBinError(i),2));
168 ndof++;
169 }
170 }
171 // chi2 per dof
172 if (!std::isnormal(ndof))
173 {ndof = 1;}
174 c->chi2 /= ndof;
175
176 c->passed = true;
177 if(c->chi2 > Compare::Chi2Tolerance)
178 {c->passed = false;}
179
180 results.push_back(c);
181}
182
183void Compare::Trees(TTree* t1, TTree* t2, std::vector<Result*>& results)
184{
185 std::vector<std::string> treesToIgnore = {"Header", "Model", "Options", "Run", "Beam", "ParticleData"};
186
187 // skip some trees
188 std::string treeName = t1->GetName();
189 if (std::find(treesToIgnore.begin(), treesToIgnore.end(), treeName) != treesToIgnore.end())
190 {return;}
191 else if (!strcmp(treeName.c_str(), "Optics"))
192 {
193 Compare::Optics(t1, t2, results);
194 return;
195 }
196 else if (!strcmp(treeName.c_str(), "Event"))
197 {
198 // We need the sampler names which are in the Model tree. If we have an
199 // event tree, we must have a Model tree too!
200 TDirectory* dir = t1->GetDirectory();
201
202 std::vector<std::string> samplerNames;
203 std::vector<std::string> samplerCNames;
204 std::vector<std::string> samplerSNames;
205
206 TTree* modTree = dynamic_cast<TTree*>(dir->Get("Model"));
207 bool warn = false;
208 if (!modTree)
209 {warn = true;} // shouldn't really happen, but we can't compare the samplers
210 else if (modTree->GetEntries() == 0)
211 {warn = true;}
212 else
213 {
214 Model* mod = new Model();
215 mod->SetBranchAddress(modTree);
216 modTree->GetEntry(0);
217 samplerNames = mod->SamplerNames();
218 samplerCNames = mod->SamplerCNames();
219 samplerSNames = mod->SamplerSNames();
220 delete mod;
221 }
222 if (warn)
223 {std::cout << "Model not stored so can't compare sampler branches." << std::endl;}
224 Compare::EventTree(t1, t2, results, samplerNames, samplerCNames, samplerSNames);
225 return;
226 }
227
228 ResultTree* c = new ResultTree();
229 c->name = treeName;
230 c->objtype = "TTree";
231 c->t1NEntries = (int)t1->GetEntries();
232 c->t2NEntries = (int)t2->GetEntries();
233
234 TObjArray* oa1 = t1->GetListOfBranches();
235 TObjArray* oa2 = t2->GetListOfBranches();
236
237 for(int j = 0; j<oa1->GetSize(); ++j)
238 {// loop over branches
239 bool branchFailed = false;
240 TBranch* b1 = (TBranch*)((*oa1)[j]);
241 TBranch* b2 = (TBranch*)((*oa2)[j]);
242 double t1v = 0;
243 double t2v = 0;
244 b1->SetAddress(&t1v);
245 b2->SetAddress(&t2v);
246 for(int i = 0; i<t1->GetEntries(); ++i)
247 {// loop over entries
248 t1->GetEntry(i);
249 t2->GetEntry(i);
250
251 if(std::abs((t1v - t2v )/t1v) > Compare::TreeTolerance)
252 {
253 c->passed = false;
254 branchFailed = true;
255 }
256 else
257 {c->passed = true;}
258 }
259 if (branchFailed)
260 {c->offendingBranches.emplace_back(std::string(b2->GetName()));}
261 }
262 results.push_back(c);
263}
264
265void Compare::Optics(TTree* t1, TTree* t2, std::vector<Result*>& results)
266{
267 ResultTree* c = new ResultTree();
268 c->name = t1->GetName();
269 c->passed = true; // set default to pass
270 c->objtype = "TTree(Optics)";
271 c->t1NEntries = (int)t1->GetEntries();
272 c->t2NEntries = (int)t2->GetEntries();
273
274 // whether the value of a variable should be >= 0
275 std::map<std::string, bool> positiveValues = {
276 {"Emitt_x" , true},
277 {"Emitt_y" , true},
278 {"Alpha_x" , false},
279 {"Alpha_y" , false},
280 {"Beta_x" , true},
281 {"Beta_x" , true},
282 {"Gamma_x" , true},
283 {"Gamma_y" , true},
284 {"Disp_x" , false},
285 {"Disp_y" , false},
286 {"Disp_xp" , false},
287 {"Disp_yp" , false},
288 {"Mean_x" , false},
289 {"Mean_y" , false},
290 {"Mean_xp" , false},
291 {"Mean_yp" , false},
292 {"S" , false},
293 {"Npart" , true},
294 {"Mean_E" , false},
295 {"Mean_t" , false}
296 };
297
298 // branches we won't compare as they're error branches
299 std::vector<std::string> errorBranches = {
300 "Sigma_Emitt_x", "Sigma_Emitt_y",
301 "Sigma_Alpha_x", "Sigma_Alpha_y",
302 "Sigma_Beta_x", "Sigma_Beta_y",
303 "Sigma_Gamma_x", "Sigma_Gamma_y",
304 "Sigma_Disp_x", "Sigma_Disp_y",
305 "Sigma_Disp_xp", "Sigma_Disp_yp",
306 "Sigma_Mean_x", "Sigma_Mean_y",
307 "Sigma_Sigma_x", "Sigma_Sigma_y",
308 "Sigma_Sigma_xp", "Sigma_Sigma_yp",
309 "Sigma_Mean_E", "Sigma_Sigma_E",
310 "Sigma_Mean_t", "Sigma_Sigma_t"
311 };
312
313 TObjArray *oa1 = t1->GetListOfBranches();
314
315 // loop over branches
316 // for each branch loop over all entries and compare to reference file
317 for (int j = 0; j< oa1->GetSize(); ++j)
318 {
319 TBranch* b1 = (TBranch*)(*oa1)[j];
320 std::string branchName = std::string(b1->GetName());
321
322 bool branchFailed = false;
323 bool shouldBeGTEZero = false;
324
325 // Don't compare an error branch to that in the reference file
326 // as it'll be different (likely reference is at higher statistics).
327 // We use a specific set of vector names because we still want to compare
328 // sigma for some values.
329 // However, we should still check they have +ve, not nan values.
330 if (Compare::IsInVector(branchName, errorBranches))
331 {
332 double t1v = 0;
333 b1->SetAddress(&t1v);
334 for(int i = 0; i<t1->GetEntries(); ++i)
335 {// loop over entries
336 t1->GetEntry(i);
337 // errors (sigmas) should be +ve and finite
338 if (LTZero(t1v) || NanOrInf(t1v))
339 {
340 branchFailed = true;
341 break;
342 }
343 }
344 }
345 else
346 {// not an error branch - compare with reference file
347 // whether this variable should always be >= 0
348 shouldBeGTEZero = positiveValues[branchName];
349
350 std::string errBranchName = "Sigma_" + branchName;
351
352 TBranch* b1err = t1->GetBranch(errBranchName.c_str());
353 TBranch* b2err = t2->GetBranch(errBranchName.c_str());
354 if (!b1err || !b2err)
355 {continue;} // There's no appropriate error branch - don't compare
356
357 TBranch* b2 = t2->GetBranch(branchName.c_str());
358
359 // setup local variables and link to root file
360 double t1v = 0;
361 double t1e = 0;
362 double t2v = 0;
363 double t2e = 0;
364 b1->SetAddress(&t1v);
365 b1err->SetAddress(&t1e);
366 b2->SetAddress(&t2v);
367 b2err->SetAddress(&t2e);
368
369 // loop over all entries and compare
370 for(int i = 0; i<t1->GetEntries(); ++i)
371 {
372 t1->GetEntry(i);
373 t2->GetEntry(i);
374
375 if (!std::isnormal(t1e) || !std::isnormal(t2e))
376 {break;} // skip test when errors are 0
377
378 // check for nans or negative values that shouldn't be there
379 // only check if greater than zero if they should be, otherwise no need to check.
380 bool valueIsGood = true;
381 if (shouldBeGTEZero)
382 {
383 if (!GTEZero(t1v) || !GTEZero(t2v))
384 {valueIsGood = false;}
385 }
386 if (NanOrInf(t1v) || NanOrInf(t2v) || !valueIsGood)
387 {
388 branchFailed = true;
389 std::cout << "Invalid value found for branch \"" << branchName << "\"" << G4endl;
390 std::cout << t1v << " " << t2v << " " << t1e << " " << t2e << " " << std::endl;
391 break; // skip testing rest of branch entries
392 }
393
394 // Here only one entry so ndof = 1
395 double chi2 = std::pow(t1v - t2v, 2) / (std::pow(t1e,2) + std::pow(t2e,2));
396
397 branchFailed = chi2 > Compare::OpticsSimgaTolerance;
398
399 if (branchFailed)
400 {
401 std::cout << t1v << " " << t2v << " " << t1e << " "
402 << t2e << " " << chi2 << std::endl;
403 break; // skip testing rest of branch entries
404 }
405 }
406 }
407
408 // record result
409 if (branchFailed)
410 {
411 std::cout << "Branch was " << branchName << std::endl << std::endl;
412 c->offendingBranches.push_back(branchName);
413 c->passed = false;
414 }
415 }
416 results.push_back(c);
417}
418
419void Compare::EventTree(TTree* t1, TTree* t2, std::vector<Result*>& results,
420 const std::vector<std::string>& samplerNames,
421 const std::vector<std::string>& samplerCNames,
422 const std::vector<std::string>& samplerSNames)
423{
424 ResultEventTree* ret = new ResultEventTree();
425 ret->name = t1->GetName();
426 ret->passed = true; // set default to pass
427 ret->objtype = "TTree(Event)";
428 ret->t1NEntries = (int)t1->GetEntries();
429 ret->t2NEntries = (int)t2->GetEntries();
430
431 // Don't proceed if uneven number of entries of the even tree
432 // ie different number of events
433 if (ret->t1NEntries != ret->t2NEntries)
434 {
435 ret->passed = false;
436 results.push_back(ret);
437 return;
438 }
439
440 // Need to tell Event to process samplers at construction time.
441 G4bool processSamplers = !samplerNames.empty();
442 Event* evtLocal1 = new Event(/*debug=*/false, processSamplers, BDSIM_DATA_VERSION);
443 Event* evtLocal2 = new Event(/*debug=*/false, processSamplers, BDSIM_DATA_VERSION);
444 evtLocal1->SetBranchAddress(t1, &samplerNames, false, nullptr, nullptr, &samplerCNames, &samplerSNames);
445 evtLocal2->SetBranchAddress(t2, &samplerNames, false, nullptr, nullptr, &samplerCNames, &samplerSNames);
446
447 for (long int i = 0; i < (long int)t1->GetEntries(); i++)
448 {
450 re.name = std::to_string(i);
451 re.passed = true; // default true
452 re.objtype = "Event of Event Tree";
453
454#ifdef DEBUGOUTPUT
455 Int_t bytesLoaded1 = t1->GetEntry(i);
456 Int_t bytesLoaded2 = t2->GetEntry(i);
457 std::cout << "Bytes loaded (1) " << bytesLoaded1 << std::endl;
458 std::cout << "Bytes loaded (2) " << bytesLoaded2 << std::endl;
459 std::cout << evtLocal1->GetPrimaries()->n << std::endl;
460 std::cout << evtLocal2->GetPrimaries()->n << std::endl;
461 std::cout << evtLocal1 << " " << evtLocal2 << std::endl;
462 std::cout << evtLocal1->GetPrimaries() << " " << evtLocal2->GetPrimaries() << std::endl;
463#else
464 t1->GetEntry(i);
465 t2->GetEntry(i);
466#endif
467
468 if (hasPrimaries)
469 {Compare::Sampler(evtLocal1->GetPrimaries(), evtLocal2->GetPrimaries(), &re);}
470 for (auto j = 0; j < (int)evtLocal1->Samplers.size(); j++)
471 {
472 Compare::Sampler(evtLocal1->Samplers[j], evtLocal2->Samplers[j], &re);
473 }
474
475 ret->eventResults.push_back(re);
476 if (!re.passed)
477 {
478 ret->passed = false;
479 break;
480 }
481 }
482 results.push_back(ret);
483
484 delete evtLocal1;
485 delete evtLocal2;
486}
487
488#ifdef __ROOTDOUBLE__
489void Compare::Sampler(BDSOutputROOTEventSampler<double>* e1,
491 ResultEvent* re)
492#else
493void Compare::Sampler(BDSOutputROOTEventSampler<float>* e1,
495 ResultEvent* re)
496#endif
497{
498 std::string samplerName = e1->samplerName;
499 if (samplerName == "sampler") // the default name in BDSOutputROOTEventSampler.cc
500 {samplerName = e2->samplerName;} // could still be the default value but worth a try
501 ResultSampler rs(samplerName);
502
503 if (e1->n != e2->n)
504 {rs.passed = false; rs.offendingLeaves.emplace_back("n");}
505 else
506 {
507 // only one z / S entry, so only check once
508 if (Diff(e1->z, e2->z))
509 {rs.passed = false; rs.offendingLeaves.emplace_back("z");}
510 if (Diff(e1->S, e2->S))
511 {rs.passed = false; rs.offendingLeaves.emplace_back("S");}
512
513 for (int i = 0; i < e1->n; i++)
514 {
515 if (Diff(e1->energy, e2->energy, i))
516 {rs.passed = false; rs.offendingLeaves.emplace_back("energy");}
517 if (Diff(e1->x, e2->x, i))
518 {rs.passed = false; rs.offendingLeaves.emplace_back("x");}
519 if (Diff(e1->y, e2->y, i))
520 {rs.passed = false; rs.offendingLeaves.emplace_back("y");}
521 if (Diff(e1->xp, e2->xp, i))
522 {rs.passed = false; rs.offendingLeaves.emplace_back("xp");}
523 if (Diff(e1->yp, e2->yp, i))
524 {rs.passed = false; rs.offendingLeaves.emplace_back("yp");}
525 if (Diff(e1->zp, e2->zp, i))
526 {rs.passed = false; rs.offendingLeaves.emplace_back("zp");}
527 if (Diff(e1->p, e2->p, i))
528 {rs.passed = false; rs.offendingLeaves.emplace_back("p");}
529 if (Diff(e1->T, e2->T, i))
530 {rs.passed = false; rs.offendingLeaves.emplace_back("T");}
531 if (Diff(e1->partID, e2->partID, i))
532 {rs.passed = false; rs.offendingLeaves.emplace_back("partID");}
533 if (Diff(e1->charge, e2->charge, i))
534 {rs.passed = false; rs.offendingLeaves.emplace_back("charge");}
535 if (Diff(e1->isIon, e2->isIon, i))
536 {rs.passed = false; rs.offendingLeaves.emplace_back("isIon");}
537 if (Diff(e1->ionA, e2->ionA, i))
538 {rs.passed = false; rs.offendingLeaves.emplace_back("ionA");}
539 if (Diff(e1->ionZ, e2->ionZ, i))
540 {rs.passed = false; rs.offendingLeaves.emplace_back("ionZ");}
541 if (Diff(e1->nElectrons, e2->nElectrons, i))
542 {rs.passed = false; rs.offendingLeaves.emplace_back("nElectrons");}
543 }
544 }
545
546 // update parent result status
547 if (!rs.passed)
548 {re->passed = false; re->offendingBranches.push_back(samplerName);}
549 re->samplerResults.push_back(rs);
550}
551
552bool Compare::Summarise(const std::vector<Result*>& results)
553{
554 bool allPassed = true;
555 const int titleWidth = 20;
556 const int fullWidth = titleWidth + 22;
557 std::cout << std::endl;
558 std::cout << "N results: " << results.size() << std::endl;
559 std::cout << "Comparison: " << std::setw(titleWidth) << "Object Name" << " "
560 << "Result" << std::endl;
561 std::cout << std::setfill('-') << std::setw(fullWidth) << " " << std::endl;
562 std::cout << std::setfill(' ');
563 for (const auto& result : results)
564 {
565 if (!(result->passed))
566 {
567 allPassed = false;
568 std::cout << *result << std::endl;
569 }
570 else
571 {std::cout << "Comparison: " << std::setw(20) << result->name << " : Passed" << std::endl;}
572 }
573 return allPassed;
574}
575
576void Compare::PrintNoMatching(std::string className, std::string objectName)
577{
578 std::cout << "Comparison file has no " << className << " called " << objectName << ". Skipping" << std::endl;
579}
580
581bool Compare::StringStartsWith(std::string aString, std::string prefix)
582{
583 try
584 {
585 if (aString.compare(0, prefix.length(), prefix) == 0)
586 {return true;}
587 else
588 {return false;}
589 }
590 catch(const std::out_of_range&)
591 {return false;} // if string isn't as long as prefix
592 return false; // for static analysis warning
593}
594
595bool Compare::IsInVector(std::string key, const std::vector<std::string>& vec)
596{
597 return std::find(vec.begin(), vec.end(), key) != vec.end();
598}
Information stored per sampler per event.
std::vector< bool > isIon
These are not filled by default.
std::vector< int > nElectrons
These are not filled by default.
std::vector< int > ionA
These are not filled by default.
std::vector< int > charge
These are not filled by default.
std::vector< int > ionZ
These are not filled by default.
Event loader.
Definition: Event.hh:50
BDSOutputROOTEventSampler< double > * GetPrimaries()
Accessor.
Definition: Event.hh:61
std::vector< BDSOutputROOTEventSampler< double > * > Samplers
Local variable ROOT data is mapped to.
Definition: Event.hh:140
void SetBranchAddress(TTree *t, const RBDS::VectorString *samplerNames=nullptr, bool allBranchesOn=false, const RBDS::VectorString *branchesToTurnOn=nullptr, const RBDS::VectorString *collimatorNamesIn=nullptr, const RBDS::VectorString *samplerCNamesIn=nullptr, const RBDS::VectorString *samplerSNamesIn=nullptr)
Definition: Event.cc:206
Model loader.
Definition: Model.hh:36
void SetBranchAddress(TTree *t, bool allBranchesOn=true, const RBDS::VectorString *branchesToTurnOn=nullptr)
Set the branch addresses to address the contents of the file.
Definition: Model.cc:50
std::vector< std::string > SamplerNames() const
Access all the unique sampler names from the model.
Definition: Model.cc:73
Options loader.
Definition: Options.hh:36
void SetBranchAddress(TTree *t, bool allBranchesOn=true, const RBDS::VectorString *branchesToTurnOn=nullptr)
Set the branch addresses to address the contents of the file.
Definition: Options.cc:43
BDSOutputROOTEventOptions * options
Member that ROOT can map file data to locally.
Definition: Options.hh:48
Result of comparison of all entries in an Event tree in BDSIM output.
Result of comparison of single entry of an Event tree in BDSIM output.
Definition: ResultEvent.hh:35
Result of comparing 2 histograms.
Result of comparison of a sampler branch.
Result of comparing 2 TTrees.
Definition: ResultTree.hh:34
std::string name
Name of object being compared in files.
Definition: Result.hh:53
bool passed
Whether it passed or not.
Definition: Result.hh:55
std::string objtype
Name of class of object being compared in files.
Definition: Result.hh:54
std::vector< Result * > Files(TFile *f1, TFile *f2)
Compare two files.
Definition: Compare.cc:53
void Trees(TTree *t1, TTree *t2, std::vector< Result * > &results)
Compare two TTrees.
Definition: Compare.cc:183
bool Summarise(const std::vector< Result * > &results)
Loop over results and print any failed ones. Returns true if all passed.
bool Diff(const std::vector< T > &v1, std::vector< T > &v2, int i)
Return true if they're different.
Definition: Compare.hh:84
bool hasPrimaries
Bool for checking if primaries were written to file.
Definition: Compare.hh:111
void PrintNoMatching(std::string className, std::string objectName)
Definition: Compare.cc:576
void Histograms(TH1 *h1, TH1 *h2, std::vector< Result * > &results)
Compare two histogams.
Definition: Compare.cc:134
void Directories(TDirectory *d1, TDirectory *d2, std::vector< Result * > &results)
Definition: Compare.cc:61
void Optics(TTree *t1, TTree *t2, std::vector< Result * > &results)
Definition: Compare.cc:265
bool StringStartsWith(std::string aString, std::string prefix)
Check whether a string is prefixed with another string.
Definition: Compare.cc:581