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 declare_BSSN_gridfunctions_if_not_declared_already(): # Step 2: Register all needed BSSN gridfunctions. # Declare as globals all variables that may be # used outside this function global hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha # Check to see if this function has already been called. # If so, do not register the gridfunctions again! for i in range(len(gri.glb_gridfcs_list)): if "hDD00" in gri.glb_gridfcs_list[i].name: hDD = ixp.declarerank2("hDD", "sym01") aDD = ixp.declarerank2("aDD", "sym01") lambdaU = ixp.declarerank1("lambdaU") vetU = ixp.declarerank1("vetU") betU = ixp.declarerank1("betU") trK, cf, alpha = sp.symbols('trK cf alpha', real=True) return hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha # Step 2.a: Register indexed quantities, using ixp.register_... functions hDD = ixp.register_gridfunctions_for_single_rank2("EVOL", "hDD", "sym01") aDD = ixp.register_gridfunctions_for_single_rank2("EVOL", "aDD", "sym01") lambdaU = ixp.register_gridfunctions_for_single_rank1("EVOL", "lambdaU") vetU = ixp.register_gridfunctions_for_single_rank1("EVOL", "vetU") betU = ixp.register_gridfunctions_for_single_rank1("EVOL", "betU") # Step 2.b: Register scalar quantities, using gri.register_gridfunctions() trK, cf, alpha = gri.register_gridfunctions("EVOL", ["trK", "cf", "alpha"]) return hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha
def BSSN_source_terms_for_BSSN_constraints(custom_T4UU=None): global sourceterm_H, sourceterm_MU # Step 4.a: Call BSSN_source_terms_ito_T4UU to get SDD, SD, S, & rho if custom_T4UU == "unrescaled BSSN source terms already given": # SDD and S unused, so we ignore their return values from ixp.declarerankN() below ixp.declarerank2("SDD", "sym01") SD = ixp.declarerank1("SD") sp.symbols("S", real=True) rho = sp.symbols("rho", real=True) else: _SDD, SD, _S, rho = stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars( "BSSN", custom_T4UU) #_SDD,_S unused. PI = par.Cparameters("REAL", thismodule, ["PI"], "3.14159265358979323846264338327950288") # Step 4.b: Add source term to the Hamiltonian constraint H sourceterm_H = -16 * PI * rho # Step 4.c: Add source term to the momentum constraint M^i # Step 4.c.i: Compute gammaUU in terms of BSSN quantities import BSSN.ADM_in_terms_of_BSSN as AitoB AitoB.ADM_in_terms_of_BSSN() # Provides gammaUU # Step 4.c.ii: Raise S_i SU = ixp.zerorank1() for i in range(3): for j in range(3): SU[i] += AitoB.gammaUU[i][j] * SD[j] # Step 4.c.iii: Add source term to momentum constraint & rescale: sourceterm_MU = ixp.zerorank1() for i in range(3): sourceterm_MU[i] = -8 * PI * SU[i] / rfm.ReU[i]
def trK_AbarDD_aDD(gammaDD, KDD): global trK, AbarDD, aDD if gammaDD == None: gammaDD = ixp.declarerank2("gammaDD", "sym01") if KDD == None: KDD = ixp.declarerank2("KDD", "sym01") if rfm.have_already_called_reference_metric_function == False: print( "BSSN.BSSN_in_terms_of_ADM.trK_AbarDD(): Must call reference_metric() first!" ) sys.exit(1) # \bar{gamma}_{ij} = (\frac{\bar{gamma}}{gamma})^{1/3}*gamma_{ij}. gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD) # K = gamma^{ij} K_{ij}, and # \bar{A}_{ij} &= (\frac{\bar{gamma}}{gamma})^{1/3}*(K_{ij} - \frac{1}{3}*gamma_{ij}*K) trK = sp.sympify(0) for i in range(DIM): for j in range(DIM): trK += gammaUU[i][j] * KDD[i][j] AbarDD = ixp.zerorank2() aDD = 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) aDD[i][j] = AbarDD[i][j] / rfm.ReDD[i][j]
def generate_everything_for_UnitTesting(): # First define hydrodynamical quantities u4U = ixp.declarerank1("u4U", DIM=4) rho_b, P, epsilon = sp.symbols('rho_b P epsilon', real=True) # Then ADM quantities gammaDD = ixp.declarerank2("gammaDD", "sym01", DIM=3) KDD = ixp.declarerank2("KDD", "sym01", DIM=3) betaU = ixp.declarerank1("betaU", DIM=3) alpha = sp.symbols('alpha', real=True) # First compute stress-energy tensor T4UU and T4UD: compute_T4UU(gammaDD, betaU, alpha, rho_b, P, epsilon, u4U) compute_T4UD(gammaDD, betaU, alpha, T4UU) # Next sqrt(gamma) compute_sqrtgammaDET(gammaDD) # Compute conservative variables in terms of primitive variables compute_rho_star(alpha, sqrtgammaDET, rho_b, u4U) compute_tau_tilde(alpha, sqrtgammaDET, T4UU, rho_star) compute_S_tildeD(alpha, sqrtgammaDET, T4UD) # Then compute v^i from u^mu compute_vU_from_u4U__no_speed_limit(u4U) # Next compute fluxes of conservative variables compute_rho_star_fluxU(vU, rho_star) compute_tau_tilde_fluxU(alpha, sqrtgammaDET, vU, T4UU, rho_star) compute_S_tilde_fluxUD(alpha, sqrtgammaDET, T4UD) # Then declare derivatives & compute g4DD_zerotimederiv_dD gammaDD_dD = ixp.declarerank3("gammaDD_dD", "sym01", DIM=3) betaU_dD = ixp.declarerank2("betaU_dD", "nosym", DIM=3) alpha_dD = ixp.declarerank1("alpha_dD", DIM=3) compute_g4DD_zerotimederiv_dD(gammaDD, betaU, alpha, gammaDD_dD, betaU_dD, alpha_dD) # Then compute source terms on tau_tilde and S_tilde equations compute_s_source_term(KDD, betaU, alpha, sqrtgammaDET, alpha_dD, T4UU) compute_S_tilde_source_termD(alpha, sqrtgammaDET, g4DD_zerotimederiv_dD, T4UU) # Then compute the 4-velocities in terms of an input Valencia 3-velocity testValenciavU[i] testValenciavU = ixp.declarerank1("testValenciavU", DIM=3) u4U_in_terms_of_ValenciavU__rescale_ValenciavU_by_applying_speed_limit( alpha, betaU, gammaDD, testValenciavU) # Finally compute the 4-velocities in terms of an input 3-velocity testvU[i] = u^i/u^0 testvU = ixp.declarerank1("testvU", DIM=3) u4U_in_terms_of_vU__rescale_vU_by_applying_speed_limit( alpha, betaU, gammaDD, testvU)
def BSSN_or_ADM_ito_g4DD(inputvars, g4DD=None): # Step 0: Declare output variables as globals, to make interfacing with other modules/functions easier if inputvars == "ADM": global gammaDD, betaU, alpha elif inputvars == "BSSN": global hDD, cf, vetU, alpha else: print("inputvars = " + str(inputvars) + " not supported. Please choose ADM or BSSN.") sys.exit(1) # Step 1: declare g4DD as symmetric rank-4 tensor: g4DD_is_input_into_this_function = True if g4DD == None: g4DD = ixp.declarerank2("g4DD", "sym01", DIM=4) g4DD_is_input_into_this_function = False # Step 2: Compute gammaDD & betaD betaD = ixp.zerorank1() gammaDD = ixp.zerorank2() for i in range(3): betaD[i] = g4DD[0][i] for j in range(3): gammaDD[i][j] = g4DD[i + 1][j + 1] # Step 3: Compute betaU # Step 3.a: Compute gammaUU based on provided gammaDD gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD) # Step 3.b: Use gammaUU to raise betaU betaU = ixp.zerorank1() for i in range(3): for j in range(3): betaU[i] += gammaUU[i][j] * betaD[j] # Step 4: Compute alpha = sqrt(beta^2 - g_{00}): # Step 4.a: Compute beta^2 = beta^k beta_k: beta_squared = sp.sympify(0) for k in range(3): beta_squared += betaU[k] * betaD[k] # Step 4.b: alpha = sqrt(beta^2 - g_{00}): if g4DD_is_input_into_this_function == False: alpha = sp.sqrt(sp.simplify(beta_squared) - g4DD[0][0]) else: alpha = sp.sqrt(beta_squared - g4DD[0][0]) # Step 5: If inputvars == "ADM", we are finished. Return. if inputvars == "ADM": return # Step 6: If inputvars == "BSSN", convert ADM to BSSN import BSSN.BSSN_in_terms_of_ADM as BitoA dummyBU = ixp.zerorank1() BitoA.gammabarDD_hDD(gammaDD) BitoA.cf_from_gammaDD(gammaDD) BitoA.betU_vetU(betaU, dummyBU) hDD = BitoA.hDD cf = BitoA.cf vetU = BitoA.vetU
def cf_from_gammaDD(gammaDD): global cf 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) gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD) cf = sp.sympify(0) if par.parval_from_str("EvolvedConformalFactor_cf") == "phi": # phi = \frac{1}{12} log(\frac{gamma}{\bar{gamma}}). cf = sp.Rational(1, 12) * sp.log(gammaDET / gammabarDET) elif par.parval_from_str("EvolvedConformalFactor_cf") == "chi": # chi = exp(-4*phi) = exp(-4*\frac{1}{12}*(\frac{gamma}{\bar{gamma}})) # = exp(-\frac{1}{3}*log(\frac{gamma}{\bar{gamma}})) = (\frac{gamma}{\bar{gamma}})^{-1/3}. # cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 3)) elif par.parval_from_str("EvolvedConformalFactor_cf") == "W": # W = exp(-2*phi) = exp(-2*\frac{1}{12}*log(\frac{gamma}{\bar{gamma}})) # = exp(-\frac{1}{6}*log(\frac{gamma}{\bar{gamma}})) = (\frac{gamma}{bar{gamma}})^{-1/6}. cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 6)) else: print("Error EvolvedConformalFactor_cf type = \"" + par.parval_from_str("EvolvedConformalFactor_cf") + "\" unknown.") sys.exit(1)
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 write_out_functions_for_StildeD_source_term(outdir,outCparams,gammaDD,betaU,alpha,ValenciavU,BU,sqrt4pi): generate_memory_access_code() # First, we declare some dummy tensors that we will use for the codegen. gammaDDdD = ixp.declarerank3("gammaDDdD","sym01",DIM=3) betaUdD = ixp.declarerank2("betaUdD","nosym",DIM=3) alphadD = ixp.declarerank1("alphadD",DIM=3) # We need to rerun a few of these functions with the reset lists to make sure these functions # don't cheat by using analytic expressions GRHD.compute_sqrtgammaDET(gammaDD) GRHD.u4U_in_terms_of_ValenciavU__rescale_ValenciavU_by_applying_speed_limit(alpha, betaU, gammaDD, ValenciavU) GRFFE.compute_smallb4U(gammaDD, betaU, alpha, GRHD.u4U_ito_ValenciavU, BU, sqrt4pi) GRFFE.compute_smallbsquared(gammaDD, betaU, alpha, GRFFE.smallb4U) GRFFE.compute_TEM4UU(gammaDD,betaU,alpha, GRFFE.smallb4U, GRFFE.smallbsquared,GRHD.u4U_ito_ValenciavU) GRHD.compute_g4DD_zerotimederiv_dD(gammaDD,betaU,alpha, gammaDDdD,betaUdD,alphadD) GRHD.compute_S_tilde_source_termD(alpha, GRHD.sqrtgammaDET,GRHD.g4DD_zerotimederiv_dD, GRFFE.TEM4UU) for i in range(3): desc = "Adds the source term to StildeD"+str(i)+"." name = "calculate_StildeD"+str(i)+"_source_term" outCfunction( outfile = os.path.join(outdir,name+".h"), desc=desc, name=name, params ="const paramstruct *params,const REAL *auxevol_gfs, REAL *rhs_gfs", body = general_access \ +metric_deriv_access[i]\ +outputC(GRHD.S_tilde_source_termD[i],"Stilde_rhsD"+str(i),"returnstring",params=outCparams).replace("IDX4","IDX4S")\ +write_final_quantity[i], loopopts ="InteriorPoints", rel_path_for_Cparams=os.path.join("../"))
def GiRaFFE_NRPy_Afield_flux(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. gammaDD = ixp.declarerank2("gammaDD", "sym01", DIM=3) betaU = ixp.declarerank1("betaU", DIM=3) alpha = sp.sympify("alpha") for flux_dirn in range(3): chsp.find_cmax_cmin(flux_dirn, gammaDD, betaU, alpha) Ccode_kernel = outputC([chsp.cmax, chsp.cmin], ["cmax", "cmin"], "returnstring", params="outCverbose=False,CSE_sorting=none") Ccode_kernel = Ccode_kernel.replace("cmax", "*cmax").replace("cmin", "*cmin") Ccode_kernel = Ccode_kernel.replace("betaU0", "betaUi").replace( "betaU1", "betaUi").replace("betaU2", "betaUi") with open( os.path.join(Ccodesdir, "compute_cmax_cmin_dirn" + str(flux_dirn) + ".h"), "w") as file: file.write(Ccode_kernel) with open(os.path.join(Ccodesdir, name + ".h"), "w") as file: file.write(body)
def add_to_Cfunction_dict__GiRaFFE_NRPy_A2B(gammaDD, AD, BU, includes=None): # 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 indexedexp.py LeviCivitaUUU = ixp.LeviCivitaTensorUUU_dim3_rank3(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] # Here, we'll use the add_to_Cfunction_dict() 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" 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]) ]) 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" add_to_Cfunction_dict(includes=includes, desc=desc, name=name, prefunc=prefunc, params=params, body=body, loopopts=loopopts, postloop=postloop) outC_function_dict[name] = outC_function_dict[name].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").replace( "../set_Cparameters.h", "set_Cparameters.h")
def ScalarWave_RHSs(): # Step 1: Get the spatial dimension, defined in the # NRPy+ "grid" module. DIM = par.parval_from_str("DIM") # Step 2: Register gridfunctions that are needed as input # to the scalar wave RHS expressions. uu, vv = gri.register_gridfunctions("EVOL", ["uu", "vv"]) # Step 3: Declare the rank-2 indexed expression \partial_{ij} u, # which is symmetric about interchange of indices i and j # Derivative variables like these must have an underscore # in them, so the finite difference module can parse the # variable name properly. uu_dDD = ixp.declarerank2("uu_dDD", "sym01") # Step 4: Specify RHSs as global variables, # to enable access outside this # function (e.g., for C code output) global uu_rhs, vv_rhs # Step 5: Define right-hand sides for the evolution. uu_rhs = vv vv_rhs = 0 for i in range(DIM): vv_rhs += wavespeed * wavespeed * uu_dDD[i][i] # # Step 5: Generate C code for scalarwave evolution equations, # # print output to the screen (standard out, or stdout). fin.FD_outputC("simple_c.py", [ lhrh(lhs=gri.gfaccess("out_gfs", "UU_rhs"), rhs=uu_rhs), lhrh(lhs=gri.gfaccess("out_gfs", "VV_rhs"), rhs=vv_rhs) ])
def generate_everything_for_UnitTesting(): # First define hydrodynamical quantities u4U = ixp.declarerank1("u4U", DIM=4) B_tildeU = ixp.declarerank1("B_tildeU", DIM=3) # Then ADM quantities gammaDD = ixp.declarerank2("gammaDD", "sym01", DIM=3) betaU = ixp.declarerank1("betaU", DIM=3) alpha = sp.symbols('alpha', real=True) # Then numerical constant sqrt4pi = sp.symbols('sqrt4pi', real=True) # First compute stress-energy tensor T4UU and T4UD: import GRHD.equations as GHeq GHeq.compute_sqrtgammaDET(gammaDD) compute_B_notildeU(GHeq.sqrtgammaDET, B_tildeU) compute_smallb4U(gammaDD, betaU, alpha, u4U, B_notildeU, sqrt4pi) compute_smallb4U_with_driftvU_for_FFE(gammaDD, betaU, alpha, u4U, B_notildeU, sqrt4pi) compute_smallbsquared(gammaDD, betaU, alpha, smallb4U) compute_TEM4UU(gammaDD, betaU, alpha, smallb4U, smallbsquared, u4U) compute_TEM4UD(gammaDD, betaU, alpha, TEM4UU) # Compute conservative variables in terms of primitive variables GHeq.compute_S_tildeD(alpha, GHeq.sqrtgammaDET, TEM4UD) global S_tildeD S_tildeD = GHeq.S_tildeD # Next compute fluxes of conservative variables GHeq.compute_S_tilde_fluxUD(alpha, GHeq.sqrtgammaDET, TEM4UD) global S_tilde_fluxUD S_tilde_fluxUD = GHeq.S_tilde_fluxUD # Then declare derivatives & compute g4DDdD gammaDD_dD = ixp.declarerank3("gammaDD_dD", "sym01", DIM=3) betaU_dD = ixp.declarerank2("betaU_dD", "nosym", DIM=3) alpha_dD = ixp.declarerank1("alpha_dD", DIM=3) GHeq.compute_g4DD_zerotimederiv_dD(gammaDD, betaU, alpha, gammaDD_dD, betaU_dD, alpha_dD) # Finally compute source terms on tau_tilde and S_tilde equations GHeq.compute_S_tilde_source_termD(alpha, GHeq.sqrtgammaDET, GHeq.g4DD_zerotimederiv_dD, TEM4UU) global S_tilde_source_termD S_tilde_source_termD = GHeq.S_tilde_source_termD
def ScalarWaveCurvilinear_RHSs(): # Step 1: Get the spatial dimension, defined in the # NRPy+ "grid" module. With reference metrics, # this must be set to 3 or fewer. DIM = par.parval_from_str("DIM") # Step 2: Set up the reference metric and # quantities derived from the # reference metric. rfm.reference_metric() # Step 3: Compute the contracted Christoffel symbols: contractedGammahatU = ixp.zerorank1() for k in range(DIM): for i in range(DIM): for j in range(DIM): contractedGammahatU[k] += rfm.ghatUU[i][j] * rfm.GammahatUDD[k][i][j] # Step 4: Register gridfunctions that are needed as input # to the scalar wave RHS expressions. uu, vv = gri.register_gridfunctions("EVOL",["uu","vv"]) # Step 5a: Declare the rank-1 indexed expression \partial_{i} u, # Derivative variables like these must have an underscore # in them, so the finite difference module can parse the # variable name properly. uu_dD = ixp.declarerank1("uu_dD") # Step 5b: Declare the rank-2 indexed expression \partial_{ij} u, # which is symmetric about interchange of indices i and j # Derivative variables like these must have an underscore # in them, so the finite difference module can parse the # variable name properly. uu_dDD = ixp.declarerank2("uu_dDD","sym01") # Step 7: Specify RHSs as global variables, # to enable access outside this # function (e.g., for C code output) global uu_rhs,vv_rhs # Step 6: Define right-hand sides for the evolution. # Step 6a: uu_rhs = vv: uu_rhs = vv # Step 6b: The right-hand side of the \partial_t v equation # is given by: # \hat{g}^{ij} \partial_i \partial_j u - \hat{\Gamma}^i \partial_i u. # ^^^^^^^^^^^^ PART 1 ^^^^^^^^^^^^^^^^ ^^^^^^^^^^ PART 2 ^^^^^^^^^^^ vv_rhs = 0 for i in range(DIM): # PART 2: vv_rhs -= contractedGammahatU[i]*uu_dD[i] for j in range(DIM): # PART 1: vv_rhs += rfm.ghatUU[i][j]*uu_dDD[i][j] vv_rhs *= wavespeed*wavespeed
def BSSN_source_terms_for_BSSN_RHSs(custom_T4UU=None): global sourceterm_trK_rhs, sourceterm_a_rhsDD, sourceterm_lambda_rhsU, sourceterm_Lambdabar_rhsU # Step 3.a: Call BSSN_source_terms_ito_T4UU to get SDD, SD, S, & rho if custom_T4UU == "unrescaled BSSN source terms already given": SDD = ixp.declarerank2("SDD", "sym01") SD = ixp.declarerank1("SD") S = sp.symbols("S", real=True) rho = sp.symbols("rho", real=True) else: SDD, SD, S, rho = stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars( "BSSN", custom_T4UU) PI = par.Cparameters("REAL", thismodule, ["PI"], "3.14159265358979323846264338327950288") alpha = sp.symbols("alpha", real=True) # Step 3.b: trK_rhs sourceterm_trK_rhs = 4 * PI * alpha * (rho + S) # Step 3.c: Abar_rhsDD: # Step 3.c.i: Compute trace-free part of S_{ij}: import BSSN.BSSN_quantities as Bq Bq.BSSN_basic_tensors() # Sets gammabarDD gammabarUU, dummydet = ixp.symm_matrix_inverter3x3( Bq.gammabarDD) # Set gammabarUU tracefree_SDD = ixp.zerorank2() for i in range(3): for j in range(3): tracefree_SDD[i][j] = SDD[i][j] for i in range(3): for j in range(3): for k in range(3): for m in range(3): tracefree_SDD[i][j] += -sp.Rational(1, 3) * Bq.gammabarDD[ i][j] * gammabarUU[k][m] * SDD[k][m] # Step 3.c.ii: Define exp_m4phi = e^{-4 phi} Bq.phi_and_derivs() # Step 3.c.iii: Evaluate stress-energy part of AbarDD's RHS sourceterm_a_rhsDD = ixp.zerorank2() for i in range(3): for j in range(3): Abar_rhsDDij = -8 * PI * alpha * Bq.exp_m4phi * tracefree_SDD[i][j] sourceterm_a_rhsDD[i][j] = Abar_rhsDDij / rfm.ReDD[i][j] # Step 3.d: Stress-energy part of Lambdabar_rhsU = stressenergy_Lambdabar_rhsU sourceterm_Lambdabar_rhsU = ixp.zerorank1() for i in range(3): for j in range(3): sourceterm_Lambdabar_rhsU[ i] += -16 * PI * alpha * gammabarUU[i][j] * SD[j] sourceterm_lambda_rhsU = ixp.zerorank1() for i in range(3): sourceterm_lambda_rhsU[i] = sourceterm_Lambdabar_rhsU[i] / rfm.ReU[i]
def register_stress_energy_source_terms_return_T4UU( enable_stress_energy_source_terms): if enable_stress_energy_source_terms: registered_already = False for i in range(len(gri.glb_gridfcs_list)): if gri.glb_gridfcs_list[i].name == "T4UU00": registered_already = True if not registered_already: return ixp.register_gridfunctions_for_single_rank2("AUXEVOL", "T4UU", "sym01", DIM=4) else: return ixp.declarerank2("T4UU", "sym01", DIM=4) return None
def gammabarDD_hDD(gammaDD): global gammabarDD, hDD if gammaDD == None: gammaDD = ixp.declarerank2("gammaDD", "sym01") if rfm.have_already_called_reference_metric_function == False: print( "BSSN.BSSN_in_terms_of_ADM.hDD_given_ADM(): Must call reference_metric() first!" ) sys.exit(1) # \bar{gamma}_{ij} = (\frac{\bar{gamma}}{gamma})^{1/3}*gamma_{ij}. gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD) gammabarDD = ixp.zerorank2() hDD = 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] hDD[i][j] = (gammabarDD[i][j] - rfm.ghatDD[i][j]) / rfm.ReDD[i][j]
def setup_ADM_quantities(inputvars): if inputvars == "ADM": gammaDD = ixp.declarerank2("gammaDD", "sym01") betaU = ixp.declarerank1("betaU") alpha = sp.symbols("alpha", real=True) elif inputvars == "BSSN": import BSSN.ADM_in_terms_of_BSSN as AitoB # Construct gamma_{ij} in terms of cf & gammabar_{ij} AitoB.ADM_in_terms_of_BSSN() gammaDD = AitoB.gammaDD # Next construct beta^i in terms of vet^i and reference metric quantities import BSSN.BSSN_quantities as Bq Bq.BSSN_basic_tensors() betaU = Bq.betaU alpha = sp.symbols("alpha", real=True) else: print("inputvars = " + str(inputvars) + " not supported. Please choose ADM or BSSN.") sys.exit(1) return gammaDD,betaU,alpha
def add_to_Cfunction_dict__AD_gauge_term_psi6Phi_fin_diff(includes=None): xi_damping = par.Cparameters("REAL",thismodule,"xi_damping",0.1) GRFFE.compute_psi6Phi_rhs_damping_term(alpha,psi6Phi,xi_damping) AevolParen_dD = ixp.declarerank1("AevolParen_dD",DIM=3) PhievolParenU_dD = ixp.declarerank2("PhievolParenU_dD","nosym",DIM=3) A_rhsD = ixp.zerorank1() psi6Phi_rhs = GRFFE.psi6Phi_damping for i in range(3): A_rhsD[i] += -AevolParen_dD[i] psi6Phi_rhs += -PhievolParenU_dD[i][i] # Add Kreiss-Oliger dissipation to the GRFFE RHSs: # psi6Phi_dKOD = ixp.declarerank1("psi6Phi_dKOD") # AD_dKOD = ixp.declarerank2("AD_dKOD","nosym") # for i in range(3): # psi6Phi_rhs += diss_strength*psi6Phi_dKOD[i]*rfm.ReU[i] # ReU[i] = 1/scalefactor_orthog_funcform[i] # for j in range(3): # A_rhsD[j] += diss_strength*AD_dKOD[j][i]*rfm.ReU[i] # ReU[i] = 1/scalefactor_orthog_funcform[i] RHSs_to_print = [ lhrh(lhs=gri.gfaccess("rhs_gfs","AD0"),rhs=A_rhsD[0]), lhrh(lhs=gri.gfaccess("rhs_gfs","AD1"),rhs=A_rhsD[1]), lhrh(lhs=gri.gfaccess("rhs_gfs","AD2"),rhs=A_rhsD[2]), lhrh(lhs=gri.gfaccess("rhs_gfs","psi6Phi"),rhs=psi6Phi_rhs), ] desc = "Calculate AD gauge term and psi6Phi RHSs" name = "calculate_AD_gauge_psi6Phi_RHSs" params ="const paramstruct *params,const REAL *in_gfs,const REAL *auxevol_gfs,REAL *rhs_gfs" body = fin.FD_outputC("returnstring",RHSs_to_print,params=outCparams) loopopts ="InteriorPoints" add_to_Cfunction_dict( includes=includes, desc=desc, name=name, params=params, body=body, loopopts=loopopts) outC_function_dict[name] = outC_function_dict[name].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")
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_E_i_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): if not inputs_provided: # declare all variables alpha_face = sp.symbols(alpha_face) beta_faceU = ixp.declarerank1("beta_faceU") gamma_faceDD = ixp.declarerank2("gamma_faceDD", "sym01") Valenciav_rU = ixp.declarerank1("Valenciav_rU") B_rU = ixp.declarerank1("B_rU") Valenciav_lU = ixp.declarerank1("Valenciav_lU") B_lU = ixp.declarerank1("B_lU") global E_fluxD E_fluxD = ixp.zerorank1() for field_comp in range(3): find_cmax_cmin(field_comp, gamma_faceDD, beta_faceU, alpha_face) calculate_flux_and_state_for_Induction(field_comp,flux_dirn, gamma_faceDD,beta_faceU,alpha_face,\ Valenciav_rU,B_rU) Fr = F Ur = U calculate_flux_and_state_for_Induction(field_comp,flux_dirn, gamma_faceDD,beta_faceU,alpha_face,\ Valenciav_lU,B_lU) Fl = F Ul = U E_fluxD[field_comp] += HLLE_solver(cmax, cmin, Fr, Fl, Ur, Ul)
def GiRaFFEfood_NRPy_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] # Initialize all components of A and E in the *spherical basis* to zero ASphD = ixp.zerorank1() ESphD = ixp.zerorank1() ASphD[2] = (r * r * sp.sin(theta)**2) / 2 ESphD[2] = 2 * M * sp.sin(theta)**2 / sp.sqrt(1 + 2 * M / r) # <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(3): for j in range(3): AD[i] = drrefmetric__dx_0UDmatrix[(j, i)] * ASphD[j] ED[i] = drrefmetric__dx_0UDmatrix[(j, i)] * ESphD[j] #Step 4: Declare the basic spacetime quantities alpha = sp.symbols("alpha", real=True) betaU = ixp.declarerank1("betaU", DIM=3) gammaDD = ixp.declarerank2("gammaDD", "sym01", DIM=3) import GRHD.equations as GRHD GRHD.compute_sqrtgammaDET(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(3): for j in range(3): for k in range(3): LeviCivitaTensorUUU[i][j][ k] = LeviCivitaSymbolDDD[i][j][k] / GRHD.sqrtgammaDET # For the initial data, we can analytically take the derivatives of A_i ADdD = ixp.zerorank2() for i in range(3): for j in range(3): ADdD[i][j] = sp.simplify(sp.diff(AD[i], rfm.xxCart[j])) #global BU BU = ixp.zerorank1() for i in range(3): for j in range(3): for k in range(3): 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(3): for j in range(3): B2 += gammaDD[i][j] * BU[i] * BU[j] # Lower the index on B^i BD = ixp.zerorank1() for i in range(3): for j in range(3): BD[i] += gammaDD[i][j] * BU[j] # Step 4c: Calculate the Valencia 3-velocity global ValenciavU ValenciavU = ixp.zerorank1() for i in range(3): for j in range(3): for k in range(3): ValenciavU[ i] += LeviCivitaTensorUUU[i][j][k] * ED[j] * BD[k] / B2
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_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 indexedexp.py LeviCivitaUUU = ixp.LeviCivitaTensorUUU_dim3_rank3(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(prefunc) # 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]) ]), # 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_to_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 ADM_in_terms_of_BSSN(): global gammaDD, gammaDDdD, gammaDDdDD, gammaUU, detgamma, GammaUDD, KDD, KDDdD # Step 1.c: Given the chosen coordinate system, set up # corresponding reference metric and needed # reference metric quantities # The following function call sets up the reference metric # and related quantities, including rescaling matrices ReDD, # ReU, and hatted quantities. rfm.reference_metric() # Step 1.d: Set spatial dimension (must be 3 for BSSN, as BSSN is # a 3+1-dimensional decomposition of the general # relativistic field equations) DIM = 3 # Step 1.e: Import all basic (unrescaled) BSSN scalars & tensors import BSSN.BSSN_quantities as Bq Bq.BSSN_basic_tensors() gammabarDD = Bq.gammabarDD cf = Bq.cf AbarDD = Bq.AbarDD trK = Bq.trK Bq.gammabar__inverse_and_derivs() gammabarDD_dD = Bq.gammabarDD_dD gammabarDD_dDD = Bq.gammabarDD_dDD Bq.AbarUU_AbarUD_trAbar_AbarDD_dD() AbarDD_dD = Bq.AbarDD_dD # Step 2: The ADM three-metric gammaDD and its # derivatives in terms of BSSN quantities. gammaDD = ixp.zerorank2() exp4phi = sp.sympify(0) if par.parval_from_str("EvolvedConformalFactor_cf") == "phi": exp4phi = sp.exp(4 * cf) elif par.parval_from_str("EvolvedConformalFactor_cf") == "chi": exp4phi = (1 / cf) elif par.parval_from_str("EvolvedConformalFactor_cf") == "W": exp4phi = (1 / cf ** 2) else: print("Error EvolvedConformalFactor_cf type = \"" + par.parval_from_str("EvolvedConformalFactor_cf") + "\" unknown.") sys.exit(1) for i in range(DIM): for j in range(DIM): gammaDD[i][j] = exp4phi * gammabarDD[i][j] # Step 2.a: Derivatives of $e^{4\phi}$ phidD = ixp.zerorank1() phidDD = ixp.zerorank2() cf_dD = ixp.declarerank1("cf_dD") cf_dDD = ixp.declarerank2("cf_dDD","sym01") if par.parval_from_str("EvolvedConformalFactor_cf") == "phi": for i in range(DIM): phidD[i] = cf_dD[i] for j in range(DIM): phidDD[i][j] = cf_dDD[i][j] elif par.parval_from_str("EvolvedConformalFactor_cf") == "chi": for i in range(DIM): phidD[i] = -sp.Rational(1,4)*exp4phi*cf_dD[i] for j in range(DIM): phidDD[i][j] = sp.Rational(1,4)*( exp4phi**2*cf_dD[i]*cf_dD[j] - exp4phi*cf_dDD[i][j] ) elif par.parval_from_str("EvolvedConformalFactor_cf") == "W": exp2phi = (1 / cf) for i in range(DIM): phidD[i] = -sp.Rational(1,2)*exp2phi*cf_dD[i] for j in range(DIM): phidDD[i][j] = sp.Rational(1,2)*( exp4phi*cf_dD[i]*cf_dD[j] - exp2phi*cf_dDD[i][j] ) else: print("Error EvolvedConformalFactor_cf type = \""+par.parval_from_str("EvolvedConformalFactor_cf")+"\" unknown.") sys.exit(1) exp4phidD = ixp.zerorank1() exp4phidDD = ixp.zerorank2() for i in range(DIM): exp4phidD[i] = 4*exp4phi*phidD[i] for j in range(DIM): exp4phidDD[i][j] = 16*exp4phi*phidD[i]*phidD[j] + 4*exp4phi*phidDD[i][j] # Step 2.b: Derivatives of gammaDD, the ADM three-metric gammaDDdD = ixp.zerorank3() gammaDDdDD = ixp.zerorank4() for i in range(DIM): for j in range(DIM): for k in range(DIM): gammaDDdD[i][j][k] = exp4phidD[k] * gammabarDD[i][j] + exp4phi * gammabarDD_dD[i][j][k] for l in range(DIM): gammaDDdDD[i][j][k][l] = exp4phidDD[k][l] * gammabarDD[i][j] + \ exp4phidD[k] * gammabarDD_dD[i][j][l] + \ exp4phidD[l] * gammabarDD_dD[i][j][k] + \ exp4phi * gammabarDD_dDD[i][j][k][l] # Step 2.c: 3-Christoffel symbols associated with ADM 3-metric gammaDD # Step 2.c.i: First compute the inverse 3-metric gammaUU: gammaUU, detgamma = ixp.symm_matrix_inverter3x3(gammaDD) GammaUDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): GammaUDD[i][j][k] += sp.Rational(1,2)*gammaUU[i][l]* \ (gammaDDdD[l][j][k] + gammaDDdD[l][k][j] - gammaDDdD[j][k][l]) # Step 3: Define ADM extrinsic curvature KDD and # its first spatial derivatives KDDdD # in terms of BSSN quantities KDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): KDD[i][j] = exp4phi * AbarDD[i][j] + sp.Rational(1, 3) * gammaDD[i][j] * trK KDDdD = ixp.zerorank3() trK_dD = ixp.declarerank1("trK_dD") for i in range(DIM): for j in range(DIM): for k in range(DIM): KDDdD[i][j][k] = exp4phidD[k] * AbarDD[i][j] + exp4phi * AbarDD_dD[i][j][k] + \ sp.Rational(1, 3) * (gammaDDdD[i][j][k] * trK + gammaDD[i][j] * trK_dD[k])
def BSSN_RHSs__generate_symbolic_expressions(): ###################################### # START: GENERATE SYMBOLIC EXPRESSIONS print("Generating symbolic expressions for BSSN RHSs...") start = time.time() # Enable rfm_precompute infrastructure, which results in # BSSN RHSs that are free of transcendental functions, # even in curvilinear coordinates, so long as # ConformalFactor is set to "W" (default). par.set_parval_from_str("reference_metric::enable_rfm_precompute","True") par.set_parval_from_str("reference_metric::rfm_precompute_Ccode_outdir",os.path.join(outdir,"rfm_files/")) # Evaluate BSSN + BSSN gauge RHSs with rfm_precompute enabled: import BSSN.BSSN_quantities as Bq par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","True") rhs.BSSN_RHSs() if T4UU != None: import BSSN.BSSN_stress_energy_source_terms as Bsest Bsest.BSSN_source_terms_for_BSSN_RHSs(T4UU) rhs.trK_rhs += Bsest.sourceterm_trK_rhs for i in range(3): # Needed for Gamma-driving shift RHSs: rhs.Lambdabar_rhsU[i] += Bsest.sourceterm_Lambdabar_rhsU[i] # Needed for BSSN RHSs: rhs.lambda_rhsU[i] += Bsest.sourceterm_lambda_rhsU[i] for j in range(3): rhs.a_rhsDD[i][j] += Bsest.sourceterm_a_rhsDD[i][j] gaugerhs.BSSN_gauge_RHSs() # Add Kreiss-Oliger dissipation to the BSSN RHSs: thismodule = "KO_Dissipation" diss_strength = par.Cparameters("REAL", thismodule, "diss_strength", default_KO_strength) alpha_dKOD = ixp.declarerank1("alpha_dKOD") cf_dKOD = ixp.declarerank1("cf_dKOD") trK_dKOD = ixp.declarerank1("trK_dKOD") betU_dKOD = ixp.declarerank2("betU_dKOD","nosym") vetU_dKOD = ixp.declarerank2("vetU_dKOD","nosym") lambdaU_dKOD = ixp.declarerank2("lambdaU_dKOD","nosym") aDD_dKOD = ixp.declarerank3("aDD_dKOD","sym01") hDD_dKOD = ixp.declarerank3("hDD_dKOD","sym01") for k in range(3): gaugerhs.alpha_rhs += diss_strength*alpha_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.cf_rhs += diss_strength* cf_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.trK_rhs += diss_strength* trK_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] for i in range(3): if "2ndOrder" in ShiftCondition: gaugerhs.bet_rhsU[i] += diss_strength* betU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] gaugerhs.vet_rhsU[i] += diss_strength* vetU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.lambda_rhsU[i] += diss_strength*lambdaU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] for j in range(3): rhs.a_rhsDD[i][j] += diss_strength*aDD_dKOD[i][j][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.h_rhsDD[i][j] += diss_strength*hDD_dKOD[i][j][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] # We use betaU as our upwinding control vector: Bq.BSSN_basic_tensors() betaU = Bq.betaU # Now that we are finished with all the rfm hatted # quantities in generic precomputed functional # form, let's restore them to their closed- # form expressions. par.set_parval_from_str("reference_metric::enable_rfm_precompute","False") # Reset to False to disable rfm_precompute. rfm.ref_metric__hatted_quantities() par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","False") end = time.time() print("(BENCH) Finished BSSN RHS symbolic expressions in "+str(end-start)+" seconds.") # END: GENERATE SYMBOLIC EXPRESSIONS ###################################### BSSN_RHSs_SymbExpressions = [lhrh(lhs=gri.gfaccess("rhs_gfs","aDD00"), rhs=rhs.a_rhsDD[0][0]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD01"), rhs=rhs.a_rhsDD[0][1]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD02"), rhs=rhs.a_rhsDD[0][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD11"), rhs=rhs.a_rhsDD[1][1]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD12"), rhs=rhs.a_rhsDD[1][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD22"), rhs=rhs.a_rhsDD[2][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","alpha"), rhs=gaugerhs.alpha_rhs), lhrh(lhs=gri.gfaccess("rhs_gfs","betU0"), rhs=gaugerhs.bet_rhsU[0]), lhrh(lhs=gri.gfaccess("rhs_gfs","betU1"), rhs=gaugerhs.bet_rhsU[1]), lhrh(lhs=gri.gfaccess("rhs_gfs","betU2"), rhs=gaugerhs.bet_rhsU[2]), lhrh(lhs=gri.gfaccess("rhs_gfs","cf"), rhs=rhs.cf_rhs), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD00"), rhs=rhs.h_rhsDD[0][0]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD01") ,rhs=rhs.h_rhsDD[0][1]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD02"), rhs=rhs.h_rhsDD[0][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD11"), rhs=rhs.h_rhsDD[1][1]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD12"), rhs=rhs.h_rhsDD[1][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD22"), rhs=rhs.h_rhsDD[2][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU0"),rhs=rhs.lambda_rhsU[0]), lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU1"),rhs=rhs.lambda_rhsU[1]), lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU2"),rhs=rhs.lambda_rhsU[2]), lhrh(lhs=gri.gfaccess("rhs_gfs","trK"), rhs=rhs.trK_rhs), lhrh(lhs=gri.gfaccess("rhs_gfs","vetU0"), rhs=gaugerhs.vet_rhsU[0]), lhrh(lhs=gri.gfaccess("rhs_gfs","vetU1"), rhs=gaugerhs.vet_rhsU[1]), lhrh(lhs=gri.gfaccess("rhs_gfs","vetU2"), rhs=gaugerhs.vet_rhsU[2]) ] return [betaU,BSSN_RHSs_SymbExpressions]
def 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 BaikalETK_C_kernels_codegen_onepart(params= "WhichPart=BSSN_RHSs,ThornName=Baikal,FD_order=4,enable_stress_energy_source_terms=True"): # Set default parameters WhichPart = "BSSN_RHSs" ThornName = "Baikal" FD_order = 4 enable_stress_energy_source_terms = True LapseCondition = "OnePlusLog" # Set the standard 1+log lapse condition # Set the standard, second-order advecting-shift, Gamma-driving shift condition: ShiftCondition = "GammaDriving2ndOrder_NoCovariant" # Default Kreiss-Oliger dissipation strength default_KO_strength = 0.1 import re if params != "": params2 = re.sub("^,","",params) params = params2.strip() splitstring = re.split("=|,", params) if len(splitstring) % 2 != 0: print("outputC: Invalid params string: "+params) sys.exit(1) parnm = [] value = [] for i in range(int(len(splitstring)/2)): parnm.append(splitstring[2*i]) value.append(splitstring[2*i+1]) for i in range(len(parnm)): parnm.append(splitstring[2*i]) value.append(splitstring[2*i+1]) for i in range(len(parnm)): if parnm[i] == "WhichPart": WhichPart = value[i] elif parnm[i] == "ThornName": ThornName = value[i] elif parnm[i] == "FD_order": FD_order = int(value[i]) elif parnm[i] == "enable_stress_energy_source_terms": enable_stress_energy_source_terms = False if value[i] == "True": enable_stress_energy_source_terms = True elif parnm[i] == "LapseCondition": LapseCondition = value[i] elif parnm[i] == "ShiftCondition": ShiftCondition = value[i] elif parnm[i] == "default_KO_strength": default_KO_strength = float(value[i]) else: print("BaikalETK Error: Could not parse input param: "+parnm[i]) sys.exit(1) # Set output directory for C kernels outdir = os.path.join(ThornName, "src") # Main C code output directory # Set spatial dimension (must be 3 for BSSN) par.set_parval_from_str("grid::DIM", 3) # Step 2: Set some core parameters, including CoordSystem MoL timestepping algorithm, # FD order, floating point precision, and CFL factor: # Choices are: Spherical, SinhSpherical, SinhSphericalv2, Cylindrical, SinhCylindrical, # SymTP, SinhSymTP # NOTE: Only CoordSystem == Cartesian makes sense here; new # boundary conditions are needed within the ETK for # Spherical, etc. coordinates. CoordSystem = "Cartesian" par.set_parval_from_str("reference_metric::CoordSystem", CoordSystem) rfm.reference_metric() # Create ReU, ReDD needed for rescaling B-L initial data, generating BSSN RHSs, etc. # Set the gridfunction memory access type to ETK-like, so that finite_difference # knows how to read and write gridfunctions from/to memory. par.set_parval_from_str("grid::GridFuncMemAccess", "ETK") par.set_parval_from_str("BSSN.BSSN_gauge_RHSs::ShiftEvolutionOption", ShiftCondition) par.set_parval_from_str("BSSN.BSSN_gauge_RHSs::LapseEvolutionOption", LapseCondition) T4UU = None if enable_stress_energy_source_terms == True: registered_already = False for i in range(len(gri.glb_gridfcs_list)): if gri.glb_gridfcs_list[i].name == "T4UU00": registered_already = True if not registered_already: T4UU = ixp.register_gridfunctions_for_single_rank2("AUXEVOL", "T4UU", "sym01", DIM=4) else: T4UU = ixp.declarerank2("T4UU", "sym01", DIM=4) # Register the BSSN constraints (Hamiltonian & momentum constraints) as gridfunctions. registered_already = False for i in range(len(gri.glb_gridfcs_list)): if gri.glb_gridfcs_list[i].name == "H": registered_already = True if not registered_already: # We ignore return values for register_gridfunctions...() calls below # as they are unused. gri.register_gridfunctions("AUX", "H") ixp.register_gridfunctions_for_single_rank1("AUX", "MU") def BSSN_RHSs__generate_symbolic_expressions(): ###################################### # START: GENERATE SYMBOLIC EXPRESSIONS print("Generating symbolic expressions for BSSN RHSs...") start = time.time() # Enable rfm_precompute infrastructure, which results in # BSSN RHSs that are free of transcendental functions, # even in curvilinear coordinates, so long as # ConformalFactor is set to "W" (default). par.set_parval_from_str("reference_metric::enable_rfm_precompute","True") par.set_parval_from_str("reference_metric::rfm_precompute_Ccode_outdir",os.path.join(outdir,"rfm_files/")) # Evaluate BSSN + BSSN gauge RHSs with rfm_precompute enabled: import BSSN.BSSN_quantities as Bq par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","True") rhs.BSSN_RHSs() if T4UU != None: import BSSN.BSSN_stress_energy_source_terms as Bsest Bsest.BSSN_source_terms_for_BSSN_RHSs(T4UU) rhs.trK_rhs += Bsest.sourceterm_trK_rhs for i in range(3): # Needed for Gamma-driving shift RHSs: rhs.Lambdabar_rhsU[i] += Bsest.sourceterm_Lambdabar_rhsU[i] # Needed for BSSN RHSs: rhs.lambda_rhsU[i] += Bsest.sourceterm_lambda_rhsU[i] for j in range(3): rhs.a_rhsDD[i][j] += Bsest.sourceterm_a_rhsDD[i][j] gaugerhs.BSSN_gauge_RHSs() # Add Kreiss-Oliger dissipation to the BSSN RHSs: thismodule = "KO_Dissipation" diss_strength = par.Cparameters("REAL", thismodule, "diss_strength", default_KO_strength) alpha_dKOD = ixp.declarerank1("alpha_dKOD") cf_dKOD = ixp.declarerank1("cf_dKOD") trK_dKOD = ixp.declarerank1("trK_dKOD") betU_dKOD = ixp.declarerank2("betU_dKOD","nosym") vetU_dKOD = ixp.declarerank2("vetU_dKOD","nosym") lambdaU_dKOD = ixp.declarerank2("lambdaU_dKOD","nosym") aDD_dKOD = ixp.declarerank3("aDD_dKOD","sym01") hDD_dKOD = ixp.declarerank3("hDD_dKOD","sym01") for k in range(3): gaugerhs.alpha_rhs += diss_strength*alpha_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.cf_rhs += diss_strength* cf_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.trK_rhs += diss_strength* trK_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] for i in range(3): if "2ndOrder" in ShiftCondition: gaugerhs.bet_rhsU[i] += diss_strength* betU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] gaugerhs.vet_rhsU[i] += diss_strength* vetU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.lambda_rhsU[i] += diss_strength*lambdaU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] for j in range(3): rhs.a_rhsDD[i][j] += diss_strength*aDD_dKOD[i][j][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.h_rhsDD[i][j] += diss_strength*hDD_dKOD[i][j][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] # We use betaU as our upwinding control vector: Bq.BSSN_basic_tensors() betaU = Bq.betaU # Now that we are finished with all the rfm hatted # quantities in generic precomputed functional # form, let's restore them to their closed- # form expressions. par.set_parval_from_str("reference_metric::enable_rfm_precompute","False") # Reset to False to disable rfm_precompute. rfm.ref_metric__hatted_quantities() par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","False") end = time.time() print("(BENCH) Finished BSSN RHS symbolic expressions in "+str(end-start)+" seconds.") # END: GENERATE SYMBOLIC EXPRESSIONS ###################################### BSSN_RHSs_SymbExpressions = [lhrh(lhs=gri.gfaccess("rhs_gfs","aDD00"), rhs=rhs.a_rhsDD[0][0]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD01"), rhs=rhs.a_rhsDD[0][1]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD02"), rhs=rhs.a_rhsDD[0][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD11"), rhs=rhs.a_rhsDD[1][1]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD12"), rhs=rhs.a_rhsDD[1][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD22"), rhs=rhs.a_rhsDD[2][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","alpha"), rhs=gaugerhs.alpha_rhs), lhrh(lhs=gri.gfaccess("rhs_gfs","betU0"), rhs=gaugerhs.bet_rhsU[0]), lhrh(lhs=gri.gfaccess("rhs_gfs","betU1"), rhs=gaugerhs.bet_rhsU[1]), lhrh(lhs=gri.gfaccess("rhs_gfs","betU2"), rhs=gaugerhs.bet_rhsU[2]), lhrh(lhs=gri.gfaccess("rhs_gfs","cf"), rhs=rhs.cf_rhs), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD00"), rhs=rhs.h_rhsDD[0][0]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD01") ,rhs=rhs.h_rhsDD[0][1]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD02"), rhs=rhs.h_rhsDD[0][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD11"), rhs=rhs.h_rhsDD[1][1]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD12"), rhs=rhs.h_rhsDD[1][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD22"), rhs=rhs.h_rhsDD[2][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU0"),rhs=rhs.lambda_rhsU[0]), lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU1"),rhs=rhs.lambda_rhsU[1]), lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU2"),rhs=rhs.lambda_rhsU[2]), lhrh(lhs=gri.gfaccess("rhs_gfs","trK"), rhs=rhs.trK_rhs), lhrh(lhs=gri.gfaccess("rhs_gfs","vetU0"), rhs=gaugerhs.vet_rhsU[0]), lhrh(lhs=gri.gfaccess("rhs_gfs","vetU1"), rhs=gaugerhs.vet_rhsU[1]), lhrh(lhs=gri.gfaccess("rhs_gfs","vetU2"), rhs=gaugerhs.vet_rhsU[2]) ] return [betaU,BSSN_RHSs_SymbExpressions] def Ricci__generate_symbolic_expressions(): ###################################### # START: GENERATE SYMBOLIC EXPRESSIONS print("Generating symbolic expressions for Ricci tensor...") start = time.time() # Enable rfm_precompute infrastructure, which results in # BSSN RHSs that are free of transcendental functions, # even in curvilinear coordinates, so long as # ConformalFactor is set to "W" (default). par.set_parval_from_str("reference_metric::enable_rfm_precompute","True") par.set_parval_from_str("reference_metric::rfm_precompute_Ccode_outdir",os.path.join(outdir,"rfm_files/")) # Evaluate BSSN + BSSN gauge RHSs with rfm_precompute enabled: import BSSN.BSSN_quantities as Bq # Next compute Ricci tensor # par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","False") RbarDD_already_registered = False for i in range(len(gri.glb_gridfcs_list)): if "RbarDD00" in gri.glb_gridfcs_list[i].name: RbarDD_already_registered = True if not RbarDD_already_registered: # We ignore the return value of ixp.register_gridfunctions_for_single_rank2() below # as it is unused. ixp.register_gridfunctions_for_single_rank2("AUXEVOL","RbarDD","sym01") rhs.BSSN_RHSs() Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU() # Now that we are finished with all the rfm hatted # quantities in generic precomputed functional # form, let's restore them to their closed- # form expressions. par.set_parval_from_str("reference_metric::enable_rfm_precompute","False") # Reset to False to disable rfm_precompute. rfm.ref_metric__hatted_quantities() end = time.time() print("(BENCH) Finished Ricci symbolic expressions in "+str(end-start)+" seconds.") # END: GENERATE SYMBOLIC EXPRESSIONS ###################################### Ricci_SymbExpressions = [lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD00"),rhs=Bq.RbarDD[0][0]), lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD01"),rhs=Bq.RbarDD[0][1]), lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD02"),rhs=Bq.RbarDD[0][2]), lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD11"),rhs=Bq.RbarDD[1][1]), lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD12"),rhs=Bq.RbarDD[1][2]), lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD22"),rhs=Bq.RbarDD[2][2])] return [Ricci_SymbExpressions] def BSSN_RHSs__generate_Ccode(all_RHSs_Ricci_exprs_list): betaU = all_RHSs_Ricci_exprs_list[0] BSSN_RHSs_SymbExpressions = all_RHSs_Ricci_exprs_list[1] print("Generating C code for BSSN RHSs (FD_order="+str(FD_order)+",Tmunu="+str(enable_stress_energy_source_terms)+") in "+par.parval_from_str("reference_metric::CoordSystem")+" coordinates.") start = time.time() # Store original finite-differencing order: FD_order_orig = par.parval_from_str("finite_difference::FD_CENTDERIVS_ORDER") # Set new finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order) BSSN_RHSs_string = fin.FD_outputC("returnstring",BSSN_RHSs_SymbExpressions, params="outCverbose=False,SIMD_enable=True,GoldenKernelsEnable=True", upwindcontrolvec=betaU) filename = "BSSN_RHSs_enable_Tmunu_"+str(enable_stress_energy_source_terms)+"_FD_order_"+str(FD_order)+".h" with open(os.path.join(outdir,filename), "w") as file: file.write(lp.loop(["i2","i1","i0"],["cctk_nghostzones[2]","cctk_nghostzones[1]","cctk_nghostzones[0]"], ["cctk_lsh[2]-cctk_nghostzones[2]","cctk_lsh[1]-cctk_nghostzones[1]","cctk_lsh[0]-cctk_nghostzones[0]"], ["1","1","SIMD_width"], ["#pragma omp parallel for", "#include \"rfm_files/rfm_struct__SIMD_outer_read2.h\"", r""" #include "rfm_files/rfm_struct__SIMD_outer_read1.h" #if (defined __INTEL_COMPILER && __INTEL_COMPILER_BUILD_DATE >= 20180804) #pragma ivdep // Forces Intel compiler (if Intel compiler used) to ignore certain SIMD vector dependencies #pragma vector always // Forces Intel compiler (if Intel compiler used) to vectorize #endif"""],"", "#include \"rfm_files/rfm_struct__SIMD_inner_read0.h\"\n"+BSSN_RHSs_string)) # Restore original finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order_orig) end = time.time() print("(BENCH) Finished BSSN_RHS C codegen (FD_order="+str(FD_order)+",Tmunu="+str(enable_stress_energy_source_terms)+") in " + str(end - start) + " seconds.") def Ricci__generate_Ccode(Ricci_exprs_list): Ricci_SymbExpressions = Ricci_exprs_list[0] print("Generating C code for Ricci tensor (FD_order="+str(FD_order)+") in "+par.parval_from_str("reference_metric::CoordSystem")+" coordinates.") start = time.time() # Store original finite-differencing order: FD_order_orig = par.parval_from_str("finite_difference::FD_CENTDERIVS_ORDER") # Set new finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order) Ricci_string = fin.FD_outputC("returnstring", Ricci_SymbExpressions, params="outCverbose=False,SIMD_enable=True,GoldenKernelsEnable=True") filename = "BSSN_Ricci_FD_order_"+str(FD_order)+".h" with open(os.path.join(outdir, filename), "w") as file: file.write(lp.loop(["i2","i1","i0"],["cctk_nghostzones[2]","cctk_nghostzones[1]","cctk_nghostzones[0]"], ["cctk_lsh[2]-cctk_nghostzones[2]","cctk_lsh[1]-cctk_nghostzones[1]","cctk_lsh[0]-cctk_nghostzones[0]"], ["1","1","SIMD_width"], ["#pragma omp parallel for", "#include \"rfm_files/rfm_struct__SIMD_outer_read2.h\"", r""" #include "rfm_files/rfm_struct__SIMD_outer_read1.h" #if (defined __INTEL_COMPILER && __INTEL_COMPILER_BUILD_DATE >= 20180804) #pragma ivdep // Forces Intel compiler (if Intel compiler used) to ignore certain SIMD vector dependencies #pragma vector always // Forces Intel compiler (if Intel compiler used) to vectorize #endif"""],"", "#include \"rfm_files/rfm_struct__SIMD_inner_read0.h\"\n"+Ricci_string)) # Restore original finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order_orig) end = time.time() print("(BENCH) Finished Ricci C codegen (FD_order="+str(FD_order)+") in " + str(end - start) + " seconds.") def BSSN_constraints__generate_symbolic_expressions_and_C_code(): ###################################### # START: GENERATE SYMBOLIC EXPRESSIONS # Define the Hamiltonian constraint and output the optimized C code. import BSSN.BSSN_constraints as bssncon bssncon.BSSN_constraints(add_T4UUmunu_source_terms=False) if T4UU != None: import BSSN.BSSN_stress_energy_source_terms as Bsest Bsest.BSSN_source_terms_for_BSSN_RHSs(T4UU) Bsest.BSSN_source_terms_for_BSSN_constraints(T4UU) bssncon.H += Bsest.sourceterm_H for i in range(3): bssncon.MU[i] += Bsest.sourceterm_MU[i] # END: GENERATE SYMBOLIC EXPRESSIONS ###################################### # Store original finite-differencing order: FD_order_orig = par.parval_from_str("finite_difference::FD_CENTDERIVS_ORDER") # Set new finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order) start = time.time() print("Generating optimized C code for Ham. & mom. constraints. May take a while, depending on CoordSystem.") Ham_mom_string = fin.FD_outputC("returnstring", [lhrh(lhs=gri.gfaccess("aux_gfs", "H"), rhs=bssncon.H), lhrh(lhs=gri.gfaccess("aux_gfs", "MU0"), rhs=bssncon.MU[0]), lhrh(lhs=gri.gfaccess("aux_gfs", "MU1"), rhs=bssncon.MU[1]), lhrh(lhs=gri.gfaccess("aux_gfs", "MU2"), rhs=bssncon.MU[2])], params="outCverbose=False") with open(os.path.join(outdir,"BSSN_constraints_enable_Tmunu_"+str(enable_stress_energy_source_terms)+"_FD_order_"+str(FD_order)+".h"), "w") as file: file.write(lp.loop(["i2","i1","i0"],["cctk_nghostzones[2]","cctk_nghostzones[1]","cctk_nghostzones[0]"], ["cctk_lsh[2]-cctk_nghostzones[2]","cctk_lsh[1]-cctk_nghostzones[1]","cctk_lsh[0]-cctk_nghostzones[0]"], ["1","1","1"],["#pragma omp parallel for","",""], "", Ham_mom_string)) # Restore original finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order_orig) end = time.time() print("(BENCH) Finished Hamiltonian & momentum constraint C codegen (FD_order="+str(FD_order)+",Tmunu="+str(enable_stress_energy_source_terms)+") in " + str(end - start) + " seconds.") def enforce_detgammabar_eq_detgammahat__generate_symbolic_expressions_and_C_code(): ###################################### # START: GENERATE SYMBOLIC EXPRESSIONS # Enable rfm_precompute infrastructure, which results in # BSSN RHSs that are free of transcendental functions, # even in curvilinear coordinates, so long as # ConformalFactor is set to "W" (default). par.set_parval_from_str("reference_metric::enable_rfm_precompute","True") par.set_parval_from_str("reference_metric::rfm_precompute_Ccode_outdir",os.path.join(outdir,"rfm_files/")) import BSSN.Enforce_Detgammabar_Constraint as EGC enforce_detg_constraint_symb_expressions = EGC.Enforce_Detgammabar_Constraint_symb_expressions() # Now that we are finished with all the rfm hatted # quantities in generic precomputed functional # form, let's restore them to their closed- # form expressions. par.set_parval_from_str("reference_metric::enable_rfm_precompute","False") # Reset to False to disable rfm_precompute. rfm.ref_metric__hatted_quantities() # END: GENERATE SYMBOLIC EXPRESSIONS ###################################### start = time.time() print("Generating optimized C code (FD_order="+str(FD_order)+") for gamma constraint. May take a while, depending on CoordSystem.") enforce_gammadet_string = fin.FD_outputC("returnstring", enforce_detg_constraint_symb_expressions, params="outCverbose=False,preindent=0,includebraces=False") with open(os.path.join(outdir,"enforcedetgammabar_constraint.h"), "w") as file: file.write(lp.loop(["i2","i1","i0"],["0", "0", "0"], ["cctk_lsh[2]","cctk_lsh[1]","cctk_lsh[0]"], ["1","1","1"], ["#pragma omp parallel for", "#include \"rfm_files/rfm_struct__read2.h\"", "#include \"rfm_files/rfm_struct__read1.h\""],"", "#include \"rfm_files/rfm_struct__read0.h\"\n"+enforce_gammadet_string)) end = time.time() print("(BENCH) Finished gamma constraint C codegen (FD_order="+str(FD_order)+") in " + str(end - start) + " seconds.") if WhichPart=="BSSN_RHSs": BSSN_RHSs__generate_Ccode(BSSN_RHSs__generate_symbolic_expressions()) elif WhichPart=="Ricci": Ricci__generate_Ccode(Ricci__generate_symbolic_expressions()) elif WhichPart=="BSSN_constraints": BSSN_constraints__generate_symbolic_expressions_and_C_code() elif WhichPart=="detgammabar_constraint": enforce_detgammabar_eq_detgammahat__generate_symbolic_expressions_and_C_code() else: print("Error: WhichPart = "+WhichPart+" is not recognized.") sys.exit(1) # Store all NRPy+ environment variables to an output string so NRPy+ environment from within this subprocess can be easily restored import pickle # Standard Python module for converting arbitrary data structures to a uniform format. # https://www.pythonforthelab.com/blog/storing-binary-data-and-serializing/ outstr = [] outstr.append(pickle.dumps(len(gri.glb_gridfcs_list))) for lst in gri.glb_gridfcs_list: outstr.append(pickle.dumps(lst.gftype)) outstr.append(pickle.dumps(lst.name)) outstr.append(pickle.dumps(lst.rank)) outstr.append(pickle.dumps(lst.DIM)) outstr.append(pickle.dumps(len(par.glb_params_list))) for lst in par.glb_params_list: outstr.append(pickle.dumps(lst.type)) outstr.append(pickle.dumps(lst.module)) outstr.append(pickle.dumps(lst.parname)) outstr.append(pickle.dumps(lst.defaultval)) outstr.append(pickle.dumps(len(par.glb_Cparams_list))) for lst in par.glb_Cparams_list: outstr.append(pickle.dumps(lst.type)) outstr.append(pickle.dumps(lst.module)) outstr.append(pickle.dumps(lst.parname)) outstr.append(pickle.dumps(lst.defaultval)) outstr.append(pickle.dumps(len(outC_function_dict))) for Cfuncname, Cfunc in outC_function_dict.items(): outstr.append(pickle.dumps(Cfuncname)) outstr.append(pickle.dumps(Cfunc)) return outstr
def GiRaFFE_NRPy_Main_Driver_generate_all(out_dir): cmd.mkdir(out_dir) gammaDD = ixp.register_gridfunctions_for_single_rank2("AUXEVOL", "gammaDD", "sym01", DIM=3) betaU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "betaU", DIM=3) alpha = gri.register_gridfunctions("AUXEVOL", "alpha") AD = ixp.register_gridfunctions_for_single_rank1("EVOL", "AD") BU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "BU") ValenciavU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "ValenciavU") psi6Phi = gri.register_gridfunctions("EVOL", "psi6Phi") StildeD = ixp.register_gridfunctions_for_single_rank1("EVOL", "StildeD") ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "PhievolParenU", DIM=3) gri.register_gridfunctions("AUXEVOL", "AevolParen") # Declare this symbol: sqrt4pi = par.Cparameters("REAL", thismodule, "sqrt4pi", "sqrt(4.0*M_PI)") GRHD.compute_sqrtgammaDET(gammaDD) GRFFE.compute_AD_source_term_operand_for_FD(GRHD.sqrtgammaDET, betaU, alpha, psi6Phi, AD) GRFFE.compute_psi6Phi_rhs_flux_term_operand(gammaDD, GRHD.sqrtgammaDET, betaU, alpha, AD, psi6Phi) parens_to_print = [\ lhrh(lhs=gri.gfaccess("auxevol_gfs","AevolParen"),rhs=GRFFE.AevolParen),\ lhrh(lhs=gri.gfaccess("auxevol_gfs","PhievolParenU0"),rhs=GRFFE.PhievolParenU[0]),\ lhrh(lhs=gri.gfaccess("auxevol_gfs","PhievolParenU1"),rhs=GRFFE.PhievolParenU[1]),\ lhrh(lhs=gri.gfaccess("auxevol_gfs","PhievolParenU2"),rhs=GRFFE.PhievolParenU[2]),\ ] subdir = "RHSs" cmd.mkdir(os.path.join(out_dir, subdir)) desc = "Calculate quantities to be finite-differenced for the GRFFE RHSs" name = "calculate_AD_gauge_term_psi6Phi_flux_term_for_RHSs" outCfunction( outfile=os.path.join(out_dir, subdir, name + ".h"), desc=desc, name=name, params= "const paramstruct *restrict params,const REAL *restrict in_gfs,REAL *restrict auxevol_gfs", body=fin.FD_outputC("returnstring", parens_to_print, params=outCparams).replace("IDX4", "IDX4S"), loopopts="AllPoints", rel_path_for_Cparams=os.path.join("../")) xi_damping = par.Cparameters("REAL", thismodule, "xi_damping", 0.1) GRFFE.compute_psi6Phi_rhs_damping_term(alpha, psi6Phi, xi_damping) AevolParen_dD = ixp.declarerank1("AevolParen_dD", DIM=3) PhievolParenU_dD = ixp.declarerank2("PhievolParenU_dD", "nosym", DIM=3) A_rhsD = ixp.zerorank1() psi6Phi_rhs = GRFFE.psi6Phi_damping for i in range(3): A_rhsD[i] += -AevolParen_dD[i] psi6Phi_rhs += -PhievolParenU_dD[i][i] # Add Kreiss-Oliger dissipation to the GRFFE RHSs: # psi6Phi_dKOD = ixp.declarerank1("psi6Phi_dKOD") # AD_dKOD = ixp.declarerank2("AD_dKOD","nosym") # for i in range(3): # psi6Phi_rhs += diss_strength*psi6Phi_dKOD[i]*rfm.ReU[i] # ReU[i] = 1/scalefactor_orthog_funcform[i] # for j in range(3): # A_rhsD[j] += diss_strength*AD_dKOD[j][i]*rfm.ReU[i] # ReU[i] = 1/scalefactor_orthog_funcform[i] RHSs_to_print = [\ lhrh(lhs=gri.gfaccess("rhs_gfs","AD0"),rhs=A_rhsD[0]),\ lhrh(lhs=gri.gfaccess("rhs_gfs","AD1"),rhs=A_rhsD[1]),\ lhrh(lhs=gri.gfaccess("rhs_gfs","AD2"),rhs=A_rhsD[2]),\ lhrh(lhs=gri.gfaccess("rhs_gfs","psi6Phi"),rhs=psi6Phi_rhs),\ ] desc = "Calculate AD gauge term and psi6Phi RHSs" name = "calculate_AD_gauge_psi6Phi_RHSs" source_Ccode = outCfunction( outfile="returnstring", desc=desc, name=name, params= "const paramstruct *params,const REAL *in_gfs,const REAL *auxevol_gfs,REAL *rhs_gfs", body=fin.FD_outputC("returnstring", RHSs_to_print, params=outCparams).replace("IDX4", "IDX4S"), 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") # Note the above .replace() functions. These serve to expand the loop range into the ghostzones, since # the second-order FD needs fewer than some other algorithms we use do. with open(os.path.join(out_dir, subdir, name + ".h"), "w") as file: file.write(source_Ccode) source.write_out_functions_for_StildeD_source_term( os.path.join(out_dir, subdir), outCparams, gammaDD, betaU, alpha, ValenciavU, BU, sqrt4pi) subdir = "FCVAL" cmd.mkdir(os.path.join(out_dir, subdir)) FCVAL.GiRaFFE_NRPy_FCVAL(os.path.join(out_dir, subdir)) subdir = "PPM" cmd.mkdir(os.path.join(out_dir, subdir)) PPM.GiRaFFE_NRPy_PPM(os.path.join(out_dir, subdir)) # 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) subdir = "RHSs" Af.GiRaFFE_NRPy_Afield_flux(os.path.join(out_dir, subdir)) Sf.generate_C_code_for_Stilde_flux(os.path.join(out_dir, subdir), True, alpha_face, gamma_faceDD, beta_faceU, Valenciav_rU, B_rU, Valenciav_lU, B_lU, sqrt4pi) subdir = "boundary_conditions" cmd.mkdir(os.path.join(out_dir, subdir)) BC.GiRaFFE_NRPy_BCs(os.path.join(out_dir, subdir)) subdir = "A2B" cmd.mkdir(os.path.join(out_dir, subdir)) A2B.GiRaFFE_NRPy_A2B(os.path.join(out_dir, subdir), gammaDD, AD, BU) C2P_P2C.GiRaFFE_NRPy_C2P(StildeD, BU, gammaDD, betaU, alpha) values_to_print = [\ lhrh(lhs=gri.gfaccess("in_gfs","StildeD0"),rhs=C2P_P2C.outStildeD[0]),\ lhrh(lhs=gri.gfaccess("in_gfs","StildeD1"),rhs=C2P_P2C.outStildeD[1]),\ lhrh(lhs=gri.gfaccess("in_gfs","StildeD2"),rhs=C2P_P2C.outStildeD[2]),\ lhrh(lhs=gri.gfaccess("auxevol_gfs","ValenciavU0"),rhs=C2P_P2C.ValenciavU[0]),\ lhrh(lhs=gri.gfaccess("auxevol_gfs","ValenciavU1"),rhs=C2P_P2C.ValenciavU[1]),\ lhrh(lhs=gri.gfaccess("auxevol_gfs","ValenciavU2"),rhs=C2P_P2C.ValenciavU[2])\ ] subdir = "C2P" cmd.mkdir(os.path.join(out_dir, subdir)) desc = "Apply fixes to \tilde{S}_i and recompute the velocity to match with current sheet prescription." name = "GiRaFFE_NRPy_cons_to_prims" outCfunction( outfile=os.path.join(out_dir, subdir, name + ".h"), desc=desc, name=name, params= "const paramstruct *params,REAL *xx[3],REAL *auxevol_gfs,REAL *in_gfs", body=fin.FD_outputC("returnstring", values_to_print, params=outCparams).replace("IDX4", "IDX4S"), loopopts="AllPoints,Read_xxs", rel_path_for_Cparams=os.path.join("../")) C2P_P2C.GiRaFFE_NRPy_P2C(gammaDD, betaU, alpha, ValenciavU, BU, sqrt4pi) values_to_print = [\ lhrh(lhs=gri.gfaccess("in_gfs","StildeD0"),rhs=C2P_P2C.StildeD[0]),\ lhrh(lhs=gri.gfaccess("in_gfs","StildeD1"),rhs=C2P_P2C.StildeD[1]),\ lhrh(lhs=gri.gfaccess("in_gfs","StildeD2"),rhs=C2P_P2C.StildeD[2]),\ ] desc = "Recompute StildeD after current sheet fix to Valencia 3-velocity to ensure consistency between conservative & primitive variables." name = "GiRaFFE_NRPy_prims_to_cons" outCfunction( outfile=os.path.join(out_dir, subdir, name + ".h"), desc=desc, name=name, params="const paramstruct *params,REAL *auxevol_gfs,REAL *in_gfs", body=fin.FD_outputC("returnstring", values_to_print, params=outCparams).replace("IDX4", "IDX4S"), loopopts="AllPoints", rel_path_for_Cparams=os.path.join("../")) # Write out the main driver itself: with open(os.path.join(out_dir, "GiRaFFE_NRPy_Main_Driver.h"), "w") as file: file.write(r"""// Structure to track ghostzones for PPM: typedef struct __gf_and_gz_struct__ { REAL *gf; int gz_lo[4],gz_hi[4]; } gf_and_gz_struct; // Some additional constants needed for PPM: const int VX=0,VY=1,VZ=2,BX=3,BY=4,BZ=5; const int NUM_RECONSTRUCT_GFS = 6; // Include ALL functions needed for evolution #include "RHSs/calculate_AD_gauge_term_psi6Phi_flux_term_for_RHSs.h" #include "RHSs/calculate_AD_gauge_psi6Phi_RHSs.h" #include "PPM/reconstruct_set_of_prims_PPM_GRFFE_NRPy.c" #include "FCVAL/interpolate_metric_gfs_to_cell_faces.h" #include "RHSs/calculate_StildeD0_source_term.h" #include "RHSs/calculate_StildeD1_source_term.h" #include "RHSs/calculate_StildeD2_source_term.h" #include "../calculate_E_field_flat_all_in_one.h" #include "RHSs/calculate_Stilde_flux_D0.h" #include "RHSs/calculate_Stilde_flux_D1.h" #include "RHSs/calculate_Stilde_flux_D2.h" #include "boundary_conditions/GiRaFFE_boundary_conditions.h" #include "A2B/driver_AtoB.h" #include "C2P/GiRaFFE_NRPy_cons_to_prims.h" #include "C2P/GiRaFFE_NRPy_prims_to_cons.h" void override_BU_with_old_GiRaFFE(const paramstruct *restrict params,REAL *restrict auxevol_gfs,const int n) { #include "set_Cparameters.h" char filename[100]; sprintf(filename,"BU0_override-%08d.bin",n); FILE *out2D = fopen(filename, "rb"); fread(auxevol_gfs+BU0GF*Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2, sizeof(double),Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2,out2D); fclose(out2D); sprintf(filename,"BU1_override-%08d.bin",n); out2D = fopen(filename, "rb"); fread(auxevol_gfs+BU1GF*Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2, sizeof(double),Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2,out2D); fclose(out2D); sprintf(filename,"BU2_override-%08d.bin",n); out2D = fopen(filename, "rb"); fread(auxevol_gfs+BU2GF*Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2, sizeof(double),Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2,out2D); fclose(out2D); } void GiRaFFE_NRPy_RHSs(const paramstruct *restrict params,REAL *restrict auxevol_gfs,const REAL *restrict in_gfs,REAL *restrict rhs_gfs) { #include "set_Cparameters.h" // First thing's first: initialize the RHSs to zero! #pragma omp parallel for for(int ii=0;ii<Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2*NUM_EVOL_GFS;ii++) { rhs_gfs[ii] = 0.0; } // Next calculate the easier source terms that don't require flux directions // This will also reset the RHSs for each gf at each new timestep. calculate_AD_gauge_term_psi6Phi_flux_term_for_RHSs(params,in_gfs,auxevol_gfs); calculate_AD_gauge_psi6Phi_RHSs(params,in_gfs,auxevol_gfs,rhs_gfs); // Now, we set up a bunch of structs of pointers to properly guide the PPM algorithm. // They also count the number of ghostzones available. gf_and_gz_struct in_prims[NUM_RECONSTRUCT_GFS], out_prims_r[NUM_RECONSTRUCT_GFS], out_prims_l[NUM_RECONSTRUCT_GFS]; int which_prims_to_reconstruct[NUM_RECONSTRUCT_GFS],num_prims_to_reconstruct; const int Nxxp2NG012 = Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2; REAL *temporary = auxevol_gfs + Nxxp2NG012*AEVOLPARENGF; //We're not using this anymore // This sets pointers to the portion of auxevol_gfs containing the relevant gridfunction. int ww=0; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAVU0GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RU0GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LU0GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAVU1GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RU1GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LU1GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAVU2GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RU2GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LU2GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*BU0GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*B_RU0GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*B_LU0GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*BU1GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*B_RU1GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*B_LU1GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*BU2GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*B_RU2GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*B_LU2GF; ww++; // Prims are defined AT ALL GRIDPOINTS, so we set the # of ghostzones to zero: for(int i=0;i<NUM_RECONSTRUCT_GFS;i++) for(int j=1;j<=3;j++) { in_prims[i].gz_lo[j]=0; in_prims[i].gz_hi[j]=0; } // Left/right variables are not yet defined, yet we set the # of gz's to zero by default: for(int i=0;i<NUM_RECONSTRUCT_GFS;i++) for(int j=1;j<=3;j++) { out_prims_r[i].gz_lo[j]=0; out_prims_r[i].gz_hi[j]=0; } for(int i=0;i<NUM_RECONSTRUCT_GFS;i++) for(int j=1;j<=3;j++) { out_prims_l[i].gz_lo[j]=0; out_prims_l[i].gz_hi[j]=0; } ww=0; which_prims_to_reconstruct[ww]=VX; ww++; which_prims_to_reconstruct[ww]=VY; ww++; which_prims_to_reconstruct[ww]=VZ; ww++; which_prims_to_reconstruct[ww]=BX; ww++; which_prims_to_reconstruct[ww]=BY; ww++; which_prims_to_reconstruct[ww]=BZ; ww++; num_prims_to_reconstruct=ww; // In each direction, perform the PPM reconstruction procedure. // Then, add the fluxes to the RHS as appropriate. for(int flux_dirn=0;flux_dirn<3;flux_dirn++) { // In each direction, interpolate the metric gfs (gamma,beta,alpha) to cell faces. interpolate_metric_gfs_to_cell_faces(params,auxevol_gfs,flux_dirn+1); // Then, reconstruct the primitive variables on the cell faces. // This function is housed in the file: "reconstruct_set_of_prims_PPM_GRFFE_NRPy.c" reconstruct_set_of_prims_PPM_GRFFE_NRPy(params, auxevol_gfs, flux_dirn+1, num_prims_to_reconstruct, which_prims_to_reconstruct, in_prims, out_prims_r, out_prims_l, temporary); // For example, if flux_dirn==0, then at gamma_faceDD00(i,j,k) represents gamma_{xx} // at (i-1/2,j,k), Valenciav_lU0(i,j,k) is the x-component of the velocity at (i-1/2-epsilon,j,k), // and Valenciav_rU0(i,j,k) is the x-component of the velocity at (i-1/2+epsilon,j,k). if(flux_dirn==0) { // Next, we calculate the source term for StildeD. Again, this also resets the rhs_gfs array at // each new timestep. calculate_StildeD0_source_term(params,auxevol_gfs,rhs_gfs); // Now, compute the electric field on each face of a cell and add it to the RHSs as appropriate //calculate_E_field_D0_right(params,auxevol_gfs,rhs_gfs); //calculate_E_field_D0_left(params,auxevol_gfs,rhs_gfs); // Finally, we calculate the flux of StildeD and add the appropriate finite-differences // to the RHSs. calculate_Stilde_flux_D0(params,auxevol_gfs,rhs_gfs); } else if(flux_dirn==1) { calculate_StildeD1_source_term(params,auxevol_gfs,rhs_gfs); //calculate_E_field_D1_right(params,auxevol_gfs,rhs_gfs); //calculate_E_field_D1_left(params,auxevol_gfs,rhs_gfs); calculate_Stilde_flux_D1(params,auxevol_gfs,rhs_gfs); } else { calculate_StildeD2_source_term(params,auxevol_gfs,rhs_gfs); //calculate_E_field_D2_right(params,auxevol_gfs,rhs_gfs); //calculate_E_field_D2_left(params,auxevol_gfs,rhs_gfs); calculate_Stilde_flux_D2(params,auxevol_gfs,rhs_gfs); } for(int count=0;count<=1;count++) { // This function is written to be general, using notation that matches the forward permutation added to AD2, // i.e., [F_HLL^x(B^y)]_z corresponding to flux_dirn=0, count=1. // The SIGN parameter is necessary because // -E_z(x_i,y_j,z_k) = 0.25 ( [F_HLL^x(B^y)]_z(i+1/2,j,k)+[F_HLL^x(B^y)]_z(i-1/2,j,k) // -[F_HLL^y(B^x)]_z(i,j+1/2,k)-[F_HLL^y(B^x)]_z(i,j-1/2,k) ) // Note the negative signs on the reversed permutation terms! // By cyclically permuting with flux_dirn, we // get contributions to the other components, and by incrementing count, we get the backward permutations: // Let's suppose flux_dirn = 0. Then we will need to update Ay (count=0) and Az (count=1): // flux_dirn=count=0 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (0+1+0)%3=AD1GF <- Updating Ay! // (flux_dirn)%3 = (0)%3 = 0 Vx // (flux_dirn-count+2)%3 = (0-0+2)%3 = 2 Vz . Inputs Vx, Vz -> SIGN = -1 ; 2.0*((REAL)count)-1.0=-1 check! // flux_dirn=0,count=1 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (0+1+1)%3=AD2GF <- Updating Az! // (flux_dirn)%3 = (0)%3 = 0 Vx // (flux_dirn-count+2)%3 = (0-1+2)%3 = 1 Vy . Inputs Vx, Vy -> SIGN = +1 ; 2.0*((REAL)count)-1.0=2-1=+1 check! // Let's suppose flux_dirn = 1. Then we will need to update Az (count=0) and Ax (count=1): // flux_dirn=1,count=0 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (1+1+0)%3=AD2GF <- Updating Az! // (flux_dirn)%3 = (1)%3 = 1 Vy // (flux_dirn-count+2)%3 = (1-0+2)%3 = 0 Vx . Inputs Vy, Vx -> SIGN = -1 ; 2.0*((REAL)count)-1.0=-1 check! // flux_dirn=count=1 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (1+1+1)%3=AD0GF <- Updating Ax! // (flux_dirn)%3 = (1)%3 = 1 Vy // (flux_dirn-count+2)%3 = (1-1+2)%3 = 2 Vz . Inputs Vy, Vz -> SIGN = +1 ; 2.0*((REAL)count)-1.0=2-1=+1 check! // Let's suppose flux_dirn = 2. Then we will need to update Ax (count=0) and Ay (count=1): // flux_dirn=2,count=0 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (2+1+0)%3=AD0GF <- Updating Ax! // (flux_dirn)%3 = (2)%3 = 2 Vz // (flux_dirn-count+2)%3 = (2-0+2)%3 = 1 Vy . Inputs Vz, Vy -> SIGN = -1 ; 2.0*((REAL)count)-1.0=-1 check! // flux_dirn=2,count=1 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (2+1+1)%3=AD1GF <- Updating Ay! // (flux_dirn)%3 = (2)%3 = 2 Vz // (flux_dirn-count+2)%3 = (2-1+2)%3 = 0 Vx . Inputs Vz, Vx -> SIGN = +1 ; 2.0*((REAL)count)-1.0=2-1=+1 check! calculate_E_field_flat_all_in_one(params, &auxevol_gfs[IDX4ptS(VALENCIAV_RU0GF+(flux_dirn)%3, 0)],&auxevol_gfs[IDX4ptS(VALENCIAV_RU0GF+(flux_dirn-count+2)%3, 0)], &auxevol_gfs[IDX4ptS(VALENCIAV_LU0GF+(flux_dirn)%3, 0)],&auxevol_gfs[IDX4ptS(VALENCIAV_LU0GF+(flux_dirn-count+2)%3, 0)], &auxevol_gfs[IDX4ptS(B_RU0GF +(flux_dirn)%3, 0)],&auxevol_gfs[IDX4ptS(B_RU0GF +(flux_dirn-count+2)%3, 0)], &auxevol_gfs[IDX4ptS(B_LU0GF +(flux_dirn)%3, 0)],&auxevol_gfs[IDX4ptS(B_LU0GF +(flux_dirn-count+2)%3, 0)], &auxevol_gfs[IDX4ptS(B_RU0GF +(flux_dirn-count+2)%3, 0)], &auxevol_gfs[IDX4ptS(B_LU0GF +(flux_dirn-count+2)%3, 0)], &rhs_gfs[IDX4ptS(AD0GF+(flux_dirn+1+count)%3,0)], 2.0*((REAL)count)-1.0, flux_dirn); } } } void GiRaFFE_NRPy_post_step(const paramstruct *restrict params,REAL *xx[3],REAL *restrict auxevol_gfs,REAL *restrict evol_gfs,const int n) { // First, apply BCs to AD and psi6Phi. Then calculate BU from AD apply_bcs_potential(params,evol_gfs); driver_A_to_B(params,evol_gfs,auxevol_gfs); //override_BU_with_old_GiRaFFE(params,auxevol_gfs,n); // Apply fixes to StildeD, then recompute the velocity at the new timestep. // Apply the current sheet prescription to the velocities GiRaFFE_NRPy_cons_to_prims(params,xx,auxevol_gfs,evol_gfs); // Then, recompute StildeD to be consistent with the new velocities //GiRaFFE_NRPy_prims_to_cons(params,auxevol_gfs,evol_gfs); // Finally, apply outflow boundary conditions to the velocities. apply_bcs_velocity(params,auxevol_gfs); } """)
def GiRaFFE_NRPy_Afield_flux(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. gammaDD = ixp.declarerank2("gammaDD", "sym01", DIM=3) betaU = ixp.declarerank1("betaU", DIM=3) alpha = sp.sympify("alpha") for flux_dirn in range(3): chsp.find_cmax_cmin(flux_dirn, gammaDD, betaU, alpha) Ccode_kernel = outputC([chsp.cmax, chsp.cmin], ["cmax", "cmin"], "returnstring", params="outCverbose=False,CSE_sorting=none") Ccode_kernel = Ccode_kernel.replace("cmax", "*cmax").replace("cmin", "*cmin") Ccode_kernel = Ccode_kernel.replace("betaU0", "betaUi").replace( "betaU1", "betaUi").replace("betaU2", "betaUi") with open( os.path.join(Ccodesdir, "compute_cmax_cmin_dirn" + str(flux_dirn) + ".h"), "w") as file: file.write(Ccode_kernel) with open(os.path.join(Ccodesdir, "calculate_E_field_flat_all_in_one.h"), "w") as file: file.write( r"""void find_cmax_cmin(const REAL gammaDD00, const REAL gammaDD01, const REAL gammaDD02, const REAL gammaDD11, const REAL gammaDD12, const REAL gammaDD22, const REAL betaUi, const REAL alpha, const int flux_dirn, REAL *cmax, REAL *cmin) { switch(flux_dirn) { case 0: #include "compute_cmax_cmin_dirn0.h" break; case 1: #include "compute_cmax_cmin_dirn1.h" break; case 2: #include "compute_cmax_cmin_dirn2.h" break; default: printf("Invalid parameter flux_dirn!"); *cmax = 1.0/0.0; *cmin = 1.0/0.0; break; } } REAL HLLE_solve(REAL F0B1_r, REAL F0B1_l, REAL U_r, REAL U_l, REAL cmin, REAL cmax) { // Eq. 3.15 of https://epubs.siam.org/doi/abs/10.1137/1025002?journalCode=siread // F_HLLE = (c_min F_R + c_max F_L - c_min c_max (U_R-U_L)) / (c_min + c_max) return (cmin*F0B1_r + cmax*F0B1_l - cmin*cmax*(U_r-U_l)) / (cmin+cmax); } /* Calculate the electric flux on both faces in the input direction. The input count is an integer that is either 0 or 1. If it is 0, this implies that the components are input in order of a backwards permutation and the final results will need to be multiplied by -1.0. If it is 1, then the permutation is forwards. */ void calculate_E_field_flat_all_in_one(const paramstruct *params, const REAL *Vr0,const REAL *Vr1, const REAL *Vl0,const REAL *Vl1, const REAL *Br0,const REAL *Br1, const REAL *Bl0,const REAL *Bl1, const REAL *Brflux_dirn, const REAL *Blflux_dirn, const REAL *gamma_faceDD00, const REAL *gamma_faceDD01, const REAL *gamma_faceDD02, const REAL *gamma_faceDD11, const REAL *gamma_faceDD12, const REAL *gamma_faceDD22, const REAL *beta_faceU0, const REAL *beta_faceU1, const REAL *alpha_face, REAL *A2_rhs,const REAL SIGN,const int flux_dirn) { // This function is written to be generic and compute the contribution for all three AD RHSs. // However, for convenience, the notation used in the function itself is for the contribution // to AD2, specifically the [F_HLL^x(B^y)]_z term, with reconstructions in the x direction. This // corresponds to flux_dirn=0 and count=1 (which corresponds to SIGN=+1.0). // Thus, Az(i,j,k) += 0.25 ( [F_HLL^x(B^y)]_z(i+1/2,j,k)+[F_HLL^x(B^y)]_z(i-1/2,j,k)) are solved here. // The other terms are computed by cyclically permuting the indices when calling this function. #include "../set_Cparameters.h" #pragma omp parallel for for(int i2=NGHOSTS; i2<NGHOSTS+Nxx2; i2++) { for(int i1=NGHOSTS; i1<NGHOSTS+Nxx1; i1++) { for(int i0=NGHOSTS; i0<NGHOSTS+Nxx0; i0++) { // First, we set the index from which we will read memory. indexp1 is incremented by // one point in the direction of reconstruction. These correspond to the faces at at // i-1/2 and i+1/2, respectively. // Now, we read in memory. We need the x and y components of velocity and magnetic field on both // the left and right sides of the interface at *both* faces. // Here, the point (i0,i1,i2) corresponds to the point (i-1/2,j,k) const int index = IDX3S(i0,i1,i2); const double alpha = alpha_face[index]; const double betaU0 = beta_faceU0[index]; const double betaU1 = beta_faceU1[index]; const double v_rU0 = alpha*Vr0[index]-betaU0; const double v_rU1 = alpha*Vr1[index]-betaU1; const double B_rU0 = Br0[index]; const double B_rU1 = Br1[index]; const double B_rflux_dirn = Brflux_dirn[index]; const double v_lU0 = alpha*Vl0[index]-betaU0; const double v_lU1 = alpha*Vl1[index]-betaU1; const double B_lU0 = Bl0[index]; const double B_lU1 = Bl1[index]; const double B_lflux_dirn = Blflux_dirn[index]; // We will also need need the square root of the metric determinant here at this point: const REAL gxx = gamma_faceDD00[index]; const REAL gxy = gamma_faceDD01[index]; const REAL gxz = gamma_faceDD02[index]; const REAL gyy = gamma_faceDD11[index]; const REAL gyz = gamma_faceDD12[index]; const REAL gzz = gamma_faceDD22[index]; const REAL sqrtgammaDET = sqrt( gxx*gyy*gzz - gxx*gyz*gyz +2*gxy*gxz*gyz - gyy*gxz*gxz - gzz*gxy*gxy ); // ******************************* // REPEAT ABOVE, but at i+1, which corresponds to point (i+1/2,j,k) // Recall that the documentation here assumes flux_dirn==0, but the // algorithm is generalized so that any flux_dirn or velocity/magnetic // field component can be computed via permuting the inputs into this // function. const int indexp1 = IDX3S(i0+(flux_dirn==0),i1+(flux_dirn==1),i2+(flux_dirn==2)); const double alpha_p1 = alpha_face[indexp1]; const double betaU0_p1 = beta_faceU0[indexp1]; const double betaU1_p1 = beta_faceU1[indexp1]; const double v_rU0_p1 = alpha_p1*Vr0[indexp1]-betaU0_p1; const double v_rU1_p1 = alpha_p1*Vr1[indexp1]-betaU1_p1; const double B_rU0_p1 = Br0[indexp1]; const double B_rU1_p1 = Br1[indexp1]; const double B_rflux_dirn_p1 = Brflux_dirn[indexp1]; const double v_lU0_p1 = alpha_p1*Vl0[indexp1]-betaU0_p1; const double v_lU1_p1 = alpha_p1*Vl1[indexp1]-betaU1_p1; const double B_lU0_p1 = Bl0[indexp1]; const double B_lU1_p1 = Bl1[indexp1]; const double B_lflux_dirn_p1 = Blflux_dirn[indexp1]; // We will also need need the square root of the metric determinant here at this point: const REAL gxx_p1 = gamma_faceDD00[indexp1]; const REAL gxy_p1 = gamma_faceDD01[indexp1]; const REAL gxz_p1 = gamma_faceDD02[indexp1]; const REAL gyy_p1 = gamma_faceDD11[indexp1]; const REAL gyz_p1 = gamma_faceDD12[indexp1]; const REAL gzz_p1 = gamma_faceDD22[indexp1]; const REAL sqrtgammaDET_p1 = sqrt( gxx_p1*gyy_p1*gzz_p1 - gxx_p1*gyz_p1*gyz_p1 +2*gxy_p1*gxz_p1*gyz_p1 - gyy_p1*gxz_p1*gxz_p1 - gzz_p1*gxy_p1*gxy_p1 ); // ******************************* // DEBUGGING: // if(flux_dirn==0 && SIGN>0 && i1==Nxx_plus_2NGHOSTS1/2 && i2==Nxx_plus_2NGHOSTS2/2) { // printf("index=%d & indexp1=%d\n",index,indexp1); // } // Since we are computing A_z, the relevant equation here is: // -E_z(x_i,y_j,z_k) = 0.25 ( [F_HLL^x(B^y)]_z(i+1/2,j,k)+[F_HLL^x(B^y)]_z(i-1/2,j,k) // -[F_HLL^y(B^x)]_z(i,j+1/2,k)-[F_HLL^y(B^x)]_z(i,j-1/2,k) ) // We will construct the above sum one half at a time, first with SIGN=+1, which // corresponds to flux_dirn = 0, count=1, and // takes care of the terms: // [F_HLL^x(B^y)]_z(i+1/2,j,k)+[F_HLL^x(B^y)]_z(i-1/2,j,k) // ( Note that we will repeat the above with flux_dirn = 1, count = 0, with SIGN=-1 // AND with the input components switched (x->y,y->x) so that we get the term // -[F_HLL^y(B^x)]_z(i,j+1/2,k)-[F_HLL^y(B^x)]_z(i,j-1/2,k) // thus completing the above sum. ) // Here, [F_HLL^i(B^j)]_k = (v^i B^j - v^j B^i) in general. // Calculate the flux vector on each face for each component of the E-field: // The F(B) terms are as Eq. 6 in Giacomazzo: https://arxiv.org/pdf/1009.2468.pdf // [F^i(B^j)]_k = \sqrt{\gamma} (v^i B^j - v^j B^i) // Therefore since we want [F_HLL^x(B^y)]_z, // we will code (v^x B^y - v^y B^x) on both left and right faces. const REAL F0B1_r = sqrtgammaDET*(v_rU0*B_rU1 - v_rU1*B_rU0); const REAL F0B1_l = sqrtgammaDET*(v_lU0*B_lU1 - v_lU1*B_lU0); // Compute the state vector for these terms: const REAL U_r = B_rflux_dirn; const REAL U_l = B_lflux_dirn; REAL cmin,cmax; // Basic HLLE solver: find_cmax_cmin(gxx,gxy,gxz, gyy,gyz,gzz, betaU0,alpha,flux_dirn, &cmax, &cmin); const REAL FHLL_0B1 = HLLE_solve(F0B1_r, F0B1_l, U_r, U_l, cmin, cmax); // ************************************ // ************************************ // REPEAT ABOVE, but at point i+1 // Calculate the flux vector on each face for each component of the E-field: const REAL F0B1_r_p1 = sqrtgammaDET_p1*(v_rU0_p1*B_rU1_p1 - v_rU1_p1*B_rU0_p1); const REAL F0B1_l_p1 = sqrtgammaDET_p1*(v_lU0_p1*B_lU1_p1 - v_lU1_p1*B_lU0_p1); // Compute the state vector for this flux direction const REAL U_r_p1 = B_rflux_dirn_p1; const REAL U_l_p1 = B_lflux_dirn_p1; //const REAL U_r_p1 = B_rU1_p1; //const REAL U_l_p1 = B_lU1_p1; // Basic HLLE solver, but at the next point: find_cmax_cmin(gxx_p1,gxy_p1,gxz_p1, gyy_p1,gyz_p1,gzz_p1, betaU0_p1,alpha_p1,flux_dirn, &cmax, &cmin); const REAL FHLL_0B1p1 = HLLE_solve(F0B1_r_p1, F0B1_l_p1, U_r_p1, U_l_p1, cmin, cmax); // ************************************ // ************************************ // With the Riemann problem solved, we add the contributions to the RHSs: // -E_z(x_i,y_j,z_k) &= 0.25 ( [F_HLL^x(B^y)]_z(i+1/2,j,k)+[F_HLL^x(B^y)]_z(i-1/2,j,k) // -[F_HLL^y(B^x)]_z(i,j+1/2,k)-[F_HLL^y(B^x)]_z(i,j-1/2,k) ) // (Eq. 11 in https://arxiv.org/pdf/1009.2468.pdf) // This code, as written, solves the first two terms for flux_dirn=0. Calling this function for count=0 // and flux_dirn=1 flips x for y to solve the latter two, switching to SIGN=-1 as well. // Here, we finally add together the output of the HLLE solver at i-1/2 and i+1/2 // We also multiply by the SIGN dictated by the order of the input vectors and divide by 4. A2_rhs[index] += SIGN*0.25*(FHLL_0B1 + FHLL_0B1p1); // flux dirn = 0 ===================> i-1/2 i+1/2 // Eq 11 in Giacomazzo: // -FxBy(avg over i-1/2 and i+1/2) + FyBx(avg over j-1/2 and j+1/2) // Eq 6 in Giacomazzo: // FxBy = vxBy - vyBx // -> // FHLL_0B1 = vyBx - vxBy } // END LOOP: for(int i0=NGHOSTS; i0<NGHOSTS+Nxx0; i0++) } // END LOOP: for(int i1=NGHOSTS; i1<NGHOSTS+Nxx1; i1++) } // END LOOP: for(int i2=NGHOSTS; i2<NGHOSTS+Nxx2; i2++) } """)