7 #ifndef READUCT_TSOPTIMIZATIONTASK_H_
8 #define READUCT_TSOPTIMIZATIONTASK_H_
25 #include <boost/exception/diagnostic_information.hpp>
26 #include <boost/filesystem.hpp>
45 :
Task(std::move(input), std::move(output), std::move(logger)) {
48 std::string
name()
const override {
49 return "TS Optimization";
54 observers = {})
const final {
58 bool silentCalculator = taskSettings.extract(
"silent_stdout_calculator",
true);
59 std::shared_ptr<Core::Calculator> calc;
63 calc = copyCalculator(systems, _input.front(),
name());
64 Utils::CalculationRoutines::setLog(*calc,
true,
true, !silentCalculator);
67 if (calc->getStructure()->size() == 1) {
68 throw std::runtime_error(
"Cannot calculate transition state for monoatomic systems.");
70 if (calc->name() ==
"QMMM") {
76 std::string optimizertype = taskSettings.extract(
"optimizer", std::string{
"bofill"});
77 std::string mmOptimizertype = taskSettings.extract(
"mm_optimizer", std::string{
"bfgs"});
78 std::transform(optimizertype.begin(), optimizertype.end(), optimizertype.begin(), ::tolower);
89 if (taskSettings.valueExists(
"automatic_mode_selection") &&
90 (taskSettings.valueExists(optimizertype +
"_follow_mode") || taskSettings.valueExists(
"ev_follow_mode"))) {
91 throw std::logic_error(
"You specified an automatic selection of the mode and gave a specific mode yourself. "
92 "This is not possible. Only give one of those options.");
95 std::vector<int> relevantAtoms = taskSettings.extract(
"automatic_mode_selection", std::vector<int>{});
96 std::tuple<double, double> modeSelectionWeights = extractWeightsForModeSelection(taskSettings);
98 if (!relevantAtoms.empty()) {
99 for (
const auto& index : relevantAtoms) {
101 throw std::logic_error(
"You gave an atom index smaller than 0 in automatic_mode_selection. "
102 "This does not make sense.");
106 bool writeSelectedMode = taskSettings.extract(
"write_selected_mode",
false);
108 using XyzHandler = Utils::XyzStreamHandler;
109 auto cout = _logger->output;
111 auto optimizer = constructOptimizer(calc, optimizertype, mmOptimizertype, isQmmm, taskSettings, relevantAtoms,
112 modeSelectionWeights, testRunOnly);
114 bool stopOnError = stopOnErrorExtraction(taskSettings);
116 auto settings = optimizer->getSettings();
117 settings.merge(taskSettings);
121 if (!relevantAtoms.empty() && !testRunOnly) {
123 if (optimizertype !=
"dimer" && optimizertype !=
"bofill") {
124 settings.modifyValue(
"ev_follow_mode", selectMode(*calc, relevantAtoms, std::get<0>(modeSelectionWeights),
125 std::get<1>(modeSelectionWeights)));
127 else if (optimizertype ==
"bofill") {
128 settings.modifyValue(
"bofill_follow_mode", selectMode(*calc, relevantAtoms, std::get<0>(modeSelectionWeights),
129 std::get<1>(modeSelectionWeights)));
132 int autoSelectedMode = 0;
133 if (optimizertype !=
"dimer" && optimizertype !=
"bofill") {
134 autoSelectedMode = settings.getInt(
"ev_follow_mode");
136 else if (optimizertype ==
"bofill") {
137 autoSelectedMode = settings.getInt(
"bofill_follow_mode");
139 if (!settings.valid()) {
140 settings.throwIncorrectSettings();
142 optimizer->setSettings(settings);
143 if (!testRunOnly && !Utils::GeometryOptimization::settingsMakeSense(*optimizer)) {
144 throw std::logic_error(
"The given calculator settings are too inaccurate for the given convergence criteria of "
145 "this optimization Task");
150 Utils::XyzStreamHandler writer;
151 const std::string& partialOutput = (!_output.empty() ? _output[0] : _input[0]);
152 boost::filesystem::path dir(partialOutput);
153 boost::filesystem::create_directory(dir);
154 boost::filesystem::path trjfile(partialOutput +
".tsopt.trj.xyz");
157 if (optimizertype ==
"dimer" && writeSelectedMode) {
158 cout <<
" Can't write a mode for the Dimer optimizer." <<
Core::Log::endl;
160 else if (optimizertype !=
"dimer" && writeSelectedMode) {
161 auto modes = getNormalModes(*calc);
162 auto trj = modes.getModeAsMolecularTrajectory(autoSelectedMode, *calc->getStructure(), 1.0);
163 std::string selectedModeFilename =
164 Utils::format(
"%s.selected.vibmode-%05d.trj.xyz", partialOutput.c_str(), autoSelectedMode + 1);
165 std::ofstream selectedModeXyz((dir / selectedModeFilename).
string(), std::ofstream::out);
167 selectedModeXyz.close();
170 std::ofstream trajectory((dir / trjfile).
string(), std::ofstream::out);
171 cout.printf(
"%7s %16s %16s %16s %16s\n",
"Cycle",
"Energy",
"Energy Diff.",
"Step RMS",
"Max. Step");
172 double oldEnergy = 0.0;
173 Eigen::VectorXd oldParams;
174 auto func = [&](
const int& cycle,
const double& energy,
const Eigen::VectorXd& params) {
175 if (oldParams.size() != params.size()) {
178 auto diff = (params - oldParams).eval();
179 auto fullEnergy = energy;
180 cout.printf(
"%7d %+16.9f %+16.9f %+16.9f %+16.9f\n", cycle, fullEnergy, fullEnergy - oldEnergy,
181 sqrt(diff.squaredNorm() / diff.size()), diff.cwiseAbs().maxCoeff());
182 oldEnergy = fullEnergy;
184 auto structure = calc->getStructure();
185 XyzHandler::write(trajectory, *structure, std::to_string(fullEnergy));
187 optimizer->addObserver(func);
190 auto customObservers = [&calc, &observers](
const int& cycle,
const double& ,
const Eigen::VectorXd& ) {
191 for (
auto& observer : observers) {
192 auto atoms = calc->getStructure();
193 Utils::Results& results = calc->results();
194 observer(cycle, *atoms, results,
"ts_optimization");
197 optimizer->addObserver(customObservers);
200 auto structure = calc->getStructure();
203 cycles = optimizer->optimize(*structure, *_logger);
206 double lastEnergy = oldEnergy;
207 if (calc->results().has<Utils::Property::Energy>()) {
208 lastEnergy = calc->results().get<Utils::Property::Energy>();
210 XyzHandler::write(trajectory, *calc->getStructure(), std::to_string(lastEnergy));
212 _logger->error <<
"TS Optimization failed with error!" <<
Core::Log::endl;
216 _logger->error << boost::current_exception_diagnostic_information() <<
Core::Log::endl;
221 int maxiter = settings.getInt(
"convergence_max_iterations");
222 if (cycles < maxiter) {
227 auto qmmmEnergy = calc->calculate(
"QMMM calculation").get<Utils::Property::Energy>();
228 cout << Core::Log::endl <<
" The final QM/MM energy is " << qmmmEnergy << Core::Log::endl <<
Core::Log::endl;
232 cout << Core::Log::endl
233 <<
" Stopped after " << maxiter <<
" iterations." << Core::Log::endl
236 throw std::runtime_error(
"Problem: TS optimization did not converge.");
241 systems[partialOutput] = calc;
242 boost::filesystem::path xyzfile(partialOutput +
".xyz");
243 std::ofstream xyz((dir / xyzfile).
string(), std::ofstream::out);
244 XyzHandler::write(xyz, *(calc->getStructure()));
247 return cycles < maxiter;
251 std::tuple<double, double> extractWeightsForModeSelection(Utils::UniversalSettings::ValueCollection& taskSettings)
const {
252 double wavenumberWeight = taskSettings.extract(
"automatic_mode_selection_wavenumber_weight", 0.5);
253 if (wavenumberWeight < 0.0 || wavenumberWeight - 1.0 > 1e-12) {
254 _logger->output <<
" Weights for the automatic mode selection must be between 0.0 and 1.0." << Core::Log::endl
255 <<
" Resetting wavenumber weight to default (0.5)." <<
Core::Log::endl;
256 wavenumberWeight = 0.5;
258 double contributionWeight = taskSettings.extract(
"automatic_mode_selection_contribution_weight", 0.5);
259 if (contributionWeight < 0.0 || contributionWeight - 1.0 > 1e-12) {
260 _logger->output <<
" Weights for the automatic mode selection must be between 0.0 and 1.0." << Core::Log::endl
261 <<
" Resetting contribution weight to default (0.5)." <<
Core::Log::endl;
262 contributionWeight = 0.5;
265 if (std::fabs(wavenumberWeight + contributionWeight - 1.0) > 1e-12) {
266 _logger->output <<
" Weights for the automatic mode selection must add up to 1.0." << Core::Log::endl
268 wavenumberWeight = 0.5;
269 contributionWeight = 0.5;
271 return std::make_tuple(wavenumberWeight, contributionWeight);
274 int selectMode(Core::Calculator& calc,
const std::vector<int>& relevantAtoms,
double wavenumberWeight,
275 double contributionWeight)
const {
276 _logger->output <<
" Automatically selecting normal mode " <<
Core::Log::endl;
277 int nAtoms = calc.getStructure()->size();
279 Eigen::MatrixXd sqRootMasses = Eigen::MatrixXd::Ones(nAtoms, 3);
280 for (
int index = 0; index < nAtoms; index++) {
281 if (index >= nAtoms) {
282 throw std::logic_error(
"You gave an atom index larger than the number of atoms in automatic_mode_selection. "
283 "This does not make sense.");
287 Utils::NormalModesContainer modes = getNormalModes(calc);
288 auto waveNumbers = modes.getWaveNumbers();
289 std::vector<double> contributions;
290 std::vector<double> imWaveNumbers;
292 for (
int i = 0; i < static_cast<int>(waveNumbers.size()); ++i) {
293 if (waveNumbers[i] >= 0.0) {
295 throw std::runtime_error(
"Structure has no imaginary normal mode.");
299 const auto& mode = modes.getMode(i);
300 auto massWeightedMode = mode.cwiseProduct(sqRootMasses).normalized();
302 double contribution = 0.0;
304 for (
int j = 0; j < mode.size(); ++j) {
305 if (std::find(relevantAtoms.begin(), relevantAtoms.end(), j) != relevantAtoms.end()) {
307 contribution += massWeightedMode.row(j).squaredNorm();
310 contributions.push_back(contribution);
311 imWaveNumbers.push_back(waveNumbers[i]);
313 Eigen::VectorXd imWaveVec = Eigen::Map<Eigen::VectorXd>(imWaveNumbers.data(),
static_cast<int>(imWaveNumbers.size()), 1);
314 Eigen::VectorXd contributionsVec =
315 Eigen::Map<Eigen::VectorXd>(contributions.data(),
static_cast<int>(contributions.size()), 1);
317 imWaveVec *= 1.0 / imWaveVec(0);
319 Eigen::VectorXd modeScore = imWaveVec * wavenumberWeight + contributionsVec * contributionWeight;
320 _logger->output <<
" Mode score considering contribution (" << std::to_string(contributionWeight)
321 <<
") and wavenumber (" << std::to_string(wavenumberWeight) <<
")." <<
Core::Log::endl;
324 Eigen::Index maxRow = 0;
325 modeScore.maxCoeff(&maxRow);
326 int selection =
static_cast<int>(maxRow);
328 _logger->output <<
" Automatically selected mode " << std::to_string(selection) <<
" with wave number "
329 << std::to_string(waveNumbers[selection]) <<
" cm^-1" <<
Core::Log::endl;
333 static Utils::NormalModesContainer getNormalModes(Core::Calculator& calc) {
334 const auto previousResults = calc.results();
335 bool calculationRequired =
false;
336 Utils::PropertyList requiredProperties = Utils::Property::Energy | Utils::Property::Gradients;
337 if (!calc.results().has<Utils::Property::Thermochemistry>() &&
338 calc.possibleProperties().containsSubSet(Utils::Property::Thermochemistry)) {
339 calculationRequired =
true;
340 requiredProperties.addProperty(Utils::Property::Thermochemistry);
343 if (calc.possibleProperties().containsSubSet(Utils::Property::PartialHessian)) {
344 requiredProperties.addProperty(Utils::Property::PartialHessian);
346 else if (calc.possibleProperties().containsSubSet(Utils::Property::Hessian)) {
347 requiredProperties.addProperty(Utils::Property::Hessian);
350 else if (!calc.results().has<Utils::Property::PartialHessian>() &&
351 calc.possibleProperties().containsSubSet(Utils::Property::PartialHessian)) {
352 requiredProperties.addProperty(Utils::Property::PartialHessian);
353 calculationRequired =
true;
355 else if (!calc.results().has<Utils::Property::Hessian>() &&
356 calc.possibleProperties().containsSubSet(Utils::Property::Hessian)) {
357 requiredProperties.addProperty(Utils::Property::Hessian);
358 calculationRequired =
true;
360 if (calculationRequired) {
361 calc.setRequiredProperties(requiredProperties);
362 auto results = calc.calculate(
"Hessian Calculation");
363 calc.results() = previousResults + results;
365 auto system = calc.getStructure();
366 if (calc.results().has<Utils::Property::PartialHessian>()) {
367 Utils::PartialHessian hessian = calc.results().get<Utils::Property::PartialHessian>();
369 return Utils::NormalModeAnalysis::calculateNormalModes(hessian, system->getElements(), system->getPositions(),
372 else if (calc.results().has<Utils::Property::Hessian>()) {
373 Utils::HessianMatrix hessian = calc.results().get<Utils::Property::Hessian>();
375 return Utils::NormalModeAnalysis::calculateNormalModes(hessian, system->getElements(), system->getPositions(),
379 throw std::runtime_error(
"Calculator is missing a Hessian property");
383 inline std::shared_ptr<Utils::GeometryOptimizerBase>
384 constructOptimizer(std::shared_ptr<Core::Calculator>& calc, std::string type, std::string mmType,
bool isQmmm,
385 Utils::UniversalSettings::ValueCollection& taskSettings,
const std::vector<int>& relevantAtoms,
386 const std::tuple<double, double>& modeSelectionWeights,
bool testRunOnly)
const {
388 std::transform(type.begin(), type.end(), type.begin(), ::tolower);
389 const std::array<std::string, 5> evfSynonyms = {
"ef",
"ev",
"evf",
"eigenvectorfollowing",
"eigenvector_following"};
391 if (type ==
"bofill") {
392 return std::make_shared<Utils::GeometryOptimizer<Utils::Bofill>>(*calc);
394 if (std::find(evfSynonyms.begin(), evfSynonyms.end(), type) != evfSynonyms.end()) {
395 return std::make_shared<Utils::GeometryOptimizer<Utils::EigenvectorFollowing>>(*calc);
397 if (type ==
"dimer") {
398 auto optimizer = std::make_shared<Utils::GeometryOptimizer<Utils::Dimer>>(*calc);
399 handleDimerOptimizer(optimizer, taskSettings, calc, relevantAtoms, modeSelectionWeights, testRunOnly);
402 throw std::runtime_error(
403 "Unknown Optimizer requested for TS optimization, available are: Bofill, EVF and Dimer!");
406 std::transform(mmType.begin(), mmType.end(), mmType.begin(), ::tolower);
407 if (type ==
"bofill") {
408 if (mmType ==
"bfgs") {
409 return std::make_shared<Utils::QmmmTransitionStateOptimizer<Utils::Bofill, Utils::Bfgs>>(calc);
411 if (mmType ==
"lbfgs") {
412 return std::make_shared<Utils::QmmmTransitionStateOptimizer<Utils::Bofill, Utils::Lbfgs>>(calc);
414 if (mmType ==
"sd" || mmType ==
"steepestdescent") {
415 return std::make_shared<Utils::QmmmTransitionStateOptimizer<Utils::Bofill, Utils::SteepestDescent>>(calc);
417 throw std::runtime_error(
418 "Unknown MMOptimizer requested for a geometry optimization, available are: SD, BFGS and LBFGS!");
420 if (std::find(evfSynonyms.begin(), evfSynonyms.end(), type) != evfSynonyms.end()) {
421 if (mmType ==
"bfgs") {
422 return std::make_shared<Utils::QmmmTransitionStateOptimizer<Utils::EigenvectorFollowing, Utils::Bfgs>>(calc);
424 if (mmType ==
"lbfgs") {
425 return std::make_shared<Utils::QmmmTransitionStateOptimizer<Utils::EigenvectorFollowing, Utils::Lbfgs>>(calc);
427 if (mmType ==
"sd" || mmType ==
"steepestdescent") {
428 return std::make_shared<Utils::QmmmTransitionStateOptimizer<Utils::EigenvectorFollowing, Utils::SteepestDescent>>(calc);
430 throw std::runtime_error(
431 "Unknown MMOptimizer requested for a geometry optimization, available are: SD, BFGS and LBFGS!");
433 if (type ==
"dimer") {
434 if (mmType ==
"bfgs") {
435 auto optimizer = std::make_shared<Utils::QmmmTransitionStateOptimizer<Utils::Dimer, Utils::Bfgs>>(calc);
436 handleDimerOptimizer(optimizer->qmOptimizer, taskSettings, calc, relevantAtoms, modeSelectionWeights, testRunOnly);
439 if (mmType ==
"lbfgs") {
440 auto optimizer = std::make_shared<Utils::QmmmTransitionStateOptimizer<Utils::Dimer, Utils::Lbfgs>>(calc);
441 handleDimerOptimizer(optimizer->qmOptimizer, taskSettings, calc, relevantAtoms, modeSelectionWeights, testRunOnly);
444 if (mmType ==
"sd" || mmType ==
"steepestdescent") {
445 auto optimizer = std::make_shared<Utils::QmmmTransitionStateOptimizer<Utils::Dimer, Utils::SteepestDescent>>(calc);
446 handleDimerOptimizer(optimizer->qmOptimizer, taskSettings, calc, relevantAtoms, modeSelectionWeights, testRunOnly);
449 throw std::runtime_error(
450 "Unknown MMOptimizer requested for a geometry optimization, available are: SD, BFGS and LBFGS!");
452 throw std::runtime_error(
"Unknown Optimizer requested for TS optimization, available are: Bofill, EVF and Dimer!");
455 inline void handleDimerOptimizer(std::shared_ptr<Utils::GeometryOptimizer<Utils::Dimer>>& optimizer,
456 Utils::UniversalSettings::ValueCollection& taskSettings,
457 const std::shared_ptr<Core::Calculator>& calc,
const std::vector<int>& relevantAtoms,
458 const std::tuple<double, double>& modeSelectionWeights,
bool testRunOnly)
const {
459 using XyzHandler = Utils::XyzStreamHandler;
461 auto coordinateSystem = optimizer->coordinateSystem;
462 if (taskSettings.valueExists(
"geoopt_coordinate_system")) {
463 coordinateSystem = Utils::CoordinateSystemInterpreter::getCoordinateSystemFromString(
464 taskSettings.getString(
"geoopt_coordinate_system"));
468 bool useEigenvectorForFirstStep = taskSettings.extract(
"dimer_calculate_hessian_once",
false);
469 if (!useEigenvectorForFirstStep && !relevantAtoms.empty()) {
470 _logger->warning <<
"Specified Dimer optimizer with automatic mode selection, this means that a Hessian has to "
474 if (taskSettings.valueExists(
"dimer_follow_mode") || !relevantAtoms.empty()) {
475 useEigenvectorForFirstStep =
true;
477 int followedMode = taskSettings.extract(
"dimer_follow_mode", 0);
478 std::string guessVectorFileName = taskSettings.extract(
"dimer_guess_vector_file", std::string(
""));
479 std::vector<std::string> discreteGuessesFileNames =
480 taskSettings.extract(
"dimer_discrete_guesses", std::vector<std::string>{
"",
""});
483 if (discreteGuessesFileNames.size() != 2) {
484 throw std::runtime_error(
"Problem with discrete guesses. You may only give list with two filepaths.");
487 if (!testRunOnly && !useEigenvectorForFirstStep && calc->results().has<Utils::Property::Hessian>()) {
488 _logger->warning <<
"Did not specify to use eigenvector for initialization of Dimer, although your system "
489 "already includes a Hessian. The Hessian will therefore be ignored. "
490 "If you want to change that, activate the setting 'dimer_calculate_hessian_once': true"
493 if (useEigenvectorForFirstStep) {
494 if (!guessVectorFileName.empty()) {
495 throw std::logic_error(
"You specified to use the eigenvector of the Hessian as Dimer initialization, "
496 "but also specified a file to read a guess vector for the initialization. "
499 if (!discreteGuessesFileNames[0].empty()) {
500 throw std::logic_error(
"You specified to use the eigenvector of the Hessian as Dimer initialization, "
501 "but also specified files to read discrete guesses for the initialization. "
505 else if (!guessVectorFileName.empty() && !discreteGuessesFileNames[0].empty()) {
506 throw std::logic_error(
"You specified a file to read a guess vector for the Dimer initialization. "
507 "but also specified files to read discrete guesses for the initialization. "
515 else if (useEigenvectorForFirstStep) {
516 optimizer->optimizer.skipFirstRotation =
true;
517 auto modes = getNormalModes(*calc);
520 bool isQmmm =
static_cast<bool>(castedCalc);
521 std::unique_ptr<Utils::AtomCollection> system;
524 if (underlying.size() != 2 || !underlying[0]) {
525 throw std::runtime_error(
"Embedding calculator has non-initialized underlying calculators");
527 system = underlying[0]->getStructure();
530 system = calc->getStructure();
533 throw std::runtime_error(
"Calculator has non-initialized structure");
535 if (!relevantAtoms.empty()) {
536 followedMode = selectMode(*calc, relevantAtoms, std::get<0>(modeSelectionWeights), std::get<1>(modeSelectionWeights));
538 auto mode = modes.getMode(followedMode);
540 if (coordinateSystem == Utils::CoordinateSystem::Internal) {
542 Utils::InternalCoordinates internalCoordinates(*system);
543 optimizer->optimizer.guessVector = std::make_shared<Eigen::VectorXd>(internalCoordinates.coordinatesToInternal(mode));
545 else if (coordinateSystem == Utils::CoordinateSystem::CartesianWithoutRotTrans) {
548 std::unique_ptr<Eigen::MatrixXd> hessian;
549 if (calc->results().has<Utils::Property::Hessian>()) {
550 hessian = std::make_unique<Eigen::MatrixXd>(calc->results().get<Utils::Property::Hessian>());
552 else if (calc->results().has<Utils::Property::PartialHessian>()) {
553 Eigen::MatrixXd partialHessian = calc->results().get<Utils::Property::PartialHessian>().getMatrix();
557 throw std::runtime_error(
"Calculator only has a PartialHessian result, but is no embedding calculator.");
559 auto size = 3 * system->size();
560 auto partialSize = partialHessian.rows();
561 hessian = std::make_unique<Eigen::MatrixXd>(size, size);
563 hessian->block(0, 0, partialSize, partialSize) = partialHessian;
565 hessian->block(partialSize, 0, size - partialSize, size).setZero();
569 throw std::runtime_error(
"Calculate Hessian, but do not have it in Results, "
570 "something went wrong in the Dimer optimizer preparation");
572 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es;
573 es.compute(*hessian);
574 int nEigenValues = es.eigenvalues().size();
575 Eigen::MatrixXd eigenValueMatrix = Eigen::MatrixXd::Identity(nEigenValues, nEigenValues);
576 eigenValueMatrix.diagonal() = es.eigenvalues();
577 eigenValueMatrix(followedMode, followedMode) = -es.eigenvalues()[followedMode];
578 Eigen::MatrixXd switchedHessian = es.eigenvectors() * eigenValueMatrix * es.eigenvectors().transpose();
580 Utils::InternalCoordinates internalCoordinates(*system,
true);
581 optimizer->optimizer.guessVector = std::make_shared<Eigen::VectorXd>(internalCoordinates.coordinatesToInternal(mode));
583 Eigen::MatrixXd switchedTransformedHessian = internalCoordinates.hessianToInternal(switchedHessian);
584 optimizer->optimizer.invH = switchedTransformedHessian.inverse();
586 else if (coordinateSystem == Utils::CoordinateSystem::Cartesian) {
588 optimizer->optimizer.guessVector =
589 std::make_shared<Eigen::VectorXd>(Eigen::Map<const Eigen::VectorXd>(mode.data(), mode.cols() * mode.rows()));
592 throw std::logic_error(
"Unknown coordinate system " +
593 Utils::CoordinateSystemInterpreter::getStringFromCoordinateSystem(coordinateSystem));
597 else if (!guessVectorFileName.empty()) {
599 std::vector<double> container;
601 std::ifstream file(guessVectorFileName, std::ios::in);
602 if (!file.is_open()) {
603 throw std::runtime_error(
"Problem when opening file " + guessVectorFileName);
605 while (file >> num) {
606 container.push_back(num);
609 Eigen::Map<Eigen::VectorXd> guessVector(container.data(), container.size());
610 auto system = calc->getStructure();
612 if (coordinateSystem != Utils::CoordinateSystem::Cartesian &&
613 static_cast<unsigned long>(guessVector.size()) == 3 * system->getElements().size()) {
614 std::shared_ptr<Utils::InternalCoordinates> transformation =
nullptr;
615 if (coordinateSystem == Utils::CoordinateSystem::Internal) {
616 transformation = std::make_shared<Utils::InternalCoordinates>(*system);
618 else if (coordinateSystem == Utils::CoordinateSystem::CartesianWithoutRotTrans) {
619 transformation = std::make_shared<Utils::InternalCoordinates>(*system,
true);
622 Utils::PositionCollection guessPosition =
623 Eigen::Map<Utils::PositionCollection>(guessVector.data(), guessVector.size() / 3, 3);
625 optimizer->optimizer.guessVector =
626 std::make_shared<Eigen::VectorXd>(transformation->coordinatesToInternal(guessPosition));
628 catch (
const Utils::InternalCoordinatesException& e) {
629 _logger->warning <<
"Could not transform given Cartesian coordinates to Internal coordinates.\n"
630 <<
"Omitting guess and initializing dimer with random vector. " <<
Core::Log::endl;
634 else if (coordinateSystem == Utils::CoordinateSystem::Cartesian &&
635 static_cast<unsigned long>(guessVector.size()) != 3 * system->getElements().size()) {
637 std::shared_ptr<Utils::InternalCoordinates> transformation =
nullptr;
638 if (static_cast<unsigned long>(guessVector.size()) == 3 * system->getElements().size() - 6 ||
639 static_cast<unsigned long>(guessVector.size()) == 3 * system->getElements().size() - 5) {
641 transformation = std::make_shared<Utils::InternalCoordinates>(*system,
true);
644 transformation = std::make_shared<Utils::InternalCoordinates>(*system);
648 auto cartPos0 = system->getPositions();
649 auto ircVec0 = transformation->coordinatesToInternal(cartPos0);
651 Eigen::VectorXd ircVec1 = ircVec0 + guessVector;
653 auto cartPos1 = transformation->coordinatesToCartesian(ircVec1);
654 Utils::PositionCollection posDiff = cartPos1 - cartPos0;
655 optimizer->optimizer.guessVector =
656 std::make_shared<Eigen::VectorXd>(Eigen::Map<Eigen::VectorXd>(posDiff.data(), posDiff.size()));
658 catch (
const Utils::InternalCoordinatesException& e) {
659 _logger->warning <<
"Could not transform given non-Cartesian coordinates to Cartesian.\n"
660 <<
"Omitting guess and initializing dimer with random vector. " <<
Core::Log::endl;
665 optimizer->optimizer.guessVector = std::make_shared<Eigen::VectorXd>(guessVector);
669 else if (!discreteGuessesFileNames.at(0).empty() && !discreteGuessesFileNames.at(1).empty()) {
670 Utils::AtomCollection system;
672 std::string filepath1 = discreteGuessesFileNames.at(0);
673 std::ifstream file1(filepath1, std::ios::in);
674 if (!file1.is_open()) {
675 throw std::runtime_error(
"Problem when opening file " + filepath1);
677 system = XyzHandler::read(file1);
678 auto position1 = system.getPositions();
682 std::string filepath2 = discreteGuessesFileNames.at(1);
683 std::ifstream file2(filepath2, std::ios::in);
684 if (!file2.is_open()) {
685 throw std::runtime_error(
"Problem when opening file " + filepath2);
687 system = XyzHandler::read(file2);
688 auto position2 = system.getPositions();
691 Utils::PositionCollection positionDiff = position2 - position1;
692 Eigen::Map<Eigen::VectorXd> mode(positionDiff.data(), positionDiff.size());
693 std::shared_ptr<Utils::InternalCoordinates> transformation =
nullptr;
694 if (coordinateSystem == Utils::CoordinateSystem::Internal) {
695 transformation = std::make_shared<Utils::InternalCoordinates>(system);
697 else if (coordinateSystem == Utils::CoordinateSystem::CartesianWithoutRotTrans) {
698 transformation = std::make_shared<Utils::InternalCoordinates>(system,
true);
700 if (transformation) {
701 optimizer->optimizer.guessVector =
702 std::make_shared<Eigen::VectorXd>(transformation->coordinatesToInternal(positionDiff));
705 optimizer->optimizer.guessVector = std::make_shared<Eigen::VectorXd>(mode);
714 #endif // READUCT_TSOPTIMIZATIONTASK_H_
void warningIfMultipleOutputsGiven() const
Warn if more than one output system was specified.
Definition: Task.h:107
static double mass(ElementType e)
static std::ostream & endl(std::ostream &os)
const std::vector< std::string > & input() const
Getter for the expected names of the input systems.
Definition: Task.h:85
std::string name() const override
Getter for the tasks name.
Definition: TsOptimizationTask.h:48
TsOptimizationTask(std::vector< std::string > input, std::vector< std::string > output, std::shared_ptr< Core::Log > logger=nullptr)
Construct a new TsOptimizationTask.
Definition: TsOptimizationTask.h:44
virtual std::vector< std::shared_ptr< Calculator > > getUnderlyingCalculators() const =0
static void write(format f, const std::string &fileName, const MolecularTrajectory &m, const BondOrderCollection &bondOrders)
bool run(SystemsMap &systems, Utils::UniversalSettings::ValueCollection taskSettings, bool testRunOnly=false, std::vector< std::function< void(const int &, const Utils::AtomCollection &, const Utils::Results &, const std::string &)>> observers={}) const final
Executes the actual task represented by this class.
Definition: TsOptimizationTask.h:52
void warningIfMultipleInputsGiven() const
Warn if more than one input system was specified.
Definition: Task.h:98
const std::vector< std::string > & output() const
Getter for the names of the output systems generated by this task.
Definition: Task.h:92
The base class for all tasks in Readuct.
Definition: Task.h:29
Definition: TsOptimizationTask.h:36