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