![]() |
ESPResSo 3.2.0-11-g9950804-git
Extensible Simulation Package for Soft Matter Research
|
00001 /* 00002 Copyright (C) 2010,2012,2013 The ESPResSo project 00003 Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 00004 Max-Planck-Institute for Polymer Research, Theory Group 00005 00006 This file is part of ESPResSo. 00007 00008 ESPResSo is free software: you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation, either version 3 of the License, or 00011 (at your option) any later version. 00012 00013 ESPResSo is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 GNU General Public License for more details. 00017 00018 You should have received a copy of the GNU General Public License 00019 along with this program. If not, see <http://www.gnu.org/licenses/>. 00020 */ 00021 /** \file rotation.c Molecular dynamics integrator for rotational motion. 00022 * 00023 * A velocity Verlet <a HREF="http://ciks.cbt.nist.gov/~garbocz/dpd1/dpd.html">algorithm</a> 00024 * using quaternions is implemented to tackle rotational motion. A random torque and a friction 00025 * term are added to provide the constant NVT conditions. Due to this feature all particles are 00026 * treated as 3D objects with 3 translational and 3 rotational degrees of freedom if ROTATION 00027 * flag is set in \ref config.h "config.h". 00028 */ 00029 00030 #include <mpi.h> 00031 #include <stdio.h> 00032 #include <stdlib.h> 00033 #include <string.h> 00034 #include <math.h> 00035 #include "utils.h" 00036 #include "integrate.h" 00037 #include "interaction_data.h" 00038 #include "particle_data.h" 00039 #include "communication.h" 00040 #include "grid.h" 00041 #include "cells.h" 00042 #include "verlet.h" 00043 #include "rotation.h" 00044 #include "ghosts.h" 00045 #include "forces.h" 00046 #include "p3m.h" 00047 #include "thermostat.h" 00048 #include "initialize.h" 00049 00050 /**************************************************** 00051 * DEFINES 00052 ***************************************************/ 00053 /**************** local variables *******************/ 00054 00055 #ifdef ROTATION 00056 00057 /** moment of inertia. Currently we define the inertia tensor here to be constant. 00058 If it is not spherical the angular velocities have to be refined several times 00059 in the \ref convert_torques_propagate_omega. Also the kinetic energy in file 00060 \ref statistics.c is calculated assuming that I[0] = I[1] = I[2] = 1 */ 00061 static double I[3] = { 1, 1, 1}; 00062 00063 /** \name Privat Functions */ 00064 /************************************************************/ 00065 /*@{*/ 00066 00067 /** define first and second time derivatives of a quaternion, as well 00068 as the angular acceleration. */ 00069 static void define_Qdd(Particle *p, double Qd[4], double Qdd[4], double S[3], double Wd[3]); 00070 00071 /*@}*/ 00072 00073 /** convert quaternions to the director */ 00074 /** Convert director to quaternions */ 00075 int convert_quatu_to_quat(double d[3], double quat[4]) 00076 { 00077 double d_xy, dm; 00078 double theta2, phi2; 00079 00080 // Calculate magnitude of the given vector 00081 dm = sqrt(d[0]*d[0] + d[1]*d[1] + d[2]*d[2]); 00082 00083 // The vector needs to be != 0 to be converted into a quaternion 00084 if (dm < ROUND_ERROR_PREC) { 00085 return 1; 00086 } 00087 else { 00088 // Calculate angles 00089 d_xy = sqrt(d[0]*d[0] + d[1]*d[1]); 00090 // If dipole points along z axis: 00091 if (d_xy == 0){ 00092 // We need to distinguish between (0,0,d_z) and (0,0,d_z) 00093 if (d[2]>0) 00094 theta2 = 0; 00095 else 00096 theta2 = PI/2.; 00097 phi2 = 0; 00098 } 00099 else { 00100 // Here, we take care of all other directions 00101 //Here we suppose that theta2 = 0.5*theta and phi2 = 0.5*(phi - PI/2), 00102 //where theta and phi - angles are in spherical coordinates 00103 theta2 = 0.5*acos(d[2]/dm); 00104 if (d[1] < 0) phi2 = -0.5*acos(d[0]/d_xy) - PI*0.25; 00105 else phi2 = 0.5*acos(d[0]/d_xy) - PI*0.25; 00106 } 00107 00108 // Calculate the quaternion from the angles 00109 quat[0] = cos(theta2) * cos(phi2); 00110 quat[1] = -sin(theta2) * cos(phi2); 00111 quat[2] = -sin(theta2) * sin(phi2); 00112 quat[3] = cos(theta2) * sin(phi2); 00113 } 00114 return 0; 00115 } 00116 00117 /** Here we use quaternions to calculate the rotation matrix which 00118 will be used then to transform torques from the laboratory to 00119 the body-fixed frames */ 00120 void define_rotation_matrix(Particle *p, double A[9]) 00121 { 00122 double q0q0 =p->r.quat[0]; 00123 q0q0 *=q0q0; 00124 00125 double q1q1 =p->r.quat[1]; 00126 q1q1 *=q1q1; 00127 00128 double q2q2 =p->r.quat[2]; 00129 q2q2 *=q2q2; 00130 00131 double q3q3 =p->r.quat[3]; 00132 q3q3 *=q3q3; 00133 00134 A[0 + 3*0] = q0q0 + q1q1 - q2q2 - q3q3; 00135 A[1 + 3*1] = q0q0 - q1q1 + q2q2 - q3q3; 00136 A[2 + 3*2] = q0q0 - q1q1 - q2q2 + q3q3; 00137 00138 A[0 + 3*1] = 2*(p->r.quat[1]*p->r.quat[2] + p->r.quat[0]*p->r.quat[3]); 00139 A[0 + 3*2] = 2*(p->r.quat[1]*p->r.quat[3] - p->r.quat[0]*p->r.quat[2]); 00140 A[1 + 3*0] = 2*(p->r.quat[1]*p->r.quat[2] - p->r.quat[0]*p->r.quat[3]); 00141 00142 A[1 + 3*2] = 2*(p->r.quat[2]*p->r.quat[3] + p->r.quat[0]*p->r.quat[1]); 00143 A[2 + 3*0] = 2*(p->r.quat[1]*p->r.quat[3] + p->r.quat[0]*p->r.quat[2]); 00144 A[2 + 3*1] = 2*(p->r.quat[2]*p->r.quat[3] - p->r.quat[0]*p->r.quat[1]); 00145 } 00146 00147 /** calculate the second derivative of the quaternion of a given particle 00148 as well as Wd vector which is the angular acceleration of this particle */ 00149 void define_Qdd(Particle *p, double Qd[4], double Qdd[4], double S[3], double Wd[3]) 00150 { 00151 double S1; 00152 00153 /* calculate the first derivative of the quaternion */ 00154 Qd[0] = 0.5 * ( -p->r.quat[1] * p->m.omega[0] - 00155 p->r.quat[2] * p->m.omega[1] - 00156 p->r.quat[3] * p->m.omega[2] ); 00157 00158 Qd[1] = 0.5 * ( p->r.quat[0] * p->m.omega[0] - 00159 p->r.quat[3] * p->m.omega[1] + 00160 p->r.quat[2] * p->m.omega[2] ); 00161 00162 Qd[2] = 0.5 * ( p->r.quat[3] * p->m.omega[0] + 00163 p->r.quat[0] * p->m.omega[1] - 00164 p->r.quat[1] * p->m.omega[2] ); 00165 00166 Qd[3] = 0.5 * ( -p->r.quat[2] * p->m.omega[0] + 00167 p->r.quat[1] * p->m.omega[1] + 00168 p->r.quat[0] * p->m.omega[2] ); 00169 00170 /* calculate the second derivative of the quaternion */ 00171 00172 #ifdef ROTATIONAL_INERTIA 00173 Wd[0] = (p->f.torque[0] + p->m.omega[1]*p->m.omega[2]*(I[1]-I[2]))/I[0]/p->p.rinertia[0]; 00174 Wd[1] = (p->f.torque[1] + p->m.omega[2]*p->m.omega[0]*(I[2]-I[0]))/I[1]/p->p.rinertia[1]; 00175 Wd[2] = (p->f.torque[2] + p->m.omega[0]*p->m.omega[1]*(I[0]-I[1]))/I[2]/p->p.rinertia[2]; 00176 #else 00177 Wd[0] = (p->f.torque[0] + p->m.omega[1]*p->m.omega[2]*(I[1]-I[2]))/I[0]; 00178 Wd[1] = (p->f.torque[1] + p->m.omega[2]*p->m.omega[0]*(I[2]-I[0]))/I[1]; 00179 Wd[2] = (p->f.torque[2] + p->m.omega[0]*p->m.omega[1]*(I[0]-I[1]))/I[2]; 00180 #endif 00181 00182 S1 = Qd[0]*Qd[0] + Qd[1]*Qd[1] + Qd[2]*Qd[2] + Qd[3]*Qd[3]; 00183 00184 Qdd[0] = 0.5 * ( -p->r.quat[1] * Wd[0] - 00185 p->r.quat[2] * Wd[1] - 00186 p->r.quat[3] * Wd[2] ) - p->r.quat[0] * S1; 00187 00188 Qdd[1] = 0.5 * ( p->r.quat[0] * Wd[0] - 00189 p->r.quat[3] * Wd[1] + 00190 p->r.quat[2] * Wd[2] ) - p->r.quat[1] * S1; 00191 00192 Qdd[2] = 0.5 * ( p->r.quat[3] * Wd[0] + 00193 p->r.quat[0] * Wd[1] - 00194 p->r.quat[1] * Wd[2] ) - p->r.quat[2] * S1; 00195 00196 Qdd[3] = 0.5 * ( -p->r.quat[2] * Wd[0] + 00197 p->r.quat[1] * Wd[1] + 00198 p->r.quat[0] * Wd[2] ) - p->r.quat[3] * S1; 00199 00200 S[0] = S1; 00201 S[1] = Qd[0]*Qdd[0] + Qd[1]*Qdd[1] + Qd[2]*Qdd[2] + Qd[3]*Qdd[3]; 00202 S[2] = Qdd[0]*Qdd[0] + Qdd[1]*Qdd[1] + Qdd[2]*Qdd[2] + Qdd[3]*Qdd[3]; 00203 } 00204 00205 /** propagate angular velocities and quaternions \todo implement for 00206 fixed_coord_flag */ 00207 void propagate_omega_quat_particle(Particle* p) 00208 { 00209 double lambda; 00210 00211 double Qd[4], Qdd[4], S[3], Wd[3]; 00212 #ifdef ROTATION_PER_PARTICLE 00213 if (!p->p.rotation) return; 00214 #endif 00215 00216 define_Qdd(p, Qd, Qdd, S, Wd); 00217 00218 lambda = 1 - S[0]*time_step_squared_half - sqrt(1 - time_step_squared*(S[0] + time_step*(S[1] + time_step_half/2.*(S[2]-S[0]*S[0])))); 00219 00220 for(int j=0; j < 3; j++){ 00221 p->m.omega[j]+= time_step_half*Wd[j]; 00222 } 00223 ONEPART_TRACE(if(p->p.identity==check_id) fprintf(stderr,"%d: OPT: PV_1 v_new = (%.3e,%.3e,%.3e)\n",this_node,p->m.v[0],p->m.v[1],p->m.v[2])); 00224 00225 p->r.quat[0]+= time_step*(Qd[0] + time_step_half*Qdd[0]) - lambda*p->r.quat[0]; 00226 p->r.quat[1]+= time_step*(Qd[1] + time_step_half*Qdd[1]) - lambda*p->r.quat[1]; 00227 p->r.quat[2]+= time_step*(Qd[2] + time_step_half*Qdd[2]) - lambda*p->r.quat[2]; 00228 p->r.quat[3]+= time_step*(Qd[3] + time_step_half*Qdd[3]) - lambda*p->r.quat[3]; 00229 // Update the director 00230 convert_quat_to_quatu(p->r.quat, p->r.quatu); 00231 #ifdef DIPOLES 00232 // When dipoles are enabled, update dipole moment 00233 convert_quatu_to_dip(p->r.quatu, p->p.dipm, p->r.dip); 00234 #endif 00235 00236 00237 ONEPART_TRACE(if(p->p.identity==check_id) fprintf(stderr,"%d: OPT: PPOS p = (%.3f,%.3f,%.3f)\n",this_node,p->r.p[0],p->r.p[1],p->r.p[2])); 00238 } 00239 00240 /** convert the torques to the body-fixed frames and propagate angular velocities */ 00241 void convert_torques_propagate_omega() 00242 { 00243 Particle *p; 00244 Cell *cell; 00245 int c,i, np, times; 00246 double tx, ty, tz; 00247 00248 INTEG_TRACE(fprintf(stderr,"%d: convert_torques_propagate_omega:\n",this_node)); 00249 for (c = 0; c < local_cells.n; c++) { 00250 cell = local_cells.cell[c]; 00251 p = cell->part; 00252 np = cell->n; 00253 for(i = 0; i < np; i++) { 00254 #ifdef ROTATION_PER_PARTICLE 00255 if (!p[i].p.rotation) 00256 continue; 00257 #endif 00258 double A[9]; 00259 define_rotation_matrix(&p[i], A); 00260 00261 tx = A[0 + 3*0]*p[i].f.torque[0] + A[0 + 3*1]*p[i].f.torque[1] + A[0 + 3*2]*p[i].f.torque[2]; 00262 ty = A[1 + 3*0]*p[i].f.torque[0] + A[1 + 3*1]*p[i].f.torque[1] + A[1 + 3*2]*p[i].f.torque[2]; 00263 tz = A[2 + 3*0]*p[i].f.torque[0] + A[2 + 3*1]*p[i].f.torque[1] + A[2 + 3*2]*p[i].f.torque[2]; 00264 00265 00266 if ( thermo_switch & THERMO_LANGEVIN ) { 00267 #if defined (VIRTUAL_SITES) && defined(THERMOSTAT_IGNORE_NON_VIRTUAL) 00268 if (!ifParticleIsVirtual(&p[i])) 00269 #endif 00270 { 00271 friction_thermo_langevin_rotation(&p[i]); 00272 00273 p[i].f.torque[0]+= tx; 00274 p[i].f.torque[1]+= ty; 00275 p[i].f.torque[2]+= tz; 00276 } 00277 } else { 00278 p[i].f.torque[0] = tx; 00279 p[i].f.torque[1] = ty; 00280 p[i].f.torque[2] = tz; 00281 } 00282 00283 ONEPART_TRACE(if(p[i].p.identity==check_id) fprintf(stderr,"%d: OPT: SCAL f = (%.3e,%.3e,%.3e) v_old = (%.3e,%.3e,%.3e)\n",this_node,p[i].f.f[0],p[i].f.f[1],p[i].f.f[2],p[i].m.v[0],p[i].m.v[1],p[i].m.v[2])); 00284 00285 #ifdef ROTATIONAL_INERTIA 00286 p[i].m.omega[0]+= time_step_half*p[i].f.torque[0]/p[i].p.rinertia[0]/I[0]; 00287 p[i].m.omega[1]+= time_step_half*p[i].f.torque[1]/p[i].p.rinertia[1]/I[1]; 00288 p[i].m.omega[2]+= time_step_half*p[i].f.torque[2]/p[i].p.rinertia[2]/I[2]; 00289 #else 00290 p[i].m.omega[0]+= time_step_half*p[i].f.torque[0]/I[0]; 00291 p[i].m.omega[1]+= time_step_half*p[i].f.torque[1]/I[1]; 00292 p[i].m.omega[2]+= time_step_half*p[i].f.torque[2]/I[2]; 00293 #endif 00294 /* if the tensor of inertia is isotrpic, the following refinement is not needed. 00295 Otherwise repeat this loop 2-3 times depending on the required accuracy */ 00296 for(times=0;times<=5;times++) { 00297 double Wd[3]; 00298 00299 #ifdef ROTATIONAL_INERTIA 00300 Wd[0] = (p[i].m.omega[1]*p[i].m.omega[2]*(I[1]-I[2]))/I[0]/p[i].p.rinertia[0]; 00301 Wd[1] = (p[i].m.omega[2]*p[i].m.omega[0]*(I[2]-I[0]))/I[1]/p[i].p.rinertia[1]; 00302 Wd[2] = (p[i].m.omega[0]*p[i].m.omega[1]*(I[0]-I[1]))/I[2]/p[i].p.rinertia[2]; 00303 #else 00304 Wd[0] = (p[i].m.omega[1]*p[i].m.omega[2]*(I[1]-I[2]))/I[0]; 00305 Wd[1] = (p[i].m.omega[2]*p[i].m.omega[0]*(I[2]-I[0]))/I[1]; 00306 Wd[2] = (p[i].m.omega[0]*p[i].m.omega[1]*(I[0]-I[1]))/I[2]; 00307 #endif 00308 00309 p[i].m.omega[0]+= time_step_half*Wd[0]; 00310 p[i].m.omega[1]+= time_step_half*Wd[1]; 00311 p[i].m.omega[2]+= time_step_half*Wd[2]; 00312 } 00313 00314 00315 ONEPART_TRACE(if(p[i].p.identity==check_id) fprintf(stderr,"%d: OPT: PV_2 v_new = (%.3e,%.3e,%.3e)\n",this_node,p[i].m.v[0],p[i].m.v[1],p[i].m.v[2])); 00316 00317 } 00318 } 00319 } 00320 00321 /** convert the torques to the body-fixed frames before the integration loop */ 00322 void convert_initial_torques() 00323 { 00324 Particle *p; 00325 Cell *cell; 00326 int c,i, np; 00327 double tx, ty, tz; 00328 00329 INTEG_TRACE(fprintf(stderr,"%d: convert_initial_torques:\n",this_node)); 00330 for (c = 0; c < local_cells.n; c++) { 00331 cell = local_cells.cell[c]; 00332 p = cell->part; 00333 np = cell->n; 00334 for(i = 0; i < np; i++) { 00335 #ifdef ROTATION_PER_PARTICLE 00336 if (!p[i].p.rotation) 00337 continue; 00338 #endif 00339 double A[9]; 00340 define_rotation_matrix(&p[i], A); 00341 00342 tx = A[0 + 3*0]*p[i].f.torque[0] + A[0 + 3*1]*p[i].f.torque[1] + A[0 + 3*2]*p[i].f.torque[2]; 00343 ty = A[1 + 3*0]*p[i].f.torque[0] + A[1 + 3*1]*p[i].f.torque[1] + A[1 + 3*2]*p[i].f.torque[2]; 00344 tz = A[2 + 3*0]*p[i].f.torque[0] + A[2 + 3*1]*p[i].f.torque[1] + A[2 + 3*2]*p[i].f.torque[2]; 00345 00346 if ( thermo_switch & THERMO_LANGEVIN ) { 00347 00348 friction_thermo_langevin_rotation(&p[i]); 00349 p[i].f.torque[0]+= tx; 00350 p[i].f.torque[1]+= ty; 00351 p[i].f.torque[2]+= tz; 00352 } else { 00353 p[i].f.torque[0] = tx; 00354 p[i].f.torque[1] = ty; 00355 p[i].f.torque[2] = tz; 00356 } 00357 00358 ONEPART_TRACE(if(p[i].p.identity==check_id) fprintf(stderr,"%d: OPT: SCAL f = (%.3e,%.3e,%.3e) v_old = (%.3e,%.3e,%.3e)\n",this_node,p[i].f.f[0],p[i].f.f[1],p[i].f.f[2],p[i].m.v[0],p[i].m.v[1],p[i].m.v[2])); 00359 } 00360 } 00361 } 00362 00363 /** convert from the body-fixed frames to space-fixed coordinates */ 00364 00365 void convert_omega_body_to_space(Particle *p, double *omega) 00366 { 00367 double A[9]; 00368 define_rotation_matrix(p, A); 00369 00370 omega[0] = A[0 + 3*0]*p->m.omega[0] + A[1 + 3*0]*p->m.omega[1] + A[2 + 3*0]*p->m.omega[2]; 00371 omega[1] = A[0 + 3*1]*p->m.omega[0] + A[1 + 3*1]*p->m.omega[1] + A[2 + 3*1]*p->m.omega[2]; 00372 omega[2] = A[0 + 3*2]*p->m.omega[0] + A[1 + 3*2]*p->m.omega[1] + A[2 + 3*2]*p->m.omega[2]; 00373 } 00374 00375 void convert_torques_body_to_space(Particle *p, double *torque) 00376 { 00377 double A[9]; 00378 define_rotation_matrix(p, A); 00379 00380 torque[0] = A[0 + 3*0]*p->f.torque[0] + A[1 + 3*0]*p->f.torque[1] + A[2 + 3*0]*p->f.torque[2]; 00381 torque[1] = A[0 + 3*1]*p->f.torque[0] + A[1 + 3*1]*p->f.torque[1] + A[2 + 3*1]*p->f.torque[2]; 00382 torque[2] = A[0 + 3*2]*p->f.torque[0] + A[1 + 3*2]*p->f.torque[1] + A[2 + 3*2]*p->f.torque[2]; 00383 } 00384 00385 /** Multiply two quaternions */ 00386 void multiply_quaternions(double a[4], double b[4], double result[4]) 00387 { 00388 // Formula from http://www.j3d.org/matrix_faq/matrfaq_latest.html 00389 result[0] = a[0]*b[0] - a[1]*b[1] - a[2]*b[2] - a[3]*b[3]; 00390 result[1] = a[0] * b[1] + a[1] * b[0] + a[2] * b[3] - a[3] * b[2]; 00391 result[2] = a[0] * b[2] + a[2] * b[0] + a[3] * b[1] - a[1] * b[3]; 00392 result[3] = a[0] * b[3] + a[3] * b[0] + a[1] * b[2] - a[2] * b[1]; 00393 } 00394 00395 00396 #endif
1.7.5.1