Program Listing for File modified_fire.hpp#
↰ Return to documentation for file (source/pele/modified_fire.hpp)
#ifndef _PELE_MODIFIED_FIRE_H__
#define _PELE_MODIFIED_FIRE_H__
#include <fstream>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <stdexcept>
#include "array.hpp"
#include "base_potential.hpp"
#include "optimizer.hpp"
namespace pele {
class MODIFIED_FIRE : public ODEBasedOptimizer {
private:
double _dtstart, _dt, _dtmax, _maxstep, _Nmin, _finc, _fdec, _fa, _astart, _a,
_fold, _ifnorm, _vnorm;
pele::Array<double> _v, _dx, _xold, _gold;
size_t _fire_iter_number, _N;
bool _stepback;
inline void _ForwardEuler_integration();
inline void _VelocityVerlet_integration();
#if PRINT_TO_FILE == 1
std::ofstream trajectory_file;
std::ofstream time_file;
std::ofstream gradient_file;
#endif
public:
MODIFIED_FIRE(
std::shared_ptr<pele::BasePotential> potential, pele::Array<double> &x0,
double dtstart, double dtmax, double maxstep, size_t Nmin = 5,
double finc = 1.1, double fdec = 0.5, double fa = 0.99,
double astart = 0.1, double tol = 1e-4, bool stepback = true,
bool save_trajectory = false, int iterations_before_save = 1,
StopCriterionType stop_criterion_type = StopCriterionType::GRADIENT);
virtual ~MODIFIED_FIRE() {
#if PRINT_TO_FILE == 1
trajectory_file.close();
#endif
}
void one_iteration();
void initialize_func_gradient();
void set_func_gradient(double f, Array<double> grad);
inline void reset(Array<double> &x0);
};
inline void MODIFIED_FIRE::reset(Array<double> &x0) {
// arrays are already wrapped by the integrator, must not wrap them again,
// just update their values, dont's use array.copy()
// or will wrap a copy to the array
if (!func_initialized_) {
initialize_func_gradient();
}
iter_number_ = 0;
x_.assign(x0);
f_ = potential_->get_energy_gradient(x_, g_);
nfev_ = 1;
// fire specific
_fire_iter_number = 0;
_dt = _dtstart;
_a = _astart;
_fold = f_;
_xold.assign(x_);
_gold.assign(g_);
for (size_t k = 0; k < x_.size(); ++k) {
_v[k] = -g_[k] * _dt;
}
_ifnorm = 1. / norm(g_);
_vnorm = norm(_v);
gradient_norm_ = 1. / (_ifnorm * sqrt(_N));
}
inline void MODIFIED_FIRE::_VelocityVerlet_integration() {
/* the minuses in the following expressions are due to the fact that
* the gradients rather than the forces appear in the expression
*/
if (_dt == 0.0) {
throw std::logic_error("time step cannot be zero");
}
for (size_t i = 0; i < _N; ++i) {
_v[i] -=
0.5 * _dt * (_gold[i] + g_[i]); // update velocity assumes all masses 1
_dx[i] =
_dt *
(_v[i] -
0.5 * _dt * g_[i]); // build displacement vector, assumes all masses 1
}
_gold.assign(g_); // save gradient as old g
double normdx = compute_pot_norm(_dx);
if (normdx > _maxstep) {
_dx *= (_maxstep /
normdx); // resize displacement vector is greater than _maxstep
}
x_ += _dx;
f_ = potential_->get_energy_gradient(x_, g_); // update gradient
}
inline void MODIFIED_FIRE::_ForwardEuler_integration() {
/* the minuses in the following expressions are due to the fact that
* the gradients rather than the forces appear in the expression
*/
for (size_t i = 0; i < _N;
++i) { // this was after get_energy_gradient, moved for testing
_v[i] -= _dt * g_[i]; // update velocity, assumes all masses are 1
_dx[i] = _dt * _v[i]; // build displacement vector
}
_gold.assign(g_); // save gradient as old g
double normdx = compute_pot_norm(_dx);
if (normdx > _maxstep) {
_dx *= (_maxstep /
normdx); // resize displacement vector is greater than _maxstep
}
x_ += _dx;
f_ = potential_->get_energy_gradient(x_, g_); // update gradient
}
inline void MODIFIED_FIRE::one_iteration() {
#if OPTIMIZER_DEBUG_LEVEL > 2
std::cout << "func initialized" << func_initialized_ << std::endl;
#endif
time_ += _dt;
if (!func_initialized_) {
initialize_func_gradient();
}
nfev_ += 1;
iter_number_ += 1;
_fire_iter_number +=
1; // this is different from iter_number_ which does not get reset
// save old configuration in case next step has P < 0
_fold = f_; // set f_ old before integration step
_xold.assign(x_); // save x as xold, gold saved in integrator (because
// velocity verlet needs it, if vv is used)
/*equation written in this conditional statement _v = (1- _a)*_v + _a * funit
* * vnorm*/
for (size_t i = 0; i < _N; ++i) {
_v[i] = (1. - _a) * _v[i] - _a * g_[i] * _ifnorm * _vnorm;
}
/*run MD*/
this->_ForwardEuler_integration();
// this->_VelocityVerlet_integration();
double P = -1 * dot(_v, g_);
if (P > 0) {
if (_fire_iter_number > _Nmin) {
_dt = std::min(_dt * _finc, _dtmax);
_a *= _fa;
}
_ifnorm = 1. / norm(g_);
_vnorm = norm(_v);
gradient_norm_ = 1. / (_ifnorm * sqrt(_N)); // update rms
} else {
_dt *= _fdec;
_a = _astart;
_fire_iter_number = 0;
_v.assign(0);
// reset position and gradient to the one before the step (core of modified
// fire) reset velocity to initial (0)
if (_stepback == true) {
f_ = _fold;
// reset position and gradient to the one before the step (core of
// modified fire) reset velocity to initial (0)
x_.assign(_xold);
g_.assign(_gold);
}
if (verbosity_ > 1) {
std::cout << "warning: step direction was uphill. inverting\n";
}
}
step_norm_ = norm(x_ - _xold);
// print some status information
if ((iprint_ > 0) && (iter_number_ % iprint_ == 0)) {
std::cout << "fire: " << iter_number_ << " fire_iter_number "
<< _fire_iter_number << " dt " << _dt << " a " << _a << " P " << P
<< " vnorm " << _vnorm << " E " << f_ << " rms " << gradient_norm_
<< " nfev " << nfev_ << "\n";
}
#if PRINT_TO_FILE == 1
trajectory_file << std::setprecision(17) << x_;
time_file << std::setprecision(17) << time_ << std::endl;
gradient_file << std::setprecision(17) << g_;
#endif
}
} // namespace pele
#endif // #ifndef _PELE_MODIFIED_FIRE_H__