Mantid
Loading...
Searching...
No Matches
NiggliCell.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 +
9#include "MantidKernel/Quat.h"
10#include <algorithm>
11#include <stdexcept>
12
13namespace Mantid::Geometry {
17
18namespace {
19const double RAD_TO_DEG = 180. / M_PI;
20}
26static bool CompareABCsum(const DblMatrix &UB_1, const DblMatrix &UB_2) {
27 V3D a1;
28 V3D b1;
29 V3D c1;
30 V3D a2;
31 V3D b2;
32 V3D c2;
33 OrientedLattice::GetABC(UB_1, a1, b1, c1);
34 OrientedLattice::GetABC(UB_2, a2, b2, c2);
35
36 double sum_1 = a1.norm() + b1.norm() + c1.norm();
37 double sum_2 = a2.norm() + b2.norm() + c2.norm();
38
39 return (sum_1 < sum_2);
40}
41
49static double GetDiffFrom90Sum(const DblMatrix &UB) {
50 V3D a;
51 V3D b;
52 V3D c;
53
54 if (!OrientedLattice::GetABC(UB, a, b, c))
55 return -1;
56
57 double alpha = b.angle(c) * RAD_TO_DEG;
58 double beta = c.angle(a) * RAD_TO_DEG;
59 double gamma = a.angle(b) * RAD_TO_DEG;
60
61 double sum = fabs(alpha - 90.0) + fabs(beta - 90.0) + fabs(gamma - 90.0);
62
63 return sum;
64}
65
70static bool CompareDiffFrom90(const DblMatrix &UB_1, const DblMatrix &UB_2) {
71 double sum_1 = GetDiffFrom90Sum(UB_1);
72 double sum_2 = GetDiffFrom90Sum(UB_2);
73
74 return (sum_2 < sum_1);
75}
80 if (Umatrix.isRotation()) {
81 U = Umatrix;
82 UB = U * getB();
83 } else
84 throw std::invalid_argument("U is not a proper rotation");
85}
86
96NiggliCell::NiggliCell(const double _a, const double _b, const double _c, const DblMatrix &Umatrix)
97 : UnitCell(_a, _b, _c) {
98 if (Umatrix.isRotation()) {
99 U = Umatrix;
100 UB = U * getB();
101 } else
102 throw std::invalid_argument("U is not a proper rotation");
103}
104
115NiggliCell::NiggliCell(const double _a, const double _b, const double _c, const double _alpha, const double _beta,
116 const double _gamma, const DblMatrix &Umatrix, const int angleunit)
117 : UnitCell(_a, _b, _c, _alpha, _beta, _gamma, angleunit) {
118 if (Umatrix.isRotation()) {
119 U = Umatrix;
120 UB = U * getB();
121 } else
122 throw std::invalid_argument("U is not a proper rotation");
123}
124
129NiggliCell::NiggliCell(const UnitCell &uc, const DblMatrix &Umatrix) : UnitCell(uc), U(Umatrix) {
130 if (Umatrix.isRotation()) {
131 U = Umatrix;
132 UB = U * getB();
133 } else
134 throw std::invalid_argument("U is not a proper rotation");
135}
136
154bool NiggliCell::HasNiggliAngles(const V3D &a_dir, const V3D &b_dir, const V3D &c_dir, double epsilon) {
155 double alpha = b_dir.angle(c_dir) * RAD_TO_DEG;
156 double beta = c_dir.angle(a_dir) * RAD_TO_DEG;
157 double gamma = a_dir.angle(b_dir) * RAD_TO_DEG;
158
159 if (alpha < 90 + epsilon && beta < 90 + epsilon && gamma < 90 + epsilon) {
160 return true;
161 }
162
163 if (alpha >= 90 - epsilon && beta >= 90 - epsilon && gamma >= 90 - epsilon) {
164 return true;
165 }
166
167 return false;
168}
169
184 V3D a;
185 V3D b;
186 V3D c;
187
188 if (!OrientedLattice::GetABC(UB, a, b, c)) {
189 return false;
190 }
191
192 V3D v1;
193 V3D v2;
194 V3D v3;
195 // first make a list of linear combinations
196 // of vectors a,b,c with coefficients up to 5
197 std::vector<V3D> directions;
198 int N_coeff = 5;
199 for (int i = -N_coeff; i <= N_coeff; i++) {
200 for (int j = -N_coeff; j <= N_coeff; j++) {
201 for (int k = -N_coeff; k <= N_coeff; k++) {
202 if (i != 0 || j != 0 || k != 0) {
203 v1 = a * i;
204 v2 = b * j;
205 v3 = c * k;
206 V3D sum(v1);
207 sum += v2;
208 sum += v3;
209 directions.emplace_back(sum);
210 }
211 }
212 }
213 }
214 // next sort the list of linear combinations
215 // in order of increasing length
216 std::sort(directions.begin(), directions.end(), V3D::compareMagnitude);
217
218 // next form a list of possible UB matrices
219 // using sides from the list of linear
220 // combinations, using shorter directions first.
221 // Keep trying more until 25 UBs are found.
222 // Only keep UBs corresponding to cells with
223 // at least a minimum cell volume
224 std::vector<DblMatrix> UB_list;
225
226 size_t num_needed = 25;
227 size_t max_to_try = 5;
228 while (UB_list.size() < num_needed && max_to_try < directions.size()) {
229 max_to_try *= 2;
230 size_t num_to_try = std::min(max_to_try, directions.size());
231
232 V3D acrossb;
233 double vol = 0;
234 double min_vol = .1f; // what should this be? 0.1 works OK, but...?
235 for (size_t i = 0; i < num_to_try - 2; i++) {
236 a = directions[i];
237 for (size_t j = i + 1; j < num_to_try - 1; j++) {
238 b = directions[j];
239 acrossb = a.cross_prod(b);
240 for (size_t k = j + 1; k < num_to_try; k++) {
241 c = directions[k];
242 vol = acrossb.scalar_prod(c);
243 if (vol > min_vol && HasNiggliAngles(a, b, c, 0.01)) {
244 Matrix<double> new_tran(3, 3, false);
245 OrientedLattice::GetUB(new_tran, a, b, c);
246 UB_list.emplace_back(new_tran);
247 }
248 }
249 }
250 }
251 }
252 // if no valid UBs could be formed, return
253 // false and the original UB
254 if (UB_list.empty()) {
255 newUB = UB;
256 return false;
257 }
258 // now sort the UB's in order of increasing
259 // total side length |a|+|b|+|c|
260 std::sort(UB_list.begin(), UB_list.end(), CompareABCsum);
261
262 // keep only those UB's with total side length
263 // within .1% of the first one. This can't
264 // be much larger or "bad" UBs are made for
265 // some tests with 5% noise
266 double length_tol = 0.001;
267 double total_length;
268
269 std::vector<DblMatrix> short_list;
270 short_list.emplace_back(UB_list[0]);
271 OrientedLattice::GetABC(short_list[0], a, b, c);
272 total_length = a.norm() + b.norm() + c.norm();
273
274 bool got_short_list = false;
275 size_t i = 1;
276 while (i < UB_list.size() && !got_short_list) {
277 OrientedLattice::GetABC(UB_list[i], v1, v2, v3);
278 double next_length = v1.norm() + v2.norm() + v3.norm();
279 if (fabs(next_length - total_length) / total_length < length_tol)
280 short_list.emplace_back(UB_list[i]);
281 else
282 got_short_list = true;
283 i++;
284 }
285 // now sort on the basis of difference of cell
286 // angles from 90 degrees and return the one
287 // with angles most different from 90
288 std::sort(short_list.begin(), short_list.end(), CompareDiffFrom90);
289
290 newUB = short_list[0];
291
292 return true;
293}
294
295} // namespace Mantid::Geometry
#define fabs(x)
Definition: Matrix.cpp:22
static bool HasNiggliAngles(const Kernel::V3D &a_dir, const Kernel::V3D &b_dir, const Kernel::V3D &c_dir, double epsilon)
Check if a,b,c cell has angles satifying Niggli condition within epsilon.
Definition: NiggliCell.cpp:154
Kernel::DblMatrix UB
Definition: NiggliCell.h:44
static bool MakeNiggliUB(const Kernel::DblMatrix &UB, Kernel::DblMatrix &newUB)
Construct a newUB corresponding to a Niggli cell from the given UB.
Definition: NiggliCell.cpp:183
NiggliCell(const Kernel::DblMatrix &Umatrix=Kernel::DblMatrix(3, 3, true))
Default constructor.
Definition: NiggliCell.cpp:79
Kernel::DblMatrix U
Definition: NiggliCell.h:43
static bool GetABC(const Kernel::DblMatrix &UB, Kernel::V3D &a_dir, Kernel::V3D &b_dir, Kernel::V3D &c_dir)
Get the real space edge vectors a, b, c corresponding to the UB matrix.
static bool GetUB(Kernel::DblMatrix &UB, const Kernel::V3D &a_dir, const Kernel::V3D &b_dir, const Kernel::V3D &c_dir)
Get the UB matix corresponding to the real space edge vectors a, b, c.
Class to implement unit cell of crystals.
Definition: UnitCell.h:44
const Kernel::DblMatrix & getB() const
Get the B-matrix.
Definition: UnitCell.cpp:758
double alpha() const
Get lattice parameter.
Definition: UnitCell.cpp:133
double c() const
Get lattice parameter.
Definition: UnitCell.cpp:128
double beta() const
Get lattice parameter.
Definition: UnitCell.cpp:138
double a() const
Get lattice parameter.
Definition: UnitCell.cpp:118
double b() const
Get lattice parameter.
Definition: UnitCell.cpp:123
double gamma() const
Get lattice parameter.
Definition: UnitCell.cpp:143
bool isRotation() const
Check if a matrix represents a proper rotation @ return :: true/false.
Definition: Matrix.cpp:1424
Class for 3D vectors.
Definition: V3D.h:34
static bool compareMagnitude(const Kernel::V3D &v1, const Kernel::V3D &v2)
Convenience method for sorting list of V3D objects based on magnitude.
Definition: V3D.cpp:483
constexpr double scalar_prod(const V3D &v) const noexcept
Calculates the cross product.
Definition: V3D.h:274
double angle(const V3D &) const
Angle between this and another vector.
Definition: V3D.cpp:165
double norm() const noexcept
Definition: V3D.h:263
static bool CompareABCsum(const DblMatrix &UB_1, const DblMatrix &UB_2)
Comparator function for sorting list of UB matrices based on the sum of the lengths of the correspond...
Definition: NiggliCell.cpp:26
static double GetDiffFrom90Sum(const DblMatrix &UB)
Get the cell angles for the unit cell corresponding to matrix UB and calculate the sum of the differe...
Definition: NiggliCell.cpp:49
static bool CompareDiffFrom90(const DblMatrix &UB_1, const DblMatrix &UB_2)
Comparator to sort a list of UBs in decreasing order based on the difference of cell angles from 90 d...
Definition: NiggliCell.cpp:70