PeakTest.h 21.5 KB
Newer Older
1
2
3
4
5
6
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
//     NScD Oak Ridge National Laboratory, European Spallation Source
//     & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
7
8
9
#ifndef MANTID_DATAOBJECTS_PEAKTEST_H_
#define MANTID_DATAOBJECTS_PEAKTEST_H_

LamarMoore's avatar
LamarMoore committed
10
11
#include "MantidGeometry/Instrument/ReferenceFrame.h"
#include "MantidKernel/PhysicalConstants.h"
12
#include "MantidKernel/System.h"
LamarMoore's avatar
LamarMoore committed
13
#include "MantidKernel/Timer.h"
14
#include "MantidKernel/Unit.h"
LamarMoore's avatar
LamarMoore committed
15
#include "MantidKernel/UnitFactory.h"
16
#include "MantidKernel/V3D.h"
17

LamarMoore's avatar
LamarMoore committed
18
19
#include "MockObjects.h"
#include <cxxtest/TestSuite.h>
20
#include <gmock/gmock.h>
21
22

#include "MantidDataObjects/Peak.h"
23
#include "MantidTestHelpers/ComponentCreationHelper.h"
24
25

using namespace Mantid::DataObjects;
26
27
using namespace Mantid::Geometry;
using namespace Mantid::Kernel;
28

Hahn, Steven's avatar
Hahn, Steven committed
29
30
31
32
33
34
35
36
37
namespace boost {
template <class CharType, class CharTrait>
std::basic_ostream<CharType, CharTrait> &
operator<<(std::basic_ostream<CharType, CharTrait> &out,
           optional<double> const &maybe) {
  if (maybe)
    out << maybe;
  return out;
}
LamarMoore's avatar
LamarMoore committed
38
} // namespace boost
Hahn, Steven's avatar
Hahn, Steven committed
39

40
class PeakTest : public CxxTest::TestSuite {
41
private:
42
43
44
  /// Common instrument
  Instrument_sptr inst;
  Instrument_sptr m_minimalInstrument;
45

46
public:
47
48
  // This pair of boilerplate methods prevent the suite being created statically
  // This means the constructor isn't called when running other tests
49
  static PeakTest *createSuite() { return new PeakTest(); }
50
51
52
  static void destroySuite(PeakTest *suite) { delete suite; }

