mcpele
1.0.0
The Monte Carlo Python Energy Landscape Explorer
|
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