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

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

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

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

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

Hahn, Steven's avatar
Hahn, Steven committed
28
29
30
31
32
33
34
35
36
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
37
} // namespace boost
Hahn, Steven's avatar
Hahn, Steven committed
38

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

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

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

55
56
  }

57
  void test_constructor() {
58
59
60
61
62
63
    // 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)
64
65
    TS_ASSERT_EQUALS(p.getDetector()->getID(), 10000)
    TS_ASSERT_EQUALS(p.getInstrument(), inst)
66
    check_Contributing_Detectors(p, std::vector<int>(1, 10000));
67
68
  }

69
  void test_constructorHKL() {
70
    // detector IDs start at 10000
71
    Peak p(inst, 10000, 2.0, V3D(1, 2, 3));
72
73
74
75
76
77
    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)
78
    check_Contributing_Detectors(p, std::vector<int>(1, 10000));
79
80
  }

81
82
83
84
85
86
87
88
89
  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;

90
    // detector IDs start at 10000
91
92
93
    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);
94
95
96
97
98
99
    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)
100
    TS_ASSERT_EQUALS(p.getGoniometerMatrix(), mat);
101
    check_Contributing_Detectors(p, std::vector<int>(1, 10000));
102
103
  }

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

110
    const Mantid::Geometry::IPeak &ipeak = p;
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
    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);
  }

126
127
128
129
130
131
  void test_ConstructorFromLeanElasticPeak() {
    const LeanElasticPeak &lpeak = p(V3D(1, 2, 3));

    TS_ASSERT_THROWS_NOTHING(Peak p(lpeak, inst));
  }

132
  void test_copyConstructor() {
133
    Peak p(inst, 10102, 2.0);
134
    p.setHKL(1, 2, 3);
135
136
137
138
139
140
141
142
143
144
    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());
145
146
    TS_ASSERT_EQUALS(p.getDetector(), p2.getDetector());
    TS_ASSERT_EQUALS(p.getInstrument(), p2.getInstrument());
147
148
    TS_ASSERT_EQUALS(p.getPeakShape().shapeName(),
                     p2.getPeakShape().shapeName());
149
    check_Contributing_Detectors(p2, std::vector<int>(1, 10102));
150
151
  }

152
  void test_getValueByColName() {
153
    Peak p(inst, 10102, 2.0);
154
    p.setHKL(1, 2, 3);
155
156
157
158
159
160
161
162
    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())
163
    TS_ASSERT_THROWS_ANYTHING(p.getValueByColName("bankname"));
164
165
  }

166
  /** Set the wavelength and see the other "versions" of it get calculated. */
167
168
  void test_wavelength_conversion() {
    // 1 angstroms wavelength, and at the opposite corner of the detector
169
170
    Peak p(inst, 19999, 1.0);
    // Energy in meV
171
172
    TS_ASSERT_DELTA(p.getInitialEnergy(), 81.805, 1e-3) // Conversion table at :
    // www.ncnr.nist.gov/instruments/dcs/dcs_usersguide/Conversion_Factors.pdf
173
    TS_ASSERT_DELTA(p.getFinalEnergy(), p.getInitialEnergy(), 1e-5)
174
175
176
    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
177
    TS_ASSERT_DELTA(p.getDSpacing(), d, 1e-3);
178
179
180
181
    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);
182
183
  }

184
  void test_badDetectorID_throws() {
185
    Peak p(inst, 10000, 2.0);
186
    TS_ASSERT_THROWS_ANYTHING(p.setDetectorID(7));
187
  }
188

189
190
  void
  test_setDetector_Adds_ID_To_Contributing_List_And_Does_Not_Remove_Old_From_Contrib_List() {
191
192
193
194
    int expectedIDs[2] = {10000, 10001};
    Peak peak(inst, expectedIDs[0], 2.0);
    peak.setDetectorID(expectedIDs[1]);

195
196
    check_Contributing_Detectors(
        peak, std::vector<int>(expectedIDs, expectedIDs + 2));
197
198
  }

199
  void test_runNumber() {
200
201
    Peak p(inst, 10000, 2.0);
    p.setRunNumber(12345);
202
    TS_ASSERT_EQUALS(p.getRunNumber(), 12345);
203
204
  }

