7 #ifndef READUCT_IRCTASK_H_
8 #define READUCT_IRCTASK_H_
21 #include <boost/exception/diagnostic_information.hpp>
22 #include <boost/filesystem.hpp>
40 IrcTask(std::vector<std::string>
input, std::vector<std::string>
output, std::shared_ptr<Core::Log> logger =
nullptr)
41 :
Task(std::move(input), std::move(output), std::move(logger)) {
44 std::string
name()
const override {
45 return "IRC Optimizations";
50 observers = {})
const final {
53 if (_output.size() == 1) {
55 <<
" Warning: To store IRC results in a new location two output systems have to be specified.\n";
58 bool silentCalculator = taskSettings.extract(
"silent_stdout_calculator",
true);
59 std::shared_ptr<Core::Calculator> calc;
62 calc = copyCalculator(systems, _input.front(),
name());
63 Utils::CalculationRoutines::setLog(*calc,
true,
true, !silentCalculator);
66 if (calc->getStructure()->size() == 1) {
67 throw std::runtime_error(
"Cannot perform IRC task for monoatomic systems.");
72 std::string optimizertype = taskSettings.extract(
"optimizer", std::string{
"SD"});
73 int mode = taskSettings.extract(
"irc_mode", 0);
75 bool stopOnError = stopOnErrorExtraction(taskSettings);
76 std::transform(optimizertype.begin(), optimizertype.end(), optimizertype.begin(), ::toupper);
77 std::shared_ptr<Utils::IrcOptimizerBase> optimizer;
78 if (optimizertype ==
"LBFGS") {
79 auto tmp = std::make_shared<Utils::IrcOptimizer<Utils::Lbfgs>>(*calc);
81 optimizer = std::move(tmp);
83 else if (optimizertype ==
"BFGS") {
84 auto tmp = std::make_shared<Utils::IrcOptimizer<Utils::Bfgs>>(*calc);
86 optimizer = std::move(tmp);
88 else if (optimizertype ==
"SD" || optimizertype ==
"STEEPESTDESCENT") {
89 auto tmp = std::make_shared<Utils::IrcOptimizer<Utils::SteepestDescent>>(*calc);
91 optimizer = std::move(tmp);
94 throw std::runtime_error(
95 "Unknown Optimizer requested for an IRC optimization, available are: SD, BFGS and LBFGS!");
98 auto settings = optimizer->getSettings();
99 settings.merge(taskSettings);
100 if (!settings.valid()) {
101 settings.throwIncorrectSettings();
103 optimizer->setSettings(settings);
104 if (!testRunOnly && !Utils::GeometryOptimization::settingsMakeSense(*optimizer)) {
105 throw std::logic_error(
"The given calculator settings are too inaccurate for the given convergence criteria of "
106 "this optimization Task");
116 auto propertyToGet = Utils::Property::Hessian;
117 if (calc->possibleProperties().containsSubSet(Utils::Property::Hessian)) {
118 propertyToGet = Utils::Property::Hessian;
120 else if (calc->possibleProperties().containsSubSet(Utils::Property::PartialHessian)) {
121 propertyToGet = Utils::Property::PartialHessian;
124 throw std::runtime_error(
"The calculator does not provide a Hessian or a partial Hessian!");
126 calc->setRequiredProperties(propertyToGet);
127 calc->calculate(
"Hessian Calculation");
129 Utils::NormalModesContainer modes;
130 auto system = calc->getStructure();
131 if (propertyToGet == Utils::Property::Hessian) {
132 auto hessian = calc->results().get<Utils::Property::Hessian>();
133 modes = Utils::NormalModeAnalysis::calculateNormalModes(hessian, system->getElements(), system->getPositions());
135 else if (propertyToGet == Utils::Property::PartialHessian) {
136 auto hessian = calc->results().get<Utils::Property::PartialHessian>();
137 if (mode >= hessian.getNumberOfAtoms()) {
138 throw std::runtime_error(
"The chosen normal mode number is larger than the number of atoms the partial "
139 "Hessian was constructed with!");
141 modes = Utils::NormalModeAnalysis::calculateNormalModes(hessian, system->getElements(), system->getPositions());
144 throw std::runtime_error(
"Have not implemented normal mode analysis for the property " +
145 std::string(Utils::propertyTypeName(propertyToGet)));
147 if (mode < 0 || mode > modes.size() - 1) {
148 throw std::runtime_error(
149 "The chosen normal mode number is smaller than 0 or larger than the number of modes present!");
151 auto ircMode = modes.getMode(mode);
152 auto ircModeVector = Eigen::Map<const Eigen::VectorXd>(ircMode.data(), ircMode.cols() * ircMode.rows());
157 auto cout = _logger->output;
158 cout <<
"\n IRC Optimization: Forward\n\n";
160 double oldEnergy = 0.0;
161 Eigen::VectorXd oldParams;
163 using Writer = Utils::XyzStreamHandler;
165 const std::string& partialOutput = ((_output.size() > 1) ? _output[0] : _input[0]);
166 boost::filesystem::path dirF(partialOutput);
167 boost::filesystem::create_directory(dirF);
168 boost::filesystem::path trjfileF(partialOutput +
".irc.forward.trj.xyz");
169 std::ofstream trajectoryF((dirF / trjfileF).
string(), std::ofstream::out);
170 auto forward = [&](
const int& cycle,
const double& energy,
const Eigen::VectorXd& params) {
171 if (oldParams.size() != params.size()) {
175 cout.printf(
"%7s %16s %16s %16s %16s\n",
"Cycle",
"Energy",
"Energy Diff.",
"Step RMS",
"Max. Step");
177 auto diff = (params - oldParams).eval();
178 cout.printf(
"%7d %+16.9f %+16.9f %+16.9f %+16.9f\n", cycle, energy, energy - oldEnergy,
179 sqrt(diff.squaredNorm() / diff.size()), diff.cwiseAbs().maxCoeff());
182 auto structure = calc->getStructure();
183 Writer::write(trajectoryF, *structure, std::to_string(energy));
185 optimizer->addObserver(forward);
188 auto customObserversForward = [&calc, &observers](
const int& cycle,
const double& ,
189 const Eigen::VectorXd& ) {
190 for (
auto& observer : observers) {
191 auto atoms = calc->getStructure();
192 Utils::Results& results = calc->results();
193 observer(cycle, *atoms, results,
"irc_forward");
196 optimizer->addObserver(customObserversForward);
199 auto structure = systems.at(_input[0])->getStructure();
201 bool forwardAborted =
false;
203 cycles = optimizer->optimize(*structure, *_logger, ircModeVector,
true);
206 Writer::write(trajectoryF, *calc->getStructure());
212 _logger->error << boost::current_exception_diagnostic_information() <<
Core::Log::endl;
213 forwardAborted =
true;
217 int maxiter = settings.getInt(
"convergence_max_iterations");
218 const bool forwardConverged = cycles < maxiter && !forwardAborted;
219 if (forwardConverged) {
224 else if (!forwardAborted) {
229 throw std::runtime_error(
"Problem: IRC optimization did not converge.");
234 if (!forwardAborted) {
236 if (_output.size() > 1) {
237 systems[_output[0]] = calc->clone();
238 boost::filesystem::path xyzfile(_output[0] +
".xyz");
239 std::ofstream xyz((dirF / xyzfile).
string(), std::ofstream::out);
240 Writer::write(xyz, *(calc->getStructure()));
244 boost::filesystem::path xyzfile(_input[0] +
".irc.forward.xyz");
245 std::ofstream xyz((dirF / xyzfile).
string(), std::ofstream::out);
246 Writer::write(xyz, *(calc->getStructure()));
254 cout <<
"\n IRC Optimization: Backward\n\n";
259 optimizer->setSettings(settings);
262 boost::filesystem::path dirB(((_output.size() > 1) ? _output[1] : _input[0]));
263 boost::filesystem::create_directory(dirB);
264 boost::filesystem::path trjfileB(((_output.size() > 1) ? _output[1] : _input[0]) +
".irc.backward.trj.xyz");
265 std::ofstream trajectoryB((dirB / trjfileB).
string(), std::ofstream::out);
266 auto backward = [&](
const int& cycle,
const double& energy,
const Eigen::VectorXd& params) {
267 if (oldParams.size() != params.size()) {
271 cout.printf(
"%7s %16s %16s %16s %16s\n",
"Cycle",
"Energy",
"Energy Diff.",
"Step RMS",
"Max. Step");
273 auto diff = (params - oldParams).eval();
274 cout.printf(
"%7d %+16.9f %+16.9f %+16.9f %+16.9f\n", cycle, energy, energy - oldEnergy,
275 sqrt(diff.squaredNorm() / diff.size()), diff.cwiseAbs().maxCoeff());
278 auto structure = calc->getStructure();
279 Writer::write(trajectoryB, *structure, std::to_string(energy));
281 optimizer->clearObservers();
282 optimizer->addObserver(backward);
285 auto customObserversBackward = [&calc, &observers](
const int& cycle,
const double& ,
286 const Eigen::VectorXd& ) {
287 for (
auto& observer : observers) {
288 auto atoms = calc->getStructure();
289 Utils::Results& results = calc->results();
290 observer(cycle, *atoms, results,
"irc_backward");
293 optimizer->addObserver(customObserversBackward);
296 structure = systems.at(_input[0])->getStructure();
299 cycles = optimizer->optimize(*structure, *_logger, ircModeVector,
false);
302 Writer::write(trajectoryB, *calc->getStructure());
304 _logger->error <<
"Calculation in IRC backward optimization failed!" <<
Core::Log::endl;
308 _logger->error << boost::current_exception_diagnostic_information() <<
Core::Log::endl;
313 maxiter = settings.getInt(
"convergence_max_iterations");
314 if (cycles < maxiter) {
315 cout << Core::Log::endl
316 <<
" Converged after " << cycles <<
" iterations." << Core::Log::endl
320 cout << Core::Log::endl
321 <<
" Stopped after " << maxiter <<
" iterations." << Core::Log::endl
324 throw std::runtime_error(
"Problem: IRC optimization did not converge.");
329 if (_output.size() > 1) {
330 systems[_output[1]] = calc->clone();
331 boost::filesystem::path xyzfile(_output[1] +
".xyz");
332 std::ofstream xyz((dirB / xyzfile).
string(), std::ofstream::out);
333 Writer::write(xyz, *(calc->getStructure()));
337 boost::filesystem::path xyzfile(_input[0] +
".irc.backward.xyz");
338 std::ofstream xyz((dirB / xyzfile).
string(), std::ofstream::out);
339 Writer::write(xyz, *(calc->getStructure()));
343 return cycles < maxiter && forwardConverged;
350 #endif // READUCT_IRCTASK_H_
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: IrcTask.h:48
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
IrcTask(std::vector< std::string > input, std::vector< std::string > output, std::shared_ptr< Core::Log > logger=nullptr)
Construct a new IrcTask.
Definition: IrcTask.h:40
void warningIfMultipleInputsGiven() const
Warn if more than one input system was specified.
Definition: Task.h:98
std::string name() const override
Getter for the tasks name.
Definition: IrcTask.h:44
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
A task to run an IRC optimization using a given normal mode.
Definition: IrcTask.h:32