  // Constructor
53
54
  PeakTest()
      : inst(ComponentCreationHelper::createTestInstrumentRectangular(5, 100)) {
55

56
57
  }

58
  void test_constructor() {
59
60
61
62
63
64
    // detector IDs start at 10000
    Peak p(inst, 10000, 2.0);
    TS_ASSERT_DELTA(p.getH(), 0.0, 1e-5)
    TS_ASSERT_DELTA(p.getK(), 0.0, 1e-5)
    TS_ASSERT_DELTA(p.getL(), 0.0, 1e-5)
    TS_ASSERT_EQUALS(p.getDetectorID(), 10000)
65
66
    TS_ASSERT_EQUALS(p.getDetector()->getID(), 10000)
    TS_ASSERT_EQUALS(p.getInstrument(), inst)
67
    check_Contributing_Detectors(p, std::vector<int>(1, 10000));
68
69
  }

70
  void test_constructorHKL() {
71
    // detector IDs start at 10000
72
    Peak p(inst, 10000, 2.0, V3D(1, 2, 3));
73
74
75
76
77
78
    TS_ASSERT_DELTA(p.getH(), 1.0, 1e-5)
    TS_ASSERT_DELTA(p.getK(), 2.0, 1e-5)
    TS_ASSERT_DELTA(p.getL(), 3.0, 1e-5)
    TS_ASSERT_EQUALS(p.getDetectorID(), 10000)
    TS_ASSERT_EQUALS(p.getDetector()->getID(), 10000)
    TS_ASSERT_EQUALS(p.getInstrument(), inst)
79
    check_Contributing_Detectors(p, std::vector<int>(1, 10000));
80
81
  }

82
83
84
85
86
87
88
89
90
  void test_constructorHKLGon() {
    Matrix<double> mats(3, 3), mat(3, 3);
    for (int x = 0; x < 3; x++)
      for (int y = 0; y < 3; y++)
        mats[x][y] = 1.0 * x + 1.0 * y;
    mat[0][0] = 1.0;
    mat[1][2] = 1.0;
    mat[2][1] = 1.0;

91
    // detector IDs start at 10000
92
93
94
    TS_ASSERT_THROWS_ANYTHING(Peak ps(inst, 10000, 2.0, V3D(1, 2, 3), mats);)
    TS_ASSERT_THROWS_NOTHING(Peak p(inst, 10000, 2.0, V3D(1, 2, 3), mat);)
    Peak p(inst, 10000, 2.0, V3D(1, 2, 3), mat);
95
96
97
98
99
100
    TS_ASSERT_DELTA(p.getH(), 1.0, 1e-5)
    TS_ASSERT_DELTA(p.getK(), 2.0, 1e-5)
    TS_ASSERT_DELTA(p.getL(), 3.0, 1e-5)
    TS_ASSERT_EQUALS(p.getDetectorID(), 10000)
    TS_ASSERT_EQUALS(p.getDetector()->getID(), 10000)
    TS_ASSERT_EQUALS(p.getInstrument(), inst)
101
    TS_ASSERT_EQUALS(p.getGoniometerMatrix(), mat);
102
    check_Contributing_Detectors(p, std::vector<int>(1, 10000));
103
104
  }

105
  void test_ConstructorFromIPeakInterface() {
106
    Peak p(inst, 10102, 2.0);
107
    p.setHKL(1, 2, 3);
108
109
110
    p.setRunNumber(1234);
    p.addContributingDetID(10103);

111
    const Mantid::Geometry::IPeak &ipeak = p;
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
    Peak p2(ipeak);
    TS_ASSERT_EQUALS(p.getRow(), p2.getRow());
    TS_ASSERT_EQUALS(p.getCol(), p2.getCol());
    TS_ASSERT_EQUALS(p.getH(), p2.getH());
    TS_ASSERT_EQUALS(p.getK(), p2.getK());
    TS_ASSERT_EQUALS(p.getL(), p2.getL());
    TS_ASSERT_EQUALS(p.getGoniometerMatrix(), p2.getGoniometerMatrix());
    TS_ASSERT_EQUALS(p.getRunNumber(), p2.getRunNumber());
    TS_ASSERT_EQUALS(p.getDetector(), p2.getDetector())
    TS_ASSERT_EQUALS(p.getInstrument(), p2.getInstrument())
    auto expectedIDs = std::vector<int>(2, 10102);
    expectedIDs[1] = 10103;
    check_Contributing_Detectors(p2, expectedIDs);
  }

127
  void test_copyConstructor() {
128
    Peak p(inst, 10102, 2.0);
129
    p.setHKL(1, 2, 3);
130
131
132
133
134
135
136
137
138
139
    p.setRunNumber(1234);
    // Default (not-explicit) copy constructor
    Peak p2(p);
    TS_ASSERT_EQUALS(p.getRow(), p2.getRow());
    TS_ASSERT_EQUALS(p.getCol(), p2.getCol());
    TS_ASSERT_EQUALS(p.getH(), p2.getH());
    TS_ASSERT_EQUALS(p.getK(), p2.getK());
    TS_ASSERT_EQUALS(p.getL(), p2.getL());
    TS_ASSERT_EQUALS(p.getGoniometerMatrix(), p2.getGoniometerMatrix());
    TS_ASSERT_EQUALS(p.getRunNumber(), p2.getRunNumber());
140
141
    TS_ASSERT_EQUALS(p.getDetector(), p2.getDetector());
    TS_ASSERT_EQUALS(p.getInstrument(), p2.getInstrument());
142
143
    TS_ASSERT_EQUALS(p.getPeakShape().shapeName(),
                     p2.getPeakShape().shapeName());
144
    check_Contributing_Detectors(p2, std::vector<int>(1, 10102));
145
146
  }

147
  void test_getValueByColName() {
148
    Peak p(inst, 10102, 2.0);
149
    p.setHKL(1, 2, 3);
150
151
152
153
154
155
156
157
    p.setRunNumber(1234);
    TS_ASSERT_EQUALS(p.getValueByColName("Row"), p.getRow());
    TS_ASSERT_EQUALS(p.getValueByColName("Col"), p.getCol());
    TS_ASSERT_EQUALS(p.getValueByColName("H"), p.getH());
    TS_ASSERT_EQUALS(p.getValueByColName("K"), p.getK());
    TS_ASSERT_EQUALS(p.getValueByColName("L"), p.getL());
    TS_ASSERT_EQUALS(p.getValueByColName("RunNumber"), p.getRunNumber());
    TS_ASSERT_EQUALS(p.getValueByColName("DetId"), p.getDetectorID())
158
    TS_ASSERT_THROWS_ANYTHING(p.getValueByColName("bankname"));
159
160
  }

161
  /** Set the wavelength and see the other "versions" of it get calculated. */
162
163
  void test_wavelength_conversion() {
    // 1 angstroms wavelength, and at the opposite corner of the detector
164
165
    Peak p(inst, 19999, 1.0);
    // Energy in meV
166
167
    TS_ASSERT_DELTA(p.getInitialEnergy(), 81.805, 1e-3) // Conversion table at :
    // www.ncnr.nist.gov/instruments/dcs/dcs_usersguide/Conversion_Factors.pdf
168
    TS_ASSERT_DELTA(p.getFinalEnergy(), p.getInitialEnergy(), 1e-5)
169
170
171
    V3D dp = p.getDetPos();
    double tt = dp.angle(V3D(0, 0, 1));
    double d = 0.5 / sin(0.5 * tt); // d=lambda/2/sin(theta)=4.5469
172
    TS_ASSERT_DELTA(p.getDSpacing(), d, 1e-3);
173
174
175
176
    TS_ASSERT_DELTA(p.getTOF(), 3823, 1);

    // Back-converting to wavelength should give you the same.
    TS_ASSERT_DELTA(p.getWavelength(), 1.00, 1e-2);
177
178
  }

179
  void test_badDetectorID_throws() {
180
    Peak p(inst, 10000, 2.0);
181
    TS_ASSERT_THROWS_ANYTHING(p.setDetectorID(7));
182
  }
183

184
185
  void
  test_setDetector_Adds_ID_To_Contributing_List_And_Does_Not_Remove_Old_From_Contrib_List() {
186
187
188
189
    int expectedIDs[2] = {10000, 10001};
    Peak peak(inst, expectedIDs[0], 2.0);
    peak.setDetectorID(expectedIDs[1]);

190
191
    check_Contributing_Detectors(
        peak, std::vector<int>(expectedIDs, expectedIDs + 2));
192
193
  }

194
  void test_runNumber() {
195
196
    Peak p(inst, 10000, 2.0);
    p.setRunNumber(12345);
197
    TS_ASSERT_EQUALS(p.getRunNumber(), 12345);
198
199
  }

200
  void test_GoniometerMatrix() {
201
    Peak p(inst, 10000, 2.0);
202
203
204
205
206
207
208
209
210
211
212
213
214
    Matrix<double> mats(3, 3), mat(3, 3);
    for (int x = 0; x < 3; x++)
      for (int y = 0; y < 3; y++)
        mats[x][y] = 1.0 * x + 1.0 * y;
    TS_ASSERT_THROWS_ANYTHING(p.setGoniometerMatrix(mats)); // matrix is
                                                            // singular
    TS_ASSERT_EQUALS(p.getGoniometerMatrix(), mats);
    mat[0][0] = 1.0;
    mat[1][2] = 1.0;
    mat[2][1] = 1.0;
    TS_ASSERT_THROWS_NOTHING(
        p.setGoniometerMatrix(mat)); // matrix is not singular
    TS_ASSERT_EQUALS(p.getGoniometerMatrix(), mat);
215
    // Matrix must be 3x3
216
217
    Matrix<double> mat2(4, 3);
    TS_ASSERT_THROWS_ANYTHING(p.setGoniometerMatrix(mat2));
218
219
  }

220
  void test_HKL() {
221
222
    Peak p(inst, 10000, 2.0);
    p.setHKL(1.0, 2.0, 3.0);
223
224
225
    TS_ASSERT_EQUALS(p.getH(), 1.0);
    TS_ASSERT_EQUALS(p.getK(), 2.0);
    TS_ASSERT_EQUALS(p.getL(), 3.0);
226
227
228
    p.setH(5);
    p.setK(6);
    p.setL(7);
229
230
231
    TS_ASSERT_EQUALS(p.getH(), 5.0);
    TS_ASSERT_EQUALS(p.getK(), 6.0);
    TS_ASSERT_EQUALS(p.getL(), 7.0);
232
    p.setHKL(V3D(1.0, 2.0, 3.0));
233
234
235
236
    TS_ASSERT_EQUALS(p.getH(), 1.0);
    TS_ASSERT_EQUALS(p.getK(), 2.0);
    TS_ASSERT_EQUALS(p.getL(), 3.0);
    TS_ASSERT_EQUALS(p.getHKL(), V3D(1.0, 2.0, 3.0));
237
238
  }

239
240
241
242
243
244
245
  void test_isIndexed() {
    Peak p(inst, 10000, 2.0);
    TS_ASSERT_EQUALS(false, p.isIndexed());
    p.setHKL(1, 2, 3);
    TS_ASSERT_EQUALS(true, p.isIndexed());
  }

Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
246
247
  void test_samplePos() {
    Peak p(inst, 10000, 2.0);
Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
248
    p.setSamplePos(1.0, 1.0, 1.0);
249
    TS_ASSERT_EQUALS(p.getSamplePos(), V3D(1.0, 1.0, 1.0));
Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
250
    p.setSamplePos(V3D(2.0, 2.0, 2.0));
251
    TS_ASSERT_EQUALS(p.getSamplePos(), V3D(2.0, 2.0, 2.0));
Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
252
253
  }

254
  void test_getBank_and_row() {
255
256
257
258
    Peak p(inst, 10000, 2.0);
    TS_ASSERT_EQUALS(p.getBankName(), "bank1")
    TS_ASSERT_EQUALS(p.getRow(), 0)
    TS_ASSERT_EQUALS(p.getCol(), 0)
259
260
261
262
263
264
    p.setDetectorID(10050);
    TS_ASSERT_EQUALS(p.getRow(), 50)
    TS_ASSERT_EQUALS(p.getCol(), 0)
    p.setDetectorID(10100);
    TS_ASSERT_EQUALS(p.getRow(), 0)
    TS_ASSERT_EQUALS(p.getCol(), 1)
265
266
  }

267
  void test_getQSampleFrame() {
268
    // Peak 3 is phi,chi,omega of 90,0,0; giving this matrix:
269
    Matrix<double> r2(3, 3, false);
270
271
272
273
274
275
276
277
278
279
280
    r2[0][2] = 1;
    r2[1][1] = 1;
    r2[2][0] = -1;

    Peak p(inst, 10000, 2.0);
    p.setGoniometerMatrix(r2);

    // Q in the lab frame
    V3D qLab = p.getQLabFrame();
    // q in the sample frame.
    V3D qSample = p.getQSampleFrame();
281
282
    // If we re-rotate q in the sample frame by the gonio matrix, we should get
    // q in the lab frame
283
284
285
286
287
288
    V3D qSampleRotated = r2 * qSample;

    // Did the peak properly invert the rotation matrix?
    TS_ASSERT_EQUALS(qLab, qSampleRotated);
  }

Samuel Jackson's avatar
Samuel Jackson committed
289
  void test_getQLabFrame() {
290
291
292
293
294
295
296
297
298
    Instrument_sptr inst =
        ComponentCreationHelper::createTestInstrumentRectangular2(1, 10);
    Peak p(inst, 0, 1.5);
    p.setQLabFrame(V3D(1, 1, 1));
    auto q = p.getQLabFrame();
    // should be the same
    TS_ASSERT_DELTA(q[0], 1, 1e-5);
    TS_ASSERT_DELTA(q[1], 1, 1e-5);
    TS_ASSERT_DELTA(q[2], 1, 1e-5);
Samuel Jackson's avatar
Samuel Jackson committed
299
300
  }

301
302
  //------------------------------------------------------------------------------------
  /** Can't have Q = 0,0,0 or 0 in the Z direction when creating */
303
  void test_setQLabFrame_ThrowsIfQIsNull() {
304
    Peak p1(inst, 10000, 2.0);
305
    const boost::optional<double> distance = 1.0;
306
307
    TS_ASSERT_THROWS_ANYTHING(Peak p2(inst, V3D(0, 0, 0), distance));
    TS_ASSERT_THROWS_ANYTHING(Peak p2(inst, V3D(1, 2, 0), distance));
308
309
310
  }

