ESPResSo 3.2.0-11-g9950804-git
Extensible Simulation Package for Soft Matter Research
mdlc_correction.c
Go to the documentation of this file.
00001 /*
00002   Copyright (C) 2010,2011,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 p3m.h  code for calculating the MDLC (magnetic dipolar layer correction).
00022  *  Purpose:   get the corrections for dipolar 3D algorithms 
00023  *             when applied to a slab geometry and dipolar
00024  *            particles. DLC & co
00025  *  Article:   A. Brodka, Chemical Physics Letters 400, 62-67 (2004).
00026  *            
00027  *            We also include a tuning function that returns the
00028  *            cut-off necessary to attend a certain accuracy.
00029  *            
00030  *  Restrictions: the slab must be such that the z is the short 
00031  *                direction. Othewise we get trash.           
00032  * 
00033  */
00034 
00035 #include "utils.h"
00036 #include "global.h"
00037 #include "grid.h"
00038 #include "domain_decomposition.h"
00039 #include "particle_data.h"
00040 #include "communication.h"
00041 #include "p3m-dipolar.h"
00042 #include "cells.h"
00043 #include "mdlc_correction.h"
00044 
00045 #ifdef DIPOLES
00046    
00047 DLC_struct dlc_params = { 1e100, 0, 0, 0, 0};
00048 
00049 static int n_local_particles=0;
00050 static double mu_max;
00051 
00052 void calc_mu_max() {
00053   Cell *cell;
00054   Particle *part;
00055   int i,c,np;
00056 
00057   mu_max = -1;
00058 
00059   for (c = 0; c < local_cells.n; c++) {
00060     cell = local_cells.cell[c];
00061     part = cell->part;
00062     np   = cell->n;
00063     for(i=0;i<np;i++) {
00064       if(mu_max <  part[i].p.dipm ) {  mu_max=part[i].p.dipm;}
00065     }
00066   }
00067   MPI_Allreduce(MPI_IN_PLACE, &mu_max, 1, MPI_DOUBLE, MPI_MAX, comm_cart);
00068 }
00069 
00070 /* ******************************************************************* */
00071 
00072 MDINLINE double g1_DLC_dip(double g,double x) {
00073   double a,c,cc2,x3;
00074   c=g/x;
00075   cc2=c*c;
00076   x3=x*x*x;
00077   a=g*g*g/x+1.5*cc2+1.5*g/x3+0.75/(x3*x);
00078   return a;
00079 }       
00080 /* ******************************************************************* */
00081 
00082 
00083 MDINLINE double g2_DLC_dip(double g,double x) {
00084   double a,x2;
00085   x2=x*x;
00086   a=g*g/x+2.0*g/x2+2.0/(x2*x); 
00087   return a;
00088 }       
00089 /* ******************************************************************* */    
00090 
00091 /* Subroutine designed to  compute Mx, My, Mz and Mtotal  */
00092      
00093 double slab_dip_count_mu(double *mt, double *mx, double *my)
00094 {  
00095   Cell *cell;
00096   Particle *part;
00097   int i,c,np;
00098   double node_sums[3], tot_sums[3],Mz,M,My,Mx;
00099  
00100 
00101   node_sums[0]=0.0; tot_sums[0]=0.0;
00102   node_sums[1]=0.0; tot_sums[1]=0.0;
00103   node_sums[2]=0.0; tot_sums[2]=0.0;
00104  
00105   for (c = 0; c < local_cells.n; c++) {
00106     cell = local_cells.cell[c];
00107     part = cell->part;
00108     np   = cell->n;
00109     for(i=0;i<np;i++) {
00110       if( part[i].p.dipm != 0.0 ) {
00111         node_sums[0] +=part[i].r.dip[0];                 
00112         node_sums[1] +=part[i].r.dip[1];                 
00113         node_sums[2] +=part[i].r.dip[2];                 
00114       }
00115     }
00116   }
00117    
00118   MPI_Allreduce(node_sums, tot_sums, 3, MPI_DOUBLE, MPI_SUM, comm_cart);
00119 
00120   M= sqrt(tot_sums[0]*tot_sums[0]+tot_sums[1]*tot_sums[1]+tot_sums[2]*tot_sums[2]);
00121   Mz=tot_sums[2]; 
00122   Mx=tot_sums[0]; 
00123   My=tot_sums[1]; 
00124    
00125   //fprintf(stderr,"Mz=%20.15le \n",Mz);
00126   //fprintf(stderr,"M=%20.15le \n",M);
00127  
00128   *mt=M;
00129   *mx=Mx;
00130   *my=My;
00131 
00132   return Mz;
00133 }    
00134 /* ******************************************************************* */    
00135      
00136      
00137 /* ****************************************************************************************************
00138    Compute the dipolar DLC corrections for forces and torques.
00139    Algorithm implemented accordingly to the paper of A. Brodka, Chem. Phys. Lett. 400, 62-67, (2004).
00140    ****************************************************************************************************
00141    */
00142 
00143 double get_DLC_dipolar(int kcut,double *fx, double *fy, double *fz, double *tx, double *ty, double *tz){
00144 
00145   int    ix,iy,ip; 
00146   double gx,gy,gr;
00147 
00148   double S[4] = {0.0,0.0,0.0,0.0}; // S of Brodka methode, oder is S[4] = {Re(S+), Im(S+), Re(S-), Im(S-)}
00149   double *ReSjp=NULL,*ReSjm=NULL;
00150   double *ImSjp=NULL,*ImSjm=NULL;
00151   double *ReGrad_Mup=NULL,*ImGrad_Mup=NULL;
00152   double *ReGrad_Mum=NULL,*ImGrad_Mum=NULL;
00153   double a,b,c,d,er,ez,f,fa1;
00154   double s1,s2,s3,s4;
00155   double s1z,s2z,s3z,s4z;
00156   double ss;
00157   double energy,piarea,facux,facuy;
00158   int cc,j,np;
00159   Cell *cell = NULL;   
00160   Particle *p1;
00161 
00162   facux=2.0*M_PI/box_l[0];
00163   facuy=2.0*M_PI/box_l[1];
00164       
00165   energy=0.0;
00166     
00167   ReSjp= (double *) malloc(sizeof(double)*n_local_particles);
00168   ReSjm= (double *) malloc(sizeof(double)*n_local_particles);
00169   ImSjp= (double *) malloc(sizeof(double)*n_local_particles);
00170   ImSjm= (double *) malloc(sizeof(double)*n_local_particles);
00171   ReGrad_Mup = (double *) malloc(sizeof(double)*n_local_particles);
00172   ImGrad_Mup = (double *) malloc(sizeof(double)*n_local_particles);
00173   ReGrad_Mum = (double *) malloc(sizeof(double)*n_local_particles);
00174   ImGrad_Mum = (double *) malloc(sizeof(double)*n_local_particles);
00175 
00176   for(ix=-kcut;ix<=+kcut;ix++){
00177     for(iy=-kcut;iy<=+kcut;iy++){
00178       if(!(ix==0 && iy==0)){  
00179         gx=(double)ix*facux;
00180         gy=(double)iy*facuy;
00181           
00182         gr=sqrt(gx*gx+gy*gy);
00183           
00184         fa1=1./(gr*(exp(gr*box_l[2])-1.0));   //We assume short slab direction is z direction
00185           
00186         // ... Compute S+,(S+)*,S-,(S-)*, and Spj,Smj for the current g
00187           
00188         S[0] = S[1] = S[2] = S[3] = 0.0;
00189           
00190         ip=0;
00191         for(cc=0; cc<local_cells.n;cc++){      
00192           cell = local_cells.cell[cc];
00193           p1   = cell->part;
00194           np   = cell->n;
00195 
00196           for(j = 0; j < np; j++)  {
00197             if(p1[j].p.dipm>0){
00198         
00199               a=gx*p1[j].r.dip[0]+gy*p1[j].r.dip[1];
00200               b=gr*p1[j].r.dip[2];
00201               er=gx*p1[j].r.p[0] +gy*p1[j].r.p[1] ;
00202               ez=gr*p1[j].r.p[2];
00203               c=cos(er);
00204               d=sin(er);
00205               f=exp(ez);
00206                 
00207               ReSjp[ip]=(b*c-a*d)*f;
00208               ImSjp[ip]=(c*a+b*d)*f;
00209               ReSjm[ip]=(-b*c-a*d)/f;
00210               ImSjm[ip]=(c*a-b*d)/f;
00211               ReGrad_Mup[ip]=c*f;
00212               ReGrad_Mum[ip]=c/f;
00213               ImGrad_Mup[ip]=d*f;
00214               ImGrad_Mum[ip]=d/f;
00215                 
00216               S[0]+=ReSjp[ip];
00217               S[1]+=ImSjp[ip];
00218               S[2]+=ReSjm[ip];
00219               S[3]+=ImSjm[ip];
00220             }  
00221             ip++;
00222           }      
00223         }      
00224 
00225         MPI_Allreduce(MPI_IN_PLACE, S, 4, MPI_DOUBLE, MPI_SUM, comm_cart);
00226 
00227         //We compute the contribution to the energy ............
00228              
00229         //s2=(ReSm*ReSp+ImSm*ImSp); s2=s1!!!
00230                   
00231         energy+=fa1*((S[0]*S[2]+S[1]*S[3])*2.0); 
00232      
00233         // ... Now we can compute the contributions to E,Fj,Ej for the current g-value
00234         ip=0;
00235         for(cc=0; cc<local_cells.n;cc++){      
00236           cell = local_cells.cell[cc];
00237           p1   = cell->part;
00238           np   = cell->n;
00239 
00240           for(j = 0; j < np; j++)  {
00241             if(p1[j].p.dipm>0){
00242               //We compute the contributions to the forces ............ 
00243 
00244               s1=-(-ReSjp[ip]*S[3]+ImSjp[ip]*S[2]); 
00245               s2=+( ReSjm[ip]*S[1]-ImSjm[ip]*S[0]); 
00246               s3=-(-ReSjm[ip]*S[1]+ImSjm[ip]*S[0]); 
00247               s4=+( ReSjp[ip]*S[3]-ImSjp[ip]*S[2]); 
00248         
00249               s1z=+(ReSjp[ip]*S[2]+ImSjp[ip]*S[3]);
00250               s2z=-(ReSjm[ip]*S[0]+ImSjm[ip]*S[1]);    
00251               s3z=-(ReSjm[ip]*S[0]+ImSjm[ip]*S[1]);    
00252               s4z=+(ReSjp[ip]*S[2]+ImSjp[ip]*S[3]);
00253             
00254               ss=s1+s2+s3+s4;
00255               fx[ip]+=fa1*gx*ss;
00256               fy[ip]+=fa1*gy*ss;
00257             
00258               fz[ip]+=fa1*gr*(s1z+s2z+s3z+s4z);
00259              
00260               //We compute the contributions to the electrical field ............
00261           
00262               s1=-(-ReGrad_Mup[ip]*S[3]+ImGrad_Mup[ip]*S[2]); 
00263               s2=+( ReGrad_Mum[ip]*S[1]-ImGrad_Mum[ip]*S[0]); 
00264               s3=-(-ReGrad_Mum[ip]*S[1]+ImGrad_Mum[ip]*S[0]); 
00265               s4=+( ReGrad_Mup[ip]*S[3]-ImGrad_Mup[ip]*S[2]); 
00266         
00267               s1z=+(ReGrad_Mup[ip]*S[2]+ImGrad_Mup[ip]*S[3]);
00268               s2z=-(ReGrad_Mum[ip]*S[0]+ImGrad_Mum[ip]*S[1]); 
00269               s3z=-(ReGrad_Mum[ip]*S[0]+ImGrad_Mum[ip]*S[1]); 
00270               s4z=+(ReGrad_Mup[ip]*S[2]+ImGrad_Mup[ip]*S[3]);
00271             
00272               ss=s1+s2+s3+s4;
00273               tx[ip]+=fa1*gx*ss;
00274               ty[ip]+=fa1*gy*ss;
00275             
00276               tz[ip]+=fa1*gr*(s1z+s2z+s3z+s4z);
00277             }//if dipm>0 ....
00278             ip++;
00279           }//loop j  
00280         } //loop cc
00281       
00282       }//end of if(ii> ... 
00283     
00284     }} //end of loops for gx,gy
00285  
00286  
00287   //Convert from the corrections to the Electrical field to the corrections for the torques ....
00288   
00289   
00290   //printf("Electical field: Ex %le, Ey %le, Ez %le",-tx[0]*M_PI/(box_l[0]*box_l[1]),-ty[0]*M_PI/(box_l[0]*box_l[1]),
00291   //-tz[0]*M_PI/(box_l[0]*box_l[1])  );
00292   
00293   ip=0;
00294   for(cc=0; cc<local_cells.n;cc++){                                                       
00295     cell = local_cells.cell[cc];                                                          
00296     p1   = cell->part;                                                            
00297     np   = cell->n;                                                                       
00298 
00299     for(j = 0; j < np; j++)  {
00300       if(p1[j].p.dipm>0){
00301         a=p1[j].r.dip[1]*tz[ip]-p1[j].r.dip[2]*ty[ip];
00302         b=p1[j].r.dip[2]*tx[ip]-p1[j].r.dip[0]*tz[ip];
00303         c=p1[j].r.dip[0]*ty[ip]-p1[j].r.dip[1]*tx[ip];
00304         tx[ip]=a;
00305         ty[ip]=b;
00306         tz[ip]=c;
00307          
00308       }
00309       ip++;
00310     }
00311   }
00312  
00313   // Multiply by the factors we have left during the loops
00314 
00315   //printf("box_l: %le %le %le \n",box_l[0],box_l[1],box_l[2]);
00316 
00317   piarea=M_PI/(box_l[0]*box_l[1]);
00318 
00319   for(j = 0; j < n_local_particles; j++)  {
00320     fx[j]*=piarea;
00321     fy[j]*=piarea;
00322     fz[j]*=piarea;
00323     tx[j]*=piarea;
00324     ty[j]*=piarea;
00325     tz[j]*=piarea;
00326   }  
00327  
00328   energy*=(-piarea);
00329   
00330   //fclose(FilePtr);
00331 
00332   free(ReSjp);
00333   free(ReSjm);
00334   free(ImSjp);
00335   free(ImSjm);
00336   free(ReGrad_Mup);
00337   free(ImGrad_Mup);
00338   free(ReGrad_Mum);
00339   free(ImGrad_Mum);
00340 
00341 
00342   //printf("Energy0= %20.15le \n",energy);
00343 
00344 
00345   return energy;
00346 }    
00347 /* ******************************************************************* */
00348 
00349 
00350 /* ****************************************************************************************************
00351    Compute the dipolar DLC corrections 
00352    Algorithm implemented accordingly to the paper of A. Brodka, Chem. Phys. Lett. 400, 62-67, (2004).
00353    ****************************************************************************************************
00354    */
00355 
00356 double get_DLC_energy_dipolar(int kcut){
00357 
00358   int    ix,iy,ip; 
00359   double gx,gy,gr;
00360 
00361   double S[4], global_S[4];
00362   double a,b,c,d,er,ez,f,fa1;
00363   double s1;
00364   double energy,piarea,facux,facuy;
00365   int    cc,j,np;                                                                                   
00366   Cell   *cell = NULL;   
00367   Particle *p1;                                                                             
00368 
00369   //FILE *FilePtr;
00370   // char File_Name[40];
00371 
00372   n_local_particles = 0;
00373   for(cc=0; cc<local_cells.n;cc++) {
00374     cell = local_cells.cell[cc];
00375     n_local_particles += cell->n;
00376   }
00377 
00378    
00379   facux=2.0*M_PI/box_l[0];
00380   facuy=2.0*M_PI/box_l[1];
00381   
00382   energy=0.0;
00383 
00384   for(ix=-kcut;ix<=+kcut;ix++){         
00385     for(iy=-kcut;iy<=+kcut;iy++){       
00386 
00387             
00388       if(!(ix==0 && iy==0)){  
00389         gx=(double)ix*facux;
00390         gy=(double)iy*facuy;
00391        
00392         gr=sqrt(gx*gx+gy*gy);
00393       
00394         fa1=1./(gr*(exp(gr*box_l[2])-1.0));   //We assume short slab direction is z direction
00395        
00396         // ... Compute S+,(S+)*,S-,(S-)*, and Spj,Smj for the current g
00397             
00398         S[0] = S[1] = S[2] = S[3] = 0.0;
00399       
00400         ip=0;
00401         for(cc=0; cc<local_cells.n;cc++){
00402           cell = local_cells.cell[cc];
00403           p1   = cell->part;
00404           np   = cell->n;
00405 
00406           for(j = 0; j < np; j++)  {
00407             if(p1[j].p.dipm>0){
00408             
00409               a=gx*p1[j].r.dip[0]+gy*p1[j].r.dip[1];{
00410                 b=gr*p1[j].r.dip[2];
00411                 er=gx*p1[j].r.p[0] +gy*p1[j].r.p[1] ;
00412                 ez=gr*p1[j].r.p[2];
00413                 c=cos(er);
00414                 d=sin(er);
00415                 f=exp(ez);
00416             
00417                 S[0]+=(b*c-a*d)*f;
00418                 S[1]+=(c*a+b*d)*f;
00419                 S[2]+=(-b*c-a*d)/f;
00420                 S[3]+=(c*a-b*d)/f;
00421               }
00422            
00423             }
00424             ip++;
00425           }                                                                                   
00426         }                                                                                             
00427         MPI_Reduce(S, global_S, 4, MPI_DOUBLE, MPI_SUM, 0, comm_cart);
00428       
00429         //We compute the contribution to the energy ............
00430         s1=(global_S[0]*global_S[2]+global_S[1]*global_S[3]);
00431         //s2=(ReSm*ReSp+ImSm*ImSp); s2=s1!!!
00432              
00433         energy+=fa1*(s1*2.0); 
00434  
00435       }//end of if(... 
00436     }} //end of loops for gx,gy
00437  
00438   
00439   // Multiply by the factors we have left during the loops
00440  
00441   piarea=M_PI/(box_l[0]*box_l[1]);
00442   energy*=(-piarea);
00443   return (this_node == 0) ? energy : 0.0;
00444 }    
00445 /* ***************************************************************** */
00446      
00447 
00448      
00449 
00450 /* **************************************************************************
00451 ********** Compute and add the terms needed to correct the 3D dipolar*****
00452 ********** methods when we have an slab geometry *************************
00453 ************************************************************************** */
00454      
00455 void    add_mdlc_force_corrections(){
00456   Cell *cell;
00457   Particle *p;
00458   int i,c,np,ip;
00459   int cc;
00460   int dip_DLC_kcut;
00461   double  *dip_DLC_f_x=NULL,*dip_DLC_f_y=NULL,*dip_DLC_f_z=NULL;
00462   double  *dip_DLC_t_x=NULL,*dip_DLC_t_y=NULL,*dip_DLC_t_z=NULL;
00463   double  dip_DLC_energy=0.0;
00464   double  mz=0.0,mx=0.0,my=0.0,volume,mtot=0.0;
00465 #if defined(ROTATION) && defined(DP3M)
00466   double dx,dy,dz,correps;
00467 #endif  
00468 
00469   dip_DLC_kcut=dlc_params.far_cut ;
00470 
00471   n_local_particles = 0;
00472   for(cc=0; cc<local_cells.n;cc++) {
00473     cell = local_cells.cell[cc];
00474     n_local_particles += cell->n;
00475   }
00476           
00477   volume=box_l[0]*box_l[1]*box_l[2];
00478         
00479   // --- Create arrays that should contain the corrections to
00480   //     the forces and torques, and set them to zero.   
00481  
00482   dip_DLC_f_x = (double *) malloc(sizeof(double)*n_total_particles);
00483   dip_DLC_f_y = (double *) malloc(sizeof(double)*n_total_particles);
00484   dip_DLC_f_z = (double *) malloc(sizeof(double)*n_total_particles);
00485          
00486   dip_DLC_t_x = (double *) malloc(sizeof(double)*n_total_particles);
00487   dip_DLC_t_y = (double *) malloc(sizeof(double)*n_total_particles);
00488   dip_DLC_t_z = (double *) malloc(sizeof(double)*n_total_particles);
00489 
00490 
00491   for(i=0;i<n_local_particles;i++){
00492     dip_DLC_f_x[i] = 0.0;
00493     dip_DLC_f_y[i] = 0.0;
00494     dip_DLC_f_z[i] = 0.0;
00495          
00496     dip_DLC_t_x[i] = 0.0;
00497     dip_DLC_t_y[i] = 0.0;
00498     dip_DLC_t_z[i] = 0.0;
00499   }   
00500 
00501   //---- Compute the corrections ----------------------------------  
00502      
00503   //First the DLC correction  
00504   dip_DLC_energy+=coulomb.Dprefactor*get_DLC_dipolar(dip_DLC_kcut,dip_DLC_f_x,dip_DLC_f_y,dip_DLC_f_z,dip_DLC_t_x,dip_DLC_t_y,dip_DLC_t_z);    
00505  
00506   //            printf("Energy DLC                                  = %20.15le \n",dip_DLC_energy);
00507  
00508   //Now we compute the the correction like Yeh and Klapp to take into account the fact that you are using a
00509   //3D PBC method which uses spherical summation instead of slab-wise sumation. Slab-wise summation is the one 
00510   //required to apply DLC correction.  This correction is often called SDC = Shape Dependent Correction.
00511   //See Brodka, Chem. Phys. Lett. 400, 62, (2004).
00512            
00513   mz=slab_dip_count_mu(&mtot, &mx, &my);
00514 #ifdef DP3M
00515   if(coulomb.Dmethod == DIPOLAR_MDLC_P3M) {
00516     if(dp3m.params.epsilon == P3M_EPSILON_METALLIC) {   
00517       dip_DLC_energy+=coulomb.Dprefactor*2.*M_PI/volume*(mz*mz);
00518     }
00519     else{   
00520       dip_DLC_energy+=coulomb.Dprefactor*2.*M_PI/volume*(mz*mz-mtot*mtot/(2.0*dp3m.params.epsilon+1.0)); 
00521     }
00522   }
00523   else
00524 #endif
00525     {
00526       dip_DLC_energy+=coulomb.Dprefactor*2.*M_PI/volume*(mz*mz);
00527       fprintf(stderr,"You are not using the P3M method, therefore p3m.epsilon is unknown, I assume metallic borders \n");   
00528     }  
00529                  
00530   // --- Transfer the computed corrections to the Forces, Energy and torques
00531   //    of the particles
00532          
00533   ip = 0;
00534   for (c = 0; c < local_cells.n; c++) {
00535     cell = local_cells.cell[c];
00536     p    = cell->part;
00537     np   = cell->n;
00538     for(i=0; i<np; i++) { 
00539       if( (p[i].p.dipm) != 0.0 ) {
00540 
00541         p[i].f.f[0] += coulomb.Dprefactor*dip_DLC_f_x[ip];    
00542         p[i].f.f[1] += coulomb.Dprefactor*dip_DLC_f_y[ip];
00543         p[i].f.f[2] += coulomb.Dprefactor*dip_DLC_f_z[ip]; //SDC correction term is zero for the forces
00544    
00545 #if defined(ROTATION) && defined(DP3M)
00546     double correc= 4.*M_PI/volume;
00547         //in the Next lines: the second term (correc*...)is the SDC correction for the torques
00548         if(dp3m.params.epsilon == P3M_EPSILON_METALLIC) {       
00549 
00550           dx=0.0;
00551           dy=0.0;
00552           dz=correc*(-1.0)*mz;    
00553                 
00554           p[i].f.torque[0] +=coulomb.Dprefactor*(dip_DLC_t_x[ip]+p[i].r.dip[1]*dz  - p[i].r.dip[2]*dy ) ; 
00555           p[i].f.torque[1] +=coulomb.Dprefactor*(dip_DLC_t_y[ip]+p[i].r.dip[2]*dx  - p[i].r.dip[0]*dz ) ;
00556           p[i].f.torque[2] +=coulomb.Dprefactor*(dip_DLC_t_z[ip]+p[i].r.dip[0]*dy  - p[i].r.dip[1]*dx ); 
00557 
00558 
00559         }else{
00560                 
00561           correps= correc/(2.0*dp3m.params.epsilon+1.0);
00562           dx=correps*mx;
00563           dy=correps*my;
00564           dz=correc*(-1.0+1./(2.0*dp3m.params.epsilon+1.0))*mz;    
00565                 
00566           p[i].f.torque[0] +=coulomb.Dprefactor*(dip_DLC_t_x[ip]+p[i].r.dip[1]*dz  - p[i].r.dip[2]*dy ) ; 
00567           p[i].f.torque[1] +=coulomb.Dprefactor*(dip_DLC_t_y[ip]+p[i].r.dip[2]*dx  - p[i].r.dip[0]*dz ) ;
00568           p[i].f.torque[2] +=coulomb.Dprefactor*(dip_DLC_t_z[ip]+p[i].r.dip[0]*dy  - p[i].r.dip[1]*dx ); 
00569                 
00570                    
00571         }   
00572 #endif                
00573       }  
00574       ip++;
00575     }    
00576   }
00577         
00578   //--- Free the memory used for computing the corrections ----------------     
00579       
00580   free(dip_DLC_f_x);
00581   free(dip_DLC_f_y);
00582   free(dip_DLC_f_z);
00583   free(dip_DLC_t_x);
00584   free(dip_DLC_t_y);
00585   free(dip_DLC_t_z);
00586    
00587     
00588 
00589 }    
00590 /* ***************************************************************** */
00591      
00592 
00593 
00594 /* **************************************************************************
00595 ********** Compute and add the terms needed to correct the energy of *****
00596 ********** 3D dipolar methods when we have an slab geometry          *****
00597 ************************************************************************** */
00598    
00599 double     add_mdlc_energy_corrections(){
00600   double  dip_DLC_energy=0.0;
00601   double  mz=0.0,mx=0.0,my=0.0,volume,mtot=0.0;
00602   int dip_DLC_kcut;
00603      
00604   volume=box_l[0]*box_l[1]*box_l[2];
00605      
00606   dip_DLC_kcut=dlc_params.far_cut ;
00607      
00608   //---- Compute the corrections ----------------------------------  
00609      
00610   //First the DLC correction  
00611   dip_DLC_energy+=coulomb.Dprefactor*get_DLC_energy_dipolar(dip_DLC_kcut);    
00612      
00613   //           printf("Energy DLC                                  = %20.15le \n",dip_DLC_energy);
00614      
00615   //Now we compute the the correction like Yeh and Klapp to take into account the fact that you are using a
00616   //3D PBC method which uses spherical summation instead of slab-wise sumation. Slab-wise summation is the one 
00617   //required to apply DLC correction.  This correction is often called SDC = Shape Dependent Correction.
00618   //See Brodka, Chem. Phys. Lett. 400, 62, (2004).
00619      
00620   mz=slab_dip_count_mu(&mtot, &mx, &my);
00621      
00622   if(this_node == 0) {
00623 #ifdef DP3M      
00624     if(coulomb.Dmethod == DIPOLAR_MDLC_P3M) {
00625       if(dp3m.params.epsilon == P3M_EPSILON_METALLIC) {
00626         dip_DLC_energy+=coulomb.Dprefactor*2.*M_PI/volume*(mz*mz);
00627       }
00628       else{   
00629         dip_DLC_energy+=coulomb.Dprefactor*2.*M_PI/volume*(mz*mz-mtot*mtot/(2.0*dp3m.params.epsilon+1.0)); 
00630       }
00631     }
00632     else
00633 #endif
00634       {
00635         dip_DLC_energy+=coulomb.Dprefactor*2.*M_PI/volume*(mz*mz);
00636         fprintf(stderr,"You are not using the P3M method, therefore dp3m.params.epsilon unknown, I assume metallic borders \n");   
00637       }  
00638      
00639     return dip_DLC_energy;
00640   } else {
00641     return 0.0;
00642   }
00643    
00644 }    
00645 /* ***************************************************************** */
00646 
00647 /* -------------------------------------------------------------------------------
00648    Subroutine to compute the cut-off (NCUT) necessary in the DLC dipolar part
00649    to get a certain accuracy (acc). We assume particles to have all them a same 
00650    value of the dipolar momentum modulus (mu_max). mu_max is taken as the largest value of
00651    mu inside the sytem. If we assum the gap has a width gap_size (within which there is no particles)
00652 
00653    Lz=h+gap_size
00654 
00655    BE CAREFUL:  (1) We assum the short distance for the slab to be in the Z direction
00656    (2) You must also tune the other 3D method to the same accuracy, otherwise
00657    it has no sense to have a good accurated result for DLC-dipolar.
00658  
00659    ---------------------------------------------------------------------------------- */     
00660 
00661 int mdlc_tune(double error)
00662 {
00663   double de,n,gc,lz,lx,a,fa1,fa2,fa0,h;
00664   int     kc,limitkc=200,flag;
00665 
00666   MDLC_TRACE(fprintf(stderr, "%d: mdlc_tune().\n", this_node));
00667  
00668   n=(double) n_total_particles;
00669   lz=box_l[2];
00670   
00671   a=box_l[0]*box_l[1];
00672   mpi_bcast_max_mu();   /* we take the maximum dipole in the system, to be sure that the errors in the other case
00673                            will be equal or less than for this one */
00674  
00675   h=dlc_params.h;
00676   if (h < 0) return ES_ERROR;
00677 
00678   if(h > lz) {
00679     fprintf(stderr,"tune DLC dipolar: Slab is larger than the box size !!! \n");
00680     exit(1);
00681   }
00682  
00683   if(fabs(box_l[0]-box_l[1])>0.001) {
00684     fprintf(stderr,"tune DLC dipolar: box size in x direction is different from y direction !!! \n");
00685     fprintf(stderr,"The tuning formula requires both to be equal. \n");
00686     exit(1);
00687   }
00688 
00689   lx=box_l[0];
00690 
00691   flag=0;
00692   for(kc=1;kc<limitkc;kc++){
00693     gc=kc*2.0*PI/lx;
00694     fa0=sqrt(9.0*exp(+2.*gc*h)*g1_DLC_dip(gc,lz-h)+22.0*g1_DLC_dip(gc,lz)+9.0*exp(-2.0*gc*h)*g1_DLC_dip(gc,lz+h) );
00695     fa1=0.5*sqrt(PI/(2.0*a))*fa0;
00696     fa2=g2_DLC_dip(gc,lz);
00697     de=n*(mu_max*mu_max)/(4.0*(exp(gc*lz)-1.0)) *(fa1+fa2);
00698     if(de<error) {flag=1;break;}
00699   }
00700  
00701   if(flag==0) {
00702     fprintf(stderr,"tune DLC dipolar: Sorry, unable to find a proper cut-off for such system and accuracy.\n");
00703     fprintf(stderr,"Try modifiying the variable limitkc in the c-code: dlc_correction.c  ... \n");
00704     return ES_ERROR;
00705   }
00706  
00707   dlc_params.far_cut=kc;
00708  
00709   MDLC_TRACE(fprintf(stderr, "%d: done mdlc_tune().\n", this_node));
00710  
00711   return ES_OK;
00712  
00713 }               
00714 
00715      
00716 //======================================================================================================================
00717 //======================================================================================================================
00718 
00719 
00720 int mdlc_sanity_checks()
00721 {
00722 #ifdef PARTIAL_PERIODIC  
00723   char *errtxt;
00724   if (!PERIODIC(0) || !PERIODIC(1) || !PERIODIC(2)) {
00725     errtxt = runtime_error(128);
00726     ERROR_SPRINTF(errtxt, "{006 mdlc requires periodicity 1 1 1} ");
00727     return 1;
00728   }
00729 #endif  
00730 
00731   // It will be desirable to have a  checking function that check that the slab geometry is such that 
00732   // the short direction is along the z component.
00733 
00734   return 0;
00735 }
00736 /* ***************************************************************** */
00737 
00738 int mdlc_set_params(double maxPWerror, double gap_size, double far_cut)
00739 {
00740   MDLC_TRACE(fprintf(stderr, "%d: mdlc_set_params().\n", this_node));
00741   
00742   dlc_params.maxPWerror = maxPWerror;
00743   dlc_params.gap_size = gap_size;
00744   dlc_params.h = box_l[2] - gap_size;
00745 
00746   
00747   switch (coulomb.Dmethod) {
00748 #ifdef DP3M
00749   case  DIPOLAR_MDLC_P3M:
00750   case  DIPOLAR_P3M:
00751     coulomb.Dmethod =DIPOLAR_MDLC_P3M;
00752     break;
00753 #endif  
00754   case  DIPOLAR_MDLC_DS:
00755   case  DIPOLAR_DS: 
00756     coulomb.Dmethod =DIPOLAR_MDLC_DS; 
00757     break;
00758   default:
00759     return ES_ERROR;
00760   }
00761 
00762   dlc_params.far_cut = far_cut;
00763   if (far_cut != -1) {
00764     dlc_params.far_calculated = 0;
00765   }
00766   else {
00767     dlc_params.far_calculated = 1;
00768     if (mdlc_tune(dlc_params.maxPWerror) == ES_ERROR) {
00769       char *errtxt = runtime_error(128);
00770       ERROR_SPRINTF(errtxt, "{009 mdlc tuning failed, gap size too small} ");
00771     }
00772   }
00773   mpi_bcast_coulomb_params();
00774 
00775   return ES_OK;
00776 }
00777 /* ***************************************************************** */
00778 
00779 #endif
00780 
00781 
00782 
00783 
00784 
00785 
00786 
00787 
00788