PoissonConvolve3D.hh

Go to the documentation of this file.
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
g4beamline