7 #ifndef READUCT_GEOMETRYOPTIMIZATIONTASK_H_
8 #define READUCT_GEOMETRYOPTIMIZATIONTASK_H_
25 #include <boost/exception/diagnostic_information.hpp>
26 #include <boost/filesystem.hpp>
42 std::shared_ptr<Core::Log> logger =
nullptr)
43 :
Task(std::move(input), std::move(output), std::move(logger)) {
46 std::string
name()
const override {
47 return "Geometry Optimization";
52 observers = {})
const final {
55 bool silentCalculator = taskSettings.extract(
"silent_stdout_calculator",
true);
58 std::shared_ptr<Core::Calculator> calc;
61 calc = copyCalculator(systems, _input.front(),
name());
62 Utils::CalculationRoutines::setLog(*calc,
true,
true, !silentCalculator);
63 if (calc->name() ==
"QMMM") {
69 auto optimizertype = taskSettings.extract(
"optimizer", std::string{
"BFGS"});
70 auto unitcelloptimizertype = taskSettings.extract(
"unitcelloptimizer", std::string{
""});
71 auto optimizer = constructOptimizer(*calc, optimizertype, unitcelloptimizertype, isQmmm);
81 bool stopOnError = stopOnErrorExtraction(taskSettings);
83 auto settings = optimizer->getSettings();
84 settings.merge(taskSettings);
85 if (!testRunOnly && systems.at(_input[0])->getStructure()->size() == 1 &&
86 Utils::CoordinateSystemInterpreter::getCoordinateSystemFromString(
87 settings.getString(
"geoopt_coordinate_system")) != Utils::CoordinateSystem::Cartesian) {
92 _logger->warning <<
" Cannot treat a monoatomic system in internal coordinates. Switching to Cartesians."
95 settings.modifyString(
"geoopt_coordinate_system", Utils::CoordinateSystemInterpreter::getStringFromCoordinateSystem(
96 Utils::CoordinateSystem::Cartesian));
98 if (!settings.valid()) {
99 settings.throwIncorrectSettings();
101 optimizer->setSettings(settings);
102 if (!testRunOnly && !Utils::GeometryOptimization::settingsMakeSense(*optimizer)) {
103 throw std::logic_error(
"The given calculator settings are too inaccurate for the given convergence criteria of "
104 "this optimization Task");
109 const std::string& outputSystem = (!_output.empty() ? _output.front() : _input.front());
110 using Writer = Utils::XyzStreamHandler;
111 boost::filesystem::path dir(outputSystem);
112 boost::filesystem::create_directory(dir);
113 boost::filesystem::path trjfile(outputSystem +
".opt.trj.xyz");
114 std::ofstream trajectory((dir / trjfile).
string(), std::ofstream::out);
115 double oldEnergy = 0.0;
116 Eigen::VectorXd oldParams;
117 auto cout = _logger->output;
118 auto func = [&](
const int& cycle,
const double& energy,
const Eigen::VectorXd& params) {
119 if (oldParams.size() != params.size()) {
123 cout.printf(
"%7s %16s %16s %16s %16s\n",
"Cycle",
"Energy",
"Energy Diff.",
"Step RMS",
"Max. Step");
125 auto diff = (params - oldParams).eval();
126 cout.printf(
"%7d %+16.9f %+16.9f %+16.9f %+16.9f\n", cycle, energy, energy - oldEnergy,
127 sqrt(diff.squaredNorm() / diff.size()), diff.cwiseAbs().maxCoeff());
130 auto structure = calc->getStructure();
131 Writer::write(trajectory, *structure, std::to_string(energy));
133 optimizer->addObserver(func);
136 auto customObservers = [&calc, &observers](
const int& cycle,
const double& ,
const Eigen::VectorXd& ) {
137 for (
auto& observer : observers) {
138 auto atoms = calc->getStructure();
139 Utils::Results& results = calc->results();
140 observer(cycle, *atoms, results,
"geometry_optimization");
143 optimizer->addObserver(customObservers);
146 auto structure = calc->getStructure();
149 cycles = optimizer->optimize(*structure, *_logger);
152 Writer::write(trajectory, *calc->getStructure());
154 _logger->error <<
"Optimization failed with error!" <<
Core::Log::endl;
158 _logger->error << boost::current_exception_diagnostic_information() <<
Core::Log::endl;
163 int maxiter = settings.getInt(
"convergence_max_iterations");
164 if (cycles < maxiter) {
165 cout << Core::Log::endl
166 <<
" Converged after " << cycles <<
" iterations." << Core::Log::endl
170 cout << Core::Log::endl
171 <<
" Stopped after " << maxiter <<
" iterations." << Core::Log::endl
174 throw std::runtime_error(
"Problem: Structure optimization did not converge.");
179 systems[outputSystem] = calc;
180 boost::filesystem::path xyzfile(outputSystem +
".xyz");
181 std::ofstream xyz((dir / xyzfile).
string(), std::ofstream::out);
182 Writer::write(xyz, *(calc->getStructure()));
185 return cycles < maxiter;
188 inline static std::shared_ptr<Utils::GeometryOptimizerBase> constructOptimizer(Core::Calculator& calc, std::string type,
189 std::string cellType,
bool isQmmm) {
191 std::transform(type.begin(), type.end(), type.begin(), ::toupper);
192 std::transform(cellType.begin(), cellType.end(), cellType.begin(), ::toupper);
193 if (type ==
"LBFGS") {
194 if (cellType.empty()) {
196 return std::make_shared<Utils::QmmmGeometryOptimizer<Utils::Lbfgs>>(calc);
198 return std::make_shared<Utils::GeometryOptimizer<Utils::Lbfgs>>(calc);
201 if (cellType ==
"LBFGS") {
202 return std::make_shared<Utils::UnitCellGeometryOptimizer<Utils::Lbfgs, Utils::Lbfgs>>(calc);
204 else if (cellType ==
"BFGS") {
205 return std::make_shared<Utils::UnitCellGeometryOptimizer<Utils::Lbfgs, Utils::Bfgs>>(calc);
207 else if (cellType ==
"SD" || cellType ==
"STEEPESTDESCENT") {
208 return std::make_shared<Utils::UnitCellGeometryOptimizer<Utils::Lbfgs, Utils::SteepestDescent>>(calc);
210 throw std::runtime_error(
211 "Unknown CellOptimizer requested for a geometry optimization, available are: SD, BFGS and LBFGS!");
214 else if (type ==
"BFGS") {
215 if (cellType.empty()) {
217 return std::make_shared<Utils::QmmmGeometryOptimizer<Utils::Bfgs>>(calc);
219 return std::make_shared<Utils::GeometryOptimizer<Utils::Bfgs>>(calc);
223 if (cellType ==
"LBFGS") {
224 return std::make_shared<Utils::UnitCellGeometryOptimizer<Utils::Bfgs, Utils::Lbfgs>>(calc);
226 else if (cellType ==
"BFGS") {
227 return std::make_shared<Utils::UnitCellGeometryOptimizer<Utils::Bfgs, Utils::Bfgs>>(calc);
229 else if (cellType ==
"SD" || cellType ==
"STEEPESTDESCENT") {
230 return std::make_shared<Utils::UnitCellGeometryOptimizer<Utils::Bfgs, Utils::SteepestDescent>>(calc);
232 throw std::runtime_error(
233 "Unknown CellOptimizer requested for a geometry optimization, available are: SD, BFGS and LBFGS!");
236 else if (type ==
"SD" || type ==
"STEEPESTDESCENT") {
237 if (cellType.empty()) {
239 return std::make_shared<Utils::QmmmGeometryOptimizer<Utils::SteepestDescent>>(calc);
241 return std::make_shared<Utils::GeometryOptimizer<Utils::SteepestDescent>>(calc);
244 if (cellType ==
"LBFGS") {
245 return std::make_shared<Utils::UnitCellGeometryOptimizer<Utils::SteepestDescent, Utils::Lbfgs>>(calc);
247 else if (cellType ==
"BFGS") {
248 return std::make_shared<Utils::UnitCellGeometryOptimizer<Utils::SteepestDescent, Utils::Bfgs>>(calc);
250 else if (cellType ==
"SD" || cellType ==
"STEEPESTDESCENT") {
251 return std::make_shared<Utils::UnitCellGeometryOptimizer<Utils::SteepestDescent, Utils::SteepestDescent>>(calc);
253 throw std::runtime_error(
254 "Unknown CellOptimizer requested for a geometry optimization, available are: SD, BFGS and LBFGS!");
257 else if (type ==
"NR" || type ==
"NEWTONRAPHSON") {
258 if (cellType.empty()) {
260 return std::make_shared<Utils::QmmmGeometryOptimizer<Utils::NewtonRaphson>>(calc);
262 return std::make_shared<Utils::GeometryOptimizer<Utils::NewtonRaphson>>(calc);
265 if (cellType ==
"LBFGS") {
266 return std::make_shared<Utils::UnitCellGeometryOptimizer<Utils::NewtonRaphson, Utils::Lbfgs>>(calc);
268 else if (cellType ==
"BFGS") {
269 return std::make_shared<Utils::UnitCellGeometryOptimizer<Utils::NewtonRaphson, Utils::Bfgs>>(calc);
271 else if (cellType ==
"SD" || cellType ==
"STEEPESTDESCENT") {
272 return std::make_shared<Utils::UnitCellGeometryOptimizer<Utils::NewtonRaphson, Utils::SteepestDescent>>(calc);
274 throw std::runtime_error(
275 "Unknown CellOptimizer requested for a geometry optimization, available are: SD, BFGS and LBFGS!");
278 throw std::runtime_error(
279 "Unknown Optimizer requested for a geometry optimization, available are: SD, NR, BFGS and LBFGS!");
286 #endif // READUCT_GEOMETRYOPTIMIZATIONTASK_H_
void warningIfMultipleOutputsGiven() const
Warn if more than one output system was specified.
Definition: Task.h:107
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: GeometryOptimizationTask.h:46
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
GeometryOptimizationTask(std::vector< std::string > input, std::vector< std::string > output, std::shared_ptr< Core::Log > logger=nullptr)
Construct a new GeometryOptimizationTask.
Definition: GeometryOptimizationTask.h:41
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: GeometryOptimizationTask.h:50
The base class for all tasks in Readuct.
Definition: Task.h:29
Definition: GeometryOptimizationTask.h:33