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

#include "MantidAPI/AnalysisDataService.h"
10
#include "MantidAPI/FileFinder.h"
LamarMoore's avatar
LamarMoore committed
11
#include "MantidAPI/FrameworkManager.h"
12
#include "MantidAPI/Sample.h"
13
#include "MantidDataHandling/LoadBinaryStl.h"
Karl Palmen's avatar
Karl Palmen committed
14
#include "MantidDataHandling/LoadInstrument.h"
LamarMoore's avatar
LamarMoore committed
15
#include "MantidDataHandling/LoadSampleShape.h"
16
#include "MantidGeometry/Instrument/Goniometer.h"
17
#include "MantidGeometry/Objects/MeshObject.h"
Karl Palmen's avatar
Karl Palmen committed
18
19
20
#include "MantidKernel/OptionalBool.h"
#include "MantidTestHelpers/ComponentCreationHelper.h"
#include "MantidTestHelpers/WorkspaceCreationHelper.h"
21

Karl Palmen's avatar
Karl Palmen committed
22
#include <cxxtest/TestSuite.h>
23
#include <numeric>
Karl Palmen's avatar
Karl Palmen committed
24
25
26
27

using namespace Mantid;
using namespace Mantid::API;
using namespace Mantid::DataHandling;
28
using namespace Mantid::DataObjects;
29
using namespace Mantid::Geometry;
Karl Palmen's avatar
Karl Palmen committed
30

31
class LoadSampleShapeTest : public CxxTest::TestSuite {
Karl Palmen's avatar
Karl Palmen committed
32
public:
mantid-builder's avatar
mantid-builder committed
33
34
35
  static LoadSampleShapeTest *createSuite() {
    return new LoadSampleShapeTest();
  }
36
  static void destroySuite(LoadSampleShapeTest *suite) { delete suite; }
Karl Palmen's avatar
Karl Palmen committed
37
38
39

  void testInit() {

Karl Palmen's avatar
Karl Palmen committed
40
41
42
    LoadSampleShape alg;
    TS_ASSERT_THROWS_NOTHING(alg.initialize());
    TS_ASSERT(alg.isInitialized());
Karl Palmen's avatar
Karl Palmen committed
43

44
    TSM_ASSERT_EQUALS("should be 4 properties here", 4,
Karl Palmen's avatar
Karl Palmen committed
45
                      (size_t)(alg.getProperties().size()));
Karl Palmen's avatar
Karl Palmen committed
46
47
  }

mantid-builder's avatar
mantid-builder committed
48
49
  void
  test_output_workspace_has_MeshObject_when_different_from_input_workspace() {
50
51
    LoadSampleShape alg;
    loadMeshObject(alg, false, "cube.stl");
52
53
  }

54
  void test_output_workspace_has_MeshObject_when_the_same_as_input_workspace() {
55
56
    LoadSampleShape alg;
    loadMeshObject(alg, true, "cube.stl");
57
58
  }

59
  void test_fail_invalid_stl_solid() {
60
61
62
63
    LoadSampleShape alg;
    loadFailureTest(alg, "invalid_solid.stl");
  }

64
65
66
67
68
69
  void test_off_cube() {
    LoadSampleShape alg;
    auto cube = loadMeshObject(alg, true, "cube.off");
    TS_ASSERT(cube->hasValidShape());
    TS_ASSERT_EQUALS(cube->numberOfVertices(), 8);
    TS_ASSERT_EQUALS(cube->numberOfTriangles(), 12);
70
    TS_ASSERT_DELTA(cube->volume(), 0.000001, 0.000001);
71
72
  }

Karl Palmen's avatar
Karl Palmen committed
73
74
75
76
77
78
  void test_off_L_shape() {
    LoadSampleShape alg;
    auto shape = loadMeshObject(alg, true, "L_shape.off");
    TS_ASSERT(shape->hasValidShape());
    TS_ASSERT_EQUALS(shape->numberOfVertices(), 12);
    TS_ASSERT_EQUALS(shape->numberOfTriangles(), 18);
79
    TS_ASSERT_DELTA(shape->volume(), 0.000003, 0.000001);
Karl Palmen's avatar
Karl Palmen committed
80
81
  }

82
83
84
85
86
87
  void test_off_cube_with_comments() {
    LoadSampleShape alg;
    auto cube = loadMeshObject(alg, true, "cube_with_comments.off");
    TS_ASSERT(cube->hasValidShape());
    TS_ASSERT_EQUALS(cube->numberOfVertices(), 8);
    TS_ASSERT_EQUALS(cube->numberOfTriangles(), 12);
88
    TS_ASSERT_DELTA(cube->volume(), 0.000001, 0.000001);
89
90
  }

91
92
93
94
95
96
97
98
  void test_off_colored_cube() {
    // Cube with colored faces should be read normally,
    // except that the colors are ignored.
    LoadSampleShape alg;
    auto cube = loadMeshObject(alg, true, "colored_cube.off");
    TS_ASSERT(cube->hasValidShape());
    TS_ASSERT_EQUALS(cube->numberOfVertices(), 8);
    TS_ASSERT_EQUALS(cube->numberOfTriangles(), 12);
99
    TS_ASSERT_DELTA(cube->volume(), 0.000001, 0.000001);
100
101
  }

102
103
104
105
106
  void test_fail_off_invalid_first_line() {
    LoadSampleShape alg;
    loadFailureTest(alg, "invalid_first_line.off");
  }

Karl Palmen's avatar
Karl Palmen committed
107
  void test_fail_off_non_triangular_face() {
108
109
110
111
    LoadSampleShape alg;
    loadFailureTest(alg, "cube4.off");
  }

112
  void test_fail_off_wrong_number_of_vertices() {
113
114
115
116
    LoadSampleShape alg;
    loadFailureTest(alg, "wrong_number_of_vertices.off");
  }

117
  void test_fail_off_wrong_number_of_triangles() {
118
119
120
121
    LoadSampleShape alg;
    loadFailureTest(alg, "wrong_number_of_triangles.off");
  }

122
123
124
125
126
127
128
129
130
131
  void test_fail_off_invalid_vertex() {
    LoadSampleShape alg;
    loadFailureTest(alg, "invalid_vertex.off");
  }

