| 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 | |
|---|
| 35 | using namespace Avogadro; |
|---|
| 36 | using namespace OpenBabel; |
|---|
| 37 | using namespace Eigen; |
|---|
| 38 | using namespace std; |
|---|
| 39 | |
|---|
| 40 | namespace 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 |
|---|