1 /******************************************************************************
2
3 This source file is part of the Avogadro project.
4
5 Copyright 2013 Kitware, Inc.
6
7 This source code is released under the New BSD License, (the "License").
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14
15 ******************************************************************************/
16
17 #include "crystaltools.h"
18
19 #include "molecule.h"
20 #include "unitcell.h"
21
22 #include <algorithm>
23 #include <iostream>
24
25 namespace Avogadro {
26 namespace Core {
27
28 namespace {
29 struct WrapAtomsToCellFunctor
30 {
31 const UnitCell& unitCell;
32
WrapAtomsToCellFunctorAvogadro::Core::__anonf775c0600111::WrapAtomsToCellFunctor33 WrapAtomsToCellFunctor(Molecule& molecule) : unitCell(*molecule.unitCell()) {}
34
operator ()Avogadro::Core::__anonf775c0600111::WrapAtomsToCellFunctor35 void operator()(Vector3& pos) { unitCell.wrapCartesian(pos, pos); }
36 };
37 }
38
wrapAtomsToUnitCell(Molecule & molecule)39 bool CrystalTools::wrapAtomsToUnitCell(Molecule& molecule)
40 {
41 if (!molecule.unitCell())
42 return false;
43
44 std::for_each(molecule.atomPositions3d().begin(),
45 molecule.atomPositions3d().end(),
46 WrapAtomsToCellFunctor(molecule));
47 return true;
48 }
49
rotateToStandardOrientation(Molecule & molecule,Options opts)50 bool CrystalTools::rotateToStandardOrientation(Molecule& molecule, Options opts)
51 {
52 if (!molecule.unitCell())
53 return false;
54
55 const UnitCell& cell = *molecule.unitCell();
56
57 const Matrix3& before = cell.cellMatrix();
58
59 // Extract vector components:
60 const Real& x1 = before(0, 0);
61 const Real& y1 = before(1, 0);
62 const Real& z1 = before(2, 0);
63
64 const Real& x2 = before(0, 1);
65 const Real& y2 = before(1, 1);
66 const Real& z2 = before(2, 1);
67
68 const Real& x3 = before(0, 2);
69 const Real& y3 = before(1, 2);
70 const Real& z3 = before(2, 2);
71
72 // Cache some frequently used values:
73 // Length of v1
74 const Real L1 = std::sqrt(x1 * x1 + y1 * y1 + z1 * z1);
75 // Squared norm of v1's yz projection
76 const Real sqrdnorm1yz = y1 * y1 + z1 * z1;
77 // Squared norm of v2's yz projection
78 const Real sqrdnorm2yz = y2 * y2 + z2 * z2;
79 // Determinant of v1 and v2's projections in yz plane
80 const Real detv1v2yz = y2 * z1 - y1 * z2;
81 // Scalar product of v1 and v2's projections in yz plane
82 const Real dotv1v2yz = y1 * y2 + z1 * z2;
83
84 // Used for denominators, since we want to check that they are
85 // sufficiently far from 0 to keep things reasonable:
86 Real denom;
87 const Real DENOM_TOL = 1e-5;
88
89 // Create target matrix, fill with zeros
90 Matrix3 newMat(Matrix3::Zero());
91
92 // Set components of new v1:
93 newMat(0, 0) = L1;
94
95 // Set components of new v2:
96 denom = L1;
97 if (fabs(denom) < DENOM_TOL)
98 return false;
99
100 newMat(0, 1) = (x1 * x2 + y1 * y2 + z1 * z2) / denom;
101
102 newMat(1, 1) = sqrt(x2 * x2 * sqrdnorm1yz + detv1v2yz * detv1v2yz -
103 2 * x1 * x2 * dotv1v2yz + x1 * x1 * sqrdnorm2yz) /
104 denom;
105
106 // Set components of new v3
107 newMat(0, 2) = (x1 * x3 + y1 * y3 + z1 * z3) / denom;
108
109 denom = L1 * L1 * newMat(1, 1);
110 if (fabs(denom) < DENOM_TOL)
111 return false;
112
113 newMat(1, 2) = (x1 * x1 * (y2 * y3 + z2 * z3) +
114 x2 * (x3 * sqrdnorm1yz - x1 * (y1 * y3 + z1 * z3)) +
115 detv1v2yz * (y3 * z1 - y1 * z3) - x1 * x3 * dotv1v2yz) /
116 denom;
117
118 denom = L1 * newMat(1, 1);
119 if (fabs(denom) < DENOM_TOL)
120 return false;
121
122 // Numerator is determinant of original cell:
123 newMat(2, 2) = before.determinant() / denom;
124
125 return setCellMatrix(molecule, newMat, opts & TransformAtoms);
126 }
127
setVolume(Molecule & molecule,Real newVolume,Options opts)128 bool CrystalTools::setVolume(Molecule& molecule, Real newVolume, Options opts)
129 {
130 if (!molecule.unitCell())
131 return false;
132
133 const UnitCell& cell = *molecule.unitCell();
134
135 const Real scaleFactor =
136 std::pow(newVolume / cell.volume(), static_cast<Real>(1.0 / 3.0));
137
138 const Matrix3 newMatrix(cell.cellMatrix() * scaleFactor);
139
140 return setCellMatrix(molecule, newMatrix, opts & TransformAtoms);
141 }
142
143 // A collection of fuzzy comparison operators used in the niggli reduction
144 // algorithm:
145 namespace {
146 const double FUZZY_TOL(1e-5);
147 template <typename T>
fuzzyLessThan(T v1,T v2,T prec=static_cast<T> (FUZZY_TOL))148 bool fuzzyLessThan(T v1, T v2, T prec = static_cast<T>(FUZZY_TOL))
149 {
150 return (v1 < (v2 - prec));
151 }
152
153 template <typename T>
fuzzyGreaterThan(T v1,T v2,T prec=static_cast<T> (FUZZY_TOL))154 bool fuzzyGreaterThan(T v1, T v2, T prec = static_cast<T>(FUZZY_TOL))
155 {
156 return (v2 < (v1 - prec));
157 }
158
159 template <typename T>
fuzzyEqual(T v1,T v2,T prec=static_cast<T> (FUZZY_TOL))160 bool fuzzyEqual(T v1, T v2, T prec = static_cast<T>(FUZZY_TOL))
161 {
162 return (!(fuzzyLessThan(v1, v2, prec) || fuzzyGreaterThan(v1, v2, prec)));
163 }
164
165 template <typename T>
fuzzyNotEqual(T v1,T v2,T prec=static_cast<T> (FUZZY_TOL))166 bool fuzzyNotEqual(T v1, T v2, T prec = static_cast<T>(FUZZY_TOL))
167 {
168 return (!(fuzzyEqual(v1, v2, prec)));
169 }
170
171 template <typename T>
fuzzyLessThanEq(T v1,T v2,T prec=static_cast<T> (FUZZY_TOL))172 bool fuzzyLessThanEq(T v1, T v2, T prec = static_cast<T>(FUZZY_TOL))
173 {
174 return (!fuzzyGreaterThan(v1, v2, prec));
175 }
176
177 template <typename T>
fuzzyGreaterThanEq(T v1,T v2,T prec=static_cast<T> (FUZZY_TOL))178 bool fuzzyGreaterThanEq(T v1, T v2, T prec = static_cast<T>(FUZZY_TOL))
179 {
180 return (!lt(v1, v2, prec));
181 }
182
183 template <typename T>
niggliSign(T v)184 T niggliSign(T v)
185 {
186 // consider 0 to be positive
187 return (v >= static_cast<T>(0.)) ? static_cast<T>(1.0) : static_cast<T>(-1.0);
188 }
189
190 template <typename T>
niggliRound(T v,T dec)191 T niggliRound(T v, T dec)
192 {
193 const T shift = std::pow(10.0, dec);
194 const T shifted = v * shift;
195 return std::floor(shifted + 0.5) / shift;
196 }
197 }
198
niggliReduce(Molecule & molecule,Options opts)199 bool CrystalTools::niggliReduce(Molecule& molecule, Options opts)
200 {
201 if (!molecule.unitCell())
202 return false;
203
204 UnitCell& cell = *molecule.unitCell();
205
206 // Maximum number of iterations
207 const unsigned int maxIterations = 1000;
208
209 // Get cell parameters in storage units, convert deg->rad
210 Real a = cell.a();
211 Real b = cell.b();
212 Real c = cell.c();
213 Real alpha = cell.alpha();
214 Real beta = cell.beta();
215 Real gamma = cell.gamma();
216
217 // Compute characteristic (step 0)
218 Real A = a * a;
219 Real B = b * b;
220 Real C = c * c;
221 Real xi = 2 * b * c * std::cos(alpha);
222 Real eta = 2 * a * c * std::cos(beta);
223 Real zeta = 2 * a * b * std::cos(gamma);
224
225 // Return value.
226 bool ret = false;
227
228 // Comparison tolerance.
229 Real tol = FUZZY_TOL * std::pow(a * b * c, static_cast<Real>(1.0 / 3.0));
230
231 // Initialize change of basis matrices:
232 //
233 // Although the reduction algorithm produces quantities directly
234 // relatable to a,b,c,alpha,beta,gamma, we will calculate a change
235 // of basis matrix to use instead, and discard A, B, C, xi, eta,
236 // zeta. By multiplying the change of basis matrix against the
237 // current cell matrix, we avoid the problem of handling the
238 // orientation matrix already present in the cell. The inverse of
239 // this matrix can also be used later to convert the atomic
240 // positions.
241
242 // tmpMat is used to build other matrices
243 Matrix3 tmpMat;
244
245 // Cache static matrices:
246
247 // Swap x, y (Used in Step 1). Negatives ensure proper sign of final
248 // determinant.
249 tmpMat << 0, -1, 0, -1, 0, 0, 0, 0, -1;
250 const Matrix3 C1(tmpMat);
251 // Swap y, z (Used in Step 2). Negatives ensure proper sign of final
252 // determinant
253 tmpMat << -1, 0, 0, 0, 0, -1, 0, -1, 0;
254 const Matrix3 C2(tmpMat);
255 // For step 8:
256 tmpMat << 1, 0, 1, 0, 1, 1, 0, 0, 1;
257 const Matrix3 C8(tmpMat);
258
259 // initial change of basis matrix
260 tmpMat << 1, 0, 0, 0, 1, 0, 0, 0, 1;
261 Matrix3 cob(tmpMat);
262
263 // Enable debugging output here:
264 /*
265 #define NIGGLI_DEBUG(step) \
266 std::cout << iter << " " << step << " " << A << " " << B << " " << C \
267 << " " << xi << " " << eta << " " << zeta << std::endl;
268 */
269 #define NIGGLI_DEBUG(step)
270
271 // Perform iterative reduction:
272 unsigned int iter;
273 for (iter = 0; iter < maxIterations; ++iter) {
274 // Step 1:
275 if (fuzzyGreaterThan(A, B, tol) ||
276 (fuzzyEqual(A, B, tol) &&
277 fuzzyGreaterThan(std::fabs(xi), std::fabs(eta), tol))) {
278 cob *= C1;
279 std::swap(A, B);
280 std::swap(xi, eta);
281 NIGGLI_DEBUG(1);
282 }
283
284 // Step 2:
285 if (fuzzyGreaterThan(B, C, tol) ||
286 (fuzzyEqual(B, C, tol) &&
287 fuzzyGreaterThan(std::fabs(eta), std::fabs(zeta), tol))) {
288 cob *= C2;
289 std::swap(B, C);
290 std::swap(eta, zeta);
291 NIGGLI_DEBUG(2);
292 continue;
293 }
294
295 // Step 3:
296 // Use exact comparisons in steps 3 and 4.
297 if (xi * eta * zeta > 0) {
298 // Update change of basis matrix:
299 tmpMat << niggliSign(xi), 0, 0, 0, niggliSign(eta), 0, 0, 0,
300 niggliSign(zeta);
301 cob *= tmpMat;
302
303 // Update characteristic
304 xi = std::fabs(xi);
305 eta = std::fabs(eta);
306 zeta = std::fabs(zeta);
307 NIGGLI_DEBUG(3);
308 ++iter;
309 }
310
311 // Step 4:
312 // Use exact comparisons for steps 3 and 4
313 else { // either step 3 or 4 should run
314 // Update change of basis matrix:
315 Real* p = nullptr;
316 Real i = 1;
317 Real j = 1;
318 Real k = 1;
319 if (xi > 0) {
320 i = -1;
321 } else if (!(xi < 0)) {
322 p = &i;
323 }
324 if (eta > 0) {
325 j = -1;
326 } else if (!(eta < 0)) {
327 p = &j;
328 }
329 if (zeta > 0) {
330 k = -1;
331 } else if (!(zeta < 0)) {
332 p = &k;
333 }
334 if (i * j * k < 0) {
335 if (!p) {
336 // This was originally an error message displayed in a dialog:
337 // Niggli-reduction failed. The input structure's lattice is confusing
338 // the Niggli-reduction algorithm. Try making a small perturbation
339 // (approx. 2 orders of magnitude smaller than the tolerance) to the
340 // input lattices and try again.
341 return false;
342 }
343 *p = -1;
344 }
345 tmpMat << i, 0, 0, 0, j, 0, 0, 0, k;
346 cob *= tmpMat;
347
348 // Update characteristic
349 xi = -std::fabs(xi);
350 eta = -std::fabs(eta);
351 zeta = -std::fabs(zeta);
352 NIGGLI_DEBUG(4);
353 ++iter;
354 }
355
356 // Step 5:
357 if (fuzzyGreaterThan(std::fabs(xi), B, tol) ||
358 (fuzzyEqual(xi, B, tol) && fuzzyLessThan(2 * eta, zeta, tol)) ||
359 (fuzzyEqual(xi, -B, tol) && fuzzyLessThan(zeta, Real(0), tol))) {
360 Real signXi = niggliSign(xi);
361 // Update change of basis matrix:
362 tmpMat << 1, 0, 0, 0, 1, -signXi, 0, 0, 1;
363 cob *= tmpMat;
364
365 // Update characteristic
366 C = B + C - xi * signXi;
367 eta = eta - zeta * signXi;
368 xi = xi - 2 * B * signXi;
369 NIGGLI_DEBUG(5);
370 continue;
371 }
372
373 // Step 6:
374 if (fuzzyGreaterThan(std::fabs(eta), A, tol) ||
375 (fuzzyEqual(eta, A, tol) && fuzzyLessThan(2 * xi, zeta, tol)) ||
376 (fuzzyEqual(eta, -A, tol) && fuzzyLessThan(zeta, Real(0), tol))) {
377 Real signEta = niggliSign(eta);
378 // Update change of basis matrix:
379 tmpMat << 1, 0, -signEta, 0, 1, 0, 0, 0, 1;
380 cob *= tmpMat;
381
382 // Update characteristic
383 C = A + C - eta * signEta;
384 xi = xi - zeta * signEta;
385 eta = eta - 2 * A * signEta;
386 NIGGLI_DEBUG(6);
387 continue;
388 }
389
390 // Step 7:
391 if (fuzzyGreaterThan(std::fabs(zeta), A, tol) ||
392 (fuzzyEqual(zeta, A, tol) && fuzzyLessThan(2 * xi, eta, tol)) ||
393 (fuzzyEqual(zeta, -A, tol) && fuzzyLessThan(eta, Real(0), tol))) {
394 Real signZeta = niggliSign(zeta);
395 // Update change of basis matrix:
396 tmpMat << 1, -signZeta, 0, 0, 1, 0, 0, 0, 1;
397 cob *= tmpMat;
398
399 // Update characteristic
400 B = A + B - zeta * signZeta;
401 xi = xi - eta * signZeta;
402 zeta = zeta - 2 * A * signZeta;
403 NIGGLI_DEBUG(7);
404 continue;
405 }
406
407 // Step 8:
408 Real sumAllButC = A + B + xi + eta + zeta;
409 if (fuzzyLessThan(sumAllButC, Real(0), tol) ||
410 (fuzzyEqual(sumAllButC, Real(0), tol) &&
411 fuzzyGreaterThan(2 * (A + eta) + zeta, Real(0), tol))) {
412 // Update change of basis matrix:
413 cob *= C8;
414
415 // Update characteristic
416 C = sumAllButC + C;
417 xi = 2 * B + xi + zeta;
418 eta = 2 * A + eta + zeta;
419 NIGGLI_DEBUG(8);
420 continue;
421 }
422
423 // Done!
424 ret = true;
425 break;
426 }
427
428 // No change
429 if (iter == 0)
430 return true;
431
432 // Iteration limit exceeded:
433 if (!ret)
434 return false;
435
436 // Update atoms if needed
437 if (opts & TransformAtoms) {
438 // Get fractional coordinates
439 Array<Vector3> fcoords;
440 if (!fractionalCoordinates(molecule, fcoords))
441 return false;
442
443 // fix coordinates with COB matrix:
444 const Matrix3 invCob(cob.inverse());
445 for (Array<Vector3>::iterator it = fcoords.begin(), itEnd = fcoords.end();
446 it != itEnd; ++it) {
447 *it = invCob * (*it);
448 }
449
450 // Update cell
451 cell.setCellMatrix(cell.cellMatrix() * cob);
452
453 // Reapply the fractional coordinates
454 setFractionalCoordinates(molecule, fcoords);
455 } else {
456 // just update the matrix:
457 cell.setCellMatrix(cell.cellMatrix() * cob);
458 }
459 return true;
460 }
461
isNiggliReduced(const Molecule & molecule)462 bool CrystalTools::isNiggliReduced(const Molecule& molecule)
463 {
464 if (!molecule.unitCell())
465 return false;
466
467 const UnitCell& cell = *molecule.unitCell();
468
469 const Real a = cell.a();
470 const Real b = cell.b();
471 const Real c = cell.c();
472 const Real alpha = cell.alpha();
473 const Real beta = cell.beta();
474 const Real gamma = cell.gamma();
475
476 const Real A = a * a;
477 const Real B = b * b;
478 const Real C = c * c;
479 const Real xi = static_cast<Real>(2) * b * c * std::cos(alpha);
480 const Real eta = static_cast<Real>(2) * a * c * std::cos(beta);
481 const Real zeta = static_cast<Real>(2) * a * b * std::cos(gamma);
482
483 const Real tol = FUZZY_TOL * ((a + b + c) * static_cast<Real>(1. / 3.));
484
485 // First check the Buerger conditions. Taken from: Gruber B.. Acta
486 // Cryst. A. 1973;29(4):433-440. Available at:
487 // http://scripts.iucr.org/cgi-bin/paper?S0567739473001063
488 // [Accessed December 15, 2010].
489 if (fuzzyGreaterThan(A, B, tol) || fuzzyGreaterThan(B, C, tol))
490 return false;
491
492 if (fuzzyEqual(A, B, tol) &&
493 fuzzyGreaterThan(std::fabs(xi), std::fabs(eta), tol)) {
494 return false;
495 }
496
497 if (fuzzyEqual(B, C, tol) &&
498 fuzzyGreaterThan(std::fabs(eta), std::fabs(zeta), tol)) {
499 return false;
500 }
501
502 if (!(fuzzyGreaterThan(xi, static_cast<Real>(0.0), tol) &&
503 fuzzyGreaterThan(eta, static_cast<Real>(0.0), tol) &&
504 fuzzyGreaterThan(zeta, static_cast<Real>(0.0), tol)) &&
505 !(fuzzyLessThanEq(zeta, static_cast<Real>(0.0), tol) &&
506 fuzzyLessThanEq(zeta, static_cast<Real>(0.0), tol) &&
507 fuzzyLessThanEq(zeta, static_cast<Real>(0.0), tol))) {
508 return false;
509 }
510
511 // Check against Niggli conditions (taken from Gruber 1973). The
512 // logic of the second comparison is reversed from the paper to
513 // simplify the algorithm.
514 if (fuzzyEqual(xi, B, tol) &&
515 fuzzyGreaterThan(zeta, static_cast<Real>(2) * eta, tol)) {
516 return false;
517 }
518 if (fuzzyEqual(eta, A, tol) &&
519 fuzzyGreaterThan(zeta, static_cast<Real>(2) * xi, tol)) {
520 return false;
521 }
522 if (fuzzyEqual(zeta, A, tol) &&
523 fuzzyGreaterThan(eta, static_cast<Real>(2) * xi, tol)) {
524 return false;
525 }
526 if (fuzzyEqual(xi, -B, tol) &&
527 fuzzyNotEqual(zeta, static_cast<Real>(0), tol)) {
528 return false;
529 }
530 if (fuzzyEqual(eta, -A, tol) &&
531 fuzzyNotEqual(zeta, static_cast<Real>(0), tol)) {
532 return false;
533 }
534 if (fuzzyEqual(zeta, -A, tol) &&
535 fuzzyNotEqual(eta, static_cast<Real>(0), tol)) {
536 return false;
537 }
538 if (fuzzyEqual(xi + eta + zeta + A + B, static_cast<Real>(0), tol) &&
539 fuzzyGreaterThan(static_cast<Real>(2) * (A + eta) + zeta,
540 static_cast<Real>(0), tol)) {
541 return false;
542 }
543
544 // all good!
545 return true;
546 }
547
buildSupercell(Molecule & molecule,unsigned int a,unsigned int b,unsigned int c)548 bool CrystalTools::buildSupercell(Molecule& molecule, unsigned int a,
549 unsigned int b, unsigned int c)
550 {
551 if (!molecule.unitCell())
552 return false;
553
554 // Just a check. Hopefully this won't happen
555 if (a == 0 || b == 0 || c == 0) {
556 std::cerr << "Warning: in buildSupercell(), a, b, or c were set to zero."
557 << "This function will not proceed. Returning false.";
558 return false;
559 }
560
561 // Get the old vectors
562 Vector3 oldA = molecule.unitCell()->aVector();
563 Vector3 oldB = molecule.unitCell()->bVector();
564 Vector3 oldC = molecule.unitCell()->cVector();
565
566 // Calculate new vectors
567 Vector3 newA = oldA * a;
568 Vector3 newB = oldB * b;
569 Vector3 newC = oldC * c;
570
571 // Add in the atoms to the new subcells of the supercell
572 Index numAtoms = molecule.atomCount();
573 Array<Vector3> atoms = molecule.atomPositions3d();
574 Array<unsigned char> atomicNums = molecule.atomicNumbers();
575 for (Index ind_a = 0; ind_a < a; ++ind_a) {
576 for (Index ind_b = 0; ind_b < b; ++ind_b) {
577 for (Index ind_c = 0; ind_c < c; ++ind_c) {
578 // Skip over the subcell that already exists
579 if (ind_a == 0 && ind_b == 0 && ind_c == 0)
580 continue;
581 // The positions of the new atoms are displacements of the old atoms
582 Vector3 displacement = ind_a * oldA + ind_b * oldB + ind_c * oldC;
583 for (Index i = 0; i < numAtoms; ++i) {
584 Atom newAtom = molecule.addAtom(atomicNums.at(i));
585 newAtom.setPosition3d(atoms.at(i) + displacement);
586 }
587 }
588 }
589 }
590
591 // Now set the unit cell
592 molecule.unitCell()->setAVector(newA);
593 molecule.unitCell()->setBVector(newB);
594 molecule.unitCell()->setCVector(newC);
595
596 // We're done!
597 return true;
598 }
599
600 namespace {
601 struct TransformAtomsFunctor
602 {
TransformAtomsFunctorAvogadro::Core::__anonf775c0600311::TransformAtomsFunctor603 TransformAtomsFunctor(const Matrix3& t) : transform(t) {}
604 const Matrix3& transform;
605
operator ()Avogadro::Core::__anonf775c0600311::TransformAtomsFunctor606 void operator()(Vector3& pos) { pos = transform * pos; }
607 };
608 }
609
setCellMatrix(Molecule & molecule,const Matrix3 & newCellColMatrix,Options opt)610 bool CrystalTools::setCellMatrix(Molecule& molecule,
611 const Matrix3& newCellColMatrix, Options opt)
612 {
613 if (opt & TransformAtoms && molecule.unitCell()) {
614 const Matrix3 xform(newCellColMatrix *
615 molecule.unitCell()->cellMatrix().inverse());
616 std::for_each(molecule.atomPositions3d().begin(),
617 molecule.atomPositions3d().end(),
618 TransformAtomsFunctor(xform));
619 }
620
621 if (!molecule.unitCell())
622 molecule.setUnitCell(new UnitCell);
623
624 molecule.unitCell()->setCellMatrix(newCellColMatrix);
625
626 return true;
627 }
628
629 namespace {
630 struct FractionalCoordinatesFunctor
631 {
632 const UnitCell& unitCell;
633
FractionalCoordinatesFunctorAvogadro::Core::__anonf775c0600411::FractionalCoordinatesFunctor634 FractionalCoordinatesFunctor(const UnitCell& uc) : unitCell(uc) {}
635
operator ()Avogadro::Core::__anonf775c0600411::FractionalCoordinatesFunctor636 void operator()(Vector3& pos) { unitCell.toFractional(pos, pos); }
637 };
638 }
639
fractionalCoordinates(const UnitCell & unitCell,const Array<Vector3> & cart,Array<Vector3> & frac)640 bool CrystalTools::fractionalCoordinates(const UnitCell& unitCell,
641 const Array<Vector3>& cart,
642 Array<Vector3>& frac)
643 {
644 if (&frac != &cart) // avoid self-copy...
645 frac = cart;
646
647 std::for_each(frac.begin(), frac.end(),
648 FractionalCoordinatesFunctor(unitCell));
649
650 return true;
651 }
652
fractionalCoordinates(const Molecule & molecule,Array<Vector3> & coords)653 bool CrystalTools::fractionalCoordinates(const Molecule& molecule,
654 Array<Vector3>& coords)
655 {
656 if (!molecule.unitCell())
657 return false;
658
659 coords = molecule.atomPositions3d();
660 coords.resize(molecule.atomCount());
661
662 return fractionalCoordinates(*molecule.unitCell(), coords, coords);
663 }
664
665 namespace {
666 struct SetFractionalCoordinatesFunctor
667 {
668 const UnitCell& unitCell;
669
SetFractionalCoordinatesFunctorAvogadro::Core::__anonf775c0600511::SetFractionalCoordinatesFunctor670 SetFractionalCoordinatesFunctor(const Molecule& molecule)
671 : unitCell(*molecule.unitCell())
672 {
673 }
674
operator ()Avogadro::Core::__anonf775c0600511::SetFractionalCoordinatesFunctor675 Vector3 operator()(const Vector3& pos) { return unitCell.toCartesian(pos); }
676 };
677 }
678
setFractionalCoordinates(Molecule & molecule,const Array<Vector3> & coords)679 bool CrystalTools::setFractionalCoordinates(Molecule& molecule,
680 const Array<Vector3>& coords)
681 {
682 if (!molecule.unitCell())
683 return false;
684
685 if (coords.size() != molecule.atomCount())
686 return false;
687
688 Array<Vector3>& output = molecule.atomPositions3d();
689 output.resize(coords.size());
690
691 std::transform(coords.begin(), coords.end(), output.begin(),
692 SetFractionalCoordinatesFunctor(molecule));
693
694 return true;
695 }
696
697 } // namespace Core
698 } // namespace Avogadro
699