PeakTest.h 21.4 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
#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
  void test_copyConstructor() {
127
    Peak p(inst, 10102, 2.0);
128
    p.setHKL(1, 2, 3);
129
130
131
132
133
134
135
136
137
138
    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());
139
140
    TS_ASSERT_EQUALS(p.getDetector(), p2.getDetector());
    TS_ASSERT_EQUALS(p.getInstrument(), p2.getInstrument());
141
142
    TS_ASSERT_EQUALS(p.getPeakShape().shapeName(),
                     p2.getPeakShape().shapeName());
143
    check_Contributing_Detectors(p2, std::vector<int>(1, 10102));
144
145
  }

146
  void test_getValueByColName() {
147
    Peak p(inst, 10102, 2.0);
148
    p.setHKL(1, 2, 3);
149
150
151
152
153
154
155
156
    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())
157
    TS_ASSERT_THROWS_ANYTHING(p.getValueByColName("bankname"));
158
159
  }

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

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

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

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

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

199
  void test_GoniometerMatrix() {
200
    Peak p(inst, 10000, 2.0);
201
202
203
204
205
206
207
208
209
210
211
212
213
    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);
214
    // Matrix must be 3x3
215
216
    Matrix<double> mat2(4, 3);
    TS_ASSERT_THROWS_ANYTHING(p.setGoniometerMatrix(mat2));
217
218
  }

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

238
239
240
241
242
243
244
  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
245
246
  void test_samplePos() {
    Peak p(inst, 10000, 2.0);
Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
247
    p.setSamplePos(1.0, 1.0, 1.0);
248
    TS_ASSERT_EQUALS(p.getSamplePos(), V3D(1.0, 1.0, 1.0));
Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
249
    p.setSamplePos(V3D(2.0, 2.0, 2.0));
250
    TS_ASSERT_EQUALS(p.getSamplePos(), V3D(2.0, 2.0, 2.0));
Sullivan, Brendan T's avatar
Sullivan, Brendan T committed
251
252
  }

253
  void test_getBank_and_row() {
254
255
256
257
    Peak p(inst, 10000, 2.0);
    TS_ASSERT_EQUALS(p.getBankName(), "bank1")
    TS_ASSERT_EQUALS(p.getRow(), 0)
    TS_ASSERT_EQUALS(p.getCol(), 0)
258
259
260
261
262
263
    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)
264
265
  }

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

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

Samuel Jackson's avatar
Samuel Jackson committed
288
  void test_getQLabFrame() {
289
290
291
292
293
294
295
296
297
    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
298
299
  }

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

  /** Compare two peaks, but not the detector IDs etc. */
310
311
312
313
314
315
316
317
318
319
320
321
322
323
  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));
324
325
326
  }

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

    // Construct using just Q
333
    Peak p2(inst, Qlab1, boost::optional<double>(detPos1.norm()));
334
    comparePeaks(p1, p2);
335
336
337
338
    TS_ASSERT_EQUALS(p2.getBankName(), "None");
    TS_ASSERT_EQUALS(p2.getRow(), -1);
    TS_ASSERT_EQUALS(p2.getCol(), -1);
    TS_ASSERT_EQUALS(p2.getDetectorID(), -1);
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
369
370
371
372
373
374
  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());
375
376
  }

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

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

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

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

    // test with & without extended detector space
413
    // extended space is a sphere, so all points should fall radius*detector
414
    // direction away from the detector direction with extended space
415
    auto testQ = [&](const V3D &q) {
416
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
      // 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)
443
444
445
446
447
448
449
450
451
452
    };

    // 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;
453
454
455
    std::generate(
        xDirections.begin(), xDirections.end(),
        [&index, &startValue]() { return startValue + index++ * 0.1; });
456
457
458
459
460

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

    yDirections = xDirections;

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

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

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

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

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

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

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

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

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

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

545
  void test_getDetectorPositionThrows() {
546
547
548
    const int detectorId = 19999;
    const double wavelength = 2;
    Peak p(inst, detectorId, wavelength);
549
550
551
552
553
554
555
    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 &);
556
557
  }

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

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

567
    Peak peak;
568

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

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

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

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

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

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

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

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

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

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

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