00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include <globalsearch/optbase.h>
00017
00018 #include <globalsearch/bt.h>
00019 #include <globalsearch/macros.h>
00020 #include <globalsearch/optimizer.h>
00021 #include <globalsearch/queuemanager.h>
00022 #include <globalsearch/queueinterface.h>
00023 #include <globalsearch/queueinterfaces/local.h>
00024 #include <globalsearch/queueinterfaces/pbs.h>
00025 #ifdef ENABLE_SSH
00026 #include <globalsearch/sshmanager.h>
00027 #ifdef USE_CLI_SSH
00028 #include <globalsearch/sshmanager_cli.h>
00029 #else // USE_CLI_SSH
00030 #include <globalsearch/sshmanager_libssh.h>
00031 #endif // USE_CLI_SSH
00032 #endif // ENABLE_SSH
00033 #include <globalsearch/structure.h>
00034 #include <globalsearch/ui/abstractdialog.h>
00035
00036 #include <QtCore/QFile>
00037 #include <QtCore/QThread>
00038
00039 #include <QtGui/QClipboard>
00040 #include <QtGui/QMessageBox>
00041 #include <QtGui/QApplication>
00042 #include <QtGui/QInputDialog>
00043
00044 using namespace OpenBabel;
00045
00046 namespace GlobalSearch {
00047
00048 OptBase::OptBase(AbstractDialog *parent) :
00049 QObject(parent),
00050 m_dialog(parent),
00051 m_tracker(new Tracker (this)),
00052 m_queueThread(new QThread),
00053 m_queue(new QueueManager(m_queueThread, this)),
00054 m_queueInterface(0),
00055 m_optimizer(0),
00056 #ifdef ENABLE_SSH
00057 m_ssh(NULL),
00058 #endif
00059 m_idString("Generic"),
00060 sOBMutex(new QMutex),
00061 stateFileMutex(new QMutex),
00062 backTraceMutex(new QMutex),
00063 usePreopt(false),
00064 savePending(false),
00065 readOnly(false),
00066 testingMode(false),
00067 test_nRunsStart(1),
00068 test_nRunsEnd(100),
00069 test_nStructs(600),
00070 cutoff(-1),
00071 m_schemaVersion(1),
00072 m_isDestroying(false)
00073 {
00074
00075 connect(this, SIGNAL(sessionStarted()),
00076 m_queueThread, SLOT(start()),
00077 Qt::DirectConnection);
00078 connect(this, SIGNAL(startingSession()),
00079 this, SLOT(setIsStartingTrue()),
00080 Qt::DirectConnection);
00081 connect(this, SIGNAL(sessionStarted()),
00082 this, SLOT(setIsStartingFalse()),
00083 Qt::DirectConnection);
00084 connect(this, SIGNAL(readOnlySessionStarted()),
00085 this, SLOT(setIsStartingFalse()),
00086 Qt::DirectConnection);
00087 connect(this, SIGNAL(needBoolean(const QString&, bool*)),
00088 this, SLOT(promptForBoolean(const QString&, bool*)),
00089 Qt::BlockingQueuedConnection);
00090 connect(this, SIGNAL(needPassword(const QString&, QString*, bool*)),
00091 this, SLOT(promptForPassword(const QString&, QString*, bool*)),
00092 Qt::BlockingQueuedConnection);
00093 connect(this, SIGNAL(sig_setClipboard(const QString&)),
00094 this, SLOT(setClipboard_(const QString&)),
00095 Qt::QueuedConnection);
00096
00097 INIT_RANDOM_GENERATOR();
00098 }
00099
00100 OptBase::~OptBase()
00101 {
00102 m_isDestroying = true;
00103
00104 delete m_queue;
00105 m_queue = 0;
00106
00107 if (m_queueThread && m_queueThread->isRunning()) {
00108 m_queueThread->wait();
00109 }
00110 delete m_queueThread;
00111 m_queueThread = 0;
00112
00113 delete m_optimizer;
00114 m_optimizer = 0;
00115
00116 delete m_queueInterface;
00117 m_queueInterface = 0;
00118
00119 delete m_tracker;
00120 m_tracker = 0;
00121 }
00122
00123 void OptBase::reset() {
00124 m_tracker->lockForWrite();
00125 m_tracker->deleteAllStructures();
00126 m_tracker->reset();
00127 m_tracker->unlock();
00128 m_queue->reset();
00129 }
00130
00131 #ifdef ENABLE_SSH
00132 bool OptBase::createSSHConnections()
00133 {
00134 #ifdef USE_CLI_SSH
00135 return this->createSSHConnections_cli();
00136 #else // USE_CLI_SSH
00137 return this->createSSHConnections_libssh();
00138 #endif // USE_CLI_SSH
00139 }
00140 #endif // ENABLE_SSH
00141
00142 void OptBase::printBackTrace() {
00143 backTraceMutex->lock();
00144 QStringList l = getBackTrace();
00145 backTraceMutex->unlock();
00146 for (int i = 0; i < l.size();i++)
00147 qDebug() << l.at(i);
00148 }
00149
00150 QList<double> OptBase::getProbabilityList(const QList<Structure*> &structures) {
00151
00152
00153
00154 if (structures.size() <= 2) {
00155 return QList<double>();
00156 }
00157
00158 QList<double> probs;
00159 Structure *s=0, *first=0, *last=0;
00160 first = structures.first();
00161 last = structures.last();
00162 first->lock()->lockForRead();
00163 last->lock()->lockForRead();
00164 double lowest = first->getEnthalpy();
00165 double highest = last->getEnthalpy();;
00166 double spread = highest - lowest;
00167 last->lock()->unlock();
00168 first->lock()->unlock();
00169
00170 if (spread <= 1e-5) {
00171 double dprob = 1.0/static_cast<double>(structures.size()-1);
00172 double prob = 0;
00173 for (int i = 0; i < structures.size()-1; i++) {
00174 probs.append(prob);
00175 prob += dprob;
00176 }
00177 return probs;
00178 }
00179
00180
00181
00182
00183
00184 for (int i = 0; i < structures.size(); i++) {
00185 s = structures.at(i);
00186 s->lock()->lockForRead();
00187 probs.append( ( s->getEnthalpy() - lowest ) / spread);
00188 s->lock()->unlock();
00189 }
00190
00191
00192
00193 double sum = 0;
00194 for (int i = 0; i < probs.size(); i++){
00195 probs[i] = 1.0 - probs.at(i);
00196 sum += probs.at(i);
00197 }
00198
00199
00200 for (int i = 0; i < probs.size(); i++){
00201 probs[i] /= sum;
00202 }
00203
00204
00205 sum = 0;
00206 for (int i = 0; i < probs.size(); i++){
00207 sum += probs.at(i);
00208 probs[i] = sum;
00209 }
00210
00211
00212 probs.removeLast();
00213
00214
00215
00216
00217
00218
00219
00220
00221 return probs;
00222 }
00223
00224 bool OptBase::save(const QString &stateFilename, bool notify)
00225 {
00226 if (isStarting ||
00227 readOnly) {
00228 savePending = false;
00229 return false;
00230 }
00231 QMutexLocker locker (stateFileMutex);
00232 QString filename;
00233 if (stateFilename.isEmpty()) {
00234 filename = filePath + "/" + m_idString.toLower() + ".state";
00235 }
00236 else {
00237 filename = stateFilename;
00238 }
00239 QString oldfilename = filename + ".old";
00240
00241 if (notify) {
00242 if (!m_dialog->startProgressUpdate(tr("Saving: Writing %1...")
00243 .arg(filename),
00244 0, 0)) {
00245
00246 notify = false;
00247 }
00248 }
00249
00250
00251 if (QFile::exists(filename) ) {
00252 if (QFile::exists(oldfilename)) {
00253 QFile::remove(oldfilename);
00254 }
00255 QFile::copy(filename, oldfilename);
00256 }
00257
00258 SETTINGS(filename);
00259 const int VERSION = m_schemaVersion;
00260 settings->beginGroup(m_idString.toLower());
00261 settings->setValue("version", VERSION);
00262 settings->setValue("saveSuccessful", false);
00263 settings->endGroup();
00264
00265
00266 m_dialog->writeSettings(filename);
00267
00268
00269 QReadLocker trackerLocker (m_tracker->rwLock());
00270 QList<Structure*> *structures = m_tracker->list();
00271
00272 QString structureStateFileName;
00273
00274 Structure* structure;
00275 for (int i = 0; i < structures->size(); i++) {
00276 structure = structures->at(i);
00277 structure->lock()->lockForRead();
00278
00279
00280 structure->setIndex(i);
00281 structureStateFileName = structure->fileName() + "/structure.state";
00282 if (notify) {
00283 m_dialog->updateProgressLabel(tr("Saving: Writing %1...")
00284 .arg(structureStateFileName));
00285 }
00286 structure->writeSettings(structureStateFileName);
00287 structure->lock()->unlock();
00288 }
00289
00291
00293
00294 QFile file (filePath + "/results.txt");
00295 QFile oldfile (filePath + "/results_old.txt");
00296 if (notify) {
00297 m_dialog->updateProgressLabel(tr("Saving: Writing %1...")
00298 .arg(file.fileName()));
00299 }
00300 if (oldfile.open(QIODevice::ReadOnly))
00301 oldfile.remove();
00302 if (file.open(QIODevice::ReadOnly))
00303 file.copy(oldfile.fileName());
00304 file.close();
00305 if (!file.open(QIODevice::WriteOnly)) {
00306 error("OptBase::save(): Error opening file "+file.fileName()+" for writing...");
00307 savePending = false;
00308 return false;
00309 }
00310 QTextStream out (&file);
00311
00312 QList<Structure*> sortedStructures;
00313
00314 for (int i = 0; i < structures->size(); i++)
00315 sortedStructures.append(structures->at(i));
00316 if (sortedStructures.size() != 0) {
00317 Structure::sortAndRankByEnthalpy(&sortedStructures);
00318 out << sortedStructures.first()->getResultsHeader() << endl;
00319 }
00320
00321 for (int i = 0; i < sortedStructures.size(); i++) {
00322 structure = sortedStructures.at(i);
00323 if (!structure) continue;
00324 structure->lock()->lockForRead();
00325 out << structure->getResultsEntry() << endl;
00326 structure->lock()->unlock();
00327 if (notify) {
00328 m_dialog->stopProgressUpdate();
00329 }
00330 }
00331
00332
00333 this->postSave(filename);
00334
00335
00336 settings->setValue(m_idString.toLower() + "/saveSuccessful", true);
00337 DESTROY_SETTINGS(filename);
00338
00339 savePending = false;
00340 return true;
00341 }
00342
00343 QString OptBase::interpretTemplate(const QString & str, Structure* structure)
00344 {
00345 QStringList list = str.split("%");
00346 QString line;
00347 QString origLine;
00348 for (int line_ind = 0; line_ind < list.size(); line_ind++) {
00349 origLine = line = list.at(line_ind);
00350 interpretKeyword_base(line, structure);
00351
00352 if (line != origLine) {
00353 list.replace(line_ind, line);
00354 }
00355 }
00356
00357 QString ret = list.join("");
00358 ret += "\n";
00359 return ret;
00360 }
00361
00362 void OptBase::interpretKeyword_base(QString &line, Structure* structure)
00363 {
00364 QString rep = "";
00365
00366 if (line == "user1") rep += optimizer()->getUser1();
00367 else if (line == "user2") rep += optimizer()->getUser2();
00368 else if (line == "user3") rep += optimizer()->getUser3();
00369 else if (line == "user4") rep += optimizer()->getUser4();
00370 else if (line == "description") rep += description;
00371 else if (line == "percent") rep += "%";
00372
00373
00374 if (line == "coords") {
00375 QList<Avogadro::Atom*> atoms = structure->atoms();
00376 QList<Avogadro::Atom*>::const_iterator it;
00377 int optIndex = -1;
00378 QHash<int, int> *lut = structure->getOptimizerLookupTable();
00379 lut->clear();
00380 const Eigen::Vector3d *vec;
00381 for (it = atoms.begin();
00382 it != atoms.end();
00383 it++) {
00384 rep += QString(OpenBabel::etab.GetSymbol((*it)->atomicNumber()))+ " ";
00385 vec = (*it)->pos();
00386 rep += QString::number(vec->x()) + " ";
00387 rep += QString::number(vec->y()) + " ";
00388 rep += QString::number(vec->z()) + "\n";
00389 lut->insert(++optIndex, (*it)->index());
00390 }
00391 }
00392 else if (line == "coordsInternalFlags") {
00393 QList<Avogadro::Atom*> atoms = structure->atoms();
00394 QList<Avogadro::Atom*>::const_iterator it;
00395 const Eigen::Vector3d *vec;
00396 int optIndex = -1;
00397 QHash<int, int> *lut = structure->getOptimizerLookupTable();
00398 lut->clear();
00399 for (it = atoms.begin();
00400 it != atoms.end();
00401 it++) {
00402 rep += QString(OpenBabel::etab.GetSymbol((*it)->atomicNumber()))+ " ";
00403 vec = (*it)->pos();
00404 rep += QString::number(vec->x()) + " 1 ";
00405 rep += QString::number(vec->y()) + " 1 ";
00406 rep += QString::number(vec->z()) + " 1\n";
00407 lut->insert(++optIndex, (*it)->index());
00408 }
00409 }
00410 else if (line == "coordsSuffixFlags") {
00411 QList<Avogadro::Atom*> atoms = structure->atoms();
00412 QList<Avogadro::Atom*>::const_iterator it;
00413 const Eigen::Vector3d *vec;
00414 int optIndex = -1;
00415 QHash<int, int> *lut = structure->getOptimizerLookupTable();
00416 lut->clear();
00417 for (it = atoms.begin();
00418 it != atoms.end();
00419 it++) {
00420 rep += QString(OpenBabel::etab.GetSymbol((*it)->atomicNumber()))+ " ";
00421 vec = (*it)->pos();
00422 rep += QString::number(vec->x()) + " ";
00423 rep += QString::number(vec->y()) + " ";
00424 rep += QString::number(vec->z()) + " 1 1 1\n";
00425 lut->insert(++optIndex, (*it)->index());
00426 }
00427 }
00428 else if (line == "coordsId") {
00429 QList<Avogadro::Atom*> atoms = structure->atoms();
00430 QList<Avogadro::Atom*>::const_iterator it;
00431 const Eigen::Vector3d *vec;
00432 int optIndex = -1;
00433 QHash<int, int> *lut = structure->getOptimizerLookupTable();
00434 lut->clear();
00435 for (it = atoms.begin();
00436 it != atoms.end();
00437 it++) {
00438 rep += QString(OpenBabel::etab.GetSymbol((*it)->atomicNumber()))+ " ";
00439 rep += QString::number((*it)->atomicNumber()) + " ";
00440 vec = (*it)->pos();
00441 rep += QString::number(vec->x()) + " ";
00442 rep += QString::number(vec->y()) + " ";
00443 rep += QString::number(vec->z()) + "\n";
00444 lut->insert(++optIndex, (*it)->index());
00445 }
00446 }
00447 else if (line == "numAtoms") rep += QString::number(structure->numAtoms());
00448 else if (line == "numSpecies") rep += QString::number(structure->getSymbols().size());
00449 else if (line == "filename") rep += structure->fileName();
00450 else if (line == "rempath") rep += structure->getRempath();
00451 else if (line == "gen") rep += QString::number(structure->getGeneration());
00452 else if (line == "id") rep += QString::number(structure->getIDNumber());
00453 else if (line == "incar") rep += QString::number(structure->getCurrentOptStep());
00454 else if (line == "optStep") rep += QString::number(structure->getCurrentOptStep());
00455
00456 if (!rep.isEmpty()) {
00457
00458 rep = rep.replace(QRegExp("\n$"), "");
00459 line = rep;
00460 }
00461 }
00462
00463 QString OptBase::getTemplateKeywordHelp_base()
00464 {
00465 QString str;
00466 QTextStream out (&str);
00467 out
00468 << "The following keywords should be used instead of the indicated variable data:\n"
00469 << "\n"
00470 << "Misc:\n"
00471 << "%percent% -- Literal percent sign (needed for CASTEP!)\n"
00472 << "\n"
00473 << "User data:\n"
00474 << "%userX% -- User specified value, where X = 1, 2, 3, or 4\n"
00475 << "%description% -- Optimization description\n"
00476 << "\n"
00477 << "Atomic coordinate formats for isolated structures:\n"
00478 << "%coords% -- cartesian coordinates\n\t[symbol] [x] [y] [z]\n"
00479 << "%coordsInternalFlags% -- cartesian coordinates; flag after each coordinate\n\t[symbol] [x] 1 [y] 1 [z] 1\n"
00480 << "%coordsSuffixFlags% -- cartesian coordinates; flags after all coordinates\n\t[symbol] [x] [y] [z] 1 1 1\n"
00481 << "%coordsId% -- cartesian coordinates with atomic number\n\t[symbol] [atomic number] [x] [y] [z]\n"
00482 << "\n"
00483 << "Generic structure data:\n"
00484 << "%numAtoms% -- Number of atoms in unit cell\n"
00485 << "%numSpecies% -- Number of unique atomic species in unit cell\n"
00486 << "%filename% -- local output filename\n"
00487 << "%rempath% -- path to structure's remote directory\n"
00488 << "%gen% -- structure generation number (if relevant)\n"
00489 << "%id% -- structure id number\n"
00490 << "%optStep% -- current optimization step\n"
00491 ;
00492 return str;
00493 }
00494
00495 void OptBase::setOptimizer(Optimizer *o)
00496 {
00497 m_optimizer = o;
00498 emit optimizerChanged(o);
00499 }
00500
00501 void OptBase::setQueueInterface(QueueInterface *q)
00502 {
00503 m_queueInterface = q;
00504 emit queueInterfaceChanged(q);
00505 }
00506
00507 void OptBase::promptForPassword(const QString &message,
00508 QString *newPassword,
00509 bool *ok)
00510 {
00511 (*newPassword) = QInputDialog::getText(dialog(), "Need password:", message,
00512 QLineEdit::Password, QString(), ok);
00513 };
00514
00515 void OptBase::promptForBoolean(const QString &message,
00516 bool *ok)
00517 {
00518 if (QMessageBox::question(dialog(), m_idString, message,
00519 QMessageBox::Yes | QMessageBox::No)
00520 == QMessageBox::Yes) {
00521 *ok = true;
00522 } else {
00523 *ok = false;
00524 }
00525 }
00526
00527 void OptBase::setClipboard(const QString &text) const
00528 {
00529 emit sig_setClipboard(text);
00530 }
00531
00532
00534 void OptBase::setClipboard_(const QString &text) const
00535 {
00536
00537 QApplication::clipboard()->setText(text, QClipboard::Clipboard);
00538
00539 if (QApplication::clipboard()->supportsSelection()) {
00540 QApplication::clipboard()->setText(text, QClipboard::Selection);
00541 }
00542 }
00544
00545 #ifdef ENABLE_SSH
00546 #ifndef USE_CLI_SSH
00547
00548 bool OptBase::createSSHConnections_libssh()
00549 {
00550 delete m_ssh;
00551 SSHManagerLibSSH *libsshManager = new SSHManagerLibSSH(5, this);
00552 m_ssh = libsshManager;
00553 QString pw = "";
00554 for (;;) {
00555 try {
00556 libsshManager->makeConnections(host, username, pw, port);
00557 }
00558 catch (SSHConnection::SSHConnectionException e) {
00559 QString err;
00560 switch (e) {
00561 case SSHConnection::SSH_CONNECTION_ERROR:
00562 case SSHConnection::SSH_UNKNOWN_ERROR:
00563 default:
00564 err = "There was a problem connection to the ssh server at "
00565 + username + "@" + host + ":" + QString::number(port) + ". "
00566 "Please check that all provided information is correct, and "
00567 "attempt to log in outside of Avogadro before trying again.";
00568 error(err);
00569 return false;
00570 case SSHConnection::SSH_UNKNOWN_HOST_ERROR: {
00571
00572
00573 err = "The host "
00574 + host + ":" + QString::number(port)
00575 + " either has an unknown key, or has changed it's key:\n"
00576 + libsshManager->getServerKeyHash() + "\n"
00577 + "Would you like to trust the specified host?";
00578 bool ok;
00579
00580
00581 emit needBoolean(err, &ok);
00582 if (!ok) {
00583 return false;
00584 }
00585 libsshManager->validateServerKey();
00586 continue;
00587 }
00588 case SSHConnection::SSH_BAD_PASSWORD_ERROR: {
00589
00590
00591 err = "Please enter a password for "
00592 + username + "@" + host + ":" + QString::number(port)
00593 + ":";
00594 bool ok;
00595 QString newPassword;
00596
00597
00598 emit needPassword(err, &newPassword, &ok);
00599 if (!ok) {
00600 return false;
00601 }
00602 pw = newPassword;
00603 continue;
00604 }
00605 }
00606 }
00607 break;
00608 }
00609 return true;
00610 }
00611
00612 #else // not USE_CLI_SSH
00613
00614 bool OptBase::createSSHConnections_cli()
00615 {
00616
00617 SSHManagerCLI *cliSSHManager = new SSHManagerCLI(5, this);
00618 cliSSHManager->makeConnections(host, username, "", port);
00619 m_ssh = cliSSHManager;
00620 return true;
00621 }
00622
00623 #endif // not USE_CLI_SSH
00624 #endif // ENABLE_SSH
00625
00626 void OptBase::warning(const QString & s) {
00627 qWarning() << "Warning: " << s;
00628 emit warningStatement(s);
00629 }
00630
00631 void OptBase::debug(const QString & s) {
00632 qDebug() << "Debug: " << s;
00633 emit debugStatement(s);
00634 }
00635
00636 void OptBase::error(const QString & s) {
00637 qWarning() << "Error: " << s;
00638 emit errorStatement(s);
00639 }
00640
00641 }
00642