ExperimentInfoTest.h 39.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_API_EXPERIMENTINFOTEST_H_
#define MANTID_API_EXPERIMENTINFOTEST_H_

10
#include "MantidAPI/ChopperModel.h"
LamarMoore's avatar
LamarMoore committed
11
#include "MantidAPI/ExperimentInfo.h"
12
#include "MantidAPI/ModeratorModel.h"
13
14
#include "MantidAPI/Run.h"
#include "MantidAPI/Sample.h"
15
#include "MantidAPI/SpectrumInfo.h"
16
#include "MantidGeometry/Crystal/OrientedLattice.h"
17
18
#include "MantidGeometry/Instrument/ComponentInfo.h"
#include "MantidGeometry/Instrument/Detector.h"
LamarMoore's avatar
LamarMoore committed
19
#include "MantidGeometry/Instrument/DetectorGroup.h"
20
#include "MantidGeometry/Instrument/DetectorInfo.h"
21
#include "MantidKernel/ConfigService.h"
22
#include "MantidKernel/DateAndTime.h"
23
#include "MantidKernel/Matrix.h"
LamarMoore's avatar
LamarMoore committed
24
#include "MantidKernel/SingletonHolder.h"
25

Karl Palmen's avatar
Karl Palmen committed
26
#include "MantidAPI/FileFinder.h"
27
#include "MantidTestHelpers/ComponentCreationHelper.h"
28
#include "MantidTestHelpers/NexusTestHelper.h"
29
#include "PropertyManagerHelper.h"
30

31
// clang-format off
Karl Palmen's avatar
Karl Palmen committed
32
33
#include <nexus/NeXusFile.hpp>
#include <nexus/NeXusException.hpp>
34
// clang-format on
Karl Palmen's avatar
Karl Palmen committed
35

36
#include <cxxtest/TestSuite.h>
37
38
39
#include <boost/regex.hpp>
#include <Poco/DirectoryIterator.h>

40
#include <set>
41
#include <unordered_map>
42
43

using namespace Mantid::API;
44
using namespace Mantid::Kernel;
45
using namespace Mantid::Geometry;
Karl Palmen's avatar
Karl Palmen committed
46
using namespace NeXus;
47
using Mantid::Types::Core::DateAndTime;
48

49
class FakeChopper : public Mantid::API::ChopperModel {
50
public:
51
  boost::shared_ptr<ChopperModel> clone() const override {
52
    return boost::make_shared<FakeChopper>(*this);
53
  }
54

55
56
57
  double calculatePulseTimeVariance() const override { return 0.0; }
  double sampleTimeDistribution(const double) const override { return 0.0; }
  double sampleJitterDistribution(const double) const override { return 0.0; }
58

59
private:
60
  void setParameterValue(const std::string &, const std::string &) override{};
61
62
};

63
class FakeSource : public Mantid::API::ModeratorModel {
64
public:
65
  boost::shared_ptr<ModeratorModel> clone() const override {
66
67
    return boost::make_shared<FakeSource>(*this);
  }
68
69
70
  double emissionTimeMean() const override { return 0.0; }
  double emissionTimeVariance() const override { return 0.0; }
  double sampleTimeDistribution(const double) const override { return 0.0; }
71

72
private:
73
  void setParameterValue(const std::string &, const std::string &) override{};
74
75
};