  /** Compare two peaks, but not the detector IDs etc. */
311
312
313
314
315
316
317
318
319
320
321
322
323
324
  void comparePeaks(Peak &p1, Peak &p2) {
    // TODO. Peak should implement bool operator==(const Peak&) and that should
    // be tested, rather than having external functionality here.
    TS_ASSERT_EQUALS(p1.getQLabFrame(), p2.getQLabFrame());
    TS_ASSERT_EQUALS(p1.getQSampleFrame(), p2.getQSampleFrame());
    TS_ASSERT_EQUALS(p1.getDetPos(), p2.getDetPos());
    TS_ASSERT_EQUALS(p1.getHKL(), p2.getHKL());
    TS_ASSERT_DELTA(p1.getWavelength(), p2.getWavelength(), 1e-5);
    TS_ASSERT_DELTA(p1.getL1(), p2.getL1(), 1e-5);
    TS_ASSERT_DELTA(p1.getL2(), p2.getL2(), 1e-5);
    TS_ASSERT_DELTA(p1.getTOF(), p2.getTOF(), 1e-5);
    TS_ASSERT_DELTA(p1.getInitialEnergy(), p2.getInitialEnergy(), 1e-5);
    TS_ASSERT_DELTA(p1.getFinalEnergy(), p2.getFinalEnergy(), 1e-5);
    TS_ASSERT(p1.getGoniometerMatrix().equals(p2.getGoniometerMatrix(), 1e-5));
325
326
327
  }

