Program Listing for File lowest_eig_potential.hpp#
↰ Return to documentation for file (source/pele/lowest_eig_potential.hpp)
#ifndef _PELE_LOWEST_EIG_POTENTIAL_H
#define _PELE_LOWEST_EIG_POTENTIAL_H
#include <algorithm>
#include <cmath>
#include <memory>
#include <vector>
#include "array.hpp"
#include "base_potential.hpp"
namespace pele {
inline void zero_modes_translational(std::vector<pele::Array<double>> &zev,
size_t natoms, size_t bdim) {
double v = 1 / sqrt(natoms);
size_t N = natoms * bdim;
for (size_t i = 0; i < bdim; ++i) {
pele::Array<double> evec(N, 0); // initialize array of zeros
for (size_t j = i; j < N; j += bdim) {
evec[j] = v;
}
zev.push_back(evec);
}
}
class Orthogonalize {
public:
virtual ~Orthogonalize(){};
virtual void orthogonalize(Array<double> const &coords,
Array<double> &vector) = 0;
};
class OrthogonalizeTranslational : public Orthogonalize {
protected:
std::vector<pele::Array<double>> _tr_evec;
size_t _natoms, _bdim, _ndim;
double _d, _tol;
public:
// OrthogonalizeTranslational(size_t natoms, size_t bdim, double tol=1e-6);
OrthogonalizeTranslational(size_t natoms, size_t bdim, double tol = 1e-6)
: _natoms(natoms), _bdim(bdim), _ndim(bdim * natoms), _tol(tol) {
/*initialize translational eigenvectors to canonical orthonormal basis*/
zero_modes_translational(_tr_evec, _natoms, _bdim);
}
virtual ~OrthogonalizeTranslational() {}
// virtual inline void orthogonalize(Array<double> const & coords,
// Array<double>& vector);
virtual inline void orthogonalize(Array<double> const &coords,
Array<double> &vector) {
bool success = true;
pele::Array<double> dot_prod(_bdim);
vector /= norm(vector);
// generally in this loop success will be set to false
for (size_t i = 0; i < _bdim; ++i) {
dot_prod[i] = dot(_tr_evec[i], vector);
if (std::abs(dot_prod[i]) > _tol) {
success = false;
};
}
while (success == false) {
success = true;
for (size_t i = 0; i < _bdim; ++i) {
for (size_t j = 0; j < _ndim; ++j) {
vector[j] -= dot_prod[i] * _tr_evec[i][j];
}
}
vector /= norm(vector);
for (size_t i = 0; i < _bdim; ++i) {
dot_prod[i] = dot(_tr_evec[i], vector);
if (std::abs(dot_prod[i]) > _tol) {
success = false;
};
}
}
}
}; // class OrthogonalizeTranslational
/*
* Lowest Eigenvalue Potential:
* gd = g(x+d)
* _v: normal unit vector in arbitrary direction
* _d: finite difference step
* */
class LowestEigPotential : public BasePotential {
protected:
std::shared_ptr<pele::BasePotential> _potential;
pele::Array<double> _coords, _coordsd, _g, _gd;
size_t _bdim, _natoms;
double _d;
OrthogonalizeTranslational _orthog;
pele::Array<double>
x_opt;
public:
// LowestEigPotential(std::shared_ptr<pele::BasePotential> potential,
// pele::Array<double>
// coords, size_t bdim, double d=1e-6);
/*constructor*/
LowestEigPotential(std::shared_ptr<pele::BasePotential> potential,
pele::Array<double> coords, size_t bdim, double d = 1e-6)
: _potential(potential),
_coords(coords.copy()),
_coordsd(coords.size()),
_g(_coords.size()),
_gd(_coords.size()),
_bdim(bdim),
_natoms(_coords.size() / _bdim),
_d(d),
_orthog(_natoms, _bdim),
x_opt(coords) {
_potential->get_energy_gradient(_coords, _g);
}
void set_x_opt(pele::Array<double> x) { x_opt = pele::Array<double>(x); }
virtual ~LowestEigPotential() {}
// virtual double inline get_energy(pele::Array<double> const & x);
/* calculate energy from distance squared, r0 is the hard core distance, r is
* the distance between the centres */
virtual double inline get_energy(pele::Array<double> const &x) {
if (x_opt != x) {
throw std::runtime_error(
"LowestEigPotential::get_energy: x_opt is different from x. Use "
"set_x_opt to pass a pointer.");
}
_orthog.orthogonalize(
_coords, x_opt); // takes care of orthogonalizing and normalizing x_opt
for (size_t i = 0; i < x_opt.size(); i++) {
_coordsd[i] = _coords[i] + _d * x_opt[i];
}
_potential->get_energy_gradient(_coordsd, _gd);
_gd -= _g;
double mu = dot(_gd, x_opt) / _d;
return mu;
}
// virtual double inline get_energy_gradient(pele::Array<double> x,
// pele::Array<double> grad);
/* calculate energy and gradient from distance squared, gradient is in
* g/|rij|, r0 is the hard core distance, r is the distance between the
* centres */
virtual double inline get_energy_gradient(pele::Array<double> const &x,
pele::Array<double> &grad) {
if (x_opt != x) {
throw std::runtime_error(
"LowestEigPotential::get_energy_gradient: x_opt is different from x. "
"Use set_x_opt to pass a pointer.");
}
_orthog.orthogonalize(
_coords, x_opt); // takes care of orthogonalizing and normalizing x_opt
for (size_t i = 0; i < x_opt.size(); i++) {
_coordsd[i] = _coords[i] + _d * x_opt[i];
}
_potential->get_energy_gradient(_coordsd, _gd);
_gd -= _g;
double mu = dot(_gd, x_opt) / _d;
for (size_t i = 0; i < x_opt.size(); i++) {
grad[i] = 2 * _gd[i] / _d - 2 * mu * x[i];
}
return mu;
}
// void reset_coords(pele::Array<double> new_coords);
void reset_coords(pele::Array<double> const &new_coords) {
_coords.assign(new_coords);
_potential->get_energy_gradient(_coords, _g);
}
};
} // namespace pele
#endif //#ifndef _PELE_LOWEST_EIG_POTENTIAL_H