76
class ExperimentInfoTest : public CxxTest::TestSuite {
77
public:
78
79
80
  // This pair of boilerplate methods prevent the suite being created statically
  // This means the constructor isn't called when running other tests
  static ExperimentInfoTest *createSuite() { return new ExperimentInfoTest(); }
81
  static void destroySuite(ExperimentInfoTest *suite) { delete suite; }
82

83
  ExperimentInfoTest() { ConfigService::Instance().updateFacilities(); }
84

85
  void test_GetInstrument_default() {
86
    ExperimentInfo ws;
87
    boost::shared_ptr<const Instrument> i = ws.getInstrument();
88
    TSM_ASSERT("ExperimentInfo gets a default, empty Instrument.", i);
89
    TS_ASSERT(i->isEmptyInstrument());
90
    TS_ASSERT_EQUALS(ws.getInstrument()->type(), "Instrument");
91

92
93
    // Should be set even though we have just an empty instrument.
    TS_ASSERT(i->getParameterMap()->hasDetectorInfo(i->baseInstrument().get()));
94
95
  }

96
  void test_GetSetInstrument_default() {
97
    ExperimentInfo ws;
98
    boost::shared_ptr<Instrument> inst1 = boost::make_shared<Instrument>();
99
100
101
    inst1->setName("MyTestInst");
    ws.setInstrument(inst1);

102
103
    // Instruments don't point to the same base place since you get back a
    // parameterized one
104
    boost::shared_ptr<const Instrument> inst2 = ws.getInstrument();
105
    TS_ASSERT_EQUALS(inst2->getName(), "MyTestInst");
106
107

    // But the base instrument does!
108
    boost::shared_ptr<const Instrument> inst3 = inst2->baseInstrument();
109
110
    TS_ASSERT_EQUALS(inst3.get(), inst1.get());
    TS_ASSERT_EQUALS(inst3->getName(), "MyTestInst");
111
112
  }

113
  void test_Setting_A_New_Source_With_NULL_Ptr_Throws() {
114
115
    ExperimentInfo ws;

116
    TS_ASSERT_THROWS(ws.setModeratorModel(nullptr), std::invalid_argument);
117
118
  }

119
  void test_Retrieving_Source_Properties_Before_Set_Throws() {
120
121
122
123
124
    ExperimentInfo ws;

    TS_ASSERT_THROWS(ws.moderatorModel(), std::runtime_error);
  }

125
  void test_Setting_New_Source_Description_With_Valid_Object_Does_Not_Throw() {
126
127
128
    using namespace Mantid::API;
    ExperimentInfo ws;

129
    ModeratorModel *source = new FakeSource;
130
    TS_ASSERT_THROWS_NOTHING(ws.setModeratorModel(source));
131
132
133
    const ModeratorModel &fetched = ws.moderatorModel();
    const ModeratorModel &constInput =
        const_cast<const Mantid::API::ModeratorModel &>(*source);
134
135
136
    TS_ASSERT_EQUALS(&fetched, &constInput);
  }

137
  void test_Setting_A_New_Chopper_With_NULL_Ptr_Throws() {
138
139
    ExperimentInfo_sptr ws = createTestInfoWithChopperPoints(1);

140
    TS_ASSERT_THROWS(ws->setChopperModel(nullptr), std::invalid_argument);
141
142
  }

143
  void test_Setting_A_New_Chopper_To_Point_Lower_Point_Succeeds() {
144
145
146
147
148
149
    ExperimentInfo_sptr ws = createTestInfoWithChopperPoints(1);

    TS_ASSERT_THROWS_NOTHING(ws->setChopperModel(new FakeChopper));
    TS_ASSERT_THROWS_NOTHING(ws->chopperModel(0));
  }

150
  void test_Setting_A_New_Chopper_To_Existing_Index_Replaces_Current() {
151
152
153
154
155
156
    ExperimentInfo_sptr ws = createTestInfoWithChopperPoints(1);

    TS_ASSERT_THROWS_NOTHING(ws->setChopperModel(new FakeChopper));
    TS_ASSERT_THROWS(ws->chopperModel(1), std::invalid_argument);
  }

157
  void test_Getting_Chopper_At_Index_Greater_Than_Descriptions_Added_Throws() {
158
159
160
161
162
    ExperimentInfo_sptr ws = createTestInfoWithChopperPoints(1);

    TS_ASSERT_THROWS(ws->chopperModel(2), std::invalid_argument);
  }

163
  void test_GetSetSample() {
164
165
    ExperimentInfo ws;
    ws.mutableSample().setName("test");
166
    TS_ASSERT_EQUALS(ws.sample().getName(), "test");
167
168
  }

169
  void test_GetSetRun() {
170
171
    ExperimentInfo ws;
    ws.mutableRun().setProtonCharge(1.234);
172
    TS_ASSERT_DELTA(ws.run().getProtonCharge(), 1.234, 0.001);
173
174
  }

175
  void test_GetLog_Throws_If_No_Log_Or_Instrument_Parameter_Exists() {
176
177
178
179
180
    ExperimentInfo expt;

    TS_ASSERT_THROWS(expt.getLog("__NOTALOG__"), std::invalid_argument);
  }

181
182
  void
  test_GetLog_Throws_If_Instrument_Contains_LogName_Parameter_But_Log_Does_Not_Exist() {
183
184
185
186
187
    ExperimentInfo expt;
    const std::string instPar = "temperature_log";
    const std::string actualLogName = "SAMPLE_TEMP";
    addInstrumentWithParameter(expt, instPar, actualLogName);

188
189
    TS_ASSERT_THROWS(expt.getLog(instPar),
                     Mantid::Kernel::Exception::NotFoundError);
190
191
  }

192
193
  void
  test_GetLog_Returns_Value_Of_Log_Named_In_Instrument_Parameter_If_It_Exists_And_Actual_Log_Entry_Exists() {
194
195
196
197
198
199
200
    ExperimentInfo expt;
    const std::string instPar = "temperature_log";
    const std::string actualLogName = "SAMPLE_TEMP";
    const double logValue(7.4);
    addRunWithLog(expt, actualLogName, logValue);
    addInstrumentWithParameter(expt, instPar, actualLogName);

201
    Property *log = nullptr;
202
    TS_ASSERT_THROWS_NOTHING(log = expt.getLog(instPar));
203
204
    if (log)
      TS_ASSERT_EQUALS(log->name(), actualLogName);
205
206
  }

207
  void test_GetLog_Picks_Run_Log_Over_Instrument_Parameter_Of_Same_Name() {
208
209
210
211
212
213
    ExperimentInfo expt;
    const std::string actualLogName = "SAMPLE_TEMP";
    const double logValue(7.4);
    addRunWithLog(expt, actualLogName, logValue);
    addInstrumentWithParameter(expt, actualLogName, "some  value");

214
    Property *log = nullptr;
215
    TS_ASSERT_THROWS_NOTHING(log = expt.getLog(actualLogName));
216
217
    if (log)
      TS_ASSERT_EQUALS(log->name(), actualLogName);
218
219
  }

220
221
  void
  test_GetLogAsSingleValue_Throws_If_No_Log_Or_Instrument_Parameter_Exists() {
222
223
    ExperimentInfo expt;

224
225
    TS_ASSERT_THROWS(expt.getLogAsSingleValue("__NOTALOG__"),
                     std::invalid_argument);
226
227
  }

228
229
  void
  test_GetLogAsSingleValue_Throws_If_Instrument_Contains_LogName_Parameter_But_Log_Does_Not_Exist() {
230
231
232
233
234
    ExperimentInfo expt;
    const std::string instPar = "temperature_log";
    const std::string actualLogName = "SAMPLE_TEMP";
    addInstrumentWithParameter(expt, instPar, actualLogName);

235
236
    TS_ASSERT_THROWS(expt.getLogAsSingleValue(instPar),
                     Mantid::Kernel::Exception::NotFoundError);
237
238
  }

239
240
  void
  test_GetLogAsSingleValue_Returns_Value_Of_Log_Named_In_Instrument_Parameter_If_It_Exists_And_Actual_Log_Entry_Exists() {
241
242
243
244
245
246
247
248
249
250
251
252
    ExperimentInfo expt;
    const std::string instPar = "temperature_log";
    const std::string actualLogName = "SAMPLE_TEMP";
    const double logValue(9.10);
    addRunWithLog(expt, actualLogName, logValue);
    addInstrumentWithParameter(expt, instPar, actualLogName);

    double value(-1.0);
    TS_ASSERT_THROWS_NOTHING(value = expt.getLogAsSingleValue(instPar));
    TS_ASSERT_DELTA(value, logValue, 1e-12);
  }

253
254
  void
  test_GetLogAsSingleValue_Picks_Run_Log_Over_Instrument_Parameter_Of_Same_Name() {
255
256
257
258
259
260
261
262
263
264
    ExperimentInfo expt;
    const std::string actualLogName = "SAMPLE_TEMP";
    const double logValue(11.5);
    addInstrumentWithParameter(expt, actualLogName, "some  value");
    addRunWithLog(expt, actualLogName, logValue);

    double value(-1.0);
    TS_ASSERT_THROWS_NOTHING(value = expt.getLogAsSingleValue(actualLogName));
    TS_ASSERT_DELTA(value, logValue, 1e-12);
  }
265

266
267
268
269
270
271
272
  void do_compare_ExperimentInfo(ExperimentInfo &ws, ExperimentInfo &ws2) {
    TS_ASSERT_EQUALS(ws2.sample().getName(), "test");
    TS_ASSERT_DELTA(ws2.sample().getOrientedLattice().a(), 1.0, 1e-4);
    TS_ASSERT_DELTA(ws2.sample().getOrientedLattice().b(), 2.0, 1e-4);
    TS_ASSERT_DELTA(ws2.sample().getOrientedLattice().c(), 3.0, 1e-4);
    TS_ASSERT_DELTA(ws2.run().getProtonCharge(), 1.234, 0.001);
    TS_ASSERT_EQUALS(ws2.getInstrument()->getName(), "MyTestInst");
273
    TS_ASSERT_DIFFERS(&ws.moderatorModel(), &ws2.moderatorModel());
274
275
276
277
278
279

    // Changing stuff in the original workspace...
    ws.mutableSample().setName("test1");
    ws.mutableRun().setProtonCharge(2.345);

    // ... does not change the copied one.
280
281
    TS_ASSERT_EQUALS(ws2.sample().getName(), "test");
    TS_ASSERT_DELTA(ws2.run().getProtonCharge(), 1.234, 0.001);
282
283

    // The original oriented lattice is ok
284
285
286
    TS_ASSERT_DELTA(ws.sample().getOrientedLattice().a(), 1.0, 1e-4);
    TS_ASSERT_DELTA(ws.sample().getOrientedLattice().b(), 2.0, 1e-4);
    TS_ASSERT_DELTA(ws.sample().getOrientedLattice().c(), 3.0, 1e-4);
287
288
  }

289
  void test_copyExperimentInfoFrom() {
290
291
292
    ExperimentInfo ws;
    ws.mutableRun().setProtonCharge(1.234);
    ws.mutableSample().setName("test");
293
294
    OrientedLattice latt(1, 2, 3, 90, 90, 90);
    ws.mutableSample().setOrientedLattice(&latt);
295
    boost::shared_ptr<Instrument> inst1 = boost::make_shared<Instrument>();
296
297
    inst1->setName("MyTestInst");
    ws.setInstrument(inst1);
298
299
    ws.setModeratorModel(new FakeSource);
    ws.setChopperModel(new FakeChopper);
300
301

    ExperimentInfo ws2;
302
303
    ws2.copyExperimentInfoFrom(&ws);
    do_compare_ExperimentInfo(ws, ws2);
304
305
  }

306
  void test_clone() {
307
308
309
    ExperimentInfo ws;
    ws.mutableRun().setProtonCharge(1.234);
    ws.mutableSample().setName("test");
310
311
    OrientedLattice latt(1, 2, 3, 90, 90, 90);
    ws.mutableSample().setOrientedLattice(&latt);
312
    boost::shared_ptr<Instrument> inst1 = boost::make_shared<Instrument>();
313
314
    inst1->setName("MyTestInst");
    ws.setInstrument(inst1);
315
316
    ws.setModeratorModel(new FakeSource);
    ws.setChopperModel(new FakeChopper);
317
318
319

    ExperimentInfo *ws2 = ws.cloneExperimentInfo();
    do_compare_ExperimentInfo(ws, *ws2);
320
    delete ws2;
321
322
  }

323
  void test_clone_then_copy() {
324
325
326
    ExperimentInfo ws;
    ws.mutableRun().setProtonCharge(1.234);
    ws.mutableSample().setName("test");
327
328
    OrientedLattice latt(1, 2, 3, 90, 90, 90);
    ws.mutableSample().setOrientedLattice(&latt);
329
    boost::shared_ptr<Instrument> inst1 = boost::make_shared<Instrument>();
330
331
    inst1->setName("MyTestInst");
    ws.setInstrument(inst1);
332
333
    ws.setModeratorModel(new FakeSource);
    ws.setChopperModel(new FakeChopper);
334

335
    ExperimentInfo *ws2 = ws.cloneExperimentInfo();
336
337
338
339

    ExperimentInfo ws3;
    ws3.copyExperimentInfoFrom(ws2);

340
    do_compare_ExperimentInfo(ws, ws3);
341
342

    delete ws2;
343
344
  }

345
  void test_default_emode_is_elastic() {
346
347
348
349
350
    ExperimentInfo exptInfo;

    TS_ASSERT_EQUALS(exptInfo.getEMode(), Mantid::Kernel::DeltaEMode::Elastic);
  }

351
  void test_runlog_with_emode_returns_correct_mode() {
352
353
354
355
356
    ExperimentInfo_sptr exptInfo = createTestInfoWithDirectEModeLog();

    TS_ASSERT_EQUALS(exptInfo->getEMode(), Mantid::Kernel::DeltaEMode::Direct);
  }

357
  void test_runlog_with_emode_overrides_instrument_emode() {
358
359
360
361
362
363
    ExperimentInfo_sptr exptInfo = createTestInfoWithDirectEModeLog();
    addInstrumentWithIndirectEmodeParameter(exptInfo);

    TS_ASSERT_EQUALS(exptInfo->getEMode(), Mantid::Kernel::DeltaEMode::Direct);
  }

364
  void test_runlog_with_only_instrument_emode_uses_this() {
365
366
367
    ExperimentInfo_sptr exptInfo(new ExperimentInfo);
    addInstrumentWithIndirectEmodeParameter(exptInfo);

368
369
    TS_ASSERT_EQUALS(exptInfo->getEMode(),
                     Mantid::Kernel::DeltaEMode::Indirect);
370
371
  }

372
  void test_getEFixed_throws_exception_if_detID_does_not_exist() {
373
374
    ExperimentInfo_sptr exptInfo = createTestInfoWithDirectEModeLog();

375
376
    TS_ASSERT_THROWS(exptInfo->getEFixed(1),
                     Mantid::Kernel::Exception::NotFoundError);
377
378
  }

379
  void test_correct_efixed_value_is_returned_for_direct_run() {
380
381
382
383
384
385
386
    ExperimentInfo_sptr exptInfo = createTestInfoWithDirectEModeLog();
    const double test_ei(15.1);
    exptInfo->mutableRun().addProperty("Ei", test_ei);

    TS_ASSERT_EQUALS(exptInfo->getEFixed(), test_ei);
  }

387
  void test_getEfixed_throws_for_indirect_mode_and_no_detector_passed() {
388
389
390
391
392
393
    ExperimentInfo_sptr exptInfo(new ExperimentInfo);
    Instrument_sptr inst = addInstrumentWithIndirectEmodeParameter(exptInfo);

    TS_ASSERT_THROWS(exptInfo->getEFixed(), std::runtime_error);
  }

394
395
  void
  test_getEfixed_throws_for_indirect_mode_when_passed_a_detector_without_parameter() {
396
397
398
399
400
401
402
    ExperimentInfo_sptr exptInfo(new ExperimentInfo);
    addInstrumentWithIndirectEmodeParameter(exptInfo);
    IDetector_const_sptr det = exptInfo->getInstrument()->getDetector(3);

    TS_ASSERT_THROWS(exptInfo->getEFixed(det), std::runtime_error);
  }

403
404
  void
  test_getEfixed_in_indirect_mode_returns_detector_level_EFixed_parameter() {
405
406
407
408
409
    ExperimentInfo_sptr exptInfo(new ExperimentInfo);
    Instrument_sptr inst = addInstrumentWithIndirectEmodeParameter(exptInfo);
    const double test_ef(32.7);
    const Mantid::detid_t test_id = 3;
    IDetector_const_sptr det = exptInfo->getInstrument()->getDetector(test_id);
410
    ParameterMap &pmap = exptInfo->instrumentParameters();
411
412
413
414
415
416
    pmap.addDouble(det.get(), "Efixed", test_ef);

    TS_ASSERT_EQUALS(exptInfo->getEFixed(det), test_ef);
    TS_ASSERT_EQUALS(exptInfo->getEFixed(test_id), test_ef);
  }

417
418
  void
  test_getEfixed_in_indirect_mode_looks_recursively_for_Efixed_parameter() {
419
420
421
422
423
424
425
426
427
428
429
    ExperimentInfo_sptr exptInfo(new ExperimentInfo);
    Instrument_sptr inst = addInstrumentWithIndirectEmodeParameter(exptInfo);
    const double test_ef(32.7);
    const Mantid::detid_t test_id = 3;
    exptInfo->instrumentParameters().addDouble(inst.get(), "Efixed", test_ef);
    IDetector_const_sptr det = exptInfo->getInstrument()->getDetector(test_id);

    TS_ASSERT_EQUALS(exptInfo->getEFixed(det), test_ef);
    TS_ASSERT_EQUALS(exptInfo->getEFixed(test_id), test_ef);
  }

430
  void test_accessing_SpectrumInfo_creates_default_grouping() {
431
    using namespace Mantid;
432
433
434
    ExperimentInfo_sptr exptInfo(new ExperimentInfo);
    addInstrumentWithParameter(*exptInfo, "a", "b");

435
436
437
438
439
    const auto &spectrumInfo = exptInfo->spectrumInfo();
    const auto *detGroup = dynamic_cast<const Geometry::DetectorGroup *>(
        &spectrumInfo.detector(0));
    TS_ASSERT(!detGroup);
    TS_ASSERT_EQUALS(spectrumInfo.detector(0).getID(), 1);
440
441
442
443
444
445
  }