  void test_fail_off_invalid_triangle() {
    LoadSampleShape alg;
    loadFailureTest(alg, "invalid_triangle.off");
  }

132
  void testXRotation() {
133
134
    MatrixWorkspace_sptr inputWS =
        WorkspaceCreationHelper::create2DWorkspaceWithFullInstrument(2, 10);
135
    inputWS->mutableRun().mutableGoniometer().pushAxis("Axis0", 1, 0, 0, 45);
136
    auto sampleMesh = createCube();
137
138

    rotate(*sampleMesh, inputWS);
139
140
141
142
143
144
145
146
147
148
149
    std::vector<double> rotatedVertices = sampleMesh->getVertices();
    std::vector<double> vectorToMatch = {
        -5, 7.07106,    -14.142136, 5,  14.142136,  -7.07106,
        5,  7.07106,    -14.142136, -5, 14.142136,  -7.07106,
        5,  -14.142136, 7.07106,    5,  -7.07106,   14.142136,
        -5, -7.07106,   14.142136,  -5, -14.142136, 7.07106};
    for (size_t i = 0; i < 24; ++i) {
      TS_ASSERT_DELTA(rotatedVertices[i], vectorToMatch[i], 1e-5);
    }
  }
  void testYRotation() {
150
151
    MatrixWorkspace_sptr inputWS =
        WorkspaceCreationHelper::create2DWorkspaceWithFullInstrument(2, 10);
152
    inputWS->mutableRun().mutableGoniometer().pushAxis("Axis0", 0, 1, 0, 90);
153
    auto sampleMesh = createCube();
154
155

    rotate(*sampleMesh, inputWS);
156
157
158
159
160
161
162
163
164
165
    std::vector<double> rotatedVertices = sampleMesh->getVertices();
    std::vector<double> vectorToMatch = {-15, -5,  5,  -15, 5,  -5, -15, -5,
                                         -5,  -15, 5,  5,   15, -5, -5,  15,
                                         5,   -5,  15, 5,   5,  15, -5,  5};
    for (size_t i = 0; i < 24; ++i) {
      TS_ASSERT_DELTA(rotatedVertices[i], vectorToMatch[i], 1e-5);
    }
  }