  /** Create peaks using Q in the lab frame */
328
  void test_setQLabFrame() {
329
330
331
332
333
    Peak p1(inst, 19999, 2.0);
    V3D Qlab1 = p1.getQLabFrame();
    V3D detPos1 = p1.getDetPos();

    // Construct using just Q
334
    Peak p2(inst, Qlab1, boost::optional<double>(detPos1.norm()));
335
    comparePeaks(p1, p2);
336
337
338
339
    TS_ASSERT_EQUALS(p2.getBankName(), "None");
    TS_ASSERT_EQUALS(p2.getRow(), -1);
    TS_ASSERT_EQUALS(p2.getCol(), -1);
    TS_ASSERT_EQUALS(p2.getDetectorID(), -1);
340
341
  }

342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
  void test_setQLabFrame2() {
    // Create fictional instrument
    const V3D source(0, 0, 0);
    const V3D sample(15, 0, 0);
    const V3D detectorPos(20, 5, 0);
    const V3D beam1 = sample - source;
    const V3D beam2 = detectorPos - sample;
    auto minimalInstrument = ComponentCreationHelper::createMinimalInstrument(
        source, sample, detectorPos);

    // Derive distances and angles
    const double l1 = beam1.norm();
    const double l2 = beam2.norm();
    const V3D qLabDir = (beam1 / l1) - (beam2 / l2);

    const double microSecsInSec = 1e6;

    // Derive QLab for diffraction
    const double wavenumber_in_angstrom_times_tof_in_microsec =
        (Mantid::PhysicalConstants::NeutronMass * (l1 + l2) * 1e-10 *
         microSecsInSec) /
        Mantid::PhysicalConstants::h_bar;

    V3D qLab = qLabDir * wavenumber_in_angstrom_times_tof_in_microsec;

    Peak peak; // Everything will be default
    peak.setInstrument(
        minimalInstrument); // Can't do anything without the instrument
    peak.setQLabFrame(qLab);
    auto detector = peak.getDetector();

    TSM_ASSERT("No detector", detector);
    TS_ASSERT_EQUALS(1, detector->getID());
    TS_ASSERT_EQUALS(detectorPos, detector->getPos());
376
377
  }

378
  /** Create peaks using Q in sample frame + a goniometer rotation matrix*/
379
  void test_setQSampleFrame() {
380
    // A goniometer rotation matrix
381
    Matrix<double> r2(3, 3, false);
382
383
384
385
    r2[0][2] = 1;
    r2[1][1] = 1;
    r2[2][0] = -1;

386
    Peak p1(inst, 19999, 2.0, V3D(1, 2, 3), r2);
387
388
389
390
391
    V3D q = p1.getQSampleFrame();
    V3D detPos1 = p1.getDetPos();

    // Construct using Q + rotation matrix
    Peak p2(inst, q, r2, detPos1.norm());
392
    p2.setHKL(V3D(1, 2, 3)); // Make sure HKL matches too.
393
    comparePeaks(p1, p2);
394
395
396
397
    TS_ASSERT_EQUALS(p2.getBankName(), "None");
    TS_ASSERT_EQUALS(p2.getRow(), -1);
    TS_ASSERT_EQUALS(p2.getCol(), -1);
    TS_ASSERT_EQUALS(p2.getDetectorID(), -1);
398
399
  }

400
401
  void test_setQSampleFrameVirtualDetectorWithQLab() {
    constexpr auto radius = 10.;
402
403
404
405
    auto sphereInst =
        ComponentCreationHelper::createTestInstrumentRectangular(5, 100);
    auto extendedSpaceObj =
        ComponentCreationHelper::createSphere(10., V3D(0, 0, 0));
406
    auto extendedSpace = std::make_unique<ObjComponent>(
407
        "extended-detector-space", extendedSpaceObj, sphereInst.get());
408
    extendedSpace->setPos(V3D(0.0, 0.0, 0.0));
409
    sphereInst->add(extendedSpace.release());
410
411
412
413
    const auto refFrame = sphereInst->getReferenceFrame();
    const auto refBeamDir = refFrame->vecPointingAlongBeam();

    // test with & without extended detector space
414
    // extended space is a sphere, so all points should fall radius*detector
415
    // direction away from the detector direction with extended space
416
    auto testQ = [&](const V3D &q) {
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
      // Compute expected direction
      const auto qBeam = q.scalar_prod(refBeamDir);
      const double norm_q = q.norm();
      const double one_over_wl = (norm_q * norm_q) / (2.0 * qBeam);

      V3D detectorDir = q * -1.0;
      detectorDir[refFrame->pointingAlongBeam()] = one_over_wl - qBeam;
      detectorDir.normalize();

      // test without extended detector space
      // should be a unit vector in the direction of the virtual detector
      // position
      Peak peak1(inst, q);

      // skip tests for which Q actually does intersect with a valid detector
      if (peak1.getDetectorID() > 0) {
        return;
      }

      TS_ASSERT_EQUALS(peak1.getDetectorID(), -1)
      TS_ASSERT_EQUALS(peak1.getDetPos(), detectorDir)

      // test with extended detector space
      // should be the full vector to the virtual detector position
      Peak peak2(sphereInst, q);
      TS_ASSERT_EQUALS(peak2.getDetectorID(), -1)
      TS_ASSERT_EQUALS(peak2.getDetPos(), detectorDir * radius)
444
445
446
447
448
449
450
451
452
453
    };

    // Make hemisphere of q vectors to test
    std::vector<double> xDirections(20);
    std::vector<double> yDirections(20);
    std::vector<double> zDirections(10);

    // create x values of the range -1 to 1
    int index = 0;
    double startValue = -1;
454
455
456
    std::generate(
        xDirections.begin(), xDirections.end(),
        [&index, &startValue]() { return startValue + index++ * 0.1; });
457
458
459
460
461

    // create z values of the range 0.1 to 1
    // ignore negative z values as these are not physical!
    index = 0;
    startValue = 0.1;
462
463
464
    std::generate(
        zDirections.begin(), zDirections.end(),
        [&index, &startValue]() { return startValue + index++ * 0.1; });
465
466
467

    yDirections = xDirections;

468
469
470
471
    for (auto &x : xDirections) {
      for (auto &y : yDirections) {
        for (auto &z : zDirections) {
          testQ(V3D(x, y, z));
472
        }
473
      }
474
475
476
477
    }
  }

