root/src/xtalopt/structures/molecularxtal.cpp @ 06f244abd607a7c31b6ed64e2a2c378638e727d2

Revision 06f244abd607a7c31b6ed64e2a2c378638e727d2, 23.5 KB (checked in by David C. Lonie <loniedavid@…>, 13 months ago)

Added a preoptimization queue for MolecularXtals?.

  • Property mode set to 100644
Line 
1/**********************************************************************
2  MolecularXtal - Molecular-crystal themed subclass to Xtal
3
4  Copyright (C) 2011 by David C. Lonie
5
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation version 2 of the License.
9
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  GNU General Public License for more details.
14 ***********************************************************************/
15
16#include <xtalopt/structures/molecularxtal.h>
17
18#include <xtalopt/structures/submolecule.h>
19#include <xtalopt/molecularxtaloptimizer.h>
20
21#include <globalsearch/macros.h>
22#include <globalsearch/obeigenconv.h>
23
24#include <avogadro/atom.h>
25#include <avogadro/bond.h>
26
27extern "C" {
28#include <spglib/spglib.h>
29}
30
31#include <xtalcomp/xtalcomp.h>
32
33using namespace Avogadro;
34
35namespace XtalOpt {
36
37  MolecularXtal::MolecularXtal(QObject *parent) :
38    Xtal(parent),
39    m_preOptStepCount(0),
40    m_preOptStep(0),
41    m_needsPreOpt(false),
42    m_mxtalOpt(NULL)
43  {
44  }
45
46  MolecularXtal::MolecularXtal(double A, double B, double C,
47                               double Alpha, double Beta, double Gamma,
48                               QObject *parent) :
49    Xtal(A, B, C, Alpha, Beta, Gamma, parent)
50  {
51  }
52
53  MolecularXtal::MolecularXtal(const MolecularXtal &other) :
54    Xtal(other.parent()),
55    m_preOptStepCount(0),
56    m_preOptStep(0),
57    m_needsPreOpt(false),
58    m_mxtalOpt(NULL)
59  {
60    *this = other;
61  }
62
63  MolecularXtal::~MolecularXtal()
64  {
65    for (int i = 0; i < this->m_subMolecules.size(); ++i) {
66      delete this->m_subMolecules.at(i);
67      this->m_subMolecules[i] = NULL;
68    }
69    this->m_subMolecules.clear();
70  }
71
72  GlobalSearch::Structure& MolecularXtal::copyStructure(
73      const GlobalSearch::Structure &other)
74  {
75    this->Xtal::copyStructure(other);
76
77    const MolecularXtal *otherMXtal =
78        qobject_cast<const MolecularXtal*>(&other);
79    if (otherMXtal == NULL) {
80      qWarning() << Q_FUNC_INFO << "Other structure is not a MolecularXtal.";
81      return *this;
82    }
83
84    // Remove existing submolecules
85    for (int i = 0; i < this->m_subMolecules.size(); ++i) {
86      delete this->m_subMolecules[i];
87      this->m_subMolecules[i] = NULL;
88    }
89    m_subMolecules.clear();
90    m_subMolecules.reserve(otherMXtal->m_subMolecules.size());
91
92    // Copy the submolecules from other to this
93    for (QList<SubMolecule*>::const_iterator
94         it = otherMXtal->m_subMolecules.constBegin(),
95         it_end = otherMXtal->m_subMolecules.constEnd(); it != it_end; ++it) {
96
97      SubMolecule *sub = new SubMolecule ();
98      sub->m_sourceId = (*it)->m_sourceId;
99      sub->m_mxtal = this;
100      sub->m_atoms.reserve((*it)->m_atoms.size());
101
102      for (QList<Atom*>::const_iterator ait = (*it)->m_atoms.constBegin(),
103           ait_end = (*it)->m_atoms.constEnd(); ait != ait_end; ++ait) {
104        sub->m_atoms.append(this->atomById((*ait)->id()));
105      }
106
107      sub->m_bonds.reserve((*it)->m_bonds.size());
108      for (QList<Bond*>::const_iterator bit = (*it)->m_bonds.constBegin(),
109           bit_end = (*it)->m_bonds.constEnd(); bit != bit_end; ++bit) {
110        sub->m_bonds.append(this->bondById((*bit)->id()));
111      }
112
113      this->m_subMolecules.append(sub);
114    }
115
116    return *this;
117  }
118
119  bool MolecularXtal::operator ==(const MolecularXtal &other) const
120  {
121    // Compare coordinates using the default tolerances
122    if (!this->MolecularXtal::compareCoordinates(other))
123      return false;
124
125    return true;
126  }
127
128  bool MolecularXtal::compareCoordinates(const MolecularXtal &other,
129                                         const double lengthTol,
130                                         const double angleTol) const
131  {
132    // Cell matrices as row vectors
133    const OpenBabel::matrix3x3 thisCellOB (this->cell()->GetCellMatrix());
134    const OpenBabel::matrix3x3 otherCellOB (other.cell()->GetCellMatrix());
135    XcMatrix thisCell (thisCellOB(0,0), thisCellOB(0,1), thisCellOB(0,2),
136                       thisCellOB(1,0), thisCellOB(1,1), thisCellOB(1,2),
137                       thisCellOB(2,0), thisCellOB(2,1), thisCellOB(2,2));
138    XcMatrix otherCell(otherCellOB(0,0), otherCellOB(0,1), otherCellOB(0,2),
139                       otherCellOB(1,0), otherCellOB(1,1), otherCellOB(1,2),
140                       otherCellOB(2,0), otherCellOB(2,1), otherCellOB(2,2));
141
142    // vectors of fractional coordinates and atomic numbers
143    std::vector<XcVector> thisCoords;
144    std::vector<XcVector> otherCoords;
145    std::vector<unsigned int> thisTypes;
146    std::vector<unsigned int> otherTypes;
147    thisCoords.reserve(this->numAtoms());
148    thisTypes.reserve(this->numAtoms());
149    otherCoords.reserve(other.numAtoms());
150    otherTypes.reserve(other.numAtoms());
151    Eigen::Vector3d pos;
152    for (QList<Atom*>::const_iterator it = this->m_atomList.constBegin(),
153           it_end = this->m_atomList.constEnd(); it != it_end; ++it) {
154      // Don't compare hydrogens -- too floppy.
155      if ((*it)->isHydrogen()) {
156        continue;
157      }
158      pos = this->cartToFrac(*(*it)->pos());
159      thisCoords.push_back(XcVector(pos.x(), pos.y(), pos.z()));
160      thisTypes.push_back((*it)->atomicNumber());
161    }
162    for (QList<Atom*>::const_iterator it = other.m_atomList.constBegin(),
163           it_end = other.m_atomList.constEnd(); it != it_end; ++it) {
164      // Don't compare hydrogens -- too floppy.
165      if ((*it)->isHydrogen()) {
166        continue;
167      }
168      pos = other.cartToFrac(*(*it)->pos());
169      otherCoords.push_back(XcVector(pos.x(), pos.y(), pos.z()));
170      otherTypes.push_back((*it)->atomicNumber());
171    }
172
173    return XtalComp::compare(thisCell,  thisTypes,  thisCoords,
174                             otherCell, otherTypes, otherCoords,
175                             NULL, lengthTol, angleTol);
176  }
177
178  int MolecularXtal::numSubMolecules() const
179  {
180    return m_subMolecules.size();
181  }
182
183  QList<SubMolecule*> MolecularXtal::subMolecules() const
184  {
185    return m_subMolecules;
186  }
187
188  SubMolecule * MolecularXtal::subMolecule(int index)
189  {
190    return this->m_subMolecules.at(index);
191  }
192
193  const SubMolecule * MolecularXtal::subMolecule(int index) const
194  {
195    return this->m_subMolecules.at(index);
196  }
197
198  bool MolecularXtal::verifySubMolecules() const
199  {
200    // Cache and pass the cell matrix to each submolecule
201    const Eigen::Matrix3d cellRowMatrix =
202        OB2Eigen(this->cell()->GetCellMatrix());
203
204    // Verify bonds in submolecules
205    for (QList<SubMolecule*>::const_iterator it = m_subMolecules.constBegin(),
206         it_end = m_subMolecules.constEnd(); it != it_end; ++it) {
207      if (!(*it)->verifyBonds(cellRowMatrix)) {
208        return false;
209      }
210    }
211
212    return true;
213  }
214
215  // helper function. Returns -1 if the atom is not "between" the bonded atoms
216  inline double squaredDistanceToBond(const Eigen::Vector3d &bondVec,
217                                      const Eigen::Vector3d &startAtomPos,
218                                      const Eigen::Vector3d &testPos,
219                                      const double bondLengthSquared)
220  {
221    const Eigen::Vector3d beginToPos (testPos - startAtomPos);
222    const Eigen::Vector3d proj
223        ((bondVec.dot(beginToPos) / bondVec.squaredNorm()) * bondVec);
224
225    // If the dot product of the projection and the bond vector is negative,
226    // then the test point is on the other side of startAtom than the bond.
227    if (proj.dot(bondVec) < 0.0) {
228      return -1.0;
229    }
230
231    // If the projection vector is larger than the bond, then the atom is on
232    // the other side of the endAtom.
233    if (proj.squaredNorm() > bondLengthSquared) {
234      return -1.0;
235    }
236
237    return (proj - beginToPos).squaredNorm();
238  }
239
240  bool MolecularXtal::checkAtomToBondDistances(double minDistance)
241  {
242    const double r2 = minDistance * minDistance;
243
244    // Cache list of translations
245    Eigen::Vector3d translations[27];
246    std::vector<OpenBabel::vector3> obvecs
247        (this->OBUnitCell()->GetCellVectors());
248    const Eigen::Vector3d v1 (obvecs[0].AsArray());
249    const Eigen::Vector3d v2 (obvecs[1].AsArray());
250    const Eigen::Vector3d v3 (obvecs[2].AsArray());
251
252    int transIndex = -1;
253    for (double x = -1.0; x < 1.5; ++x) {
254      for (double y = -1.0; y < 1.5; ++y) {
255        for (double z = -1.0; z < 1.5; ++z) {
256          translations[++transIndex] = x * v1 + y * v2 + z * v3;
257        }
258      }
259    }
260
261    // For each submolecule:
262    for (QList<SubMolecule*>::const_iterator it = m_subMolecules.constBegin(),
263         it_end = m_subMolecules.constEnd(); it != it_end; ++it) {
264
265      // Get the coherent coordinates
266      const QVector<Eigen::Vector3d> cohVecs ((*it)->getCoherentCoordinates());
267      Q_ASSERT(cohVecs.size() == (*it)->m_atoms.size());
268
269      // For each bond in current submolecule
270      for (QList<Bond*>::const_iterator bit = (*it)->m_bonds.constBegin(),
271           bit_end = (*it)->m_bonds.constEnd(); bit != bit_end; ++bit) {
272
273        // Cache the bondVector and the start atom position
274        Atom *begin = (*bit)->beginAtom();
275        Atom *end = (*bit)->endAtom();
276        const Eigen::Vector3d &beginPos = cohVecs[(*it)->m_atoms.indexOf(begin)];
277        const Eigen::Vector3d &endPos   = cohVecs[(*it)->m_atoms.indexOf(end)];
278        const Eigen::Vector3d bondVec (endPos - beginPos);
279        const double bondLengthSquared = bondVec.squaredNorm();
280
281        // For each atom in mxtal
282        for (QList<Atom*>::const_iterator ait = m_atomList.constBegin(),
283             ait_end = m_atomList.constEnd(); ait != ait_end; ++ait) {
284
285          // Skip the atoms in the current bond
286          if ((*ait) == begin || (*ait) == end)
287            continue;
288
289          // Cache a reference to this atom's original position
290          const Eigen::Vector3d &testPos (*(*ait)->pos());
291
292          // Test each translated position.
293          for (int i = 0; i < 27; ++i) {
294
295            // Calculate the distance from the atom to the bond
296            const double dist = squaredDistanceToBond(
297                  bondVec, beginPos, testPos + translations[i],
298                  bondLengthSquared);
299
300            // If dist is less than 0, then the test atom is not between the
301            // bonded atoms ( and thus ok )
302            if (dist < 0.0)
303              continue;
304
305            // If the distance is too small, return false
306            if (dist < r2) {
307              return false;
308
309            } // if (distance < r2)
310          } // For each translation
311        } // For each atom
312      } // foreach bond
313    } // foreach submolecule
314
315    // If all atoms pass, return true
316    return true;
317  }
318
319  void MolecularXtal::findSpaceGroup(double prec)
320  {
321    // Check that the precision is reasonable
322    if (prec < 1e-5) {
323      qWarning() << "MolecularXtal::findSpaceGroup called with a precision of"
324                 << prec << ". This is likely an error. Resetting prec to"
325                 << 0.05 << ".";
326      prec = 0.05;
327    }
328
329    // reset space group to 0 so we can exit if needed
330    m_spgNumber = 0;
331    m_spgSymbol = "Unknown";
332    int num = this->numAtoms();
333    // Remove hydrogens from count
334    foreach (const Atom *a, this->m_atomList) {
335      if (a->isHydrogen())
336        --num;
337    }
338
339    // if no unit cell or atoms, exit
340    if (!cell() || num == 0) {
341      qWarning() << "MolecularXtal::findSpaceGroup(" << prec << ") called on "
342                    "atom with no cell or atoms!";
343      return;
344    }
345
346    // get lattice matrix
347    std::vector<OpenBabel::vector3> vecs = cell()->GetCellVectors();
348    OpenBabel::vector3 obU1 = vecs[0];
349    OpenBabel::vector3 obU2 = vecs[1];
350    OpenBabel::vector3 obU3 = vecs[2];
351    double lattice[3][3] = {
352      {obU1.x(), obU2.x(), obU3.x()},
353      {obU1.y(), obU2.y(), obU3.y()},
354      {obU1.z(), obU2.z(), obU3.z()}
355    };
356
357    // Get atom info
358    double (*positions)[3] = new double[num][3];
359    int *types = new int[num];
360    Eigen::Vector3d fracCoord;
361    int ind = 0;
362    foreach (const Atom *a, this->m_atomList) {
363      if (a->isHydrogen())
364        continue;
365      types[ind] = a->atomicNumber();
366      fracCoord = this->cartToFrac(*a->pos());
367      positions[ind][0] = fracCoord.x();
368      positions[ind][1] = fracCoord.y();
369      positions[ind][2] = fracCoord.z();
370      ++ind;
371    }
372
373    // find spacegroup
374    char symbol[21];
375    m_spgNumber = spg_get_international(symbol,
376                                        lattice,
377                                        positions,
378                                        types,
379                                        num, prec);
380
381    delete [] positions;
382    delete [] types;
383
384    // Update the OBUnitCell object.
385    cell()->SetSpaceGroup(m_spgNumber);
386
387    // Fail if m_spgNumber is still 0
388    if (m_spgNumber == 0) {
389      return;
390    }
391
392    // Set and clean up the symbol string
393    m_spgSymbol = QString(symbol);
394    m_spgSymbol.remove(" ");
395    return;
396  }
397
398  bool MolecularXtal::isPreoptimizing() const
399  {
400    return (m_mxtalOpt == NULL) ? false : m_mxtalOpt->isRunning();
401  }
402
403  void MolecularXtal::setVolume(double Volume)
404  {
405    // Get scaling factor
406    double factor = pow(Volume/cell()->GetCellVolume(), 1.0/3.0); // Cube root
407
408    // Store submolecule centers in fractional units
409    QList<SubMolecule*> submols = this->subMolecules();
410    QList<Eigen::Vector3d> fracCenterList;
411    for (int i = 0; i < submols.size(); i++)
412      fracCenterList.append(cartToFrac(submols.at(i)->center()));
413
414    // Scale cell
415    setCellInfo(factor * cell()->GetA(),
416                factor * cell()->GetB(),
417                factor * cell()->GetC(),
418                cell()->GetAlpha(),
419                cell()->GetBeta(),
420                cell()->GetGamma());
421
422    // Recalculate coordinates:
423    for (int i = 0; i < submols.size(); i++)
424      submols.at(i)->setCenter(fracToCart(fracCenterList.at(i)));
425  }
426
427  void MolecularXtal::rescaleCell(double a, double b, double c,
428                                  double alpha, double beta, double gamma)
429  {
430    if (!a && !b && !c && !alpha && !beta && !gamma) {
431      return;
432    }
433
434    this->rotateCellAndCoordsToStandardOrientation();
435
436    // Store position of atoms in fractional units
437    QList<SubMolecule*> submols = this->subMolecules();
438    QList<Eigen::Vector3d> fracCoordsList;
439    for (int i = 0; i < submols.size(); i++)
440      fracCoordsList.append(cartToFrac(submols.at(i)->center()));
441
442    double nA = getA();
443    double nB = getB();
444    double nC = getC();
445    double nAlpha = getAlpha();
446    double nBeta = getBeta();
447    double nGamma = getGamma();
448
449    // Set angles and reset volume
450    if (alpha || beta || gamma) {
451      double volume = getVolume();
452      if (alpha) nAlpha = alpha;
453      if (beta) nBeta = beta;
454      if (gamma) nGamma = gamma;
455      setCellInfo(nA,
456                  nB,
457                  nC,
458                  nAlpha,
459                  nBeta,
460                  nGamma);
461      setVolume(volume);
462    }
463
464    if (a || b || c) {
465      // Initialize variables
466      double scale_primary, scale_secondary, scale_tertiary;
467
468      // Set lengths while preserving volume
469      // Case one length is static
470      if (a && !b && !c) {        // A
471        scale_primary = a / nA;
472        scale_secondary = pow(scale_primary, 0.5);
473        nA = a;
474        nB *= scale_secondary;
475        nC *= scale_secondary;
476      }
477      else if (!a && b && !c) {   // B
478        scale_primary = b / nB;
479        scale_secondary = pow(scale_primary, 0.5);
480        nB = b;
481        nA *= scale_secondary;
482        nC *= scale_secondary;
483      }
484      else if (!a && !b && c) {   // C
485        scale_primary = c / nC;
486        scale_secondary = pow(scale_primary, 0.5);
487        nC = c;
488        nA *= scale_secondary;
489        nB *= scale_secondary;
490      }
491      // Case two lengths are static
492      else if (a && b && !c) {    // AB
493        scale_primary   = a / nA;
494        scale_secondary = b / nB;
495        scale_tertiary  = scale_primary * scale_secondary;
496        nA = a;
497        nB = b;
498        nC *= scale_tertiary;
499      }
500      else if (a && !b && c) {    // AC
501        scale_primary   = a / nA;
502        scale_secondary = c / nC;
503        scale_tertiary  = scale_primary * scale_secondary;
504        nA = a;
505        nC = c;
506        nB *= scale_tertiary;
507      }
508      else if (!a && b && c) {    // BC
509        scale_primary   = c / nC;
510        scale_secondary = b / nB;
511        scale_tertiary  = scale_primary * scale_secondary;
512        nC = c;
513        nB = b;
514        nA *= scale_tertiary;
515      }
516      // Case three lengths are static
517      else if (a && b && c) {     // ABC
518        nA = a;
519        nB = b;
520        nC = c;
521      }
522
523      // Update unit cell
524      setCellInfo(nA,
525                  nB,
526                  nC,
527                  nAlpha,
528                  nBeta,
529                  nGamma);
530    }
531
532    // Recalculate coordinates:
533    for (int i = 0; i < submols.size(); i++)
534      submols.at(i)->setCenter(fracToCart(fracCoordsList.at(i)));
535  }
536
537  void MolecularXtal::addSubMolecule(SubMolecule *sub)
538  {
539    // Is the handle/submolecule valid?
540    if (!sub || sub->numAtoms() == 0) {
541      qWarning() << Q_FUNC_INFO << "Attempt to add NULL/empty submolecule?";
542      return;
543    }
544
545    // Do we already own this subMolecule?
546    if (m_subMolecules.contains(sub)) {
547      qWarning() << "Attempting to add SubMolecule @" << sub
548                 << " twice. Aborting.";
549      return;
550    }
551
552    // Create atoms and bonds for the subMolecule to use
553    QList<Atom*> atoms;
554    QList<Bond*> bonds;
555#if QT_VERSION >= 0x040700
556    atoms.reserve(sub->numAtoms());
557    atoms.reserve(sub->numBonds());
558#endif
559
560    for (int i = 0; i < sub->numAtoms(); ++i) {
561      atoms.append(this->addAtom());
562    }
563    for (int i = 0; i < sub->numBonds(); ++i) {
564      bonds.append(this->addBond());
565    }
566
567    // Take ownership of the submolecule:
568    sub->takeOwnership(this, atoms, bonds);
569
570    m_subMolecules.append(sub);
571  }
572
573  void MolecularXtal::removeSubMolecule(SubMolecule *sub)
574  {
575    // Is the handle/submolecule valid?
576    if (!sub) {
577      qWarning() << Q_FUNC_INFO << "Attempt to remove NULL submolecule?";
578      return;
579    }
580
581    if (!m_subMolecules.contains(sub)) {
582      qWarning() << "Attempting to remove SubMolecule @" << sub
583                 << ", which does not belong to this MolecularXtal ("
584                 << this->getIDString() << " @" << this << ". Aborting.";
585      return;
586    }
587
588    // Cache list of atoms and bonds owned by submolecule
589    QList<Atom*> atoms = sub->atoms();
590    QList<Bond*> bonds = sub->bonds();
591
592    m_subMolecules.removeAll(sub);
593    sub->releaseOwnership();
594
595    // Remove atoms and bonds previously held by submolecule.
596    // Take out bonds first.
597    for (QList<Bond*>::const_iterator it = bonds.constBegin(),
598         it_end = bonds.constEnd(); it != it_end; ++it) {
599      this->removeBond(*it);
600    }
601    for (QList<Atom*>::const_iterator it = atoms.constBegin(),
602         it_end = atoms.constEnd(); it != it_end; ++it) {
603      this->removeAtom(*it);
604    }
605
606  }
607
608  void MolecularXtal::replaceSubMolecule(int i, SubMolecule *newSub)
609  {
610    SubMolecule *oldSub = m_subMolecules.at(i);
611    this->removeSubMolecule(oldSub);
612    this->addSubMolecule(newSub);
613
614    // Move the new submol to position i
615    m_subMolecules.insert(i, m_subMolecules.takeLast());
616
617    // Clean up the old submol
618    oldSub->deleteLater();
619  }
620
621  void MolecularXtal::readMolecularXtalSettings(const QString &filename)
622  {
623    SETTINGS(filename);
624
625    if (!settings->value("molecularxtal/mxtalIsValid", false).toBool()) {
626      qWarning() << "Cannot read invalid mxtal settings for mxtal"
627                 << this->getIDString();
628      return;
629    }
630
631    // Clear existing bonds. These will be recreated below
632    while (m_bondList.size() != 0) {
633      this->removeBond(m_bondList.first());
634    }
635
636    int lengSubMoleculeArr =
637        settings->beginReadArray("molecularxtal/submolecules");
638    for (int i = 0; i < lengSubMoleculeArr; ++i) {
639      settings->setArrayIndex(i);
640
641      // Create empty submolecule data
642      SubMolecule *sub = new SubMolecule(this);
643
644      sub->setSourceId(static_cast<unsigned long>(
645                         settings->value("sourceId", -1).toULongLong()));
646
647      // Read atom indices
648      int lenAtomArr = settings->beginReadArray("atoms");
649      for (int j = 0; j < lenAtomArr; ++j) {
650        settings->setArrayIndex(j);
651        int ind = settings->value("ind", -1).toInt();
652        if (ind < 0 || ind > this->numAtoms() - 1) {
653          qFatal(QString("Invalid atom index %1 for Structure %2 in %3.")
654                 .arg(ind).arg(this->getIDString()).arg(filename)
655                 .toStdString().c_str());
656        }
657        sub->m_atoms.append(this->atom(ind));
658      }
659      settings->endArray();
660
661      // Read bond info
662      int lenBondArr = settings->beginReadArray("bonds");
663      for (int j = 0; j < lenBondArr; ++j) {
664        settings->setArrayIndex(j);
665        int begInd = settings->value("begInd", -1).toInt();
666        int endInd = settings->value("endInd", -1).toInt();
667        short order = static_cast<short>(settings->value("order", -1).toInt());
668        if (begInd < 0 || begInd > this->numAtoms() - 1) {
669          qFatal(QString("Invalid atom index %1 for Structure %2 in %3.")
670                 .arg(begInd).arg(this->getIDString()).arg(filename)
671                 .toStdString().c_str());
672        }
673        if (endInd < 0 || endInd > this->numAtoms() - 1) {
674          qFatal(QString("Invalid atom index %1 for Structure %2 in %3.")
675                 .arg(endInd).arg(this->getIDString()).arg(filename)
676                 .toStdString().c_str());
677        }
678        Bond *bond = this->addBond();
679        bond->setBegin(m_atomList[begInd]);
680        bond->setEnd(m_atomList[endInd]);
681        bond->setOrder(order);
682        sub->m_bonds.append(bond);
683      }
684      settings->endArray();
685
686      // Add submolecule to this
687      sub->m_mxtal = this;
688      m_subMolecules.append(sub);
689
690    }
691    settings->endArray();
692  }
693
694  void MolecularXtal::writeMolecularXtalSettings(const QString &filename) const
695  {
696    SETTINGS(filename);
697
698    if (this->m_optimizerLookup.isEmpty()) {
699      settings->setValue("molecularxtal/mxtalIsValid", false);
700      return; // Cannot serialize yet
701    }
702    settings->setValue("molecularxtal/mxtalIsValid", true);
703
704    settings->beginWriteArray("molecularxtal/submolecules");
705    for (int i = 0; i < this->numSubMolecules(); ++i) {
706      settings->setArrayIndex(i);
707
708      // cache submolecule data
709      const SubMolecule *sub = this->subMolecule(i);
710      settings->setValue("sourceId", static_cast<unsigned long long>(
711                           sub->sourceId()));
712      const QList<Atom*> atoms = sub->atoms();
713      const QList<Bond*> bonds = sub->bonds();
714
715      // Write atom optimizer indicies using m_optimizerLookup table
716      settings->beginWriteArray("atoms");
717      for (int j = 0; j < atoms.size(); ++j) {
718        settings->setArrayIndex(j);
719        settings->setValue("ind", static_cast<int>(atoms.at(j)->index()));
720      }
721      settings->endArray();
722
723      // Write bond ids using m_optimizerLookup table
724      settings->beginWriteArray("bonds");
725      for (int j = 0; j < bonds.size(); ++j) {
726        settings->setArrayIndex(j);
727        Bond *curbond = bonds[j];
728        int begIndex = static_cast<int>(curbond->beginAtom()->index());
729        int endIndex = static_cast<int>(curbond->endAtom()->index());
730        settings->setValue("begInd", begIndex);
731        settings->setValue("endInd", endIndex);
732        settings->setValue("order", static_cast<int>(curbond->order()));
733      }
734      settings->endArray();
735    }
736    settings->endArray();
737  }
738
739  void MolecularXtal::abortPreoptimization() const
740  {
741    if (m_mxtalOpt != NULL) {
742      m_mxtalOpt->abort();
743      m_mxtalOpt->waitForFinished();
744    }
745  }
746
747} // end namespace XtalOpt
Note: See TracBrowser for help on using the browser.