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"
25#include "MantidKernel/Unit.h"
26#include "MantidParallel/Communicator.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 = false;
76 bool difftof = false;
77 bool diffweight = false;
78 if (std::abs(e1.pulseTime().totalNanoseconds() - e2.pulseTime().totalNanoseconds()) > tolPulse) {
79 diffpulse = true;
80 ++numdiffpulse;
81 }
82 if (fabs(e1.tof() - e2.tof()) > tolTof) {
83 difftof = true;
84 ++numdifftof;
85 }
86 if (diffpulse && difftof)
87 ++numdiffboth;
88 if (fabs(e1.weight() - e2.weight()) > tolWeight) {
89 diffweight = true;
90 ++numdiffweight;
91 }
92
93 bool same = (!diffpulse) && (!difftof) && (!diffweight);
94 if (!same) {
95 returnint += 1;
96 if (printdetails) {
97 std::stringstream outss;
98 outss << "Spectrum ? Event " << i << ": ";
99 if (diffpulse)
100 outss << "Diff-Pulse: " << e1.pulseTime() << " vs. " << e2.pulseTime() << "; ";
101 if (difftof)
102 outss << "Diff-TOF: " << e1.tof() << " vs. " << e2.tof() << ";";
103 if (diffweight)
104 outss << "Diff-Weight: " << e1.weight() << " vs. " << e2.weight() << ";";
105
106 logger.information(outss.str());
107 }
108 }
109 } // End of loop on all events
110
111 // Anything that gets this far is equal within tolerances
112 return returnint;
113}
114} // namespace
115
119 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("Workspace1", "", Direction::Input),
120 "The name of the first input workspace.");
121 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("Workspace2", "", Direction::Input),
122 "The name of the second input workspace.");
123
124 declareProperty("Tolerance", 1e-10, "The maximum amount by which values may differ between the workspaces.");
125
126 declareProperty("CheckType", true,
127 "Whether to check that the data types "
128 "(Workspace2D vs EventWorkspace) match.");
129 declareProperty("CheckAxes", true, "Whether to check that the axes match.");
130 declareProperty("CheckSpectraMap", true, "Whether to check that the spectra-detector maps match. ");
131 declareProperty("CheckInstrument", true, "Whether to check that the instruments match. ");
132 declareProperty("CheckMasking", true, "Whether to check that the bin masking matches. ");
133
134 // Have this one false by default - the logs are brittle
135 declareProperty("CheckSample", false, "Whether to check that the sample (e.g. logs).");
136
137 declareProperty("ToleranceRelErr", false,
138 "Treat tolerance as relative error rather then the absolute error.\n"
139 "This is only applicable to Matrix workspaces.");
140
141 // Have this one false by default - it can be a lot of printing.
142 declareProperty("CheckAllData", false,
143 "Usually checking data ends when first mismatch occurs. This "
144 "forces algorithm to check all data and print mismatch to "
145 "the debug log.\n"
146 "Very often such logs are huge so making it true should be "
147 "the last option.");
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 this->doComparison();
173
174 if (!m_result) {
175 std::string message = m_messages->cell<std::string>(0, 0);
176 g_log.warning() << "The workspaces did not match: " << message << '\n';
177 } else {
178 std::string ws1 = Workspace_const_sptr(getProperty("Workspace1"))->getName();
179 std::string ws2 = Workspace_const_sptr(getProperty("Workspace2"))->getName();
180 g_log.notice() << "The workspaces \"" << ws1 << "\" and \"" << ws2 << "\" matched!\n";
181 }
182
183 setProperty("Result", m_result);
184 setProperty("Messages", m_messages);
185}
186
187//----------------------------------------------------------------------------------------------
195 m_result = true;
196 m_messages->setRowCount(0); // Clear table
197
198 // Get workspaces
199 Workspace_const_sptr w1 = getProperty("Workspace1");
200 Workspace_const_sptr w2 = getProperty("Workspace2");
201
202 // Attempt to cast to WorkspaceGroups (will be nullptr on failure)
203 WorkspaceGroup_const_sptr ws1 = std::dynamic_pointer_cast<const WorkspaceGroup>(w1);
204 WorkspaceGroup_const_sptr ws2 = std::dynamic_pointer_cast<const WorkspaceGroup>(w2);
205
206 if (ws1 && ws2) { // Both are groups
207 processGroups(ws1, ws2);
208 } else if (!ws1 && !ws2) { // Neither are groups (shouldn't happen)
209 m_result = false;
210 throw std::runtime_error("CompareWorkspaces::processGroups - Neither "
211 "input is a WorkspaceGroup. This is a logical "
212 "error in the code.");
213 } else if (!ws1 || !ws2) {
214 recordMismatch("Type mismatch. One workspace is a group, the other is not.");
215 }
216
217 if (m_result && ws1 && ws2) {
218 g_log.notice() << "All workspaces in workspace groups \"" << ws1->getName() << "\" and \"" << ws2->getName()
219 << "\" matched!\n";
220 }
221
222 setProperty("Result", m_result);
223 setProperty("Messages", m_messages);
224
225 return true;
226}
227
228//----------------------------------------------------------------------------------------------
234void CompareWorkspaces::processGroups(const std::shared_ptr<const API::WorkspaceGroup> &groupOne,
235 const std::shared_ptr<const API::WorkspaceGroup> &groupTwo) {
236
237 // Check their sizes
238 const auto totalNum = static_cast<size_t>(groupOne->getNumberOfEntries());
239 if (groupOne->getNumberOfEntries() != groupTwo->getNumberOfEntries()) {
240 recordMismatch("GroupWorkspaces size mismatch.");
241 return;
242 }
243
244 // See if there are any other properties that require setting
245 const std::vector<Property *> &allProps = this->getProperties();
246 std::vector<Property *> nonDefaultProps;
247 nonDefaultProps.reserve(allProps.size());
248 for (auto p : allProps) {
249 const std::string &propName = p->name();
250 // Skip those not set and the input workspaces
251 if (p->isDefault() || propName == "Workspace1" || propName == "Workspace2")
252 continue;
253 nonDefaultProps.emplace_back(p);
254 }
255 const size_t numNonDefault = nonDefaultProps.size();
256
257 const double progressFraction = 1.0 / static_cast<double>(totalNum);
258 std::vector<std::string> namesOne = groupOne->getNames();
259 std::vector<std::string> namesTwo = groupTwo->getNames();
260 for (size_t i = 0; i < totalNum; ++i) {
261 // We should use an algorithm for each so that the output properties are
262 // reset properly
263 Algorithm_sptr checker =
264 this->createChildAlgorithm(this->name(), progressFraction * static_cast<double>(i),
265 progressFraction * static_cast<double>(i + 1), false, this->version());
266 checker->setPropertyValue("Workspace1", namesOne[i]);
267 checker->setPropertyValue("Workspace2", namesTwo[i]);
268 for (size_t j = 0; j < numNonDefault; ++j) {
269 Property *p = nonDefaultProps[j];
270 checker->setPropertyValue(p->name(), p->value());
271 }
272 checker->execute();
273
274 bool success = checker->getProperty("Result");
275 if (!success) {
276 ITableWorkspace_sptr table = checker->getProperty("Messages");
277 recordMismatch(table->cell<std::string>(0, 0), namesOne[i], namesTwo[i]);
278 }
279 }
280}
281
282//----------------------------------------------------------------------------------------------
287 Workspace_sptr w1 = getProperty("Workspace1");
288 Workspace_sptr w2 = getProperty("Workspace2");
289
290 // ==============================================================================
291 // Peaks workspaces
292 // ==============================================================================
293 if (w1->id() == "PeaksWorkspace" || w2->id() == "PeaksWorkspace") {
294 // Check that both workspaces are the same type
295 PeaksWorkspace_sptr pws1 = std::dynamic_pointer_cast<PeaksWorkspace>(w1);
296 PeaksWorkspace_sptr pws2 = std::dynamic_pointer_cast<PeaksWorkspace>(w2);
297
298 // if any one of the pointer is null, record the error
299 // -- meaning at least one of the input workspace cannot be casted
300 // into peakworkspace
301 if ((pws1 && !pws2) || (!pws1 && pws2)) {
302 recordMismatch("One workspace is a PeaksWorkspace and the other is not.");
303 return;
304 }
305
306 // Check some peak-based stuff when both pointers are not null
307 if (pws1 && pws2) {
308 doPeaksComparison(pws1, pws2);
309 return;
310 }
311 }
312
313 // ==============================================================================
314 // Lean Elastic Peaks workspaces
315 // ==============================================================================
316 if (w1->id() == "LeanElasticPeaksWorkspace" || w2->id() == "LeanElasticPeaksWorkspace") {
317 auto lpws1 = std::dynamic_pointer_cast<LeanElasticPeaksWorkspace>(w1);
318 auto lpws2 = std::dynamic_pointer_cast<LeanElasticPeaksWorkspace>(w2);
319
320 if ((lpws1 && !lpws2) || (!lpws1 && lpws2)) {
321 recordMismatch("One workspace is a LeanElasticPeaksWorkspace and the other is not.");
322 }
323
324 if (lpws1 && lpws2) {
325 doLeanElasticPeaksComparison(lpws1, lpws2);
326 return;
327 }
328 }
329
330 // ==============================================================================
331 // Table workspaces
332 // ==============================================================================
333
334 // Check that both workspaces are the same type
335 auto tws1 = std::dynamic_pointer_cast<const ITableWorkspace>(w1);
336 auto tws2 = std::dynamic_pointer_cast<const ITableWorkspace>(w2);
337 if ((tws1 && !tws2) || (!tws1 && tws2)) {
338 recordMismatch("One workspace is a TableWorkspace and the other is not.");
339 return;
340 }
341 if (tws1 && tws2) {
342 doTableComparison(tws1, tws2);
343 return;
344 }
345
346 // ==============================================================================
347 // MD workspaces
348 // ==============================================================================
349
350 // Check things for IMDEventWorkspaces
351 IMDEventWorkspace_const_sptr mdews1 = std::dynamic_pointer_cast<const IMDEventWorkspace>(w1);
352 IMDEventWorkspace_const_sptr mdews2 = std::dynamic_pointer_cast<const IMDEventWorkspace>(w2);
353 if ((mdews1 && !mdews2) || (!mdews1 && mdews2)) {
354 recordMismatch("One workspace is an IMDEventWorkspace and the other is not.");
355 return;
356 }
357 // Check things for IMDHistoWorkspaces
358 IMDHistoWorkspace_const_sptr mdhws1 = std::dynamic_pointer_cast<const IMDHistoWorkspace>(w1);
359 IMDHistoWorkspace_const_sptr mdhws2 = std::dynamic_pointer_cast<const IMDHistoWorkspace>(w2);
360 if ((mdhws1 && !mdhws2) || (!mdhws1 && mdhws2)) {
361 recordMismatch("One workspace is an IMDHistoWorkspace and the other is not.");
362 return;
363 }
364
365 if (mdhws1 || mdews1) // The '2' workspaces must match because of the checks above
366 {
367 this->doMDComparison(w1, w2);
368 return;
369 }
370
371 // ==============================================================================
372 // Event workspaces
373 // ==============================================================================
374
375 // These casts must succeed or there's a logical problem in the code
376 MatrixWorkspace_const_sptr ws1 = std::dynamic_pointer_cast<const MatrixWorkspace>(w1);
377 MatrixWorkspace_const_sptr ws2 = std::dynamic_pointer_cast<const MatrixWorkspace>(w2);
378
379 EventWorkspace_const_sptr ews1 = std::dynamic_pointer_cast<const EventWorkspace>(ws1);
380 EventWorkspace_const_sptr ews2 = std::dynamic_pointer_cast<const EventWorkspace>(ws2);
381 if (getProperty("CheckType")) {
382 if ((ews1 && !ews2) || (!ews1 && ews2)) {
383 recordMismatch("One workspace is an EventWorkspace and the other is not.");
384 return;
385 }
386 }
387
388 size_t numhist = ws1->getNumberHistograms();
389
390 // Fewer steps if not events
391 if (ews1 && ews2) {
392 // we have to create the progress before the call to compareEventWorkspaces,
393 // because it uses the m_progress and it will segfault if not created
394 m_progress = std::make_unique<Progress>(this, 0.0, 1.0, numhist * 5);
395 // Compare event lists to see whether 2 event workspaces match each other
396 if (!compareEventWorkspaces(*ews1, *ews2))
397 return;
398 } else {
399 m_progress = std::make_unique<Progress>(this, 0.0, 1.0, numhist * 2);
400 }
401
402 // ==============================================================================
403 // Matrix workspaces (Event & 2D)
404 // ==============================================================================
405
406 // First check the data - always do this
407 if (!checkData(ws1, ws2))
408 return;
409
410 // Now do the other ones if requested. Bail out as soon as we see a failure.
411 m_progress->reportIncrement(numhist / 5, "Axes");
412 if (static_cast<bool>(getProperty("CheckAxes")) && !checkAxes(ws1, ws2))
413 return;
414 m_progress->reportIncrement(numhist / 5, "SpectraMap");
415 if (static_cast<bool>(getProperty("CheckSpectraMap")) && !checkSpectraMap(ws1, ws2))
416 return;
417 m_progress->reportIncrement(numhist / 5, "Instrument");
418 if (static_cast<bool>(getProperty("CheckInstrument")) && !checkInstrument(ws1, ws2))
419 return;
420 m_progress->reportIncrement(numhist / 5, "Masking");
421 if (static_cast<bool>(getProperty("CheckMasking")) && !checkMasking(ws1, ws2))
422 return;
423 m_progress->reportIncrement(numhist / 5, "Sample");
424 if (static_cast<bool>(getProperty("CheckSample"))) {
425 if (!checkSample(ws1->sample(), ws2->sample()))
426 return;
427 if (!checkRunProperties(ws1->run(), ws2->run()))
428 return;
429 }
430}
431
432//------------------------------------------------------------------------------------------------
436 const DataObjects::EventWorkspace &ews2) {
437 bool checkallspectra = getProperty("CheckAllData");
438 int numspec2print = getProperty("NumberMismatchedSpectraToPrint");
439 int wsindex2print = getProperty("DetailedPrintIndex");
440
441 // Compare number of spectra
442 if (ews1.getNumberHistograms() != ews2.getNumberHistograms()) {
443 recordMismatch("Mismatched number of histograms.");
444 return false;
445 }
446
447 if (ews1.getEventType() != ews2.getEventType()) {
448 recordMismatch("Mismatched type of events in the EventWorkspaces.");
449 return false;
450 }
451
452 // why the hell are you called after progress initialisation......... that's
453 // why it segfaults
454 // Both will end up sorted anyway
457
458 if (!m_progress) {
459 throw std::runtime_error("The progress pointer was found to be null!");
460 }
461
462 // Determine the tolerance for "tof" attribute and "weight" of events
463 double toleranceWeight = Tolerance; // Standard tolerance
464 int64_t tolerancePulse = 1;
465 double toleranceTOF = 0.05;
466 if ((ews1.getAxis(0)->unit()->label().ascii() != "microsecond") ||
467 (ews2.getAxis(0)->unit()->label().ascii() != "microsecond")) {
468 g_log.warning() << "Event workspace has unit as " << ews1.getAxis(0)->unit()->label().ascii() << " and "
469 << ews2.getAxis(0)->unit()->label().ascii() << ". Tolerance of TOF is set to 0.05 still. "
470 << "\n";
471 toleranceTOF = 0.05;
472 }
473 g_log.notice() << "TOF Tolerance = " << toleranceTOF << "\n";
474
475 bool mismatchedEvent = false;
476 int mismatchedEventWI = 0;
477
478 size_t numUnequalNumEventsSpectra = 0;
479 size_t numUnequalEvents = 0;
480 size_t numUnequalTOFEvents = 0;
481 size_t numUnequalPulseEvents = 0;
482 size_t numUnequalBothEvents = 0;
483 size_t numUnequalWeights = 0;
484
485 std::vector<int> vec_mismatchedwsindex;
487 for (int i = 0; i < static_cast<int>(ews1.getNumberHistograms()); ++i) {
489 m_progress->report("EventLists");
490 if (!mismatchedEvent || checkallspectra) // This guard will avoid checking unnecessarily
491 {
492 const EventList &el1 = ews1.getSpectrum(i);
493 const EventList &el2 = ews2.getSpectrum(i);
494 bool printdetail = (i == wsindex2print);
495 if (printdetail) {
496 g_log.information() << "Spectrum " << i << " is set to print out in details. "
497 << "\n";
498 }
499
500 if (!el1.equals(el2, toleranceTOF, toleranceWeight, tolerancePulse)) {
501 size_t tempNumTof = 0;
502 size_t tempNumPulses = 0;
503 size_t tempNumBoth = 0;
504 size_t tempNumWeight = 0;
505
506 int tempNumUnequal = 0;
507
508 if (el1.getNumberEvents() != el2.getNumberEvents()) {
509 // Number of events are different
510 tempNumUnequal = -1;
511 } else {
512 tempNumUnequal =
513 compareEventsListInDetails(el1, el2, toleranceTOF, toleranceWeight, tolerancePulse, printdetail,
514 tempNumPulses, tempNumTof, tempNumBoth, tempNumWeight);
515 }
516
517 mismatchedEvent = true;
518 mismatchedEventWI = i;
520 if (tempNumUnequal == -1) {
521 // 2 spectra have different number of events
522 ++numUnequalNumEventsSpectra;
523 } else {
524 // 2 spectra have some events different to each other
525 numUnequalEvents += static_cast<size_t>(tempNumUnequal);
526 numUnequalTOFEvents += tempNumTof;
527 numUnequalPulseEvents += tempNumPulses;
528 numUnequalBothEvents += tempNumBoth;
529 numUnequalWeights += tempNumWeight;
530 }
531
532 vec_mismatchedwsindex.emplace_back(i);
533 } // Parallel critical region
534
535 } // If elist 1 is not equal to elist 2
536 }
538 }
540
541 bool wsmatch;
542 if (mismatchedEvent) {
543 std::ostringstream mess;
544 if (checkallspectra) {
545 if (numUnequalNumEventsSpectra > 0)
546 mess << "Total " << numUnequalNumEventsSpectra << " spectra have different number of events. "
547 << "\n";
548
549 mess << "Total " << numUnequalEvents << " (in " << ews1.getNumberEvents() << ") events are differrent. "
550 << numUnequalTOFEvents << " have different TOF; " << numUnequalPulseEvents << " have different pulse time; "
551 << numUnequalBothEvents << " have different in both TOF and pulse time; " << numUnequalWeights
552 << " have different weights."
553 << "\n";
554
555 mess << "Mismatched event lists include " << vec_mismatchedwsindex.size() << " of "
556 << "total " << ews1.getNumberHistograms() << " spectra. "
557 << "\n";
558
559 std::sort(vec_mismatchedwsindex.begin(), vec_mismatchedwsindex.end());
560 numspec2print = std::min(numspec2print, static_cast<int>(vec_mismatchedwsindex.size()));
561 for (int i = 0; i < numspec2print; ++i) {
562 mess << vec_mismatchedwsindex[i] << ", ";
563 if ((i + 1) % 10 == 0)
564 mess << "\n";
565 }
566 } else {
567 mess << "Quick comparison shows 2 workspaces do not match. "
568 << "First found mismatched event list is at workspace index " << mismatchedEventWI;
569 }
570 recordMismatch(mess.str());
571 wsmatch = false;
572 } else {
573 wsmatch = true;
574 }
575
576 return wsmatch;
577}
578
579//------------------------------------------------------------------------------------------------
588 // Cache a few things for later use
589 const size_t numHists = ws1->getNumberHistograms();
590 const size_t numBins = ws1->blocksize();
591 const bool histogram = ws1->isHistogramData();
592 const bool checkAllData = getProperty("CheckAllData");
593 const bool RelErr = getProperty("ToleranceRelErr");
594
595 // First check that the workspace are the same size
596 if (numHists != ws2->getNumberHistograms() || numBins != ws2->blocksize()) {
597 recordMismatch("Size mismatch");
598 return false;
599 }
600
601 // Check that both are either histograms or point-like data
602 if (histogram != ws2->isHistogramData()) {
603 recordMismatch("Histogram/point-like mismatch");
604 return false;
605 }
606
607 const double tolerance = getProperty("Tolerance");
608 bool resultBool = true;
609
610 // Now check the data itself
611 PARALLEL_FOR_IF(m_parallelComparison && ws1->threadSafe() && ws2->threadSafe())
612 for (long i = 0; i < static_cast<long>(numHists); ++i) {
614 m_progress->report("Histograms");
615
616 if (resultBool || checkAllData) // Avoid checking unnecessarily
617 {
618 // Get references to the current spectrum
619 const auto &X1 = ws1->x(i);
620 const auto &Y1 = ws1->y(i);
621 const auto &E1 = ws1->e(i);
622 const auto &X2 = ws2->x(i);
623 const auto &Y2 = ws2->y(i);
624 const auto &E2 = ws2->e(i);
625
626 for (int j = 0; j < static_cast<int>(numBins); ++j) {
627 bool err;
628 if (RelErr) {
629 err = (relErr(X1[j], X2[j], tolerance) || relErr(Y1[j], Y2[j], tolerance) || relErr(E1[j], E2[j], tolerance));
630 } else
631 err = (std::fabs(X1[j] - X2[j]) > tolerance || std::fabs(Y1[j] - Y2[j]) > tolerance ||
632 std::fabs(E1[j] - E2[j]) > tolerance);
633
634 if (err) {
635 g_log.debug() << "Data mismatch at cell (hist#,bin#): (" << i << "," << j << ")\n";
636 g_log.debug() << " Dataset #1 (X,Y,E) = (" << X1[j] << "," << Y1[j] << "," << E1[j] << ")\n";
637 g_log.debug() << " Dataset #2 (X,Y,E) = (" << X2[j] << "," << Y2[j] << "," << E2[j] << ")\n";
638 g_log.debug() << " Difference (X,Y,E) = (" << std::fabs(X1[j] - X2[j]) << "," << std::fabs(Y1[j] - Y2[j])
639 << "," << std::fabs(E1[j] - E2[j]) << ")\n";
640 PARALLEL_CRITICAL(resultBool)
641 resultBool = false;
642 }
643 }
644
645 // Extra one for histogram data
646 if (histogram && std::fabs(X1.back() - X2.back()) > tolerance) {
647 g_log.debug() << " Data ranges mismatch for spectra N: (" << i << ")\n";
648 g_log.debug() << " Last bin ranges (X1_end vs X2_end) = (" << X1.back() << "," << X2.back() << ")\n";
649 PARALLEL_CRITICAL(resultBool)
650 resultBool = false;
651 }
652 }
654 }
656
657 if (!resultBool)
658 recordMismatch("Data mismatch");
659 // If all is well, return true
660 return resultBool;
661}
662
663//------------------------------------------------------------------------------------------------
673 const int numAxes = ws1->axes();
674
675 if (numAxes != ws2->axes()) {
676 recordMismatch("Different numbers of axes");
677 return false;
678 }
679
680 for (int i = 0; i < numAxes; ++i) {
681 std::ostringstream axis_name_ss;
682 axis_name_ss << "Axis " << i;
683 std::string axis_name = axis_name_ss.str();
684
685 const Axis *const ax1 = ws1->getAxis(i);
686 const Axis *const ax2 = ws2->getAxis(i);
687
688 if (ax1->isSpectra() != ax2->isSpectra()) {
689 recordMismatch(axis_name + " type mismatch");
690 return false;
691 }
692
693 if (ax1->title() != ax2->title()) {
694 recordMismatch(axis_name + " title mismatch");
695 return false;
696 }
697
698 Unit_const_sptr ax1_unit = ax1->unit();
699 Unit_const_sptr ax2_unit = ax2->unit();
700
701 if ((ax1_unit == nullptr && ax2_unit != nullptr) || (ax1_unit != nullptr && ax2_unit == nullptr) ||
702 (ax1_unit && ax1_unit->unitID() != ax2_unit->unitID())) {
703 recordMismatch(axis_name + " unit mismatch");
704 return false;
705 }
706
707 // Use Axis's equality operator to check length and values
708 // Don't check spectra axis as that just takes it values from the ISpectrum
709 // (see checkSpectraMap)
710 if (ax1->isNumeric() && ax2->isNumeric()) {
711 const auto *na1 = static_cast<const NumericAxis *>(ax1);
712 const double tolerance = getProperty("Tolerance");
713 if (!na1->equalWithinTolerance(*ax2, tolerance)) {
714 recordMismatch(axis_name + " values mismatch");
715 return false;
716 }
717 } else if (!ax1->isSpectra() && !ax1->operator==(*ax2)) {
718 recordMismatch(axis_name + " values mismatch");
719 return false;
720 }
721 }
722
723 if (ws1->YUnit() != ws2->YUnit()) {
724 g_log.debug() << "YUnit strings : WS1 = " << ws1->YUnit() << " WS2 = " << ws2->YUnit() << "\n";
725 recordMismatch("YUnit mismatch");
726 return false;
727 }
728
729 // Check both have the same distribution flag
730 if (ws1->isDistribution() != ws2->isDistribution()) {
731 g_log.debug() << "Distribution flags: WS1 = " << ws1->isDistribution() << " WS2 = " << ws2->isDistribution()
732 << "\n";
733 recordMismatch("Distribution flag mismatch");
734 return false;
735 }
736
737 // Everything's OK with the axes
738 return true;
739}
740
741//------------------------------------------------------------------------------------------------
748 if (ws1->getNumberHistograms() != ws2->getNumberHistograms()) {
749 recordMismatch("Number of spectra mismatch");
750 return false;
751 }
752
753 for (size_t i = 0; i < ws1->getNumberHistograms(); i++) {
754 const auto &spec1 = ws1->getSpectrum(i);
755 const auto &spec2 = ws2->getSpectrum(i);
756 if (spec1.getSpectrumNo() != spec2.getSpectrumNo()) {
757 recordMismatch("Spectrum number mismatch");
758 return false;
759 }
760 if (spec1.getDetectorIDs().size() != spec2.getDetectorIDs().size()) {
761 std::ostringstream out;
762 out << "Number of detector IDs mismatch: " << spec1.getDetectorIDs().size() << " vs "
763 << spec2.getDetectorIDs().size() << " at workspace index " << i;
764 recordMismatch(out.str());
765 return false;
766 }
767 auto it2 = spec2.getDetectorIDs().cbegin();
768 for (auto it1 = spec1.getDetectorIDs().cbegin(); it1 != spec1.getDetectorIDs().cend(); ++it1, ++it2) {
769 if (*it1 != *it2) {
770 recordMismatch("Detector IDs mismatch");
771 return false;
772 }
773 }
774 }
775
776 // Everything's OK if we get to here
777 return true;
778}
779
780//------------------------------------------------------------------------------------------------
781/* @brief Checks that the instruments match
782 *
783 * @details the following checks are performed:
784 * - instrument name
785 * - positions and rotations of detectors
786 * - mask of detectors
787 * - position of the source and sample
788 * - instrument parameters
789 *
790 * @param ws1 :: the first workspace
791 * @param ws2 :: the second workspace
792 * @retval true The instruments match
793 *
794 * @retval false The instruments do not match
795 */
798 // First check the name matches
799 if (ws1->getInstrument()->getName() != ws2->getInstrument()->getName()) {
800 g_log.debug() << "Instrument names: WS1 = " << ws1->getInstrument()->getName()
801 << " WS2 = " << ws2->getInstrument()->getName() << "\n";
802 recordMismatch("Instrument name mismatch");
803 return false;
804 }
805
806 if (!ws1->detectorInfo().isEquivalent(ws2->detectorInfo())) {
807 recordMismatch("DetectorInfo mismatch (position differences larger than "
808 "1e-9 m or other difference found)");
809 return false;
810 }
811
812 if (!ws1->componentInfo().hasEquivalentSource(ws2->componentInfo())) {
813 recordMismatch("Source mismatch: either one workspace has a source and the "
814 "other does not, or the sources are at different positions");
815 return false;
816 }
817
818 if (!ws1->componentInfo().hasEquivalentSample(ws2->componentInfo())) {
819 recordMismatch("Sample mismatch: either one workspace has a sample and the "
820 "other does not, or the samples are at different positions");
821 return false;
822 }
823
824 const Geometry::ParameterMap &ws1_parmap = ws1->constInstrumentParameters();
825 const Geometry::ParameterMap &ws2_parmap = ws2->constInstrumentParameters();
826
827 const bool checkAllData = getProperty("CheckAllData");
828 auto errorStr = ws1_parmap.diff(ws2_parmap, !checkAllData);
829 if (!errorStr.empty()) {
830 g_log.debug() << "Here information to help understand parameter map differences:\n";
831 g_log.debug() << errorStr;
832 recordMismatch("Instrument ParameterMap mismatch (differences in ordering ignored)");
833 return false;
834 }
835
836 // All OK if we're here
837 return true;
838}
839
840//------------------------------------------------------------------------------------------------
848 const auto numHists = static_cast<int>(ws1->getNumberHistograms());
849
850 for (int i = 0; i < numHists; ++i) {
851 const bool ws1_masks = ws1->hasMaskedBins(i);
852 if (ws1_masks != ws2->hasMaskedBins(i)) {
853 g_log.debug() << "Only one workspace has masked bins for spectrum " << i << "\n";
854 recordMismatch("Masking mismatch");
855 return false;
856 }
857
858 // If there are masked bins, check that they match
859 if (ws1_masks && ws1->maskedBins(i) != ws2->maskedBins(i)) {
860 g_log.debug() << "Mask lists for spectrum " << i << " do not match\n";
861 recordMismatch("Masking mismatch");
862 return false;
863 }
864 }
865
866 // All OK if here
867 return true;
868}
869
870//------------------------------------------------------------------------------------------------
876bool CompareWorkspaces::checkSample(const API::Sample &sample1, const API::Sample &sample2) {
877 std::string const name1 = sample1.getName();
878 std::string const name2 = sample2.getName();
879 if (name1 != name2) {
880 g_log.debug("WS1 sample name: " + name1);
881 g_log.debug("WS2 sample name: " + name2);
882 recordMismatch("Sample name mismatch");
883 return false;
884 }
885 // N.B. Sample shape properties are not currently written out to nexus
886 // processed files, so omit here
887
888 // All OK if here
889 return true;
890}
891
892//------------------------------------------------------------------------------------------------
899 double run1Charge(-1.0);
900 try {
901 run1Charge = run1.getProtonCharge();
902 } catch (Exception::NotFoundError &) {
903 }
904 double run2Charge(-1.0);
905 try {
906 run2Charge = run2.getProtonCharge();
907 } catch (Exception::NotFoundError &) {
908 }
909
910 if (run1Charge != run2Charge) {
911 g_log.debug() << "WS1 proton charge: " << run1Charge << "\n";
912 g_log.debug() << "WS2 proton charge: " << run2Charge << "\n";
913 recordMismatch("Proton charge mismatch");
914 return false;
915 }
916
917 std::vector<Kernel::Property *> ws1logs = run1.getLogData();
918 std::vector<Kernel::Property *> ws2logs = run2.getLogData();
919 // Check that the number of separate logs is the same
920 if (ws1logs.size() != ws2logs.size()) {
921 g_log.debug() << "WS1 number of logs: " << ws1logs.size() << "\n";
922 g_log.debug() << "WS2 number of logs: " << ws2logs.size() << "\n";
923 recordMismatch("Different numbers of logs");
924 return false;
925 } else {
926 // Sort logs by name before one-by-one comparison
927 auto compareNames = [](Kernel::Property *p1, Kernel::Property *p2) { return p1->name() < p2->name(); };
928 std::sort(ws1logs.begin(), ws1logs.end(), compareNames);
929 std::sort(ws2logs.begin(), ws2logs.end(), compareNames);
930 for (size_t i = 0; i < ws1logs.size(); ++i) {
931 if (*(ws1logs[i]) != *(ws2logs[i])) {
932 if (g_log.is(Logger::Priority::PRIO_DEBUG)) {
933 g_log.debug("WS1 log entry mismatch: " + ws1logs[i]->name());
934 g_log.debug("WS2 log entry mismatch: " + ws2logs[i]->name());
935 }
936 recordMismatch("Log mismatch");
937 return false;
938 }
939 }
940 }
941 return true;
942}
943
944//------------------------------------------------------------------------------------------------
959int CompareWorkspaces::compareEventsListInDetails(const EventList &el1, const EventList &el2, double tolTof,
960 double tolWeight, int64_t tolPulse, bool printdetails,
961 size_t &numdiffpulse, size_t &numdifftof, size_t &numdiffboth,
962 size_t &numdiffweight) const {
963 // Check
964 if (el1.getNumberEvents() != el2.getNumberEvents())
965 throw std::runtime_error("compareEventsListInDetails only work on 2 event lists with same "
966 "number of events.");
967
968 switch (el1.getEventType()) {
969 case EventType::TOF:
970 return compareEventLists<Types::Event::TofEvent>(g_log, el1, el2, tolTof, tolWeight, tolPulse, printdetails,
971 numdiffpulse, numdifftof, numdiffboth, numdiffweight);
972 case EventType::WEIGHTED:
973 return compareEventLists<DataObjects::WeightedEvent>(g_log, el1, el2, tolTof, tolWeight, tolPulse, printdetails,
974 numdiffpulse, numdifftof, numdiffboth, numdiffweight);
975 case EventType::WEIGHTED_NOTIME:
976 return compareEventLists<DataObjects::WeightedEventNoTime>(g_log, el1, el2, tolTof, tolWeight, tolPulse,
977 printdetails, numdiffpulse, numdifftof, numdiffboth,
978 numdiffweight);
979 default:
980 throw std::runtime_error("Cannot compare event lists: unknown event type.");
981 }
982}
983
984//------------------------------------------------------------------------------------------------
986 // Check some table-based stuff
987 if (tws1->getNumberPeaks() != tws2->getNumberPeaks()) {
988 recordMismatch("Mismatched number of rows.");
989 return;
990 }
991 if (tws1->columnCount() != tws2->columnCount()) {
992 recordMismatch("Mismatched number of columns.");
993 return;
994 }
995
996 // sort the workspaces before comparing
997 {
998 auto sortPeaks = createChildAlgorithm("SortPeaksWorkspace");
999 sortPeaks->setProperty("InputWorkspace", tws1);
1000 sortPeaks->setProperty("ColumnNameToSortBy", "DSpacing");
1001 sortPeaks->setProperty("SortAscending", true);
1002 sortPeaks->executeAsChildAlg();
1003 IPeaksWorkspace_sptr tmp1 = sortPeaks->getProperty("OutputWorkspace");
1004 tws1 = std::dynamic_pointer_cast<PeaksWorkspace>(tmp1);
1005
1006 sortPeaks = createChildAlgorithm("SortPeaksWorkspace");
1007 sortPeaks->setProperty("InputWorkspace", tws2);
1008 sortPeaks->setProperty("ColumnNameToSortBy", "DSpacing");
1009 sortPeaks->setProperty("SortAscending", true);
1010 sortPeaks->executeAsChildAlg();
1011 IPeaksWorkspace_sptr tmp2 = sortPeaks->getProperty("OutputWorkspace");
1012 tws2 = std::dynamic_pointer_cast<PeaksWorkspace>(tmp2);
1013 }
1014
1015 const double tolerance = getProperty("Tolerance");
1016 const bool isRelErr = getProperty("ToleranceRelErr");
1017 for (int i = 0; i < tws1->getNumberPeaks(); i++) {
1018 const Peak &peak1 = tws1->getPeak(i);
1019 const Peak &peak2 = tws2->getPeak(i);
1020 for (size_t j = 0; j < tws1->columnCount(); j++) {
1021 std::shared_ptr<const API::Column> col = tws1->getColumn(j);
1022 std::string name = col->name();
1023 double s1 = 0.0;
1024 double s2 = 0.0;
1025 if (name == "RunNumber") {
1026 s1 = double(peak1.getRunNumber());
1027 s2 = double(peak2.getRunNumber());
1028 } else if (name == "DetId") {
1029 s1 = double(peak1.getDetectorID());
1030 s2 = double(peak2.getDetectorID());
1031 } else if (name == "h") {
1032 s1 = peak1.getH();
1033 s2 = peak2.getH();
1034 } else if (name == "k") {
1035 s1 = peak1.getK();
1036 s2 = peak2.getK();
1037 } else if (name == "l") {
1038 s1 = peak1.getL();
1039 s2 = peak2.getL();
1040 } else if (name == "Wavelength") {
1041 s1 = peak1.getWavelength();
1042 s2 = peak2.getWavelength();
1043 } else if (name == "Energy") {
1044 s1 = peak1.getInitialEnergy();
1045 s2 = peak2.getInitialEnergy();
1046 } else if (name == "TOF") {
1047 s1 = peak1.getTOF();
1048 s2 = peak2.getTOF();
1049 } else if (name == "DSpacing") {
1050 s1 = peak1.getDSpacing();
1051 s2 = peak2.getDSpacing();
1052 } else if (name == "Intens") {
1053 s1 = peak1.getIntensity();
1054 s2 = peak2.getIntensity();
1055 } else if (name == "SigInt") {
1056 s1 = peak1.getSigmaIntensity();
1057 s2 = peak2.getSigmaIntensity();
1058 } else if (name == "BinCount") {
1059 s1 = peak1.getBinCount();
1060 s2 = peak2.getBinCount();
1061 } else if (name == "Row") {
1062 s1 = peak1.getRow();
1063 s2 = peak2.getRow();
1064 } else if (name == "Col") {
1065 s1 = peak1.getCol();
1066 s2 = peak2.getCol();
1067 } else {
1068 g_log.information() << "Column " << name << " is not compared\n";
1069 }
1070 bool mismatch = false;
1071 if (isRelErr) {
1072 if (relErr(s1, s2, tolerance)) {
1073 mismatch = true;
1074 }
1075 } else if (std::fabs(s1 - s2) > tolerance) {
1076 mismatch = true;
1077 }
1078 if (mismatch) {
1079 g_log.notice(name);
1080 g_log.notice() << "data mismatch in column name = " << name << "\n"
1081 << "cell (row#, col#): (" << i << "," << j << ")\n"
1082 << "value1 = " << s1 << "\n"
1083 << "value2 = " << s2 << "\n";
1084 recordMismatch("Data mismatch");
1085 return;
1086 }
1087 }
1088 }
1089}
1090
1091//------------------------------------------------------------------------------------------------
1093 const LeanElasticPeaksWorkspace_sptr &tws2) {
1094 // Check some table-based stuff
1095 if (tws1->getNumberPeaks() != tws2->getNumberPeaks()) {
1096 recordMismatch("Mismatched number of rows.");
1097 return;
1098 }
1099 if (tws1->columnCount() != tws2->columnCount()) {
1100 recordMismatch("Mismatched number of columns.");
1101 return;
1102 }
1103
1104 // sort the workspaces before comparing
1105 auto sortPeaks = createChildAlgorithm("SortPeaksWorkspace");
1106 sortPeaks->setProperty("InputWorkspace", tws1);
1107 sortPeaks->setProperty("ColumnNameToSortBy", "DSpacing");
1108 sortPeaks->setProperty("SortAscending", true);
1109 sortPeaks->executeAsChildAlg();
1110 IPeaksWorkspace_sptr ipws1 = sortPeaks->getProperty("OutputWorkspace");
1111
1112 sortPeaks = createChildAlgorithm("SortPeaksWorkspace");
1113 sortPeaks->setProperty("InputWorkspace", tws2);
1114 sortPeaks->setProperty("ColumnNameToSortBy", "DSpacing");
1115 sortPeaks->setProperty("SortAscending", true);
1116 sortPeaks->executeAsChildAlg();
1117 IPeaksWorkspace_sptr ipws2 = sortPeaks->getProperty("OutputWorkspace");
1118
1119 const double tolerance = getProperty("Tolerance");
1120 const bool isRelErr = getProperty("ToleranceRelErr");
1121 for (int peakIndex = 0; peakIndex < ipws1->getNumberPeaks(); peakIndex++) {
1122 for (size_t j = 0; j < ipws1->columnCount(); j++) {
1123 std::shared_ptr<const API::Column> col = ipws1->getColumn(j);
1124 const std::string name = col->name();
1125 double s1 = 0.0;
1126 double s2 = 0.0;
1127 if (name == "RunNumber") {
1128 s1 = double(ipws1->getPeak(peakIndex).getRunNumber());
1129 s2 = double(ipws2->getPeak(peakIndex).getRunNumber());
1130 } else if (name == "h") {
1131 s1 = ipws1->getPeak(peakIndex).getH();
1132 s2 = ipws2->getPeak(peakIndex).getH();
1133 } else if (name == "k") {
1134 s1 = ipws1->getPeak(peakIndex).getK();
1135 s2 = ipws2->getPeak(peakIndex).getK();
1136 } else if (name == "l") {
1137 s1 = ipws1->getPeak(peakIndex).getL();
1138 s2 = ipws2->getPeak(peakIndex).getL();
1139 } else if (name == "Wavelength") {
1140 s1 = ipws1->getPeak(peakIndex).getWavelength();
1141 s2 = ipws2->getPeak(peakIndex).getWavelength();
1142 } else if (name == "DSpacing") {
1143 s1 = ipws1->getPeak(peakIndex).getDSpacing();
1144 s2 = ipws2->getPeak(peakIndex).getDSpacing();
1145 } else if (name == "Intens") {
1146 s1 = ipws1->getPeak(peakIndex).getIntensity();
1147 s2 = ipws2->getPeak(peakIndex).getIntensity();
1148 } else if (name == "SigInt") {
1149 s1 = ipws1->getPeak(peakIndex).getSigmaIntensity();
1150 s2 = ipws2->getPeak(peakIndex).getSigmaIntensity();
1151 } else if (name == "BinCount") {
1152 s1 = ipws1->getPeak(peakIndex).getBinCount();
1153 s2 = ipws2->getPeak(peakIndex).getBinCount();
1154 } else if (name == "QLab") {
1155 V3D q1 = ipws1->getPeak(peakIndex).getQLabFrame();
1156 V3D q2 = ipws2->getPeak(peakIndex).getQLabFrame();
1157 // using s1 here as the diff
1158 for (int i = 0; i < 3; ++i) {
1159 s1 += (q1[i] - q2[i]) * (q1[i] - q2[i]);
1160 }
1161 s1 = std::sqrt(s1);
1162 if (isRelErr) {
1163 // divide diff by avg |Q| and then compare to 0 using absolute tol
1164 s1 /= 0.5 * (q1.norm() + q2.norm());
1165 }
1166 } else if (name == "QSample") {
1167 V3D q1 = ipws1->getPeak(peakIndex).getQSampleFrame();
1168 V3D q2 = ipws2->getPeak(peakIndex).getQSampleFrame();
1169 // using s1 here as the diff
1170 for (int i = 0; i < 3; ++i) {
1171 s1 += (q1[i] - q2[i]) * (q1[i] - q2[i]);
1172 }
1173 s1 = std::sqrt(s1);
1174 if (isRelErr) {
1175 // divide diff by avg |Q| and then compare to 0 using absolute tol
1176 s1 /= 0.5 * (q1.norm() + q2.norm());
1177 }
1178 } else {
1179 g_log.information() << "Column " << name << " is not compared\n";
1180 }
1181 bool mismatch = false;
1182 if (isRelErr && name != "QLab" && name != "QSample") {
1183 if (relErr(s1, s2, tolerance)) {
1184 mismatch = true;
1185 }
1186 } else if (std::fabs(s1 - s2) > tolerance) {
1187 mismatch = true;
1188 }
1189 if (mismatch) {
1190 g_log.notice(name);
1191 g_log.notice() << "data mismatch in column name = " << name << "\n"
1192 << "cell (row#, col#): (" << peakIndex << "," << j << ")\n"
1193 << "value1 = " << s1 << "\n"
1194 << "value2 = " << s2 << "\n";
1195 recordMismatch("Data mismatch");
1196 return;
1197 }
1198 }
1199 }
1200}
1201
1202//------------------------------------------------------------------------------------------------
1204 const API::ITableWorkspace_const_sptr &tws2) {
1205 // First the easy things
1206 const auto numCols = tws1->columnCount();
1207 if (numCols != tws2->columnCount()) {
1208 g_log.debug() << "Number of columns mismatch (" << numCols << " vs " << tws2->columnCount() << ")\n";
1209 recordMismatch("Number of columns mismatch");
1210 return;
1211 }
1212 const auto numRows = tws1->rowCount();
1213 if (numRows != tws2->rowCount()) {
1214 g_log.debug() << "Number of rows mismatch (" << numRows << " vs " << tws2->rowCount() << ")\n";
1215 recordMismatch("Number of rows mismatch");
1216 return;
1217 }
1218
1219 for (size_t i = 0; i < numCols; ++i) {
1220 auto c1 = tws1->getColumn(i);
1221 auto c2 = tws2->getColumn(i);
1222
1223 if (c1->name() != c2->name()) {
1224 g_log.debug() << "Column name mismatch at column " << i << " (" << c1->name() << " vs " << c2->name() << ")\n";
1225 recordMismatch("Column name mismatch");
1226 return;
1227 }
1228 if (c1->type() != c2->type()) {
1229 g_log.debug() << "Column type mismatch at column " << i << " (" << c1->type() << " vs " << c2->type() << ")\n";
1230 recordMismatch("Column type mismatch");
1231 return;
1232 }
1233 }
1234
1235 const bool checkAllData = getProperty("CheckAllData");
1236 const bool relErr = getProperty("ToleranceRelErr");
1237 const double tolerance = getProperty("Tolerance");
1238 bool mismatch = false;
1239 for (size_t i = 0; i < numCols; ++i) {
1240 const auto c1 = tws1->getColumn(i);
1241 const auto c2 = tws2->getColumn(i);
1242
1243 if (relErr) {
1244 if (!c1->equalsRelErr(*c2, tolerance)) {
1245 mismatch = true;
1246 }
1247 } else {
1248
1249 if (!c1->equals(*c2, tolerance)) {
1250 mismatch = true;
1251 }
1252 }
1253 if (mismatch) {
1254 g_log.debug() << "Table data mismatch at column " << i << "\n";
1255 recordMismatch("Table data mismatch");
1256 mismatch = false;
1257 if (!checkAllData) {
1258 return;
1259 }
1260 }
1261 } // loop over columns
1262}
1263
1264//------------------------------------------------------------------------------------------------
1266 IMDWorkspace_sptr mdws1, mdws2;
1267 mdws1 = std::dynamic_pointer_cast<IMDWorkspace>(w1);
1268 mdws2 = std::dynamic_pointer_cast<IMDWorkspace>(w2);
1269
1270 auto alg = createChildAlgorithm("CompareMDWorkspaces");
1271 alg->setProperty<IMDWorkspace_sptr>("Workspace1", mdws1);
1272 alg->setProperty<IMDWorkspace_sptr>("Workspace2", mdws2);
1273 const double tolerance = getProperty("Tolerance");
1274 alg->setProperty("Tolerance", tolerance);
1275 alg->executeAsChildAlg();
1276 bool doesMatch = alg->getProperty("Equals");
1277 std::string algResult = alg->getProperty("Result");
1278 if (!doesMatch) {
1279 recordMismatch(algResult);
1280 }
1281}
1282
1283//------------------------------------------------------------------------------------------------
1292void CompareWorkspaces::recordMismatch(const std::string &msg, std::string ws1, std::string ws2) {
1293 // Workspace names default to the workspaces currently being compared
1294 if (ws1.empty()) {
1295 Workspace_const_sptr w1 = getProperty("Workspace1");
1296 ws1 = w1->getName();
1297 }
1298 if (ws2.empty()) {
1299 Workspace_const_sptr w2 = getProperty("Workspace2");
1300 ws2 = w2->getName();
1301 }
1302
1303 // Add new row and flag this comparison as a mismatch
1304 TableRow row = m_messages->appendRow();
1305 row << msg << ws1 << ws2;
1306 m_result = false;
1307}
1308
1309//------------------------------------------------------------------------------------------------
1323bool CompareWorkspaces::relErr(double x1, double x2, double errorVal) const {
1324 double num = std::fabs(x1 - x2);
1325 // how to treat x1<0 and x2 > 0 ? probably this way
1326 double den = 0.5 * (std::fabs(x1) + std::fabs(x2));
1327 if (den < errorVal)
1328 return (num > errorVal);
1329
1330 return (num / den > errorVal);
1331}
1332
1333Parallel::ExecutionMode
1334CompareWorkspaces::getParallelExecutionMode(const std::map<std::string, Parallel::StorageMode> &storageModes) const {
1335 using namespace Parallel;
1336 if (storageModes.at("Workspace1") == StorageMode::Cloned) {
1337 if (storageModes.at("Workspace2") == StorageMode::Cloned)
1338 return getCorrespondingExecutionMode(StorageMode::Cloned);
1339 if (storageModes.at("Workspace2") == StorageMode::MasterOnly)
1340 return getCorrespondingExecutionMode(StorageMode::MasterOnly);
1341 }
1342 if (storageModes.at("Workspace1") == StorageMode::MasterOnly) {
1343 if (storageModes.at("Workspace2") != StorageMode::Distributed)
1344 return getCorrespondingExecutionMode(StorageMode::MasterOnly);
1345 }
1346 return ExecutionMode::Invalid;
1347}
1348
1350 if (communicator().rank() == 0)
1351 exec();
1352 else
1353 setProperty("Result", true);
1354}
1355
1356} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
#define fabs(x)
Definition: Matrix.cpp:22
#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...
Definition: MultiThreaded.h:94
#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.
Definition: Algorithm.cpp:1913
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
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.
Definition: Algorithm.cpp:842
Kernel::Logger & g_log
Definition: Algorithm.h:451
const Parallel::Communicator & communicator() const
Returns a const reference to the (MPI) communicator of the algorithm.
Definition: Algorithm.cpp:1870
const std::vector< Kernel::Property * > & getProperties() const override
Get the list of managed properties.
Definition: Algorithm.cpp:2050
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:129
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:38
double getProtonCharge() const
Get the proton charge.
Definition: Run.cpp:182
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:86
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.
void execMasterOnly() override
Runs the algorithm in master-only execution mode.
bool checkInstrument(const API::MatrixWorkspace_const_sptr &ws1, const API::MatrixWorkspace_const_sptr &ws2)
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.
Parallel::ExecutionMode getParallelExecutionMode(const std::map< std::string, Parallel::StorageMode > &storageModes) const override
Get correct execution mode based on input storage modes for an MPI run.
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.
bool relErr(double x1, double x2, double errorVal) const
Function which calculates relative error between two values and analyses if this error is within the ...
void doLeanElasticPeaksComparison(const DataObjects::LeanElasticPeaksWorkspace_sptr &tws1, const DataObjects::LeanElasticPeaksWorkspace_sptr &tws2)
void doComparison()
CompareWorkspaces::doComparison.
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.
bool checkAxes(const API::MatrixWorkspace_const_sptr &ws1, const API::MatrixWorkspace_const_sptr &ws2)
Checks that the axes matches.
double getL() const override
Get the L index of the peak.
Definition: BasePeak.cpp:100
double getIntensity() const override
Return the integrated peak intensity.
Definition: BasePeak.cpp:185
double getSigmaIntensity() const override
Return the error on the integrated peak intensity.
Definition: BasePeak.cpp:188
double getK() const override
Get the K index of the peak.
Definition: BasePeak.cpp:97
int getRunNumber() const override
Return the run number this peak was measured at.
Definition: BasePeak.cpp:78
double getH() const override
Get the H index of the peak.
Definition: BasePeak.cpp:94
double getBinCount() const override
Return the # of counts in the bin at its peak.
Definition: BasePeak.cpp:182
A class for holding :
Definition: EventList.h:56
std::vector< Types::Event::TofEvent > & getEvents()
Return the list of TofEvents contained.
Definition: EventList.cpp:780
bool equals(const EventList &rhs, const double tolTof, const double tolWeight, const int64_t tolPulse) const
Definition: EventList.cpp:602
std::size_t getNumberEvents() const override
Return the number of events in the list.
Definition: EventList.cpp:1143
Mantid::API::EventType getEventType() const override
Return the type of Event vector contained within.
Definition: EventList.cpp:643
std::vector< WeightedEventNoTime > & getWeightedEventsNoTime()
Return the list of WeightedEvent contained.
Definition: EventList.cpp:823
std::vector< WeightedEvent > & getWeightedEvents()
Return the list of WeightedEvent contained.
Definition: EventList.cpp:795
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:341
double getDSpacing() const override
Calculate the d-spacing of the peak, in 1/Angstroms
Definition: Peak.cpp:416
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:332
double getInitialEnergy() const override
Get the initial (incident) neutron energy in meV.
Definition: Peak.cpp:695
double getWavelength() const override
Calculate the neutron wavelength (in angstroms) at the peak (Note for inelastic scattering - it is th...
Definition: Peak.cpp:364
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:379
int getDetectorID() const
Get the ID of the detector at the center of the peak
Definition: Peak.cpp:271
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:52
void debug(const std::string &msg)
Logs at debug level.
Definition: Logger.cpp:114
void notice(const std::string &msg)
Logs at notice level.
Definition: Logger.cpp:95
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
bool is(int level) const
Returns true if at least the given log level is set.
Definition: Logger.cpp:146
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
Base class for properties.
Definition: Property.h:94
const std::string & name() const
Get the property's name.
Definition: Property.cpp:60
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:263
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
Definition: Workspace_fwd.h:20
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)
Definition: Workspace_fwd.h:22
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.
Definition: IMDWorkspace.h:146
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:61
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.
std::shared_ptr< const Unit > Unit_const_sptr
Shared pointer to the Unit base class (const version)
Definition: Unit.h:231
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:25
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54