Program Listing for File radial_gaussian.hpp#

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

#ifndef _PELE_RADIALGAUSSIAN_H
#define _PELE_RADIALGAUSSIAN_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 BaseRadialGaussian : public BasePotential {
 protected:
  virtual void _get_distance(const pele::Array<double> &x) = 0;
  pele::Array<double> _origin;
  pele::Array<double> _distance;
  double _k;
  double _l0;
  double _log_prefactor;
  double _r_cutoff;
  const size_t _ndim;
  const size_t _nparticles;
  BaseRadialGaussian(
               const pele::Array<double> origin,
               const double k,
               const double l0,
               const double log_prefactor,
               const double r_cutoff,
               const size_t ndim)
      : _origin(origin.copy()),
        _distance(origin.size()),
        _k(k),
        _l0(l0),
        _log_prefactor(log_prefactor),
        _r_cutoff(r_cutoff),
        _ndim(ndim),
        _nparticles(origin.size() / _ndim) {}

 public:
  virtual ~BaseRadialGaussian() {}
  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; }
  void set_l0(double newl0) {_l0 = newl0; }
  void set_log_prefactor(double newlog_prefactor) {_log_prefactor = newlog_prefactor; }
  void set_r_cutoff(double newr_cutoff) {_r_cutoff = newr_cutoff; }
  double get_k() { return _k; }
  double get_l0() {return _l0; }
  double get_log_prefactor() {return _log_prefactor; }
  double get_r_cutoff() {return _r_cutoff; }
};

/* calculate energy from distance.
 * r0 is the hard core distance,
 * r is the distance between the centres */
double inline BaseRadialGaussian::get_energy(pele::Array<double> const &x) {
  this->_get_distance(x);
  const double _distance_modulus = sqrt(dot(_distance, _distance));
  const double _spring_deformation = _distance_modulus - _l0;
  const size_t _ndof = _ndim * _nparticles;

  // The energy here assumes beta = 1, otherwise a 1/beta factor is needed in front of the log
  double _energy = 0.5 * _k * _spring_deformation * _spring_deformation;
  if (_log_prefactor != 0.0 && _distance_modulus > _r_cutoff) {
    _energy += _log_prefactor * ((double)_ndof - 1.0) * log(_distance_modulus / _r_cutoff);
  }

  return _energy;
}

/* 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 BaseRadialGaussian::get_energy_gradient(pele::Array<double> const &x,
                                                pele::Array<double> &grad) {
  assert(grad.size() == _origin.size());
  this->_get_distance(x);
  const double _distance_modulus = sqrt(dot(_distance, _distance));
  const double _spring_deformation = _distance_modulus - _l0;
  const size_t _ndof = _ndim * _nparticles;

  // The energy and gradient here both assume beta = 1, otherwise a 1/beta factor is needed in front of the log

#pragma simd
  for (size_t i = 0; i < x.size(); ++i) {
    // The gradient is singular at the cutoff, which does not matter since this potential is only used within Metropolis acceptances
    grad[i] =  _k * _distance[i] * (1.0 - _l0/_distance_modulus);
    if (_log_prefactor != 0.0 && _distance_modulus > _r_cutoff) {
      grad[i] += _log_prefactor * ((double)_ndof - 1.0) * _distance[i]/(_distance_modulus*_distance_modulus) ;
    }
  }

  double _energy = 0.5 * _k * _spring_deformation * _spring_deformation;
  if (_log_prefactor != 0.0 && _distance_modulus > _r_cutoff) {
    _energy += _log_prefactor * ((double)_ndof - 1.0) * log(_distance_modulus / _r_cutoff);
  }

  return _energy;
}

class RadialGaussian : public BaseRadialGaussian {
 public:
  RadialGaussian(pele::Array<double> origin, double k, double l0, double log_prefactor, double r_cutoff, size_t ndim)
      : BaseRadialGaussian(origin, k, l0, log_prefactor, r_cutoff, 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 RadialGaussianCOM : public BaseRadialGaussian {
 public:
  RadialGaussianCOM(pele::Array<double> origin, double k, double l0, double log_prefactor, double r_cutoff, size_t ndim)
      : BaseRadialGaussian(origin, k, l0, log_prefactor, r_cutoff, 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];
      }
    }
  }
};
}  // namespace pele

#endif  // #ifndef _PELE_RadialGaussian_H