Mantid
Loading...
Searching...
No Matches
MDWSTransform.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 +
8
12#include "MantidKernel/MDUnit.h"
16
17#include <cfloat>
18
19namespace Mantid::MDAlgorithms {
20namespace {
21// logger for the algorithm workspaces
22Kernel::Logger g_Log("MDWSTransform");
23} // namespace
24using namespace CnvrtToMD;
25
51std::vector<double> MDWSTransform::getTransfMatrix(MDWSDescription &TargWSDescription,
52 const std::string &FrameRequested,
53 const std::string &QScaleRequested) const {
54 CoordScaling ScaleID = getQScaling(QScaleRequested);
55 TargetFrame FrameID = getTargetFrame(FrameRequested);
56 std::vector<double> transf = getTransfMatrix(TargWSDescription, FrameID, ScaleID);
57
58 if (TargWSDescription.isQ3DMode()) {
59 this->setQ3DDimensionsNames(TargWSDescription, FrameID, ScaleID);
60 }
61
62 return transf;
63}
73
74 bool hasGoniometer = TargWSDescription.hasGoniometer();
75 bool hasLattice = TargWSDescription.hasLattice();
76
77 if (!hasGoniometer) {
78 return LabFrame;
79 } else {
80 if (hasLattice)
81 return HKLFrame;
82 else
83 return SampleFrame;
84 }
85}
96 const CnvrtToMD::TargetFrame CoordFrameID) const {
97 switch (CoordFrameID) {
98 case (LabFrame): // nothing needed for lab frame
99 return;
100 case (SampleFrame):
101 if (!TargWSDescription.hasGoniometer())
102 throw std::invalid_argument(" Sample frame needs goniometer to be defined on the workspace ");
103 return;
104 case (HKLFrame): // ubMatrix has to be present
105 if (!TargWSDescription.hasLattice())
106 throw std::invalid_argument(" HKL frame needs UB matrix defined on the workspace ");
107 if (!TargWSDescription.hasGoniometer())
108 g_Log.warning() << " HKL frame does not have goniometer defined on the "
109 "workspace. Assuming unit goniometer matrix\n";
110 return;
111 default:
112 throw std::runtime_error(" Unexpected argument in MDWSTransform::checkTargetFrame");
113 }
114}
115
117std::vector<double> MDWSTransform::getTransfMatrix(MDWSDescription &TargWSDescription, CnvrtToMD::TargetFrame FrameID,
118 CoordScaling &ScaleID) const {
119 Kernel::Matrix<double> mat(3, 3, true);
120
121 bool powderMode = TargWSDescription.isPowder();
122 bool has_lattice(true);
123 if (!TargWSDescription.hasLattice())
124 has_lattice = false;
125
126 if (!(powderMode || has_lattice)) {
127 const std::string &inWsName = TargWSDescription.getWSName();
128 // notice about 3D case without lattice
129 g_Log.information() << "Can not obtain transformation matrix from the input workspace: " << inWsName
130 << " as no oriented lattice has been defined. \n"
131 "Will use unit transformation matrix.\n";
132 }
133 // set the frame ID to the values, requested by properties
134 CnvrtToMD::TargetFrame CoordFrameID(FrameID);
135 if (FrameID == AutoSelect || powderMode) {
136 CoordFrameID = findTargetFrame(TargWSDescription);
137 } else {
138 checkTargetFrame(TargWSDescription, CoordFrameID);
139 }
140 switch (CoordFrameID) {
141 case (CnvrtToMD::LabFrame): {
142 ScaleID = NoScaling;
143 TargWSDescription.m_Wtransf = buildQTrahsf(TargWSDescription, ScaleID, true);
144 // ignore goniometer
145 mat = TargWSDescription.m_Wtransf;
146 break;
147 }
148 case (CnvrtToMD::SampleFrame): {
149 ScaleID = NoScaling;
150 TargWSDescription.m_Wtransf = buildQTrahsf(TargWSDescription, ScaleID, true);
151 // Obtain the transformation matrix to Cartesian related to Crystal
152 mat = TargWSDescription.getGoniometerMatr() * TargWSDescription.m_Wtransf;
153 break;
154 }
155 case (CnvrtToMD::HKLFrame): {
156 TargWSDescription.m_Wtransf = buildQTrahsf(TargWSDescription, ScaleID, false);
157 // Obtain the transformation matrix to Cartesian related to Crystal
158 if (TargWSDescription.hasGoniometer())
159 mat = TargWSDescription.getGoniometerMatr() * TargWSDescription.m_Wtransf;
160 else
161 mat = TargWSDescription.m_Wtransf;
162
163 break;
164 }
165 default:
166 throw(std::invalid_argument(" Unknown or undefined Target Frame ID"));
167 }
168 //
169 // and this is the transformation matrix to notional
170 mat.Invert();
171
172 std::vector<double> rotMat = mat.getVector();
173 g_Log.debug() << " *********** Q-transformation matrix ***********************\n";
174 g_Log.debug() << "*** *qx ! *qy ! *qz !\n";
175 g_Log.debug() << "q1= " << rotMat[0] << " ! " << rotMat[1] << " ! " << rotMat[2] << " !\n";
176 g_Log.debug() << "q2= " << rotMat[3] << " ! " << rotMat[4] << " ! " << rotMat[5] << " !\n";
177 g_Log.debug() << "q3= " << rotMat[6] << " ! " << rotMat[7] << " ! " << rotMat[8] << " !\n";
178 g_Log.debug() << " *********** *********************** ***********************\n";
179 return rotMat;
180}
186 bool UnitUB) const {
187 // implements strategy
188 if (!(TargWSDescription.hasLattice() || UnitUB)) {
189 throw(std::invalid_argument("this function should be called only on "
190 "workspace with defined oriented lattice"));
191 }
192
193 // if u,v us default, Wmat is unit transformation
194 Kernel::DblMatrix Wmat(3, 3, true);
195 // derive rotation from u0,v0 u0||ki to u,v
196 if (!m_isUVdefault) {
197 Wmat[0][0] = m_UProj[0];
198 Wmat[1][0] = m_UProj[1];
199 Wmat[2][0] = m_UProj[2];
200 Wmat[0][1] = m_VProj[0];
201 Wmat[1][1] = m_VProj[1];
202 Wmat[2][1] = m_VProj[2];
203 Wmat[0][2] = m_WProj[0];
204 Wmat[1][2] = m_WProj[1];
205 Wmat[2][2] = m_WProj[2];
206 }
207 if (ScaleID == OrthogonalHKLScale) {
208 std::vector<Kernel::V3D> dim_directions;
209 std::vector<Kernel::V3D> uv(2);
210 uv[0] = m_UProj;
211 uv[1] = m_VProj;
212 dim_directions = Kernel::V3D::makeVectorsOrthogonal(uv);
213 for (size_t i = 0; i < 3; ++i)
214 for (size_t j = 0; j < 3; ++j)
215 Wmat[i][j] = dim_directions[j][i];
216 }
217 // Now define lab frame to target frame transformation
218 Kernel::DblMatrix Scale(3, 3, true);
219 Kernel::DblMatrix Transf(3, 3, true);
220 std::shared_ptr<Geometry::OrientedLattice> spLatt;
221 if (UnitUB)
222 spLatt = std::shared_ptr<Geometry::OrientedLattice>(new Geometry::OrientedLattice(1, 1, 1));
223 else
224 spLatt = TargWSDescription.getLattice();
225
226 switch (ScaleID) {
227 case NoScaling: //< momentums in A^-1
228 {
229 Transf = spLatt->getU();
230 break;
231 }
232 case SingleScale: //< momentums divided by 2*Pi/Lattice -- equivalent to
233 // d-spacing in some sense
234 {
235 double dMax(-1.e+32);
236 for (int i = 0; i < 3; i++)
237 dMax = (dMax > spLatt->a(i)) ? (dMax) : (spLatt->a(i));
238 for (int i = 0; i < 3; i++)
239 Scale[i][i] = (2 * M_PI) / dMax;
240 Transf = spLatt->getU();
241 break;
242 }
243 case OrthogonalHKLScale: //< each momentum component divided by appropriate
244 // lattice parameter; equivalent to hkl for orthogonal
245 // axis
246 {
247 if (spLatt) {
248 for (int i = 0; i < 3; i++) {
249 Scale[i][i] = (2 * M_PI) / spLatt->a(i);
250 }
251 Transf = spLatt->getU();
252 }
253 break;
254 }
255 case HKLScale: //< non-orthogonal system for non-orthogonal lattice
256 {
257 if (spLatt)
258 Scale = spLatt->getUB() * (2 * M_PI);
259 break;
260 }
261
262 default:
263 throw(std::invalid_argument("unrecognized conversion mode"));
264 }
265 TargWSDescription.addProperty("W_MATRIX", Wmat.getVector(), true);
266 return Transf * Scale * Wmat;
267}
268
278 CnvrtToMD::CoordScaling ScaleID) const {
279
280 std::vector<Kernel::V3D> dimDirections;
281 // set default dimension names:
282 std::vector<std::string> dimNames = TargWSDescription.getDimNames();
283
284 // define B-matrix and Lattice parameters to one in case if no OrientedLattice
285 // is there
286 Kernel::DblMatrix Bm(3, 3, true);
287 std::vector<double> LatPar(3, 1);
288 if (TargWSDescription.hasLattice()) { // redefine B-matrix and Lattice
289 // parameters from real oriented lattice
290 // if there is one
291 auto spLatt = TargWSDescription.getLattice();
292 Bm = spLatt->getB();
293 for (int i = 0; i < 3; i++)
294 LatPar[i] = spLatt->a(i);
295 }
296 if (FrameID == CnvrtToMD::AutoSelect)
297 FrameID = findTargetFrame(TargWSDescription);
298
299 switch (FrameID) {
300 case (CnvrtToMD::LabFrame): {
301 dimNames[0] = "Q_lab_x";
302 dimNames[1] = "Q_lab_y";
303 dimNames[2] = "Q_lab_z";
304 TargWSDescription.setCoordinateSystem(Mantid::Kernel::QLab);
305 TargWSDescription.setFrame(Geometry::QLab::QLabName);
306 break;
307 }
308 case (CnvrtToMD::SampleFrame): {
309 dimNames[0] = "Q_sample_x";
310 dimNames[1] = "Q_sample_y";
311 dimNames[2] = "Q_sample_z";
313 TargWSDescription.setFrame(Geometry::QSample::QSampleName);
314 break;
315 }
316 case (CnvrtToMD::HKLFrame): {
317 dimNames[0] = "H";
318 dimNames[1] = "K";
319 dimNames[2] = "L";
320
321 TargWSDescription.setCoordinateSystem(Mantid::Kernel::HKL);
322 TargWSDescription.setFrame(Geometry::HKL::HKLName);
323 break;
324 }
325 default:
326 throw(std::invalid_argument(" Unknown or undefined Target Frame ID"));
327 }
328
329 dimDirections.resize(3);
330 dimDirections[0] = m_UProj;
331 dimDirections[1] = m_VProj;
332 dimDirections[2] = m_WProj;
333 if (ScaleID == OrthogonalHKLScale) {
334 std::vector<Kernel::V3D> uv(2);
335 uv[0] = m_UProj;
336 uv[1] = m_VProj;
337 dimDirections = Kernel::V3D::makeVectorsOrthogonal(uv);
338 }
339 // axis names:
340 if ((FrameID == CnvrtToMD::LabFrame) || (FrameID == CnvrtToMD::SampleFrame))
341 for (int i = 0; i < 3; i++)
342 TargWSDescription.setDimName(i, dimNames[i]);
343 else
344 for (int i = 0; i < 3; i++)
345 TargWSDescription.setDimName(i, MDAlgorithms::makeAxisName(dimDirections[i], dimNames));
346
347 if (ScaleID == NoScaling) {
348 for (int i = 0; i < 3; i++)
349 TargWSDescription.setDimUnit(i, "A^-1");
350 }
351 if (ScaleID == SingleScale) {
352 double dMax(-1.e+32);
353 for (int i = 0; i < 3; i++)
354 dMax = (dMax > LatPar[i]) ? (dMax) : (LatPar[i]);
355 for (int i = 0; i < 3; i++)
356 TargWSDescription.setDimUnit(i, "in " + MDAlgorithms::sprintfd(2 * M_PI / dMax, 1.e-3) + " A^-1");
357 }
358 if ((ScaleID == OrthogonalHKLScale) || (ScaleID == HKLScale)) {
359 // get the length along each of the axes
360 std::vector<double> len;
362 x = Bm * dimDirections[0];
363 len.emplace_back(2 * M_PI * x.norm());
364 x = Bm * dimDirections[1];
365 len.emplace_back(2 * M_PI * x.norm());
366 x = Bm * dimDirections[2];
367 len.emplace_back(2 * M_PI * x.norm());
368 for (int i = 0; i < 3; i++)
369 TargWSDescription.setDimUnit(i, "in " + MDAlgorithms::sprintfd(len[i], 1.e-3) + " A^-1");
370 }
371}
372
374 const std::string &QScaleRequested) const { // TODO: nothing meaningful has
375 // been done at the moment,
376 // should enable scaling if
377 // different coord transf modes?
378
379 UNUSED_ARG(TargWSDescription);
380 UNUSED_ARG(QScaleRequested);
381}
383bool MDWSTransform::v3DIsDefault(const std::vector<double> &vect, const std::string &message) const {
384 bool def = true;
385 if (!vect.empty()) {
386
387 if (vect.size() == 3) {
388 def = false;
389 } else {
390 g_Log.warning() << message;
391 }
392 }
393 return def;
394}
395//
396void MDWSTransform::setUVvectors(const std::vector<double> &ut, const std::vector<double> &vt,
397 const std::vector<double> &wt) {
398 // identify if u,v are present among input parameters and use defaults if not
399 bool u_default(true), v_default(true), w_default(true);
400
401 u_default = v3DIsDefault(ut, " u projection vector specified but its dimensions are "
402 "not equal to 3, using default values [1,0,0]\n");
403 v_default = v3DIsDefault(vt, " v projection vector specified but its dimensions are "
404 "not equal to 3, using default values [0,1,0]\n");
405 w_default = v3DIsDefault(wt, " w projection vector specified but its dimensions are "
406 "not equal to 3, using default values [0,0,1]\n");
407
408 if (u_default) {
409 m_UProj = Kernel::V3D(1., 0., 0.);
410 } else {
411 m_UProj = Kernel::V3D(ut[0], ut[1], ut[2]);
412 }
413
414 if (v_default) {
415 m_VProj = Kernel::V3D(0., 1., 0.);
416 } else {
417 m_VProj = Kernel::V3D(vt[0], vt[1], vt[2]);
418 }
419
420 if (w_default) {
421 m_WProj = Kernel::V3D(0., 0., 1.);
422 } else {
423 m_WProj = Kernel::V3D(wt[0], wt[1], wt[2]);
424 }
425
426 m_isUVdefault = u_default && v_default && w_default;
427
428 // check if u, v, w are coplanar
429 if (fabs((m_UProj.cross_prod(m_VProj)).scalar_prod(m_WProj)) < Kernel::Tolerance) {
430 m_UProj = Kernel::V3D(1., 0., 0.);
431 m_VProj = Kernel::V3D(0., 1., 0.);
432 m_WProj = Kernel::V3D(0., 0., 1.);
433 m_isUVdefault = true;
434 throw std::invalid_argument("Projections are coplanar");
435 }
436}
439CoordScaling MDWSTransform::getQScaling(const std::string &ScID) const {
440 int nScaling = Kernel::Strings::isMember(m_QScalingID, ScID);
441
442 if (nScaling < 0)
443 throw(std::invalid_argument(" The Q scale with ID: " + ScID + " is unavailable"));
444
445 return CoordScaling(nScaling);
446}
449std::string MDWSTransform::getQScaling(const CnvrtToMD::CoordScaling ScaleID) const { return m_QScalingID[ScaleID]; }
450
453TargetFrame MDWSTransform::getTargetFrame(const std::string &FrameID) const {
454 int nFrame = Kernel::Strings::isMember(m_TargFramesID, FrameID);
455
456 if (nFrame < 0)
457 throw(std::invalid_argument(" The Target Frame with ID: " + FrameID + " is unavailable"));
458
459 return TargetFrame(nFrame);
460}
464 return m_TargFramesID[FrameID];
465}
466
467//
468MDWSTransform::MDWSTransform() : m_isUVdefault(true), m_QScalingID(NCoordScalings), m_TargFramesID(NTargetFrames) {
469 m_UProj[0] = 1;
470 m_UProj[1] = 0;
471 m_UProj[2] = 0;
472 m_VProj[0] = 0;
473 m_VProj[1] = 1;
474 m_VProj[2] = 0;
475 m_WProj[0] = 0;
476 m_WProj[1] = 0;
477 m_WProj[2] = 1;
478
479 m_QScalingID[NoScaling] = "Q in A^-1";
480 m_QScalingID[SingleScale] = "Q in lattice units";
481 m_QScalingID[OrthogonalHKLScale] = "Orthogonal HKL";
482 m_QScalingID[HKLScale] = "HKL";
483
484 m_TargFramesID[AutoSelect] = "AutoSelect";
485 m_TargFramesID[LabFrame] = "Q_lab";
486 m_TargFramesID[SampleFrame] = "Q_sample";
487 m_TargFramesID[HKLFrame] = "HKL";
488}
489} // namespace Mantid::MDAlgorithms
#define fabs(x)
Definition: Matrix.cpp:22
#define UNUSED_ARG(x)
Function arguments are sometimes unused in certain implmentations but are required for documentation ...
Definition: System.h:64
void addProperty(Kernel::Property *prop, bool overwrite=false)
Add data to the object in the form of a property.
Definition: LogManager.h:79
static const std::string HKLName
Definition: HKL.h:27
Class to implement UB matrix.
static const std::string QLabName
Definition: QLab.h:35
static const std::string QSampleName
Definition: QSample.h:23
Numerical Matrix class.
Definition: Matrix.h:42
T Invert()
LU inversion routine.
Definition: Matrix.cpp:924
std::vector< T > getVector() const
Definition: Matrix.cpp:77
Class for 3D vectors.
Definition: V3D.h:34
static std::vector< V3D > makeVectorsOrthogonal(const std::vector< V3D > &vectors)
Take a list of 2 vectors and makes a 3D orthogonal system out of them The first vector i0 is taken as...
Definition: V3D.cpp:298
constexpr V3D cross_prod(const V3D &v) const noexcept
Cross product (this * argument)
Definition: V3D.h:278
helper class describes the properties of target MD workspace, which should be obtained as the result ...
std::shared_ptr< Geometry::OrientedLattice > getLattice() const
bool isQ3DMode() const
Is the algorithm running in Q3D mode?
void setDimName(unsigned int nDim, const std::string &Name)
set specific (non-default) dimension name
void setFrame(const std::string &frameKey)
Set the md frame.
std::vector< std::string > getDimNames() const
Kernel::Matrix< double > getGoniometerMatr() const
method returns goniometer matrix if one is defined on the workspace or unit matrix if there are no su...
void setDimUnit(unsigned int nDim, const std::string &Unit)
this is rather misleading function, as MD workspace does not currently have dimension units.
bool hasGoniometer() const
Method checks if input workspace has defined goniometer.
const std::string & getWSName() const
bool isPowder() const
Method checks if the workspace is expected to be processed in powder mode.
void setCoordinateSystem(const Mantid::Kernel::SpecialCoordinateSystem system)
Set the special coordinate system if any.
void setModQDimensionsNames(MDAlgorithms::MDWSDescription &TargWSDescription, const std::string &QScaleRequested) const
construct meaningful dimension names for ModQ case and different transformation types defined by the ...
Kernel::V3D m_UProj
vectors, which describe the projection plain the target ws is based on (notional or cryst cartezian c...
Definition: MDWSTransform.h:96
bool v3DIsDefault(const std::vector< double > &vect, const std::string &message) const
check if input vector is defined
std::vector< double > getTransfMatrix(MDAlgorithms::MDWSDescription &TargWSDescription, const std::string &FrameRequested, const std::string &QScaleRequested) const
method to build the Q-coordinates transformation.
CnvrtToMD::CoordScaling getQScaling(const std::string &ScID) const
function which convert input string representing coordinate scaling to correspondent enum
Kernel::DblMatrix buildQTrahsf(MDAlgorithms::MDWSDescription &TargWSDescription, CnvrtToMD::CoordScaling ScaleID, bool UnitUB=false) const
function generates "Kind of" W transformation matrix for different Q-conversion modes;
std::vector< std::string > m_QScalingID
string representation of QScaling ID, which would be known to user
Definition: MDWSTransform.h:99
CnvrtToMD::TargetFrame findTargetFrame(MDAlgorithms::MDWSDescription &TargWSDescription) const
Method analyzes the state of UB matrix and goniometer attached to the workspace and decides,...
void checkTargetFrame(const MDAlgorithms::MDWSDescription &TargWSDescription, const CnvrtToMD::TargetFrame CoordFrameID) const
Method verifies if the information available on the source workspace is sufficient to build appropria...
CnvrtToMD::TargetFrame getTargetFrame(const std::string &FrameID) const
converts the target frame string representation into the frame ID
void setQ3DDimensionsNames(MDAlgorithms::MDWSDescription &TargWSDescription, CnvrtToMD::TargetFrame FrameID, CnvrtToMD::CoordScaling ScaleID) const
construct meaningful dimension names for Q3D case and different transformation types defined by the c...
void setUVvectors(const std::vector< double > &ut, const std::vector< double > &vt, const std::vector< double > &wt)
helper function which verifies if projection vectors are specified and if their values are correct wh...
std::vector< std::string > m_TargFramesID
string representation of Target frames, which would be exposed to user;
MANTID_KERNEL_DLL int isMember(const std::vector< std::string > &group, const std::string &candidate)
checks if the candidate is the member of the group
Definition: Strings.cpp:1050
constexpr double Tolerance
Standard tolerance value.
Definition: Tolerance.h:12
TargetFrame
enum describes availible target coordinate systems for Q3D mode
Definition: MDWSTransform.h:40
CoordScaling
enum descrines availble momentum scalings, interpreted by this class
Definition: MDWSTransform.h:30
std::string DLLExport sprintfd(const double data, const double eps)
creates string representation of the number with accuracy, cpecified by eps
std::string DLLExport makeAxisName(const Kernel::V3D &Dir, const std::vector< std::string > &QNames)
function to build mslice-like axis name from the vector, which describes crystallographic direction a...