UnitTest.h 63.7 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
9
10

#include <cxxtest/TestSuite.h>

11
#include "MantidKernel/PhysicalConstants.h"
LamarMoore's avatar
LamarMoore committed
12
#include "MantidKernel/Unit.h"
13
#include "MantidKernel/UnitLabelTypes.h"
14
#include <boost/lexical_cast.hpp>
15
16
#include <cfloat>
#include <limits>
17
18

using namespace Mantid::Kernel;
19
using namespace Mantid::Kernel::Units;
20

21
// function checks if conversion within limits works reversibly
Samuel Jones's avatar
Samuel Jones committed
22
std::string convert_units_check_range(const Unit &aUnit, std::vector<double> &samples, std::vector<double> &results,
23
                                      double epsilon1 = 0) {
24
  std::string error_mess("");
25
26
27
28
29
30
31
32

  samples.resize(4);
  results.resize(4);
  double tof_min = aUnit.conversionTOFMin();
  double tof_max = aUnit.conversionTOFMax();
  samples[0] = tof_min;
  samples[1] = tof_max;

33
  double initValMin = aUnit.singleFromTOF(tof_min);
34
35
  double initValMax = aUnit.singleFromTOF(tof_max);
  samples[2] = initValMin;
36
  samples[3] = initValMax;
37

38
39
40
41
  results[0] = aUnit.singleToTOF(initValMin);   // tof1
  results[1] = aUnit.singleToTOF(initValMax);   // tof2
  results[2] = aUnit.singleFromTOF(results[0]); // unit 1
  results[3] = aUnit.singleFromTOF(results[1]); // unit 2
42

43
  auto range = aUnit.conversionRange();
44
45
  double tof1 = aUnit.singleToTOF(range.first);
  double tof2 = aUnit.singleToTOF(range.second);
46
  bool t_increases(true);
47
48
  if (tof1 > tof2)
    t_increases = false;
49

50
  if (tof1 == tof2) {
Samuel Jones's avatar
Samuel Jones committed
51
    error_mess = "conversion: " + aUnit.unitID() + " Time range is  zero (tof_left==tof_rignt)";
52
53
    return error_mess;
  }
54
  if (tof1 < tof_min || tof2 < tof_min) {
Samuel Jones's avatar
Samuel Jones committed
55
    error_mess = "conversion: " + aUnit.unitID() + " min time range is smaller then minimal conversion time";
56
    return error_mess;
57
  }
58
  if (tof1 > tof_max * (1 + epsilon1) || tof2 > tof_max * (1 + epsilon1)) {
Samuel Jones's avatar
Samuel Jones committed
59
    error_mess = "conversion: " + aUnit.unitID() + "max time range is bigger then maximal conversion time";
60
    return error_mess;
61
62
63
64
  }

  const size_t nSteps(100);

65
  double step = (range.second - range.first) / nSteps;
66
  if (std::isinf(step)) {
67
    step = (DBL_MAX / nSteps) * 2;
68
  }
69

70
  double t1 = aUnit.singleToTOF(range.first);
71
72
  for (size_t i = 1; i <= nSteps; i++) {
    double unitVal = range.first + double(i) * step;
73
    double tofVal = aUnit.singleToTOF(unitVal);
74
75
    if (t_increases) {
      if (tofVal * (1 + epsilon1) < t1) {
Samuel Jones's avatar
Samuel Jones committed
76
77
        error_mess = "conversion: " + aUnit.unitID() + " subsequent tof decreases for increasing function at step: " +
                     boost::lexical_cast<std::string>(i);
78
79
        return error_mess;
      }
80
81
    } else {
      if (tofVal > t1 * (1 + epsilon1)) {
Samuel Jones's avatar
Samuel Jones committed
82
83
        error_mess = "conversion: " + aUnit.unitID() + " subsequent tof increases for decreasing function at step: " +
                     boost::lexical_cast<std::string>(i);
84
85
86
        return error_mess;
      }

87
      t1 = tofVal;
88
89
90
91
    }
  }

  return error_mess;
92
93
}

94
95
96
97
98
99
100
101
102
103
104
namespace {                // anonymous
const double DIFC = 2100.; // sensible value
const double TZERO = 10.;
// intentionally goofy - reduces tzero by 1
const double DIFA1 = .25 * DIFC * DIFC;
// intentionally goofy - reduces tzero by .01
const double DIFA2 = 25 * DIFC * DIFC;
// intentionally goofy
const double DIFA3 = -.25 * DIFC * DIFC;
} // namespace

