Mantid
Loading...
Searching...
No Matches
Goniometer.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
10#include "MantidKernel/Quat.h"
12#include <boost/algorithm/string.hpp>
13#include <cstdlib>
14#include <sstream>
15#include <stdexcept>
16#include <string>
17#include <utility>
18
19#include <vector>
20
21using namespace Mantid::Kernel;
23
24namespace Mantid::Geometry {
26using Kernel::Quat;
27using Kernel::V3D;
28
30
31void GoniometerAxis::saveNexus(::NeXus::File *file, const std::string &group) const {
32 file->makeGroup(group, "NXmotor", true);
33 file->writeData("name", name);
34 file->writeData("angle", angle);
35 file->openData("angle");
36 std::string unit = (angleunit == angDegrees) ? "deg" : "rad";
37 file->putAttr("unit", unit);
38 std::string sen = (sense == CW) ? "CW" : "CCW";
39 file->putAttr("sense", sen);
40 file->closeData();
41 rotationaxis.saveNexus(file, "rotationaxis");
42 file->closeGroup();
43}
44
45void GoniometerAxis::loadNexus(::NeXus::File *file, const std::string &group) {
46 file->openGroup(group, "NXmotor");
47 file->readData("name", name);
48 file->readData("angle", angle);
49 file->openData("angle");
50 std::string s;
51 file->getAttr("sense", s);
52 sense = (s == "CW") ? CW : CCW;
53 file->getAttr("unit", s);
54 angleunit = (s == "rad") ? angRadians : angDegrees;
55 file->closeData();
56 rotationaxis.loadNexus(file, "rotationaxis");
57 file->closeGroup();
58}
59
62Goniometer::Goniometer() : R(3, 3, true), initFromR(false) {}
63
68 DblMatrix ide(3, 3), rtr(3, 3);
69 rtr = rot.Tprime() * rot;
70 ide.identityMatrix();
71 if (rtr == ide) {
72 R = rot;
73 initFromR = true;
74 } else
75 throw std::invalid_argument("rot is not a rotation matrix");
76}
77
80const Kernel::DblMatrix &Goniometer::getR() const { return R; }
81
86 R = std::move(rot);
87 initFromR = true;
88}
89
91bool Goniometer::isDefined() const { return initFromR || (!motors.empty()); }
92
93bool Goniometer::operator==(const Goniometer &other) const { return this->R == other.R; }
94
95bool Goniometer::operator!=(const Goniometer &other) const { return this->R != other.R; }
96
101std::string Goniometer::axesInfo() {
102 if (initFromR) {
103 return std::string("Goniometer was initialized from a rotation matrix. No "
104 "information about axis is available.\n");
105 } else {
106 std::stringstream info;
107 std::vector<GoniometerAxis>::iterator it;
108 std::string strCW("CW"), strCCW("CCW"), sense;
109
110 if (motors.empty()) {
111 info << "No axis is found\n";
112 } else {
113 info << "Name \t Direction \t Sense \t Angle \n";
114 for (it = motors.begin(); it < motors.end(); ++it) {
115 sense = ((*it).sense == CCW) ? strCCW : strCW;
116 double angle = ((*it).angleunit == angDegrees) ? ((*it).angle) : ((*it).angle * rad2deg);
117 info << (*it).name << "\t" << (*it).rotationaxis << "\t" << sense << "\t" << angle << '\n';
118 }
119 }
120 return info.str();
121 }
122}
123
133void Goniometer::pushAxis(const std::string &name, double axisx, double axisy, double axisz, double angle, int sense,
134 int angUnit) {
135 if (initFromR) {
136 throw std::runtime_error("Initialized from a rotation matrix, so no axes can be pushed.");
137 } else {
138 if (!std::isfinite(axisx) || !std::isfinite(axisy) || !std::isfinite(axisz) || !std::isfinite(angle)) {
139 g_log.warning() << "NaN encountered while trying to push axis to "
140 "goniometer, Operation aborted"
141 << "\naxis name" << name << "\naxisx" << axisx << "\naxisy" << axisx << "\naxisz" << axisz
142 << "\nangle" << angle;
143 return;
144 }
145 std::vector<GoniometerAxis>::iterator it;
146 // check if such axis is already defined
147 for (it = motors.begin(); it < motors.end(); ++it) {
148 if (name == it->name)
149 throw std::invalid_argument("Motor name already defined");
150 }
151 GoniometerAxis a(name, V3D(axisx, axisy, axisz), angle, sense, angUnit);
152 motors.emplace_back(a);
153 }
154 recalculateR();
155}
156
161void Goniometer::setRotationAngle(const std::string &name, double value) {
162 bool changed = false;
163 std::vector<GoniometerAxis>::iterator it;
164 for (it = motors.begin(); it < motors.end(); ++it) {
165 if (name == it->name) {
166 it->angle = value;
167 changed = true;
168 }
169 }
170 if (!changed) {
171 throw std::invalid_argument("Motor name " + name + " not found");
172 }
173 recalculateR();
174}
175
180void Goniometer::setRotationAngle(size_t axisnumber, double value) {
181 if (axisnumber >= motors.size())
182 throw std::out_of_range("Goniometer::setRotationAngle(): axis number specified is too large.");
183 (motors.at(axisnumber)).angle = value; // it will throw out of range exception
184 // if axisnumber is not in range
185 recalculateR();
186}
187
198 bool inner) {
199 V3D Q(position);
200 if (Kernel::ConfigService::Instance().getString("Q.convention") == "Crystallography")
201 Q *= -1;
202
203 Matrix<double> starting_goniometer = getR();
204
205 if (!inner)
206 Q = starting_goniometer * Q;
207
208 double wv = 2.0 * M_PI / wavelength;
209 double norm_q2 = Q.norm2();
210 double theta = acos(1 - norm_q2 / (2 * wv * wv)); // [0, pi]
211 double phi = asin(-Q[1] / (wv * sin(theta))); // [-pi/2, pi/2]
212 V3D Q_lab(-wv * sin(theta) * cos(phi), -wv * sin(theta) * sin(phi), wv * (1 - cos(theta)));
213
214 if (flip_x)
215 Q_lab[0] *= -1;
216
217 if (inner) {
218 starting_goniometer.Invert();
219 Q_lab = starting_goniometer * Q_lab;
220 }
221
222 // Solve to find rotation matrix, assuming only rotation around y-axis
223 // A * X = B
224 Matrix<double> A({Q[0], Q[2], Q[2], -Q[0]}, 2, 2);
225 A.Invert();
226 std::vector<double> B{Q_lab[0], Q_lab[2]};
227 std::vector<double> X = A * B;
228 double rot = atan2(X[1], X[0]);
229
230 Matrix<double> goniometer(3, 3, true);
231 goniometer[0][0] = cos(rot);
232 goniometer[0][2] = sin(rot);
233 goniometer[2][0] = -sin(rot);
234 goniometer[2][2] = cos(rot);
235 if (inner) {
236 starting_goniometer.Invert();
237 setR(starting_goniometer * goniometer);
238 } else {
239 setR(goniometer * starting_goniometer);
240 }
241}
242
245const GoniometerAxis &Goniometer::getAxis(size_t axisnumber) const {
246 return motors.at(axisnumber); // it will throw out of range exception if
247 // axisnumber is not in range
248}
249
252const GoniometerAxis &Goniometer::getAxis(const std::string &axisname) const {
253 for (auto it = motors.begin(); it < motors.end(); ++it) {
254 if (axisname == it->name) {
255 return (*it);
256 }
257 }
258 throw std::invalid_argument("Motor name " + axisname + " not found");
259}
260
262size_t Goniometer::getNumberAxes() const { return motors.size(); }
263
272 motors.clear();
273 this->pushAxis("omega", 0., 1., 0., 0., Mantid::Geometry::CCW, Mantid::Geometry::angDegrees);
274 this->pushAxis("chi", 0., 0., 1., 0., Mantid::Geometry::CCW, Mantid::Geometry::angDegrees);
275 this->pushAxis("phi", 0., 1., 0., 0., Mantid::Geometry::CCW, Mantid::Geometry::angDegrees);
276}
277
282std::vector<double> Goniometer::getEulerAngles(const std::string &convention) {
283 return Quat(getR()).getEulerAngles(convention);
284}
285
288 if (initFromR) {
289 g_log.warning() << "Goniometer was initialized from a rotation matrix. No "
290 << "recalculation from motors will be done.\n";
291 return;
292 }
293 std::vector<GoniometerAxis>::iterator it;
294 std::vector<double> elements;
295 Quat QGlobal, QCurrent;
296
297 for (it = motors.begin(); it < motors.end(); ++it) {
298 double ang = (*it).angle * (*it).sense;
299 if ((*it).angleunit == angRadians)
300 ang *= rad2deg;
301 QCurrent = Quat(ang, (*it).rotationaxis);
302 QGlobal *= QCurrent;
303 }
304 elements = QGlobal.getRotation();
305 R = DblMatrix(elements);
306}
307
308//--------------------------------------------------------------------------------------------
313void Goniometer::saveNexus(::NeXus::File *file, const std::string &group) const {
314 file->makeGroup(group, "NXpositioner", true);
315 file->putAttr("version", 1);
316 // Because the order of the axes is very important, they have to be written
317 // and read out in the same order
318 file->writeData("num_axes", int(motors.size()));
319 for (size_t i = 0; i < motors.size(); i++)
320 motors[i].saveNexus(file, "axis" + Strings::toString(i));
321 file->closeGroup();
322}
323
324//--------------------------------------------------------------------------------------------
329void Goniometer::loadNexus(::NeXus::File *file, const std::string &group) {
330 file->openGroup(group, "NXpositioner");
331 int num_axes;
332 file->readData("num_axes", num_axes);
333 motors.clear();
334 motors.reserve(num_axes);
335 for (int i = 0; i < num_axes; i++) {
336 GoniometerAxis newAxis;
337 newAxis.loadNexus(file, "axis" + Strings::toString(i));
338 motors.emplace_back(newAxis);
339 }
340 file->closeGroup();
341 // Refresh cached values
342 recalculateR();
343}
344
345} // namespace Mantid::Geometry
double value
The value of the point.
Definition: FitMW.cpp:51
double position
Definition: GetAllEi.cpp:154
Class to represent a particular goniometer setting, which is described by the rotation matrix.
Definition: Goniometer.h:55
Goniometer()
Default constructor The rotation matrix is initialized to identity.
Definition: Goniometer.cpp:62
bool initFromR
Flag to specify if the goniometer is initialized from a rotation matrix.
Definition: Goniometer.h:107
void setRotationAngle(const std::string &name, double value)
Set rotation angle for an axis using motor name.
Definition: Goniometer.cpp:161
std::vector< double > getEulerAngles(const std::string &convention="YZX")
Return Euler angles acording to a convention.
Definition: Goniometer.cpp:282
void recalculateR()
Private function to recalculate R when setRotationAngle is called.
Definition: Goniometer.cpp:287
void makeUniversalGoniometer()
Make a default universal goniometer with phi,chi,omega angles according to SNS convention.
Definition: Goniometer.cpp:271
bool operator!=(const Goniometer &other) const
Definition: Goniometer.cpp:95
std::vector< GoniometerAxis > motors
Motors vector contains GoniometerAxis objects, the last one is the closest to the sample.
Definition: Goniometer.h:105
bool isDefined() const
the method reports if the goniometer was defined with some parameters
Definition: Goniometer.cpp:91
bool operator==(const Goniometer &other) const
Definition: Goniometer.cpp:93
void pushAxis(const std::string &name, double axisx, double axisy, double axisz, double angle=0., int sense=CCW, int angUnit=angDegrees)
Add an additional axis to the goniometer, closer to the sample.
Definition: Goniometer.cpp:133
void loadNexus(::NeXus::File *file, const std::string &group)
Load the object from an open NeXus file.
Definition: Goniometer.cpp:329
void setR(Kernel::DblMatrix rot)
Set the new rotation matrix.
Definition: Goniometer.cpp:85
Kernel::DblMatrix R
Global rotation matrix of the goniometer.
Definition: Goniometer.h:102
void saveNexus(::NeXus::File *file, const std::string &group) const
Save the object to an open NeXus file.
Definition: Goniometer.cpp:313
const Kernel::DblMatrix & getR() const
Return global rotation matrix.
Definition: Goniometer.cpp:80
std::string axesInfo()
Return information about axes.
Definition: Goniometer.cpp:101
void calcFromQSampleAndWavelength(const Mantid::Kernel::V3D &Q, double wavelength, bool flip_x=false, bool inner=false)
Calculate goniometer for rotation around y-asix for constant wavelength from Q Sample.
Definition: Goniometer.cpp:197
const GoniometerAxis & getAxis(size_t axisnumber) const
Get GoniometerAxis obfject using motor number.
Definition: Goniometer.cpp:245
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition: Logger.h:52
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
T Invert()
LU inversion routine.
Definition: Matrix.cpp:924
void identityMatrix()
Makes the matrix an idenity matrix.
Definition: Matrix.cpp:661
Matrix< T > Tprime() const
Transpose the matrix.
Definition: Matrix.cpp:766
Class for quaternions.
Definition: Quat.h:39
std::vector< double > getEulerAngles(const std::string &convention) const
Calculate the Euler angles that are equivalent to this Quaternion.
Definition: Quat.cpp:729
std::vector< double > getRotation(bool check_normalisation=false, bool throw_on_errors=false) const
returns the rotation matrix defined by this quaternion as an 9-point
Definition: Quat.cpp:453
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
Class for 3D vectors.
Definition: V3D.h:34
void loadNexus(::NeXus::File *file, const std::string &name)
Load the object from an open NeXus file.
Definition: V3D.cpp:423
constexpr double norm2() const noexcept
Vector length squared.
Definition: V3D.h:265
void saveNexus(::NeXus::File *file, const std::string &name) const
Save the object to an open NeXus file.
Definition: V3D.cpp:412
constexpr double rad2deg
Radians to degrees conversion factor.
Definition: AngleUnits.h:23
@ CCW
Clockwise rotation.
Definition: Goniometer.h:34
Mantid::Kernel::Logger g_log("Goniometer")
std::string toString(const T &value)
Convert a number to a string.
Definition: Strings.cpp:703
Mantid::Kernel::Matrix< double > DblMatrix
Definition: Matrix.h:206
Counter clockwise rotation.
Definition: Goniometer.h:38
double angle
GoniometerAxis direction.
Definition: Goniometer.h:41
void loadNexus(::NeXus::File *file, const std::string &group)
Definition: Goniometer.cpp:45
int angleunit
Rotation sense (1 for CCW, -1 for CW)
Definition: Goniometer.h:43
int sense
Rotation angle.
Definition: Goniometer.h:42
void saveNexus(::NeXus::File *file, const std::string &group) const
Definition: Goniometer.cpp:31
Kernel::V3D rotationaxis
GoniometerAxis name.
Definition: Goniometer.h:40