ESPResSo 3.2.0-11-g9950804-git
Extensible Simulation Package for Soft Matter Research
modes.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 
00022 /** \file modes.c
00023     Implementation of \ref modes.h "modes.h"
00024 */
00025 
00026 #include "modes.h"
00027 #include "communication.h"
00028 #include "errorhandling.h"
00029 #include "grid.h"
00030 
00031 #ifdef MODES
00032 
00033 /** fftw plan for calculating the 2d mode analysis */
00034 
00035 #ifdef FFTW_ENABLE_FLOAT
00036 typedef float fftw_real;
00037 #else
00038 typedef double fftw_real;
00039 #endif
00040 
00041 /** Flag to indicate when the grid size has changed*/
00042 int mode_grid_changed = 1;
00043 
00044 /** An array containing the size of the mode grid along each
00045     coordinate axis.  Note that at present this should be a symmetric
00046     square */
00047 int mode_grid_3d[3] = {0,0,0};
00048 /** Integer label x  for grid axes compared to real axes */
00049 int xdir = -1;
00050 /** Integer label y  for grid axes compared to real axes */
00051 int ydir = -1;
00052 /** Integer label z  for grid axes compared to real axes */
00053 int zdir = -1;
00054 
00055 /** Numerical tolerance to be used only in modes2d*/
00056 #define MODES2D_NUM_TOL 0.00001
00057 
00058 /** Default value for the distance beyond which a particle is said to
00059     have escaped the bilayer.  The default is set very large so that
00060     all lipids are counted*/
00061 double stray_cut_off = 10000000.0;
00062 
00063 
00064 void fold_all ( void ) {
00065   int i ;
00066   for (i = 0 ; i < n_total_particles ; i++) {
00067     fold_coordinate(partCfg[i].r.p,partCfg[i].l.i,xdir);
00068     fold_coordinate(partCfg[i].r.p,partCfg[i].l.i,ydir);
00069     fold_coordinate(partCfg[i].r.p,partCfg[i].l.i,zdir);
00070   }
00071 }
00072 
00073 /* Simple helper function for calculating the reference
00074    zposition. Usually the bilayer midplane. Also fold the
00075    particles. Not exported */
00076 double calc_zref ( int tmpzdir ) {
00077   float zref;
00078   int i;
00079 
00080   /* Fold all the particles */
00081   fold_all ();
00082 
00083  /* Find the mean z position of folded particles*/
00084   zref = 0;
00085   for (i = 0 ; i < n_total_particles ; i++) {
00086     zref += partCfg[i].r.p[zdir];
00087   }
00088   zref = zref/(double)(n_total_particles);
00089   return zref;
00090 }
00091 
00092 
00093 /** This function takes a given grid supplied by the user and
00094     determines the correct orientation in which to do the fourier
00095     transform.  In this regard one of the grid dimensions must be 0
00096     and the other two must be integer multiples of two and equal to
00097     each other.  The dimension that is 0 will be assigned an internal
00098     reference called zdir and will be used to calculate the height
00099     function used in the fft
00100 */
00101 void map_to_2dgrid() {
00102   int i;
00103   STAT_TRACE(fprintf(stderr,"%d,executing map_to_2dgrid \n",this_node));
00104   /* Reset values of mapping */
00105   xdir = -1;
00106   ydir = -1;
00107   zdir = -1;
00108 
00109   /* Find the grid normal */
00110   for ( i = 0 ; i < 3 ; i++) {
00111     if ( mode_grid_3d[i] == 0 ) {
00112       if (zdir != -1 ) { /* grid normal must be unique */ 
00113         char *errtxt = runtime_error(128 + 3*ES_INTEGER_SPACE);
00114         ERROR_SPRINTF(errtxt, "{029 fft_modes_init: grid dimensions are <%d,%d,%d>, but one and only one must be = 0} ",
00115                 mode_grid_3d[0],mode_grid_3d[1],mode_grid_3d[2]);
00116         return;
00117       } else {
00118         zdir = i;
00119       }
00120     } 
00121     else if ( mode_grid_3d[i] < 0 ) {
00122       char *errtxt = runtime_error(128 + 3*ES_INTEGER_SPACE);
00123       ERROR_SPRINTF(errtxt, "{030 fft_modes_init: grid dimensions are <%d,%d,%d>, but all must be >= 0} ",
00124               mode_grid_3d[0],mode_grid_3d[1],mode_grid_3d[2]);
00125       return;
00126     }
00127     else {
00128       if (  xdir == -1 ) {xdir = i;}
00129       else {ydir = i;}      
00130     }    
00131   }
00132   /* Check that grid normal was found */
00133   if ( zdir == -1 ) {
00134     char *errtxt = runtime_error(128 + 3*ES_INTEGER_SPACE);
00135     ERROR_SPRINTF(errtxt, "{031 fft_modes_init: grid dimensions are <%d,%d,%d>, but one and only one must be = 0} ",
00136             mode_grid_3d[0],mode_grid_3d[1],mode_grid_3d[2]);
00137     return;
00138   }
00139   STAT_TRACE(fprintf(stderr,
00140                      "%d,map_to_2dgrid found the following mapping: xdir = %d, ydir = %d, zdir = %d \n",
00141                      this_node, xdir, ydir, zdir));
00142 
00143 
00144   /* Now that we know the grid normal check that the other two dimensions are equal and multiples of 2 */
00145   if ( mode_grid_3d[xdir] != mode_grid_3d[ydir] ) {
00146     char *errtxt = runtime_error(128 + 3*ES_INTEGER_SPACE);
00147     ERROR_SPRINTF(errtxt, "{032 fft_modes_init: grid dimensions are <%d,%d,%d>, but two must be equal and the other 0} ",
00148             mode_grid_3d[xdir],mode_grid_3d[ydir],mode_grid_3d[zdir]);
00149     return;
00150   }
00151 
00152   if ( (mode_grid_3d[xdir]/2.0 - floor(mode_grid_3d[xdir]/2.0) > MODES2D_NUM_TOL) 
00153        || (mode_grid_3d[ydir]/2.0 - floor(mode_grid_3d[ydir]/2.0) > MODES2D_NUM_TOL) ) {
00154     char *errtxt = runtime_error(128 + 3*ES_INTEGER_SPACE);
00155     ERROR_SPRINTF(errtxt, "{033 fft_modes_init: grid dimensions are <%d,%d,%d>. All non zero values must be integer multiples of 2} ",
00156             mode_grid_3d[xdir],mode_grid_3d[ydir],mode_grid_3d[zdir]);
00157     return;
00158   }
00159 }
00160 
00161 
00162 
00163 /**
00164    This routine calculates the orientational order parameter for a
00165    lipid bilayer.  
00166 */
00167 int orient_order(double* result, double* stored_dirs)
00168 {
00169   double dir[3];
00170   double refdir[3] = {0,0,1};
00171   double sumdir[3] = {0,0,0};
00172   double zref;
00173   int bilayer_cnt;
00174   int i,atom,tmpzdir;
00175   double dp;
00176   double len;
00177 
00178   IntList l_orient;
00179   init_intlist(&l_orient);
00180   realloc_intlist(&l_orient, n_molecules);
00181 
00182   bilayer_cnt = 0;
00183   *result = 0;
00184 
00185 
00186   // Check to see if the grid has been set and if not then interpret
00187   if ( xdir + ydir + zdir == -3 ) {
00188     tmpzdir = 2;
00189   } else { 
00190     tmpzdir = zdir;
00191   };
00192 
00193   /* Update particles */
00194   updatePartCfg(WITHOUT_BONDS);
00195   /* Make sure particles are sorted */
00196   if (!sortPartCfg()) {
00197     char *errtxt = runtime_error(128);
00198     ERROR_SPRINTF(errtxt, "{035 could not sort partCfg, particles have to start at 0 and have consecutive identities} ");
00199     return ES_ERROR;
00200   }
00201 
00202   /* Calculate the reference z position as its mean.*/
00203   zref = calc_zref( tmpzdir );
00204  
00205 
00206   /* Calculate the orientation of all the lipids in turn and include
00207    only UP or DOWN lipids in a calculation of the overall orientation
00208    direction of the bilayer .. ie the reference vector from which we
00209    can calculate the orientational order. */
00210 
00211   for ( i = 0 ; i < n_molecules ; i++) {
00212     atom = topology[i].part.e[0];
00213     l_orient.e[i] = lipid_orientation(atom,partCfg,zref,dir,refdir);
00214     stored_dirs[i*3] = dir[0];
00215     stored_dirs[i*3+1] = dir[1];
00216     stored_dirs[i*3+2] = dir[2];
00217 
00218     if ( l_orient.e[i] == LIPID_UP ) {
00219       sumdir[0] += dir[0];
00220       sumdir[1] += dir[1];
00221       sumdir[2] += dir[2];
00222       bilayer_cnt++;
00223     }
00224     if ( l_orient.e[i] == LIPID_DOWN ) {
00225       sumdir[0] -= dir[0];
00226       sumdir[1] -= dir[1];
00227       sumdir[2] -= dir[2];
00228       bilayer_cnt++;
00229     }
00230   }
00231 
00232   /* Normalise the bilayer normal vector */
00233   len = 0.0;
00234   for ( i = 0 ; i < 3 ; i++) {
00235     sumdir[i] = sumdir[i]/(double)(bilayer_cnt);
00236     len += sumdir[i]*sumdir[i];
00237   }
00238   for ( i = 0 ; i < 3 ; i++) {
00239     sumdir[i] = sumdir[i]/sqrt(len);
00240   }
00241 
00242   /* Calculate the orientational order */
00243   for ( i = 0 ; i < n_molecules ; i++ ) {
00244     dir[0] = stored_dirs[i*3];
00245     dir[1] = stored_dirs[i*3+1];
00246     dir[2] = stored_dirs[i*3+2];
00247 
00248     if ( l_orient.e[i] != LIPID_STRAY && l_orient.e[i] != REAL_LIPID_STRAY ) {
00249       dp = scalar(dir,sumdir);
00250       *result += dp*dp*1.5-0.5;      
00251     }
00252 
00253   }
00254 
00255   
00256   realloc_intlist(&l_orient, 0);
00257 
00258   *result = *result/(double)(bilayer_cnt);
00259   return ES_OK;
00260 }
00261 
00262 
00263 
00264 /** 
00265     This routine performs a simple check to see whether a lipid is
00266     oriented up or down or if it has escaped the bilayer. 
00267 
00268     \param id The particle identifier
00269     \param partCfg An array of sorted particles
00270 
00271     \param zref The average z position of all particles. This is used
00272     to check for stray lipids
00273 
00274     \param director director
00275     \param refdir is a vector indicating the direction indicating
00276     up. This is usually the zaxis. If it is not the z axis then lipids
00277     will not be returned as stray.
00278  */
00279 int lipid_orientation( int id, Particle* partCfg , double zref, double director[3], double refdir[3]) {
00280   int mol_size, head_id, tail_id, mol_id;
00281   int i;
00282   int tmpxdir,tmpydir,tmpzdir;
00283   double distance;
00284   double tailz;
00285   double len;
00286   double proj;
00287 
00288   if ( zdir + ydir + zdir != -3 ) {
00289     tmpxdir = xdir;
00290     tmpydir = ydir;
00291     tmpzdir = zdir;
00292   } else {
00293     tmpxdir = 0;
00294     tmpydir = 1;
00295     tmpzdir = 2;
00296   }
00297 
00298 
00299   /* check molecule information exists */
00300   if ( n_molecules < 0 ) return ES_ERROR;
00301 
00302   /* Get basic molecule parameters */
00303   mol_id = partCfg[id].p.mol_id ;
00304   mol_size = topology[mol_id].part.n;
00305  
00306   /* If the head and tail id's were not found above then assume the
00307      head atom is the first and tail is the last in the molecule */
00308   head_id = topology[mol_id].part.e[0];
00309   tail_id = topology[mol_id].part.e[mol_size -1];
00310 
00311   /* Check for stray lipids only if the system is a flat bilayer and the director is in the zdirection */
00312   if ( ( refdir[tmpxdir] == 0.0 ) && ( refdir[tmpydir] == 0 ) ) {
00313     tailz = partCfg[tail_id].r.p[tmpzdir];
00314     distance = sqrt(pow((tailz - zref),2));
00315     /* Check if the lipid is a stray based on this distance */
00316     if ( distance > stray_cut_off ) {
00317       return REAL_LIPID_STRAY;
00318     }
00319   }
00320 
00321   /* Calculate a normalised vector called director that is in the
00322      direction of the lipid from tail to head */
00323   len = 0;
00324   for ( i = 0 ; i < 3 ; i++ ) {
00325     director[i] = (partCfg[head_id].r.p[i] - 
00326                    partCfg[tail_id].r.p[i]);
00327     len += director[i]*director[i];
00328   }
00329   for ( i = 0 ; i < 3 ; i++ ) {
00330     director[i] = director[i]/sqrt(len);
00331   }
00332   
00333 
00334   proj = director[0]*refdir[0] + director[1]*refdir[1] + director[2]*refdir[2];
00335   if ( proj > 0.0 ) {
00336     /* Lipid is oriented up */
00337     return LIPID_UP;
00338   } else {
00339     return LIPID_DOWN;
00340   }
00341 }
00342 
00343 /* Get a complete list of the orientations of every lipid assuming a
00344    bilayer structure.  Requires grid*/
00345 int get_lipid_orients(IntList* l_orient) {
00346   int i,gi,gj, atom;
00347   double zreflocal,zref;  
00348   double dir[3];
00349   double refdir[3] = {0,0,1};
00350   double grid_size[2];
00351 
00352   double* height_grid;
00353 
00354   if ( xdir + ydir + zdir == -3 || mode_grid_3d[xdir] <= 0 || mode_grid_3d[ydir] <= 0 ) {
00355     char *errtxt = runtime_error(128);
00356     ERROR_SPRINTF(errtxt,"{036 cannot calculate lipid orientations with uninitialized grid} ");
00357     return ES_ERROR;
00358   }
00359 
00360   /* Allocate memory for height grid arrays and initialize these arrays */
00361   height_grid = malloc((mode_grid_3d[xdir])*sizeof(double)*mode_grid_3d[ydir]);
00362 
00363 
00364   /* Calculate physical size of grid mesh */
00365   grid_size[xdir] = box_l[xdir]/(double)mode_grid_3d[xdir];
00366   grid_size[ydir] = box_l[ydir]/(double)mode_grid_3d[ydir];
00367 
00368 
00369   /* Update particles */
00370   updatePartCfg(WITHOUT_BONDS);
00371   //Make sure particles are sorted
00372   if (!sortPartCfg()) {
00373     fprintf(stderr,"%d,could not sort partCfg \n",this_node);
00374     return -1;
00375   }
00376   
00377   if ( !calc_fluctuations(height_grid, 1) ) {
00378     char *errtxt = runtime_error(128);
00379     ERROR_SPRINTF(errtxt,"{034 calculation of height grid failed } ");
00380     return -1;
00381   }
00382 
00383   zref = calc_zref( zdir );
00384 
00385   for ( i = 0 ; i < n_molecules ; i++) {
00386     atom = topology[i].part.e[0];
00387     gi = floor( partCfg[atom].r.p[xdir]/grid_size[xdir] );
00388     gj = floor( partCfg[atom].r.p[ydir]/grid_size[ydir] );
00389     zreflocal = height_grid[gj+gi*mode_grid_3d[xdir]] + zref;
00390     l_orient->e[i] = lipid_orientation(atom,partCfg,zreflocal,dir,refdir);
00391   }
00392 
00393   free(height_grid);
00394 
00395   return 1;
00396 }
00397 
00398 
00399 /** This routine performs must of the work involved in the analyze
00400     modes2d command.  A breakdown of what the routine does is as
00401     follows
00402 
00403     \li fftw plans and in / out arrays are initialized as required
00404 
00405     \li calculate height function is called
00406 
00407     \li The height function is fourier transformed using the fftw library.
00408 
00409     Note: argument switch_fluc
00410     switch_fluc == 1 for height grid
00411     switch_fluc == 0 for thickness
00412 */
00413 int modes2d(fftw_complex* modes, int switch_fluc) {
00414   /* All these variables need to be static so that the fftw3 plan can
00415      be initialised and reused */
00416   static  fftw_plan mode_analysis_plan; // height grid
00417   /** Input values for the fft */
00418   static  double* height_grid;
00419   /** Output values for the fft */
00420   static  fftw_complex* result;
00421 
00422 /** 
00423     Every time a change is made to the grid calculate the fftw plan
00424     for the subsequent fft and destroy any existing plans
00425 */
00426   if ( mode_grid_changed ) {
00427     STAT_TRACE(fprintf(stderr,"%d,initializing fftw for mode analysis \n",this_node));
00428     if ( xdir + ydir + zdir == -3 ) {
00429       char *errtxt = runtime_error(128);
00430       ERROR_SPRINTF(errtxt,"{092 attempt to perform mode analysis with uninitialized grid} ");
00431       return -1;
00432     }
00433 
00434     STAT_TRACE(fprintf(stderr,"%d,destroying old fftw plan \n",this_node));
00435 
00436     /* Make sure all memory is free and old plan is destroyed. It's ok
00437        to call these functions on uninitialised pointers I think */
00438     fftw_free(result);
00439     fftw_free(height_grid);
00440     fftw_destroy_plan(mode_analysis_plan);
00441     fftw_cleanup(); 
00442     /* Allocate memory for input and output arrays */
00443     height_grid = malloc((mode_grid_3d[xdir])*sizeof(double)*mode_grid_3d[ydir]);
00444     result      = malloc((mode_grid_3d[ydir]/2+1)*(mode_grid_3d[xdir])*sizeof(fftw_complex)); 
00445     mode_analysis_plan = fftw_plan_dft_r2c_2d(mode_grid_3d[xdir],mode_grid_3d[ydir],height_grid, result,FFTW_ESTIMATE);
00446 
00447     STAT_TRACE(fprintf(stderr,"%d,created new fftw plan \n",this_node));
00448     mode_grid_changed = 0;  
00449     
00450   }
00451 
00452   /* Update particles */
00453   updatePartCfg(WITHOUT_BONDS);
00454   //Make sure particles are sorted
00455   if (!sortPartCfg()) {
00456     fprintf(stderr,"%d,could not sort partCfg \n",this_node);
00457     return -1;
00458   }
00459   
00460   if ( !calc_fluctuations(height_grid, switch_fluc)) {
00461     char *errtxt = runtime_error(128);
00462     ERROR_SPRINTF(errtxt,"{034 calculation of height grid failed } ");
00463     return -1;
00464   }
00465 
00466   STAT_TRACE(fprintf(stderr,"%d,calling fftw \n",this_node));
00467 
00468   fftw_execute(mode_analysis_plan);
00469   /* Copy result to modes */
00470   memcpy(modes, result, mode_grid_3d[xdir]*(mode_grid_3d[ydir]/2 + 1)*sizeof(fftw_complex));
00471   
00472   
00473   STAT_TRACE(fprintf(stderr,"%d,called fftw \n",this_node));    
00474     
00475   return 1;
00476     
00477 }
00478 
00479 
00480 /** This routine calculates density profiles for given bead types as a
00481     function of height relative to the bilayer midplane where the
00482     bilayer is assumed to wrap around a sphere of radius r and center
00483     cx,cy,cz.
00484 
00485     \li The radius value of each bead is then calculated relative to
00486     the colloid radius and the population of the relevant bin
00487     is increased.
00488 **/
00489 int bilayer_density_profile_sphere (IntList *beadids, double rrange , DoubleList *density_profile, double radius, double center[3]) {
00490   int i,j;
00491   int thisbin,nbins;
00492   double binwidth;
00493   int nbeadtypes,l_orient;
00494   double relativeradius;
00495   double rvec[3];
00496   double direction[3]; 
00497   double innerr;
00498   double outerr;
00499   double binvolume;
00500   double piconst;
00501   double rs;
00502 
00503 
00504   nbins = density_profile[0].max;
00505   nbeadtypes=beadids->max;
00506   binwidth = 2*rrange/(double)nbins;
00507 
00508 
00509   /* Update particles */
00510   updatePartCfg(WITHOUT_BONDS);
00511   //Make sure particles are sorted
00512   if (!sortPartCfg()) {
00513     char *errtxt = runtime_error(128);
00514     ERROR_SPRINTF(errtxt,"{094 could not sort partCfg} ");
00515     return -1;
00516   }
00517 
00518   if ( density_profile == NULL ) {
00519     char *errtxt = runtime_error(128);
00520     ERROR_SPRINTF(errtxt,"{095 density_profile not initialized in calc_bilayer_density_profile } ");
00521     return -1;
00522   }
00523 
00524   // Do this to fold the particles
00525   fold_all( );
00526 
00527 
00528    for (i = 0 ; i < n_total_particles ; i++) {
00529     for ( j = 0 ; j < nbeadtypes ; j++ ) {
00530       if ( beadids->e[j] == partCfg[i].p.type ) {
00531         /* What is the relative height compared to the grid */
00532         get_mi_vector(rvec,partCfg[i].r.p,center);
00533         relativeradius = sqrt(sqrlen(rvec)) - radius;
00534 
00535         /* If the particle is within our zrange then add it to the profile */
00536         if ( ( -rrange < relativeradius) && ( relativeradius < rrange) ) {
00537           thisbin = (int)(floor((relativeradius+rrange)/binwidth));
00538           if ( thisbin < 0 || thisbin >= density_profile[j].max ) {
00539                 char *errtxt = runtime_error(128);
00540                 ERROR_SPRINTF(errtxt,"{095 bin is outside range } ");
00541                 return -1;
00542           }
00543 
00544           l_orient = lipid_orientation(i,partCfg,0.0,direction,rvec);
00545           /* Distinguish between lipids that are in the top and bottom layers */
00546           if ( l_orient == LIPID_UP ) {
00547             density_profile[j].e[thisbin] += 1.0;
00548           }
00549           if ( l_orient == LIPID_DOWN ) {
00550             density_profile[2*nbeadtypes-j-1].e[thisbin] += 1.0;            
00551           }
00552         }
00553             
00554       }
00555     }
00556   }
00557   /* Normalize the density profile */
00558   piconst = (4.0*3.141592)/(3.0);
00559   rs = radius - rrange;
00560   for ( i = 0 ; i < 2*nbeadtypes ; i++ ) {
00561     for ( j = 0 ; j < nbins ; j++ ) {
00562 
00563       innerr = j*binwidth+rs;
00564       outerr = (j+1)*binwidth + rs;
00565       binvolume = piconst*(outerr*outerr*outerr - innerr*innerr*innerr);
00566       density_profile[i].e[j] = density_profile[i].e[j]/binvolume;
00567     }
00568   }
00569 
00570    return 1;
00571 
00572 }
00573 
00574 
00575 /** This routine calculates density profiles for given bead types as a
00576     function of height relative to the bilayer midplane.
00577 
00578     \li First the height function is calculated
00579 
00580     \li The height value of each bead is then calculated relative to
00581     the overall height function and the population of the relevant bin
00582     is increased.
00583 
00584 **/
00585 int bilayer_density_profile ( IntList *beadids, double hrange , DoubleList *density_profile, int usegrid) {
00586   int i,j, gi,gj;
00587   double* tmp_height_grid, zref,zreflocal;
00588   int thisbin,nbins;
00589   double grid_size[2];
00590   double binwidth;
00591   int nbeadtypes,l_orient;
00592   double relativeheight;
00593   double direction[3];  
00594   double refdir[3] = {0,0,1};
00595   int tmpzdir;
00596   double binvolume;
00597   nbins = density_profile[0].max;
00598   nbeadtypes=beadids->max;
00599 
00600   /*        Check to see that there is a mode grid to work with    */
00601   if ( xdir + ydir + zdir == -3 ) {
00602     char *errtxt = runtime_error(128);
00603     ERROR_SPRINTF(errtxt,"{092 attempt to perform mode analysis with uninitialized grid} ");
00604     return -1;
00605   }
00606   
00607   /* Allocate memory for the grid if we are going to need it */
00608   tmp_height_grid = malloc((mode_grid_3d[xdir])*sizeof(double)*mode_grid_3d[ydir]);
00609   /* Calculate physical size of grid mesh */
00610   grid_size[xdir] = box_l[xdir]/(double)mode_grid_3d[xdir];
00611   grid_size[ydir] = box_l[ydir]/(double)mode_grid_3d[ydir];
00612 
00613   
00614   /* Calculate the height grid which also ensures that particle config is updated */
00615   if ( !calc_fluctuations(tmp_height_grid, 1) ) {
00616     char *errtxt = runtime_error(128);
00617     ERROR_SPRINTF(errtxt,"{034 calculation of height grid failed } ");
00618     return -1;
00619   } 
00620   
00621   tmpzdir = zdir;
00622 
00623 
00624   binwidth = hrange*2.0/(double)(nbins);
00625 
00626   if ( density_profile == NULL ) {
00627     char *errtxt = runtime_error(128);
00628     ERROR_SPRINTF(errtxt,"{095 density_profile not initialized in calc_bilayer_density_profile } ");
00629     return -1;
00630   }
00631 
00632   zref = calc_zref( tmpzdir );
00633 
00634   for (i = 0 ; i < n_total_particles ; i++) {
00635     for ( j = 0 ; j < nbeadtypes ; j++ ) {
00636       if ( beadids->e[j] == partCfg[i].p.type ) {
00637 
00638         if ( usegrid ) {
00639           /* Where are we on the height grid */
00640           gi = (int)(floor( partCfg[i].r.p[xdir]/grid_size[xdir] ));
00641           gj = (int)(floor( partCfg[i].r.p[ydir]/grid_size[ydir] ));
00642           zreflocal = tmp_height_grid[gj + gi*mode_grid_3d[xdir]] + zref;
00643         } else {
00644           zreflocal = zref;
00645         }
00646 
00647         /* What is the relative height compared to the grid */
00648         relativeheight = partCfg[i].r.p[tmpzdir] - zreflocal;                                                          
00649         /* If the particle is within our zrange then add it to the profile */
00650         if ( (relativeheight*relativeheight - hrange*hrange) <= 0 ) {
00651           thisbin = (int)(floor((relativeheight + hrange)/binwidth));
00652           l_orient = lipid_orientation(i,partCfg,zreflocal,direction,refdir);
00653           /* Distinguish between lipids that are in the top and bottom layers */
00654           if ( l_orient == LIPID_UP ) {
00655             density_profile[j].e[thisbin] += 1.0;
00656           }
00657           if ( l_orient == LIPID_DOWN ) {
00658             density_profile[2*nbeadtypes-j-1].e[thisbin] += 1.0;            
00659           }
00660         }           
00661       }
00662     }
00663   }
00664 
00665   /* Normalize the density profile */
00666   binvolume = binwidth*box_l[xdir]*box_l[ydir];
00667   for ( i = 0 ; i < 2*nbeadtypes ; i++ ) {
00668     for ( j = 0 ; j < nbins ; j++ ) {
00669       density_profile[i].e[j] = density_profile[i].e[j]/binvolume;
00670     }
00671   }
00672 
00673 
00674   free(tmp_height_grid);
00675 
00676   return 1;
00677 }
00678 
00679 /**
00680    This routine calculates an average height for the bilayer in each
00681    grid square as follows;
00682 
00683     \li Calculates the average bead position in the height dimension
00684     \ref modes::zdir
00685 
00686     \li Calculates the average height of each bilayer leaflet above or
00687     below \ref modes::zdir separately.  These averages are then averaged
00688     together to create a height function over the 2d grid. In
00689     calculating this height function, checks are made for grid cells
00690     that contain no lipids. In such cases a value equal to the mean of
00691     surrounding cells is substituted.
00692 
00693     \li Also can calculate the thickness of a flat bilayer.
00694 */
00695 
00696 
00697 
00698 int calc_fluctuations ( double* height_grid, int switch_fluc ) {
00699   if (switch_fluc == 1){
00700     STAT_TRACE(fprintf(stderr,"%d,calculating height grid \n",this_node));
00701   } else { if (switch_fluc == 0) {
00702     STAT_TRACE(fprintf(stderr,"%d,calculating thickness \n",this_node));
00703     } else {
00704        char *errtxt = runtime_error(128);
00705        ERROR_SPRINTF(errtxt,"{097 Wrong argument in calc_fluctuations function} ");
00706        return -1;
00707     }
00708   }
00709     
00710   int i, j, gi, gj;
00711   double grid_size[2];
00712   double direction[3];  
00713   double refdir[3] = {0,0,1};
00714   double* height_grid_up;
00715   double* height_grid_down;
00716   int* grid_parts;
00717   int* grid_parts_up;
00718   int* grid_parts_down;
00719   double zreflocal, zref;
00720   int nup;
00721   int ndown;
00722   int nstray;
00723   int nrealstray;
00724   int l_orient;
00725   double norm;
00726   int xi, yi;
00727   double meanval;
00728   int nonzerocnt, gapcnt;
00729 
00730 
00731 
00732 
00733 
00734 
00735   if ( xdir + ydir + zdir == -3 ) {
00736       char *errtxt = runtime_error(128);
00737       ERROR_SPRINTF(errtxt,"{092 attempt to calculate height grid / thickness with uninitialized grid} ");
00738       return -1;
00739   }
00740   
00741 
00742   if ( height_grid == NULL ) {
00743     char *errtxt = runtime_error(128);
00744     ERROR_SPRINTF(errtxt,"{093 you must allocate memory for the height grid / thickness first} ");
00745     return -1;
00746   }
00747 
00748 
00749 
00750   /* Allocate memory for height grid / thickness arrays and initialize these arrays */
00751 
00752   height_grid_up = malloc((mode_grid_3d[xdir])*sizeof(double)*mode_grid_3d[ydir]);
00753   height_grid_down = malloc((mode_grid_3d[xdir])*sizeof(double)*mode_grid_3d[ydir]);
00754   grid_parts_up = malloc((mode_grid_3d[xdir])*sizeof(int)*mode_grid_3d[ydir]);
00755   grid_parts_down = malloc((mode_grid_3d[xdir])*sizeof(int)*mode_grid_3d[ydir]);
00756   grid_parts = malloc((mode_grid_3d[xdir])*sizeof(int)*mode_grid_3d[ydir]);
00757   for ( i = 0 ; i < mode_grid_3d[xdir] ; i++) {
00758     for ( j = 0 ; j < mode_grid_3d[ydir] ; j++) {
00759       height_grid[j+i*mode_grid_3d[xdir]] = 0;
00760       grid_parts[j+i*mode_grid_3d[xdir]] = 0;
00761       height_grid_up[j+i*mode_grid_3d[xdir]] = 0;
00762       grid_parts_up[j+i*mode_grid_3d[xdir]] = 0;
00763       height_grid_down[j+i*mode_grid_3d[xdir]] = 0;
00764       grid_parts_down[j+i*mode_grid_3d[xdir]] = 0;
00765     }
00766   }
00767   
00768   /* Calculate physical size of grid mesh */
00769   grid_size[xdir] = box_l[xdir]/(double)mode_grid_3d[xdir];
00770   grid_size[ydir] = box_l[ydir]/(double)mode_grid_3d[ydir];
00771   
00772   /* Update particles */
00773   updatePartCfg(WITHOUT_BONDS);
00774   //Make sure particles are sorted
00775   if (!sortPartCfg()) {
00776     char *errtxt = runtime_error(128);
00777     ERROR_SPRINTF(errtxt,"{094 could not sort partCfg} ");
00778     return -1;
00779   }
00780   
00781   
00782   
00783   /* Find the mean z position of folded coordinates*/ 
00784   zref = calc_zref( zdir );
00785   
00786   /* Calculate an initial height function of all particles */
00787   for (i = 0 ; i < n_total_particles ; i++) {
00788     gi = floor( partCfg[i].r.p[xdir]/grid_size[xdir] );
00789     gj = floor( partCfg[i].r.p[ydir]/grid_size[ydir] );
00790     height_grid[gj + gi*mode_grid_3d[xdir]] += partCfg[i].r.p[zdir];
00791     grid_parts[gj + gi*mode_grid_3d[xdir]] += 1;
00792   }
00793   
00794   /* Normalise the initial height function */
00795   for ( i = 0 ; i < mode_grid_3d[xdir] ; i++) {
00796     for ( j = 0 ; j < mode_grid_3d[ydir] ; j++) {
00797       if ( grid_parts[j+i*mode_grid_3d[xdir]] > 0 ) {
00798         height_grid[j+i*mode_grid_3d[xdir]] = height_grid[j+i*mode_grid_3d[xdir]]/(double)(grid_parts[j+i*mode_grid_3d[xdir]]);
00799       } else {
00800         height_grid[j+i*mode_grid_3d[xdir]] = zref;
00801       }
00802     }
00803   }
00804   
00805   /* We now use this initial height function to calculate the
00806      lipid_orientation and thereby calculate populations in upper and
00807      lower leaflets */
00808   
00809   
00810   /* Calculate the non normalized height function based on all lipids */
00811   nup = ndown = nstray = nrealstray = 0;
00812   for (i = 0 ; i < n_total_particles ; i++) {
00813     gi = floor( partCfg[i].r.p[xdir]/grid_size[xdir] );
00814     gj = floor( partCfg[i].r.p[ydir]/grid_size[ydir] );
00815     
00816     zreflocal = height_grid[gj+gi*mode_grid_3d[xdir]];
00817     
00818     l_orient = lipid_orientation(i,partCfg,zreflocal,direction,refdir);
00819     
00820     if ( l_orient != LIPID_STRAY && l_orient != REAL_LIPID_STRAY) {
00821       if ( l_orient == LIPID_UP ) {
00822         nup++;
00823         height_grid_up[gj + gi*mode_grid_3d[xdir]] += partCfg[i].r.p[zdir] - zref;
00824         grid_parts_up[gj + gi*mode_grid_3d[xdir]] += 1;
00825       } else if ( l_orient == LIPID_DOWN ) {
00826         ndown++;
00827         height_grid_down[gj + gi*mode_grid_3d[xdir]] += partCfg[i].r.p[zdir] - zref;
00828         grid_parts_down[gj + gi*mode_grid_3d[xdir]] += 1;
00829       }
00830     } else {
00831       if ( l_orient == REAL_LIPID_STRAY ) {
00832         nrealstray++;
00833       }
00834     }
00835   }
00836 
00837 
00838 
00839   /*
00840     if ( nrealstray > 0 || nstray > 0) {
00841     printf("Warning: there were %d stray lipid particles in height calculation \n", nrealstray);
00842     }
00843     printf(" Lipid particles up = %d , Lipid particles down = %d \n",nup, ndown); */
00844   
00845   STAT_TRACE(fprintf(stderr,"%d, Lipids up = %d , Lipids down = %d \n",this_node, nup, ndown));
00846   
00847   /* Reinitialise the height grid */
00848   for ( i = 0 ; i < mode_grid_3d[xdir] ; i++) {
00849     for ( j = 0 ; j < mode_grid_3d[ydir] ; j++) {
00850       height_grid[j+i*mode_grid_3d[xdir]] = 0.0;
00851       grid_parts[j+i*mode_grid_3d[xdir]] = 0;
00852     }
00853   }
00854   
00855   /* Norm we normalize the height function according the number of
00856      points in each grid cell */
00857   norm = 1.0;
00858   for ( i = 0 ; i < mode_grid_3d[xdir] ; i++) {
00859     for ( j = 0 ; j < mode_grid_3d[ydir] ; j++) {
00860       
00861       if ( ( grid_parts_up[j + i*mode_grid_3d[xdir]] > 0 ) && ( grid_parts_down[j + i*mode_grid_3d[xdir]] > 0 ) ) {
00862         /* 
00863            This is where we distinguish between height_grid and thickness:
00864            h = .5*(h_up + h_down)
00865            t = h_up - h_down
00866         */
00867         if (switch_fluc == 1)
00868           height_grid[j+i*mode_grid_3d[xdir]] = 
00869             0.5*norm*((height_grid_up[j+i*mode_grid_3d[xdir]])/(double)(grid_parts_up[j + i*mode_grid_3d[xdir]]) + 
00870                       (height_grid_down[j+i*mode_grid_3d[xdir]])/(double)(grid_parts_down[j + i*mode_grid_3d[xdir]]));
00871         else
00872           height_grid[j+i*mode_grid_3d[xdir]] = 
00873             norm*((height_grid_up[j+i*mode_grid_3d[xdir]])/(double)(grid_parts_up[j + i*mode_grid_3d[xdir]]) - 
00874                   (height_grid_down[j+i*mode_grid_3d[xdir]])/(double)(grid_parts_down[j + i*mode_grid_3d[xdir]]));
00875 
00876         grid_parts[j+i*mode_grid_3d[xdir]] = grid_parts_up[j + i*mode_grid_3d[xdir]] + grid_parts_down[j + i*mode_grid_3d[xdir]];
00877       } else {
00878         // Either upper or lower layer has no lipids
00879         
00880         
00881 
00882         height_grid[j+i*mode_grid_3d[xdir]] = 0.0;
00883         grid_parts[j+i*mode_grid_3d[xdir]] = 0;
00884       }
00885     }
00886   }
00887   
00888   
00889   /* Check height grid for zero values and substitute mean of surrounding cells */
00890   gapcnt = 0;
00891   for ( i = 0 ; i < mode_grid_3d[xdir] ; i++) {
00892     for ( j = 0 ; j < mode_grid_3d[ydir] ; j++) {
00893       if ( grid_parts[j + i*mode_grid_3d[xdir]] ==  0) {
00894         meanval = 0.0;
00895         nonzerocnt = 0;
00896         for ( xi = (i-1) ; xi <= (i+1) ; xi++) {
00897           for ( yi = (j-1) ; yi <= (j+1) ; yi++) {
00898             if ( height_grid[yi+xi*mode_grid_3d[xdir]] != 0 ) {
00899               meanval += height_grid[yi+xi*mode_grid_3d[xdir]];
00900               nonzerocnt++;
00901             }
00902           }
00903         }
00904         if ( nonzerocnt == 0 ) { 
00905           char *errtxt = runtime_error(128);
00906           ERROR_SPRINTF(errtxt,"{095 hole in membrane} ");
00907           return -1;
00908         }
00909         gapcnt++;
00910       }      
00911     }
00912   }
00913   if ( gapcnt != 0 ) { 
00914     fprintf(stderr,"Warning: %d, gridpoints with no particles \n",gapcnt);
00915     fflush(stdout);
00916   }
00917   
00918   free(grid_parts);
00919   free(height_grid_up);
00920   free(height_grid_down);
00921   free(grid_parts_up);
00922   free(grid_parts_down);
00923   
00924   return 1;
00925   
00926 }
00927 
00928 
00929 #undef MODES2D_NUM_TOL
00930 
00931 #endif