def BSSN_source_terms_for_BSSN_RHSs(custom_T4UU=None): global sourceterm_trK_rhs, sourceterm_a_rhsDD, sourceterm_lambda_rhsU, sourceterm_Lambdabar_rhsU # Step 3.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") alpha = sp.symbols("alpha", real=True) # Step 3.b: trK_rhs sourceterm_trK_rhs = 4 * PI * alpha * (rho + S) # Step 3.c: Abar_rhsDD: # Step 3.c.i: Compute trace-free part of S_{ij}: import BSSN.BSSN_quantities as Bq Bq.BSSN_basic_tensors() # Sets gammabarDD gammabarUU, dummydet = ixp.symm_matrix_inverter3x3( Bq.gammabarDD) # Set gammabarUU tracefree_SDD = ixp.zerorank2() for i in range(3): for j in range(3): tracefree_SDD[i][j] = SDD[i][j] for i in range(3): for j in range(3): for k in range(3): for m in range(3): tracefree_SDD[i][j] += -sp.Rational(1, 3) * Bq.gammabarDD[ i][j] * gammabarUU[k][m] * SDD[k][m] # Step 3.c.ii: Define exp_m4phi = e^{-4 phi} Bq.phi_and_derivs() # Step 3.c.iii: Evaluate stress-energy part of AbarDD's RHS sourceterm_a_rhsDD = ixp.zerorank2() for i in range(3): for j in range(3): Abar_rhsDDij = -8 * PI * alpha * Bq.exp_m4phi * tracefree_SDD[i][j] sourceterm_a_rhsDD[i][j] = Abar_rhsDDij / rfm.ReDD[i][j] # Step 3.d: Stress-energy part of Lambdabar_rhsU = stressenergy_Lambdabar_rhsU sourceterm_Lambdabar_rhsU = ixp.zerorank1() for i in range(3): for j in range(3): sourceterm_Lambdabar_rhsU[ i] += -16 * PI * alpha * gammabarUU[i][j] * SD[j] sourceterm_lambda_rhsU = ixp.zerorank1() for i in range(3): sourceterm_lambda_rhsU[i] = sourceterm_Lambdabar_rhsU[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 Enforce_Detgammabar_Constraint_symb_expressions(): # Set spatial dimension (must be 3 for BSSN) DIM = 3 # Then we set the coordinate system for the numerical grid rfm.reference_metric( ) # Create ReU, ReDD needed for rescaling B-L initial data, generating BSSN RHSs, etc. # We will need the h_{ij} quantities defined within BSSN_RHSs # below when we enforce the gammahat=gammabar constraint # Step 1: All barred quantities are defined in terms of BSSN rescaled gridfunctions, # which we declare here in case they haven't yet been declared elsewhere. Bq.declare_BSSN_gridfunctions_if_not_declared_already() hDD = Bq.hDD Bq.BSSN_basic_tensors() gammabarDD = Bq.gammabarDD # First define the Kronecker delta: KroneckerDeltaDD = ixp.zerorank2() for i in range(DIM): KroneckerDeltaDD[i][i] = sp.sympify(1) # The detgammabar in BSSN_RHSs is set to detgammahat when BSSN_RHSs::detgbarOverdetghat_equals_one=True (default), # so we manually compute it here: dummygammabarUU, detgammabar = ixp.symm_matrix_inverter3x3(gammabarDD) # Next apply the constraint enforcement equation above. hprimeDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): hprimeDD[i][j] = \ (nrpyAbs(rfm.detgammahat) / detgammabar) ** (sp.Rational(1, 3)) * (KroneckerDeltaDD[i][j] + hDD[i][j]) \ - KroneckerDeltaDD[i][j] enforce_detg_constraint_symb_expressions = [ lhrh(lhs=gri.gfaccess("in_gfs", "hDD00"), rhs=hprimeDD[0][0]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD01"), rhs=hprimeDD[0][1]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD02"), rhs=hprimeDD[0][2]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD11"), rhs=hprimeDD[1][1]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD12"), rhs=hprimeDD[1][2]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD22"), rhs=hprimeDD[2][2]) ] return enforce_detg_constraint_symb_expressions
def Ricci__generate_symbolic_expressions(): ###################################### # START: GENERATE SYMBOLIC EXPRESSIONS starttime = print_msg_with_timing("3-Ricci tensor", msg="Symbolic", startstop="start") # Evaluate 3-Ricci tensor: import BSSN.BSSN_quantities as Bq par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic", "False") # Register all BSSN gridfunctions if not registered already Bq.BSSN_basic_tensors() # Next compute Ricci tensor Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU() # END: GENERATE SYMBOLIC EXPRESSIONS ###################################### # Must register RbarDD as gridfunctions, as we're outputting them to gridfunctions here: foundit = False for i in range(len(gri.glb_gridfcs_list)): if "RbarDD00" in gri.glb_gridfcs_list[i].name: foundit = True if not foundit: ixp.register_gridfunctions_for_single_rank2("AUXEVOL", "RbarDD", "sym01") Ricci_SymbExpressions = [ lhrh(lhs=gri.gfaccess("auxevol_gfs", "RbarDD00"), rhs=Bq.RbarDD[0][0]), lhrh(lhs=gri.gfaccess("auxevol_gfs", "RbarDD01"), rhs=Bq.RbarDD[0][1]), lhrh(lhs=gri.gfaccess("auxevol_gfs", "RbarDD02"), rhs=Bq.RbarDD[0][2]), lhrh(lhs=gri.gfaccess("auxevol_gfs", "RbarDD11"), rhs=Bq.RbarDD[1][1]), lhrh(lhs=gri.gfaccess("auxevol_gfs", "RbarDD12"), rhs=Bq.RbarDD[1][2]), lhrh(lhs=gri.gfaccess("auxevol_gfs", "RbarDD22"), rhs=Bq.RbarDD[2][2]) ] print_msg_with_timing("3-Ricci tensor", msg="Symbolic", startstop="stop", starttime=starttime) return Ricci_SymbExpressions
def Ricci__generate_symbolic_expressions(): ###################################### # START: GENERATE SYMBOLIC EXPRESSIONS print("Generating symbolic expressions for Ricci tensor...") start = time.time() # Enable rfm_precompute infrastructure, which results in # BSSN RHSs that are free of transcendental functions, # even in curvilinear coordinates, so long as # ConformalFactor is set to "W" (default). par.set_parval_from_str("reference_metric::enable_rfm_precompute","True") par.set_parval_from_str("reference_metric::rfm_precompute_Ccode_outdir",os.path.join(outdir,"rfm_files/")) # Evaluate BSSN + BSSN gauge RHSs with rfm_precompute enabled: import BSSN.BSSN_quantities as Bq # Next compute Ricci tensor # par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","False") RbarDD_already_registered = False for i in range(len(gri.glb_gridfcs_list)): if "RbarDD00" in gri.glb_gridfcs_list[i].name: RbarDD_already_registered = True if not RbarDD_already_registered: # We ignore the return value of ixp.register_gridfunctions_for_single_rank2() below # as it is unused. ixp.register_gridfunctions_for_single_rank2("AUXEVOL","RbarDD","sym01") rhs.BSSN_RHSs() Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU() # Now that we are finished with all the rfm hatted # quantities in generic precomputed functional # form, let's restore them to their closed- # form expressions. par.set_parval_from_str("reference_metric::enable_rfm_precompute","False") # Reset to False to disable rfm_precompute. rfm.ref_metric__hatted_quantities() end = time.time() print("(BENCH) Finished Ricci symbolic expressions in "+str(end-start)+" seconds.") # END: GENERATE SYMBOLIC EXPRESSIONS ###################################### Ricci_SymbExpressions = [lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD00"),rhs=Bq.RbarDD[0][0]), lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD01"),rhs=Bq.RbarDD[0][1]), lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD02"),rhs=Bq.RbarDD[0][2]), lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD11"),rhs=Bq.RbarDD[1][1]), lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD12"),rhs=Bq.RbarDD[1][2]), lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD22"),rhs=Bq.RbarDD[2][2])] return [Ricci_SymbExpressions]
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 ADM_in_terms_of_BSSN(): global gammaDD, gammaDDdD, gammaDDdDD, gammaUU, detgamma, GammaUDD, KDD, KDDdD # Step 1.c: 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.d: 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.e: Import all basic (unrescaled) BSSN scalars & tensors import BSSN.BSSN_quantities as Bq Bq.BSSN_basic_tensors() gammabarDD = Bq.gammabarDD cf = Bq.cf AbarDD = Bq.AbarDD trK = Bq.trK Bq.gammabar__inverse_and_derivs() gammabarDD_dD = Bq.gammabarDD_dD gammabarDD_dDD = Bq.gammabarDD_dDD Bq.AbarUU_AbarUD_trAbar_AbarDD_dD() AbarDD_dD = Bq.AbarDD_dD # Step 2: The ADM three-metric gammaDD and its # derivatives in terms of BSSN quantities. gammaDD = ixp.zerorank2() exp4phi = sp.sympify(0) if par.parval_from_str("EvolvedConformalFactor_cf") == "phi": exp4phi = sp.exp(4 * cf) elif par.parval_from_str("EvolvedConformalFactor_cf") == "chi": exp4phi = (1 / cf) elif par.parval_from_str("EvolvedConformalFactor_cf") == "W": exp4phi = (1 / cf ** 2) else: print("Error EvolvedConformalFactor_cf type = \"" + par.parval_from_str("EvolvedConformalFactor_cf") + "\" unknown.") sys.exit(1) for i in range(DIM): for j in range(DIM): gammaDD[i][j] = exp4phi * gammabarDD[i][j] # Step 2.a: Derivatives of $e^{4\phi}$ phidD = ixp.zerorank1() phidDD = ixp.zerorank2() cf_dD = ixp.declarerank1("cf_dD") cf_dDD = ixp.declarerank2("cf_dDD","sym01") if par.parval_from_str("EvolvedConformalFactor_cf") == "phi": for i in range(DIM): phidD[i] = cf_dD[i] for j in range(DIM): phidDD[i][j] = cf_dDD[i][j] elif par.parval_from_str("EvolvedConformalFactor_cf") == "chi": for i in range(DIM): phidD[i] = -sp.Rational(1,4)*exp4phi*cf_dD[i] for j in range(DIM): phidDD[i][j] = sp.Rational(1,4)*( exp4phi**2*cf_dD[i]*cf_dD[j] - exp4phi*cf_dDD[i][j] ) elif par.parval_from_str("EvolvedConformalFactor_cf") == "W": exp2phi = (1 / cf) for i in range(DIM): phidD[i] = -sp.Rational(1,2)*exp2phi*cf_dD[i] for j in range(DIM): phidDD[i][j] = sp.Rational(1,2)*( exp4phi*cf_dD[i]*cf_dD[j] - exp2phi*cf_dDD[i][j] ) else: print("Error EvolvedConformalFactor_cf type = \""+par.parval_from_str("EvolvedConformalFactor_cf")+"\" unknown.") sys.exit(1) exp4phidD = ixp.zerorank1() exp4phidDD = ixp.zerorank2() for i in range(DIM): exp4phidD[i] = 4*exp4phi*phidD[i] for j in range(DIM): exp4phidDD[i][j] = 16*exp4phi*phidD[i]*phidD[j] + 4*exp4phi*phidDD[i][j] # Step 2.b: Derivatives of gammaDD, the ADM three-metric gammaDDdD = ixp.zerorank3() gammaDDdDD = ixp.zerorank4() for i in range(DIM): for j in range(DIM): for k in range(DIM): gammaDDdD[i][j][k] = exp4phidD[k] * gammabarDD[i][j] + exp4phi * gammabarDD_dD[i][j][k] for l in range(DIM): gammaDDdDD[i][j][k][l] = exp4phidDD[k][l] * gammabarDD[i][j] + \ exp4phidD[k] * gammabarDD_dD[i][j][l] + \ exp4phidD[l] * gammabarDD_dD[i][j][k] + \ exp4phi * gammabarDD_dDD[i][j][k][l] # Step 2.c: 3-Christoffel symbols associated with ADM 3-metric gammaDD # Step 2.c.i: First compute the inverse 3-metric gammaUU: gammaUU, detgamma = ixp.symm_matrix_inverter3x3(gammaDD) GammaUDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): GammaUDD[i][j][k] += sp.Rational(1,2)*gammaUU[i][l]* \ (gammaDDdD[l][j][k] + gammaDDdD[l][k][j] - gammaDDdD[j][k][l]) # Step 3: Define ADM extrinsic curvature KDD and # its first spatial derivatives KDDdD # in terms of BSSN quantities KDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): KDD[i][j] = exp4phi * AbarDD[i][j] + sp.Rational(1, 3) * gammaDD[i][j] * trK KDDdD = ixp.zerorank3() trK_dD = ixp.declarerank1("trK_dD") for i in range(DIM): for j in range(DIM): for k in range(DIM): KDDdD[i][j][k] = exp4phidD[k] * AbarDD[i][j] + exp4phi * AbarDD_dD[i][j][k] + \ sp.Rational(1, 3) * (gammaDDdD[i][j][k] * trK + gammaDD[i][j] * trK_dD[k])
def BSSN_gauge_RHSs(): # Step 1.d: 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.e: 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.f: Define needed BSSN quantities: # Declare scalars & tensors (in terms of rescaled BSSN quantities) Bq.BSSN_basic_tensors() Bq.betaU_derivs() # Declare BSSN_RHSs (excluding the time evolution equations for the gauge conditions) Brhs.BSSN_RHSs() # Step 2.a: The 1+log lapse condition: # \partial_t \alpha = \beta^i \alpha_{,i} - 2*\alpha*K # First import expressions from BSSN_quantities cf = Bq.cf trK = Bq.trK alpha = Bq.alpha betaU = Bq.betaU # Implement the 1+log lapse condition global alpha_rhs alpha_rhs = sp.sympify(0) if par.parval_from_str(thismodule + "::LapseEvolutionOption") == "OnePlusLog": alpha_rhs = -2 * alpha * trK alpha_dupD = ixp.declarerank1("alpha_dupD") for i in range(DIM): alpha_rhs += betaU[i] * alpha_dupD[i] # Implement the harmonic slicing lapse condition elif par.parval_from_str(thismodule + "::LapseEvolutionOption") == "HarmonicSlicing": if par.parval_from_str("BSSN.BSSN_quantities::ConformalFactor") == "W": alpha_rhs = -3 * cf**(-4) * Brhs.cf_rhs elif par.parval_from_str( "BSSN.BSSN_quantities::ConformalFactor") == "phi": alpha_rhs = 6 * sp.exp(6 * cf) * Brhs.cf_rhs else: print( "Error LapseEvolutionOption==HarmonicSlicing unsupported for ConformalFactor!=(W or phi)" ) exit(1) # Step 2.c: Frozen lapse # \partial_t \alpha = 0 elif par.parval_from_str(thismodule + "::LapseEvolutionOption") == "Frozen": alpha_rhs = sp.sympify(0) else: print("Error: " + thismodule + "::LapseEvolutionOption == " + par.parval_from_str(thismodule + "::LapseEvolutionOption") + " not supported!") exit(1) # Step 3.a: Set \partial_t \beta^i # First import expressions from BSSN_quantities BU = Bq.BU betU = Bq.betU betaU_dupD = Bq.betaU_dupD # Define needed quantities beta_rhsU = ixp.zerorank1() B_rhsU = ixp.zerorank1() if par.parval_from_str( thismodule + "::ShiftEvolutionOption") == "GammaDriving2ndOrder_NoCovariant": # Step 3.a.i: Compute right-hand side of beta^i # * \partial_t \beta^i = \beta^j \beta^i_{,j} + B^i for i in range(DIM): beta_rhsU[i] += BU[i] for j in range(DIM): beta_rhsU[i] += betaU[j] * betaU_dupD[i][j] # Compute right-hand side of B^i: eta = par.Cparameters("REAL", thismodule, ["eta"]) # Step 3.a.ii: Compute right-hand side of B^i # * \partial_t B^i = \beta^j B^i_{,j} + 3/4 * \partial_0 \Lambda^i - eta B^i # Step 15b: Define BU_dupD, in terms of derivative of rescaled variable \bet^i BU_dupD = ixp.zerorank2() betU_dupD = ixp.declarerank2("betU_dupD", "nosym") for i in range(DIM): for j in range(DIM): BU_dupD[i][j] = betU_dupD[i][j] * rfm.ReU[i] + betU[ i] * rfm.ReUdD[i][j] # Step 15c: Compute \partial_0 \bar{\Lambda}^i = (\partial_t - \beta^i \partial_i) \bar{\Lambda}^j Lambdabar_partial0 = ixp.zerorank1() for i in range(DIM): Lambdabar_partial0[i] = Brhs.Lambdabar_rhsU[i] for i in range(DIM): for j in range(DIM): Lambdabar_partial0[j] += -betaU[i] * Brhs.LambdabarU_dupD[j][i] # Step 15d: Evaluate RHS of B^i: for i in range(DIM): B_rhsU[i] += sp.Rational(3, 4) * Lambdabar_partial0[i] - eta * BU[i] for j in range(DIM): B_rhsU[i] += betaU[j] * BU_dupD[i][j] if par.parval_from_str( thismodule + "::ShiftEvolutionOption") == "GammaDriving2ndOrder_Covariant": # Step 14 Option 2: \partial_t \beta^i = \left[\beta^j \bar{D}_j \beta^i\right] + B^{i} # First we need GammabarUDD, defined in Bq.gammabar__inverse_and_derivs() Bq.gammabar__inverse_and_derivs() GammabarUDD = Bq.GammabarUDD # Then compute right-hand side: # Term 1: \beta^j \beta^i_{,j} for i in range(DIM): for j in range(DIM): beta_rhsU[i] += betaU[j] * betaU_dupD[i][j] # Term 2: \beta^j \bar{\Gamma}^i_{mj} \beta^m for i in range(DIM): for j in range(DIM): for m in range(DIM): beta_rhsU[i] += betaU[j] * GammabarUDD[i][m][j] * betaU[m] # Term 3: B^i for i in range(DIM): beta_rhsU[i] += BU[i] if par.parval_from_str( thismodule + "::ShiftEvolutionOption") == "GammaDriving2ndOrder_Covariant": # Step 15: Covariant option: # \partial_t B^i = \beta^j \bar{D}_j B^i # + \frac{3}{4} ( \partial_t \bar{\Lambda}^{i} - \beta^j \bar{D}_j \bar{\Lambda}^{i} ) # - \eta B^{i} # = \beta^j B^i_{,j} + \beta^j \bar{\Gamma}^i_{mj} B^m # + \frac{3}{4}[ \partial_t \bar{\Lambda}^{i} # - \beta^j (\bar{\Lambda}^i_{,j} + \bar{\Gamma}^i_{mj} \bar{\Lambda}^m)] # - \eta B^{i} # Term 1, part a: First compute B^i_{,j} using upwinded derivative BU_dupD = ixp.zerorank2() betU_dupD = ixp.declarerank2("betU_dupD", "nosym") for i in range(DIM): for j in range(DIM): BU_dupD[i][j] = betU_dupD[i][j] * rfm.ReU[i] + betU[ i] * rfm.ReUdD[i][j] # Term 1: \beta^j B^i_{,j} for i in range(DIM): for j in range(DIM): B_rhsU[i] += betaU[j] * BU_dupD[i][j] # Term 2: \beta^j \bar{\Gamma}^i_{mj} B^m for i in range(DIM): for j in range(DIM): for m in range(DIM): B_rhsU[i] += betaU[j] * GammabarUDD[i][m][j] * BU[m] # Term 3: \frac{3}{4}\partial_t \bar{\Lambda}^{i} for i in range(DIM): B_rhsU[i] += sp.Rational(3, 4) * Brhs.Lambdabar_rhsU[i] # Term 4: -\frac{3}{4}\beta^j \bar{\Lambda}^i_{,j} for i in range(DIM): for j in range(DIM): B_rhsU[i] += -sp.Rational( 3, 4) * betaU[j] * Brhs.LambdabarU_dupD[i][j] # Term 5: -\frac{3}{4}\beta^j \bar{\Gamma}^i_{mj} \bar{\Lambda}^m for i in range(DIM): for j in range(DIM): for m in range(DIM): B_rhsU[i] += -sp.Rational(3, 4) * betaU[j] * GammabarUDD[ i][m][j] * Bq.LambdabarU[m] # Term 6: - \eta B^i # eta is a free parameter; we declare it here: eta = par.Cparameters("REAL", thismodule, ["eta"]) for i in range(DIM): B_rhsU[i] += -eta * BU[i] # Step 4: Rescale the BSSN gauge RHS quantities so that the evolved # variables may remain smooth across coord singularities global vet_rhsU, bet_rhsU vet_rhsU = ixp.zerorank1() bet_rhsU = ixp.zerorank1() for i in range(DIM): vet_rhsU[i] = beta_rhsU[i] / rfm.ReU[i] bet_rhsU[i] = B_rhsU[i] / rfm.ReU[i]
def BSSN_constraints(add_T4UUmunu_source_terms=False): # Step 1.a: 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.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 2: Hamiltonian constraint. # First declare all needed variables Bq.declare_BSSN_gridfunctions_if_not_declared_already() # Sets trK Bq.BSSN_basic_tensors() # Sets AbarDD Bq.gammabar__inverse_and_derivs() # Sets gammabarUU Bq.AbarUU_AbarUD_trAbar_AbarDD_dD() # Sets AbarUU and AbarDD_dD Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU() # Sets RbarDD Bq.phi_and_derivs() # Sets phi_dBarD & phi_dBarDD ############################### ############################### # HAMILTONIAN CONSTRAINT ############################### ############################### # Term 1: 2/3 K^2 global H H = sp.Rational(2, 3) * Bq.trK**2 # Term 2: -A_{ij} A^{ij} for i in range(DIM): for j in range(DIM): H += -Bq.AbarDD[i][j] * Bq.AbarUU[i][j] # Term 3a: trace(Rbar) Rbartrace = sp.sympify(0) for i in range(DIM): for j in range(DIM): Rbartrace += Bq.gammabarUU[i][j] * Bq.RbarDD[i][j] # Term 3b: -8 \bar{\gamma}^{ij} \bar{D}_i \phi \bar{D}_j \phi = -8*phi_dBar_times_phi_dBar # Term 3c: -8 \bar{\gamma}^{ij} \bar{D}_i \bar{D}_j \phi = -8*phi_dBarDD_contraction phi_dBar_times_phi_dBar = sp.sympify(0) # Term 3b phi_dBarDD_contraction = sp.sympify(0) # Term 3c for i in range(DIM): for j in range(DIM): phi_dBar_times_phi_dBar += Bq.gammabarUU[i][j] * Bq.phi_dBarD[ i] * Bq.phi_dBarD[j] phi_dBarDD_contraction += Bq.gammabarUU[i][j] * Bq.phi_dBarDD[i][j] # Add Term 3: H += Bq.exp_m4phi * (Rbartrace - 8 * (phi_dBar_times_phi_dBar + phi_dBarDD_contraction)) if add_T4UUmunu_source_terms: M_PI = par.Cparameters("#define", thismodule, "M_PI", "") # M_PI is pi as defined in C BTmunu.define_BSSN_T4UUmunu_rescaled_source_terms() rho = BTmunu.rho H += -16 * M_PI * rho # FIXME: ADD T4UUmunu SOURCE TERMS TO MOMENTUM CONSTRAINT! # Step 3: M^i, the momentum constraint ############################### ############################### # MOMENTUM CONSTRAINT ############################### ############################### # SEE Tutorial-BSSN_constraints.ipynb for full documentation. global MU MU = ixp.zerorank1() # Term 2: 6 A^{ij} \partial_j \phi: for i in range(DIM): for j in range(DIM): MU[i] += 6 * Bq.AbarUU[i][j] * Bq.phi_dD[j] # Term 3: -2/3 \bar{\gamma}^{ij} K_{,j} trK_dD = ixp.declarerank1( "trK_dD") # Not defined in BSSN_RHSs; only trK_dupD is defined there. for i in range(DIM): for j in range(DIM): MU[i] += -sp.Rational(2, 3) * Bq.gammabarUU[i][j] * trK_dD[j] # First define aDD_dD: aDD_dD = ixp.declarerank3("aDD_dD", "sym01") # Then evaluate the conformal covariant derivative \bar{D}_j \bar{A}_{lm} AbarDD_dBarD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): AbarDD_dBarD[i][j][k] = Bq.AbarDD_dD[i][j][k] for l in range(DIM): AbarDD_dBarD[i][j][ k] += -Bq.GammabarUDD[l][k][i] * Bq.AbarDD[l][j] AbarDD_dBarD[i][j][ k] += -Bq.GammabarUDD[l][k][j] * Bq.AbarDD[i][l] # Term 1: Contract twice with the metric to make \bar{D}_{j} \bar{A}^{ij} for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): MU[i] += Bq.gammabarUU[i][k] * Bq.gammabarUU[j][ l] * AbarDD_dBarD[k][l][j] # Finally, we multiply by e^{-4 phi} and rescale the momentum constraint: for i in range(DIM): MU[i] *= Bq.exp_m4phi / rfm.ReU[i]
def ScalarField_RHSs(): # Step B.4: 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 B.5: Import all basic (unrescaled) BSSN scalars & tensors Bq.BSSN_basic_tensors() trK = Bq.trK alpha = Bq.alpha betaU = Bq.betaU Bq.gammabar__inverse_and_derivs() gammabarUU = Bq.gammabarUU global sf_rhs, sfM_rhs # Step B.5.a: Declare grid functions for varphi and Pi sf, sfM = sfgfs.declare_scalar_field_gridfunctions_if_not_declared_already( ) # Step 2.a: Add Term 1 to sf_rhs: -alpha*Pi sf_rhs = -alpha * sfM # Step 2.b: Add Term 2 to sf_rhs: beta^{i}\partial_{i}\varphi sf_dupD = ixp.declarerank1("sf_dupD") for i in range(DIM): sf_rhs += betaU[i] * sf_dupD[i] # Step 3a: Add Term 1 to sfM_rhs: alpha * K * Pi sfM_rhs = alpha * trK * sfM # Step 3b: Add Term 2 to sfM_rhs: beta^{i}\partial_{i}Pi sfM_dupD = ixp.declarerank1("sfM_dupD") for i in range(DIM): sfM_rhs += betaU[i] * sfM_dupD[i] # Step 3c: Adding Term 3 to sfM_rhs # Step 3c.i: Term 3a: gammabar^{ij}\partial_{i}\alpha\partial_{j}\varphi alpha_dD = ixp.declarerank1("alpha_dD") sf_dD = ixp.declarerank1("sf_dD") sfMrhsTerm3 = sp.sympify(0) for i in range(DIM): for j in range(DIM): sfMrhsTerm3 += -gammabarUU[i][j] * alpha_dD[i] * sf_dD[j] # Step 3c.ii: Term 3b: \alpha*gammabar^{ij}\partial_{i}\partial_{j}\varphi sf_dDD = ixp.declarerank2("sf_dDD", "sym01") for i in range(DIM): for j in range(DIM): sfMrhsTerm3 += -alpha * gammabarUU[i][j] * sf_dDD[i][j] # Step 3c.iii: Term 3c: 2*alpha*gammabar^{ij}\partial_{j}\varphi\partial_{i}\phi Bq.phi_and_derivs( ) # sets exp^{-4phi} = exp_m4phi and \partial_{i}phi = phi_dD[i] for i in range(DIM): for j in range(DIM): sfMrhsTerm3 += -2 * alpha * gammabarUU[i][j] * sf_dD[ j] * Bq.phi_dD[i] # Step 3c.iv: Multiplying Term 3 by e^{-4phi} and adding it to sfM_rhs sfMrhsTerm3 *= Bq.exp_m4phi sfM_rhs += sfMrhsTerm3 # Step 3d: Adding Term 4 to sfM_rhs # Step 3d.i: Term 4a: \alpha \bar\Lambda^{i}\partial_{i}\varphi LambdabarU = Bq.LambdabarU sfMrhsTerm4 = sp.sympify(0) for i in range(DIM): sfMrhsTerm4 += alpha * LambdabarU[i] * sf_dD[i] # Step 3d.ii: Evaluating \bar\gamma^{ij}\hat\Gamma^{k}_{ij} GammahatUDD = rfm.GammahatUDD gammabarGammahatContractionU = ixp.zerorank1() for k in range(DIM): for i in range(DIM): for j in range(DIM): gammabarGammahatContractionU[ k] += gammabarUU[i][j] * GammahatUDD[k][i][j] # Step 3d.iii: Term 4b: \alpha \bar\gamma^{ij}\hat\Gamma^{k}_{ij}\partial_{k}\varphi for i in range(DIM): sfMrhsTerm4 += alpha * gammabarGammahatContractionU[i] * sf_dD[i] # Step 3d.iii: Multplying Term 4 by e^{-4phi} and adding it to sfM_rhs sfMrhsTerm4 *= Bq.exp_m4phi sfM_rhs += sfMrhsTerm4 return
def output_Enforce_Detgammabar_Constraint_Ccode(): # Set spatial dimension (must be 3 for BSSN) DIM = 3 # Then we set the coordinate system for the numerical grid rfm.reference_metric( ) # Create ReU, ReDD needed for rescaling B-L initial data, generating BSSN RHSs, etc. # We will need the h_{ij} quantities defined within BSSN_RHSs # below when we enforce the gammahat=gammabar constraint # Step 1: All barred quantities are defined in terms of BSSN rescaled gridfunctions, # which we declare here in case they haven't yet been declared elsewhere. Bq.declare_BSSN_gridfunctions_if_not_declared_already() hDD = Bq.hDD Bq.BSSN_basic_tensors() gammabarDD = Bq.gammabarDD # First define the Kronecker delta: KroneckerDeltaDD = ixp.zerorank2() for i in range(DIM): KroneckerDeltaDD[i][i] = sp.sympify(1) # The detgammabar in BSSN_RHSs is set to detgammahat when BSSN_RHSs::detgbarOverdetghat_equals_one=True (default), # so we manually compute it here: dummygammabarUU, detgammabar = ixp.symm_matrix_inverter3x3(gammabarDD) # Next apply the constraint enforcement equation above. hprimeDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): hprimeDD[i][j] = \ (sp.Abs(rfm.detgammahat) / detgammabar) ** (sp.Rational(1, 3)) * (KroneckerDeltaDD[i][j] + hDD[i][j]) \ - KroneckerDeltaDD[i][j] enforce_detg_constraint_vars = [ \ lhrh(lhs=gri.gfaccess("in_gfs", "hDD00"), rhs=hprimeDD[0][0]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD01"), rhs=hprimeDD[0][1]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD02"), rhs=hprimeDD[0][2]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD11"), rhs=hprimeDD[1][1]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD12"), rhs=hprimeDD[1][2]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD22"), rhs=hprimeDD[2][2])] enforce_gammadet_string = fin.FD_outputC( "returnstring", enforce_detg_constraint_vars, params="outCverbose=False,preindent=0,includebraces=False") with open("BSSN/enforce_detgammabar_constraint.h", "w") as file: indent = " " file.write( "void enforce_detgammabar_constraint(const int Nxx_plus_2NGHOSTS[3],REAL *xx[3], REAL *in_gfs) {\n\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];\n" + enforce_gammadet_string)) file.write("}\n") print( "Output C implementation of det(gammabar) constraint to file BSSN/enforce_detgammabar_constraint.h" )
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_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!") 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 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.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(detgamma) * gammaUU[a][ d] * LeviCivitaSymbolDDD[d][b][c] * v1U[b] * v2U[c] # 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] elif i == 1: return v2U[a] elif i == 2: return v3U[a] else: print("ERROR: unknown vector!") exit(1) def update_omega(omegaDD, v1U, v2U, v3U, gammaDD): for i in range(DIM): for j in range(DIM): omegaDD[i][j] = sp.sympify(0) for i in range(DIM): for j in range(DIM): 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. e1U = ixp.zerorank1() e2U = ixp.zerorank1() e3U = ixp.zerorank1() update_omega(omegaDD, v1U, v2U, v3U, gammaDD) for a in range(DIM): e1U[a] = v1U[a] / sp.sqrt(omegaDD[0][0]) update_omega(omegaDD, e1U, v2U, v3U, gammaDD) 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, gammaDD) 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: 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) 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 BSSN_RHSs__generate_symbolic_expressions(): ###################################### # START: GENERATE SYMBOLIC EXPRESSIONS print("Generating symbolic expressions for BSSN RHSs...") start = time.time() # Enable rfm_precompute infrastructure, which results in # BSSN RHSs that are free of transcendental functions, # even in curvilinear coordinates, so long as # ConformalFactor is set to "W" (default). par.set_parval_from_str("reference_metric::enable_rfm_precompute","True") par.set_parval_from_str("reference_metric::rfm_precompute_Ccode_outdir",os.path.join(outdir,"rfm_files/")) # Evaluate BSSN + BSSN gauge RHSs with rfm_precompute enabled: import BSSN.BSSN_quantities as Bq par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","True") rhs.BSSN_RHSs() if T4UU != None: import BSSN.BSSN_stress_energy_source_terms as Bsest Bsest.BSSN_source_terms_for_BSSN_RHSs(T4UU) rhs.trK_rhs += Bsest.sourceterm_trK_rhs for i in range(3): # Needed for Gamma-driving shift RHSs: rhs.Lambdabar_rhsU[i] += Bsest.sourceterm_Lambdabar_rhsU[i] # Needed for BSSN RHSs: rhs.lambda_rhsU[i] += Bsest.sourceterm_lambda_rhsU[i] for j in range(3): rhs.a_rhsDD[i][j] += Bsest.sourceterm_a_rhsDD[i][j] gaugerhs.BSSN_gauge_RHSs() # Add Kreiss-Oliger dissipation to the BSSN RHSs: thismodule = "KO_Dissipation" diss_strength = par.Cparameters("REAL", thismodule, "diss_strength", default_KO_strength) alpha_dKOD = ixp.declarerank1("alpha_dKOD") cf_dKOD = ixp.declarerank1("cf_dKOD") trK_dKOD = ixp.declarerank1("trK_dKOD") betU_dKOD = ixp.declarerank2("betU_dKOD","nosym") vetU_dKOD = ixp.declarerank2("vetU_dKOD","nosym") lambdaU_dKOD = ixp.declarerank2("lambdaU_dKOD","nosym") aDD_dKOD = ixp.declarerank3("aDD_dKOD","sym01") hDD_dKOD = ixp.declarerank3("hDD_dKOD","sym01") for k in range(3): gaugerhs.alpha_rhs += diss_strength*alpha_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.cf_rhs += diss_strength* cf_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.trK_rhs += diss_strength* trK_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] for i in range(3): if "2ndOrder" in ShiftCondition: gaugerhs.bet_rhsU[i] += diss_strength* betU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] gaugerhs.vet_rhsU[i] += diss_strength* vetU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.lambda_rhsU[i] += diss_strength*lambdaU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] for j in range(3): rhs.a_rhsDD[i][j] += diss_strength*aDD_dKOD[i][j][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.h_rhsDD[i][j] += diss_strength*hDD_dKOD[i][j][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] # We use betaU as our upwinding control vector: Bq.BSSN_basic_tensors() betaU = Bq.betaU # Now that we are finished with all the rfm hatted # quantities in generic precomputed functional # form, let's restore them to their closed- # form expressions. par.set_parval_from_str("reference_metric::enable_rfm_precompute","False") # Reset to False to disable rfm_precompute. rfm.ref_metric__hatted_quantities() par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","False") end = time.time() print("(BENCH) Finished BSSN RHS symbolic expressions in "+str(end-start)+" seconds.") # END: GENERATE SYMBOLIC EXPRESSIONS ###################################### BSSN_RHSs_SymbExpressions = [lhrh(lhs=gri.gfaccess("rhs_gfs","aDD00"), rhs=rhs.a_rhsDD[0][0]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD01"), rhs=rhs.a_rhsDD[0][1]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD02"), rhs=rhs.a_rhsDD[0][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD11"), rhs=rhs.a_rhsDD[1][1]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD12"), rhs=rhs.a_rhsDD[1][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD22"), rhs=rhs.a_rhsDD[2][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","alpha"), rhs=gaugerhs.alpha_rhs), lhrh(lhs=gri.gfaccess("rhs_gfs","betU0"), rhs=gaugerhs.bet_rhsU[0]), lhrh(lhs=gri.gfaccess("rhs_gfs","betU1"), rhs=gaugerhs.bet_rhsU[1]), lhrh(lhs=gri.gfaccess("rhs_gfs","betU2"), rhs=gaugerhs.bet_rhsU[2]), lhrh(lhs=gri.gfaccess("rhs_gfs","cf"), rhs=rhs.cf_rhs), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD00"), rhs=rhs.h_rhsDD[0][0]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD01") ,rhs=rhs.h_rhsDD[0][1]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD02"), rhs=rhs.h_rhsDD[0][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD11"), rhs=rhs.h_rhsDD[1][1]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD12"), rhs=rhs.h_rhsDD[1][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD22"), rhs=rhs.h_rhsDD[2][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU0"),rhs=rhs.lambda_rhsU[0]), lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU1"),rhs=rhs.lambda_rhsU[1]), lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU2"),rhs=rhs.lambda_rhsU[2]), lhrh(lhs=gri.gfaccess("rhs_gfs","trK"), rhs=rhs.trK_rhs), lhrh(lhs=gri.gfaccess("rhs_gfs","vetU0"), rhs=gaugerhs.vet_rhsU[0]), lhrh(lhs=gri.gfaccess("rhs_gfs","vetU1"), rhs=gaugerhs.vet_rhsU[1]), lhrh(lhs=gri.gfaccess("rhs_gfs","vetU2"), rhs=gaugerhs.vet_rhsU[2]) ] return [betaU,BSSN_RHSs_SymbExpressions]
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)
def test_example_BSSN(): import NRPy_param_funcs as par, reference_metric as rfm import BSSN.BSSN_RHSs as Brhs, BSSN.BSSN_quantities as Bq Parser.clear_namespace() parse(r""" % keydef basis [x, y, z] % ignore "\\%", "\qquad" % vardef 'deltaDD', 'vetU', 'lambdaU' % vardef -numeric -sym01 'hDD', 'aDD', 'RbarDD' % assign -numeric 'cf', 'alpha', 'trK', 'vetU', 'lambdaU' % parse \hat{\gamma}_{ij} = \delta_{ij} % assign -symbolic <H> -metric 'gammahatDD' % parse \bar{\gamma}_{ij} = h_{ij} + \hat{\gamma}_{ij} % assign -numeric -metric 'gammabarDD' \begin{align} %% replace LaTeX variable(s) with BSSN variable(s) % srepl "\bar{A}" -> "\text{a}", "\beta" -> "\text{vet}", "K" -> "\text{trK}", "\bar{\Lambda}" -> "\text{lambda}" % srepl "e^{{-4\phi}}" -> "\text{cf}^{{2}}" % srepl "\partial_t \phi = <1..> \\" -> "\text{cf_rhs} = -2 \text{cf} (<1..>) \\" % srepl -global "\partial_<1> \phi" -> "\partial_<1> \text{cf} \frac{-1}{2 \text{cf}}" % srepl -global "\partial_<1> \text{phi}" -> "\partial_<1> \text{cf} \frac{-1}{2 \text{cf}}" % srepl -global "\partial_<1> (\text{phi})" -> "\partial_<1> \text{cf} \frac{-1}{2 \text{cf}}" %% upwind pattern inside Lie derivative expansion % srepl -global "\text{vet}^<1> \partial_<1>" -> "\text{vet}^<1> \vphantom{upwind} \partial_<1>" %% enforce metric constraint gammabardet == gammahatdet % srepl -global "\bar{D}^i \bar{D}_k \text{vet}^k" -> "(\bar{D}^i \partial_k \text{vet}^k)" % srepl -global "\bar{D}_k \text{vet}^k" -> "(\partial_k \text{vet}^k + \frac{\partial_k \text{gammahatdet} \text{vet}^k}{2 \text{gammahatdet}})" % parse \bar{A}^i_j = \bar{\gamma}^{ik} \bar{A}_{kj} % srepl "\partial_t \bar{\gamma}" -> "\text{h_rhs}" \partial_t \bar{\gamma}_{ij} &= \mathcal{L}_\beta \bar{\gamma}_{ij} + \frac{2}{3} \bar{\gamma}_{ij} \left(\alpha \bar{A}^k{}_k - \bar{D}_k \beta^k\right) - 2 \alpha \bar{A}_{ij} \\ \partial_t \phi &= \mathcal{L}_\beta \phi + \frac{1}{6} \left(\bar{D}_k \beta^k - \alpha K \right) \\ % parse \bar{A}^{ij} = \bar{\gamma}^{ik} \bar{\gamma}^{jl} \bar{A}_{kl} % srepl "\partial_t \text{trK}" -> "\text{trK_rhs}" \partial_t K &= \mathcal{L}_\beta K + \frac{1}{3} \alpha K^{{2}} + \alpha \bar{A}_{ij} \bar{A}^{ij} - e^{{-4\phi}} \left(\bar{D}_i \bar{D}^i \alpha + 2 \bar{D}^i \alpha \bar{D}_i \phi\right) \\ % parse \Pi^k_{ij} = \bar{\Gamma}^k_{ij} - \hat{\Gamma}^k_{ij} % parse \Pi_{ijk} = \bar{\gamma}_{il} \Pi^l_{jk} % parse \Pi^k = \bar{\gamma}^{ij} \Pi^k_{ij} % srepl "\partial_t \text{lambda}" -> "\text{Lambdabar_rhs}" \partial_t \bar{\Lambda}^i &= \mathcal{L}_\beta \bar{\Lambda}^i + \bar{\gamma}^{jk} \hat{D}_j \hat{D}_k \beta^i + \frac{2}{3} \Pi^i \bar{D}_k \beta^k + \frac{1}{3} \bar{D}^i \bar{D}_k \beta^k \\% &\qquad- 2 \bar{A}^{ij} (\partial_j \alpha - 6 \alpha \partial_j \phi) + 2 \alpha \bar{A}^{jk} \Pi^i_{jk} - \frac{4}{3} \alpha \bar{\gamma}^{ij} \partial_j K \\ X_{ij} &= -2 \alpha \bar{D}_i \bar{D}_j \phi + 4 \alpha \bar{D}_i \phi \bar{D}_j \phi + 2 \bar{D}_i \alpha \bar{D}_j \phi + 2 \bar{D}_j \alpha \bar{D}_i \phi - \bar{D}_i \bar{D}_j \alpha + \alpha \bar{R}_{ij} \\ \hat{X}_{ij} &= X_{ij} - \frac{1}{3} \bar{\gamma}_{ij} \bar{\gamma}^{kl} X_{kl} \\ % srepl "\partial_t \text{a}" -> "\text{a_rhs}" \partial_t \bar{A}_{ij} &= \mathcal{L}_\beta \bar{A}_{ij} - \frac{2}{3} \bar{A}_{ij} \bar{D}_k \beta^k - 2 \alpha \bar{A}_{ik} \bar{A}^k_j + \alpha \bar{A}_{ij} K + e^{{-4\phi}} \hat{X}_{ij} \\ \bar{R}_{ij} &= -\frac{1}{2} \bar{\gamma}^{kl} \hat{D}_k \hat{D}_l \bar{\gamma}_{ij} + \frac{1}{2} (\bar{\gamma}_{ki} \hat{D}_j \bar{\Lambda}^k + \bar{\gamma}_{kj} \hat{D}_i \bar{\Lambda}^k) + \frac{1}{2} \Pi^k (\Pi_{ijk} + \Pi_{jik}) \\% &\qquad+ \bar{\gamma}^{kl} (\Pi^m_{ki} \Pi_{jml} + \Pi^m_{kj} \Pi_{iml} + \Pi^m_{ik} \Pi_{mjl}) \end{align} """) par.set_parval_from_str('reference_metric::CoordSystem', 'Cartesian') par.set_parval_from_str('BSSN.BSSN_quantities::LeaveRicciSymbolic', 'True') rfm.reference_metric() Brhs.BSSN_RHSs() par.set_parval_from_str('BSSN.BSSN_quantities::LeaveRicciSymbolic', 'False') Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU() assert_equal( { 'h_rhsDD': h_rhsDD, 'cf_rhs': cf_rhs, 'trK_rhs': trK_rhs, 'Lambdabar_rhsU': Lambdabar_rhsU, 'a_rhsDD': a_rhsDD, 'RbarDD': RbarDD }, { 'h_rhsDD': Brhs.h_rhsDD, 'cf_rhs': Brhs.cf_rhs, 'trK_rhs': Brhs.trK_rhs, 'Lambdabar_rhsU': Brhs.Lambdabar_rhsU, 'a_rhsDD': Brhs.a_rhsDD, 'RbarDD': Bq.RbarDD })
def BSSN_gauge_RHSs(): # Step 1.d: 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.e: 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.f: Define needed BSSN quantities: # Declare scalars & tensors (in terms of rescaled BSSN quantities) Bq.BSSN_basic_tensors() Bq.betaU_derivs() # Declare BSSN_RHSs (excluding the time evolution equations for the gauge conditions), # if they haven't already been declared. if Brhs.have_already_called_BSSN_RHSs_function == False: print( "BSSN_gauge_RHSs() Error: You must call BSSN_RHSs() before calling BSSN_gauge_RHSs()." ) sys.exit(1) # Step 2: Lapse conditions LapseEvolOption = par.parval_from_str(thismodule + "::LapseEvolutionOption") # Step 2.a: The 1+log lapse condition: # \partial_t \alpha = \beta^i \alpha_{,i} - 2*\alpha*K # First import expressions from BSSN_quantities cf = Bq.cf trK = Bq.trK alpha = Bq.alpha betaU = Bq.betaU # Implement the 1+log lapse condition global alpha_rhs alpha_rhs = sp.sympify(0) if LapseEvolOption == "OnePlusLog": alpha_rhs = -2 * alpha * trK alpha_dupD = ixp.declarerank1("alpha_dupD") for i in range(DIM): alpha_rhs += betaU[i] * alpha_dupD[i] # Step 2.b: Implement the harmonic slicing lapse condition elif LapseEvolOption == "HarmonicSlicing": if par.parval_from_str( "BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "W": alpha_rhs = -3 * cf**(-4) * Brhs.cf_rhs elif par.parval_from_str( "BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "phi": alpha_rhs = 6 * sp.exp(6 * cf) * Brhs.cf_rhs else: print( "Error LapseEvolutionOption==HarmonicSlicing unsupported for EvolvedConformalFactor_cf!=(W or phi)" ) sys.exit(1) # Step 2.c: Frozen lapse # \partial_t \alpha = 0 elif LapseEvolOption == "Frozen": alpha_rhs = sp.sympify(0) else: print("Error: " + thismodule + "::LapseEvolutionOption == " + LapseEvolOption + " not supported!") sys.exit(1) # Step 3.a: Set \partial_t \beta^i # First check that ShiftEvolutionOption parameter choice is supported. ShiftEvolOption = par.parval_from_str(thismodule + "::ShiftEvolutionOption") if ShiftEvolOption != "Frozen" and \ ShiftEvolOption != "GammaDriving2ndOrder_NoCovariant" and \ ShiftEvolOption != "GammaDriving2ndOrder_Covariant" and \ ShiftEvolOption != "GammaDriving2ndOrder_Covariant__Hatted" and \ ShiftEvolOption != "GammaDriving1stOrder_Covariant" and \ ShiftEvolOption != "GammaDriving1stOrder_Covariant__Hatted": print("Error: ShiftEvolutionOption == " + ShiftEvolOption + " unsupported!") sys.exit(1) # Next import expressions from BSSN_quantities BU = Bq.BU betU = Bq.betU betaU_dupD = Bq.betaU_dupD # Define needed quantities beta_rhsU = ixp.zerorank1() B_rhsU = ixp.zerorank1() # In the case of Frozen shift condition, we # explicitly set the betaU and BU RHS's to zero # instead of relying on the ixp.zerorank1()'s above, # for safety. if ShiftEvolOption == "Frozen": for i in range(DIM): beta_rhsU[i] = sp.sympify(0) BU[i] = sp.sympify(0) if ShiftEvolOption == "GammaDriving2ndOrder_NoCovariant": # Step 3.a.i: Compute right-hand side of beta^i # * \partial_t \beta^i = \beta^j \beta^i_{,j} + B^i for i in range(DIM): beta_rhsU[i] += BU[i] for j in range(DIM): beta_rhsU[i] += betaU[j] * betaU_dupD[i][j] # Compute right-hand side of B^i: eta = par.Cparameters("REAL", thismodule, ["eta"], 2.0) # Step 3.a.ii: Compute right-hand side of B^i # * \partial_t B^i = \beta^j B^i_{,j} + 3/4 * \partial_0 \Lambda^i - eta B^i # Step 3.a.iii: Define BU_dupD, in terms of derivative of rescaled variable \bet^i BU_dupD = ixp.zerorank2() betU_dupD = ixp.declarerank2("betU_dupD", "nosym") for i in range(DIM): for j in range(DIM): BU_dupD[i][j] = betU_dupD[i][j] * rfm.ReU[i] + betU[ i] * rfm.ReUdD[i][j] # Step 3.a.iv: Compute \partial_0 \bar{\Lambda}^i = (\partial_t - \beta^i \partial_i) \bar{\Lambda}^j Lambdabar_partial0 = ixp.zerorank1() for i in range(DIM): Lambdabar_partial0[i] = Brhs.Lambdabar_rhsU[i] for i in range(DIM): for j in range(DIM): Lambdabar_partial0[j] += -betaU[i] * Brhs.LambdabarU_dupD[j][i] # Step 3.a.v: Evaluate RHS of B^i: for i in range(DIM): B_rhsU[i] += sp.Rational(3, 4) * Lambdabar_partial0[i] - eta * BU[i] for j in range(DIM): B_rhsU[i] += betaU[j] * BU_dupD[i][j] # Step 3.b: The right-hand side of the \partial_t \beta^i equation if "GammaDriving2ndOrder_Covariant" in ShiftEvolOption: # Step 3.b Option 2: \partial_t \beta^i = \left[\beta^j \bar{D}_j \beta^i\right] + B^{i} # First we need GammabarUDD, defined in Bq.gammabar__inverse_and_derivs() Bq.gammabar__inverse_and_derivs() ConnectionUDD = Bq.GammabarUDD # If instead we wish to use the Hatted covariant derivative, we replace # ConnectionUDD with GammahatUDD: if ShiftEvolOption == "GammaDriving2ndOrder_Covariant__Hatted": ConnectionUDD = rfm.GammahatUDD # Then compute right-hand side: # Term 1: \beta^j \beta^i_{,j} for i in range(DIM): for j in range(DIM): beta_rhsU[i] += betaU[j] * betaU_dupD[i][j] # Term 2: \beta^j \bar{\Gamma}^i_{mj} \beta^m for i in range(DIM): for j in range(DIM): for m in range(DIM): beta_rhsU[ i] += betaU[j] * ConnectionUDD[i][m][j] * betaU[m] # Term 3: B^i for i in range(DIM): beta_rhsU[i] += BU[i] if "GammaDriving2ndOrder_Covariant" in ShiftEvolOption: ConnectionUDD = Bq.GammabarUDD # If instead we wish to use the Hatted covariant derivative, we replace # ConnectionUDD with GammahatUDD: if ShiftEvolOption == "GammaDriving2ndOrder_Covariant__Hatted": ConnectionUDD = rfm.GammahatUDD # Step 3.c: Covariant option: # \partial_t B^i = \beta^j \bar{D}_j B^i # + \frac{3}{4} ( \partial_t \bar{\Lambda}^{i} - \beta^j \bar{D}_j \bar{\Lambda}^{i} ) # - \eta B^{i} # = \beta^j B^i_{,j} + \beta^j \bar{\Gamma}^i_{mj} B^m # + \frac{3}{4}[ \partial_t \bar{\Lambda}^{i} # - \beta^j (\bar{\Lambda}^i_{,j} + \bar{\Gamma}^i_{mj} \bar{\Lambda}^m)] # - \eta B^{i} # Term 1, part a: First compute B^i_{,j} using upwinded derivative BU_dupD = ixp.zerorank2() betU_dupD = ixp.declarerank2("betU_dupD", "nosym") for i in range(DIM): for j in range(DIM): BU_dupD[i][j] = betU_dupD[i][j] * rfm.ReU[i] + betU[ i] * rfm.ReUdD[i][j] # Term 1: \beta^j B^i_{,j} for i in range(DIM): for j in range(DIM): B_rhsU[i] += betaU[j] * BU_dupD[i][j] # Term 2: \beta^j \bar{\Gamma}^i_{mj} B^m for i in range(DIM): for j in range(DIM): for m in range(DIM): B_rhsU[i] += betaU[j] * ConnectionUDD[i][m][j] * BU[m] # Term 3: \frac{3}{4}\partial_t \bar{\Lambda}^{i} for i in range(DIM): B_rhsU[i] += sp.Rational(3, 4) * Brhs.Lambdabar_rhsU[i] # Term 4: -\frac{3}{4}\beta^j \bar{\Lambda}^i_{,j} for i in range(DIM): for j in range(DIM): B_rhsU[i] += -sp.Rational( 3, 4) * betaU[j] * Brhs.LambdabarU_dupD[i][j] # Term 5: -\frac{3}{4}\beta^j \bar{\Gamma}^i_{mj} \bar{\Lambda}^m for i in range(DIM): for j in range(DIM): for m in range(DIM): B_rhsU[i] += -sp.Rational(3, 4) * betaU[j] * ConnectionUDD[ i][m][j] * Bq.LambdabarU[m] # Term 6: - \eta B^i # eta is a free parameter; we declare it here: eta = par.Cparameters("REAL", thismodule, ["eta"], 2.0) for i in range(DIM): B_rhsU[i] += -eta * BU[i] if "GammaDriving1stOrder_Covariant" in ShiftEvolOption: # Step 3.c: \partial_t \beta^i = \left[\beta^j \bar{D}_j \beta^i\right] + 3/4 Lambdabar^i - eta*beta^i # First set \partial_t B^i = 0: B_rhsU = ixp.zerorank1() # \partial_t B^i = 0 # Second, set \partial_t beta^i RHS: # Compute covariant advection term: # We need GammabarUDD, defined in Bq.gammabar__inverse_and_derivs() Bq.gammabar__inverse_and_derivs() ConnectionUDD = Bq.GammabarUDD # If instead we wish to use the Hatted covariant derivative, we replace # ConnectionUDD with GammahatUDD: if ShiftEvolOption == "GammaDriving1stOrder_Covariant__Hatted": ConnectionUDD = rfm.GammahatUDD # Term 1: \beta^j \beta^i_{,j} for i in range(DIM): for j in range(DIM): beta_rhsU[i] += betaU[j] * betaU_dupD[i][j] # Term 2: \beta^j \bar{\Gamma}^i_{mj} \beta^m for i in range(DIM): for j in range(DIM): for m in range(DIM): beta_rhsU[ i] += betaU[j] * ConnectionUDD[i][m][j] * betaU[m] # Term 3: 3/4 Lambdabar^i - eta*beta^i eta = par.Cparameters("REAL", thismodule, ["eta"], 2.0) for i in range(DIM): beta_rhsU[i] += sp.Rational(3, 4) * Bq.LambdabarU[i] - eta * betaU[i] # Step 4: Rescale the BSSN gauge RHS quantities so that the evolved # variables may remain smooth across coord singularities global vet_rhsU, bet_rhsU vet_rhsU = ixp.zerorank1() bet_rhsU = ixp.zerorank1() for i in range(DIM): vet_rhsU[i] = beta_rhsU[i] / rfm.ReU[i] bet_rhsU[i] = B_rhsU[i] / rfm.ReU[i]
def BSSN_RHSs__generate_symbolic_expressions( LapseCondition="OnePlusLog", ShiftCondition="GammaDriving2ndOrder_Covariant", enable_KreissOliger_dissipation=True, enable_stress_energy_source_terms=False, leave_Ricci_symbolic=True): ###################################### # START: GENERATE SYMBOLIC EXPRESSIONS starttime = print_msg_with_timing("BSSN_RHSs", msg="Symbolic", startstop="start") # Returns None if enable_stress_energy_source_terms==False; otherwise returns symb expressions for T4UU T4UU = register_stress_energy_source_terms_return_T4UU( enable_stress_energy_source_terms) # Evaluate BSSN RHSs: import BSSN.BSSN_quantities as Bq par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic", str(leave_Ricci_symbolic)) rhs.BSSN_RHSs() if enable_stress_energy_source_terms: import BSSN.BSSN_stress_energy_source_terms as Bsest Bsest.BSSN_source_terms_for_BSSN_RHSs(T4UU) rhs.trK_rhs += Bsest.sourceterm_trK_rhs for i in range(3): # Needed for Gamma-driving shift RHSs: rhs.Lambdabar_rhsU[i] += Bsest.sourceterm_Lambdabar_rhsU[i] # Needed for BSSN RHSs: rhs.lambda_rhsU[i] += Bsest.sourceterm_lambda_rhsU[i] for j in range(3): rhs.a_rhsDD[i][j] += Bsest.sourceterm_a_rhsDD[i][j] par.set_parval_from_str("BSSN.BSSN_gauge_RHSs::LapseEvolutionOption", LapseCondition) par.set_parval_from_str("BSSN.BSSN_gauge_RHSs::ShiftEvolutionOption", ShiftCondition) gaugerhs.BSSN_gauge_RHSs() # Can depend on above RHSs # Restore BSSN.BSSN_quantities::LeaveRicciSymbolic to False par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic", "False") # Add Kreiss-Oliger dissipation to the BSSN RHSs: if enable_KreissOliger_dissipation: thismodule = "KO_Dissipation" diss_strength = par.Cparameters( "REAL", thismodule, "diss_strength", 0.1 ) # *Bq.cf # *Bq.cf*Bq.cf*Bq.cf # cf**1 is found better than cf**4 over the long term. alpha_dKOD = ixp.declarerank1("alpha_dKOD") cf_dKOD = ixp.declarerank1("cf_dKOD") trK_dKOD = ixp.declarerank1("trK_dKOD") betU_dKOD = ixp.declarerank2("betU_dKOD", "nosym") vetU_dKOD = ixp.declarerank2("vetU_dKOD", "nosym") lambdaU_dKOD = ixp.declarerank2("lambdaU_dKOD", "nosym") aDD_dKOD = ixp.declarerank3("aDD_dKOD", "sym01") hDD_dKOD = ixp.declarerank3("hDD_dKOD", "sym01") for k in range(3): gaugerhs.alpha_rhs += diss_strength * alpha_dKOD[k] * rfm.ReU[ k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.cf_rhs += diss_strength * cf_dKOD[k] * rfm.ReU[ k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.trK_rhs += diss_strength * trK_dKOD[k] * rfm.ReU[ k] # ReU[k] = 1/scalefactor_orthog_funcform[k] for i in range(3): if "2ndOrder" in ShiftCondition: gaugerhs.bet_rhsU[ i] += diss_strength * betU_dKOD[i][k] * rfm.ReU[ k] # ReU[k] = 1/scalefactor_orthog_funcform[k] gaugerhs.vet_rhsU[ i] += diss_strength * vetU_dKOD[i][k] * rfm.ReU[ k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.lambda_rhsU[ i] += diss_strength * lambdaU_dKOD[i][k] * rfm.ReU[ k] # ReU[k] = 1/scalefactor_orthog_funcform[k] for j in range(3): rhs.a_rhsDD[i][ j] += diss_strength * aDD_dKOD[i][j][k] * rfm.ReU[ k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.h_rhsDD[i][ j] += diss_strength * hDD_dKOD[i][j][k] * rfm.ReU[ k] # ReU[k] = 1/scalefactor_orthog_funcform[k] # We use betaU as our upwinding control vector: Bq.BSSN_basic_tensors() betaU = Bq.betaU # END: GENERATE SYMBOLIC EXPRESSIONS ###################################### lhs_names = ["alpha", "cf", "trK"] rhs_exprs = [gaugerhs.alpha_rhs, rhs.cf_rhs, rhs.trK_rhs] for i in range(3): lhs_names.append("betU" + str(i)) rhs_exprs.append(gaugerhs.bet_rhsU[i]) lhs_names.append("lambdaU" + str(i)) rhs_exprs.append(rhs.lambda_rhsU[i]) lhs_names.append("vetU" + str(i)) rhs_exprs.append(gaugerhs.vet_rhsU[i]) for j in range(i, 3): lhs_names.append("aDD" + str(i) + str(j)) rhs_exprs.append(rhs.a_rhsDD[i][j]) lhs_names.append("hDD" + str(i) + str(j)) rhs_exprs.append(rhs.h_rhsDD[i][j]) # Sort the lhss list alphabetically, and rhss to match. # This ensures the RHSs are evaluated in the same order # they're allocated in memory: lhs_names, rhs_exprs = [ list(x) for x in zip( *sorted(zip(lhs_names, rhs_exprs), key=lambda pair: pair[0])) ] # Declare the list of lhrh's BSSN_RHSs_SymbExpressions = [] for var in range(len(lhs_names)): BSSN_RHSs_SymbExpressions.append( lhrh(lhs=gri.gfaccess("rhs_gfs", lhs_names[var]), rhs=rhs_exprs[var])) print_msg_with_timing("BSSN_RHSs", msg="Symbolic", startstop="stop", starttime=starttime) return [betaU, BSSN_RHSs_SymbExpressions]
def test_example_BSSN(): parse_latex(r""" \begin{align} % keydef basis [x, y, z] % ignore "\\%", "\qquad" % vardef -kron 'deltaDD' % parse \hat{\gamma}_{ij} = \delta_{ij} % assign -diff_type=symbolic -metric 'gammahatDD' % vardef -diff_type=dD -symmetry=sym01 'hDD' % parse \bar{\gamma}_{ij} = h_{ij} + \hat{\gamma}_{ij} % assign -diff_type=dD -metric 'gammabarDD' % srepl "\beta" -> "\text{vet}" % vardef -diff_type=dD 'vetU' %% upwind pattern inside Lie derivative expansion % srepl -persist "\text{vet}^{<1>} \partial_{<1>}" -> "\text{vet}^{<1>} \vphantom{dupD} \partial_{<1>}" %% substitute tensor identity (see appropriate BSSN notebook) % srepl "\bar{D}_k \text{vet}^k" -> "(\partial_k \text{vet}^k + \frac{\partial_k \text{gammahatdet} \text{vet}^k}{2 \text{gammahatdet}})" % srepl "\bar{A}" -> "\text{a}" % vardef -diff_type=dD -symmetry=sym01 'aDD' % assign -metric='gammabar' 'aDD' % srepl "\partial_t \bar{\gamma}" -> "\text{h_rhs}" \partial_t \bar{\gamma}_{ij} &= \mathcal{L}_\beta \bar{\gamma}_{ij} + \frac{2}{3} \bar{\gamma}_{ij} \left(\alpha \bar{A}^k{}_k - \bar{D}_k \beta^k\right) - 2 \alpha \bar{A}_{ij} \\ % srepl "K" -> "\text{trK}" % vardef -diff_type=dD 'cf', 'trK' %% replace 'phi' with conformal factor cf = W = e^{-2\phi} % srepl "e^{-4\phi}" -> "\text{cf}^2" % srepl "\partial_t \phi = <1..> \\" -> "\text{cf_rhs} = -2 \text{cf} (<1..>) \\" % srepl -persist "\partial_{<1>} \phi" -> "\partial_{<1>} \text{cf} \frac{-1}{2 \text{cf}}" % srepl "\partial_<1> \phi" -> "\partial_<1> \text{cf} \frac{-1}{2 \text{cf}}" \partial_t \phi &= \mathcal{L}_\beta \phi + \frac{1}{6} \left(\bar{D}_k \beta^k - \alpha K \right) \\ % vardef -diff_type=dD 'alpha' % srepl "\partial_t \text{trK}" -> "\text{trK_rhs}" \partial_t K &= \mathcal{L}_\beta K + \frac{1}{3} \alpha K^2 + \alpha \bar{A}_{ij} \bar{A}^{ij} - e^{-4\phi} \left(\bar{D}_i \bar{D}^i \alpha + 2 \bar{D}^i \alpha \bar{D}_i \phi\right) \\ % srepl "\bar{\Lambda}" -> "\text{lambda}" % vardef -diff_type=dD 'lambdaU' % parse \Delta^k_{ij} = \bar{\Gamma}^k_{ij} - \hat{\Gamma}^k_{ij} % assign -metric='gammabar' 'DeltaUDD' % parse \Delta^k = \bar{\gamma}^{ij} \Delta^k_{ij} % srepl "\partial_t \text{lambda}" -> "\text{Lambdabar_rhs}" \partial_t \bar{\Lambda}^i &= \mathcal{L}_\beta \bar{\Lambda}^i + \bar{\gamma}^{jk} \hat{D}_j \hat{D}_k \beta^i + \frac{2}{3} \Delta^i \bar{D}_k \beta^k + \frac{1}{3} \bar{D}^i \bar{D}_k \beta^k \\% &\qquad- 2 \bar{A}^{ij} \left(\partial_j \alpha - 6 \alpha \partial_j \phi\right) + 2 \alpha \bar{A}^{jk} \Delta^i_{jk} - \frac{4}{3} \alpha \bar{\gamma}^{ij} \partial_j K \\ % vardef -diff_type=dD -symmetry=sym01 'RbarDD' X_{ij} &= -2 \alpha \bar{D}_i \bar{D}_j \phi + 4 \alpha \bar{D}_i \phi \bar{D}_j \phi + 2 \bar{D}_i \alpha \bar{D}_j \phi + 2 \bar{D}_j \alpha \bar{D}_i \phi - \bar{D}_i \bar{D}_j \alpha + \alpha \bar{R}_{ij} \\ \hat{X}_{ij} &= X_{ij} - \frac{1}{3} \bar{\gamma}_{ij} \bar{\gamma}^{kl} X_{kl} \\ % srepl "\partial_t \text{a}" -> "\text{a_rhs}" \partial_t \bar{A}_{ij} &= \mathcal{L}_\beta \bar{A}_{ij} - \frac{2}{3} \bar{A}_{ij} \bar{D}_k \beta^k - 2 \alpha \bar{A}_{ik} \bar{A}^k_j + \alpha \bar{A}_{ij} K + e^{-4\phi} \hat{X}_{ij} \\ % srepl "\partial_t \alpha" -> "\text{alpha_rhs}" \partial_t \alpha &= \mathcal{L}_\beta \alpha - 2 \alpha K \\ % srepl "B" -> "\text{bet}" % vardef -diff_type=dD 'betU' % srepl "\partial_t \text{vet}" -> "\text{vet_rhs}" \partial_t \beta^i &= \left[\beta^j \vphantom{dupD} \bar{D}_j \beta^i\right] + B^i \\ % vardef -const 'eta' % srepl "\partial_t \text{bet}" -> "\text{bet_rhs}" \partial_t B^i &= \left[\beta^j \vphantom{dupD} \bar{D}_j B^i\right] + \frac{3}{4} \left(\partial_t \bar{\Lambda}^i - \left[\beta^j \vphantom{dupD} \bar{D}_j \bar{\Lambda}^i\right]\right) - \eta B^i \\ % parse \bar{R} = \bar{\gamma}^{ij} \bar{R}_{ij} % srepl "\bar{D}^2" -> "\bar{D}^i \bar{D}_i", "\mathcal{<1>}" -> "<1>" \mathcal{H} &= \frac{2}{3} K^2 - \bar{A}_{ij} \bar{A}^{ij} + e^{-4\phi} \left(\bar{R} - 8 \bar{D}^i \phi \bar{D}_i \phi - 8 \bar{D}^2 \phi\right) \\ \mathcal{M}^i &= e^{-4\phi} \left(\bar{D}_j \bar{A}^{ij} + 6 \bar{A}^{ij} \partial_j \phi - \frac{2}{3} \bar{\gamma}^{ij} \partial_j K\right) \\ \bar{R}_{ij} &= -\frac{1}{2} \bar{\gamma}^{kl} \hat{D}_k \hat{D}_l \bar{\gamma}_{ij} + \frac{1}{2} \left(\bar{\gamma}_{ki} \hat{D}_j \bar{\Lambda}^k + \bar{\gamma}_{kj} \hat{D}_i \bar{\Lambda}^k\right) + \frac{1}{2} \Delta^k \left(\Delta_{ijk} + \Delta_{jik}\right) \\% &\qquad+ \bar{\gamma}^{kl} \left(\Delta^m_{ki} \Delta_{jml} + \Delta^m_{kj} \Delta_{iml} + \Delta^m_{ik} \Delta_{mjl}\right) \end{align} """, ignore_warning=True) par.set_parval_from_str('reference_metric::CoordSystem', 'Cartesian') par.set_parval_from_str('BSSN.BSSN_quantities::LeaveRicciSymbolic', 'True') rfm.reference_metric() Brhs.BSSN_RHSs() gaugerhs.BSSN_gauge_RHSs() bssncon.BSSN_constraints() par.set_parval_from_str('BSSN.BSSN_quantities::LeaveRicciSymbolic', 'False') Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU() assert_equal( { 'h_rhsDD': h_rhsDD, 'cf_rhs': cf_rhs, 'trK_rhs': trK_rhs, 'Lambdabar_rhsU': Lambdabar_rhsU, 'a_rhsDD': a_rhsDD, 'alpha_rhs': alpha_rhs, 'vet_rhsU': vet_rhsU, 'bet_rhsU': bet_rhsU, 'H': H, 'MU': MU, 'RbarDD': RbarDD }, { 'h_rhsDD': Brhs.h_rhsDD, 'cf_rhs': Brhs.cf_rhs, 'trK_rhs': Brhs.trK_rhs, 'Lambdabar_rhsU': Brhs.Lambdabar_rhsU, 'a_rhsDD': Brhs.a_rhsDD, 'alpha_rhs': gaugerhs.alpha_rhs, 'vet_rhsU': gaugerhs.vet_rhsU, 'bet_rhsU': gaugerhs.bet_rhsU, 'H': bssncon.H, 'MU': bssncon.MU, 'RbarDD': Bq.RbarDD }, suppress_message=True)
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 BSSN_RHSs(): # Step 1.c: 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() global have_already_called_BSSN_RHSs_function # setting to global enables other modules to see updated value. have_already_called_BSSN_RHSs_function = True # Step 1.d: 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.e: Import all basic (unrescaled) BSSN scalars & tensors import BSSN.BSSN_quantities as Bq Bq.BSSN_basic_tensors() gammabarDD = Bq.gammabarDD AbarDD = Bq.AbarDD LambdabarU = Bq.LambdabarU trK = Bq.trK alpha = Bq.alpha betaU = Bq.betaU # Step 1.f: Import all neeeded rescaled BSSN tensors: aDD = Bq.aDD cf = Bq.cf lambdaU = Bq.lambdaU # Step 2.a.i: Import derivative expressions for betaU defined in the BSSN.BSSN_quantities module: Bq.betaU_derivs() betaU_dD = Bq.betaU_dD betaU_dDD = Bq.betaU_dDD # Step 2.a.ii: Import derivative expression for gammabarDD Bq.gammabar__inverse_and_derivs() gammabarDD_dupD = Bq.gammabarDD_dupD # Step 2.a.iii: First term of \partial_t \bar{\gamma}_{i j} right-hand side: # \beta^k \bar{\gamma}_{ij,k} + \beta^k_{,i} \bar{\gamma}_{kj} + \beta^k_{,j} \bar{\gamma}_{ik} gammabar_rhsDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): for k in range(DIM): gammabar_rhsDD[i][j] += betaU[k] * gammabarDD_dupD[i][j][k] + betaU_dD[k][i] * gammabarDD[k][j] \ + betaU_dD[k][j] * gammabarDD[i][k] # Step 2.b.i: First import \bar{A}_{ij} = AbarDD[i][j], and its contraction trAbar = \bar{A}^k_k # from BSSN.BSSN_quantities Bq.AbarUU_AbarUD_trAbar_AbarDD_dD() trAbar = Bq.trAbar # Step 2.b.ii: Import detgammabar quantities from BSSN.BSSN_quantities: Bq.detgammabar_and_derivs() detgammabar = Bq.detgammabar detgammabar_dD = Bq.detgammabar_dD # Step 2.b.ii: Compute the contraction \bar{D}_k \beta^k = \beta^k_{,k} + \frac{\beta^k \bar{\gamma}_{,k}}{2 \bar{\gamma}} Dbarbetacontraction = sp.sympify(0) for k in range(DIM): Dbarbetacontraction += betaU_dD[k][ k] + betaU[k] * detgammabar_dD[k] / (2 * detgammabar) # Step 2.b.iii: Second term of \partial_t \bar{\gamma}_{i j} right-hand side: # \frac{2}{3} \bar{\gamma}_{i j} \left (\alpha \bar{A}_{k}^{k} - \bar{D}_{k} \beta^{k}\right ) for i in range(DIM): for j in range(DIM): gammabar_rhsDD[i][j] += sp.Rational(2, 3) * gammabarDD[i][j] * ( alpha * trAbar - Dbarbetacontraction) # Step 2.c: Third term of \partial_t \bar{\gamma}_{i j} right-hand side: # -2 \alpha \bar{A}_{ij} for i in range(DIM): for j in range(DIM): gammabar_rhsDD[i][j] += -2 * alpha * AbarDD[i][j] # Step 3.a: First term of \partial_t \bar{A}_{i j}: # \beta^k \partial_k \bar{A}_{ij} + \partial_i \beta^k \bar{A}_{kj} + \partial_j \beta^k \bar{A}_{ik} # First define AbarDD_dupD: AbarDD_dupD = Bq.AbarDD_dupD # From Bq.AbarUU_AbarUD_trAbar_AbarDD_dD() Abar_rhsDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): for k in range(DIM): Abar_rhsDD[i][j] += betaU[k] * AbarDD_dupD[i][j][k] + betaU_dD[k][i] * AbarDD[k][j] \ + betaU_dD[k][j] * AbarDD[i][k] # Step 3.b: Second term of \partial_t \bar{A}_{i j}: # - (2/3) \bar{A}_{i j} \bar{D}_{k} \beta^{k} - 2 \alpha \bar{A}_{i k} {\bar{A}^{k}}_{j} + \alpha \bar{A}_{i j} K gammabarUU = Bq.gammabarUU # From Bq.gammabar__inverse_and_derivs() AbarUD = Bq.AbarUD # From Bq.AbarUU_AbarUD_trAbar() for i in range(DIM): for j in range(DIM): Abar_rhsDD[i][j] += -sp.Rational(2, 3) * AbarDD[i][ j] * Dbarbetacontraction + alpha * AbarDD[i][j] * trK for k in range(DIM): Abar_rhsDD[i][j] += -2 * alpha * AbarDD[i][k] * AbarUD[k][j] # Step 3.c.i: Define partial derivatives of \phi in terms of evolved quantity "cf": Bq.phi_and_derivs() phi_dD = Bq.phi_dD phi_dupD = Bq.phi_dupD phi_dDD = Bq.phi_dDD exp_m4phi = Bq.exp_m4phi phi_dBarD = Bq.phi_dBarD # phi_dBarD = Dbar_i phi = phi_dD (since phi is a scalar) phi_dBarDD = Bq.phi_dBarDD # phi_dBarDD = Dbar_i Dbar_j phi (covariant derivative) # Step 3.c.ii: Define RbarDD Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU() RbarDD = Bq.RbarDD # Step 3.c.iii: Define first and second derivatives of \alpha, as well as # \bar{D}_i \bar{D}_j \alpha, which is defined just like phi alpha_dD = ixp.declarerank1("alpha_dD") alpha_dDD = ixp.declarerank2("alpha_dDD", "sym01") alpha_dBarD = alpha_dD alpha_dBarDD = ixp.zerorank2() GammabarUDD = Bq.GammabarUDD # Defined in Bq.gammabar__inverse_and_derivs() for i in range(DIM): for j in range(DIM): alpha_dBarDD[i][j] = alpha_dDD[i][j] for k in range(DIM): alpha_dBarDD[i][j] += -GammabarUDD[k][i][j] * alpha_dD[k] # Step 3.c.iv: Define the terms in curly braces: curlybrackettermsDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): curlybrackettermsDD[i][j] = -2 * alpha * phi_dBarDD[i][j] + 4 * alpha * phi_dBarD[i] * phi_dBarD[j] \ + 2 * alpha_dBarD[i] * phi_dBarD[j] \ + 2 * alpha_dBarD[j] * phi_dBarD[i] \ - alpha_dBarDD[i][j] + alpha * RbarDD[i][j] # Step 3.c.v: Compute the trace: curlybracketterms_trace = sp.sympify(0) for i in range(DIM): for j in range(DIM): curlybracketterms_trace += gammabarUU[i][j] * curlybrackettermsDD[ i][j] # Step 3.c.vi: Third and final term of Abar_rhsDD[i][j]: for i in range(DIM): for j in range(DIM): Abar_rhsDD[i][j] += exp_m4phi * ( curlybrackettermsDD[i][j] - sp.Rational(1, 3) * gammabarDD[i][j] * curlybracketterms_trace) # Step 4: Right-hand side of conformal factor variable "cf". Supported # options include: cf=phi, cf=W=e^(-2*phi) (default), and cf=chi=e^(-4*phi) # \partial_t phi = \left[\beta^k \partial_k \phi \right] <- TERM 1 # + \frac{1}{6} \left (\bar{D}_{k} \beta^{k} - \alpha K \right ) <- TERM 2 global cf_rhs cf_rhs = sp.Rational(1, 6) * (Dbarbetacontraction - alpha * trK) # Term 2 for k in range(DIM): cf_rhs += betaU[k] * phi_dupD[k] # Term 1 # Next multiply to convert phi_rhs to cf_rhs. if par.parval_from_str( "BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "phi": pass # do nothing; cf_rhs = phi_rhs elif par.parval_from_str( "BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "W": cf_rhs *= -2 * cf # cf_rhs = -2*cf*phi_rhs elif par.parval_from_str( "BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "chi": cf_rhs *= -4 * cf # cf_rhs = -4*cf*phi_rhs else: print("Error: EvolvedConformalFactor_cf == " + par.parval_from_str( "BSSN.BSSN_quantities::EvolvedConformalFactor_cf") + " unsupported!") exit(1) # Step 5: right-hand side of trK (trace of extrinsic curvature): # \partial_t K = \beta^k \partial_k K <- TERM 1 # + \frac{1}{3} \alpha K^{2} <- TERM 2 # + \alpha \bar{A}_{i j} \bar{A}^{i j} <- TERM 3 # - - e^{-4 \phi} (\bar{D}_{i} \bar{D}^{i} \alpha + 2 \bar{D}^{i} \alpha \bar{D}_{i} \phi ) <- TERM 4 global trK_rhs # TERM 2: trK_rhs = sp.Rational(1, 3) * alpha * trK * trK trK_dupD = ixp.declarerank1("trK_dupD") for i in range(DIM): # TERM 1: trK_rhs += betaU[i] * trK_dupD[i] for i in range(DIM): for j in range(DIM): # TERM 4: trK_rhs += -exp_m4phi * gammabarUU[i][j] * ( alpha_dBarDD[i][j] + 2 * alpha_dBarD[j] * phi_dBarD[i]) AbarUU = Bq.AbarUU # From Bq.AbarUU_AbarUD_trAbar() for i in range(DIM): for j in range(DIM): # TERM 3: trK_rhs += alpha * AbarDD[i][j] * AbarUU[i][j] # Step 6: right-hand side of \partial_t \bar{\Lambda}^i: # \partial_t \bar{\Lambda}^i = \beta^k \partial_k \bar{\Lambda}^i - \partial_k \beta^i \bar{\Lambda}^k <- TERM 1 # + \bar{\gamma}^{j k} \hat{D}_{j} \hat{D}_{k} \beta^{i} <- TERM 2 # + \frac{2}{3} \Delta^{i} \bar{D}_{j} \beta^{j} <- TERM 3 # + \frac{1}{3} \bar{D}^{i} \bar{D}_{j} \beta^{j} <- TERM 4 # - 2 \bar{A}^{i j} (\partial_{j} \alpha - 6 \partial_{j} \phi) <- TERM 5 # + 2 \alpha \bar{A}^{j k} \Delta_{j k}^{i} <- TERM 6 # - \frac{4}{3} \alpha \bar{\gamma}^{i j} \partial_{j} K <- TERM 7 # Step 6.a: Term 1 of \partial_t \bar{\Lambda}^i: \beta^k \partial_k \bar{\Lambda}^i - \partial_k \beta^i \bar{\Lambda}^k # First we declare \bar{\Lambda}^i and \bar{\Lambda}^i_{,j} in terms of \lambda^i and \lambda^i_{,j} global LambdabarU_dupD # Used on the RHS of the Gamma-driving shift conditions LambdabarU_dupD = ixp.zerorank2() lambdaU_dupD = ixp.declarerank2("lambdaU_dupD", "nosym") for i in range(DIM): for j in range(DIM): LambdabarU_dupD[i][j] = lambdaU_dupD[i][j] * rfm.ReU[i] + lambdaU[ i] * rfm.ReUdD[i][j] global Lambdabar_rhsU # Used on the RHS of the Gamma-driving shift conditions Lambdabar_rhsU = ixp.zerorank1() for i in range(DIM): for k in range(DIM): Lambdabar_rhsU[i] += betaU[k] * LambdabarU_dupD[i][k] - betaU_dD[ i][k] * LambdabarU[k] # Term 1 # Step 6.b: Term 2 of \partial_t \bar{\Lambda}^i = \bar{\gamma}^{jk} (Term 2a + Term 2b + Term 2c) # Term 2a: \bar{\gamma}^{jk} \beta^i_{,kj} Term2aUDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): Term2aUDD[i][j][k] += betaU_dDD[i][k][j] # Term 2b: \hat{\Gamma}^i_{mk,j} \beta^m + \hat{\Gamma}^i_{mk} \beta^m_{,j} # + \hat{\Gamma}^i_{dj}\beta^d_{,k} - \hat{\Gamma}^d_{kj} \beta^i_{,d} Term2bUDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): for m in range(DIM): Term2bUDD[i][j][k] += rfm.GammahatUDDdD[i][m][k][j] * betaU[m] \ + rfm.GammahatUDD[i][m][k] * betaU_dD[m][j] \ + rfm.GammahatUDD[i][m][j] * betaU_dD[m][k] \ - rfm.GammahatUDD[m][k][j] * betaU_dD[i][m] # Term 2c: \hat{\Gamma}^i_{dj}\hat{\Gamma}^d_{mk} \beta^m - \hat{\Gamma}^d_{kj} \hat{\Gamma}^i_{md} \beta^m Term2cUDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): for m in range(DIM): for d in range(DIM): Term2cUDD[i][j][k] += (rfm.GammahatUDD[i][d][j] * rfm.GammahatUDD[d][m][k] \ - rfm.GammahatUDD[d][k][j] * rfm.GammahatUDD[i][m][d]) * betaU[m] Lambdabar_rhsUpieceU = ixp.zerorank1() # Put it all together to get Term 2: for i in range(DIM): for j in range(DIM): for k in range(DIM): Lambdabar_rhsU[i] += gammabarUU[j][k] * (Term2aUDD[i][j][k] + Term2bUDD[i][j][k] + Term2cUDD[i][j][k]) Lambdabar_rhsUpieceU[i] += gammabarUU[j][k] * ( Term2aUDD[i][j][k] + Term2bUDD[i][j][k] + Term2cUDD[i][j][k]) # Step 6.c: Term 3 of \partial_t \bar{\Lambda}^i: # \frac{2}{3} \Delta^{i} \bar{D}_{j} \beta^{j} DGammaU = Bq.DGammaU # From Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU() for i in range(DIM): Lambdabar_rhsU[i] += sp.Rational( 2, 3) * DGammaU[i] * Dbarbetacontraction # Term 3 # Step 6.d: Term 4 of \partial_t \bar{\Lambda}^i: # \frac{1}{3} \bar{D}^{i} \bar{D}_{j} \beta^{j} detgammabar_dDD = Bq.detgammabar_dDD # From Bq.detgammabar_and_derivs() Dbarbetacontraction_dBarD = ixp.zerorank1() for k in range(DIM): for m in range(DIM): Dbarbetacontraction_dBarD[m] += betaU_dDD[k][k][m] + \ (betaU_dD[k][m] * detgammabar_dD[k] + betaU[k] * detgammabar_dDD[k][m]) / (2 * detgammabar) \ - betaU[k] * detgammabar_dD[k] * detgammabar_dD[m] / ( 2 * detgammabar * detgammabar) for i in range(DIM): for m in range(DIM): Lambdabar_rhsU[i] += sp.Rational( 1, 3) * gammabarUU[i][m] * Dbarbetacontraction_dBarD[m] # Step 6.e: Term 5 of \partial_t \bar{\Lambda}^i: # - 2 \bar{A}^{i j} (\partial_{j} \alpha - 6 \alpha \partial_{j} \phi) for i in range(DIM): for j in range(DIM): Lambdabar_rhsU[i] += -2 * AbarUU[i][j] * (alpha_dD[j] - 6 * alpha * phi_dD[j]) # Step 6.f: Term 6 of \partial_t \bar{\Lambda}^i: # 2 \alpha \bar{A}^{j k} \Delta^{i}_{j k} DGammaUDD = Bq.DGammaUDD # From RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU() for i in range(DIM): for j in range(DIM): for k in range(DIM): Lambdabar_rhsU[ i] += 2 * alpha * AbarUU[j][k] * DGammaUDD[i][j][k] # Step 6.g: Term 7 of \partial_t \bar{\Lambda}^i: # -\frac{4}{3} \alpha \bar{\gamma}^{i j} \partial_{j} K trK_dD = ixp.declarerank1("trK_dD") for i in range(DIM): for j in range(DIM): Lambdabar_rhsU[i] += -sp.Rational( 4, 3) * alpha * gammabarUU[i][j] * trK_dD[j] # Step 7: Rescale the RHS quantities so that the evolved # variables are smooth across coord singularities global h_rhsDD, a_rhsDD, lambda_rhsU h_rhsDD = ixp.zerorank2() a_rhsDD = ixp.zerorank2() lambda_rhsU = ixp.zerorank1() for i in range(DIM): lambda_rhsU[i] = Lambdabar_rhsU[i] / rfm.ReU[i] for j in range(DIM): h_rhsDD[i][j] = gammabar_rhsDD[i][j] / rfm.ReDD[i][j] a_rhsDD[i][j] = Abar_rhsDD[i][j] / rfm.ReDD[i][j]
def Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear( CoordType_in, ADM_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: 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: # 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: # 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()." ) 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.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() 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: Set the conformal factor variable $\texttt{cf}$, which is set # by the "BSSN_unrescaled_and_barred_vars::EvolvedConformalFactor_cf" parameter. For example if # "EvolvedConformalFactor_cf" 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_unrescaled_and_barred_vars::EvolvedConformalFactor_cf" 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_unrescaled_and_barred_vars::EvolvedConformalFactor_cf" 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("EvolvedConformalFactor_cf") == "phi": cf = sp.Rational(1, 12) * sp.log(gammaDET / gammabarDET) elif par.parval_from_str("EvolvedConformalFactor_cf") == "chi": cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 3)) elif par.parval_from_str("EvolvedConformalFactor_cf") == "W": cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 6)) else: print("Error EvolvedConformalFactor_cf type = \"" + par.parval_from_str("EvolvedConformalFactor_cf") + "\" 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. 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] 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")