def find_cmax_cmin(field_comp, gamma_faceDD, beta_faceU, alpha_face): # Inputs: flux direction field_comp, Inverse metric gamma_faceUU, shift beta_faceU, # lapse alpha_face, metric determinant gammadet_face # Outputs: maximum and minimum characteristic speeds cmax and cmin # First, we need to find the characteristic speeds on each face gamma_faceUU, unusedgammaDET = ixp.generic_matrix_inverter3x3(gamma_faceDD) find_cp_cm(alpha_face, beta_faceU[field_comp], gamma_faceUU[field_comp][field_comp]) cpr = cplus cmr = cminus find_cp_cm(alpha_face, beta_faceU[field_comp], gamma_faceUU[field_comp][field_comp]) cpl = cplus cml = cminus # The following algorithms have been verified with random floats: global cmax, cmin # Now, we need to set cmax to the larger of cpr,cpl, and 0 import Min_Max_and_Piecewise_Expressions as noif cmax = noif.max_noif(noif.max_noif(cpr, cpl), sp.sympify(0)) # And then, set cmin to the smaller of cmr,cml, and 0 cmin = -noif.min_noif(noif.min_noif(cmr, cml), sp.sympify(0))
def find_cmax_cmin(flux_dirn, gamma_faceDD, beta_faceU, alpha_face): # Inputs: flux direction flux_dirn, Inverse metric gamma_faceUU, shift beta_faceU, # lapse alpha_face, metric determinant gammadet_face # Outputs: maximum and minimum characteristic speeds cmax and cmin # First, we need to find the characteristic speeds on each face gamma_faceUU, unusedgammaDET = ixp.generic_matrix_inverter3x3(gamma_faceDD) find_cp_cm(alpha_face, beta_faceU[flux_dirn], gamma_faceUU[flux_dirn][flux_dirn]) cpr = cplus cmr = cminus find_cp_cm(alpha_face, beta_faceU[flux_dirn], gamma_faceUU[flux_dirn][flux_dirn]) cpl = cplus cml = cminus # The following algorithms have been verified with random floats: global cmax, cmin # Now, we need to set cmax to the larger of cpr,cpl, and 0 cmax = sp.Rational(1, 2) * (cpr + cpl + nrpyAbs(cpr - cpl)) cmax = sp.Rational(1, 2) * (cmax + nrpyAbs(cmax)) # And then, set cmin to the smaller of cmr,cml, and 0 cmin = sp.Rational(1, 2) * (cmr + cml - nrpyAbs(cmr - cml)) cmin = -sp.Rational(1, 2) * (cmin - nrpyAbs(cmin))
def Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear( CoordType_in, Sph_r_th_ph_or_Cart_xyz, gammaDD_inSphorCart, KDD_inSphorCart, alpha_inSphorCart, betaU_inSphorCart, BU_inSphorCart): # This routine converts the ADM variables # $$\left\{\gamma_{ij}, K_{ij}, \alpha, \beta^i\right\}$$ # in Spherical or Cartesian basis+coordinates, first to the BSSN variables # in the chosen reference_metric::CoordSystem coordinate system+basis: # $$\left\{\bar{\gamma}_{i j},\bar{A}_{i j},\phi, K, \bar{\Lambda}^{i}, \alpha, \beta^i, B^i\right\},$$ # and then to the rescaled variables: # $$\left\{h_{i j},a_{i j},\phi, K, \lambda^{i}, \alpha, \mathcal{V}^i, \mathcal{B}^i\right\}.$$ # The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations. # To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in # the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3. # Step P1: Set spatial dimension (must be 3 for BSSN) DIM = 3 # Step P2: Copy gammaSphDD_in to gammaSphDD, KSphDD_in to KSphDD, etc. # This ensures that the input arrays are not modified below; # modifying them would result in unexpected effects outside # this function. alphaSphorCart = alpha_inSphorCart betaSphorCartU = ixp.zerorank1() BSphorCartU = ixp.zerorank1() gammaSphorCartDD = ixp.zerorank2() KSphorCartDD = ixp.zerorank2() for i in range(DIM): betaSphorCartU[i] = betaU_inSphorCart[i] BSphorCartU[i] = BU_inSphorCart[i] for j in range(DIM): gammaSphorCartDD[i][j] = gammaDD_inSphorCart[i][j] KSphorCartDD[i][j] = KDD_inSphorCart[i][j] # Make sure that rfm.reference_metric() has been called. # We'll need the variables it defines throughout this module. if rfm.have_already_called_reference_metric_function == False: print( "Error. Called Convert_Spherical_ADM_to_BSSN_curvilinear() without" ) print( " first setting up reference metric, by calling rfm.reference_metric()." ) sys.exit(1) # Step 1: All input quantitiefs are in terms of r,th,ph or x,y,z. We want them in terms # of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace # r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2 # as defined for this particular reference metric in reference_metric.py's # xxSph[] or xx_to_Cart[], respectively: # Note that substitution only works when the variable is not an integer. Hence the # if isinstance(...,...) stuff: def sympify_integers__replace_rthph_or_Cartxyz(obj, rthph_or_xyz, rthph_or_xyz_of_xx): if isinstance(obj, int): return sp.sympify(obj) return obj.subs(rthph_or_xyz[0], rthph_or_xyz_of_xx[0]).\ subs(rthph_or_xyz[1], rthph_or_xyz_of_xx[1]).\ subs(rthph_or_xyz[2], rthph_or_xyz_of_xx[2]) r_th_ph_or_Cart_xyz_of_xx = [] if CoordType_in == "Spherical": r_th_ph_or_Cart_xyz_of_xx = rfm.xxSph elif CoordType_in == "Cartesian": r_th_ph_or_Cart_xyz_of_xx = rfm.xx_to_Cart else: print( "Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords." ) sys.exit(1) alphaSphorCart = sympify_integers__replace_rthph_or_Cartxyz( alphaSphorCart, Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx) for i in range(DIM): betaSphorCartU[i] = sympify_integers__replace_rthph_or_Cartxyz( betaSphorCartU[i], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx) BSphorCartU[i] = sympify_integers__replace_rthph_or_Cartxyz( BSphorCartU[i], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx) for j in range(DIM): gammaSphorCartDD[i][ j] = sympify_integers__replace_rthph_or_Cartxyz( gammaSphorCartDD[i][j], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx) KSphorCartDD[i][j] = sympify_integers__replace_rthph_or_Cartxyz( KSphorCartDD[i][j], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx) # Step 2: All ADM initial data quantities are now functions of xx0,xx1,xx2, but # they are still in the Spherical or Cartesian basis. We can now directly apply # Jacobian transformations to get them in the correct xx0,xx1,xx2 basis: # Make these globals so we can recover the ADM quantities in the destination basis if desired. global alpha, betaU, BU, gammaDD, KDD # alpha is a scalar, so no Jacobian transformation is necessary. alpha = alphaSphorCart Jac_dUSphorCart_dDrfmUD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): # Converting UIUCBlackHole ID to Cartesian coords takes 66.8s if the following isn't simplified: # Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff(r_th_ph_or_Cart_xyz_of_xx[i],rfm.xx[j]) # ... but when we simplify each term, the total conversion time is reduced to 60.93s Jac_dUSphorCart_dDrfmUD[i][j] = sp.simplify( sp.diff(r_th_ph_or_Cart_xyz_of_xx[i], rfm.xx[j])) Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3( Jac_dUSphorCart_dDrfmUD) betaU = ixp.zerorank1() BU = ixp.zerorank1() gammaDD = ixp.zerorank2() KDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j] BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j] for k in range(DIM): for l in range(DIM): gammaDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][ i] * Jac_dUSphorCart_dDrfmUD[l][j] * gammaSphorCartDD[ k][l] KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][ i] * Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l] # Step 3: All ADM quantities were input into this function in the Spherical or Cartesian # basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above, # we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2. # Here we convert ADM quantities in the "rfm" basis to their BSSN Curvilinear # counterparts: import BSSN.BSSN_in_terms_of_ADM as BitoA BitoA.gammabarDD_hDD(gammaDD) BitoA.trK_AbarDD_aDD(gammaDD, KDD) BitoA.LambdabarU_lambdaU__exact_gammaDD(gammaDD) BitoA.cf_from_gammaDD(gammaDD) BitoA.betU_vetU(betaU, BU) # Step 4: Return the BSSN Curvilinear variables in the desired xx0,xx1,xx2 # basis, and as functions of the consistent xx0,xx1,xx2 coordinates. return BitoA.cf, BitoA.hDD, BitoA.lambdaU, BitoA.aDD, BitoA.trK, alpha, BitoA.vetU, BitoA.betU
def write_dfdr_function(self, Ccodesdir, fd_order=2): # function to write c code to calculate dfdr term in Sommerfeld boundary condition # Read what # of dimensions being usded DIM = par.parval_from_str("grid::DIM") # Set up the chosen reference metric from chosen coordinate system, set within NRPy+ CoordSystem = par.parval_from_str("reference_metric::CoordSystem") rfm.reference_metric() # Simplifying the results make them easier to interpret. do_simplify = True if "Sinh" in CoordSystem: # Simplification takes too long on Sinh* coordinate systems do_simplify = False # Construct Jacobian matrix, output Jac_dUSph_dDrfmUD[i][j] = \partial x_{Sph}^i / \partial x^j: Jac_dUSph_dDrfmUD = ixp.zerorank2() for i in range(3): for j in range(3): Jac_dUSph_dDrfmUD[i][j] = sp.diff(rfm.xxSph[i], rfm.xx[j]) # Invert Jacobian matrix, output to Jac_dUrfm_dDSphUD. Jac_dUrfm_dDSphUD, dummyDET = ixp.generic_matrix_inverter3x3( Jac_dUSph_dDrfmUD) # Jac_dUrfm_dDSphUD[i][0] stores \partial x^i / \partial r if do_simplify: for i in range(3): Jac_dUrfm_dDSphUD[i][0] = sp.simplify(Jac_dUrfm_dDSphUD[i][0]) # Declare \partial_i f, which is actually computed later on fdD = ixp.declarerank1("fdD") # = [fdD0, fdD1, fdD2] contraction = sp.sympify(0) for i in range(3): contraction += fdD[i] * Jac_dUrfm_dDSphUD[i][0] contraction = sp.simplify(contraction) r_str_and_contraction_str = outputC([rfm.xxSph[0], contraction], ["*_r", "*_partial_i_f"], filename="returnstring", params="includebraces=False") def gen_central_fd_stencil_str(intdirn, fd_order): if fd_order == 2: if intdirn == 0: return "(gfs[IDX4S(which_gf,i0+1,i1,i2)]-gfs[IDX4S(which_gf,i0-1,i1,i2)])*0.5" # Does not include the 1/dx multiplication elif intdirn == 1: return "(gfs[IDX4S(which_gf,i0,i1+1,i2)]-gfs[IDX4S(which_gf,i0,i1-1,i2)])*0.5" # Does not include the 1/dy multiplication elif intdirn == 2: return "(gfs[IDX4S(which_gf,i0,i1,i2+1)]-gfs[IDX4S(which_gf,i0,i1,i2-1)])*0.5" # Does not include the 1/dz multiplication def output_dfdx(intdirn, fd_order): dirn = str(intdirn) dirnp1 = str( (intdirn + 1) % 3 ) # if dirn='0', then we want this to be '1'; '1' then '2'; and '2' then '0' dirnp2 = str( (intdirn + 2) % 3 ) # if dirn='0', then we want this to be '2'; '1' then '0'; and '2' then '1' if fd_order == 2: return """ // On a +x""" + dirn + """ or -x""" + dirn + """ face, do up/down winding as appropriate: if(abs(FACEXi[""" + dirn + """])==1 || i""" + dirn + """+NGHOSTS >= Nxx_plus_2NGHOSTS""" + dirn + """ || i""" + dirn + """-NGHOSTS <= 0) { int8_t SHIFTSTENCIL""" + dirn + """ = FACEXi[""" + dirn + """]; if(i""" + dirn + """+NGHOSTS >= Nxx_plus_2NGHOSTS""" + dirn + """) SHIFTSTENCIL""" + dirn + """ = -1; if(i""" + dirn + """-NGHOSTS <= 0) SHIFTSTENCIL""" + dirn + """ = +1; SHIFTSTENCIL""" + dirnp1 + """ = 0; SHIFTSTENCIL""" + dirnp2 + """ = 0; fdD""" + dirn + """ = SHIFTSTENCIL""" + dirn + """*(-1.5*gfs[IDX4S(which_gf,i0+0*SHIFTSTENCIL0,i1+0*SHIFTSTENCIL1,i2+0*SHIFTSTENCIL2)] +2.*gfs[IDX4S(which_gf,i0+1*SHIFTSTENCIL0,i1+1*SHIFTSTENCIL1,i2+1*SHIFTSTENCIL2)] -0.5*gfs[IDX4S(which_gf,i0+2*SHIFTSTENCIL0,i1+2*SHIFTSTENCIL1,i2+2*SHIFTSTENCIL2)] )*invdx""" + dirn + """; // Not on a +x""" + dirn + """ or -x""" + dirn + """ face, using centered difference: } else { fdD""" + dirn + """ = """ + gen_central_fd_stencil_str( intdirn, 2) + """*invdx""" + dirn + """; } """ else: print("Error: fd_order = " + str(fd_order) + " currently unsupported.") sys.exit(1) contraction_term_func = """ void contraction_term(const paramstruct *restrict params, const int which_gf, const REAL *restrict gfs, REAL *restrict xx[3], const int8_t FACEXi[3], const int i0, const int i1, const int i2, REAL *restrict _r, REAL *restrict _partial_i_f) { #include "RELATIVE_PATH__set_Cparameters.h" /* Header file containing correct #include for set_Cparameters.h; * accounting for the relative path */ // Initialize derivatives to crazy values, to ensure that // we will notice in case they aren't set properly. REAL fdD0=1e100; REAL fdD1=1e100; REAL fdD2=1e100; REAL xx0 = xx[0][i0]; REAL xx1 = xx[1][i1]; REAL xx2 = xx[2][i2]; int8_t SHIFTSTENCIL0; int8_t SHIFTSTENCIL1; int8_t SHIFTSTENCIL2; """ for i in range(DIM): if "fdD" + str(i) in r_str_and_contraction_str: contraction_term_func += output_dfdx(i, fd_order) contraction_term_func += "\n" + r_str_and_contraction_str contraction_term_func += """ } // END contraction_term function """ with open( os.path.join(Ccodesdir, "boundary_conditions/radial_derivative.h"), "w") as file: file.write(contraction_term_func)
def Psi4_tetradsv2(): global l4U, n4U, mre4U, mim4U # Step 1.c: Check if tetrad choice is implemented: if par.parval_from_str(thismodule + "::TetradChoice") != "QuasiKinnersley": print("ERROR: " + thismodule + "::TetradChoice = " + par.parval_from_str("TetradChoice") + " currently unsupported!") exit(1) # Step 1.d: Given the chosen coordinate system, set up # corresponding reference metric and needed # reference metric quantities # The following function call sets up the reference metric # and related quantities, including rescaling matrices ReDD, # ReU, and hatted quantities. rfm.reference_metric() # Step 1.e: Set spatial dimension (must be 3 for BSSN, as BSSN is # a 3+1-dimensional decomposition of the general # relativistic field equations) DIM = 3 # Step 1.f: Import all ADM quantities as written in terms of BSSN quantities import BSSN.ADM_in_terms_of_BSSN as AB AB.ADM_in_terms_of_BSSN() # Step 2.a: Declare the Cartesian x,y,z as input parameters # and v_1^a, v_2^a, and v_3^a tetrads, # as well as detgamma and gammaUU from # BSSN.ADM_in_terms_of_BSSN x, y, z = par.Cparameters("REAL", thismodule, ["x", "y", "z"]) v1UCart = ixp.zerorank1() v2UCart = ixp.zerorank1() # detgamma = AB.detgamma # gammaUU = AB.gammaUU # Step 2.b: Define v1U and v2U v1UCart = [-y, x, sp.sympify(0)] v2UCart = [x, y, z] # Step 2.c: Construct the Jacobian d x_Cart^i / d xx^j Jac_dUCart_dDrfmUD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): Jac_dUCart_dDrfmUD[i][j] = sp.diff(rfm.xxCart[i], rfm.xx[j]) # Step 2.d: Invert above Jacobian to get needed d xx^j / d x_Cart^i Jac_dUrfm_dDCartUD, dummyDET = ixp.generic_matrix_inverter3x3( Jac_dUCart_dDrfmUD) # Step 2.e: Transform gammaDD to Cartesian basis: gammaCartDD = ixp.zerorank2() gammaDD = AB.gammaDD for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): gammaCartDD[i][j] += Jac_dUrfm_dDCartUD[k][ i] * Jac_dUrfm_dDCartUD[l][j] * gammaDD[k][l] gammaCartUU, detgammaCart = ixp.symm_matrix_inverter3x3(gammaCartDD) # Step 2.e: Transform v1U and v2U from the Cartesian to the xx^i basis v1U = ixp.zerorank1() v2U = ixp.zerorank1() for i in range(DIM): v1U[i] = v1UCart[i] v2U[i] = v2UCart[i] # for i in range(DIM): # for j in range(DIM): # v1U[i] += Jac_dUrfm_dDCartUD[i][j]*v1UCart[j] # v2U[i] += Jac_dUrfm_dDCartUD[i][j]*v2UCart[j] # Step 2.f: Define the rank-3 version of the Levi-Civita symbol. Amongst # other uses, this is needed for the construction of the approximate # quasi-Kinnersley tetrad. def define_LeviCivitaSymbol_rank3(DIM=-1): if DIM == -1: DIM = par.parval_from_str("DIM") LeviCivitaSymbol = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): # From https://codegolf.stackexchange.com/questions/160359/levi-civita-symbol : LeviCivitaSymbol[i][j][k] = (i - j) * (j - k) * (k - i) / 2 return LeviCivitaSymbol # Step 2.g: Define v3U v3U = ixp.zerorank1() LeviCivitaSymbolDDD = define_LeviCivitaSymbol_rank3(DIM=3) for a in range(DIM): for b in range(DIM): for c in range(DIM): for d in range(DIM): v3U[a] += sp.sqrt(detgammaCart) * gammaCartUU[a][ d] * LeviCivitaSymbolDDD[d][b][c] * v1U[b] * v2U[c] # Step 2.h: Define omega_{ij} omegaDD = ixp.zerorank2() # Step 2.i: Define e^a_i. Note that: # omegaDD[0][0] = \omega_{11} above; # omegaDD[1][1] = \omega_{22} above, etc. e1U = ixp.zerorank1() e2U = ixp.zerorank1() e3U = ixp.zerorank1() update_omega(omegaDD, v1U, v2U, v3U, gammaCartDD) for a in range(DIM): e1U[a] = v1U[a] / sp.sqrt(omegaDD[0][0]) update_omega(omegaDD, e1U, v2U, v3U, gammaCartDD) for a in range(DIM): e2U[a] = (v2U[a] - omegaDD[0][1] * e1U[a]) / sp.sqrt(omegaDD[1][1]) update_omega(omegaDD, e1U, e2U, v3U, gammaCartDD) for a in range(DIM): e3U[a] = (v3U[a] - omegaDD[0][2] * e1U[a] - omegaDD[1][2] * e2U[a]) / sp.sqrt(omegaDD[2][2]) # Step 2.j: Construct l^mu, n^mu, and m^mu, based on r^mu, theta^mu, phi^mu, and u^mu: rCart4U = ixp.zerorank1(DIM=4) thetaCart4U = ixp.zerorank1(DIM=4) phiCart4U = ixp.zerorank1(DIM=4) for a in range(DIM): rCart4U[a + 1] = e2U[a] thetaCart4U[a + 1] = e3U[a] phiCart4U[a + 1] = e1U[a] r4U = ixp.zerorank1(DIM=4) theta4U = ixp.zerorank1(DIM=4) phi4U = ixp.zerorank1(DIM=4) for a in range(DIM): for b in range(DIM): r4U[a + 1] += Jac_dUrfm_dDCartUD[a][b] * rCart4U[b + 1] theta4U[a + 1] += Jac_dUrfm_dDCartUD[a][b] * thetaCart4U[b + 1] phi4U[a + 1] += Jac_dUrfm_dDCartUD[a][b] * phiCart4U[b + 1] u4U = ixp.zerorank1(DIM=4) # FIXME? assumes alpha=1, beta^i = 0 u4U[0] = 1 l4U = ixp.zerorank1(DIM=4) n4U = ixp.zerorank1(DIM=4) mre4U = ixp.zerorank1(DIM=4) mim4U = ixp.zerorank1(DIM=4) isqrt2 = 1 / sp.sqrt(2) for mu in range(4): l4U[mu] = isqrt2 * (u4U[mu] + r4U[mu]) n4U[mu] = isqrt2 * (u4U[mu] - r4U[mu]) mre4U[mu] = isqrt2 * theta4U[mu] mim4U[mu] = isqrt2 * phi4U[mu]
def Tmunu_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear( CoordType_in, Tmunu_input_function_name, pointer_to_ID_inputs=False): # The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations. # To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in # the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3. # Step 0: Set spatial dimension (must be 3 for BSSN) DIM = 3 # Step 1: Define the input variables: the 4D stress-energy tensor, and the ADM 3-metric, lapse, & shift: T4SphorCartUU = ixp.declarerank2("T4SphorCartUU", "sym01", DIM=4) gammaSphorCartDD = ixp.declarerank2("gammaSphorCartDD", "sym01") alphaSphorCart = sp.symbols("alphaSphorCart") betaSphorCartU = ixp.declarerank1("betaSphorCartU") # Step 2: All Tmunu initial data quantities are functions of xx0,xx1,xx2, but # in the Spherical or Cartesian basis. # We first define the BSSN stress-energy source terms in the Spherical # or Cartesian basis, respectively. # To get \gamma_{\mu \nu} = gammabar4DD[mu][nu], we'll need to construct the 4-metric, using Eq. 2.122 in B&S: # S_{ij} = \gamma_{i \mu} \gamma_{j \nu} T^{\mu \nu} # S_{i} = -\gamma_{i\mu} n_\nu T^{\mu\nu} # S = \gamma^{ij} S_{ij} # rho = n_\mu n_\nu T^{\mu\nu}, # where # \gamma_{\mu\nu} = g_{\mu\nu} + n_\mu n_\nu # and # n_mu = {-\alpha,0,0,0}, # Step 2.1: Construct the 4-metric based on the input ADM quantities. # This is provided by Eq 4.47 in [Gourgoulhon](https://arxiv.org/pdf/gr-qc/0703035.pdf): # g_{tt} = -\alpha^2 + \beta^k \beta_k # g_{ti} = \beta_i # g_{ij} = \gamma_{ij} # Eq. 2.121 in B&S betaSphorCartD = ixp.zerorank1() for i in range(DIM): for j in range(DIM): betaSphorCartD[i] += gammaSphorCartDD[i][j] * betaSphorCartU[j] # Now compute the beta contraction. beta2 = sp.sympify(0) for i in range(DIM): beta2 += betaSphorCartU[i] * betaSphorCartD[i] # Eq. 2.122 in B&S g4SphorCartDD = ixp.zerorank2(DIM=4) g4SphorCartDD[0][0] = -alphaSphorCart**2 + beta2 for i in range(DIM): g4SphorCartDD[i + 1][0] = g4SphorCartDD[0][i + 1] = betaSphorCartD[i] for i in range(DIM): for j in range(DIM): g4SphorCartDD[i + 1][j + 1] = gammaSphorCartDD[i][j] # Step 2.2: Construct \gamma_{mu nu} = g_{mu nu} + n_mu n_nu: n4SphorCartD = ixp.zerorank1(DIM=4) n4SphorCartD[0] = -alphaSphorCart gamma4SphorCartDD = ixp.zerorank2(DIM=4) for mu in range(4): for nu in range(4): gamma4SphorCartDD[mu][nu] = g4SphorCartDD[mu][ nu] + n4SphorCartD[mu] * n4SphorCartD[nu] # Step 2.3: We now have all we need to construct the BSSN source # terms in the current basis (Spherical or Cartesian): # S_{ij} = \gamma_{i \mu} \gamma_{j \nu} T^{\mu \nu} # S_{i} = -\gamma_{i\mu} n_\nu T^{\mu\nu} # S = \gamma^{ij} S_{ij} # rho = n_\mu n_\nu T^{\mu\nu}, SSphorCartDD = ixp.zerorank2() SSphorCartD = ixp.zerorank1() SSphorCart = sp.sympify(0) rhoSphorCart = sp.sympify(0) for i in range(DIM): for j in range(DIM): for mu in range(4): for nu in range(4): SSphorCartDD[i][j] += gamma4SphorCartDD[ i + 1][mu] * gamma4SphorCartDD[ j + 1][nu] * T4SphorCartUU[mu][nu] for i in range(DIM): for mu in range(4): for nu in range(4): SSphorCartD[i] += -gamma4SphorCartDD[ i + 1][mu] * n4SphorCartD[nu] * T4SphorCartUU[mu][nu] gammaSphorCartUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaSphorCartDD) for i in range(DIM): for j in range(DIM): SSphorCart += gammaSphorCartUU[i][j] * SSphorCartDD[i][j] for mu in range(4): for nu in range(4): rhoSphorCart += n4SphorCartD[mu] * n4SphorCartD[ nu] * T4SphorCartUU[mu][nu] # Step 3: Perform basis conversion to # Make sure that rfm.reference_metric() has been called. # We'll need the variables it defines throughout this module. if rfm.have_already_called_reference_metric_function == False: print( "Error. Called Tmunu_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear() without" ) print( " first setting up reference metric, by calling rfm.reference_metric()." ) exit(1) # Step 1: All input quantities are in terms of r,th,ph or x,y,z. We want them in terms # of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace # r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2 # as defined for this particular reference metric in reference_metric.py's # xxSph[] or xxCart[], respectively: r_th_ph_or_Cart_xyz_oID_xx = [] if CoordType_in == "Spherical": r_th_ph_or_Cart_xyz_oID_xx = rfm.xxSph elif CoordType_in == "Cartesian": r_th_ph_or_Cart_xyz_oID_xx = rfm.xxCart else: print( "Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords." ) exit(1) # Next apply Jacobian transformations to convert into the (xx0,xx1,xx2) basis # alpha is a scalar, so no Jacobian transformation is necessary. alpha = alphaSphorCart Jac_dUSphorCart_dDrfmUD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff( r_th_ph_or_Cart_xyz_oID_xx[i], rfm.xx[j]) Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3( Jac_dUSphorCart_dDrfmUD) betaU = ixp.zerorank1() BU = ixp.zerorank1() gammaSphorCartDD = ixp.zerorank2() KDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j] BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j] for k in range(DIM): for l in range(DIM): gammaSphorCartDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * \ gammaSphorCartDD[k][l] KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][ i] * Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l] # Step 3: All ADM quantities were input into this function in the Spherical or Cartesian # basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above, # we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2. # Here we convert ADM quantities to their BSSN Curvilinear counterparts: # Step 3.1: Convert ADM $\gamma_{ij}$ to BSSN $\bar{\gamma}_{ij}$: # We have (Eqs. 2 and 3 of [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)): # \bar{\gamma}_{i j} = \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \gamma_{ij}. gammaSphorCartUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaSphorCartDD) gammabarDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): gammabarDD[i][j] = (rfm.detgammahat / gammaDET)**(sp.Rational( 1, 3)) * gammaSphorCartDD[i][j] # Step 3.2: Convert the extrinsic curvature $K_{ij}$ to the trace-free extrinsic # curvature $\bar{A}_{ij}$, plus the trace of the extrinsic curvature $K$, # where (Eq. 3 of [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)): # K = \gamma^{ij} K_{ij}, and # \bar{A}_{ij} &= \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \left(K_{ij} - \frac{1}{3} \gamma_{ij} K \right) trK = sp.sympify(0) for i in range(DIM): for j in range(DIM): trK += gammaSphorCartUU[i][j] * KDD[i][j] AbarDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): AbarDD[i][j] = (rfm.detgammahat / gammaDET)**(sp.Rational( 1, 3)) * (KDD[i][j] - sp.Rational(1, 3) * gammaSphorCartDD[i][j] * trK) # Step 3.3: Set the conformal factor variable $\texttt{cf}$, which is set # by the "BSSN_RHSs::ConformalFactor" parameter. For example if # "ConformalFactor" is set to "phi", we can use Eq. 3 of # [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf), # which in arbitrary coordinates is written: # \phi = \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right). # Alternatively if "BSSN_RHSs::ConformalFactor" is set to "chi", then # \chi = e^{-4 \phi} = \exp\left(-4 \frac{1}{12} \left(\frac{\gamma}{\bar{\gamma}}\right)\right) # = \exp\left(-\frac{1}{3} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/3}. # # Finally if "BSSN_RHSs::ConformalFactor" is set to "W", then # W = e^{-2 \phi} = \exp\left(-2 \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = # \exp\left(-\frac{1}{6} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = # \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/6}. # First compute gammabarDET: gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD) cf = sp.sympify(0) if par.parval_from_str("ConformalFactor") == "phi": cf = sp.Rational(1, 12) * sp.log(gammaDET / gammabarDET) elif par.parval_from_str("ConformalFactor") == "chi": cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 3)) elif par.parval_from_str("ConformalFactor") == "W": cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 6)) else: print("Error ConformalFactor type = \"" + par.parval_from_str("ConformalFactor") + "\" unknown.") exit(1) # Step 4: Rescale tensorial quantities according to the prescription described in # the [BSSN in curvilinear coordinates tutorial module](Tutorial-BSSNCurvilinear.ipynb) # (also [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)): # # h_{ij} &= (\bar{\gamma}_{ij} - \hat{\gamma}_{ij})/\text{ReDD[i][j]}\\ # a_{ij} &= \bar{A}_{ij}/\text{ReDD[i][j]}\\ # \lambda^i &= \bar{\Lambda}^i/\text{ReU[i]}\\ # \mathcal{V}^i &= \beta^i/\text{ReU[i]}\\ # \mathcal{B}^i &= B^i/\text{ReU[i]}\\ hDD = ixp.zerorank2() aDD = ixp.zerorank2() vetU = ixp.zerorank1() betU = ixp.zerorank1() for i in range(DIM): vetU[i] = betaU[i] / rfm.ReU[i] betU[i] = BU[i] / rfm.ReU[i] for j in range(DIM): hDD[i][j] = (gammabarDD[i][j] - rfm.ghatDD[i][j]) / rfm.ReDD[i][j] aDD[i][j] = AbarDD[i][j] / rfm.ReDD[i][j] # Step 5: Output all ADM-to-BSSN expressions to a C function. This function # must first call the ID_ADM_SphorCart() defined above. Using these # Spherical or Cartesian data, it sets up all quantities needed for # BSSNCurvilinear initial data, *except* $\lambda^i$, which must be # computed from numerical data using finite-difference derivatives. with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", "w") as file: file.write( "void ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(const REAL xx0xx1xx2[3]," ) if pointer_to_ID_inputs == True: file.write("ID_inputs *other_inputs,") else: file.write("ID_inputs other_inputs,") file.write(""" REAL *hDD00,REAL *hDD01,REAL *hDD02,REAL *hDD11,REAL *hDD12,REAL *hDD22, REAL *aDD00,REAL *aDD01,REAL *aDD02,REAL *aDD11,REAL *aDD12,REAL *aDD22, REAL *trK, REAL *vetU0,REAL *vetU1,REAL *vetU2, REAL *betU0,REAL *betU1,REAL *betU2, REAL *alpha, REAL *cf) { REAL gammaSphorCartDD00,gammaSphorCartDD01,gammaSphorCartDD02, gammaSphorCartDD11,gammaSphorCartDD12,gammaSphorCartDD22; REAL KSphorCartDD00,KSphorCartDD01,KSphorCartDD02, KSphorCartDD11,KSphorCartDD12,KSphorCartDD22; REAL alphaSphorCart,betaSphorCartU0,betaSphorCartU1,betaSphorCartU2; REAL BSphorCartU0,BSphorCartU1,BSphorCartU2; const REAL xx0 = xx0xx1xx2[0]; const REAL xx1 = xx0xx1xx2[1]; const REAL xx2 = xx0xx1xx2[2]; REAL xyz_or_rthph[3];\n""") outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" outputC(r_th_ph_or_Cart_xyz_oID_xx[0:3], ["xyz_or_rthph[0]", "xyz_or_rthph[1]", "xyz_or_rthph[2]"], "BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", outCparams + ",CSE_enable=False") with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", "a") as file: file.write(" " + ADM_input_function_name + """(xyz_or_rthph, other_inputs, &gammaSphorCartDD00,&gammaSphorCartDD01,&gammaSphorCartDD02, &gammaSphorCartDD11,&gammaSphorCartDD12,&gammaSphorCartDD22, &KSphorCartDD00,&KSphorCartDD01,&KSphorCartDD02, &KSphorCartDD11,&KSphorCartDD12,&KSphorCartDD22, &alphaSphorCart,&betaSphorCartU0,&betaSphorCartU1,&betaSphorCartU2, &BSphorCartU0,&BSphorCartU1,&BSphorCartU2); // Next compute all rescaled BSSN curvilinear quantities:\n""") outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" outputC([ hDD[0][0], hDD[0][1], hDD[0][2], hDD[1][1], hDD[1][2], hDD[2][2], aDD[0][0], aDD[0][1], aDD[0][2], aDD[1][1], aDD[1][2], aDD[2][2], trK, vetU[0], vetU[1], vetU[2], betU[0], betU[1], betU[2], alpha, cf ], [ "*hDD00", "*hDD01", "*hDD02", "*hDD11", "*hDD12", "*hDD22", "*aDD00", "*aDD01", "*aDD02", "*aDD11", "*aDD12", "*aDD22", "*trK", "*vetU0", "*vetU1", "*vetU2", "*betU0", "*betU1", "*betU2", "*alpha", "*cf" ], "BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", params=outCparams) with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", "a") as file: file.write("}\n") # Step 5.A: Output the driver function for the above # function ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs() # Next write the driver function for ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(): with open("BSSN/ID_BSSN__ALL_BUT_LAMBDAs.h", "w") as file: file.write( "void ID_BSSN__ALL_BUT_LAMBDAs(const int Nxx_plus_2NGHOSTS[3],REAL *xx[3]," ) if pointer_to_ID_inputs == True: file.write("ID_inputs *other_inputs,") else: file.write("ID_inputs other_inputs,") file.write("REAL *in_gfs) {\n") file.write( lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], [ "Nxx_plus_2NGHOSTS[2]", "Nxx_plus_2NGHOSTS[1]", "Nxx_plus_2NGHOSTS[0]" ], ["1", "1", "1"], [ "#pragma omp parallel for", " const REAL xx2 = xx[2][i2];", " const REAL xx1 = xx[1][i1];" ], "", """const REAL xx0 = xx[0][i0]; const int idx = IDX3(i0,i1,i2); const REAL xx0xx1xx2[3] = {xx0,xx1,xx2}; ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(xx0xx1xx2,other_inputs, &in_gfs[IDX4pt(HDD00GF,idx)],&in_gfs[IDX4pt(HDD01GF,idx)],&in_gfs[IDX4pt(HDD02GF,idx)], &in_gfs[IDX4pt(HDD11GF,idx)],&in_gfs[IDX4pt(HDD12GF,idx)],&in_gfs[IDX4pt(HDD22GF,idx)], &in_gfs[IDX4pt(ADD00GF,idx)],&in_gfs[IDX4pt(ADD01GF,idx)],&in_gfs[IDX4pt(ADD02GF,idx)], &in_gfs[IDX4pt(ADD11GF,idx)],&in_gfs[IDX4pt(ADD12GF,idx)],&in_gfs[IDX4pt(ADD22GF,idx)], &in_gfs[IDX4pt(TRKGF,idx)], &in_gfs[IDX4pt(VETU0GF,idx)],&in_gfs[IDX4pt(VETU1GF,idx)],&in_gfs[IDX4pt(VETU2GF,idx)], &in_gfs[IDX4pt(BETU0GF,idx)],&in_gfs[IDX4pt(BETU1GF,idx)],&in_gfs[IDX4pt(BETU2GF,idx)], &in_gfs[IDX4pt(ALPHAGF,idx)],&in_gfs[IDX4pt(CFGF,idx)]); """)) file.write("}\n") # Step 6: Compute $\bar{\Lambda}^i$ (Eqs. 4 and 5 of # [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)), # from finite-difference derivatives of rescaled metric # quantities $h_{ij}$: # \bar{\Lambda}^i = \bar{\gamma}^{jk}\left(\bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk}\right). # The reference_metric.py module provides us with analytic expressions for # $\hat{\Gamma}^i_{jk}$, so here we need only compute # finite-difference expressions for $\bar{\Gamma}^i_{jk}$, based on # the values for $h_{ij}$ provided in the initial data. Once # $\bar{\Lambda}^i$ has been computed, we apply the usual rescaling # procedure: # \lambda^i = \bar{\Lambda}^i/\text{ReU[i]}, # and then output the result to a C file using the NRPy+ # finite-difference C output routine. # We will need all BSSN gridfunctions to be defined, as well as # expressions for gammabarDD_dD in terms of exact derivatives of # the rescaling matrix and finite-difference derivatives of # hDD's. gammabarDD = bssnrhs.gammabarDD gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD) gammabarDD_dD = bssnrhs.gammabarDD_dD # Next compute Christoffel symbols \bar{\Gamma}^i_{jk}: GammabarUDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): GammabarUDD[i][j][k] += sp.Rational( 1, 2) * gammabarUU[i][l] * (gammabarDD_dD[l][j][k] + gammabarDD_dD[l][k][j] - gammabarDD_dD[j][k][l]) # Next evaluate \bar{\Lambda}^i, based on GammabarUDD above and GammahatUDD # (from the reference metric): LambdabarU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): LambdabarU[i] += gammabarUU[j][k] * ( GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k]) # Finally apply rescaling: # lambda^i = Lambdabar^i/\text{ReU[i]} lambdaU = ixp.zerorank1() for i in range(DIM): lambdaU[i] = LambdabarU[i] / rfm.ReU[i] outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" lambdaU_expressions = [ lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU0"), rhs=lambdaU[0]), lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU1"), rhs=lambdaU[1]), lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU2"), rhs=lambdaU[2]) ] lambdaU_expressions_FDout = fin.FD_outputC("returnstring", lambdaU_expressions, outCparams) with open("BSSN/ID_BSSN_lambdas.h", "w") as file: file.write(""" void ID_BSSN_lambdas(const int Nxx[3],const int Nxx_plus_2NGHOSTS[3],REAL *xx[3],const REAL dxx[3],REAL *in_gfs) {\n""" ) file.write( lp.loop(["i2", "i1", "i0"], ["NGHOSTS", "NGHOSTS", "NGHOSTS"], ["NGHOSTS+Nxx[2]", "NGHOSTS+Nxx[1]", "NGHOSTS+Nxx[0]"], ["1", "1", "1"], [ "const REAL invdx0 = 1.0/dxx[0];\n" + "const REAL invdx1 = 1.0/dxx[1];\n" + "const REAL invdx2 = 1.0/dxx[2];\n" + "#pragma omp parallel for", " const REAL xx2 = xx[2][i2];", " const REAL xx1 = xx[1][i1];" ], "", "const REAL xx0 = xx[0][i0];\n" + lambdaU_expressions_FDout)) file.write("}\n")
def Psi4_tetrads(): global l4U, n4U, mre4U, mim4U # Step 1.c: Check if tetrad choice is implemented: if par.parval_from_str(thismodule + "::TetradChoice") != "QuasiKinnersley": print("ERROR: " + thismodule + "::TetradChoice = " + par.parval_from_str("TetradChoice") + " currently unsupported!") sys.exit(1) # Step 1.d: Given the chosen coordinate system, set up # corresponding reference metric and needed # reference metric quantities # The following function call sets up the reference metric # and related quantities, including rescaling matrices ReDD, # ReU, and hatted quantities. rfm.reference_metric() # Step 1.e: Set spatial dimension (must be 3 for BSSN, as BSSN is # a 3+1-dimensional decomposition of the general # relativistic field equations) DIM = 3 # Step 1.f: Import all ADM quantities as written in terms of BSSN quantities import BSSN.ADM_in_terms_of_BSSN as AB AB.ADM_in_terms_of_BSSN() # Step 2.a: Declare the Cartesian x,y,z in terms of # xx0,xx1,xx2. x = rfm.xxCart[0] y = rfm.xxCart[1] z = rfm.xxCart[2] # Step 2.b: Declare detgamma and gammaUU from # BSSN.ADM_in_terms_of_BSSN; # simplify detgamma & gammaUU expressions, # which expedites Psi4 codegen. detgamma = sp.simplify(AB.detgamma) gammaUU = ixp.zerorank2() for i in range(DIM): for j in range(DIM): gammaUU[i][j] = sp.simplify(AB.gammaUU[i][j]) # Step 2.c: Define v1U and v2U v1UCart = [-y, x, sp.sympify(0)] v2UCart = [x, y, z] # Step 2.d: Construct the Jacobian d x_Cart^i / d xx^j Jac_dUCart_dDrfmUD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): Jac_dUCart_dDrfmUD[i][j] = sp.simplify( sp.diff(rfm.xxCart[i], rfm.xx[j])) # Step 2.e: Invert above Jacobian to get needed d xx^j / d x_Cart^i Jac_dUrfm_dDCartUD, dummyDET = ixp.generic_matrix_inverter3x3( Jac_dUCart_dDrfmUD) # Step 2.e.i: Simplify expressions for d xx^j / d x_Cart^i: for i in range(DIM): for j in range(DIM): Jac_dUrfm_dDCartUD[i][j] = sp.simplify(Jac_dUrfm_dDCartUD[i][j]) # Step 2.f: Transform v1U and v2U from the Cartesian to the xx^i basis v1U = ixp.zerorank1() v2U = ixp.zerorank1() for i in range(DIM): for j in range(DIM): v1U[i] += Jac_dUrfm_dDCartUD[i][j] * v1UCart[j] v2U[i] += Jac_dUrfm_dDCartUD[i][j] * v2UCart[j] # Step 2.g: Define v3U v3U = ixp.zerorank1() LeviCivitaSymbolDDD = ixp.LeviCivitaSymbol_dim3_rank3() for a in range(DIM): for b in range(DIM): for c in range(DIM): for d in range(DIM): v3U[a] += sp.sqrt(detgamma) * gammaUU[a][ d] * LeviCivitaSymbolDDD[d][b][c] * v1U[b] * v2U[c] # Step 2.g.i: Simplify expressions for v1U,v2U,v3U. This greatly expedites the C code generation (~10x faster) # Drat. Simplification with certain versions of SymPy & coord systems results in a hang. Let's just # evaluate the expressions so the most trivial optimizations can be performed. for a in range(DIM): v1U[a] = v1U[a].doit() # sp.simplify(v1U[a]) v2U[a] = v2U[a].doit() # sp.simplify(v2U[a]) v3U[a] = v3U[a].doit() # sp.simplify(v3U[a]) # Step 2.h: Define omega_{ij} omegaDD = ixp.zerorank2() gammaDD = AB.gammaDD def v_vectorDU(v1U, v2U, v3U, i, a): if i == 0: return v1U[a] if i == 1: return v2U[a] if i == 2: return v3U[a] print("ERROR: unknown vector!") sys.exit(1) def update_omega(omegaDD, i, j, v1U, v2U, v3U, gammaDD): omegaDD[i][j] = sp.sympify(0) for a in range(DIM): for b in range(DIM): omegaDD[i][j] += v_vectorDU(v1U, v2U, v3U, i, a) * v_vectorDU( v1U, v2U, v3U, j, b) * gammaDD[a][b] # Step 2.i: Define e^a_i. Note that: # omegaDD[0][0] = \omega_{11} above; # omegaDD[1][1] = \omega_{22} above, etc. # First e_1^a: Orthogonalize & normalize: e1U = ixp.zerorank1() update_omega(omegaDD, 0, 0, v1U, v2U, v3U, gammaDD) for a in range(DIM): e1U[a] = v1U[a] / sp.sqrt(omegaDD[0][0]) # Next e_2^a: First orthogonalize: e2U = ixp.zerorank1() update_omega(omegaDD, 0, 1, e1U, v2U, v3U, gammaDD) for a in range(DIM): e2U[a] = (v2U[a] - omegaDD[0][1] * e1U[a]) # Then normalize: update_omega(omegaDD, 1, 1, e1U, e2U, v3U, gammaDD) for a in range(DIM): e2U[a] /= sp.sqrt(omegaDD[1][1]) # Next e_3^a: First orthogonalize: e3U = ixp.zerorank1() update_omega(omegaDD, 0, 2, e1U, e2U, v3U, gammaDD) update_omega(omegaDD, 1, 2, e1U, e2U, v3U, gammaDD) for a in range(DIM): e3U[a] = (v3U[a] - omegaDD[0][2] * e1U[a] - omegaDD[1][2] * e2U[a]) # Then normalize: update_omega(omegaDD, 2, 2, e1U, e2U, e3U, gammaDD) for a in range(DIM): e3U[a] /= sp.sqrt(omegaDD[2][2]) # Step 2.j: Construct l^mu, n^mu, and m^mu, based on r^mu, theta^mu, phi^mu, and u^mu: r4U = ixp.zerorank1(DIM=4) u4U = ixp.zerorank1(DIM=4) theta4U = ixp.zerorank1(DIM=4) phi4U = ixp.zerorank1(DIM=4) for a in range(DIM): r4U[a + 1] = e2U[a] theta4U[a + 1] = e3U[a] phi4U[a + 1] = e1U[a] # FIXME? assumes alpha=1, beta^i = 0 if par.parval_from_str(thismodule + "::UseCorrectUnitNormal") == "False": u4U[0] = 1 else: # Eq. 2.116 in Baumgarte & Shapiro: # n^mu = {1/alpha, -beta^i/alpha}. Note that n_mu = {alpha,0}, so n^mu n_mu = -1. import BSSN.BSSN_quantities as Bq Bq.declare_BSSN_gridfunctions_if_not_declared_already() Bq.BSSN_basic_tensors() u4U[0] = 1 / Bq.alpha for i in range(DIM): u4U[i + 1] = -Bq.betaU[i] / Bq.alpha l4U = ixp.zerorank1(DIM=4) n4U = ixp.zerorank1(DIM=4) mre4U = ixp.zerorank1(DIM=4) mim4U = ixp.zerorank1(DIM=4) # M_SQRT1_2 = 1 / sqrt(2) (defined in math.h on Linux) M_SQRT1_2 = par.Cparameters("#define", thismodule, "M_SQRT1_2", "") isqrt2 = M_SQRT1_2 #1/sp.sqrt(2) <- SymPy drops precision to 15 sig. digits in unit tests for mu in range(4): l4U[mu] = isqrt2 * (u4U[mu] + r4U[mu]) n4U[mu] = isqrt2 * (u4U[mu] - r4U[mu]) mre4U[mu] = isqrt2 * theta4U[mu] mim4U[mu] = isqrt2 * phi4U[mu]
def Toroidal(): system = par.parval_from_str(thismodule + "::System_to_use") DIM = par.parval_from_str("grid::DIM") dst_basis = par.parval_from_str("reference_metric::CoordSystem") # Set coordinate system to Cartesian par.set_parval_from_str("reference_metric::CoordSystem", "Cartesian") rfm.reference_metric() global AidU, EidU, psi_ID x = rfm.xxCart[0] y = rfm.xxCart[1] z = rfm.xxCart[2] AidD_Sph = ixp.zerorank1() # Set coordinate transformations: r = sp.sqrt(x * x + y * y + z * z) sin_theta = z / r u = time + r v = time - r e_lam_u = sp.exp(-lam * u**2) e_lam_v = sp.exp(-lam * v**2) # Equation 16 from https://arxiv.org/abs/gr-qc/0201051 AD_phi_hat = (amp*sin_theta)*( ((e_lam_v - e_lam_u)/r**2) - \ 2*lam*(v*e_lam_v + u*e_lam_u)/r ) AidD_Sph[2] = AD_phi_hat / (r * sin_theta) # Coordinate transformation from spherical to Cartesian AidU_Cart = ixp.zerorank1() Jac_dxSphU_dxCartD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): Jac_dxSphU_dxCartD[i][j] = sp.diff(rfm.xxSph[i], rfm.xxCart[j]) # Jac_dxCartU_dxSphD[i][j] = sp.diff(rfm.xxCart[i],rfm.xx[j]) Jac_dxCartU_dxSphD, dummy = ixp.generic_matrix_inverter3x3( Jac_dxSphU_dxCartD) for i in range(DIM): for j in range(DIM): AidU_Cart[i] += Jac_dxCartU_dxSphD[i][j] * AidD_Sph[j] for i in range(DIM): AidU_Cart[i] = sp.simplify(AidU_Cart[i]) # rfm is still defined in Cartesian coordinates cart_xx = ixp.declarerank1("cart_xx") for i in range(3): for k in range(3): AidU_Cart[i] = AidU_Cart[i].subs(rfm.xx[k], cart_xx[k]) # Set coordinate system to dst_basis par.set_parval_from_str("reference_metric::CoordSystem", dst_basis) rfm.reference_metric() for i in range(3): for k in range(3): AidU_Cart[i] = AidU_Cart[i].subs(cart_xx[k], rfm.xxCart[k]) # if radial_like_dst_xx0: # for j in range(3): # AidU_Cart[j] = sp.refine(sp.simplify(AidU_Cart[j]), sp.Q.positive(rfm.xx[0])) # Step 3: Transform BSSN tensors in Cartesian basis to destination grid basis, using center of dest. grid as origin # Step 3.a: Next construct Jacobian and inverse Jacobian matrices: # _Jac_dUCart_dDrfmUD is unused. _Jac_dUCart_dDrfmUD, Jac_dUrfm_dDCartUD = rfm.compute_Jacobian_and_inverseJacobian_tofrom_Cartesian( ) # Step 3.b: Convert basis of all BSSN *vectors* from Cartesian to destination basis AidU = rfm.basis_transform_vectorU_from_Cartesian_to_rfmbasis( Jac_dUrfm_dDCartUD, AidU_Cart) # Define electric field --> E^i = -\partial_t A^i EidU = ixp.zerorank1() for j in range(DIM): EidU[j] = -sp.diff(AidU[j], time) psi_ID = sp.sympify(0) if system == "System_II": global Gamma_ID Gamma_ID = sp.sympify(0) print('Currently using ' + system + ' initial data') elif system == "System_I": print('Currently using ' + system + ' initial data') else: print( "Invalid choice of system: System_to_use must be either System_I or System_II" ) sys.exit(1)
def generate_C_code_for_Stilde_flux( out_dir, inputs_provided=False, alpha_face=None, gamma_faceDD=None, beta_faceU=None, Valenciav_rU=None, B_rU=None, Valenciav_lU=None, B_lU=None, Stilde_flux_HLLED=None, sqrt4pi=None, outCparams="outCverbose=False,CSE_sorting=none", write_cmax_cmin=False, gamma_faceUU=None, phi_face=None): if not inputs_provided: # We will pass values of the gridfunction on the cell faces into the function. This requires us # to declare them as C parameters in NRPy+. We will denote this with the _face infix/suffix. alpha_face = gri.register_gridfunctions("AUXEVOL", "alpha_face") gamma_faceDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "gamma_faceDD", "sym01") beta_faceU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "beta_faceU") # We'll need some more gridfunctions, now, to represent the reconstructions of BU and ValenciavU # on the right and left faces Valenciav_rU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_rU", DIM=3) B_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_rU", DIM=3) Valenciav_lU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_lU", DIM=3) B_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_lU", DIM=3) sqrt4pi = par.Cparameters("REAL", thismodule, "sqrt4pi", "sqrt(4.0*M_PI)") # We'll also need to store the results of the HLLE step between functions. Stilde_flux_HLLED = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Stilde_flux_HLLED") if write_cmax_cmin: # In the staggered case, we will also want to output cmax and cmin # If we want to write cmax and cmin, we will need to be able to change auxevol_gfs: input_params_for_Stilde_flux = "const paramstruct *params,REAL *auxevol_gfs,REAL *rhs_gfs" else: input_params_for_Stilde_flux = "const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs" if gamma_faceUU is None: gamma_faceUU, unusedgammaDET = ixp.generic_matrix_inverter3x3( gamma_faceDD) if write_cmax_cmin: name_suffixes = ["_x", "_y", "_z"] for flux_dirn in range(3): calculate_Stilde_flux(flux_dirn,alpha_face,gamma_faceDD,beta_faceU,\ Valenciav_rU,B_rU,Valenciav_lU,B_lU,sqrt4pi,gamma_faceUU,phi_face) Stilde_flux_to_print = [ lhrh(lhs=gri.gfaccess("out_gfs", "Stilde_flux_HLLED0"), rhs=Stilde_fluxD[0]), lhrh(lhs=gri.gfaccess("out_gfs", "Stilde_flux_HLLED1"), rhs=Stilde_fluxD[1]), lhrh(lhs=gri.gfaccess("out_gfs", "Stilde_flux_HLLED2"), rhs=Stilde_fluxD[2]), ] if write_cmax_cmin: Stilde_flux_to_print = Stilde_flux_to_print \ +[ lhrh(lhs=gri.gfaccess("out_gfs","cmax"+name_suffixes[flux_dirn]),rhs=chsp.cmax), lhrh(lhs=gri.gfaccess("out_gfs","cmin"+name_suffixes[flux_dirn]),rhs=chsp.cmin), ] desc = "Compute the flux term of all 3 components of tilde{S}_i on the left face in the " + str( flux_dirn) + "direction for all components." name = "calculate_Stilde_flux_D" + str(flux_dirn) Ccode_function = outCfunction( outfile="returnstring", desc=desc, name=name, params=input_params_for_Stilde_flux, body=fin.FD_outputC("returnstring", Stilde_flux_to_print, params=outCparams).replace("IDX4", "IDX4S"), loopopts="InteriorPoints", rel_path_for_Cparams=os.path.join("../")).replace( "NGHOSTS+Nxx0", "NGHOSTS+Nxx0+1").replace( "NGHOSTS+Nxx1", "NGHOSTS+Nxx1+1").replace("NGHOSTS+Nxx2", "NGHOSTS+Nxx2+1") with open(os.path.join(out_dir, name + ".h"), "w") as file: file.write(Ccode_function) pre_body = """// Notice in the loop below that we go from 3 to cctk_lsh-3 for i, j, AND k, even though // we are only computing the flux in one direction. This is because in the end, // we only need the rhs's from 3 to cctk_lsh-3 for i, j, and k. const REAL invdxi[4] = {1e100,invdx0,invdx1,invdx2}; const REAL invdx = invdxi[flux_dirn];""" FD_body = """const int index = IDX3S(i0,i1,i2); const int indexp1 = IDX3S(i0+kronecker_delta[flux_dirn][0],i1+kronecker_delta[flux_dirn][1],i2+kronecker_delta[flux_dirn][2]); rhs_gfs[IDX4ptS(STILDED0GF,index)] += (auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED0GF,index)] - auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED0GF,indexp1)] ) * invdx; rhs_gfs[IDX4ptS(STILDED1GF,index)] += (auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED1GF,index)] - auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED1GF,indexp1)] ) * invdx; rhs_gfs[IDX4ptS(STILDED2GF,index)] += (auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED2GF,index)] - auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED2GF,indexp1)] ) * invdx;""" desc = "Compute the difference in the flux of StildeD on the opposite faces in flux_dirn for all components." name = "calculate_Stilde_rhsD" outCfunction( outfile=os.path.join(out_dir, name + ".h"), desc=desc, name=name, params= "const int flux_dirn,const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs", preloop=pre_body, body=FD_body, loopopts="InteriorPoints", rel_path_for_Cparams=os.path.join("../"))
def Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear( CoordType_in, Sph_r_th_ph_or_Cart_xyz, gammaDD_inSphorCart, KDD_inSphorCart, alpha_inSphorCart, betaU_inSphorCart, BU_inSphorCart): # This routine converts the ADM variables # $$\left\{\gamma_{ij}, K_{ij}, \alpha, \beta^i\right\}$$ # in Spherical or Cartesian basis+coordinates, first to the BSSN variables # in the chosen reference_metric::CoordSystem coordinate system+basis: # $$\left\{\bar{\gamma}_{i j},\bar{A}_{i j},\phi, K, \bar{\Lambda}^{i}, \alpha, \beta^i, B^i\right\},$$ # and then to the rescaled variables: # $$\left\{h_{i j},a_{i j},\phi, K, \lambda^{i}, \alpha, \mathcal{V}^i, \mathcal{B}^i\right\}.$$ # The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations. # To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in # the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3. # Step 0: Set spatial dimension (must be 3 for BSSN) DIM = 3 # Step 0: Copy gammaSphDD_in to gammaSphDD, KSphDD_in to KSphDD, etc. # This ensures that the input arrays are not modified below; # modifying them would result in unexpected effects outside # this function. alphaSphorCart = alpha_inSphorCart betaSphorCartU = ixp.zerorank1() BSphorCartU = ixp.zerorank1() gammaSphorCartDD = ixp.zerorank2() KSphorCartDD = ixp.zerorank2() for i in range(DIM): betaSphorCartU[i] = betaU_inSphorCart[i] BSphorCartU[i] = BU_inSphorCart[i] for j in range(DIM): gammaSphorCartDD[i][j] = gammaDD_inSphorCart[i][j] KSphorCartDD[i][j] = KDD_inSphorCart[i][j] # Make sure that rfm.reference_metric() has been called. # We'll need the variables it defines throughout this module. if rfm.have_already_called_reference_metric_function == False: print( "Error. Called Convert_Spherical_ADM_to_BSSN_curvilinear() without" ) print( " first setting up reference metric, by calling rfm.reference_metric()." ) exit(1) # Step 1: All input quantities are in terms of r,th,ph or x,y,z. We want them in terms # of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace # r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2 # as defined for this particular reference metric in reference_metric.py's # xxSph[] or xxCart[], respectively: # Note that substitution only works when the variable is not an integer. Hence the # if isinstance(...,...) stuff: def sympify_integers__replace_rthph_or_Cartxyz(obj, rthph_or_xyz, rthph_or_xyz_of_xx): if isinstance(obj, int): return sp.sympify(obj) else: return obj.subs(rthph_or_xyz[0], rthph_or_xyz_of_xx[0]).\ subs(rthph_or_xyz[1], rthph_or_xyz_of_xx[1]).\ subs(rthph_or_xyz[2], rthph_or_xyz_of_xx[2]) r_th_ph_or_Cart_xyz_of_xx = [] if CoordType_in == "Spherical": r_th_ph_or_Cart_xyz_of_xx = rfm.xxSph elif CoordType_in == "Cartesian": r_th_ph_or_Cart_xyz_of_xx = rfm.xxCart else: print( "Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords." ) exit(1) alphaSphorCart = sympify_integers__replace_rthph_or_Cartxyz( alphaSphorCart, Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx) for i in range(DIM): betaSphorCartU[i] = sympify_integers__replace_rthph_or_Cartxyz( betaSphorCartU[i], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx) BSphorCartU[i] = sympify_integers__replace_rthph_or_Cartxyz( BSphorCartU[i], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx) for j in range(DIM): gammaSphorCartDD[i][ j] = sympify_integers__replace_rthph_or_Cartxyz( gammaSphorCartDD[i][j], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx) KSphorCartDD[i][j] = sympify_integers__replace_rthph_or_Cartxyz( KSphorCartDD[i][j], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx) # Step 2: All ADM initial data quantities are now functions of xx0,xx1,xx2, but # they are still in the Spherical or Cartesian basis. We can now directly apply # Jacobian transformations to get them in the correct xx0,xx1,xx2 basis: # alpha is a scalar, so no Jacobian transformation is necessary. alpha = alphaSphorCart Jac_dUSphorCart_dDrfmUD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff( r_th_ph_or_Cart_xyz_of_xx[i], rfm.xx[j]) Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3( Jac_dUSphorCart_dDrfmUD) betaU = ixp.zerorank1() BU = ixp.zerorank1() gammaDD = ixp.zerorank2() KDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j] BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j] for k in range(DIM): for l in range(DIM): gammaDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][ i] * Jac_dUSphorCart_dDrfmUD[l][j] * gammaSphorCartDD[ k][l] KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][ i] * Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l] # Step 3: All ADM quantities were input into this function in the Spherical or Cartesian # basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above, # we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2. # Here we convert ADM quantities to their BSSN Curvilinear counterparts: # Step 3.1: Convert ADM $\gamma_{ij}$ to BSSN $\bar{\gamma}_{ij}$: # We have (Eqs. 2 and 3 of [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)): # \bar{\gamma}_{i j} = \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \gamma_{ij}. gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD) gammabarDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): gammabarDD[i][j] = (rfm.detgammahat / gammaDET)**(sp.Rational( 1, 3)) * gammaDD[i][j] # Step 3.2: Convert the extrinsic curvature $K_{ij}$ to the trace-free extrinsic # curvature $\bar{A}_{ij}$, plus the trace of the extrinsic curvature $K$, # where (Eq. 3 of [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)): # K = \gamma^{ij} K_{ij}, and # \bar{A}_{ij} &= \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \left(K_{ij} - \frac{1}{3} \gamma_{ij} K \right) trK = sp.sympify(0) for i in range(DIM): for j in range(DIM): trK += gammaUU[i][j] * KDD[i][j] AbarDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): AbarDD[i][j] = (rfm.detgammahat / gammaDET)**(sp.Rational( 1, 3)) * (KDD[i][j] - sp.Rational(1, 3) * gammaDD[i][j] * trK) # Step 3.3: Define $\bar{\Lambda}^i$ (Eqs. 4 and 5 of [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)): # \bar{\Lambda}^i = \bar{\gamma}^{jk}\left(\bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk}\right). gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD) # First compute \bar{\Gamma}^i_{jk}: GammabarUDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): GammabarUDD[i][j][k] += sp.Rational( 1, 2) * gammabarUU[i][l] * ( sp.diff(gammabarDD[l][j], rfm.xx[k]) + sp.diff(gammabarDD[l][k], rfm.xx[j]) - sp.diff(gammabarDD[j][k], rfm.xx[l])) # Next evaluate \bar{\Lambda}^i, based on GammabarUDD above and GammahatUDD # (from the reference metric): LambdabarU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): LambdabarU[i] += gammabarUU[j][k] * (GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k]) # Step 3.4: Set the conformal factor variable $\texttt{cf}$, which is set # by the "BSSN_quantities::ConformalFactor" parameter. For example if # "ConformalFactor" is set to "phi", we can use Eq. 3 of # [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf), # which in arbitrary coordinates is written: # \phi = \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right). # Alternatively if "BSSN_quantities::ConformalFactor" is set to "chi", then # \chi = e^{-4 \phi} = \exp\left(-4 \frac{1}{12} \left(\frac{\gamma}{\bar{\gamma}}\right)\right) # = \exp\left(-\frac{1}{3} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/3}. # # Finally if "BSSN_quantities::ConformalFactor" is set to "W", then # W = e^{-2 \phi} = \exp\left(-2 \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = # \exp\left(-\frac{1}{6} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = # \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/6}. cf = sp.sympify(0) if par.parval_from_str("ConformalFactor") == "phi": cf = sp.Rational(1, 12) * sp.log(gammaDET / gammabarDET) elif par.parval_from_str("ConformalFactor") == "chi": cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 3)) elif par.parval_from_str("ConformalFactor") == "W": cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 6)) else: print("Error ConformalFactor type = \"" + par.parval_from_str("ConformalFactor") + "\" unknown.") exit(1) # Step 4: Rescale tensorial quantities according to the prescription described in # the [BSSN in curvilinear coordinates tutorial module](Tutorial-BSSNCurvilinear.ipynb) # (also [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)): # # h_{ij} &= (\bar{\gamma}_{ij} - \hat{\gamma}_{ij})/\text{ReDD[i][j]}\\ # a_{ij} &= \bar{A}_{ij}/\text{ReDD[i][j]}\\ # \lambda^i &= \bar{\Lambda}^i/\text{ReU[i]}\\ # \mathcal{V}^i &= \beta^i/\text{ReU[i]}\\ # \mathcal{B}^i &= B^i/\text{ReU[i]}\\ hDD = ixp.zerorank2() aDD = ixp.zerorank2() lambdaU = ixp.zerorank1() vetU = ixp.zerorank1() betU = ixp.zerorank1() for i in range(DIM): lambdaU[i] = LambdabarU[i] / rfm.ReU[i] vetU[i] = betaU[i] / rfm.ReU[i] betU[i] = BU[i] / rfm.ReU[i] for j in range(DIM): hDD[i][j] = (gammabarDD[i][j] - rfm.ghatDD[i][j]) / rfm.ReDD[i][j] aDD[i][j] = AbarDD[i][j] / rfm.ReDD[i][j] #print(sp.mathematica_code(hDD[0][0])) # Step 5: Return the BSSN Curvilinear variables in the desired xx0,xx1,xx2 # basis, and as functions of the consistent xx0,xx1,xx2 coordinates. return cf, hDD, lambdaU, aDD, trK, alpha, vetU, betU
def Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear(CoordType_in, ADM_input_function_name, Ccodesdir = "BSSN", pointer_to_ID_inputs=False,loopopts=",oldloops"): # The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations. # To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in # the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3. # Step 0: Set spatial dimension (must be 3 for BSSN) DIM = 3 # Step 1: All ADM initial data quantities are now functions of xx0,xx1,xx2, but # they are still in the Spherical or Cartesian basis. We can now directly apply # Jacobian transformations to get them in the correct xx0,xx1,xx2 basis: # All input quantities are in terms of r,th,ph or x,y,z. We want them in terms # of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace # r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2 # as defined for this particular reference metric in reference_metric.py's # xxSph[] or xx_to_Cart[], respectively: # Define the input variables: gammaSphorCartDD = ixp.declarerank2("gammaSphorCartDD", "sym01") KSphorCartDD = ixp.declarerank2("KSphorCartDD", "sym01") alphaSphorCart = sp.symbols("alphaSphorCart") betaSphorCartU = ixp.declarerank1("betaSphorCartU") BSphorCartU = ixp.declarerank1("BSphorCartU") # Make sure that rfm.reference_metric() has been called. # We'll need the variables it defines throughout this module. if rfm.have_already_called_reference_metric_function == False: print("Error. Called Convert_Spherical_ADM_to_BSSN_curvilinear() without") print(" first setting up reference metric, by calling rfm.reference_metric().") sys.exit(1) r_th_ph_or_Cart_xyz_oID_xx = [] if CoordType_in == "Spherical": r_th_ph_or_Cart_xyz_oID_xx = rfm.xxSph elif CoordType_in == "Cartesian": r_th_ph_or_Cart_xyz_oID_xx = rfm.xx_to_Cart else: print("Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords.") sys.exit(1) # Step 2: All ADM initial data quantities are now functions of xx0,xx1,xx2, but # they are still in the Spherical or Cartesian basis. We can now directly apply # Jacobian transformations to get them in the correct xx0,xx1,xx2 basis: # alpha is a scalar, so no Jacobian transformation is necessary. alpha = alphaSphorCart Jac_dUSphorCart_dDrfmUD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff(r_th_ph_or_Cart_xyz_oID_xx[i], rfm.xx[j]) Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3(Jac_dUSphorCart_dDrfmUD) betaU = ixp.zerorank1() BU = ixp.zerorank1() gammaDD = ixp.zerorank2() KDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j] BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j] for k in range(DIM): for l in range(DIM): gammaDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * \ gammaSphorCartDD[k][l] KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l] # Step 3: All ADM quantities were input into this function in the Spherical or Cartesian # basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above, # we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2. # Here we convert ADM quantities in the "rfm" basis to their BSSN Curvilinear # counterparts, for all BSSN quantities *except* lambda^i: import BSSN.BSSN_in_terms_of_ADM as BitoA BitoA.gammabarDD_hDD(gammaDD) BitoA.trK_AbarDD_aDD(gammaDD, KDD) BitoA.cf_from_gammaDD(gammaDD) BitoA.betU_vetU(betaU, BU) hDD = BitoA.hDD trK = BitoA.trK aDD = BitoA.aDD cf = BitoA.cf vetU = BitoA.vetU betU = BitoA.betU # Step 4: Compute $\bar{\Lambda}^i$ (Eqs. 4 and 5 of # [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)), # from finite-difference derivatives of rescaled metric # quantities $h_{ij}$: # \bar{\Lambda}^i = \bar{\gamma}^{jk}\left(\bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk}\right). # The reference_metric.py module provides us with analytic expressions for # $\hat{\Gamma}^i_{jk}$, so here we need only compute # finite-difference expressions for $\bar{\Gamma}^i_{jk}$, based on # the values for $h_{ij}$ provided in the initial data. Once # $\bar{\Lambda}^i$ has been computed, we apply the usual rescaling # procedure: # \lambda^i = \bar{\Lambda}^i/\text{ReU[i]}, # and then output the result to a C file using the NRPy+ # finite-difference C output routine. # We will need all BSSN gridfunctions to be defined, as well as # expressions for gammabarDD_dD in terms of exact derivatives of # the rescaling matrix and finite-difference derivatives of # hDD's. This functionality is provided by BSSN.BSSN_unrescaled_and_barred_vars, # which we call here to overwrite above definitions of gammabarDD,gammabarUU, etc. Bq.gammabar__inverse_and_derivs() # Provides gammabarUU and GammabarUDD gammabarUU = Bq.gammabarUU GammabarUDD = Bq.GammabarUDD # Next evaluate \bar{\Lambda}^i, based on GammabarUDD above and GammahatUDD # (from the reference metric): LambdabarU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): LambdabarU[i] += gammabarUU[j][k] * (GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k]) # Finally apply rescaling: # lambda^i = Lambdabar^i/\text{ReU[i]} lambdaU = ixp.zerorank1() for i in range(DIM): lambdaU[i] = LambdabarU[i] / rfm.ReU[i] if ADM_input_function_name == "DoNotOutputADMInputFunction": return hDD,aDD,trK,vetU,betU,alpha,cf,lambdaU # Step 5.A: Output files containing finite-differenced lambdas. outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" lambdaU_expressions = [lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU0"), rhs=lambdaU[0]), lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU1"), rhs=lambdaU[1]), lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU2"), rhs=lambdaU[2])] desc = "Output lambdaU[i] for BSSN, built using finite-difference derivatives." name = "ID_BSSN_lambdas" params = "const paramstruct *restrict params,REAL *restrict xx[3],REAL *restrict in_gfs" preloop = "" enableCparameters=True if "oldloops" in loopopts: params = "const int Nxx[3],const int Nxx_plus_2NGHOSTS[3],REAL *xx[3],const REAL dxx[3],REAL *in_gfs" enableCparameters=False preloop = """ const REAL invdx0 = 1.0/dxx[0]; const REAL invdx1 = 1.0/dxx[1]; const REAL invdx2 = 1.0/dxx[2]; """ outCfunction( outfile=os.path.join(Ccodesdir, name + ".h"), desc=desc, name=name, params=params, preloop=preloop, body=fin.FD_outputC("returnstring", lambdaU_expressions, outCparams), loopopts="InteriorPoints,Read_xxs"+loopopts, enableCparameters=enableCparameters) # Step 5: Output all ADM-to-BSSN expressions to a C function. This function # must first call the ID_ADM_SphorCart() defined above. Using these # Spherical or Cartesian data, it sets up all quantities needed for # BSSNCurvilinear initial data, *except* $\lambda^i$, which must be # computed from numerical data using finite-difference derivatives. ID_inputs_param = "ID_inputs other_inputs," if pointer_to_ID_inputs == True: ID_inputs_param = "ID_inputs *other_inputs," desc = "Write BSSN variables in terms of ADM variables at a given point xx0,xx1,xx2" name = "ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs" enableCparameters=True params = "const paramstruct *restrict params, " if "oldloops" in loopopts: enableCparameters=False params = "" params += "const int i0i1i2[3], const REAL xx0xx1xx2[3]," + ID_inputs_param + """ REAL *hDD00,REAL *hDD01,REAL *hDD02,REAL *hDD11,REAL *hDD12,REAL *hDD22, REAL *aDD00,REAL *aDD01,REAL *aDD02,REAL *aDD11,REAL *aDD12,REAL *aDD22, REAL *trK, REAL *vetU0,REAL *vetU1,REAL *vetU2, REAL *betU0,REAL *betU1,REAL *betU2, REAL *alpha, REAL *cf""" outCparams = "preindent=1,outCverbose=False,includebraces=False" outCfunction( outfile=os.path.join(Ccodesdir, name + ".h"), desc=desc, name=name, params=params, body=""" REAL gammaSphorCartDD00,gammaSphorCartDD01,gammaSphorCartDD02, gammaSphorCartDD11,gammaSphorCartDD12,gammaSphorCartDD22; REAL KSphorCartDD00,KSphorCartDD01,KSphorCartDD02, KSphorCartDD11,KSphorCartDD12,KSphorCartDD22; REAL alphaSphorCart,betaSphorCartU0,betaSphorCartU1,betaSphorCartU2; REAL BSphorCartU0,BSphorCartU1,BSphorCartU2; const REAL xx0 = xx0xx1xx2[0]; const REAL xx1 = xx0xx1xx2[1]; const REAL xx2 = xx0xx1xx2[2]; REAL xyz_or_rthph[3];\n""" + outputC(r_th_ph_or_Cart_xyz_oID_xx[0:3], ["xyz_or_rthph[0]", "xyz_or_rthph[1]", "xyz_or_rthph[2]"], "returnstring", outCparams + ",CSE_enable=False") + " " + ADM_input_function_name + """(params,i0i1i2, xyz_or_rthph, other_inputs, &gammaSphorCartDD00,&gammaSphorCartDD01,&gammaSphorCartDD02, &gammaSphorCartDD11,&gammaSphorCartDD12,&gammaSphorCartDD22, &KSphorCartDD00,&KSphorCartDD01,&KSphorCartDD02, &KSphorCartDD11,&KSphorCartDD12,&KSphorCartDD22, &alphaSphorCart,&betaSphorCartU0,&betaSphorCartU1,&betaSphorCartU2, &BSphorCartU0,&BSphorCartU1,&BSphorCartU2); // Next compute all rescaled BSSN curvilinear quantities:\n""" + outputC([hDD[0][0], hDD[0][1], hDD[0][2], hDD[1][1], hDD[1][2], hDD[2][2], aDD[0][0], aDD[0][1], aDD[0][2], aDD[1][1], aDD[1][2], aDD[2][2], trK, vetU[0], vetU[1], vetU[2], betU[0], betU[1], betU[2], alpha, cf], ["*hDD00", "*hDD01", "*hDD02", "*hDD11", "*hDD12", "*hDD22", "*aDD00", "*aDD01", "*aDD02", "*aDD11", "*aDD12", "*aDD22", "*trK", "*vetU0", "*vetU1", "*vetU2", "*betU0", "*betU1", "*betU2", "*alpha", "*cf"], "returnstring", params=outCparams), enableCparameters=enableCparameters) # Step 5.a: Output the driver function for the above # function ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs() # Next write the driver function for ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(): desc = """Driver function for ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(), which writes BSSN variables in terms of ADM variables at a given point xx0,xx1,xx2""" name = "ID_BSSN__ALL_BUT_LAMBDAs" params = "const paramstruct *restrict params,REAL *restrict xx[3]," + ID_inputs_param + "REAL *in_gfs" enableCparameters = True funccallparams = "params, " idx3replace = "IDX3S" idx4ptreplace = "IDX4ptS" if "oldloops" in loopopts: params = "const int Nxx_plus_2NGHOSTS[3],REAL *xx[3]," + ID_inputs_param + "REAL *in_gfs" enableCparameters = False funccallparams = "" idx3replace = "IDX3" idx4ptreplace = "IDX4pt" outCfunction( outfile=os.path.join(Ccodesdir, name + ".h"), desc=desc, name=name, params=params, body=""" const int idx = IDX3(i0,i1,i2); const int i0i1i2[3] = {i0,i1,i2}; const REAL xx0xx1xx2[3] = {xx0,xx1,xx2}; ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(""".replace("IDX3",idx3replace)+funccallparams+"""i0i1i2,xx0xx1xx2,other_inputs, &in_gfs[IDX4pt(HDD00GF,idx)],&in_gfs[IDX4pt(HDD01GF,idx)],&in_gfs[IDX4pt(HDD02GF,idx)], &in_gfs[IDX4pt(HDD11GF,idx)],&in_gfs[IDX4pt(HDD12GF,idx)],&in_gfs[IDX4pt(HDD22GF,idx)], &in_gfs[IDX4pt(ADD00GF,idx)],&in_gfs[IDX4pt(ADD01GF,idx)],&in_gfs[IDX4pt(ADD02GF,idx)], &in_gfs[IDX4pt(ADD11GF,idx)],&in_gfs[IDX4pt(ADD12GF,idx)],&in_gfs[IDX4pt(ADD22GF,idx)], &in_gfs[IDX4pt(TRKGF,idx)], &in_gfs[IDX4pt(VETU0GF,idx)],&in_gfs[IDX4pt(VETU1GF,idx)],&in_gfs[IDX4pt(VETU2GF,idx)], &in_gfs[IDX4pt(BETU0GF,idx)],&in_gfs[IDX4pt(BETU1GF,idx)],&in_gfs[IDX4pt(BETU2GF,idx)], &in_gfs[IDX4pt(ALPHAGF,idx)],&in_gfs[IDX4pt(CFGF,idx)]); """.replace("IDX4pt",idx4ptreplace), loopopts="AllPoints,Read_xxs"+loopopts, enableCparameters=enableCparameters)