LeanElasticPeak.cpp 14.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 & CSNS, Institute of High Energy Physics, CAS
// SPDX - License - Identifier: GPL - 3.0 +
7
#include "MantidDataObjects/LeanElasticPeak.h"
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include "MantidDataObjects/NoShape.h"
#include "MantidGeometry/Objects/InstrumentRayTracer.h"
#include "MantidGeometry/Surfaces/LineIntersectVisit.h"
#include "MantidKernel/ConfigService.h"
#include "MantidKernel/Exception.h"
#include "MantidKernel/Strings.h"

#include "boost/make_shared.hpp"

#include <algorithm>
#include <cctype>
#include <string>
#include <utility>

using namespace Mantid;
using namespace Mantid::Kernel;
using namespace Mantid::Geometry;

namespace Mantid {
namespace DataObjects {

//----------------------------------------------------------------------------------------------
/** Default constructor */
31
32
LeanElasticPeak::LeanElasticPeak()
    : BasePeak(), m_Qsample(V3D(0, 0, 0)), m_wavelength(0.) {}
33

34
35
36
37
38
39
40
41
//----------------------------------------------------------------------------------------------
/** Constructor that uses the Q position of the peak (in the sample frame)
 * and a goniometer rotation matrix.
 * No detector ID is set.
 *
 * @param QSampleFrame :: Q of the center of the peak, in reciprocal space, in
 *the sample frame (goniometer rotation accounted for).
 */
42
LeanElasticPeak::LeanElasticPeak(const Mantid::Kernel::V3D &QSampleFrame)
43
    : BasePeak(), m_Qsample(QSampleFrame), m_wavelength(0.) {}
44

45
46
47
48
//----------------------------------------------------------------------------------------------
/** Constructor that uses the Q position of the peak (in the lab frame).
 * No detector ID is set.
 *
49
 * @param QSampleFrame :: Q of the center of the peak, in reciprocal space
50
 * @param goniometer :: a 3x3 rotation matrix
51
 * @param refFrame :: optional reference frame, will default to beam along +Z
52
 */
53
54
LeanElasticPeak::LeanElasticPeak(
    const Mantid::Kernel::V3D &QSampleFrame,
55
    const Mantid::Kernel::Matrix<double> &goniometer,
Whitfield, Ross's avatar
Whitfield, Ross committed
56
    boost::optional<std::shared_ptr<Geometry::ReferenceFrame>> refFrame)
57
58
59
60
61
    : BasePeak() {
  if (refFrame.is_initialized())
    setReferenceFrame(refFrame.get());
  setQSampleFrame(QSampleFrame, goniometer);
}
62
63
64
65
66
67
68
69

//----------------------------------------------------------------------------------------------
/** Constructor that uses the Q position of the peak (in the sample frame)
 * and a goniometer rotation matrix.
 * No detector ID is set.
 *
 * @param QSampleFrame :: Q of the center of the peak, in reciprocal space, in
 *the sample frame (goniometer rotation accounted for).
70
 * @param wavelength :: wavelength in Angstroms.
71
 */
72
73
LeanElasticPeak::LeanElasticPeak(const Mantid::Kernel::V3D &QSampleFrame,
                                 double wavelength)
74
    : BasePeak(), m_Qsample(QSampleFrame), m_wavelength(wavelength) {}
75

76
77
78
79
/**
 * @brief Copy constructor
 * @param other : Source
 */
80
LeanElasticPeak::LeanElasticPeak(const LeanElasticPeak &other)
81
    : BasePeak(other), m_Qsample(other.m_Qsample),
82
      m_wavelength(other.m_wavelength), m_refFrame(other.m_refFrame) {}
83
84

//----------------------------------------------------------------------------------------------
85
/** Constructor making a LeanElasticPeak from IPeak interface
86
87
88
 *
 * @param ipeak :: const reference to an IPeak object
 */
89
LeanElasticPeak::LeanElasticPeak(const Geometry::IPeak &ipeak)
90
91
92
93
94
95
96
97
98
99
100
    : BasePeak(ipeak), m_Qsample(ipeak.getQSampleFrame()),
      m_wavelength(ipeak.getWavelength()) {}

//----------------------------------------------------------------------------------------------
/** Set the wavelength of the neutron. Assumes elastic scattering.
 *
 * @param wavelength :: wavelength in Angstroms.
 */
void LeanElasticPeak::setWavelength(double wavelength) {
  m_wavelength = wavelength;
}
101
102
103

//----------------------------------------------------------------------------------------------
/** Set the detector ID of the pixel at the centre of the peak and look up and
Whitfield, Ross's avatar
Whitfield, Ross committed
104
105
 * cache values related to it. It also adds it to the list of contributing
 * detectors for this peak but does NOT remove the old centre.
106
 */
107
void LeanElasticPeak::setDetectorID(int) {
108
  throw Exception::NotImplementedError(
109
110
      "LeanElasticPeak::setDetectorID(): Can't set detectorID on "
      "LeanElasticPeak");
111
112
113
114
}

//----------------------------------------------------------------------------------------------
/** Get the ID of the detector at the center of the peak  */
115
int LeanElasticPeak::getDetectorID() const { return -1; }
116
117
118
119
120
121

//----------------------------------------------------------------------------------------------
/** Set the instrument (and save the source/sample pos).
 * Call setDetectorID AFTER this call.
 *
 */
122
void LeanElasticPeak::setInstrument(const Geometry::Instrument_const_sptr &) {
123
  throw Exception::NotImplementedError(
124
125
      "LeanElasticPeak::setInstrument(): Can't set instrument on "
      "LeanElasticPeak");
126
127
128
129
}

//----------------------------------------------------------------------------------------------
/** Return a shared ptr to the detector at center of peak. */
130
Geometry::IDetector_const_sptr LeanElasticPeak::getDetector() const {
131
  throw Exception::NotImplementedError(
132
      "LeanElasticPeak::getDetector(): Has no detector ID");
133
}
134
135

/** Return a shared ptr to the instrument for this peak. */
136
Geometry::Instrument_const_sptr LeanElasticPeak::getInstrument() const {
137
  throw Exception::NotImplementedError(
138
      "LeanElasticPeak::setInstrument(): Has no instrument");
139
140
}

141
/** Return a shared ptr to the reference frame for this peak. */
142
143
144
145
146
147
148
149
150
151
152
153
154
std::shared_ptr<const Geometry::ReferenceFrame>
LeanElasticPeak::getReferenceFrame() const {
  return m_refFrame;
}

/**
Setter for the reference frame.
@param frame : reference frame object to use.
*/
void LeanElasticPeak::setReferenceFrame(std::shared_ptr<ReferenceFrame> frame) {
  m_refFrame = std::move(frame);
}

155
156
157
158
// -------------------------------------------------------------------------------------
/** Return the neutron wavelength (in angstroms) */
double LeanElasticPeak::getWavelength() const { return m_wavelength; }

159
160
161
162
// -------------------------------------------------------------------------------------
/** Calculate the time of flight (in microseconds) of the neutrons for this
 * peak,
 * using the geometry of the detector  */
163
double LeanElasticPeak::getTOF() const {
164
165
  return std::numeric_limits<double>::quiet_NaN();
}
166
167
168

// -------------------------------------------------------------------------------------
/** Calculate the scattering angle of the peak  */
169
double LeanElasticPeak::getScattering() const {
170
  return asin(getWavelength() / (2 * getDSpacing())) * 2;
171
172
173
174
}

// -------------------------------------------------------------------------------------
/** Calculate the azimuthal angle of the peak  */
175
double LeanElasticPeak::getAzimuthal() const {
Whitfield, Ross's avatar
Whitfield, Ross committed
176
177
178
179
180
181
182
183
184
  const V3D qLab = getQLabFrame();
  std::shared_ptr<const ReferenceFrame> refFrame = getReferenceFrame();
  const double qSign = (convention != "Crystallography") ? 1.0 : -1.0;
  const V3D detectorDir = -qLab * qSign;
  if (refFrame)
    return atan2(detectorDir[refFrame->pointingUp()],
                 detectorDir[refFrame->pointingHorizontal()]);
  else
    return atan2(detectorDir[1], detectorDir[0]);
185
186
187
188
}

// -------------------------------------------------------------------------------------
/** Calculate the d-spacing of the peak, in 1/Angstroms  */
189
190
191
double LeanElasticPeak::getDSpacing() const {
  return 2 * M_PI / m_Qsample.norm();
}
192
193
194
195
196
197
198

//----------------------------------------------------------------------------------------------
/** Return the Q change (of the lattice, k_i - k_f) for this peak.
 * The Q is in the Lab frame: the goniometer rotation was NOT taken out.
 *
 * Note: There is a 2*pi factor used, so |Q| = 2*pi/wavelength.
 * */
199
Mantid::Kernel::V3D LeanElasticPeak::getQLabFrame() const {
200
  return getGoniometerMatrix() * m_Qsample;
201
202
203
204
205
}

//----------------------------------------------------------------------------------------------
/** Return the Q change (of the lattice, k_i - k_f) for this peak.
 * The Q is in the Sample frame: the goniometer rotation WAS taken out. */
206
207
208
Mantid::Kernel::V3D LeanElasticPeak::getQSampleFrame() const {
  return m_Qsample;
}
209
210
211
212
213
214
215
216
217

//----------------------------------------------------------------------------------------------
/** Set the peak using the peak's position in reciprocal space, in the sample
 *frame.
 *
 * @param QSampleFrame :: Q of the center of the peak, in reciprocal space
 *        This is in inelastic convention: momentum transfer of the LATTICE!
 *        Also, q does NOT have a 2pi factor = it is equal to 1/wavelength.
 */
218
219
void LeanElasticPeak::setQSampleFrame(const Mantid::Kernel::V3D &QSampleFrame,
                                      boost::optional<double>) {
220
  m_Qsample = QSampleFrame;
221
222
}

223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
void LeanElasticPeak::setQSampleFrame(
    const Mantid::Kernel::V3D &QSampleFrame,
    const Mantid::Kernel::Matrix<double> &goniometer) {
  m_Qsample = QSampleFrame;
  setGoniometerMatrix(goniometer);

  const V3D qLab = getQLabFrame();

  try {
    double wl = calculateWavelengthFromQLab(qLab);
    setWavelength(wl);
  } catch (std::exception &e) {
    g_log.warning() << "Unable to determine wavelength from q-lab\n"
                    << e.what() << '\n';
  }
}

240
241
242
243
244
245
246
//----------------------------------------------------------------------------------------------
/** Set the peak using the peak's position in reciprocal space, in the lab
 *frame.
 *
 * @param qLab :: Q of the center of the peak, in reciprocal space.
 *        This is in inelastic convention: momentum transfer of the LATTICE!
 *        Also, q does have a 2pi factor = it is equal to 2pi/wavelength (in
247
 *        Angstroms).
248
 */
249
250
void LeanElasticPeak::setQLabFrame(const Mantid::Kernel::V3D &qLab,
                                   boost::optional<double>) {
251
  this->setQSampleFrame(getInverseGoniometerMatrix() * qLab);
252
253
}

254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
//----------------------------------------------------------------------------------------------
/** Get the final neutron energy in meV */
double LeanElasticPeak::getFinalEnergy() const {
  // Velocity of the neutron (non-relativistic)
  const double velocity =
      PhysicalConstants::h /
      (m_wavelength * 1e-10 * PhysicalConstants::NeutronMass);
  // Energy in J of the neutron
  const double energy =
      PhysicalConstants::NeutronMass * velocity * velocity / 2.0;
  // Convert to meV
  return energy / PhysicalConstants::meV;
}

/** Get the initial (incident) neutron energy in meV */
double LeanElasticPeak::getInitialEnergy() const { return getFinalEnergy(); }

/** Get the difference between the initial and final neutron energy in meV,
 * elastic so always 0 */
double LeanElasticPeak::getEnergyTransfer() const { return 0.; }

/** Set sample position */
276
277
278
void LeanElasticPeak::setSamplePos(double, double, double) {
  throw Exception::NotImplementedError(
      "LeanElasticPeak has no sample information");
279
280
}

281
/** Set sample position  */
282
283
284
void LeanElasticPeak::setSamplePos(const Mantid::Kernel::V3D &) {
  throw Exception::NotImplementedError(
      "LeanElasticPeak has no sample information");
285
286
}

287
288
289
290
291
292
293
294
295
296
/** Set the final energy */
void LeanElasticPeak::setFinalEnergy(double) {
  throw Exception::NotImplementedError("Use LeanElasticPeak::setWavelength");
}

/** Set the initial energy */
void LeanElasticPeak::setInitialEnergy(double) {
  throw Exception::NotImplementedError("Use LeanElasticPeak::setWavelength");
}

297
298
// -------------------------------------------------------------------------------------
/** Return the detector position vector */
299
300
301
Mantid::Kernel::V3D LeanElasticPeak::getDetPos() const {
  throw Exception::NotImplementedError(
      "LeanElasticPeak has no detector information");
302
}
303
304
305

// -------------------------------------------------------------------------------------
/** Return the sample position vector */
306
307
308
Mantid::Kernel::V3D LeanElasticPeak::getSamplePos() const {
  throw Exception::NotImplementedError(
      "LeanElasticPeak has no sample information");
309
}
310
311
312

// -------------------------------------------------------------------------------------
/** Return the L1 flight path length (source to sample), in meters. */
313
314
315
double LeanElasticPeak::getL1() const {
  throw Exception::NotImplementedError(
      "LeanElasticPeak has no detector information");
316
}
317
318
319

// -------------------------------------------------------------------------------------
/** Return the L2 flight path length (sample to detector), in meters. */
320
321
322
double LeanElasticPeak::getL2() const {
  throw Exception::NotImplementedError(
      "LeanElasticPeak has no detector information");
323
}
324
325
326
327
328
329

/**
 * @brief Assignement operator overload
 * @param other : Other peak object to assign from
 * @return this
 */
330
LeanElasticPeak &LeanElasticPeak::operator=(const LeanElasticPeak &other) {
331
  if (&other != this) {
332
    BasePeak::operator=(other);
Whitfield, Ross's avatar
Whitfield, Ross committed
333
    m_Qsample = other.m_Qsample;
334
    m_wavelength = other.m_wavelength;
335
336
337
338
  }
  return *this;
}

Whitfield, Ross's avatar
Whitfield, Ross committed
339
340
341
342
343
344
345
346
347
/** After creating a peak using the Q in the lab frame,
 * the detPos is set to the direction of the detector (but the detector is
 *unknown)
 *
 * Using the instrument set in the peak, perform ray tracing
 * to find the exact detector.
 *
 * @return true if the detector ID was found.
 */
348
/*
349
350
351
bool LeanElasticPeak::findDetector() {
  throw Exception::NotImplementedError(
      "LeanElasticPeak has no detector information");
352
}
353
*/
Whitfield, Ross's avatar
Whitfield, Ross committed
354
355
356
357
358
359
360
361

/**
 * Performs the same algorithm as findDetector() but uses a pre-existing
 * InstrumentRayTracer object to be able to take adavtange of its caches.
 * This method should be preferred if findDetector is to be called many times
 * over the same instrument.
 * @return true if the detector ID was found.
 */
362
/*
363
364
365
bool LeanElasticPeak::findDetector(const InstrumentRayTracer &) {
  throw Exception::NotImplementedError(
      "LeanElasticPeak has no detector information");
Whitfield, Ross's avatar
Whitfield, Ross committed
366
}
367
*/
Whitfield, Ross's avatar
Whitfield, Ross committed
368

369
370
371
/**
 Forwarding function. Exposes the detector position directly.
 */
372
373
374
Mantid::Kernel::V3D LeanElasticPeak::getDetectorPositionNoCheck() const {
  throw Exception::NotImplementedError(
      "LeanElasticPeak has no detector information");
375
376
377
378
379
380
}

/**
 Forwarding function. Exposes the detector position directly, but checks that
 the detector is not null before accessing its position. Throws if null.
 */
381
382
383
Mantid::Kernel::V3D LeanElasticPeak::getDetectorPosition() const {
  throw Exception::NotImplementedError(
      "LeanElasticPeak has no detector information");
384
385
}

386
Mantid::Kernel::Logger LeanElasticPeak::g_log("PeakLogger");
387
388
389

} // namespace DataObjects
} // namespace Mantid