pele
Python energy landscape explorer
|
00001 #ifndef _PELE_FROZEN_ATOMS_H 00002 #define _PELE_FROZEN_ATOMS_H 00003 00004 #include <vector> 00005 #include <algorithm> 00006 #include <set> 00007 #include <assert.h> 00008 #include <iostream> 00009 #include <memory> 00010 00011 #include "array.h" 00012 #include "base_potential.h" 00013 00014 namespace pele{ 00021 class FrozenCoordsConverter 00022 { 00023 private: 00024 std::vector<double> const _reference_coords; 00025 std::vector<size_t> _frozen_dof; 00026 std::vector<size_t> _mobile_dof; 00027 public: 00028 FrozenCoordsConverter(Array<double> const & reference_coords, 00029 Array<size_t> const & frozen_dof) : 00030 _reference_coords(reference_coords.begin(), reference_coords.end()) 00031 { 00032 //populate _frozen_dof after removing duplicates and sorting 00033 std::set<size_t> frozenset(frozen_dof.begin(), frozen_dof.end()); 00034 _frozen_dof = std::vector<size_t>(frozenset.begin(), frozenset.end()); 00035 std::sort(_frozen_dof.begin(), _frozen_dof.end()); 00036 00037 // do a sanity check 00038 if (_frozen_dof.size() > 0){ 00039 if (_frozen_dof[_frozen_dof.size()-1] >= (size_t)ndof()) { 00040 throw std::runtime_error("index of frozen degree of freedom is out of bounds"); 00041 } 00042 } 00043 00044 //populate _mobile_dof 00045 _mobile_dof = std::vector<size_t>(ndof() - ndof_frozen()); 00046 size_t imobile = 0; 00047 for (size_t i=0; i<_reference_coords.size(); ++i){ 00048 // if degree of freedom i is not in frozen, add it to _mobile_dof 00049 if (frozenset.count(i) == 0){ 00050 _mobile_dof[imobile] = i; 00051 ++imobile; 00052 } 00053 } 00054 assert(imobile == _mobile_dof.size()); 00055 00056 } 00057 00058 size_t ndof() const { return _reference_coords.size(); } 00059 size_t ndof_frozen() const { return _frozen_dof.size(); } 00060 size_t ndof_mobile() const { return _mobile_dof.size(); } 00061 00066 Array<double> get_reduced_coords(Array<double> const &full_coords){ 00067 if (full_coords.size() != ndof()) { 00068 std::invalid_argument("full_coords has the wrong size"); 00069 } 00070 Array<double> reduced_coords(ndof_mobile()); 00071 for (size_t i=0; i < ndof_mobile(); ++i){ 00072 reduced_coords[i] = full_coords[_mobile_dof[i]]; 00073 } 00074 return reduced_coords; 00075 } 00076 00081 Array<double> get_full_coords(Array<double> const &reduced_coords) 00082 { 00083 if (reduced_coords.size() != ndof_mobile()) { 00084 std::invalid_argument("reduced_coords has the wrong size"); 00085 } 00086 // make a new array full_coords as a copy of _reference_coords. 00087 //Array<double> const a(_reference_coords); //wrap _reference_coords in an Array (returns error due to _reference_coords being const) 00089 Array<double> full_coords(ndof()); 00090 for (size_t i=0; i < ndof(); ++i){ 00091 full_coords[i] = _reference_coords[i]; 00092 } 00093 // replace the mobile degrees of freedom with those in reduced_coords 00094 for (size_t i=0; i < _mobile_dof.size(); ++i){ 00095 full_coords[_mobile_dof[i]] = reduced_coords[i]; 00096 } 00097 return full_coords; 00098 } 00099 00104 Array<double> get_full_grad(Array<double> const &reduced_grad) 00105 { 00106 if (reduced_grad.size() != ndof_mobile()) { 00107 std::invalid_argument("reduced_grad has the wrong size"); 00108 } 00109 Array<double> full_grad(ndof(), 0.); 00110 // replace the mobile degrees of freedom with those in reduced_grad 00111 for (size_t i=0; i < _mobile_dof.size(); ++i){ 00112 full_grad[_mobile_dof[i]] = reduced_grad[i]; 00113 } 00114 return full_grad; 00115 } 00116 00120 Array<double> get_reduced_hessian(Array<double> const full_hess) 00121 { 00122 if (full_hess.size() != ndof()*ndof()) { 00123 std::invalid_argument("full_hessian has the wrong size"); 00124 } 00125 Array<double> reduced_hess(ndof_mobile() * ndof_mobile()); 00126 // size_t const N = ndof_mobile(); 00127 for (size_t i=0; i < ndof_mobile(); ++i){ 00128 for (size_t j=0; j < ndof_mobile(); ++j){ 00129 size_t k_red = i * ndof_mobile() + j; 00130 size_t k_full = _mobile_dof[i] * ndof() + _mobile_dof[j]; 00131 reduced_hess[k_red] = full_hess[k_full]; 00132 } 00133 } 00134 return reduced_hess; 00135 } 00136 00137 }; 00138 00139 00140 template<typename PotentialType> 00141 class FrozenPotentialWrapper : public BasePotential 00142 { 00143 public: 00144 FrozenCoordsConverter coords_converter; 00145 protected: 00146 std::shared_ptr<PotentialType> _underlying_potential; 00147 00148 FrozenPotentialWrapper(std::shared_ptr<PotentialType> potential, 00149 Array<double> const &reference_coords, 00150 Array<size_t> const & frozen_dof) : 00151 coords_converter(reference_coords, frozen_dof), 00152 _underlying_potential(potential) 00153 {} 00154 00155 public: 00156 ~FrozenPotentialWrapper() {} 00157 00158 // inline size_t ndof() const { return coords_converter.ndof(); } 00159 // inline size_t ndof_frozen() const { return coords_converter.ndof_frozen(); } 00160 // inline size_t ndof_mobile() const { return coords_converter.ndof_mobile(); } 00161 // inline Array<double> get_reduced_coords(Array<double> const &full_coords){ 00162 // return coords_converter.get_reduced_coords(full_coords); 00163 // } 00164 // Array<double> get_full_coords(Array<double> const &reduced_coords){ 00165 // return coords_converter.get_full_coords(reduced_coords); 00166 // } 00167 00168 inline double get_energy(Array<double> reduced_coords) 00169 { 00170 if (reduced_coords.size() != coords_converter.ndof_mobile()){ 00171 throw std::runtime_error("reduced coords does not have the right size"); 00172 } 00173 Array<double> full_coords(coords_converter.get_full_coords(reduced_coords)); 00174 return _underlying_potential->get_energy(full_coords); 00175 } 00176 00177 inline double get_energy_gradient(Array<double> reduced_coords, Array<double> reduced_grad) 00178 { 00179 if (reduced_coords.size() != coords_converter.ndof_mobile()){ 00180 throw std::runtime_error("reduced coords does not have the right size"); 00181 } 00182 if (reduced_grad.size() != coords_converter.ndof_mobile()) { 00183 throw std::invalid_argument("reduced_grad has the wrong size"); 00184 } 00185 00186 Array<double> full_coords(coords_converter.get_full_coords(reduced_coords)); 00187 Array<double> gfull(coords_converter.ndof()); 00188 double energy = _underlying_potential->get_energy_gradient(full_coords, gfull); 00189 Array<double> gred = coords_converter.get_reduced_coords(gfull); 00190 for (size_t i = 0; i < gred.size(); ++i){ 00191 reduced_grad[i] = gred[i]; 00192 } 00193 return energy; 00194 } 00195 00196 inline double get_energy_gradient_hessian(Array<double> reduced_coords, 00197 Array<double> reduced_grad, Array<double> reduced_hess) 00198 { 00199 if (reduced_coords.size() != coords_converter.ndof_mobile()){ 00200 throw std::runtime_error("reduced coords does not have the right size"); 00201 } 00202 if (reduced_grad.size() != coords_converter.ndof_mobile()) { 00203 throw std::invalid_argument("reduced_grad has the wrong size"); 00204 } 00205 if (reduced_hess.size() != coords_converter.ndof_mobile()*coords_converter.ndof_mobile()){ 00206 throw std::invalid_argument("reduced_hess has the wrong size"); 00207 } 00208 Array<double> full_coords(coords_converter.get_full_coords(reduced_coords)); 00209 Array<double> gfull(coords_converter.ndof()); 00210 Array<double> hfull(coords_converter.ndof()*coords_converter.ndof()); 00211 const double energy = _underlying_potential->get_energy_gradient_hessian(full_coords, gfull, hfull); 00212 Array<double> gred = coords_converter.get_reduced_coords(gfull); 00213 Array<double> hred = coords_converter.get_reduced_hessian(hfull); 00214 reduced_grad.assign(gred); 00215 reduced_hess.assign(hred); 00216 return energy; 00217 } 00218 }; 00219 } 00220 00221 #endif