root/src/xtalopt/xtalopt.cpp @ bea169516b1c2a35f2b8a030fa4d731c09e954ec

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

Added XtalOpt::replaceWithOffspring[M]Xtal()

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