  void testZRotation() {
166
167
    MatrixWorkspace_sptr inputWS =
        WorkspaceCreationHelper::create2DWorkspaceWithFullInstrument(2, 10);
168
    inputWS->mutableRun().mutableGoniometer().pushAxis("Axis0", 0, 0, 1, 180);
169
    auto sampleMesh = createCube();
170
171

    rotate(*sampleMesh, inputWS);
172
173
174
175
176
177
178
179
180
181
    std::vector<double> rotatedVertices = sampleMesh->getVertices();
    std::vector<double> vectorToMatch = {5,   5,  -15, -5,  -5, -15, -5, 5,
                                         -15, 5,  -5,  -15, -5, 5,   15, -5,
                                         -5,  15, 5,   -5,  15, 5,   5,  15};
    for (size_t i = 0; i < 24; ++i) {
      TS_ASSERT_DELTA(rotatedVertices[i], vectorToMatch[i], 1e-5);
    }
  }

  void testMultiRotation() {
182
183
    MatrixWorkspace_sptr inputWS =
        WorkspaceCreationHelper::create2DWorkspaceWithFullInstrument(2, 10);
184
185
186
    inputWS->mutableRun().mutableGoniometer().pushAxis("Z", 0, 0, 1, 35);
    inputWS->mutableRun().mutableGoniometer().pushAxis("Y", 0, 1, 0, 20);
    inputWS->mutableRun().mutableGoniometer().pushAxis("X", 1, 0, 0, 70);
187
    auto sampleMesh = createCube();
188
189

    rotate(*sampleMesh, inputWS);
190
191
192
193
194
195
196
197
198
199
200
    std::vector<double> rotatedVertices = sampleMesh->getVertices();
    std::vector<double> vectorToMatch = {
        -13.70635, 5.52235,   -7.52591,  -5.33788,  15.55731,  -2.11589,
        -6.00884,  10.91220,  -10.94611, -13.03539, 10.16745,  1.30430,
        13.03539,  -10.16745, -1.30430,  13.70635,  -5.52235,  7.52591,
        6.00884,   -10.91220, 10.94611,  5.33788,   -15.55731, 2.11589};
    for (size_t i = 0; i < 24; ++i) {
      TS_ASSERT_DELTA(rotatedVertices[i], vectorToMatch[i], 1e-5);
    }
  }

Karl Palmen's avatar
Karl Palmen committed
201
private:
Karl Palmen's avatar
Karl Palmen committed
202
  // Create workspaces and add them to algorithm properties
203
204
  MatrixWorkspace_sptr prepareWorkspaces(LoadSampleShape &alg,
                                         bool outputWsSameAsInputWs) {
205
206
    const int nvectors(2), nbins(10);
    MatrixWorkspace_sptr inputWS =
mantid-builder's avatar
mantid-builder committed
207
208
        WorkspaceCreationHelper::create2DWorkspaceWithFullInstrument(nvectors,
                                                                     nbins);
209
210
211
212
213
    alg.setChild(true);
    alg.setProperty("InputWorkspace", inputWS);
    alg.setPropertyValue("OutputWorkspace", "__dummy_unused");
    if (outputWsSameAsInputWs) {
      alg.setProperty("OutputWorkspace", inputWS);
Karl Palmen's avatar
Karl Palmen committed
214
    }
215
    return inputWS;
Karl Palmen's avatar
Karl Palmen committed
216
217
  }

218
219
  const MeshObject *loadMeshObject(LoadSampleShape &alg,
                                   bool outputWsSameAsInputWs,
David Fairbrother's avatar
David Fairbrother committed
220
                                   const std::string &filename) {
221
222
223
224
225
226
    alg.initialize();
    alg.setPropertyValue("Filename", filename);
    prepareWorkspaces(alg, outputWsSameAsInputWs);
    TS_ASSERT_THROWS_NOTHING(alg.execute());
    TS_ASSERT(alg.isExecuted());
    return getMeshObject(alg);
227
228
  }

David Fairbrother's avatar
David Fairbrother committed
229
  void loadFailureTest(LoadSampleShape &alg, const std::string &filename) {
230
231
232
233
234
235
236
    alg.initialize();
    alg.setPropertyValue("Filename", filename);
    prepareWorkspaces(alg, true);
    TS_ASSERT_THROWS_ANYTHING(alg.execute());
    TS_ASSERT(!alg.isExecuted());
  }

237
  const MeshObject *getMeshObject(LoadSampleShape &alg) {
238
239
240
241
242
    Workspace_sptr ws = alg.getProperty("OutputWorkspace");
    auto ei = std::dynamic_pointer_cast<ExperimentInfo>(ws);
    if (!ei)
      throw std::invalid_argument("Wrong type of input workspace");
    const auto &s(ei->sample());
243
    auto &obj = s.getShape();
mantid-builder's avatar
mantid-builder committed
244
    auto mObj = dynamic_cast<const MeshObject *>(&obj);
245
246
247
    TSM_ASSERT_DIFFERS("Shape is not a mesh object", mObj, nullptr);
    return mObj;
  }
248

249
250
251
252
253
254
255
256
257
258
259
  std::unique_ptr<MeshObject> createCube() {
    const std::vector<uint32_t> faces{0, 1, 2, 0, 3, 1, 0, 2, 4, 2, 1, 5,
                                      2, 5, 4, 6, 1, 3, 6, 5, 1, 4, 5, 6,
                                      7, 3, 0, 0, 4, 7, 7, 6, 3, 4, 6, 7};
    const std::vector<Mantid::Kernel::V3D> vertices{
        Mantid::Kernel::V3D(-5, -5, -15), Mantid::Kernel::V3D(5, 5, -15),
        Mantid::Kernel::V3D(5, -5, -15),  Mantid::Kernel::V3D(-5, 5, -15),
        Mantid::Kernel::V3D(5, -5, 15),   Mantid::Kernel::V3D(5, 5, 15),
        Mantid::Kernel::V3D(-5, 5, 15),   Mantid::Kernel::V3D(-5, -5, 15)};
    auto cube = std::make_unique<MeshObject>(faces, vertices,
                                             Mantid::Kernel::Material());
260
261
    return cube;
  }
Karl Palmen's avatar
Karl Palmen committed
262
};
Karl Palmen's avatar
Karl Palmen committed
263