  void test_cacheDetectorGroupings_creates_correct_SpectrumInfo() {
    using namespace Mantid;
    ExperimentInfo_sptr exptInfo(new ExperimentInfo);
    addInstrumentWithParameter(*exptInfo, "a", "b");
446

447
    // Set a mapping
448
    std::set<detid_t> group{1, 2};
449
    Mantid::det2group_map mapping{{1, group}};
450
    exptInfo->cacheDetectorGroupings(mapping);
451

452
453
454
455
456
    const auto &spectrumInfo = exptInfo->spectrumInfo();
    const auto *detGroup = dynamic_cast<const Geometry::DetectorGroup *>(
        &spectrumInfo.detector(0));
    TS_ASSERT(detGroup);
    TS_ASSERT_EQUALS(detGroup->getDetectorIDs(), (std::vector<detid_t>{1, 2}));
457
458
  }

459
  void test_Setting_Group_Lookup_To_Empty_Map_Does_Not_Throw() {
460
    ExperimentInfo expt;
Martyn Gigg's avatar
Martyn Gigg committed
461
    Mantid::det2group_map mappings;
462
463
464
465

    TS_ASSERT_THROWS_NOTHING(expt.cacheDetectorGroupings(mappings));
  }

466
  void test_Getting_Group_For_Unknown_ID_Throws() {
467
468
    ExperimentInfo expt;

469
    TS_ASSERT_THROWS(expt.groupOfDetectorID(1), std::out_of_range);
470
471
  }

472
  void
473
  test_Setting_Group_Lookup_To_Non_Empty_Map_Allows_Retrieval_Of_Correct_Group() {
474
    ExperimentInfo expt;
Martyn Gigg's avatar
Martyn Gigg committed
475
    Mantid::det2group_map mappings;
476
    mappings.emplace(1, std::set<Mantid::detid_t>{2});
477
478
    expt.cacheDetectorGroupings(mappings);

Peterson, Peter's avatar
Peterson, Peter committed
479
    size_t index{0};
480
481
    TS_ASSERT_THROWS_NOTHING(index = expt.groupOfDetectorID(1));
    TS_ASSERT_EQUALS(index, 0);
482
  }
483

484
  struct fromToEntry {
485
486
487
488
489
490
    std::string path;
    DateAndTime from;
    DateAndTime to;
  };

