![]() |
Bimetric 3+1 toolkit for spherical symmetry
|
00001 /** 00002 * @file maximalSlice.h 00003 * @brief Maximal slicing algorithm. 00004 * @author Mikica Kocic 00005 * @copyright GNU General Public License (GPLv3). 00006 */ 00007 00008 ///////////////////////////////////////////////////////////////////////////////////////// 00009 00010 /** PP is P(x) in the equation: y''(x) = P(x) y'(x) + Q(x) y(x) 00011 */ 00012 #define PP(n) ( -2 / r(m,n) ) 00013 00014 /** QQ is Q(x) in the equation: y''(x) = P(x) y'(x) + Q(x) y(x) 00015 */ 00016 #define QQ(n) ( pow2( gK1(m,n) ) + 2 * pow2( gK2(m,n) ) \ 00017 + k_g * ( eq_pf_rho(m,n) + eq_pf_J1(m,n) + 2 * eq_pf_J2(m,n) ) ) 00018 00019 #define altQQ(n) ( pow2( gK1(m,n) ) + 2 * pow2( gK2(m,n) ) + pow2( fK1(m,n) ) + 2 * pow2( fK2(m,n) ) \ 00020 + k_g * ( eq_pf_rho(m,n) + eq_pf_J1(m,n) + 2 * eq_pf_J2(m,n) ) ) 00021 00022 /// @todo fixme: Should be k_g/2 in the above. Removing 1/2, however, speeds up the collapse. 00023 /// @todo fixme: Current implementation uses rho + J of the perfect fluid (should use bimetric). 00024 00025 /** Finds the maximal slice using a second-order accurate finite difference scheme. 00026 * 00027 * The maximal slicing involves solving a differential equation: 00028 * <pre> 00029 * y''(x) = P(x) y'(x) + Q(x) y(x) </pre> 00030 * 00031 * with the Neumann BC on the left `y´(0) = 0`, 00032 * and the Dirichlet BC on the right `y(r_1) = 1`. 00033 * 00034 * The solution is found using a linear finite-difference method. 00035 * The algorithm comprises two major steps: 00036 * 00037 * - Prepare the band-diagonal linear system corresponding to a discretized DE. 00038 * 00039 * - Solve the linear system (using Crout's method). 00040 * 00041 * The coefficients for the discretized DE are generated in the Mathematica notebook: 00042 * `Maximal slicing, FD method.nb`. The linear system is solved used Bandec. 00043 */ 00044 void BimetricEvolve::maximalSlice_2 00045 ( 00046 Int m, //!< The time slice 00047 Int N, //!< The radial coordinate of the right boundary 00048 Real gAlp_at_N //!< The right boundary condition 00049 ) 00050 { 00051 const Int offset = nGhost - 1; N = N + 1; 00052 const Real h = delta_r; 00053 MatReal A( N, 3 ); 00054 VecReal b( N ); 00055 00056 ///////////////////////////////////////////////////////////////////////////////////// 00057 00058 OMP_parallel_for( Int i = 0; i < N; ++i ) 00059 { 00060 A[i][0] = -2 - h * PP( offset + i ); 00061 A[i][1] = 4 + 2 * h * h * QQ( offset + i ); 00062 A[i][2] = -2 + h * PP( offset + i ); 00063 b[i] = 0; 00064 } 00065 00066 // Override the default row values 00067 00068 A[0][0] = 0; 00069 A[0][2] = -4; 00070 00071 A[N-1][2] = 0; 00072 b[N-1] = gAlp_at_N * ( 2 - h * PP( offset + (N-1) ) ) ; 00073 00074 ///////////////////////////////////////////////////////////////////////////////////// 00075 00076 VecReal_O x( N ); 00077 BandLUDecomposition( A, 1, 1 ).solve( b, x ); 00078 00079 // Retrieve the results from x 00080 00081 gAlp( m, offset + N ) = gAlp_at_N; 00082 00083 for( Int i = 0; i < N; ++i ) { 00084 gAlp( m, offset + i ) = x[i]; 00085 } 00086 00087 maximalSlice_PostSteps( m, N ); 00088 } 00089 00090 /** Finds the maximal slice using a fourth-order accurate finite difference scheme. 00091 * @see maximalSlice_2 00092 */ 00093 void BimetricEvolve::maximalSlice_4 00094 ( 00095 Int m, //!< The time slice 00096 Int N, //!< The radial coordinate of the right boundary 00097 Real gAlp_at_N //!< The right boundary condition 00098 ) 00099 { 00100 const Int offset = nGhost; 00101 const Real h = delta_r; 00102 MatReal A( N, 7 ); 00103 VecReal b( N ); 00104 00105 ///////////////////////////////////////////////////////////////////////////////////// 00106 00107 OMP_parallel_for( Int i = 0; i < N; ++i ) 00108 { 00109 A[i][0] = 0; 00110 A[i][1] = -1 - h * PP( offset + i ); 00111 A[i][2] = 16 + 8 * h * PP( offset + i ); 00112 A[i][3] = -30 - 12 * h * h * QQ( offset + i ); 00113 A[i][4] = 16 - 8 * h * PP( offset + i ); 00114 A[i][5] = -1 + h * PP( offset + i ); 00115 A[i][6] = 0; 00116 b[i] = 0; 00117 } 00118 00119 // Override the default row values 00120 00121 Int i = 0; 00122 A[i][1] = 0; 00123 A[i][2] = 0; 00124 A[i][3] = -20 + 10 * h * PP( offset + i ) - 12 * h * h * QQ( offset + i ); 00125 A[i][4] = 17 - 15 * h * PP( offset + i ); 00126 A[i][5] = 4 + 6 * h * PP( offset + i ); 00127 A[i][6] = -1 - h * PP( offset + i ); 00128 00129 i = 1; 00130 A[i][1] = 0; 00131 A[i][3] = -31 - h * PP( offset + i ) - 12 * h * h * QQ( offset + i ); 00132 00133 i = N - 2; 00134 A[i][5] = 0; 00135 b[i] = gAlp_at_N * ( 1 - 8 * h * PP( offset + i ) ) ; 00136 00137 i = N - 1; 00138 A[i][0] = -1 + h * PP( offset + i ); 00139 A[i][1] = 4 - 6 * h * PP( offset + i ); 00140 A[i][2] = 6 + 18 * h * PP( offset + i ); 00141 A[i][3] = -20 - 10 * h * PP( offset + i ) - 12 * h * h * QQ( offset + i ); 00142 A[i][4] = 0; 00143 A[i][5] = 0; 00144 b[i] = gAlp_at_N * ( -11 + 3 * h * PP( offset + i ) ) ; 00145 00146 ///////////////////////////////////////////////////////////////////////////////////// 00147 00148 VecReal_O x( N ); 00149 BandLUDecomposition( A, 3, 3 ).solve( b, x ); 00150 00151 // Retrieve the results from x 00152 00153 gAlp( m, offset + N ) = gAlp_at_N; 00154 00155 for( Int i = 0; i < N; ++i ) { 00156 gAlp( m, offset + i ) = x[i]; 00157 } 00158 00159 maximalSlice_PostSteps( m, N ); 00160 } 00161 00162 /** Finds the maximal slice using a second-order accurate finite difference scheme. 00163 * 00164 * This is a slightly optimized version where the differential equation is solved 00165 * using Algorithm 11.3 from Burden & Faires, Numerical Analysis, 2016 00166 * (found in the section Finite-Difference Methods for Linear Problems on p. 700). 00167 * 00168 * @see maximalSlice_2 00169 */ 00170 void BimetricEvolve::maximalSlice_2opt 00171 ( 00172 Int m, //!< The time slice 00173 Int N, //!< The radial coordinate of the right boundary 00174 Real gAlp_at_N //!< The right boundary condition 00175 ) 00176 { 00177 const Int offset = nGhost - 1; N = N + 1; 00178 const Real h = delta_r; 00179 00180 // First, allocate memory used to store the intermediate variables. 00181 // 00182 static Real* workArea = NULL; 00183 static Real *a, *b, *c, *d; // Used in steps 1-3 00184 static Real *l, *u, *z, *w; // Used in steps 4-8 00185 if ( workArea == NULL ) { 00186 size_t chunkSize = nLen + 2 * nGhost; 00187 workArea = new Real[ 8 * chunkSize ]; 00188 a = workArea; b = a + chunkSize; c = b + chunkSize; d = c + chunkSize; 00189 l = d + chunkSize; u = l + chunkSize; z = u + chunkSize; w = z + chunkSize; 00190 } 00191 00192 // Note: a[] = diagonal, b[] = upper band, c[] = lower band, 00193 // and d[] is on the right hand side of the equation A x = d 00194 00195 ///////////////////////////////////////////////////////////////////////////////////// 00196 // Prepare the tridiagonal linear system corresponding to DE. 00197 00198 // Steps 1-3 00199 00200 OMP_parallel_for( Int i = 0; i < N; ++i ) 00201 { 00202 c[i] = -2 - h * PP( offset + i ); 00203 a[i] = 4 + 2 * h * h * QQ( offset + i ); 00204 b[i] = -2 + h * PP( offset + i ); 00205 d[i] = 0; 00206 } 00207 00208 c[0] = 0; 00209 b[0] = -4; // Set by Neumann BC: w{-1} = w_{1} 00210 00211 b[N-1] = 0; 00212 d[N-1] = ( 2 - h * PP( offset + N-1 ) ) * gAlp_at_N; 00213 00214 ///////////////////////////////////////////////////////////////////////////////////// 00215 // Solve the linear system using Crout's factorization. 00216 00217 // Step 4 00218 // 00219 l[0] = a[0]; 00220 u[0] = b[0] / a[0]; 00221 z[0] = d[0] / l[0]; 00222 00223 // Step 5 00224 // 00225 for( Int i = 0; i < N - 1; ++i ) 00226 { 00227 l[i] = a[i] - c[i] * u[i-1]; 00228 u[i] = b[i] / l[i]; 00229 z[i] = ( d[i] - c[i] * z[i-1] ) / l[i]; 00230 } 00231 00232 // Step 6-7 00233 // 00234 Int i = N - 1; 00235 l[i] = a[i] - c[i] * u[i-1]; 00236 gAlp( m, offset + i ) = w[i] = z[i] = ( d[i] - c[i] * z[i-1] ) / l[i]; 00237 00238 // Step 8 00239 // 00240 for( Int i = N - 2; i >= 1; --i ) { 00241 gAlp( m, offset + i ) = w[i] = z[i] - u[i] * w[i+1]; 00242 } 00243 00244 gAlp( m, offset + N ) = w[N] = gAlp_at_N; 00245 00246 maximalSlice_PostSteps( m, N ); 00247 } 00248 00249 /** Additional steps for gAlp and gDAlp (like fixing boundary conditions.) 00250 */ 00251 void BimetricEvolve::maximalSlice_PostSteps( Int m, Int N ) 00252 { 00253 // Fix the ghost cells on the left (using parity BC) 00254 // 00255 for( Int i = 0; i < nGhost; ++i ) { 00256 gAlp( m, nGhost - 1 - i ) = gAlp( m, nGhost + i ); 00257 } 00258 00259 // Determine cells on the right by solving the differential equation. 00260 // 00261 Real h = delta_r; 00262 Int overlap = 10; 00263 for( Int n = nGhost + N - overlap; n < nGhost + nLen + nGhost - 1; ++n ) 00264 { 00265 gAlp( m, n+1 ) = 00266 ( ( 4 + 2 * h * h * QQ(n) ) * gAlp(m,n) - ( 2 + h * PP(n) ) * gAlp(m,n-1) ) 00267 / ( 2 - h * PP(n) ); 00268 } 00269 00270 #if 0 00271 // Alternative smoothing: 00272 // 00273 OMP_parallel_for( Int n = nGhost; n < nTotal - CFDS_ORDER/2; ++n ) { 00274 gAlp_r ( m, n ) = GF_r( gAlp, m, n ); 00275 } 00276 smoothenGF( m, fld::gAlp_r, fld::gdbg, fld::gAlp_r, -1 ); 00277 OMP_parallel_for( Int n = nGhost; n < nTotal - CFDS_ORDER/2; ++n ) { 00278 gDAlp(m,n) = gAlp_r(m,n) / gAlp(m,n); 00279 } 00280 smoothenGF( m, fld::gDAlp, fld::fdbg, fld::gDAlp, -1 ); 00281 return; 00282 #endif 00283 00284 // Calculate gDAlp 00285 // 00286 OMP_parallel_for( Int n = nGhost; n < nTotal - CFDS_ORDER/2; ++n ) 00287 { 00288 gAlp_r ( m, n ) = GF_r( gAlp, m, n ); 00289 /// @todo fixme: add +TINY in the case if zero 00290 gDAlp(m,n) = gAlp_r(m,n) / gAlp(m,n); 00291 } 00292 00293 // The accuracy is very low at low r. Assume that a few first cells are linear, 00294 // then apply a six-point cubic spline to smooth the region around r = 0. 00295 // 00296 cubicSplineShmooth( m, fld::gDAlp, lin2n, cub2n ); 00297 00298 // Fix the left boundary using parity BC 00299 // 00300 for( Int i = 0; i < nGhost; ++i ) { 00301 gDAlp( m, nGhost - 1 - i ) = - gDAlp( m, nGhost + i ); 00302 } 00303 } 00304 00305 #undef PP 00306 #undef QQ