Program Listing for File harmonic.hpp#

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

#ifndef _PELE_HARMONIC_H
#define _PELE_HARMONIC_H

#include <algorithm>
#include <functional>

#include "atomlist_potential.hpp"
#include "base_interaction.hpp"
#include "base_potential.hpp"
#include "distance.hpp"
#include "simple_pairwise_ilist.hpp"

namespace pele {

class BaseHarmonic : public BasePotential {
 protected:
  virtual void _get_distance(const pele::Array<double> &x) = 0;
  pele::Array<double> _origin;
  pele::Array<double> _distance;
  double _k;
  const size_t _ndim;
  const size_t _nparticles;
  BaseHarmonic(const pele::Array<double> origin, const double k,
               const size_t ndim)
      : _origin(origin.copy()),
        _distance(origin.size()),
        _k(k),
        _ndim(ndim),
        _nparticles(origin.size() / _ndim) {}

 public:
  virtual ~BaseHarmonic() {}
  virtual double inline get_energy(pele::Array<double> const &x);
  virtual double inline get_energy_gradient(pele::Array<double> const &x,
                                            pele::Array<double> &grad);
  void set_k(double newk) { _k = newk; }
  double get_k() { return _k; }
};

/* calculate energy from distance squared, r0 is the hard core distance, r is
 * the distance between the centres */
double inline BaseHarmonic::get_energy(pele::Array<double> const &x) {
  this->_get_distance(x);
  return 0.5 * _k * dot(_distance, _distance);
}

/* calculate energy and gradient from distance squared, gradient is in g/|rij|,
 * r0 is the hard core distance, r is the distance between the centres */
// It seems that there the gradient is not returned as -g/r, as in other places?
double inline BaseHarmonic::get_energy_gradient(pele::Array<double> const &x,
                                                pele::Array<double> &grad) {
  assert(grad.size() == _origin.size());
  this->_get_distance(x);
#pragma simd
  for (size_t i = 0; i < x.size(); ++i) {
    grad[i] = _k * _distance[i];
  }
  return 0.5 * _k * dot(_distance, _distance);
}

class Harmonic : public BaseHarmonic {
 public:
  Harmonic(pele::Array<double> origin, double k, size_t ndim)
      : BaseHarmonic(origin, k, ndim) {}
  virtual void inline _get_distance(const pele::Array<double> &x) {
    assert(x.size() == _origin.size());
#pragma simd
    for (size_t i = 0; i < x.size(); ++i) {
      _distance[i] = x[i] - _origin[i];
    }
  }
};

class HarmonicCOM : public BaseHarmonic {
 public:
  HarmonicCOM(pele::Array<double> origin, double k, size_t ndim)
      : BaseHarmonic(origin, k, ndim) {}
  virtual void inline _get_distance(const pele::Array<double> &x) {
    assert(x.size() == _origin.size());
    pele::Array<double> delta_com(_ndim, 0);

    for (size_t i = 0; i < _nparticles; ++i) {
      const size_t i1 = i * _ndim;
      for (size_t j = 0; j < _ndim; ++j) {
        const double d = x[i1 + j] - _origin[i1 + j];
        _distance[i1 + j] = d;
        delta_com[j] += d;
      }
    }

    delta_com /= _nparticles;

    for (size_t i = 0; i < _nparticles; ++i) {
      const size_t i1 = i * _ndim;
      for (size_t j = 0; j < _ndim; ++j) {
        _distance[i1 + j] -= delta_com[j];
      }
    }
  }
};

/*
 * redo the harmonic potential in the pair potential framework
 */

struct harmonic_interaction : BaseInteraction {
  double const m_k;
  harmonic_interaction(double k) : m_k(k) {}

  /* calculate energy from distance squared */
  double inline energy(double r2, const double dij) const {
    return 0.5 * m_k * r2;
  }

  /* calculate energy and gradient from distance squared, gradient is in g/|rij|
   */
  double inline energy_gradient(double r2, double *gij,
                                const double dij) const {
    *gij = -m_k;
    return 0.5 * m_k * r2;
  }

  double inline energy_gradient_hessian(double r2, double *gij, double *hij,
                                        const double dij) const {
    *gij = -m_k;
    *hij = 1.;
    return 0.5 * m_k * r2;
  }
};

class HarmonicAtomList
    : public AtomListPotential<harmonic_interaction, cartesian_distance<3>> {
 public:
  HarmonicAtomList(double k, Array<size_t> atoms1, Array<size_t> atoms2)
      : AtomListPotential<harmonic_interaction, cartesian_distance<3>>(
            std::make_shared<harmonic_interaction>(k),
            std::make_shared<cartesian_distance<3>>(), atoms1, atoms2) {}

  HarmonicAtomList(double k, Array<size_t> atoms1)
      : AtomListPotential<harmonic_interaction, cartesian_distance<3>>(
            std::make_shared<harmonic_interaction>(k),
            std::make_shared<cartesian_distance<3>>(), atoms1) {}
};

class HarmonicNeighborList
    : public SimplePairwiseNeighborList<harmonic_interaction> {
 public:
  HarmonicNeighborList(double k, Array<size_t> ilist)
      : SimplePairwiseNeighborList<harmonic_interaction>(
            std::make_shared<harmonic_interaction>(k), ilist) {}
};

}  // namespace pele

#endif  // #ifndef _PELE_HARMONIC_H