def BSSN_source_terms_for_BSSN_constraints(custom_T4UU=None): global sourceterm_H, sourceterm_MU # Step 4.a: Call BSSN_source_terms_ito_T4UU to get SDD, SD, S, & rho if custom_T4UU == "unrescaled BSSN source terms already given": SDD = ixp.declarerank2("SDD", "sym01") SD = ixp.declarerank1("SD") S = sp.symbols("S", real=True) rho = sp.symbols("rho", real=True) else: SDD, SD, S, rho = stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars( "BSSN", custom_T4UU) PI = par.Cparameters("REAL", thismodule, ["PI"], "3.14159265358979323846264338327950288") # Step 4.b: Add source term to the Hamiltonian constraint H sourceterm_H = -16 * PI * rho # Step 4.c: Add source term to the momentum constraint M^i # Step 4.c.i: Compute gammaUU in terms of BSSN quantities import BSSN.ADM_in_terms_of_BSSN as AitoB AitoB.ADM_in_terms_of_BSSN() # Provides gammaUU # Step 4.c.ii: Raise S_i SU = ixp.zerorank1() for i in range(3): for j in range(3): SU[i] += AitoB.gammaUU[i][j] * SD[j] # Step 4.c.iii: Add source term to momentum constraint & rescale: sourceterm_MU = ixp.zerorank1() for i in range(3): sourceterm_MU[i] = -8 * PI * SU[i] / rfm.ReU[i]
def ScalarField_Tmunu(): global T4UU # Step 1.c: 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.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: Import all basic (unrescaled) BSSN scalars & tensors Bq.BSSN_basic_tensors() alpha = Bq.alpha betaU = Bq.betaU # Step 1.g: Define ADM quantities in terms of BSSN quantities BtoA.ADM_in_terms_of_BSSN() gammaDD = BtoA.gammaDD gammaUU = BtoA.gammaUU # Step 1.h: Define scalar field quantitites sf_dD = ixp.declarerank1("sf_dD") Pi = sp.Symbol("sfM", real=True) # Step 2a: Set up \partial^{t}\varphi = Pi/alpha sf4dU = ixp.zerorank1(DIM=4) sf4dU[0] = Pi / alpha # Step 2b: Set up \partial^{i}\varphi = -Pi*beta^{i}/alpha + gamma^{ij}\partial_{j}\varphi for i in range(DIM): sf4dU[i + 1] = -Pi * betaU[i] / alpha for j in range(DIM): sf4dU[i + 1] += gammaUU[i][j] * sf_dD[j] # Step 2c: Set up \partial^{i}\varphi\partial_{i}\varphi = -Pi**2 + gamma^{ij}\partial_{i}\varphi\partial_{j}\varphi sf4d2 = -Pi**2 for i in range(DIM): for j in range(DIM): sf4d2 += gammaUU[i][j] * sf_dD[i] * sf_dD[j] # Step 3a: Setting up g^{\mu\nu} ADMg.g4UU_ito_BSSN_or_ADM("ADM", gammaDD=gammaDD, betaU=betaU, alpha=alpha, gammaUU=gammaUU) g4UU = ADMg.g4UU # Step 3b: Setting up T^{\mu\nu} for a massless scalar field T4UU = ixp.zerorank2(DIM=4) for mu in range(4): for nu in range(4): T4UU[mu][nu] = sf4dU[mu] * sf4dU[nu] - g4UU[mu][nu] * sf4d2 / 2
def setup_ADM_quantities(inputvars): if inputvars == "ADM": gammaDD = ixp.declarerank2("gammaDD", "sym01") betaU = ixp.declarerank1("betaU") alpha = sp.symbols("alpha", real=True) elif inputvars == "BSSN": import BSSN.ADM_in_terms_of_BSSN as AitoB # Construct gamma_{ij} in terms of cf & gammabar_{ij} AitoB.ADM_in_terms_of_BSSN() gammaDD = AitoB.gammaDD # Next construct beta^i in terms of vet^i and reference metric quantities import BSSN.BSSN_quantities as Bq Bq.BSSN_basic_tensors() betaU = Bq.betaU alpha = sp.symbols("alpha", real=True) else: print("inputvars = " + str(inputvars) + " not supported. Please choose ADM or BSSN.") sys.exit(1) return gammaDD,betaU,alpha
def Psi4(specify_tetrad=True): global psi4_im_pt, psi4_re_pt # Step 1.b: 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.c: 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.d: 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 1.e: Set up tetrad vectors if specify_tetrad == True: import BSSN.Psi4_tetrads as BP4t BP4t.Psi4_tetrads() mre4U = BP4t.mre4U mim4U = BP4t.mim4U n4U = BP4t.n4U else: # For code validation against NRPy+ psi_4 tutorial module (Tutorial-Psi4.ipynb); # ensures a more complete code validation. mre4U = ixp.declarerank1("mre4U", DIM=4) mim4U = ixp.declarerank1("mim4U", DIM=4) n4U = ixp.declarerank1("n4U", DIM=4) # Step 2: Construct the (rank-4) Riemann curvature tensor associated with the ADM 3-metric: RDDDD = ixp.zerorank4() gammaDDdDD = AB.gammaDDdDD for i in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): RDDDD[i][k][l][m] = sp.Rational(1, 2) * \ (gammaDDdDD[i][m][k][l] + gammaDDdDD[k][l][i][m] - gammaDDdDD[i][l][k][m] - gammaDDdDD[k][m][i][l]) # ... then we add the term on the right: gammaDD = AB.gammaDD GammaUDD = AB.GammaUDD for i in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): for n in range(DIM): for p in range(DIM): RDDDD[i][k][l][m] += gammaDD[n][p] * \ (GammaUDD[n][k][l] * GammaUDD[p][i][m] - GammaUDD[n][k][m] * GammaUDD[p][i][l]) # Step 3: Construct the (rank-4) tensor in term 1 of psi_4 (referring to Eq 5.1 in # Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf rank4term1DDDD = ixp.zerorank4() KDD = AB.KDD for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): rank4term1DDDD[i][j][k][l] = RDDDD[i][j][k][ l] + KDD[i][k] * KDD[l][j] - KDD[i][l] * KDD[k][j] # Step 4: Construct the (rank-3) tensor in term 2 of psi_4 (referring to Eq 5.1 in # Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf rank3term2DDD = ixp.zerorank3() KDDdD = AB.KDDdD for j in range(DIM): for k in range(DIM): for l in range(DIM): rank3term2DDD[j][k][l] = sp.Rational( 1, 2) * (KDDdD[j][k][l] - KDDdD[j][l][k]) # ... then we construct the second term in this sum: # \Gamma^{p}_{j[k} K_{l]p} = \frac{1}{2} (\Gamma^{p}_{jk} K_{lp}-\Gamma^{p}_{jl} K_{kp}): for j in range(DIM): for k in range(DIM): for l in range(DIM): for p in range(DIM): rank3term2DDD[j][k][l] += sp.Rational( 1, 2) * (GammaUDD[p][j][k] * KDD[l][p] - GammaUDD[p][j][l] * KDD[k][p]) # Finally, we multiply the term by $-8$: for j in range(DIM): for k in range(DIM): for l in range(DIM): rank3term2DDD[j][k][l] *= sp.sympify(-8) # Step 5: Construct the (rank-2) tensor in term 3 of psi_4 (referring to Eq 5.1 in # Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf # Step 5.1: Construct 3-Ricci tensor R_{ij} = gamma^{im} R_{ijml} RDD = ixp.zerorank2() gammaUU = AB.gammaUU for j in range(DIM): for l in range(DIM): for i in range(DIM): for m in range(DIM): RDD[j][l] += gammaUU[i][m] * RDDDD[i][j][m][l] # Step 5.2: Construct K^p_l = gamma^{pi} K_{il} KUD = ixp.zerorank2() for p in range(DIM): for l in range(DIM): for i in range(DIM): KUD[p][l] += gammaUU[p][i] * KDD[i][l] # Step 5.3: Construct trK = gamma^{ij} K_{ij} trK = sp.sympify(0) for i in range(DIM): for j in range(DIM): trK += gammaUU[i][j] * KDD[i][j] # Next we put these terms together to construct the entire term in parentheses: # +4 \left(R_{jl} - K_{jp} K^p_l + K K_{jl} \right), rank2term3DD = ixp.zerorank2() for j in range(DIM): for l in range(DIM): rank2term3DD[j][l] = RDD[j][l] + trK * KDD[j][l] for p in range(DIM): rank2term3DD[j][l] += -KDD[j][p] * KUD[p][l] # Finally we multiply by +4: for j in range(DIM): for l in range(DIM): rank2term3DD[j][l] *= sp.sympify(4) # Step 6: Construct real & imaginary parts of psi_4 # by contracting constituent rank 2, 3, and 4 # tensors with input tetrads mre4U, mim4U, & n4U. def tetrad_product__Real_psi4(n, Mre, Mim, mu, nu, eta, delta): return +n[mu] * Mre[nu] * n[eta] * Mre[delta] - n[mu] * Mim[nu] * n[ eta] * Mim[delta] def tetrad_product__Imag_psi4(n, Mre, Mim, mu, nu, eta, delta): return -n[mu] * Mre[nu] * n[eta] * Mim[delta] - n[mu] * Mim[nu] * n[ eta] * Mre[delta] # We split psi_4 into three pieces, to expedite & possibly parallelize C code generation. psi4_re_pt = [sp.sympify(0), sp.sympify(0), sp.sympify(0)] psi4_im_pt = [sp.sympify(0), sp.sympify(0), sp.sympify(0)] # First term: for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): psi4_re_pt[0] += rank4term1DDDD[i][j][ k][l] * tetrad_product__Real_psi4( n4U, mre4U, mim4U, i + 1, j + 1, k + 1, l + 1) psi4_im_pt[0] += rank4term1DDDD[i][j][ k][l] * tetrad_product__Imag_psi4( n4U, mre4U, mim4U, i + 1, j + 1, k + 1, l + 1) # Second term: for j in range(DIM): for k in range(DIM): for l in range(DIM): psi4_re_pt[1] += rank3term2DDD[j][k][l] * \ sp.Rational(1, 2) * (+tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, k + 1, l + 1) - tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, k + 1, l + 1)) psi4_im_pt[1] += rank3term2DDD[j][k][l] * \ sp.Rational(1, 2) * (+tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, k + 1, l + 1) - tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, k + 1, l + 1)) # Third term: for j in range(DIM): for l in range(DIM): psi4_re_pt[2] += rank2term3DD[j][l] * \ (sp.Rational(1, 4) * (+tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, 0, l + 1) - tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, 0, l + 1) - tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, l + 1, 0) + tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, l + 1, 0))) psi4_im_pt[2] += rank2term3DD[j][l] * \ (sp.Rational(1, 4) * (+tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, 0, l + 1) - tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, 0, l + 1) - tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, l + 1, 0) + tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, l + 1, 0)))
def driver_C_codes(Csrcdict, ThornName, rhs_list, evol_gfs_list, aux_gfs_list, auxevol_gfs_list, LapseCondition="OnePlusLog", enable_stress_energy_source_terms=False): # First the ETK banner code, proudly showing the NRPy+ banner import NRPy_logo as logo outstr = """ #include <stdio.h> void BaikalETK_Banner() { """ logostr = logo.print_logo(print_to_stdout=False) outstr += "printf(\"BaikalETK: another Einstein Toolkit thorn generated by\\n\");\n" for line in logostr.splitlines(): outstr += " printf(\"" + line + "\\n\");\n" outstr += "}\n" # Finally add C code string to dictionaries (Python dictionaries are immutable) # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("Banner.c")] = outstr.replace( "BaikalETK", ThornName) # Then the RegisterSlicing() function, needed for other ETK thorns outstr = """ #include "cctk.h" #include "Slicing.h" int BaikalETK_RegisterSlicing (void) { Einstein_RegisterSlicing ("BaikalETK"); return 0; }""" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "RegisterSlicing.c")] = outstr.replace("BaikalETK", ThornName) # Next BaikalETK_Symmetry_registration(): Register symmetries full_gfs_list = [] full_gfs_list.extend(evol_gfs_list) full_gfs_list.extend(auxevol_gfs_list) full_gfs_list.extend(aux_gfs_list) outstr = """ #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" #include "Symmetry.h" void BaikalETK_Symmetry_registration(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; // Stores gridfunction parity across x=0, y=0, and z=0 planes, respectively int sym[3]; // Next register parities for each gridfunction based on its name // (to ensure this algorithm is robust, gridfunctions with integers // in their base names are forbidden in NRPy+). """ outstr += "" for gf in full_gfs_list: # Do not add T4UU gridfunctions if enable_stress_energy_source_terms==False: if not (enable_stress_energy_source_terms == False and "T4UU" in gf): outstr += """ // Default to scalar symmetry: sym[0] = 1; sym[1] = 1; sym[2] = 1; // Now modify sym[0], sym[1], and/or sym[2] as needed // to account for gridfunction parity across // x=0, y=0, and/or z=0 planes, respectively """ # If gridfunction name does not end in a digit, by NRPy+ syntax, it must be a scalar if gf[len(gf) - 1].isdigit() == False: pass # Scalar = default elif len(gf) > 2: # Rank-1 indexed expression (e.g., vector) if gf[len(gf) - 2].isdigit() == False: if int(gf[-1]) > 2: print("Error: Found invalid gridfunction name: " + gf) sys.exit(1) symidx = gf[-1] outstr += " sym[" + symidx + "] = -1;\n" # Rank-2 indexed expression elif gf[len(gf) - 2].isdigit() == True: if len(gf) > 3 and gf[len(gf) - 3].isdigit() == True: print("Error: Found a Rank-3 or above gridfunction: " + gf + ", which is at the moment unsupported.") print("It should be easy to support this if desired.") sys.exit(1) symidx0 = gf[-2] outstr += " sym[" + symidx0 + "] *= -1;\n" symidx1 = gf[-1] outstr += " sym[" + symidx1 + "] *= -1;\n" else: print( "Don't know how you got this far with a gridfunction named " + gf + ", but I'll take no more of this nonsense.") print( " Please follow best-practices and rename your gridfunction to be more descriptive" ) sys.exit(1) outstr += " SetCartSymVN(cctkGH, sym, \"BaikalETK::" + gf + "\");\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("Symmetry_registration_oldCartGrid3D.c")] = \ outstr.replace("BaikalETK",ThornName) # Next set RHSs to zero outstr = """ #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" #include "Symmetry.h" void BaikalETK_zero_rhss(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; """ set_rhss_to_zero = "" for gf in rhs_list: set_rhss_to_zero += gf + "[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)] = 0.0;\n" outstr += lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], ["cctk_lsh[2]", "cctk_lsh[1]", "cctk_lsh[0]"], ["1", "1", "1"], [ "#pragma omp parallel for", "", "", ], "", set_rhss_to_zero) outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("zero_rhss.c")] = outstr.replace( "BaikalETK", ThornName) # Next registration with the Method of Lines thorn outstr = """ //-------------------------------------------------------------------------- // Register with the Method of Lines time stepper // (MoL thorn, found in arrangements/CactusBase/MoL) // MoL documentation located in arrangements/CactusBase/MoL/doc //-------------------------------------------------------------------------- #include <stdio.h> #include "cctk.h" #include "cctk_Parameters.h" #include "cctk_Arguments.h" #include "Symmetry.h" void BaikalETK_MoL_registration(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; CCTK_INT ierr = 0, group, rhs; // Register evolution & RHS gridfunction groups with MoL, so it knows group = CCTK_GroupIndex("BaikalETK::evol_variables"); rhs = CCTK_GroupIndex("BaikalETK::evol_variables_rhs"); ierr += MoLRegisterEvolvedGroup(group, rhs); if (ierr) CCTK_ERROR("Problems registering with MoL"); } """ # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "MoL_registration.c")] = outstr.replace("BaikalETK", ThornName) # Next register with the boundary conditions thorns. # PART 1: Set BC type to "none" for all variables # Since we choose NewRad boundary conditions, we must register all # gridfunctions to have boundary type "none". This is because # NewRad is seen by the rest of the Toolkit as a modification to the # RHSs. # This code is based on Kranc's McLachlan/ML_BSSN/src/Boundaries.cc code. outstr = """ #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" #include "cctk_Faces.h" #include "util_Table.h" #include "Symmetry.h" // Set `none` boundary conditions on BSSN RHSs, as these are set via NewRad. void BaikalETK_BoundaryConditions_evolved_gfs(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; CCTK_INT ierr CCTK_ATTRIBUTE_UNUSED = 0; """ for gf in evol_gfs_list: outstr += """ ierr = Boundary_SelectVarForBC(cctkGH, CCTK_ALL_FACES, 1, -1, "BaikalETK::""" + gf + """", "none"); if (ierr < 0) CCTK_ERROR("Failed to register BC for BaikalETK::""" + gf + """!"); """ outstr += """ } // Set `flat` boundary conditions on BSSN constraints, similar to what Lean does. void BaikalETK_BoundaryConditions_aux_gfs(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; CCTK_INT ierr CCTK_ATTRIBUTE_UNUSED = 0; """ for gf in aux_gfs_list: outstr += """ ierr = Boundary_SelectVarForBC(cctkGH, CCTK_ALL_FACES, cctk_nghostzones[0], -1, "BaikalETK::""" + gf + """", "flat"); if (ierr < 0) CCTK_ERROR("Failed to register BC for BaikalETK::""" + gf + """!"); """ outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "BoundaryConditions.c")] = outstr.replace("BaikalETK", ThornName) # PART 2: Set C code for calling NewRad BCs # As explained in lean_public/LeanBSSNMoL/src/calc_bssn_rhs.F90, # the function NewRad_Apply takes the following arguments: # NewRad_Apply(cctkGH, var, rhs, var0, v0, radpower), # which implement the boundary condition: # var = var_at_infinite_r + u(r-var_char_speed*t)/r^var_radpower # Obviously for var_radpower>0, var_at_infinite_r is the value of # the variable at r->infinity. var_char_speed is the propagation # speed at the outer boundary, and var_radpower is the radial # falloff rate. outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_NewRad(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; """ for gf in evol_gfs_list: var_at_infinite_r = "0.0" var_char_speed = "1.0" var_radpower = "1.0" if gf == "alpha": var_at_infinite_r = "1.0" if LapseCondition == "OnePlusLog": var_char_speed = "sqrt(2.0)" else: pass # 1.0 (default) is fine if "aDD" in gf or "trK" in gf: # consistent with Lean code. var_radpower = "2.0" outstr += " NewRad_Apply(cctkGH, " + gf + ", " + gf.replace( "GF", "" ) + "_rhsGF, " + var_at_infinite_r + ", " + var_char_speed + ", " + var_radpower + ");\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "BoundaryCondition_NewRad.c")] = outstr.replace( "BaikalETK", ThornName) # First we convert from ADM to BSSN, as is required to convert initial data # (given using) ADM quantities, to the BSSN evolved variables import BSSN.ADM_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear as atob IDhDD,IDaDD,IDtrK,IDvetU,IDbetU,IDalpha,IDcf,IDlambdaU = \ atob.Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear("Cartesian","DoNotOutputADMInputFunction",os.path.join(ThornName,"src")) # Store the original list of registered gridfunctions; we'll want to unregister # all the *SphorCart* gridfunctions after we're finished with them below. orig_glb_gridfcs_list = [] for gf in gri.glb_gridfcs_list: orig_glb_gridfcs_list.append(gf) alphaSphorCart = gri.register_gridfunctions("AUXEVOL", "alphaSphorCart") betaSphorCartU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "betaSphorCartU") BSphorCartU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "BSphorCartU") gammaSphorCartDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "gammaSphorCartDD", "sym01") KSphorCartDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "KSphorCartDD", "sym01") # ADM to BSSN conversion, used for converting ADM initial data into a form readable by this thorn. # ADM to BSSN, Part 1: Set up function call and pointers to ADM gridfunctions outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_ADM_to_BSSN(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; CCTK_REAL *alphaSphorCartGF = alp; """ # It's ugly if we output code in the following ordering, so we'll first # output to a string and then sort the string to beautify the code a bit. outstrtmp = [] for i in range(3): outstrtmp.append(" CCTK_REAL *betaSphorCartU" + str(i) + "GF = beta" + chr(ord('x') + i) + ";\n") outstrtmp.append(" CCTK_REAL *BSphorCartU" + str(i) + "GF = dtbeta" + chr(ord('x') + i) + ";\n") for j in range(i, 3): outstrtmp.append(" CCTK_REAL *gammaSphorCartDD" + str(i) + str(j) + "GF = g" + chr(ord('x') + i) + chr(ord('x') + j) + ";\n") outstrtmp.append(" CCTK_REAL *KSphorCartDD" + str(i) + str(j) + "GF = k" + chr(ord('x') + i) + chr(ord('x') + j) + ";\n") outstrtmp.sort() for line in outstrtmp: outstr += line # ADM to BSSN, Part 2: Set up ADM to BSSN conversions for BSSN gridfunctions that do not require # finite-difference derivatives (i.e., all gridfunctions except lambda^i (=Gamma^i # in non-covariant BSSN)): # h_{ij}, a_{ij}, trK, vet^i=beta^i,bet^i=B^i, cf (conformal factor), and alpha all_but_lambdaU_expressions = [ lhrh(lhs=gri.gfaccess("in_gfs", "hDD00"), rhs=IDhDD[0][0]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD01"), rhs=IDhDD[0][1]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD02"), rhs=IDhDD[0][2]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD11"), rhs=IDhDD[1][1]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD12"), rhs=IDhDD[1][2]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD22"), rhs=IDhDD[2][2]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD00"), rhs=IDaDD[0][0]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD01"), rhs=IDaDD[0][1]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD02"), rhs=IDaDD[0][2]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD11"), rhs=IDaDD[1][1]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD12"), rhs=IDaDD[1][2]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD22"), rhs=IDaDD[2][2]), lhrh(lhs=gri.gfaccess("in_gfs", "trK"), rhs=IDtrK), lhrh(lhs=gri.gfaccess("in_gfs", "vetU0"), rhs=IDvetU[0]), lhrh(lhs=gri.gfaccess("in_gfs", "vetU1"), rhs=IDvetU[1]), lhrh(lhs=gri.gfaccess("in_gfs", "vetU2"), rhs=IDvetU[2]), lhrh(lhs=gri.gfaccess("in_gfs", "betU0"), rhs=IDbetU[0]), lhrh(lhs=gri.gfaccess("in_gfs", "betU1"), rhs=IDbetU[1]), lhrh(lhs=gri.gfaccess("in_gfs", "betU2"), rhs=IDbetU[2]), lhrh(lhs=gri.gfaccess("in_gfs", "alpha"), rhs=IDalpha), lhrh(lhs=gri.gfaccess("in_gfs", "cf"), rhs=IDcf) ] outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" all_but_lambdaU_outC = fin.FD_outputC("returnstring", all_but_lambdaU_expressions, outCparams) outstr += lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], ["cctk_lsh[2]", "cctk_lsh[1]", "cctk_lsh[0]"], ["1", "1", "1"], ["#pragma omp parallel for", "", ""], " ", all_but_lambdaU_outC) # ADM to BSSN, Part 3: Set up ADM to BSSN conversions for BSSN gridfunctions defined from # finite-difference derivatives: lambda^i, which is Gamma^i in non-covariant BSSN): outstr += """ const CCTK_REAL invdx0 = 1.0/CCTK_DELTA_SPACE(0); const CCTK_REAL invdx1 = 1.0/CCTK_DELTA_SPACE(1); const CCTK_REAL invdx2 = 1.0/CCTK_DELTA_SPACE(2); """ path = os.path.join(ThornName, "src") BSSN_RHS_FD_orders_output = [] for root, dirs, files in os.walk(path): for file in files: if "BSSN_RHSs_FD_order" in file: array = file.replace(".", "_").split("_") BSSN_RHS_FD_orders_output.append(int(array[4])) for current_FD_order in BSSN_RHS_FD_orders_output: # Store original finite-differencing order: orig_FD_order = par.parval_from_str( "finite_difference::FD_CENTDERIVS_ORDER") # Set new finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", current_FD_order) outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" lambdaU_expressions = [ lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU0"), rhs=IDlambdaU[0]), lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU1"), rhs=IDlambdaU[1]), lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU2"), rhs=IDlambdaU[2]) ] lambdaU_expressions_FDout = fin.FD_outputC("returnstring", lambdaU_expressions, outCparams) lambdafile = "ADM_to_BSSN__compute_lambdaU_FD_order_" + str( current_FD_order) + ".h" with open(os.path.join(ThornName, "src", lambdafile), "w") as file: file.write( lp.loop(["i2", "i1", "i0"], [ "cctk_nghostzones[2]", "cctk_nghostzones[1]", "cctk_nghostzones[0]" ], [ "cctk_lsh[2]-cctk_nghostzones[2]", "cctk_lsh[1]-cctk_nghostzones[1]", "cctk_lsh[0]-cctk_nghostzones[0]" ], ["1", "1", "1"], ["#pragma omp parallel for", "", ""], "", lambdaU_expressions_FDout)) outstr += " if(FD_order == " + str(current_FD_order) + ") {\n" outstr += " #include \"" + lambdafile + "\"\n" outstr += " }\n" # Restore original FD order par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", orig_FD_order) outstr += """ ExtrapolateGammas(cctkGH,lambdaU0GF); ExtrapolateGammas(cctkGH,lambdaU1GF); ExtrapolateGammas(cctkGH,lambdaU2GF); } """ # Unregister the *SphorCartGF's. gri.glb_gridfcs_list = orig_glb_gridfcs_list # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("ADM_to_BSSN.c")] = outstr.replace( "BaikalETK", ThornName) import BSSN.ADM_in_terms_of_BSSN as btoa import BSSN.BSSN_quantities as Bq btoa.ADM_in_terms_of_BSSN() Bq.BSSN_basic_tensors() # Gives us betaU & BU outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_BSSN_to_ADM(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; """ btoa_lhrh = [] for i in range(3): for j in range(i, 3): btoa_lhrh.append( lhrh(lhs="g" + chr(ord('x') + i) + chr(ord('x') + j) + "[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)]", rhs=btoa.gammaDD[i][j])) for i in range(3): for j in range(i, 3): btoa_lhrh.append( lhrh(lhs="k" + chr(ord('x') + i) + chr(ord('x') + j) + "[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)]", rhs=btoa.KDD[i][j])) btoa_lhrh.append( lhrh(lhs="alp[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)]", rhs=Bq.alpha)) for i in range(3): btoa_lhrh.append( lhrh(lhs="beta" + chr(ord('x') + i) + "[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)]", rhs=Bq.betaU[i])) for i in range(3): btoa_lhrh.append( lhrh(lhs="dtbeta" + chr(ord('x') + i) + "[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)]", rhs=Bq.BU[i])) outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" bssn_to_adm_Ccode = fin.FD_outputC("returnstring", btoa_lhrh, outCparams) outstr += lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], ["cctk_lsh[2]", "cctk_lsh[1]", "cctk_lsh[0]"], ["1", "1", "1"], ["#pragma omp parallel for", "", ""], "", bssn_to_adm_Ccode) outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("BSSN_to_ADM.c")] = outstr.replace( "BaikalETK", ThornName) # Next, the driver for computing the Ricci tensor: outstr = """ #include <math.h> #include "SIMD/SIMD_intrinsics.h" #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_driver_pt1_BSSN_Ricci(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; const CCTK_INT *FD_order = CCTK_ParameterGet("FD_order","BaikalETK",NULL); const CCTK_REAL NOSIMDinvdx0 = 1.0/CCTK_DELTA_SPACE(0); const REAL_SIMD_ARRAY invdx0 = ConstSIMD(NOSIMDinvdx0); const CCTK_REAL NOSIMDinvdx1 = 1.0/CCTK_DELTA_SPACE(1); const REAL_SIMD_ARRAY invdx1 = ConstSIMD(NOSIMDinvdx1); const CCTK_REAL NOSIMDinvdx2 = 1.0/CCTK_DELTA_SPACE(2); const REAL_SIMD_ARRAY invdx2 = ConstSIMD(NOSIMDinvdx2); """ path = os.path.join(ThornName, "src") for root, dirs, files in os.walk(path): for file in files: if "BSSN_Ricci_FD_order" in file: array = file.replace(".", "_").split("_") outstr += " if(*FD_order == " + str(array[4]) + ") {\n" outstr += " #include \"" + file + "\"\n" outstr += " }\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "driver_pt1_BSSN_Ricci.c")] = outstr.replace("BaikalETK", ThornName) def SIMD_declare_C_params(): SIMD_declare_C_params_str = "" for i in range(len(par.glb_Cparams_list)): # keep_param is a boolean indicating whether we should accept or reject # the parameter. singleparstring will contain the string indicating # the variable type. keep_param, singleparstring = ccl.keep_param__return_type( par.glb_Cparams_list[i]) if (keep_param) and ("CCTK_REAL" in singleparstring): parname = par.glb_Cparams_list[i].parname SIMD_declare_C_params_str += " const "+singleparstring + "*NOSIMD"+parname+\ " = CCTK_ParameterGet(\""+parname+"\",\"BaikalETK\",NULL);\n" SIMD_declare_C_params_str += " const REAL_SIMD_ARRAY " + parname + " = ConstSIMD(*NOSIMD" + parname + ");\n" return SIMD_declare_C_params_str # Next, the driver for computing the BSSN RHSs: outstr = """ #include <math.h> #include "SIMD/SIMD_intrinsics.h" #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_driver_pt2_BSSN_RHSs(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; const CCTK_INT *FD_order = CCTK_ParameterGet("FD_order","BaikalETK",NULL); const CCTK_REAL NOSIMDinvdx0 = 1.0/CCTK_DELTA_SPACE(0); const REAL_SIMD_ARRAY invdx0 = ConstSIMD(NOSIMDinvdx0); const CCTK_REAL NOSIMDinvdx1 = 1.0/CCTK_DELTA_SPACE(1); const REAL_SIMD_ARRAY invdx1 = ConstSIMD(NOSIMDinvdx1); const CCTK_REAL NOSIMDinvdx2 = 1.0/CCTK_DELTA_SPACE(2); const REAL_SIMD_ARRAY invdx2 = ConstSIMD(NOSIMDinvdx2); """ + SIMD_declare_C_params() path = os.path.join(ThornName, "src") for root, dirs, files in os.walk(path): for file in files: if "BSSN_RHSs_FD_order" in file: array = file.replace(".", "_").split("_") outstr += " if(*FD_order == " + str(array[4]) + ") {\n" outstr += " #include \"" + file + "\"\n" outstr += " }\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "driver_pt2_BSSN_RHSs.c")] = outstr.replace("BaikalETK", ThornName) # Next, the driver for enforcing detgammabar = detgammahat constraint: outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_enforce_detgammabar_constraint(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; """ path = os.path.join(ThornName, "src") for root, dirs, files in os.walk(path): for file in files: if "enforcedetgammabar_constraint_FD_order" in file: array = file.replace(".", "_").split("_") outstr += " if(FD_order == " + str(array[4]) + ") {\n" outstr += " #include \"" + file + "\"\n" outstr += " }\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("driver_enforcedetgammabar_constraint.c")] = \ outstr.replace("BaikalETK",ThornName) # Next, the driver for computing the BSSN Hamiltonian & momentum constraints outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_BSSN_constraints(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; const CCTK_REAL invdx0 = 1.0/CCTK_DELTA_SPACE(0); const CCTK_REAL invdx1 = 1.0/CCTK_DELTA_SPACE(1); const CCTK_REAL invdx2 = 1.0/CCTK_DELTA_SPACE(2); """ path = os.path.join(ThornName, "src") for root, dirs, files in os.walk(path): for file in files: if "BSSN_constraints_FD_order" in file: array = file.replace(".", "_").split("_") outstr += " if(FD_order == " + str(array[4]) + ") {\n" outstr += " #include \"" + file + "\"\n" outstr += " }\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "driver_BSSN_constraints.c")] = outstr.replace("BaikalETK", ThornName) if enable_stress_energy_source_terms == True: # Declare T4DD as a set of gridfunctions. These won't # actually appear in interface.ccl, as interface.ccl # was set above. Thus before calling the code output # by FD_outputC(), we'll have to set pointers # to the actual gridfunctions they reference. # (In this case the eTab's.) T4DD = ixp.register_gridfunctions_for_single_rank2("AUXEVOL", "T4DD", "sym01", DIM=4) import BSSN.ADMBSSN_tofrom_4metric as AB4m AB4m.g4UU_ito_BSSN_or_ADM("BSSN") T4UUraised = ixp.zerorank2(DIM=4) for mu in range(4): for nu in range(4): for delta in range(4): for gamma in range(4): T4UUraised[mu][nu] += AB4m.g4UU[mu][delta] * AB4m.g4UU[ nu][gamma] * T4DD[delta][gamma] T4UU_expressions = [ lhrh(lhs=gri.gfaccess("in_gfs", "T4UU00"), rhs=T4UUraised[0][0]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU01"), rhs=T4UUraised[0][1]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU02"), rhs=T4UUraised[0][2]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU03"), rhs=T4UUraised[0][3]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU11"), rhs=T4UUraised[1][1]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU12"), rhs=T4UUraised[1][2]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU13"), rhs=T4UUraised[1][3]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU22"), rhs=T4UUraised[2][2]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU23"), rhs=T4UUraised[2][3]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU33"), rhs=T4UUraised[3][3]) ] outCparams = "outCverbose=False,includebraces=False,preindent=2,SIMD_enable=True" T4UUstr = fin.FD_outputC("returnstring", T4UU_expressions, outCparams) T4UUstr_loop = lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], ["cctk_lsh[2]", "cctk_lsh[1]", "cctk_lsh[0]"], ["1", "1", "SIMD_width"], ["#pragma omp parallel for", "", ""], "", T4UUstr) outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" #include "SIMD/SIMD_intrinsics.h" void BaikalETK_driver_BSSN_T4UU(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; const CCTK_REAL *restrict T4DD00GF = eTtt; const CCTK_REAL *restrict T4DD01GF = eTtx; const CCTK_REAL *restrict T4DD02GF = eTty; const CCTK_REAL *restrict T4DD03GF = eTtz; const CCTK_REAL *restrict T4DD11GF = eTxx; const CCTK_REAL *restrict T4DD12GF = eTxy; const CCTK_REAL *restrict T4DD13GF = eTxz; const CCTK_REAL *restrict T4DD22GF = eTyy; const CCTK_REAL *restrict T4DD23GF = eTyz; const CCTK_REAL *restrict T4DD33GF = eTzz; """ + T4UUstr_loop + """ }\n""" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "driver_BSSN_T4UU.c")] = outstr.replace("BaikalETK", ThornName)
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 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 stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars( inputvars, custom_T4UU=None): # Step 1: Check if rfm.reference_metric() already called. If not, BSSN # quantities are not yet defined, so cannot proceed! if rfm.have_already_called_reference_metric_function == False: print( "BSSN_source_terms_ito_T4UU(): Must call reference_metric() first!" ) sys.exit(1) # Step 2.a: Define gamma4DD[mu][nu] = g_{mu nu} + n_{mu} n_{nu} alpha = sp.symbols("alpha", real=True) zero = sp.sympify(0) n4D = [-alpha, zero, zero, zero] AB4m.g4DD_ito_BSSN_or_ADM(inputvars) gamma4DD = ixp.zerorank2(DIM=4) for mu in range(4): for nu in range(4): gamma4DD[mu][nu] = AB4m.g4DD[mu][nu] + n4D[mu] * n4D[nu] # Step 2.b: If expression for components of T4UU not given, declare T4UU here if custom_T4UU == None: T4UU = ixp.declarerank2("T4UU", "sym01", DIM=4) else: T4UU = custom_T4UU # Step 2.c: Define BSSN source terms global SDD, SD, S, rho # Step 2.c.i: S_{ij} = gamma_{i mu} gamma_{j nu} T^{mu nu} SDD = ixp.zerorank2() for i in range(3): for j in range(3): for mu in range(4): for nu in range(4): SDD[i][j] += gamma4DD[i + 1][mu] * gamma4DD[ j + 1][nu] * T4UU[mu][nu] # Step 2.c.ii: S_{i} = -gamma_{i mu} n_{nu} T^{mu nu} SD = ixp.zerorank1() for i in range(3): for mu in range(4): for nu in range(4): SD[i] += -gamma4DD[i + 1][mu] * n4D[nu] * T4UU[mu][nu] # Step 2.c.iii: S = gamma^{ij} S_{ij} if inputvars == "ADM": gammaDD = ixp.declarerank2("gammaDD", "sym01") gammaUU, dummydet = ixp.symm_matrix_inverter3x3(gammaDD) # Set gammaUU elif inputvars == "BSSN": import BSSN.ADM_in_terms_of_BSSN as AitoB # NRPy+: ADM quantities in terms of BSSN quantities AitoB.ADM_in_terms_of_BSSN() gammaUU = AitoB.gammaUU S = zero for i in range(3): for j in range(3): S += gammaUU[i][j] * SDD[i][j] # Step 2.c.iv: rho = n_{mu} n_{nu} T^{mu nu} rho = zero for mu in range(4): for nu in range(4): rho += n4D[mu] * n4D[nu] * T4UU[mu][nu] return SDD, SD, S, rho
def Psi4(): global psi4_im, psi4_re # Step 1.b: 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.c: 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.d: 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: Construct the (rank-4) Riemann curvature tensor associated with the ADM 3-metric: RDDDD = ixp.zerorank4() gammaDDdDD = AB.gammaDDdDD for i in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): RDDDD[i][k][l][m] = sp.Rational(1, 2) * \ (gammaDDdDD[i][m][k][l] + gammaDDdDD[k][l][i][m] - gammaDDdDD[i][l][k][m] - gammaDDdDD[k][m][i][l]) # ... then we add the term on the right: gammaDD = AB.gammaDD GammaUDD = AB.GammaUDD for i in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): for n in range(DIM): for p in range(DIM): RDDDD[i][k][l][m] += gammaDD[n][p] * \ (GammaUDD[n][k][l] * GammaUDD[p][i][m] - GammaUDD[n][k][m] * GammaUDD[p][i][l]) # Step 3: Construct the (rank-4) tensor in term 1 of psi_4 (referring to Eq 5.1 in # Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf rank4term1 = ixp.zerorank4() KDD = AB.KDD for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): rank4term1[i][j][k][l] = RDDDD[i][j][k][ l] + KDD[i][k] * KDD[l][j] - KDD[i][l] * KDD[k][j] # Step 4: Construct the (rank-3) tensor in term 2 of psi_4 (referring to Eq 5.1 in # Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf rank3term2 = ixp.zerorank3() KDDdD = AB.KDDdD for j in range(DIM): for k in range(DIM): for l in range(DIM): rank3term2[j][k][l] = sp.Rational( 1, 2) * (KDDdD[j][k][l] - KDDdD[j][l][k]) # ... then we construct the second term in this sum: # \Gamma^{p}_{j[k} K_{l]p} = \frac{1}{2} (\Gamma^{p}_{jk} K_{lp}-\Gamma^{p}_{jl} K_{kp}): for j in range(DIM): for k in range(DIM): for l in range(DIM): for p in range(DIM): rank3term2[j][k][l] += sp.Rational( 1, 2) * (GammaUDD[p][j][k] * KDD[l][p] - GammaUDD[p][j][l] * KDD[k][p]) # Finally, we multiply the term by $-8$: for j in range(DIM): for k in range(DIM): for l in range(DIM): rank3term2[j][k][l] *= sp.sympify(-8) # Step 5: Construct the (rank-2) tensor in term 3 of psi_4 (referring to Eq 5.1 in # Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf rank2term3 = ixp.zerorank2() gammaUU = AB.gammaUU for j in range(DIM): for l in range(DIM): for i in range(DIM): for m in range(DIM): rank2term3[j][l] += gammaUU[i][m] * RDDDD[i][j][m][l] # ... then we add on the second term in parentheses, where $K^p_l = \gamma^{mp} K_{ml}$ for j in range(DIM): for l in range(DIM): for m in range(DIM): for p in range(DIM): rank2term3[j][l] += -KDD[j][p] * gammaUU[p][m] * KDD[m][l] # Finally we add the third term in parentheses, and multiply all terms by $+4$: for j in range(DIM): for l in range(DIM): for i in range(DIM): for m in range(DIM): rank2term3[j][l] += gammaUU[i][m] * KDD[i][m] * KDD[j][l] for j in range(DIM): for l in range(DIM): rank2term3[j][l] *= sp.sympify(4) mre4U = ixp.declarerank1("mre4U", DIM=4) mim4U = ixp.declarerank1("mim4U", DIM=4) n4U = ixp.declarerank1("n4U", DIM=4) def tetrad_product__Real_psi4(n, Mre, Mim, mu, nu, eta, delta): return +n[mu] * Mre[nu] * n[eta] * Mre[delta] - n[mu] * Mim[nu] * n[ eta] * Mim[delta] def tetrad_product__Imag_psi4(n, Mre, Mim, mu, nu, eta, delta): return -n[mu] * Mre[nu] * n[eta] * Mim[delta] - n[mu] * Mim[nu] * n[ eta] * Mre[delta] psi4_re = sp.sympify(0) psi4_im = sp.sympify(0) # First term: for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): psi4_re += rank4term1[i][j][ k][l] * tetrad_product__Real_psi4( n4U, mre4U, mim4U, i + 1, j + 1, k + 1, l + 1) psi4_im += rank4term1[i][j][ k][l] * tetrad_product__Imag_psi4( n4U, mre4U, mim4U, i + 1, j + 1, k + 1, l + 1) # Second term: for j in range(DIM): for k in range(DIM): for l in range(DIM): psi4_re += rank3term2[j][k][l] * \ sp.Rational(1, 2) * (+tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, k + 1, l + 1) - tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, k + 1, l + 1)) psi4_im += rank3term2[j][k][l] * \ sp.Rational(1, 2) * (+tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, k + 1, l + 1) - tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, k + 1, l + 1)) # Third term: for j in range(DIM): for l in range(DIM): psi4_re += rank2term3[j][l] * \ (sp.Rational(1, 4) * (+tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, 0, l + 1) - tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, 0, l + 1) - tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, l + 1, 0) + tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, l + 1, 0))) psi4_im += rank2term3[j][l] * \ (sp.Rational(1, 4) * (+tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, 0, l + 1) - tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, 0, l + 1) - tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, l + 1, 0) + tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, l + 1, 0)))