PeakTest.h 21.3 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
  }

Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
239
240
  void test_samplePos() {
    Peak p(inst, 10000, 2.0);
Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
241
    p.setSamplePos(1.0, 1.0, 1.0);
242
    TS_ASSERT_EQUALS(p.getSamplePos(), V3D(1.0, 1.0, 1.0));
Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
243
    p.setSamplePos(V3D(2.0, 2.0, 2.0));
244
    TS_ASSERT_EQUALS(p.getSamplePos(), V3D(2.0, 2.0, 2.0));
Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
245
246
  }

247
  void test_getBank_and_row() {
248
249
250
251
    Peak p(inst, 10000, 2.0);
    TS_ASSERT_EQUALS(p.getBankName(), "bank1")
    TS_ASSERT_EQUALS(p.getRow(), 0)
    TS_ASSERT_EQUALS(p.getCol(), 0)
252
253
254
255
256
257
    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)
258
259
  }

260
  void test_getQSampleFrame() {
261
    // Peak 3 is phi,chi,omega of 90,0,0; giving this matrix:
262
    Matrix<double> r2(3, 3, false);
263
264
265
266
267
268
269
270
271
272
273
    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();
274
275
    // If we re-rotate q in the sample frame by the gonio matrix, we should get
    // q in the lab frame
276
277
278
279
280
281
    V3D qSampleRotated = r2 * qSample;

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

Samuel Jackson's avatar
Samuel Jackson committed
282
  void test_getQLabFrame() {
283
284
285
286
287
288
289
290
291
    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
292
293
  }

294
295
  //------------------------------------------------------------------------------------
  /** Can't have Q = 0,0,0 or 0 in the Z direction when creating */
296
  void test_setQLabFrame_ThrowsIfQIsNull() {
297
    Peak p1(inst, 10000, 2.0);
298
    const boost::optional<double> distance = 1.0;
299
300
    TS_ASSERT_THROWS_ANYTHING(Peak p2(inst, V3D(0, 0, 0), distance));
    TS_ASSERT_THROWS_ANYTHING(Peak p2(inst, V3D(1, 2, 0), distance));
301
302
303
  }

  /** Compare two peaks, but not the detector IDs etc. */
304
305
306
307
308
309
310
311
312
313
314
315
316
317
  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));
318
319
320
  }

  /** Create peaks using Q in the lab frame */
321
  void test_setQLabFrame() {
322
323
324
325
326
    Peak p1(inst, 19999, 2.0);
    V3D Qlab1 = p1.getQLabFrame();
    V3D detPos1 = p1.getDetPos();

    // Construct using just Q
327
    Peak p2(inst, Qlab1, boost::optional<double>(detPos1.norm()));
328
    comparePeaks(p1, p2);
329
330
331
332
    TS_ASSERT_EQUALS(p2.getBankName(), "None");
    TS_ASSERT_EQUALS(p2.getRow(), -1);
    TS_ASSERT_EQUALS(p2.getCol(), -1);
    TS_ASSERT_EQUALS(p2.getDetectorID(), -1);
333
334
  }

335
336
337
338
339
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
  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());
369
370
  }

371
  /** Create peaks using Q in sample frame + a goniometer rotation matrix*/
372
  void test_setQSampleFrame() {
373
    // A goniometer rotation matrix
374
    Matrix<double> r2(3, 3, false);
375
376
377
378
    r2[0][2] = 1;
    r2[1][1] = 1;
    r2[2][0] = -1;

379
    Peak p1(inst, 19999, 2.0, V3D(1, 2, 3), r2);
380
381
382
383
384
    V3D q = p1.getQSampleFrame();
    V3D detPos1 = p1.getDetPos();

    // Construct using Q + rotation matrix
    Peak p2(inst, q, r2, detPos1.norm());
385
    p2.setHKL(V3D(1, 2, 3)); // Make sure HKL matches too.
386
    comparePeaks(p1, p2);
387
388
389
390
    TS_ASSERT_EQUALS(p2.getBankName(), "None");
    TS_ASSERT_EQUALS(p2.getRow(), -1);
    TS_ASSERT_EQUALS(p2.getCol(), -1);
    TS_ASSERT_EQUALS(p2.getDetectorID(), -1);
391
392
  }