  void test_setQSampleFrameVirtualDetectorWithScatteringAngle() {
478
479
480
481
    auto sphereInst =
        ComponentCreationHelper::createTestInstrumentRectangular(5, 100);
    auto extendedSpaceObj =
        ComponentCreationHelper::createSphere(10., V3D(0, 0, 0));
482
    auto extendedSpace = std::make_unique<ObjComponent>(
483
        "extended-detector-space", extendedSpaceObj, sphereInst.get());
484
    extendedSpace->setPos(V3D(0.0, 0.0, 0.0));
485
    sphereInst->add(extendedSpace.release());
486
487

    // test with & without extended detector space
488
    // extended space is a sphere, so all points should fall radius*detector
489
    // direction away from the detector direction with extended space
Hahn, Steven's avatar
Hahn, Steven committed
490
    auto testTheta = [this, &sphereInst](const double theta) {
Hahn, Steven's avatar
Hahn, Steven committed
491
      constexpr auto radius = 10.;
492
493
494
495
496
497
498
499
500
501
502
503
504
      const auto expectedDir = V3D(sin(theta), 0., cos(theta));

      // test without extended detector space
      // should be {sin(theta), 0, cos(theta)}
      Peak p1(this->inst, theta, 2.0);
      V3D detPos1 = p1.getDetPos();
      TS_ASSERT_EQUALS(detPos1, expectedDir);

      // test with extended detector space
      // should be radius*{sin(theta), 0, cos(theta)}
      Peak p2(sphereInst, theta, 2.0);
      V3D detPos2 = p2.getDetPos();
      TS_ASSERT_EQUALS(detPos2, expectedDir * radius);
505
506
507
508
509
    };

    // generate & test a range of angles from 0 - 360
    int index = 0;
    std::vector<double> angles(8);
510
    std::generate(angles.begin(), angles.end(), [&index, &angles]() {
511
512
      return static_cast<double>(index++) * M_PI /
             static_cast<double>(angles.size());
513
    });
514
515
516
517

    std::for_each(angles.begin(), angles.end(), testTheta);
  }

518
519
  /** Create peaks using Q in the lab frame,
   * then find the corresponding detector ID */
520
  void test_findDetector() {
521
522
523
524
525
    Peak p1(inst, 19999, 2.0);
    V3D Qlab1 = p1.getQLabFrame();
    V3D detPos1 = p1.getDetPos();

    // Construct using just Q
526
    Peak p2(inst, Qlab1, boost::optional<double>(detPos1.norm()));
527
    TS_ASSERT(p2.findDetector());
528
    comparePeaks(p1, p2);
529
530
531
532
    TS_ASSERT_EQUALS(p2.getBankName(), "bank1");
    TS_ASSERT_EQUALS(p2.getRow(), 99);
    TS_ASSERT_EQUALS(p2.getCol(), 99);
    TS_ASSERT_EQUALS(p2.getDetectorID(), 19999);
533
534
  }

535
  void test_getDetectorPosition() {
536
537
538
539
540
    const int detectorId = 19999;
    const double wavelength = 2;
    Peak p(inst, detectorId, wavelength);

    V3D a = p.getDetectorPosition();
Owen Arnold's avatar
Owen Arnold committed
541
    V3D b = p.getDetectorPositionNoCheck();
542
543
544
545

    TSM_ASSERT_EQUALS("Results should be the same", a, b);
  }

546
  void test_getDetectorPositionThrows() {
547
548
549
    const int detectorId = 19999;
    const double wavelength = 2;
    Peak p(inst, detectorId, wavelength);
550
551
552
553
554
555
556
    TSM_ASSERT_THROWS_NOTHING("Nothing wrong here, detector is valid",
                              p.getDetectorPosition());
    p.setQLabFrame(
        V3D(1, 1, 1),
        1.0); // This sets the detector pointer to null and detector id to -1;
    TSM_ASSERT_THROWS("Detector is not valid", p.getDetectorPosition(),
                      Mantid::Kernel::Exception::NullPointerException &);
557
558
  }

559
560
561
562
  void test_get_peak_shape_default() {
    Peak peak;
    const PeakShape &integratedShape = peak.getPeakShape();
    TS_ASSERT_EQUALS("none", integratedShape.shapeName());
563
564
  }

565
566
  void test_set_peak_shape() {
    using namespace testing;
567

568
    Peak peak;
569

570
571
572
    MockPeakShape *replacementShape = new MockPeakShape;
    EXPECT_CALL(*replacementShape, shapeName()).Times(1);
    peak.setPeakShape(replacementShape);
573

574
575
    const PeakShape &currentShape = peak.getPeakShape();
    currentShape.shapeName();
576

577
    TS_ASSERT(Mock::VerifyAndClearExpectations(replacementShape));
578
579
  }

Samuel Jackson's avatar
Samuel Jackson committed
580
581
582
  void test_get_intensity_over_sigma() {
    const int detectorId = 19999;
    const double wavelength = 2;
583
584
    const double intensity{100};
    const double sigma{10};
Samuel Jackson's avatar
Samuel Jackson committed
585
586
587
588
589
    Peak p(inst, detectorId, wavelength);

    p.setIntensity(intensity);
    p.setSigmaIntensity(sigma);

590
    TS_ASSERT_EQUALS(p.getIntensityOverSigma(), intensity / sigma);
Samuel Jackson's avatar
Samuel Jackson committed
591
592
593
594
595
  }

