root/src/xtalopt/xtalopt.cpp @ a426e387c8cde074662fe6aa3a60c39141e5390b

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

Add XtalOpt::generateNewMXtal().

  • Property mode set to 100644
Line 
1/**********************************************************************
2  XtalOpt - "Engine" for the optimization process
3
4  Copyright (C) 2009-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/xtalopt.h>
17
18#include <xtalopt/structures/molecularxtal.h>
19#include <xtalopt/structures/submolecule.h>
20#include <xtalopt/structures/submoleculesource.h>
21#include <xtalopt/structures/xtal.h>
22#include <xtalopt/optimizers/vasp.h>
23#include <xtalopt/optimizers/gulp.h>
24#include <xtalopt/optimizers/pwscf.h>
25#include <xtalopt/optimizers/castep.h>
26#include <xtalopt/ui/dialog.h>
27#include <xtalopt/genetic.h>
28
29#include <xtalopt/mxtaloptgenetic.h>
30
31#include <globalsearch/optbase.h>
32#include <globalsearch/optimizer.h>
33#include <globalsearch/queueinterface.h>
34#include <globalsearch/queueinterfaces/local.h>
35#include <globalsearch/queuemanager.h>
36#include <globalsearch/slottedwaitcondition.h>
37#include <globalsearch/macros.h>
38#include <globalsearch/bt.h>
39
40#ifdef ENABLE_SSH
41#include <globalsearch/sshmanager.h>
42#include <globalsearch/queueinterfaces/remote.h>
43#endif // ENABLE_SSH
44
45#include <QtCore/QDir>
46#include <QtCore/QList>
47#include <QtCore/QFile>
48#include <QtCore/QDebug>
49#include <QtCore/QTimer>
50#include <QtCore/QFileInfo>
51#include <QtCore/QReadWriteLock>
52#include <QtCore/QtConcurrentRun>
53#include <QtCore/QtConcurrentMap>
54
55#define ANGSTROM_TO_BOHR 1.889725989
56
57using namespace GlobalSearch;
58using namespace OpenBabel;
59using namespace Avogadro;
60
61namespace XtalOpt {
62
63  XtalOpt::XtalOpt(XtalOptDialog *parent) :
64    OptBase(parent),
65    m_initWC(new SlottedWaitCondition (this)),
66    m_isMolecular(false),
67    m_currentSubMolSourceProgress(-1)
68  {
69    xtalInitMutex = new QMutex;
70    m_idString = "XtalOpt";
71    m_schemaVersion = 2;
72
73    // Connections
74    connect(m_tracker, SIGNAL(newStructureAdded(GlobalSearch::Structure*)),
75            this, SLOT(checkForDuplicates()));
76    connect(this, SIGNAL(sessionStarted()),
77            this, SLOT(resetDuplicates()));
78  }
79
80  XtalOpt::~XtalOpt()
81  {
82    // Stop queuemanager thread
83    if (m_queueThread->isRunning()) {
84      m_queueThread->disconnect();
85      m_queueThread->quit();
86      m_queueThread->wait();
87    }
88
89    // Delete queuemanager
90    delete m_queue;
91    m_queue = 0;
92
93#ifdef ENABLE_SSH
94    // Stop SSHManager
95    delete m_ssh;
96    m_ssh = 0;
97#endif // ENABLE_SSH
98
99    // Wait for save to finish
100    unsigned int timeout = 30;
101    while (timeout > 0 && savePending) {
102      qDebug() << "Spinning on save before destroying XtalOpt ("
103               << timeout << "seconds until timeout).";
104      timeout--;
105      GS_SLEEP(1);
106    };
107
108    savePending = true;
109
110    // Clean up various members
111    m_initWC->deleteLater();
112    m_initWC = 0;
113  }
114
115  void XtalOpt::startSearch()
116  {
117
118    // Settings checks
119    // Check lattice parameters, volume, etc
120    if (!XtalOpt::checkLimits()) {
121      error("Cannot create structures. Check log for details.");
122      return;
123    }
124
125    // Do we have a composition?
126    if (comp.isEmpty()) {
127      error("Cannot create structures. Composition is not set.");
128      return;
129    }
130
131    // Are the selected queueinterface and optimizer happy?
132    QString err;
133    if (!m_optimizer->isReadyToSearch(&err)) {
134      error(tr("Optimizer is not fully initialized:") + "\n\n" + err);
135      return;
136    }
137
138    if (!m_queueInterface->isReadyToSearch(&err)) {
139      error(tr("QueueInterface is not fully initialized:") + "\n\n" + err);
140      return;
141    }
142
143    // Warn user if runningJobLimit is 0
144    if (limitRunningJobs && runningJobLimit == 0) {
145      error(tr("Warning: the number of running jobs is currently set to 0."
146               "\n\nYou will need to increase this value before the search "
147               "can begin (The option is on the 'Search Settings' tab)."));
148    };
149
150    // Warn user if contStructs is 0
151    if (contStructs == 0) {
152      error(tr("Warning: the number of continuous structures is "
153               "currently set to 0."
154               "\n\nYou will need to increase this value before the search "
155               "can move past the first generation (The option is on the "
156               "'Search Settings' tab)."));
157    };
158
159    // VASP checks:
160    if (m_optimizer->getIDString() == "VASP") {
161      // Is the POTCAR generated? If not, warn user in log and launch
162      // generator. Every POTCAR will be identical in this case!
163      QList<uint> oldcomp, atomicNums = comp.keys();
164      QList<QVariant> oldcomp_ = m_optimizer->getData("Composition").toList();
165      for (int i = 0; i < oldcomp_.size(); i++)
166        oldcomp.append(oldcomp_.at(i).toUInt());
167      qSort(atomicNums);
168      if (m_optimizer->getData("POTCAR info").toList().isEmpty() || // No info
169          oldcomp != atomicNums // Composition has changed!
170          ) {
171        error("Using VASP and POTCAR is empty. Please select the "
172              "pseudopotentials before continuing.");
173        return;
174      }
175
176      // Build up the latest and greatest POTCAR compilation
177      qobject_cast<VASPOptimizer*>(m_optimizer)->buildPOTCARs();
178    }
179
180#ifdef ENABLE_SSH
181    // Create the SSHManager if running remotely
182    if (qobject_cast<RemoteQueueInterface*>(m_queueInterface) != 0) {
183      if (!this->createSSHConnections()) {
184        error(tr("Could not create ssh connections."));
185        return;
186      }
187    }
188#endif // ENABLE_SSH
189
190    // Generate conformers for submolecules, if needed
191    if (this->isMolecularXtalSearch()) {
192      // Reassign source ids now that list is final
193      for (int i = 0; i < this->mcomp.size(); ++i) {
194        mcomp[i].source->setSourceId(i);
195      }
196
197      // Generate conformers for submolecules, if needed
198      bool notify = m_dialog->startProgressUpdate("Generating conformers...",
199                                                  0, 100);
200      if (!this->initializeSubMoleculeSources(notify)) {
201        error("Error generating submolecule conformers. Aborting.");
202        if (notify)
203          m_dialog->stopProgressUpdate();
204        return;
205      }
206
207      if (notify) {
208        m_dialog->updateProgressValue(100);
209        m_dialog->stopProgressUpdate();
210      }
211    }
212
213    // Here we go!
214    debug("Starting optimization.");
215    emit startingSession();
216
217    // prepare pointers
218    m_tracker->lockForWrite();
219    m_tracker->deleteAllStructures();
220    m_tracker->unlock();
221
222    ///////////////////////////////////////////////
223    // Generate random structures and load seeds //
224    ///////////////////////////////////////////////
225
226    // Set up progress bar
227    m_dialog->startProgressUpdate(tr("Generating structures..."), 0, 0);
228
229    // Initalize loop variables
230    int failed = 0;
231    uint progCount = 0;
232    QString filename;
233    Xtal *xtal = 0;
234    // Use new xtal count in case "addXtal" falls behind so that we
235    // don't duplicate structures when switching from seeds -> random.
236    uint newXtalCount=0;
237
238    // Load seeds...
239    for (int i = 0; i < seedList.size(); i++) {
240      filename = seedList.at(i);
241      if (this->addSeed(filename)) {
242        m_dialog->updateProgressLabel(
243              tr("%1 structures generated (%2 kept, %3 rejected)...")
244              .arg(i + failed).arg(i).arg(failed));
245        newXtalCount++;
246      }
247    }
248
249    // Generation loop...
250    for (uint i = newXtalCount; i < numInitial; i++) {
251      // Update progress bar
252      m_dialog->updateProgressMaximum(
253            (i == 0) ? 0 : int(progCount/static_cast<double>(i))*numInitial);
254      m_dialog->updateProgressValue(progCount);
255      progCount++;
256      m_dialog->updateProgressLabel(
257            tr("%1 structures generated (%2 kept, %3 rejected)...")
258            .arg(i + failed).arg(i).arg(failed));
259
260      // Generate/Check xtal
261      xtal = generateRandomXtal(1, i+1);
262      if (!checkXtal(xtal)) {
263        delete xtal;
264        i--;
265        failed++;
266      }
267      else {
268        xtal->findSpaceGroup(tol_spg);
269        initializeAndAddXtal(xtal, 1, xtal->getParents());
270        newXtalCount++;
271      }
272    }
273
274    // Wait for all structures to appear in tracker
275    m_dialog->updateProgressLabel(
276          tr("Waiting for structures to initialize..."));
277    m_dialog->updateProgressMinimum(0);
278    m_dialog->updateProgressMinimum(newXtalCount);
279
280    connect(m_tracker, SIGNAL(newStructureAdded(GlobalSearch::Structure*)),
281            m_initWC, SLOT(wakeAllSlot()));
282
283    m_initWC->prewaitLock();
284    do {
285      m_dialog->updateProgressValue(m_tracker->size());
286      m_dialog->updateProgressLabel(
287            tr("Waiting for structures to initialize (%1 of %2)...")
288            .arg(m_tracker->size()).arg(newXtalCount));
289      // Don't block here forever -- there is a race condition where
290      // the final newStructureAdded signal may be emitted while the
291      // WC is not waiting. Since this is just trivial GUI updating
292      // and we check the condition in the do-while loop, this is
293      // acceptable. The following call will timeout in 250 ms.
294      m_initWC->wait(250);
295    }
296    while (m_tracker->size() < newXtalCount);
297    m_initWC->postwaitUnlock();
298
299    // We're done with m_initWC.
300    m_initWC->disconnect();
301
302    m_dialog->stopProgressUpdate();
303
304    m_dialog->saveSession();
305    emit sessionStarted();
306  }
307
308  bool XtalOpt::initializeSubMoleculeSources(bool notify)
309  {
310    m_currentSubMolSourceProgress = 0;
311    for (QList<MolecularCompStruct>::const_iterator
312         it = this->mcomp.constBegin(), it_end = this->mcomp.constEnd();
313         it != it_end; ++it) {
314      if (notify)
315        this->connect(it->source, SIGNAL(conformerGenerated(int,int)),
316                      SLOT(initializeSMSProgressUpdate(int,int)),
317                      Qt::BlockingQueuedConnection);
318      it->source->setMaxConformers(this->maxConf);
319      it->source->findAndSetConformers();
320      if (notify)
321        disconnect(it->source, SIGNAL(conformerGenerated(int,int)),
322                   this, SLOT(initializeSMSProgressUpdate(int,int)));
323
324      ++m_currentSubMolSourceProgress;
325    }
326    m_currentSubMolSourceProgress = -1;
327
328    return true;
329  }
330
331  void XtalOpt::initializeSMSProgressUpdate(int finished, int total)
332  {
333    // Total number of conformers assuming all sources generate "total"
334    int allConfCount = total * this->mcomp.size();
335    // Account for all sources already processed
336    int alreadyDone = total * (m_currentSubMolSourceProgress);
337    // Calculate percent completed
338    int percent = 100 * (alreadyDone + finished) / allConfCount;
339    // Update progress bar:
340    m_dialog->updateProgressValue(percent);
341  }
342
343  bool XtalOpt::addSeed(const QString &filename)
344  {
345    QString err;
346    Xtal *xtal = new Xtal;
347    xtal->setFileName(filename);
348    xtal->setStatus(Xtal::WaitingForOptimization);
349    // Create atoms
350    for (QHash<unsigned int, XtalCompositionStruct>::const_iterator
351         it = comp.constBegin(), it_end = comp.constEnd();
352         it != it_end; ++it) {
353      for (int i = 0; i < it.value().quantity; ++i) {
354        xtal->addAtom();
355      }
356    }
357    xtal->moveToThread(m_queue->thread());
358    if ( !m_optimizer->read(xtal, filename) || !this->checkXtal(xtal, &err)) {
359      error(tr("Error loading seed %1\n\n%2").arg(filename).arg(err));
360      xtal->deleteLater();
361      return false;
362    }
363    QString parents =tr("Seeded: %1", "1 is a filename").arg(filename);
364    this->m_queue->addManualStructureRequest(1);
365    initializeAndAddXtal(xtal, 1, parents);
366    debug(tr("XtalOpt::addSeed: Loaded seed: %1",
367             "1 is a filename").arg(filename));
368    return true;
369  }
370
371  Structure* XtalOpt::replaceWithRandom(Structure *s, const QString & reason)
372  {
373    if (this->isMolecularXtalSearch()) {
374      MolecularXtal *mxtal =  qobject_cast<MolecularXtal*>(s);
375      return static_cast<Structure*>(
376            this->replaceWithRandomMXtal(mxtal, reason));
377    }
378    else {
379      Xtal *xtal =  qobject_cast<Xtal*>(s);
380      return static_cast<Structure*>(
381            this->replaceWithRandomXtal(xtal, reason));
382    }
383    // Shouldn't happen, but some compilers aren't that bright...
384    return NULL;
385  }
386
387  Xtal* XtalOpt::replaceWithRandomXtal(Xtal *oldXtal,
388                                       const QString & reason)
389  {
390    QWriteLocker locker1 (oldXtal->lock());
391
392    // Generate/Check new xtal
393    Xtal *xtal = 0;
394    while (!checkXtal(xtal)) {
395      if (xtal) {
396        delete xtal;
397        xtal = 0;
398      }
399
400      xtal = generateRandomXtal(0, 0);
401    }
402
403    // Copy info over
404    QWriteLocker locker2 (xtal->lock());
405    oldXtal->clear();
406    oldXtal->setOBUnitCell(new OpenBabel::OBUnitCell);
407    oldXtal->setCellInfo(xtal->OBUnitCell()->GetCellMatrix());
408    oldXtal->resetEnergy();
409    oldXtal->resetEnthalpy();
410    oldXtal->setPV(0);
411    oldXtal->setCurrentOptStep(1);
412    QString parents = "Randomly generated";
413    if (!reason.isEmpty())
414      parents += " (" + reason + ")";
415    oldXtal->setParents(parents);
416
417    Atom *atom1, *atom2;
418    for (uint i = 0; i < xtal->numAtoms(); i++) {
419      atom1 = oldXtal->addAtom();
420      atom2 = xtal->atom(i);
421      atom1->setPos(atom2->pos());
422      atom1->setAtomicNumber(atom2->atomicNumber());
423    }
424    oldXtal->findSpaceGroup(tol_spg);
425    oldXtal->resetFailCount();
426
427    // Delete random xtal
428    xtal->deleteLater();
429    return oldXtal;
430  }
431
432  MolecularXtal* XtalOpt::replaceWithRandomMXtal(MolecularXtal *oldMXtal,
433                                                 const QString & reason)
434  {
435    QWriteLocker locker1 (oldMXtal->lock());
436
437    // Generate/Check new mxtal
438    MolecularXtal *mxtal = NULL;
439    while (!checkXtal(mxtal)) {
440      if (mxtal) {
441        delete mxtal;
442        mxtal = NULL;
443      }
444      mxtal = generateRandomMXtal(0, 0);
445    }
446
447    // Copy info over
448    QWriteLocker locker2 (mxtal->lock());
449    //! @todo Verify that this assignment doesn't do anything unsual.
450    oldMXtal->copyStructure(*mxtal);
451    oldMXtal->resetEnergy();
452    oldMXtal->resetEnthalpy();
453    oldMXtal->setPV(0);
454    oldMXtal->setCurrentOptStep(1);
455    QString parents = "Randomly generated";
456    if (!reason.isEmpty())
457      parents += " (" + reason + ")";
458    oldMXtal->setParents(parents);
459    oldMXtal->findSpaceGroup(tol_spg);
460    oldMXtal->resetFailCount();
461
462    // Flag for preoptimization
463    if (this->usePreopt) {
464      oldMXtal->setNeedsPreoptimization(true);
465    }
466
467    // Delete random xtal
468    mxtal->deleteLater();
469    return oldMXtal;
470  }
471
472  Structure* XtalOpt::replaceWithOffspring(Structure *s,
473                                           const QString & reason)
474  {
475    if (this->isMolecularXtalSearch()) {
476      MolecularXtal *mxtal =  qobject_cast<MolecularXtal*>(s);
477      return static_cast<Structure*>(
478            this->replaceWithOffspringMXtal(mxtal, reason));
479    }
480    else {
481      Xtal *xtal =  qobject_cast<Xtal*>(s);
482      return static_cast<Structure*>(
483            this->replaceWithOffspringXtal(xtal, reason));
484    }
485    // Shouldn't happen, but some compilers aren't that bright...
486    return NULL;
487  }
488
489  Xtal* XtalOpt::replaceWithOffspringXtal(Xtal *oldXtal, const QString &reason)
490  {
491    // Generate/Check new xtal
492    Xtal *xtal = 0;
493    while (!checkXtal(xtal)) {
494      if (xtal) {
495        xtal->deleteLater();
496        xtal = NULL;
497      }
498      xtal = generateNewXtal();
499    }
500
501    // Copy info over
502    QWriteLocker locker1 (oldXtal->lock());
503    QWriteLocker locker2 (xtal->lock());
504    oldXtal->setOBUnitCell(new OpenBabel::OBUnitCell);
505    oldXtal->setCellInfo(xtal->OBUnitCell()->GetCellMatrix());
506    oldXtal->resetEnergy();
507    oldXtal->resetEnthalpy();
508    oldXtal->resetFailCount();
509    oldXtal->setPV(0);
510    oldXtal->setCurrentOptStep(1);
511    if (!reason.isEmpty()) {
512      QString parents = xtal->getParents();
513      parents += " (" + reason + ")";
514      oldXtal->setParents(parents);
515    }
516
517    Q_ASSERT_X(xtal->numAtoms() == oldXtal->numAtoms(), Q_FUNC_INFO,
518               "Number of atoms don't match. Cannot copy.");
519
520    for (uint i = 0; i < xtal->numAtoms(); ++i) {
521      (*oldXtal->atom(i)) = (*xtal->atom(i));
522    }
523    oldXtal->findSpaceGroup(tol_spg);
524
525    // Delete random xtal
526    xtal->deleteLater();
527    return oldXtal;
528  }
529
530  MolecularXtal* XtalOpt::replaceWithOffspringMXtal(
531      MolecularXtal *oldMXtal, const QString &reason)
532  {
533    // Generate/Check new mxtal
534    MolecularXtal *mxtal = NULL;
535    while (!checkXtal(mxtal)) {
536      if (mxtal) {
537        mxtal->deleteLater();
538        mxtal = NULL;
539      }
540      mxtal = generateNewMXtal();
541    }
542
543    // Copy info over
544    QWriteLocker locker (oldMXtal->lock());
545    QWriteLocker locker2 (mxtal->lock());
546    //! @todo Verify that this assignment doesn't do anything unusual.
547    oldMXtal->copyStructure(*mxtal);
548    oldMXtal->resetEnergy();
549    oldMXtal->resetEnthalpy();
550    oldMXtal->setPV(0);
551    oldMXtal->setCurrentOptStep(1);
552    QString parents = mxtal->getParents();
553    if (!reason.isEmpty())
554      parents += " (" + reason + ")";
555    oldMXtal->setParents(parents);
556    oldMXtal->findSpaceGroup(tol_spg);
557    oldMXtal->resetFailCount();
558
559    // Flag for preoptimization
560    if (this->usePreopt) {
561      oldMXtal->setNeedsPreoptimization(true);
562    }
563
564    // Delete offspring mxtal
565    mxtal->deleteLater();
566    return oldMXtal;
567  }
568
569  Xtal* XtalOpt::generateRandomXtal(uint generation, uint id)
570  {
571    INIT_RANDOM_GENERATOR();
572    // Set cell parameters
573    double a            = RANDDOUBLE() * (a_max-a_min) + a_min;
574    double b            = RANDDOUBLE() * (b_max-b_min) + b_min;
575    double c            = RANDDOUBLE() * (c_max-c_min) + c_min;
576    double alpha        = RANDDOUBLE() * (alpha_max - alpha_min) + alpha_min;
577    double beta         = RANDDOUBLE() * (beta_max  - beta_min ) + beta_min;
578    double gamma        = RANDDOUBLE() * (gamma_max - gamma_min) + gamma_min;
579
580    // Create crystal
581    Xtal *xtal  = new Xtal(a, b, c, alpha, beta, gamma);
582    QWriteLocker locker (xtal->lock());
583
584    xtal->setStatus(Xtal::Empty);
585
586    if (using_fixed_volume)
587      xtal->setVolume(vol_fixed);
588
589    // Populate crystal
590    QList<uint> atomicNums = comp.keys();
591    // Sort atomic number by decreasing minimum radius. Adding the "larger"
592    // atoms first encourages a more even (and ordered) distribution
593    for (int i = 0; i < atomicNums.size()-1; ++i) {
594      for (int j = i + 1; j < atomicNums.size(); ++j) {
595        if (this->comp.value(atomicNums[i]).minRadius <
596            this->comp.value(atomicNums[j]).minRadius) {
597          atomicNums.swap(i,j);
598        }
599      }
600    }
601
602    unsigned int atomicNum;
603    unsigned int q;
604    for (int num_idx = 0; num_idx < atomicNums.size(); num_idx++) {
605      atomicNum = atomicNums.at(num_idx);
606      q = comp.value(atomicNum).quantity;
607      for (uint i = 0; i < q; i++) {
608        if (!xtal->addAtomRandomly(atomicNum, this->comp)) {
609          xtal->deleteLater();
610          debug("XtalOpt::generateRandomXtal: Failed to add atoms with "
611                "specified interatomic distance.");
612          return 0;
613        }
614      }
615    }
616
617    // Set up geneology info
618    xtal->setGeneration(generation);
619    xtal->setIDNumber(id);
620    xtal->setParents("Randomly generated");
621    xtal->setStatus(Xtal::WaitingForOptimization);
622
623    // Set up xtal data
624    return xtal;
625  }
626
627  MolecularXtal* XtalOpt::generateRandomMXtal(uint generation, uint id)
628  {
629    INIT_RANDOM_GENERATOR();
630    // Set cell parameters
631    double a      = RANDDOUBLE() * (a_max-a_min) + a_min;
632    double b      = RANDDOUBLE() * (b_max-b_min) + b_min;
633    double c      = RANDDOUBLE() * (c_max-c_min) + c_min;
634    double alpha  = RANDDOUBLE() * (alpha_max - alpha_min) + alpha_min;
635    double beta   = RANDDOUBLE() * (beta_max  - beta_min ) + beta_min;
636    double gamma  = RANDDOUBLE() * (gamma_max - gamma_min) + gamma_min;
637
638    // Create crystal
639    MolecularXtal *mxtal        = new MolecularXtal(a, b, c, alpha, beta, gamma);
640    QWriteLocker locker (mxtal->lock());
641
642    mxtal->setStatus(MolecularXtal::Empty);
643
644    if (using_fixed_volume) {
645      mxtal->setVolume(vol_fixed);
646    }
647
648    // Populate crystal
649    //  Calculate maximum translation (cell diagonal length)
650    const std::vector<OpenBabel::vector3> obvecs =
651        mxtal->OBUnitCell()->GetCellVectors();
652    Q_ASSERT(obvecs.size() == 3);
653    const OpenBabel::vector3 obcellDiagonal = obvecs[0]+obvecs[1]+obvecs[2];
654    const double unitCellDiagonal = obcellDiagonal.length();
655
656    Eigen::Matrix3d rowVectors;
657    rowVectors.row(0) = Eigen::Vector3d(obvecs[0].AsArray());
658    rowVectors.row(1) = Eigen::Vector3d(obvecs[1].AsArray());
659    rowVectors.row(2) = Eigen::Vector3d(obvecs[2].AsArray());
660
661    for (QList<MolecularCompStruct>::const_iterator
662         it = mcomp.constBegin(), it_end = mcomp.constEnd();
663         it != it_end; ++it) {
664      for (int i = 0; i < it->quantity; ++i) {
665        SubMolecule * sub = it->source->getRandomSubMolecule();
666        QList<Atom*> sAtoms = sub->atoms();
667        // Attempt to add submolecule using various translations
668        //! @todo There needs to be a limit on the number of iterations here
669        while (true) { // Use break to pop out of this loop
670          Eigen::Vector3d trans (RANDDOUBLE(), RANDDOUBLE(), RANDDOUBLE());
671          trans = mxtal->fracToCart(trans);
672          sub->setCenter(trans);
673
674          // Compare the distances between the atoms in sub with the atoms in
675          // mxtal. If they meet the minimum radius restrictions, add sub.
676          if (this->using_interatomicDistanceLimit) {
677            int atom1, atom2;
678            double IAD;
679            if (mxtal->checkInteratomicDistances(
680                  this->comp, sAtoms, &atom1, &atom2, &IAD)) {
681              mxtal->addSubMolecule(sub);
682              break;
683            }
684            else /* mxtal->checkInteratomicDistances(...) */ {
685              qDebug() << "Cannot add submolecule; bad IAD:" << IAD;
686            }
687          }
688          // If we aren't using interatomic distance limits, just add sub.
689          else /* (!this->using_interatomicDistanceLimit) */ {
690            mxtal->addSubMolecule(sub);
691            break;
692          }
693        } // end while(true)
694      } // end for quantity
695    } // end for source
696
697    // Set up geneology info
698    mxtal->setGeneration(generation);
699    mxtal->setIDNumber(id);
700    mxtal->setParents("Randomly generated");
701    mxtal->setStatus(MolecularXtal::WaitingForOptimization);
702
703    return mxtal;
704  }
705
706  void XtalOpt::initializeAndAddXtal(Xtal *xtal, uint generation,
707                                     const QString &parents)
708    {
709    xtalInitMutex->lock();
710    QList<Structure*> allStructures = m_queue->lockForNaming();
711    Structure *structure;
712    uint id = 1;
713    for (int j = 0; j < allStructures.size(); j++) {
714      structure = allStructures.at(j);
715      structure->lock()->lockForRead();
716      if (structure->getGeneration() == generation &&
717          structure->getIDNumber() >= id) {
718        id = structure->getIDNumber() + 1;
719      }
720      structure->lock()->unlock();
721    }
722
723    QWriteLocker xtalLocker (xtal->lock());
724    xtal->moveToThread(m_queueThread);
725    xtal->setIDNumber(id);
726    xtal->setGeneration(generation);
727    xtal->setParents(parents);
728    QString id_s, gen_s, locpath_s, rempath_s;
729    id_s.sprintf("%05d",xtal->getIDNumber());
730    gen_s.sprintf("%05d",xtal->getGeneration());
731    locpath_s = filePath + "/" + gen_s + "x" + id_s + "/";
732    rempath_s = rempath + "/" + gen_s + "x" + id_s + "/";
733    QDir dir (locpath_s);
734    if (!dir.exists()) {
735      if (!dir.mkpath(locpath_s)) {
736        error(tr("XtalOpt::initializeAndAddXtal: Cannot write to path: %1 "
737                 "(path creation failure)", "1 is a file path.")
738              .arg(locpath_s));
739      }
740    }
741    xtal->moveToThread(m_tracker->thread());
742    xtal->setupConnections();
743    xtal->setFileName(locpath_s);
744    xtal->setRempath(rempath_s);
745    xtal->setCurrentOptStep(1);
746    // If none of the cell parameters are fixed, perform a normalization on
747    // the lattice (currently a Niggli reduction)
748    if (fabs(a_min     - a_max)     > 0.01 &&
749        fabs(b_min     - b_max)     > 0.01 &&
750        fabs(c_min     - c_max)     > 0.01 &&
751        fabs(alpha_min - alpha_max) > 0.01 &&
752        fabs(beta_min  - beta_max)  > 0.01 &&
753        fabs(gamma_min - gamma_max) > 0.01) {
754      xtal->fixAngles();
755    }
756    xtal->findSpaceGroup(tol_spg);
757    xtalLocker.unlock();
758    xtal->update();
759    m_queue->unlockForNaming(xtal);
760    xtalInitMutex->unlock();
761  }
762
763  void XtalOpt::generateNewStructure()
764  {
765    // Generate in background thread:
766    QtConcurrent::run(this, &XtalOpt::generateNewStructure_);
767  }
768
769  void XtalOpt::generateNewStructure_()
770  {
771    if (this->isMolecularXtalSearch()) {
772      MolecularXtal *newMXtal = generateNewMXtal();
773      initializeAndAddXtal(newMXtal, newMXtal->getGeneration(),
774                           newMXtal->getParents());
775    }
776    else {
777      Xtal *newXtal = generateNewXtal();
778      initializeAndAddXtal(newXtal, newXtal->getGeneration(),
779                           newXtal->getParents());
780    }
781  }
782
783  Xtal* XtalOpt::generateNewXtal()
784  {
785    // Get all optimized structures
786    QList<Structure*> structures = m_queue->getAllOptimizedStructures();
787
788    // Check to see if there are enough optimized structure to perform
789    // genetic operations
790    if (structures.size() < 3) {
791      Xtal *xtal = 0;
792      while (!checkXtal(xtal)) {
793        if (xtal) xtal->deleteLater();
794        xtal = generateRandomXtal(1, 0);
795      }
796      xtal->setParents(xtal->getParents() + " (too few optimized structures "
797                       "to generate offspring)");
798      return xtal;
799    }
800
801    // Sort structure list
802    Structure::sortByEnthalpy(&structures);
803
804    // Trim list
805    // Remove all but (popSize + 1). The "+ 1" will be removed
806    // during probability generation.
807    while ( static_cast<uint>(structures.size()) > popSize + 1 ) {
808      structures.removeLast();
809    }
810
811    // Make list of weighted probabilities based on enthalpy values
812    QList<double> probs = getProbabilityList(structures);
813
814    // Cast Structures into Xtals
815    QList<Xtal*> xtals;
816#if QT_VERSION >= 0x040700
817    xtals.reserve(structures.size());
818#endif // QT_VERSION
819    for (int i = 0; i < structures.size(); ++i) {
820      xtals.append(qobject_cast<Xtal*>(structures.at(i)));
821    }
822
823    // Initialize loop vars
824    double r;
825    unsigned int gen;
826    QString parents;
827    Xtal *xtal = NULL;
828
829    // Perform operation until xtal is valid:
830    while (!checkXtal(xtal)) {
831      // First delete any previous failed structure in xtal
832      if (xtal) {
833        xtal->deleteLater();
834        xtal = 0;
835      }
836
837      // Decide operator:
838      r = RANDDOUBLE();
839      Operators op;
840      if (r < p_cross/100.0)
841        op = OP_Crossover;
842      else if (r < (p_cross + p_strip)/100.0)
843        op = OP_Stripple;
844      else
845        op = OP_Permustrain;
846
847      // Try 1000 times to get a good structure from the selected
848      // operation. If not possible, send a warning to the log and
849      // start anew.
850      int attemptCount = 0;
851      while (attemptCount < 1000 && !checkXtal(xtal)) {
852        attemptCount++;
853        if (xtal) {
854          delete xtal;
855          xtal = 0;
856        }
857
858        // Operation specific set up:
859        switch (op) {
860        case OP_Crossover: {
861          int ind1, ind2;
862          Xtal *xtal1=0, *xtal2=0;
863          // Select structures
864          ind1 = ind2 = 0;
865          double r1 = RANDDOUBLE();
866          double r2 = RANDDOUBLE();
867          for (ind1 = 0; ind1 < probs.size(); ind1++)
868            if (r1 < probs.at(ind1)) break;
869          for (ind2 = 0; ind2 < probs.size(); ind2++)
870            if (r2 < probs.at(ind2)) break;
871
872          xtal1 = xtals.at(ind1);
873          xtal2 = xtals.at(ind2);
874
875          // Perform operation
876          double percent1;
877          xtal = XtalOptGenetic::crossover(
878                xtal1, xtal2, cross_minimumContribution, percent1);
879
880          // Lock parents and get info from them
881          xtal1->lock()->lockForRead();
882          xtal2->lock()->lockForRead();
883          uint gen1 = xtal1->getGeneration();
884          uint gen2 = xtal2->getGeneration();
885          uint id1 = xtal1->getIDNumber();
886          uint id2 = xtal2->getIDNumber();
887          xtal2->lock()->unlock();
888          xtal1->lock()->unlock();
889
890          // Determine generation number
891          gen = ( gen1 >= gen2 ) ?
892            gen1 + 1 :
893            gen2 + 1 ;
894          parents = tr("Crossover: %1x%2 (%3%) + %4x%5 (%6%)")
895            .arg(gen1)
896            .arg(id1)
897            .arg(percent1, 0, 'f', 0)
898            .arg(gen2)
899            .arg(id2)
900            .arg(100.0 - percent1, 0, 'f', 0);
901          continue;
902        }
903        case OP_Stripple: {
904          // Pick a parent
905          int ind;
906          double r = RANDDOUBLE();
907          for (ind = 0; ind < probs.size(); ind++)
908            if (r < probs.at(ind)) break;
909          Xtal *xtal1 = xtals.at(ind);
910
911          // Perform stripple
912          double amplitude=0, stdev=0;
913          xtal = XtalOptGenetic::stripple(xtal1,
914                                          strip_strainStdev_min,
915                                          strip_strainStdev_max,
916                                          strip_amp_min,
917                                          strip_amp_max,
918                                          strip_per1,
919                                          strip_per2,
920                                          stdev,
921                                          amplitude);
922
923          // Lock parent and extract info
924          xtal1->lock()->lockForRead();
925          uint gen1 = xtal1->getGeneration();
926          uint id1 = xtal1->getIDNumber();
927          xtal1->lock()->unlock();
928
929          // Determine generation number
930          gen = gen1 + 1;
931          parents = tr("Stripple: %1x%2 stdev=%3 amp=%4 waves=%5,%6")
932            .arg(gen1)
933            .arg(id1)
934            .arg(stdev, 0, 'f', 5)
935            .arg(amplitude, 0, 'f', 5)
936            .arg(strip_per1)
937            .arg(strip_per2);
938          continue;
939        }
940        case OP_Permustrain: {
941          int ind;
942          double r = RANDDOUBLE();
943          for (ind = 0; ind < probs.size(); ind++)
944            if (r < probs.at(ind)) break;
945
946          Xtal *xtal1 = xtals.at(ind);
947          double stdev=0;
948          xtal = XtalOptGenetic::permustrain(
949                xtals.at(ind), perm_strainStdev_max, perm_ex, stdev);
950
951          // Lock parent and extract info
952          xtal1->lock()->lockForRead();
953          uint gen1 = xtal1->getGeneration();
954          uint id1 = xtal1->getIDNumber();
955          xtal1->lock()->unlock();
956
957          // Determine generation number
958          gen = gen1 + 1;
959          parents = tr("Permustrain: %1x%2 stdev=%3 exch=%4")
960            .arg(gen1)
961            .arg(id1)
962            .arg(stdev, 0, 'f', 5)
963            .arg(perm_ex);
964          continue;
965        }
966        default:
967          warning("XtalOpt::generateSingleOffspring: Attempt to use an "
968                  "invalid operator.");
969        }
970      }
971      if (attemptCount >= 1000) {
972        QString opStr;
973        switch (op) {
974        case OP_Crossover:   opStr = "crossover"; break;
975        case OP_Stripple:    opStr = "stripple"; break;
976        case OP_Permustrain: opStr = "permustrain"; break;
977        default:             opStr = "(unknown)"; break;
978        }
979        warning(tr("Unable to perform operation %1 after 1000 tries. "
980                   "Reselecting operator...").arg(opStr));
981      }
982    }
983    xtal->setGeneration(gen);
984    xtal->setParents(parents);
985    return xtal;
986  }
987
988  MolecularXtal* XtalOpt::generateNewMXtal()
989  {
990    // Get all optimized structures
991    QList<Structure*> structures = m_queue->getAllOptimizedStructures();
992
993    // Check to see if there are enough optimized structure to perform
994    // genetic operations
995    if (structures.size() < 3) {
996      MolecularXtal *mxtal = 0;
997      while (!checkXtal(mxtal)) {
998        if (mxtal) {
999          mxtal->deleteLater();
1000          mxtal = NULL;
1001        }
1002        mxtal = generateRandomMXtal(1, 0);
1003      }
1004      mxtal->setParents(mxtal->getParents() + " (too few optimized structures "
1005                       "to generate offspring)");
1006      return mxtal;
1007    }
1008
1009#warning Remove when operator GUI is setup // TODO
1010    mga_p_cross = 34;
1011    mga_cross_minimumContribution = .25;
1012    // - Reconf
1013    mga_p_reconf = 33;
1014    mga_reconf_minSubMolsToReplace = 1;
1015    mga_reconf_maxSubMolsToReplace = 3;
1016    mga_reconf_minStrain = 0.0;
1017    mga_reconf_maxStrain = 0.5;
1018    // - Swirl
1019    mga_p_swirl = 33;
1020    mga_swirl_minSubMolsToRotate = 1;
1021    mga_swirl_maxSubMolsToRotate = 3;
1022    mga_swirl_minRotationDegree = 30;
1023    mga_swirl_fracInPlane = 0.5;
1024    mga_swirl_minStrain = 0.0;
1025    mga_swirl_maxStrain = 0.5;
1026
1027    // Sort structure list
1028    Structure::sortByEnthalpy(&structures);
1029
1030    // Trim list
1031    // Remove all but (popSize + 1). The "+ 1" will be removed
1032    // during probability generation.
1033    while ( static_cast<uint>(structures.size()) > popSize + 1 ) {
1034      structures.removeLast();
1035    }
1036
1037    // Make list of weighted probabilities based on enthalpy values
1038    QList<double> probs = getProbabilityList(structures);
1039
1040    // Cast Structures into MXtals
1041    QList<MolecularXtal*> mxtals;
1042#if QT_VERSION >= 0x040700
1043    mxtals.reserve(structures.size());
1044#endif // QT_VERSION
1045    for (int i = 0; i < structures.size(); ++i) {
1046      mxtals.append(qobject_cast<MolecularXtal*>(structures.at(i)));
1047    }
1048
1049    // Initialize loop vars
1050    double r;
1051    unsigned int gen;
1052    MolecularXtal *mxtal = NULL;
1053
1054    // Perform operation until xtal is valid:
1055    while (!checkXtal(mxtal)) {
1056      // First delete any previous failed structure in xtal
1057      if (mxtal) {
1058        mxtal->deleteLater();
1059        mxtal = NULL;
1060      }
1061
1062      // Decide operator:
1063      r = RANDDOUBLE();
1064      MXtalOperator op;
1065      if (r < mga_p_cross/100.0)
1066        op = MXOP_Crossover;
1067      else if (r < (mga_p_cross + mga_p_reconf)/100.0)
1068        op = MXOP_Reconf;
1069      else // if (r < (mga_p_cross + mga_p_reconf + mga_p_swirl)/100.0
1070        op = MXOP_Swirl;
1071
1072      // Try 1000 times to get a good structure from the selected
1073      // operation. If not possible, send a warning to the log and
1074      // start anew.
1075      int attemptCount = 0;
1076      while (attemptCount < 1000 && !checkXtal(mxtal)) {
1077        attemptCount++;
1078        if (mxtal) {
1079          delete mxtal;
1080          mxtal = NULL;
1081        }
1082
1083        // Select two potential parents
1084        int ind1, ind2;
1085        MolecularXtal *parent1=0, *parent2=0;
1086        // Select structures
1087        double r1 = RANDDOUBLE();
1088        double r2 = RANDDOUBLE();
1089        for (ind1 = 0; ind1 < probs.size(); ind1++)
1090          if (r1 < probs.at(ind1)) break;
1091        for (ind2 = 0; ind2 < probs.size(); ind2++)
1092          if (r2 < probs.at(ind2)) break;
1093
1094        parent1 = mxtals.at(ind1);
1095        parent2 = mxtals.at(ind2);
1096
1097        // Operation specific set up:
1098        switch (op) {
1099        case MXOP_Crossover:
1100          mxtal = MXtalOptGenetic::crossover(parent1, parent2, this);
1101          break;
1102        case MXOP_Reconf:
1103          mxtal = MXtalOptGenetic::reconf(parent1, this);
1104          break;
1105        case MXOP_Swirl:
1106          mxtal = MXtalOptGenetic::swirl(parent1, this);
1107          break;
1108        default:
1109          qWarning() << "Unknown genetic operation, enum code:" << op;
1110          mxtal = NULL;
1111          continue;
1112        }
1113
1114        // Assign id
1115        int gen = mxtal->getGeneration();
1116        int id = 0;
1117        for (QList<MolecularXtal*>::const_iterator it = mxtals.constBegin(),
1118             it_end = mxtals.constEnd(); it != it_end; ++it) {
1119          (*it)->lock()->lockForRead();
1120          const int curGen = (*it)->getGeneration();
1121          const int curId = (*it)->getIDNumber();
1122          (*it)->lock()->unlock();
1123          if (curGen == gen &&
1124              curId  >= id) {
1125            id = curId + 1;
1126          }
1127        }
1128        mxtal->setIDNumber(id);
1129
1130      }
1131      if (attemptCount >= 1000) {
1132        QString opStr;
1133        switch (op) {
1134        case MXOP_Crossover: opStr = "crossover"; break;
1135        case MXOP_Reconf:    opStr = "reconf"; break;
1136        case MXOP_Swirl:     opStr = "swirl"; break;
1137        default:             opStr = "(unknown)"; break;
1138        }
1139        warning(tr("Unable to perform operation %1 after 1000 tries. "
1140                   "Reselecting operator...").arg(opStr));
1141      }
1142    }
1143
1144    return mxtal;
1145  }
1146
1147  bool XtalOpt::checkLimits() {
1148    if (a_min > a_max) {
1149      warning("XtalOptRand::checkLimits error: Illogical A limits.");
1150      return false;
1151    }
1152    if (b_min > b_max) {
1153      warning("XtalOptRand::checkLimits error: Illogical B limits.");
1154      return false;
1155    }
1156    if (c_min > c_max) {
1157      warning("XtalOptRand::checkLimits error: Illogical C limits.");
1158      return false;
1159    }
1160    if (alpha_min > alpha_max) {
1161      warning("XtalOptRand::checkLimits error: Illogical Alpha limits.");
1162      return false;
1163    }
1164    if (beta_min > beta_max) {
1165      warning("XtalOptRand::checkLimits error: Illogical Beta limits.");
1166      return false;
1167    }
1168    if (gamma_min > gamma_max) {
1169      warning("XtalOptRand::checkLimits error: Illogical Gamma limits.");
1170      return false;
1171    }
1172    if (
1173        ( using_fixed_volume &&
1174          ( (a_min * b_min * c_min) > vol_fixed ||
1175            (a_max * b_max * c_max) < vol_fixed )
1176          ) ||
1177        ( !using_fixed_volume &&
1178          ( (a_min * b_min * c_min) > vol_max ||
1179            (a_max * b_max * c_max) < vol_min ||
1180            vol_min > vol_max)
1181          )) {
1182      warning("XtalOptRand::checkLimits error: Illogical Volume limits. "
1183              "(Also check min/max volumes based on cell lengths)");
1184      return false;
1185    }
1186    return true;
1187  }
1188
1189  bool XtalOpt::checkXtal(Xtal *xtal, QString * err) {
1190    if (!xtal) {
1191      if (err != NULL) {
1192        *err = "Xtal pointer is NULL.";
1193      }
1194      return false;
1195    }
1196
1197    // Lock xtal
1198    QWriteLocker locker (xtal->lock());
1199
1200    if (xtal->getStatus() == Xtal::Empty) {
1201      if (err != NULL) {
1202        *err = "Xtal status is empty.";
1203      }
1204      return false;
1205    }
1206
1207    // Check composition
1208    QList<unsigned int> atomTypes = comp.keys();
1209    QList<unsigned int> atomCounts;
1210#if QT_VERSION >= 0x040700
1211    atomCounts.reserve(atomTypes.size());
1212#endif // QT_VERSION
1213    for (int i = 0; i < atomTypes.size(); ++i) {
1214      atomCounts.append(0);
1215    }
1216    // Count atoms of each type
1217    for (int i = 0; i < xtal->numAtoms(); ++i) {
1218      int typeIndex = atomTypes.indexOf(
1219            static_cast<unsigned int>(xtal->atom(i)->atomicNumber()));
1220      // Type not found:
1221      if (typeIndex == -1) {
1222        qDebug() << "XtalOpt::checkXtal: Composition incorrect.";
1223        if (err != NULL) {
1224          *err = "Bad composition.";
1225        }
1226        return false;
1227      }
1228      ++atomCounts[typeIndex];
1229    }
1230    // Check counts
1231    for (int i = 0; i < atomTypes.size(); ++i) {
1232      if (atomCounts[i] != comp[atomTypes[i]].quantity) {
1233        // Incorrect count:
1234        qDebug() << "XtalOpt::checkXtal: Composition incorrect.";
1235        if (err != NULL) {
1236          *err = "Bad composition.";
1237        }
1238        return false;
1239      }
1240    }
1241
1242    // Check volume
1243    if (using_fixed_volume) {
1244      locker.unlock();
1245      xtal->setVolume(vol_fixed);
1246      locker.relock();
1247    }
1248    else if ( xtal->getVolume() < vol_min ||
1249              xtal->getVolume() > vol_max ) {
1250      // I don't want to initialize a random number generator here, so
1251      // just use the modulus of the current volume as a random float.
1252      double newvol = fabs(fmod(xtal->getVolume(), 1)) *
1253          (vol_max - vol_min) + vol_min;
1254      // If the user has set vol_min to 0, we can end up with a null
1255      // volume. Fix this here. This is just to keep things stable
1256      // numerically during the rescaling -- it's unlikely that other
1257      // cells with small, nonzero volumes will pass the other checks
1258      // so long as other limits are reasonable.
1259      if (fabs(newvol) < 1.0) {
1260        newvol = (vol_max - vol_min)*0.5 + vol_min;
1261      }
1262      qDebug() << "XtalOpt::checkXtal: Rescaling volume from "
1263               << xtal->getVolume() << " to " << newvol;
1264      xtal->setVolume(newvol);
1265    }
1266
1267    // Scale to any fixed parameters
1268    double a, b, c, alpha, beta, gamma;
1269    a = b = c = alpha = beta = gamma = 0;
1270    if (fabs(a_min - a_max) < 0.01) a = a_min;
1271    if (fabs(b_min - b_max) < 0.01) b = b_min;
1272    if (fabs(c_min - c_max) < 0.01) c = c_min;
1273    if (fabs(alpha_min - alpha_max) < 0.01) alpha = alpha_min;
1274    if (fabs(beta_min -  beta_max)  < 0.01)  beta = beta_min;
1275    if (fabs(gamma_min - gamma_max) < 0.01) gamma = gamma_min;
1276    xtal->rescaleCell(a, b, c, alpha, beta, gamma);
1277
1278    // Reject the structure if using VASP and the determinant of the
1279    // cell matrix is negative (otherwise VASP complains about a
1280    // "negative triple product")
1281    if (qobject_cast<VASPOptimizer*>(m_optimizer) != 0 &&
1282        xtal->OBUnitCell()->GetCellMatrix().determinant() <= 0.0) {
1283      qDebug() << "Rejecting structure" << xtal->getIDString()
1284               << ": using VASP negative triple product.";
1285      if (err != NULL) {
1286        *err = "Unit cell matrix cannot have a negative triple product "
1287            "when using VASP.";
1288      }
1289      return false;
1290    }
1291
1292    // Before fixing angles, make sure that the current cell
1293    // parameters are realistic
1294    if (GS_IS_NAN_OR_INF(xtal->getA()) || fabs(xtal->getA()) < 1e-8 ||
1295        GS_IS_NAN_OR_INF(xtal->getB()) || fabs(xtal->getB()) < 1e-8 ||
1296        GS_IS_NAN_OR_INF(xtal->getC()) || fabs(xtal->getC()) < 1e-8 ||
1297        GS_IS_NAN_OR_INF(xtal->getAlpha()) || fabs(xtal->getAlpha()) < 1e-8 ||
1298        GS_IS_NAN_OR_INF(xtal->getBeta())  || fabs(xtal->getBeta())  < 1e-8 ||
1299        GS_IS_NAN_OR_INF(xtal->getGamma()) || fabs(xtal->getGamma()) < 1e-8 ) {
1300      qDebug() << "XtalOpt::checkXtal: A cell parameter is either 0, nan, or "
1301                  "inf. Discarding.";
1302      if (err != NULL) {
1303        *err = "A cell parameter is too small (<10^-8) or not a number.";
1304      }
1305      return false;
1306    }
1307
1308    // If no cell parameters are fixed, normalize lattice
1309    if (fabs(a + b + c + alpha + beta + gamma) < 1e-8) {
1310      xtal->fixAngles();
1311    }
1312
1313    // Check lattice
1314    if ( ( !a     && ( xtal->getA() < a_min         || xtal->getA() > a_max         ) ) ||
1315         ( !b     && ( xtal->getB() < b_min         || xtal->getB() > b_max         ) ) ||
1316         ( !c     && ( xtal->getC() < c_min         || xtal->getC() > c_max         ) ) ||
1317         ( !alpha && ( xtal->getAlpha() < alpha_min || xtal->getAlpha() > alpha_max ) ) ||
1318         ( !beta  && ( xtal->getBeta()  < beta_min  || xtal->getBeta()  > beta_max  ) ) ||
1319         ( !gamma && ( xtal->getGamma() < gamma_min || xtal->getGamma() > gamma_max ) ) )  {
1320      qDebug() << "Discarding structure -- Bad lattice:" <<endl
1321               << "A:     " << a_min << " " << xtal->getA() << " " << a_max << endl
1322               << "B:     " << b_min << " " << xtal->getB() << " " << b_max << endl
1323               << "C:     " << c_min << " " << xtal->getC() << " " << c_max << endl
1324               << "Alpha: " << alpha_min << " " << xtal->getAlpha() << " " << alpha_max << endl
1325               << "Beta:  " << beta_min  << " " << xtal->getBeta()  << " " << beta_max << endl
1326               << "Gamma: " << gamma_min << " " << xtal->getGamma() << " " << gamma_max;
1327      if (err != NULL) {
1328        *err = "The unit cell parameters do not fall within the specified "
1329            "limits.";
1330      }
1331      return false;
1332    }
1333
1334    // Check interatomic distances
1335    if (using_interatomicDistanceLimit) {
1336      int atom1, atom2;
1337      double IAD;
1338      if (!xtal->checkInteratomicDistances(this->comp, &atom1, &atom2, &IAD)){
1339        Atom *a1 = xtal->atom(atom1);
1340        Atom *a2 = xtal->atom(atom2);
1341        const double minIAD =
1342            this->comp.value(a1->atomicNumber()).minRadius +
1343            this->comp.value(a2->atomicNumber()).minRadius;
1344
1345        qDebug() << "Discarding structure -- Bad IAD ("
1346                 << IAD << " < "
1347                 << minIAD << ")";
1348        if (err != NULL) {
1349          *err = "Two atoms are too close together.";
1350        }
1351        return false;
1352      }
1353    }
1354
1355    // Xtal is OK!
1356    if (err != NULL) {
1357      *err = "";
1358    }
1359    return true;
1360  }
1361
1362  QString XtalOpt::interpretTemplate(const QString & templateString,
1363                                     Structure* structure)
1364  {
1365    QStringList list = templateString.split("%");
1366    QString line;
1367    QString origLine;
1368    Xtal *xtal = qobject_cast<Xtal*>(structure);
1369    for (int line_ind = 0; line_ind < list.size(); line_ind++) {
1370      origLine = line = list.at(line_ind);
1371      interpretKeyword_base(line, structure);
1372      interpretKeyword(line, structure);
1373      if (line != origLine) { // Line was a keyword
1374        list.replace(line_ind, line);
1375      }
1376    }
1377    // Rejoin string
1378    QString ret = list.join("");
1379    ret += "\n";
1380    return ret;
1381  }
1382
1383  void XtalOpt::interpretKeyword(QString &line, Structure* structure)
1384  {
1385    QString rep = "";
1386    Xtal *xtal = qobject_cast<Xtal*>(structure);
1387
1388    // Xtal specific keywords
1389    if (line == "a")                    rep += QString::number(xtal->getA());
1390    else if (line == "b")               rep += QString::number(xtal->getB());
1391    else if (line == "c")               rep += QString::number(xtal->getC());
1392    else if (line == "alphaRad")        rep += QString::number(xtal->getAlpha() * DEG_TO_RAD);
1393    else if (line == "betaRad")         rep += QString::number(xtal->getBeta() * DEG_TO_RAD);
1394    else if (line == "gammaRad")        rep += QString::number(xtal->getGamma() * DEG_TO_RAD);
1395    else if (line == "alphaDeg")        rep += QString::number(xtal->getAlpha());
1396    else if (line == "betaDeg")         rep += QString::number(xtal->getBeta());
1397    else if (line == "gammaDeg")        rep += QString::number(xtal->getGamma());
1398    else if (line == "volume")          rep += QString::number(xtal->getVolume());
1399    else if (line == "coordsFrac") {
1400      QList<Atom*> atoms = structure->atoms();
1401      QList<Atom*>::const_iterator it;
1402      for (it  = atoms.begin();
1403           it != atoms.end();
1404           it++) {
1405        const Eigen::Vector3d coords = xtal->cartToFrac(*((*it)->pos()));
1406        rep += static_cast<QString>(OpenBabel::etab.GetSymbol((*it)->atomicNumber())) + " ";
1407        rep += QString::number(coords.x()) + " ";
1408        rep += QString::number(coords.y()) + " ";
1409        rep += QString::number(coords.z()) + "\n";
1410      }
1411    }
1412    else if (line == "coordsFracId") {
1413      QList<Atom*> atoms = structure->atoms();
1414      QList<Atom*>::const_iterator it;
1415      for (it  = atoms.begin();
1416           it != atoms.end();
1417           it++) {
1418        const Eigen::Vector3d coords = xtal->cartToFrac(*(*it)->pos());
1419        rep += static_cast<QString>(OpenBabel::etab.GetSymbol((*it)->atomicNumber())) + " ";
1420        rep += QString::number((*it)->atomicNumber()) + " ";
1421        rep += QString::number(coords.x()) + " ";
1422        rep += QString::number(coords.y()) + " ";
1423        rep += QString::number(coords.z()) + "\n";
1424      }
1425    }
1426    else if (line == "gulpFracShell") {
1427      QList<Atom*> atoms = structure->atoms();
1428      QList<Atom*>::const_iterator it;
1429      for (it  = atoms.begin();
1430           it != atoms.end();
1431           it++) {
1432        const Eigen::Vector3d coords = xtal->cartToFrac(*((*it)->pos()));
1433        const char *symbol = OpenBabel::etab.GetSymbol((*it)->atomicNumber());
1434        rep += QString("%1 core %2 %3 %4\n")
1435            .arg(symbol).arg(coords.x()).arg(coords.y()).arg(coords.z());
1436        rep += QString("%1 shel %2 %3 %4\n")
1437            .arg(symbol).arg(coords.x()).arg(coords.y()).arg(coords.z());
1438        }
1439    }
1440    else if (line == "cellMatrixAngstrom") {
1441      matrix3x3 m = xtal->OBUnitCell()->GetCellMatrix();
1442      for (int i = 0; i < 3; i++) {
1443        for (int j = 0; j < 3; j++) {
1444          rep += QString::number(m.Get(i,j)) + "\t";
1445        }
1446        rep += "\n";
1447      }
1448    }
1449    else if (line == "cellVector1Angstrom") {
1450      vector3 v = xtal->OBUnitCell()->GetCellVectors()[0];
1451      for (int i = 0; i < 3; i++) {
1452        rep += QString::number(v[i]) + "\t";
1453      }
1454    }
1455    else if (line == "cellVector2Angstrom") {
1456      vector3 v = xtal->OBUnitCell()->GetCellVectors()[1];
1457      for (int i = 0; i < 3; i++) {
1458        rep += QString::number(v[i]) + "\t";
1459      }
1460    }
1461    else if (line == "cellVector3Angstrom") {
1462      vector3 v = xtal->OBUnitCell()->GetCellVectors()[2];
1463      for (int i = 0; i < 3; i++) {
1464        rep += QString::number(v[i]) + "\t";
1465      }
1466    }
1467    else if (line == "cellMatrixBohr") {
1468      matrix3x3 m = xtal->OBUnitCell()->GetCellMatrix();
1469      for (int i = 0; i < 3; i++) {
1470        for (int j = 0; j < 3; j++) {
1471          rep += QString::number(m.Get(i,j) * ANGSTROM_TO_BOHR) + "\t";
1472        }
1473        rep += "\n";
1474      }
1475    }
1476    else if (line == "cellVector1Bohr") {
1477      vector3 v = xtal->OBUnitCell()->GetCellVectors()[0];
1478      for (int i = 0; i < 3; i++) {
1479        rep += QString::number(v[i] * ANGSTROM_TO_BOHR) + "\t";
1480      }
1481    }
1482    else if (line == "cellVector2Bohr") {
1483      vector3 v = xtal->OBUnitCell()->GetCellVectors()[1];
1484      for (int i = 0; i < 3; i++) {
1485        rep += QString::number(v[i] * ANGSTROM_TO_BOHR) + "\t";
1486      }
1487    }
1488    else if (line == "cellVector3Bohr") {
1489      vector3 v = xtal->OBUnitCell()->GetCellVectors()[2];
1490      for (int i = 0; i < 3; i++) {
1491        rep += QString::number(v[i] * ANGSTROM_TO_BOHR) + "\t";
1492      }
1493    }
1494    else if (line == "POSCAR") {
1495      // Comment line -- set to composition then filename
1496      // Construct composition
1497      QStringList symbols = xtal->getSymbols();
1498      QList<unsigned int> atomCounts = xtal->getNumberOfAtomsAlpha();
1499      Q_ASSERT_X(symbols.size() == atomCounts.size(), Q_FUNC_INFO,
1500                 "xtal->getSymbols is not the same size as xtal->getNumberOfAtomsAlpha.");
1501      for (unsigned int i = 0; i < symbols.size(); i++) {
1502        rep += QString("%1%2").arg(symbols[i]).arg(atomCounts[i]);
1503      }
1504      rep += " ";
1505      rep += xtal->fileName();
1506      rep += "\n";
1507      // Scaling factor. Just 1.0
1508      rep += QString::number(1.0);
1509      rep += "\n";
1510      // Unit Cell Vectors
1511      std::vector< vector3 > vecs = xtal->OBUnitCell()->GetCellVectors();
1512      for (uint i = 0; i < vecs.size(); i++) {
1513        rep += QString("  %1 %2 %3\n")
1514          .arg(vecs[i].x(), 12, 'f', 8)
1515          .arg(vecs[i].y(), 12, 'f', 8)
1516          .arg(vecs[i].z(), 12, 'f', 8);
1517      }
1518      // Number of each type of atom (sorted alphabetically by symbol)
1519      for (int i = 0; i < atomCounts.size(); i++) {
1520        rep += QString::number(atomCounts.at(i)) + " ";
1521      }
1522      rep += "\n";
1523      // Use fractional coordinates:
1524      rep += "Direct\n";
1525      // Coordinates of each atom (sorted alphabetically by symbol)
1526      QList<Eigen::Vector3d> coords = xtal->getAtomCoordsFrac();
1527      for (int i = 0; i < coords.size(); i++) {
1528        rep += QString("  %1 %2 %3\n")
1529          .arg(coords[i].x(), 12, 'f', 8)
1530          .arg(coords[i].y(), 12, 'f', 8)
1531          .arg(coords[i].z(), 12, 'f', 8);
1532      }
1533    } // End %POSCAR%
1534
1535    if (!rep.isEmpty()) {
1536      // Remove any trailing newlines
1537      rep = rep.replace(QRegExp("\n$"), "");
1538      line = rep;
1539    }
1540  }
1541
1542  QString XtalOpt::getTemplateKeywordHelp()
1543  {
1544    QString help = "";
1545    help.append(getTemplateKeywordHelp_base());
1546    help.append("\n");
1547    help.append(getTemplateKeywordHelp_xtalopt());
1548    return help;
1549  }
1550
1551  QString XtalOpt::getTemplateKeywordHelp_xtalopt()
1552  {
1553    QString str;
1554    QTextStream out (&str);
1555    out
1556      << "Crystal specific information:\n"
1557      << "%POSCAR% -- VASP poscar generator\n"
1558      << "%coordsFrac% -- fractional coordinate data\n\t[symbol] [x] [y] [z]\n"
1559      << "%coordsFracId% -- fractional coordinate data with atomic number\n\t[symbol] [atomic number] [x] [y] [z]\n"
1560      << "%gulpFracShell% -- fractional coordinates for use in GULP core/shell calculations:\n"
1561         "\tBoth of the following are printed for each atom:\n"
1562         "\t[symbol] core [x] [y] [z]\n"
1563         "\t[symbol] shell [x] [y] [z]\n"
1564      << "%cellMatrixAngstrom% -- Cell matrix in Angstrom\n"
1565      << "%cellVector1Angstrom% -- First cell vector in Angstrom\n"
1566      << "%cellVector2Angstrom% -- Second cell vector in Angstrom\n"
1567      << "%cellVector3Angstrom% -- Third cell vector in Angstrom\n"
1568      << "%cellMatrixBohr% -- Cell matrix in Bohr\n"
1569      << "%cellVector1Bohr% -- First cell vector in Bohr\n"
1570      << "%cellVector2Bohr% -- Second cell vector in Bohr\n"
1571      << "%cellVector3Bohr% -- Third cell vector in Bohr\n"
1572      << "%a% -- Lattice parameter A\n"
1573      << "%b% -- Lattice parameter B\n"
1574      << "%c% -- Lattice parameter C\n"
1575      << "%alphaRad% -- Lattice parameter Alpha in rad\n"
1576      << "%betaRad% -- Lattice parameter Beta in rad\n"
1577      << "%gammaRad% -- Lattice parameter Gamma in rad\n"
1578      << "%alphaDeg% -- Lattice parameter Alpha in degrees\n"
1579      << "%betaDeg% -- Lattice parameter Beta in degrees\n"
1580      << "%gammaDeg% -- Lattice parameter Gamma in degrees\n"
1581      << "%volume% -- Unit cell volume\n"
1582      << "%gen% -- xtal generation number\n"
1583      << "%id% -- xtal id number\n";
1584
1585    return str;
1586  }
1587
1588  bool XtalOpt::load(const QString &filename, const bool forceReadOnly)
1589  {
1590    if (forceReadOnly) {
1591      readOnly = true;
1592    }
1593
1594    // Attempt to open state file
1595    QFile file (filename);
1596    if (!file.open(QIODevice::ReadOnly)) {
1597      error("XtalOpt::load(): Error opening file "
1598            +file.fileName() + " for reading...");
1599      return false;
1600    }
1601
1602    SETTINGS(filename);
1603    int loadedVersion = settings->value("xtalopt/version", 0).toInt();
1604
1605    // Update config data. Be sure to bump m_schemaVersion in ctor if
1606    // adding updates.
1607    switch (loadedVersion) {
1608    case 0:
1609    case 1:
1610    case 2: // Tab edit bumped to V2. No change here.
1611      break;
1612    default:
1613      error("XtalOpt::load(): Settings in file "+file.fileName()+
1614            " cannot be opened by this version of XtalOpt. Please "
1615            "visit http://xtalopt.openmolecules.net to obtain a "
1616            "newer version.");
1617      return false;
1618    }
1619
1620    bool stateFileIsValid = settings->value("xtalopt/saveSuccessful",
1621                                            false).toBool();
1622    if (!stateFileIsValid) {
1623      error("XtalOpt::load(): File " + file.fileName() +
1624            " is incomplete, corrupt, or invalid. (Try "
1625            + file.fileName() + ".old if it exists)");
1626      return false;
1627    }
1628
1629    // Get path and other info for later:
1630    QFileInfo stateInfo (file);
1631    // path to resume file
1632    QDir dataDir  = stateInfo.absoluteDir();
1633    QString dataPath = dataDir.absolutePath() + "/";
1634    // list of xtal dirs
1635    QStringList xtalDirs = dataDir.entryList(QStringList(),
1636                                             QDir::AllDirs, QDir::Size);
1637    xtalDirs.removeAll(".");
1638    xtalDirs.removeAll("..");
1639    for (int i = 0; i < xtalDirs.size(); i++) {
1640      // old versions of xtalopt used xtal.state, so still check for it.
1641      if (!QFile::exists(dataPath + "/" + xtalDirs.at(i)
1642                         + "/structure.state") &&
1643          !QFile::exists(dataPath + "/" + xtalDirs.at(i)
1644                         + "/xtal.state") ) {
1645          xtalDirs.removeAt(i);
1646          i--;
1647      }
1648    }
1649
1650    // Set filePath:
1651    QString newFilePath = dataPath;
1652    QString newFileBase = filename;
1653    newFileBase.remove(newFilePath);
1654    newFileBase.remove("xtalopt.state.old");
1655    newFileBase.remove("xtalopt.state.tmp");
1656    newFileBase.remove("xtalopt.state");
1657
1658    // TODO For some reason, the local view of "this" is not changed
1659    // when the settings are loaded in the following line. The tabs
1660    // are loading the settings and setting the variables in their
1661    // scope, but it isn't changing it here. Caching issue maybe?
1662    m_dialog->readSettings(filename);
1663
1664#ifdef ENABLE_SSH
1665    // Create the SSHManager if running remotely
1666    if (qobject_cast<RemoteQueueInterface*>(m_queueInterface) != 0) {
1667      if (!this->createSSHConnections()) {
1668        error(tr("Could not create ssh connections."));
1669        return false;
1670      }
1671    }
1672#endif // ENABLE_SSH
1673
1674    debug(tr("Resuming XtalOpt session in '%1' (%2) readOnly = %3")
1675          .arg(filename)
1676          .arg((m_optimizer) ? m_optimizer->getIDString()
1677                             : "No set optimizer")
1678          .arg( (readOnly) ? "true" : "false"));
1679
1680    // Xtals
1681    // Initialize progress bar:
1682    m_dialog->updateProgressMaximum(xtalDirs.size());
1683    // If a local queue interface was used, all InProcess structures must be
1684    // Restarted.
1685    bool restartInProcessStructures = false;
1686    bool clearJobIDs = false;
1687    if (qobject_cast<LocalQueueInterface*>(m_queueInterface)) {
1688      restartInProcessStructures = true;
1689      clearJobIDs = true;
1690    }
1691    // Load xtals
1692    Xtal* xtal;
1693    QList<uint> keys = comp.keys();
1694    QList<Structure*> loadedStructures;
1695    QString xtalStateFileName;
1696    for (int i = 0; i < xtalDirs.size(); i++) {
1697      m_dialog->updateProgressLabel(tr("Loading structures(%1 of %2)...")
1698                                    .arg(i+1).arg(xtalDirs.size()));
1699      m_dialog->updateProgressValue(i);
1700
1701      xtalStateFileName = dataPath + "/" + xtalDirs.at(i) + "/structure.state";
1702      debug(tr("Loading structure %1").arg(xtalStateFileName));
1703      // Check if this is an older session that used xtal.state instead.
1704      if ( !QFile::exists(xtalStateFileName) &&
1705           QFile::exists(dataPath + "/" + xtalDirs.at(i) + "/xtal.state") ) {
1706        xtalStateFileName = dataPath + "/" + xtalDirs.at(i) + "/xtal.state";
1707      }
1708
1709      xtal = new Xtal();
1710      QWriteLocker locker (xtal->lock());
1711      xtal->moveToThread(m_tracker->thread());
1712      xtal->setupConnections();
1713      // Add empty atoms to xtal, updateXtal will populate it
1714      for (int j = 0; j < keys.size(); j++) {
1715        unsigned int quantity = comp.value(keys.at(j)).quantity;
1716        for (uint k = 0; k < quantity; k++)
1717          xtal->addAtom();
1718      }
1719      xtal->setFileName(dataPath + "/" + xtalDirs.at(i) + "/");
1720      xtal->readSettings(xtalStateFileName);
1721
1722      // Store current state -- updateXtal will overwrite it.
1723      Xtal::State state = xtal->getStatus();
1724      // Set state from InProcess -> Restart if needed
1725      if (restartInProcessStructures && state == Structure::InProcess) {
1726        state = Structure::Restart;
1727      }
1728      QDateTime endtime = xtal->getOptTimerEnd();
1729
1730      locker.unlock();
1731
1732      if (!m_optimizer->load(xtal)) {
1733        error(tr("Error, no (or not appropriate for %1) xtal data in "
1734                 "%2.\n\nThis could be a result of resuming a structure "
1735                 "that has not yet done any local optimizations. If so, "
1736                 "safely ignore this message.")
1737              .arg(m_optimizer->getIDString())
1738              .arg(xtal->fileName()));
1739        continue;
1740      }
1741
1742      // Reset state
1743      locker.relock();
1744      xtal->setStatus(state);
1745      xtal->setOptTimerEnd(endtime);
1746      if (clearJobIDs) {
1747        xtal->setJobID(0);
1748      }
1749      locker.unlock();
1750      loadedStructures.append(qobject_cast<Structure*>(xtal));
1751    }
1752
1753    m_dialog->updateProgressMinimum(0);
1754    m_dialog->updateProgressValue(0);
1755    m_dialog->updateProgressMaximum(loadedStructures.size());
1756    m_dialog->updateProgressLabel("Sorting and checking structures...");
1757
1758    // Sort Xtals by index values
1759    int curpos = 0;
1760    //dialog->stopProgressUpdate();
1761    //dialog->startProgressUpdate("Sorting xtals...", 0, loadedStructures.size()-1);
1762    for (int i = 0; i < loadedStructures.size(); i++) {
1763      m_dialog->updateProgressValue(i);
1764      for (int j = 0; j < loadedStructures.size(); j++) {
1765        //dialog->updateProgressValue(curpos);
1766        if (loadedStructures.at(j)->getIndex() == i) {
1767          loadedStructures.swap(j, curpos);
1768          curpos++;
1769        }
1770      }
1771    }
1772
1773    m_dialog->updateProgressMinimum(0);
1774    m_dialog->updateProgressValue(0);
1775    m_dialog->updateProgressMaximum(loadedStructures.size());
1776    m_dialog->updateProgressLabel("Updating structure indices...");
1777
1778    // Reassign indices (shouldn't always be necessary, but just in case...)
1779    for (int i = 0; i < loadedStructures.size(); i++) {
1780      m_dialog->updateProgressValue(i);
1781      loadedStructures.at(i)->setIndex(i);
1782    }
1783
1784    m_dialog->updateProgressMinimum(0);
1785    m_dialog->updateProgressValue(0);
1786    m_dialog->updateProgressMaximum(loadedStructures.size());
1787    m_dialog->updateProgressLabel("Preparing GUI and tracker...");
1788
1789    // Reset the local file path information in case the files have moved
1790    filePath = newFilePath;
1791
1792    Structure *s= 0;
1793    for (int i = 0; i < loadedStructures.size(); i++) {
1794      s = loadedStructures.at(i);
1795      m_dialog->updateProgressValue(i);
1796      m_tracker->lockForWrite();
1797      m_tracker->append(s);
1798      m_tracker->unlock();
1799      if (s->getStatus() == Structure::WaitingForOptimization)
1800        m_queue->appendToJobStartTracker(s);
1801    }
1802
1803    m_dialog->updateProgressLabel("Done!");
1804
1805    // Check if user wants to resume the search
1806    if (!readOnly) {
1807      bool resume;
1808      needBoolean(tr("Session '%1' (%2) loaded. Would you like to start "
1809                     "submitting jobs and resume the search? (Answering "
1810                     "\"No\" will enter read-only mode.)")
1811                       .arg(description).arg(filePath),
1812                       &resume);
1813
1814      readOnly = !resume;
1815      qDebug() << "Read only? " << readOnly;
1816    }
1817
1818    return true;
1819  }
1820
1821  void XtalOpt::resetSpacegroups() {
1822    if (isStarting) {
1823      return;
1824    }
1825    QtConcurrent::run(this, &XtalOpt::resetSpacegroups_);
1826  }
1827
1828  void XtalOpt::resetSpacegroups_() {
1829    const QList<Structure*> structures = *(m_tracker->list());
1830    for (QList<Structure*>::const_iterator it = structures.constBegin(),
1831         it_end = structures.constEnd(); it != it_end; ++it)
1832    {
1833      (*it)->lock()->lockForWrite();
1834      qobject_cast<Xtal*>(*it)->findSpaceGroup(tol_spg);
1835      (*it)->lock()->unlock();
1836    }
1837  }
1838
1839  void XtalOpt::resetDuplicates() {
1840    if (isStarting) {
1841      return;
1842    }
1843    QtConcurrent::run(this, &XtalOpt::resetDuplicates_);
1844  }
1845
1846  void XtalOpt::resetDuplicates_() {
1847    const QList<Structure*> *structures = m_tracker->list();
1848    Xtal *xtal = 0;
1849    for (int i = 0; i < structures->size(); i++) {
1850      xtal = qobject_cast<Xtal*>(structures->at(i));
1851      xtal->lock()->lockForWrite();
1852      if (xtal->getStatus() == Xtal::Duplicate)
1853        xtal->setStatus(Xtal::Optimized);
1854      xtal->structureChanged(); // Reset cached comparisons
1855      xtal->lock()->unlock();
1856    }
1857    checkForDuplicates();
1858  }
1859
1860  // Helper struct for the map below
1861  struct dupCheckStruct
1862  {
1863    Xtal *i, *j;
1864    double tol_len, tol_ang;
1865  };
1866
1867  void checkIfDups(dupCheckStruct & st)
1868  {
1869    Xtal *kickXtal, *keepXtal;
1870    st.i->lock()->lockForRead();
1871    st.j->lock()->lockForRead();
1872    if (st.i->compareCoordinates(*st.j, st.tol_len, st.tol_ang)) {
1873      // Mark the newest xtal as a duplicate of the oldest. This keeps the
1874      // lowest-energy plot trace accurate.
1875      if (st.i->getIndex() > st.j->getIndex()) {
1876        kickXtal = st.i;
1877        keepXtal = st.j;
1878      }
1879      else {
1880        kickXtal = st.j;
1881        keepXtal = st.i;
1882      }
1883      kickXtal->lock()->unlock();
1884      kickXtal->lock()->lockForWrite();
1885      kickXtal->setStatus(Xtal::Duplicate);
1886      kickXtal->setDuplicateString(QString("%1x%2")
1887                                   .arg(keepXtal->getGeneration())
1888                                   .arg(keepXtal->getIDNumber()));
1889    }
1890    st.i->lock()->unlock();
1891    st.j->lock()->unlock();
1892  }
1893
1894  void XtalOpt::checkForDuplicates() {
1895    if (isStarting) {
1896      return;
1897    }
1898    QtConcurrent::run(this, &XtalOpt::checkForDuplicates_);
1899  }
1900
1901  void XtalOpt::checkForDuplicates_() {
1902    m_tracker->lockForRead();
1903    const QList<Structure*> *structures = m_tracker->list();
1904    QList<Xtal*> xtals;
1905    Xtal *xtal;
1906    for (int i = 0; i < structures->size(); i++) {
1907      xtal = qobject_cast<Xtal*>(structures->at(i));
1908      xtals.append(xtal);
1909    }
1910    m_tracker->unlock();
1911
1912    // Build helper structs
1913    QList<dupCheckStruct> sts;
1914    dupCheckStruct st;
1915    for (QList<Xtal*>::iterator xi = xtals.begin();
1916         xi != xtals.end(); xi++) {
1917      (*xi)->lock()->lockForRead();
1918      if ((*xi)->getStatus() == Xtal::Duplicate) {
1919        (*xi)->lock()->unlock();
1920        continue;
1921      }
1922
1923      for (QList<Xtal*>::iterator xj = xi + 1;
1924           xj != xtals.end(); xj++) {
1925        (*xj)->lock()->lockForRead();
1926        if ((*xj)->getStatus() == Xtal::Duplicate) {
1927          (*xj)->lock()->unlock();
1928          continue;
1929        }
1930        if (((*xi)->hasChangedSinceDupChecked() ||
1931             (*xj)->hasChangedSinceDupChecked()) &&
1932            // Perform a course enthalpy screening to cut down on number of
1933            // comparisons
1934            fabs((*xi)->getEnthalpy() - (*xj)->getEnthalpy()) < 1.0)
1935        {
1936          st.i = (*xi);
1937          st.j = (*xj);
1938          st.tol_len = this->tol_xcLength;
1939          st.tol_ang = this->tol_xcAngle;
1940          sts.append(st);
1941        }
1942        (*xj)->lock()->unlock();
1943      }
1944      // Nothing else should be setting this, so just update under a
1945      // read lock
1946      (*xi)->setChangedSinceDupChecked(false);
1947      (*xi)->lock()->unlock();
1948    }
1949
1950    QtConcurrent::blockingMap(sts, checkIfDups);
1951
1952    emit refreshAllStructureInfo();
1953  }
1954
1955} // end namespace XtalOpt
Note: See TracBrowser for help on using the browser.