105
class UnitTest : public CxxTest::TestSuite {
106

107
108
109
  class UnitTester : public Unit {
  public:
    UnitTester() : Unit() {
110
111
112
      addConversion("a", 1.1);
      addConversion("b", 2.2, 0.5);
    }
113
    ~UnitTester() override {}
114

115
    // Empty overrides of virtual methods
116
117
118
119
120
121
    const std::string unitID() const override { return "aUnit"; }
    const std::string caption() const override { return ""; }
    const UnitLabel label() const override { return UnitLabel(""); }
    void init() override {}
    double singleToTOF(const double) const override { return 0; }
    double singleFromTOF(const double) const override { return 0; }
Samuel Jones's avatar
Samuel Jones committed
122
123
    double conversionTOFMax() const override { return std::numeric_limits<double>::quiet_NaN(); }
    double conversionTOFMin() const override { return std::numeric_limits<double>::quiet_NaN(); }
124

125
    Unit *clone() const override { return new UnitTester(); }
126
  };
127

128
public:
129
130
131
132
  //----------------------------------------------------------------------
  // Label tests
  //----------------------------------------------------------------------

133
  void testLabel_constructor() {
134
135
    Label lbl("Temperature", "K");
    TS_ASSERT_EQUALS(lbl.caption(), "Temperature");
136
    TS_ASSERT_EQUALS(lbl.label().ascii(), "K");
137
138
  }

139
  void testLabel_unitID() { TS_ASSERT_EQUALS(label.unitID(), "Label"); }
140

141
  void testLabel_caption() { TS_ASSERT_EQUALS(label.caption(), "Quantity"); }
142

143
  void testLabel_label() { TS_ASSERT_EQUALS(label.label().ascii(), ""); }
144

145
  void testLabel_cast() {
146
    Unit *u = nullptr;
147
    TS_ASSERT_THROWS_NOTHING(u = dynamic_cast<Unit *>(&label));
148
149
150
    TS_ASSERT_EQUALS(u->unitID(), "Label");
  }

151
  void testLabel_setLabel() {
152
153
    label.setLabel("Temperature", "K");
    TS_ASSERT_EQUALS(label.caption(), "Temperature");
154
    TS_ASSERT_EQUALS(label.label().ascii(), "K");
155
  }
156
157
158
159
160
  void testLabel_limits() {
    double volatile lim_min = label.conversionTOFMin();
    TS_ASSERT(lim_min != label.conversionTOFMin());
    double volatile lim_max = label.conversionTOFMax();
    TS_ASSERT(lim_max != label.conversionTOFMax());
161
162
  }

163
164
165
  /**
   * Tests the two equality operators == and !=
   */
166
167
  void testEqualityOperators() {
    // Get some units to test equality with
168
169
170
    auto *e1 = Energy().clone();
    auto *e2 = Energy().clone();
    auto *wl = Wavelength().clone();
171

172
    // Test equality operator
173
174
    TS_ASSERT(*e1 == *e2);

175
    // Test inequality oeprator
176
    TS_ASSERT(*e1 != *wl);
Dan Nixon's avatar
Dan Nixon committed
177
178
179
180

    delete e1;
    delete e2;
    delete wl;
181
  }
182

183
184
185
  //----------------------------------------------------------------------
  // Base Unit class tests
  //----------------------------------------------------------------------
186

187
  void testUnit_quickConversion() {
188
189
190
    UnitTester t;
    double factor;
    double power;
191
192
193
194
195
196
197
    TS_ASSERT(t.quickConversion("a", factor, power));
    TS_ASSERT_EQUALS(factor, 1.1);
    TS_ASSERT_EQUALS(power, 1.0);
    TS_ASSERT(t.quickConversion("b", factor, power));
    TS_ASSERT_EQUALS(factor, 2.2);
    TS_ASSERT_EQUALS(power, 0.5);
    TS_ASSERT(!t.quickConversion("notThere", factor, power));
Russell Taylor's avatar
Russell Taylor committed
198

199
200
    // Test the quickConversion method that takes a Unit
    Units::TOF tof;
201
    TS_ASSERT(!t.quickConversion(tof, factor, power));
202
  }
203

204
  void test_clone() {
205
    auto unit = Empty().clone();
206
    TS_ASSERT(dynamic_cast<Empty *>(unit));
207
208
    delete unit;
    unit = Label().clone();
209
    TS_ASSERT(dynamic_cast<Label *>(unit));
210
211
    delete unit;
    unit = Wavelength().clone();
212
    TS_ASSERT(dynamic_cast<Wavelength *>(unit));
213
214
    delete unit;
    unit = Energy().clone();
215
    TS_ASSERT(dynamic_cast<Energy *>(unit));
216
217
    delete unit;
    unit = Energy_inWavenumber().clone();
218
    TS_ASSERT(dynamic_cast<Energy_inWavenumber *>(unit));
219
220
    delete unit;
    unit = dSpacing().clone();
221
    TS_ASSERT(dynamic_cast<dSpacing *>(unit));
222
    delete unit;
223
224
    unit = dSpacingPerpendicular().clone();
    TS_ASSERT(dynamic_cast<dSpacingPerpendicular *>(unit));
225
    delete unit;
226
    unit = MomentumTransfer().clone();
227
    TS_ASSERT(dynamic_cast<MomentumTransfer *>(unit));
228
229
    delete unit;
    unit = QSquared().clone();
230
    TS_ASSERT(dynamic_cast<QSquared *>(unit));
231
232
    delete unit;
    unit = DeltaE().clone();
233
    TS_ASSERT(dynamic_cast<DeltaE *>(unit));
234
235
    delete unit;
    unit = DeltaE_inWavenumber().clone();
236
    TS_ASSERT(dynamic_cast<DeltaE_inWavenumber *>(unit));
237
    delete unit;
238
239
240
    unit = DeltaE_inFrequency().clone();
    TS_ASSERT(dynamic_cast<DeltaE_inFrequency *>(unit));
    delete unit;
241
    unit = Momentum().clone();
242
    TS_ASSERT(dynamic_cast<Momentum *>(unit));
243
    delete unit;
244
  }
245

246
247
248
249
  //----------------------------------------------------------------------
  // TOF tests
  //----------------------------------------------------------------------

250
  void testTOF_unitID() { TS_ASSERT_EQUALS(tof.unitID(), "TOF"); }
251

252
  void test_copy_constructor_on_concrete_type() {
253
    Units::TOF first;
Samuel Jones's avatar
Samuel Jones committed
254
255
256
    first.initialize(
        1.0, 2,
        {{UnitParams::l2, 1.0}, {UnitParams::twoTheta, 1.0}, {UnitParams::efixed, 1.0}, {UnitParams::delta, 1.0}});
257
258
259
260
    Units::TOF second(first);
    TS_ASSERT_EQUALS(first.isInitialized(), second.isInitialized());
    TS_ASSERT_EQUALS(first.unitID(), second.unitID())
    TS_ASSERT_EQUALS(first.caption(), second.caption())
261
262
    TS_ASSERT_EQUALS(first.label().ascii(), second.label().ascii())
    TS_ASSERT_EQUALS(first.label().utf8(), second.label().utf8())
263
264
  }

265
  void test_copy_assignment_operator_on_concrete_type() {
266
    Units::TOF first;
Samuel Jones's avatar
Samuel Jones committed
267
268
269
    first.initialize(
        1.0, 2,
        {{UnitParams::l2, 1.0}, {UnitParams::twoTheta, 1.0}, {UnitParams::efixed, 1.0}, {UnitParams::delta, 1.0}});
270
271
272
273
274
    Units::TOF second;
    second = first;
    TS_ASSERT_EQUALS(first.isInitialized(), second.isInitialized());
    TS_ASSERT_EQUALS(first.unitID(), second.unitID())
    TS_ASSERT_EQUALS(first.caption(), second.caption())
275
276
    TS_ASSERT_EQUALS(first.label().ascii(), second.label().ascii())
    TS_ASSERT_EQUALS(first.label().utf8(), second.label().utf8())
277
278
  }

279
  void testTOF_caption() { TS_ASSERT_EQUALS(tof.caption(), "Time-of-flight"); }
280

281
282
283
  void testTOF_label() {
    TS_ASSERT_EQUALS(tof.label().ascii(), "microsecond")
    TS_ASSERT_EQUALS(tof.label().utf8(), L"\u03bcs")
284
285
  }

286
  void testTOF_cast() {
287
    Unit *u = nullptr;
288
    TS_ASSERT_THROWS_NOTHING(u = dynamic_cast<Unit *>(&tof));
289
    TS_ASSERT_EQUALS(u->unitID(), "TOF");
290
291
  }

292
  void testTOF_toTOF() {
293
294
    std::vector<double> x(20, 9.9), y(20, 8.8);
    std::vector<double> xx = x;
295
    std::vector<double> yy = y;
296
    TS_ASSERT_THROWS_NOTHING(tof.toTOF(x, y, 1.0, 1, {}))
297
    // Check vectors are unchanged
298
299
    TS_ASSERT(xx == x)
    TS_ASSERT(yy == y)
300
301
  }

302
  void testTOF_fromTOF() {
303
    std::vector<double> x(20, 9.9), y(20, 8.8);
304
305
    std::vector<double> xx = x;
    std::vector<double> yy = y;
306
    TS_ASSERT_THROWS_NOTHING(tof.fromTOF(x, y, 1.0, 1, {}))
307
    // Check vectors are unchanged
308
309
    TS_ASSERT(xx == x)
    TS_ASSERT(yy == y)
310
  }
311
312
313
314
  void testTOFrange() {
    std::vector<double> sample, rezult;
    std::string err_mess = convert_units_check_range(tof, sample, rezult);
    TSM_ASSERT(" ERROR:" + err_mess, err_mess.size() == 0);
315

316
317
318
319
    for (size_t i = 0; i < sample.size(); i++) {
      TS_ASSERT_DELTA(sample[i], rezult[i], FLT_EPSILON);
    }
  }
320
321
322
323
324

