Program Listing for File atomlist_potential.hpp#

↰ Return to documentation for file (source/pele/atomlist_potential.hpp)

#ifndef _ATOMLIST_POTENTIAL_H_
#define _ATOMLIST_POTENTIAL_H_

#include <iostream>

#include "array.hpp"
#include "pairwise_potential_interface.hpp"

namespace pele {

template <typename pairwise_interaction, typename distance_policy>
class AtomListPotential : public PairwisePotentialInterface {
 protected:
  std::shared_ptr<pairwise_interaction> _interaction;
  std::shared_ptr<distance_policy> _dist;
  Array<size_t> _atoms1;
  Array<size_t> _atoms2;
  bool _one_list;
  static const size_t _ndim = distance_policy::_ndim;

  AtomListPotential(std::shared_ptr<pairwise_interaction> interaction,
                    std::shared_ptr<distance_policy> dist,
                    Array<size_t> &atoms1, Array<size_t> &atoms2,
                    const Array<double> radii)
      : PairwisePotentialInterface(radii),
        _interaction(interaction),
        _dist(dist),
        _atoms1(atoms1.copy()),
        _atoms2(atoms2.copy()),
        _one_list(false) {}

  AtomListPotential(std::shared_ptr<pairwise_interaction> interaction,
                    std::shared_ptr<distance_policy> dist,
                    Array<size_t> &atoms1, Array<size_t> &atoms2)
      : _interaction(interaction),
        _dist(dist),
        _atoms1(atoms1.copy()),
        _atoms2(atoms2.copy()),
        _one_list(false) {}

  AtomListPotential(std::shared_ptr<pairwise_interaction> interaction,
                    std::shared_ptr<distance_policy> dist,
                    Array<size_t> &atoms1)
      : _interaction(interaction),
        _dist(dist),
        _atoms1(atoms1.copy()),
        _atoms2(_atoms1),
        _one_list(true) {}

 public:
  virtual inline double get_energy(Array<double> const &x) {
    double e = 0.;
    size_t jstart = 0;
    double dr[_ndim];

    for (size_t i = 0; i < _atoms1.size(); ++i) {
      size_t atom1 = _atoms1[i];
      size_t i1 = _ndim * atom1;
      if (_one_list) jstart = i + 1;
      for (size_t j = jstart; j < _atoms2.size(); ++j) {
        size_t atom2 = _atoms2[j];
        size_t i2 = _ndim * atom2;

        _dist->get_rij(dr, &x[i1], &x[i2]);
        double r2 = 0;
        for (size_t k = 0; k < _ndim; ++k) {
          r2 += dr[k] * dr[k];
        }
        e += _interaction->energy(r2, get_non_additive_cutoff(atom1, atom2));
      }
    }

    return e;
  }

  virtual inline double add_energy_gradient(Array<double> const &x,
                                            Array<double> &grad) {
    if (x.size() != grad.size()) {
      throw std::invalid_argument("the gradient has the wrong size");
    }
    double e = 0.;
    double gij;
    size_t jstart = 0;
    double dr[_ndim];
    ;

    for (size_t i = 0; i < _atoms1.size(); ++i) {
      size_t atom1 = _atoms1[i];
      size_t i1 = _ndim * atom1;
      if (_one_list) {
        jstart = i + 1;
      }
      for (size_t j = jstart; j < _atoms2.size(); ++j) {
        size_t atom2 = _atoms2[j];
        size_t i2 = _ndim * atom2;

        _dist->get_rij(dr, &x[i1], &x[i2]);

        double r2 = 0;
        for (size_t k = 0; k < _ndim; ++k) {
          r2 += dr[k] * dr[k];
        }

        e += _interaction->energy_gradient(
            r2, &gij, get_non_additive_cutoff(atom1, atom2));
        for (size_t k = 0; k < _ndim; ++k) grad[i1 + k] -= gij * dr[k];
        for (size_t k = 0; k < _ndim; ++k) grad[i2 + k] += gij * dr[k];
      }
    }

    return e;
  }

  virtual inline double add_energy_gradient_hessian(Array<double> const &x,
                                                    Array<double> &grad,
                                                    Array<double> &hess) {
    if (x.size() != grad.size()) {
      throw std::invalid_argument("the gradient has the wrong size");
    }
    if (hess.size() != x.size() * x.size()) {
      throw std::invalid_argument("the Hessian has the wrong size");
    }

    double e = 0.;
    double hij, gij;
    size_t jstart = 0;
    double dr[_ndim];
    const size_t N = x.size();

    for (size_t i = 0; i < _atoms1.size(); ++i) {
      size_t atom1 = _atoms1[i];
      size_t i1 = _ndim * atom1;

      if (_one_list) {
        jstart = i + 1;
      }
      for (size_t j = jstart; j < _atoms2.size(); ++j) {
        size_t atom2 = _atoms2[j];
        size_t i2 = _ndim * atom2;

        _dist->get_rij(dr, &x[i1], &x[i2]);
        double r2 = 0;
        for (size_t k = 0; k < _ndim; ++k) {
          r2 += dr[k] * dr[k];
        }

        e += _interaction->energy_gradient_hessian(
            r2, &gij, &hij, get_non_additive_cutoff(atom1, atom2));
        for (size_t k = 0; k < _ndim; ++k) grad[i1 + k] -= gij * dr[k];
        for (size_t k = 0; k < _ndim; ++k) grad[i2 + k] += gij * dr[k];

        for (size_t k = 0; k < _ndim; ++k) {
          // diagonal block - diagonal terms
          double Hii_diag = (hij + gij) * dr[k] * dr[k] / r2 - gij;
          hess[N * (i1 + k) + i1 + k] += Hii_diag;
          hess[N * (i2 + k) + i2 + k] += Hii_diag;
          // off diagonal block - diagonal terms
          double Hij_diag = -Hii_diag;
          hess[N * (i1 + k) + i2 + k] = Hij_diag;
          hess[N * (i2 + k) + i1 + k] = Hij_diag;
          for (size_t l = k + 1; l < _ndim; ++l) {
            // diagonal block - off diagonal terms
            double Hii_off = (hij + gij) * dr[k] * dr[l] / r2;
            hess[N * (i1 + k) + i1 + l] += Hii_off;
            hess[N * (i1 + l) + i1 + k] += Hii_off;
            hess[N * (i2 + k) + i2 + l] += Hii_off;
            hess[N * (i2 + l) + i2 + k] += Hii_off;
            // off diagonal block - off diagonal terms
            double Hij_off = -Hii_off;
            hess[N * (i1 + k) + i2 + l] = Hij_off;
            hess[N * (i1 + l) + i2 + k] = Hij_off;
            hess[N * (i2 + k) + i1 + l] = Hij_off;
            hess[N * (i2 + l) + i1 + k] = Hij_off;
          }
        }
      }
    }

    return e;
  }
};
}  // namespace pele

#endif