pele
Python energy landscape explorer
|
00001 #ifndef _PELE_CELL_LIST_POTENTIAL_H 00002 #define _PELE_CELL_LIST_POTENTIAL_H 00003 00004 #include <iostream> 00005 #include <memory> 00006 #include <vector> 00007 #include <utility> 00008 #include <stdexcept> 00009 00010 #include "base_potential.h" 00011 #include "array.h" 00012 #include "distance.h" 00013 #include "cell_lists.h" 00014 00015 namespace pele{ 00016 00017 // js850> note: this class is commented because it is unused. But it has been tested 00018 // and works. We should probably replace SimplePairwiseNeighborList with this more general version. 00020 // * Potential to iterate over the list of atom pairs generated with the 00021 // * cell list implementation in cell_lists.h. 00022 // * This should also do the cell list construction and refresh, such that 00023 // * the interface is the same for the user as with SimplePairwise. 00024 // */ 00025 //template <typename pairwise_interaction, typename distance_policy, typename pair_iterator> 00026 //class PairIteratorPotential : public BasePotential { 00027 //protected: 00028 // const static size_t m_ndim = distance_policy::_ndim; 00029 // std::shared_ptr<pairwise_interaction> m_interaction; 00030 // std::shared_ptr<distance_policy> m_dist; 00031 // std::shared_ptr<pair_iterator > m_pair_iter; 00032 //public: 00033 // virtual ~PairIteratorPotential() {} 00034 // PairIteratorPotential(std::shared_ptr<pairwise_interaction> interaction, 00035 // std::shared_ptr<distance_policy> dist, 00036 // std::shared_ptr<pair_iterator> pair_iter) 00037 // : m_interaction(interaction), 00038 // m_dist(dist), 00039 // m_pair_iter(pair_iter) 00040 // {} 00041 // 00042 // virtual double get_energy(Array<double> xa) 00043 // { 00044 // refresh_iterator(xa); 00045 // const double* x = xa.data(); 00046 // double result = 0; 00047 // for (auto const & ijpair : *m_pair_iter) { 00048 // const size_t i = ijpair.first; 00049 // const size_t j = ijpair.second; 00050 // const size_t xi_off = m_ndim * i; 00051 // const size_t xj_off = m_ndim * j; 00052 // double dr[m_ndim]; 00053 // m_dist->get_rij(dr, x + xi_off, x + xj_off); 00054 // double r2 = 0; 00055 // for (size_t k = 0; k < m_ndim; ++k) { 00056 // r2 += dr[k] * dr[k]; 00057 // } 00058 // result += m_interaction->energy(r2, i, j); 00059 // } 00060 // return result; 00061 // } 00062 // 00063 // virtual double get_energy_gradient(Array<double> xa, Array<double> grad) 00064 // { 00065 // refresh_iterator(xa); 00066 // const double* x = xa.data(); 00067 // double result = 0; 00068 // grad.assign(double(0)); 00069 // if (xa.size() != grad.size()) { 00070 // throw std::runtime_error("CellListPotential::get_energy_gradient: illegal input"); 00071 // } 00072 // for (auto const & ijpair : *m_pair_iter) { 00073 // const size_t i = ijpair.first; 00074 // const size_t j = ijpair.second; 00075 // const size_t xi_off = m_ndim * i; 00076 // const size_t xj_off = m_ndim * j; 00077 // double dr[m_ndim]; 00078 // m_dist->get_rij(dr, x + xi_off, x + xj_off); 00079 // double r2 = 0; 00080 // for (size_t k = 0; k < m_ndim; ++k) { 00081 // r2 += dr[k] * dr[k]; 00082 // } 00083 // double gij; 00084 // result += m_interaction->energy_gradient(r2, &gij, i, j); 00085 // for (size_t k = 0; k < m_ndim; ++k) { 00086 // grad[xi_off + k] -= gij * dr[k]; 00087 // } 00088 // for (size_t k = 0; k < m_ndim; ++k) { 00089 // grad[xj_off + k] += gij * dr[k]; 00090 // } 00091 // } 00092 // return result; 00093 // } 00094 // 00095 // virtual double get_energy_gradient_hessian(Array<double> xa, 00096 // Array<double> grad, Array<double> hess) 00097 // { 00098 // if (xa.size() != grad.size()) { 00099 // throw std::runtime_error("CellListPotential::get_energy_gradient_hessian: illegal input grad"); 00100 // } 00101 // if (hess.size() != xa.size() * xa.size()) { 00102 // throw std::runtime_error("CellListPotential::get_energy_gradient_hessian: illegal input hess"); 00103 // } 00104 // refresh_iterator(xa); 00105 // const double* x = xa.data(); 00106 // double result = 0; 00107 // grad.assign(double(0)); 00108 // hess.assign(double(0)); 00109 // for (auto const & ijpair : *m_pair_iter) { 00110 // const size_t i = ijpair.first; 00111 // const size_t j = ijpair.second; 00112 // const size_t xi_off = m_ndim * i; 00113 // const size_t xj_off = m_ndim * j; 00114 // double dr[m_ndim]; 00115 // m_dist->get_rij(dr, x + xi_off, x + xj_off); 00116 // double r2 = 0; 00117 // for (size_t k = 0; k < m_ndim; ++k) { 00118 // r2 += dr[k] * dr[k]; 00119 // } 00120 // double gij, hij; 00121 // result += m_interaction->energy_gradient_hessian(r2, &gij, &hij, i, j); 00122 // for (size_t k = 0; k < m_ndim; ++k) { 00123 // grad[xi_off + k] -= gij * dr[k]; 00124 // } 00125 // for (size_t k = 0; k < m_ndim; ++k) { 00126 // grad[xj_off + k] += gij * dr[k]; 00127 // } 00128 // //this part is copied from simple_pairwise_potential.h 00129 // //(even more so than the rest) 00130 // const size_t N = xa.size(); 00131 // const size_t i1 = xi_off; 00132 // const size_t j1 = xj_off; 00133 // for (size_t k = 0; k < m_ndim; ++k) { 00134 // //diagonal block - diagonal terms 00135 // const double Hii_diag = (hij + gij) * dr[k] * dr[k] / r2 - gij; 00136 // hess[N * (i1 + k) + i1 + k] += Hii_diag; 00137 // hess[N * (j1 + k) + j1 + k] += Hii_diag; 00138 // //off diagonal block - diagonal terms 00139 // const double Hij_diag = -Hii_diag; 00140 // hess[N * (i1 + k) + j1 + k] = Hij_diag; 00141 // hess[N * (j1 + k) + i1 + k] = Hij_diag; 00142 // for (size_t l = k + 1; l < m_ndim; ++l) { 00143 // //diagonal block - off diagonal terms 00144 // const double Hii_off = (hij + gij) * dr[k] * dr[l] / r2; 00145 // hess[N * (i1 + k) + i1 + l] += Hii_off; 00146 // hess[N * (i1 + l) + i1 + k] += Hii_off; 00147 // hess[N * (j1 + k) + j1 + l] += Hii_off; 00148 // hess[N * (j1 + l) + j1 + k] += Hii_off; 00149 // //off diagonal block - off diagonal terms 00150 // const double Hij_off = -Hii_off; 00151 // hess[N * (i1 + k) + j1 + l] = Hij_off; 00152 // hess[N * (i1 + l) + j1 + k] = Hij_off; 00153 // hess[N * (j1 + k) + i1 + l] = Hij_off; 00154 // hess[N * (j1 + l) + i1 + k] = Hij_off; 00155 // } 00156 // } 00157 // } 00158 // return result; 00159 // } 00160 //protected: 00161 // void refresh_iterator(Array<double> x) 00162 // { 00163 // m_pair_iter->reset(x); 00164 // } 00165 //}; 00166 00167 00171 template <typename pairwise_interaction, typename distance_policy> 00172 class EnergyAccumulator { 00173 const static size_t m_ndim = distance_policy::_ndim; 00174 std::shared_ptr<pairwise_interaction> m_interaction; 00175 std::shared_ptr<distance_policy> m_dist; 00176 00177 double const * const m_x_ptr; 00178 public: 00179 double m_energy; 00180 00181 EnergyAccumulator(std::shared_ptr<pairwise_interaction> interaction, 00182 std::shared_ptr<distance_policy> dist, 00183 pele::Array<double> x) 00184 : m_interaction(interaction), 00185 m_dist(dist), 00186 m_x_ptr(x.data()), 00187 m_energy(0.) 00188 {} 00189 00190 void insert_atom_pair(size_t const atom_i, size_t const atom_j) 00191 { 00192 const size_t xi_off = m_ndim * atom_i; 00193 const size_t xj_off = m_ndim * atom_j; 00194 double dr[m_ndim]; 00195 m_dist->get_rij(dr, m_x_ptr + xi_off, m_x_ptr + xj_off); 00196 double r2 = 0; 00197 for (size_t k = 0; k < m_ndim; ++k) { 00198 r2 += dr[k] * dr[k]; 00199 } 00200 m_energy += m_interaction->energy(r2, atom_i, atom_j); 00201 } 00202 }; 00203 00207 template <typename pairwise_interaction, typename distance_policy> 00208 class EnergyGradientAccumulator { 00209 const static size_t m_ndim = distance_policy::_ndim; 00210 std::shared_ptr<pairwise_interaction> m_interaction; 00211 std::shared_ptr<distance_policy> m_dist; 00212 00213 double const * const m_x_ptr; 00214 public: 00215 double m_energy; 00216 pele::Array<double> m_gradient; 00217 00218 EnergyGradientAccumulator(std::shared_ptr<pairwise_interaction> interaction, 00219 std::shared_ptr<distance_policy> dist, 00220 pele::Array<double> x, pele::Array<double> gradient) 00221 : m_interaction(interaction), 00222 m_dist(dist), 00223 m_x_ptr(x.data()), 00224 m_energy(0.), 00225 m_gradient(gradient) 00226 {} 00227 00228 void insert_atom_pair(size_t const atom_i, size_t const atom_j) 00229 { 00230 const size_t xi_off = m_ndim * atom_i; 00231 const size_t xj_off = m_ndim * atom_j; 00232 double dr[m_ndim]; 00233 m_dist->get_rij(dr, m_x_ptr + xi_off, m_x_ptr + xj_off); 00234 double r2 = 0; 00235 for (size_t k = 0; k < m_ndim; ++k) { 00236 r2 += dr[k] * dr[k]; 00237 } 00238 double gij; 00239 m_energy += m_interaction->energy_gradient(r2, &gij, atom_i, atom_j); 00240 for (size_t k = 0; k < m_ndim; ++k) { 00241 m_gradient[xi_off + k] -= gij * dr[k]; 00242 } 00243 for (size_t k = 0; k < m_ndim; ++k) { 00244 m_gradient[xj_off + k] += gij * dr[k]; 00245 } 00246 } 00247 }; 00248 00252 template <typename pairwise_interaction, typename distance_policy> 00253 class EnergyGradientHessianAccumulator { 00254 const static size_t m_ndim = distance_policy::_ndim; 00255 std::shared_ptr<pairwise_interaction> m_interaction; 00256 std::shared_ptr<distance_policy> m_dist; 00257 00258 double const * const m_x_ptr; 00259 size_t const m_ndof; 00260 public: 00261 double m_energy; 00262 pele::Array<double> m_gradient; 00263 pele::Array<double> m_hessian; 00264 00265 EnergyGradientHessianAccumulator(std::shared_ptr<pairwise_interaction> interaction, 00266 std::shared_ptr<distance_policy> dist, 00267 pele::Array<double> x, pele::Array<double> gradient, 00268 pele::Array<double> hessian) 00269 : m_interaction(interaction), 00270 m_dist(dist), 00271 m_x_ptr(x.data()), 00272 m_ndof(x.size()), 00273 m_energy(0.), 00274 m_gradient(gradient), 00275 m_hessian(hessian) 00276 {} 00277 00278 void insert_atom_pair(size_t const atom_i, size_t const atom_j) 00279 { 00280 const size_t xi_off = m_ndim * atom_i; 00281 const size_t xj_off = m_ndim * atom_j; 00282 double dr[m_ndim]; 00283 m_dist->get_rij(dr, m_x_ptr + xi_off, m_x_ptr + xj_off); 00284 double r2 = 0; 00285 for (size_t k = 0; k < m_ndim; ++k) { 00286 r2 += dr[k] * dr[k]; 00287 } 00288 double gij, hij; 00289 m_energy += m_interaction->energy_gradient_hessian(r2, &gij, &hij, atom_i, atom_j); 00290 for (size_t k = 0; k < m_ndim; ++k) { 00291 m_gradient[xi_off + k] -= gij * dr[k]; 00292 } 00293 for (size_t k = 0; k < m_ndim; ++k) { 00294 m_gradient[xj_off + k] += gij * dr[k]; 00295 } 00296 //this part is copied from simple_pairwise_potential.h 00297 //(even more so than the rest) 00298 const size_t N = m_ndof; 00299 const size_t i1 = xi_off; 00300 const size_t j1 = xj_off; 00301 for (size_t k = 0; k < m_ndim; ++k) { 00302 //diagonal block - diagonal terms 00303 const double Hii_diag = (hij + gij) * dr[k] * dr[k] / r2 - gij; 00304 m_hessian[N * (i1 + k) + i1 + k] += Hii_diag; 00305 m_hessian[N * (j1 + k) + j1 + k] += Hii_diag; 00306 //off diagonal block - diagonal terms 00307 const double Hij_diag = -Hii_diag; 00308 m_hessian[N * (i1 + k) + j1 + k] = Hij_diag; 00309 m_hessian[N * (j1 + k) + i1 + k] = Hij_diag; 00310 for (size_t l = k + 1; l < m_ndim; ++l) { 00311 //diagonal block - off diagonal terms 00312 const double Hii_off = (hij + gij) * dr[k] * dr[l] / r2; 00313 m_hessian[N * (i1 + k) + i1 + l] += Hii_off; 00314 m_hessian[N * (i1 + l) + i1 + k] += Hii_off; 00315 m_hessian[N * (j1 + k) + j1 + l] += Hii_off; 00316 m_hessian[N * (j1 + l) + j1 + k] += Hii_off; 00317 //off diagonal block - off diagonal terms 00318 const double Hij_off = -Hii_off; 00319 m_hessian[N * (i1 + k) + j1 + l] = Hij_off; 00320 m_hessian[N * (i1 + l) + j1 + k] = Hij_off; 00321 m_hessian[N * (j1 + k) + i1 + l] = Hij_off; 00322 m_hessian[N * (j1 + l) + i1 + k] = Hij_off; 00323 } 00324 } 00325 } 00326 }; 00327 00328 00329 00336 template <typename pairwise_interaction, typename distance_policy> 00337 class CellListPotential : public BasePotential { 00338 protected: 00339 const static size_t m_ndim = distance_policy::_ndim; 00340 pele::CellLists<distance_policy> m_cell_lists; 00341 std::shared_ptr<pairwise_interaction> m_interaction; 00342 std::shared_ptr<distance_policy> m_dist; 00343 public: 00344 ~CellListPotential() {} 00345 CellListPotential( 00346 std::shared_ptr<pairwise_interaction> interaction, 00347 std::shared_ptr<distance_policy> dist, 00348 pele::Array<double> boxvec, 00349 double rcut, double ncellx_scale) 00350 : m_cell_lists(dist, boxvec, rcut, ncellx_scale), 00351 m_interaction(interaction), 00352 m_dist(dist) 00353 {} 00354 00355 virtual double get_energy(Array<double> x) 00356 { 00357 const size_t natoms = x.size() / m_ndim; 00358 if (m_ndim * natoms != x.size()) { 00359 throw std::runtime_error("x.size() is not divisible by the number of dimensions"); 00360 } 00361 00362 refresh_iterator(x); 00363 typedef EnergyAccumulator<pairwise_interaction, distance_policy> accumulator_t; 00364 accumulator_t accumulator(m_interaction, m_dist, x); 00365 auto looper = m_cell_lists.get_atom_pair_looper(accumulator); 00366 00367 looper.loop_through_atom_pairs(); 00368 00369 return accumulator.m_energy; 00370 } 00371 00372 virtual double get_energy_gradient(Array<double> x, Array<double> grad) 00373 { 00374 const size_t natoms = x.size() / m_ndim; 00375 if (m_ndim * natoms != x.size()) { 00376 throw std::runtime_error("x.size() is not divisible by the number of dimensions"); 00377 } 00378 if (x.size() != grad.size()) { 00379 throw std::invalid_argument("the gradient has the wrong size"); 00380 } 00381 00382 refresh_iterator(x); 00383 grad.assign(0.); 00384 typedef EnergyGradientAccumulator<pairwise_interaction, distance_policy> accumulator_t; 00385 accumulator_t accumulator(m_interaction, m_dist, x, grad); 00386 auto looper = m_cell_lists.get_atom_pair_looper(accumulator); 00387 00388 looper.loop_through_atom_pairs(); 00389 00390 return accumulator.m_energy; 00391 } 00392 00393 virtual double get_energy_gradient_hessian(Array<double> x, 00394 Array<double> grad, Array<double> hess) 00395 { 00396 const size_t natoms = x.size() / m_ndim; 00397 if (m_ndim * natoms != x.size()) { 00398 throw std::runtime_error("x.size() is not divisible by the number of dimensions"); 00399 } 00400 if (x.size() != grad.size()) { 00401 throw std::invalid_argument("the gradient has the wrong size"); 00402 } 00403 if (hess.size() != x.size() * x.size()) { 00404 throw std::invalid_argument("the Hessian has the wrong size"); 00405 } 00406 00407 refresh_iterator(x); 00408 grad.assign(0.); 00409 hess.assign(0.); 00410 typedef EnergyGradientHessianAccumulator<pairwise_interaction, distance_policy> accumulator_t; 00411 accumulator_t accumulator(m_interaction, m_dist, x, grad, hess); 00412 auto looper = m_cell_lists.get_atom_pair_looper(accumulator); 00413 00414 looper.loop_through_atom_pairs(); 00415 00416 return accumulator.m_energy; 00417 } 00418 00419 00420 protected: 00421 void refresh_iterator(Array<double> x) 00422 { 00423 m_cell_lists.reset(x); 00424 } 00425 }; 00426 00427 } //namespace pele 00428 00429 #endif //#ifndef _PELE_CELL_LIST_POTENTIAL_H