  //----------------------------------------------------------------------
  // Wavelength tests
  //----------------------------------------------------------------------

Samuel Jones's avatar
Samuel Jones committed
325
  void testWavelength_unitID() { TS_ASSERT_EQUALS(lambda.unitID(), "Wavelength") }
326

Samuel Jones's avatar
Samuel Jones committed
327
  void testWavelength_caption() { TS_ASSERT_EQUALS(lambda.caption(), "Wavelength") }
328

329
330
331
  void testWavelength_label() {
    TS_ASSERT_EQUALS(lambda.label().ascii(), "Angstrom")
    TS_ASSERT_EQUALS(lambda.label().utf8(), L"\u212b")
332
333
  }

334
  void testWavelength_cast() {
335
    Unit *u = nullptr;
336
    TS_ASSERT_THROWS_NOTHING(u = dynamic_cast<Unit *>(&lambda));
337
    TS_ASSERT_EQUALS(u->unitID(), "Wavelength");
338
339
  }

340
  void testWavelength_toTOF() {
341
    std::vector<double> x(1, 1.5), y(1, 1.5);
342
    std::vector<double> yy = y;
Samuel Jones's avatar
Samuel Jones committed
343
    TS_ASSERT_THROWS_NOTHING(lambda.toTOF(x, y, 1.0, 1, {{UnitParams::l2, 1.0}, {UnitParams::efixed, 1.0}}))
344
345
    TS_ASSERT_DELTA(x[0], 2665.4390, 0.0001) //  758.3352
    TS_ASSERT(yy == y)
346

Samuel Jones's avatar
Samuel Jones committed
347
348
    TS_ASSERT_DELTA(lambda.convertSingleToTOF(1.5, 1.0, 1, {{UnitParams::l2, 1.0}, {UnitParams::efixed, 1.0}}),
                    2665.4390, 0.0001);
349
  }
350

351
  void testWavelength_fromTOF() {
352
    std::vector<double> x(1, 1000.5), y(1, 1.5);
353
    std::vector<double> yy = y;
Samuel Jones's avatar
Samuel Jones committed
354
    TS_ASSERT_THROWS_NOTHING(lambda.fromTOF(x, y, 1.0, 1, {{UnitParams::l2, 1.0}, {UnitParams::efixed, 1.0}}))
355
356
    TS_ASSERT_DELTA(x[0], -5.0865, 0.0001) // 1.979006
    TS_ASSERT(yy == y)
357

Samuel Jones's avatar
Samuel Jones committed
358
359
    TS_ASSERT_DELTA(lambda.convertSingleFromTOF(1000.5, 1.0, 1, {{UnitParams::l2, 1.0}, {UnitParams::efixed, 1.0}}),
                    -5.0865, 0.0001);
360
  }
361

362
  void testWavelength_quickConversions() {
363
364
    // Test it gives the same answer as going 'the long way'
    double factor, power;
365
    TS_ASSERT(lambda.quickConversion(energy, factor, power))
366
    double input = 1.1;
367
368
    double result = factor * std::pow(input, power);
    std::vector<double> x(1, input);
Samuel Jones's avatar
Samuel Jones committed
369
    lambda.toTOF(x, x, 99.0, 99, {{UnitParams::l2, 99.0}, {UnitParams::efixed, 1.0}});
370
    energy.fromTOF(x, x, 99.0, 99, {{UnitParams::l2, 99.0}});
371
372
373
374
    TS_ASSERT_DELTA(x[0], result, 1.0e-10)

    TS_ASSERT(lambda.quickConversion(energyk, factor, power))
    double result2 = factor * std::pow(input, power);
Samuel Jones's avatar
Samuel Jones committed
375
    TS_ASSERT_EQUALS(result2 / result, Mantid::PhysicalConstants::meVtoWavenumber)
376
    std::vector<double> x2(1, input);
Samuel Jones's avatar
Samuel Jones committed
377
    lambda.toTOF(x2, x2, 99.0, 99, {{UnitParams::l2, 99.0}, {UnitParams::efixed, 99.0}});
378
379

    energyk.fromTOF(x2, x2, 99.0, 99, {{UnitParams::l2, 99.0}});
380
381
382
383
384
385
386
387
388
    TS_ASSERT_DELTA(x2[0], result2, 1.0e-10)
  }

