Mantid
Loading...
Searching...
No Matches
NonOrthogonal.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 +
12#include "MantidAPI/Run.h"
13#include "MantidAPI/Sample.h"
17#include "MantidKernel/Matrix.h"
18
19#include <algorithm>
20#include <array>
21#include <boost/pointer_cast.hpp>
22#include <cmath>
23
54namespace {
55void checkForSampleAndRunEntries(const Mantid::API::Sample &sample, const Mantid::API::Run &run,
56 Mantid::Kernel::SpecialCoordinateSystem specialCoordinateSystem) {
57
58 if (Mantid::Kernel::HKL != specialCoordinateSystem) {
59 throw std::invalid_argument("Cannot create non-orthogonal view for non-HKL coordinates");
60 }
61
62 if (!sample.hasOrientedLattice()) {
63 throw std::invalid_argument("OrientedLattice is not present on workspace");
64 }
65
66 if (!run.hasProperty("W_MATRIX")) {
67 throw std::invalid_argument("W_MATRIX is not present on workspace");
68 }
69}
70
71void normalizeColumns(Mantid::Kernel::DblMatrix &skewMatrix) {
72 const auto numberOfColumns = skewMatrix.numCols();
73 const auto numberOfRows = skewMatrix.numRows();
74 std::vector<double> bNorm;
75 bNorm.reserve(skewMatrix.numCols());
76 for (size_t column = 0; column < numberOfColumns; ++column) {
77 double sumOverRow(0.0);
78 for (size_t row = 0; row < numberOfRows; ++row) {
79 sumOverRow += std::pow(skewMatrix[row][column], 2);
80 }
81 bNorm.emplace_back(std::sqrt(sumOverRow));
82 }
83
84 // Apply column normalisation to skew matrix
85 const size_t dim = 3;
86 Mantid::Kernel::DblMatrix scaleMat(3, 3, true);
87 for (size_t index = 0; index < dim; ++index) {
88 scaleMat[index][index] /= bNorm[index];
89 }
90
91 skewMatrix *= scaleMat;
92}
93
94void stripMatrix(Mantid::Kernel::DblMatrix &matrix) {
95 std::size_t dim = matrix.Ssize() - 1;
96 Mantid::Kernel::DblMatrix temp(dim, dim);
97 for (std::size_t i = 0; i < dim; i++) {
98 for (std::size_t j = 0; j < dim; j++) {
99 temp[i][j] = matrix[i][j];
100 }
101 }
102 matrix = temp;
103}
104
105template <typename T> void doProvideSkewMatrix(Mantid::Kernel::DblMatrix &skewMatrix, const T &workspace) {
106 // The input workspace needs have
107 // 1. an HKL frame
108 // 2. an oriented lattice
109 // else we cannot create the skew matrix
110 const auto &sample = workspace.getExperimentInfo(0)->sample();
111 const auto &run = workspace.getExperimentInfo(0)->run();
112 auto specialCoordnateSystem = workspace.getSpecialCoordinateSystem();
113 checkForSampleAndRunEntries(sample, run, specialCoordnateSystem);
114
115 // Create Affine Matrix
117 try {
118 auto const *transform = workspace.getTransformToOriginal();
119 if (transform)
120 affineMatrix = transform->makeAffineMatrix();
121 else {
122 // Create identity matrix of dimension+1
123 std::size_t nDims = workspace.getNumDims() + 1;
124 affineMatrix = Mantid::Kernel::Matrix<Mantid::coord_t>(nDims, nDims, true);
125 }
126 } catch (std::runtime_error &) {
127 // Create identity matrix of dimension+1
128 std::size_t nDims = workspace.getNumDims() + 1;
129 Mantid::Kernel::Matrix<Mantid::coord_t> temp(nDims, nDims, true);
130 affineMatrix = temp;
131 }
132
133 // Extract W Matrix
134 auto wMatrixAsArray = run.template getPropertyValueAsType<std::vector<double>>("W_MATRIX");
135 Mantid::Kernel::DblMatrix wMatrix(wMatrixAsArray);
136
137 // Get the B Matrix from the oriented lattice
138 const auto &orientedLattice = sample.getOrientedLattice();
139 Mantid::Kernel::DblMatrix bMatrix = orientedLattice.getB();
140 bMatrix *= wMatrix;
141
142 // Get G* Matrix
143 Mantid::Kernel::DblMatrix gStarMatrix = bMatrix.Tprime() * bMatrix;
144
145 // Get recalculated BMatrix from Unit Cell
146 Mantid::Geometry::UnitCell unitCell(orientedLattice);
147 unitCell.recalculateFromGstar(gStarMatrix);
148 skewMatrix = unitCell.getB();
149
150 // Provide column normalisation of the skewMatrix
151 normalizeColumns(skewMatrix);
152
153 // Setup basis normalisation array
154 std::vector<double> basisNormalization = {orientedLattice.astar(), orientedLattice.bstar(), orientedLattice.cstar()};
155
156 // Expand matrix to 4 dimensions if necessary
157 if (4 == workspace.getNumDims()) {
158 basisNormalization.emplace_back(1.0);
159 Mantid::Kernel::DblMatrix temp(4, 4, true);
160 for (std::size_t i = 0; i < 3; i++) {
161 for (std::size_t j = 0; j < 3; j++) {
162 temp[i][j] = skewMatrix[i][j];
163 }
164 }
165 skewMatrix = temp;
166 }
167
168 // The affine matrix has a underlying type of coord_t(float) but
169 // we need a double
170
171 auto reducedDimension = affineMatrix.Ssize() - 1;
172 Mantid::Kernel::DblMatrix affMat(reducedDimension, reducedDimension);
173 for (std::size_t i = 0; i < reducedDimension; i++) {
174 for (std::size_t j = 0; j < reducedDimension; j++) {
175 affMat[i][j] = affineMatrix[i][j];
176 }
177 }
178
179 // Perform similarity transform to get coordinate orientation correct
180 skewMatrix = affMat.Tprime() * (skewMatrix * affMat);
181
182 if (4 == workspace.getNumDims()) {
183 stripMatrix(skewMatrix);
184 }
185 // Current fix so skewed image displays in correct orientation
186 skewMatrix.Invert();
187}
188
189template <typename T> bool doRequiresSkewMatrix(const T &workspace) {
190 auto requiresSkew(true);
191 try {
192 const auto &sample = workspace.getExperimentInfo(0)->sample();
193 const auto &run = workspace.getExperimentInfo(0)->run();
194 auto specialCoordnateSystem = workspace.getSpecialCoordinateSystem();
195 checkForSampleAndRunEntries(sample, run, specialCoordnateSystem);
196 } catch (std::invalid_argument &) {
197 requiresSkew = false;
198 }
199
200 return requiresSkew;
201}
202
203template <size_t N>
204std::array<Mantid::coord_t, N> getTransformedArray(const std::array<Mantid::coord_t, N * N> &skewMatrix,
205 size_t dimension) {
206 std::array<Mantid::coord_t, N> vec = {{0., 0., 0.}};
207 for (size_t index = 0; index < N; ++index) {
208 vec[index] = skewMatrix[dimension + index * N];
209 }
210 return vec;
211}
212
213template <typename T, size_t N> void normalizeVector(std::array<T, N> &vector) {
214 auto sumOfSquares = [](T sum, T element) { return sum + element * element; };
215 auto norm = std::accumulate(vector.begin(), vector.end(), 0.f, sumOfSquares);
216 norm = std::sqrt(norm);
217 for (auto &element : vector) {
218 element /= norm;
219 }
220}
225std::array<Mantid::coord_t, 3> getNormalVector(std::array<Mantid::coord_t, 3> vector1,
226 std::array<Mantid::coord_t, 3> vector2) {
227 std::array<Mantid::coord_t, 3> normalVector;
228 for (size_t index = 0; index < 3; ++index) {
229 normalVector[index] =
230 vector1[(index + 1) % 3] * vector2[(index + 2) % 3] - vector1[(index + 2) % 3] * vector2[(index + 1) % 3];
231 }
232
233 // Make sure that the output is truely normalized
234 normalizeVector<Mantid::coord_t, 3>(normalVector);
235 return normalVector;
236}
237
246std::array<Mantid::coord_t, 3> getNormalVector(size_t dimX, size_t dimY) {
247 std::array<Mantid::coord_t, 3> vector1 = {{0., 0., 0.}};
248 std::array<Mantid::coord_t, 3> vector2 = {{0., 0., 0.}};
249 vector1[dimX] = 1.0;
250 vector2[dimY] = 1.0;
251
252 std::array<Mantid::coord_t, 3> normalVector;
253 for (size_t index = 0; index < 3; ++index) {
254 normalVector[index] =
255 vector1[(index + 1) % 3] * vector2[(index + 2) % 3] - vector1[(index + 2) % 3] * vector2[(index + 1) % 3];
256 }
257
258 // Make sure that the output is normalized
259 normalizeVector<Mantid::coord_t, 3>(normalVector);
260 return normalVector;
261}
266template <size_t N>
267double
268getAngleInRadian(std::array<Mantid::coord_t, N> orthogonalVector, std::array<Mantid::coord_t, N> nonOrthogonalVector,
269 const std::array<Mantid::coord_t, N> &normalVector, size_t currentDimension, size_t otherDimension) {
270 // We want to get the angle between an orthogonal basis vector eX, eY, eZ and
271 // the corresponding
272 // nonorthogonal basis vector H, K, L
273 // There are several special cases to consider.
274 // 1. When the currentDimension or otherDimension is x, then the angle is 0
275 // since x and H are aligned
276 // 2. When currentDimension is y and otherDimension is z,
277 // then the angle betwee K and eY is set to 0. This is a slight
278 // oddity since y-z and K are not in a plane. Mathematically, there is of
279 // course a potentially non-zero
280 // angle between K and eY, but this is not relevant for our 2D display.
281 // 3. When dimX/Y is z, then L needs to be projected onto either the x-z or
282 // the y-z plane (denpending
283 // on the current selection). The angle is calculatd between the projection
284 // and the eZ axis.
285
286 double angle(0.);
287 if (currentDimension == 0) {
288 // Handle case 1.
289 return 0.;
290 } else if (currentDimension == 1 && otherDimension == 2) {
291 // Handle case 2.
292 return 0.;
293 } else if (currentDimension == 2) {
294 // Handle case 3.
295 normalizeVector<Mantid::coord_t, N>(orthogonalVector);
296 normalizeVector<Mantid::coord_t, N>(nonOrthogonalVector);
297
298 // projecting onto third dimension by setting dimension coming out of screen
299 // to zero
300 std::array<Mantid::coord_t, 3> temporaryNonOrthogonal{{0.f, 0.f, 0.f}};
301 temporaryNonOrthogonal[currentDimension] = nonOrthogonalVector[currentDimension];
302 temporaryNonOrthogonal[otherDimension] = nonOrthogonalVector[otherDimension];
303 nonOrthogonalVector = temporaryNonOrthogonal;
304 }
305
306 normalizeVector<Mantid::coord_t, N>(orthogonalVector);
307 normalizeVector<Mantid::coord_t, N>(nonOrthogonalVector);
308 // Get the value of the angle from the dot product: v1*v2 = cos (a)*|v1|*|v2|
309 auto dotProduct =
310 std::inner_product(orthogonalVector.begin(), orthogonalVector.end(), nonOrthogonalVector.begin(), 0.f);
311
312 // Handle case where the dotProduct is 1 or -
313 if (dotProduct == 1.) {
314 angle = 0.;
315
316 } else if (dotProduct == -1.) {
317 angle = static_cast<double>(M_PI);
318
319 } else {
320 angle = std::acos(dotProduct);
321 }
322 // Get the direction of the angle
323 auto normalForDirection = getNormalVector(orthogonalVector, nonOrthogonalVector);
324 auto direction = std::inner_product(normalForDirection.begin(), normalForDirection.end(), normalVector.begin(), 0.f);
325
326 if (direction < 0) {
327 angle *= -1.f;
328 }
329 return angle;
330}
331} // namespace
332
333namespace MantidQt {
334namespace API {
335
337 for (size_t i = 0; i < workspace->getNumDims(); ++i) {
338 auto dimension = workspace->getDimension(i);
339 const auto &frame = dimension->getMDFrame();
340 if ((frame.name() == Mantid::Geometry::HKL::HKLName) && (i != dimX) && (i != dimY)) {
341 return i;
342 }
343 }
344 return static_cast<size_t>(NULL);
345}
346
348 if (auto mdew = dynamic_cast<const Mantid::API::IMDEventWorkspace *>(&workspace)) {
349 doProvideSkewMatrix(skewMatrix, *mdew);
350 } else if (auto mdhw = dynamic_cast<const Mantid::API::IMDHistoWorkspace *>(&workspace)) {
351 doProvideSkewMatrix(skewMatrix, *mdhw);
352 } else {
353 throw std::invalid_argument("NonOrthogonal: The provided workspace "
354 "must either be an IMDEvent or IMDHisto "
355 "workspace.");
356 }
357}
358
360 if (auto mdew = dynamic_cast<const Mantid::API::IMDEventWorkspace *>(&workspace)) {
361 return doRequiresSkewMatrix(*mdew);
362 } else if (auto mdhw = dynamic_cast<const Mantid::API::IMDHistoWorkspace *>(&workspace)) {
363 return doRequiresSkewMatrix(*mdhw);
364 } else {
365 return false;
366 }
367}
368
369bool isHKLDimensions(const Mantid::API::IMDWorkspace &workspace, size_t dimX, size_t dimY) {
370 auto hklname = [&workspace](size_t index) {
371 return workspace.getDimension(index)->getMDFrame().name() == Mantid::Geometry::HKL::HKLName;
372 };
373 return hklname(dimX) && hklname(dimY);
374}
375
377 std::array<Mantid::coord_t, 9> &skewMatrixCoord) {
378 std::size_t index = 0;
379 for (std::size_t i = 0; i < skewMatrix.numRows(); ++i) {
380 for (std::size_t j = 0; j < skewMatrix.numCols(); ++j) {
381 skewMatrixCoord[index] = static_cast<Mantid::coord_t>(skewMatrix[i][j]);
382 ++index;
383 }
384 }
385}
398void transformLookpointToWorkspaceCoord(Mantid::coord_t *lookPoint, const std::array<Mantid::coord_t, 9> &skewMatrix,
399 const size_t &dimX, const size_t &dimY, const size_t &dimSlice) {
400
401 auto sliceDimResult = (lookPoint[dimSlice] - skewMatrix[3 * dimSlice + dimX] * lookPoint[dimX] -
402 skewMatrix[3 * dimSlice + dimY] * lookPoint[dimY]) /
403 skewMatrix[3 * dimSlice + dimSlice];
404
405 auto OrigDimSliceValue = lookPoint[dimSlice];
406 lookPoint[dimSlice] = sliceDimResult;
407
408 auto v1 = lookPoint[0];
409 auto v2 = lookPoint[1];
410 auto v3 = lookPoint[2];
411
412 lookPoint[dimX] = v1 * skewMatrix[0 + 3 * dimX] + v2 * skewMatrix[1 + 3 * dimX] + v3 * skewMatrix[2 + 3 * dimX];
413 lookPoint[dimY] = v1 * skewMatrix[0 + 3 * dimY] + v2 * skewMatrix[1 + 3 * dimY] + v3 * skewMatrix[2 + 3 * dimY];
414
415 lookPoint[dimSlice] = OrigDimSliceValue;
416}
417
440std::pair<double, double> getGridLineAnglesInRadian(const std::array<Mantid::coord_t, 9> &skewMatrixCoord, size_t dimX,
441 size_t dimY) {
442 // Get the two vectors for the selected dimensions in the orthogonal axis
443 // representation.
444
445 std::array<Mantid::coord_t, 3> dimXOriginal = {{0., 0., 0.}};
446 std::array<Mantid::coord_t, 3> dimYOriginal = {{0., 0., 0.}};
447 dimXOriginal[dimX] = 1.0;
448 dimYOriginal[dimY] = 1.0;
449 auto dimXTransformed = getTransformedArray<3>(skewMatrixCoord, dimX);
450 auto dimYTransformed = getTransformedArray<3>(skewMatrixCoord, dimY);
451
452 // Get the normal vector for the selected dimensions
453 auto normalVector = getNormalVector(dimX, dimY);
454
455 // Get the angle for dimX and dimY
456 auto angleDimX = getAngleInRadian(dimXOriginal, dimXTransformed, normalVector, dimX, dimY);
457 auto angleDimY = getAngleInRadian(dimYOriginal, dimYTransformed, normalVector, dimY, dimX);
458 return std::make_pair(angleDimX, angleDimY);
459}
460} // namespace API
461} // namespace MantidQt
IPeaksWorkspace_sptr workspace
Definition: IndexPeaks.cpp:114
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
Abstract base class for multi-dimension event workspaces (MDEventWorkspace).
Abstract interface to MDHistoWorkspace, for use in exposing to Python.
Basic MD Workspace Abstract Class.
Definition: IMDWorkspace.h:40
bool hasProperty(const std::string &name) const
Does the property exist on the object.
Definition: LogManager.cpp:265
This class stores information regarding an experimental run as a series of log entries.
Definition: Run.h:38
This class stores information about the sample used in particular run.
Definition: Sample.h:33
const Geometry::OrientedLattice & getOrientedLattice() const
Get a reference to the sample's OrientedLattice.
Definition: Sample.cpp:154
bool hasOrientedLattice() const
Definition: Sample.cpp:179
static const std::string HKLName
Definition: HKL.h:27
Class to implement unit cell of crystals.
Definition: UnitCell.h:44
T Invert()
LU inversion routine.
Definition: Matrix.cpp:924
size_t Ssize() const
Return the smallest matrix size.
Definition: Matrix.h:150
Matrix< T > Tprime() const
Transpose the matrix.
Definition: Matrix.cpp:766
size_t numRows() const
Return the number of rows in the matrix.
Definition: Matrix.h:144
size_t numCols() const
Return the number of columns in the matrix.
Definition: Matrix.h:147
std::pair< double, double > EXPORT_OPT_MANTIDQT_COMMON getGridLineAnglesInRadian(const std::array< Mantid::coord_t, 9 > &skewMatrixCoord, size_t dimX, size_t dimY)
We get the angles that are used for plotting of the grid lines.
void EXPORT_OPT_MANTIDQT_COMMON provideSkewMatrix(Mantid::Kernel::DblMatrix &skewMatrix, const Mantid::API::IMDWorkspace &workspace)
bool EXPORT_OPT_MANTIDQT_COMMON requiresSkewMatrix(const Mantid::API::IMDWorkspace &workspace)
bool EXPORT_OPT_MANTIDQT_COMMON isHKLDimensions(const Mantid::API::IMDWorkspace &workspace, size_t dimX, size_t dimY)
void transformLookpointToWorkspaceCoord(T &lookPoint, const std::array< Mantid::coord_t, 9 > &skewMatrix, const size_t &dimX, const size_t &dimY, const size_t &dimSlice)
Definition: NonOrthogonal.h:33
void EXPORT_OPT_MANTIDQT_COMMON transformFromDoubleToCoordT(const Mantid::Kernel::DblMatrix &skewMatrix, std::array< Mantid::coord_t, 9 > &skewMatrixCoord)
size_t EXPORT_OPT_MANTIDQT_COMMON getMissingHKLDimensionIndex(const Mantid::API::IMDWorkspace_const_sptr &workspace, size_t dimX, size_t dimY)
The AlgorithmProgressDialogPresenter keeps track of the running algorithms and displays a progress ba...
std::shared_ptr< const IMDWorkspace > IMDWorkspace_const_sptr
Shared pointer to the IMDWorkspace base class (const version)
Definition: IMDWorkspace.h:148
double dotProduct(const DoubleFortranVector &x, const DoubleFortranVector &y)
Dot product of two vectors.
Definition: TrustRegion.cpp:93
std::vector< T > normalizeVector(const std::vector< T > &x)
Normalize a vector of any size to unity, using the sum of the squares of the components.
Definition: VectorHelper.h:136
SpecialCoordinateSystem
Special coordinate systems for Q3D.
float coord_t
Typedef for the data type to use for coordinate axes in MD objects such as MDBox, MDEventWorkspace,...
Definition: MDTypes.h:27