![]() |
ESPResSo 3.2.0-11-g9950804-git
Extensible Simulation Package for Soft Matter Research
|
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
1.7.5.1