  void testWavelengthrange() {
    std::vector<double> sample, rezult;
    std::string err_mess = convert_units_check_range(lambda, sample, rezult);
    TSM_ASSERT(" ERROR:" + err_mess, err_mess.size() == 0);

    for (size_t i = 0; i < sample.size(); i++) {
Samuel Jones's avatar
Samuel Jones committed
389
390
      TSM_ASSERT_DELTA(" Failed for conversion N: " + boost::lexical_cast<std::string>(i), sample[i], rezult[i],
                       FLT_EPSILON);
391
392
393
    }
  }

394
395
396
397
398
  void testWavelength_WithoutParams() {
    std::vector<double> x(1, 2.0), y(1, 1.0);
    TS_ASSERT_THROWS(lambda.fromTOF(x, y, 1.0, 1, {}), const std::runtime_error &)
  }

399
  //----------------------------------------------------------------------
400
401
402
  // Energy tests
  //----------------------------------------------------------------------

403
  void testEnergy_unitID() { TS_ASSERT_EQUALS(energy.unitID(), "Energy") }
404

405
  void testEnergy_caption() { TS_ASSERT_EQUALS(energy.caption(), "Energy") }
406

407
408
409
  void testEnergy_label() {
    TS_ASSERT_EQUALS(energy.label().ascii(), "meV")
    TS_ASSERT_EQUALS(energy.label().utf8(), L"meV")
410
411
  }

412
  void testEnergy_cast() {
413
    Unit *u = nullptr;
414
    TS_ASSERT_THROWS_NOTHING(u = dynamic_cast<Unit *>(&energy));
415
    TS_ASSERT_EQUALS(u->unitID(), "Energy");
416
417
  }

418
  void testEnergy_toTOF() {
419
    std::vector<double> x(1, 4.0), y(1, 1.0);
420
    std::vector<double> yy = y;
Samuel Jones's avatar
Samuel Jones committed
421
    TS_ASSERT_THROWS_NOTHING(energy.toTOF(x, y, 1.0, 1, {{UnitParams::l2, 1.0}}))
422
423
    TS_ASSERT_DELTA(x[0], 2286.271, 0.001)
    TS_ASSERT(yy == y)
424
425
  }

426
  void testEnergy_fromTOF() {
427
    std::vector<double> x(1, 4.0), y(1, 1.0);
428
    std::vector<double> yy = y;
Samuel Jones's avatar
Samuel Jones committed
429
    TS_ASSERT_THROWS_NOTHING(energy.fromTOF(x, y, 1.0, 1, {{UnitParams::l2, 1.0}}))
430
431
    TS_ASSERT_DELTA(x[0], 1306759.0, 1.0)
    TS_ASSERT(yy == y)
432
433
  }

434
  void testEnergy_quickConversions() {
435
436
    // Test it gives the same answer as going 'the long way'
    double factor, power;
437
    TS_ASSERT(energy.quickConversion(energyk, factor, power))
438
    double input = 100.1;
439
440
441
    double result = factor * std::pow(input, power);
    TS_ASSERT_EQUALS(result / input, Mantid::PhysicalConstants::meVtoWavenumber)
    std::vector<double> x(1, input);
442
443
    energy.toTOF(x, x, 99.0, 99, {{UnitParams::l2, 99.0}});
    energyk.fromTOF(x, x, 99.0, 99, {{UnitParams::l2, 99.0}});
444
445
446
447
448
    TS_ASSERT_DELTA(x[0], result, 1.0e-12)

    TS_ASSERT(energy.quickConversion(lambda, factor, power))
    result = factor * std::pow(input, power);
    std::vector<double> x2(1, input);
449
    energy.toTOF(x2, x2, 99.0, 99, {{UnitParams::l2, 99.0}});
Samuel Jones's avatar
Samuel Jones committed
450
    lambda.fromTOF(x2, x2, 99.0, 99, {{UnitParams::l2, 99.0}, {UnitParams::efixed, 99.0}});
451
452
453
454
455
456
457
458
    TS_ASSERT_DELTA(x2[0], result, 1.0e-15)
  }
  void testEnergyRange() {
    std::vector<double> sample, rezult;
    std::string err_mess = convert_units_check_range(energy, sample, rezult);
    TSM_ASSERT(" ERROR:" + err_mess, err_mess.size() == 0);
    for (size_t i = 0; i < sample.size(); i++) {
      if (std::fabs(sample[i]) < 10 * FLT_EPSILON) {
Samuel Jones's avatar
Samuel Jones committed
459
460
        TSM_ASSERT_DELTA("Energy limits Failed for conversion N: " + boost::lexical_cast<std::string>(i), sample[i],
                         rezult[i], 10 * FLT_EPSILON);
461
      } else {
Samuel Jones's avatar
Samuel Jones committed
462
        TSM_ASSERT_DELTA("Energy limits Failed for conversion N: " + boost::lexical_cast<std::string>(i),
463
                         rezult[i] / sample[i], 1., 10 * FLT_EPSILON);
464
465
466
467
      }
    }
  }

468
469
470
471
472
  void testEnergy_WithoutParams() {
    std::vector<double> x(1, 4.0), y(1, 1.0);
    TS_ASSERT_THROWS(energy.fromTOF(x, y, 1.0, 1, {}), const std::runtime_error &)
  }

473
474
475
476
  //----------------------------------------------------------------------
  // Energy_inWavenumber tests
  //----------------------------------------------------------------------

Samuel Jones's avatar
Samuel Jones committed
477
  void testEnergy_inWavenumber_unitID() { TS_ASSERT_EQUALS(energyk.unitID(), "Energy_inWavenumber") }
478

Samuel Jones's avatar
Samuel Jones committed
479
  void testEnergy_inWavenumber_caption() { TS_ASSERT_EQUALS(energyk.caption(), "Energy") }
480

481
482
483
  void testEnergy_inWavenumber_label() {
    TS_ASSERT_EQUALS(energyk.label().ascii(), "cm^-1")
    TS_ASSERT_EQUALS(energyk.label().utf8(), L"cm\u207b\u00b9")
484
485
  }

486
  void testEnergy_inWavenumber_cast() {
487
    Unit *u = nullptr;
488
    TS_ASSERT_THROWS_NOTHING(u = dynamic_cast<Unit *>(&energyk));
489
490
491
    TS_ASSERT_EQUALS(u->unitID(), "Energy_inWavenumber");
  }

492
  void testEnergy_inWavenumber_toTOF() {
493
494
    std::vector<double> x(1, 4.0), y(1, 1.0);
    std::vector<double> yy = y;
Samuel Jones's avatar
Samuel Jones committed
495
    TS_ASSERT_THROWS_NOTHING(energyk.toTOF(x, y, 1.0, 1, {{UnitParams::l2, 1.0}}))
496
497
    TS_ASSERT_DELTA(x[0], 6492.989, 0.001)
    TS_ASSERT(yy == y)
498
499
  }

500
  void testEnergy_inWavenumber_fromTOF() {
501
502
    std::vector<double> x(1, 4.0), y(1, 1.0);
    std::vector<double> yy = y;
Samuel Jones's avatar
Samuel Jones committed
503
    TS_ASSERT_THROWS_NOTHING(energyk.fromTOF(x, y, 1.0, 1, {{UnitParams::l2, 1.0}}))
504
505
    TS_ASSERT_DELTA(x[0], 10539725, 1.0)
    TS_ASSERT(yy == y)
506
507
  }

508
  void testEnergy_inWavenumber_quickConversions() {
509
510
    // Test it gives the same answer as going 'the long way'
    double factor, power;
511
    TS_ASSERT(energyk.quickConversion(energy, factor, power))
512
    double input = 100.1;
513
514
515
    double result = factor * std::pow(input, power);
    TS_ASSERT_EQUALS(input / result, Mantid::PhysicalConstants::meVtoWavenumber)
    std::vector<double> x(1, input);
516
517
    energyk.toTOF(x, x, 99.0, 99, {{UnitParams::l2, 99.0}});
    energy.fromTOF(x, x, 99.0, 99, {{UnitParams::l2, 99.0}});
518
    TS_ASSERT_DELTA(x[0], result, 1.0e-14)
519

520
521
522
    TS_ASSERT(energyk.quickConversion(lambda, factor, power))
    result = factor * std::pow(input, power);
    std::vector<double> x2(1, input);
523
    energyk.toTOF(x2, x2, 99.0, 99, {{UnitParams::l2, 99.0}});
Samuel Jones's avatar
Samuel Jones committed
524
    lambda.fromTOF(x2, x2, 99.0, 99, {{UnitParams::l2, 99.0}, {UnitParams::efixed, 99.0}});
525
    TS_ASSERT_DELTA(x2[0], result, 1.0e-15)
526
  }
Russell Taylor's avatar
Russell Taylor committed
527

528
529
530
531
532
  void testEnergy_inWavenumber_WithoutParams() {
    std::vector<double> x(1, 2.0), y(1, 1.0);
    TS_ASSERT_THROWS(energyk.fromTOF(x, y, 1.0, 1, {}), const std::runtime_error &)
  }

533
534
535
536
  //----------------------------------------------------------------------
  // d-Spacing tests
  //----------------------------------------------------------------------

537
  void testdSpacing_unitID() { TS_ASSERT_EQUALS(d.unitID(), "dSpacing") }
538

539
  void testdSpacing_caption() { TS_ASSERT_EQUALS(d.caption(), "d-Spacing") }
540

541
542
543
  void testdSpacing_label() {
    TS_ASSERT_EQUALS(d.label().ascii(), "Angstrom")
    TS_ASSERT_EQUALS(d.label().utf8(), L"\u212b")
544
545
  }

546
  void testdSpacing_cast() {
547
    Unit *u = nullptr;
548
    TS_ASSERT_THROWS_NOTHING(u = dynamic_cast<Unit *>(&d));
549
    TS_ASSERT_EQUALS(u->unitID(), "dSpacing");
550
  }
551

552
  void testdSpacing_toTOF() {
553
    std::vector<double> x(1, 1.0), y(1, 1.0);
554
    std::vector<double> yy = y;
Samuel Jones's avatar
Samuel Jones committed
555
556
    double difc =
        2.0 * Mantid::PhysicalConstants::NeutronMass * sin(0.5) * (1.0 + 1.0) * 1e-4 / Mantid::PhysicalConstants::h;
557
558
559
560
561
562
563
564
    TS_ASSERT_THROWS_NOTHING(d.toTOF(x, y, 1.0, 1, {{UnitParams::difc, difc}}))
    TS_ASSERT_DELTA(x[0], 484.7537, 0.0001)
    TS_ASSERT(yy == y)
  }

