ESPResSo 3.2.0-11-g9950804-git
Extensible Simulation Package for Soft Matter Research
maggs.c
Go to the documentation of this file.
00001 /*
00002   Copyright (C) 2010,2011,2012,2013 The ESPResSo project
00003   Copyright (C) 2010,2011 Florian Fahrenberger
00004   Copyright (C) 2007,2008,2009,2010 
00005     Max-Planck-Institute for Polymer Research, Theory Group
00006  
00007   This file is part of ESPResSo.
00008  
00009   ESPResSo is free software: you can redistribute it and/or modify
00010   it under the terms of the GNU General Public License as published by
00011   the Free Software Foundation, either version 3 of the License, or
00012   (at your option) any later version.
00013  
00014   ESPResSo is distributed in the hope that it will be useful,
00015   but WITHOUT ANY WARRANTY; without even the implied warranty of
00016   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017   GNU General Public License for more details.
00018  
00019   You should have received a copy of the GNU General Public License
00020   along with this program.  If not, see <http://www.gnu.org/licenses/>. 
00021 */
00022 
00023 /** \file maggs.c
00024  *  Maxwell Equations Molecular Dynamics (MEMD) method for electrostatic
00025  *  interactions.
00026  *
00027  *  We use a local update scheme to propagate artificial B-fields on a
00028  *  lattice in the system. In principal, the algorithm simulates full
00029  *  electrodynamics, but with a tunable speed of light.
00030  *
00031  *  The method is very usable for large particle numbers or highly
00032  *  parallel architectures, since it is local and scales linearly.
00033  *  It is not suited for high-precision calculation of forces, since
00034  *  the simple interpolation scheme produces errors in the order of
00035  *  10^-5 in the force.
00036  *
00037  *  The chosen mesh should roughly be of the size of the particles.
00038  *
00039  *  Further reading on the algorithm:
00040  *  I. Pasichnyk and B. Dunweg, Coulomb interaction via local
00041  *  dynamics: a molecular-dynamics algorithm. J. Phys:
00042  *  Condens. Matter, 16 ,p. 1399-4020, (2004).
00043  *  
00044  */
00045 
00046 
00047 #include <mpi.h>
00048 #include <stdio.h>
00049 #include <stdlib.h>
00050 #include <string.h>
00051 #include "ghosts.h"
00052 #include "global.h"
00053 #include "grid.h"
00054 #include "integrate.h"
00055 #include "initialize.h"
00056 #include "interaction_data.h"
00057 #include "particle_data.h"
00058 #include "communication.h"
00059 #include "maggs.h"
00060 #include "thermostat.h"
00061 #include "cells.h"
00062 #include "domain_decomposition.h"
00063 #include "errorhandling.h"
00064 
00065 #ifdef ELECTROSTATICS
00066 
00067 /* MPI tags for the maggs communications: */
00068 /* Used in maggs_init() -> calc_glue_patch(). */
00069 #define REQ_MAGGS_SPREAD 300
00070 #define REQ_MAGGS_EQUIL  301
00071 
00072 /* Factors for self-influence currection */
00073 /* (stemming from epsilon_zero and 4*pi) */
00074 #define SELF_FACTOR_1 1.57364595
00075 #define SELF_FACTOR_2 1.5078141
00076 // from lattice Green's function: 0.5054620197
00077 // and 0.126365505
00078 // e_0 = 8.85418781762 * 10^-12
00079 
00080 /* Define numbers for directions and dimensions: */
00081 #define SPACE_DIM 3                 /* number of dimensions */
00082 #define NOWHERE -1                  /* not a direction */
00083 #define NDIRS 6                                 /* number of directions */
00084 #define XPLUS 0                     /* add for specific direction */
00085 #define YPLUS 1
00086 #define ZPLUS 2
00087 #define ZMINUS 3
00088 #define YMINUS 4
00089 #define XMINUS 5
00090 #define OPP_DIR(dir)    (5-(dir))       /* Opposite direction */
00091 
00092 
00093 /* Three often used macros for looping over 3D */
00094 #define FOR3D(dir) for(dir=0; dir<SPACE_DIM; dir++)
00095 
00096 #define FORALL_INNER_SITES(i,j,k)                                       \
00097   for(i=lparams.inner_left_down[0];i<lparams.inner_up_right[0];i++)     \
00098     for(j=lparams.inner_left_down[1];j<lparams.inner_up_right[1];j++)   \
00099       for(k=lparams.inner_left_down[2];k<lparams.inner_up_right[2];k++) 
00100 
00101 #define FORALL_SITES(i,j,k)                     \
00102   for(i=0;i<lparams.dim[0];i++)                 \
00103     for(j=0;j<lparams.dim[1];j++)               \
00104       for(k=0;k<lparams.dim[2];k++) 
00105 /* from ifndef MAGGS_H */
00106 
00107 /***************************************/
00108 /****** data types and structures ******/
00109 /***************************************/
00110 
00111 typedef int      t_ivector [SPACE_DIM]; /* integer vector for position in grid */
00112 typedef double   t_dvector [SPACE_DIM]; /* double vector for fields etc. */
00113 typedef int      t_dirs    [NDIRS];     /* integer vector for directions */
00114 
00115 /** Structure of local lattice parameters. */
00116 typedef struct {
00117   t_dvector left_down_position;        /* spatial position of left down grid point */  
00118   t_dvector upper_right_position;      /* spatial positon of upper right grid point */ 
00119   int inner_left_down[3];              /* inner left down grid point    */
00120   int inner_up_right[3];               /* inner up right grid point + (1,1,1) */
00121   int halo_left_down[3];               /* halo-region left down grid point  */
00122   int halo_upper_right[3];             /* halo-region up right global grid point  */
00123   int margin[SPACE_DIM*2];             /* number of margin mesh points (even index - left, odd - right). */
00124   t_ivector dim;                       /* grid dimension (size + glue_patch region) of local mesh.  */
00125   t_ivector size;                      /* dimension of mesh inside node domain. */
00126   int volume;                          /* number of lattice sites in local domain */
00127 } lattice_parameters;
00128 
00129 /** surface_patch structure for communication. */
00130 typedef struct {
00131   int offset;        /* source offset for the site index */
00132   int doffset;       /* destination offset for the site index */
00133   int stride;        /* minimal contiguous block */  
00134   int skip;          /* gap between two strides (from the first element of one stride to the first elem. of next stride */
00135   int nblocks;       /* number of strides */
00136   int coord[2];      /* coordinates of the vector fields which has to be exchanged */
00137   int volume;        /* number of lattice sites in surface patch */
00138 } t_surf_patch;
00139 
00140 /** structure for properties of each lattice site */
00141 typedef struct {
00142   double    charge;                     /* charge on site */
00143   double    permittivity[SPACE_DIM];    /* dielectric properties on adjoined bonds. */
00144   int       r[SPACE_DIM];               /* position of site in space */
00145 } t_site;
00146 
00147 
00148 
00149 
00150 
00151 
00152 /*******************************/
00153 /****** global variables ***** */
00154 /*******************************/
00155 
00156 /* create system structure with all zeros. Filled in maggs_set_parameters(); */
00157 MAGGS_struct maggs = { 1, 1.0, 0. , 0. , 0. , 0. , 0. , 0 , 0. , 0. , {{0.},{0.}}};
00158 
00159 /* local mesh. */
00160 static lattice_parameters lparams;
00161 /* local lattice */
00162 static t_site* lattice;
00163 /* local D field */
00164 static double* Dfield;
00165 /* local B field */
00166 static double* Bfield;
00167 /* site neighbors */
00168 static t_dirs* neighbor;
00169 
00170 
00171 
00172 
00173 
00174 
00175 /*******************************************************/
00176 /*      Private Functions list with short comment      */
00177 /*******************************************************/
00178 /*
00179  * This is supposed to give an overview on the contained functions.
00180  * Declaration is not needed, since the functions are in right order,
00181  * but it is possible to do so here, if something is added with
00182  * non-fitting cross-references.
00183  *
00184  * Longer descriptions of the functions' tasks can be found in the
00185  * implementation.
00186  */
00187 
00188 /****** small helper functions: ******/
00189 // int maggs_get_linear_index(int x, int y, int z, int latticedim[SPACE_DIM]); /* linear indexing for speed */
00190 // int maggs_count_charged_particles(); /* global number of charges */
00191 // int maggs_get_offset(int index_shift, int index_base, int axes, int adim[3]); /* offset of currect lattice site */
00192 // void maggs_calc_directions(int j, int* dir1, int*dir2); /* for a given direction, give back the other two */
00193 // double maggs_calc_curl(int mue, int nue, double* field, int* Neighbor, int index); /* calculate curl in real space */
00194 // double maggs_calc_dual_curl(int mue, int nue, double* field, int* Neighbor, int index); /* calculate curl in dual space */
00195 // void maggs_update_plaquette(int mue, int nue, int* Neighbor, int index, double delta); /* update fields on plaquette */
00196 
00197 /****** setup everything: ******/
00198 // int maggs_set_parameters(double bjerrum, double f_mass, int mesh); /* set the system parameters */
00199 // void maggs_setup_neighbors(); /* setup the site neighbors */
00200 // void maggs_setup_local_lattice(); /* set lattice parameters */
00201 // void maggs_calc_surface_patches(t_surf_patch* surface_patch); /* prepare for communication */
00202 // void maggs_prepare_surface_planes(int dim, MPI_Datatype *xy, MPI_Datatype *xz, MPI_Datatype *yz, t_surf_patch *surface_patch); /* prepare for communication */
00203 
00204 /****** communication function: ******/
00205 // void maggs_exchange_surface_patch(double *field, int dim, int e_equil); /* communicate */
00206 
00207 /****** interpolate charges on lattice: ******/
00208 // double maggs_interpol1D(double x); /* interpolate charge linearly in one direction */
00209 // void maggs_interpolate_charge(int *first, double *rel, double q); /* interpolate all charges */
00210 // void maggs_accumulate_charge_from_ghosts(); /* interplate ghost charges */
00211 // void maggs_distribute_particle_charges(); /* find cube and call interpolation */
00212 // void maggs_calc_charge_gradients(double *rel, double q, double *grad); /* calculate gradients */
00213 // void maggs_update_charge_gradients(double *grad); /* find cube and call calculate gradients */
00214 
00215 /****** initialization procedure: ******/
00216 // double maggs_check_curl_E(); /* check Gauss law (maximum deviation) */
00217 // void maggs_perform_rot_move_inplane(int i, int n); /* calculate correction for plaquette */
00218 // void maggs_minimize_transverse_field(); /* B field minimization routine */
00219 // void maggs_calc_init_e_field(); /* initial solution */
00220 
00221 /****** calculate currents and E-fields ******/
00222 // void maggs_calc_charge_fluxes_1D(double q, double *help, double *flux, int dir); /* charge flux for current */
00223 // int maggs_check_intersect_1D(double delta, double r_new, int dir, int first, double *t_step, int identity); /* check if particles change cubes */
00224 // void maggs_calc_e_field_on_link_1D(int index, double *flux, double v, int dir); /* update field from current */
00225 // void maggs_add_current_on_segment(Particle *p, int ghost_cell); /* loop, find cube, add current */
00226 // void maggs_couple_current_to_Dfield() /* update field from current */
00227 
00228 /****** calculate B-fields and forces ******/
00229 // void maggs_propagate_B_field(double dt); /* propagate the B-field */
00230 // void maggs_add_transverse_field(double dt) /* calculate E-field from B-field */
00231 // void maggs_calc_self_influence(Particle* P); /* correct self influence */
00232 // void maggs_calc_part_link_forces(Particle *p, int index, double *grad) /* calculate force from D-field */
00233 // void maggs_calc_forces(); /* calculate all forces correctly */
00234 
00235 /****** get energy and print out stuff ******/
00236 // double maggs_electric_energy(); /* calculate electric (E-field) energy */
00237 // double maggs_magnetic_energy(); /* calculate magnetic (B-field) energy */
00238 
00239 /****** init and exit ******/
00240 // maggs_init(); /* initialize: check parameters, assign memory, calculate initial field */
00241 // maggs_exit(); /* free memory */
00242 
00243 
00244 
00245 
00246 
00247 
00248 
00249 /*************************************/
00250 /****** small helper functions: ******/
00251 /*************************************/
00252 
00253 /** Turns the 3D index into a linear index.
00254     adim contains the dimensions of the lattice in
00255     the 3 directions: adim[0] = x_max, ..., adim[2] = z_max
00256     z is the first index!!!
00257     @return linear index
00258     @param x           index in x direction
00259     @param y           index in y direction
00260     @param z           index in z direction
00261     @param latticedim  dimensions of the lattice
00262 */
00263 int maggs_get_linear_index(int x, int y, int z, int latticedim[SPACE_DIM])
00264 {
00265   return (z + latticedim[ZPLUS]*(y + latticedim[YPLUS]*x));
00266 }
00267 
00268 /** Counts the total number of charged particles on 
00269     all processors.
00270     @return total number of charged particles in the system
00271 */
00272 int maggs_count_charged_particles()
00273 {  
00274   Cell *cell;
00275   Particle *part;
00276   int i,c,np;
00277   double node_sum, tot_sum;
00278         
00279   node_sum=0.0; 
00280   tot_sum =0.0;
00281         
00282   for (c = 0; c < local_cells.n; c++) {
00283     cell = local_cells.cell[c];
00284     part = cell->part;
00285     np   = cell->n;
00286     for(i=0;i<np;i++) 
00287       if( part[i].p.q != 0.0 ) node_sum += 1.0;
00288   }
00289         
00290   MPI_Reduce(&node_sum, &tot_sum, 1, MPI_DOUBLE, MPI_SUM, 0, comm_cart);
00291         
00292   return tot_sum;
00293 }
00294 
00295 
00296 /** Index shift is calculated for moving in various
00297     directions with the linear index.
00298     @return Offset of current node from base
00299     @param index_shift     amount to move in direction
00300     @param index_base      index of base on current processor
00301     @param axes            in which direction to move
00302     @param adim            dimensions of the local lattice
00303 */
00304 int maggs_get_offset(int index_shift, int index_base, int axes, int adim[3])
00305 {
00306   int dif;
00307   dif = index_shift - index_base;
00308   if(axes <= 1) dif *= adim[2];
00309   if(axes == 0) dif *= adim[1]; 
00310         
00311   return (dif);
00312 }
00313 
00314 
00315 /** For any direction j, write the two other directions
00316     into the pointers in circular permutation.
00317     @param j    given first direction
00318     @param dir1 write second direction into
00319     @param dir2 write third direction into
00320 */
00321 void maggs_calc_directions(int j, int* dir1, int*dir2)
00322 {
00323   *dir1 = *dir2 = -1;
00324   switch(j) {
00325   case 0 :
00326     *dir1 = 2;
00327     *dir2 = 1;
00328     break;
00329   case 1 :
00330     *dir1 = 2;
00331     *dir2 = 0;
00332     break;  
00333   case 2 :
00334     *dir1 = 1;
00335     *dir2 = 0;
00336     break;
00337   }  
00338 }
00339 
00340 /** Calculates the finite differences rotation in real space in mue-nue plane:
00341     \f$\frac{\partial}{\partial t}{D} = \nabla \times B\f$ (and prefactors plus current)
00342     The given "double* field" should be a B-Field!!
00343     @return rotation result
00344     @param mue       direction 1 of plane to rotate in
00345     @param nue       direction 2 of plane to rotate in
00346     @param field     input B-field
00347     @param Neighbor  neighbor lattice site
00348     @param index     index of current lattice site
00349 */
00350 double maggs_calc_curl(int mue, int nue, double* field, int* Neighbor, int index)
00351 {
00352   double result;
00353         
00354   result = field[index+mue] + field[3*Neighbor[OPP_DIR(mue)]+nue] -
00355     field[3*Neighbor[OPP_DIR(nue)]+mue] - field[index+nue];
00356         
00357   return result;
00358 }
00359 
00360 /** Calculates the finite differences rotation in dual space in mue-nue plane:
00361     \f$\frac{\partial}{\partial t}{B} = - \nabla \times D / (\epsilon)\f$
00362     The given "double* field" should be a D-Field!!
00363     @return rotation result
00364     @param mue       direction 1 of plane to rotate in
00365     @param nue       direction 2 of plane to rotate in
00366     @param field     input D-field
00367     @param Neighbor  neighbor lattice site
00368     @param index     index of current lattice site
00369 */
00370 double maggs_calc_dual_curl(int mue, int nue, double* field, int* Neighbor, int index)
00371 {
00372   double res;
00373         
00374   res = field[index+mue] + field[3*Neighbor[mue]+nue] -
00375     field[3*Neighbor[nue]+mue] - field[index+nue];
00376         
00377   return res;
00378 }
00379 
00380 
00381 /** updates all D-fields on links of the plaquette
00382     and the surroundings. delta was calculated before
00383     in function "maggs_perform_rot_move_inplane".
00384     @param mue        direction 1 of update
00385     @param nue        direction 2 of update
00386     @param Neighbor   neighbor lattice site
00387     @param index      index of current lattice site
00388     @param delta      by which amount to update field
00389 */
00390 void maggs_update_plaquette(int mue, int nue, int* Neighbor, int index, double delta)
00391 {
00392   int i = 3*index;
00393   Dfield[i+mue]             += delta;
00394   Dfield[3*Neighbor[mue]+nue] += delta;
00395   Dfield[3*Neighbor[nue]+mue] -= delta;
00396   Dfield[i+nue]             -= delta;  
00397 }
00398 
00399 
00400 /** Basic sanity checks to see if the code will run.
00401     @return zero if everything is fine. -1 otherwise.
00402 */
00403 int maggs_sanity_checks()
00404 {
00405   char *errtxt;
00406   int ret = 0;
00407   int d;
00408   int max_node_grid = 1;
00409 
00410   FOR3D(d) if(node_grid[d] > max_node_grid) max_node_grid = node_grid[d];
00411         
00412   if (maggs.bjerrum == 0.) {
00413     errtxt = runtime_error(128);
00414     ERROR_SPRINTF(errtxt, "{301 MEMD: bjerrum length is zero.} ");
00415     ret = -1;
00416   }
00417   else if ( (box_l[0] != box_l[1]) || (box_l[1] != box_l[2]) ) {
00418     errtxt = runtime_error(128);
00419     ERROR_SPRINTF(errtxt, "{302 MEMD needs cubic box.} ");
00420     ret = -1;
00421   }
00422   if (!PERIODIC(0) || !PERIODIC(1) || !PERIODIC(2)) {
00423     errtxt = runtime_error(128);
00424     ERROR_SPRINTF(errtxt, "{303 MEMD requires periodicity 1 1 1} ");
00425     ret = 1;
00426   }
00427   else if ( maggs.mesh%max_node_grid != 0 ) {
00428     errtxt = runtime_error(128);
00429     ERROR_SPRINTF(errtxt, "{304 MEMD: meshsize is incompatible with number of processes.} ");
00430     ret = -1;
00431   }
00432   /*
00433   else if ( maggs_count_charged_particles() == 0 ) {
00434       errtxt = runtime_error(128);
00435       ERROR_SPRINTF(errtxt, "{30? MEMD: No charges in the system.} ");
00436       ret = -1;
00437   }
00438   */
00439   else if (cell_structure.type != CELL_STRUCTURE_DOMDEC) {
00440     errtxt = runtime_error(128);
00441     ERROR_SPRINTF(errtxt, "{305 MEMD requires domain-decomposition cellsystem.} ");
00442     ret = -1;
00443   }
00444   else if (dd.use_vList) {
00445     errtxt = runtime_error(128);
00446     ERROR_SPRINTF(errtxt, "{306 MEMD requires no Verlet Lists.} ");
00447     ret = -1;
00448   }
00449   /** check if speed of light parameter makes sense */
00450   else if (maggs.f_mass < 2. * time_step * time_step * maggs.a * maggs.a) {
00451     errtxt = runtime_error(128);
00452     ERROR_SPRINTF(errtxt, "{307 MEMD: Speed of light is set too high. Increase f_mass.} ");
00453     ret = -1;      
00454   }
00455   else if (maggs.a < skin) {
00456     errtxt = runtime_error(128);
00457     ERROR_SPRINTF(errtxt, "{308 MEMD: Skin should be smaller than MEMD mesh size.} ");
00458     ret = -1;
00459   }
00460 #ifdef EXTERNAL_FORCES
00461   /** check for fixed particles */
00462   for (int cellnumber = 0; cellnumber < local_cells.n; cellnumber++) {
00463     Cell *cell = local_cells.cell[cellnumber];
00464     Particle *p  = cell->part;
00465     int np = cell->n;
00466     for(int i = 0; i < np; i++) {
00467       if ( (p[i].p.q != 0.0) & p[i].l.ext_flag & COORDS_FIX_MASK) {
00468         errtxt = runtime_error(128);
00469         ERROR_SPRINTF(errtxt, "{309 MEMD does not work with fixed particles.} ");
00470         ret = -1;
00471       }
00472     }
00473   }  
00474 #endif
00475   
00476   return ret;
00477 }
00478 
00479 void maggs_compute_dipole_correction()
00480 {
00481 /*
00482 maggs.prefactor  = sqrt(4. * M_PI * maggs.bjerrum * temperature);
00483                  = sqrt(1/epsilon)
00484 maggs.pref2      = maggs.bjerrum * temperature;
00485                  = 1/4*pi*epsilon
00486 */
00487  
00488     /* Local dipole moment */
00489     double local_dipole_moment[3] = {0.0, 0.0, 0.0};
00490     /* Global dipole moment */
00491     double dipole_moment[3];
00492     
00493     int dim,c,np,i;
00494     Particle* p;
00495     Cell* cell;
00496 
00497     /* Compute the global dipole moment */
00498     for (c = 0; c < local_cells.n; c++) {
00499         cell = local_cells.cell[c];
00500         p  = cell->part;
00501         np = cell->n;
00502         for(i=0; i<np; i++)
00503             for (dim=0;dim<3;dim++)
00504                  local_dipole_moment[dim] += p[i].r.p[dim] * p[i].p.q;
00505     }
00506 
00507     MPI_Allreduce(local_dipole_moment, dipole_moment, 3, MPI_DOUBLE, MPI_SUM, comm_cart);
00508     
00509     double volume = box_l[0] * box_l[1] * box_l[2];
00510     double dipole_prefactor = 4.0*M_PI / (2.0*volume*(maggs.epsilon_infty + 1.0));
00511 
00512     /* apply correction to all particles: */
00513     for (c = 0; c < local_cells.n; c++) {
00514         cell = local_cells.cell[c];
00515         p  = cell->part;
00516         np = cell->n;
00517         for(i=0; i<np; i++)
00518             for (dim=0;dim<3;dim++)
00519                 p[i].f.f[dim] += p[i].p.q * dipole_prefactor * dipole_moment[dim];
00520     }
00521 }
00522 
00523 
00524 
00525 
00526 
00527 /*******************************/
00528 /****** setup everything: ******/
00529 /*******************************/
00530 
00531 int maggs_set_parameters(double bjerrum, double f_mass, int mesh, int finite_epsilon_flag, double epsilon_infty)
00532 {
00533   if (f_mass <=0.) {
00534     return -1;
00535   } 
00536   if(mesh<0) {
00537     return -2;
00538   }
00539         
00540   maggs.mesh           = mesh;
00541   maggs.finite_epsilon_flag  = finite_epsilon_flag;
00542   maggs.epsilon_infty  = epsilon_infty;
00543   maggs.bjerrum        = bjerrum;
00544   maggs.f_mass         = f_mass; 
00545   maggs.invsqrt_f_mass = 1./sqrt(f_mass);       
00546         
00547   mpi_bcast_coulomb_params();
00548         
00549   return ES_OK;
00550 }
00551 
00552 
00553 /** sets up nearest neighbors for each site in linear index */
00554 void maggs_setup_neighbors()
00555 {
00556   int ix = 0;
00557   int iy = 0;
00558   int iz = 0;
00559         
00560   int xsize = lparams.dim[0];
00561   int ysize = lparams.dim[1];
00562   int zsize = lparams.dim[2];
00563         
00564   int ixplus = 0;
00565   int ixminus = 0;
00566   int iyplus = 0;
00567   int iyminus = 0;
00568   int izplus = 0;
00569   int izminus = 0;
00570         
00571   int kount = 0;
00572         
00573   int kountxplus = 0;
00574   int kountxminus = 0;
00575   int kountyplus = 0;
00576   int kountyminus = 0;
00577   int kountzplus = 0;
00578   int kountzminus = 0;
00579         
00580   for (ix = 0; ix < xsize; ix++) 
00581     {
00582       ixplus  = ix + 1;
00583       ixminus = ix - 1;
00584       for(iy = 0; iy < ysize; iy ++)
00585         {
00586           iyplus  = iy + 1;
00587           iyminus = iy - 1;
00588           for(iz = 0; iz < zsize; iz ++)
00589             {
00590               izplus  = iz + 1;
00591               izminus = iz - 1;
00592                                 
00593               kount         = maggs_get_linear_index(ix,      iy,      iz,      lparams.dim);
00594               kountzplus    = maggs_get_linear_index(ix,      iy,      izplus,  lparams.dim);
00595               kountzminus   = maggs_get_linear_index(ix,      iy,      izminus, lparams.dim);
00596               kountyplus    = maggs_get_linear_index(ix,      iyplus,  iz,      lparams.dim);
00597               kountyminus   = maggs_get_linear_index(ix,      iyminus, iz,      lparams.dim);
00598               kountxplus    = maggs_get_linear_index(ixplus,  iy,      iz,      lparams.dim);
00599               kountxminus   = maggs_get_linear_index(ixminus, iy,      iz,      lparams.dim);
00600                                 
00601               if(ixminus < 0)     neighbor[kount][XMINUS] = -1;
00602               else                neighbor[kount][XMINUS] = kountxminus;
00603               if(ixplus >= xsize) neighbor[kount][XPLUS]  = -1;
00604               else                neighbor[kount][XPLUS]  = kountxplus;
00605                                 
00606               if(iyminus < 0)     neighbor[kount][YMINUS] = -1;
00607               else                neighbor[kount][YMINUS] = kountyminus;
00608               if(iyplus >= ysize) neighbor[kount][YPLUS]  = -1;
00609               else                neighbor[kount][YPLUS]  = kountyplus;
00610                                 
00611               if(izminus < 0)     neighbor[kount][ZMINUS] = -1;
00612               else                neighbor[kount][ZMINUS] = kountzminus;
00613               if(izplus >= zsize) neighbor[kount][ZPLUS]  = -1;
00614               else                neighbor[kount][ZPLUS]  = kountzplus;
00615             }
00616         }
00617     }
00618   return;
00619 }
00620 
00621 
00622 /** Set up lattice, calculate dimensions and lattice parameters
00623     Allocate memory for lattice sites and fields */
00624 void maggs_setup_local_lattice()
00625 {
00626   int i;
00627   int ix = 0;
00628   int iy = 0;
00629   int iz = 0;
00630   int linearindex = 0;
00631   int xyzcube;  
00632         
00633   xyzcube = 1;
00634   FOR3D(i) {
00635     /** inner left down grid point (global index) */
00636     lparams.inner_left_down[i] = (int)ceil(my_left[i]*maggs.inva); 
00637     /** inner up right grid point (global index) */
00638     lparams.inner_up_right[i] = (int)floor(my_right[i]*maggs.inva); 
00639     /** correct roundof errors at boundary */
00640     if(my_right[i]*maggs.inva-lparams.inner_up_right[i]<ROUND_ERROR_PREC) lparams.inner_up_right[i]--;
00641     if(1.0+my_left[i]*maggs.inva-lparams.inner_left_down[i]<ROUND_ERROR_PREC) lparams.inner_left_down[i]--;
00642     /** inner grid dimensions */
00643     lparams.size[i] = lparams.inner_up_right[i] - lparams.inner_left_down[i] + 1;
00644     /** spacial position of left down grid point */
00645     lparams.left_down_position[i] = my_left[i] - maggs.a;  
00646     /** spacial position of upper right grid point */
00647     lparams.upper_right_position[i] = my_right[i] + maggs.a;  
00648     /** left down margin */
00649     lparams.margin[i*2] = 1;
00650     /** up right margin */
00651     lparams.margin[(i*2)+1] = 1;
00652                 
00653     lparams.dim[i] = lparams.size[i] + lparams.margin[i*2] + lparams.margin[i*2+1];
00654     xyzcube *= lparams.dim[i];
00655     /** reduce inner grid indices from global to local */
00656     lparams.inner_left_down[i] = lparams.margin[i*2];
00657     lparams.inner_up_right[i] = lparams.margin[i*2]+lparams.size[i];
00658     lparams.halo_left_down[i] = 0;
00659     lparams.halo_upper_right[i] = lparams.inner_up_right[i];      
00660   }
00661         
00662         
00663   lparams.volume    = xyzcube;
00664   /** allocate memory for sites and neighbors */
00665   lattice  = (t_site*) malloc(xyzcube*sizeof(t_site));
00666   neighbor = (t_dirs*) malloc(xyzcube*sizeof(t_dirs));
00667         
00668   Bfield   = (double*) malloc(3*xyzcube*sizeof(double));
00669   Dfield   = (double*) malloc(3*xyzcube*sizeof(double));
00670         
00671   /** set up lattice sites */
00672   FORALL_SITES(ix, iy, iz) {
00673     linearindex = maggs_get_linear_index(ix, iy, iz, lparams.dim);
00674                 
00675     lattice[linearindex].r[0] = ix;
00676     lattice[linearindex].r[1] = iy;
00677     lattice[linearindex].r[2] = iz;
00678 
00679     FOR3D(i) {
00680       Bfield[3*linearindex+i]  = 0.;
00681       Dfield[3*linearindex+i]  = 0.;
00682     }
00683 
00684     lattice[linearindex].charge = 0.;
00685                 
00686     /* Here, we need a function to set PERMITTIVITY!!!!! */
00687     FOR3D(i) lattice[linearindex].permittivity[i] = 1.;
00688   }
00689         
00690   maggs_setup_neighbors();
00691 }
00692 
00693 
00694 /** sets up surface patches for all domains.
00695     @param surface_patch the local surface patch
00696 */
00697 void maggs_calc_surface_patches(t_surf_patch* surface_patch)
00698 {
00699   /* x=lparams.size[0] plane */
00700   surface_patch[0].offset   = lparams.dim[2]*lparams.dim[1]*lparams.size[0];    /*(size[0],0,0) point */
00701   surface_patch[0].doffset  = 0;                                             /*(0,0,0) point */
00702   surface_patch[0].stride   = lparams.dim[2]*lparams.dim[1];
00703   surface_patch[0].skip     = 0;
00704   surface_patch[0].nblocks  = 1;
00705   surface_patch[0].coord[0] = 2;
00706   surface_patch[0].coord[1] = 1;
00707   surface_patch[0].volume   = lparams.dim[2]*lparams.dim[1];
00708         
00709   /* x=1 plane */
00710   surface_patch[1].offset   = lparams.dim[2]*lparams.dim[1];                    /*(1,0,0) point */
00711   surface_patch[1].doffset  = lparams.dim[2]*lparams.dim[1]*lparams.inner_up_right[0];    /*(halo[0],0,0) point */
00712   surface_patch[1].stride   = lparams.dim[2]*lparams.dim[1];
00713   surface_patch[1].skip     = 0;
00714   surface_patch[1].nblocks  = 1;
00715   surface_patch[1].coord[0] = 2;
00716   surface_patch[1].coord[1] = 1;
00717   surface_patch[1].volume   = lparams.dim[2]*lparams.dim[1]; 
00718         
00719   /* y=lparams.size[1] plane */
00720   surface_patch[2].offset   = lparams.dim[2]*lparams.size[1];               /*(0,size[1],0) point */
00721   surface_patch[2].doffset  = 0;                                          /*(0,0,0) point */
00722   surface_patch[2].stride   = lparams.dim[2];
00723   surface_patch[2].skip     = lparams.dim[2]*lparams.dim[1];
00724   surface_patch[2].nblocks  = lparams.dim[0];  
00725   surface_patch[2].coord[0] = 2;
00726   surface_patch[2].coord[1] = 0;
00727   surface_patch[2].volume   = lparams.dim[2]*lparams.dim[0];
00728         
00729   /* y=1 plane */
00730   surface_patch[3].offset   = lparams.dim[2];                             /*(0,1,0) point */
00731   surface_patch[3].doffset  = lparams.dim[2]*lparams.inner_up_right[1];             /*(0,inner_up_right[1],0) point */
00732   surface_patch[3].stride   = lparams.dim[2];
00733   surface_patch[3].skip     = lparams.dim[2]*lparams.dim[1];
00734   surface_patch[3].nblocks  = lparams.dim[0];
00735   surface_patch[3].coord[0] = 2;
00736   surface_patch[3].coord[1] = 0;
00737   surface_patch[3].volume   = lparams.dim[2]*lparams.dim[0];
00738         
00739   /* z=lparams.size[2] plane */
00740   surface_patch[4].offset   = lparams.size[2];    /*(0,0,size[2]) point */
00741   surface_patch[4].doffset  = 0;                 /*(0,0,0) point */
00742   surface_patch[4].stride   = 1;
00743   surface_patch[4].skip     = lparams.dim[2];
00744   surface_patch[4].nblocks  = lparams.dim[0]*lparams.dim[1];
00745   surface_patch[4].coord[0] = 1;
00746   surface_patch[4].coord[1] = 0;
00747   surface_patch[4].volume   = lparams.dim[0]*lparams.dim[1];
00748         
00749   /* z=1 plane for z it must be higher*/
00750   surface_patch[5].offset   = 1;                   /*(0,0,1) point */
00751   surface_patch[5].doffset  = lparams.inner_up_right[2];     /*(0,0,inner_up_right[2]) point */
00752   surface_patch[5].stride   = 1;
00753   surface_patch[5].skip     = lparams.dim[2];
00754   surface_patch[5].nblocks  = lparams.dim[0]*lparams.dim[1];
00755   surface_patch[5].coord[0] = 1;
00756   surface_patch[5].coord[1] = 0;
00757   surface_patch[5].volume   = lparams.dim[0]*lparams.dim[1];
00758 }
00759 
00760 
00761 /** sets up MPI communications for domain surfaces */
00762 void maggs_prepare_surface_planes(int dim, MPI_Datatype *xy, MPI_Datatype *xz, MPI_Datatype *yz, 
00763                                   t_surf_patch *surface_patch)
00764 {
00765   MPI_Type_contiguous(dim*surface_patch[0].stride*sizeof(double),MPI_BYTE,yz);  
00766   MPI_Type_commit(yz);
00767   MPI_Type_vector(surface_patch[4].nblocks, dim*surface_patch[4].stride,
00768                   dim*surface_patch[4].skip, MPI_DOUBLE,xy);
00769   MPI_Type_commit(xy); 
00770   MPI_Type_vector(surface_patch[2].nblocks, dim*surface_patch[2].stride,
00771                   dim*surface_patch[2].skip, MPI_DOUBLE,xz);
00772   MPI_Type_commit(xz);
00773 }
00774 
00775 
00776 
00777 
00778 
00779 
00780 
00781 /*****************************************/
00782 /****** Surface patch communication ******/
00783 /*****************************************/
00784 
00785 /** MPI communication of surface region.
00786     works for D- and B-fields.
00787     @param field   Field to communicate. Can be B- or D-field.
00788     @param dim     Dimension in which to communicate
00789     @param e_equil Flag if field is already equilibated
00790 */
00791 void maggs_exchange_surface_patch(double *field, int dim, int e_equil)
00792 {
00793   static int init = 1;
00794   static MPI_Datatype xyPlane,xzPlane,yzPlane; 
00795   static MPI_Datatype xzPlane2D, xyPlane2D, yzPlane2D;
00796   /*  int coord[2]; */
00797   int l, s_dir, r_dir;
00798   /*  int pos=0; */
00799   MPI_Status status[2];
00800   MPI_Request request[]={MPI_REQUEST_NULL, MPI_REQUEST_NULL};
00801   int offset, doffset, skip, stride, nblocks;
00802   /** surface_patch */
00803   static t_surf_patch  surface_patch[6];
00804         
00805   if(init) {
00806     MPI_Datatype xz_plaq, oneslice;
00807                 
00808     maggs_calc_surface_patches(surface_patch);
00809     maggs_prepare_surface_planes(dim, &xyPlane, &xzPlane, &yzPlane, surface_patch);
00810                 
00811     MPI_Type_vector(surface_patch[0].stride, 2, 3, MPI_DOUBLE,&yzPlane2D);    
00812     MPI_Type_commit(&yzPlane2D);
00813                 
00814     /* create data type for xz plaquette */
00815     MPI_Type_hvector(2,1*sizeof(double),2*sizeof(double), MPI_BYTE, &xz_plaq);
00816     /* create data type for a 1D section */
00817     MPI_Type_contiguous(surface_patch[2].stride, xz_plaq, &oneslice); 
00818     /* create data type for a 2D xz plane */
00819     MPI_Type_hvector(surface_patch[2].nblocks, 1, dim*surface_patch[2].skip*sizeof(double), oneslice, &xzPlane2D);
00820     MPI_Type_commit(&xzPlane2D);    
00821     /* create data type for a 2D xy plane */
00822     MPI_Type_vector(surface_patch[4].nblocks, 2, dim*surface_patch[4].skip, MPI_DOUBLE, &xyPlane2D);
00823     MPI_Type_commit(&xyPlane2D); 
00824                 
00825     init = 0;
00826   }
00827         
00828                 
00829   /** direction loop */
00830   for(s_dir=0; s_dir < 6; s_dir++) { 
00831     offset = dim * surface_patch[s_dir].offset;
00832     doffset= dim * surface_patch[s_dir].doffset;
00833                 
00834     if(s_dir%2==0) r_dir = s_dir+1;
00835     else           r_dir = s_dir-1;
00836     /** pack send halo-plane data */
00837     if(node_neighbors[s_dir] != this_node) {
00838       /** communication */
00839       switch(s_dir) {
00840       case 0 :
00841       case 1 :
00842         if(e_equil || dim == 1) {
00843           MPI_Irecv (&field[doffset],1,yzPlane,node_neighbors[s_dir],REQ_MAGGS_SPREAD,comm_cart,&request[0]);
00844           MPI_Isend(&field[offset],1,yzPlane,node_neighbors[r_dir],REQ_MAGGS_SPREAD,comm_cart,&request[1]);
00845         }
00846         else {
00847           MPI_Irecv (&field[doffset+1],1,yzPlane2D,node_neighbors[s_dir],REQ_MAGGS_SPREAD,comm_cart,&request[0]);
00848           MPI_Isend(&field[offset+1],1,yzPlane2D,node_neighbors[r_dir],REQ_MAGGS_SPREAD,comm_cart,&request[1]);
00849         }         
00850                                         
00851         MPI_Waitall(2,request,status);
00852         break;
00853       case 2 :
00854       case 3 :
00855         if(e_equil || dim == 1) {
00856           MPI_Irecv (&field[doffset],1,xzPlane,node_neighbors[s_dir],REQ_MAGGS_SPREAD,comm_cart,&request[0]);
00857           MPI_Isend(&field[offset],1,xzPlane,node_neighbors[r_dir],REQ_MAGGS_SPREAD,comm_cart,&request[1]);
00858         }
00859         else {
00860           MPI_Irecv (&field[doffset],1,xzPlane2D,node_neighbors[s_dir],REQ_MAGGS_SPREAD,comm_cart,&request[0]);
00861           MPI_Isend(&field[offset],1,xzPlane2D,node_neighbors[r_dir],REQ_MAGGS_SPREAD,comm_cart,&request[1]);
00862         }         
00863         MPI_Waitall(2,request,status);
00864         break;
00865       case 4 :
00866       case 5 : 
00867         if(e_equil || dim == 1) {
00868           MPI_Irecv (&field[doffset],1,xyPlane,node_neighbors[s_dir],REQ_MAGGS_SPREAD,comm_cart,&request[0]);
00869           MPI_Isend(&field[offset],1,xyPlane,node_neighbors[r_dir],REQ_MAGGS_SPREAD,comm_cart,&request[1]);
00870         }
00871         else {
00872           MPI_Irecv (&field[doffset],1,xyPlane2D,node_neighbors[s_dir],REQ_MAGGS_SPREAD,comm_cart,&request[0]);
00873           MPI_Isend(&field[offset],1,xyPlane2D,node_neighbors[r_dir],REQ_MAGGS_SPREAD,comm_cart,&request[1]);
00874         }
00875         MPI_Waitall(2,request,status);
00876         break;
00877       }
00878     }
00879 
00880     else {
00881       /** copy locally */
00882       skip    = dim * surface_patch[s_dir].skip;
00883       stride  = dim * surface_patch[s_dir].stride * sizeof(double);
00884       nblocks = surface_patch[s_dir].nblocks;
00885                         
00886       for(l=0; l<nblocks; l++){
00887         memcpy(&(field[doffset]), &(field[offset]), stride);
00888         offset  += skip;
00889         doffset += skip;
00890       }
00891                         
00892     }
00893   }
00894 }
00895 
00896 
00897 
00898 
00899 
00900 
00901 
00902 /*********************************************/
00903 /****** interpolate charges on lattice: ******/
00904 /*********************************************/
00905 
00906 /** Interpolation function in one dimension.
00907     Currently only linear interpolation conserves charge.
00908     @return interpolation value
00909     @param x relative position of particle
00910 */
00911 double maggs_interpol1D(double x)
00912 {       
00913   return x;
00914   /* Cosine interpolation would be: */
00915   /* return sqr(sin(M_PI_2*x)); */
00916 }
00917 
00918 
00919 /** Does the actual interpolating calculation.
00920     @param first  3dim-array lattice position,
00921     @param rel    3dim-array relative position in cube,
00922     @param q      charge to interpolate
00923 */
00924 void maggs_interpolate_charge(int *first, double *rel, double q)
00925 {
00926   int i, k, l, m, index, temp_ind;
00927   int help_index[3];
00928   double temp;
00929   double help[SPACE_DIM];
00930         
00931   FOR3D(i) help[i] = 1. - rel[i];     /** relative pos. w.r.t. first */
00932   //  printf("first: %d %d %d\n", first[0], first[1], first[2]);
00933   /** calculate charges at each vertex */
00934   index = maggs_get_linear_index(first[0],first[1],first[2],lparams.dim);
00935         
00936   FOR3D(i) {
00937     temp_ind = neighbor[index][i];
00938     if(temp_ind == NOWHERE) help_index[i] = lparams.volume; /* force huge index */
00939     else { /* incr. for x-neighbor */
00940       help_index[i] = maggs_get_offset(lattice[neighbor[index][i]].r[i], first[i], i, lparams.dim);
00941     }
00942                 
00943   }
00944         
00945   for(k=0;k<2;k++){   /* jumps from x- to x+ */
00946     for(l=0;l<2;l++){  /* jumps from y- to y+ */
00947       for(m=0;m<2;m++){ /* jumps from z- to z+ */      
00948         if(index < lparams.volume) {
00949           temp = q;
00950           FOR3D(i) temp *= maggs_interpol1D(help[i]);
00951           lattice[index].charge += temp;
00952         }
00953                                 
00954         index+=help_index[2];
00955         help[2]=1.-help[2];
00956         help_index[2]=-help_index[2];
00957       }
00958       index+=help_index[1];
00959       help[1]=1.-help[1];
00960       help_index[1]=-help_index[1];
00961     }
00962     index+=help_index[0];
00963     help[0]=1.-help[0];
00964     help_index[0]=-help_index[0];
00965   }             
00966 }
00967 
00968 /** add charges from ghost cells to lattice sites. */
00969 void maggs_accumulate_charge_from_ghosts()
00970 {
00971   Cell *cell;
00972   Particle* p;
00973   int i, c, d;
00974   int np;
00975   int flag_inner=0;
00976   int first[SPACE_DIM];
00977   double q;
00978   double pos[SPACE_DIM], rel[SPACE_DIM];
00979         
00980   /** loop over ghost cells */
00981   for (c = 0; c < ghost_cells.n; c++) {
00982     cell = ghost_cells.cell[c];
00983     p  = cell->part;
00984     np = cell->n;
00985     for(i = 0; i < np; i++) {
00986       if( (q=p[i].p.q) != 0.0 ) {
00987         flag_inner=1;
00988         FOR3D(d) {
00989           if(p[i].r.p[d]<lparams.left_down_position[d]||p[i].r.p[d]>=lparams.upper_right_position[d])
00990             {flag_inner=0; break;}
00991         }
00992       }
00993       if(flag_inner) {
00994         FOR3D(d) {
00995           pos[d]        = (p[i].r.p[d] - lparams.left_down_position[d])* maggs.inva;
00996           first[d]      = (int) pos[d];
00997           rel[d]        = pos[d] - first[d];
00998         }
00999         //              fprintf(stderr,"pos: %f %f %f\n", p[i].r.p[0], p[i].r.p[1], p[i].r.p[2]);
01000         maggs_interpolate_charge(first, rel, q);
01001       }
01002     }      
01003   }  
01004         
01005 }
01006 
01007 
01008 /** finds current lattice site of each particle.
01009     calculates charge interpolation on cube. */
01010 void maggs_distribute_particle_charges()
01011 {       
01012   Cell *cell;
01013   Particle* p;
01014   int i, c, d;
01015   int np;
01016   int first[SPACE_DIM];
01017   double q;
01018   double pos[SPACE_DIM], rel[SPACE_DIM];
01019                 
01020   for(i=0;i<lparams.volume;i++) lattice[i].charge = 0.;
01021   /** === charge assignment === */ 
01022   /** loop over inner cells */
01023   for (c = 0; c < local_cells.n; c++) {
01024     cell = local_cells.cell[c];
01025     p  = cell->part;
01026     np = cell->n;
01027     for(i = 0; i < np; i++) {
01028       if( (q=p[i].p.q) != 0.0 ) {
01029         FOR3D(d) {
01030           pos[d]        = (p[i].r.p[d] - lparams.left_down_position[d])* maggs.inva;
01031           first[d]      = (int) pos[d];
01032           rel[d]        = pos[d] - first[d];
01033         }
01034         maggs_interpolate_charge(first, rel, q);
01035       }
01036     }      
01037   }
01038   maggs_accumulate_charge_from_ghosts();        
01039 }
01040         
01041 /** Does the actual calculation of the gradient. Parameters:
01042     @param rel  3dim-array of relative position in cube,
01043     @param q    charge,
01044     @param grad huge gradient array to write into
01045 */
01046 void maggs_calc_charge_gradients(double *rel, double q, double *grad)
01047 {
01048   int i,l,m,index, d;
01049   double help[3];
01050   int dir1, dir2;
01051         
01052   FOR3D(i) help[i] = 1. - rel[i];     /* relative pos. w.r.t. x_int */
01053         
01054   index = 0;
01055         
01056   FOR3D(d) {
01057     maggs_calc_directions(d, &dir1, &dir2);
01058     for(l=0;l<2;l++){  /* jumps from dir2- to dir2+ */
01059       for(m=0;m<2;m++){ /* jumps from dir1- to dir1+ */          
01060                                 
01061         /* with q!!! */
01062         grad[index] = - q * help[dir1] * help[dir2];
01063                                 
01064         index++;
01065         help[dir1] = 1.-help[dir1];
01066       }
01067       help[dir2] = 1.-help[dir2];
01068     }
01069   }
01070 }
01071 
01072 
01073 
01074 /** finds correct lattice cube for particles.
01075     calculates charge gradients on this cube.
01076     @param grad gradient array to write into
01077 */
01078 void maggs_update_charge_gradients(double *grad)
01079 {
01080   Cell *cell;
01081   Particle* p;
01082   int i, c, d, ip;
01083   int np;
01084   int first[SPACE_DIM];
01085   double q;
01086   double pos[SPACE_DIM], rel[SPACE_DIM];
01087   
01088   /* === grad assignment for real particles and self-force === */ 
01089   ip = 0;
01090   for (c = 0; c < local_cells.n; c++) {
01091     cell = local_cells.cell[c];
01092     p  = cell->part;
01093     np = cell->n;
01094     for(i = 0; i < np; i++) {
01095       if( (q=p[i].p.q) != 0.0 ) {
01096         FOR3D(d) {
01097           pos[d]        = (p[i].r.p[d] - lparams.left_down_position[d])* maggs.inva;
01098           first[d]      = (int) pos[d];
01099           rel[d]        = pos[d] - first[d];
01100         }
01101         maggs_calc_charge_gradients(rel, q, &grad[ip]);
01102         ip += 12;
01103       }
01104     }      
01105   }  
01106   
01107 } 
01108 
01109 
01110 
01111 
01112 
01113 
01114 
01115 /***************************************/
01116 /****** initialization procedure: ******/
01117 /***************************************/
01118 
01119 /** Calculate the self energy coefficients for the system, if
01120     corrected with Lattice Green's function. */
01121 void calc_self_energy_coeffs()
01122 {
01123   double factor, prefac;
01124   int px = 0;
01125   int py = 0;
01126   int pz = 0;
01127   int i, j, k, l, m, index;
01128   double sx = 0.;
01129   double sy = 0.;
01130   double sz = 0.;
01131   double sxy = 0.;
01132   double sxyz = 0.;
01133   double nomx, nomy, nomz;
01134   double inva = maggs.inva;
01135   double invasq = inva*inva;
01136   double n[8][SPACE_DIM];// = {{0.,0.,0.}, {1.,0.,0.}, {0.,1.,0.}, {1.,1.,0.}, 
01137   // {0.,0.,1.}, {1.,0.,1.}, {0.,1.,1.}, {1.,1.,1.}};
01138 
01139   m=0;
01140   l=0;
01141   index = 0;
01142   for(i=0;i<2;i++)
01143     for(j=0;j<2;j++)
01144       for(k=0;k<2;k++) {
01145         n[index][2] = m; 
01146         n[index][1] = l; 
01147         n[index][0] = k; 
01148         index++;
01149   }
01150 
01151   factor = M_PI / maggs.mesh;
01152   prefac = 1. / maggs.mesh;
01153   prefac = 0.5 * prefac * prefac * prefac * SQR(maggs.prefactor);
01154   
01155   for(i=0;i<8;i++) 
01156     {
01157       for(j=0;j<8;j++) 
01158         {
01159           maggs.alpha[i][j] = 0.;
01160           for(px = 0; px < maggs.mesh; ++px)
01161             {
01162               sx = sin( factor * px );
01163               sx = sx * sx;
01164               nomx = 2.*factor*px*(n[i][0] - n[j][0]);
01165               for(py = 0; py < maggs.mesh; ++py)
01166                 {
01167                   sy = sin( factor * py );
01168                   sy = sy * sy; 
01169                   nomy = 2.*factor*py*(n[i][1] - n[j][1]);
01170                   sxy = sx + sy;
01171                   for(pz = 0; pz < maggs.mesh; ++pz)
01172                     {
01173                       sz = sin( factor * pz );
01174                       sz = sz * sz;
01175                       nomz = 2.*factor*pz*(n[i][2] - n[j][2]);
01176                       sxyz = sxy + sz;
01177                       sxyz *= 4.;
01178                       if(sxyz > 0)
01179                         {
01180                           maggs.alpha[i][j] += cos(nomx + nomy + nomz) / sxyz;
01181                         }
01182                     }
01183                 }
01184             }
01185           /* invasq is needed for the calculation of forces */
01186           maggs.alpha[i][j] = invasq * prefac * maggs.alpha[i][j];
01187         }
01188     }
01189                       
01190 }
01191 
01192 /** For energy minimization:
01193     @return maximum of curl(E) from all sites.
01194     May also be used to verify constraint surface condition. */
01195 double maggs_check_curl_E()
01196 {
01197   int i, ix, iy, iz;
01198   double curl, maxcurl, gmaxcurl;
01199   int* anchor_neighb;
01200         
01201   maxcurl = 0.;
01202         
01203   FORALL_INNER_SITES(ix, iy, iz) {
01204     i = maggs_get_linear_index(ix, iy, iz, lparams.dim); 
01205     anchor_neighb = neighbor[i];
01206     curl = Dfield[3*i] + Dfield[3*anchor_neighb[0]+1] 
01207       - Dfield[3*anchor_neighb[1]] - Dfield[3*i+1];
01208     curl *= maggs.inva;
01209     if(fabs(curl)>maxcurl) maxcurl = fabs(curl);
01210     curl = Dfield[3*i+2] + Dfield[3*anchor_neighb[2]] 
01211       - Dfield[3*anchor_neighb[0]+2] - Dfield[3*i];
01212     curl *= maggs.inva;
01213     if(fabs(curl)>maxcurl) maxcurl = fabs(curl);
01214     curl = Dfield[3*i+1] + Dfield[3*anchor_neighb[1]+2] 
01215       - Dfield[3*anchor_neighb[2]+1] - Dfield[3*i+2];
01216     curl *= maggs.inva;
01217     if(fabs(curl)>maxcurl) maxcurl = fabs(curl);
01218   }
01219   MPI_Allreduce(&maxcurl,&gmaxcurl,1,MPI_DOUBLE,MPI_MAX,comm_cart);  
01220   return gmaxcurl;
01221 }
01222 
01223 /** If curl(E) is not zero, update plaquette with corrections.
01224     @param i index of the current lattice site
01225     @param n coordinate, is the normal direction to the plaquette
01226 */
01227 void maggs_perform_rot_move_inplane(int i, int n)
01228 {
01229   int mue, nue;
01230   int * anchor_neighb;
01231   double delta;
01232   double ROUND_ERR = 0.01*ROUND_ERROR_PREC;
01233         
01234   mue = 0; nue = 0;
01235         
01236   switch(n) {
01237   case 0 :
01238     mue = 1;
01239     nue = 2;
01240     break;
01241                         
01242   case 1 :
01243     mue = 2;
01244     nue = 0;
01245     break;
01246   case 2 :
01247     mue = 0;
01248     nue = 1;
01249     break;
01250   }
01251         
01252   anchor_neighb = &neighbor[i][0];
01253         
01254   delta = Dfield[3*i+mue] + Dfield[3*anchor_neighb[mue]+nue] 
01255     - Dfield[3*anchor_neighb[nue]+mue] - Dfield[3*i+nue];
01256   if(fabs(delta)>=ROUND_ERR) {
01257     delta = -delta/4.; 
01258     maggs_update_plaquette(mue, nue, anchor_neighb, i, delta);
01259   }
01260 }
01261 
01262 
01263 /** For energy minimization:
01264     Relax B-fields in all directions.*/
01265 void maggs_minimize_transverse_field()
01266 {
01267   int k, l, m;
01268   int i, d;
01269   int ind_i, ind_j;
01270   int size[2]={0,0};
01271   int index = -1; /* force allocation error */
01272         
01273   FOR3D(d) {
01274     switch(d) {
01275     case 0 :
01276       size[0] = lparams.size[2];
01277       size[1] = lparams.size[1];
01278       break;
01279     case 1 :
01280       size[0] = lparams.size[2];
01281       size[1] = lparams.size[0];
01282       break;
01283     case 2 :
01284       size[0] = lparams.size[1];
01285       size[1] = lparams.size[0];
01286       break;
01287     }
01288     for(i=0;i<2;i++) {
01289       /* at first even sites (i==0) then odd */
01290       for(k=1;k<=lparams.size[d];k++) {
01291         /* update every plane in direction d */
01292         ind_i=0;
01293         for(l=0; l<=size[1]; l++){
01294           ind_j=0;
01295           for(m=0;m<=size[0]; m++) {
01296             switch(d) {
01297             case 0 :
01298               index=maggs_get_linear_index(k,l,m,lparams.dim);
01299               break;
01300             case 1 :
01301               index=maggs_get_linear_index(l,k,m,lparams.dim); 
01302               break;
01303             case 2 :
01304               index=maggs_get_linear_index(l,m,k,lparams.dim); 
01305               break;
01306             }
01307             if((ind_i+ind_j)%2==i)
01308               maggs_perform_rot_move_inplane(index, d);
01309             ind_j++;
01310           }
01311           ind_i++;
01312         }   
01313       }
01314       /* update boundaries - update halo regions */
01315       maggs_exchange_surface_patch(Dfield, 3, 0);
01316     }
01317   }
01318 }
01319 
01320 
01321 /** calculates initial electric field configuration.
01322     currently uses simple and slow method of plaquettes and links.
01323     energy minimization takes up lots of time. */
01324 void maggs_calc_init_e_field()
01325 {
01326   double localqy, localqz;
01327   double qplane, qline;
01328   int    i, k, ix, iy, iz;
01329   int index = 0;
01330   double invasq, tmp_field=0.0;
01331   double sqrE, gsqrE, gavgEx, gavgEy, gavgEz;
01332   double qz, qy, qx, avgEx, avgEy, avgEz;
01333   double Eall[SPACE_DIM], gEall[SPACE_DIM];
01334   double maxcurl;
01335   MPI_Status status;
01336   MPI_Comm zplane, yline;
01337   int color, rank, dim;
01338         
01339   MAGGS_TRACE(fprintf(stderr,"%d:Initialize field\n",this_node));
01340         
01341   invasq = maggs.inva*maggs.inva;
01342         
01343   /* sort particles for the calculation of initial charge distribution */
01344   MAGGS_TRACE(fprintf(stderr,"%d:Sorting particles...\n",this_node));
01345         
01346   cells_resort_particles(CELL_GLOBAL_EXCHANGE);
01347   /*  fprintf(stderr, "done\n"); */
01348   maggs_distribute_particle_charges();
01349         
01350   dim = node_grid[1]*node_grid[0];
01351   color = node_pos[2];
01352   rank  = this_node%dim;
01353   MPI_Comm_split(comm_cart, color, rank, &zplane);
01354   color = node_pos[1];
01355   rank  = rank%node_grid[0];
01356   MPI_Comm_split(zplane, color, rank, &yline);
01357         
01358   /* calculate initial solution of Poisson equation */
01359         
01360   /* CAUTION: the indexing of the neighbor nodes in Espresso
01361    *  starts from x left neighbor node
01362    */
01363         
01364   /* get process coordinates */
01365   if(node_pos[2]!= 0) {
01366     MPI_Recv(&tmp_field, 1, MPI_DOUBLE, node_neighbors[4], REQ_MAGGS_EQUIL, comm_cart, &status);
01367     for(iy=lparams.inner_left_down[1];iy<lparams.inner_up_right[1];iy++) {
01368       for(ix=lparams.inner_left_down[0];ix<lparams.inner_up_right[0];ix++) {  
01369         index = maggs_get_linear_index(ix, iy, lparams.inner_left_down[2], lparams.dim);
01370         Dfield[3*neighbor[index][ZMINUS]+ZPLUS] = tmp_field;
01371       }
01372     }
01373   }
01374 
01375   localqz = 0.;
01376   for(iz=lparams.inner_left_down[2];iz<lparams.inner_up_right[2];iz++) { /* loop over z-planes */
01377     localqz = 0.;
01378     for(iy=lparams.inner_left_down[1];iy<lparams.inner_up_right[1];iy++) {
01379       for(ix=lparams.inner_left_down[0];ix<lparams.inner_up_right[0];ix++) {  
01380         index = maggs_get_linear_index(ix, iy, iz, lparams.dim);
01381         /* Sum over the charge of all sides in z-plane */
01382         localqz += lattice[index].charge / lattice[index].permittivity[2];
01383       }
01384     } 
01385 
01386     MPI_Allreduce(&localqz, &qz, 1, MPI_DOUBLE, MPI_SUM, zplane);
01387     qz = qz/(maggs.mesh*maggs.mesh);
01388     qplane = qz*maggs.prefactor*invasq;
01389     /*    if(fabs(qplane) >= 0.01*ROUND_ERROR_PREC) { */
01390     for(iy=lparams.inner_left_down[1];iy<lparams.inner_up_right[1];iy++) {
01391       for(ix=lparams.inner_left_down[0];ix<lparams.inner_up_right[0];ix++) {  
01392         index = maggs_get_linear_index(ix, iy, iz, lparams.dim);
01393         Dfield[3*index+ZPLUS]  = Dfield[3*neighbor[index][ZMINUS]+ZPLUS] + qplane;
01394         /*          + qz*maggs.prefactor*invasq; */
01395       }
01396     }
01397     /*    } */
01398     if(iz>=lparams.inner_up_right[2]-1) {
01399       if (node_pos[2]<node_grid[2]-1) {
01400         if(node_grid[2]>1) {
01401           MPI_Send(&Dfield[3*index+ZPLUS], 1, MPI_DOUBLE, node_neighbors[5], REQ_MAGGS_EQUIL, comm_cart); 
01402         }
01403       }
01404       else 
01405         if (fabs(Dfield[3*index+ZPLUS]) > 100.*ROUND_ERROR_PREC) {
01406           fprintf(stderr, "%d: Error in the calculation of Ez(%d,%d,%d)=%f!!\n", 
01407                   this_node,lattice[index].r[0], lattice[index].r[1], lattice[index].r[2],
01408                   Dfield[3*index+ZPLUS]);
01409           fflush(stderr);
01410         }
01411     }
01412 
01413     if(node_pos[1]!= 0) {
01414       MPI_Recv(&tmp_field, 1, MPI_DOUBLE, node_neighbors[2], REQ_MAGGS_EQUIL, comm_cart, &status);
01415       for(ix=lparams.inner_left_down[0];ix<lparams.inner_up_right[0];ix++) {  
01416         index = maggs_get_linear_index(ix, lparams.inner_left_down[1], iz, lparams.dim);
01417         Dfield[3*neighbor[index][YMINUS]+YPLUS] = tmp_field;
01418       }
01419     }
01420                 
01421     for(iy=lparams.inner_left_down[1];iy<lparams.inner_up_right[1];iy++) {
01422       localqy = 0.;
01423       for(ix=lparams.inner_left_down[0];ix<lparams.inner_up_right[0];ix++) {  
01424         index = maggs_get_linear_index(ix, iy, iz, lparams.dim);
01425         localqy += lattice[index].charge / lattice[index].permittivity[1];
01426       }
01427 
01428       MPI_Allreduce(&localqy, &qy, 1, MPI_DOUBLE, MPI_SUM, yline);
01429                         
01430       qy = qy/maggs.mesh;
01431       qline = (qy-qz)*maggs.prefactor*invasq;
01432       /*      if(fabs(qy-qz)>=ROUND_ERROR_PREC) { */
01433       for(ix=lparams.inner_left_down[0];ix<lparams.inner_up_right[0];ix++) {  
01434         index = maggs_get_linear_index(ix, iy, iz, lparams.dim);
01435         Dfield[3*index+YPLUS]  = Dfield[3*neighbor[index][YMINUS]+YPLUS] + qline;
01436         /*          (qy-qz)*maggs.prefactor*invasq; */
01437       }
01438       /*      } */
01439                         
01440       if(iy>=lparams.inner_up_right[1]-1) {
01441         if(node_pos[1] < node_grid[1]-1) {
01442           if (node_grid[1]>1)
01443             MPI_Send(&Dfield[3*index+YPLUS], 1, MPI_DOUBLE, node_neighbors[3], REQ_MAGGS_EQUIL, comm_cart); 
01444         }
01445         else
01446           if (fabs(Dfield[3*index+YPLUS]) > 100.*ROUND_ERROR_PREC)
01447             fprintf(stderr, "%d: Error in the calculation of Ey(%d,%d,%d)=%f!!\n",
01448                     this_node, lattice[index].r[0], lattice[index].r[1], lattice[index].r[2],
01449                     Dfield[3*index+YPLUS]);       
01450       }
01451                         
01452       if(node_pos[0]!= 0) {
01453         MPI_Recv(&tmp_field, 1, MPI_DOUBLE, node_neighbors[0], REQ_MAGGS_EQUIL, comm_cart, &status);
01454         index = maggs_get_linear_index(lparams.inner_left_down[0], iy, iz, lparams.dim);
01455         Dfield[3*neighbor[index][XMINUS]+XPLUS] = tmp_field;
01456       }
01457                         
01458       for(ix=lparams.inner_left_down[0];ix<lparams.inner_up_right[0];ix++) {  
01459         index = maggs_get_linear_index(ix, iy, iz, lparams.dim);
01460         qx = lattice[index].charge / lattice[index].permittivity[0]; 
01461         Dfield[3*index+XPLUS] = Dfield[3*neighbor[index][XMINUS]+XPLUS] + 
01462           (qx-qy)*maggs.prefactor*invasq;
01463       }
01464                         
01465       if(ix>=lparams.inner_up_right[0]-1) {
01466         if(node_pos[0] < node_grid[0]-1) {
01467           if(node_grid[0]>1)
01468             MPI_Send(&Dfield[3*index+XPLUS], 1, MPI_DOUBLE, node_neighbors[1], REQ_MAGGS_EQUIL, comm_cart); 
01469         }
01470         else
01471           if (fabs(Dfield[3*index+XPLUS]) > 100.*ROUND_ERROR_PREC)
01472             fprintf(stderr, "%d: Error in the calculation of Ex(%d,%d,%d)=%f!!\n",
01473                     this_node, lattice[index].r[0], lattice[index].r[1], lattice[index].r[2],
01474                     Dfield[3*index+XPLUS]);       
01475       }
01476     }    /*** loop over iy */
01477   }
01478         
01479   /* exchange halo-surfaces */
01480   maggs_exchange_surface_patch(Dfield, 3, 1); 
01481         
01482   avgEz = 0.;
01483   for(iz=lparams.inner_left_down[2];iz<lparams.inner_up_right[2];iz++) {
01484     index = maggs_get_linear_index(lparams.inner_left_down[0], lparams.inner_left_down[1], iz, lparams.dim);
01485     avgEz += Dfield[3*index+ZPLUS];
01486   }
01487         
01488         
01489   /*  MPI_Barrier(comm_cart); */
01490   MPI_Allreduce(&avgEz,&gavgEz,1,MPI_DOUBLE,MPI_SUM,comm_cart);
01491   gavgEz = gavgEz/(maggs.mesh*node_grid[0]*node_grid[1]);
01492         
01493   FORALL_INNER_SITES(ix, iy,iz) {
01494     index = maggs_get_linear_index(ix, iy, iz, lparams.dim);
01495     Dfield[3*index+ZPLUS] -= gavgEz;
01496   }
01497         
01498   for(iz = lparams.inner_left_down[2];iz<lparams.inner_up_right[2];iz++) {
01499     avgEy = 0.;  
01500     for(iy = lparams.inner_left_down[1];iy<lparams.inner_up_right[1];iy++) {
01501       index = maggs_get_linear_index(lparams.inner_left_down[0], iy, iz, lparams.dim);
01502       avgEy += Dfield[3*index+YPLUS];
01503     }    
01504                 
01505     MPI_Allreduce(&avgEy, &gavgEy, 1, MPI_DOUBLE, MPI_SUM, zplane);
01506     gavgEy = gavgEy/(maggs.mesh*node_grid[0]);
01507                 
01508     for(iy=lparams.inner_left_down[1];iy<lparams.inner_up_right[1];iy++) {
01509       for(ix=lparams.inner_left_down[0];ix<lparams.inner_up_right[0];ix++)  
01510         Dfield[3*maggs_get_linear_index(ix, iy, iz, lparams.dim)+YPLUS] -= gavgEy;
01511     }
01512   }
01513         
01514   for(iz=lparams.inner_left_down[2];iz<lparams.inner_up_right[2];iz++) {
01515     for(iy=lparams.inner_left_down[1];iy<lparams.inner_up_right[1];iy++) {
01516       avgEx = 0.;
01517       for(ix=lparams.inner_left_down[0];ix<lparams.inner_up_right[0];ix++) {
01518         avgEx += Dfield[3*maggs_get_linear_index(ix, iy, iz, lparams.dim)+XPLUS];
01519       }
01520                         
01521       MPI_Allreduce(&avgEx, &gavgEx, 1, MPI_DOUBLE, MPI_SUM, yline);
01522       gavgEx = gavgEx/maggs.mesh;
01523                         
01524       for(ix=lparams.inner_left_down[0];ix<lparams.inner_up_right[0];ix++)  
01525         Dfield[3*maggs_get_linear_index(ix, iy, iz, lparams.dim)+XPLUS] -= gavgEx;
01526     }
01527   }
01528         
01529         
01530   /* exchange halo-surfaces */
01531   maggs_exchange_surface_patch(Dfield, 3, 1);
01532         
01533 
01534         
01535   MPI_Comm_free(&zplane);
01536   MPI_Comm_free(&yline);
01537         
01538         
01539         
01540         
01541   /* iterative procedure of energy minimization */
01542 
01543   sqrE = 0.;
01544   FORALL_INNER_SITES(ix, iy, iz) {
01545     i = maggs_get_linear_index(ix, iy, iz, lparams.dim);
01546     FOR3D(k) sqrE += SQR(Dfield[3*i+k]);
01547   }
01548 
01549   MPI_Allreduce(&sqrE,&gsqrE,1,MPI_DOUBLE,MPI_SUM,comm_cart); 
01550   gsqrE = gsqrE/(SPACE_DIM*maggs.mesh*maggs.mesh*maggs.mesh);  
01551 
01552 #ifdef MAGGS_DEBUG
01553   int iteration; 
01554   if(!this_node) iteration = 0;
01555 #endif
01556   do {
01557 #ifdef MAGGS_DEBUG
01558     double goldE = gsqrE;
01559 #endif
01560     sqrE = 0.;
01561     maggs_minimize_transverse_field();
01562                 
01563     FORALL_INNER_SITES(ix, iy, iz) {
01564       i = maggs_get_linear_index(ix, iy, iz, lparams.dim);
01565       FOR3D(k) sqrE += SQR(Dfield[3*i+k]);
01566     }
01567     MPI_Allreduce(&sqrE,&gsqrE,1,MPI_DOUBLE,MPI_SUM,comm_cart); 
01568     gsqrE = gsqrE/(SPACE_DIM*maggs.mesh*maggs.mesh*maggs.mesh);  
01569     maxcurl = maggs_check_curl_E();
01570                 
01571 #ifdef MAGGS_DEBUG
01572     if(!this_node) {
01573       iteration++;
01574       if(iteration%1==0) {
01575         fprintf(stderr, "# iteration for field equilibration %d, diff=%9.4e, curlE=%9.4e\n", 
01576                 iteration, fabs(gsqrE-goldE),maxcurl);
01577         fflush(stderr);    
01578       }
01579     }
01580 #endif
01581                 
01582   } while(fabs(maxcurl)>1000000.*ROUND_ERROR_PREC);
01583         
01584 
01585         
01586         
01587         
01588         
01589         
01590         
01591         
01592   /* exchange halo-surfaces */
01593         
01594   FOR3D(k) Eall[k] = 0.;
01595   FORALL_INNER_SITES(ix, iy, iz) {
01596     i = maggs_get_linear_index(ix, iy, iz, lparams.dim);
01597     FOR3D(k) {
01598       Eall[k] += Dfield[3*i+k];
01599     }
01600   }
01601         
01602   MPI_Allreduce(Eall,gEall,3,MPI_DOUBLE,MPI_SUM,comm_cart);
01603         
01604   FOR3D(k) gEall[k] /= (n_nodes*lparams.size[0]*lparams.size[1]*lparams.size[2]);
01605         
01606   FORALL_INNER_SITES(ix, iy, iz) {
01607     i = maggs_get_linear_index(ix, iy, iz, lparams.dim);
01608     FOR3D(k) Dfield[3*i+k] -= gEall[k];
01609   }
01610   /* exchange whole glue-patch region */
01611   maggs_exchange_surface_patch(Dfield, 3, 0);
01612   if(!this_node)
01613     MAGGS_TRACE(fprintf(stderr, "Ex = %16.12e, Ey = %15.12e, Ez = %15.12e\n", gEall[0], gEall[1], gEall[2]));
01614 }
01615 
01616 
01617 
01618 
01619 
01620 
01621 
01622 
01623 /*********************************************/
01624 /****** calculate currents and E-fields ******/
01625 /*********************************************/
01626 
01627 /** calculate the charge flux in direction "dir".
01628     @param q     charge of the moving particle
01629     @param help  the relative position in the cube to the opposite of left down front lattice site.
01630     @param flux  flux variable to write into
01631     @param dir   direction in which to calculate the flux
01632 */
01633 void maggs_calc_charge_fluxes_1D(double q, double *help, double *flux, int dir)
01634 {
01635   /* at the moment works only for linear interpolation */
01636   int index, dir1, dir2;
01637   int l,m; 
01638   double q_scaled;
01639         
01640   q_scaled = q * maggs.prefactor*maggs.inva;
01641   index = 0;
01642         
01643   maggs_calc_directions(dir, &dir1, &dir2);   
01644         
01645   for(l=0;l<2;l++){  /* jumps from dir2- to dir2+ */
01646     for(m=0;m<2;m++){ /* jumps from dir1- to dir1+ */   
01647                         
01648       flux[index] = q_scaled * help[dir1]*help[dir2];
01649       index++;
01650                         
01651       help[dir1] = 1. - help[dir1];
01652     }
01653     help[dir2] = 1. - help[dir2]; 
01654   }
01655 }
01656 
01657 /** Extend particle trajectories on the one time step and check if the
01658     trajectory intersects the cell boundary in direction dirx#
01659     @return zero if no particle crosses intersection
01660     @param delta    amount by which the particle moves
01661     @param r_new    current position of particle
01662     @param dir      direction in which to check for intersection
01663     @param first    position of particle within lattice cube
01664     @param t_step   time step
01665     @param identity particle ID
01666 */
01667 int maggs_check_intersect_1D(double delta, double r_new, int dir, int first, double *t_step, int identity)
01668 {       
01669   int candidateplane = -1; /* force alloc error */
01670   int f_crossing; 
01671   double r_old, temp;
01672   double ZERO = 0.0;
01673   double ONE  = 1.0;
01674         
01675   f_crossing = 0;
01676   r_old = r_new - delta;
01677   f_crossing = f_crossing||(r_old>=ONE||r_old<ZERO);
01678         
01679   if(dir==2) temp = 1.;
01680   else       temp = 0.5;
01681         
01682   if(f_crossing) {
01683     MAGGS_TRACE(
01684                 fprintf(stderr, "Cube crossing in dir %d for particle %d at time = %f:\n", dir, identity, sim_time);
01685                 fprintf(stderr,"  rold[%d]=%f, rnew[%d]=%f\n", dir, (first+r_old-1.)*maggs.a,
01686                         dir, (first+r_new-1.)*maggs.a);
01687                 fflush(stderr);
01688                 );
01689                 
01690     if(r_old >= ONE) candidateplane = ONE;
01691     if(r_old < ZERO) candidateplane = ZERO;
01692                 
01693     /****** Update time step *********************/
01694     *t_step = temp * fabs((candidateplane-r_new)/delta);
01695   } /* end if crossing */
01696   else *t_step = temp;
01697   return f_crossing;
01698 }
01699 
01700 /** updates field on link coupling it with current.
01701     Force is multiplied by the time_step
01702     @param index   index of lattice site
01703     @param flux    charge flux in lattice site
01704     @param v       speed of particle
01705     @param dir     first direction of flux calculation
01706 */
01707 void maggs_calc_e_field_on_link_1D(int index, double *flux, double v, int dir)
01708 {  
01709   int l, m, ind_flux, dir1, dir2;
01710   int temp_ind;
01711   int help_index[2];
01712   int* anchor_neighb;
01713   t_site* anchor_site;
01714         
01715   maggs_calc_directions(dir, &dir1, &dir2);
01716         
01717   anchor_neighb = &neighbor[index][0]; 
01718   anchor_site = &lattice[index];
01719         
01720   temp_ind = anchor_neighb[dir1];
01721   if(temp_ind == NOWHERE) help_index[0] = lparams.volume;
01722   else
01723     help_index[0] = maggs_get_offset(lattice[temp_ind].r[dir1], anchor_site->r[dir1], dir1, lparams.dim);
01724   temp_ind = anchor_neighb[dir2];
01725   if(temp_ind == NOWHERE) help_index[1] = lparams.volume;
01726   else
01727     help_index[1] = maggs_get_offset(lattice[temp_ind].r[dir2], anchor_site->r[dir2], dir2, lparams.dim);
01728         
01729         
01730   ind_flux = 0;
01731   for(l=0;l<2;l++){  /* jumps from dir2- to dir2+ */
01732     for(m=0;m<2;m++){ /* jumps from dir1- to dir1+ */  
01733                         
01734       if(index < lparams.volume){
01735         Dfield[3*index+dir] -= flux[ind_flux] * v;
01736       }
01737                         
01738       ind_flux++; 
01739                         
01740       index+=help_index[0];
01741       help_index[0]=-help_index[0];     
01742     }
01743     index+=help_index[1];
01744     help_index[1]=-help_index[1];     
01745   }
01746 }
01747 
01748 
01749 /** loop over all cells and call charge current functions
01750     @param p           Particle pointer
01751     @param ghost_cell  flag if cell is ghost cell
01752 */
01753 void maggs_add_current_on_segment(Particle *p, int ghost_cell)
01754 {
01755   int d;
01756   int icoord, dir;
01757   int lat_index = -1; /* force alloc error */
01758   int first[SPACE_DIM];
01759   int f_crossing, flag_update_flux;
01760   t_dvector r_temp; 
01761   double delta;
01762   double flux[4], v;
01763   double v_inva[SPACE_DIM], v_invasq[SPACE_DIM];
01764   double pos[SPACE_DIM], help[3];
01765   double t_step;
01766         
01767   FOR3D(d) {
01768     pos[d]   = (p->r.p[d] - lparams.left_down_position[d])* maggs.inva;
01769     first[d] = (int) floor(pos[d]);
01770     r_temp[d]   = pos[d] - first[d]; /* it is the updated coord (we have to go back) */
01771     help[d]     = 1. - r_temp[d];
01772     v_inva[d]   = maggs.inva * p->m.v[d];
01773     v_invasq[d] = maggs.inva * v_inva[d];
01774   }
01775         
01776   flag_update_flux = 1;
01777   if(ghost_cell) {
01778     FOR3D(d) if(first[d]<lparams.halo_left_down[d] || first[d]>= lparams.halo_upper_right[d])
01779       {flag_update_flux = 0;break;}
01780   }
01781         
01782   if(flag_update_flux) {
01783     lat_index = maggs_get_linear_index(first[0], first[1], first[2], lparams.dim);
01784   }
01785         
01786   /* loop coordinates in order x->y->z->y->x */
01787   for(dir=0; dir<5; dir++) {
01788     icoord = dir;
01789     if(dir>2) icoord = dir%2;
01790     if(icoord == 2) delta = v_inva[icoord];
01791     else            delta = 0.5 * v_inva[icoord];
01792                 
01793     f_crossing = maggs_check_intersect_1D(delta, r_temp[icoord], icoord, first[icoord], &t_step, p->p.identity);
01794                 
01795     /* calculate flux */
01796     if(flag_update_flux) {
01797       maggs_calc_charge_fluxes_1D(p->p.q, help, flux, icoord);
01798                         
01799       v = t_step * v_invasq[icoord];
01800       maggs_calc_e_field_on_link_1D(lat_index, flux, v, icoord);
01801     }
01802                 
01803     if(f_crossing) {
01804       if(delta > 0.) {
01805         first[icoord]--;
01806         r_temp[icoord] += 1.;
01807       }
01808       else {
01809         first[icoord]++;
01810         r_temp[icoord] -= 1.;
01811       }
01812       if(icoord == 2) t_step = 1.  - t_step;
01813       else            t_step = 0.5 - t_step;
01814                         
01815       if(ghost_cell){
01816         if(flag_update_flux) {
01817           if(first[icoord]<lparams.halo_left_down[icoord] || first[icoord]>= lparams.halo_upper_right[icoord])
01818             {flag_update_flux = 0;}
01819         }
01820         else {
01821           flag_update_flux = 1;
01822           FOR3D(d) if(first[d]<lparams.halo_left_down[d] || first[d]>= lparams.halo_upper_right[d])
01823             {flag_update_flux = 0;break;}
01824           if(flag_update_flux) maggs_calc_charge_fluxes_1D(p->p.q, help, flux, icoord); 
01825         }
01826       }
01827                         
01828       if(flag_update_flux) {
01829         v = t_step * v_invasq[icoord];
01830         lat_index = maggs_get_linear_index(first[0], first[1], first[2], lparams.dim);
01831         maggs_calc_e_field_on_link_1D(lat_index, flux, v, icoord);
01832       }
01833     }
01834     r_temp[icoord] -= delta;
01835     help[icoord]    = 1. - r_temp[icoord];
01836   }
01837 }
01838 
01839 
01840 /** Calculate fluxes and couple them with fields symplectically.  It
01841     is assumed that the particle can not cross more than one cell
01842     boundary per direction */
01843 void maggs_couple_current_to_Dfield()
01844 {
01845   Cell *cell;
01846   Particle* p;
01847   int i, c, d, np;
01848   int flag_inner;
01849   double q;
01850   double r1, r2;
01851         
01852   /** loop over real particles */
01853   for (c = 0; c < local_cells.n; c++) {
01854     cell = local_cells.cell[c];
01855     p  = cell->part;
01856     np = cell->n;
01857     for(i = 0; i < np; i++) {
01858       if((q=p[i].p.q) != 0.) {
01859         /*      if(sim_time>49.08&&p[i].p.identity==231) */
01860         /*        fprintf(stderr,"time=%f, v=(%f,%f,%f)\n",sim_time, p[i].m.v[0], p[i].m.v[1],p[i].m.v[2]); */
01861         maggs_add_current_on_segment(&p[i], 0);
01862       }/* if particle.q != ZERO */
01863     }
01864   }
01865         
01866   /** loop over ghost particles */
01867   for (c = 0; c < ghost_cells.n; c++) {
01868     cell = ghost_cells.cell[c];
01869     p  = cell->part;
01870     np = cell->n;
01871     for(i = 0; i < np; i++) {
01872       if((q=p[i].p.q) != 0.) {
01873         flag_inner = 1;
01874         FOR3D(d) {
01875           r2 = p[i].r.p[d];
01876           r1 = r2 - p[i].m.v[d];
01877           if(((r2 < lparams.left_down_position[d])&&(r1 < lparams.left_down_position[d]))
01878              ||((r2 >= lparams.upper_right_position[d] && r1 >= lparams.upper_right_position[d])))
01879             {flag_inner = 0; break;}
01880         }
01881         if(flag_inner) {
01882           maggs_add_current_on_segment(&p[i], 1);
01883         }
01884       }/* if particle.q != ZERO */
01885     }
01886   }
01887 }
01888 
01889 
01890 
01891 
01892 
01893 
01894 
01895 
01896 
01897 /*******************************************/
01898 /****** calculate B-fields and forces ******/
01899 /*******************************************/
01900 
01901 /** propagate the B-field via \f$\frac{\partial}{\partial t}{B} = \nabla\times D\f$ (and prefactor)
01902     CAREFUL: Usually this function is called twice, with dt/2 each time
01903     to ensure a time reversible integration scheme!
01904     @param dt time step for update. Should be half the MD time step
01905 */
01906 void maggs_propagate_B_field(double dt)
01907 {
01908   int x, y, z, i, offset, index;
01909   int xoffset, yoffset;
01910   double help = dt*maggs.invsqrt_f_mass;
01911   /* B(t+h/2) = B(t-h/2) + h*curlE(t) */ 
01912         
01913   offset = maggs_get_linear_index(1,1,1, lparams.dim);
01914   yoffset = lparams.dim[2];
01915   xoffset = 2*lparams.dim[2];
01916         
01917   for(x=0;x<lparams.size[0];x++) {
01918     for(y=0;y<lparams.size[1];y++) {
01919       for(z=0;z<lparams.size[2];z++) {
01920                                 
01921         i = offset+z;
01922         index = 3*i;
01923         Bfield[index+0] += - help*maggs_calc_dual_curl(1,2, Dfield, neighbor[i], index); 
01924         Bfield[index+1] += - help*maggs_calc_dual_curl(2,0, Dfield, neighbor[i], index); 
01925         Bfield[index+2] += - help*maggs_calc_dual_curl(0,1, Dfield, neighbor[i], index);  
01926       }
01927       offset += yoffset;
01928     }
01929     offset += xoffset;
01930   }
01931         
01932   maggs_exchange_surface_patch(Bfield, 3, 0);
01933 }
01934 
01935 /** calculate D-field from B-field according to
01936     \f$\frac{\partial}{\partial t}{D} = \nabla\times B\f$ (and prefactors)
01937     @param dt MD time step
01938 */
01939 void maggs_add_transverse_field(double dt)
01940 {
01941   int i, index;
01942   double invasq; 
01943   int x, y, z;
01944   int offset, xoffset, yoffset;
01945   double help;
01946         
01947   invasq = SQR(maggs.inva);
01948   help = dt * invasq * maggs.invsqrt_f_mass;
01949         
01950   /***calculate e-field***/ 
01951   offset = maggs_get_linear_index(1,1,1, lparams.dim);
01952   yoffset = lparams.dim[2];
01953   xoffset = 2*lparams.dim[2];
01954   for(x=0;x<lparams.size[0];x++) {
01955     for(y=0;y<lparams.size[1];y++) {
01956       for(z=0;z<lparams.size[2];z++) {
01957         /*  FORALL_INNER_SITES(x, y, z) { */
01958         /*    i = maggs_get_linear_index(x, y, z, lparams.dim); */
01959         i = offset+z;
01960         index = 3*i;
01961         Dfield[index  ] += help * maggs_calc_curl(2, 1, Bfield, neighbor[i], index);
01962         Dfield[index+1] += help * maggs_calc_curl(0, 2, Bfield, neighbor[i], index);
01963         Dfield[index+2] += help * maggs_calc_curl(1, 0, Bfield, neighbor[i], index);
01964       }
01965       offset += yoffset;
01966     }
01967     offset += xoffset;
01968   } 
01969         
01970   maggs_exchange_surface_patch(Dfield, 3, 0);
01971 }
01972 
01973 
01974 /** interpolation function, solely for new self force correction. */
01975 void interpolate_part_charge_from_grad(double rel_x, double *grad, double *rho)
01976 {
01977   int i, k, l, m, index;
01978   int grad_ind;
01979 
01980   int help_index[3];
01981   double help_x;
01982 
01983   help_x = 1. - rel_x;     /* relative pos. w.r.t. first */  
01984 
01985   help_index[0] = 4;
01986   help_index[1] = 2; 
01987   help_index[2] = 1;
01988 
01989   grad_ind = 0;
01990   index = 0;
01991   for(i=0;i<8;i++) rho[i] = 0.;
01992 
01993   for(k=0;k<2;k++){   /* jumps from x- to x+ */
01994     for(l=0;l<2;l++){  /* jumps from y- to y+ */
01995       for(m=0;m<2;m++){ /* jumps from z- to z+ */
01996         // without q!!!
01997         if(k==0) rho[index] += - help_x * grad[grad_ind];
01998         else {
01999           rho[index] += - rel_x * grad[grad_ind%4];
02000         }
02001 
02002         grad_ind ++;
02003         index+=help_index[2];
02004         help_index[2]=-help_index[2];
02005       }
02006       index+=help_index[1];
02007       help_index[1]=-help_index[1];
02008     }
02009     index+=help_index[0];
02010     help_index[0]=-help_index[0];
02011   }
02012 }
02013 
02014 /** new self force correction with lattice Green's function.
02015     @param p Particle pointer
02016 */
02017 void calc_part_self_force(Particle *p)
02018 {
02019   int i, j, k, ip=0;
02020   double self, temp;
02021   static int help_index[SPACE_DIM];
02022 
02023   int dir1, dir2, d, grad_ind;
02024   int l, m, index, temp_ind;
02025   double grad[24];
02026   double grad2[24];
02027   double rho[8];
02028   double *force = p->f.f;
02029   double rel = 0.;
02030 
02031   help_index[0] = 12;
02032   help_index[1] = 6;
02033   help_index[2] = 3; 
02034 
02035   maggs_calc_charge_gradients(&rel, p->p.q, &grad[ip]);
02036   interpolate_part_charge_from_grad(rel, grad, rho);
02037   index = 0;
02038   grad_ind = 0;
02039   FOR3D(d) {
02040     maggs_calc_directions(d, &dir1, &dir2);
02041     for(l=0;l<2;l++){  /* jumps from dir2- to dir2+ */
02042       for(m=0;m<2;m++){ /* jumps from dir1- to dir1+ */          
02043 
02044         temp_ind = index + d;
02045         grad2[temp_ind] = grad[grad_ind];
02046         grad2[temp_ind + help_index[d]] = -grad[grad_ind];
02047 
02048         grad_ind++;
02049         index+=help_index[dir1];
02050         help_index[dir1]=-help_index[dir1];
02051       }
02052       index+=help_index[dir2];
02053       help_index[dir2]=-help_index[dir2];
02054 
02055     }
02056   }
02057 
02058   FOR3D(k) {
02059     self = 0.; 
02060     for(i=0;i<8;i++) {
02061 
02062       temp = rho[i]*grad2[i*SPACE_DIM + k];
02063       self += maggs.alpha[i][i] * temp;
02064 
02065       for(j=i+1;j<8;j++) {
02066         temp = rho[i]*grad2[j*SPACE_DIM + k] + rho[j]*grad2[i*SPACE_DIM + k];
02067         self += maggs.alpha[i][j] * temp;
02068       }
02069     }
02070     force[k] += 2. * self; 
02071   }
02072 }
02073 
02074 
02075 /** For each particle P, calculates self energy influence
02076     with direct Greens function, assuming constant permittivity
02077     on each lattice site.
02078     @param P Particle pointer
02079 */
02080 void maggs_calc_self_influence(Particle* P)
02081 {
02082   int k;
02083   //    int ix, iy, iz;
02084   double invasq = maggs.inva*maggs.inva;
02085   double position[SPACE_DIM], left_down_position[SPACE_DIM], relative_position[SPACE_DIM];
02086   //    int index = 0;
02087   //    int globalindex = 0;
02088   double local_force[SPACE_DIM];
02089   double particle_charge = P->p.q;
02090         
02091   /* calculate position in cell, normalized to lattice size: */
02092   FOR3D(k) {
02093     position[k]           = (P->r.p[k] - lparams.left_down_position[k]) * maggs.inva;
02094     left_down_position[k] = floor(position[k]);
02095     relative_position[k]  = position[k] - left_down_position[k];
02096     local_force[k] = 0.0;
02097   }
02098         
02099         
02100   /* Copy permittivity values to the mini-lattice: */
02101   /*
02102     for (iz=0;iz<zmax;iz++) {
02103     for (iy=0;iy<ymax;iy++) {
02104     for (ix=0;ix<xmax;ix++) {
02105     index = (iz + zmax*(iy + ymax*ix));
02106     globalindex = maggs_get_linear_index((left_down_position[0]+ix),
02107     (left_down_position[1]+iy),
02108     (left_down_position[2]+iz), lparams.dim);
02109     self_influence_correction[index].permittivity = lattice[globalindex].permittivity;
02110     self_influence_correction[index].charge = 0.0; //background_charge;
02111     FOR3D(k) {
02112     charge_gradient[index][k] = 0.0;
02113     local_E_field[index][k] = 0.0;
02114     }
02115     }
02116     }
02117     }
02118   */
02119         
02120         
02121   /* Calculate self-force directly: */  
02122   FOR3D(k) {
02123     local_force[k] = pow(((0.5 - relative_position[k])*SELF_FACTOR_1), 3.0) +
02124       (0.5 - relative_position[k]) * SELF_FACTOR_2;
02125     local_force[k] *= maggs.bjerrum;
02126     local_force[k] *= particle_charge * particle_charge;
02127     local_force[k] *= invasq;
02128   }
02129   // maggs.prefactor = 3.544907702 = sqrt(4. * M_PI * maggs.bjerrum * temperature);
02130   // Fehlender Faktor: 4*pi=12.5663706
02131   // a^3/c = 2.58448064965803
02132         
02133   /* Correct force: */
02134   FOR3D(k) {
02135     P->f.f[k] -= local_force[k];
02136   }
02137 }
02138 
02139 /** Calculate the actual force from the E-Field
02140     @param p      Particle pointer
02141     @param index  index of the lattice site
02142     @param grad   charge gradient
02143  */
02144 void maggs_calc_part_link_forces(Particle *p, int index, double *grad)
02145 {
02146   static int init = 1;
02147   static int help_index[SPACE_DIM];
02148   int ind_grad, j;
02149   int dir1, dir2;
02150   /*  int* anchor_neighb; */
02151   int l,m;
02152   double local_force[SPACE_DIM];
02153         
02154   if(init) {
02155     t_site* anchor_site;
02156     anchor_site = &lattice[index];
02157     FOR3D(j) {
02158       help_index[j] = maggs_get_offset(lattice[neighbor[index][j]].r[j], anchor_site->r[j], j, lparams.dim);
02159     }
02160     init = 0;
02161   }
02162         
02163   FOR3D(j){
02164     local_force[j] = 0.;
02165   }
02166         
02167   ind_grad = 0; 
02168         
02169   FOR3D(j) {
02170     maggs_calc_directions(j, &dir1, &dir2);
02171                 
02172     for(l=0;l<2;l++){  /* jumps from dir2- to dir2+ */
02173       for(m=0;m<2;m++){ /* jumps from dir1- to dir1+ */   
02174         local_force[j] += -grad[ind_grad]*Dfield[3*index+j];
02175         //fprintf(stderr, "charge_gradient %d: %1.9f\n", ind_grad, grad[ind_grad]);
02176         ind_grad++;
02177         index += help_index[dir1];
02178         help_index[dir1] = -help_index[dir1];
02179       }
02180       index += help_index[dir2];
02181       help_index[dir2] = -help_index[dir2];
02182     }
02183   }  
02184   /* Attention! Here, the INTERLACING is done! */
02185   FOR3D(j){
02186     p->f.f[j] += maggs.prefactor * local_force[j];
02187   }
02188 }
02189 
02190 
02191 /** Public function.
02192     Calculates the actual force on each particle
02193     by calling all other needed functions (except
02194     for maggs_propagate_B_field) */
02195 void maggs_calc_forces()
02196 { 
02197   Cell *cell;
02198   static int init = 1;
02199   static int Npart_old;
02200   Particle *p;
02201   int i, c, np, d, index, Npart, ip; 
02202   double q;
02203   /* position of a particle in local lattice units */
02204   double pos[SPACE_DIM];
02205   /* index of first assignment lattice point */
02206   int first[3];
02207   /* charge gradient (number of neighbor sites X number of dimensions) */
02208   static double *grad;
02209         
02210   if(init) Npart_old = 0;
02211         
02212   Npart = cells_get_n_particles();
02213   if(Npart>Npart_old) {
02214     grad = (double *) realloc(grad, 12*Npart*sizeof(double));
02215     Npart_old = Npart;
02216   }
02217         
02218   /* Hopefully only needed for Yukawa: */
02219   maggs_update_charge_gradients(grad);
02220         
02221   if(!init) {
02222     MAGGS_TRACE(fprintf(stderr, "running symplectic update\n"));
02223     maggs_couple_current_to_Dfield();
02224     maggs_add_transverse_field(time_step);  
02225   }
02226   else init = 0;
02227         
02228   ip = 0;
02229   for (c = 0; c < local_cells.n; c++) {
02230     cell = local_cells.cell[c];
02231     p  = cell->part;
02232     np = cell->n;
02233     for(i=0; i<np; i++) { 
02234       q = p[i].p.q;
02235       if( abs(q) > 1.0e-5 ) {
02236         FOR3D(d) {
02237           pos[d]   = (p[i].r.p[d] - lparams.left_down_position[d])* maggs.inva;
02238           first[d] = (int) pos[d];
02239         }
02240                                 
02241         index = maggs_get_linear_index(first[0],first[1],first[2],lparams.dim);
02242         maggs_calc_part_link_forces(&p[i], index, &grad[ip]);
02243         maggs_calc_self_influence(&p[i]);
02244 
02245         ip+=12;
02246       }
02247     }
02248   }
02249 
02250   if (maggs.finite_epsilon_flag) maggs_compute_dipole_correction();
02251 
02252 }
02253 
02254 
02255 
02256 
02257 
02258 
02259 
02260 
02261 /********************************************/
02262 /****** get energy and print out stuff ******/
02263 /********************************************/
02264 
02265 /** integrates 0.5*D*E over the whole system
02266     public function!
02267     @return returns electric energy
02268 */
02269 double maggs_electric_energy()
02270 {
02271   int x, y, z, i, k;
02272   double localresult = 0.;
02273   double globalresult = 0.;
02274         
02275   FORALL_INNER_SITES(x, y, z) {
02276     i = maggs_get_linear_index(x, y, z, lparams.dim);     
02277     FOR3D(k){
02278       localresult += SQR(Dfield[i*3+k]) / lattice[i].permittivity[k];
02279     }
02280   }
02281   localresult *= 0.5*maggs.a;
02282   MPI_Allreduce(&localresult,&globalresult,1,MPI_DOUBLE,MPI_SUM,comm_cart);  
02283   return globalresult;
02284 }
02285 
02286 
02287 /** Public funxtion.
02288     Integrates the B-field over the whole system to get the
02289     energy of the magnetic field.
02290     @return returns magnetic energy
02291 */
02292 double maggs_magnetic_energy()
02293 {
02294   int x, y, z, i;
02295   double result = 0.;
02296   /*  double invmass = 1./maggs.f_mass; we have B^~=B*c !!!! */
02297         
02298   FORALL_INNER_SITES(x, y, z) {
02299     i = maggs_get_linear_index(x, y, z, lparams.dim);     
02300     result += SQR(Bfield[i*3]) + SQR(Bfield[i*3+1]) + SQR(Bfield[i*3+2]);
02301   }
02302   /* B is rescaled !!! ATTENTION!!! */
02303   result *= 0.5*maggs.a;
02304   return result;
02305 }
02306 
02307 /***************************/
02308 /****** init and exit ******/
02309 /***************************/
02310 
02311 /** Initialization function.
02312     Sets maggs structure variables.
02313     Calls calculation of initial D-field. */
02314 void maggs_init()
02315 {
02316     maggs.inva  = (double) maggs.mesh/box_l[0]; 
02317     maggs.a     = 1.0/maggs.inva;
02318     if (temperature>0.0) {
02319       maggs.prefactor  = sqrt(4. * M_PI * maggs.bjerrum * temperature);
02320       maggs.pref2      = maggs.bjerrum * temperature;
02321     } else {
02322       maggs.prefactor  = sqrt(4. * M_PI * maggs.bjerrum);
02323       maggs.pref2      = maggs.bjerrum;
02324     }                   
02325     
02326     if (maggs_sanity_checks()) {
02327       maggs.bjerrum = 0.0;
02328       coulomb.bjerrum = 0.0;
02329       return;
02330     }
02331                 
02332     maggs_setup_local_lattice();
02333                 
02334     /* enforce electric field onto the Born-Oppenheimer surface */
02335     maggs_calc_init_e_field();
02336     //    if(!this_node) fprintf(stderr, "%d: Electric field is initialized\n", this_node);
02337 }
02338 
02339 /** Frees the dynamically allocated memory
02340     Currently not called from anywhere. */
02341 void maggs_exit()
02342 {
02343   free(lattice);
02344   free(neighbor);
02345   free(Dfield);
02346   free(Bfield);
02347 }
02348 
02349 
02350 
02351 #endif // ELECTROSTATICS