def gammabar__inverse_and_derivs(): # Step 4.a: Declare as globals all expressions that may be used # outside this function, declare BSSN gridfunctions # if not defined already, and set DIM=3. global gammabarUU, gammabarDD_dD, gammabarDD_dupD, gammabarDD_dDD, GammabarUDD hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already( ) DIM = 3 # This function needs gammabarDD, defined in BSSN_basic_tensors() BSSN_basic_tensors() # Step 4.a.i: gammabarUU: gammabarUU, dummydet = ixp.symm_matrix_inverter3x3(gammabarDD) # Step 4.b.i: gammabarDDdD[i][j][k] # = \hat{\gamma}_{ij,k} + h_{ij,k} \text{ReDD[i][j]} + h_{ij} \text{ReDDdD[i][j][k]}. gammabarDD_dD = ixp.zerorank3() gammabarDD_dupD = ixp.zerorank3() hDD_dD = ixp.declarerank3("hDD_dD", "sym01") hDD_dupD = ixp.declarerank3("hDD_dupD", "sym01") for i in range(DIM): for j in range(DIM): for k in range(DIM): gammabarDD_dD[i][j][k] = rfm.ghatDDdD[i][j][k] + \ hDD_dD[i][j][k] * rfm.ReDD[i][j] + hDD[i][j] * rfm.ReDDdD[i][j][k] # Compute associated upwinded derivative, needed for the \bar{\gamma}_{ij} RHS gammabarDD_dupD[i][j][k] = rfm.ghatDDdD[i][j][k] + \ hDD_dupD[i][j][k] * rfm.ReDD[i][j] + hDD[i][j] * rfm.ReDDdD[i][j][k] # Step 4.b.ii: Compute gammabarDD_dDD in terms of the rescaled BSSN quantity hDD # and its derivatives, as well as the reference metric and rescaling # matrix, and its derivatives (expression given below): hDD_dDD = ixp.declarerank4("hDD_dDD", "sym01_sym23") gammabarDD_dDD = ixp.zerorank4() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): # gammabar_{ij,kl} = gammahat_{ij,kl} # + h_{ij,kl} ReDD[i][j] # + h_{ij,k} ReDDdD[i][j][l] + h_{ij,l} ReDDdD[i][j][k] # + h_{ij} ReDDdDD[i][j][k][l] gammabarDD_dDD[i][j][k][l] = rfm.ghatDDdDD[i][j][k][l] gammabarDD_dDD[i][j][k][ l] += hDD_dDD[i][j][k][l] * rfm.ReDD[i][j] gammabarDD_dDD[i][j][k][l] += hDD_dD[i][j][k] * rfm.ReDDdD[i][j][l] + \ hDD_dD[i][j][l] * rfm.ReDDdD[i][j][k] gammabarDD_dDD[i][j][k][ l] += hDD[i][j] * rfm.ReDDdDD[i][j][k][l] # Step 4.b.iii: Define barred Christoffel symbol \bar{\Gamma}^{i}_{kl} = GammabarUDD[i][k][l] (see expression below) GammabarUDD = ixp.zerorank3() for i in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): # Gammabar^i_{kl} = 1/2 * gammabar^{im} ( gammabar_{mk,l} + gammabar_{ml,k} - gammabar_{kl,m}): GammabarUDD[i][k][l] += sp.Rational(1, 2) * gammabarUU[i][m] * \ (gammabarDD_dD[m][k][l] + gammabarDD_dD[m][l][k] - gammabarDD_dD[k][l][m])
def AbarUU_AbarUD_trAbar_AbarDD_dD(): # Step 6.a: Declare as globals all expressions that may be used # outside this function, declare BSSN gridfunctions # if not defined already, and set DIM=3. global AbarUU, AbarUD, trAbar, AbarDD_dD, AbarDD_dupD hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already( ) DIM = 3 # Define AbarDD and gammabarDD in terms of BSSN gridfunctions BSSN_basic_tensors() # Define gammabarUU in terms of BSSN gridfunctions gammabar__inverse_and_derivs() # Step 6.a.i: Compute Abar^{ij} in terms of Abar_{ij} and gammabar^{ij} AbarUU = ixp.zerorank2() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): # Abar^{ij} = gammabar^{ik} gammabar^{jl} Abar_{kl} AbarUU[i][j] += gammabarUU[i][k] * gammabarUU[j][ l] * AbarDD[k][l] # Step 6.a.ii: Compute Abar^i_j in terms of Abar_{ij} and gammabar^{ij} AbarUD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): for k in range(DIM): # Abar^i_j = gammabar^{ik} Abar_{kj} AbarUD[i][j] += gammabarUU[i][k] * AbarDD[k][j] # Step 6.a.iii: Compute Abar^k_k = trace of Abar: trAbar = sp.sympify(0) for k in range(DIM): for j in range(DIM): # Abar^k_k = gammabar^{kj} Abar_{jk} trAbar += gammabarUU[k][j] * AbarDD[j][k] # Step 6.a.iv: Compute Abar_{ij,k} AbarDD_dD = ixp.zerorank3() AbarDD_dupD = ixp.zerorank3() aDD_dD = ixp.declarerank3("aDD_dD", "sym01") aDD_dupD = ixp.declarerank3("aDD_dupD", "sym01") for i in range(DIM): for j in range(DIM): for k in range(DIM): AbarDD_dupD[i][j][k] = rfm.ReDDdD[i][j][k] * aDD[i][ j] + rfm.ReDD[i][j] * aDD_dupD[i][j][k] AbarDD_dD[i][j][k] = rfm.ReDDdD[i][j][k] * aDD[i][ j] + rfm.ReDD[i][j] * aDD_dD[i][j][k]
def LambdabarU_lambdaU__exact_gammaDD(gammaDD): global LambdabarU, lambdaU if gammaDD == None: gammaDD = ixp.declarerank2("gammaDD", "sym01") # \bar{Lambda}^i = \bar{gamma}^{jk}(\bar{Gamma}^i_{jk} - \hat{Gamma}^i_{jk}). gammabarDD_hDD(gammaDD) gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD) # First compute Christoffel symbols \bar{Gamma}^i_{jk}, with respect to barred metric: GammabarUDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): GammabarUDD[i][j][k] += sp.Rational( 1, 2) * gammabarUU[i][l] * ( sp.diff(gammabarDD[l][j], rfm.xx[k]) + sp.diff(gammabarDD[l][k], rfm.xx[j]) - sp.diff(gammabarDD[j][k], rfm.xx[l])) # Next evaluate \bar{Lambda}^i, based on GammabarUDD above and GammahatUDD # (from the reference metric): LambdabarU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): LambdabarU[i] += gammabarUU[j][k] * (GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k]) lambdaU = ixp.zerorank1() for i in range(DIM): lambdaU[i] = LambdabarU[i] / rfm.ReU[i]
def compute_g4DD_zerotimederiv_dD(gammaDD,betaU,alpha, gammaDD_dD,betaU_dD,alpha_dD): global g4DD_zerotimederiv_dD # Eq. 2.121 in B&S betaD = ixp.zerorank1() for i in range(3): for j in range(3): betaD[i] += gammaDD[i][j]*betaU[j] # gammaDD_dD = ixp.declarerank3("gammaDD_dDD","sym12",DIM=3) # betaU_dD = ixp.declarerank2("betaU_dD" ,"nosym",DIM=3) betaDdD = ixp.zerorank2() for i in range(3): for j in range(3): for k in range(3): # Recall that betaD[i] = gammaDD[i][j]*betaU[j] (Eq. 2.121 in B&S) betaDdD[i][k] += gammaDD_dD[i][j][k]*betaU[j] + gammaDD[i][j]*betaU_dD[j][k] # Eq. 2.122 in B&S g4DD_zerotimederiv_dD = ixp.zerorank3(DIM=4) for k in range(3): # Recall that g4DD[0][0] = -alpha^2 + betaU[j]*betaD[j] g4DD_zerotimederiv_dD[0][0][k+1] += -2*alpha*alpha_dD[k] for j in range(3): g4DD_zerotimederiv_dD[0][0][k+1] += betaU_dD[j][k]*betaD[j] + betaU[j]*betaDdD[j][k] for i in range(3): for k in range(3): # Recall that g4DD[i][0] = g4DD[0][i] = betaD[i] g4DD_zerotimederiv_dD[i+1][0][k+1] = g4DD_zerotimederiv_dD[0][i+1][k+1] = betaDdD[i][k] for i in range(3): for j in range(3): for k in range(3): # Recall that g4DD[i][j] = gammaDD[i][j] g4DD_zerotimederiv_dD[i+1][j+1][k+1] = gammaDD_dD[i][j][k]
def betaU_derivs(): # Step 8.i: Declare as globals all expressions that may be used # outside this function, declare BSSN gridfunctions # if not defined already, and set DIM=3. global betaU_dD, betaU_dupD, betaU_dDD hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already( ) DIM = 3 # Step 8.ii: Compute the unrescaled shift vector beta^i = ReU[i]*vet^i vetU_dD = ixp.declarerank2("vetU_dD", "nosym") vetU_dupD = ixp.declarerank2("vetU_dupD", "nosym") # Needed for upwinded \beta^i_{,j} vetU_dDD = ixp.declarerank3("vetU_dDD", "sym12") # Needed for \beta^i_{,j} betaU_dD = ixp.zerorank2() betaU_dupD = ixp.zerorank2() # Needed for, e.g., \beta^i RHS betaU_dDD = ixp.zerorank3() # Needed for, e.g., \bar{\Lambda}^i RHS for i in range(DIM): for j in range(DIM): betaU_dD[i][ j] = vetU_dD[i][j] * rfm.ReU[i] + vetU[i] * rfm.ReUdD[i][j] betaU_dupD[i][j] = vetU_dupD[i][j] * rfm.ReU[i] + vetU[ i] * rfm.ReUdD[i][j] # Needed for \beta^i RHS for k in range(DIM): # Needed for, e.g., \bar{\Lambda}^i RHS: betaU_dDD[i][j][k] = vetU_dDD[i][j][k] * rfm.ReU[i] + vetU_dD[i][j] * rfm.ReUdD[i][k] + \ vetU_dD[i][k] * rfm.ReUdD[i][j] + vetU[i] * rfm.ReUdDD[i][j][k]
def define_LeviCivitaSymbol(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
def calculate_Stilde_flux(flux_dirn, inputs_provided=False, alpha_face=None, gammadet_face=None, gamma_faceDD=None, gamma_faceUU=None, beta_faceU=None, Valenciav_rU=None, B_rU=None, Valenciav_lU=None, B_lU=None): if not inputs_provided: # We will pass values of the gridfunction on the cell faces into the function. This requires us # to declare them as C parameters in NRPy+. We will denote this with the _face infix/suffix. alpha_face, gammadet_face = gri.register_gridfunctions( "AUXEVOL", ["alpha_face", "gammadet_face"]) gamma_faceDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "gamma_faceDD", "sym01") gamma_faceUU = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "gamma_faceUU", "sym01") beta_faceU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "beta_faceU") # We'll need some more gridfunctions, now, to represent the reconstructions of BU and ValenciavU # on the right and left faces Valenciav_rU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_rU", DIM=3) B_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_rU", DIM=3) Valenciav_lU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_lU", DIM=3) B_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_lU", DIM=3) # We'll also compute some powers of the conformal factor psi: # Since psi^6 = sqrt(gammadet) by definition, # psi = sp.sqrt(gammadet_face)**(1.0/6.0) # psim4 = psi**(-4.0) global Stilde_fluxD Stilde_fluxD = ixp.zerorank3() for mom_comp in range(DIM): Stilde_fluxD[mom_comp] = HLLE_solver(flux_dirn, mom_comp, alpha_face, gammadet_face, gamma_faceDD, gamma_faceUU, beta_faceU, Valenciav_rU, B_rU, Valenciav_lU, B_lU)
def compute_AD_flux_term(sqrtgammaDET, driftvU, BU): # Levi-Civita tensor for cross products import WeylScal4NRPy.WeylScalars_Cartesian as weyl LeviCivitaDDD = weyl.define_LeviCivitaSymbol_rank3() LeviCivitaUUU = ixp.zerorank3() for i in range(3): for j in range(3): for k in range(3): LeviCivitaDDD[i][j][k] *= sqrtgammaDET global A_fluxD A_fluxD = ixp.zerorank1() for i in range(3): for j in range(3): for k in range(3): # \epsilon_{ijk} v^j B^k A_fluxD[i] += LeviCivitaDDD[i][j][k] * driftvU[j] * BU[k]
def calculate_Stilde_flux(flux_dirn,alpha_face,gamma_faceDD,beta_faceU,\ Valenciav_rU,B_rU,Valenciav_lU,B_lU,sqrt4pi): find_cmax_cmin(flux_dirn,gamma_faceDD,beta_faceU,alpha_face) global Stilde_fluxD Stilde_fluxD = ixp.zerorank3() for mom_comp in range(3): calculate_GRFFE_Tmunu_and_contractions(flux_dirn, mom_comp, gamma_faceDD,beta_faceU,alpha_face,\ Valenciav_rU,B_rU,sqrt4pi) Fr = F Ur = U calculate_GRFFE_Tmunu_and_contractions(flux_dirn, mom_comp, gamma_faceDD,beta_faceU,alpha_face,\ Valenciav_lU,B_lU,sqrt4pi) Fl = F Ul = U Stilde_fluxD[mom_comp] = HLLE_solver(cmax, cmin, Fr, Fl, Ur, Ul)
def calculate_Stilde_flux(flux_dirn,inputs_provided=True,alpha_face=None,gamma_faceDD=None,beta_faceU=None,\ Valenciav_rU=None,B_rU=None,Valenciav_lU=None,B_lU=None,sqrt4pi=None): if not inputs_provided: # We will pass values of the gridfunction on the cell faces into the function. This requires us # to declare them as C parameters in NRPy+. We will denote this with the _face infix/suffix. alpha_face = gri.register_gridfunctions("AUXEVOL", "alpha_face") gamma_faceDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "gamma_faceDD", "sym01") beta_faceU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "beta_faceU") # We'll need some more gridfunctions, now, to represent the reconstructions of BU and ValenciavU # on the right and left faces Valenciav_rU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_rU", DIM=3) B_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_rU", DIM=3) Valenciav_lU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_lU", DIM=3) B_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_lU", DIM=3) sqrt4pi = par.Cparameters("REAL", thismodule, "sqrt4pi", "sqrt(4.0*M_PI)") find_cmax_cmin(flux_dirn, gamma_faceDD, beta_faceU, alpha_face) global Stilde_fluxD Stilde_fluxD = ixp.zerorank3() for mom_comp in range(3): calculate_GRFFE_Tmunu_and_contractions(flux_dirn, mom_comp, gamma_faceDD,beta_faceU,alpha_face,\ Valenciav_rU,B_rU,sqrt4pi) Fr = F Ur = U calculate_GRFFE_Tmunu_and_contractions(flux_dirn, mom_comp, gamma_faceDD,beta_faceU,alpha_face,\ Valenciav_lU,B_lU,sqrt4pi) Fl = F Ul = U Stilde_fluxD[mom_comp] = HLLE_solver(cmax, cmin, Fr, Fl, Ur, Ul)
def LambdabarU_lambdaU__exact_gammaDD(gammaDD): global LambdabarU, lambdaU if gammaDD is None: # Use "is None" instead of "==None", as the former is more correct. gammaDD = ixp.declarerank2("gammaDD", "sym01") # \bar{Lambda}^i = \bar{gamma}^{jk}(\bar{Gamma}^i_{jk} - \hat{Gamma}^i_{jk}). gammabarDD_hDD(gammaDD) gammabarUU, _gammabarDET = ixp.symm_matrix_inverter3x3( gammabarDD) # _gammabarDET unused. # First compute Christoffel symbols \bar{Gamma}^i_{jk}, with respect to barred metric: GammabarUDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): GammabarUDD[i][j][k] += sp.Rational( 1, 2) * gammabarUU[i][l] * ( sp.diff(gammabarDD[l][j], rfm.xx[k]) + sp.diff(gammabarDD[l][k], rfm.xx[j]) - sp.diff(gammabarDD[j][k], rfm.xx[l])) # Next evaluate \bar{Lambda}^i, based on GammabarUDD above and GammahatUDD # (from the reference metric): LambdabarU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): LambdabarU[i] += gammabarUU[j][k] * (GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k]) for i in range(DIM): # We evaluate LambdabarU[i] here to ensure proper cancellations. If these cancellations # are not applied, certain expressions (e.g., lambdaU[0] in StaticTrumpet) will # cause SymPy's (v1.5+) CSE algorithm to hang LambdabarU[i] = LambdabarU[i].doit() lambdaU = ixp.zerorank1() for i in range(DIM): lambdaU[i] = LambdabarU[i] / rfm.ReU[i]
def calculate_Stilde_flux(flux_dirn,alpha_face,gamma_faceDD,beta_faceU,\ Valenciav_rU,B_rU,Valenciav_lU,B_lU,sqrt4pi): chsp.find_cmax_cmin(flux_dirn, gamma_faceDD, beta_faceU, alpha_face) global Stilde_fluxD Stilde_fluxD = ixp.zerorank3() for mom_comp in range(3): calculate_GRFFE_Tmunu_and_contractions(flux_dirn, mom_comp, gamma_faceDD,beta_faceU,alpha_face,\ Valenciav_rU,B_rU,sqrt4pi) Fr = F Ur = U if mom_comp == 0: global F_out, U_out, smallb_out F_out = F U_out = U smallb_out = GRFFE.smallbsquared calculate_GRFFE_Tmunu_and_contractions(flux_dirn, mom_comp, gamma_faceDD,beta_faceU,alpha_face,\ Valenciav_lU,B_lU,sqrt4pi) Fl = F Ul = U Stilde_fluxD[mom_comp] = HLLE_solver(chsp.cmax, chsp.cmin, Fr, Fl, Ur, Ul)
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 GiRaFFE_NRPy_A2B(outdir,gammaDD,AD,BU): cmd.mkdir(outdir) # Set spatial dimension (must be 3 for BSSN) DIM = 3 par.set_parval_from_str("grid::DIM",DIM) # Compute the sqrt of the three metric determinant. import GRHD.equations as gh gh.compute_sqrtgammaDET(gammaDD) # Import the Levi-Civita symbol and build the corresponding tensor. # We already have a handy function to define the Levi-Civita symbol in WeylScalars import WeylScal4NRPy.WeylScalars_Cartesian as weyl LeviCivitaDDD = weyl.define_LeviCivitaSymbol_rank3() LeviCivitaUUU = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): LCijk = LeviCivitaDDD[i][j][k] #LeviCivitaDDD[i][j][k] = LCijk * sp.sqrt(gho.gammadet) LeviCivitaUUU[i][j][k] = LCijk / gh.sqrtgammaDET AD_dD = ixp.declarerank2("AD_dD","nosym") BU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j] # Write the code to compute derivatives with shifted stencils as needed. with open(os.path.join(outdir,"driver_AtoB.h"),"w") as file: file.write("""void compute_A2B_in_ghostzones(const paramstruct *restrict params,REAL *restrict in_gfs,REAL *restrict auxevol_gfs, const int i0min,const int i0max, const int i1min,const int i1max, const int i2min,const int i2max) { #include "../set_Cparameters.h" for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) { REAL dx_Ay,dx_Az,dy_Ax,dy_Az,dz_Ax,dz_Ay; // Check to see if we're on the +x or -x face. If so, use a downwinded- or upwinded-stencil, respectively. // Otherwise, use a centered stencil. if (i0 > 0 && i0 < Nxx_plus_2NGHOSTS0-1) { dx_Ay = invdx0*(-1.0/2.0*in_gfs[IDX4S(AD1GF, i0-1,i1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD1GF, i0+1,i1,i2)]); dx_Az = invdx0*(-1.0/2.0*in_gfs[IDX4S(AD2GF, i0-1,i1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD2GF, i0+1,i1,i2)]); } else if (i0==0) { dx_Ay = invdx0*(-3.0/2.0*in_gfs[IDX4S(AD1GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD1GF, i0+1,i1,i2)] - 1.0/2.0*in_gfs[IDX4S(AD1GF, i0+2,i1,i2)]); dx_Az = invdx0*(-3.0/2.0*in_gfs[IDX4S(AD2GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD2GF, i0+1,i1,i2)] - 1.0/2.0*in_gfs[IDX4S(AD2GF, i0+2,i1,i2)]); } else { dx_Ay = invdx0*((3.0/2.0)*in_gfs[IDX4S(AD1GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD1GF, i0-1,i1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD1GF, i0-2,i1,i2)]); dx_Az = invdx0*((3.0/2.0)*in_gfs[IDX4S(AD2GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD2GF, i0-1,i1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD2GF, i0-2,i1,i2)]); } // As above, but in the y direction. if (i1 > 0 && i1 < Nxx_plus_2NGHOSTS1-1) { dy_Ax = invdx1*(-1.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1-1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1+1,i2)]); dy_Az = invdx1*(-1.0/2.0*in_gfs[IDX4S(AD2GF, i0,i1-1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD2GF, i0,i1+1,i2)]); } else if (i1==0) { dy_Ax = invdx1*(-3.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD0GF, i0,i1+1,i2)] - 1.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1+2,i2)]); dy_Az = invdx1*(-3.0/2.0*in_gfs[IDX4S(AD2GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD2GF, i0,i1+1,i2)] - 1.0/2.0*in_gfs[IDX4S(AD2GF, i0,i1+2,i2)]); } else { dy_Ax = invdx1*((3.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD0GF, i0,i1-1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1-2,i2)]); dy_Az = invdx1*((3.0/2.0)*in_gfs[IDX4S(AD2GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD2GF, i0,i1-1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD2GF, i0,i1-2,i2)]); } // As above, but in the z direction. if (i2 > 0 && i2 < Nxx_plus_2NGHOSTS2-1) { dz_Ax = invdx2*(-1.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1,i2-1)] + (1.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1,i2+1)]); dz_Ay = invdx2*(-1.0/2.0*in_gfs[IDX4S(AD1GF, i0,i1,i2-1)] + (1.0/2.0)*in_gfs[IDX4S(AD1GF, i0,i1,i2+1)]); } else if (i2==0) { dz_Ax = invdx2*(-3.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD0GF, i0,i1,i2+1)] - 1.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1,i2+2)]); dz_Ay = invdx2*(-3.0/2.0*in_gfs[IDX4S(AD1GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD1GF, i0,i1,i2+1)] - 1.0/2.0*in_gfs[IDX4S(AD1GF, i0,i1,i2+2)]); } else { dz_Ax = invdx2*((3.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD0GF, i0,i1,i2-1)] + (1.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1,i2-2)]); dz_Ay = invdx2*((3.0/2.0)*in_gfs[IDX4S(AD1GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD1GF, i0,i1,i2-1)] + (1.0/2.0)*in_gfs[IDX4S(AD1GF, i0,i1,i2-2)]); } // Compute the magnetic field in the normal way, using the previously calculated derivatives. const double gammaDD00 = auxevol_gfs[IDX4S(GAMMADD00GF, i0,i1,i2)]; const double gammaDD01 = auxevol_gfs[IDX4S(GAMMADD01GF, i0,i1,i2)]; const double gammaDD02 = auxevol_gfs[IDX4S(GAMMADD02GF, i0,i1,i2)]; const double gammaDD11 = auxevol_gfs[IDX4S(GAMMADD11GF, i0,i1,i2)]; const double gammaDD12 = auxevol_gfs[IDX4S(GAMMADD12GF, i0,i1,i2)]; const double gammaDD22 = auxevol_gfs[IDX4S(GAMMADD22GF, i0,i1,i2)]; /* * NRPy+ Finite Difference Code Generation, Step 2 of 1: Evaluate SymPy expressions and write to main memory: */ const double invsqrtg = 1.0/sqrt(gammaDD00*gammaDD11*gammaDD22 - gammaDD00*gammaDD12*gammaDD12 + 2*gammaDD01*gammaDD02*gammaDD12 - gammaDD11*gammaDD02*gammaDD02 - gammaDD22*gammaDD01*gammaDD01); auxevol_gfs[IDX4S(BU0GF, i0,i1,i2)] = (dy_Az-dz_Ay)*invsqrtg; auxevol_gfs[IDX4S(BU1GF, i0,i1,i2)] = (dz_Ax-dx_Az)*invsqrtg; auxevol_gfs[IDX4S(BU2GF, i0,i1,i2)] = (dx_Ay-dy_Ax)*invsqrtg; } } """) # Now, we'll also write some more auxiliary functions to handle the order-lowering method for A2B with open(os.path.join(outdir,"driver_AtoB.h"),"a") as file: file.write("""REAL relative_error(REAL a, REAL b) { if((a+b)!=0.0) { return 2.0*fabs(a-b)/fabs(a+b); } else { return 0.0; } } #define M2 0 #define M1 1 #define P0 2 #define P1 3 #define P2 4 #define CN4 0 #define CN2 1 #define UP2 2 #define DN2 3 #define UP1 4 #define DN1 5 void compute_Bx_pointwise(REAL *Bx, const REAL invdy, const REAL *Ay, const REAL invdz, const REAL *Az) { REAL dz_Ay,dy_Az; dz_Ay = invdz*((Ay[P1]-Ay[M1])*2.0/3.0 - (Ay[P2]-Ay[M2])/12.0); dy_Az = invdy*((Az[P1]-Az[M1])*2.0/3.0 - (Az[P2]-Az[M2])/12.0); Bx[CN4] = dy_Az - dz_Ay; dz_Ay = invdz*(Ay[P1]-Ay[M1])/2.0; dy_Az = invdy*(Az[P1]-Az[M1])/2.0; Bx[CN2] = dy_Az - dz_Ay; dz_Ay = invdz*(-1.5*Ay[P0]+2.0*Ay[P1]-0.5*Ay[P2]); dy_Az = invdy*(-1.5*Az[P0]+2.0*Az[P1]-0.5*Az[P2]); Bx[UP2] = dy_Az - dz_Ay; dz_Ay = invdz*(1.5*Ay[P0]-2.0*Ay[M1]+0.5*Ay[M2]); dy_Az = invdy*(1.5*Az[P0]-2.0*Az[M1]+0.5*Az[M2]); Bx[DN2] = dy_Az - dz_Ay; dz_Ay = invdz*(Ay[P1]-Ay[P0]); dy_Az = invdy*(Az[P1]-Az[P0]); Bx[UP1] = dy_Az - dz_Ay; dz_Ay = invdz*(Ay[P0]-Ay[M1]); dy_Az = invdy*(Az[P0]-Az[M1]); Bx[DN1] = dy_Az - dz_Ay; } #define TOLERANCE_A2B 1.0e-4 REAL find_accepted_Bx_order(REAL *Bx) { REAL accepted_val = Bx[CN4]; REAL Rel_error_o2_vs_o4 = relative_error(Bx[CN2],Bx[CN4]); REAL Rel_error_oCN2_vs_oDN2 = relative_error(Bx[CN2],Bx[DN2]); REAL Rel_error_oCN2_vs_oUP2 = relative_error(Bx[CN2],Bx[UP2]); if(Rel_error_o2_vs_o4 > TOLERANCE_A2B) { accepted_val = Bx[CN2]; if(Rel_error_o2_vs_o4 > Rel_error_oCN2_vs_oDN2 || Rel_error_o2_vs_o4 > Rel_error_oCN2_vs_oUP2) { // Should we use AND or OR in if statement? if(relative_error(Bx[UP2],Bx[UP1]) < relative_error(Bx[DN2],Bx[DN1])) { accepted_val = Bx[UP2]; } else { accepted_val = Bx[DN2]; } } } return accepted_val; } """) order_lowering_body = """REAL AD0_1[5],AD0_2[5],AD1_2[5],AD1_0[5],AD2_0[5],AD2_1[5]; const double gammaDD00 = auxevol_gfs[IDX4S(GAMMADD00GF, i0,i1,i2)]; const double gammaDD01 = auxevol_gfs[IDX4S(GAMMADD01GF, i0,i1,i2)]; const double gammaDD02 = auxevol_gfs[IDX4S(GAMMADD02GF, i0,i1,i2)]; const double gammaDD11 = auxevol_gfs[IDX4S(GAMMADD11GF, i0,i1,i2)]; const double gammaDD12 = auxevol_gfs[IDX4S(GAMMADD12GF, i0,i1,i2)]; const double gammaDD22 = auxevol_gfs[IDX4S(GAMMADD22GF, i0,i1,i2)]; AD0_2[M2] = in_gfs[IDX4S(AD0GF, i0,i1,i2-2)]; AD0_2[M1] = in_gfs[IDX4S(AD0GF, i0,i1,i2-1)]; AD0_1[M2] = in_gfs[IDX4S(AD0GF, i0,i1-2,i2)]; AD0_1[M1] = in_gfs[IDX4S(AD0GF, i0,i1-1,i2)]; AD0_1[P0] = AD0_2[P0] = in_gfs[IDX4S(AD0GF, i0,i1,i2)]; AD0_1[P1] = in_gfs[IDX4S(AD0GF, i0,i1+1,i2)]; AD0_1[P2] = in_gfs[IDX4S(AD0GF, i0,i1+2,i2)]; AD0_2[P1] = in_gfs[IDX4S(AD0GF, i0,i1,i2+1)]; AD0_2[P2] = in_gfs[IDX4S(AD0GF, i0,i1,i2+2)]; AD1_2[M2] = in_gfs[IDX4S(AD1GF, i0,i1,i2-2)]; AD1_2[M1] = in_gfs[IDX4S(AD1GF, i0,i1,i2-1)]; AD1_0[M2] = in_gfs[IDX4S(AD1GF, i0-2,i1,i2)]; AD1_0[M1] = in_gfs[IDX4S(AD1GF, i0-1,i1,i2)]; AD1_2[P0] = AD1_0[P0] = in_gfs[IDX4S(AD1GF, i0,i1,i2)]; AD1_0[P1] = in_gfs[IDX4S(AD1GF, i0+1,i1,i2)]; AD1_0[P2] = in_gfs[IDX4S(AD1GF, i0+2,i1,i2)]; AD1_2[P1] = in_gfs[IDX4S(AD1GF, i0,i1,i2+1)]; AD1_2[P2] = in_gfs[IDX4S(AD1GF, i0,i1,i2+2)]; AD2_1[M2] = in_gfs[IDX4S(AD2GF, i0,i1-2,i2)]; AD2_1[M1] = in_gfs[IDX4S(AD2GF, i0,i1-1,i2)]; AD2_0[M2] = in_gfs[IDX4S(AD2GF, i0-2,i1,i2)]; AD2_0[M1] = in_gfs[IDX4S(AD2GF, i0-1,i1,i2)]; AD2_0[P0] = AD2_1[P0] = in_gfs[IDX4S(AD2GF, i0,i1,i2)]; AD2_0[P1] = in_gfs[IDX4S(AD2GF, i0+1,i1,i2)]; AD2_0[P2] = in_gfs[IDX4S(AD2GF, i0+2,i1,i2)]; AD2_1[P1] = in_gfs[IDX4S(AD2GF, i0,i1+1,i2)]; AD2_1[P2] = in_gfs[IDX4S(AD2GF, i0,i1+2,i2)]; const double invsqrtg = 1.0/sqrt(gammaDD00*gammaDD11*gammaDD22 - gammaDD00*gammaDD12*gammaDD12 + 2*gammaDD01*gammaDD02*gammaDD12 - gammaDD11*gammaDD02*gammaDD02 - gammaDD22*gammaDD01*gammaDD01); REAL BU0[4],BU1[4],BU2[4]; compute_Bx_pointwise(BU0,invdx2,AD1_2,invdx1,AD2_1); compute_Bx_pointwise(BU1,invdx0,AD2_0,invdx2,AD0_2); compute_Bx_pointwise(BU2,invdx1,AD0_1,invdx0,AD1_0); auxevol_gfs[IDX4S(BU0GF, i0,i1,i2)] = find_accepted_Bx_order(BU0)*invsqrtg; auxevol_gfs[IDX4S(BU1GF, i0,i1,i2)] = find_accepted_Bx_order(BU1)*invsqrtg; auxevol_gfs[IDX4S(BU2GF, i0,i1,i2)] = find_accepted_Bx_order(BU2)*invsqrtg; """ # Here, we'll use the outCfunction() function to output a function that will compute the magnetic field # on the interior. Then, we'll add postloop code to handle the ghostzones. desc="Compute the magnetic field from the vector potential everywhere, including ghostzones" name="driver_A_to_B" driver_Ccode = outCfunction( outfile = "returnstring", desc=desc, name=name, params = "const paramstruct *restrict params,REAL *restrict in_gfs,REAL *restrict auxevol_gfs", body = fin.FD_outputC("returnstring",[lhrh(lhs=gri.gfaccess("out_gfs","BU0"),rhs=BU[0]),\ lhrh(lhs=gri.gfaccess("out_gfs","BU1"),rhs=BU[1]),\ lhrh(lhs=gri.gfaccess("out_gfs","BU2"),rhs=BU[2])]).replace("IDX4","IDX4S"), # body = order_lowering_body, postloop = """ int imin[3] = { NGHOSTS_A2B, NGHOSTS_A2B, NGHOSTS_A2B }; int imax[3] = { NGHOSTS+Nxx0, NGHOSTS+Nxx1, NGHOSTS+Nxx2 }; // Now, we loop over the ghostzones to calculate the magnetic field there. for(int which_gz = 0; which_gz < NGHOSTS_A2B; which_gz++) { // After updating each face, adjust imin[] and imax[] // to reflect the newly-updated face extents. compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2]); imin[0]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2]); imax[0]++; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2]); imin[1]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2]); imax[1]++; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2]); imin[2]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1); imax[2]++; } """, loopopts="InteriorPoints", rel_path_for_Cparams=os.path.join("../")).replace("=NGHOSTS","=NGHOSTS_A2B").replace("NGHOSTS+Nxx0","Nxx_plus_2NGHOSTS0-NGHOSTS_A2B").replace("NGHOSTS+Nxx1","Nxx_plus_2NGHOSTS1-NGHOSTS_A2B").replace("NGHOSTS+Nxx2","Nxx_plus_2NGHOSTS2-NGHOSTS_A2B") with open(os.path.join(outdir,"driver_AtoB.h"),"a") as file: file.write(driver_Ccode)
def Psi4(specify_tetrad=True): global psi4_im_pt, psi4_re_pt # Step 1.b: Given the chosen coordinate system, set up # corresponding reference metric and needed # reference metric quantities # The following function call sets up the reference metric # and related quantities, including rescaling matrices ReDD, # ReU, and hatted quantities. rfm.reference_metric() # Step 1.c: Set spatial dimension (must be 3 for BSSN, as BSSN is # a 3+1-dimensional decomposition of the general # relativistic field equations) DIM = 3 # Step 1.d: Import all ADM quantities as written in terms of BSSN quantities import BSSN.ADM_in_terms_of_BSSN as AB AB.ADM_in_terms_of_BSSN() # Step 1.e: Set up tetrad vectors if specify_tetrad == True: import BSSN.Psi4_tetrads as BP4t BP4t.Psi4_tetrads() mre4U = BP4t.mre4U mim4U = BP4t.mim4U n4U = BP4t.n4U else: # For code validation against NRPy+ psi_4 tutorial module (Tutorial-Psi4.ipynb); # ensures a more complete code validation. mre4U = ixp.declarerank1("mre4U", DIM=4) mim4U = ixp.declarerank1("mim4U", DIM=4) n4U = ixp.declarerank1("n4U", DIM=4) # Step 2: Construct the (rank-4) Riemann curvature tensor associated with the ADM 3-metric: RDDDD = ixp.zerorank4() gammaDDdDD = AB.gammaDDdDD for i in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): RDDDD[i][k][l][m] = sp.Rational(1, 2) * \ (gammaDDdDD[i][m][k][l] + gammaDDdDD[k][l][i][m] - gammaDDdDD[i][l][k][m] - gammaDDdDD[k][m][i][l]) # ... then we add the term on the right: gammaDD = AB.gammaDD GammaUDD = AB.GammaUDD for i in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): for n in range(DIM): for p in range(DIM): RDDDD[i][k][l][m] += gammaDD[n][p] * \ (GammaUDD[n][k][l] * GammaUDD[p][i][m] - GammaUDD[n][k][m] * GammaUDD[p][i][l]) # Step 3: Construct the (rank-4) tensor in term 1 of psi_4 (referring to Eq 5.1 in # Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf rank4term1DDDD = ixp.zerorank4() KDD = AB.KDD for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): rank4term1DDDD[i][j][k][l] = RDDDD[i][j][k][ l] + KDD[i][k] * KDD[l][j] - KDD[i][l] * KDD[k][j] # Step 4: Construct the (rank-3) tensor in term 2 of psi_4 (referring to Eq 5.1 in # Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf rank3term2DDD = ixp.zerorank3() KDDdD = AB.KDDdD for j in range(DIM): for k in range(DIM): for l in range(DIM): rank3term2DDD[j][k][l] = sp.Rational( 1, 2) * (KDDdD[j][k][l] - KDDdD[j][l][k]) # ... then we construct the second term in this sum: # \Gamma^{p}_{j[k} K_{l]p} = \frac{1}{2} (\Gamma^{p}_{jk} K_{lp}-\Gamma^{p}_{jl} K_{kp}): for j in range(DIM): for k in range(DIM): for l in range(DIM): for p in range(DIM): rank3term2DDD[j][k][l] += sp.Rational( 1, 2) * (GammaUDD[p][j][k] * KDD[l][p] - GammaUDD[p][j][l] * KDD[k][p]) # Finally, we multiply the term by $-8$: for j in range(DIM): for k in range(DIM): for l in range(DIM): rank3term2DDD[j][k][l] *= sp.sympify(-8) # Step 5: Construct the (rank-2) tensor in term 3 of psi_4 (referring to Eq 5.1 in # Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf # Step 5.1: Construct 3-Ricci tensor R_{ij} = gamma^{im} R_{ijml} RDD = ixp.zerorank2() gammaUU = AB.gammaUU for j in range(DIM): for l in range(DIM): for i in range(DIM): for m in range(DIM): RDD[j][l] += gammaUU[i][m] * RDDDD[i][j][m][l] # Step 5.2: Construct K^p_l = gamma^{pi} K_{il} KUD = ixp.zerorank2() for p in range(DIM): for l in range(DIM): for i in range(DIM): KUD[p][l] += gammaUU[p][i] * KDD[i][l] # Step 5.3: Construct trK = gamma^{ij} K_{ij} trK = sp.sympify(0) for i in range(DIM): for j in range(DIM): trK += gammaUU[i][j] * KDD[i][j] # Next we put these terms together to construct the entire term in parentheses: # +4 \left(R_{jl} - K_{jp} K^p_l + K K_{jl} \right), rank2term3DD = ixp.zerorank2() for j in range(DIM): for l in range(DIM): rank2term3DD[j][l] = RDD[j][l] + trK * KDD[j][l] for p in range(DIM): rank2term3DD[j][l] += -KDD[j][p] * KUD[p][l] # Finally we multiply by +4: for j in range(DIM): for l in range(DIM): rank2term3DD[j][l] *= sp.sympify(4) # Step 6: Construct real & imaginary parts of psi_4 # by contracting constituent rank 2, 3, and 4 # tensors with input tetrads mre4U, mim4U, & n4U. def tetrad_product__Real_psi4(n, Mre, Mim, mu, nu, eta, delta): return +n[mu] * Mre[nu] * n[eta] * Mre[delta] - n[mu] * Mim[nu] * n[ eta] * Mim[delta] def tetrad_product__Imag_psi4(n, Mre, Mim, mu, nu, eta, delta): return -n[mu] * Mre[nu] * n[eta] * Mim[delta] - n[mu] * Mim[nu] * n[ eta] * Mre[delta] # We split psi_4 into three pieces, to expedite & possibly parallelize C code generation. psi4_re_pt = [sp.sympify(0), sp.sympify(0), sp.sympify(0)] psi4_im_pt = [sp.sympify(0), sp.sympify(0), sp.sympify(0)] # First term: for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): psi4_re_pt[0] += rank4term1DDDD[i][j][ k][l] * tetrad_product__Real_psi4( n4U, mre4U, mim4U, i + 1, j + 1, k + 1, l + 1) psi4_im_pt[0] += rank4term1DDDD[i][j][ k][l] * tetrad_product__Imag_psi4( n4U, mre4U, mim4U, i + 1, j + 1, k + 1, l + 1) # Second term: for j in range(DIM): for k in range(DIM): for l in range(DIM): psi4_re_pt[1] += rank3term2DDD[j][k][l] * \ sp.Rational(1, 2) * (+tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, k + 1, l + 1) - tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, k + 1, l + 1)) psi4_im_pt[1] += rank3term2DDD[j][k][l] * \ sp.Rational(1, 2) * (+tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, k + 1, l + 1) - tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, k + 1, l + 1)) # Third term: for j in range(DIM): for l in range(DIM): psi4_re_pt[2] += rank2term3DD[j][l] * \ (sp.Rational(1, 4) * (+tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, 0, l + 1) - tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, 0, l + 1) - tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, l + 1, 0) + tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, l + 1, 0))) psi4_im_pt[2] += rank2term3DD[j][l] * \ (sp.Rational(1, 4) * (+tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, 0, l + 1) - tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, 0, l + 1) - tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, l + 1, 0) + tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, l + 1, 0)))
def 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 WeylScalars_Cartesian(): # Step 3.a: Set spatial dimension (must be 3 for BSSN) DIM = 3 par.set_parval_from_str("grid::DIM", DIM) # Step 3.b: declare the additional gridfunctions (i.e., functions whose values are declared # at every grid point, either inside or outside of our SymPy expressions) needed # for this thorn: # * the physical metric $\gamma_{ij}$, # * the extrinsic curvature $K_{ij}$, # * the real and imaginary components of $\psi_4$, and # * the Weyl curvature invariants: gammaDD = ixp.register_gridfunctions_for_single_rank2( "AUX", "gammaDD", "sym01") # The AUX or EVOL designation is *not* # used in diagnostic modules. kDD = ixp.register_gridfunctions_for_single_rank2("AUX", "kDD", "sym01") x, y, z = gri.register_gridfunctions("AUX", ["x", "y", "z"]) global psi4r, psi4i, psi3r, psi3i, psi2r, psi2i, psi1r, psi1i, psi0r, psi0i psi4r, psi4i, psi3r, psi3i, psi2r, psi2i, psi1r, psi1i, psi0r, psi0i = gri.register_gridfunctions( "AUX", [ "psi4r", "psi4i", "psi3r", "psi3i", "psi2r", "psi2i", "psi1r", "psi1i", "psi0r", "psi0i" ]) # Step 4: Set which tetrad is used; at the moment, only one supported option # The tetrad depends in general on the inverse 3-metric gammaUU[i][j]=\gamma^{ij} # and the determinant of the 3-metric (detgamma), which are defined in # the following line of code from gammaDD[i][j]=\gamma_{ij}. tmpgammaUU, detgamma = ixp.symm_matrix_inverter3x3(gammaDD) detgamma = sp.simplify(detgamma) gammaUU = ixp.zerorank2() for i in range(3): for j in range(3): gammaUU[i][j] = sp.simplify(tmpgammaUU[i][j]) if par.parval_from_str("WeylScal4NRPy.WeylScalars_Cartesian::TetradChoice" ) == "Approx_QuasiKinnersley": # Eqs 5.6 in https://arxiv.org/pdf/gr-qc/0104063.pdf xmoved = x # - xorig ymoved = y # - yorig zmoved = z # - zorig # Step 5.a: Choose 3 orthogonal vectors. Here, we choose one in the azimuthal # direction, one in the radial direction, and the cross product of the two. # Eqs 5.7 v1U = ixp.zerorank1() v2U = ixp.zerorank1() v3U = ixp.zerorank1() v1U[0] = -ymoved v1U[1] = xmoved # + offset v1U[2] = sp.sympify(0) v2U[0] = xmoved # + offset v2U[1] = ymoved v2U[2] = zmoved LeviCivitaSymbol_rank3 = 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] * LeviCivitaSymbol_rank3[ d][b][c] * v1U[b] * v2U[c] for a in range(DIM): v3U[a] = sp.simplify(v3U[a]) # Step 5.b: Gram-Schmidt orthonormalization of the vectors. # The w_i^a vectors here are used to temporarily hold values on the way to the final vectors e_i^a # e_1^a &= \frac{v_1^a}{\omega_{11}} # e_2^a &= \frac{v_2^a - \omega_{12} e_1^a}{\omega_{22}} # e_3^a &= \frac{v_3^a - \omega_{13} e_1^a - \omega_{23} e_2^a}{\omega_{33}}, # Normalize the first vector w1U = ixp.zerorank1() for a in range(DIM): w1U[a] = v1U[a] omega11 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega11 += w1U[a] * w1U[b] * gammaDD[a][b] e1U = ixp.zerorank1() for a in range(DIM): e1U[a] = w1U[a] / sp.sqrt(omega11) # Subtract off the portion of the first vector along the second, then normalize omega12 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega12 += e1U[a] * v2U[b] * gammaDD[a][b] w2U = ixp.zerorank1() for a in range(DIM): w2U[a] = v2U[a] - omega12 * e1U[a] omega22 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega22 += w2U[a] * w2U[b] * gammaDD[a][b] e2U = ixp.zerorank1() for a in range(DIM): e2U[a] = w2U[a] / sp.sqrt(omega22) # Subtract off the portion of the first and second vectors along the third, then normalize omega13 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega13 += e1U[a] * v3U[b] * gammaDD[a][b] omega23 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega23 += e2U[a] * v3U[b] * gammaDD[a][b] w3U = ixp.zerorank1() for a in range(DIM): w3U[a] = v3U[a] - omega13 * e1U[a] - omega23 * e2U[a] omega33 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega33 += w3U[a] * w3U[b] * gammaDD[a][b] e3U = ixp.zerorank1() for a in range(DIM): e3U[a] = w3U[a] / sp.sqrt(omega33) # Step 5.c: Construct the tetrad itself. # Eqs. 5.6: # l^a &= \frac{1}{\sqrt{2}} e_2^a \\ # n^a &= -\frac{1}{\sqrt{2}} e_2^a \\ # m^a &= \frac{1}{\sqrt{2}} (e_3^a + i e_1^a) \\ # \overset{*}{m}{}^a &= \frac{1}{\sqrt{2}} (e_3^a - i e_1^a) isqrt2 = 1 / sp.sqrt(2) ltetU = ixp.zerorank1() ntetU = ixp.zerorank1() # mtetU = ixp.zerorank1() # mtetccU = ixp.zerorank1() remtetU = ixp.zerorank1( ) # SymPy did not like trying to take the real/imaginary parts of such a immtetU = ixp.zerorank1( ) # complicated expression, so we do it ourselves. for i in range(DIM): ltetU[i] = isqrt2 * e2U[i] ntetU[i] = -isqrt2 * e2U[i] remtetU[i] = isqrt2 * e3U[i] immtetU[i] = isqrt2 * e1U[i] nn = isqrt2 else: print("Error: TetradChoice == " + par.parval_from_str("TetradChoice") + " unsupported!") sys.exit(1) # Step 5: Declare and construct the second derivative of the metric. gammaDD_dD = ixp.declarerank3("gammaDD_dD", "sym01") # Define the Christoffel symbols GammaUDD = ixp.zerorank3(DIM) for i in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): GammaUDD[i][k][l] += (sp.Rational(1, 2)) * gammaUU[i][m] * \ (gammaDD_dD[m][k][l] + gammaDD_dD[m][l][k] - gammaDD_dD[k][l][m]) # Step 6.a: Declare and construct the Riemann curvature tensor: # R_{abcd} = \frac{1}{2} (\gamma_{ad,cb}+\gamma_{bc,da}-\gamma_{ac,bd}-\gamma_{bd,ac}) # + \gamma_{je} \Gamma^{j}_{bc}\Gamma^{e}_{ad} - \gamma_{je} \Gamma^{j}_{bd} \Gamma^{e}_{ac} gammaDD_dDD = ixp.declarerank4("gammaDD_dDD", "sym01_sym23") RiemannDDDD = ixp.zerorank4() for a in range(DIM): for b in range(DIM): for c in range(DIM): for d in range(DIM): RiemannDDDD[a][b][c][d] = (gammaDD_dDD[a][d][c][b] + \ gammaDD_dDD[b][c][d][a] - \ gammaDD_dDD[a][c][b][d] - \ gammaDD_dDD[b][d][a][c]) / 2 for e in range(DIM): for j in range(DIM): RiemannDDDD[a][b][c][d] += gammaDD[j][e] * GammaUDD[j][b][c] * GammaUDD[e][a][d] - \ gammaDD[j][e] * GammaUDD[j][b][d] * GammaUDD[e][a][c] # Step 6.b: We also need the extrinsic curvature tensor $K_{ij}$. # In Cartesian coordinates, we already made the components gridfunctions. # We will, however, need to calculate the trace of K seperately: trK = sp.sympify(0) for i in range(DIM): for j in range(DIM): trK += gammaUU[i][j] * kDD[i][j] # Step 7: Build the formula for \psi_4. # Gauss equation: involving the Riemann tensor and extrinsic curvature. # GaussDDDD[i][j][k][l] =& R_{ijkl} + 2K_{i[k}K_{l]j} GaussDDDD = ixp.zerorank4() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): GaussDDDD[i][j][k][l] = RiemannDDDD[i][j][k][ l] + kDD[i][k] * kDD[l][j] - kDD[i][l] * kDD[k][j] # Codazzi equation: involving partial derivatives of the extrinsic curvature. # We will first need to declare derivatives of kDD # CodazziDDD[j][k][l] =& -2 (K_{j[k,l]} + \Gamma^p_{j[k} K_{l]p}) kDD_dD = ixp.declarerank3("kDD_dD", "sym01") CodazziDDD = ixp.zerorank3() for j in range(DIM): for k in range(DIM): for l in range(DIM): CodazziDDD[j][k][l] = kDD_dD[j][l][k] - kDD_dD[j][k][l] for p in range(DIM): CodazziDDD[j][k][l] += GammaUDD[p][j][l] * kDD[k][ p] - GammaUDD[p][j][k] * kDD[l][p] # Another piece. While not associated with any particular equation, # this is still useful for organizational purposes. # RojoDD[j][l]} = & R_{jl} - K_{jp} K^p_l + KK_{jl} \\ # = & \gamma^{pd} R_{jpld} - K_{jp} K^p_l + KK_{jl} RojoDD = ixp.zerorank2() for j in range(DIM): for l in range(DIM): RojoDD[j][l] = trK * kDD[j][l] for p in range(DIM): for d in range(DIM): RojoDD[j][l] += gammaUU[p][d] * RiemannDDDD[j][p][l][ d] - kDD[j][p] * gammaUU[p][d] * kDD[d][l] # Now we can calculate $\psi_4$ itself! We assume l^0 = n^0 = \frac{1}{\sqrt{2}} # and m^0 = \overset{*}{m}{}^0 = 0 to simplify these equations. # We calculate the Weyl scalars as defined in https://arxiv.org/abs/gr-qc/0104063 # In terms of the above-defined quantites, the psis are defined as: # \psi_4 =&\ (\text{GaussDDDD[i][j][k][l]}) n^i \overset{*}{m}{}^j n^k \overset{*}{m}{}^l \\ # &+2 (\text{CodazziDDD[j][k][l]}) n^{0} \overset{*}{m}{}^{j} n^k \overset{*}{m}{}^l \\ # &+ (\text{RojoDD[j][l]}) n^{0} \overset{*}{m}{}^{j} n^{0} \overset{*}{m}{}^{l}. # \psi_3 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i n^j \overset{*}{m}{}^k n^l \\ # &+ (\text{CodazziDDD[j][k][l]}) (l^{0} n^{j} \overset{*}{m}{}^k n^l - l^{j} n^{0} \overset{*}{m}{}^k n^l - l^k n^j\overset{*}{m}{}^l n^0) \\ # &- (\text{RojoDD[j][l]}) l^{0} n^{j} \overset{*}{m}{}^l n^0 - l^{j} n^{0} \overset{*}{m}{}^l n^0 \\ # \psi_2 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i m^j \overset{*}{m}{}^k n^l \\ # &+ (\text{CodazziDDD[j][k][l]}) (l^{0} m^{j} \overset{*}{m}{}^k n^l - l^{j} m^{0} \overset{*}{m}{}^k n^l - l^k m^l \overset{*}{m}{}^l n^0) \\ # &- (\text{RojoDD[j][l]}) l^0 m^j \overset{*}{m}{}^l n^0 \\ # \psi_1 =&\ (\text{GaussDDDD[i][j][k][l]}) n^i l^j m^k l^l \\ # &+ (\text{CodazziDDD[j][k][l]}) (n^{0} l^{j} m^k l^l - n^{j} l^{0} m^k l^l - n^k l^l m^j l^0) \\ # &- (\text{RojoDD[j][l]}) (n^{0} l^{j} m^l l^0 - n^{j} l^{0} m^l l^0) \\ # \psi_0 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i m^j l^k m^l \\ # &+2 (\text{CodazziDDD[j][k][l]}) (l^0 m^j l^k m^l + l^k m^l l^0 m^j) \\ # &+ (\text{RojoDD[j][l]}) l^0 m^j l^0 m^j. \\ psi4r = sp.sympify(0) psi4i = sp.sympify(0) psi3r = sp.sympify(0) psi3i = sp.sympify(0) psi2r = sp.sympify(0) psi2i = sp.sympify(0) psi1r = sp.sympify(0) psi1i = sp.sympify(0) psi0r = sp.sympify(0) psi0i = sp.sympify(0) for l in range(DIM): for j in range(DIM): psi4r += RojoDD[j][l] * nn * nn * (remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi4i += RojoDD[j][l] * nn * nn * (-remtetU[j] * immtetU[l] - immtetU[j] * remtetU[l]) psi3r += -RojoDD[j][l] * nn * nn * (ntetU[j] - ltetU[j]) * remtetU[l] psi3i += RojoDD[j][l] * nn * nn * (ntetU[j] - ltetU[j]) * immtetU[l] psi2r += -RojoDD[j][l] * nn * nn * (remtetU[l] * remtetU[j] + immtetU[j] * immtetU[l]) psi2i += -RojoDD[j][l] * nn * nn * (immtetU[l] * remtetU[j] - remtetU[j] * immtetU[l]) psi1r += RojoDD[j][l] * nn * nn * (ntetU[j] * remtetU[l] - ltetU[j] * remtetU[l]) psi1i += RojoDD[j][l] * nn * nn * (ntetU[j] * immtetU[l] - ltetU[j] * immtetU[l]) psi0r += RojoDD[j][l] * nn * nn * (remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi0i += RojoDD[j][l] * nn * nn * (remtetU[j] * immtetU[l] + immtetU[j] * remtetU[l]) for l in range(DIM): for j in range(DIM): for k in range(DIM): psi4r += 2 * CodazziDDD[j][k][l] * ntetU[k] * nn * ( remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi4i += 2 * CodazziDDD[j][k][l] * ntetU[k] * nn * ( -remtetU[j] * immtetU[l] - immtetU[j] * remtetU[l]) psi3r += 1 * CodazziDDD[j][k][l] * nn * ( (ntetU[j] - ltetU[j]) * remtetU[k] * ntetU[l] - remtetU[j] * ltetU[k] * ntetU[l]) psi3i += -1 * CodazziDDD[j][k][l] * nn * ( (ntetU[j] - ltetU[j]) * immtetU[k] * ntetU[l] - immtetU[j] * ltetU[k] * ntetU[l]) psi2r += 1 * CodazziDDD[j][k][l] * nn * ( ntetU[l] * (remtetU[j] * remtetU[k] + immtetU[j] * immtetU[k]) - ltetU[k] * (remtetU[j] * remtetU[l] + immtetU[j] * immtetU[l])) psi2i += 1 * CodazziDDD[j][k][l] * nn * ( ntetU[l] * (immtetU[j] * remtetU[k] - remtetU[j] * immtetU[k]) - ltetU[k] * (remtetU[j] * immtetU[l] - immtetU[j] * remtetU[l])) psi1r += 1 * CodazziDDD[j][k][l] * nn * ( ltetU[j] * remtetU[k] * ltetU[l] - remtetU[j] * ntetU[k] * ltetU[l] - ntetU[j] * remtetU[k] * ltetU[l]) psi1i += 1 * CodazziDDD[j][k][l] * nn * ( ltetU[j] * immtetU[k] * ltetU[l] - immtetU[j] * ntetU[k] * ltetU[l] - ntetU[j] * immtetU[k] * ltetU[l]) psi0r += 2 * CodazziDDD[j][k][l] * nn * ltetU[k] * ( remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi0i += 2 * CodazziDDD[j][k][l] * nn * ltetU[k] * ( remtetU[j] * immtetU[l] + immtetU[j] * remtetU[l]) for l in range(DIM): for j in range(DIM): for k in range(DIM): for i in range(DIM): psi4r += GaussDDDD[i][j][k][l] * ntetU[i] * ntetU[k] * ( remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi4i += GaussDDDD[i][j][k][l] * ntetU[i] * ntetU[k] * ( -remtetU[j] * immtetU[l] - immtetU[j] * remtetU[l]) psi3r += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[ j] * remtetU[k] * ntetU[l] psi3i += -GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[ j] * immtetU[k] * ntetU[l] psi2r += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[l] * ( remtetU[j] * remtetU[k] + immtetU[j] * immtetU[k]) psi2i += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[l] * ( immtetU[j] * remtetU[k] - remtetU[j] * immtetU[k]) psi1r += GaussDDDD[i][j][k][l] * ntetU[i] * ltetU[ j] * remtetU[k] * ltetU[l] psi1i += GaussDDDD[i][j][k][l] * ntetU[i] * ltetU[ j] * immtetU[k] * ltetU[l] psi0r += GaussDDDD[i][j][k][l] * ltetU[i] * ltetU[k] * ( remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi0i += GaussDDDD[i][j][k][l] * ltetU[i] * ltetU[k] * ( remtetU[j] * immtetU[l] + immtetU[j] * remtetU[l])
def WeylScalars_Cartesian(): # We do not need the barred or hatted quantities calculated when using Cartesian coordinates. # Instead, we declare the PHYSICAL metric and extrinsic curvature as grid functions. gammaDD = ixp.register_gridfunctions_for_single_rank2( "EVOL", "gammaDD", "sym01") kDD = ixp.register_gridfunctions_for_single_rank2("EVOL", "kDD", "sym01") gammaUU, detgamma = ixp.symm_matrix_inverter3x3(gammaDD) output_scalars = par.parval_from_str("output_scalars") global psi4r, psi4i, psi3r, psi3i, psi2r, psi2i, psi1r, psi1i, psi0r, psi0i # if output_scalars is "all_psis_and_invariants": # psi4r,psi4i,psi3r,psi3i,psi2r,psi2i,psi1r,psi1i,psi0r,psi0i = sp.symbols("psi4r psi4i\ # psi3r psi3i\ # psi2r psi2i\ # psi1r psi1i\ # psi0r psi0i") # elif output_scalars is "all_psis": psi4r,psi4i,psi3r,psi3i,psi2r,psi2i,psi1r,psi1i,psi0r,psi0i = gri.register_gridfunctions("AUX",["psi4r","psi4i",\ "psi3r","psi3i",\ "psi2r","psi2i",\ "psi1r","psi1i",\ "psi0r","psi0i"]) # Step 2a: Set spatial dimension (must be 3 for BSSN) DIM = 3 par.set_parval_from_str("grid::DIM", DIM) # Step 2b: Set the coordinate system to Cartesian x, y, z = gri.register_gridfunctions("AUX", ["x", "y", "z"]) # Step 2c: Set which tetrad is used; at the moment, only one supported option if par.parval_from_str("WeylScal4NRPy.WeylScalars_Cartesian::TetradChoice" ) == "Approx_QuasiKinnersley": # Step 3a: Choose 3 orthogonal vectors. Here, we choose one in the azimuthal # direction, one in the radial direction, and the cross product of the two. # Eqs 5.6, 5.7 in https://arxiv.org/pdf/gr-qc/0104063.pdf: # v_1^a &= [-y,x,0] \\ # v_2^a &= [x,y,z] \\ # v_3^a &= {\rm det}(g)^{1/2} g^{ad} \epsilon_{dbc} v_1^b v_2^c, v1U = ixp.zerorank1() v2U = ixp.zerorank1() v3U = ixp.zerorank1() v1U[0] = -y v1U[1] = x v1U[2] = sp.sympify(0) v2U[0] = x v2U[1] = y v2U[2] = z LeviCivitaSymbol_rank3 = define_LeviCivitaSymbol_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] * LeviCivitaSymbol_rank3[ d][b][c] * v1U[b] * v2U[c] # Step 3b: Gram-Schmidt orthonormalization of the vectors. # The w_i^a vectors here are used to temporarily hold values on the way to the final vectors e_i^a # e_1^a &= \frac{v_1^a}{\omega_{11}} \\ # e_2^a &= \frac{v_2^a - \omega_{12} e_1^a}{\omega_{22}} \\ # e_3^a &= \frac{v_3^a - \omega_{13} e_1^a - \omega_{23} e_2^a}{\omega_{33}}, \\ # Normalize the first vector w1U = ixp.zerorank1() for a in range(DIM): w1U[a] = v1U[a] omega11 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega11 += w1U[a] * w1U[b] * gammaDD[a][b] e1U = ixp.zerorank1() for a in range(DIM): e1U[a] = w1U[a] / sp.sqrt(omega11) # Subtract off the portion of the first vector along the second, then normalize omega12 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega12 += e1U[a] * v2U[b] * gammaDD[a][b] w2U = ixp.zerorank1() for a in range(DIM): w2U[a] = v2U[a] - omega12 * e1U[a] omega22 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega22 += w2U[a] * w2U[b] * gammaDD[a][b] e2U = ixp.zerorank1() for a in range(DIM): e2U[a] = w2U[a] / sp.sqrt(omega22) # Subtract off the portion of the first and second vectors along the third, then normalize omega13 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega13 += e1U[a] * v3U[b] * gammaDD[a][b] omega23 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega23 += e2U[a] * v3U[b] * gammaDD[a][b] w3U = ixp.zerorank1() for a in range(DIM): w3U[a] = v3U[a] - omega13 * e1U[a] - omega23 * e2U[a] omega33 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega33 += w3U[a] * w3U[b] * gammaDD[a][b] e3U = ixp.zerorank1() for a in range(DIM): e3U[a] = w3U[a] / sp.sqrt(omega33) # Step 3c: Construct the tetrad itself. # Eqs. 5.6: # l^a &= \frac{1}{\sqrt{2}} e_2^a \\ # n^a &= -\frac{1}{\sqrt{2}} e_2^a \\ # m^a &= \frac{1}{\sqrt{2}} (e_3^a + i e_1^a) \\ # \overset{*}{m}{}^a &= \frac{1}{\sqrt{2}} (e_3^a - i e_1^a) isqrt2 = 1 / sp.sqrt(2) ltetU = ixp.zerorank1() ntetU = ixp.zerorank1() #mtetU = ixp.zerorank1() #mtetccU = ixp.zerorank1() remtetU = ixp.zerorank1( ) # SymPy does not like trying to take the real/imaginary parts of such a immtetU = ixp.zerorank1( ) # complicated expression as the Weyl scalars, so we will do it ourselves. for i in range(DIM): ltetU[i] = isqrt2 * e2U[i] ntetU[i] = -isqrt2 * e2U[i] remtetU[i] = isqrt2 * e3U[i] immtetU[i] = isqrt2 * e1U[i] nn = isqrt2 else: print("Error: TetradChoice == " + par.parval_from_str("TetradChoice") + " unsupported!") exit(1) gammaDD_dD = ixp.declarerank3("gammaDD_dD", "sym01") # Define the Christoffel symbols GammaUDD = ixp.zerorank3(DIM) for i in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): GammaUDD[i][k][l] += (sp.Rational(1,2))*gammaUU[i][m]*\ (gammaDD_dD[m][k][l] + gammaDD_dD[m][l][k] - gammaDD_dD[k][l][m]) # Step 4b: Declare and construct the Riemann curvature tensor: # R_{abcd} = \frac{1}{2} (\gamma_{ad,cb}+\gamma_{bc,da}-\gamma_{ac,bd}-\gamma_{bd,ac}) # + \gamma_{je} \Gamma^{j}_{bc}\Gamma^{e}_{ad} - \gamma_{je} \Gamma^{j}_{bd} \Gamma^{e}_{ac} gammaDD_dDD = ixp.declarerank4("gammaDD_dDD", "sym01_sym23") RiemannDDDD = ixp.zerorank4() for a in range(DIM): for b in range(DIM): for c in range(DIM): for d in range(DIM): RiemannDDDD[a][b][c][d] = (gammaDD_dDD[a][d][c][b] + \ gammaDD_dDD[b][c][d][a] - \ gammaDD_dDD[a][c][b][d] - \ gammaDD_dDD[b][d][a][c]) / 2 for e in range(DIM): for j in range(DIM): RiemannDDDD[a][b][c][d] += gammaDD[j][e] * GammaUDD[j][b][c] * GammaUDD[e][a][d] - \ gammaDD[j][e] * GammaUDD[j][b][d] * GammaUDD[e][a][c] # Step 4c: We also need the extrinsic curvature tensor $K_{ij}$. # In Cartesian coordinates, we already made the components gridfunctions. # We will, however, need to calculate the trace of K seperately: trK = sp.sympify(0) for i in range(DIM): for j in range(DIM): trK += gammaUU[i][j] * kDD[i][j] # Step 5: Build the formula for \psi_4. # Gauss equation: involving the Riemann tensor and extrinsic curvature. # GaussDDDD[i][j][k][l] =& R_{ijkl} + 2K_{i[k}K_{l]j} GaussDDDD = ixp.zerorank4() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): GaussDDDD[i][j][k][l] = RiemannDDDD[i][j][k][ l] + kDD[i][k] * kDD[l][j] - kDD[i][l] * kDD[k][j] # Codazzi equation: involving partial derivatives of the extrinsic curvature. # We will first need to declare derivatives of kDD # CodazziDDD[j][k][l] =& -2 (K_{j[k,l]} + \Gamma^p_{j[k} K_{l]p}) kDD_dD = ixp.declarerank3("kDD_dD", "sym01") CodazziDDD = ixp.zerorank3() for j in range(DIM): for k in range(DIM): for l in range(DIM): CodazziDDD[j][k][l] = kDD_dD[j][l][k] - kDD_dD[j][k][l] for p in range(DIM): CodazziDDD[j][k][l] += GammaUDD[p][j][l] * kDD[k][ p] - GammaUDD[p][j][k] * kDD[l][p] # Another piece. While not associated with any particular equation, # this is still useful for organizational purposes. # RojoDD[j][l]} = & R_{jl} - K_{jp} K^p_l + KK_{jl} \\ # = & \gamma^{pd} R_{jpld} - K_{jp} K^p_l + KK_{jl} RojoDD = ixp.zerorank2() for j in range(DIM): for l in range(DIM): RojoDD[j][l] = trK * kDD[j][l] for p in range(DIM): for d in range(DIM): RojoDD[j][l] += gammaUU[p][d] * RiemannDDDD[j][p][l][ d] - kDD[j][p] * gammaUU[p][d] * kDD[d][l] # Now we can calculate $\psi_4$ itself! We assume l^0 = n^0 = \frac{1}{\sqrt{2}} # and m^0 = \overset{*}{m}{}^0 = 0 to simplify these equations. # We calculate the Weyl scalars as defined in https://arxiv.org/abs/gr-qc/0104063 # In terms of the above-defined quantites, the psis are defined as: # \psi_4 =&\ (\text{GaussDDDD[i][j][k][l]}) n^i \overset{*}{m}{}^j n^k \overset{*}{m}{}^l \\ # &+2 (\text{CodazziDDD[j][k][l]}) n^{0} \overset{*}{m}{}^{j} n^k \overset{*}{m}{}^l \\ # &+ (\text{RojoDD[j][l]}) n^{0} \overset{*}{m}{}^{j} n^{0} \overset{*}{m}{}^{l}. # \psi_3 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i n^j \overset{*}{m}{}^k n^l \\ # &+ (\text{CodazziDDD[j][k][l]}) (l^{0} n^{j} \overset{*}{m}{}^k n^l - l^{j} n^{0} \overset{*}{m}{}^k n^l - l^k n^j\overset{*}{m}{}^l n^0) \\ # &- (\text{RojoDD[j][l]}) l^{0} n^{j} \overset{*}{m}{}^l n^0 - l^{j} n^{0} \overset{*}{m}{}^l n^0 \\ # \psi_2 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i m^j \overset{*}{m}{}^k n^l \\ # &+ (\text{CodazziDDD[j][k][l]}) (l^{0} m^{j} \overset{*}{m}{}^k n^l - l^{j} m^{0} \overset{*}{m}{}^k n^l - l^k m^l \overset{*}{m}{}^l n^0) \\ # &- (\text{RojoDD[j][l]}) l^0 m^j \overset{*}{m}{}^l n^0 \\ # \psi_1 =&\ (\text{GaussDDDD[i][j][k][l]}) n^i l^j m^k l^l \\ # &+ (\text{CodazziDDD[j][k][l]}) (n^{0} l^{j} m^k l^l - n^{j} l^{0} m^k l^l - n^k l^l m^j l^0) \\ # &- (\text{RojoDD[j][l]}) (n^{0} l^{j} m^l l^0 - n^{j} l^{0} m^l l^0) \\ # \psi_0 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i m^j l^k m^l \\ # &+2 (\text{CodazziDDD[j][k][l]}) (l^0 m^j l^k m^l + l^k m^l l^0 m^j) \\ # &+ (\text{RojoDD[j][l]}) l^0 m^j l^0 m^j. \\ psi4r = sp.sympify(0) psi4i = sp.sympify(0) psi3r = sp.sympify(0) psi3i = sp.sympify(0) psi2r = sp.sympify(0) psi2i = sp.sympify(0) psi1r = sp.sympify(0) psi1i = sp.sympify(0) psi0r = sp.sympify(0) psi0i = sp.sympify(0) for l in range(DIM): for j in range(DIM): psi4r += RojoDD[j][l] * nn * nn * (remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi4i += RojoDD[j][l] * nn * nn * (-remtetU[j] * immtetU[l] - immtetU[j] * remtetU[l]) psi3r += -RojoDD[j][l] * nn * nn * (ntetU[j] - ltetU[j]) * remtetU[l] psi3i += RojoDD[j][l] * nn * nn * (ntetU[j] - ltetU[j]) * immtetU[l] psi2r += -RojoDD[j][l] * nn * nn * (remtetU[l] * remtetU[j] + immtetU[j] * immtetU[l]) psi2i += -RojoDD[j][l] * nn * nn * (immtetU[l] * remtetU[j] - remtetU[j] * immtetU[l]) psi1r += RojoDD[j][l] * nn * nn * (ntetU[j] * remtetU[l] - ltetU[j] * remtetU[l]) psi1i += RojoDD[j][l] * nn * nn * (ntetU[j] * immtetU[l] - ltetU[j] * immtetU[l]) psi0r += RojoDD[j][l] * nn * nn * (remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi0i += RojoDD[j][l] * nn * nn * (remtetU[j] * immtetU[l] + immtetU[j] * remtetU[l]) for l in range(DIM): for j in range(DIM): for k in range(DIM): psi4r += 2 * CodazziDDD[j][k][l] * ntetU[k] * nn * ( remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi4i += 2 * CodazziDDD[j][k][l] * ntetU[k] * nn * ( -remtetU[j] * immtetU[l] - immtetU[j] * remtetU[l]) psi3r += 1 * CodazziDDD[j][k][l] * nn * ( (ntetU[j] - ltetU[j]) * remtetU[k] * ntetU[l] - remtetU[j] * ltetU[k] * ntetU[l]) psi3i += -1 * CodazziDDD[j][k][l] * nn * ( (ntetU[j] - ltetU[j]) * immtetU[k] * ntetU[l] - immtetU[j] * ltetU[k] * ntetU[l]) psi2r += 1 * CodazziDDD[j][k][l] * nn * ( ntetU[l] * (remtetU[j] * remtetU[k] + immtetU[j] * immtetU[k]) - ltetU[k] * (remtetU[j] * remtetU[l] + immtetU[j] * immtetU[l])) psi2i += 1 * CodazziDDD[j][k][l] * nn * ( ntetU[l] * (immtetU[j] * remtetU[k] - remtetU[j] * immtetU[k]) - ltetU[k] * (remtetU[j] * immtetU[l] - immtetU[j] * remtetU[l])) psi1r += 1 * CodazziDDD[j][k][l] * nn * ( ltetU[j] * remtetU[k] * ltetU[l] - remtetU[j] * ntetU[k] * ltetU[l] - ntetU[j] * remtetU[k] * ltetU[l]) psi1i += 1 * CodazziDDD[j][k][l] * nn * ( ltetU[j] * immtetU[k] * ltetU[l] - immtetU[j] * ntetU[k] * ltetU[l] - ntetU[j] * immtetU[k] * ltetU[l]) psi0r += 2 * CodazziDDD[j][k][l] * nn * ltetU[k] * ( remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi0i += 2 * CodazziDDD[j][k][l] * nn * ltetU[k] * ( remtetU[j] * immtetU[l] + immtetU[j] * remtetU[l]) for l in range(DIM): for j in range(DIM): for k in range(DIM): for i in range(DIM): psi4r += GaussDDDD[i][j][k][l] * ntetU[i] * ntetU[k] * ( remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi4i += GaussDDDD[i][j][k][l] * ntetU[i] * ntetU[k] * ( -remtetU[j] * immtetU[l] - immtetU[j] * remtetU[l]) psi3r += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[ j] * remtetU[k] * ntetU[l] psi3i += -GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[ j] * immtetU[k] * ntetU[l] psi2r += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[l] * ( remtetU[j] * remtetU[k] + immtetU[j] * immtetU[k]) psi2i += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[l] * ( immtetU[j] * remtetU[k] - remtetU[j] * immtetU[k]) psi1r += GaussDDDD[i][j][k][l] * ntetU[i] * ltetU[ j] * remtetU[k] * ltetU[l] psi1i += GaussDDDD[i][j][k][l] * ntetU[i] * ltetU[ j] * immtetU[k] * ltetU[l] psi0r += GaussDDDD[i][j][k][l] * ltetU[i] * ltetU[k] * ( remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi0i += GaussDDDD[i][j][k][l] * ltetU[i] * ltetU[k] * ( remtetU[j] * immtetU[l] + immtetU[j] * remtetU[l])
def GiRaFFEfood_HO_Exact_Wald(): # <a id='step2'></a> # # ### Step 2: Set the vectors A and E in Spherical coordinates # $$\label{step2}$$ # # \[Back to [top](#top)\] # # We will first build the fundamental vectors $A_i$ and $E_i$ in spherical coordinates (see [Table 3](https://arxiv.org/pdf/1704.00599.pdf)). Note that we use reference_metric.py to set $r$ and $\theta$ in terms of Cartesian coordinates; this will save us a step later when we convert to Cartesian coordinates. Since $C_0 = 1$, # \begin{align} # A_{\phi} &= \frac{1}{2} r^2 \sin^2 \theta \\ # E_{\phi} &= 2 M \left( 1+ \frac {2M}{r} \right)^{-1/2} \sin^2 \theta. \\ # \end{align} # While we have $E_i$ set as a variable in NRPy+, note that the final C code won't store these values. # Step 2: Set the vectors A and E in Spherical coordinates r = rfm.xxSph[ 0] + KerrSchild_radial_shift # We are setting the data up in Shifted Kerr-Schild coordinates theta = rfm.xxSph[1] IDchoice = par.parval_from_str("IDchoice") # Initialize all components of A and E in the *spherical basis* to zero ASphD = ixp.zerorank1() ESphD = ixp.zerorank1() if IDchoice is "Exact_Wald": ASphD[2] = (r * r * sp.sin(theta)**2) / 2 ESphD[2] = 2 * M * sp.sin(theta)**2 / sp.sqrt(1 + 2 * M / r) else: print("Error: IDchoice == " + par.parval_from_str("IDchoice") + " unsupported!") exit(1) # <a id='step3'></a> # # ### Step 3: Use the Jacobian matrix to transform the vectors to Cartesian coordinates. # $$\label{step3}$$ # # \[Back to [top](#top)\] # # Now, we will use the coordinate transformation definitions provided by reference_metric.py to build the Jacobian # $$ # \frac{\partial x_{\rm Sph}^j}{\partial x_{\rm Cart}^i}, # $$ # where $x_{\rm Sph}^j \in \{r,\theta,\phi\}$ and $x_{\rm Cart}^i \in \{x,y,z\}$. We would normally compute its inverse, but since none of the quantities we need to transform have upper indices, it is not necessary. Then, since both $A_i$ and $E_i$ have one lower index, both will need to be multiplied by the Jacobian: # # $$ # A_i^{\rm Cart} = A_j^{\rm Sph} \frac{\partial x_{\rm Sph}^j}{\partial x_{\rm Cart}^i}, # $$ # Step 3: Use the Jacobian matrix to transform the vectors to Cartesian coordinates. drrefmetric__dx_0UDmatrix = sp.Matrix([[ sp.diff(rfm.xxSph[0], rfm.xx[0]), sp.diff(rfm.xxSph[0], rfm.xx[1]), sp.diff(rfm.xxSph[0], rfm.xx[2]) ], [ sp.diff(rfm.xxSph[1], rfm.xx[0]), sp.diff(rfm.xxSph[1], rfm.xx[1]), sp.diff(rfm.xxSph[1], rfm.xx[2]) ], [ sp.diff(rfm.xxSph[2], rfm.xx[0]), sp.diff(rfm.xxSph[2], rfm.xx[1]), sp.diff(rfm.xxSph[2], rfm.xx[2]) ]]) #dx__drrefmetric_0UDmatrix = drrefmetric__dx_0UDmatrix.inv() # We don't actually need this in this case. global AD AD = ixp.register_gridfunctions_for_single_rank1("EVOL", "AD") ED = ixp.zerorank1() for i in range(DIM): for j in range(DIM): AD[i] = drrefmetric__dx_0UDmatrix[(j, i)] * ASphD[j] ED[i] = drrefmetric__dx_0UDmatrix[(j, i)] * ESphD[j] #Step 4: Register the basic spacetime quantities alpha = gri.register_gridfunctions("AUX", "alpha") betaU = ixp.register_gridfunctions_for_single_rank1("AUX", "betaU", DIM=3) gammaDD = ixp.register_gridfunctions_for_single_rank2("AUX", "gammaDD", "sym01", DIM=3) gammaUU, gammadet = ixp.symm_matrix_inverter3x3(gammaDD) # <a id='step4'></a> # # ### Step 4: Calculate $v^i$ from $A_i$ and $E_i$ # $$\label{step4}$$ # # \[Back to [top](#top)\] # # We will now find the magnetic field using equation 18 in [the original paper](https://arxiv.org/pdf/1704.00599.pdf) $$B^i = \frac{[ijk]}{\sqrt{\gamma}} \partial_j A_k. $$ We will need the metric quantites: the lapse $\alpha$, the shift $\beta^i$, and the three-metric $\gamma_{ij}$. We will also need the Levi-Civita symbol, provided by $\text{WeylScal4NRPy}$. # Step 4: Calculate v^i from A_i and E_i # Step 4a: Calculate the magnetic field B^i # Here, we build the Levi-Civita tensor from the Levi-Civita symbol. # Here, we build the Levi-Civita tensor from the Levi-Civita symbol. import WeylScal4NRPy.WeylScalars_Cartesian as weyl LeviCivitaSymbolDDD = weyl.define_LeviCivitaSymbol_rank3() LeviCivitaTensorUUU = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): LeviCivitaTensorUUU[i][j][ k] = LeviCivitaSymbolDDD[i][j][k] / sp.sqrt(gammadet) # For the initial data, we can analytically take the derivatives of A_i ADdD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): ADdD[i][j] = sp.simplify(sp.diff(AD[i], rfm.xxCart[j])) #global BU BU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): BU[i] += LeviCivitaTensorUUU[i][j][k] * ADdD[k][j] # We will now build the initial velocity using equation 152 in [this paper,](https://arxiv.org/pdf/1310.3274v2.pdf) cited in the original $\texttt{GiRaFFE}$ code: $$ v^i = \alpha \frac{\epsilon^{ijk} E_j B_k}{B^2} -\beta^i. $$ # However, our code needs the Valencia 3-velocity while this expression is for the drift velocity. So, we will need to transform it to the Valencia 3-velocity using the rule $\bar{v}^i = \frac{1}{\alpha} \left(v^i +\beta^i \right)$. # Thus, $$\bar{v}^i = \frac{\epsilon^{ijk} E_j B_k}{B^2}$$ # Step 4b: Calculate B^2 and B_i # B^2 is an inner product defined in the usual way: B2 = sp.sympify(0) for i in range(DIM): for j in range(DIM): B2 += gammaDD[i][j] * BU[i] * BU[j] # Lower the index on B^i BD = ixp.zerorank1() for i in range(DIM): for j in range(DIM): BD[i] += gammaDD[i][j] * BU[j] # Step 4c: Calculate the Valencia 3-velocity global ValenciavU ValenciavU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): ValenciavU[ i] += LeviCivitaTensorUUU[i][j][k] * ED[j] * BD[k] / B2
def RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU(): # Step 7.a: Declare as globals all expressions that may be used # outside this function, declare BSSN gridfunctions # if not defined already, and set DIM=3. global RbarDD, DGammaUDD, gammabarDD_dHatD, DGammaU hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already( ) DIM = 3 # GammabarUDD is used below, defined in # gammabar__inverse_and_derivs() gammabar__inverse_and_derivs() # Step 7.a.i: Define \varepsilon_{ij} = epsDD[i][j] epsDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): epsDD[i][j] = hDD[i][j] * rfm.ReDD[i][j] # Step 7.a.ii: Define epsDD_dD[i][j][k] hDD_dD = ixp.declarerank3("hDD_dD", "sym01") epsDD_dD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): epsDD_dD[i][j][k] = hDD_dD[i][j][k] * rfm.ReDD[i][j] + hDD[i][ j] * rfm.ReDDdD[i][j][k] # Step 7.a.iii: Define epsDD_dDD[i][j][k][l] hDD_dDD = ixp.declarerank4("hDD_dDD", "sym01_sym23") epsDD_dDD = ixp.zerorank4() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): epsDD_dDD[i][j][k][l] = hDD_dDD[i][j][k][l] * rfm.ReDD[i][j] + \ hDD_dD[i][j][k] * rfm.ReDDdD[i][j][l] + \ hDD_dD[i][j][l] * rfm.ReDDdD[i][j][k] + \ hDD[i][j] * rfm.ReDDdDD[i][j][k][l] # Step 7.a.iv: DhatgammabarDDdD[i][j][l] = \bar{\gamma}_{ij;\hat{l}} # \bar{\gamma}_{ij;\hat{l}} = \varepsilon_{i j,l} # - \hat{\Gamma}^m_{i l} \varepsilon_{m j} # - \hat{\Gamma}^m_{j l} \varepsilon_{i m} gammabarDD_dHatD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for l in range(DIM): gammabarDD_dHatD[i][j][l] = epsDD_dD[i][j][l] for m in range(DIM): gammabarDD_dHatD[i][j][l] += - rfm.GammahatUDD[m][i][l] * epsDD[m][j] \ - rfm.GammahatUDD[m][j][l] * epsDD[i][m] # Step 7.a.v: \bar{\gamma}_{ij;\hat{l},k} = DhatgammabarDD_dHatD_dD[i][j][l][k]: # \bar{\gamma}_{ij;\hat{l},k} = \varepsilon_{ij,lk} # - \hat{\Gamma}^m_{i l,k} \varepsilon_{m j} # - \hat{\Gamma}^m_{i l} \varepsilon_{m j,k} # - \hat{\Gamma}^m_{j l,k} \varepsilon_{i m} # - \hat{\Gamma}^m_{j l} \varepsilon_{i m,k} gammabarDD_dHatD_dD = ixp.zerorank4() for i in range(DIM): for j in range(DIM): for l in range(DIM): for k in range(DIM): gammabarDD_dHatD_dD[i][j][l][k] = epsDD_dDD[i][j][l][k] for m in range(DIM): gammabarDD_dHatD_dD[i][j][l][k] += -rfm.GammahatUDDdD[m][i][l][k] * epsDD[m][j] \ - rfm.GammahatUDD[m][i][l] * epsDD_dD[m][j][k] \ - rfm.GammahatUDDdD[m][j][l][k] * epsDD[i][m] \ - rfm.GammahatUDD[m][j][l] * epsDD_dD[i][m][k] # Step 7.a.vi: \bar{\gamma}_{ij;\hat{l}\hat{k}} = DhatgammabarDD_dHatDD[i][j][l][k] # \bar{\gamma}_{ij;\hat{l}\hat{k}} = \partial_k \hat{D}_{l} \varepsilon_{i j} # - \hat{\Gamma}^m_{lk} \left(\hat{D}_{m} \varepsilon_{i j}\right) # - \hat{\Gamma}^m_{ik} \left(\hat{D}_{l} \varepsilon_{m j}\right) # - \hat{\Gamma}^m_{jk} \left(\hat{D}_{l} \varepsilon_{i m}\right) gammabarDD_dHatDD = ixp.zerorank4() for i in range(DIM): for j in range(DIM): for l in range(DIM): for k in range(DIM): gammabarDD_dHatDD[i][j][l][k] = gammabarDD_dHatD_dD[i][j][ l][k] for m in range(DIM): gammabarDD_dHatDD[i][j][l][k] += - rfm.GammahatUDD[m][l][k] * gammabarDD_dHatD[i][j][m] \ - rfm.GammahatUDD[m][i][k] * gammabarDD_dHatD[m][j][l] \ - rfm.GammahatUDD[m][j][k] * gammabarDD_dHatD[i][m][l] # Step 7.b: Second term of RhatDD: compute \hat{D}_{j} \bar{\Lambda}^{k} = LambarU_dHatD[k][j] lambdaU_dD = ixp.declarerank2("lambdaU_dD", "nosym") LambarU_dHatD = ixp.zerorank2() for j in range(DIM): for k in range(DIM): LambarU_dHatD[k][j] = lambdaU_dD[k][j] * rfm.ReU[k] + lambdaU[ k] * rfm.ReUdD[k][j] for m in range(DIM): LambarU_dHatD[k][ j] += rfm.GammahatUDD[k][m][j] * lambdaU[m] * rfm.ReU[m] # Step 7.c: Conformal Ricci tensor, part 3: The \Delta^{k} \Delta_{(i j) k} # + \bar{\gamma}^{k l}*(2 \Delta_{k(i}^{m} \Delta_{j) m l} # + \Delta_{i k}^{m} \Delta_{m j l}) terms # Step 7.c.i: Define \Delta^i_{jk} = \bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk} = DGammaUDD[i][j][k] DGammaUDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): DGammaUDD[i][j][ k] = GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k] # Step 7.c.ii: Define \Delta^i = \bar{\gamma}^{jk} \Delta^i_{jk} DGammaU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): DGammaU[i] += gammabarUU[j][k] * DGammaUDD[i][j][k] # Step 7.c.iii: Define \Delta_{ijk} = \bar{\gamma}_{im} \Delta^m_{jk} DGammaDDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): for m in range(DIM): DGammaDDD[i][j][k] += gammabarDD[i][m] * DGammaUDD[m][j][k] if par.parval_from_str(thismodule + "::LeaveRicciSymbolic") == "True": for i in range(len(gri.glb_gridfcs_list)): if "RbarDD00" in gri.glb_gridfcs_list[i].name: return RbarDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "RbarDD", "sym01") return # Step 7.d: Summing the terms and defining \bar{R}_{ij} # Step 7.d.i: Add the first term to RbarDD: # Rbar_{ij} += - \frac{1}{2} \bar{\gamma}^{k l} \hat{D}_{k} \hat{D}_{l} \bar{\gamma}_{i j} RbarDD = ixp.zerorank2() RbarDDpiece = ixp.zerorank2() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): RbarDD[i][j] += -sp.Rational(1, 2) * gammabarUU[k][ l] * gammabarDD_dHatDD[i][j][l][k] RbarDDpiece[i][j] += -sp.Rational(1, 2) * gammabarUU[k][ l] * gammabarDD_dHatDD[i][j][l][k] # Step 7.d.ii: Add the second term to RbarDD: # Rbar_{ij} += (1/2) * (gammabar_{ki} Lambar^k_{;\hat{j}} + gammabar_{kj} Lambar^k_{;\hat{i}}) for i in range(DIM): for j in range(DIM): for k in range(DIM): RbarDD[i][j] += sp.Rational( 1, 2) * (gammabarDD[k][i] * LambarU_dHatD[k][j] + gammabarDD[k][j] * LambarU_dHatD[k][i]) # Step 7.d.iii: Add the remaining term to RbarDD: # Rbar_{ij} += \Delta^{k} \Delta_{(i j) k} = 1/2 \Delta^{k} (\Delta_{i j k} + \Delta_{j i k}) for i in range(DIM): for j in range(DIM): for k in range(DIM): RbarDD[i][j] += sp.Rational(1, 2) * DGammaU[k] * ( DGammaDDD[i][j][k] + DGammaDDD[j][i][k]) # Step 7.d.iv: Add the final term to RbarDD: # Rbar_{ij} += \bar{\gamma}^{k l} (\Delta^{m}_{k i} \Delta_{j m l} # + \Delta^{m}_{k j} \Delta_{i m l} # + \Delta^{m}_{i k} \Delta_{m j l}) for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): RbarDD[i][j] += gammabarUU[k][l] * ( DGammaUDD[m][k][i] * DGammaDDD[j][m][l] + DGammaUDD[m][k][j] * DGammaDDD[i][m][l] + DGammaUDD[m][i][k] * DGammaDDD[m][j][l])
def ref_metric__hatted_quantities(): # Step 0: Set dimension DIM DIM = par.parval_from_str("grid::DIM") global ReU,ReDD,ghatDD,ghatUU,detgammahat ReU = ixp.zerorank1() ReDD = ixp.zerorank2() ghatDD = ixp.zerorank2() # Step 1: Compute ghatDD (reference metric), ghatUU # (inverse reference metric), as well as # rescaling vector ReU & rescaling matrix ReDD for i in range(DIM): scalefactor_orthog[i] = sp.sympify(scalefactor_orthog[i]) ghatDD[i][i] = scalefactor_orthog[i]**2 ReU[i] = 1/scalefactor_orthog[i] for j in range(DIM): ReDD[i][j] = scalefactor_orthog[i]*scalefactor_orthog[j] # Step 1b: Compute ghatUU ghatUU, detgammahat = ixp.symm_matrix_inverter3x3(ghatDD) # Step 1c: Sanity check: verify that ReDD, ghatDD, # and ghatUU are all symmetric rank-2: for i in range(DIM): for j in range(DIM): if ReDD[i][j] != ReDD[j][i]: print("Error: ReDD["+ str(i) + "][" + str(j) + "] != ReDD["+ str(j) + "][" + str(i) + ": " + str(ReDD[i][j]) + "!=" + str(ReDD[j][i])) exit(1) if ghatDD[i][j] != ghatDD[j][i]: print("Error: ghatDD["+ str(i) + "][" + str(j) + "] != ghatDD["+ str(j) + "][" + str(i) + ": " + str(ghatDD[i][j]) + "!=" + str(ghatDD[j][i])) exit(1) if ghatUU[i][j] != ghatUU[j][i]: print("Error: ghatUU["+ str(i) + "][" + str(j) + "] != ghatUU["+ str(j) + "][" + str(i) + ": " + str(ghatUU[i][j]) + "!=" + str(ghatUU[j][i])) exit(1) # Step 2: Compute det(ghat) and its 1st & 2nd derivatives global detgammahatdD,detgammahatdDD detgammahatdD = ixp.zerorank1(DIM) detgammahatdDD = ixp.zerorank2(DIM) for i in range(DIM): detgammahatdD[i] = (sp.diff(detgammahat, xx[i])) for j in range(DIM): detgammahatdDD[i][j] = sp.diff(detgammahatdD[i], xx[j]) # Step 3a: Compute 1st & 2nd derivatives of rescaling vector. # (E.g., needed in BSSN for betaUdDD computation) global ReUdD,ReUdDD ReUdD = ixp.zerorank2(DIM) ReUdDD = ixp.zerorank3(DIM) for i in range(DIM): for j in range(DIM): ReUdD[i][j] = sp.diff(ReU[i], xx[j]) for k in range(DIM): ReUdDD[i][j][k] = sp.diff(ReUdD[i][j], xx[k]) # Step 3b: Compute 1st & 2nd derivatives of rescaling matrix. global ReDDdD,ReDDdDD ReDDdD = ixp.zerorank3(DIM) ReDDdDD = ixp.zerorank4(DIM) for i in range(DIM): for j in range(DIM): for k in range(DIM): ReDDdD[i][j][k] = (sp.diff(ReDD[i][j],xx[k])) for l in range(DIM): # Simplifying this doesn't appear to help overall NRPy run time. ReDDdDD[i][j][k][l] = sp.diff(ReDDdD[i][j][k],xx[l]) # Step 3c: Compute 1st & 2nd derivatives of reference metric. global ghatDDdD,ghatDDdDD ghatDDdD = ixp.zerorank3(DIM) ghatDDdDD = ixp.zerorank4(DIM) for i in range(DIM): for j in range(DIM): for k in range(DIM): ghatDDdD[i][j][k] = sp.simplify(sp.diff(ghatDD[i][j],xx[k])) # FIXME: BAD: MUST BE SIMPLIFIED OR ANSWER IS INCORRECT! Must be some bug in sympy... for l in range(DIM): ghatDDdDD[i][j][k][l] = (sp.diff(ghatDDdD[i][j][k],xx[l])) # Step 4a: Compute Christoffel symbols of reference metric. global GammahatUDD GammahatUDD = ixp.zerorank3(DIM) for i in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): GammahatUDD[i][k][l] += (sp.Rational(1,2))*ghatUU[i][m]*\ (ghatDDdD[m][k][l] + ghatDDdD[m][l][k] - ghatDDdD[k][l][m]) # Step 4b: Compute derivs of Christoffel symbols of reference metric. global GammahatUDDdD GammahatUDDdD = ixp.zerorank4(DIM) for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): GammahatUDDdD[i][j][k][l] = (sp.diff(GammahatUDD[i][j][k],xx[l]))
def GiRaFFE_Higher_Order_v2(): #Step 1.0: Set the spatial dimension parameter to 3. par.set_parval_from_str("grid::DIM", 3) DIM = par.parval_from_str("grid::DIM") # Step 1.1: Set the finite differencing order to 4. #par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", 4) thismodule = "GiRaFFE_NRPy" # M_PI will allow the C code to substitute the correct value M_PI = par.Cparameters("#define",thismodule,"M_PI","") # ADMBase defines the 4-metric in terms of the 3+1 spacetime metric quantities gamma_{ij}, beta^i, and alpha gammaDD = ixp.register_gridfunctions_for_single_rank2("AUX","gammaDD", "sym01",DIM=3) betaU = ixp.register_gridfunctions_for_single_rank1("AUX","betaU",DIM=3) alpha = gri.register_gridfunctions("AUX","alpha") # GiRaFFE uses the Valencia 3-velocity and A_i, which are defined in the initial data module(GiRaFFEfood) ValenciavU = ixp.register_gridfunctions_for_single_rank1("AUX","ValenciavU",DIM=3) AD = ixp.register_gridfunctions_for_single_rank1("EVOL","AD",DIM=3) # B^i must be computed at each timestep within GiRaFFE so that the Valencia 3-velocity can be evaluated BU = ixp.register_gridfunctions_for_single_rank1("AUX","BU",DIM=3) # <a id='step3'></a> # # ## Step 1.2: Build the four metric $g_{\mu\nu}$, its inverse $g^{\mu\nu}$ and spatial derivatives $g_{\mu\nu,i}$ from ADM 3+1 quantities $\gamma_{ij}$, $\beta^i$, and $\alpha$ \[Back to [top](#top)\] # # Notice that the time evolution equation for $\tilde{S}_i$ # $$ # \partial_t \tilde{S}_i = - \partial_j \left( \alpha \sqrt{\gamma} T^j_{{\rm EM} i} \right) + \frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu} # $$ # contains $\partial_i g_{\mu \nu} = g_{\mu\nu,i}$. We will now focus on evaluating this term. # # The four-metric $g_{\mu\nu}$ is related to the three-metric $\gamma_{ij}$, index-lowered shift $\beta_i$, and lapse $\alpha$ by # $$ # g_{\mu\nu} = \begin{pmatrix} # -\alpha^2 + \beta^k \beta_k & \beta_j \\ # \beta_i & \gamma_{ij} # \end{pmatrix}. # $$ # This tensor and its inverse have already been built by the u0_smallb_Poynting__Cartesian.py module ([documented here](Tutorial-u0_smallb_Poynting-Cartesian.ipynb)), so we can simply load the module and import the variables. # $$\label{step3}$$ # Step 1.2: import u0_smallb_Poynting__Cartesian.py to set # the four metric and its inverse. This module also sets b^2 and u^0. import u0_smallb_Poynting__Cartesian.u0_smallb_Poynting__Cartesian as u0b u0b.compute_u0_smallb_Poynting__Cartesian(gammaDD,betaU,alpha,ValenciavU,BU) betaD = ixp.zerorank1() for i in range(DIM): for j in range(DIM): betaD[i] += gammaDD[i][j] * betaU[j] # Error check: fixed = to += # We will now pull in the four metric and its inverse. import BSSN.ADMBSSN_tofrom_4metric as AB4m # NRPy+: ADM/BSSN <-> 4-metric conversions AB4m.g4DD_ito_BSSN_or_ADM("ADM") g4DD = AB4m.g4DD AB4m.g4UU_ito_BSSN_or_ADM("ADM") g4UU = AB4m.g4UU # Next we compute spatial derivatives of the metric, $\partial_i g_{\mu\nu} = g_{\mu\nu,i}$, written in terms of the three-metric, shift, and lapse. Simply taking the derivative of the expression for $g_{\mu\nu}$ above, we find # $$ # g_{\mu\nu,l} = \begin{pmatrix} # -2\alpha \alpha_{,l} + \beta^k_{\ ,l} \beta_k + \beta^k \beta_{k,l} & \beta_{j,l} \\ # \beta_{i,l} & \gamma_{ij,l} # \end{pmatrix}. # $$ # # Notice the derivatives of the shift vector with its indexed lowered, $\beta_{i,j} = \partial_j \beta_i$. This can be easily computed in terms of the given ADMBase quantities $\beta^i$ and $\gamma_{ij}$ via: # \begin{align} # \beta_{i,j} &= \partial_j \beta_i \\ # &= \partial_j (\gamma_{ik} \beta^k) \\ # &= \gamma_{ik} \partial_j\beta^k + \beta^k \partial_j \gamma_{ik} \\ # \beta_{i,j} &= \gamma_{ik} \beta^k_{\ ,j} + \beta^k \gamma_{ik,j}. # \end{align} # # Since this expression mixes Greek and Latin indices, we will declare this as a 4 dimensional quantity, but only set the three spatial components of its last index (that is, leaving $l=0$ unset). # # So, we will first set # $$ g_{00,l} = \underbrace{-2\alpha \alpha_{,l}}_{\rm Term\ 1} + \underbrace{\beta^k_{\ ,l} \beta_k}_{\rm Term\ 2} + \underbrace{\beta^k \beta_{k,l}}_{\rm Term\ 3} $$ # Step 1.2, cont'd: Build spatial derivatives of the four metric # Step 1.2.a: Declare derivatives of grid functions. These will be handled by FD_outputC alpha_dD = ixp.declarerank1("alpha_dD") betaU_dD = ixp.declarerank2("betaU_dD","nosym") gammaDD_dD = ixp.declarerank3("gammaDD_dD","sym01") # Step 1.2.b: These derivatives will be constructed analytically. betaDdD = ixp.zerorank2() g4DDdD = ixp.zerorank3(DIM=4) for i in range(DIM): for j in range(DIM): for k in range(DIM): # \gamma_{ik} \beta^k_{,j} + \beta^k \gamma_{ik,j} betaDdD[i][j] += gammaDD[i][k] * betaU_dD[k][j] + betaU[k] * gammaDD_dD[i][k][j] #Error check: changed = to += above # Step 1.2.c: Set the 00 components # Step 1.2.c.i: Term 1: -2\alpha \alpha_{,l} for l in range(DIM): g4DDdD[0][0][l+1] = -2*alpha*alpha_dD[l] # Step 1.2.c.ii: Term 2: \beta^k_{\ ,l} \beta_k for l in range(DIM): for k in range(DIM): g4DDdD[0][0][l+1] += betaU_dD[k][l] * betaD[k] # Step 1.2.c.iii: Term 3: \beta^k \beta_{k,l} for l in range(DIM): for k in range(DIM): g4DDdD[0][0][l+1] += betaU[k] * betaDdD[k][l] # Now we will contruct the other components of $g_{\mu\nu,l}$. We will first construct # $$ g_{i0,l} = g_{0i,l} = \beta_{i,l}, $$ # then # $$ g_{ij,l} = \gamma_{ij,l} $$ # Step 1.2.d: Set the i0 and 0j components for l in range(DIM): for i in range(DIM): # \beta_{i,l} g4DDdD[i+1][0][l+1] = g4DDdD[0][i+1][l+1] = betaDdD[i][l] #Step 1.2.e: Set the ij components for l in range(DIM): for i in range(DIM): for j in range(DIM): # \gamma_{ij,l} g4DDdD[i+1][j+1][l+1] = gammaDD_dD[i][j][l] # <a id='step4'></a> # # # $T^{\mu\nu}_{\rm EM}$ and its derivatives # # Now that the metric and its derivatives are out of the way, let's return to the evolution equation for $\tilde{S}_i$, # $$ # \partial_t \tilde{S}_i = - \partial_j \left( \alpha \sqrt{\gamma} T^j_{{\rm EM} i} \right) + \frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu}. # $$ # We turn our focus now to $T^j_{{\rm EM} i}$ and its derivatives. To this end, we start by computing $T^{\mu \nu}_{\rm EM}$ (from eq. 27 of [Paschalidis & Shapiro's paper on their GRFFE code](https://arxiv.org/pdf/1310.3274.pdf)): # # $$\boxed{T^{\mu \nu}_{\rm EM} = b^2 u^{\mu} u^{\nu} + \frac{b^2}{2} g^{\mu \nu} - b^{\mu} b^{\nu}.}$$ # # Notice that $T^{\mu\nu}_{\rm EM}$ is written in terms of # # * $b^\mu$, the 4-component magnetic field vector, related to the comoving magnetic field vector $B^i_{(u)}$ # * $u^\mu$, the 4-velocity # * $g^{\mu \nu}$, the inverse 4-metric # # However, $\texttt{GiRaFFE}$ has access to only the following quantities, requiring in the following sections that we write the above quantities in terms of the following ones: # # * $\gamma_{ij}$, the 3-metric # * $\alpha$, the lapse # * $\beta^i$, the shift # * $A_i$, the vector potential # * $B^i$, the magnetic field (we assume only in the grid interior, not the ghost zones) # * $\left[\sqrt{\gamma}\Phi\right]$, the zero-component of the vector potential $A_\mu$, times the square root of the determinant of the 3-metric # * $v_{(n)}^i$, the Valencia 3-velocity # * $u^0$, the zero-component of the 4-velocity # # ## Step 2.0: $u^i$ and $b^i$ and related quantities \[Back to [top](#top)\] # # We begin by importing what we can from u0_smallb_Poynting__Cartesian.py. We will need the four-velocity $u^\mu$, which is related to the Valencia 3-velocity $v^i_{(n)}$ used directly by $\texttt{GiRaFFE}$ (see also [Duez, et al, eqs. 53 and 56](https://arxiv.org/pdf/astro-ph/0503420.pdf)) # \begin{align} # u^i &= u^0 (\alpha v^i_{(n)} - \beta^i), \\ # u_j &= \alpha u^0 \gamma_{ij} v^i_{(n)}, # \end{align} # where $v^i_{(n)}$ is the Valencia three-velocity. These have already been constructed in terms of the Valencia 3-velocity and other 3+1 ADM quantities by the u0_smallb_Poynting__Cartesian.py module, so we can simply import these variables: # $$\label{step4}$$ # Step 2.0: u^i, b^i, and related quantities # Step 2.0.a: import the four-velocity, as written in terms of the Valencia 3-velocity uD = ixp.zerorank1() uU = ixp.zerorank1() u4upperZero = gri.register_gridfunctions("AUX","u4upperZero") for i in range(DIM): uD[i] = u0b.uD[i].subs(u0b.u0,u4upperZero) uU[i] = u0b.uU[i].subs(u0b.u0,u4upperZero) # We also need the magnetic field 4-vector $b^{\mu}$, which is related to the magnetic field by [eqs. 23, 24, and 31 in Duez, et al](https://arxiv.org/pdf/astro-ph/0503420.pdf): # \begin{align} # b^0 &= \frac{1}{\sqrt{4\pi}} B^0_{\rm (u)} = \frac{u_j B^j}{\sqrt{4\pi}\alpha}, \\ # b^i &= \frac{1}{\sqrt{4\pi}} B^i_{\rm (u)} = \frac{B^i + (u_j B^j) u^i}{\sqrt{4\pi}\alpha u^0}, \\ # \end{align} # where $B^i$ is the variable tracked by the HydroBase thorn in the Einstein Toolkit. Again, these have already been built by the u0_smallb_Poynting__Cartesian.py module, so we can simply import the variables. # Step 2.0.b: import the small b terms smallb4U = ixp.zerorank1(DIM=4) smallb4D = ixp.zerorank1(DIM=4) for mu in range(4): smallb4U[mu] = u0b.smallb4U[mu].subs(u0b.u0,u4upperZero) smallb4D[mu] = u0b.smallb4D[mu].subs(u0b.u0,u4upperZero) smallb2 = u0b.smallb2etk.subs(u0b.u0,u4upperZero) # <a id='step5'></a> # # ## Step 2.1: Construct all components of the electromagnetic stress-energy tensor $T^{\mu \nu}_{\rm EM}$ \[Back to [top](#top)\] # # We now have all the pieces to calculate the stress-energy tensor, # $$T^{\mu \nu}_{\rm EM} = \underbrace{b^2 u^{\mu} u^{\nu}}_{\rm Term\ 1} + # \underbrace{\frac{b^2}{2} g^{\mu \nu}}_{\rm Term\ 2} # - \underbrace{b^{\mu} b^{\nu}}_{\rm Term\ 3}.$$ # Because $u^0$ is a separate variable, we could build the $00$ component separately, then the $\mu0$ and $0\nu$ components, and finally the $\mu\nu$ components. Alternatively, for clarity, we could create a temporary variable $u^\mu=\left( u^0, u^i \right)$ # $$\label{step5}$$ # Step 2.1: Construct the electromagnetic stress-energy tensor # Step 2.1.a: Set up the four-velocity vector u4U = ixp.zerorank1(DIM=4) u4U[0] = u4upperZero for i in range(DIM): u4U[i+1] = uU[i] # Step 2.1.b: Build T4EMUU itself T4EMUU = ixp.zerorank2(DIM=4) for mu in range(4): for nu in range(4): # Term 1: b^2 u^{\mu} u^{\nu} T4EMUU[mu][nu] = smallb2*u4U[mu]*u4U[nu] for mu in range(4): for nu in range(4): # Term 2: b^2 / 2 g^{\mu \nu} T4EMUU[mu][nu] += smallb2*g4UU[mu][nu]/2 for mu in range(4): for nu in range(4): # Term 3: -b^{\mu} b^{\nu} T4EMUU[mu][nu] += -smallb4U[mu]*smallb4U[nu] # # # Evolution equation for $\tilde{S}_i$ # <a id='step7'></a> # # ## Step 3.0: Construct the evolution equation for $\tilde{S}_i$ \[Back to [top](#top)\] # # Finally, we will return our attention to the time evolution equation (from eq. 13 of the [original paper](https://arxiv.org/pdf/1704.00599.pdf)), # \begin{align} # \partial_t \tilde{S}_i &= - \partial_j \underbrace{\left( \alpha \sqrt{\gamma} T^j_{{\rm EM} i} \right)}_{\rm SevolParenUD} + \frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu} \\ # &= - \partial_j{\rm SevolParenUD[i][j]} + \frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu} . # \end{align} # We will first take construct ${\rm SevolParenUD}$, then use its derivatives to construct the evolution equation. Note that # \begin{align} # {\rm SevolParenUD[i][j]} &= \alpha \sqrt{\gamma} T^j_{{\rm EM} i} \\ # &= \alpha \sqrt{\gamma} g_{\mu i} T^{\mu j}_{\rm EM}. # \end{align} # $$\label{step7}$$ # Step 3.0: Construct the evolution equation for \tilde{S}_i # Here, we set up the necessary machinery to take FD derivatives of alpha * sqrt(gamma) global gammaUU,gammadet gammaUU = ixp.register_gridfunctions_for_single_rank2("AUX","gammaUU","sym01") gammadet = gri.register_gridfunctions("AUX","gammadet") gammaUU, gammadet = ixp.symm_matrix_inverter3x3(gammaDD) alpsqrtgam = alpha*sp.sqrt(gammadet) global SevolParenUD SevolParenUD = ixp.register_gridfunctions_for_single_rank2("AUX","SevolParenUD","nosym") SevolParenUD = ixp.zerorank2() for i in range(DIM): for j in range (DIM): for mu in range(4): SevolParenUD[j][i] += alpsqrtgam * g4DD[mu][i+1] * T4EMUU[mu][j+1] SevolParenUD_dD = ixp.declarerank3("SevolParenUD_dD","nosym") global Stilde_rhsD Stilde_rhsD = ixp.zerorank1() # The first term: \alpha \sqrt{\gamma} \partial_j T^j_{{\rm EM} i} for i in range(DIM): for j in range(DIM): Stilde_rhsD[i] += -SevolParenUD_dD[j][i][j] # The second term: \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu} / 2 for i in range(DIM): for mu in range(4): for nu in range(4): Stilde_rhsD[i] += alpsqrtgam * T4EMUU[mu][nu] * g4DDdD[mu][nu][i+1] / 2 # # Evolution equations for $A_i$ and $\Phi$ # <a id='step8'></a> # # ## Step 4.0: Construct the evolution equations for $A_i$ and $[\sqrt{\gamma}\Phi]$ \[Back to [top](#top)\] # # We will also need to evolve the vector potential $A_i$. This evolution is given as eq. 17 in the [$\texttt{GiRaFFE}$](https://arxiv.org/pdf/1704.00599.pdf) paper: # $$\boxed{\partial_t A_i = \epsilon_{ijk} v^j B^k - \partial_i (\underbrace{\alpha \Phi - \beta^j A_j}_{\rm AevolParen}),}$$ # where $\epsilon_{ijk} = [ijk] \sqrt{\gamma}$ is the antisymmetric Levi-Civita tensor, the drift velocity $v^i = u^i/u^0$, $\gamma$ is the determinant of the three metric, $B^k$ is the magnetic field, $\alpha$ is the lapse, and $\beta$ is the shift. # The scalar electric potential $\Phi$ is also evolved by eq. 19: # $$\boxed{\partial_t [\sqrt{\gamma} \Phi] = -\partial_j (\underbrace{\alpha\sqrt{\gamma} \gamma^{ij} A_i - \beta^j [\sqrt{\gamma} \Phi]}_{\rm PevolParenU[j]}) - \xi \alpha [\sqrt{\gamma} \Phi],}$$ # with $\xi$ chosen as a damping factor. # # ### Step 4.0.a: Construct some useful auxiliary gridfunctions for the other evolution equations # # After declaring a some needed quantities, we will also define the parenthetical terms (underbrace above) that we need to take derivatives of. That way, we can take finite-difference derivatives easily. Note that the above equations incorporate the fact that $\gamma^{ij}$ is the appropriate raising operator for $A_i$: $A^j = \gamma^{ij} A_i$. This is correct since $n_\mu A^\mu = 0$, where $n_\mu$ is a normal to the hypersurface, so $A^0=0$ (according to Sec. II, subsection C of [the "Improved EM gauge condition" paper of Etienne *et al*](https://arxiv.org/pdf/1110.4633.pdf)). # $$\label{step8}$$ # Step 4.0: Construct the evolution equations for A_i and sqrt(gamma)Phi # Step 4.0.a: Construct some useful auxiliary gridfunctions for the other evolution equations xi = par.Cparameters("REAL",thismodule,"xi", 0.1) # The (dimensionful) Lorenz damping factor. Recommendation: set to ~1.5/max(delta t). # Define sqrt(gamma)Phi as psi6Phi psi6Phi = gri.register_gridfunctions("EVOL","psi6Phi") Phi = psi6Phi / sp.sqrt(gammadet) # We'll define a few extra gridfunctions to avoid complicated derivatives global AevolParen,PevolParenU AevolParen = gri.register_gridfunctions("AUX","AevolParen") PevolParenU = ixp.register_gridfunctions_for_single_rank1("AUX","PevolParenU") # {\rm AevolParen} = \alpha \Phi - \beta^j A_j AevolParen = alpha*Phi for j in range(DIM): AevolParen += -betaU[j] * AD[j] # {\rm PevolParenU[j]} = \alpha\sqrt{\gamma} \gamma^{ij} A_i - \beta^j [\sqrt{\gamma} \Phi] for j in range(DIM): PevolParenU[j] = -betaU[j] * psi6Phi for i in range(DIM): PevolParenU[j] += alpsqrtgam * gammaUU[i][j] * AD[i] AevolParen_dD = ixp.declarerank1("AevolParen_dD") PevolParenU_dD = ixp.declarerank2("PevolParenU_dD","nosym") # ### Step 4.0.b: Complete the construction of the evolution equations for $A_i$ and $[\sqrt{\gamma}\Phi]$ # # Now to set the evolution equations ([eqs. 17 and 19](https://arxiv.org/pdf/1704.00599.pdf)), recalling that the drift velocity $v^i = u^i/u^0$: # \begin{align} # \partial_t A_i &= \epsilon_{ijk} v^j B^k - \partial_i (\alpha \Phi - \beta^j A_j) \\ # &= \epsilon_{ijk} \frac{u^j}{u^0} B^k - {\rm AevolParen\_dD[i]} \\ # \partial_t [\sqrt{\gamma} \Phi] &= -\partial_j \left(\left(\alpha\sqrt{\gamma}\right)A^j - \beta^j [\sqrt{\gamma} \Phi]\right) - \xi \alpha [\sqrt{\gamma} \Phi] \\ # &= -{\rm PevolParenU\_dD[j][j]} - \xi \alpha [\sqrt{\gamma} \Phi]. \\ # \end{align} # Step 4.0.b: Construct the actual evolution equations for A_i and sqrt(gamma)Phi global A_rhsD,psi6Phi_rhs A_rhsD = ixp.zerorank1() psi6Phi_rhs = sp.sympify(0) # We already have a handy function to define the Levi-Civita symbol in WeylScalars import WeylScal4NRPy.WeylScalars_Cartesian as weyl # Initialize the Levi-Civita tensor by setting it equal to the Levi-Civita symbol LeviCivitaSymbolDDD = weyl.define_LeviCivitaSymbol_rank3() LeviCivitaTensorDDD = ixp.zerorank3() #LeviCivitaTensorUUU = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): LeviCivitaTensorDDD[i][j][k] = LeviCivitaSymbolDDD[i][j][k] * sp.sqrt(gammadet) #LeviCivitaTensorUUU[i][j][k] = LeviCivitaSymbolDDD[i][j][k] / sp.sqrt(gammadet) for i in range(DIM): A_rhsD[i] = -AevolParen_dD[i] for j in range(DIM): for k in range(DIM): A_rhsD[i] += LeviCivitaTensorDDD[i][j][k]*(uU[j]/u4upperZero)*BU[k] psi6Phi_rhs = -xi*alpha*psi6Phi for j in range(DIM): psi6Phi_rhs += -PevolParenU_dD[j][j]
def Psi4(): global psi4_im, psi4_re # Step 1.b: Given the chosen coordinate system, set up # corresponding reference metric and needed # reference metric quantities # The following function call sets up the reference metric # and related quantities, including rescaling matrices ReDD, # ReU, and hatted quantities. rfm.reference_metric() # Step 1.c: Set spatial dimension (must be 3 for BSSN, as BSSN is # a 3+1-dimensional decomposition of the general # relativistic field equations) DIM = 3 # Step 1.d: Import all ADM quantities as written in terms of BSSN quantities import BSSN.ADM_in_terms_of_BSSN as AB AB.ADM_in_terms_of_BSSN() # Step 2: Construct the (rank-4) Riemann curvature tensor associated with the ADM 3-metric: RDDDD = ixp.zerorank4() gammaDDdDD = AB.gammaDDdDD for i in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): RDDDD[i][k][l][m] = sp.Rational(1, 2) * \ (gammaDDdDD[i][m][k][l] + gammaDDdDD[k][l][i][m] - gammaDDdDD[i][l][k][m] - gammaDDdDD[k][m][i][l]) # ... then we add the term on the right: gammaDD = AB.gammaDD GammaUDD = AB.GammaUDD for i in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): for n in range(DIM): for p in range(DIM): RDDDD[i][k][l][m] += gammaDD[n][p] * \ (GammaUDD[n][k][l] * GammaUDD[p][i][m] - GammaUDD[n][k][m] * GammaUDD[p][i][l]) # Step 3: Construct the (rank-4) tensor in term 1 of psi_4 (referring to Eq 5.1 in # Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf rank4term1 = ixp.zerorank4() KDD = AB.KDD for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): rank4term1[i][j][k][l] = RDDDD[i][j][k][ l] + KDD[i][k] * KDD[l][j] - KDD[i][l] * KDD[k][j] # Step 4: Construct the (rank-3) tensor in term 2 of psi_4 (referring to Eq 5.1 in # Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf rank3term2 = ixp.zerorank3() KDDdD = AB.KDDdD for j in range(DIM): for k in range(DIM): for l in range(DIM): rank3term2[j][k][l] = sp.Rational( 1, 2) * (KDDdD[j][k][l] - KDDdD[j][l][k]) # ... then we construct the second term in this sum: # \Gamma^{p}_{j[k} K_{l]p} = \frac{1}{2} (\Gamma^{p}_{jk} K_{lp}-\Gamma^{p}_{jl} K_{kp}): for j in range(DIM): for k in range(DIM): for l in range(DIM): for p in range(DIM): rank3term2[j][k][l] += sp.Rational( 1, 2) * (GammaUDD[p][j][k] * KDD[l][p] - GammaUDD[p][j][l] * KDD[k][p]) # Finally, we multiply the term by $-8$: for j in range(DIM): for k in range(DIM): for l in range(DIM): rank3term2[j][k][l] *= sp.sympify(-8) # Step 5: Construct the (rank-2) tensor in term 3 of psi_4 (referring to Eq 5.1 in # Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf rank2term3 = ixp.zerorank2() gammaUU = AB.gammaUU for j in range(DIM): for l in range(DIM): for i in range(DIM): for m in range(DIM): rank2term3[j][l] += gammaUU[i][m] * RDDDD[i][j][m][l] # ... then we add on the second term in parentheses, where $K^p_l = \gamma^{mp} K_{ml}$ for j in range(DIM): for l in range(DIM): for m in range(DIM): for p in range(DIM): rank2term3[j][l] += -KDD[j][p] * gammaUU[p][m] * KDD[m][l] # Finally we add the third term in parentheses, and multiply all terms by $+4$: for j in range(DIM): for l in range(DIM): for i in range(DIM): for m in range(DIM): rank2term3[j][l] += gammaUU[i][m] * KDD[i][m] * KDD[j][l] for j in range(DIM): for l in range(DIM): rank2term3[j][l] *= sp.sympify(4) mre4U = ixp.declarerank1("mre4U", DIM=4) mim4U = ixp.declarerank1("mim4U", DIM=4) n4U = ixp.declarerank1("n4U", DIM=4) def tetrad_product__Real_psi4(n, Mre, Mim, mu, nu, eta, delta): return +n[mu] * Mre[nu] * n[eta] * Mre[delta] - n[mu] * Mim[nu] * n[ eta] * Mim[delta] def tetrad_product__Imag_psi4(n, Mre, Mim, mu, nu, eta, delta): return -n[mu] * Mre[nu] * n[eta] * Mim[delta] - n[mu] * Mim[nu] * n[ eta] * Mre[delta] psi4_re = sp.sympify(0) psi4_im = sp.sympify(0) # First term: for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): psi4_re += rank4term1[i][j][ k][l] * tetrad_product__Real_psi4( n4U, mre4U, mim4U, i + 1, j + 1, k + 1, l + 1) psi4_im += rank4term1[i][j][ k][l] * tetrad_product__Imag_psi4( n4U, mre4U, mim4U, i + 1, j + 1, k + 1, l + 1) # Second term: for j in range(DIM): for k in range(DIM): for l in range(DIM): psi4_re += rank3term2[j][k][l] * \ sp.Rational(1, 2) * (+tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, k + 1, l + 1) - tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, k + 1, l + 1)) psi4_im += rank3term2[j][k][l] * \ sp.Rational(1, 2) * (+tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, k + 1, l + 1) - tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, k + 1, l + 1)) # Third term: for j in range(DIM): for l in range(DIM): psi4_re += rank2term3[j][l] * \ (sp.Rational(1, 4) * (+tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, 0, l + 1) - tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, 0, l + 1) - tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, l + 1, 0) + tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, l + 1, 0))) psi4_im += rank2term3[j][l] * \ (sp.Rational(1, 4) * (+tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, 0, l + 1) - tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, 0, l + 1) - tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, l + 1, 0) + tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, l + 1, 0)))
def VacuumMaxwellRHSs_rescaled(): global erhsU, arhsU, psi_rhs, Gamma_rhs, C, G, EU_Cart, AU_Cart #Step 0: Set the spatial dimension parameter to 3. par.set_parval_from_str("grid::DIM", 3) DIM = par.parval_from_str("grid::DIM") # Set reference metric related quantities rfm.reference_metric() # Register gridfunctions that are needed as input. # Declare the rank-1 indexed expressions E^{i}, A^{i}, # and \partial^{i} \psi, that are to be evolved in time. # Derivative variables like these must have an underscore # in them, so the finite difference module can parse # the variable name properly. # e^i eU = ixp.register_gridfunctions_for_single_rank1("EVOL", "eU") # \partial_k ( E^i ) --> rank two tensor eU_dD = ixp.declarerank2("eU_dD", "nosym") # a^i aU = ixp.register_gridfunctions_for_single_rank1("EVOL", "aU") # \partial_k ( a^i ) --> rank two tensor aU_dD = ixp.declarerank2("aU_dD", "nosym") # \partial_k partial_m ( a^i ) --> rank three tensor aU_dDD = ixp.declarerank3("aU_dDD", "sym12") # \psi is a scalar function that is time evolved # psi is unused here _psi = gri.register_gridfunctions("EVOL", ["psi"]) # \Gamma is a scalar function that is time evolved Gamma = gri.register_gridfunctions("EVOL", ["Gamma"]) # \partial_i \psi psi_dD = ixp.declarerank1("psi_dD") # \partial_i \Gamma Gamma_dD = ixp.declarerank1("Gamma_dD") # partial_i \partial_j \psi psi_dDD = ixp.declarerank2("psi_dDD", "sym01") ghatUU = rfm.ghatUU GammahatUDD = rfm.GammahatUDD GammahatUDDdD = rfm.GammahatUDDdD ReU = rfm.ReU ReUdD = rfm.ReUdD ReUdDD = rfm.ReUdDD # \partial_t a^i = -e^i - \frac{\hat{g}^{ij}\partial_j \varphi}{\text{ReU}[i]} arhsU = ixp.zerorank1() for i in range(DIM): arhsU[i] -= eU[i] for j in range(DIM): arhsU[i] -= (ghatUU[i][j] * psi_dD[j]) / ReU[i] # A^i AU = ixp.zerorank1() # \partial_k ( A^i ) --> rank two tensor AU_dD = ixp.zerorank2() # \partial_k partial_m ( A^i ) --> rank three tensor AU_dDD = ixp.zerorank3() for i in range(DIM): AU[i] = aU[i] * ReU[i] for j in range(DIM): AU_dD[i][j] = aU_dD[i][j] * ReU[i] + aU[i] * ReUdD[i][j] for k in range(DIM): AU_dDD[i][j][k] = aU_dDD[i][j][k]*ReU[i] + aU_dD[i][j]*ReUdD[i][k] +\ aU_dD[i][k]*ReUdD[i][j] + aU[i]*ReUdDD[i][j][k] # Term 1 = \hat{g}^{ij}\partial_j \Gamma Term1U = ixp.zerorank1() for i in range(DIM): for j in range(DIM): Term1U[i] += ghatUU[i][j] * Gamma_dD[j] # Term 2: A^i_{,kj} Term2UDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): Term2UDD[i][j][k] += AU_dDD[i][k][j] # Term 3: \hat{\Gamma}^i_{mk,j} A^m + \hat{\Gamma}^i_{mk} A^m_{,j} # + \hat{\Gamma}^i_{dj}A^d_{,k} - \hat{\Gamma}^d_{kj} A^i_{,d} Term3UDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): for m in range(DIM): Term3UDD[i][j][k] += GammahatUDDdD[i][m][k][j]*AU[m] \ + GammahatUDD[i][m][k]*AU_dD[m][j] \ + GammahatUDD[i][m][j]*AU_dD[m][k] \ - GammahatUDD[m][k][j]*AU_dD[i][m] # Term 4: \hat{\Gamma}^i_{dj}\hat{\Gamma}^d_{mk} A^m - # \hat{\Gamma}^d_{kj} \hat{\Gamma}^i_{md} A^m Term4UDD = 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): Term4UDD[i][j][k] += ( GammahatUDD[i][d][j]*GammahatUDD[d][m][k] \ -GammahatUDD[d][k][j]*GammahatUDD[i][m][d])*AU[m] # \partial_t E^i = \hat{g}^{ij}\partial_j \Gamma - \hat{\gamma}^{jk}* # (A^i_{,kj} # + \hat{\Gamma}^i_{mk,j} A^m + \hat{\Gamma}^i_{mk} A^m_{,j} # + \hat{\Gamma}^i_{dj} A^d_{,k} - \hat{\Gamma}^d_{kj} A^i_{,d} # + \hat{\Gamma}^i_{dj}\hat{\Gamma}^d_{mk} A^m # - \hat{\Gamma}^d_{kj} \hat{\Gamma}^i_{md} A^m) ErhsU = ixp.zerorank1() for i in range(DIM): ErhsU[i] += Term1U[i] for j in range(DIM): for k in range(DIM): ErhsU[i] -= ghatUU[j][k] * ( Term2UDD[i][j][k] + Term3UDD[i][j][k] + Term4UDD[i][j][k]) erhsU = ixp.zerorank1() for i in range(DIM): erhsU[i] = ErhsU[i] / ReU[i] # \partial_t \Gamma = -\hat{g}^{ij} (\partial_i \partial_j \varphi - # \hat{\Gamma}^k_{ji} \partial_k \varphi) Gamma_rhs = sp.sympify(0) for i in range(DIM): for j in range(DIM): Gamma_rhs -= ghatUU[i][j] * psi_dDD[i][j] for k in range(DIM): Gamma_rhs += ghatUU[i][j] * GammahatUDD[k][j][i] * psi_dD[k] # \partial_t \varphi = -\Gamma psi_rhs = -Gamma # \mathcal{G} \equiv \Gamma - \partial_i A^i + \hat{\Gamma}^i_{ji} A^j G = Gamma for i in range(DIM): G -= AU_dD[i][i] for j in range(DIM): G += GammahatUDD[i][j][i] * AU[j] # E^i EU = ixp.zerorank1() EU_dD = ixp.zerorank2() for i in range(DIM): EU[i] = eU[i] * ReU[i] for j in range(DIM): EU_dD[i][j] = eU_dD[i][j] * ReU[i] + eU[i] * ReUdD[i][j] C = sp.sympify(0) for i in range(DIM): C += EU_dD[i][i] for j in range(DIM): C += GammahatUDD[i][j][i] * EU[j] def Convert_to_Cartesian_basis(VU): # Coordinate transformation from original basis to Cartesian rfm.reference_metric() VU_Cart = ixp.zerorank1() Jac_dxCartU_dxOrigD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): Jac_dxCartU_dxOrigD[i][j] = sp.diff(rfm.xxCart[i], rfm.xx[j]) for i in range(DIM): for j in range(DIM): VU_Cart[i] += Jac_dxCartU_dxOrigD[i][j] * VU[j] return VU_Cart AU_Cart = Convert_to_Cartesian_basis(AU) EU_Cart = Convert_to_Cartesian_basis(EU)
def Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear( CoordType_in, Sph_r_th_ph_or_Cart_xyz, gammaDD_inSphorCart, KDD_inSphorCart, alpha_inSphorCart, betaU_inSphorCart, BU_inSphorCart): # This routine converts the ADM variables # $$\left\{\gamma_{ij}, K_{ij}, \alpha, \beta^i\right\}$$ # in Spherical or Cartesian basis+coordinates, first to the BSSN variables # in the chosen reference_metric::CoordSystem coordinate system+basis: # $$\left\{\bar{\gamma}_{i j},\bar{A}_{i j},\phi, K, \bar{\Lambda}^{i}, \alpha, \beta^i, B^i\right\},$$ # and then to the rescaled variables: # $$\left\{h_{i j},a_{i j},\phi, K, \lambda^{i}, \alpha, \mathcal{V}^i, \mathcal{B}^i\right\}.$$ # The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations. # To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in # the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3. # Step 0: Set spatial dimension (must be 3 for BSSN) DIM = 3 # Step 0: Copy gammaSphDD_in to gammaSphDD, KSphDD_in to KSphDD, etc. # This ensures that the input arrays are not modified below; # modifying them would result in unexpected effects outside # this function. alphaSphorCart = alpha_inSphorCart betaSphorCartU = ixp.zerorank1() BSphorCartU = ixp.zerorank1() gammaSphorCartDD = ixp.zerorank2() KSphorCartDD = ixp.zerorank2() for i in range(DIM): betaSphorCartU[i] = betaU_inSphorCart[i] BSphorCartU[i] = BU_inSphorCart[i] for j in range(DIM): gammaSphorCartDD[i][j] = gammaDD_inSphorCart[i][j] KSphorCartDD[i][j] = KDD_inSphorCart[i][j] # Make sure that rfm.reference_metric() has been called. # We'll need the variables it defines throughout this module. if rfm.have_already_called_reference_metric_function == False: print( "Error. Called Convert_Spherical_ADM_to_BSSN_curvilinear() without" ) print( " first setting up reference metric, by calling rfm.reference_metric()." ) exit(1) # Step 1: All input quantities are in terms of r,th,ph or x,y,z. We want them in terms # of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace # r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2 # as defined for this particular reference metric in reference_metric.py's # xxSph[] or xxCart[], respectively: # Note that substitution only works when the variable is not an integer. Hence the # if isinstance(...,...) stuff: def sympify_integers__replace_rthph_or_Cartxyz(obj, rthph_or_xyz, rthph_or_xyz_of_xx): if isinstance(obj, int): return sp.sympify(obj) else: return obj.subs(rthph_or_xyz[0], rthph_or_xyz_of_xx[0]).\ subs(rthph_or_xyz[1], rthph_or_xyz_of_xx[1]).\ subs(rthph_or_xyz[2], rthph_or_xyz_of_xx[2]) r_th_ph_or_Cart_xyz_of_xx = [] if CoordType_in == "Spherical": r_th_ph_or_Cart_xyz_of_xx = rfm.xxSph elif CoordType_in == "Cartesian": r_th_ph_or_Cart_xyz_of_xx = rfm.xxCart else: print( "Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords." ) exit(1) alphaSphorCart = sympify_integers__replace_rthph_or_Cartxyz( alphaSphorCart, Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx) for i in range(DIM): betaSphorCartU[i] = sympify_integers__replace_rthph_or_Cartxyz( betaSphorCartU[i], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx) BSphorCartU[i] = sympify_integers__replace_rthph_or_Cartxyz( BSphorCartU[i], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx) for j in range(DIM): gammaSphorCartDD[i][ j] = sympify_integers__replace_rthph_or_Cartxyz( gammaSphorCartDD[i][j], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx) KSphorCartDD[i][j] = sympify_integers__replace_rthph_or_Cartxyz( KSphorCartDD[i][j], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx) # Step 2: All ADM initial data quantities are now functions of xx0,xx1,xx2, but # they are still in the Spherical or Cartesian basis. We can now directly apply # Jacobian transformations to get them in the correct xx0,xx1,xx2 basis: # alpha is a scalar, so no Jacobian transformation is necessary. alpha = alphaSphorCart Jac_dUSphorCart_dDrfmUD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff( r_th_ph_or_Cart_xyz_of_xx[i], rfm.xx[j]) Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3( Jac_dUSphorCart_dDrfmUD) betaU = ixp.zerorank1() BU = ixp.zerorank1() gammaDD = ixp.zerorank2() KDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j] BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j] for k in range(DIM): for l in range(DIM): gammaDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][ i] * Jac_dUSphorCart_dDrfmUD[l][j] * gammaSphorCartDD[ k][l] KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][ i] * Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l] # Step 3: All ADM quantities were input into this function in the Spherical or Cartesian # basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above, # we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2. # Here we convert ADM quantities to their BSSN Curvilinear counterparts: # Step 3.1: Convert ADM $\gamma_{ij}$ to BSSN $\bar{\gamma}_{ij}$: # We have (Eqs. 2 and 3 of [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)): # \bar{\gamma}_{i j} = \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \gamma_{ij}. gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD) gammabarDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): gammabarDD[i][j] = (rfm.detgammahat / gammaDET)**(sp.Rational( 1, 3)) * gammaDD[i][j] # Step 3.2: Convert the extrinsic curvature $K_{ij}$ to the trace-free extrinsic # curvature $\bar{A}_{ij}$, plus the trace of the extrinsic curvature $K$, # where (Eq. 3 of [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)): # K = \gamma^{ij} K_{ij}, and # \bar{A}_{ij} &= \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \left(K_{ij} - \frac{1}{3} \gamma_{ij} K \right) trK = sp.sympify(0) for i in range(DIM): for j in range(DIM): trK += gammaUU[i][j] * KDD[i][j] AbarDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): AbarDD[i][j] = (rfm.detgammahat / gammaDET)**(sp.Rational( 1, 3)) * (KDD[i][j] - sp.Rational(1, 3) * gammaDD[i][j] * trK) # Step 3.3: Define $\bar{\Lambda}^i$ (Eqs. 4 and 5 of [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)): # \bar{\Lambda}^i = \bar{\gamma}^{jk}\left(\bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk}\right). gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD) # First compute \bar{\Gamma}^i_{jk}: GammabarUDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): GammabarUDD[i][j][k] += sp.Rational( 1, 2) * gammabarUU[i][l] * ( sp.diff(gammabarDD[l][j], rfm.xx[k]) + sp.diff(gammabarDD[l][k], rfm.xx[j]) - sp.diff(gammabarDD[j][k], rfm.xx[l])) # Next evaluate \bar{\Lambda}^i, based on GammabarUDD above and GammahatUDD # (from the reference metric): LambdabarU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): LambdabarU[i] += gammabarUU[j][k] * (GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k]) # Step 3.4: Set the conformal factor variable $\texttt{cf}$, which is set # by the "BSSN_quantities::ConformalFactor" parameter. For example if # "ConformalFactor" is set to "phi", we can use Eq. 3 of # [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf), # which in arbitrary coordinates is written: # \phi = \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right). # Alternatively if "BSSN_quantities::ConformalFactor" is set to "chi", then # \chi = e^{-4 \phi} = \exp\left(-4 \frac{1}{12} \left(\frac{\gamma}{\bar{\gamma}}\right)\right) # = \exp\left(-\frac{1}{3} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/3}. # # Finally if "BSSN_quantities::ConformalFactor" is set to "W", then # W = e^{-2 \phi} = \exp\left(-2 \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = # \exp\left(-\frac{1}{6} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = # \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/6}. cf = sp.sympify(0) if par.parval_from_str("ConformalFactor") == "phi": cf = sp.Rational(1, 12) * sp.log(gammaDET / gammabarDET) elif par.parval_from_str("ConformalFactor") == "chi": cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 3)) elif par.parval_from_str("ConformalFactor") == "W": cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 6)) else: print("Error ConformalFactor type = \"" + par.parval_from_str("ConformalFactor") + "\" unknown.") exit(1) # Step 4: Rescale tensorial quantities according to the prescription described in # the [BSSN in curvilinear coordinates tutorial module](Tutorial-BSSNCurvilinear.ipynb) # (also [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)): # # h_{ij} &= (\bar{\gamma}_{ij} - \hat{\gamma}_{ij})/\text{ReDD[i][j]}\\ # a_{ij} &= \bar{A}_{ij}/\text{ReDD[i][j]}\\ # \lambda^i &= \bar{\Lambda}^i/\text{ReU[i]}\\ # \mathcal{V}^i &= \beta^i/\text{ReU[i]}\\ # \mathcal{B}^i &= B^i/\text{ReU[i]}\\ hDD = ixp.zerorank2() aDD = ixp.zerorank2() lambdaU = ixp.zerorank1() vetU = ixp.zerorank1() betU = ixp.zerorank1() for i in range(DIM): lambdaU[i] = LambdabarU[i] / rfm.ReU[i] vetU[i] = betaU[i] / rfm.ReU[i] betU[i] = BU[i] / rfm.ReU[i] for j in range(DIM): hDD[i][j] = (gammabarDD[i][j] - rfm.ghatDD[i][j]) / rfm.ReDD[i][j] aDD[i][j] = AbarDD[i][j] / rfm.ReDD[i][j] #print(sp.mathematica_code(hDD[0][0])) # Step 5: Return the BSSN Curvilinear variables in the desired xx0,xx1,xx2 # basis, and as functions of the consistent xx0,xx1,xx2 coordinates. return cf, hDD, lambdaU, aDD, trK, alpha, vetU, betU
def Tmunu_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear( CoordType_in, Tmunu_input_function_name, pointer_to_ID_inputs=False): # The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations. # To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in # the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3. # Step 0: Set spatial dimension (must be 3 for BSSN) DIM = 3 # Step 1: Define the input variables: the 4D stress-energy tensor, and the ADM 3-metric, lapse, & shift: T4SphorCartUU = ixp.declarerank2("T4SphorCartUU", "sym01", DIM=4) gammaSphorCartDD = ixp.declarerank2("gammaSphorCartDD", "sym01") alphaSphorCart = sp.symbols("alphaSphorCart") betaSphorCartU = ixp.declarerank1("betaSphorCartU") # Step 2: All Tmunu initial data quantities are functions of xx0,xx1,xx2, but # in the Spherical or Cartesian basis. # We first define the BSSN stress-energy source terms in the Spherical # or Cartesian basis, respectively. # To get \gamma_{\mu \nu} = gammabar4DD[mu][nu], we'll need to construct the 4-metric, using Eq. 2.122 in B&S: # S_{ij} = \gamma_{i \mu} \gamma_{j \nu} T^{\mu \nu} # S_{i} = -\gamma_{i\mu} n_\nu T^{\mu\nu} # S = \gamma^{ij} S_{ij} # rho = n_\mu n_\nu T^{\mu\nu}, # where # \gamma_{\mu\nu} = g_{\mu\nu} + n_\mu n_\nu # and # n_mu = {-\alpha,0,0,0}, # Step 2.1: Construct the 4-metric based on the input ADM quantities. # This is provided by Eq 4.47 in [Gourgoulhon](https://arxiv.org/pdf/gr-qc/0703035.pdf): # g_{tt} = -\alpha^2 + \beta^k \beta_k # g_{ti} = \beta_i # g_{ij} = \gamma_{ij} # Eq. 2.121 in B&S betaSphorCartD = ixp.zerorank1() for i in range(DIM): for j in range(DIM): betaSphorCartD[i] += gammaSphorCartDD[i][j] * betaSphorCartU[j] # Now compute the beta contraction. beta2 = sp.sympify(0) for i in range(DIM): beta2 += betaSphorCartU[i] * betaSphorCartD[i] # Eq. 2.122 in B&S g4SphorCartDD = ixp.zerorank2(DIM=4) g4SphorCartDD[0][0] = -alphaSphorCart**2 + beta2 for i in range(DIM): g4SphorCartDD[i + 1][0] = g4SphorCartDD[0][i + 1] = betaSphorCartD[i] for i in range(DIM): for j in range(DIM): g4SphorCartDD[i + 1][j + 1] = gammaSphorCartDD[i][j] # Step 2.2: Construct \gamma_{mu nu} = g_{mu nu} + n_mu n_nu: n4SphorCartD = ixp.zerorank1(DIM=4) n4SphorCartD[0] = -alphaSphorCart gamma4SphorCartDD = ixp.zerorank2(DIM=4) for mu in range(4): for nu in range(4): gamma4SphorCartDD[mu][nu] = g4SphorCartDD[mu][ nu] + n4SphorCartD[mu] * n4SphorCartD[nu] # Step 2.3: We now have all we need to construct the BSSN source # terms in the current basis (Spherical or Cartesian): # S_{ij} = \gamma_{i \mu} \gamma_{j \nu} T^{\mu \nu} # S_{i} = -\gamma_{i\mu} n_\nu T^{\mu\nu} # S = \gamma^{ij} S_{ij} # rho = n_\mu n_\nu T^{\mu\nu}, SSphorCartDD = ixp.zerorank2() SSphorCartD = ixp.zerorank1() SSphorCart = sp.sympify(0) rhoSphorCart = sp.sympify(0) for i in range(DIM): for j in range(DIM): for mu in range(4): for nu in range(4): SSphorCartDD[i][j] += gamma4SphorCartDD[ i + 1][mu] * gamma4SphorCartDD[ j + 1][nu] * T4SphorCartUU[mu][nu] for i in range(DIM): for mu in range(4): for nu in range(4): SSphorCartD[i] += -gamma4SphorCartDD[ i + 1][mu] * n4SphorCartD[nu] * T4SphorCartUU[mu][nu] gammaSphorCartUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaSphorCartDD) for i in range(DIM): for j in range(DIM): SSphorCart += gammaSphorCartUU[i][j] * SSphorCartDD[i][j] for mu in range(4): for nu in range(4): rhoSphorCart += n4SphorCartD[mu] * n4SphorCartD[ nu] * T4SphorCartUU[mu][nu] # Step 3: Perform basis conversion to # Make sure that rfm.reference_metric() has been called. # We'll need the variables it defines throughout this module. if rfm.have_already_called_reference_metric_function == False: print( "Error. Called Tmunu_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear() without" ) print( " first setting up reference metric, by calling rfm.reference_metric()." ) exit(1) # Step 1: All input quantities are in terms of r,th,ph or x,y,z. We want them in terms # of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace # r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2 # as defined for this particular reference metric in reference_metric.py's # xxSph[] or xxCart[], respectively: r_th_ph_or_Cart_xyz_oID_xx = [] if CoordType_in == "Spherical": r_th_ph_or_Cart_xyz_oID_xx = rfm.xxSph elif CoordType_in == "Cartesian": r_th_ph_or_Cart_xyz_oID_xx = rfm.xxCart else: print( "Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords." ) exit(1) # Next apply Jacobian transformations to convert into the (xx0,xx1,xx2) basis # alpha is a scalar, so no Jacobian transformation is necessary. alpha = alphaSphorCart Jac_dUSphorCart_dDrfmUD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff( r_th_ph_or_Cart_xyz_oID_xx[i], rfm.xx[j]) Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3( Jac_dUSphorCart_dDrfmUD) betaU = ixp.zerorank1() BU = ixp.zerorank1() gammaSphorCartDD = ixp.zerorank2() KDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j] BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j] for k in range(DIM): for l in range(DIM): gammaSphorCartDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * \ gammaSphorCartDD[k][l] KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][ i] * Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l] # Step 3: All ADM quantities were input into this function in the Spherical or Cartesian # basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above, # we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2. # Here we convert ADM quantities to their BSSN Curvilinear counterparts: # Step 3.1: Convert ADM $\gamma_{ij}$ to BSSN $\bar{\gamma}_{ij}$: # We have (Eqs. 2 and 3 of [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)): # \bar{\gamma}_{i j} = \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \gamma_{ij}. gammaSphorCartUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaSphorCartDD) gammabarDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): gammabarDD[i][j] = (rfm.detgammahat / gammaDET)**(sp.Rational( 1, 3)) * gammaSphorCartDD[i][j] # Step 3.2: Convert the extrinsic curvature $K_{ij}$ to the trace-free extrinsic # curvature $\bar{A}_{ij}$, plus the trace of the extrinsic curvature $K$, # where (Eq. 3 of [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)): # K = \gamma^{ij} K_{ij}, and # \bar{A}_{ij} &= \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \left(K_{ij} - \frac{1}{3} \gamma_{ij} K \right) trK = sp.sympify(0) for i in range(DIM): for j in range(DIM): trK += gammaSphorCartUU[i][j] * KDD[i][j] AbarDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): AbarDD[i][j] = (rfm.detgammahat / gammaDET)**(sp.Rational( 1, 3)) * (KDD[i][j] - sp.Rational(1, 3) * gammaSphorCartDD[i][j] * trK) # Step 3.3: Set the conformal factor variable $\texttt{cf}$, which is set # by the "BSSN_RHSs::ConformalFactor" parameter. For example if # "ConformalFactor" is set to "phi", we can use Eq. 3 of # [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf), # which in arbitrary coordinates is written: # \phi = \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right). # Alternatively if "BSSN_RHSs::ConformalFactor" is set to "chi", then # \chi = e^{-4 \phi} = \exp\left(-4 \frac{1}{12} \left(\frac{\gamma}{\bar{\gamma}}\right)\right) # = \exp\left(-\frac{1}{3} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/3}. # # Finally if "BSSN_RHSs::ConformalFactor" is set to "W", then # W = e^{-2 \phi} = \exp\left(-2 \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = # \exp\left(-\frac{1}{6} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = # \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/6}. # First compute gammabarDET: gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD) cf = sp.sympify(0) if par.parval_from_str("ConformalFactor") == "phi": cf = sp.Rational(1, 12) * sp.log(gammaDET / gammabarDET) elif par.parval_from_str("ConformalFactor") == "chi": cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 3)) elif par.parval_from_str("ConformalFactor") == "W": cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 6)) else: print("Error ConformalFactor type = \"" + par.parval_from_str("ConformalFactor") + "\" unknown.") exit(1) # Step 4: Rescale tensorial quantities according to the prescription described in # the [BSSN in curvilinear coordinates tutorial module](Tutorial-BSSNCurvilinear.ipynb) # (also [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)): # # h_{ij} &= (\bar{\gamma}_{ij} - \hat{\gamma}_{ij})/\text{ReDD[i][j]}\\ # a_{ij} &= \bar{A}_{ij}/\text{ReDD[i][j]}\\ # \lambda^i &= \bar{\Lambda}^i/\text{ReU[i]}\\ # \mathcal{V}^i &= \beta^i/\text{ReU[i]}\\ # \mathcal{B}^i &= B^i/\text{ReU[i]}\\ hDD = ixp.zerorank2() aDD = ixp.zerorank2() vetU = ixp.zerorank1() betU = ixp.zerorank1() for i in range(DIM): vetU[i] = betaU[i] / rfm.ReU[i] betU[i] = BU[i] / rfm.ReU[i] for j in range(DIM): hDD[i][j] = (gammabarDD[i][j] - rfm.ghatDD[i][j]) / rfm.ReDD[i][j] aDD[i][j] = AbarDD[i][j] / rfm.ReDD[i][j] # Step 5: Output all ADM-to-BSSN expressions to a C function. This function # must first call the ID_ADM_SphorCart() defined above. Using these # Spherical or Cartesian data, it sets up all quantities needed for # BSSNCurvilinear initial data, *except* $\lambda^i$, which must be # computed from numerical data using finite-difference derivatives. with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", "w") as file: file.write( "void ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(const REAL xx0xx1xx2[3]," ) if pointer_to_ID_inputs == True: file.write("ID_inputs *other_inputs,") else: file.write("ID_inputs other_inputs,") file.write(""" REAL *hDD00,REAL *hDD01,REAL *hDD02,REAL *hDD11,REAL *hDD12,REAL *hDD22, REAL *aDD00,REAL *aDD01,REAL *aDD02,REAL *aDD11,REAL *aDD12,REAL *aDD22, REAL *trK, REAL *vetU0,REAL *vetU1,REAL *vetU2, REAL *betU0,REAL *betU1,REAL *betU2, REAL *alpha, REAL *cf) { REAL gammaSphorCartDD00,gammaSphorCartDD01,gammaSphorCartDD02, gammaSphorCartDD11,gammaSphorCartDD12,gammaSphorCartDD22; REAL KSphorCartDD00,KSphorCartDD01,KSphorCartDD02, KSphorCartDD11,KSphorCartDD12,KSphorCartDD22; REAL alphaSphorCart,betaSphorCartU0,betaSphorCartU1,betaSphorCartU2; REAL BSphorCartU0,BSphorCartU1,BSphorCartU2; const REAL xx0 = xx0xx1xx2[0]; const REAL xx1 = xx0xx1xx2[1]; const REAL xx2 = xx0xx1xx2[2]; REAL xyz_or_rthph[3];\n""") outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" outputC(r_th_ph_or_Cart_xyz_oID_xx[0:3], ["xyz_or_rthph[0]", "xyz_or_rthph[1]", "xyz_or_rthph[2]"], "BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", outCparams + ",CSE_enable=False") with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", "a") as file: file.write(" " + ADM_input_function_name + """(xyz_or_rthph, other_inputs, &gammaSphorCartDD00,&gammaSphorCartDD01,&gammaSphorCartDD02, &gammaSphorCartDD11,&gammaSphorCartDD12,&gammaSphorCartDD22, &KSphorCartDD00,&KSphorCartDD01,&KSphorCartDD02, &KSphorCartDD11,&KSphorCartDD12,&KSphorCartDD22, &alphaSphorCart,&betaSphorCartU0,&betaSphorCartU1,&betaSphorCartU2, &BSphorCartU0,&BSphorCartU1,&BSphorCartU2); // Next compute all rescaled BSSN curvilinear quantities:\n""") outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" outputC([ hDD[0][0], hDD[0][1], hDD[0][2], hDD[1][1], hDD[1][2], hDD[2][2], aDD[0][0], aDD[0][1], aDD[0][2], aDD[1][1], aDD[1][2], aDD[2][2], trK, vetU[0], vetU[1], vetU[2], betU[0], betU[1], betU[2], alpha, cf ], [ "*hDD00", "*hDD01", "*hDD02", "*hDD11", "*hDD12", "*hDD22", "*aDD00", "*aDD01", "*aDD02", "*aDD11", "*aDD12", "*aDD22", "*trK", "*vetU0", "*vetU1", "*vetU2", "*betU0", "*betU1", "*betU2", "*alpha", "*cf" ], "BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", params=outCparams) with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", "a") as file: file.write("}\n") # Step 5.A: Output the driver function for the above # function ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs() # Next write the driver function for ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(): with open("BSSN/ID_BSSN__ALL_BUT_LAMBDAs.h", "w") as file: file.write( "void ID_BSSN__ALL_BUT_LAMBDAs(const int Nxx_plus_2NGHOSTS[3],REAL *xx[3]," ) if pointer_to_ID_inputs == True: file.write("ID_inputs *other_inputs,") else: file.write("ID_inputs other_inputs,") file.write("REAL *in_gfs) {\n") file.write( lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], [ "Nxx_plus_2NGHOSTS[2]", "Nxx_plus_2NGHOSTS[1]", "Nxx_plus_2NGHOSTS[0]" ], ["1", "1", "1"], [ "#pragma omp parallel for", " const REAL xx2 = xx[2][i2];", " const REAL xx1 = xx[1][i1];" ], "", """const REAL xx0 = xx[0][i0]; const int idx = IDX3(i0,i1,i2); const REAL xx0xx1xx2[3] = {xx0,xx1,xx2}; ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(xx0xx1xx2,other_inputs, &in_gfs[IDX4pt(HDD00GF,idx)],&in_gfs[IDX4pt(HDD01GF,idx)],&in_gfs[IDX4pt(HDD02GF,idx)], &in_gfs[IDX4pt(HDD11GF,idx)],&in_gfs[IDX4pt(HDD12GF,idx)],&in_gfs[IDX4pt(HDD22GF,idx)], &in_gfs[IDX4pt(ADD00GF,idx)],&in_gfs[IDX4pt(ADD01GF,idx)],&in_gfs[IDX4pt(ADD02GF,idx)], &in_gfs[IDX4pt(ADD11GF,idx)],&in_gfs[IDX4pt(ADD12GF,idx)],&in_gfs[IDX4pt(ADD22GF,idx)], &in_gfs[IDX4pt(TRKGF,idx)], &in_gfs[IDX4pt(VETU0GF,idx)],&in_gfs[IDX4pt(VETU1GF,idx)],&in_gfs[IDX4pt(VETU2GF,idx)], &in_gfs[IDX4pt(BETU0GF,idx)],&in_gfs[IDX4pt(BETU1GF,idx)],&in_gfs[IDX4pt(BETU2GF,idx)], &in_gfs[IDX4pt(ALPHAGF,idx)],&in_gfs[IDX4pt(CFGF,idx)]); """)) file.write("}\n") # Step 6: Compute $\bar{\Lambda}^i$ (Eqs. 4 and 5 of # [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)), # from finite-difference derivatives of rescaled metric # quantities $h_{ij}$: # \bar{\Lambda}^i = \bar{\gamma}^{jk}\left(\bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk}\right). # The reference_metric.py module provides us with analytic expressions for # $\hat{\Gamma}^i_{jk}$, so here we need only compute # finite-difference expressions for $\bar{\Gamma}^i_{jk}$, based on # the values for $h_{ij}$ provided in the initial data. Once # $\bar{\Lambda}^i$ has been computed, we apply the usual rescaling # procedure: # \lambda^i = \bar{\Lambda}^i/\text{ReU[i]}, # and then output the result to a C file using the NRPy+ # finite-difference C output routine. # We will need all BSSN gridfunctions to be defined, as well as # expressions for gammabarDD_dD in terms of exact derivatives of # the rescaling matrix and finite-difference derivatives of # hDD's. gammabarDD = bssnrhs.gammabarDD gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD) gammabarDD_dD = bssnrhs.gammabarDD_dD # Next compute Christoffel symbols \bar{\Gamma}^i_{jk}: GammabarUDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): GammabarUDD[i][j][k] += sp.Rational( 1, 2) * gammabarUU[i][l] * (gammabarDD_dD[l][j][k] + gammabarDD_dD[l][k][j] - gammabarDD_dD[j][k][l]) # Next evaluate \bar{\Lambda}^i, based on GammabarUDD above and GammahatUDD # (from the reference metric): LambdabarU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): LambdabarU[i] += gammabarUU[j][k] * ( GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k]) # Finally apply rescaling: # lambda^i = Lambdabar^i/\text{ReU[i]} lambdaU = ixp.zerorank1() for i in range(DIM): lambdaU[i] = LambdabarU[i] / rfm.ReU[i] outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" lambdaU_expressions = [ lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU0"), rhs=lambdaU[0]), lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU1"), rhs=lambdaU[1]), lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU2"), rhs=lambdaU[2]) ] lambdaU_expressions_FDout = fin.FD_outputC("returnstring", lambdaU_expressions, outCparams) with open("BSSN/ID_BSSN_lambdas.h", "w") as file: file.write(""" void ID_BSSN_lambdas(const int Nxx[3],const int Nxx_plus_2NGHOSTS[3],REAL *xx[3],const REAL dxx[3],REAL *in_gfs) {\n""" ) file.write( lp.loop(["i2", "i1", "i0"], ["NGHOSTS", "NGHOSTS", "NGHOSTS"], ["NGHOSTS+Nxx[2]", "NGHOSTS+Nxx[1]", "NGHOSTS+Nxx[0]"], ["1", "1", "1"], [ "const REAL invdx0 = 1.0/dxx[0];\n" + "const REAL invdx1 = 1.0/dxx[1];\n" + "const REAL invdx2 = 1.0/dxx[2];\n" + "#pragma omp parallel for", " const REAL xx2 = xx[2][i2];", " const REAL xx1 = xx[1][i1];" ], "", "const REAL xx0 = xx[0][i0];\n" + lambdaU_expressions_FDout)) file.write("}\n")
def MaxwellCartesian_Evol(): #Step 0: Set the spatial dimension parameter to 3. par.set_parval_from_str("grid::DIM", 3) DIM = par.parval_from_str("grid::DIM") # Step 1: Set the finite differencing order to 4. # (not needed here) # par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", 4) # Step 2: Register gridfunctions that are needed as input. _psi = gri.register_gridfunctions( "EVOL", ["psi"]) # lgtm [py/unused-local-variable] # Step 3a: Declare the rank-1 indexed expressions E_{i}, A_{i}, # and \partial_{i} \psi. Derivative variables like these # must have an underscore in them, so the finite # difference module can parse the variable name properly. ED = ixp.register_gridfunctions_for_single_rank1("EVOL", "ED") AD = ixp.register_gridfunctions_for_single_rank1("EVOL", "AD") psi_dD = ixp.declarerank1("psi_dD") ## Step 3b: Declare the conformal metric tensor and its first # derivative. These are needed to find the Christoffel # symbols, which we need for covariant derivatives. gammaDD = ixp.register_gridfunctions_for_single_rank2( "AUX", "gammaDD", "sym01") # The AUX or EVOL designation is *not* # used in diagnostic modules. gammaDD_dD = ixp.declarerank3("gammaDD_dD", "sym01") gammaDD_dDD = ixp.declarerank4("gammaDD_dDD", "sym01_sym23") gammaUU, detgamma = ixp.symm_matrix_inverter3x3(gammaDD) gammaUU_dD = ixp.declarerank3("gammaDD_dD", "sym01") # Define the Christoffel symbols GammaUDD = ixp.zerorank3(DIM) for i in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): GammaUDD[i][k][l] += (sp.Rational(1,2))*gammaUU[i][m]*\ (gammaDD_dD[m][k][l] + gammaDD_dD[m][l][k] - gammaDD_dD[k][l][m]) # Step 3b: Declare the rank-2 indexed expression \partial_{j} A_{i}, # which is not symmetric in its indices. # Derivative variables like these must have an underscore # in them, so the finite difference module can parse the # variable name properly. AD_dD = ixp.declarerank2("AD_dD", "nosym") # Step 3c: Declare the rank-3 indexed expression \partial_{jk} A_{i}, # which is symmetric in the two {jk} indices. AD_dDD = ixp.declarerank3("AD_dDD", "sym12") # Step 4: Calculate first and second covariant derivatives, and the # necessary contractions. # First covariant derivative # D_{j} A_{i} = A_{i,j} - \Gamma^{k}_{ij} A_{k} AD_dcovD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): AD_dcovD[i][j] = AD_dD[i][j] for k in range(DIM): AD_dcovD[i][j] -= GammaUDD[k][i][j] * AD[k] # First, we must construct the lowered Christoffel symbols: # \Gamma_{ijk} = \gamma_{il} \Gamma^l_{jk} # And raise the index on A: # A^j = \gamma^{ij} A_i GammaDDD = ixp.zerorank3() AU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): AU[j] += gammaUU[i][j] * AD[i] for k in range(DIM): for l in range(DIM): GammaDDD[i][j][k] += gammaDD[i][l] * GammaUDD[l][j][k] # Covariant second derivative (the bracketed terms): # D_j D^j A_i = \gamma^{jk} [A_{i,jk} - A^l (\gamma_{li,kj} + \gamma_{kl,ij} - \gamma_{ik,lj}) # + \Gamma_{lik} (\gamma^{lm} A_{m,j} + A_m \gamma^{lm}{}_{,j}) # - (\Gamma^l_{ij} A_{l,k} + \Gamma^l_{jk} A_{i,l}) # + (\Gamma^m_{ij} \Gamma^l_{mk} A_l + \Gamma ^m_{jk} \Gamma^l_{im} A_l) AD_dcovDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): AD_dcovDD[i][j][k] = AD_dDD[i][j][k] for l in range(DIM): # Terms 1 and 3 AD_dcovDD[i][j][k] -= AU[l] * (gammaDD_dDD[l][i][k][j] + gammaDD_dDD[k][l][i][j] - \ gammaDD_dDD[i][k][l][j]) \ + GammaUDD[l][i][j] * AD_dD[l][k] + GammaUDD[l][j][k] * AD_dD[i][l] for m in range(DIM): # Terms 2 and 4 AD_dcovDD[i][j][k] += GammaDDD[l][i][k] * (gammaUU[l][m] * AD_dD[m][j] + AD[m] * gammaUU_dD[l][m][j]) \ + GammaUDD[m][i][j] * GammaUDD[l][m][k] * AD[l] \ + GammaUDD[m][j][k] * GammaUDD[l][i][m] * AD[l] # Covariant divergence # D_{i} A^{i} = \gamma^{ij} D_{j} A_{i} DivA = 0 # Gradient of covariant divergence # DivA_dD_{i} = \gamma^{jk} A_{k;\hat{j}\hat{i}} DivA_dD = ixp.zerorank1() # Covariant Laplacian # LapAD_{i} = \gamma^{jk} A_{i;\hat{j}\hat{k}} LapAD = ixp.zerorank1() for i in range(DIM): for j in range(DIM): DivA += gammaUU[i][j] * AD_dcovD[i][j] for k in range(DIM): DivA_dD[i] += gammaUU[j][k] * AD_dcovDD[k][j][i] LapAD[i] += gammaUU[j][k] * AD_dcovDD[i][j][k] global ArhsD, ErhsD, psi_rhs system = par.parval_from_str("System_to_use") if system == "System_I": # Step 5: Define right-hand sides for the evolution. print("Warning: System I is less stable!") ArhsD = ixp.zerorank1() ErhsD = ixp.zerorank1() for i in range(DIM): ArhsD[i] = -ED[i] - psi_dD[i] ErhsD[i] = -LapAD[i] + DivA_dD[i] psi_rhs = -DivA elif system == "System_II": # We inherit here all of the definitions from System I, above # Step 7a: Register the scalar auxiliary variable \Gamma Gamma = gri.register_gridfunctions("EVOL", ["Gamma"]) # Step 7b: Declare the ordinary gradient \partial_{i} \Gamma Gamma_dD = ixp.declarerank1("Gamma_dD") # Step 8a: Construct the second covariant derivative of the scalar \psi # \psi_{;\hat{i}\hat{j}} = \psi_{,i;\hat{j}} # = \psi_{,ij} - \Gamma^{k}_{ij} \psi_{,k} psi_dDD = ixp.declarerank2("psi_dDD", "sym01") psi_dcovDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): psi_dcovDD[i][j] = psi_dDD[i][j] for k in range(DIM): psi_dcovDD[i][j] += -GammaUDD[k][i][j] * psi_dD[k] # Step 8b: Construct the covariant Laplacian of \psi # Lappsi = ghat^{ij} D_{j} D_{i} \psi Lappsi = 0 for i in range(DIM): for j in range(DIM): Lappsi += gammaUU[i][j] * psi_dcovDD[i][j] # Step 9: Define right-hand sides for the evolution. global Gamma_rhs ArhsD = ixp.zerorank1() ErhsD = ixp.zerorank1() for i in range(DIM): ArhsD[i] = -ED[i] - psi_dD[i] ErhsD[i] = -LapAD[i] + Gamma_dD[i] psi_rhs = -Gamma Gamma_rhs = -Lappsi else: print( "Invalid choice of system: System_to_use must be either System_I or System_II" ) ED_dD = ixp.declarerank2("ED_dD", "nosym") global Cviolation Cviolation = gri.register_gridfunctions("AUX", ["Cviolation"]) Cviolation = sp.sympify(0) for i in range(DIM): for j in range(DIM): Cviolation += gammaUU[i][j] * ED_dD[j][i] for b in range(DIM): Cviolation -= gammaUU[i][j] * GammaUDD[b][i][j] * ED[b]
def GiRaFFE_Higher_Order(): #Step 1.0: Set the spatial dimension parameter to 3. par.set_parval_from_str("grid::DIM", 3) DIM = par.parval_from_str("grid::DIM") # Step 1.1: Set the finite differencing order to 4. #par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", 4) thismodule = "GiRaFFE_NRPy" # M_PI will allow the C code to substitute the correct value M_PI = par.Cparameters("#define", thismodule, "M_PI", "") # ADMBase defines the 4-metric in terms of the 3+1 spacetime metric quantities gamma_{ij}, beta^i, and alpha gammaDD = ixp.register_gridfunctions_for_single_rank2("AUX", "gammaDD", "sym01", DIM=3) betaU = ixp.register_gridfunctions_for_single_rank1("AUX", "betaU", DIM=3) alpha = gri.register_gridfunctions("AUX", "alpha") # GiRaFFE uses the Valencia 3-velocity and A_i, which are defined in the initial data module(GiRaFFEfood) ValenciavU = ixp.register_gridfunctions_for_single_rank1("AUX", "ValenciavU", DIM=3) AD = ixp.register_gridfunctions_for_single_rank1("EVOL", "AD", DIM=3) # B^i must be computed at each timestep within GiRaFFE so that the Valencia 3-velocity can be evaluated BU = ixp.register_gridfunctions_for_single_rank1("AUX", "BU", DIM=3) # <a id='step3'></a> # # ## Step 1.2: Build the four metric $g_{\mu\nu}$, its inverse $g^{\mu\nu}$ and spatial derivatives $g_{\mu\nu,i}$ from ADM 3+1 quantities $\gamma_{ij}$, $\beta^i$, and $\alpha$ # # $$\label{step3}$$ # \[Back to [top](#top)\] # # Notice that the time evolution equation for $\tilde{S}_i$ # $$ # \partial_t \tilde{S}_i = - \partial_j \left( \alpha \sqrt{\gamma} T^j_{{\rm EM} i} \right) + \frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu} # $$ # contains $\partial_i g_{\mu \nu} = g_{\mu\nu,i}$. We will now focus on evaluating this term. # # The four-metric $g_{\mu\nu}$ is related to the three-metric $\gamma_{ij}$, index-lowered shift $\beta_i$, and lapse $\alpha$ by # $$ # g_{\mu\nu} = \begin{pmatrix} # -\alpha^2 + \beta^k \beta_k & \beta_j \\ # \beta_i & \gamma_{ij} # \end{pmatrix}. # $$ # This tensor and its inverse have already been built by the u0_smallb_Poynting__Cartesian.py module ([documented here](Tutorial-u0_smallb_Poynting-Cartesian.ipynb)), so we can simply load the module and import the variables. # Step 1.2: import u0_smallb_Poynting__Cartesian.py to set # the four metric and its inverse. This module also sets b^2 and u^0. import u0_smallb_Poynting__Cartesian.u0_smallb_Poynting__Cartesian as u0b u0b.compute_u0_smallb_Poynting__Cartesian(gammaDD, betaU, alpha, ValenciavU, BU) betaD = ixp.zerorank1() for i in range(DIM): for j in range(DIM): betaD[i] += gammaDD[i][j] * betaU[j] # We will now pull in the four metric and its inverse. import BSSN.ADMBSSN_tofrom_4metric as AB4m # NRPy+: ADM/BSSN <-> 4-metric conversions AB4m.g4DD_ito_BSSN_or_ADM("ADM") g4DD = AB4m.g4DD AB4m.g4UU_ito_BSSN_or_ADM("ADM") g4UU = AB4m.g4UU # Next we compute spatial derivatives of the metric, $\partial_i g_{\mu\nu} = g_{\mu\nu,i}$, written in terms of the three-metric, shift, and lapse. Simply taking the derivative of the expression for $g_{\mu\nu}$ above, we find # $$ # g_{\mu\nu,l} = \begin{pmatrix} # -2\alpha \alpha_{,l} + \beta^k_{\ ,l} \beta_k + \beta^k \beta_{k,l} & \beta_{i,l} \\ # \beta_{j,l} & \gamma_{ij,l} # \end{pmatrix}. # $$ # # Notice the derivatives of the shift vector with its indexed lowered, $\beta_{i,j} = \partial_j \beta_i$. This can be easily computed in terms of the given ADMBase quantities $\beta^i$ and $\gamma_{ij}$ via: # \begin{align} # \beta_{i,j} &= \partial_j \beta_i \\ # &= \partial_j (\gamma_{ik} \beta^k) \\ # &= \gamma_{ik} \partial_j\beta^k + \beta^k \partial_j \gamma_{ik} \\ # \beta_{i,j} &= \gamma_{ik} \beta^k_{\ ,j} + \beta^k \gamma_{ik,j}. # \end{align} # # Since this expression mixes Greek and Latin indices, we will need to store the expressions for each of the three spatial derivatives as separate variables. # # So, we will first set # $$ g_{00,l} = \underbrace{-2\alpha \alpha_{,l}}_{\rm Term\ 1} + \underbrace{\beta^k_{\ ,l} \beta_k}_{\rm Term\ 2} + \underbrace{\beta^k \beta_{k,l}}_{\rm Term\ 3} $$ # Step 1.2, cont'd: Build spatial derivatives of the four metric # Step 1.2.a: Declare derivatives of grid functions. These will be handled by FD_outputC alpha_dD = ixp.declarerank1("alpha_dD") betaU_dD = ixp.declarerank2("betaU_dD", "nosym") gammaDD_dD = ixp.declarerank3("gammaDD_dD", "sym01") # Step 1.2.b: These derivatives will be constructed analytically. betaDdD = ixp.zerorank2() g4DDdD = ixp.zerorank3(DIM=4) for i in range(DIM): for j in range(DIM): for k in range(DIM): # \gamma_{ik} \beta^k_{,j} + \beta^k \gamma_{ik,j} betaDdD[i][j] += gammaDD[i][k] * betaU_dD[k][j] + betaU[ k] * gammaDD_dD[i][k][j] # Step 1.2.c: Set the 00 components # Step 1.2.c.i: Term 1: -2\alpha \alpha_{,l} for l in range(DIM): g4DDdD[0][0][l + 1] = -2 * alpha * alpha_dD[l] # Step 1.2.c.ii: Term 2: \beta^k_{\ ,l} \beta_k for l in range(DIM): for k in range(DIM): g4DDdD[0][0][l + 1] += betaU_dD[k][l] * betaD[k] # Step 1.2.c.iii: Term 3: \beta^k \beta_{k,l} for l in range(DIM): for k in range(DIM): g4DDdD[0][0][l + 1] += betaU[k] * betaDdD[k][l] # Now we will contruct the other components of $g_{\mu\nu,l}$. We will first construct # $$ g_{i0,l} = g_{0i,l} = \beta_{i,l}, $$ # then # $$ g_{ij,l} = \gamma_{ij,l} $$ # Step 1.2.d: Set the i0 and 0j components for l in range(DIM): for i in range(DIM): # \beta_{i,l} g4DDdD[i + 1][0][l + 1] = g4DDdD[0][i + 1][l + 1] = betaDdD[i][l] #Step 1.2.e: Set the ij components for l in range(DIM): for i in range(DIM): for j in range(DIM): # \gamma_{ij,l} g4DDdD[i + 1][j + 1][l + 1] = gammaDD_dD[i][j][l] # <a id='step4'></a> # # # $T^{\mu\nu}_{\rm EM}$ and its derivatives # # Now that the metric and its derivatives are out of the way, let's return to the evolution equation for $\tilde{S}_i$, # $$ # \partial_t \tilde{S}_i = - \partial_j \left( \alpha \sqrt{\gamma} T^j_{{\rm EM} i} \right) + \frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu}. # $$ # We turn our focus now to $T^j_{{\rm EM} i}$ and its derivatives. To this end, we start by computing $T^{\mu \nu}_{\rm EM}$ (from eq. 27 of [Paschalidis & Shapiro's paper on their GRFFE code](https://arxiv.org/pdf/1310.3274.pdf)): # # $$\boxed{T^{\mu \nu}_{\rm EM} = b^2 u^{\mu} u^{\nu} + \frac{b^2}{2} g^{\mu \nu} - b^{\mu} b^{\nu}.}$$ # # Notice that $T^{\mu\nu}_{\rm EM}$ is written in terms of # # * $b^\mu$, the 4-component magnetic field vector, related to the comoving magnetic field vector $B^i_{(u)}$ # * $u^\mu$, the 4-velocity # * $g^{\mu \nu}$, the inverse 4-metric # # However, $\texttt{GiRaFFE}$ has access to only the following quantities, requiring in the following sections that we write the above quantities in terms of the following ones: # # * $\gamma_{ij}$, the 3-metric # * $\alpha$, the lapse # * $\beta^i$, the shift # * $A_i$, the vector potential # * $B^i$, the magnetic field (we assume only in the grid interior, not the ghost zones) # * $\left[\sqrt{\gamma}\Phi\right]$, the zero-component of the vector potential $A_\mu$, times the square root of the determinant of the 3-metric # * $v_{(n)}^i$, the Valencia 3-velocity # * $u^0$, the zero-component of the 4-velocity # # ## Step 2.0: $u^i$ and $b^i$ and related quantities # $$\label{step4}$$ # \[Back to [top](#top)\] # # We begin by importing what we can from u0_smallb_Poynting__Cartesian.py. We will need the four-velocity $u^\mu$, which is related to the Valencia 3-velocity $v^i_{(n)}$ used directly by $\texttt{GiRaFFE}$ (see also [Duez, et al, eqs. 53 and 56](https://arxiv.org/pdf/astro-ph/0503420.pdf)) # \begin{align} # u^i &= u^0 (\alpha v^i_{(n)} - \beta^i), \\ # u_j &= \alpha u^0 \gamma_{ij} v^i_{(n)}, # \end{align} # and $v^i_{(n)}$ is the Valencia three-velocity. These have already been constructed in terms of the Valencia 3-velocity and other 3+1 ADM quantities by the u0_smallb_Poynting__Cartesian.py module, so we can simply import these variables: # Step 2.0: u^i, b^i, and related quantities # Step 2.0.a: import the four-velocity, as written in terms of the Valencia 3-velocity global uD, uU uD = ixp.register_gridfunctions_for_single_rank1("AUX", "uD") uU = ixp.register_gridfunctions_for_single_rank1("AUX", "uU") u4upperZero = gri.register_gridfunctions("AUX", "u4upperZero") for i in range(DIM): uD[i] = u0b.uD[i].subs(u0b.u0, u4upperZero) uU[i] = u0b.uU[i].subs(u0b.u0, u4upperZero) # We also need the magnetic field 4-vector $b^{\mu}$, which is related to the magnetic field by [eqs. 23, 24, and 31 in Duez, et al](https://arxiv.org/pdf/astro-ph/0503420.pdf): # \begin{align} # b^0 &= \frac{1}{\sqrt{4\pi}} B^0_{\rm (u)} = \frac{u_j B^j}{\sqrt{4\pi}\alpha}, \\ # b^i &= \frac{1}{\sqrt{4\pi}} B^i_{\rm (u)} = \frac{B^i + (u_j B^j) u^i}{\sqrt{4\pi}\alpha u^0}, \\ # \end{align} # where $B^i$ is the variable tracked by the HydroBase thorn in the Einstein Toolkit. Again, these have already been built by the u0_smallb_Poynting__Cartesian.py module, so we can simply import the variables. # Step 2.0.b: import the small b terms smallb4U = ixp.zerorank1(DIM=4) smallb4D = ixp.zerorank1(DIM=4) for mu in range(4): smallb4U[mu] = u0b.smallb4U[mu].subs(u0b.u0, u4upperZero) smallb4D[mu] = u0b.smallb4D[mu].subs(u0b.u0, u4upperZero) smallb2 = u0b.smallb2etk.subs(u0b.u0, u4upperZero) # <a id='step5'></a> # # ## Step 2.1: Construct all components of the electromagnetic stress-energy tensor $T^{\mu \nu}_{\rm EM}$ # $$\label{step5}$$ # # \[Back to [top](#top)\] # # We now have all the pieces to calculate the stress-energy tensor, # $$T^{\mu \nu}_{\rm EM} = \underbrace{b^2 u^{\mu} u^{\nu}}_{\rm Term\ 1} + # \underbrace{\frac{b^2}{2} g^{\mu \nu}}_{\rm Term\ 2} # - \underbrace{b^{\mu} b^{\nu}}_{\rm Term\ 3}.$$ # Because $u^0$ is a separate variable, we could build the $00$ component separately, then the $\mu0$ and $0\nu$ components, and finally the $\mu\nu$ components. Alternatively, for clarity, we could create a temporary variable $u^\mu=\left( u^0, u^i \right)$ # Step 2.1: Construct the electromagnetic stress-energy tensor # Step 2.1.a: Set up the four-velocity vector u4U = ixp.zerorank1(DIM=4) u4U[0] = u4upperZero for i in range(DIM): u4U[i + 1] = uU[i] # Step 2.1.b: Build T4EMUU itself T4EMUU = ixp.zerorank2(DIM=4) for mu in range(4): for nu in range(4): # Term 1: b^2 u^{\mu} u^{\nu} T4EMUU[mu][nu] = smallb2 * u4U[mu] * u4U[nu] for mu in range(4): for nu in range(4): # Term 2: b^2 / 2 g^{\mu \nu} T4EMUU[mu][nu] += smallb2 * g4UU[mu][nu] / 2 for mu in range(4): for nu in range(4): # Term 3: -b^{\mu} b^{\nu} T4EMUU[mu][nu] += -smallb4U[mu] * smallb4U[nu] # <a id='step6'></a> # # # Step 2.2: Derivatives of the electromagnetic stress-energy tensor # $$\label{step6}$$ # # \[Back to [top](#top)\] # # If we look at the evolution equation, we see that we will need spatial derivatives of $T^{\mu\nu}_{\rm EM}$. When confronted with derivatives of complicated expressions, it is generally convenient to declare those expressions as gridfunctions themselves, allowing NRPy+ to take finite-difference derivatives of the expressions. This can even reduce the truncation error associated with the finite differences, because the alternative is to use a function of several finite-difference derivatives, allowing more error to accumulate than the extra gridfunction will introduce. While we will use that technique for some of the subexpressions of $T^{\mu\nu}_{\rm EM}$, we don't want to rely on it for the whole expression; doing so would require us to take the derivative of the magnetic field $B^i$, which is itself found by finite-differencing the vector potential $A_i$. Thus $B^i$ cannot be *consistently* defined in ghost zones. To potentially reduce numerical errors induced by inconsistent finite differencing, we will differentiate $T^{\mu\nu}_{\rm EM}$ term-by-term so that finite-difference derivatives of $A_i$ appear. # # We will now now take these spatial derivatives of $T^{\mu\nu}_{\rm EM}$, applying the chain rule until it is only in terms of basic gridfunctions and their derivatives: $\alpha$, $\beta^i$, $\gamma_{ij}$, $A_i$, and the four-velocity $u^i$. Along the way, we will also set up useful temporary variables representing the steps of the chain rule. (Notably, *all* of these quantities will be written in terms of $A_i$ and its derivatives): # # * $B^i$ (already computed in terms of $A_k$, via $B^i = \epsilon^{ijk} \partial_j A_k$), # * $B^i_{,l}$, # * $b^i$ and $b_i$ (already computed), # * $b^i_{,k}$, # * $b^2$ (already computed), # * and $\left(b^2\right)_{,j}$. # # (The variables not already computed will not be seen by the ETK, as they are written in terms of $A_i$ and its derivatives; they simply help to organize the NRPy+ code.) # # So then, # \begin{align} # \partial_j T^{j}_{{\rm EM} i} &= \partial_j (g_{\mu i} T^{\mu j}_{\rm EM}) \\ # &= \partial_j \left[g_{\mu i} \left(b^2 u^j u^\mu + \frac{b^2}{2} g^{j\mu} - b^j b^\mu\right)\right] \\ # &= \underbrace{g_{\mu i,j} T^{\mu j}_{\rm EM}}_{\rm Term\ A} + g_{\mu i} \left( \underbrace{\partial_j \left(b^2 u^j u^\mu \right)}_{\rm Term\ B} + \underbrace{\partial_j \left(\frac{b^2}{2} g^{j\mu}\right)}_{\rm Term\ C} - \underbrace{\partial_j \left(b^j b^k\right)}_{\rm Term\ D} \right) \\ # \end{align} # Following the product and chain rules for each term, we find that # \begin{align} # {\rm Term\ B} &= \partial_j (b^2 u^j u^\mu) \\ # &= \partial_j b^2 u^j u^\mu + b^2 \partial_j u^j u^\mu + b^2 u^j \partial_j u^\mu \\ # &= \underbrace{\left(b^2\right)_{,j} u^j u^\mu + b^2 u^j_{,j} u^\mu + b^2 u^j u^{\mu}_{,j}}_{\rm To\ Term\ 2\ below} \\ # {\rm Term\ C} &= \partial_j \left(\frac{b^2}{2} g^{j\mu}\right) \\ # &= \frac{1}{2} \left( \partial_j b^2 g^{j\mu} + b^2 \partial_j g^{j\mu} \right) \\ # &= \underbrace{\frac{1}{2} \left(b^2\right)_{,j} g^{j\mu} + \frac{b^2}{2} g^{j\mu}_{\ ,j}}_{\rm To\ Term\ 3\ below} \\ # {\rm Term\ D} &= \partial_j (b^j b^\mu) \\ # &= \underbrace{b^j_{,j} b^\mu + b^j b^\mu_{,j}}_{\rm To\ Term\ 2\ below}\\ # \end{align} # # So, # \begin{align} # \partial_j T^{j}_{{\rm EM} i} &= g_{\mu i,j} T^{\mu j}_{\rm EM} \\ # &+ g_{\mu i} \left(\left(b^2\right)_{,j} u^j u^\mu +b^2 u^j_{,j} u^\mu + b^2 u^j u^{\mu}_{,j} + \frac{1}{2}\left(b^2\right)_{,j} g^{j\mu} + \frac{b^2}{2} g^{j\mu}_{\ ,j} + b^j_{,j} b^\mu + b^j b^\mu_{,j}\right); # \end{align} # We will rearrange this once more, collecting the $b^2$ terms together, noting that Term A will become Term 1: # \begin{align} # \partial_j T^{j}_{{\rm EM} i} =& \ # \underbrace{g_{\mu i,j} T^{\mu j}_{\rm EM}}_{\rm Term\ 1} \\ # & + \underbrace{g_{\mu i} \left( b^2 u^j_{,j} u^\mu + b^2 u^j u^\mu_{,j} + \frac{b^2}{2} g^{j\mu}_{\ ,j} + b^j_{,j} b^\mu + b^j b^\mu_{,j} \right)}_{\rm Term\ 2} \\ # & + \underbrace{g_{\mu i} \left( \left(b^2\right)_{,j} u^j u^\mu + \frac{1}{2} \left(b^2\right)_{,j} g^{j\mu} \right).}_{\rm Term\ 3} \\ # \end{align} # # <a id='table2'></a> # # **List of Derivatives** # $$\label{table2}$$ # # Note that this is in terms of the derivatives of several other quantities: # # * [Step 2.2.a](#capitalBideriv): $B^i_{,l}$: Since $b^i$ is itself a function of $B^i$, we will first need the derivatives $B^i_{,l}$ in terms of the evolved quantity $A_i$ (the vector potential). # * [Step 2.2.b](#bideriv): $b^i_{,k}$: Once we have $B^i_{,l}$ we can evaluate derivatives of $b^i$, $b^i_{,k}$ # * [Step 2.2.c](#b2deriv): The derivative of $b^2 = g_{\mu\nu} b^\mu b^\nu$, $\left(b^2\right)_{,j}$ # * [Step 2.2.d](#gupijderiv): Derivatives of $g^{\mu\nu}$, $g^{\mu\nu}_{\ ,k}$ # * [Step 2.2.e](#alltogether): Putting it together: $\partial_j T^{j}_{{\rm EM} i}$ # * [Step 2.2.e.i](#alltogether1): Putting it together: Term 1 # * [Step 2.2.e.ii](#alltogether2): Putting it together: Term 2 # * [Step 2.2.e.iii](#alltogether3): Putting it together: Term 3 # <a id='capitalBideriv'></a> # # ## Step 2.2.a: Derivatives of $B^i$ # # $$\label{capitalbideriv}$$ # # \[Back to [List of Derivatives](#table2)\] # # First, we will build the derivatives of the magnetic field. Since $b^i$ is a function of $B^i$, we will start from the definition of $B^i$ in terms of $A_i$, $B^i = \frac{[ijk]}{\sqrt{\gamma}} \partial_j A_k$. We will first apply the product rule, noting that the symbol $[ijk]$ consists purely of the integers $-1, 0, 1$ and thus can be treated as a constant in this process. # \begin{align} # B^i_{,l} &= \partial_l \left( \frac{[ijk]}{\sqrt{\gamma}} \partial_j A_k \right) \\ # &= [ijk] \partial_l \left( \frac{1}{\sqrt{\gamma}}\right) \partial_j A_k + \frac{[ijk]}{\sqrt{\gamma}} \partial_l \partial_j A_k \\ # &= [ijk]\left(-\frac{\gamma_{,l}}{2\gamma^{3/2}}\right) \partial_j A_k + \frac{[ijk]}{\sqrt{\gamma}} \partial_l \partial_j A_k \\ # \end{align} # Now, we will substitute back in for the definition of the Levi-Civita tensor: $\epsilon^{ijk} = [ijk] / \sqrt{\gamma}$. Then we will substitute the magnetic field $B^i$ back in. # \begin{align} # B^i_{,l} &= -\frac{\gamma_{,l}}{2\gamma} \epsilon^{ijk} \partial_j A_k + \epsilon^{ijk} \partial_l \partial_j A_k \\ # &= -\frac{\gamma_{,l}}{2\gamma} B^i + \epsilon^{ijk} A_{k,jl}, \\ # \end{align} # # Thus, the expression we are left with for the derivatives of the magnetic field is: # \begin{align} # B^i_{,l} &= \underbrace{-\frac{\gamma_{,l}}{2\gamma} B^i}_{\rm Term\ 1} + \underbrace{\epsilon^{ijk} A_{k,jl}}_{\rm Term\ 2}, \\ # \end{align} # where $\epsilon^{ijk} = [ijk] / \sqrt{\gamma}$ is the antisymmetric Levi-Civita tensor and $\gamma$ is the determinant of the three-metric. # # Step 2.2: Derivatives of the electromagnetic stress-energy tensor # We already have a handy function to define the Levi-Civita symbol in WeylScalars import WeylScal4NRPy.WeylScalars_Cartesian as weyl # Initialize the Levi-Civita tensor by setting it equal to the Levi-Civita symbol LeviCivitaSymbolDDD = weyl.define_LeviCivitaSymbol_rank3() LeviCivitaTensorDDD = ixp.zerorank3() LeviCivitaTensorUUU = ixp.zerorank3() global gammaUU, gammadet gammaUU = ixp.register_gridfunctions_for_single_rank2( "AUX", "gammaUU", "sym01") gammadet = gri.register_gridfunctions("AUX", "gammadet") gammaUU, gammadet = ixp.symm_matrix_inverter3x3(gammaDD) for i in range(DIM): for j in range(DIM): for k in range(DIM): LeviCivitaTensorDDD[i][j][ k] = LeviCivitaSymbolDDD[i][j][k] * sp.sqrt(gammadet) LeviCivitaTensorUUU[i][j][ k] = LeviCivitaSymbolDDD[i][j][k] / sp.sqrt(gammadet) AD_dD = ixp.declarerank2("AD_dD", "nosym") # Step 2.2.a: Construct the derivatives of the magnetic field. gammadet_dD = ixp.declarerank1("gammadet_dD") AD_dDD = ixp.declarerank3("AD_dDD", "sym12") # The other partial derivatives of B^i BUdD = ixp.zerorank2() for i in range(DIM): for l in range(DIM): # Term 1: -\gamma_{,l} / (2\gamma) B^i BUdD[i][l] = -gammadet_dD[l] * BU[i] / (2 * gammadet) for i in range(DIM): for l in range(DIM): for j in range(DIM): for k in range(DIM): # Term 2: \epsilon^{ijk} A_{k,jl} BUdD[i][ l] += LeviCivitaTensorUUU[i][j][k] * AD_dDD[k][j][l] # <a id='bideriv'></a> # # ## Step 2.2.b: Derivatives of $b^i$ # $$\label{bideriv}$$ # # \[Back to [List of Derivatives](#table2)\] # # Now, we will code the derivatives of the spatial components of $b^{\mu}$, $b^i$: # $$ # b^i_{,k} = \frac{1}{\sqrt{4 \pi}} \frac{\left(\alpha u^0\right) \left(B^i_{,k} + u_{j,k} B^j u^i + u_j B^j_{,k} u^i + u_j B^j u^i_{,k}\right) - \left(B^i + (u_j B^j) u^i\right) \partial_k \left(\alpha u^0\right)}{\left(\alpha u^0\right)^2}. # $$ # # We should note that while $b^\mu$ is a four-vector (and the code reflects this: $\text{smallb4U}$ and $\text{smallb4U}$ have $\text{DIM=4}$), we only need the spatial components. We will only focus on the spatial components for the moment. # # # Let's go into a little more detail on where this comes from. We start from the definition $$b^i = \frac{B^i + (u_j B^j) u^i}{\sqrt{4\pi}\alpha u^0};$$ we then apply the quotient rule: # \begin{align} # b^i_{,k} &= \frac{\left(\sqrt{4\pi}\alpha u^0\right) \partial_k \left(B^i + (u_j B^j) u^i\right) - \left(B^i + (u_j B^j) u^i\right) \partial_k \left(\sqrt{4\pi}\alpha u^0\right)}{\left(\sqrt{4\pi}\alpha u^0\right)^2} \\ # &= \frac{1}{\sqrt{4 \pi}} \frac{\left(\alpha u^0\right) \partial_k \left(B^i + (u_j B^j) u^i\right) - \left(B^i + (u_j B^j) u^i\right) \partial_k \left(\alpha u^0\right)}{\left(\alpha u^0\right)^2} \\ # \end{align} # Note that $\left( \alpha u^0 \right)$ is being used as its own gridfunction, so $\partial_k \left(a u^0\right)$ will be finite-differenced by NRPy+ directly. We will also apply the product rule to the term $\partial_k \left(B^i + (u_j B^j) u^i\right) = B^i_{,k} + u_{j,k} B^j u^i + u_j B^j_{,k} u^i + u_j B^j u^i_{,k}$. So, # $$ b^i_{,k} = \frac{1}{\sqrt{4 \pi}} \frac{\left(\alpha u^0\right) \left(B^i_{,k} + u_{j,k} B^j u^i + u_j B^j_{,k} u^i + u_j B^j u^i_{,k}\right) - \left(B^i + (u_j B^j) u^i\right) \partial_k \left(\alpha u^0\right)}{\left(\alpha u^0\right)^2}. $$ # # It will be easier to code this up if we rearrange these terms to group together the terms that involve contractions over $j$. Doing that, we find # $$ # b^i_{,k} = \frac{\overbrace{\alpha u^0 B^i_{,k} - B^i \partial_k (\alpha u^0)}^{\rm Term\ Num1} + \overbrace{\left( \alpha u^0 \right) \left( u_{j,k} B^j u^i + u_j B^j_{,k} u^i + u_j B^j u^i_{,k} \right)}^{\rm Term\ Num2.a} - \overbrace{\left( u_j B^j u^i \right) \partial_k \left( \alpha u^0 \right) }^{\rm Term\ Num2.b}}{\underbrace{\sqrt{4 \pi} \left( \alpha u^0 \right)^2}_{\rm Term\ Denom}}. # $$ global u0alpha u0alpha = gri.register_gridfunctions("AUX", "u0alpha") u0alpha = alpha * u4upperZero u0alpha_dD = ixp.declarerank1("u0alpha_dD") uU_dD = ixp.declarerank2("uU_dD", "nosym") uD_dD = ixp.declarerank2("uD_dD", "nosym") # Step 2.2.b: Construct derivatives of the small b vector # smallbUdD represents the derivative of smallb4U smallbUdD = ixp.zerorank2() for i in range(DIM): for k in range(DIM): # Term Num1: \alpha u^0 B^i_{,k} - B^i \partial_k (\alpha u^0) smallbUdD[i][k] += u0alpha * BUdD[i][k] - BU[i] * u0alpha_dD[k] for i in range(DIM): for k in range(DIM): for j in range(DIM): # Term Num2.a: terms that require contractions over k, and thus an extra loop. # ( \alpha u^0 ) ( u_{j,k} B^j u^i # + u_j B^j_{,k} u^i # + u_j B^j u^i_{,k} ) smallbUdD[i][k] += u0alpha * (uD_dD[j][k] * BU[j] * uU[i] + uD[j] * BUdD[j][k] * uU[i] + uD[j] * BU[j] * uU_dD[i][k]) for i in range(DIM): for k in range(DIM): for j in range(DIM): #Term 2.b (More contractions over k): ( u_j B^j u^i ) ( \alpha u^0 ),k smallbUdD[i][k] += -(uD[j] * BU[j] * uU[i]) * u0alpha_dD[k] for i in range(DIM): for k in range(DIM): # Term Denom: Divide the numerator by sqrt(4 pi) * (alpha u^0)^2 smallbUdD[i][k] /= sp.sqrt(4 * M_PI) * u0alpha * u0alpha # <a id='b2deriv'></a> # # ## Step 2.2.c: Derivative of $b^2$ # $$\label{b2deriv}$$ # # \[Back to [List of Derivatives](#table2)\] # # Here, we will take the derivative of $b^2 = g_{\mu\nu} b^\mu b^\nu$. Using the product rule, # \begin{align} # \left(b^2\right)_{,j} &= \partial_j \left( g_{\mu\nu} b^\mu b^\nu \right) \\ # &= g_{\mu\nu,j} b^\mu b^\nu + g_{\mu\nu} b^\mu_{,j} b^\nu + g_{\mu\nu} b^\mu b^\nu_{,j} \\ # &= g_{\mu\nu,j} b^\mu b^\nu + 2 g_{\mu\nu} b^\mu_{,j} b^\nu. # \end{align} # We have already defined the spatial derivatives of the four-metric $g_{\mu\nu,j}$ in [this section](#step3); we have also defined the spatial derivatives of spatial components of $b^\mu$, $b^i_{,k}$ in [this section](#bideriv). Notice the above expression requires spatial derivatives of the *zeroth* component of $b^\mu$ as well, $b^0_{,j}$, which we will now compute. Starting with the definition, and applying the quotient rule: # \begin{align} # b^0 &= \frac{u_k B^k}{\sqrt{4\pi}\alpha}, \\ # \rightarrow b^0_{,j} &= \frac{1}{\sqrt{4\pi}} \frac{\alpha \left( u_{k,j} B^k + u_k B^k_{,j} \right) - u_k B^k \alpha_{,j}}{\alpha^2} \\ # &= \frac{\alpha u_{k,j} B^k + \alpha u_k B^k_{,j} - \alpha_{,j} u_k B^k}{\sqrt{4\pi} \alpha^2}. # \end{align} # We will first code the numerator, and then divide through by the denominator. # Step 2.2.c: Construct the derivative of b^2 # First construct the derivative b^0_{,j} # This four-vector will make b^2 simpler: smallb4UdD = ixp.zerorank2(DIM=4) # Fill in the zeroth component for j in range(DIM): for k in range(DIM): # The numerator: \alpha u_{k,j} B^k # + \alpha u_k B^k_{,j} # - \alpha_{,j} u_k B^k smallb4UdD[0][j + 1] += alpha * uD_dD[k][j] * BU[k] + alpha * uD[ k] * BUdD[k][j] - alpha_dD[j] * uD[k] * BU[k] for j in range(DIM): # Divide through by the denominator: \sqrt{4\pi} \alpha^2 smallb4UdD[0][j + 1] /= sp.sqrt(4 * M_PI) * alpha * alpha # At this point, both $b^0_{\ ,j}$ and $b^i_{\ ,j}$ have been computed, but one exists inconveniently in the $4\times 4$ component $\verb|smallb4UdD[][]|$ and the other in the $3\times 3$ component $\verb|smallbUdD[][]|$. So that we can perform full implied sums over $g_{\mu\nu} b^\mu_{,j} b^\nu$ more conveniently, we will now store all information from $\verb|smallbUdD[i][j]|$ into $\verb|smallb4UdD[i+1][j+1]|$: # Now, we'll fill out the rest of the four-vector with b^i_{,j} that we derived above. for i in range(DIM): for j in range(DIM): smallb4UdD[i + 1][j + 1] = smallbUdD[i][j] # Using 4-component (Greek-indexed) quantities, we can now complete our construction of # $$\left(b^2\right)_{,j} = g_{\mu\nu,j} b^\mu b^\nu + 2 g_{\mu\nu} b^\mu_{,j} b^\nu:$$ smallb2_dD = ixp.zerorank1() for j in range(DIM): for mu in range(4): for nu in range(4): # g_{\mu\nu,j} b^\mu b^\nu # + 2 g_{\mu\nu} b^\mu_{,j} b^\nu smallb2_dD[j] += g4DDdD[mu][nu][j + 1] * smallb4U[ mu] * smallb4U[nu] + 2 * g4DD[mu][nu] * smallb4UdD[mu][ j + 1] * smallb4U[nu] # <a id='gupijderiv'></a> # # ## Step 2.2.d: Derivatives of $g^{\mu\nu}$ # $$\label{gupijderiv}$$ # # \[Back to [List of Derivatives](#table2)\] # # We will need derivatives of the inverse four-metric, as well. Let us begin with $g^{00}$: since $g^{00} = -1/\alpha^2$ ([Gourgoulhon, eq. 4.49](https://arxiv.org/pdf/gr-qc/0703035.pdf)), $$g^{00}_{\ ,k} = \frac{2 \alpha_{,k}}{\alpha^3}$$ # # Step 2.2.d: Construct derivatives of the components of g^{\mu\nu} g4UUdD = ixp.zerorank3(DIM=4) for k in range(DIM): # 2 \alpha_{,k} / \alpha^3 g4UUdD[0][0][k + 1] = 2 * alpha_dD[k] / alpha**3 # Now, we will code the $g^{i0}_{\ ,k}$ and $g^{0j}_{\ ,k}$ components. According to [Gourgoulhon, eq. 4.49](https://arxiv.org/pdf/gr-qc/0703035.pdf), $g^{i0} = g^{0i} = \beta^i/\alpha^2$, so $$g^{i0}_{\ ,k} = g^{0i}_{\ ,k} = \frac{\alpha^2 \beta^i_{,k} - 2 \beta^i \alpha \alpha_{,k}}{\alpha^4}$$ by the quotient rule. So, we'll code # $$ # g^{i0} = g^{0i} = # \underbrace{\frac{\beta^i_{,k}}{\alpha^2}}_{\rm Term\ 1} # - \underbrace{\frac{2 \beta^i \alpha_{,k}}{\alpha^3}}_{\rm Term\ 2} # $$ for k in range(DIM): for i in range(DIM): # Term 1: \beta^i_{,k} / \alpha^2 g4UUdD[i + 1][0][k + 1] = g4UUdD[0][i + 1][k + 1] = betaU_dD[i][k] / alpha**2 for k in range(DIM): for i in range(DIM): # Term 2: -2 \beta^i \alpha_{,k} / \alpha^3 g4UUdD[i + 1][0][k + 1] += -2 * betaU[i] * alpha_dD[k] / alpha**3 g4UUdD[0][i + 1][k + 1] += -2 * betaU[i] * alpha_dD[k] / alpha**3 # We will also need derivatives of the spatial part of the inverse four-metric: since $g^{ij} = \gamma^{ij} - \frac{\beta^i \beta^j}{\alpha^2}$ ([Gourgoulhon, eq. 4.49](https://arxiv.org/pdf/gr-qc/0703035.pdf)), # \begin{align} # g^{ij}_{\ ,k} &= \gamma^{ij}_{\ ,k} - \frac{\alpha^2 \partial_k (\beta^i \beta^j) - \beta^i \beta^j \partial_k \alpha^2}{(\alpha^2)^2} \\ # &= \gamma^{ij}_{\ ,k} - \frac{\alpha^2\beta^i \beta^j_{,k}+\alpha^2\beta^i_{,k} \beta^j-2\beta^i \beta^j \alpha \alpha_{,k}}{\alpha^4}. \\ # &= \gamma^{ij}_{\ ,k} - \frac{\alpha\beta^i \beta^j_{,k}+\alpha\beta^i_{,k} \beta^j-2\beta^i \beta^j \alpha_{,k}}{\alpha^3} \\ # g^{ij}_{\ ,k} &= \underbrace{\gamma^{ij}_{\ ,k}}_{\rm Term\ 1} - \underbrace{\frac{\beta^i \beta^j_{,k}}{\alpha^2}}_{\rm Term\ 2} - \underbrace{\frac{\beta^i_{,k} \beta^j}{\alpha^2}}_{\rm Term\ 3} + \underbrace{\frac{2\beta^i \beta^j \alpha_{,k}}{\alpha^3}}_{\rm Term\ 4}. \\ # \end{align} # gammaUU_dD = ixp.declarerank3("gammaUU_dD", "sym01") # The spatial derivatives of the spatial components of the four metric: # Term 1: \gamma^{ij}_{\ ,k} for i in range(DIM): for j in range(DIM): for k in range(DIM): g4UUdD[i + 1][j + 1][k + 1] = gammaUU_dD[i][j][k] # Term 2: - \beta^i \beta^j_{,k} / \alpha^2 for i in range(DIM): for j in range(DIM): for k in range(DIM): g4UUdD[i + 1][j + 1][k + 1] += -betaU[i] * betaU_dD[j][k] / alpha**2 # Term 3: - \beta^i_{,k} \beta^j / \alpha^2 for i in range(DIM): for j in range(DIM): for k in range(DIM): g4UUdD[i + 1][j + 1][k + 1] += -betaU_dD[i][k] * betaU[j] / alpha**2 # Term 4: 2\beta^i \beta^j \alpha_{,k}\alpha^3 for i in range(DIM): for j in range(DIM): for k in range(DIM): g4UUdD[i + 1][j + 1][ k + 1] += 2 * betaU[i] * betaU[j] * alpha_dD[k] / alpha**3 # <a id='alltogether'></a> # # ## Step 2.2.e: Putting it together: # $$\label{alltogether}$$ # # \[Back to [List of Derivatives](#table2)\] # # So, we can now put it all together, starting from the expression we derived above in [Step 2.2](#step6): # \begin{align} # \partial_j T^{j}_{{\rm EM} i} =& \ # \underbrace{g_{\mu i,j} T^{\mu j}_{\rm EM}}_{\rm Term\ 1} \\ # & + \underbrace{g_{\mu i} \left( b^2 u^j_{,j} u^\mu + b^2 u^j u^\mu_{,j} + \frac{b^2}{2} g^{j\mu}_{\ ,j} + b^j_{,j} b^\mu + b^j b^\mu_{,j} \right)}_{\rm Term\ 2} \\ # & + \underbrace{g_{\mu i} \left( \left(b^2\right)_{,j} u^j u^\mu + \frac{1}{2} \left(b^2\right)_{,j} g^{j\mu} \right).}_{\rm Term\ 3} \\ # \end{align} # # <a id='alltogether1'></a> # # ### Step 2.2.e.i: Putting it together: Term 1 # $$\label{alltogether1}$$ # # \[Back to [List of Derivatives](#table2)\] # # We will now construct this term by term. Term 1 is straightforward: $${\rm Term\ 1} = \gamma_{\mu i,j} T^{\mu j}_{\rm EM}.$$ # Step 2.2.e: Construct TEMUDdD_contracted itself # Step 2.2.e.i TEMUDdD_contracted = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for mu in range(4): # Term 1: g_{\mu i,j} T^{\mu j}_{\rm EM} TEMUDdD_contracted[i] += g4DDdD[mu][i + 1][j + 1] * T4EMUU[mu][j + 1] # We'll need derivatives of u4U for the next part: u4UdD = ixp.zerorank2(DIM=4) u4upperZero_dD = ixp.declarerank1( "u4upperZero_dD" ) # Note that derivatives can't be done in 4-D with the current version of NRPy for i in range(DIM): u4UdD[0][i + 1] = u4upperZero_dD[i] for i in range(DIM): for j in range(DIM): u4UdD[i + 1][j + 1] = uU_dD[i][j] # <a id='alltogether2'></a> # # ### Step 2.2.e.ii: Putting it together: Term 2 # $$\label{alltogether2}$$ # # \[Back to [List of Derivatives](#table2)\] # # We will now add $${\rm Term\ 2} = g_{\mu i} \left( \underbrace{b^2 u^j_{,j} u^\mu}_{\rm Term\ 2a} + \underbrace{b^2 u^j u^\mu_{,j}}_{\rm Term\ 2b} + \underbrace{\frac{b^2}{2} g^{j\mu}_{\ ,j}}_{\rm Term\ 2c} + \underbrace{b^j_{,j} b^\mu}_{\rm Term\ 2d} + \underbrace{b^j b^\mu_{,j}}_{\rm Term\ 2e} \right)$$ to $\partial_j T^{j}_{{\rm EM} i}$. These are the terms that involve contractions over $k$ (but no metric derivatives like Term 1 had). # # Step 2.2.e.ii for i in range(DIM): for j in range(DIM): for mu in range(4): # Term 2a: g_{\mu i} b^2 u^j_{,j} u^\mu TEMUDdD_contracted[i] += g4DD[mu][ i + 1] * smallb2 * uU_dD[j][j] * u4U[mu] for i in range(DIM): for j in range(DIM): for mu in range(4): # Term 2b: g_{\mu i} b^2 u^j u^\mu_{,j} TEMUDdD_contracted[i] += g4DD[mu][ i + 1] * smallb2 * uU[j] * u4UdD[mu][j + 1] for i in range(DIM): for j in range(DIM): for mu in range(4): # Term 2c: g_{\mu i} b^2 g^{j \mu}_{,j} / 2 TEMUDdD_contracted[i] += g4DD[mu][i + 1] * smallb2 * g4UUdD[ j + 1][mu][j + 1] / 2 for i in range(DIM): for j in range(DIM): for mu in range(4): # Term 2d: g_{\mu i} b^j_{,j} b^\mu TEMUDdD_contracted[i] += g4DD[mu][ i + 1] * smallbUdD[j][j] * smallb4U[mu] for i in range(DIM): for j in range(DIM): for mu in range(4): # Term 2e: g_{\mu i} b^j b^\mu_{,j} TEMUDdD_contracted[i] += g4DD[mu][i + 1] * smallb4U[ j + 1] * smallb4UdD[mu][j + 1] # <a id='alltogether3'></a> # # ### Step 2.2.e.iii: Putting it together: Term 3 # $$\label{alltogether3}$$ # # \[Back to [List of Derivatives](#table2)\] # # Now, we will add $${\rm Term\ 3} = g_{\mu i} \left( \underbrace{\left(b^2\right)_{,j} u^j u^\mu}_{\rm Term\ 3a} + \underbrace{\frac{1}{2} \left(b^2\right)_{,j} g^{j\mu}}_{\rm Term\ 3b} \right).$$ # Step 2.2.e.iii for i in range(DIM): for j in range(DIM): for mu in range(4): # Term 3a: g_{\mu i} ( b^2 )_{,j} u^j u^\mu TEMUDdD_contracted[i] += g4DD[mu][ i + 1] * smallb2_dD[j] * uU[j] * u4U[mu] for i in range(DIM): for j in range(DIM): for mu in range(4): # Term 3b: g_{mu i} ( b^2 )_{,j} g^{j\mu} / 2 TEMUDdD_contracted[i] += g4DD[mu][ i + 1] * smallb2_dD[j] * g4UU[j + 1][mu] / 2 # # # Evolution equation for $\tilde{S}_i$ # <a id='step7'></a> # # ## Step 3.0: Construct the evolution equation for $\tilde{S}_i$ # $$\label{step7}$$ # # \[Back to [top](#top)\] # # Finally, we will return our attention to the time evolution equation (from eq. 13 of the [original paper](https://arxiv.org/pdf/1704.00599.pdf)), # \begin{align} # \partial_t \tilde{S}_i &= - \partial_j \left( \alpha \sqrt{\gamma} T^j_{{\rm EM} i} \right) + \frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu} \\ # &= -T^j_{{\rm EM} i} \partial_j (\alpha \sqrt{\gamma}) - \alpha \sqrt{\gamma} \partial_j T^j_{{\rm EM} i} + \frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu} \\ # &= \underbrace{-g_{i\mu} T^{\mu j}_{\rm EM} \partial_j (\alpha \sqrt{\gamma}) # }_{\rm Term\ 1} - \underbrace{\alpha \sqrt{\gamma} \partial_j T^j_{{\rm EM} i}}_{\rm Term\ 2} + \underbrace{\frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu}}_{\rm Term\ 3} . # \end{align} # We will first take derivatives of $\alpha \sqrt{\gamma}$, then construct each term in turn. # Step 3.0: Construct the evolution equation for \tilde{S}_i # Here, we set up the necessary machinery to take FD derivatives of alpha * sqrt(gamma) global alpsqrtgam alpsqrtgam = gri.register_gridfunctions("AUX", "alpsqrtgam") alpsqrtgam = alpha * sp.sqrt(gammadet) alpsqrtgam_dD = ixp.declarerank1("alpsqrtgam_dD") global Stilde_rhsD Stilde_rhsD = ixp.zerorank1() # The first term: g_{i\mu} T^{\mu j}_{\rm EM} \partial_j (\alpha \sqrt{\gamma}) for i in range(DIM): for j in range(DIM): for mu in range(4): Stilde_rhsD[i] += -g4DD[i + 1][mu] * T4EMUU[mu][ j + 1] * alpsqrtgam_dD[j] # The second term: \alpha \sqrt{\gamma} \partial_j T^j_{{\rm EM} i} for i in range(DIM): Stilde_rhsD[i] += -alpsqrtgam * TEMUDdD_contracted[i] # The third term: \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu} / 2 for i in range(DIM): for mu in range(4): for nu in range(4): Stilde_rhsD[i] += alpsqrtgam * T4EMUU[mu][nu] * g4DDdD[mu][nu][ i + 1] / 2 # # Evolution equations for $A_i$ and $\Phi$ # <a id='step8'></a> # # ## Step 4.0: Construct the evolution equations for $A_i$ and $[\sqrt{\gamma}\Phi]$ # $$\label{step8}$$ # # \[Back to [top](#top)\] # # We will also need to evolve the vector potential $A_i$. This evolution is given as eq. 17 in the [$\texttt{GiRaFFE}$](https://arxiv.org/pdf/1704.00599.pdf) paper: # $$\boxed{\partial_t A_i = \epsilon_{ijk} v^j B^k - \partial_i (\underbrace{\alpha \Phi - \beta^j A_j}_{\rm AevolParen}),}$$ # where $\epsilon_{ijk} = [ijk] \sqrt{\gamma}$ is the antisymmetric Levi-Civita tensor, the drift velocity $v^i = u^i/u^0$, $\gamma$ is the determinant of the three metric, $B^k$ is the magnetic field, $\alpha$ is the lapse, and $\beta$ is the shift. # The scalar electric potential $\Phi$ is also evolved by eq. 19: # $$\boxed{\partial_t [\sqrt{\gamma} \Phi] = -\partial_j (\underbrace{\alpha\sqrt{\gamma}A^j - \beta^j [\sqrt{\gamma} \Phi]}_{\rm PevolParenU[j]}) - \xi \alpha [\sqrt{\gamma} \Phi],}$$ # with $\xi$ chosen as a damping factor. # # ### Step 4.0.a: Construct some useful auxiliary gridfunctions for the other evolution equations # # After declaring a some needed quantities, we will also define the parenthetical terms (underbrace above) that we need to take derivatives of. That way, we can take finite-difference derivatives easily. Note that we use $A^j = \gamma^{ij} A_i$, while $A_i$ (with $\Phi$) is technically a four-vector; this is justified, however, since $n_\mu A^\mu = 0$, where $n_\mu$ is a normal to the hypersurface, $A^0=0$ (according to Sec. II, subsection C of [this paper](https://arxiv.org/pdf/1110.4633.pdf)). # Step 4.0: Construct the evolution equations for A_i and sqrt(gamma)Phi # Step 4.0.a: Construct some useful auxiliary gridfunctions for the other evolution equations xi = par.Cparameters( "REAL", thismodule, "xi", 0.1 ) # The (dimensionful) Lorenz damping factor. Recommendation: set to ~1.5/max(delta t). # Define sqrt(gamma)Phi as psi6Phi psi6Phi = gri.register_gridfunctions("EVOL", "psi6Phi") Phi = psi6Phi / sp.sqrt(gammadet) # We'll define a few extra gridfunctions to avoid complicated derivatives global AevolParen, PevolParenU AevolParen = gri.register_gridfunctions("AUX", "AevolParen") PevolParenU = ixp.register_gridfunctions_for_single_rank1( "AUX", "PevolParenU") # {\rm AevolParen} = \alpha \Phi - \beta^j A_j AevolParen = alpha * Phi for j in range(DIM): AevolParen += -betaU[j] * AD[j] # {\rm PevolParenU[j]} = \alpha\sqrt{\gamma} \gamma^{ij} A_i - \beta^j [\sqrt{\gamma} \Phi] for j in range(DIM): PevolParenU[j] = -betaU[j] * psi6Phi for i in range(DIM): PevolParenU[j] += alpha * sp.sqrt(gammadet) * gammaUU[i][j] * AD[i] AevolParen_dD = ixp.declarerank1("AevolParen_dD") PevolParenU_dD = ixp.declarerank2("PevolParenU_dD", "nosym") # ### Step 4.0.b: Construct the actual evolution equations for $A_i$ and $[\sqrt{\gamma}\Phi]$ # # Now to set the evolution equations ([eqs. 17 and 19](https://arxiv.org/pdf/1704.00599.pdf)), recalling that the drift velocity $v^i = u^i/u^0$: # \begin{align} # \partial_t A_i &= \epsilon_{ijk} v^j B^k - \partial_i (\alpha \Phi - \beta^j A_j) \\ # &= \epsilon_{ijk} \frac{u^j}{u^0} B^k - {\rm AevolParen\_dD[i]} \\ # \partial_t [\sqrt{\gamma} \Phi] &= -\partial_j \left(\left(\alpha\sqrt{\gamma}\right)A^j - \beta^j [\sqrt{\gamma} \Phi]\right) - \xi \alpha [\sqrt{\gamma} \Phi] \\ # &= -{\rm PevolParenU\_dD[j][j]} - \xi \alpha [\sqrt{\gamma} \Phi]. \\ # \end{align} # Step 4.0.b: Construct the actual evolution equations for A_i and sqrt(gamma)Phi global A_rhsD, psi6Phi_rhs A_rhsD = ixp.zerorank1() psi6Phi_rhs = sp.sympify(0) for i in range(DIM): A_rhsD[i] = -AevolParen_dD[i] for j in range(DIM): for k in range(DIM): A_rhsD[i] += LeviCivitaTensorDDD[i][j][k] * ( uU[j] / u4upperZero) * BU[k] psi6Phi_rhs = -xi * alpha * psi6Phi for j in range(DIM): psi6Phi_rhs += -PevolParenU_dD[j][j]
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 GiRaFFE_HO_A2B(outdir): # Register the gridfunction gammadet. This determinant will be calculated separately gammadet = gri.register_gridfunctions("AUXEVOL", "gammadet") # Import the Levi-Civita symbol and build the corresponding tensor. # We already have a handy function to define the Levi-Civita symbol in WeylScalars import WeylScal4NRPy.WeylScalars_Cartesian as weyl LeviCivitaDDD = weyl.define_LeviCivitaSymbol_rank3() LeviCivitaUUU = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): LCijk = LeviCivitaDDD[i][j][k] #LeviCivitaDDD[i][j][k] = LCijk * sp.sqrt(gho.gammadet) LeviCivitaUUU[i][j][k] = LCijk / sp.sqrt(gammadet) # We can use this function to compactly reset to expressions to print at each FD order. def set_BU_to_print(): return [lhrh(lhs=gri.gfaccess("out_gfs","BU0"),rhs=BU[0]),\ lhrh(lhs=gri.gfaccess("out_gfs","BU1"),rhs=BU[1]),\ lhrh(lhs=gri.gfaccess("out_gfs","BU2"),rhs=BU[2])] AD = ixp.register_gridfunctions_for_single_rank1("EVOL", "AD") BU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "BU") AD_dD = ixp.declarerank2("AD_dD", "nosym") BU = ixp.zerorank1( ) # BU is already registered as a gridfunction, but we need to zero its values and declare it in this scope. for i in range(DIM): for j in range(DIM): for k in range(DIM): BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j] par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", 10) fin.FD_outputC(os.path.join(outdir, "B_from_A_order10.h"), set_BU_to_print(), params="outCverbose=False") par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", 8) fin.FD_outputC(os.path.join(outdir, "B_from_A_order8.h"), set_BU_to_print(), params="outCverbose=False") par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", 6) fin.FD_outputC(os.path.join(outdir, "B_from_A_order6.h"), set_BU_to_print(), params="outCverbose=False") par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", 4) fin.FD_outputC(os.path.join(outdir, "B_from_A_order4.h"), set_BU_to_print(), params="outCverbose=False") par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", 2) fin.FD_outputC(os.path.join(outdir, "B_from_A_order2.h"), set_BU_to_print(), params="outCverbose=False") # For the outermost points, we'll need a separate file for each face. # These will correspond to an upwinded and a downwinded file for each direction. AD_ddnD = ixp.declarerank2("AD_ddnD", "nosym") for i in range(DIM): BU[i] = 0 for j in range(DIM): for k in range(DIM): if j == 0: BU[i] += LeviCivitaUUU[i][j][k] * AD_ddnD[k][j] else: BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j] fin.FD_outputC(os.path.join(outdir, "B_from_A_order2_dirx0_dnwind.h"), set_BU_to_print(), params="outCverbose=False") AD_dupD = ixp.declarerank2("AD_dupD", "nosym") for i in range(DIM): BU[i] = 0 for j in range(DIM): for k in range(DIM): if j == 0: BU[i] += LeviCivitaUUU[i][j][k] * AD_dupD[k][j] else: BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j] fin.FD_outputC(os.path.join(outdir, "B_from_A_order2_dirx0_upwind.h"), set_BU_to_print(), params="outCverbose=False") for i in range(DIM): BU[i] = 0 for j in range(DIM): for k in range(DIM): if j == 1: BU[i] += LeviCivitaUUU[i][j][k] * AD_ddnD[k][j] else: BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j] fin.FD_outputC(os.path.join(outdir, "B_from_A_order2_dirx1_dnwind.h"), set_BU_to_print(), params="outCverbose=False") for i in range(DIM): BU[i] = 0 for j in range(DIM): for k in range(DIM): if j == 1: BU[i] += LeviCivitaUUU[i][j][k] * AD_dupD[k][j] else: BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j] fin.FD_outputC(os.path.join(outdir, "B_from_A_order2_dirx1_upwind.h"), set_BU_to_print(), params="outCverbose=False") for i in range(DIM): BU[i] = 0 for j in range(DIM): for k in range(DIM): if j == 2: BU[i] += LeviCivitaUUU[i][j][k] * AD_ddnD[k][j] else: BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j] fin.FD_outputC(os.path.join(outdir, "B_from_A_order2_dirx2_dnwind.h"), set_BU_to_print(), params="outCverbose=False") for i in range(DIM): BU[i] = 0 for j in range(DIM): for k in range(DIM): if j == 2: BU[i] += LeviCivitaUUU[i][j][k] * AD_dupD[k][j] else: BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j] fin.FD_outputC(os.path.join(outdir, "B_from_A_order2_dirx2_upwind.h"), set_BU_to_print(), params="outCverbose=False")