  void testdSpacing_toTOFWithL2TwoTheta() {
    std::vector<double> x(1, 1.0), y(1, 1.0);
    std::vector<double> yy = y;
Samuel Jones's avatar
Samuel Jones committed
565
    TS_ASSERT_THROWS_NOTHING(d.toTOF(x, y, 1.0, 1, {{UnitParams::l2, 1.0}, {UnitParams::twoTheta, 1.0}}))
566
567
    TS_ASSERT_DELTA(x[0], 484.7537, 0.0001)
    TS_ASSERT(yy == y)
568
569
  }

570
571
572
  void testdSpacing_toTOFWithDIFATZERO() {
    std::vector<double> x(1, 2.0), y(1, 1.0);
    std::vector<double> yy = y;
Samuel Jones's avatar
Samuel Jones committed
573
574
    TS_ASSERT_THROWS_NOTHING(
        d.toTOF(x, y, 1.0, 1, {{UnitParams::difc, 3.0}, {UnitParams::difa, 2.0}, {UnitParams::tzero, 1.0}}))
575
576
577
578
    TS_ASSERT_DELTA(x[0], 6.0 + 8.0 + 1.0, 0.0001)
    TS_ASSERT(yy == y)
  }

579
  void testdSpacing_fromTOF() {
580
581
582
583
    const std::vector<double> x_in{-1., 0., 1001.1, 16000.};
    const std::vector<double> y_in{1., 2., 3., 4.};
    std::vector<double> x(x_in.begin(), x_in.end());
    std::vector<double> y(y_in.begin(), y_in.end());
Samuel Jones's avatar
Samuel Jones committed
584
585
    double difc =
        2.0 * Mantid::PhysicalConstants::NeutronMass * sin(0.5) * (1.0 + 1.0) * 1e-4 / Mantid::PhysicalConstants::h;
586
587
588
589
590
591
592
    TS_ASSERT_THROWS_NOTHING(d.fromTOF(x, y, 1.0, 1, {{UnitParams::difc, difc}}));

    TS_ASSERT(y == y_in);
    for (size_t i = 0; i < x.size(); ++i)
      TS_ASSERT_DELTA(x[i], x_in[i] / difc, 0.000001);

    // test for exception thrown
593
594
    x[0] = 1.0;
    TS_ASSERT_THROWS(d.fromTOF(x, y, 1.0, 1, {{UnitParams::difc, -1.0}}), const std::runtime_error &)
595
596
  }

597
  void testdSpacing_fromTOFWithDIFATZERO() {
598
599
600
    // solves the quadratic ax^2 + bx + c =0
    // where a=difa, b=difc, c=tzero-tof
    // a>0 and c<0
601
602
    std::vector<double> x(1, 2.0), y(1, 1.0);
    std::vector<double> yy = y;
Samuel Jones's avatar
Samuel Jones committed
603
604
    TS_ASSERT_THROWS_NOTHING(
        d.fromTOF(x, y, 1.0, 1, {{UnitParams::difc, 2.0}, {UnitParams::difa, 3.0}, {UnitParams::tzero, 1.0}}))
605
606
    TS_ASSERT_DELTA(x[0], 1.0 / 3.0, 0.0001)
    TS_ASSERT(yy == y)
607
608
    // a>0 and c=0
    x[0] = 1.0;
609
610
611
    TS_ASSERT_THROWS_NOTHING(
        d.fromTOF(x, y, 1.0, 1, {{UnitParams::difc, 2.0}, {UnitParams::difa, 3.0}, {UnitParams::tzero, 1.0}}))
    TS_ASSERT_DELTA(x[0], 0.0, 0.0001)
612
    TS_ASSERT(yy == y)
613
    // a<0 and c=0
614
    x[0] = 1.0;
Samuel Jones's avatar
Samuel Jones committed
615
616
    TS_ASSERT_THROWS_NOTHING(
        d.fromTOF(x, y, 1.0, 1, {{UnitParams::difc, 3.0}, {UnitParams::difa, -2.0}, {UnitParams::tzero, 1.0}}))
617
618
    TS_ASSERT_DELTA(x[0], 1.5, 0.0001)
    TS_ASSERT(yy == y)
619
620
621
622
623
624
625
626
627
628
    // a<0 and c<0 - two positive roots
    x[0] = 2.0;
    TS_ASSERT_THROWS_NOTHING(
        d.fromTOF(x, y, 1.0, 1, {{UnitParams::difc, 3.0}, {UnitParams::difa, -2.0}, {UnitParams::tzero, 1.0}}))
    TS_ASSERT_DELTA(x[0], 0.5, 0.0001)
    TS_ASSERT(yy == y)
    x[0] = 2.0;
    TS_ASSERT_THROWS(
        d.fromTOF(x, y, 1.0, 1, {{UnitParams::difc, 2.0}, {UnitParams::difa, -3.0}, {UnitParams::tzero, 1.0}}),
        const std::runtime_error &)
629
630
631
632
633
    x[0] = 10000.0;
    TS_ASSERT_THROWS_NOTHING(
        d.fromTOF(x, y, 1.0, 1, {{UnitParams::difc, 20000.0}, {UnitParams::difa, -1E-10}, {UnitParams::tzero, 1.0}}))
    TS_ASSERT_DELTA(x[0], 0.49995, 0.0001)
    TS_ASSERT(yy == y)
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
    // Finally check some c>0 for completeness - unlikely to happen
    // a>0 and c>0
    x[0] = 1.0;
    TS_ASSERT_THROWS(
        d.fromTOF(x, y, 1.0, 1, {{UnitParams::difc, 2.0}, {UnitParams::difa, 1.0}, {UnitParams::tzero, 2.0}}),
        const std::runtime_error &)
    x[0] = 1.0;
    TS_ASSERT_THROWS(
        d.fromTOF(x, y, 1.0, 1, {{UnitParams::difc, 2.0}, {UnitParams::difa, 2.0}, {UnitParams::tzero, 2.0}}),
        const std::runtime_error &)
    // a<0 and c>0
    x[0] = 1.0;
    TS_ASSERT_THROWS_NOTHING(
        d.fromTOF(x, y, 1.0, 1, {{UnitParams::difc, 2.0}, {UnitParams::difa, -3.0}, {UnitParams::tzero, 2.0}}))
    TS_ASSERT_DELTA(x[0], 1.0, 0.0001)
    TS_ASSERT(yy == y)
650
651
  }

652
653
654
655
656
  void testdSpacing_WithoutParams() {
    std::vector<double> x(1, 2.0), y(1, 1.0);
    TS_ASSERT_THROWS(d.fromTOF(x, y, 1.0, 1, {}), const std::runtime_error &)
  }

657
  void testdSpacing_quickConversions() {
658
659
660
    // Test it gives the same answer as going 'the long way'
    // To MomentumTransfer
    double factor, power;
661
    TS_ASSERT(d.quickConversion(q, factor, power))
662
    double input = 1.1;
663
664
    double result = factor * std::pow(input, power);
    std::vector<double> x(1, input);
Samuel Jones's avatar
Samuel Jones committed
665
666
    double difc =
        2.0 * Mantid::PhysicalConstants::NeutronMass * sin(0.5) * (99.0 + 99.0) * 1e-4 / Mantid::PhysicalConstants::h;
667
668
    d.toTOF(x, x, 99.0, 0, {{UnitParams::difc, difc}});
    q.fromTOF(x, x, 99.0, 0, {{UnitParams::difc, difc}});
669
    TS_ASSERT_DELTA(x[0], result, 1.0e-12)
Russell Taylor's avatar
Russell Taylor committed
670

671
    // To QSquared
672
    TS_ASSERT(d.quickConversion(q2, factor, power))
673
    input = 1.1;
674
    result = factor * std::pow(input, power);
675
    x[0] = input;
676
677
    d.toTOF(x, x, 99.0, 0, {{UnitParams::difc, difc}});
    q2.fromTOF(x, x, 99.0, 0, {{UnitParams::difc, difc}});
678
679
680
681
682
    TS_ASSERT_DELTA(x[0], result, 1.0e-12)
  }
  void testdSpacingRange() {
    std::vector<double> sample, rezult;

Samuel Jones's avatar
Samuel Jones committed
683
    double difc = 2.0 * Mantid::PhysicalConstants::NeutronMass * sin(0.5 * M_PI / 180) * (99.0 + 99.0) * 1e-4 /
684
685
                  Mantid::PhysicalConstants::h;
    d.initialize(99.0, 0, {{UnitParams::difc, difc}});
686
687
688
689
690
    std::string err_mess = convert_units_check_range(d, sample, rezult);
    TSM_ASSERT(" ERROR:" + err_mess, err_mess.size() == 0);

    for (size_t i = 0; i < sample.size(); i++) {
      if (std::fabs(sample[i]) < 10 * FLT_EPSILON) {
Samuel Jones's avatar
Samuel Jones committed
691
692
        TSM_ASSERT_DELTA("d-spacing limits Failed for conversion N: " + boost::lexical_cast<std::string>(i), sample[i],
                         rezult[i], 10 * FLT_EPSILON);
693
      } else {
Samuel Jones's avatar
Samuel Jones committed
694
        TSM_ASSERT_DELTA("d-spacing limits Failed for conversion N: " + boost::lexical_cast<std::string>(i),
695
                         rezult[i] / sample[i], 1., 10 * FLT_EPSILON);
696
697
698
699
      }
    }
  }

700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
  void test_calcTofMin() {
    const double TMIN = 300.;

    // just difc
    TS_ASSERT_EQUALS(d.calcTofMin(DIFC, 0., 0.), 0.);
    TS_ASSERT_EQUALS(d.calcTofMin(DIFC, 0., 0., TMIN), TMIN);
    // difc + tzero
    TS_ASSERT_EQUALS(d.calcTofMin(DIFC, 0., TZERO, 0.), TZERO);
    TS_ASSERT_EQUALS(d.calcTofMin(DIFC, 0., TZERO, TMIN), TMIN);

    // difc + difa + tzero
    TS_ASSERT_EQUALS(d.calcTofMin(DIFC, DIFA1, 0., 0.), 0.);
    TS_ASSERT_EQUALS(d.calcTofMin(DIFC, DIFA1, 0., TMIN), TMIN);
    TS_ASSERT_EQUALS(d.calcTofMin(DIFC, DIFA1, TZERO, 0.), TZERO - 1.);
    TS_ASSERT_EQUALS(d.calcTofMin(DIFC, DIFA1, TZERO, TMIN), TMIN);

    TS_ASSERT_EQUALS(d.calcTofMin(DIFC, DIFA2, 0., 0.), 0.);
    TS_ASSERT_EQUALS(d.calcTofMin(DIFC, DIFA2, 0., TMIN), TMIN);
    TS_ASSERT_EQUALS(d.calcTofMin(DIFC, DIFA2, TZERO, 0.), TZERO - .01);
    TS_ASSERT_EQUALS(d.calcTofMin(DIFC, DIFA2, TZERO, TMIN), TMIN);
  }