205
  void test_GoniometerMatrix() {
206
    Peak p(inst, 10000, 2.0);
207
208
209
210
211
212
213
214
215
216
217
218
219
    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);
220
    // Matrix must be 3x3
221
222
    Matrix<double> mat2(4, 3);
    TS_ASSERT_THROWS_ANYTHING(p.setGoniometerMatrix(mat2));
223
224
  }

225
  void test_HKL() {
226
227
    Peak p(inst, 10000, 2.0);
    p.setHKL(1.0, 2.0, 3.0);
228
229
230
    TS_ASSERT_EQUALS(p.getH(), 1.0);
    TS_ASSERT_EQUALS(p.getK(), 2.0);
    TS_ASSERT_EQUALS(p.getL(), 3.0);
231
232
233
    p.setH(5);
    p.setK(6);
    p.setL(7);
234
235
236
    TS_ASSERT_EQUALS(p.getH(), 5.0);
    TS_ASSERT_EQUALS(p.getK(), 6.0);
    TS_ASSERT_EQUALS(p.getL(), 7.0);
237
    p.setHKL(V3D(1.0, 2.0, 3.0));
238
239
240
241
    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));
242
243
  }

244
245
246
247
248
249
250
  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
251
252
  void test_samplePos() {
    Peak p(inst, 10000, 2.0);
Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
253
    p.setSamplePos(1.0, 1.0, 1.0);
254
    TS_ASSERT_EQUALS(p.getSamplePos(), V3D(1.0, 1.0, 1.0));
Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
255
    p.setSamplePos(V3D(2.0, 2.0, 2.0));
256
    TS_ASSERT_EQUALS(p.getSamplePos(), V3D(2.0, 2.0, 2.0));
Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
257
258
  }

259
  void test_getBank_and_row() {
260
261
262
263
    Peak p(inst, 10000, 2.0);
    TS_ASSERT_EQUALS(p.getBankName(), "bank1")
    TS_ASSERT_EQUALS(p.getRow(), 0)
    TS_ASSERT_EQUALS(p.getCol(), 0)
264
265
266
267
268
269
    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)
270
271
  }

272
  void test_getQSampleFrame() {
273
    // Peak 3 is phi,chi,omega of 90,0,0; giving this matrix:
274
    Matrix<double> r2(3, 3, false);
275
276
277
278
279
280
281
282
283
284
285
    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();
286
287
    // If we re-rotate q in the sample frame by the gonio matrix, we should get
    // q in the lab frame
288
289
290
291
292
293
    V3D qSampleRotated = r2 * qSample;

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

Samuel Jackson's avatar
Samuel Jackson committed
294
  void test_getQLabFrame() {
295
296
297
298
299
300
301
302
303
    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
304
305
  }

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

  /** Compare two peaks, but not the detector IDs etc. */
316
317
318
319
320
321
322
323
324
325
326
327
328
329
  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));
330
331
332
  }

  /** Create peaks using Q in the lab frame */
333
  void test_setQLabFrame() {
334
335
336
337
338
    Peak p1(inst, 19999, 2.0);
    V3D Qlab1 = p1.getQLabFrame();
    V3D detPos1 = p1.getDetPos();

    // Construct using just Q
339
    Peak p2(inst, Qlab1, boost::optional<double>(detPos1.norm()));
340
    comparePeaks(p1, p2);
341
342
343
344
    TS_ASSERT_EQUALS(p2.getBankName(), "None");
    TS_ASSERT_EQUALS(p2.getRow(), -1);
    TS_ASSERT_EQUALS(p2.getCol(), -1);
    TS_ASSERT_EQUALS(p2.getDetectorID(), -1);
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
376
377
378
379
380
  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());
381
382
  }

383
  /** Create peaks using Q in sample frame + a goniometer rotation matrix*/
