Program Listing for File aatopology.hpp#
↰ Return to documentation for file (source/pele/aatopology.hpp)
/*
* This is a partial c++ implementation of the tools needed to interact with
* systems of rigid bodies. This is not a complete reimplementation, only the
* parts that were too slow in python were implemented here.
*
*/
#ifndef _PELE_AATOPOLOGY_H_
#define _PELE_AATOPOLOGY_H_
#include <cmath>
#include <list>
#include <stdexcept>
#include <string>
#include "pele/array.hpp"
#include "pele/base_potential.hpp"
#include "pele/distance.hpp"
#include "pele/lowest_eig_potential.hpp"
#include "pele/matrix.hpp"
#include "pele/rotations.hpp"
#include "pele/vecn.hpp"
namespace pele {
class CoordsAdaptor {
size_t _nrigid;
size_t _natoms;
pele::Array<double> _coords;
static const size_t _ndim = 3;
public:
CoordsAdaptor(size_t nrigid, size_t natoms, pele::Array<double> coords)
: _nrigid(nrigid), _natoms(natoms), _coords(coords) {
if (coords.size() != (6 * _nrigid + 3 * _natoms)) {
throw std::invalid_argument(std::string("coords has the wrong size ") +
std::to_string(coords.size()));
}
if (natoms != 0)
throw std::runtime_error("coords adaptor doesn't support free atoms yet");
}
pele::Array<double> get_coords() { return _coords; }
pele::Array<double> get_rb_positions() {
if (_nrigid == 0) {
// return empty array
return pele::Array<double>();
}
return _coords.view(0, 3 * _nrigid);
}
pele::Array<double> get_rb_position(size_t isite) {
if (_nrigid == 0) {
// return empty array
return pele::Array<double>();
}
if (isite > _nrigid) {
throw std::invalid_argument("isite must be less than nrigid");
}
size_t const istart = 3 * isite;
return _coords.view(istart, istart + 3);
}
pele::Array<double> get_rb_rotations() {
if (_nrigid == 0) {
// return empty array
return pele::Array<double>();
}
return _coords.view(3 * _nrigid, 6 * _nrigid);
}
pele::Array<double> get_rb_rotation(size_t isite) {
if (_nrigid == 0) {
// return empty array
return pele::Array<double>();
}
if (isite > _nrigid) {
throw std::invalid_argument("isite must be less than nrigid");
}
size_t const istart = 3 * _nrigid + 3 * isite;
return _coords.view(istart, istart + 3);
}
pele::Array<double> get_atom_positions() {
if (_natoms == 0) {
// return empty array
return pele::Array<double>();
}
return _coords.view(6 * _nrigid, 6 * _nrigid + 3 * _natoms);
}
};
// forward definition of RBTopology needed for TrasnformAACluster
class RBTopology;
class TransformPolicy {
public:
// void translate(self, X, d) {
// ''' translate the coordinates '''
// }
virtual ~TransformPolicy() {}
virtual void rotate(pele::Array<double> x,
pele::MatrixNM<3, 3> const &mx) = 0;
// def can_invert(self):
// ''' returns True or False if an inversion can be performed'''
//
// def invert(self, X):
// ''' perform an inversion at the origin '''
//
// def permute(self, X, perm):
// ''' returns the permuted coordinates '''
};
class TransformAACluster : public TransformPolicy {
public:
pele::RBTopology *m_topology;
TransformAACluster(pele::RBTopology *topology) : m_topology(topology) {}
virtual ~TransformAACluster() {}
void rotate(pele::Array<double> x, pele::MatrixNM<3, 3> const &mx);
// inline void rotate(pele::Array<double> x, pele::Array<double> mx)
// {
// return rotate(x, pele::MatrixNM<3,3>(mx));
// }
};
class MeasureAngleAxisCluster {
public:
pele::RBTopology *m_topology;
MeasureAngleAxisCluster(pele::RBTopology *topology) : m_topology(topology) {}
void align(pele::Array<double> const x1, pele::Array<double> x2);
};
class RigidFragment {
static const size_t _ndim = 3;
pele::Array<double> _atom_positions;
pele::MatrixAdapter<double> _atom_positions_matrix;
size_t _natoms;
std::shared_ptr<DistanceInterface> m_distance_function;
double m_M; // total mass of the angle axis site
double m_W; // sum of all weights
pele::VecN<3> m_cog; // center of geometry
pele::MatrixNM<3, 3>
m_S; // weighted tensor of gyration S_ij = \sum m_i x_i x_j
pele::MatrixNM<3, 3>
m_inversion; // matrix that applies the appropriate inversion
bool m_can_invert;
// a list of rotations that leave the rigid body unchanged.
std::vector<pele::MatrixNM<3, 3>> m_symmetry_rotations;
// the list of indices that each atom takes in the coords array
// coords[atom_indices[i]] is the first coordinate of the i'th atom
pele::Array<size_t> m_atom_indices;
public:
RigidFragment(pele::Array<double> atom_positions, Array<double> cog, double M,
double W, Array<double> S, Array<double> inversion,
bool can_invert,
std::shared_ptr<DistanceInterface> distance_function)
: _atom_positions(atom_positions.copy()),
_atom_positions_matrix(_atom_positions, _ndim),
_natoms(_atom_positions.size() / _ndim),
m_distance_function(distance_function),
m_M(M),
m_W(W),
m_cog(cog),
m_S(S),
m_inversion(inversion),
m_can_invert(can_invert) {
if (_atom_positions.size() == 0) {
throw std::invalid_argument("the atom positions must not have zero size");
}
if (_atom_positions.size() != _natoms * _ndim) {
throw std::invalid_argument(
"the length of atom_positions must be divisible by 3");
}
}
inline size_t natoms() const { return _natoms; }
inline void add_symmetry_rotation(pele::Array<double> R) {
m_symmetry_rotations.push_back(R.copy());
}
inline void set_atom_indices(pele::Array<size_t> atom_indices) {
if (atom_indices.size() != natoms()) {
throw std::runtime_error("atom_indices has the wrong size");
}
m_atom_indices = atom_indices.copy();
}
inline pele::Array<size_t> &get_atom_indices() { return m_atom_indices; }
inline std::vector<pele::MatrixNM<3, 3>> const &get_symmetry_rotations()
const {
return m_symmetry_rotations;
}
pele::Array<double> to_atomistic(pele::Array<double> const com,
pele::VecN<3> const &p);
void transform_grad(pele::VecN<3> const &p, pele::Array<double> const g,
pele::VecN<3> &g_com, pele::VecN<3> &g_rot);
void transform_grad(pele::Array<double> const p, pele::Array<double> const g,
pele::Array<double> g_com, pele::Array<double> g_rot) {
pele::VecN<3> p_vec = p;
pele::VecN<3> g_com_vec = g_com;
pele::VecN<3> g_rot_vec = g_rot;
transform_grad(p_vec, g, g_com_vec, g_rot_vec);
for (size_t i = 0; i < 3; ++i) {
g_com[i] = g_com_vec[i];
g_rot[i] = g_rot_vec[i];
}
}
inline pele::VecN<3> get_smallest_rij(pele::VecN<3> const &com1,
pele::VecN<3> const &com2) const {
pele::VecN<3> distance;
m_distance_function->get_rij(distance.data(), com2.data(), com1.data());
return distance;
}
double distance_squared(pele::VecN<3> const &com1, pele::VecN<3> const &p1,
pele::VecN<3> const &com2,
pele::VecN<3> const &p2) const;
void distance_squared_grad(pele::VecN<3> const &com1, pele::VecN<3> const &p1,
pele::VecN<3> const &com2, pele::VecN<3> const &p2,
VecN<3> &g_M, VecN<3> &g_P) const;
};
// Inactive angle-axis topology prototype.
// class AATopology {
//};
class RBTopology {
std::vector<RigidFragment> _sites;
size_t _natoms_total;
public:
RBTopology() : _natoms_total(0) {}
void add_site(RigidFragment const &site) {
_sites.push_back(site);
_natoms_total += site.natoms();
}
std::vector<RigidFragment> const &get_sites() const { return _sites; };
void assign_atom_indices() {
size_t atom = 0;
for (auto &site : _sites) {
pele::Array<size_t> atom_indices(site.natoms());
for (size_t i = 0; i < atom_indices.size(); ++i)
atom_indices[i] = atom + i;
site.set_atom_indices(atom_indices);
atom += site.natoms();
}
}
size_t nrigid() const { return _sites.size(); }
size_t natoms_total() const { return _natoms_total; }
size_t number_of_non_rigid_atoms() const { return 0; }
CoordsAdaptor get_coords_adaptor(pele::Array<double> x) const {
return CoordsAdaptor(nrigid(), number_of_non_rigid_atoms(), x);
}
Array<double> to_atomistic(Array<double> rbcoords);
void transform_gradient(pele::Array<double> rbcoords,
pele::Array<double> grad, pele::Array<double> rbgrad);
pele::VecN<3> align_angle_axis_vectors(pele::VecN<3> const &p1,
pele::VecN<3> const &p2in);
void align_all_angle_axis_vectors(pele::Array<double> x1,
pele::Array<double> x2);
void align_path(std::list<pele::Array<double>> path);
void get_zero_modes(pele::Array<double> const x,
std::vector<pele::Array<double>> &zev) {
auto ca = get_coords_adaptor(x);
pele::Array<double> v(x.size(), 0);
auto cv = get_coords_adaptor(v);
// get the zero eigenvectors corresponding to translation
std::vector<pele::Array<double>> zev_t;
pele::zero_modes_translational(zev_t, nrigid(), 3);
for (auto const &v : zev_t) {
cv.get_rb_positions().assign(v);
zev.push_back(cv.get_coords().copy());
}
// get the zero eigenvectors corresponding to rotation
TransformAACluster transform(this);
double d = 1e-5;
pele::VecN<3> v3;
pele::Array<double> delta(x.size());
// do rotations around the x y and z axes
for (size_t i = 0; i < 3; ++i) {
delta.assign(x);
v3.assign(0.0);
v3[i] = d;
transform.rotate(delta, pele::aa_to_rot_mat(v3));
align_all_angle_axis_vectors(x, delta);
delta -= x;
delta /= norm(delta);
zev.push_back(delta.copy());
}
}
double distance_squared(pele::Array<double> const x1,
pele::Array<double> const x2) const {
double d_sq = 0;
auto ca1 = get_coords_adaptor(x1);
auto ca2 = get_coords_adaptor(x2);
for (size_t isite = 0; isite < nrigid(); ++isite) {
d_sq += _sites[isite].distance_squared(
ca1.get_rb_position(isite), ca1.get_rb_rotation(isite),
ca2.get_rb_position(isite), ca2.get_rb_rotation(isite));
}
return d_sq;
}
void distance_squared_grad(pele::Array<double> const x1,
pele::Array<double> const x2,
pele::Array<double> grad) const {
if (grad.size() != x1.size()) {
throw std::runtime_error("grad has the wrong size");
}
grad.assign(0);
auto ca1 = get_coords_adaptor(x1);
auto ca2 = get_coords_adaptor(x2);
auto ca_spring = get_coords_adaptor(grad);
// first distance for sites only
for (size_t isite = 0; isite < nrigid(); ++isite) {
pele::VecN<3> g_M, g_P;
_sites[isite].distance_squared_grad(
ca1.get_rb_position(isite), ca1.get_rb_rotation(isite),
ca2.get_rb_position(isite), ca2.get_rb_rotation(isite), g_M, g_P);
auto spring_com = ca_spring.get_rb_position(isite);
std::copy(g_M.begin(), g_M.end(), spring_com.begin());
auto spring_rot = ca_spring.get_rb_rotation(isite);
std::copy(g_P.begin(), g_P.end(), spring_rot.begin());
}
}
};
class RBPotentialWrapper : public BasePotential {
std::shared_ptr<BasePotential> potential_;
std::shared_ptr<RBTopology> topology_;
public:
RBPotentialWrapper(std::shared_ptr<BasePotential> potential,
std::shared_ptr<RBTopology> top)
: potential_(potential),
topology_(top)
{}
inline double get_energy(pele::Array<double> const &rbcoords) {
auto x = topology_->to_atomistic(rbcoords);
return potential_->get_energy(x);
}
inline double get_energy_gradient(pele::Array<double> const &rbcoords,
pele::Array<double> &rbgrad) {
auto x = topology_->to_atomistic(rbcoords);
pele::Array<double> grad_atomistic(topology_->natoms_total() * 3);
double e = potential_->get_energy_gradient(x, grad_atomistic);
topology_->transform_gradient(rbcoords, grad_atomistic, rbgrad);
return e;
}
};
} // namespace pele
#endif