![]() |
ESPResSo 3.2.0-11-g9950804-git
Extensible Simulation Package for Soft Matter Research
|
00001 /* 00002 Copyright (C) 2010,2011,2012,2013 The ESPResSo project 00003 Copyright (C) 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
1.7.5.1