  // Test that all the IDFs contain valid-to and valid-from dates and that
491
  // for a single instrument none of the valid-from dates are equal
492
  void testAllDatesInIDFs() {
493
494
495
496
    ExperimentInfo helper;

    // Collect all IDF filenames and put them in a multimap where the instrument
    // identifier is the key
497
    std::unordered_multimap<std::string, fromToEntry> idfFiles;
498
    std::unordered_set<std::string> idfIdentifiers;
499

500
    boost::regex regex(".*_Definition.*\\.xml", boost::regex_constants::icase);
501
    Poco::DirectoryIterator end_iter;
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
    for (Poco::DirectoryIterator dir_itr(ConfigService::Instance().getString(
             "instrumentDefinition.directory"));
         dir_itr != end_iter; ++dir_itr) {
      if (!Poco::File(dir_itr->path()).isFile())
        continue;

      std::string l_filenamePart = Poco::Path(dir_itr->path()).getFileName();

      if (boost::regex_match(l_filenamePart, regex)) {
        std::string validFrom, validTo;
        helper.getValidFromTo(dir_itr->path(), validFrom, validTo);

        size_t found;
        found = l_filenamePart.find("_Definition");
        fromToEntry ft;
        ft.path = dir_itr->path();
        ft.from.setFromISO8601(validFrom);
        // Valid TO is optional
        if (validTo.length() > 0)
          ft.to.setFromISO8601(validTo);
        else
          ft.to.setFromISO8601("2100-01-01T00:00:00");

525
        idfFiles.emplace(l_filenamePart.substr(0, found), ft);
526
527
        idfIdentifiers.insert(l_filenamePart.substr(0, found));
      }
528
529
530
    }

    // iterator to browse through the multimap: paramInfoFromIDF
531
532
    std::unordered_multimap<std::string, fromToEntry>::const_iterator it1, it2;
    std::pair<std::unordered_multimap<std::string, fromToEntry>::iterator,
LamarMoore's avatar
LamarMoore committed
533
534
              std::unordered_multimap<std::string, fromToEntry>::iterator>
        ret;
535

536
    for (const auto &idfIdentifier : idfIdentifiers) {
537
      ret = idfFiles.equal_range(idfIdentifier);
538
539
540
      for (it1 = ret.first; it1 != ret.second; ++it1) {
        for (it2 = ret.first; it2 != ret.second; ++it2) {
          if (it1 != it2) {
541
542
            // some more intelligent stuff here later
            std::stringstream messageBuffer;
543
544
545
546
547
548
549
            messageBuffer
                << "Two IDFs for one instrument have equal valid-from dates"
                << "IDFs are: " << it1->first << " and " << it2->first
                << " Date One: " << it1->second.from.toFormattedString()
                << " Date Two: " << it2->second.from.toFormattedString();
            TSM_ASSERT_DIFFERS(messageBuffer.str(), it2->second.from,
                               it1->second.from);
550
551
552
553
554
555
556
          }
        }
      }
    }
  }

