pele
Python energy landscape explorer
/home/js850/projects/pele/source/pele/simple_pairwise_potential.h
00001 #ifndef PYGMIN_SIMPLE_PAIRWISE_POTENTIAL_H
00002 #define PYGMIN_SIMPLE_PAIRWISE_POTENTIAL_H
00003 
00004 #include "base_potential.h"
00005 #include "array.h"
00006 #include "distance.h"
00007 #include <memory>
00008 
00009 namespace pele
00010 {
00020 template<typename pairwise_interaction, 
00021     typename distance_policy = cartesian_distance<3> >
00022 class SimplePairwisePotential : public BasePotential
00023 {
00024 protected:
00025     static const size_t _ndim = distance_policy::_ndim;
00026     std::shared_ptr<pairwise_interaction> _interaction;
00027     std::shared_ptr<distance_policy> _dist;
00028 
00029     SimplePairwisePotential( std::shared_ptr<pairwise_interaction> interaction,
00030             std::shared_ptr<distance_policy> dist=NULL) 
00031         : _interaction(interaction), _dist(dist)
00032     {
00033         if(_dist == NULL) _dist = std::make_shared<distance_policy>();
00034     }
00035 
00036 public:
00037     virtual ~SimplePairwisePotential() 
00038     {}
00039 
00040     virtual double get_energy(Array<double> x);
00041     virtual double get_energy_gradient(Array<double> x, Array<double> grad)
00042     {
00043         grad.assign(0);
00044         return add_energy_gradient(x, grad);
00045     }
00046     virtual double get_energy_gradient_hessian(Array<double> x, Array<double> grad, Array<double> hess)
00047     {
00048         grad.assign(0);
00049         hess.assign(0);
00050         return add_energy_gradient_hessian(x, grad, hess);
00051     }
00052     virtual double add_energy_gradient(Array<double> x, Array<double> grad);
00053     virtual double add_energy_gradient_hessian(Array<double> x, Array<double> grad, Array<double> hess);
00054 };
00055 
00056 template<typename pairwise_interaction, typename distance_policy>
00057 inline double
00058 SimplePairwisePotential<pairwise_interaction,distance_policy>::add_energy_gradient(
00059         Array<double> x, Array<double> grad)
00060 {
00061     const size_t natoms = x.size() / _ndim;
00062     if (_ndim * natoms != x.size()) {
00063         throw std::runtime_error("x is not divisible by the number of dimensions");
00064     }
00065     if (grad.size() != x.size()) {
00066         throw std::runtime_error("grad must have the same size as x");
00067     }
00068 
00069     double e = 0.;
00070     double gij;
00071     double dr[_ndim];
00072 
00073 //    grad.assign(0.);
00074 
00075     for (size_t atomi=0; atomi<natoms; ++atomi) {
00076         size_t const i1 = _ndim * atomi;
00077         for (size_t atomj=0; atomj<atomi; ++atomj) {
00078             size_t const j1 = _ndim * atomj;
00079 
00080             _dist->get_rij(dr, &x[i1], &x[j1]);
00081 
00082             double r2 = 0;
00083             for (size_t k=0; k<_ndim; ++k) {
00084                 r2 += dr[k]*dr[k];
00085             }
00086             e += _interaction->energy_gradient(r2, &gij, atomi, atomj);
00087             for (size_t k=0; k<_ndim; ++k) {
00088                 grad[i1+k] -= gij * dr[k];
00089             }
00090             for (size_t k=0; k<_ndim; ++k) {
00091                 grad[j1+k] += gij * dr[k];
00092             }
00093         }
00094     }
00095     return e;
00096 }
00097 
00098 template<typename pairwise_interaction, typename distance_policy>
00099 inline double SimplePairwisePotential<pairwise_interaction, distance_policy>::add_energy_gradient_hessian(Array<double> x,
00100         Array<double> grad, Array<double> hess)
00101 {
00102     double hij, gij;
00103     double dr[_ndim];
00104     const size_t N = x.size();
00105     const size_t natoms = x.size()/_ndim;
00106     if (_ndim * natoms != x.size()) {
00107         throw std::runtime_error("x is not divisible by the number of dimensions");
00108     }
00109     if (x.size() != grad.size()) {
00110         throw std::invalid_argument("the gradient has the wrong size");
00111     }
00112     if (hess.size() != x.size() * x.size()) {
00113         throw std::invalid_argument("the Hessian has the wrong size");
00114     }
00115 //    hess.assign(0.);
00116 //    grad.assign(0.);
00117 
00118     double e = 0.;
00119     for (size_t atomi=0; atomi<natoms; ++atomi) {
00120         int i1 = _ndim*atomi;
00121         for (size_t atomj=0;atomj<atomi;++atomj){
00122             int j1 = _ndim*atomj;
00123             _dist->get_rij(dr, &x[i1], &x[j1]);
00124             double r2 = 0;
00125             for (size_t k=0;k<_ndim;++k){r2 += dr[k]*dr[k];}
00126 
00127             e += _interaction->energy_gradient_hessian(r2, &gij, &hij, atomi, atomj);
00128 
00129             for (size_t k=0; k<_ndim; ++k)
00130                 grad[i1+k] -= gij * dr[k];
00131             for (size_t k=0; k<_ndim; ++k)
00132                 grad[j1+k] += gij * dr[k];
00133 
00134             for (size_t k=0; k<_ndim; ++k){
00135                 //diagonal block - diagonal terms
00136                 double Hii_diag = (hij+gij)*dr[k]*dr[k]/r2 - gij;
00137                 hess[N*(i1+k)+i1+k] += Hii_diag;
00138                 hess[N*(j1+k)+j1+k] += Hii_diag;
00139                 //off diagonal block - diagonal terms
00140                 double Hij_diag = -Hii_diag;
00141                 hess[N*(i1+k)+j1+k] = Hij_diag;
00142                 hess[N*(j1+k)+i1+k] = Hij_diag;
00143                 for (size_t l = k+1; l<_ndim; ++l){
00144                     //diagonal block - off diagonal terms
00145                     double Hii_off = (hij+gij)*dr[k]*dr[l]/r2;
00146                     hess[N*(i1+k)+i1+l] += Hii_off;
00147                     hess[N*(i1+l)+i1+k] += Hii_off;
00148                     hess[N*(j1+k)+j1+l] += Hii_off;
00149                     hess[N*(j1+l)+j1+k] += Hii_off;
00150                     //off diagonal block - off diagonal terms
00151                     double Hij_off = -Hii_off;
00152                     hess[N*(i1+k)+j1+l] = Hij_off;
00153                     hess[N*(i1+l)+j1+k] = Hij_off;
00154                     hess[N*(j1+k)+i1+l] = Hij_off;
00155                     hess[N*(j1+l)+i1+k] = Hij_off;
00156                 }
00157             }
00158         }
00159     }
00160     return e;
00161 }
00162 
00163 template<typename pairwise_interaction, typename distance_policy>
00164 inline double SimplePairwisePotential<pairwise_interaction, distance_policy>::get_energy(Array<double> x)
00165 {
00166     size_t const natoms = x.size()/_ndim;
00167     if (_ndim * natoms != x.size()) {
00168         throw std::runtime_error("x is not divisible by the number of dimensions");
00169     }
00170     double e=0.;
00171     double dr[_ndim];
00172 
00173     for (size_t atomi=0; atomi<natoms; ++atomi) {
00174         size_t i1 = _ndim*atomi;
00175         for (size_t atomj=0; atomj<atomi; ++atomj) {
00176             size_t j1 = _ndim*atomj;
00177             _dist->get_rij(dr, &x[i1], &x[j1]);
00178             double r2 = 0;
00179             for (size_t k=0;k<_ndim;++k) {
00180                 r2 += dr[k]*dr[k];
00181             }
00182             e += _interaction->energy(r2, atomi, atomj);
00183         }
00184     }
00185     return e;
00186 }
00187 }
00188 
00189 #endif
 All Classes Namespaces Functions Variables Typedefs