Mantid
Loading...
Searching...
No Matches
CompareWorkspaces.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8
14#include "MantidAPI/Run.h"
15#include "MantidAPI/Sample.h"
16#include "MantidAPI/TableRow.h"
26#include "MantidKernel/Unit.h"
27
28namespace Mantid::Algorithms {
29
30using namespace Mantid::API;
31using namespace Mantid::Kernel;
32using namespace Mantid::DataObjects;
33using namespace Mantid::Geometry;
34using Types::Event::TofEvent;
35
36// Register the algorithm into the AlgorithmFactory
37DECLARE_ALGORITHM(CompareWorkspaces)
38
39namespace {
40
41template <class ET> const std::vector<ET> &getEventVector(const EventList &el);
42
43template <> const std::vector<Types::Event::TofEvent> &getEventVector(const EventList &el) { return el.getEvents(); }
44
45template <> const std::vector<DataObjects::WeightedEvent> &getEventVector(const EventList &el) {
46 return el.getWeightedEvents();
47}
48
49template <> const std::vector<DataObjects::WeightedEventNoTime> &getEventVector(const EventList &el) {
50 return el.getWeightedEventsNoTime();
51}
52
53template <class ET>
54int compareEventLists(Kernel::Logger &logger, const EventList &el1, const EventList &el2, double tolTof,
55 double tolWeight, int64_t tolPulse, bool printdetails, size_t &numdiffpulse, size_t &numdifftof,
56 size_t &numdiffboth, size_t &numdiffweight) {
57
58 // Initialize
59 numdiffpulse = 0;
60 numdifftof = 0;
61 numdiffboth = 0;
62 numdiffweight = 0;
63
64 // Compare event by event including all events
65 const auto &events1 = getEventVector<ET>(el1);
66 const auto &events2 = getEventVector<ET>(el2);
67
68 int returnint = 0;
69 size_t numevents = events1.size();
70 for (size_t i = 0; i < numevents; ++i) {
71 // Compare 2 individual events
72 const auto &e1 = events1[i];
73 const auto &e2 = events2[i];
74
75 bool diffpulse =
76 !withinAbsoluteDifference(e1.pulseTime().totalNanoseconds(), e2.pulseTime().totalNanoseconds(), tolPulse);
77 bool difftof = !withinAbsoluteDifference(e1.tof(), e2.tof(), tolTof);
78 bool diffweight = !withinAbsoluteDifference(e1.weight(), e2.weight(), tolWeight);
79 if (diffpulse && difftof)
80 numdiffboth++;
81 if (diffpulse)
82 numdiffpulse++;
83 if (difftof)
84 numdifftof++;
85 if (diffweight)
86 numdiffweight++;
87
88 bool same = (!diffpulse) && (!difftof) && (!diffweight);
89 if (!same) {
90 returnint += 1;
91 if (printdetails) {
92 std::stringstream outss;
93 outss << "Spectrum ? Event " << i << ": ";
94 if (diffpulse)
95 outss << "Diff-Pulse: " << e1.pulseTime() << " vs. " << e2.pulseTime() << "; ";
96 if (difftof)
97 outss << "Diff-TOF: " << e1.tof() << " vs. " << e2.tof() << ";";
98 if (diffweight)
99 outss << "Diff-Weight: " << e1.weight() << " vs. " << e2.weight() << ";";
100
101 logger.information(outss.str());
102 }
103 }
104 } // End of loop on all events
105
106 // Anything that gets this far is equal within tolerances
107 return returnint;
108}
109} // namespace
110
114 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("Workspace1", "", Direction::Input),
115 "The name of the first input workspace.");
116 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("Workspace2", "", Direction::Input),
117 "The name of the second input workspace.");
118
119 declareProperty("Tolerance", 1e-10, "The maximum amount by which values may differ between the workspaces.");
120
121 declareProperty("CheckUncertainty", true,
122 "Whether to check that the y-value uncertainties (E) match "
123 "(only for matrix workspaces). ");
124 declareProperty("CheckType", true,
125 "Whether to check that the data types "
126 "(Workspace2D vs EventWorkspace) match.");
127 declareProperty("CheckAxes", true, "Whether to check that the axes match.");
128 declareProperty("CheckSpectraMap", true, "Whether to check that the spectra-detector maps match. ");
129 declareProperty("CheckInstrument", true, "Whether to check that the instruments match. ");
130 declareProperty("CheckMasking", true, "Whether to check that the bin masking matches. ");
131
132 // Have this one false by default - the logs are brittle
133 declareProperty("CheckSample", false, "Whether to check that the sample (e.g. logs).");
134
135 declareProperty("ToleranceRelErr", false,
136 "Treat tolerance as relative error rather then the absolute error.\n"
137 "This is only applicable to Matrix workspaces.");
138
139 // Have this one false by default - it can be a lot of printing.
140 declareProperty("CheckAllData", false,
141 "Usually checking data ends when first mismatch occurs. This "
142 "forces algorithm to check all data and print mismatch to "
143 "the debug log.\n"
144 "Very often such logs are huge so making it true should be "
145 "the last option.");
146
147 declareProperty("NaNsEqual", false, "Whether NaN values should compare as equal with other NaN values.");
148
149 declareProperty("NumberMismatchedSpectraToPrint", 1, "Number of mismatched spectra from lowest to be listed. ");
150
151 declareProperty("DetailedPrintIndex", EMPTY_INT(), "Mismatched spectra that will be printed out in details. ");
152
153 declareProperty("Result", false, Direction::Output);
154 declareProperty(std::make_unique<WorkspaceProperty<ITableWorkspace>>("Messages", "compare_msgs", Direction::Output),
155 "TableWorkspace containing messages about any mismatches detected");
156
157 m_messages = std::make_shared<TableWorkspace>();
158 m_messages->addColumn("str", "Message");
159 m_messages->addColumn("str", "Workspace 1");
160 m_messages->addColumn("str", "Workspace 2");
161}
162
166 m_result = true;
167 m_messages->setRowCount(0); // Clear table
168
169 if (g_log.is(Logger::Priority::PRIO_DEBUG))
170 m_parallelComparison = false;
171
172 double const tolerance = getProperty("Tolerance");
173 bool const nanEqual = getProperty("NaNsEqual");
174 if (getProperty("ToleranceRelErr")) {
175 this->m_compare = [tolerance, nanEqual](double const x1, double const x2) -> bool {
177 };
178 } else {
179 this->m_compare = [tolerance, nanEqual](double const x1, double const x2) -> bool {
181 };
182 }
183
184 this->doComparison();
185
186 if (!m_result) {
187 std::string message = m_messages->cell<std::string>(0, 0);
188 g_log.warning() << "The workspaces did not match: " << message << '\n';
189 } else {
190 std::string ws1 = Workspace_const_sptr(getProperty("Workspace1"))->getName();
191 std::string ws2 = Workspace_const_sptr(getProperty("Workspace2"))->getName();
192 g_log.notice() << "The workspaces \"" << ws1 << "\" and \"" << ws2 << "\" matched!\n";
193 }
194
195 setProperty("Result", m_result);
196 setProperty("Messages", m_messages);
197}
198
199//----------------------------------------------------------------------------------------------
207 m_result = true;
208 m_messages->setRowCount(0); // Clear table
209
210 // Get workspaces
211 Workspace_const_sptr w1 = getProperty("Workspace1");
212 Workspace_const_sptr w2 = getProperty("Workspace2");
213
214 // Attempt to cast to WorkspaceGroups (will be nullptr on failure)
215 WorkspaceGroup_const_sptr ws1 = std::dynamic_pointer_cast<const WorkspaceGroup>(w1);
216 WorkspaceGroup_const_sptr ws2 = std::dynamic_pointer_cast<const WorkspaceGroup>(w2);
217
218 if (ws1 && ws2) { // Both are groups
219 processGroups(ws1, ws2);
220 } else if (!ws1 && !ws2) { // Neither are groups (shouldn't happen)
221 m_result = false;
222 throw std::runtime_error("CompareWorkspaces::processGroups - Neither "
223 "input is a WorkspaceGroup. This is a logical "
224 "error in the code.");
225 } else if (!ws1 || !ws2) {
226 recordMismatch("Type mismatch. One workspace is a group, the other is not.");
227 }
228
229 if (m_result && ws1 && ws2) {
230 g_log.notice() << "All workspaces in workspace groups \"" << ws1->getName() << "\" and \"" << ws2->getName()
231 << "\" matched!\n";
232 }
233
234 setProperty("Result", m_result);
235 setProperty("Messages", m_messages);
236
237 return true;
238}
239
240//----------------------------------------------------------------------------------------------
246void CompareWorkspaces::processGroups(const std::shared_ptr<const API::WorkspaceGroup> &groupOne,
247 const std::shared_ptr<const API::WorkspaceGroup> &groupTwo) {
248
249 // Check their sizes
250 const auto totalNum = static_cast<size_t>(groupOne->getNumberOfEntries());
251 if (groupOne->getNumberOfEntries() != groupTwo->getNumberOfEntries()) {
252 recordMismatch("GroupWorkspaces size mismatch.");
253 return;
254 }
255
256 // See if there are any other properties that require setting
257 const std::vector<Property *> &allProps = this->getProperties();
258 std::vector<Property *> nonDefaultProps;
259 nonDefaultProps.reserve(allProps.size());
260 for (auto p : allProps) {
261 const std::string &propName = p->name();
262 // Skip those not set and the input workspaces
263 if (p->isDefault() || propName == "Workspace1" || propName == "Workspace2")
264 continue;
265 nonDefaultProps.emplace_back(p);
266 }
267 const size_t numNonDefault = nonDefaultProps.size();
268
269 const double progressFraction = 1.0 / static_cast<double>(totalNum);
270 std::vector<std::string> namesOne = groupOne->getNames();
271 std::vector<std::string> namesTwo = groupTwo->getNames();
272 for (size_t i = 0; i < totalNum; ++i) {
273 // We should use an algorithm for each so that the output properties are
274 // reset properly
275 Algorithm_sptr checker =
276 this->createChildAlgorithm(this->name(), progressFraction * static_cast<double>(i),
277 progressFraction * static_cast<double>(i + 1), false, this->version());
278 checker->setPropertyValue("Workspace1", namesOne[i]);
279 checker->setPropertyValue("Workspace2", namesTwo[i]);
280 for (size_t j = 0; j < numNonDefault; ++j) {
281 Property const *p = nonDefaultProps[j];
282 checker->setPropertyValue(p->name(), p->value());
283 }
284 checker->execute();
285
286 bool success = checker->getProperty("Result");
287 if (!success) {
288 ITableWorkspace_sptr table = checker->getProperty("Messages");
289 recordMismatch(table->cell<std::string>(0, 0), namesOne[i], namesTwo[i]);
290 }
291 }
292}
293
294//----------------------------------------------------------------------------------------------
299 Workspace_sptr w1 = getProperty("Workspace1");
300 Workspace_sptr w2 = getProperty("Workspace2");
301
302 // ==============================================================================
303 // Peaks workspaces
304 // ==============================================================================
305 if (w1->id() == "PeaksWorkspace" || w2->id() == "PeaksWorkspace") {
306 // Check that both workspaces are the same type
307 PeaksWorkspace_sptr pws1 = std::dynamic_pointer_cast<PeaksWorkspace>(w1);
308 PeaksWorkspace_sptr pws2 = std::dynamic_pointer_cast<PeaksWorkspace>(w2);
309
310 // if any one of the pointer is null, record the error
311 // -- meaning at least one of the input workspace cannot be casted
312 // into peakworkspace
313 if ((pws1 && !pws2) || (!pws1 && pws2)) {
314 recordMismatch("One workspace is a PeaksWorkspace and the other is not.");
315 return;
316 }
317
318 // Check some peak-based stuff when both pointers are not null
319 if (pws1 && pws2) {
320 doPeaksComparison(pws1, pws2);
321 return;
322 }
323 }
324
325 // ==============================================================================
326 // Lean Elastic Peaks workspaces
327 // ==============================================================================
328 if (w1->id() == "LeanElasticPeaksWorkspace" || w2->id() == "LeanElasticPeaksWorkspace") {
329 auto lpws1 = std::dynamic_pointer_cast<LeanElasticPeaksWorkspace>(w1);
330 auto lpws2 = std::dynamic_pointer_cast<LeanElasticPeaksWorkspace>(w2);
331
332 if ((lpws1 && !lpws2) || (!lpws1 && lpws2)) {
333 recordMismatch("One workspace is a LeanElasticPeaksWorkspace and the other is not.");
334 }
335
336 if (lpws1 && lpws2) {
337 doLeanElasticPeaksComparison(lpws1, lpws2);
338 return;
339 }
340 }
341
342 // ==============================================================================
343 // Table workspaces
344 // ==============================================================================
345
346 // Check that both workspaces are the same type
347 auto tws1 = std::dynamic_pointer_cast<const ITableWorkspace>(w1);
348 auto tws2 = std::dynamic_pointer_cast<const ITableWorkspace>(w2);
349 if ((tws1 && !tws2) || (!tws1 && tws2)) {
350 recordMismatch("One workspace is a TableWorkspace and the other is not.");
351 return;
352 }
353 if (tws1 && tws2) {
354 doTableComparison(tws1, tws2);
355 return;
356 }
357
358 // ==============================================================================
359 // MD workspaces
360 // ==============================================================================
361
362 // Check things for IMDEventWorkspaces
363 IMDEventWorkspace_const_sptr mdews1 = std::dynamic_pointer_cast<const IMDEventWorkspace>(w1);
364 IMDEventWorkspace_const_sptr mdews2 = std::dynamic_pointer_cast<const IMDEventWorkspace>(w2);
365 if ((mdews1 && !mdews2) || (!mdews1 && mdews2)) {
366 recordMismatch("One workspace is an IMDEventWorkspace and the other is not.");
367 return;
368 }
369 // Check things for IMDHistoWorkspaces
370 IMDHistoWorkspace_const_sptr mdhws1 = std::dynamic_pointer_cast<const IMDHistoWorkspace>(w1);
371 IMDHistoWorkspace_const_sptr mdhws2 = std::dynamic_pointer_cast<const IMDHistoWorkspace>(w2);
372 if ((mdhws1 && !mdhws2) || (!mdhws1 && mdhws2)) {
373 recordMismatch("One workspace is an IMDHistoWorkspace and the other is not.");
374 return;
375 }
376
377 if (mdhws1 || mdews1) // The '2' workspaces must match because of the checks above
378 {
379 this->doMDComparison(w1, w2);
380 return;
381 }
382
383 // ==============================================================================
384 // Event workspaces
385 // ==============================================================================
386
387 // These casts must succeed or there's a logical problem in the code
388 MatrixWorkspace_const_sptr ws1 = std::dynamic_pointer_cast<const MatrixWorkspace>(w1);
389 MatrixWorkspace_const_sptr ws2 = std::dynamic_pointer_cast<const MatrixWorkspace>(w2);
390
391 EventWorkspace_const_sptr ews1 = std::dynamic_pointer_cast<const EventWorkspace>(ws1);
392 EventWorkspace_const_sptr ews2 = std::dynamic_pointer_cast<const EventWorkspace>(ws2);
393 if (getProperty("CheckType")) {
394 if ((ews1 && !ews2) || (!ews1 && ews2)) {
395 recordMismatch("One workspace is an EventWorkspace and the other is not.");
396 return;
397 } else if (w1 && w2 && (w1->id() != w2->id())) {
398 std::stringstream msg;
399 msg << "Workspace ids do not match: \"" << w1->id() << "\" != \"" << w2->id() << "\"";
400 recordMismatch(msg.str());
401 return;
402 }
403 }
404
405 size_t numhist = ws1->getNumberHistograms();
406
407 // Fewer steps if not events
408 if (ews1 && ews2) {
409 // we have to create the progress before the call to compareEventWorkspaces,
410 // because it uses the m_progress and it will segfault if not created
411 m_progress = std::make_unique<Progress>(this, 0.0, 1.0, numhist * 5);
412 // Compare event lists to see whether 2 event workspaces match each other
413 if (!compareEventWorkspaces(*ews1, *ews2))
414 return;
415 } else {
416 m_progress = std::make_unique<Progress>(this, 0.0, 1.0, numhist * 2);
417 }
418
419 // ==============================================================================
420 // Matrix workspaces (Event & 2D)
421 // ==============================================================================
422
423 // First check the data - always do this
424 if (!checkData(ws1, ws2))
425 return;
426
427 // Now do the other ones if requested. Bail out as soon as we see a failure.
428 m_progress->reportIncrement(numhist / 5, "Axes");
429 if (static_cast<bool>(getProperty("CheckAxes")) && !checkAxes(ws1, ws2))
430 return;
431 m_progress->reportIncrement(numhist / 5, "SpectraMap");
432 if (static_cast<bool>(getProperty("CheckSpectraMap")) && !checkSpectraMap(ws1, ws2))
433 return;
434 m_progress->reportIncrement(numhist / 5, "Instrument");
435 if (static_cast<bool>(getProperty("CheckInstrument")) && !checkInstrument(ws1, ws2))
436 return;
437 m_progress->reportIncrement(numhist / 5, "Masking");
438 if (static_cast<bool>(getProperty("CheckMasking")) && !checkMasking(ws1, ws2))
439 return;
440 m_progress->reportIncrement(numhist / 5, "Sample");
441 if (static_cast<bool>(getProperty("CheckSample"))) {
442 if (!checkSample(ws1->sample(), ws2->sample()))
443 return;
444 if (!checkRunProperties(ws1->run(), ws2->run()))
445 return;
446 }
447}
448
449//------------------------------------------------------------------------------------------------
453 const DataObjects::EventWorkspace &ews2) {
454 bool checkallspectra = getProperty("CheckAllData");
455 int numspec2print = getProperty("NumberMismatchedSpectraToPrint");
456 int wsindex2print = getProperty("DetailedPrintIndex");
457
458 // Compare number of spectra
459 if (ews1.getNumberHistograms() != ews2.getNumberHistograms()) {
460 recordMismatch("Mismatched number of histograms.");
461 return false;
462 }
463
464 if (ews1.getEventType() != ews2.getEventType()) {
465 recordMismatch("Mismatched type of events in the EventWorkspaces.");
466 return false;
467 }
468
469 // why the hell are you called after progress initialisation......... that's
470 // why it segfaults
471 // Both will end up sorted anyway
474
475 if (!m_progress) {
476 throw std::runtime_error("The progress pointer was found to be null!");
477 }
478
479 // Determine the tolerance for "tof" attribute and "weight" of events
480 double toleranceWeight = Tolerance; // Standard tolerance
481 int64_t tolerancePulse = 1;
482 double toleranceTOF = 0.05;
483 if ((ews1.getAxis(0)->unit()->label().ascii() != "microsecond") ||
484 (ews2.getAxis(0)->unit()->label().ascii() != "microsecond")) {
485 g_log.warning() << "Event workspace has unit as " << ews1.getAxis(0)->unit()->label().ascii() << " and "
486 << ews2.getAxis(0)->unit()->label().ascii() << ". Tolerance of TOF is set to 0.05 still. "
487 << "\n";
488 toleranceTOF = 0.05;
489 }
490 g_log.notice() << "TOF Tolerance = " << toleranceTOF << "\n";
491
492 bool mismatchedEvent = false;
493 int mismatchedEventWI = 0;
494
495 size_t numUnequalNumEventsSpectra = 0;
496 size_t numUnequalEvents = 0;
497 size_t numUnequalTOFEvents = 0;
498 size_t numUnequalPulseEvents = 0;
499 size_t numUnequalBothEvents = 0;
500 size_t numUnequalWeights = 0;
501
502 std::vector<int> vec_mismatchedwsindex;
504 for (int i = 0; i < static_cast<int>(ews1.getNumberHistograms()); ++i) {
506 m_progress->report("EventLists");
507 if (!mismatchedEvent || checkallspectra) // This guard will avoid checking unnecessarily
508 {
509 const EventList &el1 = ews1.getSpectrum(i);
510 const EventList &el2 = ews2.getSpectrum(i);
511 bool printdetail = (i == wsindex2print);
512 if (printdetail) {
513 g_log.information() << "Spectrum " << i << " is set to print out in details. "
514 << "\n";
515 }
516
517 if (!el1.equals(el2, toleranceTOF, toleranceWeight, tolerancePulse)) {
518 size_t tempNumTof = 0;
519 size_t tempNumPulses = 0;
520 size_t tempNumBoth = 0;
521 size_t tempNumWeight = 0;
522
523 int tempNumUnequal = 0;
524
525 if (el1.getNumberEvents() != el2.getNumberEvents()) {
526 // Number of events are different
527 tempNumUnequal = -1;
528 } else {
529 tempNumUnequal =
530 compareEventsListInDetails(el1, el2, toleranceTOF, toleranceWeight, tolerancePulse, printdetail,
531 tempNumPulses, tempNumTof, tempNumBoth, tempNumWeight);
532 }
533
534 mismatchedEvent = true;
535 mismatchedEventWI = i;
537 if (tempNumUnequal == -1) {
538 // 2 spectra have different number of events
539 ++numUnequalNumEventsSpectra;
540 } else {
541 // 2 spectra have some events different to each other
542 numUnequalEvents += static_cast<size_t>(tempNumUnequal);
543 numUnequalTOFEvents += tempNumTof;
544 numUnequalPulseEvents += tempNumPulses;
545 numUnequalBothEvents += tempNumBoth;
546 numUnequalWeights += tempNumWeight;
547 }
548
549 vec_mismatchedwsindex.emplace_back(i);
550 } // Parallel critical region
551
552 } // If elist 1 is not equal to elist 2
553 }
555 }
557
558 bool wsmatch;
559 if (mismatchedEvent) {
560 std::ostringstream mess;
561 if (checkallspectra) {
562 if (numUnequalNumEventsSpectra > 0)
563 mess << "Total " << numUnequalNumEventsSpectra << " spectra have different number of events. "
564 << "\n";
565
566 mess << "Total " << numUnequalEvents << " (in " << ews1.getNumberEvents() << ") events are differrent. "
567 << numUnequalTOFEvents << " have different TOF; " << numUnequalPulseEvents << " have different pulse time; "
568 << numUnequalBothEvents << " have different in both TOF and pulse time; " << numUnequalWeights
569 << " have different weights."
570 << "\n";
571
572 mess << "Mismatched event lists include " << vec_mismatchedwsindex.size() << " of "
573 << "total " << ews1.getNumberHistograms() << " spectra. "
574 << "\n";
575
576 std::sort(vec_mismatchedwsindex.begin(), vec_mismatchedwsindex.end());
577 numspec2print = std::min(numspec2print, static_cast<int>(vec_mismatchedwsindex.size()));
578 for (int i = 0; i < numspec2print; ++i) {
579 mess << vec_mismatchedwsindex[i] << ", ";
580 if ((i + 1) % 10 == 0)
581 mess << "\n";
582 }
583 } else {
584 mess << "Quick comparison shows 2 workspaces do not match. "
585 << "First found mismatched event list is at workspace index " << mismatchedEventWI;
586 }
587 recordMismatch(mess.str());
588 wsmatch = false;
589 } else {
590 wsmatch = true;
591 }
592
593 return wsmatch;
594}
595
596//------------------------------------------------------------------------------------------------
605 // Cache a few things for later use
606 const size_t numHists = ws1->getNumberHistograms();
607 bool raggedWorkspace{false};
608 size_t numBins(0UL);
609 try {
610 numBins = ws1->blocksize();
611 } catch (std::length_error &) {
612 raggedWorkspace = true;
613 }
614 const bool histogram = ws1->isHistogramData();
615 const bool checkAllData = getProperty("CheckAllData");
616 const bool checkError = getProperty("CheckUncertainty");
617
618 // First check that the workspace are the same size
619 if (numHists != ws2->getNumberHistograms() ||
620 (raggedWorkspace ? !ws2->isRaggedWorkspace() : numBins != ws2->blocksize())) {
621 recordMismatch("Size mismatch");
622 return false;
623 }
624
625 // Check that both are either histograms or point-like data
626 if (histogram != ws2->isHistogramData()) {
627 recordMismatch("Histogram/point-like mismatch");
628 return false;
629 }
630
631 bool resultBool = true;
632 bool logDebug = g_log.is(Logger::Priority::PRIO_DEBUG);
633
634 // Now check the data itself
635 PARALLEL_FOR_IF(m_parallelComparison && ws1->threadSafe() && ws2->threadSafe())
636 for (long i = 0; i < static_cast<long>(numHists); ++i) {
638 m_progress->report("Histograms");
639
640 if (resultBool || checkAllData) // Avoid checking unnecessarily
641 {
642 // Get references to the current spectrum
643 const auto &X1 = ws1->x(i);
644 const auto &Y1 = ws1->y(i);
645 const auto &E1 = ws1->e(i);
646 const auto &X2 = ws2->x(i);
647 const auto &Y2 = ws2->y(i);
648 const auto &E2 = ws2->e(i);
649
650 if (Y1.size() != Y2.size()) {
651 g_log.debug() << "Spectra " << i << " have different lenghts, " << X1.size() << " vs " << X2.size() << "\n";
652 recordMismatch("Mismatch in spectra length");
653 PARALLEL_CRITICAL(resultBool)
654 resultBool = false;
655 } else {
656
657 for (int j = 0; j < static_cast<int>(Y1.size()); ++j) {
658 bool err = (!m_compare(X1[j], X2[j]) || !m_compare(Y1[j], Y2[j]));
659 // if CheckUncertianty flag is set, also compare the uncertainties
660 // only need to do this if not already a mismatch (err == false)
661 // then, there is a mismatch only if the uncertainties don't match
662 if (checkError && !err)
663 err = !m_compare(E1[j], E2[j]);
664 if (err) {
665 if (logDebug) {
666 g_log.debug() << "Data mismatch at cell (hist#,bin#): (" << i << "," << j << ")\n";
667 g_log.debug() << " Dataset #1 (X,Y,E) = (" << X1[j] << "," << Y1[j] << "," << E1[j] << ")\n";
668 g_log.debug() << " Dataset #2 (X,Y,E) = (" << X2[j] << "," << Y2[j] << "," << E2[j] << ")\n";
669 g_log.debug() << " Difference (X,Y,E) = (" << std::abs(X1[j] - X2[j]) << "," << std::abs(Y1[j] - Y2[j])
670 << "," << std::abs(E1[j] - E2[j]) << ")\n";
671 }
672 PARALLEL_CRITICAL(resultBool)
673 resultBool = false;
674 }
675 }
676
677 // Extra one for histogram data
678 if (histogram && !m_compare(X1.back(), X2.back())) {
679 if (logDebug) {
680 g_log.debug() << " Data ranges mismatch for spectra N: (" << i << ")\n";
681 g_log.debug() << " Last bin ranges (X1_end vs X2_end) = (" << X1.back() << "," << X2.back() << ")\n";
682 }
683 PARALLEL_CRITICAL(resultBool)
684 resultBool = false;
685 }
686 }
687 }
689 }
691
692 if (!resultBool)
693 recordMismatch("Data mismatch");
694 // return result
695 return resultBool;
696}
697
698//------------------------------------------------------------------------------------------------
708 const int numAxes = ws1->axes();
709
710 if (numAxes != ws2->axes()) {
711 recordMismatch("Different numbers of axes");
712 return false;
713 }
714
715 for (int i = 0; i < numAxes; ++i) {
716 std::ostringstream axis_name_ss;
717 axis_name_ss << "Axis " << i;
718 std::string axis_name = axis_name_ss.str();
719
720 const Axis *const ax1 = ws1->getAxis(i);
721 const Axis *const ax2 = ws2->getAxis(i);
722
723 if (ax1->isSpectra() != ax2->isSpectra()) {
724 recordMismatch(axis_name + " type mismatch");
725 return false;
726 }
727
728 if (ax1->title() != ax2->title()) {
729 recordMismatch(axis_name + " title mismatch");
730 return false;
731 }
732
733 Unit_const_sptr ax1_unit = ax1->unit();
734 Unit_const_sptr ax2_unit = ax2->unit();
735
736 if ((ax1_unit == nullptr && ax2_unit != nullptr) || (ax1_unit != nullptr && ax2_unit == nullptr) ||
737 (ax1_unit && ax1_unit->unitID() != ax2_unit->unitID())) {
738 recordMismatch(axis_name + " unit mismatch");
739 return false;
740 }
741
742 // Use Axis's equality operator to check length and values
743 // Don't check spectra axis as that just takes it values from the ISpectrum
744 // (see checkSpectraMap)
745 if (ax1->isNumeric() && ax2->isNumeric()) {
746 const auto *na1 = static_cast<const NumericAxis *>(ax1);
747 const double tolerance = getProperty("Tolerance");
748 if (!na1->equalWithinTolerance(*ax2, tolerance)) {
749 recordMismatch(axis_name + " values mismatch");
750 return false;
751 }
752 } else if (!ax1->isSpectra() && !ax1->operator==(*ax2)) {
753 recordMismatch(axis_name + " values mismatch");
754 return false;
755 }
756 }
757
758 if (ws1->YUnit() != ws2->YUnit()) {
759 g_log.debug() << "YUnit strings : WS1 = " << ws1->YUnit() << " WS2 = " << ws2->YUnit() << "\n";
760 recordMismatch("YUnit mismatch");
761 return false;
762 }
763
764 // Check both have the same distribution flag
765 if (ws1->isDistribution() != ws2->isDistribution()) {
766 g_log.debug() << "Distribution flags: WS1 = " << ws1->isDistribution() << " WS2 = " << ws2->isDistribution()
767 << "\n";
768 recordMismatch("Distribution flag mismatch");
769 return false;
770 }
771
772 // Everything's OK with the axes
773 return true;
774}
775
776//------------------------------------------------------------------------------------------------
783 if (ws1->getNumberHistograms() != ws2->getNumberHistograms()) {
784 recordMismatch("Number of spectra mismatch");
785 return false;
786 }
787
788 for (size_t i = 0; i < ws1->getNumberHistograms(); i++) {
789 const auto &spec1 = ws1->getSpectrum(i);
790 const auto &spec2 = ws2->getSpectrum(i);
791 if (spec1.getSpectrumNo() != spec2.getSpectrumNo()) {
792 recordMismatch("Spectrum number mismatch");
793 return false;
794 }
795 if (spec1.getDetectorIDs().size() != spec2.getDetectorIDs().size()) {
796 std::ostringstream out;
797 out << "Number of detector IDs mismatch: " << spec1.getDetectorIDs().size() << " vs "
798 << spec2.getDetectorIDs().size() << " at workspace index " << i;
799 recordMismatch(out.str());
800 return false;
801 }
802 auto it2 = spec2.getDetectorIDs().cbegin();
803 for (auto it1 = spec1.getDetectorIDs().cbegin(); it1 != spec1.getDetectorIDs().cend(); ++it1, ++it2) {
804 if (*it1 != *it2) {
805 recordMismatch("Detector IDs mismatch");
806 return false;
807 }
808 }
809 }
810
811 // Everything's OK if we get to here
812 return true;
813}
814
815//------------------------------------------------------------------------------------------------
816/* @brief Checks that the instruments match
817 *
818 * @details the following checks are performed:
819 * - instrument name
820 * - positions and rotations of detectors
821 * - mask of detectors
822 * - position of the source and sample
823 * - instrument parameters
824 *
825 * @param ws1 :: the first workspace
826 * @param ws2 :: the second workspace
827 * @retval true The instruments match
828 *
829 * @retval false The instruments do not match
830 */
833 // First check the name matches
834 if (ws1->getInstrument()->getName() != ws2->getInstrument()->getName()) {
835 g_log.debug() << "Instrument names: WS1 = " << ws1->getInstrument()->getName()
836 << " WS2 = " << ws2->getInstrument()->getName() << "\n";
837 recordMismatch("Instrument name mismatch");
838 return false;
839 }
840
841 if (!ws1->detectorInfo().isEquivalent(ws2->detectorInfo())) {
842 recordMismatch("DetectorInfo mismatch (position differences larger than "
843 "1e-9 m or other difference found)");
844 return false;
845 }
846
847 if (!ws1->componentInfo().hasEquivalentSource(ws2->componentInfo())) {
848 recordMismatch("Source mismatch: either one workspace has a source and the "
849 "other does not, or the sources are at different positions");
850 return false;
851 }
852
853 if (!ws1->componentInfo().hasEquivalentSample(ws2->componentInfo())) {
854 recordMismatch("Sample mismatch: either one workspace has a sample and the "
855 "other does not, or the samples are at different positions");
856 return false;
857 }
858
859 const Geometry::ParameterMap &ws1_parmap = ws1->constInstrumentParameters();
860 const Geometry::ParameterMap &ws2_parmap = ws2->constInstrumentParameters();
861
862 const bool checkAllData = getProperty("CheckAllData");
863 auto errorStr = ws1_parmap.diff(ws2_parmap, !checkAllData);
864 if (!errorStr.empty()) {
865 g_log.debug() << "Here information to help understand parameter map differences:\n";
866 g_log.debug() << errorStr;
867 recordMismatch("Instrument ParameterMap mismatch (differences in ordering ignored)");
868 return false;
869 }
870
871 // All OK if we're here
872 return true;
873}
874
875//------------------------------------------------------------------------------------------------
883 const auto numHists = static_cast<int>(ws1->getNumberHistograms());
884
885 for (int i = 0; i < numHists; ++i) {
886 const bool ws1_masks = ws1->hasMaskedBins(i);
887 if (ws1_masks != ws2->hasMaskedBins(i)) {
888 g_log.debug() << "Only one workspace has masked bins for spectrum " << i << "\n";
889 recordMismatch("Masking mismatch");
890 return false;
891 }
892
893 // If there are masked bins, check that they match
894 if (ws1_masks && ws1->maskedBins(i) != ws2->maskedBins(i)) {
895 g_log.debug() << "Mask lists for spectrum " << i << " do not match\n";
896 recordMismatch("Masking mismatch");
897 return false;
898 }
899 }
900
901 // All OK if here
902 return true;
903}
904
905//------------------------------------------------------------------------------------------------
911bool CompareWorkspaces::checkSample(const API::Sample &sample1, const API::Sample &sample2) {
912 std::string const name1 = sample1.getName();
913 std::string const name2 = sample2.getName();
914 if (name1 != name2) {
915 g_log.debug("WS1 sample name: " + name1);
916 g_log.debug("WS2 sample name: " + name2);
917 recordMismatch("Sample name mismatch");
918 return false;
919 }
920 // N.B. Sample shape properties are not currently written out to nexus
921 // processed files, so omit here
922
923 // All OK if here
924 return true;
925}
926
927//------------------------------------------------------------------------------------------------
934 double run1Charge(-1.0);
935 try {
936 run1Charge = run1.getProtonCharge();
937 } catch (Exception::NotFoundError &) {
938 }
939 double run2Charge(-1.0);
940 try {
941 run2Charge = run2.getProtonCharge();
942 } catch (Exception::NotFoundError &) {
943 }
944
945 if (run1Charge != run2Charge) {
946 g_log.debug() << "WS1 proton charge: " << run1Charge << "\n";
947 g_log.debug() << "WS2 proton charge: " << run2Charge << "\n";
948 recordMismatch("Proton charge mismatch");
949 return false;
950 }
951
952 std::vector<Kernel::Property *> ws1logs = run1.getLogData();
953 std::vector<Kernel::Property *> ws2logs = run2.getLogData();
954 // Check that the number of separate logs is the same
955 if (ws1logs.size() != ws2logs.size()) {
956 g_log.debug() << "WS1 number of logs: " << ws1logs.size() << "\n";
957 g_log.debug() << "WS2 number of logs: " << ws2logs.size() << "\n";
958 recordMismatch("Different numbers of logs");
959 return false;
960 } else {
961 // Sort logs by name before one-by-one comparison
962 auto compareNames = [](Kernel::Property const *p1, Kernel::Property const *p2) { return p1->name() < p2->name(); };
963 std::sort(ws1logs.begin(), ws1logs.end(), compareNames);
964 std::sort(ws2logs.begin(), ws2logs.end(), compareNames);
965 for (size_t i = 0; i < ws1logs.size(); ++i) {
966 if (*(ws1logs[i]) != *(ws2logs[i])) {
967 if (g_log.is(Logger::Priority::PRIO_DEBUG)) {
968 g_log.debug("WS1 log entry mismatch: " + ws1logs[i]->name());
969 g_log.debug("WS2 log entry mismatch: " + ws2logs[i]->name());
970 }
971 recordMismatch("Log mismatch");
972 return false;
973 }
974 }
975 }
976 return true;
977}
978
979//------------------------------------------------------------------------------------------------
994int CompareWorkspaces::compareEventsListInDetails(const EventList &el1, const EventList &el2, double tolTof,
995 double tolWeight, int64_t tolPulse, bool printdetails,
996 size_t &numdiffpulse, size_t &numdifftof, size_t &numdiffboth,
997 size_t &numdiffweight) const {
998 // Check
999 if (el1.getNumberEvents() != el2.getNumberEvents())
1000 throw std::runtime_error("compareEventsListInDetails only work on 2 event lists with same "
1001 "number of events.");
1002
1003 switch (el1.getEventType()) {
1004 case EventType::TOF:
1005 return compareEventLists<Types::Event::TofEvent>(g_log, el1, el2, tolTof, tolWeight, tolPulse, printdetails,
1006 numdiffpulse, numdifftof, numdiffboth, numdiffweight);
1007 case EventType::WEIGHTED:
1008 return compareEventLists<DataObjects::WeightedEvent>(g_log, el1, el2, tolTof, tolWeight, tolPulse, printdetails,
1009 numdiffpulse, numdifftof, numdiffboth, numdiffweight);
1010 case EventType::WEIGHTED_NOTIME:
1011 return compareEventLists<DataObjects::WeightedEventNoTime>(g_log, el1, el2, tolTof, tolWeight, tolPulse,
1012 printdetails, numdiffpulse, numdifftof, numdiffboth,
1013 numdiffweight);
1014 default:
1015 throw std::runtime_error("Cannot compare event lists: unknown event type.");
1016 }
1017}
1018
1019//------------------------------------------------------------------------------------------------
1021 // Check some table-based stuff
1022 if (tws1->getNumberPeaks() != tws2->getNumberPeaks()) {
1023 recordMismatch("Mismatched number of rows.");
1024 return;
1025 }
1026 if (tws1->columnCount() != tws2->columnCount()) {
1027 recordMismatch("Mismatched number of columns.");
1028 return;
1029 }
1030
1031 // sort the workspaces before comparing
1032 {
1033 auto sortPeaks = createChildAlgorithm("SortPeaksWorkspace");
1034 sortPeaks->setProperty("InputWorkspace", tws1);
1035 sortPeaks->setProperty("ColumnNameToSortBy", "DSpacing");
1036 sortPeaks->setProperty("SortAscending", true);
1037 sortPeaks->executeAsChildAlg();
1038 IPeaksWorkspace_sptr tmp1 = sortPeaks->getProperty("OutputWorkspace");
1039 tws1 = std::dynamic_pointer_cast<PeaksWorkspace>(tmp1);
1040
1041 sortPeaks = createChildAlgorithm("SortPeaksWorkspace");
1042 sortPeaks->setProperty("InputWorkspace", tws2);
1043 sortPeaks->setProperty("ColumnNameToSortBy", "DSpacing");
1044 sortPeaks->setProperty("SortAscending", true);
1045 sortPeaks->executeAsChildAlg();
1046 IPeaksWorkspace_sptr tmp2 = sortPeaks->getProperty("OutputWorkspace");
1047 tws2 = std::dynamic_pointer_cast<PeaksWorkspace>(tmp2);
1048 }
1049
1050 const bool isRelErr = getProperty("ToleranceRelErr");
1051 const bool checkAllData = getProperty("CheckAllData");
1052 for (int i = 0; i < tws1->getNumberPeaks(); i++) {
1053 const Peak &peak1 = tws1->getPeak(i);
1054 const Peak &peak2 = tws2->getPeak(i);
1055 for (std::size_t j = 0; j < tws1->columnCount(); j++) {
1056 std::shared_ptr<const API::Column> col = tws1->getColumn(j);
1057 std::string name = col->name();
1058 double s1 = 0.0;
1059 double s2 = 0.0;
1060 V3D v1(0, 0, 0);
1061 V3D v2(0, 0, 0);
1062 if (name == "RunNumber") {
1063 s1 = double(peak1.getRunNumber());
1064 s2 = double(peak2.getRunNumber());
1065 } else if (name == "DetId") {
1066 s1 = double(peak1.getDetectorID());
1067 s2 = double(peak2.getDetectorID());
1068 } else if (name == "h") {
1069 s1 = peak1.getH();
1070 s2 = peak2.getH();
1071 } else if (name == "k") {
1072 s1 = peak1.getK();
1073 s2 = peak2.getK();
1074 } else if (name == "l") {
1075 s1 = peak1.getL();
1076 s2 = peak2.getL();
1077 } else if (name == "Wavelength") {
1078 s1 = peak1.getWavelength();
1079 s2 = peak2.getWavelength();
1080 } else if (name == "Energy") {
1081 s1 = peak1.getInitialEnergy();
1082 s2 = peak2.getInitialEnergy();
1083 } else if (name == "TOF") {
1084 s1 = peak1.getTOF();
1085 s2 = peak2.getTOF();
1086 } else if (name == "DSpacing") {
1087 s1 = peak1.getDSpacing();
1088 s2 = peak2.getDSpacing();
1089 } else if (name == "Intens") {
1090 s1 = peak1.getIntensity();
1091 s2 = peak2.getIntensity();
1092 } else if (name == "SigInt") {
1093 s1 = peak1.getSigmaIntensity();
1094 s2 = peak2.getSigmaIntensity();
1095 } else if (name == "BinCount") {
1096 s1 = peak1.getBinCount();
1097 s2 = peak2.getBinCount();
1098 } else if (name == "Row") {
1099 s1 = peak1.getRow();
1100 s2 = peak2.getRow();
1101 } else if (name == "Col") {
1102 s1 = peak1.getCol();
1103 s2 = peak2.getCol();
1104 } else if (name == "IntHKL") {
1105 v1 = peak1.getIntHKL();
1106 v2 = peak2.getIntHKL();
1107 } else if (name == "IntMNP") {
1108 v1 = peak1.getIntMNP();
1109 v2 = peak2.getIntMNP();
1110 } else {
1111 g_log.information() << "Column " << name << " is not compared\n";
1112 }
1113 bool mismatch = false;
1114 if (isRelErr) {
1115 mismatch = !m_compare(s1, s2);
1116 // Q: why should we not also compare the vectors?
1117 } else {
1118 mismatch = !m_compare(s1, s2) || //
1119 !m_compare(v1[0], v2[0]) || //
1120 !m_compare(v1[1], v2[1]) || //
1121 !m_compare(v1[2], v2[2]); //
1122 }
1123 if (mismatch) {
1124 g_log.notice(name);
1125 g_log.notice() << "data mismatch in column name = " << name << "\n"
1126 << "cell (row#, col#): (" << i << "," << j << ")\n"
1127 << "value1 = " << s1 << "\n"
1128 << "value2 = " << s2 << "\n";
1129 recordMismatch("Data mismatch");
1130 if (!checkAllData)
1131 return;
1132 }
1133 }
1134 }
1135}
1136
1137//------------------------------------------------------------------------------------------------
1139 const LeanElasticPeaksWorkspace_sptr &tws2) {
1140 // Check some table-based stuff
1141 if (tws1->getNumberPeaks() != tws2->getNumberPeaks()) {
1142 recordMismatch("Mismatched number of rows.");
1143 return;
1144 }
1145 if (tws1->columnCount() != tws2->columnCount()) {
1146 recordMismatch("Mismatched number of columns.");
1147 return;
1148 }
1149
1150 // sort the workspaces before comparing
1151 auto sortPeaks = createChildAlgorithm("SortPeaksWorkspace");
1152 sortPeaks->setProperty("InputWorkspace", tws1);
1153 sortPeaks->setProperty("ColumnNameToSortBy", "DSpacing");
1154 sortPeaks->setProperty("SortAscending", true);
1155 sortPeaks->executeAsChildAlg();
1156 IPeaksWorkspace_sptr ipws1 = sortPeaks->getProperty("OutputWorkspace");
1157
1158 sortPeaks = createChildAlgorithm("SortPeaksWorkspace");
1159 sortPeaks->setProperty("InputWorkspace", tws2);
1160 sortPeaks->setProperty("ColumnNameToSortBy", "DSpacing");
1161 sortPeaks->setProperty("SortAscending", true);
1162 sortPeaks->executeAsChildAlg();
1163 IPeaksWorkspace_sptr ipws2 = sortPeaks->getProperty("OutputWorkspace");
1164
1165 const double tolerance = getProperty("Tolerance");
1166 const bool isRelErr = getProperty("ToleranceRelErr");
1167 const bool checkAllData = getProperty("CheckAllData");
1168 const bool nanEqual = getProperty("NaNsEqual");
1169 for (int peakIndex = 0; peakIndex < ipws1->getNumberPeaks(); peakIndex++) {
1170 for (std::size_t j = 0; j < ipws1->columnCount(); j++) {
1171 std::shared_ptr<const API::Column> col = ipws1->getColumn(j);
1172 const std::string name = col->name();
1173 double s1 = 0.0;
1174 double s2 = 0.0;
1175 if (name == "RunNumber") {
1176 s1 = double(ipws1->getPeak(peakIndex).getRunNumber());
1177 s2 = double(ipws2->getPeak(peakIndex).getRunNumber());
1178 } else if (name == "h") {
1179 s1 = ipws1->getPeak(peakIndex).getH();
1180 s2 = ipws2->getPeak(peakIndex).getH();
1181 } else if (name == "k") {
1182 s1 = ipws1->getPeak(peakIndex).getK();
1183 s2 = ipws2->getPeak(peakIndex).getK();
1184 } else if (name == "l") {
1185 s1 = ipws1->getPeak(peakIndex).getL();
1186 s2 = ipws2->getPeak(peakIndex).getL();
1187 } else if (name == "Wavelength") {
1188 s1 = ipws1->getPeak(peakIndex).getWavelength();
1189 s2 = ipws2->getPeak(peakIndex).getWavelength();
1190 } else if (name == "DSpacing") {
1191 s1 = ipws1->getPeak(peakIndex).getDSpacing();
1192 s2 = ipws2->getPeak(peakIndex).getDSpacing();
1193 } else if (name == "Intens") {
1194 s1 = ipws1->getPeak(peakIndex).getIntensity();
1195 s2 = ipws2->getPeak(peakIndex).getIntensity();
1196 } else if (name == "SigInt") {
1197 s1 = ipws1->getPeak(peakIndex).getSigmaIntensity();
1198 s2 = ipws2->getPeak(peakIndex).getSigmaIntensity();
1199 } else if (name == "BinCount") {
1200 s1 = ipws1->getPeak(peakIndex).getBinCount();
1201 s2 = ipws2->getPeak(peakIndex).getBinCount();
1202 } else if (name == "QLab") {
1203 V3D q1 = ipws1->getPeak(peakIndex).getQLabFrame();
1204 V3D q2 = ipws2->getPeak(peakIndex).getQLabFrame();
1205 // using s1 here as the diff
1206 for (int i = 0; i < 3; ++i) {
1207 s1 += (q1[i] - q2[i]) * (q1[i] - q2[i]);
1208 }
1209 s1 = std::sqrt(s1);
1210 if (isRelErr) {
1211 // divide diff by avg |Q| and then compare to 0 using absolute tol
1212 s1 /= 0.5 * (q1.norm() + q2.norm());
1213 }
1214 } else if (name == "QSample") {
1215 V3D q1 = ipws1->getPeak(peakIndex).getQSampleFrame();
1216 V3D q2 = ipws2->getPeak(peakIndex).getQSampleFrame();
1217 // using s1 here as the diff
1218 for (int i = 0; i < 3; ++i) {
1219 s1 += (q1[i] - q2[i]) * (q1[i] - q2[i]);
1220 }
1221 s1 = std::sqrt(s1);
1222 if (isRelErr) {
1223 // divide diff by avg |Q| and then compare to 0 using absolute tol
1224 s1 /= 0.5 * (q1.norm() + q2.norm());
1225 }
1226 } else {
1227 g_log.information() << "Column " << name << " is not compared\n";
1228 }
1229 bool mismatch = false;
1230 // Q: why does it not perform the user-specified operation for QLab and QSample?
1231 // if this is not necessary, then
1232 // bool mismatch = !m_compare(s1, s2)
1233 // can replace this if/else, and isRelErr and tolerance can be deleted
1234 if (isRelErr && name != "QLab" && name != "QSample") {
1235 if (!withinRelativeTolerance(s1, s2, tolerance, nanEqual)) {
1236 mismatch = true;
1237 }
1238 } else if (!withinAbsoluteTolerance(s1, s2, tolerance, nanEqual)) {
1239 mismatch = true;
1240 }
1241 if (mismatch) {
1242 g_log.notice(name);
1243 g_log.notice() << "data mismatch in column name = " << name << "\n"
1244 << "cell (row#, col#): (" << peakIndex << "," << j << ")\n"
1245 << "value1 = " << s1 << "\n"
1246 << "value2 = " << s2 << "\n";
1247 recordMismatch("Data mismatch");
1248 if (!checkAllData)
1249 return;
1250 }
1251 }
1252 }
1253}
1254
1255//------------------------------------------------------------------------------------------------
1257 const API::ITableWorkspace_const_sptr &tws2) {
1258 // First the easy things
1259 const auto numCols = tws1->columnCount();
1260 if (numCols != tws2->columnCount()) {
1261 g_log.debug() << "Number of columns mismatch (" << numCols << " vs " << tws2->columnCount() << ")\n";
1262 recordMismatch("Number of columns mismatch");
1263 return;
1264 }
1265 const auto numRows = tws1->rowCount();
1266 if (numRows != tws2->rowCount()) {
1267 g_log.debug() << "Number of rows mismatch (" << numRows << " vs " << tws2->rowCount() << ")\n";
1268 recordMismatch("Number of rows mismatch");
1269 return;
1270 }
1271
1272 for (size_t i = 0; i < numCols; ++i) {
1273 auto c1 = tws1->getColumn(i);
1274 auto c2 = tws2->getColumn(i);
1275
1276 if (c1->name() != c2->name()) {
1277 g_log.debug() << "Column name mismatch at column " << i << " (" << c1->name() << " vs " << c2->name() << ")\n";
1278 recordMismatch("Column name mismatch");
1279 return;
1280 }
1281 if (c1->type() != c2->type()) {
1282 g_log.debug() << "Column type mismatch at column " << i << " (" << c1->type() << " vs " << c2->type() << ")\n";
1283 recordMismatch("Column type mismatch");
1284 return;
1285 }
1286 }
1287
1288 const bool checkAllData = getProperty("CheckAllData");
1289 const bool isRelErr = getProperty("ToleranceRelErr");
1290 const bool nanEqual = getProperty("NaNsEqual");
1291 const double tolerance = getProperty("Tolerance");
1292 bool mismatch;
1293 for (std::size_t i = 0; i < numCols; ++i) {
1294 const auto c1 = tws1->getColumn(i);
1295 const auto c2 = tws2->getColumn(i);
1296
1297 if (isRelErr) {
1298 mismatch = !c1->equalsRelErr(*c2, tolerance, nanEqual);
1299 } else {
1300 mismatch = !c1->equals(*c2, tolerance, nanEqual);
1301 }
1302 if (mismatch) {
1303 g_log.debug() << "Table data mismatch at column " << i << "\n";
1304 for (std::size_t j = 0; j < c1->size(); j++) {
1305 g_log.debug() << "\t" << j << " | " << c1->cell<double>(j) << ", " << c2->cell<double>(j) << "\n";
1306 }
1307 recordMismatch("Table data mismatch");
1308 if (!checkAllData) {
1309 return;
1310 }
1311 }
1312 } // loop over columns
1313}
1314
1315//------------------------------------------------------------------------------------------------
1317 IMDWorkspace_sptr mdws1, mdws2;
1318 mdws1 = std::dynamic_pointer_cast<IMDWorkspace>(w1);
1319 mdws2 = std::dynamic_pointer_cast<IMDWorkspace>(w2);
1320
1321 auto alg = createChildAlgorithm("CompareMDWorkspaces");
1322 alg->setProperty<IMDWorkspace_sptr>("Workspace1", mdws1);
1323 alg->setProperty<IMDWorkspace_sptr>("Workspace2", mdws2);
1324 const double tolerance = getProperty("Tolerance");
1325 alg->setProperty("Tolerance", tolerance);
1326 alg->executeAsChildAlg();
1327 bool doesMatch = alg->getProperty("Equals");
1328 std::string algResult = alg->getProperty("Result");
1329 if (!doesMatch) {
1330 recordMismatch(algResult);
1331 }
1332}
1333
1334//------------------------------------------------------------------------------------------------
1343void CompareWorkspaces::recordMismatch(const std::string &msg, std::string ws1, std::string ws2) {
1344 // Workspace names default to the workspaces currently being compared
1345 if (ws1.empty()) {
1346 Workspace_const_sptr w1 = getProperty("Workspace1");
1347 ws1 = w1->getName();
1348 }
1349 if (ws2.empty()) {
1350 Workspace_const_sptr w2 = getProperty("Workspace2");
1351 ws2 = w2->getName();
1352 }
1353
1354 // Add new row and flag this comparison as a mismatch
1355 TableRow row = m_messages->appendRow();
1356 row << msg << ws1 << ws2;
1357 m_result = false;
1358}
1359
1360//------------------------------------------------------------------------------------------------
1371bool CompareWorkspaces::withinAbsoluteTolerance(double const x1, double const x2, double const atol,
1372 bool const nanEqual) {
1373 if (nanEqual && std::isnan(x1) && std::isnan(x2))
1374 return true;
1375 return Kernel::withinAbsoluteDifference(x1, x2, atol);
1376}
1377
1378//------------------------------------------------------------------------------------------------
1390bool CompareWorkspaces::withinRelativeTolerance(double const x1, double const x2, double const rtol,
1391 bool const nanEqual) {
1392 if (nanEqual && std::isnan(x1) && std::isnan(x2))
1393 return true;
1394 return Kernel::withinRelativeDifference(x1, x2, rtol);
1395}
1396} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
#define PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
#define PARALLEL_CRITICAL(name)
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
double tolerance
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Kernel::Logger & g_log
Definition Algorithm.h:422
const std::vector< Kernel::Property * > & getProperties() const override
Get the list of managed properties.
Class to represent the axis of a workspace.
Definition Axis.h:30
const std::string & title() const
Returns the user-defined title for this axis.
Definition Axis.cpp:20
virtual bool isNumeric() const
Returns true if the axis is numeric.
Definition Axis.h:52
const std::shared_ptr< Kernel::Unit > & unit() const
The unit for this axis.
Definition Axis.cpp:28
virtual bool isSpectra() const
Returns true is the axis is a Spectra axis.
Definition Axis.h:50
Kernel::Property * getLogData(const std::string &name) const
Access a single log entry.
Definition LogManager.h:141
virtual Axis * getAxis(const std::size_t &axisIndex) const
Get a non owning pointer to a workspace axis.
Class to represent a numeric axis of a workspace.
Definition NumericAxis.h:29
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:35
double getProtonCharge() const
Get the proton charge.
Definition Run.cpp:300
This class stores information about the sample used in particular run.
Definition Sample.h:33
const std::string & getName() const
Returns the name of the sample.
Definition Sample.cpp:85
TableRow represents a row in a TableWorkspace.
Definition TableRow.h:39
A property class for workspaces.
Compares two workspaces for equality.
bool compareEventWorkspaces(const DataObjects::EventWorkspace &ews1, const DataObjects::EventWorkspace &ews2)
Check whether 2 event lists are identical.
bool checkInstrument(const API::MatrixWorkspace_const_sptr &ws1, const API::MatrixWorkspace_const_sptr &ws2)
static bool withinAbsoluteTolerance(double x1, double x2, double atol, bool const nanEqual=false)
Function which calculates absolute error between two values and analyses if this error is within the ...
int compareEventsListInDetails(const DataObjects::EventList &el1, const DataObjects::EventList &el2, double tolTof, double tolWeight, int64_t tolPulse, bool printdetails, size_t &numdiffpulse, size_t &numdifftof, size_t &numdiffboth, size_t &numdiffweight) const
Compare 2 EventsList.
bool m_parallelComparison
Variable states if one wants to compare workspaces in parallell.
std::unique_ptr< API::Progress > m_progress
Report progress of comparison.
void init() override
Initialise algorithm.
bool checkSample(const API::Sample &sample1, const API::Sample &sample2)
Checks that the sample matches.
void doPeaksComparison(DataObjects::PeaksWorkspace_sptr tws1, DataObjects::PeaksWorkspace_sptr tws2)
void exec() override
Execute algorithm.
bool m_result
Result of comparison (true if equal, false otherwise)
int version() const override
Algorithm's version for identification.
void doMDComparison(const API::Workspace_sptr &w1, const API::Workspace_sptr &w2)
API::ITableWorkspace_sptr m_messages
Mismatch messages that resulted from comparison.
bool checkRunProperties(const API::Run &run1, const API::Run &run2)
Checks that the Run matches.
bool checkSpectraMap(const API::MatrixWorkspace_const_sptr &ws1, const API::MatrixWorkspace_const_sptr &ws2)
Checks that the spectra maps match.
bool processGroups() override
Process two groups and ensure the Result string is set properly on the final algorithm.
void doLeanElasticPeaksComparison(const DataObjects::LeanElasticPeaksWorkspace_sptr &tws1, const DataObjects::LeanElasticPeaksWorkspace_sptr &tws2)
void doComparison()
CompareWorkspaces::doComparison.
std::function< bool(const double, const double)> m_compare
The comparison method to use (true if equal, false otherwise)
bool checkMasking(const API::MatrixWorkspace_const_sptr &ws1, const API::MatrixWorkspace_const_sptr &ws2)
Checks that the bin masking matches.
const std::string name() const override
Algorithm's name.
void doTableComparison(const API::ITableWorkspace_const_sptr &tws1, const API::ITableWorkspace_const_sptr &tws2)
bool checkData(const API::MatrixWorkspace_const_sptr &ws1, const API::MatrixWorkspace_const_sptr &ws2)
Checks that the data matches.
void recordMismatch(const std::string &msg, std::string ws1="", std::string ws2="")
Records a mismatch in the Messages workspace and sets Result to false.
static bool withinRelativeTolerance(double x1, double x2, double rtol, bool const nanEqual=false)
Function which calculates relative error between two values and analyses if this error is within the ...
bool checkAxes(const API::MatrixWorkspace_const_sptr &ws1, const API::MatrixWorkspace_const_sptr &ws2)
Checks that the axes matches.
Mantid::Kernel::V3D getIntMNP() const override
Return the int MNP vector.
Definition BasePeak.cpp:115
double getL() const override
Get the L index of the peak.
Definition BasePeak.cpp:99
double getIntensity() const override
Return the integrated peak intensity.
Definition BasePeak.cpp:184
double getSigmaIntensity() const override
Return the error on the integrated peak intensity.
Definition BasePeak.cpp:187
double getK() const override
Get the K index of the peak.
Definition BasePeak.cpp:96
int getRunNumber() const override
Return the run number this peak was measured at.
Definition BasePeak.cpp:77
double getH() const override
Get the H index of the peak.
Definition BasePeak.cpp:93
double getBinCount() const override
Return the # of counts in the bin at its peak.
Definition BasePeak.cpp:181
Mantid::Kernel::V3D getIntHKL() const override
Return the int HKL vector.
Definition BasePeak.cpp:112
A class for holding :
Definition EventList.h:57
std::vector< Types::Event::TofEvent > & getEvents()
Return the list of TofEvents contained.
bool equals(const EventList &rhs, const double tolTof, const double tolWeight, const int64_t tolPulse) const
std::size_t getNumberEvents() const override
Return the number of events in the list.
Mantid::API::EventType getEventType() const override
Return the type of Event vector contained within.
std::vector< WeightedEventNoTime > & getWeightedEventsNoTime()
Return the list of WeightedEvent contained.
std::vector< WeightedEvent > & getWeightedEvents()
Return the list of WeightedEvent contained.
This class is intended to fulfill the design specified in <https://github.com/mantidproject/documents...
EventList & getSpectrum(const size_t index) override
Return the underlying ISpectrum ptr at the given workspace index.
std::size_t getNumberHistograms() const override
Get the number of histograms, usually the same as the number of pixels or detectors.
bool threadSafe() const override
Returns true if the EventWorkspace is safe for multithreaded operations.
void sortAll(EventSortType sortType, Mantid::API::Progress *prog) const
Mantid::API::EventType getEventType() const override
Get the EventType of the most-specialized EventList in the workspace.
std::size_t getNumberEvents() const override
The total number of events across all of the spectra.
Structure describing a single-crystal peak.
Definition Peak.h:34
int getCol() const override
For RectangularDetectors only, returns the column (x) of the pixel of the detector or -1 if not found...
Definition Peak.cpp:335
double getDSpacing() const override
Calculate the d-spacing of the peak, in 1/Angstroms
Definition Peak.cpp:426
int getRow() const override
For RectangularDetectors only, returns the row (y) of the pixel of the detector or -1 if not found.
Definition Peak.cpp:326
double getInitialEnergy() const override
Get the initial (incident) neutron energy in meV.
Definition Peak.cpp:705
int getDetectorID() const override
Get the ID of the detector at the center of the peak
Definition Peak.cpp:265
double getWavelength() const override
Calculate the neutron wavelength (in angstroms) at the peak (Note for inelastic scattering - it is th...
Definition Peak.cpp:358
double getTOF() const override
Calculate the time of flight (in microseconds) of the neutrons for this peak, using the geometry of t...
Definition Peak.cpp:373
const std::string diff(const ParameterMap &rhs, const bool &firstDiffOnly=false, const bool relative=false, const double doubleTolerance=Kernel::Tolerance) const
Output information that helps understanding the mismatch between two parameter maps.
Exception for when an item is not found in a collection.
Definition Exception.h:145
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void notice(const std::string &msg)
Logs at notice level.
Definition Logger.cpp:126
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
bool is(int level) const
Returns true if at least the given log level is set.
Definition Logger.cpp:177
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
Base class for properties.
Definition Property.h:94
const std::string & name() const
Get the property's name.
Definition Property.cpp:61
virtual std::string value() const =0
Returns the value of the property as a string.
Class for 3D vectors.
Definition V3D.h:34
double norm() const noexcept
Definition V3D.h:269
std::shared_ptr< const IMDEventWorkspace > IMDEventWorkspace_const_sptr
Shared pointer to Mantid::API::IMDEventWorkspace (const version)
std::shared_ptr< IPeaksWorkspace > IPeaksWorkspace_sptr
shared pointer to Mantid::API::IPeaksWorkspace
std::complex< double > MANTID_API_DLL E1(std::complex< double > z)
Integral for Gamma.
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< const ITableWorkspace > ITableWorkspace_const_sptr
shared pointer to Mantid::API::ITableWorkspace (const version)
std::shared_ptr< const Workspace > Workspace_const_sptr
shared pointer to Mantid::API::Workspace (const version)
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< IMDWorkspace > IMDWorkspace_sptr
Shared pointer to the IMDWorkspace base class.
std::shared_ptr< const WorkspaceGroup > WorkspaceGroup_const_sptr
shared pointer to Mantid::API::WorkspaceGroup, pointer to const version
std::shared_ptr< Algorithm > Algorithm_sptr
Typedef for a shared pointer to an Algorithm.
Definition Algorithm.h:52
std::shared_ptr< const IMDHistoWorkspace > IMDHistoWorkspace_const_sptr
shared pointer to Mantid::API::IMDHistoWorkspace (const version)
std::shared_ptr< LeanElasticPeaksWorkspace > LeanElasticPeaksWorkspace_sptr
Typedef for a shared pointer to a peaks workspace.
std::shared_ptr< const EventWorkspace > EventWorkspace_const_sptr
shared pointer to a const Workspace2D
std::shared_ptr< PeaksWorkspace > PeaksWorkspace_sptr
Typedef for a shared pointer to a peaks workspace.
MANTID_KERNEL_DLL bool withinRelativeDifference(T const x, T const y, S const tolerance)
Test whether x, y are within relative tolerance tol.
std::shared_ptr< const Unit > Unit_const_sptr
Shared pointer to the Unit base class (const version)
Definition Unit.h:196
MANTID_KERNEL_DLL bool withinAbsoluteDifference(T const x, T const y, S const tolerance)
Test whether x, y are within absolute tolerance tol.
constexpr double Tolerance
Standard tolerance value.
Definition Tolerance.h:12
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
Definition EmptyValues.h:24
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54