264
class LoadSampleShapeTestPerformance : public CxxTest::TestSuite {
Karl Palmen's avatar
Karl Palmen committed
265
266
public:
  void setUp() override {
mantid-builder's avatar
mantid-builder committed
267
268
    auto inWs =
        WorkspaceCreationHelper::create2DWorkspaceWithFullInstrument(10, 4);
269
    alg = setupAlg(inWs);
Karl Palmen's avatar
Karl Palmen committed
270
271
  }

272
  void testLoadSampleShapePerformance() {
273
    for (int i = 0; i < numberOfIterations; ++i) {
Karl Palmen's avatar
Karl Palmen committed
274
275
276
277
278
      TS_ASSERT_THROWS_NOTHING(alg->execute());
    }
  }

private:
279
  std::unique_ptr<LoadSampleShape> alg;
Karl Palmen's avatar
Karl Palmen committed
280
281
  const int numberOfIterations = 5;

David Fairbrother's avatar
David Fairbrother committed
282
  std::unique_ptr<LoadSampleShape> setupAlg(const Workspace2D_sptr &inputWS) {
283
    auto loadAlg = std::make_unique<LoadSampleShape>();
Karl Palmen's avatar
Karl Palmen committed
284
    loadAlg->initialize();
285
286
287
    loadAlg->setChild(true);
    loadAlg->setProperty("InputWorkspace", inputWS);
    loadAlg->setPropertyValue("OutputWorkspace", "__dummy_unused");
Karl Palmen's avatar
Karl Palmen committed
288
289
290
291
292
    loadAlg->setPropertyValue("Filename", "tube.stl");

    loadAlg->setRethrows(true);
    return loadAlg;
  }
293
};