mcpele
1.0.0
The Monte Carlo Python Energy Landscape Explorer
|
00001 00007 #ifndef _PELE_AATOPOLOGY_H_ 00008 #define _PELE_AATOPOLOGY_H_ 00009 00010 #include <string> 00011 #include <list> 00012 #include <cmath> 00013 #include <stdexcept> 00014 00015 #include "pele/array.h" 00016 #include "pele/rotations.h" 00017 #include "pele/base_potential.h" 00018 #include "pele/vecn.h" 00019 #include "pele/lowest_eig_potential.h" 00020 #include "pele/matrix.h" 00021 #include "pele/distance.h" 00022 00023 namespace pele{ 00024 00025 00026 00027 00038 class CoordsAdaptor { 00039 size_t _nrigid; 00040 size_t _natoms; 00041 pele::Array<double> _coords; 00042 static const size_t _ndim = 3; 00043 00044 public: 00045 CoordsAdaptor(size_t nrigid, size_t natoms, pele::Array<double> coords) 00046 : _nrigid(nrigid), 00047 _natoms(natoms), 00048 _coords(coords) 00049 { 00050 if (coords.size() != (6*_nrigid + 3*_natoms)) { 00051 throw std::invalid_argument(std::string("coords has the wrong size ") + std::to_string(coords.size())); 00052 } 00053 if (natoms != 0) 00054 throw std::runtime_error("coords adaptor doesn't support free atoms yet"); 00055 } 00056 00060 pele::Array<double> get_coords() { return _coords; } 00061 00065 pele::Array<double> get_rb_positions() 00066 { 00067 if (_nrigid == 0) { 00068 // return empty array 00069 return pele::Array<double>(); 00070 } 00071 return _coords.view(0, 3*_nrigid); 00072 } 00073 00077 pele::Array<double> get_rb_position(size_t isite) 00078 { 00079 if (_nrigid == 0) { 00080 // return empty array 00081 return pele::Array<double>(); 00082 } 00083 if (isite > _nrigid) { 00084 throw std::invalid_argument("isite must be less than nrigid"); 00085 } 00086 size_t const istart = 3*isite; 00087 return _coords.view(istart, istart+3); 00088 } 00089 00093 pele::Array<double> get_rb_rotations() 00094 { 00095 if (_nrigid == 0) { 00096 // return empty array 00097 return pele::Array<double>(); 00098 } 00099 return _coords.view(3*_nrigid, 6*_nrigid); 00100 } 00101 00105 pele::Array<double> get_rb_rotation(size_t isite) 00106 { 00107 if (_nrigid == 0) { 00108 // return empty array 00109 return pele::Array<double>(); 00110 } 00111 if (isite > _nrigid) { 00112 throw std::invalid_argument("isite must be less than nrigid"); 00113 } 00114 size_t const istart = 3*_nrigid + 3*isite; 00115 return _coords.view(istart, istart+3); 00116 } 00117 00118 pele::Array<double> get_atom_positions() 00119 { 00120 if (_natoms == 0) { 00121 // return empty array 00122 return pele::Array<double>(); 00123 } 00124 00125 return _coords.view(6*_nrigid, 6*_nrigid + 3*_natoms); 00126 } 00127 00128 }; 00129 00130 // forward definition of RBTopology needed for TrasnformAACluster 00131 class RBTopology; 00132 00136 class TransformPolicy { 00137 public: 00138 // void translate(self, X, d) { 00139 // ''' translate the coordinates ''' 00140 // } 00141 virtual ~TransformPolicy() {} 00142 00146 virtual void rotate(pele::Array<double> x, pele::MatrixNM<3,3> const & mx) = 0; 00147 00148 // def can_invert(self): 00149 // ''' returns True or False if an inversion can be performed''' 00150 // 00151 // def invert(self, X): 00152 // ''' perform an inversion at the origin ''' 00153 // 00154 // def permute(self, X, perm): 00155 // ''' returns the permuted coordinates ''' 00156 00157 }; 00158 00162 class TransformAACluster : public TransformPolicy { 00163 public: 00164 pele::RBTopology * m_topology; 00165 TransformAACluster(pele::RBTopology * topology) 00166 : m_topology(topology) 00167 { } 00168 virtual ~TransformAACluster() {} 00169 00173 void rotate(pele::Array<double> x, pele::MatrixNM<3,3> const & mx); 00174 // inline void rotate(pele::Array<double> x, pele::Array<double> mx) 00175 // { 00176 // return rotate(x, pele::MatrixNM<3,3>(mx)); 00177 // } 00178 }; 00179 00180 class MeasureAngleAxisCluster { 00181 public: 00182 pele::RBTopology * m_topology; 00183 MeasureAngleAxisCluster(pele::RBTopology * topology) 00184 : m_topology(topology) 00185 { } 00186 00190 void align(pele::Array<double> const x1, pele::Array<double> x2); 00191 00192 00193 }; 00194 00195 00199 class RigidFragment { 00200 static const size_t _ndim = 3; 00201 pele::Array<double> _atom_positions; 00202 pele::MatrixAdapter<double> _atom_positions_matrix; 00203 size_t _natoms; 00204 00205 std::shared_ptr<DistanceInterface> m_distance_function; 00206 00207 double m_M; // total mass of the angle axis site 00208 double m_W; // sum of all weights 00209 pele::VecN<3> m_cog; // center of gravity 00210 pele::MatrixNM<3,3> m_S; // weighted tensor of gyration S_ij = \sum m_i x_i x_j 00211 pele::MatrixNM<3,3> m_inversion; // matrix that applies the appropriate inversion 00212 bool m_can_invert; 00213 00214 // a list of rotations that leave the rigid body unchanged. 00215 std::vector<pele::MatrixNM<3,3> > m_symmetry_rotations; 00216 00217 00218 public: 00219 RigidFragment(pele::Array<double> atom_positions, 00220 Array<double> cog, 00221 double M, 00222 double W, 00223 Array<double> S, 00224 Array<double> inversion, bool can_invert, 00225 std::shared_ptr<DistanceInterface> distance_function 00226 ) 00227 : _atom_positions(atom_positions.copy()), 00228 _atom_positions_matrix(_atom_positions, _ndim), 00229 _natoms(_atom_positions.size() / _ndim), 00230 m_distance_function(distance_function), 00231 m_M(M), 00232 m_W(W), 00233 m_cog(cog), 00234 m_S(S), 00235 m_inversion(inversion), 00236 m_can_invert(can_invert) 00237 { 00238 if (_atom_positions.size() == 0 ) { 00239 throw std::invalid_argument("the atom positions must not have zero size"); 00240 } 00241 if (_atom_positions.size() != _natoms * _ndim ) { 00242 throw std::invalid_argument("the length of atom_positions must be divisible by 3"); 00243 } 00244 } 00245 00249 inline size_t natoms() const { return _natoms; } 00250 00254 inline void add_symmetry_rotation(pele::Array<double> R) 00255 { 00256 m_symmetry_rotations.push_back(R); 00257 } 00258 00262 inline std::vector<pele::MatrixNM<3,3> > const & get_symmetry_rotations() const 00263 { 00264 return m_symmetry_rotations; 00265 } 00266 00270 pele::Array<double> to_atomistic(pele::Array<double> const com, 00271 pele::VecN<3> const & p); 00272 00277 void transform_grad( 00278 pele::VecN<3> const & p, 00279 pele::Array<double> const g, 00280 pele::VecN<3> & g_com, 00281 pele::VecN<3> & g_rot 00282 ); 00283 00291 void transform_grad( 00292 pele::Array<double> const p, 00293 pele::Array<double> const g, 00294 pele::Array<double> g_com, 00295 pele::Array<double> g_rot 00296 ) 00297 { 00298 pele::VecN<3> p_vec = p; 00299 pele::VecN<3> g_com_vec = g_com; 00300 pele::VecN<3> g_rot_vec = g_rot; 00301 transform_grad(p_vec, g, g_com_vec, g_rot_vec); 00305 for (size_t i = 0; i<3; ++i) { 00306 g_com[i] = g_com_vec[i]; 00307 g_rot[i] = g_rot_vec[i]; 00308 } 00309 00310 } 00311 00317 inline pele::VecN<3> get_smallest_rij(pele::VecN<3> const & com1, pele::VecN<3> const & com2) const 00318 { 00319 pele::VecN<3> distance; 00320 m_distance_function->get_rij(distance.data(), com2.data(), com1.data()); 00321 return distance; 00322 } 00323 00327 double distance_squared(pele::VecN<3> const & com1, pele::VecN<3> const & p1, 00328 pele::VecN<3> const & com2, pele::VecN<3> const & p2) const; 00329 00330 void distance_squared_grad(pele::VecN<3> const & com1, pele::VecN<3> const & p1, 00331 pele::VecN<3> const & com2, pele::VecN<3> const & p2, 00332 VecN<3> & g_M, VecN<3> & g_P 00333 ) const; 00334 }; 00335 00337 // * Angle axis topology 00338 // * 00339 // * An angle axis topology stores all topology information for an angle axis 00340 // * system. The AATopology is composed of several angle axis sites, 00341 // * which describe the shape of the angle axis site and each site carries a 00342 // * position and orientation. Therefore, the length of the coordinate array 00343 // * must be 6*number_of_sites. 00344 // */ 00345 //class AATopology { 00346 //}; 00347 00351 class RBTopology { 00352 std::vector<RigidFragment> _sites; 00353 size_t _natoms_total; 00354 00355 public: 00356 RBTopology() 00357 : _natoms_total(0) 00358 {} 00359 00360 void add_site(RigidFragment const & site) 00361 { 00362 _sites.push_back(site); 00363 _natoms_total += site.natoms(); 00364 } 00365 00369 std::vector<RigidFragment> const & get_sites() const { return _sites; }; 00370 00374 size_t nrigid() const { return _sites.size(); } 00375 00379 size_t natoms_total() const { return _natoms_total; } 00380 00381 size_t number_of_non_rigid_atoms() const { return 0; } 00382 00386 CoordsAdaptor get_coords_adaptor(pele::Array<double> x) const 00387 { 00388 return CoordsAdaptor(nrigid(), number_of_non_rigid_atoms(), x); 00389 } 00390 00394 Array<double> to_atomistic(Array<double> rbcoords); 00395 00399 void transform_gradient(pele::Array<double> rbcoords, 00400 pele::Array<double> grad, pele::Array<double> rbgrad); 00401 00407 pele::VecN<3> align_angle_axis_vectors(pele::VecN<3> const & p1, 00408 pele::VecN<3> const & p2in); 00409 00415 void align_all_angle_axis_vectors(pele::Array<double> x1, 00416 pele::Array<double> x2); 00417 00423 void align_path(std::list<pele::Array<double> > path); 00424 00431 void get_zero_modes(pele::Array<double> const x, 00432 std::vector<pele::Array<double> > & zev) 00433 { 00434 auto ca = get_coords_adaptor(x); 00435 pele::Array<double> v(x.size(), 0); 00436 auto cv = get_coords_adaptor(v); 00437 00438 // get the zero eigenvectors corresponding to translation 00439 std::vector<pele::Array<double> > zev_t; 00440 pele::zero_modes_translational(zev_t, nrigid(), 3); 00441 00442 for (auto const & v : zev_t) { 00443 cv.get_rb_positions().assign(v); 00444 zev.push_back(cv.get_coords().copy()); 00445 } 00446 00447 // get the zero eigenvectors corresponding to rotation 00448 TransformAACluster transform(this); 00449 double d = 1e-5; 00450 pele::VecN<3> v3; 00451 pele::Array<double> delta(x.size()); 00452 00453 // do rotations around the x y and z axes 00454 for (size_t i = 0; i < 3; ++i) { 00455 delta.assign(x); 00456 v3.assign(0); 00457 v3[i] = d; 00458 transform.rotate(delta, pele::aa_to_rot_mat(v3)); 00459 align_all_angle_axis_vectors(x, delta); 00460 delta -= x; 00461 delta /= norm(delta); 00462 zev.push_back(delta.copy()); 00463 } 00464 } 00465 00469 double distance_squared(pele::Array<double> const x1, pele::Array<double> const x2) const 00470 { 00471 double d_sq = 0; 00472 auto ca1 = get_coords_adaptor(x1); 00473 auto ca2 = get_coords_adaptor(x2); 00474 for (size_t isite = 0; isite < nrigid(); ++isite) { 00475 d_sq += _sites[isite].distance_squared( 00476 ca1.get_rb_position(isite), 00477 ca1.get_rb_rotation(isite), 00478 ca2.get_rb_position(isite), 00479 ca2.get_rb_rotation(isite) 00480 ); 00481 } 00482 return d_sq; 00483 } 00484 00490 void distance_squared_grad(pele::Array<double> const x1, pele::Array<double> const x2, 00491 pele::Array<double> grad 00492 ) const 00493 { 00494 if (grad.size() != x1.size()) { 00495 throw std::runtime_error("grad has the wrong size"); 00496 } 00497 grad.assign(0); 00498 auto ca1 = get_coords_adaptor(x1); 00499 auto ca2 = get_coords_adaptor(x2); 00500 auto ca_spring = get_coords_adaptor(grad); 00501 00502 // first distance for sites only 00503 for (size_t isite=0; isite<nrigid(); ++isite) { 00504 pele::VecN<3> g_M, g_P; 00505 _sites[isite].distance_squared_grad( 00506 ca1.get_rb_position(isite), 00507 ca1.get_rb_rotation(isite), 00508 ca2.get_rb_position(isite), 00509 ca2.get_rb_rotation(isite), 00510 g_M, g_P); 00511 auto spring_com = ca_spring.get_rb_position(isite); 00512 std::copy(g_M.begin(), g_M.end(), spring_com.begin()); 00513 auto spring_rot = ca_spring.get_rb_rotation(isite); 00514 std::copy(g_P.begin(), g_P.end(), spring_rot.begin()); 00515 } 00516 } 00517 }; 00518 00526 class RBPotentialWrapper : public BasePotential { 00527 std::shared_ptr<BasePotential> potential_; 00528 std::shared_ptr<RBTopology> topology_; 00529 public: 00530 00531 RBPotentialWrapper(std::shared_ptr<BasePotential> potential, 00532 std::shared_ptr<RBTopology> top) 00533 : potential_(potential), 00534 topology_(top) 00535 00536 {} 00537 00538 inline double get_energy(pele::Array<double> rbcoords) 00539 { 00540 auto x = topology_->to_atomistic(rbcoords); 00541 return potential_->get_energy(x); 00542 } 00543 00544 inline double get_energy_gradient(pele::Array<double> rbcoords, 00545 pele::Array<double> rbgrad) 00546 { 00547 auto x = topology_->to_atomistic(rbcoords); 00548 pele::Array<double> grad_atomistic(topology_->natoms_total() * 3); 00549 double e = potential_->get_energy_gradient(x, grad_atomistic); 00550 topology_->transform_gradient(rbcoords, grad_atomistic, rbgrad); 00551 return e; 00552 } 00553 }; 00554 00555 } 00556 00557 #endif