Scine::Readuct  4.0.0
This is the SCINE module Readuct.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Pages
AfirOptimizationTask.h
Go to the documentation of this file.
1 
7 #ifndef READUCT_AFIROPTIMIZATIONTASK_H_
8 #define READUCT_AFIROPTIMIZATIONTASK_H_
9 
10 /* Readuct */
11 #include "Tasks/Task.h"
12 /* Scine */
20 /* External */
21 #include <boost/exception/diagnostic_information.hpp>
22 #include <boost/filesystem.hpp>
23 /* std c++ */
24 #include <cstdio>
25 #include <fstream>
26 #include <iostream>
27 
28 namespace Scine {
29 namespace Readuct {
30 
31 class AfirOptimizationTask : public Task {
32  public:
39  AfirOptimizationTask(std::vector<std::string> input, std::vector<std::string> output, std::shared_ptr<Core::Log> logger = nullptr)
40  : Task(std::move(input), std::move(output), std::move(logger)) {
41  }
42 
43  std::string name() const override {
44  return "AFIR Optimization";
45  }
46 
47  bool run(SystemsMap& systems, Utils::UniversalSettings::ValueCollection taskSettings, bool testRunOnly = false) const final {
50 
51  bool silentCalculator = taskSettings.extract("silent_stdout_calculator", true);
52  std::shared_ptr<Core::Calculator> calc;
53  if (!testRunOnly) { // leave out in case of task chaining --> attention calc is NULL
54  // Note: _input is guaranteed not to be empty by Task constructor
55  calc = copyCalculator(systems, _input.front(), name());
56  Utils::CalculationRoutines::setLog(*calc, true, true, !silentCalculator);
57 
58  // Check system size
59  if (calc->getStructure()->size() == 1) {
60  throw std::runtime_error("Cannot calculate AFIR for monoatomic systems.");
61  }
62  }
63 
64  // Generate optimizer
65  auto optimizertype = taskSettings.extract("optimizer", std::string{"BFGS"});
66  std::transform(optimizertype.begin(), optimizertype.end(), optimizertype.begin(), ::toupper);
67  std::shared_ptr<Utils::AfirOptimizerBase> optimizer;
68  if (optimizertype == "LBFGS") {
69  auto tmp = std::make_shared<Utils::AfirOptimizer<Utils::Lbfgs>>(*calc);
70  tmp->optimizer.useTrustRadius = true;
71  tmp->optimizer.trustRadius = 0.1;
72  optimizer = std::move(tmp);
73  }
74  else if (optimizertype == "BFGS") {
75  auto tmp = std::make_shared<Utils::AfirOptimizer<Utils::Bfgs>>(*calc);
76  tmp->optimizer.useTrustRadius = true;
77  tmp->optimizer.trustRadius = 0.1;
78  optimizer = std::move(tmp);
79  }
80  else if (optimizertype == "SD" || optimizertype == "STEEPESTDESCENT") {
81  auto tmp = std::make_shared<Utils::AfirOptimizer<Utils::SteepestDescent>>(*calc);
82  optimizer = std::move(tmp);
83  }
84  else {
85  throw std::runtime_error("Unknown Optimizer requested for AFIR, available are: SD, BFGS and LBFGS!");
86  }
87 
88  // Read and delete special settings
89  bool stopOnError = stopOnErrorExtraction(taskSettings);
90 
91  // Apply user settings
92  auto settings = optimizer->getSettings();
93  settings.merge(taskSettings);
94  if (!settings.valid()) {
95  settings.throwIncorrectSettings();
96  }
97  optimizer->setSettings(settings);
98  if (!testRunOnly && !Utils::GeometryOptimization::settingsMakeSense(*optimizer)) {
99  throw std::logic_error("The given calculator settings are too inaccurate for the given convergence criteria of "
100  "this optimization Task");
101  }
102 
103  // If no errors encountered until here, the basic settings should be alright
104  if (testRunOnly) {
105  return true;
106  }
107 
108  // Add observer
109  // Trajectory stream
110  using Writer = Utils::XyzStreamHandler;
111  const std::string& outputSystem = (!_output.empty() ? _output.front() : _input.front());
112  boost::filesystem::path dir(outputSystem);
113  boost::filesystem::create_directory(dir);
114  boost::filesystem::path trjfile(outputSystem + ".afir.trj.xyz");
115  std::ofstream trajectory((dir / trjfile).string(), std::ofstream::out);
116  double oldEnergy = 0.0;
117  Eigen::VectorXd oldParams;
118  auto cout = _logger->output;
119  auto func = [&](const int& cycle, const double& energy, const Eigen::VectorXd& params) {
120  if (oldParams.size() != params.size()) {
121  oldParams = params;
122  }
123  if (cycle == 1) {
124  cout.printf("%7s %16s %16s %16s %16s\n", "Cycle", "Energy", "Energy Diff.", "Step RMS", "Max. Step");
125  }
126  auto diff = (params - oldParams).eval();
127  cout.printf("%7d %+16.9f %+16.9f %+16.9f %+16.9f\n", cycle, energy, energy - oldEnergy,
128  sqrt(diff.squaredNorm() / diff.size()), diff.cwiseAbs().maxCoeff());
129  oldEnergy = energy;
130  oldParams = params;
131  auto structure = calc->getStructure();
132  Writer::write(trajectory, *structure, std::to_string(energy));
133  };
134  optimizer->addObserver(func);
135 
136  // Run optimization
137  auto structure = calc->getStructure();
138  int cycles = 0;
139  try {
140  cycles = optimizer->optimize(*structure, *_logger);
141  }
142  catch (...) {
143  Writer::write(trajectory, *calc->getStructure());
144  trajectory.close();
145  _logger->error << "AFIR Optimization failed with error!" << Core::Log::endl;
146  if (stopOnError) {
147  throw;
148  }
149  _logger->error << boost::current_exception_diagnostic_information() << Core::Log::endl;
150  return false;
151  }
152  trajectory.close();
153 
154  int maxiter = settings.getInt("convergence_max_iterations");
155  if (maxiter > cycles) {
156  cout << Core::Log::endl
157  << " Converged after " << cycles << " iterations." << Core::Log::endl
158  << Core::Log::endl;
159  }
160  else {
161  cout << Core::Log::endl
162  << " Stopped after " << maxiter << " iterations." << Core::Log::endl
163  << Core::Log::endl;
164  if (stopOnError) {
165  throw std::runtime_error("Problem: AFIR optimization did not converge.");
166  }
167  }
168 
169  // Print/Store results
170  boost::filesystem::path xyzfile(outputSystem + ".xyz");
171  std::ofstream xyz((dir / xyzfile).string(), std::ofstream::out);
172  Writer::write(xyz, *(calc->getStructure()));
173  xyz.close();
174  systems[outputSystem] = std::move(calc);
175 
176  return cycles < maxiter;
177  }
178 };
179 
180 } // namespace Readuct
181 } // namespace Scine
182 
183 #endif // READUCT_AFIROPTIMIZATIONTASK_H_
std::string name() const override
Getter for the tasks name.
Definition: AfirOptimizationTask.h:43
void warningIfMultipleOutputsGiven() const
Warn if more than one output system was specified.
Definition: Task.h:99
bool run(SystemsMap &systems, Utils::UniversalSettings::ValueCollection taskSettings, bool testRunOnly=false) const final
Executes the actual task represented by this class.
Definition: AfirOptimizationTask.h:47
AfirOptimizationTask(std::vector< std::string > input, std::vector< std::string > output, std::shared_ptr< Core::Log > logger=nullptr)
Construct a new AfirOptimizationTask.
Definition: AfirOptimizationTask.h:39
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:77
Definition: AfirOptimizationTask.h:31
void warningIfMultipleInputsGiven() const
Warn if more than one input system was specified.
Definition: Task.h:90
const std::vector< std::string > & output() const
Getter for the names of the output systems generated by this task.
Definition: Task.h:84
The base class for all tasks in Readuct.
Definition: Task.h:28