Program Listing for File frozen_atoms.hpp#
↰ Return to documentation for file (source/pele/frozen_atoms.hpp)
#ifndef _PELE_FROZEN_ATOMS_H
#define _PELE_FROZEN_ATOMS_H
#include <algorithm>
#include <assert.h>
#include <iostream>
#include <memory>
#include <set>
#include <vector>
#include "array.hpp"
#include "base_potential.hpp"
namespace pele {
class FrozenCoordsConverter {
protected:
std::vector<double> const _reference_coords;
std::vector<size_t> _frozen_dof;
std::vector<size_t> _mobile_dof;
public:
FrozenCoordsConverter(Array<double> const reference_coords,
Array<size_t> const frozen_dof)
: _reference_coords(reference_coords.begin(), reference_coords.end()) {
// populate _frozen_dof after removing duplicates and sorting
std::set<size_t> frozenset(frozen_dof.begin(), frozen_dof.end());
_frozen_dof = std::vector<size_t>(frozenset.begin(), frozenset.end());
std::sort(_frozen_dof.begin(), _frozen_dof.end());
// do a sanity check
if (_frozen_dof.size() > 0) {
if (_frozen_dof[_frozen_dof.size() - 1] >= (size_t)ndof()) {
throw std::runtime_error(
"index of frozen degree of freedom is out of bounds");
}
}
// populate _mobile_dof
_mobile_dof = std::vector<size_t>(ndof() - ndof_frozen());
size_t imobile = 0;
for (size_t i = 0; i < _reference_coords.size(); ++i) {
// if degree of freedom i is not in frozen, add it to _mobile_dof
if (frozenset.count(i) == 0) {
_mobile_dof[imobile] = i;
++imobile;
}
}
assert(imobile == _mobile_dof.size());
}
size_t ndof() const { return _reference_coords.size(); }
size_t ndof_frozen() const { return _frozen_dof.size(); }
size_t ndof_mobile() const { return _mobile_dof.size(); }
pele::Array<size_t> get_frozen_dof() {
return pele::Array<size_t>(_frozen_dof);
}
pele::Array<size_t> get_mobile_dof() {
return pele::Array<size_t>(_mobile_dof);
}
Array<double> get_reduced_coords(Array<double> const &full_coords) {
if (full_coords.size() != ndof()) {
std::invalid_argument("full_coords has the wrong size");
}
Array<double> reduced_coords(ndof_mobile());
for (size_t i = 0; i < ndof_mobile(); ++i) {
reduced_coords[i] = full_coords[_mobile_dof[i]];
}
return reduced_coords;
}
Array<double> get_full_coords(Array<double> const &reduced_coords) {
if (reduced_coords.size() != ndof_mobile()) {
std::invalid_argument("reduced_coords has the wrong size");
}
// make a new array full_coords as a copy of _reference_coords.
// Array<double> const a(_reference_coords); //wrap _reference_coords in an
// Array (returns error due to _reference_coords being const)
Array<double> full_coords(ndof());
std::copy(_reference_coords.begin(), _reference_coords.end(),
full_coords.begin());
// replace the mobile degrees of freedom with those in reduced_coords
for (size_t i = 0; i < _mobile_dof.size(); ++i) {
full_coords[_mobile_dof[i]] = reduced_coords[i];
}
return full_coords;
}
Array<double> get_full_grad(Array<double> const &reduced_grad) {
if (reduced_grad.size() != ndof_mobile()) {
std::invalid_argument("reduced_grad has the wrong size");
}
Array<double> full_grad(ndof(), 0.);
// replace the mobile degrees of freedom with those in reduced_grad
for (size_t i = 0; i < _mobile_dof.size(); ++i) {
full_grad[_mobile_dof[i]] = reduced_grad[i];
}
return full_grad;
}
Array<double> get_reduced_hessian(Array<double> const full_hess) {
if (full_hess.size() != ndof() * ndof()) {
std::invalid_argument("full_hessian has the wrong size");
}
Array<double> reduced_hess(ndof_mobile() * ndof_mobile());
// size_t const N = ndof_mobile();
for (size_t i = 0; i < ndof_mobile(); ++i) {
for (size_t j = 0; j < ndof_mobile(); ++j) {
size_t k_red = i * ndof_mobile() + j;
size_t k_full = _mobile_dof[i] * ndof() + _mobile_dof[j];
reduced_hess[k_red] = full_hess[k_full];
}
}
return reduced_hess;
}
};
class FrozenPotentialWrapper : public BasePotential {
protected:
std::shared_ptr<BasePotential> _underlying_potential;
public:
FrozenCoordsConverter coords_converter;
FrozenPotentialWrapper(std::shared_ptr<BasePotential> potential,
Array<double> const reference_coords,
Array<size_t> const frozen_dof)
: _underlying_potential(potential),
coords_converter(reference_coords, frozen_dof)
{}
~FrozenPotentialWrapper() {}
// inline size_t ndof() const { return coords_converter.ndof(); }
// inline size_t ndof_frozen() const { return
// coords_converter.ndof_frozen(); } inline size_t ndof_mobile() const {
// return coords_converter.ndof_mobile(); }
inline pele::Array<size_t> get_mobile_dof() {
return coords_converter.get_mobile_dof();
}
inline pele::Array<size_t> get_frozen_dof() {
return coords_converter.get_frozen_dof();
}
inline Array<double> get_reduced_coords(Array<double> const full_coords) {
return coords_converter.get_reduced_coords(full_coords);
}
Array<double> get_full_coords(Array<double> const reduced_coords) {
return coords_converter.get_full_coords(reduced_coords);
}
inline double get_energy(Array<double> const &reduced_coords) {
if (reduced_coords.size() != coords_converter.ndof_mobile()) {
throw std::runtime_error("reduced coords does not have the right size");
}
Array<double> full_coords(coords_converter.get_full_coords(reduced_coords));
double const energy = _underlying_potential->get_energy(full_coords);
// reduced_coords.assign(coords_converter.get_reduced_coords(full_coords));
return energy;
}
inline double get_energy_gradient(Array<double> const &reduced_coords,
Array<double> &reduced_grad) {
if (reduced_coords.size() != coords_converter.ndof_mobile()) {
throw std::runtime_error("reduced coords does not have the right size");
}
if (reduced_grad.size() != coords_converter.ndof_mobile()) {
throw std::invalid_argument("reduced_grad has the wrong size");
}
Array<double> full_coords(coords_converter.get_full_coords(reduced_coords));
Array<double> gfull(coords_converter.ndof());
double const energy =
_underlying_potential->get_energy_gradient(full_coords, gfull);
// reduced_coords.assign(coords_converter.get_reduced_coords(full_coords));
reduced_grad.assign(coords_converter.get_reduced_coords(gfull));
return energy;
}
inline double get_energy_gradient_hessian(Array<double> const &reduced_coords,
Array<double> &reduced_grad,
Array<double> &reduced_hess) {
if (reduced_coords.size() != coords_converter.ndof_mobile()) {
throw std::runtime_error("reduced coords does not have the right size");
}
if (reduced_grad.size() != coords_converter.ndof_mobile()) {
throw std::invalid_argument("reduced_grad has the wrong size");
}
if (reduced_hess.size() !=
coords_converter.ndof_mobile() * coords_converter.ndof_mobile()) {
throw std::invalid_argument("reduced_hess has the wrong size");
}
Array<double> full_coords(coords_converter.get_full_coords(reduced_coords));
Array<double> gfull(coords_converter.ndof());
Array<double> hfull(coords_converter.ndof() * coords_converter.ndof());
const double energy = _underlying_potential->get_energy_gradient_hessian(
full_coords, gfull, hfull);
// reduced_coords.assign(coords_converter.get_reduced_coords(full_coords));
reduced_grad.assign(coords_converter.get_reduced_coords(gfull));
reduced_hess.assign(coords_converter.get_reduced_hessian(hfull));
return energy;
}
};
} // namespace pele
#endif