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