393
394
  void test_setQSampleFrameVirtualDetectorWithQLab() {
    constexpr auto radius = 10.;
395
396
397
398
    auto sphereInst =
        ComponentCreationHelper::createTestInstrumentRectangular(5, 100);
    auto extendedSpaceObj =
        ComponentCreationHelper::createSphere(10., V3D(0, 0, 0));
399
    auto extendedSpace = std::make_unique<ObjComponent>(
400
        "extended-detector-space", extendedSpaceObj, sphereInst.get());
401
    extendedSpace->setPos(V3D(0.0, 0.0, 0.0));
402
    sphereInst->add(extendedSpace.release());
403
404
405
406
    const auto refFrame = sphereInst->getReferenceFrame();
    const auto refBeamDir = refFrame->vecPointingAlongBeam();

    // test with & without extended detector space
407
    // extended space is a sphere, so all points should fall radius*detector
408
    // direction away from the detector direction with extended space
409
    auto testQ = [&](const V3D &q) {
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
      // 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)
437
438
439
440
441
442
443
444
445
446
    };

    // 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;
447
448
449
    std::generate(
        xDirections.begin(), xDirections.end(),
        [&index, &startValue]() { return startValue + index++ * 0.1; });
450
451
452
453
454

    // create z values of the range 0.1 to 1
    // ignore negative z values as these are not physical!
    index = 0;
    startValue = 0.1;
455
456
457
    std::generate(
        zDirections.begin(), zDirections.end(),
        [&index, &startValue]() { return startValue + index++ * 0.1; });
458
459
460

    yDirections = xDirections;

461
462
463
464
    for (auto &x : xDirections) {
      for (auto &y : yDirections) {
        for (auto &z : zDirections) {
          testQ(V3D(x, y, z));
465
        }
466
      }
467
468
469
470
    }
  }

  void test_setQSampleFrameVirtualDetectorWithScatteringAngle() {
471
472
473
474
    auto sphereInst =
        ComponentCreationHelper::createTestInstrumentRectangular(5, 100);
    auto extendedSpaceObj =
        ComponentCreationHelper::createSphere(10., V3D(0, 0, 0));
475
    auto extendedSpace = std::make_unique<ObjComponent>(
476
        "extended-detector-space", extendedSpaceObj, sphereInst.get());
477
    extendedSpace->setPos(V3D(0.0, 0.0, 0.0));
478
    sphereInst->add(extendedSpace.release());
479
480

    // test with & without extended detector space
481
    // extended space is a sphere, so all points should fall radius*detector
482
    // direction away from the detector direction with extended space
Hahn, Steven's avatar
Hahn, Steven committed
483
    auto testTheta = [this, &sphereInst](const double theta) {
Hahn, Steven's avatar
Hahn, Steven committed
484
      constexpr auto radius = 10.;
485
486
487
488
489
490
491
492
493
494
495
496
497
      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);
498
499
500
501
502
    };

    // generate & test a range of angles from 0 - 360
    int index = 0;
    std::vector<double> angles(8);
503
    std::generate(angles.begin(), angles.end(), [&index, &angles]() {
504
505
      return static_cast<double>(index++) * M_PI /
             static_cast<double>(angles.size());
506
    });
507
508
509
510

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

511
512
  /** Create peaks using Q in the lab frame,
   * then find the corresponding detector ID */
