ESPResSo 3.2.0-11-g9950804-git
Extensible Simulation Package for Soft Matter Research
rotation.c
Go to the documentation of this file.
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