| 1 | /********************************************************************** |
|---|
| 2 | OptBase - Base class for global search extensions |
|---|
| 3 | |
|---|
| 4 | Copyright (C) 2010-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/optbase.h> |
|---|
| 17 | |
|---|
| 18 | #include <globalsearch/bt.h> |
|---|
| 19 | #include <globalsearch/macros.h> |
|---|
| 20 | #include <globalsearch/optimizer.h> |
|---|
| 21 | #include <globalsearch/queuemanager.h> |
|---|
| 22 | #include <globalsearch/queueinterface.h> |
|---|
| 23 | #include <globalsearch/queueinterfaces/local.h> |
|---|
| 24 | #include <globalsearch/queueinterfaces/pbs.h> |
|---|
| 25 | #ifdef ENABLE_SSH |
|---|
| 26 | #include <globalsearch/sshmanager.h> |
|---|
| 27 | #ifdef USE_CLI_SSH |
|---|
| 28 | #include <globalsearch/sshmanager_cli.h> |
|---|
| 29 | #else // USE_CLI_SSH |
|---|
| 30 | #include <globalsearch/sshmanager_libssh.h> |
|---|
| 31 | #endif // USE_CLI_SSH |
|---|
| 32 | #endif // ENABLE_SSH |
|---|
| 33 | #include <globalsearch/structure.h> |
|---|
| 34 | #include <globalsearch/ui/abstractdialog.h> |
|---|
| 35 | |
|---|
| 36 | #include <QtCore/QFile> |
|---|
| 37 | #include <QtCore/QThread> |
|---|
| 38 | |
|---|
| 39 | #include <QtGui/QClipboard> |
|---|
| 40 | #include <QtGui/QMessageBox> |
|---|
| 41 | #include <QtGui/QApplication> |
|---|
| 42 | #include <QtGui/QInputDialog> |
|---|
| 43 | |
|---|
| 44 | using namespace OpenBabel; |
|---|
| 45 | |
|---|
| 46 | namespace GlobalSearch { |
|---|
| 47 | |
|---|
| 48 | OptBase::OptBase(AbstractDialog *parent) : |
|---|
| 49 | QObject(parent), |
|---|
| 50 | m_dialog(parent), |
|---|
| 51 | m_tracker(new Tracker (this)), |
|---|
| 52 | m_queueThread(new QThread), |
|---|
| 53 | m_queue(new QueueManager(m_queueThread, this)), |
|---|
| 54 | m_queueInterface(0), // This will be set when the GUI is initialized |
|---|
| 55 | m_optimizer(0), // This will be set when the GUI is initialized |
|---|
| 56 | #ifdef ENABLE_SSH |
|---|
| 57 | m_ssh(NULL), |
|---|
| 58 | #endif // ENABLE_SSH |
|---|
| 59 | m_idString("Generic"), |
|---|
| 60 | sOBMutex(new QMutex), |
|---|
| 61 | stateFileMutex(new QMutex), |
|---|
| 62 | backTraceMutex(new QMutex), |
|---|
| 63 | usePreopt(false), |
|---|
| 64 | savePending(false), |
|---|
| 65 | readOnly(false), |
|---|
| 66 | testingMode(false), |
|---|
| 67 | test_nRunsStart(1), |
|---|
| 68 | test_nRunsEnd(100), |
|---|
| 69 | test_nStructs(600), |
|---|
| 70 | cutoff(-1), |
|---|
| 71 | m_schemaVersion(1) |
|---|
| 72 | { |
|---|
| 73 | // Connections |
|---|
| 74 | connect(this, SIGNAL(sessionStarted()), |
|---|
| 75 | m_queueThread, SLOT(start()), |
|---|
| 76 | Qt::DirectConnection); |
|---|
| 77 | connect(this, SIGNAL(startingSession()), |
|---|
| 78 | m_queueThread, SLOT(start()), |
|---|
| 79 | Qt::DirectConnection); |
|---|
| 80 | connect(this, SIGNAL(startingSession()), |
|---|
| 81 | this, SLOT(setIsStartingTrue()), |
|---|
| 82 | Qt::DirectConnection); |
|---|
| 83 | connect(this, SIGNAL(sessionStarted()), |
|---|
| 84 | this, SLOT(setIsStartingFalse()), |
|---|
| 85 | Qt::DirectConnection); |
|---|
| 86 | connect(this, SIGNAL(readOnlySessionStarted()), |
|---|
| 87 | this, SLOT(setIsStartingFalse()), |
|---|
| 88 | Qt::DirectConnection); |
|---|
| 89 | connect(this, SIGNAL(needBoolean(const QString&, bool*)), |
|---|
| 90 | this, SLOT(promptForBoolean(const QString&, bool*)), |
|---|
| 91 | Qt::BlockingQueuedConnection); // Wait until slot returns |
|---|
| 92 | connect(this, SIGNAL(needPassword(const QString&, QString*, bool*)), |
|---|
| 93 | this, SLOT(promptForPassword(const QString&, QString*, bool*)), |
|---|
| 94 | Qt::BlockingQueuedConnection); // Wait until slot returns |
|---|
| 95 | connect(this, SIGNAL(sig_setClipboard(const QString&)), |
|---|
| 96 | this, SLOT(setClipboard_(const QString&)), |
|---|
| 97 | Qt::QueuedConnection); |
|---|
| 98 | |
|---|
| 99 | INIT_RANDOM_GENERATOR(); |
|---|
| 100 | } |
|---|
| 101 | |
|---|
| 102 | OptBase::~OptBase() |
|---|
| 103 | { |
|---|
| 104 | delete m_queue; |
|---|
| 105 | m_queue = 0; |
|---|
| 106 | |
|---|
| 107 | if (m_queueThread && m_queueThread->isRunning()) { |
|---|
| 108 | m_queueThread->wait(); |
|---|
| 109 | } |
|---|
| 110 | delete m_queueThread; |
|---|
| 111 | m_queueThread = 0; |
|---|
| 112 | |
|---|
| 113 | delete m_optimizer; |
|---|
| 114 | m_optimizer = 0; |
|---|
| 115 | |
|---|
| 116 | delete m_queueInterface; |
|---|
| 117 | m_queueInterface = 0; |
|---|
| 118 | |
|---|
| 119 | delete m_tracker; |
|---|
| 120 | m_tracker = 0; |
|---|
| 121 | } |
|---|
| 122 | |
|---|
| 123 | void OptBase::reset() { |
|---|
| 124 | m_tracker->lockForWrite(); |
|---|
| 125 | m_tracker->deleteAllStructures(); |
|---|
| 126 | m_tracker->reset(); |
|---|
| 127 | m_tracker->unlock(); |
|---|
| 128 | m_queue->reset(); |
|---|
| 129 | } |
|---|
| 130 | |
|---|
| 131 | #ifdef ENABLE_SSH |
|---|
| 132 | bool OptBase::createSSHConnections() |
|---|
| 133 | { |
|---|
| 134 | #ifdef USE_CLI_SSH |
|---|
| 135 | return this->createSSHConnections_cli(); |
|---|
| 136 | #else // USE_CLI_SSH |
|---|
| 137 | return this->createSSHConnections_libssh(); |
|---|
| 138 | #endif // USE_CLI_SSH |
|---|
| 139 | } |
|---|
| 140 | #endif // ENABLE_SSH |
|---|
| 141 | |
|---|
| 142 | void OptBase::printBackTrace() { |
|---|
| 143 | backTraceMutex->lock(); |
|---|
| 144 | QStringList l = getBackTrace(); |
|---|
| 145 | backTraceMutex->unlock(); |
|---|
| 146 | for (int i = 0; i < l.size();i++) |
|---|
| 147 | qDebug() << l.at(i); |
|---|
| 148 | } |
|---|
| 149 | |
|---|
| 150 | QList<double> OptBase::getProbabilityList(const QList<Structure*> &structures) { |
|---|
| 151 | // IMPORTANT: structures must contain one more structure than |
|---|
| 152 | // needed -- the last structure in the list will be removed from |
|---|
| 153 | // the probability list! |
|---|
| 154 | if (structures.size() <= 2) { |
|---|
| 155 | return QList<double>(); |
|---|
| 156 | } |
|---|
| 157 | |
|---|
| 158 | QList<double> probs; |
|---|
| 159 | Structure *s=0, *first=0, *last=0; |
|---|
| 160 | first = structures.first(); |
|---|
| 161 | last = structures.last(); |
|---|
| 162 | first->lock()->lockForRead(); |
|---|
| 163 | last->lock()->lockForRead(); |
|---|
| 164 | double lowest = first->getEnthalpy(); |
|---|
| 165 | double highest = last->getEnthalpy();; |
|---|
| 166 | double spread = highest - lowest; |
|---|
| 167 | last->lock()->unlock(); |
|---|
| 168 | first->lock()->unlock(); |
|---|
| 169 | // If all structures are at the same enthalpy, lets save some time... |
|---|
| 170 | if (spread <= 1e-5) { |
|---|
| 171 | double dprob = 1.0/static_cast<double>(structures.size()-1); |
|---|
| 172 | double prob = 0; |
|---|
| 173 | for (int i = 0; i < structures.size()-1; i++) { |
|---|
| 174 | probs.append(prob); |
|---|
| 175 | prob += dprob; |
|---|
| 176 | } |
|---|
| 177 | return probs; |
|---|
| 178 | } |
|---|
| 179 | // Generate a list of floats from 0->1 proportional to the enthalpies; |
|---|
| 180 | // E.g. if enthalpies are: |
|---|
| 181 | // -5 -2 -1 3 5 |
|---|
| 182 | // We'll have: |
|---|
| 183 | // 0 0.3 0.4 0.8 1 |
|---|
| 184 | for (int i = 0; i < structures.size(); i++) { |
|---|
| 185 | s = structures.at(i); |
|---|
| 186 | s->lock()->lockForRead(); |
|---|
| 187 | probs.append( ( s->getEnthalpy() - lowest ) / spread); |
|---|
| 188 | s->lock()->unlock(); |
|---|
| 189 | } |
|---|
| 190 | // Subtract each value from one, and find the sum of the resulting list |
|---|
| 191 | // We'll end up with: |
|---|
| 192 | // 1 0.7 0.6 0.2 0 -- sum = 2.5 |
|---|
| 193 | double sum = 0; |
|---|
| 194 | for (int i = 0; i < probs.size(); i++){ |
|---|
| 195 | probs[i] = 1.0 - probs.at(i); |
|---|
| 196 | sum += probs.at(i); |
|---|
| 197 | } |
|---|
| 198 | // Normalize with the sum so that the list adds to 1 |
|---|
| 199 | // 0.4 0.28 0.24 0.08 0 |
|---|
| 200 | for (int i = 0; i < probs.size(); i++){ |
|---|
| 201 | probs[i] /= sum; |
|---|
| 202 | } |
|---|
| 203 | // Then replace each entry with a cumulative total: |
|---|
| 204 | // 0.4 0.68 0.92 1 1 |
|---|
| 205 | sum = 0; |
|---|
| 206 | for (int i = 0; i < probs.size(); i++){ |
|---|
| 207 | sum += probs.at(i); |
|---|
| 208 | probs[i] = sum; |
|---|
| 209 | } |
|---|
| 210 | // Pop off the last entry (remember the n_popSize + 1 earlier?) |
|---|
| 211 | // 0.4 0.68 0.92 1 |
|---|
| 212 | probs.removeLast(); |
|---|
| 213 | // And we have a enthalpy weighted probability list! To use: |
|---|
| 214 | // |
|---|
| 215 | // double r = rand.NextFloat(); |
|---|
| 216 | // uint ind; |
|---|
| 217 | // for (ind = 0; ind < probs.size(); ind++) |
|---|
| 218 | // if (r < probs.at(ind)) break; |
|---|
| 219 | // |
|---|
| 220 | // ind will hold the chosen index. |
|---|
| 221 | return probs; |
|---|
| 222 | } |
|---|
| 223 | |
|---|
| 224 | bool OptBase::save(const QString &stateFilename, bool notify) |
|---|
| 225 | { |
|---|
| 226 | if (isStarting || |
|---|
| 227 | readOnly) { |
|---|
| 228 | savePending = false; |
|---|
| 229 | return false; |
|---|
| 230 | } |
|---|
| 231 | QReadLocker trackerLocker (m_tracker->rwLock()); |
|---|
| 232 | QMutexLocker locker (stateFileMutex); |
|---|
| 233 | QString filename; |
|---|
| 234 | if (stateFilename.isEmpty()) { |
|---|
| 235 | filename = filePath + "/" + m_idString.toLower() + ".state"; |
|---|
| 236 | } |
|---|
| 237 | else { |
|---|
| 238 | filename = stateFilename; |
|---|
| 239 | } |
|---|
| 240 | QString oldfilename = filename + ".old"; |
|---|
| 241 | |
|---|
| 242 | if (notify) { |
|---|
| 243 | if (!m_dialog->startProgressUpdate(tr("Saving: Writing %1...") |
|---|
| 244 | .arg(filename), |
|---|
| 245 | 0, 0)) { |
|---|
| 246 | // The progress bar is already in use -- disable notifications |
|---|
| 247 | notify = false; |
|---|
| 248 | } |
|---|
| 249 | } |
|---|
| 250 | |
|---|
| 251 | // Copy .state -> .state.old |
|---|
| 252 | if (QFile::exists(filename) ) { |
|---|
| 253 | if (QFile::exists(oldfilename)) { |
|---|
| 254 | QFile::remove(oldfilename); |
|---|
| 255 | } |
|---|
| 256 | QFile::copy(filename, oldfilename); |
|---|
| 257 | } |
|---|
| 258 | |
|---|
| 259 | SETTINGS(filename); |
|---|
| 260 | const int VERSION = m_schemaVersion; |
|---|
| 261 | settings->beginGroup(m_idString.toLower()); |
|---|
| 262 | settings->setValue("version", VERSION); |
|---|
| 263 | settings->setValue("saveSuccessful", false); |
|---|
| 264 | settings->endGroup(); |
|---|
| 265 | |
|---|
| 266 | // Write/update .state |
|---|
| 267 | m_dialog->writeSettings(filename); |
|---|
| 268 | |
|---|
| 269 | // Loop over structures and save them |
|---|
| 270 | QList<Structure*> *structures = m_tracker->list(); |
|---|
| 271 | |
|---|
| 272 | QString structureStateFileName; |
|---|
| 273 | |
|---|
| 274 | Structure* structure; |
|---|
| 275 | for (int i = 0; i < structures->size(); i++) { |
|---|
| 276 | structure = structures->at(i); |
|---|
| 277 | structure->lock()->lockForRead(); |
|---|
| 278 | // Set index here -- this is the only time these are written, so |
|---|
| 279 | // this is "ok" under a read lock because of the savePending logic |
|---|
| 280 | structure->setIndex(i); |
|---|
| 281 | structureStateFileName = structure->fileName() + "/structure.state"; |
|---|
| 282 | if (notify) { |
|---|
| 283 | m_dialog->updateProgressLabel(tr("Saving: Writing %1...") |
|---|
| 284 | .arg(structureStateFileName)); |
|---|
| 285 | } |
|---|
| 286 | structure->writeSettings(structureStateFileName); |
|---|
| 287 | structure->lock()->unlock(); |
|---|
| 288 | } |
|---|
| 289 | |
|---|
| 290 | ///////////////////////// |
|---|
| 291 | // Print results files // |
|---|
| 292 | ///////////////////////// |
|---|
| 293 | |
|---|
| 294 | QFile file (filePath + "/results.txt"); |
|---|
| 295 | QFile oldfile (filePath + "/results_old.txt"); |
|---|
| 296 | if (notify) { |
|---|
| 297 | m_dialog->updateProgressLabel(tr("Saving: Writing %1...") |
|---|
| 298 | .arg(file.fileName())); |
|---|
| 299 | } |
|---|
| 300 | if (oldfile.open(QIODevice::ReadOnly)) |
|---|
| 301 | oldfile.remove(); |
|---|
| 302 | if (file.open(QIODevice::ReadOnly)) |
|---|
| 303 | file.copy(oldfile.fileName()); |
|---|
| 304 | file.close(); |
|---|
| 305 | if (!file.open(QIODevice::WriteOnly)) { |
|---|
| 306 | error("OptBase::save(): Error opening file "+file.fileName()+" for writing..."); |
|---|
| 307 | savePending = false; |
|---|
| 308 | return false; |
|---|
| 309 | } |
|---|
| 310 | QTextStream out (&file); |
|---|
| 311 | |
|---|
| 312 | QList<Structure*> sortedStructures; |
|---|
| 313 | |
|---|
| 314 | for (int i = 0; i < structures->size(); i++) |
|---|
| 315 | sortedStructures.append(structures->at(i)); |
|---|
| 316 | if (sortedStructures.size() != 0) { |
|---|
| 317 | Structure::sortAndRankByEnthalpy(&sortedStructures); |
|---|
| 318 | out << sortedStructures.first()->getResultsHeader() << endl; |
|---|
| 319 | } |
|---|
| 320 | |
|---|
| 321 | for (int i = 0; i < sortedStructures.size(); i++) { |
|---|
| 322 | structure = sortedStructures.at(i); |
|---|
| 323 | if (!structure) continue; // In case there was a problem copying. |
|---|
| 324 | structure->lock()->lockForRead(); |
|---|
| 325 | out << structure->getResultsEntry() << endl; |
|---|
| 326 | structure->lock()->unlock(); |
|---|
| 327 | if (notify) { |
|---|
| 328 | m_dialog->stopProgressUpdate(); |
|---|
| 329 | } |
|---|
| 330 | } |
|---|
| 331 | |
|---|
| 332 | // Mark operation successful |
|---|
| 333 | settings->setValue(m_idString.toLower() + "/saveSuccessful", true); |
|---|
| 334 | DESTROY_SETTINGS(filename); |
|---|
| 335 | |
|---|
| 336 | savePending = false; |
|---|
| 337 | return true; |
|---|
| 338 | } |
|---|
| 339 | |
|---|
| 340 | QString OptBase::interpretTemplate(const QString & str, Structure* structure) |
|---|
| 341 | { |
|---|
| 342 | QStringList list = str.split("%"); |
|---|
| 343 | QString line; |
|---|
| 344 | QString origLine; |
|---|
| 345 | for (int line_ind = 0; line_ind < list.size(); line_ind++) { |
|---|
| 346 | origLine = line = list.at(line_ind); |
|---|
| 347 | interpretKeyword_base(line, structure); |
|---|
| 348 | // Add other interpret keyword sections here if needed when subclassing |
|---|
| 349 | if (line != origLine) { // Line was a keyword |
|---|
| 350 | list.replace(line_ind, line); |
|---|
| 351 | } |
|---|
| 352 | } |
|---|
| 353 | // Rejoin string |
|---|
| 354 | QString ret = list.join(""); |
|---|
| 355 | ret += "\n"; |
|---|
| 356 | return ret; |
|---|
| 357 | } |
|---|
| 358 | |
|---|
| 359 | void OptBase::interpretKeyword_base(QString &line, Structure* structure) |
|---|
| 360 | { |
|---|
| 361 | QString rep = ""; |
|---|
| 362 | // User data |
|---|
| 363 | if (line == "user1") rep += optimizer()->getUser1(); |
|---|
| 364 | else if (line == "user2") rep += optimizer()->getUser2(); |
|---|
| 365 | else if (line == "user3") rep += optimizer()->getUser3(); |
|---|
| 366 | else if (line == "user4") rep += optimizer()->getUser4(); |
|---|
| 367 | else if (line == "description") rep += description; |
|---|
| 368 | else if (line == "percent") rep += "%"; |
|---|
| 369 | |
|---|
| 370 | // Structure specific data |
|---|
| 371 | if (line == "coords") { |
|---|
| 372 | QList<Avogadro::Atom*> atoms = structure->atoms(); |
|---|
| 373 | QList<Avogadro::Atom*>::const_iterator it; |
|---|
| 374 | int optIndex = -1; |
|---|
| 375 | QHash<int, int> *lut = structure->getOptimizerLookupTable(); |
|---|
| 376 | lut->clear(); |
|---|
| 377 | const Eigen::Vector3d *vec; |
|---|
| 378 | for (it = atoms.begin(); |
|---|
| 379 | it != atoms.end(); |
|---|
| 380 | it++) { |
|---|
| 381 | rep += QString(OpenBabel::etab.GetSymbol((*it)->atomicNumber()))+ " "; |
|---|
| 382 | vec = (*it)->pos(); |
|---|
| 383 | rep += QString::number(vec->x()) + " "; |
|---|
| 384 | rep += QString::number(vec->y()) + " "; |
|---|
| 385 | rep += QString::number(vec->z()) + "\n"; |
|---|
| 386 | lut->insert(++optIndex, (*it)->index()); |
|---|
| 387 | } |
|---|
| 388 | } |
|---|
| 389 | else if (line == "coordsInternalFlags") { |
|---|
| 390 | QList<Avogadro::Atom*> atoms = structure->atoms(); |
|---|
| 391 | QList<Avogadro::Atom*>::const_iterator it; |
|---|
| 392 | const Eigen::Vector3d *vec; |
|---|
| 393 | int optIndex = -1; |
|---|
| 394 | QHash<int, int> *lut = structure->getOptimizerLookupTable(); |
|---|
| 395 | lut->clear(); |
|---|
| 396 | for (it = atoms.begin(); |
|---|
| 397 | it != atoms.end(); |
|---|
| 398 | it++) { |
|---|
| 399 | rep += QString(OpenBabel::etab.GetSymbol((*it)->atomicNumber()))+ " "; |
|---|
| 400 | vec = (*it)->pos(); |
|---|
| 401 | rep += QString::number(vec->x()) + " 1 "; |
|---|
| 402 | rep += QString::number(vec->y()) + " 1 "; |
|---|
| 403 | rep += QString::number(vec->z()) + " 1\n"; |
|---|
| 404 | lut->insert(++optIndex, (*it)->index()); |
|---|
| 405 | } |
|---|
| 406 | } |
|---|
| 407 | else if (line == "coordsSuffixFlags") { |
|---|
| 408 | QList<Avogadro::Atom*> atoms = structure->atoms(); |
|---|
| 409 | QList<Avogadro::Atom*>::const_iterator it; |
|---|
| 410 | const Eigen::Vector3d *vec; |
|---|
| 411 | int optIndex = -1; |
|---|
| 412 | QHash<int, int> *lut = structure->getOptimizerLookupTable(); |
|---|
| 413 | lut->clear(); |
|---|
| 414 | for (it = atoms.begin(); |
|---|
| 415 | it != atoms.end(); |
|---|
| 416 | it++) { |
|---|
| 417 | rep += QString(OpenBabel::etab.GetSymbol((*it)->atomicNumber()))+ " "; |
|---|
| 418 | vec = (*it)->pos(); |
|---|
| 419 | rep += QString::number(vec->x()) + " "; |
|---|
| 420 | rep += QString::number(vec->y()) + " "; |
|---|
| 421 | rep += QString::number(vec->z()) + " 1 1 1\n"; |
|---|
| 422 | lut->insert(++optIndex, (*it)->index()); |
|---|
| 423 | } |
|---|
| 424 | } |
|---|
| 425 | else if (line == "coordsId") { |
|---|
| 426 | QList<Avogadro::Atom*> atoms = structure->atoms(); |
|---|
| 427 | QList<Avogadro::Atom*>::const_iterator it; |
|---|
| 428 | const Eigen::Vector3d *vec; |
|---|
| 429 | int optIndex = -1; |
|---|
| 430 | QHash<int, int> *lut = structure->getOptimizerLookupTable(); |
|---|
| 431 | lut->clear(); |
|---|
| 432 | for (it = atoms.begin(); |
|---|
| 433 | it != atoms.end(); |
|---|
| 434 | it++) { |
|---|
| 435 | rep += QString(OpenBabel::etab.GetSymbol((*it)->atomicNumber()))+ " "; |
|---|
| 436 | rep += QString::number((*it)->atomicNumber()) + " "; |
|---|
| 437 | vec = (*it)->pos(); |
|---|
| 438 | rep += QString::number(vec->x()) + " "; |
|---|
| 439 | rep += QString::number(vec->y()) + " "; |
|---|
| 440 | rep += QString::number(vec->z()) + "\n"; |
|---|
| 441 | lut->insert(++optIndex, (*it)->index()); |
|---|
| 442 | } |
|---|
| 443 | } |
|---|
| 444 | else if (line == "numAtoms") rep += QString::number(structure->numAtoms()); |
|---|
| 445 | else if (line == "numSpecies") rep += QString::number(structure->getSymbols().size()); |
|---|
| 446 | else if (line == "filename") rep += structure->fileName(); |
|---|
| 447 | else if (line == "rempath") rep += structure->getRempath(); |
|---|
| 448 | else if (line == "gen") rep += QString::number(structure->getGeneration()); |
|---|
| 449 | else if (line == "id") rep += QString::number(structure->getIDNumber()); |
|---|
| 450 | else if (line == "incar") rep += QString::number(structure->getCurrentOptStep()); |
|---|
| 451 | else if (line == "optStep") rep += QString::number(structure->getCurrentOptStep()); |
|---|
| 452 | |
|---|
| 453 | if (!rep.isEmpty()) { |
|---|
| 454 | // Remove any trailing newlines |
|---|
| 455 | rep = rep.replace(QRegExp("\n$"), ""); |
|---|
| 456 | line = rep; |
|---|
| 457 | } |
|---|
| 458 | } |
|---|
| 459 | |
|---|
| 460 | QString OptBase::getTemplateKeywordHelp_base() |
|---|
| 461 | { |
|---|
| 462 | QString str; |
|---|
| 463 | QTextStream out (&str); |
|---|
| 464 | out |
|---|
| 465 | << "The following keywords should be used instead of the indicated variable data:\n" |
|---|
| 466 | << "\n" |
|---|
| 467 | << "Misc:\n" |
|---|
| 468 | << "%percent% -- Literal percent sign (needed for CASTEP!)\n" |
|---|
| 469 | << "\n" |
|---|
| 470 | << "User data:\n" |
|---|
| 471 | << "%userX% -- User specified value, where X = 1, 2, 3, or 4\n" |
|---|
| 472 | << "%description% -- Optimization description\n" |
|---|
| 473 | << "\n" |
|---|
| 474 | << "Atomic coordinate formats for isolated structures:\n" |
|---|
| 475 | << "%coords% -- cartesian coordinates\n\t[symbol] [x] [y] [z]\n" |
|---|
| 476 | << "%coordsInternalFlags% -- cartesian coordinates; flag after each coordinate\n\t[symbol] [x] 1 [y] 1 [z] 1\n" |
|---|
| 477 | << "%coordsSuffixFlags% -- cartesian coordinates; flags after all coordinates\n\t[symbol] [x] [y] [z] 1 1 1\n" |
|---|
| 478 | << "%coordsId% -- cartesian coordinates with atomic number\n\t[symbol] [atomic number] [x] [y] [z]\n" |
|---|
| 479 | << "\n" |
|---|
| 480 | << "Generic structure data:\n" |
|---|
| 481 | << "%numAtoms% -- Number of atoms in unit cell\n" |
|---|
| 482 | << "%numSpecies% -- Number of unique atomic species in unit cell\n" |
|---|
| 483 | << "%filename% -- local output filename\n" |
|---|
| 484 | << "%rempath% -- path to structure's remote directory\n" |
|---|
| 485 | << "%gen% -- structure generation number (if relevant)\n" |
|---|
| 486 | << "%id% -- structure id number\n" |
|---|
| 487 | << "%optStep% -- current optimization step\n" |
|---|
| 488 | ; |
|---|
| 489 | return str; |
|---|
| 490 | } |
|---|
| 491 | |
|---|
| 492 | void OptBase::setOptimizer(Optimizer *o) |
|---|
| 493 | { |
|---|
| 494 | m_optimizer = o; |
|---|
| 495 | emit optimizerChanged(o); |
|---|
| 496 | } |
|---|
| 497 | |
|---|
| 498 | void OptBase::setQueueInterface(QueueInterface *q) |
|---|
| 499 | { |
|---|
| 500 | m_queueInterface = q; |
|---|
| 501 | emit queueInterfaceChanged(q); |
|---|
| 502 | } |
|---|
| 503 | |
|---|
| 504 | void OptBase::promptForPassword(const QString &message, |
|---|
| 505 | QString *newPassword, |
|---|
| 506 | bool *ok) |
|---|
| 507 | { |
|---|
| 508 | (*newPassword) = QInputDialog::getText(dialog(), "Need password:", message, |
|---|
| 509 | QLineEdit::Password, QString(), ok); |
|---|
| 510 | }; |
|---|
| 511 | |
|---|
| 512 | void OptBase::promptForBoolean(const QString &message, |
|---|
| 513 | bool *ok) |
|---|
| 514 | { |
|---|
| 515 | if (QMessageBox::question(dialog(), m_idString, message, |
|---|
| 516 | QMessageBox::Yes | QMessageBox::No) |
|---|
| 517 | == QMessageBox::Yes) { |
|---|
| 518 | *ok = true; |
|---|
| 519 | } else { |
|---|
| 520 | *ok = false; |
|---|
| 521 | } |
|---|
| 522 | } |
|---|
| 523 | |
|---|
| 524 | void OptBase::setClipboard(const QString &text) const |
|---|
| 525 | { |
|---|
| 526 | emit sig_setClipboard(text); |
|---|
| 527 | } |
|---|
| 528 | |
|---|
| 529 | // No need to document this |
|---|
| 530 | /// @cond |
|---|
| 531 | void OptBase::setClipboard_(const QString &text) const |
|---|
| 532 | { |
|---|
| 533 | // Set to system clipboard |
|---|
| 534 | QApplication::clipboard()->setText(text, QClipboard::Clipboard); |
|---|
| 535 | // For middle-click on X11 |
|---|
| 536 | if (QApplication::clipboard()->supportsSelection()) { |
|---|
| 537 | QApplication::clipboard()->setText(text, QClipboard::Selection); |
|---|
| 538 | } |
|---|
| 539 | } |
|---|
| 540 | /// @endcond |
|---|
| 541 | |
|---|
| 542 | #ifdef ENABLE_SSH |
|---|
| 543 | #ifndef USE_CLI_SSH |
|---|
| 544 | |
|---|
| 545 | bool OptBase::createSSHConnections_libssh() |
|---|
| 546 | { |
|---|
| 547 | delete m_ssh; |
|---|
| 548 | SSHManagerLibSSH *libsshManager = new SSHManagerLibSSH(5, this); |
|---|
| 549 | m_ssh = libsshManager; |
|---|
| 550 | QString pw = ""; |
|---|
| 551 | for (;;) { |
|---|
| 552 | try { |
|---|
| 553 | libsshManager->makeConnections(host, username, pw, port); |
|---|
| 554 | } |
|---|
| 555 | catch (SSHConnection::SSHConnectionException e) { |
|---|
| 556 | QString err; |
|---|
| 557 | switch (e) { |
|---|
| 558 | case SSHConnection::SSH_CONNECTION_ERROR: |
|---|
| 559 | case SSHConnection::SSH_UNKNOWN_ERROR: |
|---|
| 560 | default: |
|---|
| 561 | err = "There was a problem connection to the ssh server at " |
|---|
| 562 | + username + "@" + host + ":" + QString::number(port) + ". " |
|---|
| 563 | "Please check that all provided information is correct, and " |
|---|
| 564 | "attempt to log in outside of Avogadro before trying again."; |
|---|
| 565 | error(err); |
|---|
| 566 | return false; |
|---|
| 567 | case SSHConnection::SSH_UNKNOWN_HOST_ERROR: { |
|---|
| 568 | // The host is not known, or has changed its key. |
|---|
| 569 | // Ask user if this is ok. |
|---|
| 570 | err = "The host " |
|---|
| 571 | + host + ":" + QString::number(port) |
|---|
| 572 | + " either has an unknown key, or has changed it's key:\n" |
|---|
| 573 | + libsshManager->getServerKeyHash() + "\n" |
|---|
| 574 | + "Would you like to trust the specified host?"; |
|---|
| 575 | bool ok; |
|---|
| 576 | // This is a BlockingQueuedConnection, which blocks until |
|---|
| 577 | // the slot returns. |
|---|
| 578 | emit needBoolean(err, &ok); |
|---|
| 579 | if (!ok) { // user cancels |
|---|
| 580 | return false; |
|---|
| 581 | } |
|---|
| 582 | libsshManager->validateServerKey(); |
|---|
| 583 | continue; |
|---|
| 584 | } // end case |
|---|
| 585 | case SSHConnection::SSH_BAD_PASSWORD_ERROR: { |
|---|
| 586 | // Chances are that the pubkey auth was attempted but failed, |
|---|
| 587 | // so just prompt user for password. |
|---|
| 588 | err = "Please enter a password for " |
|---|
| 589 | + username + "@" + host + ":" + QString::number(port) |
|---|
| 590 | + ":"; |
|---|
| 591 | bool ok; |
|---|
| 592 | QString newPassword; |
|---|
| 593 | // This is a BlockingQueuedConnection, which blocks until |
|---|
| 594 | // the slot returns. |
|---|
| 595 | emit needPassword(err, &newPassword, &ok); |
|---|
| 596 | if (!ok) { // user cancels |
|---|
| 597 | return false; |
|---|
| 598 | } |
|---|
| 599 | pw = newPassword; |
|---|
| 600 | continue; |
|---|
| 601 | } // end case |
|---|
| 602 | } // end switch |
|---|
| 603 | } // end catch |
|---|
| 604 | break; |
|---|
| 605 | } // end forever |
|---|
| 606 | return true; |
|---|
| 607 | } |
|---|
| 608 | |
|---|
| 609 | #else // not USE_CLI_SSH |
|---|
| 610 | |
|---|
| 611 | bool OptBase::createSSHConnections_cli() |
|---|
| 612 | { |
|---|
| 613 | // Since we rely on public key auth here, it's much easier to set up: |
|---|
| 614 | SSHManagerCLI *cliSSHManager = new SSHManagerCLI(5, this); |
|---|
| 615 | cliSSHManager->makeConnections(host, username, "", port); |
|---|
| 616 | m_ssh = cliSSHManager; |
|---|
| 617 | return true; |
|---|
| 618 | } |
|---|
| 619 | |
|---|
| 620 | #endif // not USE_CLI_SSH |
|---|
| 621 | #endif // ENABLE_SSH |
|---|
| 622 | |
|---|
| 623 | void OptBase::warning(const QString & s) { |
|---|
| 624 | qWarning() << "Warning: " << s; |
|---|
| 625 | emit warningStatement(s); |
|---|
| 626 | } |
|---|
| 627 | |
|---|
| 628 | void OptBase::debug(const QString & s) { |
|---|
| 629 | qDebug() << "Debug: " << s; |
|---|
| 630 | emit debugStatement(s); |
|---|
| 631 | } |
|---|
| 632 | |
|---|
| 633 | void OptBase::error(const QString & s) { |
|---|
| 634 | qWarning() << "Error: " << s; |
|---|
| 635 | emit errorStatement(s); |
|---|
| 636 | } |
|---|
| 637 | |
|---|
| 638 | } // end namespace GlobalSearch |
|---|
| 639 | |
|---|