513
  void test_findDetector() {
514
515
516
517
518
    Peak p1(inst, 19999, 2.0);
    V3D Qlab1 = p1.getQLabFrame();
    V3D detPos1 = p1.getDetPos();

    // Construct using just Q
519
    Peak p2(inst, Qlab1, boost::optional<double>(detPos1.norm()));
520
    TS_ASSERT(p2.findDetector());
521
    comparePeaks(p1, p2);
522
523
524
525
    TS_ASSERT_EQUALS(p2.getBankName(), "bank1");
    TS_ASSERT_EQUALS(p2.getRow(), 99);
    TS_ASSERT_EQUALS(p2.getCol(), 99);
    TS_ASSERT_EQUALS(p2.getDetectorID(), 19999);
526
527
  }

528
  void test_getDetectorPosition() {
529
530
531
532
533
    const int detectorId = 19999;
    const double wavelength = 2;
    Peak p(inst, detectorId, wavelength);

    V3D a = p.getDetectorPosition();
Owen Arnold's avatar
Owen Arnold committed
534
    V3D b = p.getDetectorPositionNoCheck();
535
536
537
538

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

539
  void test_getDetectorPositionThrows() {
540
541
542
    const int detectorId = 19999;
    const double wavelength = 2;
    Peak p(inst, detectorId, wavelength);
543
544
545
546
547
548
549
    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 &);
550
551
  }

552
553
554
555
  void test_get_peak_shape_default() {
    Peak peak;
    const PeakShape &integratedShape = peak.getPeakShape();
    TS_ASSERT_EQUALS("none", integratedShape.shapeName());
556
557
  }

558
559
  void test_set_peak_shape() {
    using namespace testing;
560

561
    Peak peak;
562

563
564
565
    MockPeakShape *replacementShape = new MockPeakShape;
    EXPECT_CALL(*replacementShape, shapeName()).Times(1);
    peak.setPeakShape(replacementShape);
566

567
568
    const PeakShape &currentShape = peak.getPeakShape();
    currentShape.shapeName();
569

570
    TS_ASSERT(Mock::VerifyAndClearExpectations(replacementShape));
571
572
  }

Samuel Jackson's avatar
Samuel Jackson committed
573
574
575
  void test_get_intensity_over_sigma() {
    const int detectorId = 19999;
    const double wavelength = 2;
576
577
    const double intensity{100};
    const double sigma{10};
Samuel Jackson's avatar
Samuel Jackson committed
578
579
580
581
582
    Peak p(inst, detectorId, wavelength);

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

583
    TS_ASSERT_EQUALS(p.getIntensityOverSigma(), intensity / sigma);
Samuel Jackson's avatar
Samuel Jackson committed
584
585
586
587
588
  }

  void test_get_intensity_over_sigma_empty_sigma() {
    const int detectorId = 19999;
    const double wavelength = 2;
589
    const double intensity{10};
590
    const double sigma{0};
Samuel Jackson's avatar
Samuel Jackson committed
591
592
593
594
595
    Peak p(inst, detectorId, wavelength);

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

596
597
    const double expectedResult{0.0};
    const double tolerance{1e-10};
598
    TS_ASSERT_DELTA(p.getIntensityOverSigma(), expectedResult, tolerance);
Samuel Jackson's avatar
Samuel Jackson committed
599
600
601
602
603
604
605
606
607
608
609
610
  }

  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
611
    TS_ASSERT_EQUALS(p.getEnergyTransfer(), initialEnergy - finalEnergy);
Samuel Jackson's avatar
Samuel Jackson committed
612
613
  }

614
private:
615
616
  void check_Contributing_Detectors(const Peak &peak,
                                    const std::vector<int> &expected) {
617
    auto peakIDs = peak.getContributingDetIDs();
618
    for (int id : expected) {
619
620
621
      TSM_ASSERT_EQUALS("Expected " + boost::lexical_cast<std::string>(id) +
                            " in contribution list",
                        1, peakIDs.count(id))
622
623
    }
  }
624
625
626
};

#endif /* MANTID_DATAOBJECTS_PEAKTEST_H_ */