  void test_get_intensity_over_sigma_empty_sigma() {
    const int detectorId = 19999;
    const double wavelength = 2;
596
    const double intensity{10};
597
    const double sigma{0};
Samuel Jackson's avatar
Samuel Jackson committed
598
599
600
601
602
    Peak p(inst, detectorId, wavelength);

    p.setIntensity(intensity);
    p.setSigmaIntensity(sigma);

603
604
    const double expectedResult{0.0};
    const double tolerance{1e-10};
605
    TS_ASSERT_DELTA(p.getIntensityOverSigma(), expectedResult, tolerance);
Samuel Jackson's avatar
Samuel Jackson committed
606
607
608
609
610
611
612
613
614
615
616
617
  }

  void test_get_energy() {
    const int detectorId = 19999;
    const double wavelength = 2;
    const double initialEnergy{100};
    const double finalEnergy{110};
    Peak p(inst, detectorId, wavelength);

    p.setInitialEnergy(initialEnergy);
    p.setFinalEnergy(finalEnergy);

Samuel Jackson's avatar
Samuel Jackson committed
618
    TS_ASSERT_EQUALS(p.getEnergyTransfer(), initialEnergy - finalEnergy);
Samuel Jackson's avatar
Samuel Jackson committed
619
620
  }

621
private:
622
623
  void check_Contributing_Detectors(const Peak &peak,
                                    const std::vector<int> &expected) {
624
    auto peakIDs = peak.getContributingDetIDs();
625
    for (int id : expected) {
626
627
628
      TSM_ASSERT_EQUALS("Expected " + boost::lexical_cast<std::string>(id) +
                            " in contribution list",
                        1, peakIDs.count(id))
629
630
    }
  }
631
632
633
};

#endif /* MANTID_DATAOBJECTS_PEAKTEST_H_ */