root/src/globalsearch/structure.cpp @ 06f244abd607a7c31b6ed64e2a2c378638e727d2

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

Added a preoptimization queue for MolecularXtals?.

  • Property mode set to 100644
Line 
1/**********************************************************************
2  Structure - Wrapper for Avogadro's molecule class
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 <globalsearch/structure.h>
17#include <globalsearch/macros.h>
18
19#include <avogadro/primitive.h>
20#include <avogadro/molecule.h>
21#include <avogadro/atom.h>
22
23#include <openbabel/generic.h>
24#include <openbabel/forcefield.h>
25
26#include <QtCore/QFile>
27#include <QtCore/QDebug>
28#include <QtCore/QTimer>
29#include <QtCore/QRegExp>
30#include <QtCore/QStringList>
31#include <QtCore/QtConcurrentMap>
32
33#define KCAL_PER_MOL_TO_EV 0.043364122
34
35using namespace Avogadro;
36using namespace OpenBabel;
37using namespace Eigen;
38using namespace std;
39
40namespace GlobalSearch {
41
42  Structure::Structure(QObject *parent) :
43    Molecule(parent),
44    m_hasEnthalpy(false),
45    m_updatedSinceDupChecked(true),
46    m_histogramGenerationPending(false),
47    m_hasBestOffspring(false),
48    m_generation(0),
49    m_id(0),
50    m_rank(0),
51    m_jobID(0),
52    m_PV(0),
53    m_optStart(QDateTime()),
54    m_optEnd(QDateTime()),
55    m_index(-1)
56  {
57    m_currentOptStep = 1;
58    setStatus(Empty);
59    resetFailCount();
60  }
61
62  Structure::Structure(const Structure &other) :
63    Molecule(other),
64    m_histogramGenerationPending(false),
65    m_updatedSinceDupChecked(true),
66    m_hasBestOffspring(false),
67    m_generation(0),
68    m_id(0),
69    m_rank(0),
70    m_jobID(0),
71    m_PV(0),
72    m_optStart(QDateTime()),
73    m_optEnd(QDateTime()),
74    m_index(-1)
75  {
76    *this = other;
77  }
78
79  Structure::Structure(const Avogadro::Molecule &other) :
80    Molecule(other),
81    m_histogramGenerationPending(false),
82    m_updatedSinceDupChecked(true),
83    m_hasBestOffspring(false),
84    m_generation(0),
85    m_id(0),
86    m_rank(0),
87    m_jobID(0),
88    m_PV(0),
89    m_optStart(QDateTime()),
90    m_optEnd(QDateTime()),
91    m_index(-1)
92  {
93    *this = other;
94  }
95
96  void Structure::setupConnections()
97  {
98    // List of slots to be called each time the structure changes
99    QList<const char*> slotlist;
100    slotlist.append(SLOT(structureChanged()));
101    for (int i = 0; i < slotlist.size(); i++) {
102      connect(this, SIGNAL(updated()),
103              this, slotlist.at(i),
104              Qt::QueuedConnection);
105      connect(this, SIGNAL(atomAdded(Atom*)),
106              this, slotlist.at(i),
107              Qt::QueuedConnection);
108      connect(this, SIGNAL(atomUpdated(Atom*)),
109              this, slotlist.at(i),
110              Qt::QueuedConnection);
111      connect(this, SIGNAL(atomRemoved(Atom*)),
112              this, slotlist.at(i),
113              Qt::QueuedConnection);
114      connect(this, SIGNAL(bondAdded(Bond*)),
115              this, slotlist.at(i),
116              Qt::QueuedConnection);
117      connect(this, SIGNAL(bondUpdated(Bond*)),
118              this, slotlist.at(i),
119              Qt::QueuedConnection);
120      connect(this, SIGNAL(bondRemoved(Bond*)),
121              this, slotlist.at(i),
122              Qt::QueuedConnection);
123      connect(this, SIGNAL(primitiveAdded(Primitive*)),
124              this, slotlist.at(i),
125              Qt::QueuedConnection);
126      connect(this, SIGNAL(primitiveUpdated(Primitive*)),
127              this, slotlist.at(i),
128              Qt::QueuedConnection);
129      connect(this, SIGNAL(primitiveRemoved(Primitive*)),
130              this, slotlist.at(i),
131              Qt::QueuedConnection);
132    }
133  }
134
135  void Structure::enableAutoHistogramGeneration(bool b)
136  {
137    if (b) {
138      connect(this, SIGNAL(updated()),
139              this, SLOT(requestHistogramGeneration()),
140              Qt::QueuedConnection);
141      connect(this, SIGNAL(atomAdded(Atom*)),
142              this, SLOT(requestHistogramGeneration()),
143              Qt::QueuedConnection);
144      connect(this, SIGNAL(atomUpdated(Atom*)),
145              this, SLOT(requestHistogramGeneration()),
146              Qt::QueuedConnection);
147      connect(this, SIGNAL(atomRemoved(Atom*)),
148              this, SLOT(requestHistogramGeneration()),
149              Qt::QueuedConnection);
150      connect(this, SIGNAL(bondAdded(Bond*)),
151              this, SLOT(requestHistogramGeneration()),
152              Qt::QueuedConnection);
153      connect(this, SIGNAL(bondUpdated(Bond*)),
154              this, SLOT(requestHistogramGeneration()),
155              Qt::QueuedConnection);
156      connect(this, SIGNAL(bondRemoved(Bond*)),
157              this, SLOT(requestHistogramGeneration()),
158              Qt::QueuedConnection);
159      connect(this, SIGNAL(primitiveAdded(Primitive*)),
160              this, SLOT(requestHistogramGeneration()),
161              Qt::QueuedConnection);
162      connect(this, SIGNAL(primitiveUpdated(Primitive*)),
163              this, SLOT(requestHistogramGeneration()),
164              Qt::QueuedConnection);
165      connect(this, SIGNAL(primitiveRemoved(Primitive*)),
166              this, SLOT(requestHistogramGeneration()),
167              Qt::QueuedConnection);
168    } else {
169      disconnect(this, SIGNAL(updated()),
170                 this, SLOT(requestHistogramGeneration()));
171      disconnect(this, SIGNAL(atomAdded(Atom*)),
172                 this, SLOT(requestHistogramGeneration()));
173      disconnect(this, SIGNAL(atomUpdated(Atom*)),
174                 this, SLOT(requestHistogramGeneration()));
175      disconnect(this, SIGNAL(atomRemoved(Atom*)),
176                 this, SLOT(requestHistogramGeneration()));
177      disconnect(this, SIGNAL(bondAdded(Bond*)),
178                 this, SLOT(requestHistogramGeneration()));
179      disconnect(this, SIGNAL(bondUpdated(Bond*)),
180                 this, SLOT(requestHistogramGeneration()));
181      disconnect(this, SIGNAL(bondRemoved(Bond*)),
182                 this, SLOT(requestHistogramGeneration()));
183      disconnect(this, SIGNAL(primitiveAdded(Primitive*)),
184                 this, SLOT(requestHistogramGeneration()));
185      disconnect(this, SIGNAL(primitiveUpdated(Primitive*)),
186                 this, SLOT(requestHistogramGeneration()));
187      disconnect(this, SIGNAL(primitiveRemoved(Primitive*)),
188                 this, SLOT(requestHistogramGeneration()));
189    }
190  }
191
192  Structure::~Structure()
193  {
194  }
195
196  Structure& Structure::operator=(const Structure& other)
197  {
198    this->copyStructure(other);
199
200    // Set properties
201    m_histogramGenerationPending = other.m_histogramGenerationPending;
202    m_hasEnthalpy                = other.m_hasEnthalpy;
203    m_generation                 = other.m_generation;
204    m_id                         = other.m_id;
205    m_rank                       = other.m_rank;
206    m_jobID                      = other.m_jobID;
207    m_currentOptStep             = other.m_currentOptStep;
208    m_failCount                  = other.m_failCount;
209    m_parents                    = other.m_parents;
210    m_dupString                  = other.m_dupString;
211    m_rempath                    = other.m_rempath;
212    m_enthalpy                   = other.m_enthalpy;
213    m_PV                         = other.m_PV;
214    m_status                     = other.m_status;
215    m_optStart                   = other.m_optStart;
216    m_optEnd                     = other.m_optEnd;
217    m_index                      = other.m_index;
218
219    return *this;
220  }
221
222  Structure& Structure::operator=(const Avogadro::Molecule &mol)
223  {
224    this->copyStructure(mol);
225    return *this;
226  }
227
228  Structure& Structure::copyStructure(const Structure &other)
229  {
230    Molecule::operator=(other);
231
232    if (other.OBUnitCell()) {
233      OpenBabel::OBUnitCell *cell = new OpenBabel::OBUnitCell(*other.OBUnitCell());
234      setOBUnitCell(cell);
235    }
236
237    return *this;
238  }
239
240  void Structure::writeStructureSettings(const QString &filename)
241  {
242    SETTINGS(filename);
243    const int VERSION = 2;
244    settings->beginGroup("structure");
245    settings->setValue("version",     VERSION);
246    settings->setValue("generation", getGeneration());
247    settings->setValue("id", getIDNumber());
248    settings->setValue("index", getIndex());
249    settings->setValue("rank", getRank());
250    settings->setValue("jobID", getJobID());
251    settings->setValue("currentOptStep", getCurrentOptStep());
252    settings->setValue("parents", getParents());
253    settings->setValue("rempath", getRempath());
254    settings->setValue("status", int(getStatus()));
255    settings->setValue("failCount", getFailCount());
256    settings->setValue("startTime", getOptTimerStart().toString());
257    settings->setValue("endTime", getOptTimerEnd().toString());
258    settings->setValue("needsPreoptimization", needsPreoptimization());
259    settings->setValue("hasBestOffspring", hasBestOffspring());
260
261    // History
262    settings->beginGroup("history");
263    //  Atomic nums
264    settings->beginWriteArray("atomicNums");
265    for (int i = 0; i < m_histAtomicNums.size(); i++) {
266      settings->setArrayIndex(i);
267      const QList<unsigned int> *ptr = &(m_histAtomicNums.at(i));
268      settings->beginWriteArray(QString("atomicNums-%1").arg(i));
269      for (int j = 0; j < ptr->size(); j++) {
270        settings->setArrayIndex(j);
271        settings->setValue("value", ptr->at(j));
272      }
273      settings->endArray();
274    }
275    settings->endArray();
276
277    //  Coords
278    settings->beginWriteArray("coords");
279    for (int i = 0; i < m_histCoords.size(); i++) {
280      settings->setArrayIndex(i);
281      const QList<Eigen::Vector3d> *ptr = &(m_histCoords.at(i));
282      settings->beginWriteArray(QString("coords-%1").arg(i));
283      for (int j = 0; j < ptr->size(); j++) {
284        settings->setArrayIndex(j);
285        settings->setValue("x", ptr->at(j).x());
286        settings->setValue("y", ptr->at(j).y());
287        settings->setValue("z", ptr->at(j).z());
288      }
289      settings->endArray();
290    }
291    settings->endArray();
292
293    //  Energies
294    settings->beginWriteArray("energies");
295    for (int i = 0; i < m_histEnergies.size(); i++) {
296      settings->setArrayIndex(i);
297      settings->setValue("value", m_histEnergies.at(i));
298    }
299    settings->endArray();
300
301    //  Enthalpies
302    settings->beginWriteArray("enthalpies");
303    for (int i = 0; i < m_histEnthalpies.size(); i++) {
304      settings->setArrayIndex(i);
305      settings->setValue("value", m_histEnthalpies.at(i));
306    }
307    settings->endArray();
308
309    //  Cells
310    settings->beginWriteArray("cells");
311    for (int i = 0; i < m_histCells.size(); i++) {
312      settings->setArrayIndex(i);
313      const Eigen::Matrix3d *ptr = &m_histCells.at(i);
314      settings->setValue("00", (*ptr)(0, 0));
315      settings->setValue("01", (*ptr)(0, 1));
316      settings->setValue("02", (*ptr)(0, 2));
317      settings->setValue("10", (*ptr)(1, 0));
318      settings->setValue("11", (*ptr)(1, 1));
319      settings->setValue("12", (*ptr)(1, 2));
320      settings->setValue("20", (*ptr)(2, 0));
321      settings->setValue("21", (*ptr)(2, 1));
322      settings->setValue("22", (*ptr)(2, 2));
323    }
324    settings->endArray();
325
326    settings->endGroup(); // history
327
328    // Optimizer index lookup table
329    settings->beginWriteArray("optimizerLookup");
330    QList<int> optKeys = m_optimizerLookup.keys();
331    for (int i = 0; i < optKeys.size(); ++i) {
332      settings->setArrayIndex(i);
333      settings->setValue("key", optKeys.at(i));
334      settings->setValue("value", m_optimizerLookup.value(optKeys.at(i)));
335    }
336    settings->endArray();
337
338    settings->endGroup(); // structure
339    DESTROY_SETTINGS(filename);
340  }
341
342  void Structure::readStructureSettings(const QString &filename)
343  {
344    SETTINGS(filename);
345    settings->beginGroup("structure");
346    int loadedVersion = settings->value("version", 0).toInt();
347    if (loadedVersion >= 1) { // Version 0 uses save(QTextStream)
348      setGeneration(     settings->value("generation",     0).toInt());
349      setIDNumber(       settings->value("id",             0).toInt());
350      setIndex(          settings->value("index",          0).toInt());
351      setRank(           settings->value("rank",           0).toInt());
352      setJobID(          settings->value("jobID",          0).toInt());
353      setCurrentOptStep( settings->value("currentOptStep", 0).toInt());
354      setFailCount(      settings->value("failCount",      0).toInt());
355      setParents(        settings->value("parents",        "").toString());
356      setRempath(        settings->value("rempath",        "").toString());
357      setStatus(   State(settings->value("status",         -1).toInt()));
358
359      setOptTimerStart( QDateTime::fromString(settings->value("startTime", "").toString()));
360      setOptTimerEnd(   QDateTime::fromString(settings->value("endTime",   "").toString()));
361
362      setNeedsPreoptimization(settings->value("needsPreoptimization", true).toBool());
363      setHasBestOffspring(settings->value("hasBestOffspring", false).toBool());
364
365      // History
366      settings->beginGroup("history");
367      //  Atomic nums
368      int size, size2;
369      size = settings->beginReadArray("atomicNums");
370      m_histAtomicNums.clear();
371      for (int i = 0; i < size; i++) {
372        settings->setArrayIndex(i);
373        size2 = settings->beginReadArray(QString("atomicNums-%1").arg(i));
374        QList<unsigned int> cur;
375        for (int j = 0; j < size2; j++) {
376          settings->setArrayIndex(j);
377          cur.append(settings->value("value").toUInt());
378        }
379        settings->endArray();
380        m_histAtomicNums.append(cur);
381      }
382      settings->endArray();
383
384      //  Coords
385      size = settings->beginReadArray("coords");
386      m_histCoords.clear();
387      for (int i = 0; i < size; i++) {
388        settings->setArrayIndex(i);
389        size2 = settings->beginReadArray(QString("coords-%1").arg(i));
390        QList<Eigen::Vector3d> cur;
391        for (int j = 0; j < size2; j++) {
392          settings->setArrayIndex(j);
393          double x = settings->value("x").toDouble();
394          double y = settings->value("y").toDouble();
395          double z = settings->value("z").toDouble();
396          cur.append(Eigen::Vector3d(x, y, z));
397        }
398        settings->endArray();
399        m_histCoords.append(cur);
400      }
401      settings->endArray();
402
403      //  Energies
404      size = settings->beginReadArray("energies");
405      m_histEnergies.clear();
406      for (int i = 0; i < size; i++) {
407        settings->setArrayIndex(i);
408        m_histEnergies.append(settings->value("value").toDouble());
409      }
410      settings->endArray();
411
412      //  Enthalpies
413      size = settings->beginReadArray("enthalpies");
414      m_histEnthalpies.clear();
415      for (int i = 0; i < size; i++) {
416        settings->setArrayIndex(i);
417        m_histEnthalpies.append(settings->value("value").toDouble());
418      }
419      settings->endArray();
420
421      //  Cells
422      size = settings->beginReadArray("cells");
423      m_histCells.clear();
424      for (int i = 0; i < size; i++) {
425        settings->setArrayIndex(i);
426        Eigen::Matrix3d cur;
427        cur(0, 0) = settings->value("00").toDouble();
428        cur(0, 1) = settings->value("01").toDouble();
429        cur(0, 2) = settings->value("02").toDouble();
430        cur(1, 0) = settings->value("10").toDouble();
431        cur(1, 1) = settings->value("11").toDouble();
432        cur(1, 2) = settings->value("12").toDouble();
433        cur(2, 0) = settings->value("20").toDouble();
434        cur(2, 1) = settings->value("21").toDouble();
435        cur(2, 2) = settings->value("22").toDouble();
436        m_histCells.append(cur);
437      }
438      settings->endArray();
439
440      settings->endGroup(); // history
441
442      // Optimizer index lookup table
443      size = settings->beginReadArray("optimizerLookup");
444      m_optimizerLookup.clear();
445      for (int i = 0; i < size; ++i) {
446        settings->setArrayIndex(i);
447        int key = settings->value("key", -1).toInt();
448        int value = settings->value("value", -1).toInt();
449        if (key < 0 || value < 0) {
450          qDebug() << "Missing key (" << key << ") value (" << value << ")"
451                      "pair for structure" << this->getIDString();
452          continue;
453        }
454        m_optimizerLookup.insert(key, value);
455      }
456      settings->endArray();
457
458    } // end if version >= 1
459    settings->endGroup();
460
461    // Update config data
462    switch (loadedVersion) {
463    case 0: {
464      // Call load(QTextStream) to update
465      qDebug() << "Updating "
466               << filename
467               << " from Version 0 -> 1";
468      QFile file (filename);
469      file.open(QIODevice::ReadOnly);
470      QTextStream stream (&file);
471      load(stream);
472    }
473    case 1: // Histories added. Nothing to do, structures loaded will
474            // have empty histories
475    case 2:
476    default:
477      break;
478    }
479  }
480
481  void Structure::structureChanged()
482  {
483    m_updatedSinceDupChecked = true;
484  }
485
486  void Structure::updateAndSkipHistory(const QList<unsigned int> &atomicNums,
487                                       const QList<Eigen::Vector3d> &coords,
488                                       const double energy,
489                                       const double enthalpy,
490                                       const Eigen::Matrix3d &cell)
491  {
492    Q_ASSERT_X(atomicNums.size() == coords.size() && coords.size() == numAtoms(),
493               Q_FUNC_INFO,
494               "Lengths of atomicNums and coords must match numAtoms().");
495
496    // Update atoms
497    Atom *atm;
498    bool useLUT = !m_optimizerLookup.isEmpty();
499    for (int optIndex = 0; optIndex < numAtoms(); ++optIndex) {
500      int atomIndex = optIndex;
501      if (useLUT) {
502        atomIndex = m_optimizerLookup.value(optIndex);
503      }
504      atm = atom(atomIndex);
505      atm->setAtomicNumber(atomicNums.at(optIndex));
506      atm->setPos(coords.at(optIndex));
507    }
508
509    // Update energy/enthalpy
510    if (fabs(enthalpy) < 1e-6) {
511      m_hasEnthalpy = false;
512      m_PV = 0.0;
513    }
514    else {
515      m_hasEnthalpy = true;
516      m_PV = enthalpy - energy;
517    }
518    m_enthalpy = enthalpy;
519    setEnergy(energy * EV_TO_KJ_PER_MOL);
520
521    // Update cell if necessary
522    if (!cell.isZero()) {
523      OpenBabel::matrix3x3 obmat;
524      for (int i = 0; i < 3; i++) {
525        for (int j = 0; j < 3; j++) {
526          obmat.Set(i,j,cell(i,j));
527        }
528      }
529      OpenBabel::OBUnitCell *obcell = OBUnitCell();
530      obcell->SetData(obmat);
531      setOBUnitCell(obcell);
532    }
533
534    emit moleculeChanged();
535    update();
536  }
537
538  void Structure::updateAndAddToHistory(const QList<unsigned int> &atomicNums,
539                                        const QList<Eigen::Vector3d> &coords,
540                                        const double energy,
541                                        const double enthalpy,
542                                        const Eigen::Matrix3d &cell)
543  {
544    Q_ASSERT_X(atomicNums.size() == coords.size() && coords.size() == numAtoms(),
545               Q_FUNC_INFO,
546               "Lengths of atomicNums and coords must match numAtoms().");
547
548    // Update history
549    m_histAtomicNums.append(atomicNums);
550    m_histCoords.append(coords);
551    m_histEnergies.append(energy);
552    m_histEnthalpies.append(enthalpy);
553    m_histCells.append(cell);
554
555    // Update atoms
556    Atom *atm;
557    bool useLUT = !m_optimizerLookup.isEmpty();
558    for (int optIndex = 0; optIndex < numAtoms(); ++optIndex) {
559      int atomIndex = optIndex;
560      if (useLUT) {
561        atomIndex = m_optimizerLookup.value(optIndex);
562      }
563      atm = atom(atomIndex);
564      atm->setAtomicNumber(atomicNums.at(optIndex));
565      atm->setPos(coords.at(optIndex));
566    }
567
568    // Update energy/enthalpy
569    if (fabs(enthalpy) < 1e-6) {
570      m_hasEnthalpy = false;
571      m_PV = 0.0;
572    }
573    else {
574      m_hasEnthalpy = true;
575      m_PV = enthalpy - energy;
576    }
577    m_enthalpy = enthalpy;
578    setEnergy(energy * EV_TO_KJ_PER_MOL);
579
580    // Update cell if necessary
581    if (!cell.isZero()) {
582      OpenBabel::matrix3x3 obmat;
583      for (int i = 0; i < 3; i++) {
584        for (int j = 0; j < 3; j++) {
585          obmat.Set(i,j,cell(i,j));
586        }
587      }
588      OpenBabel::OBUnitCell *obcell = OBUnitCell();
589      obcell->SetData(obmat);
590      setOBUnitCell(obcell);
591    }
592
593    emit moleculeChanged();
594    update();
595  }
596
597  void Structure::deleteFromHistory(unsigned int index) {
598    Q_ASSERT_X(index <= sizeOfHistory() - 1, Q_FUNC_INFO,
599               "Requested history index greater than the number of available entries.");
600
601    m_histAtomicNums.removeAt(index);
602    m_histEnthalpies.removeAt(index);
603    m_histEnergies.removeAt(index);
604    m_histCoords.removeAt(index);
605    m_histCells.removeAt(index);
606  }
607
608  void Structure::retrieveHistoryEntry(unsigned int index,
609                                       QList<unsigned int> *atomicNums,
610                                       QList<Eigen::Vector3d> *coords,
611                                       double *energy,
612                                       double *enthalpy,
613                                       Eigen::Matrix3d *cell)
614  {
615    Q_ASSERT_X(index <= sizeOfHistory() - 1, Q_FUNC_INFO,
616               "Requested history index greater than the number of available entries.");
617
618    if (atomicNums != NULL) {
619      *atomicNums = m_histAtomicNums.at(index);
620    }
621    if (coords != NULL) {
622      *coords = m_histCoords.at(index);
623    }
624    if (energy != NULL) {
625      *energy = m_histEnergies.at(index);
626    }
627    if (enthalpy != NULL) {
628      *enthalpy = m_histEnthalpies.at(index);
629    }
630    if (cell != NULL) {
631      *cell = m_histCells.at(index);
632    }
633
634  }
635
636  bool Structure::addAtomRandomly(uint atomicNumber, double minIAD, double maxIAD, int maxAttempts, Atom **atom)
637  {
638    INIT_RANDOM_GENERATOR();
639    double IAD = -1;
640    int i = 0;
641    vector3 coords;
642
643    // For first atom, add to 0, 0, 0
644    if (numAtoms() == 0) {
645      coords = vector3 (0,0,0);
646    }
647    else {
648      do {
649        // Generate random coordinates
650        IAD = -1;
651        double x = RANDDOUBLE() * radius() + maxIAD;
652        double y = RANDDOUBLE() * radius() + maxIAD;
653        double z = RANDDOUBLE() * radius() + maxIAD;
654
655        coords.Set(x,y,z);
656        if (minIAD != -1) {
657          getNearestNeighborDistance(x, y, z, IAD);
658        }
659        else { break;};
660        i++;
661      } while (i < maxAttempts && IAD <= minIAD);
662
663      if (i >= maxAttempts) return false;
664    }
665
666    Atom *atm = addAtom();
667    atom = &atm;
668    Eigen::Vector3d pos (coords[0],coords[1],coords[2]);
669    (*atom)->setPos(pos);
670    (*atom)->setAtomicNumber(static_cast<int>(atomicNumber));
671    return true;
672  }
673
674  void Structure::setOBEnergy(const QString &ff) {
675    OBForceField* pFF = OBForceField::FindForceField(ff.toStdString().c_str());
676    if (!pFF) return;
677    OpenBabel::OBMol obmol = OBMol();
678    if (!pFF->Setup(obmol)) {
679      qWarning() << "Structure::setOBEnergy: could not setup force field " << ff << ".";
680      return;
681    }
682    std::vector<double> E;
683    E.push_back(pFF->Energy());
684    setEnergies(E);
685  }
686
687  QString Structure::getResultsEntry() const
688  {
689    QString status;
690    switch (getStatus()) {
691    case Optimized:
692      status = "Optimized";
693      break;
694    case Killed:
695    case Removed:
696      status = "Killed";
697      break;
698    case Duplicate:
699      status = "Duplicate";
700      break;
701    case Error:
702      status = "Error";
703      break;
704    case StepOptimized:
705    case WaitingForOptimization:
706    case InProcess:
707    case Empty:
708    case Updating:
709    case Submitted:
710    case Restart:
711      status = "In progress";
712      break;
713    case Preoptimizing:
714      status = "Preoptimizing";
715      break;
716    default:
717      status = "Unknown";
718      break;
719    }
720    return QString("%1 %2 %3 %4 %5")
721      .arg(getRank(), 6)
722      .arg(getGeneration(), 6)
723      .arg(getIDNumber(), 6)
724      .arg(getEnthalpy(), 10)
725      .arg(status, 11);
726  };
727
728  bool Structure::getNearestNeighborDistances(QList<double> * list) const
729  {
730    list->clear();
731    QList<Atom *> atomList = atoms();
732    if (atomList.size() < 2) return false;
733
734#if QT_VERSION >= 0x040700
735    list->reserve(atomList.size());
736#endif // QT_VERSION
737
738    double shortest;
739    for (QList<Atom*>::const_iterator it = atomList.constBegin(),
740         it_end = atomList.constEnd(); it != it_end; ++it) {
741      getNearestNeighborDistance((*it), shortest);
742      list->append(shortest);
743    }
744    return true;
745  }
746
747  bool Structure::getShortestInteratomicDistance(double & shortest) const
748  {
749    QList<Atom*> atomList = atoms();
750    if (atomList.size() <= 1) return false; // Need at least two atoms!
751    QList<Vector3d> atomPositions;
752    for (int i = 0; i < atomList.size(); i++)
753      atomPositions.push_back(*(atomList.at(i)->pos()));
754
755    Vector3d v1= atomPositions.at(0);
756    Vector3d v2= atomPositions.at(1);
757    shortest = abs((v1-v2).norm());
758    double distance;
759
760    // Find shortest distance
761    for (int i = 0; i < atomList.size(); i++) {
762      v1 = atomPositions.at(i);
763      for (int j = i+1; j < atomList.size(); j++) {
764        v2 = atomPositions.at(j);
765        // Intercell
766        distance = abs((v1-v2).norm());
767        if (distance < shortest) shortest = distance;
768      }
769    }
770    return true;
771  }
772
773  bool Structure::getNearestNeighborDistance(const double x,
774                                             const double y,
775                                             const double z,
776                                             double & shortest) const
777  {
778    QList<Atom*> atomList = atoms();
779    if (atomList.size() < 2) return false; // Need at least two atoms!
780    QList<Vector3d> atomPositions;
781    for (int i = 0; i < atomList.size(); i++)
782      atomPositions.push_back(*(atomList.at(i)->pos()));
783
784    Vector3d v1 (x, y, z);
785    Vector3d v2 = atomPositions.at(0);
786    shortest = fabs((v1-v2).norm());
787    double distance;
788
789    // Find shortest distance
790    for (int j = 0; j < atomList.size(); j++) {
791      v2 = atomPositions.at(j);
792      // Intercell
793      distance = abs((v1-v2).norm());
794      if (distance < shortest) shortest = distance;
795    }
796    return true;
797  }
798
799  bool Structure::getNearestNeighborDistance(const Avogadro::Atom *atom,
800                                             double & shortest) const
801  {
802    const Eigen::Vector3d *position = atom->pos();
803    return getNearestNeighborDistance(position->x(),
804                                      position->y(),
805                                      position->z(), shortest);
806  }
807
808  QList<Atom*> Structure::getNeighbors(const double x,
809                                       const double y,
810                                       const double z,
811                                       const double cutoff,
812                                       QList<double> *distances) const
813  {
814    QList<Atom*> neighbors;
815    if (distances) {
816      distances->clear();
817    }
818    Eigen::Vector3d vec (x,y,z);
819    double cutoffSquared = cutoff*cutoff;
820    for (QList<Atom*>::const_iterator it = m_atomList.constBegin(),
821           it_end = m_atomList.constEnd(); it != it_end; ++it) {
822      double distSq = ((*(*it)->pos()) - vec).squaredNorm();
823      if (distSq <= cutoffSquared) {
824        neighbors << *it;
825        if (distances) {
826          *distances << sqrt(distSq);
827        }
828      }
829    }
830    return neighbors;
831  }
832
833  QList<Atom*> Structure::getNeighbors(const Avogadro::Atom *atom,
834                                       const double cutoff,
835                                       QList<double> *distances) const
836  {
837    const Eigen::Vector3d *position = atom->pos();
838    return getNeighbors(position->x(),
839                        position->y(),
840                        position->z(), cutoff, distances);
841  }
842
843  void Structure::requestHistogramGeneration()
844  {
845    if (!m_histogramGenerationPending) {
846      m_histogramGenerationPending = true;
847      // Wait 250 ms before requesting to limit number of requests
848      QTimer::singleShot(250, this, SLOT(generateDefaultHistogram()));
849    }
850  }
851
852  void Structure::generateDefaultHistogram()
853  {
854    generateIADHistogram(&m_histogramDist, &m_histogramFreq, 0, 10, 0.01);
855    m_histogramGenerationPending = false;
856  }
857
858  void Structure::getDefaultHistogram(QList<double> *dist, QList<double> *freq) const
859  {
860    dist->clear();
861    freq->clear();
862    for (int i = 0; i < m_histogramDist.size(); i++) {
863      dist->append(m_histogramDist.at(i).toDouble());
864      freq->append(m_histogramFreq.at(i).toDouble());
865    }
866  }
867
868  void Structure::getDefaultHistogram(QList<QVariant> *dist, QList<QVariant> *freq) const
869  {
870    (*dist) = m_histogramDist;
871    (*freq) = m_histogramFreq;
872  }
873
874  bool Structure::generateIADHistogram(QList<double> * dist,
875                                       QList<double> * freq,
876                                       double min,
877                                       double max,
878                                       double step,
879                                       Avogadro::Atom *atom) const
880  {
881    QList<QVariant> distv, freqv;
882    if (!generateIADHistogram(&distv, &freqv, min, max, step, atom)) {
883      return false;
884    }
885    dist->clear();
886    freq->clear();
887    for (int i = 0; i < distv.size(); i++) {
888      dist->append(distv.at(i).toDouble());
889      freq->append(freqv.at(i).toDouble());
890    }
891    return true;
892  }
893
894  // Helper functions and structs for the histogram generator
895  // Skip doxygenation:
896  /// \cond
897  struct NNHistMap {
898    int i;
899    double halfstep;
900    QList<Vector3d> *atomPositions;
901    QList<QVariant> *dist;
902  };
903
904  // End doxygenation skip:
905  /// \endcond
906
907  // Returns the frequencies for this chunk
908  QList<int> calcNNHistChunk(const NNHistMap &m)
909  {
910    const Vector3d *v1 = &(m.atomPositions->at(m.i));
911    const Vector3d *v2;
912    QList<int> freq;
913    double diff;
914    for (int ind = 0; ind < m.dist->size(); ind++) {
915      freq.append(0);
916    }
917    for (int j = m.i+1; j < m.atomPositions->size(); j++) {
918      v2 = &(m.atomPositions->at(j));
919      diff = fabs(((*v1)-(*v2)).norm());
920      for (int k = 0; k < m.dist->size(); k++) {
921        if (fabs(diff-(m.dist->at(k).toDouble())) < m.halfstep) {
922          freq[k]++;
923        }
924      }
925    }
926    return freq;
927  }
928
929  void reduceNNHistChunks(QList<QVariant> &final, const QList<int> &tmp)
930  {
931    if (final.size() != tmp.size()) {
932      final.clear();
933      for (int i = 0; i < tmp.size(); i++) {
934        final.append(tmp.at(i));
935      }
936    }
937    else {
938      double d;
939      for (int i = 0; i < final.size(); i++) {
940        d = final.at(i).toDouble();
941        d += tmp.at(i);
942        final.replace(i, d);
943      }
944    }
945    return;
946  }
947
948  bool Structure::generateIADHistogram(QList<QVariant> * distance,
949                                       QList<QVariant> * frequency,
950                                       double min, double max, double step,
951                                       Avogadro::Atom *atom) const
952  {
953    distance->clear();
954    frequency->clear();
955
956    if (min > max && step > 0) {
957      qWarning() << "Structure::getNearestNeighborHistogram: min cannot be greater than max!";
958      return false;
959    }
960    if (step <= 0) {
961      qWarning() << "Structure::getNearestNeighborHistogram: invalid step size:" << step;
962      return false;
963    }
964
965    double halfstep = step / 2.0;
966
967    double val = min;
968    do {
969      distance->append(val);
970      frequency->append(0);
971      val += step;
972    } while (val < max);
973
974    QList<Atom*> atomList = atoms();
975    QList<Vector3d> atomPositions;
976    for (int i = 0; i < atomList.size(); i++)
977      atomPositions.push_back(*(atomList.at(i)->pos()));
978
979    Vector3d v1= atomPositions.at(0);
980    Vector3d v2= atomPositions.at(1);
981    double diff;
982
983    // build histogram
984    // Loop over all atoms -- use map-reduce
985    if (atom == 0) {
986      QList<NNHistMap> ml;
987      for (int i = 0; i < atomList.size(); i++) {
988        NNHistMap m;
989        m.i = i; m.halfstep = halfstep; m.atomPositions = &atomPositions; m.dist = distance;
990        ml.append(m);
991      }
992      (*frequency) = QtConcurrent::blockingMappedReduced(ml, calcNNHistChunk, reduceNNHistChunks);
993    }
994    // Or, just the one requested
995    else {
996      v1 = *atom->pos();
997      for (int j = 0; j < atomList.size(); j++) {
998        if (atomList.at(j) == atom) continue;
999        v2 = atomPositions.at(j);
1000        // Intercell
1001        diff = fabs((v1-v2).norm());
1002        for (int k = 0; k < distance->size(); k++) {
1003          double radius = distance->at(k).toDouble();
1004          double d;
1005          if (diff != 0 && fabs(diff-radius) < halfstep) {
1006            d = frequency->at(k).toDouble();
1007            d++;
1008            frequency->replace(k, d);
1009          }
1010        }
1011      }
1012    }
1013
1014    return true;
1015  }
1016
1017  bool Structure::compareIADDistributions(const std::vector<double> &d,
1018                                          const std::vector<double> &f1,
1019                                          const std::vector<double> &f2,
1020                                          double decay,
1021                                          double smear,
1022                                          double *error)
1023  {
1024    // Check that smearing is possible
1025    if (smear != 0 && d.size() <= 1) {
1026      qWarning() << "Cluster::compareNNDist: Cannot smear with 1 or fewer points.";
1027      return false;
1028    }
1029    // Check sizes
1030    if (d.size() != f1.size() || f1.size() != f2.size()) {
1031      qWarning() << "Cluster::compareNNDist: Vectors are not the same size.";
1032      return false;
1033    }
1034
1035    // Perform a boxcar smoothing over range set by "smear"
1036    // First determine step size of d, then convert smear to index units
1037    double stepSize = fabs(d.at(1) - d.at(0));
1038    int boxSize = ceil(smear/stepSize);
1039    if (boxSize > d.size()) {
1040      qWarning() << "Cluster::compareNNDist: Smear length is greater then d vector range.";
1041      return false;
1042    }
1043    // Smear
1044    vector<double> f1s, f2s, ds; // smeared vectors
1045    if (smear != 0) {
1046      double f1t, f2t, dt; // temporary variables
1047      for (int i = 0; i < d.size() - boxSize; i++) {
1048        f1t = f2t = dt = 0;
1049        for (int j = 0; j < boxSize; j++) {
1050          f1t += f1.at(i+j);
1051          f2t += f2.at(i+j);
1052        }
1053        f1s.push_back(f1t / double(boxSize));
1054        f2s.push_back(f2t / double(boxSize));
1055        ds.push_back(dt / double(boxSize));
1056      }
1057    } else {
1058      for (int i = 0; i < d.size() - boxSize; i++) {
1059        f1s.push_back(f1.at(i));
1060        f2s.push_back(f2.at(i));
1061        ds.push_back(d.at(i));
1062      }
1063    }
1064
1065    // Calculate diff vector
1066    vector<double> diff;
1067    for (int i = 0; i < ds.size(); i++) {
1068      diff.push_back(fabs(f1s.at(i) - f2s.at(i)));
1069    }
1070
1071    // Calculate decay function: Standard exponential decay with a
1072    // halflife of decay. If decay==0, no decay.
1073    double decayFactor = 0;
1074    // ln(2) / decay:
1075    if (decay != 0) {
1076      decayFactor = 0.69314718055994530941723 / decay;
1077    }
1078
1079    // Calculate error:
1080    (*error) = 0;
1081    for (int i = 0; i < ds.size(); i++) {
1082      (*error) += exp(-decayFactor * ds.at(i)) * diff.at(i);
1083    }
1084
1085    return true;
1086  }
1087
1088  bool Structure::compareIADDistributions(const QList<double> &d,
1089                                          const QList<double> &f1,
1090                                          const QList<double> &f2,
1091                                          double decay,
1092                                          double smear,
1093                                          double *error)
1094  {
1095    // Check sizes
1096    if (d.size() != f1.size() || f1.size() != f2.size()) {
1097      qWarning() << "Cluster::compareIADDist: Vectors are not the same size.";
1098      return false;
1099    }
1100    vector<double> dd, f1d, f2d;
1101    for (int i = 0; i < d.size(); i++) {
1102      dd.push_back(d.at(i));
1103      f1d.push_back(f1.at(i));
1104      f2d.push_back(f2.at(i));
1105    }
1106    return compareIADDistributions(dd, f1d, f2d, decay, smear, error);
1107  }
1108
1109  bool Structure::compareIADDistributions(const QList<QVariant> &d,
1110                                          const QList<QVariant> &f1,
1111                                          const QList<QVariant> &f2,
1112                                          double decay,
1113                                          double smear,
1114                                          double *error)
1115  {
1116    // Check sizes
1117    if (d.size() != f1.size() || f1.size() != f2.size()) {
1118      qWarning() << "Cluster::compareIADDist: Vectors are not the same size.";
1119      return false;
1120    }
1121    vector<double> dd, f1d, f2d;
1122    for (int i = 0; i < d.size(); i++) {
1123      dd.push_back(d.at(i).toDouble());
1124      f1d.push_back(f1.at(i).toDouble());
1125      f2d.push_back(f2.at(i).toDouble());
1126    }
1127    return compareIADDistributions(dd, f1d, f2d, decay, smear, error);
1128  }
1129
1130  QList<QString> Structure::getSymbols() const {
1131    QList<QString> list;
1132    for (QList<Atom*>::const_iterator
1133           it = m_atomList.begin(),
1134           it_end = m_atomList.end();
1135         it != it_end;
1136         ++it) {
1137      QString symbol = QString(OpenBabel::etab.GetSymbol((*it)->atomicNumber()));
1138      if (!list.contains(symbol)) {
1139        list.append(symbol);
1140      }
1141    }
1142    qSort(list);
1143    return list;
1144  }
1145
1146  QList<uint> Structure::getNumberOfAtomsAlpha() const {
1147    QList<uint> list;
1148    QList<QString> symbols = getSymbols();
1149    for (int i = 0; i < symbols.size(); i++)
1150      list.append(0);
1151    int ind;
1152    for (QList<Atom*>::const_iterator
1153           it = m_atomList.begin(),
1154           it_end = m_atomList.end();
1155         it != it_end;
1156         ++it) {
1157      QString symbol = QString(OpenBabel::etab.GetSymbol((*it)->atomicNumber()));
1158      Q_ASSERT_X(symbols.contains(symbol), Q_FUNC_INFO,
1159                 "getNumberOfAtomsAlpha found a symbol not in getSymbols.");
1160      ind = symbols.indexOf(symbol);
1161      ++list[ind];
1162    }
1163    return list;
1164  }
1165
1166  QString Structure::getOptElapsed() const {
1167    int secs;
1168    if (m_optStart.toString() == "") return "0:00:00";
1169    if (m_optEnd.toString() == "")
1170      secs = m_optStart.secsTo(QDateTime::currentDateTime());
1171    else
1172      secs = m_optStart.secsTo(m_optEnd);
1173    int hours   = static_cast<int>(secs/3600);
1174    int mins    = static_cast<int>( (secs - hours * 3600) / 60);
1175    secs        = secs % 60;
1176    QString ret;
1177    ret.sprintf("%d:%02d:%02d", hours, mins, secs);
1178    return ret;
1179  }
1180
1181  void Structure::load(QTextStream &in) {
1182    QString line, str;
1183    QStringList strl;
1184    setStatus(InProcess); // Override later if status is available
1185    in.seek(0);
1186    while (!in.atEnd()) {
1187      line = in.readLine();
1188      strl = line.split(QRegExp(" "));
1189      //qDebug() << strl;
1190
1191      if (line.contains("Generation:") && strl.size() > 1)
1192        setGeneration( (strl.at(1)).toUInt() );
1193      if (line.contains("ID#:") && strl.size() > 1)
1194        setIDNumber( (strl.at(1)).toUInt() );
1195      if (line.contains("Index:") && strl.size() > 1)
1196        setIndex( (strl.at(1)).toUInt() );
1197      if (line.contains("Enthalpy Rank:") && strl.size() > 2)
1198        setRank( (strl.at(2)).toUInt() );
1199      if (line.contains("Job ID:") && strl.size() > 2)
1200        setJobID( (strl.at(2)).toUInt() );
1201      if (line.contains("Current INCAR:") && strl.size() > 2)
1202        setCurrentOptStep( (strl.at(2)).toUInt() );
1203      if (line.contains("Current OptStep:") && strl.size() > 2)
1204        setCurrentOptStep( (strl.at(2)).toUInt() );
1205      if (line.contains("Ancestry:") && strl.size() > 1) {
1206        strl.removeFirst();
1207        setParents( strl.join(" ") );
1208      }
1209      if (line.contains("Remote Path:") && strl.size() > 2) {
1210        strl.removeFirst(); strl.removeFirst();
1211        setRempath( strl.join(" ") );
1212      }
1213      if (line.contains("Start Time:") && strl.size() > 2) {
1214        strl.removeFirst(); strl.removeFirst();
1215        str = strl.join(" ");
1216        setOptTimerStart(QDateTime::fromString(str));
1217      }
1218      if (line.contains("Status:") && strl.size() > 1) {
1219        setStatus( Structure::State((strl.at(1)).toInt() ));
1220      }
1221      if (line.contains("failCount:") && strl.size() > 1) {
1222        setFailCount((strl.at(1)).toUInt());
1223      }
1224      if (line.contains("End Time:") && strl.size() > 2) {
1225        strl.removeFirst(); strl.removeFirst();
1226        str = strl.join(" ");
1227        setOptTimerEnd(QDateTime::fromString(str));
1228      }
1229    }
1230  }
1231
1232  QHash<QString, QVariant> Structure::getFingerprint() const
1233  {
1234    QHash<QString, QVariant> fp;
1235    fp.insert("enthalpy", getEnthalpy());
1236    return fp;
1237  }
1238
1239  void Structure::sortByEnthalpy(QList<Structure*> *structures)
1240  {
1241    uint numStructs = structures->size();
1242
1243    if (numStructs <= 1) return;
1244
1245    // Simple selection sort
1246    Structure *structure_i=0, *structure_j=0, *tmp=0;
1247    for (uint i = 0; i < numStructs-1; i++) {
1248      structure_i = structures->at(i);
1249      structure_i->lock()->lockForRead();
1250      for (uint j = i+1; j < numStructs; j++) {
1251        structure_j = structures->at(j);
1252        structure_j->lock()->lockForRead();
1253        if (structure_j->getEnthalpy() < structure_i->getEnthalpy()) {
1254          structures->swap(i,j);
1255          tmp = structure_i;
1256          structure_i = structure_j;
1257          structure_j = tmp;
1258        }
1259        structure_j->lock()->unlock();
1260      }
1261      structure_i->lock()->unlock();
1262    }
1263  }
1264
1265  void rankInPlace(const QList<Structure*> &structures)
1266  {
1267    if (structures.size() == 0) return;
1268    Structure *s;
1269    for (uint i = 0; i < structures.size(); i++) {
1270      s = structures.at(i);
1271      s->lock()->lockForWrite();
1272      s->setRank(i+1);
1273      s->lock()->unlock();
1274    }
1275  }
1276
1277  void Structure::rankByEnthalpy(const QList<Structure*> &structures)
1278  {
1279    uint numStructs = structures.size();
1280
1281    if (numStructs == 0) return;
1282
1283    QList<Structure*> rstructures;
1284
1285    // Copy structures to a temporary list (don't modify input list!)
1286    for (uint i = 0; i < numStructs; i++)
1287      rstructures.append(structures.at(i));
1288
1289    // Simple selection sort
1290    Structure *structure_i=0, *structure_j=0, *tmp=0;
1291    for (uint i = 0; i < numStructs-1; i++) {
1292      structure_i = rstructures.at(i);
1293      structure_i->lock()->lockForRead();
1294      for (uint j = i+1; j < numStructs; j++) {
1295        structure_j = rstructures.at(j);
1296        structure_j->lock()->lockForRead();
1297        if (structure_j->getEnthalpy() < structure_i->getEnthalpy()) {
1298          rstructures.swap(i,j);
1299          tmp = structure_i;
1300          structure_i = structure_j;
1301          structure_j = tmp;
1302        }
1303        structure_j->lock()->unlock();
1304      }
1305      structure_i->lock()->unlock();
1306    }
1307
1308    rankInPlace(rstructures);
1309  }
1310
1311  void Structure::sortAndRankByEnthalpy(QList<Structure*> *structures)
1312  {
1313    sortByEnthalpy(structures);
1314    rankInPlace(*structures);
1315  }
1316
1317
1318} // end namespace GlobalSearch
Note: See TracBrowser for help on using the browser.