384
  void test_setQSampleFrame() {
385
    // A goniometer rotation matrix
386
    Matrix<double> r2(3, 3, false);
387
388
389
390
    r2[0][2] = 1;
    r2[1][1] = 1;
    r2[2][0] = -1;

391
    Peak p1(inst, 19999, 2.0, V3D(1, 2, 3), r2);
392
393
394
395
396
    V3D q = p1.getQSampleFrame();
    V3D detPos1 = p1.getDetPos();

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

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

    // test with & without extended detector space
419
    // extended space is a sphere, so all points should fall radius*detector
420
    // direction away from the detector direction with extended space
421
    auto testQ = [&](const V3D &q) {
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
      // 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)
449
450
451
452
453
454
455
456
457
458
    };

    // 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;
459
460
461
    std::generate(
        xDirections.begin(), xDirections.end(),
        [&index, &startValue]() { return startValue + index++ * 0.1; });
462
463
464
465
466

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

    yDirections = xDirections;

473
474
475
476
    for (auto &x : xDirections) {
      for (auto &y : yDirections) {
        for (auto &z : zDirections) {
          testQ(V3D(x, y, z));
477
        }
478
      }
479
480
481
482
    }
  }

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

    // test with & without extended detector space
493
    // extended space is a sphere, so all points should fall radius*detector
494
    // direction away from the detector direction with extended space
Hahn, Steven's avatar
Hahn, Steven committed
495
    auto testTheta = [this, &sphereInst](const double theta) {
Hahn, Steven's avatar
Hahn, Steven committed
496
      constexpr auto radius = 10.;
497
498
499
500
501
502
503
504
505
506
507
508
509
      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);
510
511
512
513
514
    };

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

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

523
524
  /** Create peaks using Q in the lab frame,
   * then find the corresponding detector ID */
525
  void test_findDetector() {
526
527
528
529
530
    Peak p1(inst, 19999, 2.0);
    V3D Qlab1 = p1.getQLabFrame();
    V3D detPos1 = p1.getDetPos();

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

540
  void test_getDetectorPosition() {
541
542
543
544
545
    const int detectorId = 19999;
    const double wavelength = 2;
    Peak p(inst, detectorId, wavelength);

    V3D a = p.getDetectorPosition();
Owen Arnold's avatar
Owen Arnold committed
546
    V3D b = p.getDetectorPositionNoCheck();
547
548
549
550

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

551
  void test_getDetectorPositionThrows() {
552
553
554
    const int detectorId = 19999;
    const double wavelength = 2;
    Peak p(inst, detectorId, wavelength);
555
556
557
558
559
560
561
    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 &);
562
563
  }

564
565
566
567
  void test_get_peak_shape_default() {
    Peak peak;
    const PeakShape &integratedShape = peak.getPeakShape();
    TS_ASSERT_EQUALS("none", integratedShape.shapeName());
568
569
  }

570
571
  void test_set_peak_shape() {
    using namespace testing;
572

573
    Peak peak;
574

575
576
577
    MockPeakShape *replacementShape = new MockPeakShape;
    EXPECT_CALL(*replacementShape, shapeName()).Times(1);
    peak.setPeakShape(replacementShape);
578

579
580
    const PeakShape &currentShape = peak.getPeakShape();
    currentShape.shapeName();
581

582
    TS_ASSERT(Mock::VerifyAndClearExpectations(replacementShape));
583
584
  }

Samuel Jackson's avatar
Samuel Jackson committed
585
586
587
  void test_get_intensity_over_sigma() {
    const int detectorId = 19999;
    const double wavelength = 2;
588
589
    const double intensity{100};
    const double sigma{10};
Samuel Jackson's avatar
Samuel Jackson committed
590
591
592
593
594
    Peak p(inst, detectorId, wavelength);

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

595
    TS_ASSERT_EQUALS(p.getIntensityOverSigma(), intensity / sigma);
Samuel Jackson's avatar
Samuel Jackson committed
596
597
598
599
600
  }

  void test_get_intensity_over_sigma_empty_sigma() {
    const int detectorId = 19999;
    const double wavelength = 2;
601
    const double intensity{10};
602
    const double sigma{0};
Samuel Jackson's avatar
Samuel Jackson committed
603
604
605
606
607
    Peak p(inst, detectorId, wavelength);

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

608
609
    const double expectedResult{0.0};
    const double tolerance{1e-10};
610
    TS_ASSERT_DELTA(p.getIntensityOverSigma(), expectedResult, tolerance);
Samuel Jackson's avatar
Samuel Jackson committed
611
612
613
614
615
616
617
618
619
620
621
622
  }

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

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