  //
557
  void testHelperFunctions() {
558
    ConfigService::Instance().updateFacilities();
559
    ExperimentInfo helper;
560
561
    std::string boevs =
        helper.getInstrumentFilename("BIOSANS", "2100-01-31 22:59:59");
562
563
564
565
    TS_ASSERT(!boevs.empty());
  }

  //
566
  void testHelper_TOPAZ_No_To_Date() {
567
    ExperimentInfo helper;
568
569
    std::string boevs =
        helper.getInstrumentFilename("TOPAZ", "2011-01-31 22:59:59");
570
571
572
    TS_ASSERT(!boevs.empty());
  }

573
574
575
  void testHelper_ValidDateOverlap() {
    const std::string instDir =
        ConfigService::Instance().getInstrumentDirectory();
576
    const std::string testDir = instDir + "unit_testing";
577
578
    ConfigService::Instance().setString("instrumentDefinition.directory",
                                        testDir);
579
    ExperimentInfo helper;
580
581
582
    std::string boevs =
        helper.getInstrumentFilename("ARGUS", "1909-01-31 22:59:59");
    TS_ASSERT_DIFFERS(boevs.find("TEST1_ValidDateOverlap"), std::string::npos);
583
    boevs = helper.getInstrumentFilename("ARGUS", "1909-03-31 22:59:59");
584
    TS_ASSERT_DIFFERS(boevs.find("TEST2_ValidDateOverlap"), std::string::npos);
585
    boevs = helper.getInstrumentFilename("ARGUS", "1909-05-31 22:59:59");
586
587
588
    TS_ASSERT_DIFFERS(boevs.find("TEST1_ValidDateOverlap"), std::string::npos);
    ConfigService::Instance().setString("instrumentDefinition.directory",
                                        instDir);
Jose Borreguero's avatar
Jose Borreguero committed
589
590
591
592

    std::vector<std::string> formats = {"xml"};
    std::vector<std::string> dirs;
    dirs.push_back(testDir);
593
594
595
596
    std::vector<std::string> fnames = helper.getResourceFilenames(
        "ARGUS", formats, dirs, "1909-01-31 22:59:59");
    TS_ASSERT_DIFFERS(fnames[0].find("TEST1_ValidDateOverlap"),
                      std::string::npos);
Jose Borreguero's avatar
Jose Borreguero committed
597
    TS_ASSERT_EQUALS(fnames.size(), 1);
598
599
600
601
602
603
604
605
606
607
    fnames = helper.getResourceFilenames("ARGUS", formats, dirs,
                                         "1909-03-31 22:59:59");
    TS_ASSERT_DIFFERS(fnames[0].find("TEST2_ValidDateOverlap"),
                      std::string::npos);
    TS_ASSERT_DIFFERS(fnames[1].find("TEST1_ValidDateOverlap"),
                      std::string::npos);
    fnames = helper.getResourceFilenames("ARGUS", formats, dirs,
                                         "1909-05-31 22:59:59");
    TS_ASSERT_DIFFERS(fnames[0].find("TEST1_ValidDateOverlap"),
                      std::string::npos);
Jose Borreguero's avatar
Jose Borreguero committed
608
    TS_ASSERT_EQUALS(fnames.size(), 1);
609
610
  }

Owen Arnold's avatar
Owen Arnold committed
611
612
613
  void test_nexus_geometry_getInstrumentFilename() {
    const std::string instrumentName = "LOKI";
    ExperimentInfo info;
614
    const auto path = info.getInstrumentFilename(instrumentName, "");
Owen Arnold's avatar
Owen Arnold committed
615
616
617
618
619
    TS_ASSERT(!path.empty());
    TS_ASSERT(
        boost::regex_match(path, boost::regex(".*LOKI_Definition\\.hdf5$")));
  }

620
  void test_nexus() {
Nick Draper's avatar
Nick Draper committed
621
    std::string filename = "ExperimentInfoTest1.nxs";
622
    NexusTestHelper th(true);
Nick Draper's avatar
Nick Draper committed
623
    th.createFile(filename);
624
    ExperimentInfo ws;
625
    boost::shared_ptr<Instrument> inst1 = boost::make_shared<Instrument>();
626
627
628
    inst1->setName("GEM");
    inst1->setFilename("GEM_Definition.xml");
    inst1->setXmlText("");
629
    ws.setInstrument(inst1);
630
631

    TS_ASSERT_THROWS_NOTHING(ws.saveExperimentInfoNexus(th.file););
632
633
634
635
636

    // ------------------------ Re-load the contents ----------------------
    ExperimentInfo ws2;
    std::string parameterStr;
    th.reopenFile();
637
638
    TS_ASSERT_THROWS_NOTHING(
        ws2.loadExperimentInfoNexus(filename, th.file, parameterStr));
639
    Instrument_const_sptr inst = ws2.getInstrument();
640
641
642
643
    TS_ASSERT_EQUALS(inst->getName(), "GEM");
    TS_ASSERT(inst->getFilename().find("GEM_Definition.xml", 0) !=
              std::string::npos);
    TS_ASSERT_EQUALS(parameterStr, "");
644
645
  }

646
  void test_nexus_empty_instrument() {
Nick Draper's avatar
Nick Draper committed
647
    std::string filename = "ExperimentInfoTest2.nxs";
648
    NexusTestHelper th(true);
Nick Draper's avatar
Nick Draper committed
649
    th.createFile(filename);
650
    ExperimentInfo ws;
651
    boost::shared_ptr<Instrument> inst1 = boost::make_shared<Instrument>();
652
653
654
655
656
    inst1->setName("");
    inst1->setFilename("");
    inst1->setXmlText("");
    ws.setInstrument(inst1);

657
    TS_ASSERT_THROWS_NOTHING(ws.saveExperimentInfoNexus(th.file););
658
659
660
661
662

    // ------------------------ Re-load the contents ----------------------
    ExperimentInfo ws2;
    std::string parameterStr;
    th.reopenFile();
663
664
    TS_ASSERT_THROWS_NOTHING(
        ws2.loadExperimentInfoNexus(filename, th.file, parameterStr));
665
    Instrument_const_sptr inst = ws2.getInstrument();
666
667
    TS_ASSERT_EQUALS(inst->getName(), "");
    TS_ASSERT_EQUALS(parameterStr, "");
668
669
  }

670
671
672
673
674
  void testNexus_W_matrix() {
    std::string filename = "ExperimentInfoWMatrixTest.nxs";
    NexusTestHelper th(true);
    th.createFile(filename);
    ExperimentInfo ei;
675

676
677
678
679
680
681
682
683
    DblMatrix WTransf(3, 3, true);
    // let's add some tricky stuff to w-transf
    WTransf[0][1] = 0.5;
    WTransf[0][2] = 2.5;
    WTransf[1][0] = 10.5;
    WTransf[1][2] = 12.5;
    WTransf[2][0] = 20.5;
    WTransf[2][1] = 21.5;
684

685
    auto wTrVector = WTransf.getVector();
686

687
688
    // this occurs in ConvertToMD, copy methadata
    ei.mutableRun().addProperty("W_MATRIX", wTrVector, true);
689

690
    TS_ASSERT_THROWS_NOTHING(ei.saveExperimentInfoNexus(th.file));
691

692
    th.reopenFile();
693

694
695
696
697
    ExperimentInfo other;
    std::string InstrParameters;
    TS_ASSERT_THROWS_NOTHING(
        other.loadExperimentInfoNexus(filename, th.file, InstrParameters));
698

699
700
    std::vector<double> wMatrRestored =
        other.run().getPropertyValueAsType<std::vector<double>>("W_MATRIX");
701

702
703
704
    for (int i = 0; i < 9; i++) {
      TS_ASSERT_DELTA(wTrVector[i], wMatrRestored[i], 1.e-9);
    }
705
706
  }

707
  void test_nexus_instrument_info() {
708
    ExperimentInfo ei;
Karl Palmen's avatar
Karl Palmen committed
709

710
711
    // We get an instrument group from a test file in the form that would occur
    // in an ISIS Nexus file
712
    // with an embedded instrument definition and parameters
Karl Palmen's avatar
Karl Palmen committed
713
714

    // Create the root Nexus class
715
716
    std::string testFile = "LOQinstrument.h5";
    std::string path = FileFinder::Instance().getFullPath(testFile);
717

718
    // Get nexus file for this.
719
    ::NeXus::File nxFile(path, NXACC_READ);
Karl Palmen's avatar
Karl Palmen committed
720
721

    // Load the Nexus IDF info
Karl Palmen's avatar
Karl Palmen committed
722
    std::string params;
723
724
    TS_ASSERT_THROWS_NOTHING(
        ei.loadInstrumentInfoNexus(testFile, &nxFile, params));
725
    Instrument_const_sptr inst = ei.getInstrument();
726
727
    TS_ASSERT_EQUALS(inst->getName(), "LOQ"); // Check instrument name
    TS_ASSERT_EQUALS(params.size(), 613);     // Check size of parameter string
Karl Palmen's avatar
Karl Palmen committed
728
729
  }

730
731
  void test_nexus_parameters() {
    ExperimentInfo ei;
732

733
734
    // We get an instrument group from a test file in the form that would occur
    // in an ISIS Nexus file
735
736
737
738
739
740
741
742
743
744
745
746
747
748
    // with an embedded instrument definition and parameters

    // Create the root Nexus class
    std::string testFile = "LOQinstrument.h5";
    std::string path = FileFinder::Instance().getFullPath(testFile);

    // Get nexus file for this.
    ::NeXus::File nxFile(path, NXACC_READ);
    // Open instrument group
    nxFile.openGroup("instrument", "NXinstrument");

    // Load the Nexus IDF info
    std::string params;
    TS_ASSERT_THROWS_NOTHING(ei.loadInstrumentParametersNexus(&nxFile, params));
749
    TS_ASSERT_EQUALS(params.size(), 613); // Check size of parameter string
750
751
  }

752
  /**
LamarMoore's avatar
LamarMoore committed
753
754
755
   * Test declaring an ExperimentInfo property and retrieving as const or
   * non-const
   */
756
757
758
759
760
761
762
763
764
765
766
  void testGetProperty_const_sptr() {
    const std::string eiName = "InputEi";
    ExperimentInfo_sptr eiInput(new ExperimentInfo());
    PropertyManagerHelper manager;
    manager.declareProperty(eiName, eiInput, Mantid::Kernel::Direction::Input);

    // Check property can be obtained as const_sptr or sptr
    ExperimentInfo_const_sptr eiConst;
    ExperimentInfo_sptr eiNonConst;
    TS_ASSERT_THROWS_NOTHING(
        eiConst = manager.getValue<ExperimentInfo_const_sptr>(eiName));
767
    TS_ASSERT(eiConst != nullptr);
768
769
    TS_ASSERT_THROWS_NOTHING(eiNonConst =
                                 manager.getValue<ExperimentInfo_sptr>(eiName));
770
    TS_ASSERT(eiNonConst != nullptr);
771
772
773
774
775
776
777
    TS_ASSERT_EQUALS(eiConst, eiNonConst);

    // Check TypedValue can be cast to const_sptr or to sptr
    PropertyManagerHelper::TypedValue val(manager, eiName);
    ExperimentInfo_const_sptr eiCastConst;
    ExperimentInfo_sptr eiCastNonConst;
    TS_ASSERT_THROWS_NOTHING(eiCastConst = (ExperimentInfo_const_sptr)val);
778
    TS_ASSERT(eiCastConst != nullptr);
779
    TS_ASSERT_THROWS_NOTHING(eiCastNonConst = (ExperimentInfo_sptr)val);
780
    TS_ASSERT(eiCastNonConst != nullptr);
781
782
783
    TS_ASSERT_EQUALS(eiCastConst, eiCastNonConst);
  }

784
785
786
787
788
789
790
791
792
793
794
795
796
  void test_getInstrument_setInstrument_copies_masking() {
    auto inst = ComponentCreationHelper::createTestInstrumentCylindrical(1);
    ExperimentInfo source;
    ExperimentInfo target;
    source.setInstrument(inst);
    target.setInstrument(inst);

    source.mutableDetectorInfo().setMasked(0, true);
    TS_ASSERT(!target.detectorInfo().isMasked(0));
    target.setInstrument(source.getInstrument());
    TS_ASSERT(target.detectorInfo().isMasked(0));
  }

797
798
799
800
801
802
803
804
805
806
807
808
809
  void test_getInstrument_setInstrument_copy_on_write_not_broken() {
    auto inst = ComponentCreationHelper::createTestInstrumentCylindrical(1);
    ExperimentInfo source;
    ExperimentInfo target;
    source.setInstrument(inst);
    target.setInstrument(inst);

    TS_ASSERT(!target.detectorInfo().isMasked(0));
    target.setInstrument(source.getInstrument());
    source.mutableDetectorInfo().setMasked(0, true);
    TS_ASSERT(!target.detectorInfo().isMasked(0));
  }

Owen Arnold's avatar
Owen Arnold committed
810
811
  void test_create_componentInfo() {

Owen Arnold's avatar
Owen Arnold committed
812
    const int nPixels = 10;
Owen Arnold's avatar
Owen Arnold committed
813
    auto inst = ComponentCreationHelper::createTestInstrumentRectangular(
Owen Arnold's avatar
Owen Arnold committed
814
        1 /*n banks*/, nPixels /*10 by 10 dets in bank*/,
Owen Arnold's avatar
Owen Arnold committed
815
816
        1 /*sample-bank distance*/);

Owen Arnold's avatar
Owen Arnold committed
817
818
    ExperimentInfo expInfo;
    expInfo.setInstrument(inst);
Owen Arnold's avatar
Owen Arnold committed
819
    const Mantid::Geometry::ComponentInfo &compInfo = expInfo.componentInfo();
Owen Arnold's avatar
Owen Arnold committed
820
821
822
823
824
825
826
827
828
829

    size_t nComponents = nPixels * nPixels;
    nComponents += nPixels; // One additional CompAssembly per row.
    nComponents += 1;       // Rectangular Detector (bank)
    nComponents += 1;       // source
    nComponents += 1;       // sample
    nComponents += 1;       // Instrument itself
    TS_ASSERT_EQUALS(compInfo.size(), nComponents);
  }

830
  void test_component_info_detector_indices_for_assembly_component_types() {
Owen Arnold's avatar
Owen Arnold committed
831
832
833
834
835

    const int nPixels = 10;
    auto inst = ComponentCreationHelper::createTestInstrumentRectangular(
        1 /*n banks*/, nPixels /*10 by 10 dets in bank*/,
        1 /*sample-bank distance*/);
Owen Arnold's avatar
Owen Arnold committed
836
837
838

    ExperimentInfo expInfo;
    expInfo.setInstrument(inst);
839
840
841
    const auto &compInfo = expInfo.componentInfo();
    const auto &detInfo = expInfo.detectorInfo();
    // Test the single bank
Owen Arnold's avatar
Owen Arnold committed
842
843
    auto bank = inst->getComponentByName("bank1");
    auto bankID = bank->getComponentID();
Owen Arnold's avatar
Owen Arnold committed
844
    auto allBankDetectorIndexes =
845
        compInfo.detectorsInSubtree(compInfo.indexOf(bankID));
Owen Arnold's avatar
Owen Arnold committed
846
847

    TSM_ASSERT_EQUALS("Should have all detectors under this bank",
Owen Arnold's avatar
Owen Arnold committed
848
849
                      allBankDetectorIndexes.size(),
                      detInfo.size()); //
Owen Arnold's avatar
Owen Arnold committed
850

851
    // Test one of the bank rows
Owen Arnold's avatar
Owen Arnold committed
852
853
854
855
856
    auto bankRowID =
        boost::dynamic_pointer_cast<const Mantid::Geometry::ICompAssembly>(bank)
            ->getChild(0)
            ->getComponentID();
    auto allRowDetectorIndexes =
857
        compInfo.detectorsInSubtree(compInfo.indexOf(bankRowID));
Owen Arnold's avatar
Owen Arnold committed
858
859

    TSM_ASSERT_EQUALS("Should have all detectors under this row",
Owen Arnold's avatar
Owen Arnold committed
860
861
                      allRowDetectorIndexes.size(),
                      10); //
862
863
864
865
866
867
868
  }
  void test_component_info_detector_indices_for_detector_component_types() {

    const int nPixels = 10;
    auto inst = ComponentCreationHelper::createTestInstrumentRectangular(
        1 /*n banks*/, nPixels /*10 by 10 dets in bank*/,
        1 /*sample-bank distance*/);
Owen Arnold's avatar
Owen Arnold committed
869

870
871
    ExperimentInfo expInfo;
    expInfo.setInstrument(inst);
872
873
    const auto &compInfo = expInfo.componentInfo();
    const auto &detInfo = expInfo.detectorInfo();
874
    // Test one of the detectors
Owen Arnold's avatar
Owen Arnold committed
875
876
877
    const auto targetDetectorIndex = 0;
    const auto detCompId =
        detInfo.detector(targetDetectorIndex).getComponentID();
Owen Arnold's avatar
Owen Arnold committed
878
    TSM_ASSERT_EQUALS(
Owen Arnold's avatar
Owen Arnold committed
879
        "Detector should report the detector index of itself",
880
        compInfo.detectorsInSubtree(compInfo.indexOf(detCompId)).size(), 1);
Owen Arnold's avatar
Owen Arnold committed
881
    TS_ASSERT_EQUALS(
882
        compInfo.detectorsInSubtree(compInfo.indexOf(detCompId))[0],
Owen Arnold's avatar
Owen Arnold committed
883
        targetDetectorIndex);
884
885
886
887

    size_t detectorIndex =
        0; // interchangeable as either component or detector index
    TSM_ASSERT_EQUALS("Gurantee violated of detectorindex == componentIndex",
888
                      compInfo.detectorsInSubtree(detectorIndex),
889
890
891
892
                      std::vector<size_t>{detectorIndex});

    detectorIndex = 99; // interchangeable as either component or detector index
    TSM_ASSERT_EQUALS("Gurantee violated of detectorindex == componentIndex",
893
                      compInfo.detectorsInSubtree(detectorIndex),
894
                      std::vector<size_t>{detectorIndex});
895
896
897
898
899
900
901
902
903
904
  }

