![]() |
Gowdy solver
|
00001 /** 00002 * @file gridDriver.h 00003 * @brief Implements a spatially cell-centered uniform grid. 00004 * @author Mikica Kocic 00005 * @copyright GNU General Public License (GPLv3). 00006 * 00007 * @page fdm Finite Difference Approximation 00008 * 00009 * At the core of finite difference approximation is a discretization of the 00010 * spacetime, or a numerical grid (shown below).\ Then, a function `f(t,r)` is 00011 * represented by values at a discrete set of points `(m,n)`. 00012 * 00013 * <pre> 00014 * p in M <--> (t,r) <--> (m,n) 00015 * (point in a manifold) (in a chart) (discretized point) 00016 * | | 00017 * V V 00018 * f(t,r) f(m,n) </pre> 00019 * 00020 * The points `(m,n)` are called the grid points or nodes. The grid points are chosen 00021 * to reside at the center of grid cells (the other approach would be to associate the 00022 * grid points with the vertices of the grid cells). 00023 * 00024 * The data structure GridPoint contains the set of fields `{f,...}` that are kept 00025 * in the memory for the point `(m,n)`. The numerical grid is an array of grid points 00026 * `grid[m][n]` where a field `f` is directly accessible as `grid[m][n].f`, or using 00027 * a macro as `f(m,n)`. 00028 * 00029 * The grid consists of the *interior points*, and the *virtual points* at the boundary. 00030 * The virtual points are used to impose the boundary conditions. 00031 * 00032 * A cell-centered uniform grid with `nLen` grid points in each grid row is shown below 00033 * (see also @cite Baumgarte:2010nr, p.192). 00034 * 00035 * <pre> 00036 * left virtual grid cell grid point right virtual 00037 * point(s) .---^---. | point(s) 00038 * . | n-1 ' n ' n+1 | | 00039 * : +----|----+-------+-------+---------+-------+---|---+-------+----|----+ 00040 * m+1 | O | | | (m+1,n) | | X | | O | 00041 * +---------+-------+-------.=========.-------+-------+-------+---------+ 00042 * m | | |(m,n-1)| (m,n) |(m,n+1)| | | | 00043 * +---------+-------+-------'========='-------+-------+-------+---------+ 00044 * m-1 | | | | | | | | | 00045 * . +---------'-------+-------+---------+-------+-------+-------'---------+ 00046 * : 0 | 1 2 . . . nLen | nLen+1 00047 * ^ ^ 00048 * r_min r_max </pre> 00049 * 00050 * The cell-centered uniform grid implies: 00051 * @code 00052 * r = r_min + ( n - 1/2 ) delta_r 00053 * delta_r = ( r_max - r_min ) / nLen 00054 * @endcode 00055 * @li if `nGhost = 1`, `n` ranges between 1 and `nLen`; 00056 * the boundary virtual points are `grid[m][0]` and `grid[m][nLen+1]` 00057 * 00058 * @li `m` ranges between 1 and mLen, and wraps around; 00059 * the slices `grid[mLen+1]`... are used for intermediate integration steps. 00060 * 00061 * In parallel simulations, the grid is split across processes using MPI, 00062 * allowing storage of large grid functions and parallel work on each part. 00063 * 00064 */ 00065 00066 ///////////////////////////////////////////////////////////////////////////////////////// 00067 /** @defgroup g4 Grid driver */ 00068 ///////////////////////////////////////////////////////////////////////////////////////// 00069 /*@{*/ 00070 /** Uniform grid of grid functions. 00071 */ 00072 class UniformGrid 00073 : public MPIWorld 00074 { 00075 /** Allocates memory for the numerical grid. 00076 * 00077 * We allocate `mExtra` extra m grid rows for the intermediate steps. 00078 * We also allocate 2*nGhost n-cells as the L/R spatial boundary. 00079 */ 00080 void createGrid () 00081 { 00082 size_t mDim = mLen + mExtra; 00083 size_t nDim = nLen + 2 * nGhost; 00084 00085 grid = new GridPoint* [ mDim ]; 00086 grid[0] = new GridPoint[ mDim * nDim ]; 00087 00088 for( size_t m = 0; m < mDim - 1; ++m ) { 00089 grid[m+1] = grid[m] + nDim; 00090 } 00091 00092 slog << " Allocated " << mDim << " * " << nDim 00093 << " * " << sizeof(GridPoint) / sizeof(Real) 00094 << " * Real" << 8 * sizeof(Real) 00095 << " = " << mDim * nDim * sizeof(GridPoint) 00096 << " bytes" << std::endl 00097 << " Tracking " << GFCNT << " grid functions at a point" 00098 << std::endl << std::endl; 00099 } 00100 00101 /** Releases memory allocated for the numerical grid. 00102 */ 00103 void deleteGrid () 00104 { 00105 if ( ! grid ) return; 00106 00107 delete[] grid[0]; 00108 delete[] grid; 00109 grid = NULL; 00110 } 00111 00112 protected: 00113 00114 GridPoint** grid; //!< The grid storage 00115 // Spatial discretization: 00116 Int nLen; //!< Number of cells in `r`-direction 00117 Int nGhost; //!< Number of virtual (ghost) cells on the `r`-boundary 00118 Real delta_r; //!< The grid spacing accross `r` 00119 // Temporal discretization: 00120 Int mLen; //!< Number of cached cells in `t`-direction 00121 Int mExtra; //!< Number of extra cells (for integration substeps) 00122 00123 public: 00124 00125 GridPoint** get_grid () { return grid; } 00126 00127 Int get_nLen () const { return nLen; } 00128 Int get_nGhost () const { return nGhost; } 00129 Int get_mLen () const { return mLen; } 00130 Real get_delta_r () const { return delta_r; } 00131 00132 Int mpiSize () const { return size (); } 00133 Int mpiRank () const { return rank (); } 00134 00135 /** Constructs the uniform grid as specified in the parameter file. 00136 */ 00137 UniformGrid( Parameters& params ) 00138 { 00139 params.get( "grid.nLen", nLen, 10 ); 00140 params.get( "grid.nGhost", nGhost, 10 ); 00141 params.get( "grid.delta_r", delta_r, 0.1 ); 00142 params.get( "grid.mLen", mLen, 5 ); 00143 params.get( "grid.mExtra", mExtra, 9 ); 00144 00145 // Split the data equally among all the ranks in the MPI world 00146 nLen = nLen / size(); 00147 00148 slog << "Uniform Grid Driver:" << std::endl << std::endl 00149 << " nLen = " << nLen << ", nGhost = " << nGhost 00150 << ", delta_r = " << delta_r 00151 << ", mLen = " << mLen << ", mExtra = " << mExtra << std::endl 00152 << " Compile-time: CFDS_ORDER = " << CFDS_ORDER << std::endl; 00153 00154 #if _OPENMP 00155 slog << " Using OpenMP: #procs = " 00156 << omp_get_num_procs() << ", max #threads = " 00157 << omp_get_max_threads() << std::endl; 00158 #endif 00159 00160 // Sanity check that nGhost > CFDS_ORDER/2 00161 // 00162 if( nGhost < CFDS_ORDER/2 ) { 00163 slog << "Error: Ghost region does not fit the FD order." << std::endl; 00164 exit( -1 ); 00165 } 00166 00167 createGrid (); 00168 } 00169 00170 /** Exchange the boundaries at the given time slice. 00171 */ 00172 void exchangeBoundaries( Int m ) 00173 { 00174 MPIWorld::exchangeBoundaries( 00175 &grid[m][ 0 ], // Left ghost (received) 00176 &grid[m][ nGhost ], // Left edge (sent) 00177 &grid[m][ nGhost + nLen - nGhost ], // Right_edge (sent) 00178 &grid[m][ nGhost + nLen ], // Right ghost (received) 00179 nGhost * GFCNT // Exchanged size of Reals 00180 ); 00181 } 00182 }; 00183 00184 ///////////////////////////////////////////////////////////////////////////////////////// 00185 00186 /** Access the given grid function data (a utility macro for the grid users). 00187 */ 00188 #define GF(f,m,n) grid[m][n][f] 00189 00190 /** `emitField` is a macro that emits a method encapsulating 00191 * a grid function that access data in a grid at a point given by 00192 * `m` (discretized t-coordinate) and `n` (discretized r-coordinate). 00193 * For example, `emitField(r)` defines `r(m,n)`. 00194 */ 00195 #define emitField(gf) \ 00196 inline Real& gf( Int m, Int n ) { return grid[m][n][fld::gf]; } 00197 00198 /** GridUser contains cached variables from the grid-driver. 00199 * @note Finite differences are defined for the grid users. 00200 */ 00201 struct GridUser 00202 { 00203 UniformGrid* gridDriver; //!< Grid object 00204 GridPoint** grid; //!< Actual grid data 00205 00206 Int nLen; //!< Number of cells in `r`-direction 00207 Int mLen; //!< Number of cells in `t`-direction 00208 Int nGhost; //!< Number of virtual (ghost) cells on the `r`-boundary 00209 00210 Real delta_r; //!< The grid spacing accross `r` 00211 Real inv_delta_r; //!< `1 / delta_r` 00212 Real inv_delta_rr; //!< `1 / delta_r^2` 00213 00214 emitField(t) 00215 emitField(r) 00216 00217 GridUser( UniformGrid& ug ) 00218 { 00219 grid = ug.get_grid (); 00220 nLen = ug.get_nLen (); 00221 mLen = ug.get_mLen (); 00222 nGhost = ug.get_nGhost (); 00223 delta_r = ug.get_delta_r (); 00224 00225 // Calculate various constants involving 1/delta_r 00226 // 00227 inv_delta_r = 1 / ug.get_delta_r (); 00228 inv_delta_rr = 1 / ( ug.get_delta_r () * ug.get_delta_r () ); 00229 } 00230 00231 /** Cubic spline smoother of a grid function. 00232 */ 00233 void cubicSplineShmooth( Int m, Int gf, Int lin2n, Int cub2n ) 00234 { 00235 // The accuracy is very low at low r. Assume that a few first cells are linear. 00236 // 00237 if( lin2n > 0 ) 00238 { 00239 Real dydx = delta_r * GF(gf,m,nGhost+lin2n) / r(m,nGhost+lin2n); 00240 for ( Int i = 0; i < lin2n; ++i ) { 00241 GF(gf,m,nGhost+i) = ( i + 0.5 ) * dydx; 00242 } 00243 } 00244 00245 // A six-point cubic spline to smooth the region around r = 0. 00246 // 00247 if( cub2n > 0 ) 00248 { 00249 static CubicSpline spline( 6 ); 00250 00251 Real pts[6][2] = { 00252 { r(m,nGhost ), GF(gf,m,nGhost ) }, 00253 { r(m,nGhost + 3 ), GF(gf,m,nGhost + 3 ) }, 00254 { r(m,nGhost + cub2n/2 ), GF(gf,m,nGhost + cub2n/2 ) }, 00255 { r(m,nGhost + cub2n-6 ), GF(gf,m,nGhost + cub2n-6 ) }, 00256 { r(m,nGhost + cub2n-3 ), GF(gf,m,nGhost + cub2n-3 ) }, 00257 { r(m,nGhost + cub2n ), GF(gf,m,nGhost + cub2n ) } 00258 }; 00259 spline.initialize( pts ); 00260 00261 for( Int i = 0; i < cub2n; ++i ) { 00262 GF(gf,m,nGhost+i) = spline( r(m,nGhost+i) ); 00263 } 00264 } 00265 } 00266 }; 00267 /*@}*/ 00268 /////////////////////////////////////////////////////////////////////////////////////////