pele
Python energy landscape explorer
/home/js850/projects/pele/source/pele/cell_list_potential.h
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
 All Classes Namespaces Functions Variables Typedefs