00001 // PoissonConvolve3D.hh 00002 /* 00003 NOTE: fftw must be installed with float libraries. 00004 */ 00005 00006 #ifndef POISSONCONVOLVE3D_HH 00007 #define POISSONCONVOLVE3D_HH 00008 00009 #include <assert.h> 00010 #include <math.h> 00011 #include <vector> 00012 00013 #include "fftw3.h" 00014 00015 #ifdef USE_DOUBLE 00016 #define float double 00017 #define sqrtf sqrt 00018 #define fftwf_complex fftw_complex 00019 #define fftwf_plan fftw_plan 00020 #define fftwf_malloc fftw_malloc 00021 #define fftwf_free fftw_free 00022 #define fftwf_destroy_plan fftw_destroy_plan 00023 #define fftwf_set_timelimit fftw_set_timelimit 00024 #define fftwf_plan_dft_r2c_3d fftw_plan_dft_r2c_3d 00025 #define fftwf_plan_dft_c2r_3d fftw_plan_dft_c2r_3d 00026 #define fftwf_execute fftw_execute 00027 #endif 00028 00029 #ifndef M_PI 00030 #define M_PI 3.14159265358979323846264338327950288 00031 #endif 00032 00033 #ifdef MAIN 00034 ///@ testing - don't require Geant4 headers. 00035 struct G4ThreeVector { 00036 double xx, yy, zz; 00037 G4ThreeVector(double x, double y, double z) { xx=x; yy=y; zz=z; } 00038 double x() { return xx; } 00039 double y() { return yy; } 00040 double z() { return zz; } 00041 double mag() { return sqrt(xx*xx+yy*yy+zz*zz); } 00042 G4ThreeVector& operator += (G4ThreeVector& r) 00043 { xx += r.xx; yy += r.yy; zz += r.zz; return *this; } 00044 G4ThreeVector& operator -= (G4ThreeVector& r) 00045 { xx -= r.xx; yy -= r.yy; zz -= r.zz; return *this; } 00046 G4ThreeVector& operator *= (double v) 00047 { xx *= v; yy*= v; xx*= v; return *this; } 00048 G4ThreeVector& operator /= (double v) 00049 { xx /= v; yy/= v; xx/= v; return *this; } 00050 }; 00051 const double megavolt=1.0, meter=1.0; 00052 #else 00053 #include "G4ThreeVector.hh" 00054 #endif //MAIN 00055 00056 /// Charge represents one point charge for the approximation outside the grid. 00057 struct Charge { 00058 float x, y, z, c; 00059 Charge(float _x, float _y, float _z, float _c) 00060 { x=_x; y=_y; z=_z; c=_c; } 00061 G4ThreeVector getPosition() { return G4ThreeVector(x,y,z); } 00062 float getCharge() { return c; } 00063 }; 00064 00065 /** class PoissonConvolve3D 00066 * 00067 * A solver for Poisson's equation in 3-D Cartesian coordinates {x,y,z}, 00068 * using convolution of the Green's function (whch has infinite boundary 00069 * conditions). The convolution is sped up by using an FFT on a grid 00070 * twice as large (in each dimension) as the solution (physical) grid. 00071 * 00072 * This class uses the Poisson solver on a rectangular 3-d grid; inside 00073 * the grid, it calculates E by applying the analytic derivative of the 00074 * interpolation function to the values of phi at the surrounding 8 00075 * grid points. Outside the grid, by default it uses the analytic value 00076 * of E for a single point charge, with its charge equal to the total 00077 * charge in the grid, and its position at the mean position of the 00078 * charge in the grid. More complicated approximations, based on 00079 * multiple point charges, can be used (not discussed here). 00080 * 00081 * Units are determined by the code using this class. Normally the 00082 * charge values will not be integers, but rather integers times the 00083 * positron charge divided by epsilon_0 (the permittivity of free space), 00084 * the grid sizes will have units of length; the result is that 00085 * phi() and getE() will then have the appropriate units. 00086 * 00087 * This class solves: 00088 * del^2 phi = -4*pi*rho 00089 * On a specified grid in {x,y,z}. rho(x,y,z) is the charge density. 00090 * The -4*pi and the conversion from charge to charge density is handled 00091 * internally in putCharge(); they are NOT handled in setRhs() (which 00092 * is intended primarily for testing). 00093 * 00094 * This class includes an approximation for use outside the grid. The 00095 * default is simply to use the total charge put into the grid (via 00096 * putCharge()), and the weighted mean position of that charge. Better 00097 * approximations can be computed externally as a vector of point 00098 * charges and used via setApproximation(). 00099 * 00100 * Note: array[] contains CHARGE between calls to zeroRhs() and solve(); 00101 * it contains PHI between calls to solve() and zeroRhs(); status 00102 * reflects its contents, ans assert()-s are used to ensure accesses 00103 * are valid.. 00104 * 00105 * 00106 * Accuracy for a 65x65x65 grid of unit size: 00107 * ... MORE TO COME ... 00108 * 00109 * 00110 * The CPU time increases with increasing N, and can vary by as much as a 00111 * factor of 3 for adjacent values. The following are good values: 00112 * 128, 120, 112, 100, 96, 90, 84, 80, 75, 70, 64, 60, 56, 48, 42, 36, 32. 00113 * Below 28 the variations are small. The following are particularly SLOW 00114 * values: 129, 127, 123, 107, 103, 101. 00115 **/ 00116 class PoissonConvolve3D { 00117 public: 00118 /// enum PutMode determines how putCharge() places charge into 00119 /// the grid. PRORATED is the default. 00120 /// LOWEST places charge into the grid point lower than (x,y,z). 00121 /// NEAREST places charge into the nearest grid point to (x,y,z). 00122 /// PRORATED places charge into the 8 nearest grid points, pro-rated 00123 /// by proximity to (x,y,z). 00124 /// For 1M particles into a 129^3 grid, NEAREST and LOWER were less 00125 /// than 10% faster than PRORATED, giving slightly more ragged results. 00126 enum PutMode {LOWEST, NEAREST, PRORATED}; 00127 protected: 00128 enum Status {UNINITIALIZED, CHARGE, PHI}; 00129 int nx, ny, nz; 00130 float xMin, xMax, yMin, yMax, zMin, zMax; 00131 Status status; 00132 float dx, dy, dz; // grid spacings 00133 float *array; // phi[index(ix,iy,iz)] or rho[index(ix,iy,iz)] 00134 fftwf_complex *transf; // transform of array (working) 00135 fftwf_complex *greensFunction; 00136 std::vector<Charge> approx; 00137 float sumX, sumY, sumZ, totalCharge; // for defaultApproximation() 00138 float vol; 00139 PutMode putMode; 00140 int nc; 00141 fftwf_plan gfPlan; 00142 fftwf_plan plan1; 00143 fftwf_plan plan2; 00144 int index(int ix, int iy, int iz) const { 00145 assert(ix>=0&&ix<2*nx && iy>=0&&iy<2*ny && iz>=0&&iz<2*nz); 00146 return iz+2*nz*(iy+2*ny*ix); // C order 00147 } 00148 public: 00149 /// Constructor. 00150 /// nx,ny,nz are the # grid points in the 3 dimensions -- NOTE: 00151 /// these are the size of the PHYSICAL grid, the actual grid is 00152 /// doubled in each dimension. They can be any integers >= 9, but 00153 /// powers of 2 have a performance advantage. 00154 /// Note that 512x512x512 allocates too much memory for a 32-bit 00155 /// program on an Intel processor. 00156 /// xMin,xMax,yMin,yMax,zMin,zMax are the grid limits in 3 dimensions. 00157 /// That implies array[index(0,iy,iz)] is the boundary at x=xMin; 00158 /// array[index(nx-1,iy,iz)] is the boundary at x=xMax; etc. 00159 /// 1.0e-4 is appropriate. 00160 PoissonConvolve3D(int _nx, int _ny, int _nz, float _xMin, float _xMax, 00161 float _yMin, float _yMax, float _zMin, float _zMax); 00162 00163 /// Destructor. 00164 virtual ~PoissonConvolve3D() { 00165 if(array) fftwf_free(array); 00166 if(transf) fftwf_free(transf); 00167 if(greensFunction) fftwf_free(greensFunction); 00168 fftwf_destroy_plan(gfPlan); 00169 fftwf_destroy_plan(plan1); 00170 fftwf_destroy_plan(plan2); 00171 approx.clear(); 00172 } 00173 00174 /// updatePosition() updates the position of the boundaries. 00175 /// Usually followed by a call to setApproximation(), with the 00176 /// approximation charge positions updated similarly, or by 00177 /// a series of calls to putCharge() and then defaultApproximation(). 00178 void updatePosition(float _xMin, float _xMax, 00179 float _yMin, float _yMax, float _zMin, float _zMax) { 00180 xMin=_xMin; xMax=_xMax; 00181 yMin=_yMin; yMax=_yMax; 00182 zMin=_zMin; zMax=_zMax; 00183 dx = (xMax-xMin)/(nx-1); 00184 dy = (yMax-yMin)/(ny-1); 00185 dz = (zMax-zMin)/(nz-1); 00186 vol = dx*dy*dz; 00187 computeGreensFunction(); 00188 } 00189 00190 /// phi() works for all {x,y,z}, using the solution inside the grid 00191 /// and the approximation outside the grid. 00192 /// Note that phi() is not necessarily continuous or differentiable, 00193 /// but it should be approximately continuous. 00194 /// Do not differentiate this function, use getE() instead (it is 00195 /// MUCH more intelligent about the grid and its bounadries). 00196 /// Intended primarily for testing. 00197 float phi(float x, float y, float z); 00198 00199 /// phiGrid() returns the value of the solution to Poisson's equation 00200 /// (i.e. the electrostatic potential) on grid points. 00201 /// ix,iy,iz MUST be in range: 0..nx-1, 0..ny-1, 0..nz-1 00202 /// Intended primarily for testing. 00203 float phiGrid(int ix, int iy, int iz) const 00204 { assert(status==PHI); return array[index(ix,iy,iz)]; } 00205 00206 /// setPhi() will set a value in array[]. 00207 /// Intended primarily for testing. 00208 void setPhi(int ix, int iy, int iz, float v) 00209 { assert(status==PHI); array[index(ix,iy,iz)] = v; } 00210 00211 /// addPhi() will add v to a value in array[]. 00212 /// Intended primarily for testing. 00213 void addPhi(int ix, int iy, int iz, float v) 00214 { assert(status==PHI); array[index(ix,iy,iz)] += v; } 00215 00216 /// rhs() returns the charge at a grid point of the right-hand-side 00217 /// of Poisson's equation. 00218 /// NOTE: ix,iy,iz MUST be in range: 0..nx-1, 0..ny-1, 0..nz-1 00219 float rhs(int ix, int iy, int iz) const 00220 { assert(status==CHARGE); 00221 return 4.0*M_PI*array[index(ix,iy,iz)]; } 00222 00223 /// setRhs() will set an entry in rhs[]. Normally putCharge() is better. 00224 /// ix,iy,iz MUST be in range: 0..nx-1, 0..ny-1, 0..nz-1 00225 /// This directly sets the rhs array entry, so you probably need 00226 /// to multiply by -4*pi/(dx*dy*dz) to get the proper R.H.S. for 00227 /// Poisson's equation; putCharge() does that internally. 00228 /// Intended primarily for testing. 00229 void setRhs(int ix, int iy, int iz, float v) 00230 { assert(status==CHARGE); array[index(ix,iy,iz)] = v; } 00231 00232 /// putCharge() puts a charge into the rhs grid according to 00233 /// the current putMode. Does nothing if outside the grid. 00234 /// Multiplies by -1.0/(dx*dy*dz) to convert the charge to the R.H.S. 00235 /// of Poisson's equation, which is minus the charge density. 00236 /// NOTE: dividing by epsilon0 is up to the user's choice of units. 00237 /// Accumulates the total charge and mean position of charge actually 00238 /// placed into the grid (for use by defaultApproximation()). 00239 /// Returns true if the charge was placed within the grid, false if not. 00240 bool putCharge(float x, float y, float z, float c); 00241 bool putCharge(Charge &c) { return putCharge(c.x,c.y,c.z,c.c); } 00242 00243 /// setPutMode() sets the mode of putCharge(). See PutMode above. 00244 void setPutMode(PutMode m) { putMode = m; } 00245 00246 /// zeroRhs() will zero rhs. 00247 void zeroRhs(); 00248 00249 /// getE() returns the value of the E field at a given point. 00250 /// Uses the solution inside the grid and the approximation outside. 00251 /// It handles grid cells and boundaries intelligently. 00252 G4ThreeVector getE(float x, float y, float z); 00253 00254 /// approxE() evaluates the approximation E field. 00255 /// {x,y,z} can be anywhere, but normally are outside the grid or on 00256 /// its boundary. 00257 G4ThreeVector approxE(double x, double y, double z); 00258 00259 /// approxPhi() evaluates the approximation phi. 00260 /// {x,y,z} can be anywhere, but normally are outside the grid or on 00261 /// its boundary. 00262 float approxPhi(double x, double y, double z); 00263 00264 /// getPosition() returns the position corresponding to grid point 00265 /// indexes. Intended primarily for testing. 00266 G4ThreeVector getPosition(int ix, int iy, int iz) 00267 { return G4ThreeVector(xMin+ix*dx,yMin+iy*dy,zMin+iz*dz); } 00268 00269 /// setApproximation() sets the approximation to use outside the 00270 /// grid, and on its boundary. v is a vector of point charges. 00271 /// Updates the boundary values of phi. 00272 void setApproximation(std::vector<Charge> v) 00273 { approx = v; } 00274 00275 /// defaultApproximation() calls setApproximation() with the 00276 /// total charge and mean position in rhs (from putCharge()). 00277 /// Updates the boundary values of phi, and the interior if 00278 /// interior=true. 00279 void defaultApproximation(); 00280 00281 /// computeGreensFunction() does just that. Must be called whenever 00282 /// the physical size of the grid is changed. 00283 void computeGreensFunction(); 00284 00285 /// solve() will solve Poisson's equation 00286 void solve(); 00287 00288 /// printRhs() prints the rhs. 00289 /// Intended primarily for testing. 00290 void printRhs(); 00291 00292 /// printPhi() prints phi. 00293 /// Intended primarily for testing. 00294 void printPhi(); 00295 00296 /// accessors. 00297 int getNx() const { return nx; } 00298 int getNy() const { return ny; } 00299 int getNz() const { return nz; } 00300 }; 00301 00302 #endif // POISSONCONVOLVE3D_HH