  void test_component_info_detector_indices_for_generic_component_types() {
    const int nPixels = 10;
    auto inst = ComponentCreationHelper::createTestInstrumentRectangular(
        1 /*n banks*/, nPixels /*10 by 10 dets in bank*/,
        1 /*sample-bank distance*/);

    ExperimentInfo expInfo;
    expInfo.setInstrument(inst);
Owen Arnold's avatar
Owen Arnold committed
905
    const Mantid::Geometry::ComponentInfo &compInfo = expInfo.componentInfo();
906
907
908
909
910

    // Test non-detector, non-assembly components
    auto sampleId = inst->getComponentByName("sample")->getComponentID();
    TSM_ASSERT_EQUALS(
        "Sample should not report any nested detector indexes",
911
        compInfo.detectorsInSubtree(compInfo.indexOf(sampleId)).size(), 0);
912
913
914
915

    auto sourceId = inst->getComponentByName("source")->getComponentID();
    TSM_ASSERT_EQUALS(
        "Source should not report any nested detector indexes",
916
        compInfo.detectorsInSubtree(compInfo.indexOf(sourceId)).size(), 0);
Owen Arnold's avatar
Owen Arnold committed
917
918
  }

919
920
921
922
923
924
925
926
  void test_component_info_source_sample_l1() {

    auto inst = ComponentCreationHelper::createMinimalInstrument(
        V3D{-2, 0, 0} /*source*/, V3D{10, 0, 0} /*sample*/,
        V3D{12, 0, 0} /*detector*/);

    ExperimentInfo expInfo;
    expInfo.setInstrument(inst);
Owen Arnold's avatar
Owen Arnold committed
927
    const Mantid::Geometry::ComponentInfo &compInfo = expInfo.componentInfo();
928
929
930
931
932
933
934
935

    TS_ASSERT_EQUALS((V3D{-2, 0, 0}), compInfo.sourcePosition());

    TS_ASSERT_EQUALS((V3D{10, 0, 0}), compInfo.samplePosition());

    TS_ASSERT_DELTA(12, compInfo.l1(), 1e-12);
  }

Owen Arnold's avatar
Owen Arnold committed
936
937
938
939
940
941
  void test_component_info_component_index_tree() {

    const int nPixels = 10;
    auto inst = ComponentCreationHelper::createTestInstrumentRectangular(
        1 /*n banks*/, nPixels /*10 by 10 dets in bank*/,
        1 /*sample-bank distance*/);
942
943

    ExperimentInfo expInfo;
Owen Arnold's avatar
Owen Arnold committed
944
    expInfo.setInstrument(inst);