Sample.cpp 12.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
#include "MantidAPI/Sample.h"
8
#include "MantidGeometry/Crystal/CrystalStructure.h"
9
#include "MantidGeometry/Crystal/OrientedLattice.h"
LamarMoore's avatar
LamarMoore committed
10
#include "MantidGeometry/IComponent.h"
11
#include "MantidGeometry/Instrument/SampleEnvironment.h"
LamarMoore's avatar
LamarMoore committed
12
13
#include "MantidGeometry/Objects/CSGObject.h"
#include "MantidGeometry/Objects/ShapeFactory.h"
14
#include "MantidKernel/Material.h"
15
#include "MantidKernel/Strings.h"
16

17
#include <nexus/NeXusException.hpp>
18

19
20
21
22
namespace Mantid {

namespace API {
using namespace Mantid::Kernel;
23
24
using Geometry::IObject;
using Geometry::IObject_sptr;
25
using Geometry::OrientedLattice;
26
using Geometry::SampleEnvironment;
27
28
29
30
31
32
using Geometry::ShapeFactory;

/**
 * Default constructor. Required for cow_ptr.
 */
Sample::Sample()
33
    : m_name(), m_shape(ShapeFactory().createShape("")), m_environment(),
34
35
      m_lattice(nullptr), m_crystalStructure(), m_samples(), m_geom_id(0),
      m_thick(0.0), m_height(0.0), m_width(0.0) {}
36
37
38
39
40
41
42

/**
 * Copy constructor
 *  @param copy :: const reference to the sample object
 */
Sample::Sample(const Sample &copy)
    : m_name(copy.m_name), m_shape(copy.m_shape),
Hahn, Steven's avatar
Hahn, Steven committed
43
44
45
46
      m_environment(copy.m_environment), m_lattice(nullptr),
      m_crystalStructure(), m_samples(copy.m_samples),
      m_geom_id(copy.m_geom_id), m_thick(copy.m_thick), m_height(copy.m_height),
      m_width(copy.m_width) {
47
  if (copy.m_lattice)
48
    m_lattice = std::make_unique<OrientedLattice>(copy.getOrientedLattice());
49
50

  if (copy.hasCrystalStructure()) {
51
52
    m_crystalStructure = std::make_unique<Geometry::CrystalStructure>(
        copy.getCrystalStructure());
53
  }
54
}
55

56
/// Destructor
57
Sample::~Sample() = default;
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

/** Assignment operator
 * @param rhs :: const reference to the sample object
 * @return A reference to this object, which will have the same
 * state as the argument
 */
Sample &Sample::operator=(const Sample &rhs) {
  if (this == &rhs)
    return *this;
  m_name = rhs.m_name;
  m_shape = rhs.m_shape;
  m_environment = rhs.m_environment;
  m_geom_id = rhs.m_geom_id;
  m_samples = std::vector<boost::shared_ptr<Sample>>(rhs.m_samples);
  m_thick = rhs.m_thick;
  m_height = rhs.m_height;
  m_width = rhs.m_width;
  if (rhs.m_lattice)
76
    m_lattice = std::make_unique<OrientedLattice>(rhs.getOrientedLattice());
77
  else
78
    m_lattice.reset(nullptr);
79

80
81
82
83
84
85
  m_crystalStructure.reset();
  if (rhs.hasCrystalStructure()) {
    m_crystalStructure.reset(
        new Geometry::CrystalStructure(rhs.getCrystalStructure()));
  }

86
87
  return *this;
}
88

89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/**
 * Returns the name of the sample
 * @returns The name of this  sample
 */
const std::string &Sample::getName() const { return m_name; }

/**
 * Update the name of the sample
 * @param name :: The name of the sample
 */
void Sample::setName(const std::string &name) { m_name = name; }

/**
 * Get a pointer to the sample shape object. It is assumed that this is defined
 * within
 * its own coordinate system with its centre at [0,0,0]
 * @return A reference to the object describing the shape
 */
107
const IObject &Sample::getShape() const { return *m_shape; }
108
109
110
111
112

/** Set the object that describes the sample shape. The object is defined within
 * its own coordinate system
 * @param shape :: The object describing the shape
 */
113
114
115
116
117
118
119
void Sample::setShape(const IObject_sptr &shape) {
  if (shape) {
    m_shape = shape;
  } else {
    m_shape = ShapeFactory().createShape("");
  }
}
120
121
122
123

/** Return the material.
 * @return A reference to the material the sample is composed of
 */
124
const Material &Sample::getMaterial() const { return m_shape->material(); }
125

126
127
bool Sample::hasEnvironment() const { return (m_environment != nullptr); }

128
129
130
131
132
133
134
135
136
137
138
139
/**
 * Return a reference to the sample environment that this sample is attached to
 * @return A const reference to a SampleEnvironment object
 * @throw std::runtime_error If the environment has not been defined
 */
const SampleEnvironment &Sample::getEnvironment() const {
  if (!m_environment) {
    throw std::runtime_error(
        "Sample::getEnvironment - No sample enviroment has been defined.");
  }
  return *m_environment;
}
140

141
142
143
144
145
/**
 * Attach an environment onto this sample
 * @param env :: A pointer to a created sample environment. This takes
 * ownership of the object.
 */
146
void Sample::setEnvironment(std::unique_ptr<SampleEnvironment> env) {
147
  m_environment = boost::shared_ptr<SampleEnvironment>(std::move(env));
148
}
149

150
151
152
153
154
155
156
157
158
159
160
/** Return a const reference to the OrientedLattice of this sample
 * @return A const reference to a OrientedLattice object
 * @throw std::runtime_error If the OrientedLattice has not been defined
 */
const OrientedLattice &Sample::getOrientedLattice() const {
  if (!m_lattice) {
    throw std::runtime_error(
        "Sample::getOrientedLattice - No OrientedLattice has been defined.");
  }
  return *m_lattice;
}
161

162
163
164
165
166
167
168
169
170
171
172
/** Return a reference to the OrientedLattice of this sample
 * @return A reference to a OrientedLattice object
 * @throw std::runtime_error If the OrientedLattice has not been defined
 */
OrientedLattice &Sample::getOrientedLattice() {
  if (!m_lattice) {
    throw std::runtime_error(
        "Sample::getOrientedLattice - No OrientedLattice has been defined.");
  }
  return *m_lattice;
}
173

174
175
/** Attach an OrientedLattice onto this sample
 *
176
 * @param lattice :: A pointer to a OrientedLattice.
177
 */
178
179
180
void Sample::setOrientedLattice(
    std::unique_ptr<Geometry::OrientedLattice> lattice) {
  m_lattice = std::move(lattice);
181
}
182

183
/** @return true if the sample has an OrientedLattice  */
184
bool Sample::hasOrientedLattice() const { return (m_lattice != nullptr); }
185

186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
const Geometry::CrystalStructure &Sample::getCrystalStructure() const {
  if (!hasCrystalStructure()) {
    throw std::runtime_error(
        "Sample::getCrystalStructure - No CrystalStructure has been defined.");
  }

  return *m_crystalStructure;
}

/// Resets the internal pointer to the new CrystalStructure (it's copied).
void Sample::setCrystalStructure(
    const Geometry::CrystalStructure &newCrystalStructure) {
  m_crystalStructure.reset(new Geometry::CrystalStructure(newCrystalStructure));
}

/// Returns true if the sample actually holds a CrystalStructure.
bool Sample::hasCrystalStructure() const {
203
204
  // Conversion to bool seems to be a problem in VS2012, so this is a bit more
  // verbose than it should be.
205
  return static_cast<bool>(m_crystalStructure);
206
207
208
209
210
}

/// Destroys the internally stored CrystalStructure-object.
void Sample::clearCrystalStructure() { m_crystalStructure.reset(); }

211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/**
 * Set the geometry flag that is specfied in the raw file within the SPB_STRUCT
 * 1 = cylinder, 2 = flat plate, 3 = disc, 4 = single crystal
 * @param geom_id :: The flag for the geometry
 */
void Sample::setGeometryFlag(int geom_id) { m_geom_id = geom_id; }

/**
 * Get the geometry flag that is specified in the raw file within the SPB_STRUCT
 * 1 = cylinder, 2 = flat plate, 3 = disc, 4 = single crystal
 * @returns The flag for the sample geometry
 */
int Sample::getGeometryFlag() const { return m_geom_id; }

/**
 * Set the thickness value
 * @param thick :: The parameter e_thick in the SPB_STRUCT
 */
void Sample::setThickness(double thick) { m_thick = thick; }

/**
 * Get the thickness value
 * @returns The parameter thickness parameter
 */
double Sample::getThickness() const { return m_thick; }

/**
 * Set the height value
 * @param height :: The parameter e_height in the SPB_STRUCT
 */
void Sample::setHeight(double height) { m_height = height; }

/**
 * Get the height value
 * @returns The parameter height parameter
 */
double Sample::getHeight() const { return m_height; }

/**
 * Set the width value
 * @param width :: The parameter e_width in the SPB_STRUCT
 */
void Sample::setWidth(double width) { m_width = width; }

/**
 * Get the height value
 * @returns The parameter height parameter
 */
double Sample::getWidth() const { return m_width; }

/**
 * Gets the desired sample, 0 is the current sample
 * @param index The index of the desired sample
 * @returns The desired sample
 */
Sample &Sample::operator[](const int index) {
  if (index == 0) {
    return *this;
  } else if ((static_cast<std::size_t>(index) > m_samples.size()) ||
             (index < 0)) {
    throw std::out_of_range("The index value provided was out of range");
  } else {
    return *m_samples[index - 1];
  }
}
276

277
278
279
280
281
282
283
284
285
286
/**
 * Gets the number of samples in this collection
 * @returns The count of samples
 */
std::size_t Sample::size() const { return m_samples.size() + 1; }

/**
 * Adds a sample to the sample collection
 * @param childSample The child sample to be added
 */
David Fairbrother's avatar
David Fairbrother committed
287
void Sample::addSample(const boost::shared_ptr<Sample> &childSample) {
288
  m_samples.emplace_back(childSample);
289
}
290

291
292
293
294
295
296
//--------------------------------------------------------------------------------------------
/** Save the object to an open NeXus file.
 * @param file :: open NeXus file
 * @param group :: name of the group to create
 */
void Sample::saveNexus(::NeXus::File *file, const std::string &group) const {
297
  file->makeGroup(group, "NXsample", true);
298
  file->putAttr("name", m_name);
299
300
301
  if (m_name.empty()) {
    file->putAttr("name_empty", 1);
  }
302
  file->putAttr("version", 1);
303
304
305
306
307
308
  std::string shapeXML("");
  if (auto csgObject =
          boost::dynamic_pointer_cast<Mantid::Geometry::CSGObject>(m_shape)) {
    shapeXML = csgObject->getShapeXML();
  }
  file->putAttr("shape_xml", shapeXML);
309

310
  m_shape->material().saveNexus(file, "material");
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
  // Write out the other (indexes 1+) samples
  file->writeData("num_other_samples", int(m_samples.size()));
  for (size_t i = 0; i < m_samples.size(); i++)
    m_samples[i]->saveNexus(file, "sample" + Strings::toString(i + 1));
  // TODO: SampleEnvironment
  // OrientedLattice
  if (hasOrientedLattice()) {
    file->writeData("num_oriented_lattice", 1);
    m_lattice->saveNexus(file, "oriented_lattice");
  } else
    file->writeData("num_oriented_lattice", 0);

  // Legacy info from RAW file (I think)
  file->writeData("geom_id", m_geom_id);
  file->writeData("geom_thickness", m_thick);
  file->writeData("geom_height", m_height);
  file->writeData("geom_width", m_width);

  file->closeGroup();
}
331

332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
//--------------------------------------------------------------------------------------------
/** Load the object from an open NeXus file.
 * @param file :: open NeXus file
 * @param group :: name of the group to open
 * @return the version tag of the sample group
 */
int Sample::loadNexus(::NeXus::File *file, const std::string &group) {
  file->openGroup(group, "NXsample");

  // Version 0 = saveNexusProcessed before Sep 8, 2011
  int version = 0;
  try {
    file->getAttr("version", version);
  } catch (::NeXus::Exception &) {
    version = 0;
  }
348

349
350
351
352
353
354
  if (version == 0) {
    // Sample NAME field may/may not be present
    try {
      file->readData("name", m_name);
    } catch (::NeXus::Exception &) {
      m_name = "";
355
    }
356
  }
357

358
359
360
  if (version > 0) {
    // Name is an attribute
    file->getAttr("name", m_name);
361
362
363
364
365
366
367
    if (file->hasAttr("name_empty")) {
      int isEmpty;
      file->getAttr("name_empty", isEmpty);
      if (isEmpty) {
        m_name.clear();
      }
    }
368
369
370
371
372
373
374

    // Shape (from XML)
    std::string shape_xml;
    file->getAttr("shape_xml", shape_xml);
    shape_xml = Strings::strip(shape_xml);
    if (!shape_xml.empty()) {
      ShapeFactory shapeMaker;
375
      m_shape = shapeMaker.createShape(shape_xml,
376
                                       false /*Don't wrap with <type> tag*/);
Nick Draper's avatar
Nick Draper committed
377
    }
378
379
    Kernel::Material material;
    material.loadNexus(file, "material");
380
    // CSGObject expected, if so, set its material
mantid-builder's avatar
mantid-builder committed
381
382
    if (auto csgObj =
            boost::dynamic_pointer_cast<Geometry::CSGObject>(m_shape)) {
383
384
      csgObj->setMaterial(material);
    }
385
386
387
388
389

    // Load other samples
    int num_other_samples;
    file->readData("num_other_samples", num_other_samples);
    for (int i = 0; i < num_other_samples; i++) {
390
      auto extra = boost::make_shared<Sample>();
391
392
      extra->loadNexus(file, "sample" + Strings::toString(i + 1));
      this->addSample(extra);
Nick Draper's avatar
Nick Draper committed
393
    }
394

395
396
397
398
    // OrientedLattice
    int num_oriented_lattice;
    file->readData("num_oriented_lattice", num_oriented_lattice);
    if (num_oriented_lattice > 0) {
399
      m_lattice = std::make_unique<OrientedLattice>();
400
      m_lattice->loadNexus(file, "oriented_lattice");
Nick Draper's avatar
Nick Draper committed
401
    }
402
  }
Nick Draper's avatar
Nick Draper committed
403

404
405
406
407
408
409
410
411
  try {
    // Legacy info from RAW file (I think)
    file->readData("geom_id", m_geom_id);
    file->readData("geom_thickness", m_thick);
    file->readData("geom_height", m_height);
    file->readData("geom_width", m_width);
  } catch (...) { /* Very old files don't have them. Ignore. */
  }
412

413
  file->closeGroup();
414

415
416
  return version;
}
417

418
419
420
421
422
/**
 * Delete the oriented lattice.
 */
void Sample::clearOrientedLattice() {
  if (m_lattice) {
423
    m_lattice.reset(nullptr);
424
  }
425
}
LamarMoore's avatar
LamarMoore committed
426
427
} // namespace API
} // namespace Mantid