  void test_calcTofMax() {
    const double TMAX = 16666.7;
    const double TSUPERMAX = std::numeric_limits<double>::max();

    // just difc
    TS_ASSERT_EQUALS(d.calcTofMax(DIFC, 0., 0., TMAX), TMAX);
    TS_ASSERT_EQUALS(d.calcTofMax(DIFC, 0., 0., TSUPERMAX), TSUPERMAX);
    // difc + tzero
    TS_ASSERT_EQUALS(d.calcTofMax(DIFC, 0., TZERO, TMAX), TMAX);
    TS_ASSERT_EQUALS(d.calcTofMax(DIFC, 0., TZERO, TSUPERMAX), TSUPERMAX);

    // difc + difa + tzero
    TS_ASSERT_EQUALS(d.calcTofMax(DIFC, DIFA1, 0., TMAX), TMAX);
    TS_ASSERT_EQUALS(d.calcTofMax(DIFC, DIFA1, 0., TSUPERMAX), TSUPERMAX);
    TS_ASSERT_EQUALS(d.calcTofMax(DIFC, DIFA1, TZERO, TMAX), TMAX);
    TS_ASSERT_EQUALS(d.calcTofMax(DIFC, DIFA1, TZERO, TSUPERMAX), TSUPERMAX);

    TS_ASSERT_DELTA(d.calcTofMax(DIFC, DIFA3, 0., TMAX), 1., 1E-10);
    TS_ASSERT_DELTA(d.calcTofMax(DIFC, DIFA3, 0., TSUPERMAX), 1., 1E-10);
    TS_ASSERT_DELTA(d.calcTofMax(DIFC, DIFA3, TZERO, TMAX), TZERO + 1., 1E-10);
Samuel Jones's avatar
Samuel Jones committed
742
    TS_ASSERT_DELTA(d.calcTofMax(DIFC, DIFA3, TZERO, TSUPERMAX), TZERO + 1., 1E-10);
743
744
  }

745
  //----------------------------------------------------------------------
746
  // d-SpacingPerpebdicular tests
747
748
  //----------------------------------------------------------------------

Samuel Jones's avatar
Samuel Jones committed
749
  void testdSpacingPerpendicular_unitID() { TS_ASSERT_EQUALS(dp.unitID(), "dSpacingPerpendicular") }
750

Samuel Jones's avatar
Samuel Jones committed
751
  void testdSpacingPerpendicular_caption() { TS_ASSERT_EQUALS(dp.caption(), "d-SpacingPerpendicular") }
752

753
  void testdSpacingPerpendicular_label() {
754
755
756
757
    TS_ASSERT_EQUALS(dp.label().ascii(), "Angstrom")
    TS_ASSERT_EQUALS(dp.label().utf8(), L"\u212b")
  }

758
  void testdSpacingPerpendicular_cast() {
759
    Unit *u = nullptr;
760
    TS_ASSERT_THROWS_NOTHING(u = dynamic_cast<Unit *>(&dp));
761
    TS_ASSERT_EQUALS(u->unitID(), "dSpacingPerpendicular");
762
763
  }

764
  void testdSpacingPerpendicular_toTOF() {
765
766
    std::vector<double> x(1, 1.0), y(1, 1.0);
    std::vector<double> yy = y;
Samuel Jones's avatar
Samuel Jones committed
767
    TS_ASSERT_THROWS_NOTHING(dp.toTOF(x, y, 1.0, 1, {{UnitParams::l2, 1.0}, {UnitParams::twoTheta, 1.0}}))
768
769
770
771
    TS_ASSERT_DELTA(x[0], 434.5529, 0.0001)
    TS_ASSERT(yy == y)
  }

772
  void testdSpacingPerpendicular_fromTOF() {
773
774
    std::vector<double> x(1, 1001.1), y(1, 1.0);
    std::vector<double> yy = y;
Samuel Jones's avatar
Samuel Jones committed
775
    TS_ASSERT_THROWS_NOTHING(dp.fromTOF(x, y, 1.0, 1, {{UnitParams::l2, 1.0}, {UnitParams::twoTheta, 1.0}}))
776
777
778
779
    TS_ASSERT_DELTA(x[0], 2.045075, 0.000001)
    TS_ASSERT(yy == y)
  }

780
  void testdSpacingPerpendicularRange() {
781
782
783
784
785
786
787
    std::vector<double> sample, rezult;

    std::string err_mess = convert_units_check_range(dp, sample, rezult);
    TSM_ASSERT(" ERROR:" + err_mess, err_mess.size() == 0);

    for (size_t i = 0; i < sample.size(); i++) {
      if (std::fabs(sample[i]) < 10 * FLT_EPSILON) {
Samuel Jones's avatar
Samuel Jones committed
788
789
790
        TSM_ASSERT_DELTA("d-spacingPerpendicular limits Failed for conversion N: " +
                             boost::lexical_cast<std::string>(i),
                         sample[i], rezult[i], 10 * FLT_EPSILON);
791
      } else {
Samuel Jones's avatar
Samuel Jones committed
792
793
794
        TSM_ASSERT_DELTA("d-spacingPerpendicular limits Failed for conversion N: " +
                             boost::lexical_cast<std::string>(i),
                         rezult[i] / sample[i], 1., 10 * FLT_EPSILON);
795
796
797
798
      }
    }
  }

799
800
801
802
803
  void testdSpacingPerpendicular_WithoutParams() {
    std::vector<double> x(1, 2.0), y(1, 1.0);
    TS_ASSERT_THROWS(dp.fromTOF(x, y, 1.0, 1, {}), const std::runtime_error &)
  }

804
805
806
807
  //----------------------------------------------------------------------
  // Momentum Transfer tests
  //----------------------------------------------------------------------

Samuel Jones's avatar
Samuel Jones committed
808
  void testQTransfer_unitID() { TS_ASSERT_EQUALS(q.unitID(), "MomentumTransfer") }
809

810
  void testQTransfer_caption() { TS_ASSERT_EQUALS(q.caption(), "q") }
811

812
813
814
  void testQTransfer_label() {
    TS_ASSERT_EQUALS(q.label().ascii(), "Angstrom^-1")
    TS_ASSERT_EQUALS(q.label().utf8(), L"\u212b\u207b\u00b9")
815
816
  }

817
  void testQTransfer_cast() {
818
    Unit *u = nullptr;
819
    TS_ASSERT_THROWS_NOTHING(u = dynamic_cast<Unit *>(&q));