ESPResSo 3.2.0-159-gf5c8922-git
Extensible Simulation Package for Soft Matter Research
pressure.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 pressure.c
00022     Implementation of \ref pressure.h "pressure.h".
00023 */
00024 #include "pressure.h"
00025 #include "cells.h"
00026 #include "integrate.h"
00027 #include "initialize.h"
00028 #include "domain_decomposition.h"
00029 #include "nsquare.h"
00030 #include "layered.h"
00031 
00032 Observable_stat virials  = {0, {NULL,0,0}, 0,0,0,0};
00033 Observable_stat total_pressure = {0, {NULL,0,0}, 0,0,0,0};
00034 Observable_stat p_tensor = {0, {NULL,0,0},0,0,0,0};
00035 Observable_stat total_p_tensor = {0, {NULL,0,0},0,0,0,0};
00036 
00037 /* Observables used in the calculation of intra- and inter- molecular
00038    non-bonded contributions to pressure and to stress tensor */
00039 Observable_stat_non_bonded virials_non_bonded  = {0, {NULL,0,0}, 0,0,0};
00040 Observable_stat_non_bonded total_pressure_non_bonded = {0, {NULL,0,0}, 0,0,0};
00041 Observable_stat_non_bonded p_tensor_non_bonded = {0, {NULL,0,0},0,0,0};
00042 Observable_stat_non_bonded total_p_tensor_non_bonded = {0, {NULL,0,0},0,0,0};
00043 
00044 nptiso_struct   nptiso   = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,{0.0,0.0,0.0},{0.0,0.0,0.0},1, 0 ,{NPTGEOM_XDIR, NPTGEOM_YDIR, NPTGEOM_ZDIR},0,0,0};
00045 
00046 /************************************************************/
00047 /* callbacks for setmd                                      */
00048 /************************************************************/
00049 
00050 
00051 
00052 /************************************************************/
00053 /* local prototypes                                         */
00054 /************************************************************/
00055 
00056 /** Calculate long range virials (P3M, MMM2d...). */
00057 void calc_long_range_virials();
00058 
00059 /** Initializes a virials Observable stat. */
00060 void init_virials(Observable_stat *stat);
00061 
00062 /** Initializes a virials Observable stat. */
00063 void init_virials_non_bonded(Observable_stat_non_bonded *stat_nb);
00064 
00065 /** on the master node: calc energies only if necessary
00066     @param v_comp flag which enables (1) compensation of the velocities required 
00067                   for deriving a pressure reflecting \ref nptiso_struct::p_inst
00068                   (hence it only works with domain decomposition); naturally it
00069                   therefore doesn't make sense to use it without NpT. */
00070 void master_pressure_calc(int v_comp);
00071 
00072 /** Initializes stat to be used by \ref pressure_calc. */
00073 void init_p_tensor(Observable_stat *stat);
00074 
00075 /** Initializes stat_nb to be used by \ref pressure_calc. */
00076 void init_p_tensor_non_bonded(Observable_stat_non_bonded *stat_nb);
00077 
00078 /*********************************/
00079 /* Scalar and Tensorial Pressure */
00080 /*********************************/
00081 
00082 void pressure_calc(double *result, double *result_t, double *result_nb, double *result_t_nb, int v_comp)
00083 {
00084   int n, i;
00085   double volume = box_l[0]*box_l[1]*box_l[2];
00086 
00087   if (!check_obs_calc_initialized())
00088     return;
00089 
00090   init_virials(&virials);
00091 
00092   init_p_tensor(&p_tensor);
00093   
00094   init_virials_non_bonded(&virials_non_bonded);
00095 
00096   init_p_tensor_non_bonded(&p_tensor_non_bonded);
00097 
00098   on_observable_calc();
00099 
00100   switch (cell_structure.type) {
00101   case CELL_STRUCTURE_LAYERED:
00102     layered_calculate_virials(v_comp);
00103     break;
00104   case CELL_STRUCTURE_DOMDEC:
00105     if(dd.use_vList) {
00106       if (rebuild_verletlist)  
00107         build_verlet_lists();
00108       calculate_verlet_virials(v_comp);
00109     }
00110     else
00111       calculate_link_cell_virials(v_comp);
00112     break;
00113   case CELL_STRUCTURE_NSQUARE:
00114     nsq_calculate_virials(v_comp);
00115   }
00116   /* rescale kinetic energy (=ideal contribution) */
00117 #ifdef ROTATION_PER_PARTICLE
00118     fprintf(stderr, "Switching rotation per particle (#define ROTATION_PER_PARTICLE) and pressure calculation are incompatible.\n");
00119 #endif
00120   
00121   virials.data.e[0] /= (3.0*volume*time_step*time_step);
00122 
00123   calc_long_range_virials();
00124 
00125   for (n = 1; n < virials.data.n; n++)
00126     virials.data.e[n] /= 3.0*volume;
00127 
00128     
00129  /* stress tensor part */
00130  /* The ROTATION option does not effect stress tensor calculations
00131     since rotational energy is not included in the ideal term (unlike
00132     for the pressure) */
00133   for(i=0; i<9; i++)
00134     p_tensor.data.e[i] /= (volume*time_step*time_step);
00135   
00136   for(i=9; i<p_tensor.data.n; i++)
00137     p_tensor.data.e[i]  /= volume;
00138   
00139   /* Intra- and Inter- part of nonbonded interaction */
00140   for (n = 0; n < virials_non_bonded.data_nb.n; n++)
00141     virials_non_bonded.data_nb.e[n] /= 3.0*volume;
00142   
00143   for(i=0; i<p_tensor_non_bonded.data_nb.n; i++)
00144     p_tensor_non_bonded.data_nb.e[i]  /= volume;
00145   
00146   /* gather data */
00147   MPI_Reduce(virials.data.e, result, virials.data.n, MPI_DOUBLE, MPI_SUM, 0, comm_cart);
00148   MPI_Reduce(p_tensor.data.e, result_t, p_tensor.data.n, MPI_DOUBLE, MPI_SUM, 0, comm_cart);
00149   
00150   MPI_Reduce(virials_non_bonded.data_nb.e, result_nb, virials_non_bonded.data_nb.n, MPI_DOUBLE, MPI_SUM, 0, comm_cart);
00151   MPI_Reduce(p_tensor_non_bonded.data_nb.e, result_t_nb, p_tensor_non_bonded.data_nb.n, MPI_DOUBLE, MPI_SUM, 0, comm_cart); 
00152 }
00153 
00154 /************************************************************/
00155 
00156 void calc_long_range_virials()
00157 {
00158 #ifdef ELECTROSTATICS
00159   /* calculate k-space part of electrostatic interaction. */
00160   switch (coulomb.method) {
00161 #ifdef P3M
00162   case COULOMB_ELC_P3M:
00163     fprintf(stderr, "WARNING: pressure calculated, but ELC pressure not implemented\n");
00164     break;
00165   case COULOMB_P3M_GPU:
00166     fprintf(stderr, "WARNING: pressure calculated, but GPU P3M pressure not implemented\n");
00167     break;
00168   case COULOMB_P3M: {
00169     int k;
00170     p3m_charge_assign();
00171     virials.coulomb[1] = p3m_calc_kspace_forces(0,1);
00172     
00173     for(k=0;k<3;k++)
00174       p_tensor.coulomb[9+ k*3 + k] = virials.coulomb[1]/3.;
00175     p3m_calc_kspace_stress(p_tensor.coulomb);
00176     break;
00177   }
00178 #endif
00179   case COULOMB_MMM2D:
00180     fprintf(stderr, "WARNING: pressure calculated, but MMM2D pressure not implemented\n");
00181     break;
00182   case COULOMB_MMM1D:
00183     fprintf(stderr, "WARNING: pressure calculated, but MMM1D pressure not implemented\n");
00184     break;
00185   }
00186 #endif /*ifdef ELECTROSTATICS */  
00187   
00188 #ifdef DIPOLES
00189   /* calculate k-space part of magnetostatic interaction. */
00190   switch (coulomb.Dmethod) {
00191      case DIPOLAR_ALL_WITH_ALL_AND_NO_REPLICA:
00192     fprintf(stderr, "WARNING: pressure calculated, but DAWAANR pressure not implemented\n");
00193     break;
00194   case DIPOLAR_MDLC_DS:
00195     fprintf(stderr, "WARNING: pressure calculated, but DLC pressure not implemented\n");
00196     break;
00197   case DIPOLAR_DS:
00198     fprintf(stderr, "WARNING: pressure calculated, but  MAGNETIC DIRECT SUM pressure not implemented\n");
00199     break;
00200 
00201    
00202 #ifdef DP3M
00203   case DIPOLAR_MDLC_P3M:
00204     fprintf(stderr, "WARNING: pressure calculated, but DLC pressure not implemented\n");
00205     break;
00206   case DIPOLAR_P3M: {
00207     int k;
00208     dp3m_dipole_assign();
00209     virials.dipolar[1] = dp3m_calc_kspace_forces(0,1);
00210      
00211     for(k=0;k<3;k++)
00212       p_tensor.coulomb[9+ k*3 + k] = virials.dipolar[1]/3.;
00213     fprintf(stderr, "WARNING: stress tensor calculated, but dipolar P3M stress tensor not implemented\n");
00214     fprintf(stderr, "WARNING: things have been added to the coulomb virial and p_tensor arrays !!!!!!!\n");
00215     
00216     break;
00217   }
00218 #endif
00219  } 
00220 #endif /*ifdef DIPOLES */
00221 }
00222 
00223 /* Initialize the virials used in the calculation of the scalar pressure */
00224 /************************************************************/
00225 void init_virials(Observable_stat *stat)
00226 {
00227     int n_pre, n_non_bonded, n_coulomb, n_dipolar;
00228 
00229   n_pre        = 1;
00230   n_non_bonded = (n_particle_types*(n_particle_types+1))/2;
00231 
00232   n_coulomb    = 0;
00233   n_dipolar    = 0;
00234 #ifdef ELECTROSTATICS
00235   switch (coulomb.method) {
00236   case COULOMB_NONE: n_coulomb = 0; break;
00237   case COULOMB_P3M_GPU:
00238   case COULOMB_P3M:  n_coulomb = 2; break;
00239   default: n_coulomb  = 1;
00240   }
00241 #endif
00242 #ifdef DIPOLES
00243   switch (coulomb.Dmethod) {
00244   case DIPOLAR_NONE:  n_dipolar = 0; break;
00245   case DIPOLAR_ALL_WITH_ALL_AND_NO_REPLICA:  n_dipolar = 0; break;
00246   case DIPOLAR_DS:  n_dipolar = 0; break;
00247   case DIPOLAR_P3M:   n_dipolar = 2; break;
00248   }
00249 #endif
00250 
00251 
00252   
00253   obsstat_realloc_and_clear(stat, n_pre, n_bonded_ia, n_non_bonded, n_coulomb, n_dipolar, 1);
00254   stat->init_status = 0;
00255 }
00256 
00257 /************************************************************/
00258 void init_virials_non_bonded(Observable_stat_non_bonded *stat_nb)
00259 {
00260   int n_non_bonded;
00261 
00262   n_non_bonded = (n_particle_types*(n_particle_types+1))/2;
00263 
00264   obsstat_realloc_and_clear_non_bonded(stat_nb, n_non_bonded, 1);
00265   stat_nb->init_status_nb = 0;
00266 }
00267 
00268 /* Initialize the p_tensor */
00269 /***************************/
00270 void init_p_tensor(Observable_stat *stat)
00271 {
00272     int n_pre, n_non_bonded, n_coulomb, n_dipolar;
00273 
00274   n_pre        = 1;
00275   n_non_bonded = (n_particle_types*(n_particle_types+1))/2;
00276 
00277   n_coulomb = 0;
00278   n_dipolar = 0;
00279 
00280 #ifdef ELECTROSTATICS
00281   switch (coulomb.method) {
00282   case COULOMB_NONE: n_coulomb = 0; break;
00283   case COULOMB_P3M_GPU:
00284   case COULOMB_P3M:  n_coulomb = 2; break;
00285   default: n_coulomb  = 1;
00286   }
00287 #endif
00288   
00289 #ifdef DIPOLES
00290   switch (coulomb.Dmethod) {
00291   case DIPOLAR_NONE: n_dipolar = 0; break;
00292   case DIPOLAR_ALL_WITH_ALL_AND_NO_REPLICA:  n_dipolar = 0; break;
00293   case DIPOLAR_DS:  n_dipolar = 0; break;
00294   case DIPOLAR_P3M:  n_dipolar = 2; break;
00295   }
00296 #endif
00297 
00298   obsstat_realloc_and_clear(stat, n_pre, n_bonded_ia, n_non_bonded, n_coulomb, n_dipolar, 9);
00299   stat->init_status = 0;
00300 }
00301 
00302 /***************************/
00303 void init_p_tensor_non_bonded(Observable_stat_non_bonded *stat_nb)
00304 {
00305   int n_nonbonded;
00306   n_nonbonded = (n_particle_types*(n_particle_types+1))/2;
00307 
00308   obsstat_realloc_and_clear_non_bonded(stat_nb, n_nonbonded, 9);
00309   stat_nb->init_status_nb = 0;
00310 }
00311 
00312 /************************************************************/
00313 void master_pressure_calc(int v_comp) {
00314   if(v_comp)
00315     mpi_gather_stats(3, total_pressure.data.e, total_p_tensor.data.e, total_pressure_non_bonded.data_nb.e, total_p_tensor_non_bonded.data_nb.e);
00316   else
00317     mpi_gather_stats(2, total_pressure.data.e, total_p_tensor.data.e, total_pressure_non_bonded.data_nb.e, total_p_tensor_non_bonded.data_nb.e);
00318 
00319   total_pressure.init_status = 1+v_comp;
00320   total_p_tensor.init_status = 1+v_comp;
00321   total_pressure_non_bonded.init_status_nb = 1+v_comp;
00322   total_p_tensor_non_bonded.init_status_nb = 1+v_comp;
00323 }
00324 
00325 
00326 /*****************************************************/
00327 /* Routines for Local Stress Tensor                  */
00328 /*****************************************************/
00329 
00330 int getintersection(double pos1[3], double pos2[3],int given, int get, double value, double *answer, double box_size[3])
00331 {
00332   /*pos1 and pos2 are two particle positions.                                                  */
00333   /*given and get are integers from 0 to 2. 0 = x direction. 1 = y direction. 2 = z direction  */
00334   /*there is a point on the line between the two particles p1 and p2 such that r[given]=value  */
00335   /*this procedure returns the value of r[get] at that point                                   */
00336 
00337   double p2r[3];
00338   int i;
00339 
00340   for (i=0;i<3;i++) {
00341     p2r[i] = drem_down((pos2[i]-pos1[i])+box_size[i]/2.0,box_size[i])-box_size[i]/2.0;
00342   }
00343   value = drem_down((value-pos1[given])+box_size[given]/2.0,box_size[given])-box_size[given]/2.0;
00344   //PTENSOR_TRACE(fprintf(stderr,"%d: getintersection: p1 is %f %f %f p2 is %f %f %f p2r is %f %f %f newvalue is %f\n",this_node,pos1[0],pos1[1],pos1[2],pos2[0],pos2[1],pos2[2],p2r[0],p2r[1],p2r[2],value););
00345   
00346   if ((value)*(p2r[given]) < -0.0001) {
00347     char *errtxt = runtime_error(128 + 3*ES_INTEGER_SPACE);
00348     ERROR_SPRINTF(errtxt, "{analyze stress_profile: getintersection: intersection is not between the two given particles - %e is not between %e and %e and box size is %e, given is %d\n ",value,0.0,p2r[given],box_size[given],given);
00349     return 0; 
00350   } else if (given == get) {
00351     *answer =  drem_down(value + pos1[given],box_size[given]);;
00352   } else if (0==p2r[given]) {
00353     char *errtxt = runtime_error(128 + 3*ES_INTEGER_SPACE);
00354     ERROR_SPRINTF(errtxt, "{analyze stress_profile: getintersection: intersection is a line, not a point - value is %g same as %g and %g\n",value,0.0,p2r[given]);
00355     return 0;   
00356   } else {
00357     *answer =  drem_down(pos1[get]+p2r[get]/p2r[given]*value,box_size[get]);
00358   }
00359   return 1;
00360 }
00361 
00362 int getlength(double pos1[3], double pos2[3], int d1, double val1, int d2, double val2, int l, double *answer)
00363 {
00364   /*p1 and p2 are two particles positions                                                          
00365     d1 and d2 are integers between 0 and 2 denoting an axis (x, y, or z)                        
00366     l and k are integers between 0 and 2 denoting an axis (x, y, or z)                          
00367     two points on the line connecting these particles are defined by r[d1]=val1 and r[d2]=val2  
00368     call these two points p3 and p4 (not program variables)                                     
00369     this program returns the distance between p3 and p4 in the l direction
00370   */
00371   
00372   double intersect1, intersect2;
00373 
00374   *answer = 0;
00375   
00376   if  (! getintersection(pos1,pos2,d2,l,val2,&intersect1,box_l) || ! getintersection(pos1,pos2,d1,l,val1,&intersect2,box_l)) {
00377     return 0;
00378   } else {
00379     *answer = drem_down(intersect2 - intersect1 + box_l[l]/2.0, box_l[l]) - box_l[l]/2.0;
00380     return 1;
00381   }
00382 }
00383 
00384 int does_line_go_through_cube(double pos1[3], double pos2[3], double range_start[3], double range[3], int sign[3], double entry[3], double exit[3],int *facein, int *faceout)
00385      /* p1 and p2 are two particle positions
00386         there is a cube in the simulation box with one vertex at range_start and the opposite vertex at range_start + range
00387         this routine calculates where the line connecting p1 and p2 enters and exits this cube
00388         these points are returned in the variables entry and exit
00389         the function returns a 0 if the line does not pass through the cube and 1 if it does
00390      */
00391 {
00392 
00393   double centre[3];             /* centre of the cube */
00394   double vect2centre1[3];       /* vectors from centre of cube to pos1 and pos2 */
00395   double vect2centre2[3];
00396   int i;
00397 
00398   int doesntenter = 0;          /* boolean that indicates whether we have already determined that the line does not enter the cube at all */
00399   double intersection1 = 0.0, intersection2 = 0.0;
00400   int found_entry = 0;          /* boolean that indicates whether we have determined where the line enters */
00401   int found_exit = 0;           /* boolean that indicates whether we have determined where the line exits */
00402   int i1, i2;
00403   int inside1, inside2;
00404 
00405    /* find centre of analyzed cube */
00406   for (i=0;i<3;i++) {
00407     centre[i] = range_start[i] + range[i]/2.0;
00408   }
00409 
00410   /* find vectors connecting two particles to centre */
00411   get_mi_vector(vect2centre1,pos1,centre);
00412   get_mi_vector(vect2centre2,pos2,centre);
00413 
00414   *facein = -1;
00415   *faceout = -1;
00416 
00417   /* check if particles are inside cube */
00418   inside1 = 1;
00419   inside2 = 1;
00420   for (i=0;i<3;i++) {
00421     if (fabs(vect2centre1[i])>range[i]/2.0) inside1 = 0;
00422     if (fabs(vect2centre2[i])>range[i]/2.0) inside2 = 0;
00423   }
00424   PTENSOR_TRACE(fprintf(stderr,"%d: does_line_go_through_cube: Particle1 inside cube = %d Particle2 inside cube = %d \n",this_node,inside1,inside2););
00425 
00426   /* work out which face connecting line enters through */
00427   if (! inside1) {
00428     for (i=0;i<3;i++) {
00429       i1 = (i+1)%3;
00430       i2 = (i+2)%3;
00431       /*does the line start outside the cube in direction i?*/
00432       if ( (! doesntenter ) && (! found_entry) && fabs(vect2centre1[i])>range[i]/2.0 ) { 
00433          /*does the bond heads away from the cube or is part2 is before the cube in direction i? */
00434         if ((vect2centre1[i] * sign[i] > 0) ||  (vect2centre2[i]*sign[i] < -range[i]/2.0)) {
00435           doesntenter = 1;
00436         } else {
00437           getintersection(vect2centre1, vect2centre2, i, i1, -range[i]/2.0*sign[i],&intersection1,box_l);
00438           if (intersection1 > box_l[i1]/2) intersection1 -= box_l[i1];
00439           if (fabs(intersection1) < range[i1]/2.0) {
00440             getintersection(vect2centre1, vect2centre2, i, i2, -range[i]/2.0*sign[i],&intersection2,box_l);
00441             if (intersection2 > box_l[i2]/2) intersection2 -= box_l[i2];
00442             if (fabs(intersection2) < range[i2]/2.0) {
00443               found_entry = 1;
00444               entry[i] = centre[i] -range[i]/2.0*sign[i];
00445               entry[i1] = centre[i1] + intersection1;
00446               entry[i2] = centre[i2] + intersection2;
00447               *facein = i; 
00448             }
00449           }
00450         }
00451       }
00452     }
00453   } else {
00454     entry[0] = pos1[0];
00455     entry[1] = pos1[1];
00456     entry[2] = pos1[2];
00457   }
00458   if (! (found_entry) && ! (inside1)) {
00459     PTENSOR_TRACE(fprintf(stderr,"%d: does_line_go_through_cube: Line does not pass through cube \n",this_node););
00460     return 0;
00461   } else {
00462     PTENSOR_TRACE(fprintf(stderr,"%d: does_line_go_through_cube: Entry is %f %f %f \n",this_node,entry[0], entry[1], entry[2]););
00463     if (! inside2) {
00464       /*check which outside faces of box the line exits through */
00465       for (i=0;i<3;i++) {
00466         i1 = (i+1)%3;
00467         i2 = (i+2)%3;
00468         /* does it enter into the box through the i face */
00469         if ((found_exit == 0) && (fabs(vect2centre2[i]) > range[i]/2.0)) {  /*starts outside cube in i direction */
00470           getintersection(vect2centre1, vect2centre2, i, i1, range[i]/2.0*sign[i],&intersection1,box_l);
00471           if (intersection1 > box_l[i1]/2) intersection1 -= box_l[i1];
00472           if (fabs(intersection1) < range[i1]/2.0) {
00473             getintersection(vect2centre1, vect2centre2, i, i2, range[i]/2.0*sign[i],&intersection2,box_l);
00474             if (intersection2 > box_l[i2]/2) intersection2 -= box_l[i2];
00475             if (fabs(intersection2) < range[i2]/2.0) {
00476               found_exit = 1;
00477               exit[i] = centre[i]   + range[i]/2.0*sign[i];
00478               exit[i1] = centre[i1] + intersection1;
00479               exit[i2] = centre[i2] + intersection2;
00480               *faceout = i; 
00481             }
00482           }
00483         }
00484       }
00485     } else {
00486       exit[0] = pos2[0];
00487       exit[1] = pos2[1];
00488       exit[2] = pos2[2];
00489     }
00490     PTENSOR_TRACE(fprintf(stderr,"%d: does_line_go_through_cube: Exit is %f %f %f\n",this_node,exit[0], exit[1], exit[2]););
00491   }
00492     PTENSOR_TRACE(fprintf(stderr,"%d: does_line_go_through_cube: facein is %d faceout is %d\n",this_node,*facein,*faceout););
00493   return 1;
00494 }
00495 
00496 int distribute_tensors(DoubleList *TensorInBin, double *force, int bins[3], double range_start[3], double range[3], double pos1[3], double pos2[3])
00497 {
00498   /*calculates how to distribute a force tensor between two particles between various bins
00499     the amount distributed to a bin is proportional to the length of the line between the
00500       two particles p1 and p2 that passes through the bin volume
00501     we consider a cube of space starting with a corner at {x_range_start, y_range_start, z_range_start} extending to 
00502       {x_range_start+x_range, y_range_start+y_range, z_range_start+z_range}
00503     this cube is split into x_bins bins in the x direction - we refer to these as x-bins
00504     each x-bin into y_bins bins in the y direction - we refer to these as y-bins
00505     each y_bin into z_bins bins in the z direction - we refer to these as z-bins
00506   */
00507 
00508   int sign[3],sign10[3];    /* indicates whether the line goes in the positive or negative direction in each dimension */
00509   double entry[3], exit[3]; /* the positions at which the line enters and exits the cube */
00510   int startx, endx;         /* x-bins in which the line starts and ends in */
00511   int occupiedxbins;        /* number of x-bins occuped by the line */
00512   int *starty;              /* y-bins in which the line starts in for each x-bin.  This array has dimension occupiedxbins+1. */
00513   int totoccupiedybins;     /* total number of y-bins through which the line passes  */
00514   int *occupiedybins;       /* number of occupied y-bins for each x-bin */
00515   int *occupiedzbins;       /* number of occupied z-bins for each y-bin */
00516   int *startz;              /* z-bins in which the line starts in for each y_bin.  This array has dimension totaloccupiedybins. */
00517   int xbin, ybin, zbin;     /* counters to keep track of bins x_bin goes from 0 to x_bins-1, y_bins from 0 to y_bins-1, z_bins from 0 to Z-bins-1 */
00518   int i ,k, l;    
00519   int counter;              /* keeps track of where we are in the startz array */
00520   int zi;                           
00521   double length;            /* length of line between the two points */
00522   int d1,d2;                /* for each z-bin d1 and d2 are calculated.  they indicate through which faces of the bin the line enters and leaves the bin.  i.e. if the line enters through the face corresponding to y = 0.34 then d1 = 1 (y-direction) and val1 = 0.34 */
00523   double val1, val2;
00524   double intersect;
00525   double segment,segment2;
00526   double calclength;        
00527   int xa, ya, za;           /* counters for bins */
00528   double temp[3];
00529   double redentry[3], redexit[3];  /* like entry and exit but using a coordinate system where range_start corresponds to (0,0,0) and the length scale in each direction is the bin width */
00530   double redbox_l[3];       /* box size is reduced units */
00531   int facein,faceout;
00532 
00533   PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: Distributing tensors for particle p1 %f %f %f and p2 %f %f %f\n",this_node,pos1[0],pos1[1],pos1[2],pos2[0],pos2[1],pos2[2]););
00534   PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: range_start is %f %f %f range is %f %f %f bins is %d %d %d\n",this_node,range_start[0],range_start[1],range_start[2],range[0],range[1],range[2],bins[0],bins[1],bins[2]););
00535   /* work out what direction the line joining the particles goes in */
00536   length = 0;
00537   get_mi_vector(temp, pos2, pos1);
00538   for (i=0;i<3;i++) {
00539     sign[i] = (temp[i] > 0)*2-1;
00540     sign10[i]  = (sign[i]+1)/2.0;
00541   }
00542   PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: sign is %d %d %d\n",this_node,sign[0],sign[1],sign[2]););
00543   
00544   calclength = 0;
00545 
00546   /* check if the line passes through the cube and if so where it enters and exits it */
00547   if (does_line_go_through_cube(pos1, pos2, range_start, range, sign, entry, exit,&facein,&faceout)) {
00548     
00549     /* calculate reduced coordinates */
00550     /* range_start becomes (0,0,0) and range_start + range becomes (bins[0],bins[1],bins[2]) */
00551     get_mi_vector(redentry, entry, range_start);
00552     get_mi_vector(redexit, exit, range_start);
00553     for (i=0;i<3;i++) {
00554       redentry[i] = drem_down(redentry[i]+box_l[i],box_l[i]);
00555       redexit[i] = drem_down(redexit[i]+box_l[i],box_l[i]);
00556     }
00557     length = 0;
00558     get_mi_vector(temp, exit, entry);
00559     for (i=0;i<3;i++) {
00560       redentry[i] = redentry[i]/range[i]*bins[i];
00561       redexit[i] = redexit[i]/range[i]*bins[i];
00562       length += pow(temp[i],2);
00563     }
00564     length = pow(length,0.5);
00565     PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: Reduced Entry is %f %f %f Reduced Exit is %f %f %f \n",this_node,redentry[0],redentry[1],redentry[2],redexit[0], redexit[1], redexit[2]););
00566 
00567     for (i=0;i<3;i++) {
00568       redbox_l[i] = box_l[i]/range[i]*bins[i];
00569     }
00570    
00571     /* find in which x-bins the line starts and stops */ 
00572     if (facein == 0) {
00573       startx = dround(redentry[0]) - 1 + sign10[0];
00574     } else {
00575       startx = floor(redentry[0]);
00576     }
00577     if ((startx < 0) && (range[0]==box_l[0])) startx += bins[0];
00578     if (faceout == 0) {
00579       endx = dround(redexit[0] - sign10[0]);
00580     } else {
00581       endx = floor(redexit[0]);
00582     }
00583     if ((endx < 0) && (range[0]==box_l[0])) startx += bins[0];
00584     occupiedxbins = (sign[0]*(endx-startx) + bins[0])%bins[0] +1;
00585   
00586     PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: x goes from %d to %d\n",this_node,startx, endx);)
00587     /* Initialise starty array */
00588     starty = (int *)malloc(sizeof(int)*(occupiedxbins+1));
00589     occupiedybins = (int *)malloc(sizeof(int)*occupiedxbins);
00590 
00591     /* find in which y-bins the line starts and stops for each x-bin */
00592     /* in xbin the line starts in y-bin number starty[xbin-startx] and ends in starty[xbin-startx+1] */
00593     totoccupiedybins = 0;
00594     if (facein == 1) {
00595       starty[0] = dround(redentry[1]) - 1 + sign10[1];
00596     } else {
00597       starty[0] = floor(redentry[1]);
00598     }
00599     if ((starty[0] < 0) && (range[1]==box_l[1])) starty[0] += bins[1];
00600     for (xa=0; xa < occupiedxbins; xa++) {
00601       xbin = (startx + xa*sign[0]+bins[0])%bins[0];
00602       if (xbin == endx) {
00603         intersect  = redexit[1];
00604         if (faceout == 1) intersect -= sign10[1];
00605         if (( intersect < 0) && (range[1]==box_l[1])) intersect += bins[1];
00606       } else {
00607         if (getintersection(redentry,redexit,0,1,xbin+sign10[0],&intersect,redbox_l) != 1) return 0;
00608       }
00609       starty[xa+1]=floor(intersect);
00610       occupiedybins[xa] = ((starty[xa+1]-starty[xa])*sign[1]+bins[1])%bins[1]+1;
00611       totoccupiedybins += occupiedybins[xa];
00612       PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: in xbin %d y goes from %d to %d\n",this_node, xbin, starty[xa],starty[xa+1]););
00613     }
00614 
00615     /* Initialise startz array */
00616     occupiedzbins = (int *)malloc(sizeof(int)*totoccupiedybins);
00617     startz = (int *)malloc(sizeof(int)*(totoccupiedybins+1));
00618     /* find in which z-bins the line starts and stops for each y-bin*/
00619     counter = 0;
00620     if (facein == 2) {
00621       zi = dround(redentry[2]) - 1 + sign10[2];
00622     } else {
00623       zi = floor(redentry[2]);
00624     }
00625     if (( zi < 0) && (range[2]==box_l[2])) zi += bins[2];
00626     startz[counter] = zi;
00627 
00628     for (xa=0; xa < occupiedxbins; xa++) {
00629       xbin = (startx + xa*sign[0]+bins[0])%bins[0];
00630       for (ya = 0; ya < occupiedybins[xa]-1; ya++) {
00631         ybin = (starty[xa] + ya*sign[1] + bins[1])%bins[1];
00632         PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: xbin is %d ya is %d, occupiedybins[xa] is %d, ybin is %d\n",this_node,xbin,ya,occupiedybins[xa],ybin););
00633         if (getintersection(redentry,redexit,1,2,ybin+sign10[1],&intersect,redbox_l) != 1) return 0;
00634         zi = floor(intersect);
00635         startz[counter+1] = zi;
00636         occupiedzbins[counter]= (sign[2]*(startz[counter+1]-startz[counter]) + bins[2])%bins[2] +1;
00637         PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: in xbin %d ybin %d zbin goes from %d to %d\n",this_node,xbin,ybin,startz[counter],startz[counter+1]););
00638         counter ++;
00639       }
00640       ybin = starty[xa+1];
00641       if (xbin == endx) {
00642         if (faceout == 2) {
00643           zi = dround(redexit[2] -sign10[2]);
00644         } else {
00645           zi = floor(redexit[2]);
00646         }
00647         if (( zi < 0) && (range[2]==box_l[2])) zi += bins[2];
00648       
00649       } else {
00650         if (getintersection(redentry,redexit,0,2,xbin+sign10[0], &intersect, redbox_l) != 1) return 0;
00651         zi = floor(intersect);
00652       }
00653       startz[counter+1]=zi;
00654       occupiedzbins[counter]= (sign[2]*(startz[counter+1]-startz[counter]) + bins[2])%bins[2] +1;
00655       PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: in xbin %d ybin %d zbin goes from %d to %d\n",this_node,xbin,ybin,startz[counter],startz[counter+1]););
00656       counter ++;
00657     }
00658     PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: occupiedxbins is %d,occupiedybins[0] is %d, occupiedzbins[0] is %d\n",this_node,occupiedxbins,occupiedybins[0],occupiedzbins[0]););
00659 
00660     /* find out what length of the line passes through each z-bin */
00661     counter = 0;
00662     for (xa = 0; xa < occupiedxbins; xa ++) {
00663       xbin = (startx + xa*sign[0]+bins[0])%bins[0];
00664       for (ya = 0; ya < occupiedybins[xa]; ya++) {
00665         ybin = (starty[xa] + ya*sign[1] + bins[1])%bins[1];
00666         for (za = 0; za < occupiedzbins[counter]; za++) {
00667           zbin = (startz[counter] + za*sign[2] +bins[2])%bins[2];
00668           if (zbin == startz[counter]) {
00669             if (ybin == starty[xa]) {
00670               if (xbin == startx) {
00671                 if (entry[0]-exit[0] != 0) {
00672                   d1 = 0;
00673                   val1 = redentry[0];
00674                 } else if (entry[1]-exit[1] != 0){
00675                   d1 = 1;
00676                   val1 = redentry[1];
00677                 } else {
00678                   d1 = 2;
00679                   val1 = redentry[2];
00680                 }                                                        
00681                 PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: %d %d %d line starts at point p1 inside bin\n",this_node,xbin,ybin,zbin););
00682               } else {
00683                 d1 = 0;
00684                 val1  = xbin+1-sign10[0];
00685                 PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: %d %d %d line enters through x = %e face of box\n",this_node,xbin,ybin,zbin,val1););
00686               }  
00687             } else {
00688               d1 = 1;
00689               val1 = ybin+1-sign10[1];        
00690               PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: %d %d %d line enters through y = %e face of box\n",this_node,xbin,ybin,zbin,val1););
00691             }
00692           } else {
00693             d1 = 2;
00694             val1 = zbin+1-sign10[2]; 
00695             PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: %d %d %d line enters through z = %e face of box zbin is %d\n",this_node,xbin,ybin,zbin,val1,zbin););
00696           }
00697           if (zbin == startz[counter+1]) {
00698             if (ybin == starty[xa+1]) {
00699               if (xbin == endx) {
00700                 if (entry[0]-exit[0] != 0) {
00701                   d2 = 0;
00702                   val2 = redexit[0];
00703                 } else if (entry[1]-exit[1]!= 0){
00704                   d2 = 1;
00705                   val2 = redexit[1];
00706                 } else {
00707                   d2 = 2;
00708                   val2 = redexit[2];
00709                 }                                                      
00710                 PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: %d %d %d line ends at p2 inside bin\n",this_node,xbin,ybin,zbin););
00711               }
00712               else {
00713                 d2 = 0;
00714                 val2 = xbin+sign10[0];
00715                 PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: %d %d %d line leaves through x = %e face of box\n",this_node,xbin,ybin,zbin,val2););
00716               }
00717             }
00718             else {
00719               d2 = 1;
00720               val2 = ybin+sign10[1]; 
00721               PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: %d %d %d line leaves through y = %e face of box direction %d\n",this_node,xbin,ybin,zbin,val2,sign[1]););
00722             }
00723           }
00724         else {
00725           d2= 2;
00726           val2 = zbin+sign10[2];
00727           PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: %d %d %d line leaves through z = %e face of box\n",this_node,xbin,ybin,zbin,val2););
00728         }
00729           segment2 = 0;
00730             PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: entry %f %f %f exit %f %f %f d1 %d val1 %f d2 %d val2 %f\n",this_node,entry[0],entry[1],entry[2],exit[0],exit[1],exit[2],d1,range_start[d1]+val1*range[d1]/(double)bins[d1],d2,range_start[d2]+val2*range[d2]/(double)bins[d2]););
00731           for (l=0;l<3;l++) {
00732             if (getlength(entry,exit,d1,range_start[d1]+val1*range[d1]/(double)bins[d1],d2,range_start[d2]+val2*range[d2]/(double)bins[d2],l,&segment) != 1) return 0;
00733             segment2 += pow(segment,2);
00734             for (k=0;k<3;k++) {
00735               TensorInBin[xbin*bins[1]*bins[2]+ybin*bins[2]+zbin].e[3*k+l] += force[k] * segment;
00736             }
00737           }
00738           calclength += pow(segment2,0.5);
00739         }
00740         counter ++;
00741       }
00742     }
00743     PTENSOR_TRACE(fprintf(stderr,"%d: distribute_tensors: calclength is %e and length is %e\n}",this_node,calclength,length););
00744     
00745     if (calclength - length >0.0000000001) {
00746       char *errtxt = runtime_error(128 + 3*ES_INTEGER_SPACE);
00747       ERROR_SPRINTF(errtxt, "{%d: analyze stress_profile: bug in distribute tensor code - calclength is %e and length is %e}",this_node,calclength,length);
00748       return 0;
00749     }
00750     free(occupiedzbins);
00751     free(occupiedybins);
00752     free(starty);
00753     free(startz);
00754   }
00755   return 1;
00756 } 
00757 
00758 int reducepos(double pos[3], int bins[3], double centre[3], double range[3], int reducedpos[3]) {
00759   int i;
00760   double working[3];
00761   get_mi_vector(working, pos, centre);
00762   for (i=0;i<3;i++) {
00763     reducedpos[i] = floor((working[i]+range[i]/2.0)*(double)bins[i]/range[i]);
00764   }
00765   return 1; 
00766 }
00767 
00768 int incubewithskin(double pos[3], double centre[3], double range[3])
00769 {
00770   double working[3];
00771   int i;
00772   get_mi_vector(working, pos, centre);
00773   for (i=0; i <3; i++) {
00774     if (fabs(working[i]) > range[i]/2.0 + skin+max_cut) return 0;
00775   }
00776   return 1; 
00777 }
00778 
00779 int whichbin(double pos[3], int bins[3], double centre[3], double range[3], int *bin)
00780 /*calculates which bin a particle is in for local_stress_tensor */
00781 {
00782   int reducedpos[3];
00783   int i;
00784   reducepos(pos, bins, centre, range, reducedpos);
00785   for (i=0;i<3;i++) {
00786     if ((reducedpos[i] < 0) || (reducedpos[i] >= bins[i])) {
00787       *bin = -1;
00788       return 1;
00789     }
00790   }
00791   *bin = reducedpos[0]*bins[1]*bins[2] + reducedpos[1]*bins[2] + reducedpos[2];
00792   return 1;
00793 }
00794 
00795 int get_nonbonded_interaction(Particle *p1, Particle *p2, double *force)
00796 {
00797   /* returns the non_bonded interaction between two particles */
00798 
00799   double dist2, dist;
00800   double d[3];
00801 #ifdef ELECTROSTATICS
00802   int i;
00803   double eforce[3];
00804 #endif
00805 
00806   force[0]=0; force[1]=0; force[2]=0; 
00807   
00808 
00809   if ((p1->p.identity != p2->p.identity)&&(checkIfParticlesInteract(p1->p.type, p2->p.type))) {
00810     /* distance calculation */
00811     get_mi_vector(d, p1->r.p, p2->r.p);
00812     dist2 = SQR(d[0]) + SQR(d[1]) + SQR(d[2]);
00813     dist  = sqrt(dist2);
00814     calc_non_bonded_pair_force_simple(p1,p2,d,dist,dist2,force);
00815 #ifdef ELECTROSTATICS
00816     if (coulomb.method != COULOMB_NONE) {
00817       switch (coulomb.method) {
00818 #ifdef P3M
00819       case COULOMB_P3M_GPU:
00820         fprintf(stderr,"WARNING: Local stress tensor calculation cannot handle GPU P3M electrostatics so it is left out\n");  
00821         break;
00822       case COULOMB_P3M:
00823         fprintf(stderr,"WARNING: Local stress tensor calculation cannot handle P3M electrostatics so it is left out\n");  
00824         break;
00825 #endif
00826       case COULOMB_DH:
00827         for (i = 0; i < 3; i++)
00828           eforce[i] = 0;
00829         add_dh_coulomb_pair_force(p1,p2,d,dist, eforce);
00830         for(i=0;i<3;i++)
00831           force[i] += eforce[i];
00832         break;
00833       case COULOMB_RF:
00834         for (i = 0; i < 3; i++)
00835           eforce[i] = 0;
00836         add_rf_coulomb_pair_force(p1,p2,d,dist, eforce);
00837         for(i=0;i<3;i++)
00838             force[i] += eforce[i];
00839         break;
00840       case COULOMB_INTER_RF:
00841         // this is done elsewhere
00842         break;
00843       case COULOMB_MMM1D:
00844         fprintf(stderr,"WARNING: Local stress tensor calculation cannot handle MMM1D electrostatics so it is left out\n");  
00845       default:
00846         fprintf(stderr,"WARNING: Local stress tensor calculation does not recognise this electrostatic interaction\n");  
00847       }
00848     }
00849 #endif /*ifdef ELECTROSTATICS */
00850 
00851 #ifdef DIPOLES
00852     if (coulomb.Dmethod != DIPOLAR_NONE) {
00853       switch (coulomb.Dmethod) {
00854 #ifdef DP3M
00855       case DIPOLAR_P3M:
00856         fprintf(stderr,"WARNING: Local stress tensor calculation cannot handle P3M magnetostatics so it is left out\n");  
00857         break;
00858 #endif
00859       case DIPOLAR_ALL_WITH_ALL_AND_NO_REPLICA:
00860         fprintf(stderr,"WARNING: Local stress tensor calculation cannot handle DAWAANR magnetostatics so it is left out\n");  
00861         break;
00862       case DIPOLAR_DS:
00863         fprintf(stderr,"WARNING: Local stress tensor calculation cannot handle MAGNETIC DIPOLAR SUM magnetostatics so it is left out\n");  
00864         break;
00865 
00866       default:
00867         fprintf(stderr,"WARNING: Local stress tensor calculation does not recognise this magnetostatic interaction\n");  
00868       }
00869     }
00870 #endif /*ifdef DIPOLES */
00871 
00872 
00873   } /*if p1-> ... */
00874   return 0;
00875 }
00876 
00877 int local_stress_tensor_calc(DoubleList *TensorInBin, int bins[3], int periodic[3], double range_start[3], double range[3])
00878 {
00879   /*calculates local stress tensors in cuboid bins
00880     uses Irving Kirkwood method
00881     we consider a cube of space starting with a corner at position range_start extending to 
00882       range_start + range
00883     if the variable periodic is set to 1 in dimension i then the cube is assumed to span the periodic box
00884     this cube is divided into bins[0] bins in the x direction bins[1] in the y direction, and bins[2] in the z direction
00885   */
00886 
00887   int i,j;                       /*counter for dimension */
00888   double binvolume;
00889   int c, np, n, bin;
00890   double centre[3];
00891   Cell *cell;
00892   Particle *p1, *p2, **pairs;
00893   Particle *particles;
00894   double force[3];
00895   int k,l;
00896   int type_num;
00897   Bonded_ia_parameters *iaparams;
00898   double dx[3];
00899 
00900   
00901   for (i = 0; i < 3; i ++) {
00902     if (periodic[i]) {
00903       range[i] = box_l[i];
00904       range_start[i] = 0;
00905     }
00906   }
00907 
00908   /* find centre of analyzed cube */
00909   for (i=0;i<3;i++) {
00910     centre[i] = range_start[i] + range[i]/2.0;
00911   }
00912 
00913   /* We consider all particles that are within a certain distance of the cube. The skin is used as this distance.  If the
00914      skin from on opposite sides of the box overlaps then we produce an error message.  To code dround this would be
00915      creating unnecessary work since I can't imagine when we might want that */
00916 
00917   if (skin < 0.0) {
00918     char *errtxt = runtime_error(128 + 3*ES_INTEGER_SPACE);
00919     ERROR_SPRINTF(errtxt, "{analyze stress_profile: parameter skin not set}");
00920     return 0;
00921   }
00922   for (i=0;i<3;i++) {
00923     if ((! periodic[i]) && (range[i] + 2*skin +2*max_cut > box_l[i])) {
00924       char *errtxt = runtime_error(128 + 3*ES_INTEGER_SPACE);
00925       ERROR_SPRINTF(errtxt, "{analyze stress_profile: Analyzed box (%g) with skin+max_cut(%g) is larger than simulation box (%g).\n",range[i],skin+max_cut,box_l[i]);
00926       return 0;
00927     }
00928     range_start[i] = drem_down(range_start[i],box_l[i]);
00929   }
00930   PTENSOR_TRACE(fprintf(stderr,"%d: Running stress_profile\n",this_node));
00931 
00932   binvolume = range[0]*range[1]*range[2]/(double)bins[0]/(double)bins[1]/(double)bins[2];
00933 
00934   /* this next bit loops over all pair of particles, calculates the force between them, and distributes it amongst the tensors */
00935 
00936   // loop over all local cells
00937   for (c = 0; c < local_cells.n; c++) {
00938     cell = local_cells.cell[c];
00939     particles   = cell->part;
00940     np  = cell->n;
00941     // loop over all particles in this cell
00942     for(i = 0; i < np; i++)  {
00943       p1 = &(particles[i]);
00944       whichbin(p1->r.p,bins,centre, range, &bin); 
00945       if (bin >= 0) {
00946         PTENSOR_TRACE(fprintf(stderr,"%d:Got particle number %d i is %d pos is %f %f %f \n",this_node,p1->p.identity,i,p1->r.p[0],p1->r.p[1],p1->r.p[2]));
00947         PTENSOR_TRACE(fprintf(stderr,"%d:Ideal gas component is {",this_node));
00948         for(k=0;k<3;k++) {
00949           for(l=0;l<3;l++) {
00950             TensorInBin[bin].e[k*3 + l] += (p1->m.v[k])*(p1->m.v[l])*PMASS(*p1)/time_step/time_step;
00951             PTENSOR_TRACE(fprintf(stderr,"%f ",(p1->m.v[k])*(p1->m.v[l])*PMASS(*p1)/time_step/time_step));
00952           }
00953         }
00954 
00955         PTENSOR_TRACE(fprintf(stderr,"}\n"));
00956       }
00957       
00958       /* bonded contributions */
00959       j = 0;
00960       while(j < p1->bl.n) {
00961         type_num = p1->bl.e[j++];
00962         iaparams = &bonded_ia_params[type_num];
00963 
00964         /* fetch particle 2 */
00965         p2 = local_particles[p1->bl.e[j++]];
00966         get_mi_vector(dx, p1->r.p, p2->r.p);
00967         calc_bonded_force(p1,p2,iaparams,&j,dx,force);
00968         PTENSOR_TRACE(fprintf(stderr,"%d: Bonded to particle %d with force %f %f %f\n",this_node,p2->p.identity,force[0],force[1],force[2]));
00969         if ((pow(force[0],2)+pow(force[1],2)+pow(force[2],2)) > 0) {
00970           if (distribute_tensors(TensorInBin,force,bins,range_start,range,p1->r.p, p2->r.p) != 1) return 0;
00971         }
00972       }
00973     }
00974 
00975     // Loop cell neighbors
00976     for (n = 0; n < dd.cell_inter[c].n_neighbors; n++) {
00977       pairs = dd.cell_inter[c].nList[n].vList.pair;
00978       np    = dd.cell_inter[c].nList[n].vList.n;
00979 
00980       // verlet list loop //
00981       for(i=0; i<2*np; i+=2) {
00982         p1 = pairs[i];                    // pointer to particle 1
00983         p2 = pairs[i+1];                  // pointer to particle 2
00984         if ((incubewithskin(p1->r.p,centre,range)) && (incubewithskin(p2->r.p,centre,range))) {
00985           get_nonbonded_interaction(p1,p2, force);
00986           PTENSOR_TRACE(fprintf(stderr,"%d:Looking at pair %d %d force is %f %f %f\n",this_node,p1->p.identity, p2->p.identity,force[0],force[1], force[2]));
00987           if ((pow(force[0],2)+pow(force[1],2)+pow(force[2],2)) > 0) {
00988             if (distribute_tensors(TensorInBin,force,bins,range_start,range,p1->r.p, p2->r.p) != 1) return 0;
00989           }
00990         } else {
00991           // PTENSOR_TRACE(fprintf(stderr,"%d:Looking at pair %d %d not in cube with skin\n",this_node,p1->p.identity, p2->p.identity));
00992         }
00993       }
00994     }
00995   }
00996 
00997   for (i=0;i<bins[0]*bins[1]*bins[2];i++) {
00998     for (j=0;j<9;j++) {
00999         TensorInBin[i].e[j] /= binvolume;
01000     }
01001   }
01002 
01003   return 1;
01004 }
01005 
01006 
01007 /************************************************************/
01008 int observable_compute_stress_tensor(int v_comp, double *A, unsigned int n_A)
01009 {
01010   int i, j;
01011   double value;
01012   double p_vel[3];
01013 
01014   /* if desired (v_comp==1) replace ideal component with instantaneous one */
01015    if (total_pressure.init_status != 1+v_comp ) {
01016     init_virials(&total_pressure);
01017     init_p_tensor(&total_p_tensor);
01018 
01019     init_virials_non_bonded(&total_pressure_non_bonded);
01020     init_p_tensor_non_bonded(&total_p_tensor_non_bonded);
01021 
01022     if(v_comp && (integ_switch == INTEG_METHOD_NPT_ISO) && !(nptiso.invalidate_p_vel)) {
01023       if (total_pressure.init_status == 0)
01024         master_pressure_calc(0);
01025       p_tensor.data.e[0] = 0.0;
01026       MPI_Reduce(nptiso.p_vel, p_vel, 3, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
01027       for(i=0; i<3; i++)
01028         if(nptiso.geometry & nptiso.nptgeom_dir[i])
01029           p_tensor.data.e[0] += p_vel[i];
01030       p_tensor.data.e[0] /= (nptiso.dimension*nptiso.volume);
01031       total_pressure.init_status = 1+v_comp;   }
01032     else
01033       master_pressure_calc(v_comp);
01034   }
01035 
01036   for(j=0; j<9; j++) {
01037     value = total_p_tensor.data.e[j];
01038     for (i = 1; i < total_p_tensor.data.n/9; i++) value += total_p_tensor.data.e[9*i + j];
01039     A[j]=value;
01040   }
01041   return 0;
01042 }