def register_gridfunctions_for_single_rank2(gf_type, gf_basename, symmetry_option, DIM=-1): # Step 0: Verify the gridfunction basename is valid: gri.verify_gridfunction_basename_is_valid(gf_basename) # Step 1: Declare a list of lists of SymPy variables, # where IDX_OBJ_TMP[i][j] = gf_basename+str(i)+str(j) IDX_OBJ_TMP = declarerank2(gf_basename, symmetry_option, DIM) # Step 2: register each gridfunction, being careful not # not to store duplicates due to rank-2 symmetries. if DIM == -1: DIM = par.parval_from_str("DIM") # Register only unique gridfunctions. Otherwise # rank-2 symmetries might result in duplicates gf_list = [] for i in range(DIM): for j in range(DIM): save = True for l in range(len(gf_list)): if gf_list[l] == str(IDX_OBJ_TMP[i][j]): save = False if save == True: gf_list.append(str(IDX_OBJ_TMP[i][j])) gri.register_gridfunctions(gf_type, gf_list, rank=2, is_indexed=True, DIM=DIM) # Step 3: Return array of SymPy variables return IDX_OBJ_TMP
def MaxwellCartesian_ID(): DIM = par.parval_from_str("grid::DIM") x, y, z = gri.register_gridfunctions("AUX", ["x", "y", "z"]) gammaDD = ixp.register_gridfunctions_for_single_rank2("AUX","gammaDD", "sym01") # The AUX or EVOL designation is *not* # used in diagnostic modules. # Step 1: Declare free parameters intrinsic to these initial data amp,lam = par.Cparameters("REAL",__name__,["amp","lam"], [1.0,1.0]) # __name__ = "MaxwellCartesian_ID", this module's name # Step 2: Set the initial data system = par.parval_from_str("System_to_use") if system == "System_I" or system == "System_II": global AidD,EidD,psi_ID AidD = ixp.zerorank1() EidD = ixp.zerorank1() EidU = ixp.zerorank1() # Set the coordinate transformations: radial = sp.sqrt(x*x + y*y + z*z) polar = sp.atan2(sp.sqrt(x*x + y*y),z) EU_phi = 8*amp*radial*sp.sin(polar)*lam*lam*sp.exp(-lam*radial*radial) EidU[0] = -(y * EU_phi)/sp.sqrt(x*x + y*y) EidU[1] = (x * EU_phi)/sp.sqrt(x*x + y*y) # The z component (2)is zero. for i in range(DIM): for j in range(DIM): EidD[i] += gammaDD[i][j] * EidU[j] psi_ID = sp.sympify(0) if system == "System_II": global Gamma_ID Gamma_ID = sp.sympify(0) else: print("Invalid choice of system: System_to_use must be either System_I or System_II")
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 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 register_gridfunctions_for_single_rank1(gf_type,gf_basename, DIM=-1, f_infinity=0.0, wavespeed=1.0): # Step 0: Verify the gridfunction basename is valid: gri.verify_gridfunction_basename_is_valid(gf_basename) # Step 1: Declare a list of SymPy variables, # where IDX_OBJ_TMP[i] = gf_basename+str(i) IDX_OBJ_TMP = declarerank1(gf_basename, DIM) # Step 2: Register each gridfunction if DIM==-1: DIM = par.parval_from_str("DIM") gf_list = [] for i in range(DIM): gf_list.append(str(IDX_OBJ_TMP[i])) gri.register_gridfunctions(gf_type, gf_list, rank=1, is_indexed=True, DIM=DIM, f_infinity=f_infinity, wavespeed=wavespeed) # Step 3: Return array of SymPy variables return IDX_OBJ_TMP
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 generate_C_code_for_Stilde_flux(out_dir,inputs_provided = False, alpha_face=None, gamma_faceDD=None, beta_faceU=None, Valenciav_rU=None, B_rU=None, Valenciav_lU=None, B_lU=None, sqrt4pi=None, outCparams = "outCverbose=False,CSE_sorting=none"): if not inputs_provided: # We will pass values of the gridfunction on the cell faces into the function. This requires us # to declare them as C parameters in NRPy+. We will denote this with the _face infix/suffix. alpha_face = gri.register_gridfunctions("AUXEVOL","alpha_face") gamma_faceDD = ixp.register_gridfunctions_for_single_rank2("AUXEVOL","gamma_faceDD","sym01") beta_faceU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","beta_faceU") # We'll need some more gridfunctions, now, to represent the reconstructions of BU and ValenciavU # on the right and left faces Valenciav_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","Valenciav_rU",DIM=3) B_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","B_rU",DIM=3) Valenciav_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","Valenciav_lU",DIM=3) B_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","B_lU",DIM=3) sqrt4pi = par.Cparameters("REAL",thismodule,"sqrt4pi","sqrt(4.0*M_PI)") for flux_dirn in range(3): calculate_Stilde_flux(flux_dirn,alpha_face,gamma_faceDD,beta_faceU,\ Valenciav_rU,B_rU,Valenciav_lU,B_lU,sqrt4pi) Stilde_flux_to_print = [\ Stilde_fluxD[0],\ Stilde_fluxD[1],\ Stilde_fluxD[2],\ ] Stilde_flux_names = [\ "Stilde_fluxD0",\ "Stilde_fluxD1",\ "Stilde_fluxD2",\ ] desc = "Compute the flux of all 3 components of tilde{S}_i on the right face in the " + str(flux_dirn) + "." name = "calculate_Stilde_flux_D" + str(flux_dirn) + "_right" outCfunction( outfile = os.path.join(out_dir,name+".h"), desc=desc, name=name, params ="const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs", body = Memory_Read \ +outputC(Stilde_flux_to_print,Stilde_flux_names,"returnstring",params=outCparams).replace("IDX4","IDX4S")\ +Memory_Write.replace(invdx[0],invdx[flux_dirn]), loopopts ="InteriorPoints", rel_path_for_Cparams=os.path.join("../")) desc = "Compute the flux of all 3 components of tilde{S}_i on the left face in the " + str(flux_dirn) + "." name = "calculate_Stilde_flux_D" + str(flux_dirn) + "_left" outCfunction( outfile = os.path.join(out_dir,name+".h"), desc=desc, name=name, params ="const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs", body = Memory_Read.replace(indices[flux_dirn],indicesp1[flux_dirn]) \ +outputC(Stilde_flux_to_print,Stilde_flux_names,"returnstring",params=outCparams).replace("IDX4","IDX4S")\ +Memory_Write.replace(invdx[0],invdx[flux_dirn]).replace(assignment,assignmentp1), loopopts ="InteriorPoints", rel_path_for_Cparams=os.path.join("../"))
def calculate_Stilde_flux(flux_dirn, inputs_provided=False, alpha_face=None, gammadet_face=None, gamma_faceDD=None, gamma_faceUU=None, beta_faceU=None, Valenciav_rU=None, B_rU=None, Valenciav_lU=None, B_lU=None): if not inputs_provided: # We will pass values of the gridfunction on the cell faces into the function. This requires us # to declare them as C parameters in NRPy+. We will denote this with the _face infix/suffix. alpha_face, gammadet_face = gri.register_gridfunctions( "AUXEVOL", ["alpha_face", "gammadet_face"]) gamma_faceDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "gamma_faceDD", "sym01") gamma_faceUU = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "gamma_faceUU", "sym01") beta_faceU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "beta_faceU") # We'll need some more gridfunctions, now, to represent the reconstructions of BU and ValenciavU # on the right and left faces Valenciav_rU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_rU", DIM=3) B_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_rU", DIM=3) Valenciav_lU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_lU", DIM=3) B_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_lU", DIM=3) # We'll also compute some powers of the conformal factor psi: # Since psi^6 = sqrt(gammadet) by definition, # psi = sp.sqrt(gammadet_face)**(1.0/6.0) # psim4 = psi**(-4.0) global Stilde_fluxD Stilde_fluxD = ixp.zerorank3() for mom_comp in range(DIM): Stilde_fluxD[mom_comp] = HLLE_solver(flux_dirn, mom_comp, alpha_face, gammadet_face, gamma_faceDD, gamma_faceUU, beta_faceU, Valenciav_rU, B_rU, Valenciav_lU, B_lU)
def declare_scalar_field_gridfunctions_if_not_declared_already(): # Step 2: Register all needed BSSN gridfunctions. global sf, sfM # Step 2.a: First 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 "sf" in gri.glb_gridfcs_list[i].name: sf, sfM = sp.symbols('sf sfM', real=True) return sf, sfM # Step 2.b: Register indexed quantities, using ixp.register_... functions sf, sfM = gri.register_gridfunctions("EVOL", ["sf", "sfM"]) return sf, sfM
def define_BSSN_T4UUmunu_rescaled_source_terms(): # Step 0: First 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 "sDD00" in gri.glb_gridfcs_list[i].name: return # Step 1: Declare as globals all quantities declared in this function. global rho, S, sD, sDD # Step 2: Register all needed *evolved* gridfunctions. # Step 2a: Register indexed quantities, using ixp.register_... functions sDD = ixp.register_gridfunctions_for_single_rank2("AUX", "sDD", "sym01") sD = ixp.register_gridfunctions_for_single_rank1("AUX", "sD") # Step 2b: Register scalar quantities, using gri.register_gridfunctions() rho, S = gri.register_gridfunctions("AUX", ["rho", "S"])
def calculate_Stilde_flux(flux_dirn,inputs_provided=True,alpha_face=None,gamma_faceDD=None,beta_faceU=None,\ Valenciav_rU=None,B_rU=None,Valenciav_lU=None,B_lU=None,sqrt4pi=None): if not inputs_provided: # We will pass values of the gridfunction on the cell faces into the function. This requires us # to declare them as C parameters in NRPy+. We will denote this with the _face infix/suffix. alpha_face = gri.register_gridfunctions("AUXEVOL", "alpha_face") gamma_faceDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "gamma_faceDD", "sym01") beta_faceU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "beta_faceU") # We'll need some more gridfunctions, now, to represent the reconstructions of BU and ValenciavU # on the right and left faces Valenciav_rU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_rU", DIM=3) B_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_rU", DIM=3) Valenciav_lU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_lU", DIM=3) B_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_lU", DIM=3) sqrt4pi = par.Cparameters("REAL", thismodule, "sqrt4pi", "sqrt(4.0*M_PI)") find_cmax_cmin(flux_dirn, gamma_faceDD, beta_faceU, alpha_face) global Stilde_fluxD Stilde_fluxD = ixp.zerorank3() for mom_comp in range(3): calculate_GRFFE_Tmunu_and_contractions(flux_dirn, mom_comp, gamma_faceDD,beta_faceU,alpha_face,\ Valenciav_rU,B_rU,sqrt4pi) Fr = F Ur = U calculate_GRFFE_Tmunu_and_contractions(flux_dirn, mom_comp, gamma_faceDD,beta_faceU,alpha_face,\ Valenciav_lU,B_lU,sqrt4pi) Fl = F Ul = U Stilde_fluxD[mom_comp] = HLLE_solver(cmax, cmin, Fr, Fl, Ur, Ul)
def 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 WeylScalars_Cartesian(): # Step 3.a: Set spatial dimension (must be 3 for BSSN) DIM = 3 par.set_parval_from_str("grid::DIM", DIM) # Step 3.b: declare the additional gridfunctions (i.e., functions whose values are declared # at every grid point, either inside or outside of our SymPy expressions) needed # for this thorn: # * the physical metric $\gamma_{ij}$, # * the extrinsic curvature $K_{ij}$, # * the real and imaginary components of $\psi_4$, and # * the Weyl curvature invariants: gammaDD = ixp.register_gridfunctions_for_single_rank2( "AUX", "gammaDD", "sym01") # The AUX or EVOL designation is *not* # used in diagnostic modules. kDD = ixp.register_gridfunctions_for_single_rank2("AUX", "kDD", "sym01") x, y, z = gri.register_gridfunctions("AUX", ["x", "y", "z"]) global psi4r, psi4i, psi3r, psi3i, psi2r, psi2i, psi1r, psi1i, psi0r, psi0i psi4r, psi4i, psi3r, psi3i, psi2r, psi2i, psi1r, psi1i, psi0r, psi0i = gri.register_gridfunctions( "AUX", [ "psi4r", "psi4i", "psi3r", "psi3i", "psi2r", "psi2i", "psi1r", "psi1i", "psi0r", "psi0i" ]) # Step 4: Set which tetrad is used; at the moment, only one supported option # The tetrad depends in general on the inverse 3-metric gammaUU[i][j]=\gamma^{ij} # and the determinant of the 3-metric (detgamma), which are defined in # the following line of code from gammaDD[i][j]=\gamma_{ij}. tmpgammaUU, detgamma = ixp.symm_matrix_inverter3x3(gammaDD) detgamma = sp.simplify(detgamma) gammaUU = ixp.zerorank2() for i in range(3): for j in range(3): gammaUU[i][j] = sp.simplify(tmpgammaUU[i][j]) if par.parval_from_str("WeylScal4NRPy.WeylScalars_Cartesian::TetradChoice" ) == "Approx_QuasiKinnersley": # Eqs 5.6 in https://arxiv.org/pdf/gr-qc/0104063.pdf xmoved = x # - xorig ymoved = y # - yorig zmoved = z # - zorig # Step 5.a: Choose 3 orthogonal vectors. Here, we choose one in the azimuthal # direction, one in the radial direction, and the cross product of the two. # Eqs 5.7 v1U = ixp.zerorank1() v2U = ixp.zerorank1() v3U = ixp.zerorank1() v1U[0] = -ymoved v1U[1] = xmoved # + offset v1U[2] = sp.sympify(0) v2U[0] = xmoved # + offset v2U[1] = ymoved v2U[2] = zmoved LeviCivitaSymbol_rank3 = ixp.LeviCivitaSymbol_dim3_rank3() for a in range(DIM): for b in range(DIM): for c in range(DIM): for d in range(DIM): v3U[a] += sp.sqrt( detgamma) * gammaUU[a][d] * LeviCivitaSymbol_rank3[ d][b][c] * v1U[b] * v2U[c] for a in range(DIM): v3U[a] = sp.simplify(v3U[a]) # Step 5.b: Gram-Schmidt orthonormalization of the vectors. # The w_i^a vectors here are used to temporarily hold values on the way to the final vectors e_i^a # e_1^a &= \frac{v_1^a}{\omega_{11}} # e_2^a &= \frac{v_2^a - \omega_{12} e_1^a}{\omega_{22}} # e_3^a &= \frac{v_3^a - \omega_{13} e_1^a - \omega_{23} e_2^a}{\omega_{33}}, # Normalize the first vector w1U = ixp.zerorank1() for a in range(DIM): w1U[a] = v1U[a] omega11 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega11 += w1U[a] * w1U[b] * gammaDD[a][b] e1U = ixp.zerorank1() for a in range(DIM): e1U[a] = w1U[a] / sp.sqrt(omega11) # Subtract off the portion of the first vector along the second, then normalize omega12 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega12 += e1U[a] * v2U[b] * gammaDD[a][b] w2U = ixp.zerorank1() for a in range(DIM): w2U[a] = v2U[a] - omega12 * e1U[a] omega22 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega22 += w2U[a] * w2U[b] * gammaDD[a][b] e2U = ixp.zerorank1() for a in range(DIM): e2U[a] = w2U[a] / sp.sqrt(omega22) # Subtract off the portion of the first and second vectors along the third, then normalize omega13 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega13 += e1U[a] * v3U[b] * gammaDD[a][b] omega23 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega23 += e2U[a] * v3U[b] * gammaDD[a][b] w3U = ixp.zerorank1() for a in range(DIM): w3U[a] = v3U[a] - omega13 * e1U[a] - omega23 * e2U[a] omega33 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega33 += w3U[a] * w3U[b] * gammaDD[a][b] e3U = ixp.zerorank1() for a in range(DIM): e3U[a] = w3U[a] / sp.sqrt(omega33) # Step 5.c: Construct the tetrad itself. # Eqs. 5.6: # l^a &= \frac{1}{\sqrt{2}} e_2^a \\ # n^a &= -\frac{1}{\sqrt{2}} e_2^a \\ # m^a &= \frac{1}{\sqrt{2}} (e_3^a + i e_1^a) \\ # \overset{*}{m}{}^a &= \frac{1}{\sqrt{2}} (e_3^a - i e_1^a) isqrt2 = 1 / sp.sqrt(2) ltetU = ixp.zerorank1() ntetU = ixp.zerorank1() # mtetU = ixp.zerorank1() # mtetccU = ixp.zerorank1() remtetU = ixp.zerorank1( ) # SymPy did not like trying to take the real/imaginary parts of such a immtetU = ixp.zerorank1( ) # complicated expression, so we do it ourselves. for i in range(DIM): ltetU[i] = isqrt2 * e2U[i] ntetU[i] = -isqrt2 * e2U[i] remtetU[i] = isqrt2 * e3U[i] immtetU[i] = isqrt2 * e1U[i] nn = isqrt2 else: print("Error: TetradChoice == " + par.parval_from_str("TetradChoice") + " unsupported!") sys.exit(1) # Step 5: Declare and construct the second derivative of the metric. gammaDD_dD = ixp.declarerank3("gammaDD_dD", "sym01") # Define the Christoffel symbols GammaUDD = ixp.zerorank3(DIM) for i in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): GammaUDD[i][k][l] += (sp.Rational(1, 2)) * gammaUU[i][m] * \ (gammaDD_dD[m][k][l] + gammaDD_dD[m][l][k] - gammaDD_dD[k][l][m]) # Step 6.a: Declare and construct the Riemann curvature tensor: # R_{abcd} = \frac{1}{2} (\gamma_{ad,cb}+\gamma_{bc,da}-\gamma_{ac,bd}-\gamma_{bd,ac}) # + \gamma_{je} \Gamma^{j}_{bc}\Gamma^{e}_{ad} - \gamma_{je} \Gamma^{j}_{bd} \Gamma^{e}_{ac} gammaDD_dDD = ixp.declarerank4("gammaDD_dDD", "sym01_sym23") RiemannDDDD = ixp.zerorank4() for a in range(DIM): for b in range(DIM): for c in range(DIM): for d in range(DIM): RiemannDDDD[a][b][c][d] = (gammaDD_dDD[a][d][c][b] + \ gammaDD_dDD[b][c][d][a] - \ gammaDD_dDD[a][c][b][d] - \ gammaDD_dDD[b][d][a][c]) / 2 for e in range(DIM): for j in range(DIM): RiemannDDDD[a][b][c][d] += gammaDD[j][e] * GammaUDD[j][b][c] * GammaUDD[e][a][d] - \ gammaDD[j][e] * GammaUDD[j][b][d] * GammaUDD[e][a][c] # Step 6.b: We also need the extrinsic curvature tensor $K_{ij}$. # In Cartesian coordinates, we already made the components gridfunctions. # We will, however, need to calculate the trace of K seperately: trK = sp.sympify(0) for i in range(DIM): for j in range(DIM): trK += gammaUU[i][j] * kDD[i][j] # Step 7: Build the formula for \psi_4. # Gauss equation: involving the Riemann tensor and extrinsic curvature. # GaussDDDD[i][j][k][l] =& R_{ijkl} + 2K_{i[k}K_{l]j} GaussDDDD = ixp.zerorank4() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): GaussDDDD[i][j][k][l] = RiemannDDDD[i][j][k][ l] + kDD[i][k] * kDD[l][j] - kDD[i][l] * kDD[k][j] # Codazzi equation: involving partial derivatives of the extrinsic curvature. # We will first need to declare derivatives of kDD # CodazziDDD[j][k][l] =& -2 (K_{j[k,l]} + \Gamma^p_{j[k} K_{l]p}) kDD_dD = ixp.declarerank3("kDD_dD", "sym01") CodazziDDD = ixp.zerorank3() for j in range(DIM): for k in range(DIM): for l in range(DIM): CodazziDDD[j][k][l] = kDD_dD[j][l][k] - kDD_dD[j][k][l] for p in range(DIM): CodazziDDD[j][k][l] += GammaUDD[p][j][l] * kDD[k][ p] - GammaUDD[p][j][k] * kDD[l][p] # Another piece. While not associated with any particular equation, # this is still useful for organizational purposes. # RojoDD[j][l]} = & R_{jl} - K_{jp} K^p_l + KK_{jl} \\ # = & \gamma^{pd} R_{jpld} - K_{jp} K^p_l + KK_{jl} RojoDD = ixp.zerorank2() for j in range(DIM): for l in range(DIM): RojoDD[j][l] = trK * kDD[j][l] for p in range(DIM): for d in range(DIM): RojoDD[j][l] += gammaUU[p][d] * RiemannDDDD[j][p][l][ d] - kDD[j][p] * gammaUU[p][d] * kDD[d][l] # Now we can calculate $\psi_4$ itself! We assume l^0 = n^0 = \frac{1}{\sqrt{2}} # and m^0 = \overset{*}{m}{}^0 = 0 to simplify these equations. # We calculate the Weyl scalars as defined in https://arxiv.org/abs/gr-qc/0104063 # In terms of the above-defined quantites, the psis are defined as: # \psi_4 =&\ (\text{GaussDDDD[i][j][k][l]}) n^i \overset{*}{m}{}^j n^k \overset{*}{m}{}^l \\ # &+2 (\text{CodazziDDD[j][k][l]}) n^{0} \overset{*}{m}{}^{j} n^k \overset{*}{m}{}^l \\ # &+ (\text{RojoDD[j][l]}) n^{0} \overset{*}{m}{}^{j} n^{0} \overset{*}{m}{}^{l}. # \psi_3 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i n^j \overset{*}{m}{}^k n^l \\ # &+ (\text{CodazziDDD[j][k][l]}) (l^{0} n^{j} \overset{*}{m}{}^k n^l - l^{j} n^{0} \overset{*}{m}{}^k n^l - l^k n^j\overset{*}{m}{}^l n^0) \\ # &- (\text{RojoDD[j][l]}) l^{0} n^{j} \overset{*}{m}{}^l n^0 - l^{j} n^{0} \overset{*}{m}{}^l n^0 \\ # \psi_2 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i m^j \overset{*}{m}{}^k n^l \\ # &+ (\text{CodazziDDD[j][k][l]}) (l^{0} m^{j} \overset{*}{m}{}^k n^l - l^{j} m^{0} \overset{*}{m}{}^k n^l - l^k m^l \overset{*}{m}{}^l n^0) \\ # &- (\text{RojoDD[j][l]}) l^0 m^j \overset{*}{m}{}^l n^0 \\ # \psi_1 =&\ (\text{GaussDDDD[i][j][k][l]}) n^i l^j m^k l^l \\ # &+ (\text{CodazziDDD[j][k][l]}) (n^{0} l^{j} m^k l^l - n^{j} l^{0} m^k l^l - n^k l^l m^j l^0) \\ # &- (\text{RojoDD[j][l]}) (n^{0} l^{j} m^l l^0 - n^{j} l^{0} m^l l^0) \\ # \psi_0 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i m^j l^k m^l \\ # &+2 (\text{CodazziDDD[j][k][l]}) (l^0 m^j l^k m^l + l^k m^l l^0 m^j) \\ # &+ (\text{RojoDD[j][l]}) l^0 m^j l^0 m^j. \\ psi4r = sp.sympify(0) psi4i = sp.sympify(0) psi3r = sp.sympify(0) psi3i = sp.sympify(0) psi2r = sp.sympify(0) psi2i = sp.sympify(0) psi1r = sp.sympify(0) psi1i = sp.sympify(0) psi0r = sp.sympify(0) psi0i = sp.sympify(0) for l in range(DIM): for j in range(DIM): psi4r += RojoDD[j][l] * nn * nn * (remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi4i += RojoDD[j][l] * nn * nn * (-remtetU[j] * immtetU[l] - immtetU[j] * remtetU[l]) psi3r += -RojoDD[j][l] * nn * nn * (ntetU[j] - ltetU[j]) * remtetU[l] psi3i += RojoDD[j][l] * nn * nn * (ntetU[j] - ltetU[j]) * immtetU[l] psi2r += -RojoDD[j][l] * nn * nn * (remtetU[l] * remtetU[j] + immtetU[j] * immtetU[l]) psi2i += -RojoDD[j][l] * nn * nn * (immtetU[l] * remtetU[j] - remtetU[j] * immtetU[l]) psi1r += RojoDD[j][l] * nn * nn * (ntetU[j] * remtetU[l] - ltetU[j] * remtetU[l]) psi1i += RojoDD[j][l] * nn * nn * (ntetU[j] * immtetU[l] - ltetU[j] * immtetU[l]) psi0r += RojoDD[j][l] * nn * nn * (remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi0i += RojoDD[j][l] * nn * nn * (remtetU[j] * immtetU[l] + immtetU[j] * remtetU[l]) for l in range(DIM): for j in range(DIM): for k in range(DIM): psi4r += 2 * CodazziDDD[j][k][l] * ntetU[k] * nn * ( remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi4i += 2 * CodazziDDD[j][k][l] * ntetU[k] * nn * ( -remtetU[j] * immtetU[l] - immtetU[j] * remtetU[l]) psi3r += 1 * CodazziDDD[j][k][l] * nn * ( (ntetU[j] - ltetU[j]) * remtetU[k] * ntetU[l] - remtetU[j] * ltetU[k] * ntetU[l]) psi3i += -1 * CodazziDDD[j][k][l] * nn * ( (ntetU[j] - ltetU[j]) * immtetU[k] * ntetU[l] - immtetU[j] * ltetU[k] * ntetU[l]) psi2r += 1 * CodazziDDD[j][k][l] * nn * ( ntetU[l] * (remtetU[j] * remtetU[k] + immtetU[j] * immtetU[k]) - ltetU[k] * (remtetU[j] * remtetU[l] + immtetU[j] * immtetU[l])) psi2i += 1 * CodazziDDD[j][k][l] * nn * ( ntetU[l] * (immtetU[j] * remtetU[k] - remtetU[j] * immtetU[k]) - ltetU[k] * (remtetU[j] * immtetU[l] - immtetU[j] * remtetU[l])) psi1r += 1 * CodazziDDD[j][k][l] * nn * ( ltetU[j] * remtetU[k] * ltetU[l] - remtetU[j] * ntetU[k] * ltetU[l] - ntetU[j] * remtetU[k] * ltetU[l]) psi1i += 1 * CodazziDDD[j][k][l] * nn * ( ltetU[j] * immtetU[k] * ltetU[l] - immtetU[j] * ntetU[k] * ltetU[l] - ntetU[j] * immtetU[k] * ltetU[l]) psi0r += 2 * CodazziDDD[j][k][l] * nn * ltetU[k] * ( remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi0i += 2 * CodazziDDD[j][k][l] * nn * ltetU[k] * ( remtetU[j] * immtetU[l] + immtetU[j] * remtetU[l]) for l in range(DIM): for j in range(DIM): for k in range(DIM): for i in range(DIM): psi4r += GaussDDDD[i][j][k][l] * ntetU[i] * ntetU[k] * ( remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi4i += GaussDDDD[i][j][k][l] * ntetU[i] * ntetU[k] * ( -remtetU[j] * immtetU[l] - immtetU[j] * remtetU[l]) psi3r += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[ j] * remtetU[k] * ntetU[l] psi3i += -GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[ j] * immtetU[k] * ntetU[l] psi2r += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[l] * ( remtetU[j] * remtetU[k] + immtetU[j] * immtetU[k]) psi2i += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[l] * ( immtetU[j] * remtetU[k] - remtetU[j] * immtetU[k]) psi1r += GaussDDDD[i][j][k][l] * ntetU[i] * ltetU[ j] * remtetU[k] * ltetU[l] psi1i += GaussDDDD[i][j][k][l] * ntetU[i] * ltetU[ j] * immtetU[k] * ltetU[l] psi0r += GaussDDDD[i][j][k][l] * ltetU[i] * ltetU[k] * ( remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi0i += GaussDDDD[i][j][k][l] * ltetU[i] * ltetU[k] * ( remtetU[j] * immtetU[l] + immtetU[j] * remtetU[l])
def GiRaFFEfood_HO_Exact_Wald(): # <a id='step2'></a> # # ### Step 2: Set the vectors A and E in Spherical coordinates # $$\label{step2}$$ # # \[Back to [top](#top)\] # # We will first build the fundamental vectors $A_i$ and $E_i$ in spherical coordinates (see [Table 3](https://arxiv.org/pdf/1704.00599.pdf)). Note that we use reference_metric.py to set $r$ and $\theta$ in terms of Cartesian coordinates; this will save us a step later when we convert to Cartesian coordinates. Since $C_0 = 1$, # \begin{align} # A_{\phi} &= \frac{1}{2} r^2 \sin^2 \theta \\ # E_{\phi} &= 2 M \left( 1+ \frac {2M}{r} \right)^{-1/2} \sin^2 \theta. \\ # \end{align} # While we have $E_i$ set as a variable in NRPy+, note that the final C code won't store these values. # Step 2: Set the vectors A and E in Spherical coordinates r = rfm.xxSph[ 0] + KerrSchild_radial_shift # We are setting the data up in Shifted Kerr-Schild coordinates theta = rfm.xxSph[1] IDchoice = par.parval_from_str("IDchoice") # Initialize all components of A and E in the *spherical basis* to zero ASphD = ixp.zerorank1() ESphD = ixp.zerorank1() if IDchoice is "Exact_Wald": ASphD[2] = (r * r * sp.sin(theta)**2) / 2 ESphD[2] = 2 * M * sp.sin(theta)**2 / sp.sqrt(1 + 2 * M / r) else: print("Error: IDchoice == " + par.parval_from_str("IDchoice") + " unsupported!") exit(1) # <a id='step3'></a> # # ### Step 3: Use the Jacobian matrix to transform the vectors to Cartesian coordinates. # $$\label{step3}$$ # # \[Back to [top](#top)\] # # Now, we will use the coordinate transformation definitions provided by reference_metric.py to build the Jacobian # $$ # \frac{\partial x_{\rm Sph}^j}{\partial x_{\rm Cart}^i}, # $$ # where $x_{\rm Sph}^j \in \{r,\theta,\phi\}$ and $x_{\rm Cart}^i \in \{x,y,z\}$. We would normally compute its inverse, but since none of the quantities we need to transform have upper indices, it is not necessary. Then, since both $A_i$ and $E_i$ have one lower index, both will need to be multiplied by the Jacobian: # # $$ # A_i^{\rm Cart} = A_j^{\rm Sph} \frac{\partial x_{\rm Sph}^j}{\partial x_{\rm Cart}^i}, # $$ # Step 3: Use the Jacobian matrix to transform the vectors to Cartesian coordinates. drrefmetric__dx_0UDmatrix = sp.Matrix([[ sp.diff(rfm.xxSph[0], rfm.xx[0]), sp.diff(rfm.xxSph[0], rfm.xx[1]), sp.diff(rfm.xxSph[0], rfm.xx[2]) ], [ sp.diff(rfm.xxSph[1], rfm.xx[0]), sp.diff(rfm.xxSph[1], rfm.xx[1]), sp.diff(rfm.xxSph[1], rfm.xx[2]) ], [ sp.diff(rfm.xxSph[2], rfm.xx[0]), sp.diff(rfm.xxSph[2], rfm.xx[1]), sp.diff(rfm.xxSph[2], rfm.xx[2]) ]]) #dx__drrefmetric_0UDmatrix = drrefmetric__dx_0UDmatrix.inv() # We don't actually need this in this case. global AD AD = ixp.register_gridfunctions_for_single_rank1("EVOL", "AD") ED = ixp.zerorank1() for i in range(DIM): for j in range(DIM): AD[i] = drrefmetric__dx_0UDmatrix[(j, i)] * ASphD[j] ED[i] = drrefmetric__dx_0UDmatrix[(j, i)] * ESphD[j] #Step 4: Register the basic spacetime quantities alpha = gri.register_gridfunctions("AUX", "alpha") betaU = ixp.register_gridfunctions_for_single_rank1("AUX", "betaU", DIM=3) gammaDD = ixp.register_gridfunctions_for_single_rank2("AUX", "gammaDD", "sym01", DIM=3) gammaUU, gammadet = ixp.symm_matrix_inverter3x3(gammaDD) # <a id='step4'></a> # # ### Step 4: Calculate $v^i$ from $A_i$ and $E_i$ # $$\label{step4}$$ # # \[Back to [top](#top)\] # # We will now find the magnetic field using equation 18 in [the original paper](https://arxiv.org/pdf/1704.00599.pdf) $$B^i = \frac{[ijk]}{\sqrt{\gamma}} \partial_j A_k. $$ We will need the metric quantites: the lapse $\alpha$, the shift $\beta^i$, and the three-metric $\gamma_{ij}$. We will also need the Levi-Civita symbol, provided by $\text{WeylScal4NRPy}$. # Step 4: Calculate v^i from A_i and E_i # Step 4a: Calculate the magnetic field B^i # Here, we build the Levi-Civita tensor from the Levi-Civita symbol. # Here, we build the Levi-Civita tensor from the Levi-Civita symbol. import WeylScal4NRPy.WeylScalars_Cartesian as weyl LeviCivitaSymbolDDD = weyl.define_LeviCivitaSymbol_rank3() LeviCivitaTensorUUU = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): LeviCivitaTensorUUU[i][j][ k] = LeviCivitaSymbolDDD[i][j][k] / sp.sqrt(gammadet) # For the initial data, we can analytically take the derivatives of A_i ADdD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): ADdD[i][j] = sp.simplify(sp.diff(AD[i], rfm.xxCart[j])) #global BU BU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): BU[i] += LeviCivitaTensorUUU[i][j][k] * ADdD[k][j] # We will now build the initial velocity using equation 152 in [this paper,](https://arxiv.org/pdf/1310.3274v2.pdf) cited in the original $\texttt{GiRaFFE}$ code: $$ v^i = \alpha \frac{\epsilon^{ijk} E_j B_k}{B^2} -\beta^i. $$ # However, our code needs the Valencia 3-velocity while this expression is for the drift velocity. So, we will need to transform it to the Valencia 3-velocity using the rule $\bar{v}^i = \frac{1}{\alpha} \left(v^i +\beta^i \right)$. # Thus, $$\bar{v}^i = \frac{\epsilon^{ijk} E_j B_k}{B^2}$$ # Step 4b: Calculate B^2 and B_i # B^2 is an inner product defined in the usual way: B2 = sp.sympify(0) for i in range(DIM): for j in range(DIM): B2 += gammaDD[i][j] * BU[i] * BU[j] # Lower the index on B^i BD = ixp.zerorank1() for i in range(DIM): for j in range(DIM): BD[i] += gammaDD[i][j] * BU[j] # Step 4c: Calculate the Valencia 3-velocity global ValenciavU ValenciavU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): ValenciavU[ i] += LeviCivitaTensorUUU[i][j][k] * ED[j] * BD[k] / B2
def generate_C_code_for_Stilde_flux( out_dir, inputs_provided=False, alpha_face=None, gamma_faceDD=None, beta_faceU=None, Valenciav_rU=None, B_rU=None, Valenciav_lU=None, B_lU=None, sqrt4pi=None, outCparams="outCverbose=False,CSE_sorting=none", write_cmax_cmin=False): if not inputs_provided: # We will pass values of the gridfunction on the cell faces into the function. This requires us # to declare them as C parameters in NRPy+. We will denote this with the _face infix/suffix. alpha_face = gri.register_gridfunctions("AUXEVOL", "alpha_face") gamma_faceDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "gamma_faceDD", "sym01") beta_faceU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "beta_faceU") # We'll need some more gridfunctions, now, to represent the reconstructions of BU and ValenciavU # on the right and left faces Valenciav_rU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_rU", DIM=3) B_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_rU", DIM=3) Valenciav_lU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_lU", DIM=3) B_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_lU", DIM=3) sqrt4pi = par.Cparameters("REAL", thismodule, "sqrt4pi", "sqrt(4.0*M_PI)") Memory_Read = write_C_Code_to_read_memory(write_cmax_cmin) Memory_Write = write_C_code_to_write_results(write_cmax_cmin) if write_cmax_cmin: # In the staggered case, we will also want to output cmax and cmin # If we want to write cmax and cmin, we will need to be able to change auxevol_gfs: input_params_for_Stilde_flux = "const paramstruct *params,REAL *auxevol_gfs,REAL *rhs_gfs" else: input_params_for_Stilde_flux = "const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs" for flux_dirn in range(3): calculate_Stilde_flux(flux_dirn,alpha_face,gamma_faceDD,beta_faceU,\ Valenciav_rU,B_rU,Valenciav_lU,B_lU,sqrt4pi) Stilde_flux_to_print = [ Stilde_fluxD[0], Stilde_fluxD[1], Stilde_fluxD[2] ] Stilde_flux_names = ["Stilde_fluxD0", "Stilde_fluxD1", "Stilde_fluxD2"] if write_cmax_cmin: Stilde_flux_to_print = Stilde_flux_to_print + [ chsp.cmax, chsp.cmin ] Stilde_flux_names = Stilde_flux_names + ["cmax", "cmin"] desc = "Compute the flux term of all 3 components of tilde{S}_i on the right face in the " + str( flux_dirn) + "direction." name = "calculate_Stilde_flux_D" + str(flux_dirn) Ccode_function = outCfunction( outfile = "returnstring", desc=desc, name=name, params = input_params_for_Stilde_flux, body = Memory_Read \ +outputC(Stilde_flux_to_print,Stilde_flux_names,"returnstring",params=outCparams).replace("IDX4","IDX4S")\ +Memory_Write[flux_dirn], loopopts ="InteriorPoints", rel_path_for_Cparams=os.path.join("../")).replace("NGHOSTS+Nxx0","NGHOSTS+Nxx0+1").replace("NGHOSTS+Nxx1","NGHOSTS+Nxx1+1").replace("NGHOSTS+Nxx2","NGHOSTS+Nxx2+1") with open(os.path.join(out_dir, name + ".h"), "w") as file: file.write(Ccode_function)
def generate_C_code_for_Stilde_flux( out_dir, inputs_provided=False, alpha_face=None, gamma_faceDD=None, beta_faceU=None, Valenciav_rU=None, B_rU=None, Valenciav_lU=None, B_lU=None, sqrt4pi=None, outCparams="outCverbose=False,CSE_sorting=none", write_cmax_cmin=False): if not inputs_provided: # We will pass values of the gridfunction on the cell faces into the function. This requires us # to declare them as C parameters in NRPy+. We will denote this with the _face infix/suffix. alpha_face = gri.register_gridfunctions("AUXEVOL", "alpha_face") gamma_faceDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "gamma_faceDD", "sym01") beta_faceU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "beta_faceU") # We'll need some more gridfunctions, now, to represent the reconstructions of BU and ValenciavU # on the right and left faces Valenciav_rU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_rU", DIM=3) B_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_rU", DIM=3) Valenciav_lU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_lU", DIM=3) B_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_lU", DIM=3) sqrt4pi = par.Cparameters("REAL", thismodule, "sqrt4pi", "sqrt(4.0*M_PI)") # We'll also need to store the results of the HLLE step between functions. ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "Stilde_flux_HLLED") input_params_for_Stilde_flux = "const paramstruct *params,REAL *auxevol_gfs,REAL *rhs_gfs" if write_cmax_cmin: name_suffixes = ["_x", "_y", "_z"] for flux_dirn in range(3): calculate_Stilde_flux(flux_dirn,alpha_face,gamma_faceDD,beta_faceU,\ Valenciav_rU,B_rU,Valenciav_lU,B_lU,sqrt4pi) Stilde_flux_to_print = [ lhrh(lhs=gri.gfaccess("out_gfs", "Stilde_flux_HLLED0"), rhs=Stilde_fluxD[0]), lhrh(lhs=gri.gfaccess("out_gfs", "Stilde_flux_HLLED1"), rhs=Stilde_fluxD[1]), lhrh(lhs=gri.gfaccess("out_gfs", "Stilde_flux_HLLED2"), rhs=Stilde_fluxD[2]) ] if write_cmax_cmin: Stilde_flux_to_print = Stilde_flux_to_print \ +[ lhrh(lhs=gri.gfaccess("out_gfs","cmax"+name_suffixes[flux_dirn]),rhs=chsp.cmax), lhrh(lhs=gri.gfaccess("out_gfs","cmin"+name_suffixes[flux_dirn]),rhs=chsp.cmin) ] desc = "Compute the flux term of all 3 components of tilde{S}_i on the left face in the " + str( flux_dirn) + "direction for all components." name = "calculate_Stilde_flux_D" + str(flux_dirn) Ccode_function = outCfunction( outfile="returnstring", desc=desc, name=name, params=input_params_for_Stilde_flux, body=fin.FD_outputC("returnstring", Stilde_flux_to_print, params=outCparams), loopopts="InteriorPoints", rel_path_to_Cparams=os.path.join("../")).replace( "NGHOSTS+Nxx0", "NGHOSTS+Nxx0+1").replace( "NGHOSTS+Nxx1", "NGHOSTS+Nxx1+1").replace("NGHOSTS+Nxx2", "NGHOSTS+Nxx2+1") with open(os.path.join(out_dir, name + ".h"), "w") as file: file.write(Ccode_function) pre_body = """// Notice in the loop below that we go from 3 to cctk_lsh-3 for i, j, AND k, even though // we are only computing the flux in one direction. This is because in the end, // we only need the rhs's from 3 to cctk_lsh-3 for i, j, and k. const REAL invdxi[4] = {1e100,invdx0,invdx1,invdx2}; const REAL invdx = invdxi[flux_dirn];""" FD_body = """const int index = IDX3S(i0,i1,i2); const int indexp1 = IDX3S(i0+kronecker_delta[flux_dirn][0],i1+kronecker_delta[flux_dirn][1],i2+kronecker_delta[flux_dirn][2]); rhs_gfs[IDX4ptS(STILDED0GF,index)] += (auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED0GF,index)] - auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED0GF,indexp1)] ) * invdx; rhs_gfs[IDX4ptS(STILDED1GF,index)] += (auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED1GF,index)] - auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED1GF,indexp1)] ) * invdx; rhs_gfs[IDX4ptS(STILDED2GF,index)] += (auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED2GF,index)] - auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED2GF,indexp1)] ) * invdx;""" desc = "Compute the difference in the flux of StildeD on the opposite faces in flux_dirn for all components." name = "calculate_Stilde_rhsD" outCfunction( outfile=os.path.join(out_dir, name + ".h"), desc=desc, name=name, params= "const int flux_dirn,const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs", preloop=pre_body, body=FD_body, loopopts="InteriorPoints", rel_path_to_Cparams=os.path.join("../"))
def WeylScalarInvariants_Cartesian(): # Step 1: Declare Weyl scalar psi's as gridfunctions, as they will be read from memory. psi4r,psi4i,psi3r,psi3i,psi2r,psi2i,psi1r,psi1i,psi0r,psi0i = gri.register_gridfunctions("AUX",["psi4r","psi4i",\ "psi3r","psi3i",\ "psi2r","psi2i",\ "psi1r","psi1i",\ "psi0r","psi0i"]) # We will first set the complex psis as the appropriate combinations of their real and imaginary parts. # This will allow us to take advantage of SymPy's functions for handling complex numbers. psi4 = psi4r + sp.I * psi4i psi3 = psi3r + sp.I * psi3i psi2 = psi2r + sp.I * psi2i psi1 = psi1r + sp.I * psi1i psi0 = psi0r + sp.I * psi0i # We declare the variants as global to access them from other codes. We will also declare them as gridfunctions. global curvIr,curvIi,curvJr,curvJi,J1curv,J2curv,J3curv,J4curv curvIr, curvIi, curvJr, curvJi, J1curv, J2curv, J3curv, J4curv = gri.register_gridfunctions("AUX",["curvIr","curvIi",\ "curvJr","curvJi",\ "J1curv","J2curv",\ "J3curv","J4curv"]) # The equations for the real and complex parts of I and J, from arXiv:gr-qc/0407013, equations (2.2a) and (2.2b): # I &= 3 \psi_2^2 - 4 \psi_1 \psi_3 + \psi_4 \psi_0 \\ # J &= det |\psi_4 & \psi_3 & \psi_2| # |\psi_3 & \psi_2 & \psi_1| # |\psi_2 & \psi_1 & \psi_0| curvIr = sp.re(3*psi2*psi2 - 4*psi1*psi3 + psi4*psi0) curvIi = sp.im(3*psi2*psi2 - 4*psi1*psi3 + psi4*psi0) curvJr = sp.re(psi4 * (psi2*psi0 - psi1*psi1) - \ psi3 * (psi3*psi0 - psi1*psi2) +\ psi2 * (psi3*psi1 - psi2*psi2) ) curvJi = sp.im(psi4 * (psi2*psi0 - psi1*psi1) - \ psi3 * (psi3*psi0 - psi1*psi2) +\ psi2 * (psi3*psi1 - psi2*psi2) ) # Next, the real scalars J_1, J_2, J_3, and J_4 from equations B5-B8 of arXiv:0704.1756. # These equations are based directly on those used in the Mathematica notebook that generates WeylScal4 # (available at https://bitbucket.org/einsteintoolkit/einsteinanalysis/src), modified so that Python can # interpret them. Those equations were generated in turn using xTensor from equations B5-B8. J1curv =-16*(3*psi2i**2-3*psi2r**2-4*psi1i*psi3i+4*psi1r*psi3r+psi0i*psi4i-psi0r*psi4r) J2curv = 96*(-3*psi2i**2*psi2r+psi2r**3+2*psi1r*psi2i*psi3i+2*psi1i*psi2r*psi3i-psi0r*psi3i**2+\ 2*psi1i*psi2i*psi3r-2*psi1r*psi2r*psi3r-2*psi0i*psi3i*psi3r+psi0r*psi3r**2-\ 2*psi1i*psi1r*psi4i+psi0r*psi2i*psi4i+psi0i*psi2r*psi4i-psi1i**2*psi4r+psi1r**2*psi4r+\ psi0i*psi2i*psi4r-psi0r*psi2r*psi4r) J3curv = 64*(9*psi2i**4-54*psi2i**2*psi2r**2+9*psi2r**4-24*psi1i*psi2i**2*psi3i+48*psi1r*psi2i*psi2r*psi3i+\ 24*psi1i*psi2r**2*psi3i+16*psi1i**2*psi3i**2-16*psi1r**2*psi3i**2+\ 24*psi1r*psi2i**2*psi3r+48*psi1i*psi2i*psi2r*psi3r-24*psi1r*psi2r**2*psi3r-64*psi1i*psi1r*psi3i*psi3r-\ 16*psi1i**2*psi3r**2+16*psi1r**2*psi3r**2+6*psi0i*psi2i**2*psi4i-12*psi0r*psi2i*psi2r*psi4i-\ 6*psi0i*psi2r**2*psi4i-8*psi0i*psi1i*psi3i*psi4i+8*psi0r*psi1r*psi3i*psi4i+8*psi0r*psi1i*psi3r*psi4i+\ 8*psi0i*psi1r*psi3r*psi4i+psi0i**2*psi4i**2-psi0r**2*psi4i**2-6*psi0r*psi2i**2*psi4r-\ 12*psi0i*psi2i*psi2r*psi4r+6*psi0r*psi2r**2*psi4r+8*psi0r*psi1i*psi3i*psi4r+8*psi0i*psi1r*psi3i*psi4r+\ 8*psi0i*psi1i*psi3r*psi4r-8*psi0r*psi1r*psi3r*psi4r-4*psi0i*psi0r*psi4i*psi4r-psi0i**2*psi4r**2+\ psi0r**2*psi4r**2) J4curv = -640*(-15*psi2i**4*psi2r+30*psi2i**2*psi2r**3-3*psi2r**5+10*psi1r*psi2i**3*psi3i+\ 30*psi1i*psi2i**2*psi2r*psi3i-30*psi1r*psi2i*psi2r**2*psi3i-10*psi1i*psi2r**3*psi3i-\ 16*psi1i*psi1r*psi2i*psi3i**2-3*psi0r*psi2i**2*psi3i**2-8*psi1i**2*psi2r*psi3i**2+\ 8*psi1r**2*psi2r*psi3i**2-6*psi0i*psi2i*psi2r*psi3i**2+3*psi0r*psi2r**2*psi3i**2+\ 4*psi0r*psi1i*psi3i**3+4*psi0i*psi1r*psi3i**3+10*psi1i*psi2i**3*psi3r-\ 30*psi1r*psi2i**2*psi2r*psi3r-30*psi1i*psi2i*psi2r**2*psi3r+10*psi1r*psi2r**3*psi3r-\ 16*psi1i**2*psi2i*psi3i*psi3r+16*psi1r**2*psi2i*psi3i*psi3r-6*psi0i*psi2i**2*psi3i*psi3r+\ 32*psi1i*psi1r*psi2r*psi3i*psi3r+12*psi0r*psi2i*psi2r*psi3i*psi3r+6*psi0i*psi2r**2*psi3i*psi3r+\ 12*psi0i*psi1i*psi3i**2*psi3r-12*psi0r*psi1r*psi3i**2*psi3r+16*psi1i*psi1r*psi2i*psi3r**2+\ 3*psi0r*psi2i**2*psi3r**2+8*psi1i**2*psi2r*psi3r**2-8*psi1r**2*psi2r*psi3r**2+\ 6*psi0i*psi2i*psi2r*psi3r**2-3*psi0r*psi2r**2*psi3r**2-12*psi0r*psi1i*psi3i*psi3r**2-\ 12*psi0i*psi1r*psi3i*psi3r**2-4*psi0i*psi1i*psi3r**3+4*psi0r*psi1r*psi3r**3-\ 6*psi1i*psi1r*psi2i**2*psi4i+2*psi0r*psi2i**3*psi4i-6*psi1i**2*psi2i*psi2r*psi4i+\ 6*psi1r**2*psi2i*psi2r*psi4i+6*psi0i*psi2i**2*psi2r*psi4i+6*psi1i*psi1r*psi2r**2*psi4i-\ 6*psi0r*psi2i*psi2r**2*psi4i-2*psi0i*psi2r**3*psi4i+12*psi1i**2*psi1r*psi3i*psi4i-\ 4*psi1r**3*psi3i*psi4i-2*psi0r*psi1i*psi2i*psi3i*psi4i-2*psi0i*psi1r*psi2i*psi3i*psi4i-\ 2*psi0i*psi1i*psi2r*psi3i*psi4i+2*psi0r*psi1r*psi2r*psi3i*psi4i-2*psi0i*psi0r*psi3i**2*psi4i+\ 4*psi1i**3*psi3r*psi4i-12*psi1i*psi1r**2*psi3r*psi4i-2*psi0i*psi1i*psi2i*psi3r*psi4i+\ 2*psi0r*psi1r*psi2i*psi3r*psi4i+2*psi0r*psi1i*psi2r*psi3r*psi4i+2*psi0i*psi1r*psi2r*psi3r*psi4i-\ 2*psi0i**2*psi3i*psi3r*psi4i+2*psi0r**2*psi3i*psi3r*psi4i+2*psi0i*psi0r*psi3r**2*psi4i-\ psi0r*psi1i**2*psi4i**2-2*psi0i*psi1i*psi1r*psi4i**2+psi0r*psi1r**2*psi4i**2+\ 2*psi0i*psi0r*psi2i*psi4i**2+psi0i**2*psi2r*psi4i**2-psi0r**2*psi2r*psi4i**2-3*psi1i**2*psi2i**2*psi4r+\ 3*psi1r**2*psi2i**2*psi4r+2*psi0i*psi2i**3*psi4r+12*psi1i*psi1r*psi2i*psi2r*psi4r-\ 6*psi0r*psi2i**2*psi2r*psi4r+3*psi1i**2*psi2r**2*psi4r-3*psi1r**2*psi2r**2*psi4r-\ 6*psi0i*psi2i*psi2r**2*psi4r+2*psi0r*psi2r**3*psi4r+4*psi1i**3*psi3i*psi4r-12*psi1i*psi1r**2*psi3i*psi4r-\ 2*psi0i*psi1i*psi2i*psi3i*psi4r+2*psi0r*psi1r*psi2i*psi3i*psi4r+2*psi0r*psi1i*psi2r*psi3i*psi4r+\ 2*psi0i*psi1r*psi2r*psi3i*psi4r-psi0i**2*psi3i**2*psi4r+psi0r**2*psi3i**2*psi4r-\ 12*psi1i**2*psi1r*psi3r*psi4r+4*psi1r**3*psi3r*psi4r+2*psi0r*psi1i*psi2i*psi3r*psi4r+\ 2*psi0i*psi1r*psi2i*psi3r*psi4r+2*psi0i*psi1i*psi2r*psi3r*psi4r-2*psi0r*psi1r*psi2r*psi3r*psi4r+\ 4*psi0i*psi0r*psi3i*psi3r*psi4r+psi0i**2*psi3r**2*psi4r-psi0r**2*psi3r**2*psi4r-\ 2*psi0i*psi1i**2*psi4i*psi4r+4*psi0r*psi1i*psi1r*psi4i*psi4r+2*psi0i*psi1r**2*psi4i*psi4r+\ 2*psi0i**2*psi2i*psi4i*psi4r-2*psi0r**2*psi2i*psi4i*psi4r-4*psi0i*psi0r*psi2r*psi4i*psi4r+\ psi0r*psi1i**2*psi4r**2+2*psi0i*psi1i*psi1r*psi4r**2-psi0r*psi1r**2*psi4r**2- 2*psi0i*psi0r*psi2i*psi4r**2-psi0i**2*psi2r*psi4r**2+psi0r**2*psi2r*psi4r**2)
def FishboneMoncriefID(): par.set_parval_from_str("reference_metric::CoordSystem","Cartesian") rfm.reference_metric() #Set the spatial dimension parameter to 3. par.set_parval_from_str("grid::DIM", 3) DIM = par.parval_from_str("grid::DIM") gPhys4UU = ixp.register_gridfunctions_for_single_rank2("AUX","gPhys4UU", "sym01", DIM=4) KDD = ixp.register_gridfunctions_for_single_rank2("EVOL","KDD", "sym01") # Variables needed for initial data given in spherical basis r, th, ph = gri.register_gridfunctions("AUX",["r","th","ph"]) r_in,r_at_max_density,a,M = par.Cparameters("REAL",thismodule,["r_in","r_at_max_density","a","M"]) kappa,gamma = par.Cparameters("REAL",thismodule,["kappa","gamma"]) LorentzFactor = gri.register_gridfunctions("AUX","LorentzFactor") def calculate_l_at_r(r): l = sp.sqrt(M/r**3) * (r**4 + r**2*a**2 - 2*M*r*a**2 - a*sp.sqrt(M*r)*(r**2-a**2)) l /= r**2 - 3*M*r + 2*a*sp.sqrt(M*r) return l # First compute angular momentum at r_at_max_density, TAKING POSITIVE ROOT. This way disk is co-rotating with black hole # Eq 3.8: l = calculate_l_at_r(r_at_max_density) # Eq 3.6: # First compute the radially-independent part of the log of the enthalpy, ln_h_const Delta = r**2 - 2*M*r + a**2 Sigma = r**2 + a**2*sp.cos(th)**2 A = (r**2 + a**2)**2 - Delta*a**2*sp.sin(th)**2 # Next compute the radially-dependent part of log(enthalpy), ln_h tmp3 = sp.sqrt(1 + 4*l**2*Sigma**2*Delta/(A*sp.sin(th))**2) # Term 1 of Eq 3.6 ln_h = sp.Rational(1,2)*sp.log( ( 1 + tmp3) / (Sigma*Delta/A)) # Term 2 of Eq 3.6 ln_h -= sp.Rational(1,2)*tmp3 # Term 3 of Eq 3.6 ln_h -= 2*a*M*r*l/A # Next compute the radially-INdependent part of log(enthalpy), ln_h # Note that there is some typo in the expression for these terms given in Eq 3.6, so we opt to just evaluate # negative of the first three terms at r=r_in and th=pi/2 (the integration constant), as described in # the text below Eq. 3.6, basically just copying the above lines of code. # Delin = Delta_in ; Sigin = Sigma_in ; Ain = A_in . Delin = r_in**2 - 2*M*r_in + a**2 Sigin = r_in**2 + a**2*sp.cos(sp.pi/2)**2 Ain = (r_in**2 + a**2)**2 - Delin*a**2*sp.sin(sp.pi/2)**2 tmp3in = sp.sqrt(1 + 4*l**2*Sigin**2*Delin/(Ain*sp.sin(sp.pi/2))**2) # Term 4 of Eq 3.6 mln_h_in = -sp.Rational(1,2)*sp.log( ( 1 + tmp3in) / (Sigin*Delin/Ain)) # Term 5 of Eq 3.6 mln_h_in += sp.Rational(1,2)*tmp3in # Term 6 of Eq 3.6 mln_h_in += 2*a*M*r_in*l/Ain global hm1 hm1 = sp.exp(ln_h + mln_h_in) - 1 global rho_initial rho_initial,Pressure_initial = gri.register_gridfunctions("AUX",["rho_initial","Pressure_initial"]) # Python 3.4 + sympy 1.0.0 has a serious problem taking the power here, hangs forever. # so instead we use the identity x^{1/y} = exp( [1/y] * log(x) ) # Original expression (works with Python 2.7 + sympy 0.7.4.1): # rho_initial = ( hm1*(gamma-1)/(kappa*gamma) )**(1/(gamma - 1)) # New expression (workaround): rho_initial = sp.exp( (1/(gamma-1)) * sp.log( hm1*(gamma-1)/(kappa*gamma) )) Pressure_initial = kappa * rho_initial**gamma # Eq 3.3: First compute exp(-2 chi), assuming Boyer-Lindquist coordinates # Eq 2.16: chi = psi - nu, so # Eq 3.5 -> exp(-2 chi) = exp(-2 (psi - nu)) = exp(2 nu)/exp(2 psi) exp2nu = Sigma*Delta / A exp2psi = A*sp.sin(th)**2 / Sigma expm2chi = exp2nu / exp2psi # Eq 3.3: Next compute u_(phi). u_pphip = sp.sqrt((-1 + sp.sqrt(1 + 4*l**2*expm2chi))/2) # Eq 2.13: Compute u_(t) u_ptp = -sp.sqrt(1 + u_pphip**2) # Next compute spatial components of 4-velocity in Boyer-Lindquist coordinates: uBL4D = ixp.zerorank1(DIM=4) # Components 1 and 2: u_r = u_theta = 0 # Eq 2.12 (typo): u_(phi) = e^(-psi) u_phi -> u_phi = e^(psi) u_(phi) uBL4D[3] = sp.sqrt(exp2psi)*u_pphip # Assumes Boyer-Lindquist coordinates: omega = 2*a*M*r/A # Eq 2.13: u_(t) = 1/sqrt(exp2nu) * ( u_t + omega*u_phi ) # --> u_t = u_(t) * sqrt(exp2nu) - omega*u_phi # --> u_t = u_ptp * sqrt(exp2nu) - omega*uBL4D[3] uBL4D[0] = u_ptp*sp.sqrt(exp2nu) - omega*uBL4D[3] # Eq. 3.5: # w = 2*a*M*r/A; # Eqs. 3.5 & 2.1: # gtt = -Sig*Del/A + w^2*Sin[th]^2*A/Sig; # gtp = w*Sin[th]^2*A/Sig; # gpp = Sin[th]^2*A/Sig; # FullSimplify[Inverse[{{gtt,gtp},{gtp,gpp}}]] gPhys4BLUU = ixp.zerorank2(DIM=4) gPhys4BLUU[0][0] = -A/(Delta*Sigma) # DO NOT NEED TO SET gPhys4BLUU[1][1] or gPhys4BLUU[2][2]! gPhys4BLUU[0][3] = gPhys4BLUU[3][0] = -2*a*M*r/(Delta*Sigma) gPhys4BLUU[3][3] = -4*a**2*M**2*r**2/(Delta*A*Sigma) + Sigma**2/(A*Sigma*sp.sin(th)**2) uBL4U = ixp.zerorank1(DIM=4) for i in range(4): for j in range(4): uBL4U[i] += gPhys4BLUU[i][j]*uBL4D[j] # https://github.com/atchekho/harmpi/blob/master/init.c # Next transform Boyer-Lindquist velocity to Kerr-Schild basis: transformBLtoKS = ixp.zerorank2(DIM=4) for i in range(4): transformBLtoKS[i][i] = 1 transformBLtoKS[0][1] = 2*r/(r**2 - 2*r + a*a) transformBLtoKS[3][1] = a/(r**2 - 2*r + a*a) uKS4U = ixp.zerorank1(DIM=4) for i in range(4): for j in range(4): uKS4U[i] += transformBLtoKS[i][j]*uBL4U[j] # Adopt the Kerr-Schild metric for Fishbone-Moncrief disks # http://gravity.psu.edu/numrel/jclub/jc/Cook___LivRev_2000-5.pdf # Alternatively, Appendix of https://arxiv.org/pdf/1704.00599.pdf rhoKS2 = r**2 + a**2*sp.cos(th)**2 # Eq 79 of Cook's Living Review article DeltaKS = r**2 - 2*M*r + a**2 # Eq 79 of Cook's Living Review article alphaKS = 1/sp.sqrt(1 + 2*M*r/rhoKS2) betaKSU = ixp.zerorank1() betaKSU[0] = alphaKS**2*2*M*r/rhoKS2 gammaKSDD = ixp.zerorank2() gammaKSDD[0][0] = 1 + 2*M*r/rhoKS2 gammaKSDD[0][2] = gammaKSDD[2][0] = -(1 + 2*M*r/rhoKS2)*a*sp.sin(th)**2 gammaKSDD[1][1] = rhoKS2 gammaKSDD[2][2] = (r**2 + a**2 + 2*M*r/rhoKS2 * a**2*sp.sin(th)**2) * sp.sin(th)**2 AA = a**2 * sp.cos(2*th) + a**2 + 2*r**2 BB = AA + 4*M*r DD = sp.sqrt(2*M*r / (a**2 * sp.cos(th)**2 + r**2) + 1) KDD[0][0] = DD*(AA + 2*M*r)/(AA**2*BB) * (4*M*(a**2 * sp.cos(2*th) + a**2 - 2*r**2)) KDD[0][1] = KDD[1][0] = DD/(AA*BB) * 8*a**2*M*r*sp.sin(th)*sp.cos(th) KDD[0][2] = KDD[2][0] = DD/AA**2 * (-2*a*M*sp.sin(th)**2 * (a**2 * sp.cos(2*th) + a**2 - 2*r**2)) KDD[1][1] = DD/BB * 4*M*r**2 KDD[1][2] = KDD[2][1] = DD/(AA*BB) * (-8*a**3*M*r*sp.sin(th)**3*sp.cos(th)) KDD[2][2] = DD/(AA**2*BB) * \ (2*M*r*sp.sin(th)**2 * (a**4*(r-M)*sp.cos(4*th) + a**4*(M+3*r) + 4*a**2*r**2*(2*r-M) + 4*a**2*r*sp.cos(2*th)*(a**2 + r*(M+2*r)) + 8*r**5)) # For compatibility, we must compute gPhys4UU gammaKSUU,gammaKSDET = ixp.symm_matrix_inverter3x3(gammaKSDD) # See, e.g., Eq. 4.49 of https://arxiv.org/pdf/gr-qc/0703035.pdf , where N = alpha gPhys4UU[0][0] = -1 / alphaKS**2 for i in range(1,4): if i>0: # if the quantity does not have a "4", then it is assumed to be a 3D quantity. # E.g., betaKSU[] is a spatial vector, with indices ranging from 0 to 2: gPhys4UU[0][i] = gPhys4UU[i][0] = betaKSU[i-1]/alphaKS**2 for i in range(1,4): for j in range(1,4): # if the quantity does not have a "4", then it is assumed to be a 3D quantity. # E.g., betaKSU[] is a spatial vector, with indices ranging from 0 to 2, # and gammaKSUU[][] is a spatial tensor, with indices again ranging from 0 to 2. gPhys4UU[i][j] = gPhys4UU[j][i] = gammaKSUU[i-1][j-1] - betaKSU[i-1]*betaKSU[j-1]/alphaKS**2 A_b = par.Cparameters("REAL",thismodule,"A_b") A_3vecpotentialD = ixp.zerorank1() # Set A_phi = A_b*rho_initial FIXME: why is there a sign error? A_3vecpotentialD[2] = -A_b * rho_initial BtildeU = ixp.register_gridfunctions_for_single_rank1("EVOL","BtildeU") # Eq 15 of https://arxiv.org/pdf/1501.07276.pdf: # B = curl A -> B^r = d_th A_ph - d_ph A_th BtildeU[0] = sp.diff(A_3vecpotentialD[2],th) - sp.diff(A_3vecpotentialD[1],ph) # B = curl A -> B^th = d_ph A_r - d_r A_ph BtildeU[1] = sp.diff(A_3vecpotentialD[0],ph) - sp.diff(A_3vecpotentialD[2],r) # B = curl A -> B^ph = d_r A_th - d_th A_r BtildeU[2] = sp.diff(A_3vecpotentialD[1],r) - sp.diff(A_3vecpotentialD[0],th) # Construct spacetime metric in 3+1 form: # See, e.g., Eq. 4.49 of https://arxiv.org/pdf/gr-qc/0703035.pdf , where N = alpha alpha = gri.register_gridfunctions("EVOL",["alpha"]) betaU = ixp.register_gridfunctions_for_single_rank1("EVOL","betaU") alpha = sp.sqrt(1/(-gPhys4UU[0][0])) betaU = ixp.zerorank1() for i in range(3): betaU[i] = alpha**2 * gPhys4UU[0][i+1] gammaUU = ixp.zerorank2() for i in range(3): for j in range(3): gammaUU[i][j] = gPhys4UU[i+1][j+1] + betaU[i]*betaU[j]/alpha**2 gammaDD = ixp.register_gridfunctions_for_single_rank2("EVOL","gammaDD","sym01") gammaDD,igammaDET = ixp.symm_matrix_inverter3x3(gammaUU) gammaDET = 1/igammaDET ############### # Next compute g_{\alpha \beta} from lower 3-metric, using # Eq 4.47 of https://arxiv.org/pdf/gr-qc/0703035.pdf betaD = ixp.zerorank1() for i in range(3): for j in range(3): betaD[i] += gammaDD[i][j]*betaU[j] beta2 = sp.sympify(0) for i in range(3): beta2 += betaU[i]*betaD[i] gPhys4DD = ixp.zerorank2(DIM=4) gPhys4DD[0][0] = -alpha**2 + beta2 for i in range(3): gPhys4DD[0][i+1] = gPhys4DD[i+1][0] = betaD[i] for j in range(3): gPhys4DD[i+1][j+1] = gammaDD[i][j] ############### # Next compute b^{\mu} using Eqs 23 and 31 of https://arxiv.org/pdf/astro-ph/0503420.pdf uKS4D = ixp.zerorank1(DIM=4) for i in range(4): for j in range(4): uKS4D[i] += gPhys4DD[i][j] * uKS4U[j] # Eq 27 of https://arxiv.org/pdf/astro-ph/0503420.pdf BU = ixp.zerorank1() for i in range(3): BU[i] = BtildeU[i]/sp.sqrt(gammaDET) # Eq 23 of https://arxiv.org/pdf/astro-ph/0503420.pdf BU0_u = sp.sympify(0) for i in range(3): BU0_u += uKS4D[i+1]*BU[i]/alpha smallbU = ixp.zerorank1(DIM=4) smallbU[0] = BU0_u / sp.sqrt(4 * sp.pi) # Eqs 24 and 31 of https://arxiv.org/pdf/astro-ph/0503420.pdf for i in range(3): smallbU[i+1] = (BU[i]/alpha + BU0_u*uKS4U[i+1])/(sp.sqrt(4*sp.pi)*uKS4U[0]) smallbD = ixp.zerorank1(DIM=4) for i in range(4): for j in range(4): smallbD[i] += gPhys4DD[i][j]*smallbU[j] smallb2 = sp.sympify(0) for i in range(4): smallb2 += smallbU[i]*smallbD[i] ############### LorentzFactor = alpha * uKS4U[0] # Define Valencia 3-velocity v^i_(n), which sets the 3-velocity as measured by normal observers to the spatial slice: # v^i_(n) = u^i/(u^0*alpha) + beta^i/alpha. See eq 11 of https://arxiv.org/pdf/1501.07276.pdf Valencia3velocityU = ixp.zerorank1() for i in range(3): Valencia3velocityU[i] = uKS4U[i + 1] / (alpha * uKS4U[0]) + betaU[i] sqrtgamma4DET = sp.symbols("sqrtgamma4DET") sqrtgamma4DET = sp.sqrt(gammaDET)*alpha alpha = alpha.subs(r,rfm.xxSph[0]).subs(th,rfm.xxSph[1]).subs(ph,rfm.xxSph[2]) for i in range(DIM): betaU[i] = betaU[i].subs(r,rfm.xxSph[0]).subs(th,rfm.xxSph[1]).subs(ph,rfm.xxSph[2]) for j in range(DIM): gammaDD[i][j] = gammaDD[i][j].subs(r,rfm.xxSph[0]).subs(th,rfm.xxSph[1]).subs(ph,rfm.xxSph[2]) KDD[i][j] = KDD[i][j].subs(r,rfm.xxSph[0]).subs(th,rfm.xxSph[1]).subs(ph,rfm.xxSph[2]) # GRMHD variables: # Density and pressure: hm1 = hm1.subs(r,rfm.xxSph[0]).subs(th,rfm.xxSph[1]).subs(ph,rfm.xxSph[2]) rho_initial = rho_initial.subs(r,rfm.xxSph[0]).subs(th,rfm.xxSph[1]).subs(ph,rfm.xxSph[2]) Pressure_initial = Pressure_initial.subs(r,rfm.xxSph[0]).subs(th,rfm.xxSph[1]).subs(ph,rfm.xxSph[2]) LorentzFactor = LorentzFactor.subs(r,rfm.xxSph[0]).subs(th,rfm.xxSph[1]).subs(ph,rfm.xxSph[2]) # "Valencia" three-velocity for i in range(DIM): BtildeU[i] = BtildeU[i].subs(r,rfm.xxSph[0]).subs(th,rfm.xxSph[1]).subs(ph,rfm.xxSph[2]) uKS4U[i+1] = uKS4U[i+1].subs(r,rfm.xxSph[0]).subs(th,rfm.xxSph[1]).subs(ph,rfm.xxSph[2]) Valencia3velocityU[i] = Valencia3velocityU[i].subs(r,rfm.xxSph[0]).subs(th,rfm.xxSph[1]).subs(ph,rfm.xxSph[2]) # Transform initial data to our coordinate system: # First compute Jacobian and its inverse 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() # Declare as gridfunctions the final quantities we will output for the initial data global IDalpha,IDgammaDD,IDKDD,IDbetaU,IDValencia3velocityU IDalpha = gri.register_gridfunctions("EVOL","IDalpha") IDgammaDD = ixp.register_gridfunctions_for_single_rank2("EVOL","IDgammaDD","sym01") IDKDD = ixp.register_gridfunctions_for_single_rank2("EVOL","IDKDD","sym01") IDbetaU = ixp.register_gridfunctions_for_single_rank1("EVOL","IDbetaU") IDValencia3velocityU = ixp.register_gridfunctions_for_single_rank1("EVOL","IDValencia3velocityU") IDalpha = alpha for i in range(3): IDbetaU[i] = 0 IDValencia3velocityU[i] = 0 for j in range(3): # Matrices are stored in row, column format, so (i,j) <-> (row,column) IDbetaU[i] += dx__drrefmetric_0UDmatrix[(i,j)]*betaU[j] IDValencia3velocityU[i] += dx__drrefmetric_0UDmatrix[(i,j)]*Valencia3velocityU[j] IDgammaDD[i][j] = 0 IDKDD[i][j] = 0 for k in range(3): for l in range(3): IDgammaDD[i][j] += drrefmetric__dx_0UDmatrix[(k,i)]*drrefmetric__dx_0UDmatrix[(l,j)]*gammaDD[k][l] IDKDD[i][j] += drrefmetric__dx_0UDmatrix[(k,i)]*drrefmetric__dx_0UDmatrix[(l,j)]* KDD[k][l]
def GiRaFFE_Higher_Order(): #Step 1.0: Set the spatial dimension parameter to 3. par.set_parval_from_str("grid::DIM", 3) DIM = par.parval_from_str("grid::DIM") # Step 1.1: Set the finite differencing order to 4. #par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", 4) thismodule = "GiRaFFE_NRPy" # M_PI will allow the C code to substitute the correct value M_PI = par.Cparameters("#define", thismodule, "M_PI", "") # ADMBase defines the 4-metric in terms of the 3+1 spacetime metric quantities gamma_{ij}, beta^i, and alpha gammaDD = ixp.register_gridfunctions_for_single_rank2("AUX", "gammaDD", "sym01", DIM=3) betaU = ixp.register_gridfunctions_for_single_rank1("AUX", "betaU", DIM=3) alpha = gri.register_gridfunctions("AUX", "alpha") # GiRaFFE uses the Valencia 3-velocity and A_i, which are defined in the initial data module(GiRaFFEfood) ValenciavU = ixp.register_gridfunctions_for_single_rank1("AUX", "ValenciavU", DIM=3) AD = ixp.register_gridfunctions_for_single_rank1("EVOL", "AD", DIM=3) # B^i must be computed at each timestep within GiRaFFE so that the Valencia 3-velocity can be evaluated BU = ixp.register_gridfunctions_for_single_rank1("AUX", "BU", DIM=3) # <a id='step3'></a> # # ## Step 1.2: Build the four metric $g_{\mu\nu}$, its inverse $g^{\mu\nu}$ and spatial derivatives $g_{\mu\nu,i}$ from ADM 3+1 quantities $\gamma_{ij}$, $\beta^i$, and $\alpha$ # # $$\label{step3}$$ # \[Back to [top](#top)\] # # Notice that the time evolution equation for $\tilde{S}_i$ # $$ # \partial_t \tilde{S}_i = - \partial_j \left( \alpha \sqrt{\gamma} T^j_{{\rm EM} i} \right) + \frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu} # $$ # contains $\partial_i g_{\mu \nu} = g_{\mu\nu,i}$. We will now focus on evaluating this term. # # The four-metric $g_{\mu\nu}$ is related to the three-metric $\gamma_{ij}$, index-lowered shift $\beta_i$, and lapse $\alpha$ by # $$ # g_{\mu\nu} = \begin{pmatrix} # -\alpha^2 + \beta^k \beta_k & \beta_j \\ # \beta_i & \gamma_{ij} # \end{pmatrix}. # $$ # This tensor and its inverse have already been built by the u0_smallb_Poynting__Cartesian.py module ([documented here](Tutorial-u0_smallb_Poynting-Cartesian.ipynb)), so we can simply load the module and import the variables. # Step 1.2: import u0_smallb_Poynting__Cartesian.py to set # the four metric and its inverse. This module also sets b^2 and u^0. import u0_smallb_Poynting__Cartesian.u0_smallb_Poynting__Cartesian as u0b u0b.compute_u0_smallb_Poynting__Cartesian(gammaDD, betaU, alpha, ValenciavU, BU) betaD = ixp.zerorank1() for i in range(DIM): for j in range(DIM): betaD[i] += gammaDD[i][j] * betaU[j] # We will now pull in the four metric and its inverse. import BSSN.ADMBSSN_tofrom_4metric as AB4m # NRPy+: ADM/BSSN <-> 4-metric conversions AB4m.g4DD_ito_BSSN_or_ADM("ADM") g4DD = AB4m.g4DD AB4m.g4UU_ito_BSSN_or_ADM("ADM") g4UU = AB4m.g4UU # Next we compute spatial derivatives of the metric, $\partial_i g_{\mu\nu} = g_{\mu\nu,i}$, written in terms of the three-metric, shift, and lapse. Simply taking the derivative of the expression for $g_{\mu\nu}$ above, we find # $$ # g_{\mu\nu,l} = \begin{pmatrix} # -2\alpha \alpha_{,l} + \beta^k_{\ ,l} \beta_k + \beta^k \beta_{k,l} & \beta_{i,l} \\ # \beta_{j,l} & \gamma_{ij,l} # \end{pmatrix}. # $$ # # Notice the derivatives of the shift vector with its indexed lowered, $\beta_{i,j} = \partial_j \beta_i$. This can be easily computed in terms of the given ADMBase quantities $\beta^i$ and $\gamma_{ij}$ via: # \begin{align} # \beta_{i,j} &= \partial_j \beta_i \\ # &= \partial_j (\gamma_{ik} \beta^k) \\ # &= \gamma_{ik} \partial_j\beta^k + \beta^k \partial_j \gamma_{ik} \\ # \beta_{i,j} &= \gamma_{ik} \beta^k_{\ ,j} + \beta^k \gamma_{ik,j}. # \end{align} # # Since this expression mixes Greek and Latin indices, we will need to store the expressions for each of the three spatial derivatives as separate variables. # # So, we will first set # $$ g_{00,l} = \underbrace{-2\alpha \alpha_{,l}}_{\rm Term\ 1} + \underbrace{\beta^k_{\ ,l} \beta_k}_{\rm Term\ 2} + \underbrace{\beta^k \beta_{k,l}}_{\rm Term\ 3} $$ # Step 1.2, cont'd: Build spatial derivatives of the four metric # Step 1.2.a: Declare derivatives of grid functions. These will be handled by FD_outputC alpha_dD = ixp.declarerank1("alpha_dD") betaU_dD = ixp.declarerank2("betaU_dD", "nosym") gammaDD_dD = ixp.declarerank3("gammaDD_dD", "sym01") # Step 1.2.b: These derivatives will be constructed analytically. betaDdD = ixp.zerorank2() g4DDdD = ixp.zerorank3(DIM=4) for i in range(DIM): for j in range(DIM): for k in range(DIM): # \gamma_{ik} \beta^k_{,j} + \beta^k \gamma_{ik,j} betaDdD[i][j] += gammaDD[i][k] * betaU_dD[k][j] + betaU[ k] * gammaDD_dD[i][k][j] # Step 1.2.c: Set the 00 components # Step 1.2.c.i: Term 1: -2\alpha \alpha_{,l} for l in range(DIM): g4DDdD[0][0][l + 1] = -2 * alpha * alpha_dD[l] # Step 1.2.c.ii: Term 2: \beta^k_{\ ,l} \beta_k for l in range(DIM): for k in range(DIM): g4DDdD[0][0][l + 1] += betaU_dD[k][l] * betaD[k] # Step 1.2.c.iii: Term 3: \beta^k \beta_{k,l} for l in range(DIM): for k in range(DIM): g4DDdD[0][0][l + 1] += betaU[k] * betaDdD[k][l] # Now we will contruct the other components of $g_{\mu\nu,l}$. We will first construct # $$ g_{i0,l} = g_{0i,l} = \beta_{i,l}, $$ # then # $$ g_{ij,l} = \gamma_{ij,l} $$ # Step 1.2.d: Set the i0 and 0j components for l in range(DIM): for i in range(DIM): # \beta_{i,l} g4DDdD[i + 1][0][l + 1] = g4DDdD[0][i + 1][l + 1] = betaDdD[i][l] #Step 1.2.e: Set the ij components for l in range(DIM): for i in range(DIM): for j in range(DIM): # \gamma_{ij,l} g4DDdD[i + 1][j + 1][l + 1] = gammaDD_dD[i][j][l] # <a id='step4'></a> # # # $T^{\mu\nu}_{\rm EM}$ and its derivatives # # Now that the metric and its derivatives are out of the way, let's return to the evolution equation for $\tilde{S}_i$, # $$ # \partial_t \tilde{S}_i = - \partial_j \left( \alpha \sqrt{\gamma} T^j_{{\rm EM} i} \right) + \frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu}. # $$ # We turn our focus now to $T^j_{{\rm EM} i}$ and its derivatives. To this end, we start by computing $T^{\mu \nu}_{\rm EM}$ (from eq. 27 of [Paschalidis & Shapiro's paper on their GRFFE code](https://arxiv.org/pdf/1310.3274.pdf)): # # $$\boxed{T^{\mu \nu}_{\rm EM} = b^2 u^{\mu} u^{\nu} + \frac{b^2}{2} g^{\mu \nu} - b^{\mu} b^{\nu}.}$$ # # Notice that $T^{\mu\nu}_{\rm EM}$ is written in terms of # # * $b^\mu$, the 4-component magnetic field vector, related to the comoving magnetic field vector $B^i_{(u)}$ # * $u^\mu$, the 4-velocity # * $g^{\mu \nu}$, the inverse 4-metric # # However, $\texttt{GiRaFFE}$ has access to only the following quantities, requiring in the following sections that we write the above quantities in terms of the following ones: # # * $\gamma_{ij}$, the 3-metric # * $\alpha$, the lapse # * $\beta^i$, the shift # * $A_i$, the vector potential # * $B^i$, the magnetic field (we assume only in the grid interior, not the ghost zones) # * $\left[\sqrt{\gamma}\Phi\right]$, the zero-component of the vector potential $A_\mu$, times the square root of the determinant of the 3-metric # * $v_{(n)}^i$, the Valencia 3-velocity # * $u^0$, the zero-component of the 4-velocity # # ## Step 2.0: $u^i$ and $b^i$ and related quantities # $$\label{step4}$$ # \[Back to [top](#top)\] # # We begin by importing what we can from u0_smallb_Poynting__Cartesian.py. We will need the four-velocity $u^\mu$, which is related to the Valencia 3-velocity $v^i_{(n)}$ used directly by $\texttt{GiRaFFE}$ (see also [Duez, et al, eqs. 53 and 56](https://arxiv.org/pdf/astro-ph/0503420.pdf)) # \begin{align} # u^i &= u^0 (\alpha v^i_{(n)} - \beta^i), \\ # u_j &= \alpha u^0 \gamma_{ij} v^i_{(n)}, # \end{align} # and $v^i_{(n)}$ is the Valencia three-velocity. These have already been constructed in terms of the Valencia 3-velocity and other 3+1 ADM quantities by the u0_smallb_Poynting__Cartesian.py module, so we can simply import these variables: # Step 2.0: u^i, b^i, and related quantities # Step 2.0.a: import the four-velocity, as written in terms of the Valencia 3-velocity global uD, uU uD = ixp.register_gridfunctions_for_single_rank1("AUX", "uD") uU = ixp.register_gridfunctions_for_single_rank1("AUX", "uU") u4upperZero = gri.register_gridfunctions("AUX", "u4upperZero") for i in range(DIM): uD[i] = u0b.uD[i].subs(u0b.u0, u4upperZero) uU[i] = u0b.uU[i].subs(u0b.u0, u4upperZero) # We also need the magnetic field 4-vector $b^{\mu}$, which is related to the magnetic field by [eqs. 23, 24, and 31 in Duez, et al](https://arxiv.org/pdf/astro-ph/0503420.pdf): # \begin{align} # b^0 &= \frac{1}{\sqrt{4\pi}} B^0_{\rm (u)} = \frac{u_j B^j}{\sqrt{4\pi}\alpha}, \\ # b^i &= \frac{1}{\sqrt{4\pi}} B^i_{\rm (u)} = \frac{B^i + (u_j B^j) u^i}{\sqrt{4\pi}\alpha u^0}, \\ # \end{align} # where $B^i$ is the variable tracked by the HydroBase thorn in the Einstein Toolkit. Again, these have already been built by the u0_smallb_Poynting__Cartesian.py module, so we can simply import the variables. # Step 2.0.b: import the small b terms smallb4U = ixp.zerorank1(DIM=4) smallb4D = ixp.zerorank1(DIM=4) for mu in range(4): smallb4U[mu] = u0b.smallb4U[mu].subs(u0b.u0, u4upperZero) smallb4D[mu] = u0b.smallb4D[mu].subs(u0b.u0, u4upperZero) smallb2 = u0b.smallb2etk.subs(u0b.u0, u4upperZero) # <a id='step5'></a> # # ## Step 2.1: Construct all components of the electromagnetic stress-energy tensor $T^{\mu \nu}_{\rm EM}$ # $$\label{step5}$$ # # \[Back to [top](#top)\] # # We now have all the pieces to calculate the stress-energy tensor, # $$T^{\mu \nu}_{\rm EM} = \underbrace{b^2 u^{\mu} u^{\nu}}_{\rm Term\ 1} + # \underbrace{\frac{b^2}{2} g^{\mu \nu}}_{\rm Term\ 2} # - \underbrace{b^{\mu} b^{\nu}}_{\rm Term\ 3}.$$ # Because $u^0$ is a separate variable, we could build the $00$ component separately, then the $\mu0$ and $0\nu$ components, and finally the $\mu\nu$ components. Alternatively, for clarity, we could create a temporary variable $u^\mu=\left( u^0, u^i \right)$ # Step 2.1: Construct the electromagnetic stress-energy tensor # Step 2.1.a: Set up the four-velocity vector u4U = ixp.zerorank1(DIM=4) u4U[0] = u4upperZero for i in range(DIM): u4U[i + 1] = uU[i] # Step 2.1.b: Build T4EMUU itself T4EMUU = ixp.zerorank2(DIM=4) for mu in range(4): for nu in range(4): # Term 1: b^2 u^{\mu} u^{\nu} T4EMUU[mu][nu] = smallb2 * u4U[mu] * u4U[nu] for mu in range(4): for nu in range(4): # Term 2: b^2 / 2 g^{\mu \nu} T4EMUU[mu][nu] += smallb2 * g4UU[mu][nu] / 2 for mu in range(4): for nu in range(4): # Term 3: -b^{\mu} b^{\nu} T4EMUU[mu][nu] += -smallb4U[mu] * smallb4U[nu] # <a id='step6'></a> # # # Step 2.2: Derivatives of the electromagnetic stress-energy tensor # $$\label{step6}$$ # # \[Back to [top](#top)\] # # If we look at the evolution equation, we see that we will need spatial derivatives of $T^{\mu\nu}_{\rm EM}$. When confronted with derivatives of complicated expressions, it is generally convenient to declare those expressions as gridfunctions themselves, allowing NRPy+ to take finite-difference derivatives of the expressions. This can even reduce the truncation error associated with the finite differences, because the alternative is to use a function of several finite-difference derivatives, allowing more error to accumulate than the extra gridfunction will introduce. While we will use that technique for some of the subexpressions of $T^{\mu\nu}_{\rm EM}$, we don't want to rely on it for the whole expression; doing so would require us to take the derivative of the magnetic field $B^i$, which is itself found by finite-differencing the vector potential $A_i$. Thus $B^i$ cannot be *consistently* defined in ghost zones. To potentially reduce numerical errors induced by inconsistent finite differencing, we will differentiate $T^{\mu\nu}_{\rm EM}$ term-by-term so that finite-difference derivatives of $A_i$ appear. # # We will now now take these spatial derivatives of $T^{\mu\nu}_{\rm EM}$, applying the chain rule until it is only in terms of basic gridfunctions and their derivatives: $\alpha$, $\beta^i$, $\gamma_{ij}$, $A_i$, and the four-velocity $u^i$. Along the way, we will also set up useful temporary variables representing the steps of the chain rule. (Notably, *all* of these quantities will be written in terms of $A_i$ and its derivatives): # # * $B^i$ (already computed in terms of $A_k$, via $B^i = \epsilon^{ijk} \partial_j A_k$), # * $B^i_{,l}$, # * $b^i$ and $b_i$ (already computed), # * $b^i_{,k}$, # * $b^2$ (already computed), # * and $\left(b^2\right)_{,j}$. # # (The variables not already computed will not be seen by the ETK, as they are written in terms of $A_i$ and its derivatives; they simply help to organize the NRPy+ code.) # # So then, # \begin{align} # \partial_j T^{j}_{{\rm EM} i} &= \partial_j (g_{\mu i} T^{\mu j}_{\rm EM}) \\ # &= \partial_j \left[g_{\mu i} \left(b^2 u^j u^\mu + \frac{b^2}{2} g^{j\mu} - b^j b^\mu\right)\right] \\ # &= \underbrace{g_{\mu i,j} T^{\mu j}_{\rm EM}}_{\rm Term\ A} + g_{\mu i} \left( \underbrace{\partial_j \left(b^2 u^j u^\mu \right)}_{\rm Term\ B} + \underbrace{\partial_j \left(\frac{b^2}{2} g^{j\mu}\right)}_{\rm Term\ C} - \underbrace{\partial_j \left(b^j b^k\right)}_{\rm Term\ D} \right) \\ # \end{align} # Following the product and chain rules for each term, we find that # \begin{align} # {\rm Term\ B} &= \partial_j (b^2 u^j u^\mu) \\ # &= \partial_j b^2 u^j u^\mu + b^2 \partial_j u^j u^\mu + b^2 u^j \partial_j u^\mu \\ # &= \underbrace{\left(b^2\right)_{,j} u^j u^\mu + b^2 u^j_{,j} u^\mu + b^2 u^j u^{\mu}_{,j}}_{\rm To\ Term\ 2\ below} \\ # {\rm Term\ C} &= \partial_j \left(\frac{b^2}{2} g^{j\mu}\right) \\ # &= \frac{1}{2} \left( \partial_j b^2 g^{j\mu} + b^2 \partial_j g^{j\mu} \right) \\ # &= \underbrace{\frac{1}{2} \left(b^2\right)_{,j} g^{j\mu} + \frac{b^2}{2} g^{j\mu}_{\ ,j}}_{\rm To\ Term\ 3\ below} \\ # {\rm Term\ D} &= \partial_j (b^j b^\mu) \\ # &= \underbrace{b^j_{,j} b^\mu + b^j b^\mu_{,j}}_{\rm To\ Term\ 2\ below}\\ # \end{align} # # So, # \begin{align} # \partial_j T^{j}_{{\rm EM} i} &= g_{\mu i,j} T^{\mu j}_{\rm EM} \\ # &+ g_{\mu i} \left(\left(b^2\right)_{,j} u^j u^\mu +b^2 u^j_{,j} u^\mu + b^2 u^j u^{\mu}_{,j} + \frac{1}{2}\left(b^2\right)_{,j} g^{j\mu} + \frac{b^2}{2} g^{j\mu}_{\ ,j} + b^j_{,j} b^\mu + b^j b^\mu_{,j}\right); # \end{align} # We will rearrange this once more, collecting the $b^2$ terms together, noting that Term A will become Term 1: # \begin{align} # \partial_j T^{j}_{{\rm EM} i} =& \ # \underbrace{g_{\mu i,j} T^{\mu j}_{\rm EM}}_{\rm Term\ 1} \\ # & + \underbrace{g_{\mu i} \left( b^2 u^j_{,j} u^\mu + b^2 u^j u^\mu_{,j} + \frac{b^2}{2} g^{j\mu}_{\ ,j} + b^j_{,j} b^\mu + b^j b^\mu_{,j} \right)}_{\rm Term\ 2} \\ # & + \underbrace{g_{\mu i} \left( \left(b^2\right)_{,j} u^j u^\mu + \frac{1}{2} \left(b^2\right)_{,j} g^{j\mu} \right).}_{\rm Term\ 3} \\ # \end{align} # # <a id='table2'></a> # # **List of Derivatives** # $$\label{table2}$$ # # Note that this is in terms of the derivatives of several other quantities: # # * [Step 2.2.a](#capitalBideriv): $B^i_{,l}$: Since $b^i$ is itself a function of $B^i$, we will first need the derivatives $B^i_{,l}$ in terms of the evolved quantity $A_i$ (the vector potential). # * [Step 2.2.b](#bideriv): $b^i_{,k}$: Once we have $B^i_{,l}$ we can evaluate derivatives of $b^i$, $b^i_{,k}$ # * [Step 2.2.c](#b2deriv): The derivative of $b^2 = g_{\mu\nu} b^\mu b^\nu$, $\left(b^2\right)_{,j}$ # * [Step 2.2.d](#gupijderiv): Derivatives of $g^{\mu\nu}$, $g^{\mu\nu}_{\ ,k}$ # * [Step 2.2.e](#alltogether): Putting it together: $\partial_j T^{j}_{{\rm EM} i}$ # * [Step 2.2.e.i](#alltogether1): Putting it together: Term 1 # * [Step 2.2.e.ii](#alltogether2): Putting it together: Term 2 # * [Step 2.2.e.iii](#alltogether3): Putting it together: Term 3 # <a id='capitalBideriv'></a> # # ## Step 2.2.a: Derivatives of $B^i$ # # $$\label{capitalbideriv}$$ # # \[Back to [List of Derivatives](#table2)\] # # First, we will build the derivatives of the magnetic field. Since $b^i$ is a function of $B^i$, we will start from the definition of $B^i$ in terms of $A_i$, $B^i = \frac{[ijk]}{\sqrt{\gamma}} \partial_j A_k$. We will first apply the product rule, noting that the symbol $[ijk]$ consists purely of the integers $-1, 0, 1$ and thus can be treated as a constant in this process. # \begin{align} # B^i_{,l} &= \partial_l \left( \frac{[ijk]}{\sqrt{\gamma}} \partial_j A_k \right) \\ # &= [ijk] \partial_l \left( \frac{1}{\sqrt{\gamma}}\right) \partial_j A_k + \frac{[ijk]}{\sqrt{\gamma}} \partial_l \partial_j A_k \\ # &= [ijk]\left(-\frac{\gamma_{,l}}{2\gamma^{3/2}}\right) \partial_j A_k + \frac{[ijk]}{\sqrt{\gamma}} \partial_l \partial_j A_k \\ # \end{align} # Now, we will substitute back in for the definition of the Levi-Civita tensor: $\epsilon^{ijk} = [ijk] / \sqrt{\gamma}$. Then we will substitute the magnetic field $B^i$ back in. # \begin{align} # B^i_{,l} &= -\frac{\gamma_{,l}}{2\gamma} \epsilon^{ijk} \partial_j A_k + \epsilon^{ijk} \partial_l \partial_j A_k \\ # &= -\frac{\gamma_{,l}}{2\gamma} B^i + \epsilon^{ijk} A_{k,jl}, \\ # \end{align} # # Thus, the expression we are left with for the derivatives of the magnetic field is: # \begin{align} # B^i_{,l} &= \underbrace{-\frac{\gamma_{,l}}{2\gamma} B^i}_{\rm Term\ 1} + \underbrace{\epsilon^{ijk} A_{k,jl}}_{\rm Term\ 2}, \\ # \end{align} # where $\epsilon^{ijk} = [ijk] / \sqrt{\gamma}$ is the antisymmetric Levi-Civita tensor and $\gamma$ is the determinant of the three-metric. # # Step 2.2: Derivatives of the electromagnetic stress-energy tensor # We already have a handy function to define the Levi-Civita symbol in WeylScalars import WeylScal4NRPy.WeylScalars_Cartesian as weyl # Initialize the Levi-Civita tensor by setting it equal to the Levi-Civita symbol LeviCivitaSymbolDDD = weyl.define_LeviCivitaSymbol_rank3() LeviCivitaTensorDDD = ixp.zerorank3() LeviCivitaTensorUUU = ixp.zerorank3() global gammaUU, gammadet gammaUU = ixp.register_gridfunctions_for_single_rank2( "AUX", "gammaUU", "sym01") gammadet = gri.register_gridfunctions("AUX", "gammadet") gammaUU, gammadet = ixp.symm_matrix_inverter3x3(gammaDD) for i in range(DIM): for j in range(DIM): for k in range(DIM): LeviCivitaTensorDDD[i][j][ k] = LeviCivitaSymbolDDD[i][j][k] * sp.sqrt(gammadet) LeviCivitaTensorUUU[i][j][ k] = LeviCivitaSymbolDDD[i][j][k] / sp.sqrt(gammadet) AD_dD = ixp.declarerank2("AD_dD", "nosym") # Step 2.2.a: Construct the derivatives of the magnetic field. gammadet_dD = ixp.declarerank1("gammadet_dD") AD_dDD = ixp.declarerank3("AD_dDD", "sym12") # The other partial derivatives of B^i BUdD = ixp.zerorank2() for i in range(DIM): for l in range(DIM): # Term 1: -\gamma_{,l} / (2\gamma) B^i BUdD[i][l] = -gammadet_dD[l] * BU[i] / (2 * gammadet) for i in range(DIM): for l in range(DIM): for j in range(DIM): for k in range(DIM): # Term 2: \epsilon^{ijk} A_{k,jl} BUdD[i][ l] += LeviCivitaTensorUUU[i][j][k] * AD_dDD[k][j][l] # <a id='bideriv'></a> # # ## Step 2.2.b: Derivatives of $b^i$ # $$\label{bideriv}$$ # # \[Back to [List of Derivatives](#table2)\] # # Now, we will code the derivatives of the spatial components of $b^{\mu}$, $b^i$: # $$ # b^i_{,k} = \frac{1}{\sqrt{4 \pi}} \frac{\left(\alpha u^0\right) \left(B^i_{,k} + u_{j,k} B^j u^i + u_j B^j_{,k} u^i + u_j B^j u^i_{,k}\right) - \left(B^i + (u_j B^j) u^i\right) \partial_k \left(\alpha u^0\right)}{\left(\alpha u^0\right)^2}. # $$ # # We should note that while $b^\mu$ is a four-vector (and the code reflects this: $\text{smallb4U}$ and $\text{smallb4U}$ have $\text{DIM=4}$), we only need the spatial components. We will only focus on the spatial components for the moment. # # # Let's go into a little more detail on where this comes from. We start from the definition $$b^i = \frac{B^i + (u_j B^j) u^i}{\sqrt{4\pi}\alpha u^0};$$ we then apply the quotient rule: # \begin{align} # b^i_{,k} &= \frac{\left(\sqrt{4\pi}\alpha u^0\right) \partial_k \left(B^i + (u_j B^j) u^i\right) - \left(B^i + (u_j B^j) u^i\right) \partial_k \left(\sqrt{4\pi}\alpha u^0\right)}{\left(\sqrt{4\pi}\alpha u^0\right)^2} \\ # &= \frac{1}{\sqrt{4 \pi}} \frac{\left(\alpha u^0\right) \partial_k \left(B^i + (u_j B^j) u^i\right) - \left(B^i + (u_j B^j) u^i\right) \partial_k \left(\alpha u^0\right)}{\left(\alpha u^0\right)^2} \\ # \end{align} # Note that $\left( \alpha u^0 \right)$ is being used as its own gridfunction, so $\partial_k \left(a u^0\right)$ will be finite-differenced by NRPy+ directly. We will also apply the product rule to the term $\partial_k \left(B^i + (u_j B^j) u^i\right) = B^i_{,k} + u_{j,k} B^j u^i + u_j B^j_{,k} u^i + u_j B^j u^i_{,k}$. So, # $$ b^i_{,k} = \frac{1}{\sqrt{4 \pi}} \frac{\left(\alpha u^0\right) \left(B^i_{,k} + u_{j,k} B^j u^i + u_j B^j_{,k} u^i + u_j B^j u^i_{,k}\right) - \left(B^i + (u_j B^j) u^i\right) \partial_k \left(\alpha u^0\right)}{\left(\alpha u^0\right)^2}. $$ # # It will be easier to code this up if we rearrange these terms to group together the terms that involve contractions over $j$. Doing that, we find # $$ # b^i_{,k} = \frac{\overbrace{\alpha u^0 B^i_{,k} - B^i \partial_k (\alpha u^0)}^{\rm Term\ Num1} + \overbrace{\left( \alpha u^0 \right) \left( u_{j,k} B^j u^i + u_j B^j_{,k} u^i + u_j B^j u^i_{,k} \right)}^{\rm Term\ Num2.a} - \overbrace{\left( u_j B^j u^i \right) \partial_k \left( \alpha u^0 \right) }^{\rm Term\ Num2.b}}{\underbrace{\sqrt{4 \pi} \left( \alpha u^0 \right)^2}_{\rm Term\ Denom}}. # $$ global u0alpha u0alpha = gri.register_gridfunctions("AUX", "u0alpha") u0alpha = alpha * u4upperZero u0alpha_dD = ixp.declarerank1("u0alpha_dD") uU_dD = ixp.declarerank2("uU_dD", "nosym") uD_dD = ixp.declarerank2("uD_dD", "nosym") # Step 2.2.b: Construct derivatives of the small b vector # smallbUdD represents the derivative of smallb4U smallbUdD = ixp.zerorank2() for i in range(DIM): for k in range(DIM): # Term Num1: \alpha u^0 B^i_{,k} - B^i \partial_k (\alpha u^0) smallbUdD[i][k] += u0alpha * BUdD[i][k] - BU[i] * u0alpha_dD[k] for i in range(DIM): for k in range(DIM): for j in range(DIM): # Term Num2.a: terms that require contractions over k, and thus an extra loop. # ( \alpha u^0 ) ( u_{j,k} B^j u^i # + u_j B^j_{,k} u^i # + u_j B^j u^i_{,k} ) smallbUdD[i][k] += u0alpha * (uD_dD[j][k] * BU[j] * uU[i] + uD[j] * BUdD[j][k] * uU[i] + uD[j] * BU[j] * uU_dD[i][k]) for i in range(DIM): for k in range(DIM): for j in range(DIM): #Term 2.b (More contractions over k): ( u_j B^j u^i ) ( \alpha u^0 ),k smallbUdD[i][k] += -(uD[j] * BU[j] * uU[i]) * u0alpha_dD[k] for i in range(DIM): for k in range(DIM): # Term Denom: Divide the numerator by sqrt(4 pi) * (alpha u^0)^2 smallbUdD[i][k] /= sp.sqrt(4 * M_PI) * u0alpha * u0alpha # <a id='b2deriv'></a> # # ## Step 2.2.c: Derivative of $b^2$ # $$\label{b2deriv}$$ # # \[Back to [List of Derivatives](#table2)\] # # Here, we will take the derivative of $b^2 = g_{\mu\nu} b^\mu b^\nu$. Using the product rule, # \begin{align} # \left(b^2\right)_{,j} &= \partial_j \left( g_{\mu\nu} b^\mu b^\nu \right) \\ # &= g_{\mu\nu,j} b^\mu b^\nu + g_{\mu\nu} b^\mu_{,j} b^\nu + g_{\mu\nu} b^\mu b^\nu_{,j} \\ # &= g_{\mu\nu,j} b^\mu b^\nu + 2 g_{\mu\nu} b^\mu_{,j} b^\nu. # \end{align} # We have already defined the spatial derivatives of the four-metric $g_{\mu\nu,j}$ in [this section](#step3); we have also defined the spatial derivatives of spatial components of $b^\mu$, $b^i_{,k}$ in [this section](#bideriv). Notice the above expression requires spatial derivatives of the *zeroth* component of $b^\mu$ as well, $b^0_{,j}$, which we will now compute. Starting with the definition, and applying the quotient rule: # \begin{align} # b^0 &= \frac{u_k B^k}{\sqrt{4\pi}\alpha}, \\ # \rightarrow b^0_{,j} &= \frac{1}{\sqrt{4\pi}} \frac{\alpha \left( u_{k,j} B^k + u_k B^k_{,j} \right) - u_k B^k \alpha_{,j}}{\alpha^2} \\ # &= \frac{\alpha u_{k,j} B^k + \alpha u_k B^k_{,j} - \alpha_{,j} u_k B^k}{\sqrt{4\pi} \alpha^2}. # \end{align} # We will first code the numerator, and then divide through by the denominator. # Step 2.2.c: Construct the derivative of b^2 # First construct the derivative b^0_{,j} # This four-vector will make b^2 simpler: smallb4UdD = ixp.zerorank2(DIM=4) # Fill in the zeroth component for j in range(DIM): for k in range(DIM): # The numerator: \alpha u_{k,j} B^k # + \alpha u_k B^k_{,j} # - \alpha_{,j} u_k B^k smallb4UdD[0][j + 1] += alpha * uD_dD[k][j] * BU[k] + alpha * uD[ k] * BUdD[k][j] - alpha_dD[j] * uD[k] * BU[k] for j in range(DIM): # Divide through by the denominator: \sqrt{4\pi} \alpha^2 smallb4UdD[0][j + 1] /= sp.sqrt(4 * M_PI) * alpha * alpha # At this point, both $b^0_{\ ,j}$ and $b^i_{\ ,j}$ have been computed, but one exists inconveniently in the $4\times 4$ component $\verb|smallb4UdD[][]|$ and the other in the $3\times 3$ component $\verb|smallbUdD[][]|$. So that we can perform full implied sums over $g_{\mu\nu} b^\mu_{,j} b^\nu$ more conveniently, we will now store all information from $\verb|smallbUdD[i][j]|$ into $\verb|smallb4UdD[i+1][j+1]|$: # Now, we'll fill out the rest of the four-vector with b^i_{,j} that we derived above. for i in range(DIM): for j in range(DIM): smallb4UdD[i + 1][j + 1] = smallbUdD[i][j] # Using 4-component (Greek-indexed) quantities, we can now complete our construction of # $$\left(b^2\right)_{,j} = g_{\mu\nu,j} b^\mu b^\nu + 2 g_{\mu\nu} b^\mu_{,j} b^\nu:$$ smallb2_dD = ixp.zerorank1() for j in range(DIM): for mu in range(4): for nu in range(4): # g_{\mu\nu,j} b^\mu b^\nu # + 2 g_{\mu\nu} b^\mu_{,j} b^\nu smallb2_dD[j] += g4DDdD[mu][nu][j + 1] * smallb4U[ mu] * smallb4U[nu] + 2 * g4DD[mu][nu] * smallb4UdD[mu][ j + 1] * smallb4U[nu] # <a id='gupijderiv'></a> # # ## Step 2.2.d: Derivatives of $g^{\mu\nu}$ # $$\label{gupijderiv}$$ # # \[Back to [List of Derivatives](#table2)\] # # We will need derivatives of the inverse four-metric, as well. Let us begin with $g^{00}$: since $g^{00} = -1/\alpha^2$ ([Gourgoulhon, eq. 4.49](https://arxiv.org/pdf/gr-qc/0703035.pdf)), $$g^{00}_{\ ,k} = \frac{2 \alpha_{,k}}{\alpha^3}$$ # # Step 2.2.d: Construct derivatives of the components of g^{\mu\nu} g4UUdD = ixp.zerorank3(DIM=4) for k in range(DIM): # 2 \alpha_{,k} / \alpha^3 g4UUdD[0][0][k + 1] = 2 * alpha_dD[k] / alpha**3 # Now, we will code the $g^{i0}_{\ ,k}$ and $g^{0j}_{\ ,k}$ components. According to [Gourgoulhon, eq. 4.49](https://arxiv.org/pdf/gr-qc/0703035.pdf), $g^{i0} = g^{0i} = \beta^i/\alpha^2$, so $$g^{i0}_{\ ,k} = g^{0i}_{\ ,k} = \frac{\alpha^2 \beta^i_{,k} - 2 \beta^i \alpha \alpha_{,k}}{\alpha^4}$$ by the quotient rule. So, we'll code # $$ # g^{i0} = g^{0i} = # \underbrace{\frac{\beta^i_{,k}}{\alpha^2}}_{\rm Term\ 1} # - \underbrace{\frac{2 \beta^i \alpha_{,k}}{\alpha^3}}_{\rm Term\ 2} # $$ for k in range(DIM): for i in range(DIM): # Term 1: \beta^i_{,k} / \alpha^2 g4UUdD[i + 1][0][k + 1] = g4UUdD[0][i + 1][k + 1] = betaU_dD[i][k] / alpha**2 for k in range(DIM): for i in range(DIM): # Term 2: -2 \beta^i \alpha_{,k} / \alpha^3 g4UUdD[i + 1][0][k + 1] += -2 * betaU[i] * alpha_dD[k] / alpha**3 g4UUdD[0][i + 1][k + 1] += -2 * betaU[i] * alpha_dD[k] / alpha**3 # We will also need derivatives of the spatial part of the inverse four-metric: since $g^{ij} = \gamma^{ij} - \frac{\beta^i \beta^j}{\alpha^2}$ ([Gourgoulhon, eq. 4.49](https://arxiv.org/pdf/gr-qc/0703035.pdf)), # \begin{align} # g^{ij}_{\ ,k} &= \gamma^{ij}_{\ ,k} - \frac{\alpha^2 \partial_k (\beta^i \beta^j) - \beta^i \beta^j \partial_k \alpha^2}{(\alpha^2)^2} \\ # &= \gamma^{ij}_{\ ,k} - \frac{\alpha^2\beta^i \beta^j_{,k}+\alpha^2\beta^i_{,k} \beta^j-2\beta^i \beta^j \alpha \alpha_{,k}}{\alpha^4}. \\ # &= \gamma^{ij}_{\ ,k} - \frac{\alpha\beta^i \beta^j_{,k}+\alpha\beta^i_{,k} \beta^j-2\beta^i \beta^j \alpha_{,k}}{\alpha^3} \\ # g^{ij}_{\ ,k} &= \underbrace{\gamma^{ij}_{\ ,k}}_{\rm Term\ 1} - \underbrace{\frac{\beta^i \beta^j_{,k}}{\alpha^2}}_{\rm Term\ 2} - \underbrace{\frac{\beta^i_{,k} \beta^j}{\alpha^2}}_{\rm Term\ 3} + \underbrace{\frac{2\beta^i \beta^j \alpha_{,k}}{\alpha^3}}_{\rm Term\ 4}. \\ # \end{align} # gammaUU_dD = ixp.declarerank3("gammaUU_dD", "sym01") # The spatial derivatives of the spatial components of the four metric: # Term 1: \gamma^{ij}_{\ ,k} for i in range(DIM): for j in range(DIM): for k in range(DIM): g4UUdD[i + 1][j + 1][k + 1] = gammaUU_dD[i][j][k] # Term 2: - \beta^i \beta^j_{,k} / \alpha^2 for i in range(DIM): for j in range(DIM): for k in range(DIM): g4UUdD[i + 1][j + 1][k + 1] += -betaU[i] * betaU_dD[j][k] / alpha**2 # Term 3: - \beta^i_{,k} \beta^j / \alpha^2 for i in range(DIM): for j in range(DIM): for k in range(DIM): g4UUdD[i + 1][j + 1][k + 1] += -betaU_dD[i][k] * betaU[j] / alpha**2 # Term 4: 2\beta^i \beta^j \alpha_{,k}\alpha^3 for i in range(DIM): for j in range(DIM): for k in range(DIM): g4UUdD[i + 1][j + 1][ k + 1] += 2 * betaU[i] * betaU[j] * alpha_dD[k] / alpha**3 # <a id='alltogether'></a> # # ## Step 2.2.e: Putting it together: # $$\label{alltogether}$$ # # \[Back to [List of Derivatives](#table2)\] # # So, we can now put it all together, starting from the expression we derived above in [Step 2.2](#step6): # \begin{align} # \partial_j T^{j}_{{\rm EM} i} =& \ # \underbrace{g_{\mu i,j} T^{\mu j}_{\rm EM}}_{\rm Term\ 1} \\ # & + \underbrace{g_{\mu i} \left( b^2 u^j_{,j} u^\mu + b^2 u^j u^\mu_{,j} + \frac{b^2}{2} g^{j\mu}_{\ ,j} + b^j_{,j} b^\mu + b^j b^\mu_{,j} \right)}_{\rm Term\ 2} \\ # & + \underbrace{g_{\mu i} \left( \left(b^2\right)_{,j} u^j u^\mu + \frac{1}{2} \left(b^2\right)_{,j} g^{j\mu} \right).}_{\rm Term\ 3} \\ # \end{align} # # <a id='alltogether1'></a> # # ### Step 2.2.e.i: Putting it together: Term 1 # $$\label{alltogether1}$$ # # \[Back to [List of Derivatives](#table2)\] # # We will now construct this term by term. Term 1 is straightforward: $${\rm Term\ 1} = \gamma_{\mu i,j} T^{\mu j}_{\rm EM}.$$ # Step 2.2.e: Construct TEMUDdD_contracted itself # Step 2.2.e.i TEMUDdD_contracted = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for mu in range(4): # Term 1: g_{\mu i,j} T^{\mu j}_{\rm EM} TEMUDdD_contracted[i] += g4DDdD[mu][i + 1][j + 1] * T4EMUU[mu][j + 1] # We'll need derivatives of u4U for the next part: u4UdD = ixp.zerorank2(DIM=4) u4upperZero_dD = ixp.declarerank1( "u4upperZero_dD" ) # Note that derivatives can't be done in 4-D with the current version of NRPy for i in range(DIM): u4UdD[0][i + 1] = u4upperZero_dD[i] for i in range(DIM): for j in range(DIM): u4UdD[i + 1][j + 1] = uU_dD[i][j] # <a id='alltogether2'></a> # # ### Step 2.2.e.ii: Putting it together: Term 2 # $$\label{alltogether2}$$ # # \[Back to [List of Derivatives](#table2)\] # # We will now add $${\rm Term\ 2} = g_{\mu i} \left( \underbrace{b^2 u^j_{,j} u^\mu}_{\rm Term\ 2a} + \underbrace{b^2 u^j u^\mu_{,j}}_{\rm Term\ 2b} + \underbrace{\frac{b^2}{2} g^{j\mu}_{\ ,j}}_{\rm Term\ 2c} + \underbrace{b^j_{,j} b^\mu}_{\rm Term\ 2d} + \underbrace{b^j b^\mu_{,j}}_{\rm Term\ 2e} \right)$$ to $\partial_j T^{j}_{{\rm EM} i}$. These are the terms that involve contractions over $k$ (but no metric derivatives like Term 1 had). # # Step 2.2.e.ii for i in range(DIM): for j in range(DIM): for mu in range(4): # Term 2a: g_{\mu i} b^2 u^j_{,j} u^\mu TEMUDdD_contracted[i] += g4DD[mu][ i + 1] * smallb2 * uU_dD[j][j] * u4U[mu] for i in range(DIM): for j in range(DIM): for mu in range(4): # Term 2b: g_{\mu i} b^2 u^j u^\mu_{,j} TEMUDdD_contracted[i] += g4DD[mu][ i + 1] * smallb2 * uU[j] * u4UdD[mu][j + 1] for i in range(DIM): for j in range(DIM): for mu in range(4): # Term 2c: g_{\mu i} b^2 g^{j \mu}_{,j} / 2 TEMUDdD_contracted[i] += g4DD[mu][i + 1] * smallb2 * g4UUdD[ j + 1][mu][j + 1] / 2 for i in range(DIM): for j in range(DIM): for mu in range(4): # Term 2d: g_{\mu i} b^j_{,j} b^\mu TEMUDdD_contracted[i] += g4DD[mu][ i + 1] * smallbUdD[j][j] * smallb4U[mu] for i in range(DIM): for j in range(DIM): for mu in range(4): # Term 2e: g_{\mu i} b^j b^\mu_{,j} TEMUDdD_contracted[i] += g4DD[mu][i + 1] * smallb4U[ j + 1] * smallb4UdD[mu][j + 1] # <a id='alltogether3'></a> # # ### Step 2.2.e.iii: Putting it together: Term 3 # $$\label{alltogether3}$$ # # \[Back to [List of Derivatives](#table2)\] # # Now, we will add $${\rm Term\ 3} = g_{\mu i} \left( \underbrace{\left(b^2\right)_{,j} u^j u^\mu}_{\rm Term\ 3a} + \underbrace{\frac{1}{2} \left(b^2\right)_{,j} g^{j\mu}}_{\rm Term\ 3b} \right).$$ # Step 2.2.e.iii for i in range(DIM): for j in range(DIM): for mu in range(4): # Term 3a: g_{\mu i} ( b^2 )_{,j} u^j u^\mu TEMUDdD_contracted[i] += g4DD[mu][ i + 1] * smallb2_dD[j] * uU[j] * u4U[mu] for i in range(DIM): for j in range(DIM): for mu in range(4): # Term 3b: g_{mu i} ( b^2 )_{,j} g^{j\mu} / 2 TEMUDdD_contracted[i] += g4DD[mu][ i + 1] * smallb2_dD[j] * g4UU[j + 1][mu] / 2 # # # Evolution equation for $\tilde{S}_i$ # <a id='step7'></a> # # ## Step 3.0: Construct the evolution equation for $\tilde{S}_i$ # $$\label{step7}$$ # # \[Back to [top](#top)\] # # Finally, we will return our attention to the time evolution equation (from eq. 13 of the [original paper](https://arxiv.org/pdf/1704.00599.pdf)), # \begin{align} # \partial_t \tilde{S}_i &= - \partial_j \left( \alpha \sqrt{\gamma} T^j_{{\rm EM} i} \right) + \frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu} \\ # &= -T^j_{{\rm EM} i} \partial_j (\alpha \sqrt{\gamma}) - \alpha \sqrt{\gamma} \partial_j T^j_{{\rm EM} i} + \frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu} \\ # &= \underbrace{-g_{i\mu} T^{\mu j}_{\rm EM} \partial_j (\alpha \sqrt{\gamma}) # }_{\rm Term\ 1} - \underbrace{\alpha \sqrt{\gamma} \partial_j T^j_{{\rm EM} i}}_{\rm Term\ 2} + \underbrace{\frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu}}_{\rm Term\ 3} . # \end{align} # We will first take derivatives of $\alpha \sqrt{\gamma}$, then construct each term in turn. # Step 3.0: Construct the evolution equation for \tilde{S}_i # Here, we set up the necessary machinery to take FD derivatives of alpha * sqrt(gamma) global alpsqrtgam alpsqrtgam = gri.register_gridfunctions("AUX", "alpsqrtgam") alpsqrtgam = alpha * sp.sqrt(gammadet) alpsqrtgam_dD = ixp.declarerank1("alpsqrtgam_dD") global Stilde_rhsD Stilde_rhsD = ixp.zerorank1() # The first term: g_{i\mu} T^{\mu j}_{\rm EM} \partial_j (\alpha \sqrt{\gamma}) for i in range(DIM): for j in range(DIM): for mu in range(4): Stilde_rhsD[i] += -g4DD[i + 1][mu] * T4EMUU[mu][ j + 1] * alpsqrtgam_dD[j] # The second term: \alpha \sqrt{\gamma} \partial_j T^j_{{\rm EM} i} for i in range(DIM): Stilde_rhsD[i] += -alpsqrtgam * TEMUDdD_contracted[i] # The third term: \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu} / 2 for i in range(DIM): for mu in range(4): for nu in range(4): Stilde_rhsD[i] += alpsqrtgam * T4EMUU[mu][nu] * g4DDdD[mu][nu][ i + 1] / 2 # # Evolution equations for $A_i$ and $\Phi$ # <a id='step8'></a> # # ## Step 4.0: Construct the evolution equations for $A_i$ and $[\sqrt{\gamma}\Phi]$ # $$\label{step8}$$ # # \[Back to [top](#top)\] # # We will also need to evolve the vector potential $A_i$. This evolution is given as eq. 17 in the [$\texttt{GiRaFFE}$](https://arxiv.org/pdf/1704.00599.pdf) paper: # $$\boxed{\partial_t A_i = \epsilon_{ijk} v^j B^k - \partial_i (\underbrace{\alpha \Phi - \beta^j A_j}_{\rm AevolParen}),}$$ # where $\epsilon_{ijk} = [ijk] \sqrt{\gamma}$ is the antisymmetric Levi-Civita tensor, the drift velocity $v^i = u^i/u^0$, $\gamma$ is the determinant of the three metric, $B^k$ is the magnetic field, $\alpha$ is the lapse, and $\beta$ is the shift. # The scalar electric potential $\Phi$ is also evolved by eq. 19: # $$\boxed{\partial_t [\sqrt{\gamma} \Phi] = -\partial_j (\underbrace{\alpha\sqrt{\gamma}A^j - \beta^j [\sqrt{\gamma} \Phi]}_{\rm PevolParenU[j]}) - \xi \alpha [\sqrt{\gamma} \Phi],}$$ # with $\xi$ chosen as a damping factor. # # ### Step 4.0.a: Construct some useful auxiliary gridfunctions for the other evolution equations # # After declaring a some needed quantities, we will also define the parenthetical terms (underbrace above) that we need to take derivatives of. That way, we can take finite-difference derivatives easily. Note that we use $A^j = \gamma^{ij} A_i$, while $A_i$ (with $\Phi$) is technically a four-vector; this is justified, however, since $n_\mu A^\mu = 0$, where $n_\mu$ is a normal to the hypersurface, $A^0=0$ (according to Sec. II, subsection C of [this paper](https://arxiv.org/pdf/1110.4633.pdf)). # Step 4.0: Construct the evolution equations for A_i and sqrt(gamma)Phi # Step 4.0.a: Construct some useful auxiliary gridfunctions for the other evolution equations xi = par.Cparameters( "REAL", thismodule, "xi", 0.1 ) # The (dimensionful) Lorenz damping factor. Recommendation: set to ~1.5/max(delta t). # Define sqrt(gamma)Phi as psi6Phi psi6Phi = gri.register_gridfunctions("EVOL", "psi6Phi") Phi = psi6Phi / sp.sqrt(gammadet) # We'll define a few extra gridfunctions to avoid complicated derivatives global AevolParen, PevolParenU AevolParen = gri.register_gridfunctions("AUX", "AevolParen") PevolParenU = ixp.register_gridfunctions_for_single_rank1( "AUX", "PevolParenU") # {\rm AevolParen} = \alpha \Phi - \beta^j A_j AevolParen = alpha * Phi for j in range(DIM): AevolParen += -betaU[j] * AD[j] # {\rm PevolParenU[j]} = \alpha\sqrt{\gamma} \gamma^{ij} A_i - \beta^j [\sqrt{\gamma} \Phi] for j in range(DIM): PevolParenU[j] = -betaU[j] * psi6Phi for i in range(DIM): PevolParenU[j] += alpha * sp.sqrt(gammadet) * gammaUU[i][j] * AD[i] AevolParen_dD = ixp.declarerank1("AevolParen_dD") PevolParenU_dD = ixp.declarerank2("PevolParenU_dD", "nosym") # ### Step 4.0.b: Construct the actual evolution equations for $A_i$ and $[\sqrt{\gamma}\Phi]$ # # Now to set the evolution equations ([eqs. 17 and 19](https://arxiv.org/pdf/1704.00599.pdf)), recalling that the drift velocity $v^i = u^i/u^0$: # \begin{align} # \partial_t A_i &= \epsilon_{ijk} v^j B^k - \partial_i (\alpha \Phi - \beta^j A_j) \\ # &= \epsilon_{ijk} \frac{u^j}{u^0} B^k - {\rm AevolParen\_dD[i]} \\ # \partial_t [\sqrt{\gamma} \Phi] &= -\partial_j \left(\left(\alpha\sqrt{\gamma}\right)A^j - \beta^j [\sqrt{\gamma} \Phi]\right) - \xi \alpha [\sqrt{\gamma} \Phi] \\ # &= -{\rm PevolParenU\_dD[j][j]} - \xi \alpha [\sqrt{\gamma} \Phi]. \\ # \end{align} # Step 4.0.b: Construct the actual evolution equations for A_i and sqrt(gamma)Phi global A_rhsD, psi6Phi_rhs A_rhsD = ixp.zerorank1() psi6Phi_rhs = sp.sympify(0) for i in range(DIM): A_rhsD[i] = -AevolParen_dD[i] for j in range(DIM): for k in range(DIM): A_rhsD[i] += LeviCivitaTensorDDD[i][j][k] * ( uU[j] / u4upperZero) * BU[k] psi6Phi_rhs = -xi * alpha * psi6Phi for j in range(DIM): psi6Phi_rhs += -PevolParenU_dD[j][j]
def MaxwellCartesian_Evol(): #Step 0: Set the spatial dimension parameter to 3. par.set_parval_from_str("grid::DIM", 3) DIM = par.parval_from_str("grid::DIM") # Step 1: Set the finite differencing order to 4. # (not needed here) # par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", 4) # Step 2: Register gridfunctions that are needed as input. _psi = gri.register_gridfunctions( "EVOL", ["psi"]) # lgtm [py/unused-local-variable] # Step 3a: Declare the rank-1 indexed expressions E_{i}, A_{i}, # and \partial_{i} \psi. Derivative variables like these # must have an underscore in them, so the finite # difference module can parse the variable name properly. ED = ixp.register_gridfunctions_for_single_rank1("EVOL", "ED") AD = ixp.register_gridfunctions_for_single_rank1("EVOL", "AD") psi_dD = ixp.declarerank1("psi_dD") ## Step 3b: Declare the conformal metric tensor and its first # derivative. These are needed to find the Christoffel # symbols, which we need for covariant derivatives. gammaDD = ixp.register_gridfunctions_for_single_rank2( "AUX", "gammaDD", "sym01") # The AUX or EVOL designation is *not* # used in diagnostic modules. gammaDD_dD = ixp.declarerank3("gammaDD_dD", "sym01") gammaDD_dDD = ixp.declarerank4("gammaDD_dDD", "sym01_sym23") gammaUU, detgamma = ixp.symm_matrix_inverter3x3(gammaDD) gammaUU_dD = ixp.declarerank3("gammaDD_dD", "sym01") # Define the Christoffel symbols GammaUDD = ixp.zerorank3(DIM) for i in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): GammaUDD[i][k][l] += (sp.Rational(1,2))*gammaUU[i][m]*\ (gammaDD_dD[m][k][l] + gammaDD_dD[m][l][k] - gammaDD_dD[k][l][m]) # Step 3b: Declare the rank-2 indexed expression \partial_{j} A_{i}, # which is not symmetric in its indices. # Derivative variables like these must have an underscore # in them, so the finite difference module can parse the # variable name properly. AD_dD = ixp.declarerank2("AD_dD", "nosym") # Step 3c: Declare the rank-3 indexed expression \partial_{jk} A_{i}, # which is symmetric in the two {jk} indices. AD_dDD = ixp.declarerank3("AD_dDD", "sym12") # Step 4: Calculate first and second covariant derivatives, and the # necessary contractions. # First covariant derivative # D_{j} A_{i} = A_{i,j} - \Gamma^{k}_{ij} A_{k} AD_dcovD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): AD_dcovD[i][j] = AD_dD[i][j] for k in range(DIM): AD_dcovD[i][j] -= GammaUDD[k][i][j] * AD[k] # First, we must construct the lowered Christoffel symbols: # \Gamma_{ijk} = \gamma_{il} \Gamma^l_{jk} # And raise the index on A: # A^j = \gamma^{ij} A_i GammaDDD = ixp.zerorank3() AU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): AU[j] += gammaUU[i][j] * AD[i] for k in range(DIM): for l in range(DIM): GammaDDD[i][j][k] += gammaDD[i][l] * GammaUDD[l][j][k] # Covariant second derivative (the bracketed terms): # D_j D^j A_i = \gamma^{jk} [A_{i,jk} - A^l (\gamma_{li,kj} + \gamma_{kl,ij} - \gamma_{ik,lj}) # + \Gamma_{lik} (\gamma^{lm} A_{m,j} + A_m \gamma^{lm}{}_{,j}) # - (\Gamma^l_{ij} A_{l,k} + \Gamma^l_{jk} A_{i,l}) # + (\Gamma^m_{ij} \Gamma^l_{mk} A_l + \Gamma ^m_{jk} \Gamma^l_{im} A_l) AD_dcovDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): AD_dcovDD[i][j][k] = AD_dDD[i][j][k] for l in range(DIM): # Terms 1 and 3 AD_dcovDD[i][j][k] -= AU[l] * (gammaDD_dDD[l][i][k][j] + gammaDD_dDD[k][l][i][j] - \ gammaDD_dDD[i][k][l][j]) \ + GammaUDD[l][i][j] * AD_dD[l][k] + GammaUDD[l][j][k] * AD_dD[i][l] for m in range(DIM): # Terms 2 and 4 AD_dcovDD[i][j][k] += GammaDDD[l][i][k] * (gammaUU[l][m] * AD_dD[m][j] + AD[m] * gammaUU_dD[l][m][j]) \ + GammaUDD[m][i][j] * GammaUDD[l][m][k] * AD[l] \ + GammaUDD[m][j][k] * GammaUDD[l][i][m] * AD[l] # Covariant divergence # D_{i} A^{i} = \gamma^{ij} D_{j} A_{i} DivA = 0 # Gradient of covariant divergence # DivA_dD_{i} = \gamma^{jk} A_{k;\hat{j}\hat{i}} DivA_dD = ixp.zerorank1() # Covariant Laplacian # LapAD_{i} = \gamma^{jk} A_{i;\hat{j}\hat{k}} LapAD = ixp.zerorank1() for i in range(DIM): for j in range(DIM): DivA += gammaUU[i][j] * AD_dcovD[i][j] for k in range(DIM): DivA_dD[i] += gammaUU[j][k] * AD_dcovDD[k][j][i] LapAD[i] += gammaUU[j][k] * AD_dcovDD[i][j][k] global ArhsD, ErhsD, psi_rhs system = par.parval_from_str("System_to_use") if system == "System_I": # Step 5: Define right-hand sides for the evolution. print("Warning: System I is less stable!") ArhsD = ixp.zerorank1() ErhsD = ixp.zerorank1() for i in range(DIM): ArhsD[i] = -ED[i] - psi_dD[i] ErhsD[i] = -LapAD[i] + DivA_dD[i] psi_rhs = -DivA elif system == "System_II": # We inherit here all of the definitions from System I, above # Step 7a: Register the scalar auxiliary variable \Gamma Gamma = gri.register_gridfunctions("EVOL", ["Gamma"]) # Step 7b: Declare the ordinary gradient \partial_{i} \Gamma Gamma_dD = ixp.declarerank1("Gamma_dD") # Step 8a: Construct the second covariant derivative of the scalar \psi # \psi_{;\hat{i}\hat{j}} = \psi_{,i;\hat{j}} # = \psi_{,ij} - \Gamma^{k}_{ij} \psi_{,k} psi_dDD = ixp.declarerank2("psi_dDD", "sym01") psi_dcovDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): psi_dcovDD[i][j] = psi_dDD[i][j] for k in range(DIM): psi_dcovDD[i][j] += -GammaUDD[k][i][j] * psi_dD[k] # Step 8b: Construct the covariant Laplacian of \psi # Lappsi = ghat^{ij} D_{j} D_{i} \psi Lappsi = 0 for i in range(DIM): for j in range(DIM): Lappsi += gammaUU[i][j] * psi_dcovDD[i][j] # Step 9: Define right-hand sides for the evolution. global Gamma_rhs ArhsD = ixp.zerorank1() ErhsD = ixp.zerorank1() for i in range(DIM): ArhsD[i] = -ED[i] - psi_dD[i] ErhsD[i] = -LapAD[i] + Gamma_dD[i] psi_rhs = -Gamma Gamma_rhs = -Lappsi else: print( "Invalid choice of system: System_to_use must be either System_I or System_II" ) ED_dD = ixp.declarerank2("ED_dD", "nosym") global Cviolation Cviolation = gri.register_gridfunctions("AUX", ["Cviolation"]) Cviolation = sp.sympify(0) for i in range(DIM): for j in range(DIM): Cviolation += gammaUU[i][j] * ED_dD[j][i] for b in range(DIM): Cviolation -= gammaUU[i][j] * GammaUDD[b][i][j] * ED[b]
def VacuumMaxwellRHSs(): system = par.parval_from_str("Maxwell.InitialData::System_to_use") global ArhsU, ErhsU, C, psi_rhs #Step 0: Set the spatial dimension parameter to 3. DIM = par.parval_from_str("grid::DIM") rfm.reference_metric() # Register gridfunctions that are needed as input. # Declare the rank-1 indexed expressions E_{i}, A_{i}, # that are to be evolved in time. # Derivative variables like these must have an underscore # in them, so the finite difference module can parse # the variable name properly. # E^i EU = ixp.register_gridfunctions_for_single_rank1("EVOL", "EU") # A^i, _AU is unused _AU = ixp.register_gridfunctions_for_single_rank1("EVOL", "AU") # \psi is a scalar function that is time evolved # _psi is unused _psi = gri.register_gridfunctions("EVOL", ["psi"]) # \partial_i \psi psi_dD = ixp.declarerank1("psi_dD") # \partial_k ( A^i ) --> rank two tensor AU_dD = ixp.declarerank2("AU_dD", "nosym") # \partial_k partial_m ( A^i ) --> rank three tensor AU_dDD = ixp.declarerank3("AU_dDD", "sym12") EU_dD = ixp.declarerank2("EU_dD", "nosym") C = gri.register_gridfunctions("AUX", "DivE") # Equation 12 of https://arxiv.org/abs/gr-qc/0201051 C = EU_dD[0][0] + EU_dD[1][1] + EU_dD[2][2] if system == "System_I": print('Currently using ' + system + ' RHSs \n') # Define right-hand sides for the evolution. # Equations 10 and 11 from https://arxiv.org/abs/gr-qc/0201051 # \partial_t A^i = E^i - \partial_i \psi ArhsU = ixp.zerorank1() # \partial_t E^i = -\partial_j^2 A^i + \partial_j \partial_i A^j ErhsU = ixp.zerorank1() # Lorenz gauge condition # \partial_t \psi = -\partial_i A^i psi_rhs = sp.sympify(0) for i in range(DIM): ArhsU[i] = -EU[i] - psi_dD[i] psi_rhs -= AU_dD[i][i] for j in range(DIM): ErhsU[i] += -AU_dDD[i][j][j] + AU_dDD[j][j][i] elif system == "System_II": global Gamma_rhs, G print('Currently using ' + system + ' RHSs \n') # We inherit here all of the definitions from System I, above # Register the scalar auxiliary variable \Gamma Gamma = gri.register_gridfunctions("EVOL", ["Gamma"]) # Declare the ordinary gradient \partial_{i} \Gamma Gamma_dD = ixp.declarerank1("Gamma_dD") # partial_i \partial_j \psi psi_dDD = ixp.declarerank2("psi_dDD", "sym01") # Lorenz gauge condition psi_rhs = -Gamma # Define right-hand sides for the evolution. # Equations 10 and 14 https://arxiv.org/abs/gr-qc/0201051 ArhsU = ixp.zerorank1() ErhsU = ixp.zerorank1() # Equation 13 of https://arxiv.org/abs/gr-qc/0201051 # G = \Gamma - \partial_i A^i G = gri.register_gridfunctions("AUX", ["G"]) G = Gamma - AU_dD[0][0] - AU_dD[1][1] - AU_dD[2][2] # Equation 15 https://arxiv.org/abs/gr-qc/0201051 # Gamma_rhs = -DivE Gamma_rhs = sp.sympify(0) for i in range(DIM): ArhsU[i] = -EU[i] - psi_dD[i] ErhsU[i] = Gamma_dD[i] Gamma_rhs -= psi_dDD[i][i] for j in range(DIM): ErhsU[i] -= AU_dDD[i][j][j] else: print( "Invalid choice of system: System_to_use must be either System_I or System_II" )
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") ixp.register_gridfunctions_for_single_rank1("EVOL", "AD") BU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "BU") ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "BstaggerU") ValenciavU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "ValenciavU") gri.register_gridfunctions("EVOL", "psi6Phi") StildeD = ixp.register_gridfunctions_for_single_rank1("EVOL", "StildeD") gri.register_gridfunctions("AUXEVOL", "psi6_temp") gri.register_gridfunctions("AUXEVOL", "psi6center") gri.register_gridfunctions("AUXEVOL", "cmax_x") gri.register_gridfunctions("AUXEVOL", "cmin_x") gri.register_gridfunctions("AUXEVOL", "cmax_y") gri.register_gridfunctions("AUXEVOL", "cmin_y") gri.register_gridfunctions("AUXEVOL", "cmax_z") gri.register_gridfunctions("AUXEVOL", "cmin_z") subdir = "RHSs" stgsrc.GiRaFFE_NRPy_Source_Terms(os.path.join(out_dir, subdir)) # Declare this symbol: sqrt4pi = par.Cparameters("REAL", thismodule, "sqrt4pi", "sqrt(4.0*M_PI)") cmd.mkdir(os.path.join(out_dir, subdir)) 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) ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "Stilde_flux_HLLED") ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "Valenciav_rrU", DIM=3) ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "Valenciav_rlU", DIM=3) ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "Valenciav_lrU", DIM=3) ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "Valenciav_llU", DIM=3) ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "Bstagger_rU", DIM=3) ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "Bstagger_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, write_cmax_cmin=True) 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)) 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), loopopts="AllPoints,Read_xxs", rel_path_to_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), loopopts="AllPoints", rel_path_to_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: static const int VX=0,VY=1,VZ=2, BX_CENTER=3,BY_CENTER=4,BZ_CENTER=5,BX_STAGGER=6,BY_STAGGER=7,BZ_STAGGER=8, VXR=9,VYR=10,VZR=11,VXL=12,VYL=13,VZL=14; //<-- Be _sure_ to define MAXNUMVARS appropriately! const int NUM_RECONSTRUCT_GFS = 15; #define WORKAROUND_ENABLED // Include ALL functions needed for evolution #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 "RHSs/Lorenz_psi6phi_rhs__add_gauge_terms_to_A_i_rhs.h" #include "RHSs/A_i_rhs_no_gauge_terms.h" #include "A2B/compute_B_and_Bstagger_from_A.h" #include "RHSs/calculate_Stilde_flux_D0.h" #include "RHSs/calculate_Stilde_flux_D1.h" #include "RHSs/calculate_Stilde_flux_D2.h" #include "RHSs/calculate_Stilde_rhsD.h" #include "boundary_conditions/GiRaFFE_boundary_conditions.h" #include "C2P/GiRaFFE_NRPy_cons_to_prims.h" #include "C2P/GiRaFFE_NRPy_prims_to_cons.h" void workaround_Valencia_to_Drift_velocity(const paramstruct *params, REAL *vU0, const REAL *alpha, const REAL *betaU0, const REAL flux_dirn) { #include "set_Cparameters.h" // Converts Valencia 3-velocities to Drift 3-velocities for testing. The variable argument // vu0 is any Valencia 3-velocity component or reconstruction thereof. #pragma omp parallel for for (int i2 = 2*(flux_dirn==3);i2 < Nxx_plus_2NGHOSTS2-1*(flux_dirn==3);i2++) for (int i1 = 2*(flux_dirn==2);i1 < Nxx_plus_2NGHOSTS1-1*(flux_dirn==2);i1++) for (int i0 = 2*(flux_dirn==1);i0 < Nxx_plus_2NGHOSTS0-1*(flux_dirn==1);i0++) { int ii = IDX3S(i0,i1,i2); // Here, we modify the velocity in place. vU0[ii] = alpha[ii]*vU0[ii]-betaU0[ii]; } } void workaround_Drift_to_Valencia_velocity(const paramstruct *params, REAL *vU0, const REAL *alpha, const REAL *betaU0, const REAL flux_dirn) { #include "set_Cparameters.h" // Converts Drift 3-velocities to Valencia 3-velocities for testing. The variable argument // vu0 is any drift (i.e. IllinoisGRMHD's definition) 3-velocity component or reconstruction thereof. #pragma omp parallel for for (int i2 = 2*(flux_dirn==3);i2 < Nxx_plus_2NGHOSTS2-1*(flux_dirn==3);i2++) for (int i1 = 2*(flux_dirn==2);i1 < Nxx_plus_2NGHOSTS1-1*(flux_dirn==2);i1++) for (int i0 = 2*(flux_dirn==1);i0 < Nxx_plus_2NGHOSTS0-1*(flux_dirn==1);i0++) { int ii = IDX3S(i0,i1,i2); // Here, we modify the velocity in place. vU0[ii] = (vU0[ii]+betaU0[ii])/alpha[ii]; } } void GiRaFFE_NRPy_RHSs(const paramstruct *restrict params,REAL *restrict auxevol_gfs,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; } // 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*PSI6_TEMPGF; // Using dedicated temporary variables for the staggered grid REAL *psi6center = auxevol_gfs + Nxxp2NG012*PSI6CENTERGF; // Because the prescription requires more acrobatics. // 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++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*BSTAGGERU0GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*BSTAGGER_RU0GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*BSTAGGER_LU0GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*BSTAGGERU1GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*BSTAGGER_RU1GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*BSTAGGER_LU1GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*BSTAGGERU2GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*BSTAGGER_RU2GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*BSTAGGER_LU2GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RU0GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RRU0GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RLU0GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RU1GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RRU1GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RLU1GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RU2GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RRU2GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RLU2GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LU0GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LRU0GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LLU0GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LU1GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LRU1GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LLU1GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LU2GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LRU2GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LLU2GF; 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; } int flux_dirn; flux_dirn=0; interpolate_metric_gfs_to_cell_faces(params,auxevol_gfs,flux_dirn+1); // ftilde = 0 in GRFFE, since P=rho=0. /* There are two stories going on here: * 1) Computation of \partial_x on RHS of \partial_t {mhd_st_{x,y,z}}, * via PPM reconstruction onto (i-1/2,j,k), so that * \partial_y F = [ F(i+1/2,j,k) - F(i-1/2,j,k) ] / dx * 2) Computation of \partial_t A_i, where A_i are *staggered* gridfunctions, * where A_x is defined at (i,j+1/2,k+1/2), A_y at (i+1/2,j,k+1/2), etc. * Ai_rhs = \partial_t A_i = \epsilon_{ijk} \psi^{6} (v^j B^k - v^j B^k), * where \epsilon_{ijk} is the flat-space antisymmetric operator. * 2A) Az_rhs is defined at (i+1/2,j+1/2,k), and it depends on {Bx,By,vx,vy}, * so the trick is to reconstruct {Bx,By,vx,vy} cleverly to get to these * staggered points. For example: * 2Aa) vx and vy are at (i,j,k), and we reconstruct them to (i-1/2,j,k) below. After * this, we'll reconstruct again in the y-dir'n to get {vx,vy} at (i-1/2,j-1/2,k) * 2Ab) By_stagger is at (i,j+1/2,k), and we reconstruct below to (i-1/2,j+1/2,k). */ 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_CENTER; ww++; which_prims_to_reconstruct[ww]=BY_CENTER; ww++; which_prims_to_reconstruct[ww]=BZ_CENTER; ww++; which_prims_to_reconstruct[ww]=BY_STAGGER;ww++; num_prims_to_reconstruct=ww; // This function is housed in the file: "reconstruct_set_of_prims_PPM_GRFFE.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); //Right and left face values of BI_CENTER are used in GRFFE__S_i__flux computation (first to compute b^a). // Instead of reconstructing, we simply set B^x face values to be consistent with BX_STAGGER. #pragma omp parallel for for(int k=0;k<Nxx_plus_2NGHOSTS2;k++) for(int j=0;j<Nxx_plus_2NGHOSTS1;j++) for(int i=0;i<Nxx_plus_2NGHOSTS0;i++) { const int index=IDX3S(i,j,k), indexim1=IDX3S(i-1+(i==0),j,k); /* indexim1=0 when i=0 */ out_prims_r[BX_CENTER].gf[index]=out_prims_l[BX_CENTER].gf[index]=in_prims[BX_STAGGER].gf[indexim1]; } // Then add fluxes to RHS for hydro variables {vx,vy,vz}: // This function is housed in the file: "add_fluxes_and_source_terms_to_hydro_rhss.C" calculate_StildeD0_source_term(params,auxevol_gfs,rhs_gfs); calculate_Stilde_flux_D0(params,auxevol_gfs,rhs_gfs); calculate_Stilde_rhsD(flux_dirn+1,params,auxevol_gfs,rhs_gfs); // Note that we have already reconstructed vx and vy along the x-direction, // at (i-1/2,j,k). That result is stored in v{x,y}{r,l}. Bx_stagger data // are defined at (i+1/2,j,k). // Next goal: reconstruct Bx, vx and vy at (i+1/2,j+1/2,k). flux_dirn=1; // ftilde = 0 in GRFFE, since P=rho=0. // in_prims[{VXR,VXL,VYR,VYL}].gz_{lo,hi} ghostzones are set to all zeros, which // is incorrect. We fix this below. // [Note that this is a cheap operation, copying only 8 integers and a pointer.] in_prims[VXR]=out_prims_r[VX]; in_prims[VXL]=out_prims_l[VX]; in_prims[VYR]=out_prims_r[VY]; in_prims[VYL]=out_prims_l[VY]; /* There are two stories going on here: * 1) Computation of \partial_y on RHS of \partial_t {mhd_st_{x,y,z}}, * via PPM reconstruction onto (i,j-1/2,k), so that * \partial_y F = [ F(i,j+1/2,k) - F(i,j-1/2,k) ] / dy * 2) Computation of \partial_t A_i, where A_i are *staggered* gridfunctions, * where A_x is defined at (i,j+1/2,k+1/2), A_y at (i+1/2,j,k+1/2), etc. * Ai_rhs = \partial_t A_i = \epsilon_{ijk} \psi^{6} (v^j B^k - v^j B^k), * where \epsilon_{ijk} is the flat-space antisymmetric operator. * 2A) Az_rhs is defined at (i+1/2,j+1/2,k), and it depends on {Bx,By,vx,vy}, * so the trick is to reconstruct {Bx,By,vx,vy} cleverly to get to these * staggered points. For example: * 2Aa) VXR = [right-face of vx reconstructed along x-direction above] is at (i-1/2,j,k), * and we reconstruct it to (i-1/2,j-1/2,k) below. Similarly for {VXL,VYR,VYL} * 2Ab) Bx_stagger is at (i+1/2,j,k), and we reconstruct to (i+1/2,j-1/2,k) below * 2Ac) By_stagger is at (i-1/2,j+1/2,k) already for Az_rhs, from the previous step. * 2B) Ax_rhs is defined at (i,j+1/2,k+1/2), and it depends on {By,Bz,vy,vz}. * Again the trick is to reconstruct these onto these staggered points. * 2Ba) Bz_stagger is at (i,j,k+1/2), and we reconstruct to (i,j-1/2,k+1/2) below */ ww=0; // NOTE! The order of variable reconstruction is important here, // as we don't want to overwrite {vxr,vxl,vyr,vyl}! which_prims_to_reconstruct[ww]=VXR; ww++; which_prims_to_reconstruct[ww]=VYR; ww++; which_prims_to_reconstruct[ww]=VXL; ww++; which_prims_to_reconstruct[ww]=VYL; ww++; num_prims_to_reconstruct=ww; #ifdef WORKAROUND_ENABLED workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU0GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU0GF,flux_dirn+1); workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU1GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU1GF,flux_dirn+1); workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU0GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU0GF,flux_dirn+1); workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU1GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU1GF,flux_dirn+1); #endif /*WORKAROUND_ENABLED*/ // This function is housed in the file: "reconstruct_set_of_prims_PPM_GRFFE.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); #ifdef WORKAROUND_ENABLED workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU0GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU0GF,flux_dirn+1); workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU1GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU1GF,flux_dirn+1); workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU0GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU0GF,flux_dirn+1); workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU1GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU1GF,flux_dirn+1); #endif /*WORKAROUND_ENABLED*/ interpolate_metric_gfs_to_cell_faces(params,auxevol_gfs,flux_dirn+1); ww=0; // Reconstruct other primitives last! 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_CENTER; ww++; //which_prims_to_reconstruct[ww]=BY_CENTER; ww++; which_prims_to_reconstruct[ww]=BZ_CENTER; ww++; which_prims_to_reconstruct[ww]=BX_STAGGER;ww++; which_prims_to_reconstruct[ww]=BZ_STAGGER;ww++; num_prims_to_reconstruct=ww; // This function is housed in the file: "reconstruct_set_of_prims_PPM_GRFFE.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); //Right and left face values of BI_CENTER are used in GRFFE__S_i__flux computation (first to compute b^a). // Instead of reconstructing, we simply set B^y face values to be consistent with BY_STAGGER. #pragma omp parallel for for(int k=0;k<Nxx_plus_2NGHOSTS2;k++) for(int j=0;j<Nxx_plus_2NGHOSTS1;j++) for(int i=0;i<Nxx_plus_2NGHOSTS0;i++) { const int index=IDX3S(i,j,k), indexjm1=IDX3S(i,j-1+(j==0),k); /* indexjm1=0 when j=0 */ out_prims_r[BY_CENTER].gf[index]=out_prims_l[BY_CENTER].gf[index]=in_prims[BY_STAGGER].gf[indexjm1]; } // Then add fluxes to RHS for hydro variables {vx,vy,vz}: // This function is housed in the file: "add_fluxes_and_source_terms_to_hydro_rhss.C" calculate_StildeD1_source_term(params,auxevol_gfs,rhs_gfs); calculate_Stilde_flux_D1(params,auxevol_gfs,rhs_gfs); calculate_Stilde_rhsD(flux_dirn+1,params,auxevol_gfs,rhs_gfs); /***************************************** * COMPUTING RHS OF A_z, BOOKKEEPING NOTE: * We want to compute * \partial_t A_z - [gauge terms] = \psi^{6} (v^x B^y - v^y B^x). * A_z is defined at (i+1/2,j+1/2,k). * ========================== * Where defined | Variables * (i-1/2,j-1/2,k)| {vxrr,vxrl,vxlr,vxll,vyrr,vyrl,vylr,vyll} * (i+1/2,j-1/2,k)| {Bx_stagger_r,Bx_stagger_l} (see Table 1 in arXiv:1007.2848) * (i-1/2,j+1/2,k)| {By_stagger_r,By_stagger_l} (see Table 1 in arXiv:1007.2848) * (i,j,k) | {phi} * ========================== ******************************************/ // Interpolates to i+1/2 #define IPH(METRICm1,METRICp0,METRICp1,METRICp2) (-0.0625*((METRICm1) + (METRICp2)) + 0.5625*((METRICp0) + (METRICp1))) // Next compute sqrt(gamma)=psi^6 at (i+1/2,j+1/2,k): // To do so, we first compute the sqrt of the metric determinant at all points: #pragma omp parallel for for(int k=0;k<Nxx_plus_2NGHOSTS2;k++) for(int j=0;j<Nxx_plus_2NGHOSTS1;j++) for(int i=0;i<Nxx_plus_2NGHOSTS0;i++) { const int index=IDX3S(i,j,k); const REAL gxx = auxevol_gfs[IDX4ptS(GAMMADD00GF,index)]; const REAL gxy = auxevol_gfs[IDX4ptS(GAMMADD01GF,index)]; const REAL gxz = auxevol_gfs[IDX4ptS(GAMMADD02GF,index)]; const REAL gyy = auxevol_gfs[IDX4ptS(GAMMADD11GF,index)]; const REAL gyz = auxevol_gfs[IDX4ptS(GAMMADD12GF,index)]; const REAL gzz = auxevol_gfs[IDX4ptS(GAMMADD22GF,index)]; psi6center[index] = sqrt( gxx*gyy*gzz - gxx*gyz*gyz +2*gxy*gxz*gyz - gyy*gxz*gxz - gzz*gxy*gxy ); } #pragma omp parallel for for(int k=0;k<Nxx_plus_2NGHOSTS2;k++) for(int j=1;j<Nxx_plus_2NGHOSTS1-2;j++) for(int i=1;i<Nxx_plus_2NGHOSTS0-2;i++) { temporary[IDX3S(i,j,k)]= IPH(IPH(psi6center[IDX3S(i-1,j-1,k)],psi6center[IDX3S(i,j-1,k)],psi6center[IDX3S(i+1,j-1,k)],psi6center[IDX3S(i+2,j-1,k)]), IPH(psi6center[IDX3S(i-1,j ,k)],psi6center[IDX3S(i,j ,k)],psi6center[IDX3S(i+1,j ,k)],psi6center[IDX3S(i+2,j ,k)]), IPH(psi6center[IDX3S(i-1,j+1,k)],psi6center[IDX3S(i,j+1,k)],psi6center[IDX3S(i+1,j+1,k)],psi6center[IDX3S(i+2,j+1,k)]), IPH(psi6center[IDX3S(i-1,j+2,k)],psi6center[IDX3S(i,j+2,k)],psi6center[IDX3S(i+1,j+2,k)],psi6center[IDX3S(i+2,j+2,k)])); } int A_directionz=3; A_i_rhs_no_gauge_terms(A_directionz,params,out_prims_r,out_prims_l,temporary, auxevol_gfs+Nxxp2NG012*CMAX_XGF, auxevol_gfs+Nxxp2NG012*CMIN_XGF, auxevol_gfs+Nxxp2NG012*CMAX_YGF, auxevol_gfs+Nxxp2NG012*CMIN_YGF, rhs_gfs+Nxxp2NG012*AD2GF); // in_prims[{VYR,VYL,VZR,VZL}].gz_{lo,hi} ghostzones are not correct, so we fix // this below. // [Note that this is a cheap operation, copying only 8 integers and a pointer.] in_prims[VYR]=out_prims_r[VY]; in_prims[VYL]=out_prims_l[VY]; in_prims[VZR]=out_prims_r[VZ]; in_prims[VZL]=out_prims_l[VZ]; flux_dirn=2; // ftilde = 0 in GRFFE, since P=rho=0. /* There are two stories going on here: * 1) Single reconstruction to (i,j,k-1/2) for {vx,vy,vz,Bx,By,Bz} to compute * z-dir'n advection terms in \partial_t {mhd_st_{x,y,z}} at (i,j,k) * 2) Multiple reconstructions for *staggered* gridfunctions A_i: * \partial_t A_i = \epsilon_{ijk} \psi^{6} (v^j B^k - v^j B^k), * where \epsilon_{ijk} is the flat-space antisymmetric operator. * 2A) Ax_rhs is defined at (i,j+1/2,k+1/2), depends on v{y,z} and B{y,z} * 2Aa) v{y,z}{r,l} are at (i,j-1/2,k), so we reconstruct here to (i,j-1/2,k-1/2) * 2Ab) Bz_stagger{r,l} are at (i,j-1/2,k+1/2) already. * 2Ac) By_stagger is at (i,j+1/2,k), and below we reconstruct its value at (i,j+1/2,k-1/2) * 2B) Ay_rhs is defined at (i+1/2,j,k+1/2), depends on v{z,x} and B{z,x}. * 2Ba) v{x,z} are reconstructed to (i,j,k-1/2). Later we'll reconstruct again to (i-1/2,j,k-1/2). * 2Bb) Bz_stagger is at (i,j,k+1/2). Later we will reconstruct to (i-1/2,j,k+1/2). * 2Bc) Bx_stagger is at (i+1/2,j,k), and below we reconstruct its value at (i+1/2,j,k-1/2) */ ww=0; // NOTE! The order of variable reconstruction is important here, // as we don't want to overwrite {vxr,vxl,vyr,vyl}! which_prims_to_reconstruct[ww]=VYR; ww++; which_prims_to_reconstruct[ww]=VZR; ww++; which_prims_to_reconstruct[ww]=VYL; ww++; which_prims_to_reconstruct[ww]=VZL; ww++; num_prims_to_reconstruct=ww; #ifdef WORKAROUND_ENABLED workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU1GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU1GF,flux_dirn+1); workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU2GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU2GF,flux_dirn+1); workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU1GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU1GF,flux_dirn+1); workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU2GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU2GF,flux_dirn+1); #endif /*WORKAROUND_ENABLED*/ // This function is housed in the file: "reconstruct_set_of_prims_PPM_GRFFE.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); #ifdef WORKAROUND_ENABLED workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU1GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU1GF,flux_dirn+1); workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU2GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU2GF,flux_dirn+1); workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU1GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU1GF,flux_dirn+1); workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU2GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU2GF,flux_dirn+1); #endif /*WORKAROUND_ENABLED*/ interpolate_metric_gfs_to_cell_faces(params,auxevol_gfs,flux_dirn+1); // Reconstruct other primitives last! 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_CENTER; ww++; which_prims_to_reconstruct[ww]=BY_CENTER; ww++; //which_prims_to_reconstruct[ww]=BZ_CENTER; ww++; which_prims_to_reconstruct[ww]=BX_STAGGER; ww++; which_prims_to_reconstruct[ww]=BY_STAGGER; ww++; num_prims_to_reconstruct=ww; // This function is housed in the file: "reconstruct_set_of_prims_PPM_GRFFE.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); //Right and left face values of BI_CENTER are used in GRFFE__S_i__flux computation (first to compute b^a). // Instead of reconstructing, we simply set B^z face values to be consistent with BZ_STAGGER. #pragma omp parallel for for(int k=0;k<Nxx_plus_2NGHOSTS2;k++) for(int j=0;j<Nxx_plus_2NGHOSTS1;j++) for(int i=0;i<Nxx_plus_2NGHOSTS0;i++) { const int index=IDX3S(i,j,k), indexkm1=IDX3S(i,j,k-1+(k==0)); /* indexkm1=0 when k=0 */ out_prims_r[BZ_CENTER].gf[index]=out_prims_l[BZ_CENTER].gf[index]=in_prims[BZ_STAGGER].gf[indexkm1]; } // Then add fluxes to RHS for hydro variables {vx,vy,vz}: // This function is housed in the file: "add_fluxes_and_source_terms_to_hydro_rhss.C" calculate_StildeD2_source_term(params,auxevol_gfs,rhs_gfs); calculate_Stilde_flux_D2(params,auxevol_gfs,rhs_gfs); calculate_Stilde_rhsD(flux_dirn+1,params,auxevol_gfs,rhs_gfs); // in_prims[{VYR,VYL,VZR,VZL}].gz_{lo,hi} ghostzones are not set correcty. // We fix this below. // [Note that this is a cheap operation, copying only 8 integers and a pointer.] in_prims[VXR]=out_prims_r[VX]; in_prims[VZR]=out_prims_r[VZ]; in_prims[VXL]=out_prims_l[VX]; in_prims[VZL]=out_prims_l[VZ]; // FIXME: lines above seem to be inconsistent with lines below.... Possible bug, not major enough to affect evolutions though. in_prims[VZR].gz_lo[1]=in_prims[VZR].gz_hi[1]=0; in_prims[VXR].gz_lo[1]=in_prims[VXR].gz_hi[1]=0; in_prims[VZL].gz_lo[1]=in_prims[VZL].gz_hi[1]=0; in_prims[VXL].gz_lo[1]=in_prims[VXL].gz_hi[1]=0; /***************************************** * COMPUTING RHS OF A_x, BOOKKEEPING NOTE: * We want to compute * \partial_t A_x - [gauge terms] = \psi^{6} (v^y B^z - v^z B^y). * A_x is defined at (i,j+1/2,k+1/2). * ========================== * Where defined | Variables * (i,j-1/2,k-1/2)| {vyrr,vyrl,vylr,vyll,vzrr,vzrl,vzlr,vzll} * (i,j+1/2,k-1/2)| {By_stagger_r,By_stagger_l} (see Table 1 in arXiv:1007.2848) * (i,j-1/2,k+1/2)| {Bz_stagger_r,Bz_stagger_l} (see Table 1 in arXiv:1007.2848) * (i,j,k) | {phi} * ========================== ******************************************/ // Next compute phi at (i,j+1/2,k+1/2): #pragma omp parallel for for(int k=1;k<Nxx_plus_2NGHOSTS2-2;k++) for(int j=1;j<Nxx_plus_2NGHOSTS1-2;j++) for(int i=0;i<Nxx_plus_2NGHOSTS0;i++) { temporary[IDX3S(i,j,k)]= IPH(IPH(psi6center[IDX3S(i,j-1,k-1)],psi6center[IDX3S(i,j,k-1)],psi6center[IDX3S(i,j+1,k-1)],psi6center[IDX3S(i,j+2,k-1)]), IPH(psi6center[IDX3S(i,j-1,k )],psi6center[IDX3S(i,j,k )],psi6center[IDX3S(i,j+1,k )],psi6center[IDX3S(i,j+2,k )]), IPH(psi6center[IDX3S(i,j-1,k+1)],psi6center[IDX3S(i,j,k+1)],psi6center[IDX3S(i,j+1,k+1)],psi6center[IDX3S(i,j+2,k+1)]), IPH(psi6center[IDX3S(i,j-1,k+2)],psi6center[IDX3S(i,j,k+2)],psi6center[IDX3S(i,j+1,k+2)],psi6center[IDX3S(i,j+2,k+2)])); } int A_directionx=1; A_i_rhs_no_gauge_terms(A_directionx,params,out_prims_r,out_prims_l,temporary, auxevol_gfs+Nxxp2NG012*CMAX_YGF, auxevol_gfs+Nxxp2NG012*CMIN_YGF, auxevol_gfs+Nxxp2NG012*CMAX_ZGF, auxevol_gfs+Nxxp2NG012*CMIN_ZGF, rhs_gfs+Nxxp2NG012*AD0GF); // We reprise flux_dirn=0 to finish up computations of Ai_rhs's! flux_dirn=0; // ftilde = 0 in GRFFE, since P=rho=0. ww=0; // NOTE! The order of variable reconstruction is important here, // as we don't want to overwrite {vxr,vxl,vyr,vyl}! which_prims_to_reconstruct[ww]=VXR; ww++; which_prims_to_reconstruct[ww]=VZR; ww++; which_prims_to_reconstruct[ww]=VXL; ww++; which_prims_to_reconstruct[ww]=VZL; ww++; which_prims_to_reconstruct[ww]=BZ_STAGGER;ww++; num_prims_to_reconstruct=ww; #ifdef WORKAROUND_ENABLED workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU0GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU0GF,flux_dirn+1); workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU2GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU2GF,flux_dirn+1); workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU0GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU0GF,flux_dirn+1); workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU2GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU2GF,flux_dirn+1); #endif /*WORKAROUND_ENABLED*/ // This function is housed in the file: "reconstruct_set_of_prims_PPM_GRFFE.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); #ifdef WORKAROUND_ENABLED workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU0GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU0GF,flux_dirn+1); workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU2GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU2GF,flux_dirn+1); workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU0GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU0GF,flux_dirn+1); workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU2GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU2GF,flux_dirn+1); #endif /*WORKAROUND_ENABLED*/ /***************************************** * COMPUTING RHS OF A_y, BOOKKEEPING NOTE: * We want to compute * \partial_t A_y - [gauge terms] = \psi^{6} (v^z B^x - v^x B^z). * A_y is defined at (i+1/2,j,k+1/2). * ========================== * Where defined | Variables * (i-1/2,j,k-1/2)| {vyrr,vyrl,vylr,vyll,vzrr,vzrl,vzlr,vzll} * (i+1/2,j,k-1/2)| {By_stagger_r,By_stagger_l} (see Table 1 in arXiv:1007.2848) * (i-1/2,j,k+1/2)| {Bz_stagger_r,Bz_stagger_l} (see Table 1 in arXiv:1007.2848) * (i,j,k) | {phi} * ========================== ******************************************/ // Next compute phi at (i+1/2,j,k+1/2): #pragma omp parallel for for(int k=1;k<Nxx_plus_2NGHOSTS2-2;k++) for(int j=0;j<Nxx_plus_2NGHOSTS1;j++) for(int i=1;i<Nxx_plus_2NGHOSTS0-2;i++) { temporary[IDX3S(i,j,k)]= IPH(IPH(psi6center[IDX3S(i-1,j,k-1)],psi6center[IDX3S(i,j,k-1)],psi6center[IDX3S(i+1,j,k-1)],psi6center[IDX3S(i+2,j,k-1)]), IPH(psi6center[IDX3S(i-1,j,k )],psi6center[IDX3S(i,j,k )],psi6center[IDX3S(i+1,j,k )],psi6center[IDX3S(i+2,j,k )]), IPH(psi6center[IDX3S(i-1,j,k+1)],psi6center[IDX3S(i,j,k+1)],psi6center[IDX3S(i+1,j,k+1)],psi6center[IDX3S(i+2,j,k+1)]), IPH(psi6center[IDX3S(i-1,j,k+2)],psi6center[IDX3S(i,j,k+2)],psi6center[IDX3S(i+1,j,k+2)],psi6center[IDX3S(i+2,j,k+2)])); } int A_directiony=2; A_i_rhs_no_gauge_terms(A_directiony,params,out_prims_r,out_prims_l,temporary, auxevol_gfs+Nxxp2NG012*CMAX_ZGF, auxevol_gfs+Nxxp2NG012*CMIN_ZGF, auxevol_gfs+Nxxp2NG012*CMAX_XGF, auxevol_gfs+Nxxp2NG012*CMIN_XGF, rhs_gfs+Nxxp2NG012*AD1GF); // Next compute psi6phi_rhs, and add gauge terms to A_i_rhs terms! // Note that in the following function, we don't bother with reconstruction, instead interpolating. // We need A^i, but only have A_i. So we add gtupij to the list of input variables. REAL *interp_vars[MAXNUMINTERP]; ww=0; interp_vars[ww]=auxevol_gfs+Nxxp2NG012*BETAU0GF; ww++; interp_vars[ww]=auxevol_gfs+Nxxp2NG012*BETAU1GF; ww++; interp_vars[ww]=auxevol_gfs+Nxxp2NG012*BETAU2GF; ww++; interp_vars[ww]=auxevol_gfs+Nxxp2NG012*GAMMADD00GF; ww++; interp_vars[ww]=auxevol_gfs+Nxxp2NG012*GAMMADD01GF; ww++; interp_vars[ww]=auxevol_gfs+Nxxp2NG012*GAMMADD02GF; ww++; interp_vars[ww]=auxevol_gfs+Nxxp2NG012*GAMMADD11GF; ww++; interp_vars[ww]=auxevol_gfs+Nxxp2NG012*GAMMADD12GF; ww++; interp_vars[ww]=auxevol_gfs+Nxxp2NG012*GAMMADD22GF; ww++; interp_vars[ww]=temporary;ww++; interp_vars[ww]=auxevol_gfs+Nxxp2NG012*ALPHAGF; ww++; interp_vars[ww]=in_gfs+Nxxp2NG012*AD0GF; ww++; interp_vars[ww]=in_gfs+Nxxp2NG012*AD1GF; ww++; interp_vars[ww]=in_gfs+Nxxp2NG012*AD2GF; ww++; const int max_num_interp_variables=ww; // if(max_num_interp_variables>MAXNUMINTERP) {CCTK_VError(VERR_DEF_PARAMS,"Error: Didn't allocate enough space for interp_vars[]."); } // We are FINISHED with v{x,y,z}{r,l} and P{r,l} so we use these 8 gridfunctions' worth of space as temp storage. Lorenz_psi6phi_rhs__add_gauge_terms_to_A_i_rhs(params,interp_vars, in_gfs+Nxxp2NG012*PSI6PHIGF, auxevol_gfs+Nxxp2NG012*VALENCIAV_RU0GF, // WARNING: auxevol_gfs+Nxxp2NG012*VALENCIAV_RU1GF, // ALL VARIABLES auxevol_gfs+Nxxp2NG012*VALENCIAV_RU2GF, // ON THESE LINES auxevol_gfs+Nxxp2NG012*VALENCIAV_LU0GF, // ARE OVERWRITTEN auxevol_gfs+Nxxp2NG012*VALENCIAV_LU1GF, // FOR TEMP STORAGE auxevol_gfs+Nxxp2NG012*VALENCIAV_LU2GF, // . auxevol_gfs+Nxxp2NG012*VALENCIAV_RRU0GF, // . auxevol_gfs+Nxxp2NG012*VALENCIAV_RLU0GF, // . rhs_gfs+Nxxp2NG012*PSI6PHIGF, rhs_gfs+Nxxp2NG012*AD0GF, rhs_gfs+Nxxp2NG012*AD1GF, rhs_gfs+Nxxp2NG012*AD2GF); /*#pragma omp parallel for for(int k=0;k<Nxx_plus_2NGHOSTS2;k++) for(int j=0;j<Nxx_plus_2NGHOSTS1;j++) for(int i=0;i<Nxx_plus_2NGHOSTS0;i++) { REAL x = xx[0][i]; REAL y = xx[1][j]; REAL z = xx[2][k]; if(sqrt(x*x+y*y+z*z)<min_radius_inside_of_which_conserv_to_prims_FFE_and_FFE_evolution_is_DISABLED) { rhs_gfs[IDX4S(STILDED0GF,i,j,k)] = 0.0; rhs_gfs[IDX4S(STILDED1GF,i,j,k)] = 0.0; rhs_gfs[IDX4S(STILDED2GF,i,j,k)] = 0.0; rhs_gfs[IDX4S(PSI6PHIGF,i,j,k)] = 0.0; rhs_gfs[IDX4S(AD0GF,i,j,k)] = 0.0; rhs_gfs[IDX4S(AD1GF,i,j,k)] = 0.0; rhs_gfs[IDX4S(AD2GF,i,j,k)] = 0.0; } }*/ } void GiRaFFE_NRPy_post_step(const paramstruct *restrict params,REAL *xx[3],REAL *restrict auxevol_gfs,REAL *restrict evol_gfs,const int n) { #include "set_Cparameters.h" // First, apply BCs to AD and psi6Phi. Then calculate BU from AD apply_bcs_potential(params,evol_gfs); const int Nxxp2NG012 = Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2; GiRaFFE_compute_B_and_Bstagger_from_A(params, auxevol_gfs+Nxxp2NG012*GAMMADD00GF, auxevol_gfs+Nxxp2NG012*GAMMADD01GF, auxevol_gfs+Nxxp2NG012*GAMMADD02GF, auxevol_gfs+Nxxp2NG012*GAMMADD11GF, auxevol_gfs+Nxxp2NG012*GAMMADD12GF, auxevol_gfs+Nxxp2NG012*GAMMADD22GF, auxevol_gfs + Nxxp2NG012*PSI6_TEMPGF, /* Temporary storage */ evol_gfs+Nxxp2NG012*AD0GF, evol_gfs+Nxxp2NG012*AD1GF, evol_gfs+Nxxp2NG012*AD2GF, auxevol_gfs+Nxxp2NG012*BU0GF, auxevol_gfs+Nxxp2NG012*BU1GF, auxevol_gfs+Nxxp2NG012*BU2GF, auxevol_gfs+Nxxp2NG012*BSTAGGERU0GF, auxevol_gfs+Nxxp2NG012*BSTAGGERU1GF, auxevol_gfs+Nxxp2NG012*BSTAGGERU2GF); //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_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 VacuumMaxwellRHSs_rescaled(): global erhsU, arhsU, psi_rhs, Gamma_rhs, C, G, EU_Cart, AU_Cart #Step 0: Set the spatial dimension parameter to 3. par.set_parval_from_str("grid::DIM", 3) DIM = par.parval_from_str("grid::DIM") # Set reference metric related quantities rfm.reference_metric() # Register gridfunctions that are needed as input. # Declare the rank-1 indexed expressions E^{i}, A^{i}, # and \partial^{i} \psi, that are to be evolved in time. # Derivative variables like these must have an underscore # in them, so the finite difference module can parse # the variable name properly. # e^i eU = ixp.register_gridfunctions_for_single_rank1("EVOL", "eU") # \partial_k ( E^i ) --> rank two tensor eU_dD = ixp.declarerank2("eU_dD", "nosym") # a^i aU = ixp.register_gridfunctions_for_single_rank1("EVOL", "aU") # \partial_k ( a^i ) --> rank two tensor aU_dD = ixp.declarerank2("aU_dD", "nosym") # \partial_k partial_m ( a^i ) --> rank three tensor aU_dDD = ixp.declarerank3("aU_dDD", "sym12") # \psi is a scalar function that is time evolved # psi is unused here _psi = gri.register_gridfunctions("EVOL", ["psi"]) # \Gamma is a scalar function that is time evolved Gamma = gri.register_gridfunctions("EVOL", ["Gamma"]) # \partial_i \psi psi_dD = ixp.declarerank1("psi_dD") # \partial_i \Gamma Gamma_dD = ixp.declarerank1("Gamma_dD") # partial_i \partial_j \psi psi_dDD = ixp.declarerank2("psi_dDD", "sym01") ghatUU = rfm.ghatUU GammahatUDD = rfm.GammahatUDD GammahatUDDdD = rfm.GammahatUDDdD ReU = rfm.ReU ReUdD = rfm.ReUdD ReUdDD = rfm.ReUdDD # \partial_t a^i = -e^i - \frac{\hat{g}^{ij}\partial_j \varphi}{\text{ReU}[i]} arhsU = ixp.zerorank1() for i in range(DIM): arhsU[i] -= eU[i] for j in range(DIM): arhsU[i] -= (ghatUU[i][j] * psi_dD[j]) / ReU[i] # A^i AU = ixp.zerorank1() # \partial_k ( A^i ) --> rank two tensor AU_dD = ixp.zerorank2() # \partial_k partial_m ( A^i ) --> rank three tensor AU_dDD = ixp.zerorank3() for i in range(DIM): AU[i] = aU[i] * ReU[i] for j in range(DIM): AU_dD[i][j] = aU_dD[i][j] * ReU[i] + aU[i] * ReUdD[i][j] for k in range(DIM): AU_dDD[i][j][k] = aU_dDD[i][j][k]*ReU[i] + aU_dD[i][j]*ReUdD[i][k] +\ aU_dD[i][k]*ReUdD[i][j] + aU[i]*ReUdDD[i][j][k] # Term 1 = \hat{g}^{ij}\partial_j \Gamma Term1U = ixp.zerorank1() for i in range(DIM): for j in range(DIM): Term1U[i] += ghatUU[i][j] * Gamma_dD[j] # Term 2: A^i_{,kj} Term2UDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): Term2UDD[i][j][k] += AU_dDD[i][k][j] # Term 3: \hat{\Gamma}^i_{mk,j} A^m + \hat{\Gamma}^i_{mk} A^m_{,j} # + \hat{\Gamma}^i_{dj}A^d_{,k} - \hat{\Gamma}^d_{kj} A^i_{,d} Term3UDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): for m in range(DIM): Term3UDD[i][j][k] += GammahatUDDdD[i][m][k][j]*AU[m] \ + GammahatUDD[i][m][k]*AU_dD[m][j] \ + GammahatUDD[i][m][j]*AU_dD[m][k] \ - GammahatUDD[m][k][j]*AU_dD[i][m] # Term 4: \hat{\Gamma}^i_{dj}\hat{\Gamma}^d_{mk} A^m - # \hat{\Gamma}^d_{kj} \hat{\Gamma}^i_{md} A^m Term4UDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): for m in range(DIM): for d in range(DIM): Term4UDD[i][j][k] += ( GammahatUDD[i][d][j]*GammahatUDD[d][m][k] \ -GammahatUDD[d][k][j]*GammahatUDD[i][m][d])*AU[m] # \partial_t E^i = \hat{g}^{ij}\partial_j \Gamma - \hat{\gamma}^{jk}* # (A^i_{,kj} # + \hat{\Gamma}^i_{mk,j} A^m + \hat{\Gamma}^i_{mk} A^m_{,j} # + \hat{\Gamma}^i_{dj} A^d_{,k} - \hat{\Gamma}^d_{kj} A^i_{,d} # + \hat{\Gamma}^i_{dj}\hat{\Gamma}^d_{mk} A^m # - \hat{\Gamma}^d_{kj} \hat{\Gamma}^i_{md} A^m) ErhsU = ixp.zerorank1() for i in range(DIM): ErhsU[i] += Term1U[i] for j in range(DIM): for k in range(DIM): ErhsU[i] -= ghatUU[j][k] * ( Term2UDD[i][j][k] + Term3UDD[i][j][k] + Term4UDD[i][j][k]) erhsU = ixp.zerorank1() for i in range(DIM): erhsU[i] = ErhsU[i] / ReU[i] # \partial_t \Gamma = -\hat{g}^{ij} (\partial_i \partial_j \varphi - # \hat{\Gamma}^k_{ji} \partial_k \varphi) Gamma_rhs = sp.sympify(0) for i in range(DIM): for j in range(DIM): Gamma_rhs -= ghatUU[i][j] * psi_dDD[i][j] for k in range(DIM): Gamma_rhs += ghatUU[i][j] * GammahatUDD[k][j][i] * psi_dD[k] # \partial_t \varphi = -\Gamma psi_rhs = -Gamma # \mathcal{G} \equiv \Gamma - \partial_i A^i + \hat{\Gamma}^i_{ji} A^j G = Gamma for i in range(DIM): G -= AU_dD[i][i] for j in range(DIM): G += GammahatUDD[i][j][i] * AU[j] # E^i EU = ixp.zerorank1() EU_dD = ixp.zerorank2() for i in range(DIM): EU[i] = eU[i] * ReU[i] for j in range(DIM): EU_dD[i][j] = eU_dD[i][j] * ReU[i] + eU[i] * ReUdD[i][j] C = sp.sympify(0) for i in range(DIM): C += EU_dD[i][i] for j in range(DIM): C += GammahatUDD[i][j][i] * EU[j] def Convert_to_Cartesian_basis(VU): # Coordinate transformation from original basis to Cartesian rfm.reference_metric() VU_Cart = ixp.zerorank1() Jac_dxCartU_dxOrigD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): Jac_dxCartU_dxOrigD[i][j] = sp.diff(rfm.xxCart[i], rfm.xx[j]) for i in range(DIM): for j in range(DIM): VU_Cart[i] += Jac_dxCartU_dxOrigD[i][j] * VU[j] return VU_Cart AU_Cart = Convert_to_Cartesian_basis(AU) EU_Cart = Convert_to_Cartesian_basis(EU)
def driver_C_codes(Csrcdict, ThornName, rhs_list, evol_gfs_list, aux_gfs_list, auxevol_gfs_list, LapseCondition="OnePlusLog", enable_stress_energy_source_terms=False): # First the ETK banner code, proudly showing the NRPy+ banner import NRPy_logo as logo outstr = """ #include <stdio.h> void BaikalETK_Banner() { """ logostr = logo.print_logo(print_to_stdout=False) outstr += "printf(\"BaikalETK: another Einstein Toolkit thorn generated by\\n\");\n" for line in logostr.splitlines(): outstr += " printf(\"" + line + "\\n\");\n" outstr += "}\n" # Finally add C code string to dictionaries (Python dictionaries are immutable) # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("Banner.c")] = outstr.replace( "BaikalETK", ThornName) # Then the RegisterSlicing() function, needed for other ETK thorns outstr = """ #include "cctk.h" #include "Slicing.h" int BaikalETK_RegisterSlicing (void) { Einstein_RegisterSlicing ("BaikalETK"); return 0; }""" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "RegisterSlicing.c")] = outstr.replace("BaikalETK", ThornName) # Next BaikalETK_Symmetry_registration(): Register symmetries full_gfs_list = [] full_gfs_list.extend(evol_gfs_list) full_gfs_list.extend(auxevol_gfs_list) full_gfs_list.extend(aux_gfs_list) outstr = """ #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" #include "Symmetry.h" void BaikalETK_Symmetry_registration(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; // Stores gridfunction parity across x=0, y=0, and z=0 planes, respectively int sym[3]; // Next register parities for each gridfunction based on its name // (to ensure this algorithm is robust, gridfunctions with integers // in their base names are forbidden in NRPy+). """ outstr += "" for gf in full_gfs_list: # Do not add T4UU gridfunctions if enable_stress_energy_source_terms==False: if not (enable_stress_energy_source_terms == False and "T4UU" in gf): outstr += """ // Default to scalar symmetry: sym[0] = 1; sym[1] = 1; sym[2] = 1; // Now modify sym[0], sym[1], and/or sym[2] as needed // to account for gridfunction parity across // x=0, y=0, and/or z=0 planes, respectively """ # If gridfunction name does not end in a digit, by NRPy+ syntax, it must be a scalar if gf[len(gf) - 1].isdigit() == False: pass # Scalar = default elif len(gf) > 2: # Rank-1 indexed expression (e.g., vector) if gf[len(gf) - 2].isdigit() == False: if int(gf[-1]) > 2: print("Error: Found invalid gridfunction name: " + gf) sys.exit(1) symidx = gf[-1] outstr += " sym[" + symidx + "] = -1;\n" # Rank-2 indexed expression elif gf[len(gf) - 2].isdigit() == True: if len(gf) > 3 and gf[len(gf) - 3].isdigit() == True: print("Error: Found a Rank-3 or above gridfunction: " + gf + ", which is at the moment unsupported.") print("It should be easy to support this if desired.") sys.exit(1) symidx0 = gf[-2] outstr += " sym[" + symidx0 + "] *= -1;\n" symidx1 = gf[-1] outstr += " sym[" + symidx1 + "] *= -1;\n" else: print( "Don't know how you got this far with a gridfunction named " + gf + ", but I'll take no more of this nonsense.") print( " Please follow best-practices and rename your gridfunction to be more descriptive" ) sys.exit(1) outstr += " SetCartSymVN(cctkGH, sym, \"BaikalETK::" + gf + "\");\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("Symmetry_registration_oldCartGrid3D.c")] = \ outstr.replace("BaikalETK",ThornName) # Next set RHSs to zero outstr = """ #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" #include "Symmetry.h" void BaikalETK_zero_rhss(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; """ set_rhss_to_zero = "" for gf in rhs_list: set_rhss_to_zero += gf + "[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)] = 0.0;\n" outstr += lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], ["cctk_lsh[2]", "cctk_lsh[1]", "cctk_lsh[0]"], ["1", "1", "1"], [ "#pragma omp parallel for", "", "", ], "", set_rhss_to_zero) outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("zero_rhss.c")] = outstr.replace( "BaikalETK", ThornName) # Next registration with the Method of Lines thorn outstr = """ //-------------------------------------------------------------------------- // Register with the Method of Lines time stepper // (MoL thorn, found in arrangements/CactusBase/MoL) // MoL documentation located in arrangements/CactusBase/MoL/doc //-------------------------------------------------------------------------- #include <stdio.h> #include "cctk.h" #include "cctk_Parameters.h" #include "cctk_Arguments.h" #include "Symmetry.h" void BaikalETK_MoL_registration(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; CCTK_INT ierr = 0, group, rhs; // Register evolution & RHS gridfunction groups with MoL, so it knows group = CCTK_GroupIndex("BaikalETK::evol_variables"); rhs = CCTK_GroupIndex("BaikalETK::evol_variables_rhs"); ierr += MoLRegisterEvolvedGroup(group, rhs); if (ierr) CCTK_ERROR("Problems registering with MoL"); } """ # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "MoL_registration.c")] = outstr.replace("BaikalETK", ThornName) # Next register with the boundary conditions thorns. # PART 1: Set BC type to "none" for all variables # Since we choose NewRad boundary conditions, we must register all # gridfunctions to have boundary type "none". This is because # NewRad is seen by the rest of the Toolkit as a modification to the # RHSs. # This code is based on Kranc's McLachlan/ML_BSSN/src/Boundaries.cc code. outstr = """ #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" #include "cctk_Faces.h" #include "util_Table.h" #include "Symmetry.h" // Set `none` boundary conditions on BSSN RHSs, as these are set via NewRad. void BaikalETK_BoundaryConditions_evolved_gfs(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; CCTK_INT ierr CCTK_ATTRIBUTE_UNUSED = 0; """ for gf in evol_gfs_list: outstr += """ ierr = Boundary_SelectVarForBC(cctkGH, CCTK_ALL_FACES, 1, -1, "BaikalETK::""" + gf + """", "none"); if (ierr < 0) CCTK_ERROR("Failed to register BC for BaikalETK::""" + gf + """!"); """ outstr += """ } // Set `flat` boundary conditions on BSSN constraints, similar to what Lean does. void BaikalETK_BoundaryConditions_aux_gfs(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; CCTK_INT ierr CCTK_ATTRIBUTE_UNUSED = 0; """ for gf in aux_gfs_list: outstr += """ ierr = Boundary_SelectVarForBC(cctkGH, CCTK_ALL_FACES, cctk_nghostzones[0], -1, "BaikalETK::""" + gf + """", "flat"); if (ierr < 0) CCTK_ERROR("Failed to register BC for BaikalETK::""" + gf + """!"); """ outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "BoundaryConditions.c")] = outstr.replace("BaikalETK", ThornName) # PART 2: Set C code for calling NewRad BCs # As explained in lean_public/LeanBSSNMoL/src/calc_bssn_rhs.F90, # the function NewRad_Apply takes the following arguments: # NewRad_Apply(cctkGH, var, rhs, var0, v0, radpower), # which implement the boundary condition: # var = var_at_infinite_r + u(r-var_char_speed*t)/r^var_radpower # Obviously for var_radpower>0, var_at_infinite_r is the value of # the variable at r->infinity. var_char_speed is the propagation # speed at the outer boundary, and var_radpower is the radial # falloff rate. outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_NewRad(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; """ for gf in evol_gfs_list: var_at_infinite_r = "0.0" var_char_speed = "1.0" var_radpower = "1.0" if gf == "alpha": var_at_infinite_r = "1.0" if LapseCondition == "OnePlusLog": var_char_speed = "sqrt(2.0)" else: pass # 1.0 (default) is fine if "aDD" in gf or "trK" in gf: # consistent with Lean code. var_radpower = "2.0" outstr += " NewRad_Apply(cctkGH, " + gf + ", " + gf.replace( "GF", "" ) + "_rhsGF, " + var_at_infinite_r + ", " + var_char_speed + ", " + var_radpower + ");\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "BoundaryCondition_NewRad.c")] = outstr.replace( "BaikalETK", ThornName) # First we convert from ADM to BSSN, as is required to convert initial data # (given using) ADM quantities, to the BSSN evolved variables import BSSN.ADM_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear as atob IDhDD,IDaDD,IDtrK,IDvetU,IDbetU,IDalpha,IDcf,IDlambdaU = \ atob.Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear("Cartesian","DoNotOutputADMInputFunction",os.path.join(ThornName,"src")) # Store the original list of registered gridfunctions; we'll want to unregister # all the *SphorCart* gridfunctions after we're finished with them below. orig_glb_gridfcs_list = [] for gf in gri.glb_gridfcs_list: orig_glb_gridfcs_list.append(gf) alphaSphorCart = gri.register_gridfunctions("AUXEVOL", "alphaSphorCart") betaSphorCartU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "betaSphorCartU") BSphorCartU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "BSphorCartU") gammaSphorCartDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "gammaSphorCartDD", "sym01") KSphorCartDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "KSphorCartDD", "sym01") # ADM to BSSN conversion, used for converting ADM initial data into a form readable by this thorn. # ADM to BSSN, Part 1: Set up function call and pointers to ADM gridfunctions outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_ADM_to_BSSN(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; CCTK_REAL *alphaSphorCartGF = alp; """ # It's ugly if we output code in the following ordering, so we'll first # output to a string and then sort the string to beautify the code a bit. outstrtmp = [] for i in range(3): outstrtmp.append(" CCTK_REAL *betaSphorCartU" + str(i) + "GF = beta" + chr(ord('x') + i) + ";\n") outstrtmp.append(" CCTK_REAL *BSphorCartU" + str(i) + "GF = dtbeta" + chr(ord('x') + i) + ";\n") for j in range(i, 3): outstrtmp.append(" CCTK_REAL *gammaSphorCartDD" + str(i) + str(j) + "GF = g" + chr(ord('x') + i) + chr(ord('x') + j) + ";\n") outstrtmp.append(" CCTK_REAL *KSphorCartDD" + str(i) + str(j) + "GF = k" + chr(ord('x') + i) + chr(ord('x') + j) + ";\n") outstrtmp.sort() for line in outstrtmp: outstr += line # ADM to BSSN, Part 2: Set up ADM to BSSN conversions for BSSN gridfunctions that do not require # finite-difference derivatives (i.e., all gridfunctions except lambda^i (=Gamma^i # in non-covariant BSSN)): # h_{ij}, a_{ij}, trK, vet^i=beta^i,bet^i=B^i, cf (conformal factor), and alpha all_but_lambdaU_expressions = [ lhrh(lhs=gri.gfaccess("in_gfs", "hDD00"), rhs=IDhDD[0][0]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD01"), rhs=IDhDD[0][1]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD02"), rhs=IDhDD[0][2]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD11"), rhs=IDhDD[1][1]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD12"), rhs=IDhDD[1][2]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD22"), rhs=IDhDD[2][2]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD00"), rhs=IDaDD[0][0]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD01"), rhs=IDaDD[0][1]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD02"), rhs=IDaDD[0][2]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD11"), rhs=IDaDD[1][1]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD12"), rhs=IDaDD[1][2]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD22"), rhs=IDaDD[2][2]), lhrh(lhs=gri.gfaccess("in_gfs", "trK"), rhs=IDtrK), lhrh(lhs=gri.gfaccess("in_gfs", "vetU0"), rhs=IDvetU[0]), lhrh(lhs=gri.gfaccess("in_gfs", "vetU1"), rhs=IDvetU[1]), lhrh(lhs=gri.gfaccess("in_gfs", "vetU2"), rhs=IDvetU[2]), lhrh(lhs=gri.gfaccess("in_gfs", "betU0"), rhs=IDbetU[0]), lhrh(lhs=gri.gfaccess("in_gfs", "betU1"), rhs=IDbetU[1]), lhrh(lhs=gri.gfaccess("in_gfs", "betU2"), rhs=IDbetU[2]), lhrh(lhs=gri.gfaccess("in_gfs", "alpha"), rhs=IDalpha), lhrh(lhs=gri.gfaccess("in_gfs", "cf"), rhs=IDcf) ] outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" all_but_lambdaU_outC = fin.FD_outputC("returnstring", all_but_lambdaU_expressions, outCparams) outstr += lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], ["cctk_lsh[2]", "cctk_lsh[1]", "cctk_lsh[0]"], ["1", "1", "1"], ["#pragma omp parallel for", "", ""], " ", all_but_lambdaU_outC) # ADM to BSSN, Part 3: Set up ADM to BSSN conversions for BSSN gridfunctions defined from # finite-difference derivatives: lambda^i, which is Gamma^i in non-covariant BSSN): outstr += """ const CCTK_REAL invdx0 = 1.0/CCTK_DELTA_SPACE(0); const CCTK_REAL invdx1 = 1.0/CCTK_DELTA_SPACE(1); const CCTK_REAL invdx2 = 1.0/CCTK_DELTA_SPACE(2); """ path = os.path.join(ThornName, "src") BSSN_RHS_FD_orders_output = [] for root, dirs, files in os.walk(path): for file in files: if "BSSN_RHSs_FD_order" in file: array = file.replace(".", "_").split("_") BSSN_RHS_FD_orders_output.append(int(array[4])) for current_FD_order in BSSN_RHS_FD_orders_output: # Store original finite-differencing order: orig_FD_order = par.parval_from_str( "finite_difference::FD_CENTDERIVS_ORDER") # Set new finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", current_FD_order) outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" lambdaU_expressions = [ lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU0"), rhs=IDlambdaU[0]), lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU1"), rhs=IDlambdaU[1]), lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU2"), rhs=IDlambdaU[2]) ] lambdaU_expressions_FDout = fin.FD_outputC("returnstring", lambdaU_expressions, outCparams) lambdafile = "ADM_to_BSSN__compute_lambdaU_FD_order_" + str( current_FD_order) + ".h" with open(os.path.join(ThornName, "src", lambdafile), "w") as file: file.write( lp.loop(["i2", "i1", "i0"], [ "cctk_nghostzones[2]", "cctk_nghostzones[1]", "cctk_nghostzones[0]" ], [ "cctk_lsh[2]-cctk_nghostzones[2]", "cctk_lsh[1]-cctk_nghostzones[1]", "cctk_lsh[0]-cctk_nghostzones[0]" ], ["1", "1", "1"], ["#pragma omp parallel for", "", ""], "", lambdaU_expressions_FDout)) outstr += " if(FD_order == " + str(current_FD_order) + ") {\n" outstr += " #include \"" + lambdafile + "\"\n" outstr += " }\n" # Restore original FD order par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", orig_FD_order) outstr += """ ExtrapolateGammas(cctkGH,lambdaU0GF); ExtrapolateGammas(cctkGH,lambdaU1GF); ExtrapolateGammas(cctkGH,lambdaU2GF); } """ # Unregister the *SphorCartGF's. gri.glb_gridfcs_list = orig_glb_gridfcs_list # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("ADM_to_BSSN.c")] = outstr.replace( "BaikalETK", ThornName) import BSSN.ADM_in_terms_of_BSSN as btoa import BSSN.BSSN_quantities as Bq btoa.ADM_in_terms_of_BSSN() Bq.BSSN_basic_tensors() # Gives us betaU & BU outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_BSSN_to_ADM(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; """ btoa_lhrh = [] for i in range(3): for j in range(i, 3): btoa_lhrh.append( lhrh(lhs="g" + chr(ord('x') + i) + chr(ord('x') + j) + "[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)]", rhs=btoa.gammaDD[i][j])) for i in range(3): for j in range(i, 3): btoa_lhrh.append( lhrh(lhs="k" + chr(ord('x') + i) + chr(ord('x') + j) + "[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)]", rhs=btoa.KDD[i][j])) btoa_lhrh.append( lhrh(lhs="alp[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)]", rhs=Bq.alpha)) for i in range(3): btoa_lhrh.append( lhrh(lhs="beta" + chr(ord('x') + i) + "[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)]", rhs=Bq.betaU[i])) for i in range(3): btoa_lhrh.append( lhrh(lhs="dtbeta" + chr(ord('x') + i) + "[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)]", rhs=Bq.BU[i])) outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" bssn_to_adm_Ccode = fin.FD_outputC("returnstring", btoa_lhrh, outCparams) outstr += lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], ["cctk_lsh[2]", "cctk_lsh[1]", "cctk_lsh[0]"], ["1", "1", "1"], ["#pragma omp parallel for", "", ""], "", bssn_to_adm_Ccode) outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("BSSN_to_ADM.c")] = outstr.replace( "BaikalETK", ThornName) # Next, the driver for computing the Ricci tensor: outstr = """ #include <math.h> #include "SIMD/SIMD_intrinsics.h" #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_driver_pt1_BSSN_Ricci(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; const CCTK_INT *FD_order = CCTK_ParameterGet("FD_order","BaikalETK",NULL); const CCTK_REAL NOSIMDinvdx0 = 1.0/CCTK_DELTA_SPACE(0); const REAL_SIMD_ARRAY invdx0 = ConstSIMD(NOSIMDinvdx0); const CCTK_REAL NOSIMDinvdx1 = 1.0/CCTK_DELTA_SPACE(1); const REAL_SIMD_ARRAY invdx1 = ConstSIMD(NOSIMDinvdx1); const CCTK_REAL NOSIMDinvdx2 = 1.0/CCTK_DELTA_SPACE(2); const REAL_SIMD_ARRAY invdx2 = ConstSIMD(NOSIMDinvdx2); """ path = os.path.join(ThornName, "src") for root, dirs, files in os.walk(path): for file in files: if "BSSN_Ricci_FD_order" in file: array = file.replace(".", "_").split("_") outstr += " if(*FD_order == " + str(array[4]) + ") {\n" outstr += " #include \"" + file + "\"\n" outstr += " }\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "driver_pt1_BSSN_Ricci.c")] = outstr.replace("BaikalETK", ThornName) def SIMD_declare_C_params(): SIMD_declare_C_params_str = "" for i in range(len(par.glb_Cparams_list)): # keep_param is a boolean indicating whether we should accept or reject # the parameter. singleparstring will contain the string indicating # the variable type. keep_param, singleparstring = ccl.keep_param__return_type( par.glb_Cparams_list[i]) if (keep_param) and ("CCTK_REAL" in singleparstring): parname = par.glb_Cparams_list[i].parname SIMD_declare_C_params_str += " const "+singleparstring + "*NOSIMD"+parname+\ " = CCTK_ParameterGet(\""+parname+"\",\"BaikalETK\",NULL);\n" SIMD_declare_C_params_str += " const REAL_SIMD_ARRAY " + parname + " = ConstSIMD(*NOSIMD" + parname + ");\n" return SIMD_declare_C_params_str # Next, the driver for computing the BSSN RHSs: outstr = """ #include <math.h> #include "SIMD/SIMD_intrinsics.h" #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_driver_pt2_BSSN_RHSs(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; const CCTK_INT *FD_order = CCTK_ParameterGet("FD_order","BaikalETK",NULL); const CCTK_REAL NOSIMDinvdx0 = 1.0/CCTK_DELTA_SPACE(0); const REAL_SIMD_ARRAY invdx0 = ConstSIMD(NOSIMDinvdx0); const CCTK_REAL NOSIMDinvdx1 = 1.0/CCTK_DELTA_SPACE(1); const REAL_SIMD_ARRAY invdx1 = ConstSIMD(NOSIMDinvdx1); const CCTK_REAL NOSIMDinvdx2 = 1.0/CCTK_DELTA_SPACE(2); const REAL_SIMD_ARRAY invdx2 = ConstSIMD(NOSIMDinvdx2); """ + SIMD_declare_C_params() path = os.path.join(ThornName, "src") for root, dirs, files in os.walk(path): for file in files: if "BSSN_RHSs_FD_order" in file: array = file.replace(".", "_").split("_") outstr += " if(*FD_order == " + str(array[4]) + ") {\n" outstr += " #include \"" + file + "\"\n" outstr += " }\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "driver_pt2_BSSN_RHSs.c")] = outstr.replace("BaikalETK", ThornName) # Next, the driver for enforcing detgammabar = detgammahat constraint: outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_enforce_detgammabar_constraint(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; """ path = os.path.join(ThornName, "src") for root, dirs, files in os.walk(path): for file in files: if "enforcedetgammabar_constraint_FD_order" in file: array = file.replace(".", "_").split("_") outstr += " if(FD_order == " + str(array[4]) + ") {\n" outstr += " #include \"" + file + "\"\n" outstr += " }\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("driver_enforcedetgammabar_constraint.c")] = \ outstr.replace("BaikalETK",ThornName) # Next, the driver for computing the BSSN Hamiltonian & momentum constraints outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_BSSN_constraints(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; const CCTK_REAL invdx0 = 1.0/CCTK_DELTA_SPACE(0); const CCTK_REAL invdx1 = 1.0/CCTK_DELTA_SPACE(1); const CCTK_REAL invdx2 = 1.0/CCTK_DELTA_SPACE(2); """ path = os.path.join(ThornName, "src") for root, dirs, files in os.walk(path): for file in files: if "BSSN_constraints_FD_order" in file: array = file.replace(".", "_").split("_") outstr += " if(FD_order == " + str(array[4]) + ") {\n" outstr += " #include \"" + file + "\"\n" outstr += " }\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "driver_BSSN_constraints.c")] = outstr.replace("BaikalETK", ThornName) if enable_stress_energy_source_terms == True: # Declare T4DD as a set of gridfunctions. These won't # actually appear in interface.ccl, as interface.ccl # was set above. Thus before calling the code output # by FD_outputC(), we'll have to set pointers # to the actual gridfunctions they reference. # (In this case the eTab's.) T4DD = ixp.register_gridfunctions_for_single_rank2("AUXEVOL", "T4DD", "sym01", DIM=4) import BSSN.ADMBSSN_tofrom_4metric as AB4m AB4m.g4UU_ito_BSSN_or_ADM("BSSN") T4UUraised = ixp.zerorank2(DIM=4) for mu in range(4): for nu in range(4): for delta in range(4): for gamma in range(4): T4UUraised[mu][nu] += AB4m.g4UU[mu][delta] * AB4m.g4UU[ nu][gamma] * T4DD[delta][gamma] T4UU_expressions = [ lhrh(lhs=gri.gfaccess("in_gfs", "T4UU00"), rhs=T4UUraised[0][0]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU01"), rhs=T4UUraised[0][1]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU02"), rhs=T4UUraised[0][2]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU03"), rhs=T4UUraised[0][3]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU11"), rhs=T4UUraised[1][1]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU12"), rhs=T4UUraised[1][2]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU13"), rhs=T4UUraised[1][3]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU22"), rhs=T4UUraised[2][2]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU23"), rhs=T4UUraised[2][3]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU33"), rhs=T4UUraised[3][3]) ] outCparams = "outCverbose=False,includebraces=False,preindent=2,SIMD_enable=True" T4UUstr = fin.FD_outputC("returnstring", T4UU_expressions, outCparams) T4UUstr_loop = lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], ["cctk_lsh[2]", "cctk_lsh[1]", "cctk_lsh[0]"], ["1", "1", "SIMD_width"], ["#pragma omp parallel for", "", ""], "", T4UUstr) outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" #include "SIMD/SIMD_intrinsics.h" void BaikalETK_driver_BSSN_T4UU(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; const CCTK_REAL *restrict T4DD00GF = eTtt; const CCTK_REAL *restrict T4DD01GF = eTtx; const CCTK_REAL *restrict T4DD02GF = eTty; const CCTK_REAL *restrict T4DD03GF = eTtz; const CCTK_REAL *restrict T4DD11GF = eTxx; const CCTK_REAL *restrict T4DD12GF = eTxy; const CCTK_REAL *restrict T4DD13GF = eTxz; const CCTK_REAL *restrict T4DD22GF = eTyy; const CCTK_REAL *restrict T4DD23GF = eTyz; const CCTK_REAL *restrict T4DD33GF = eTzz; """ + T4UUstr_loop + """ }\n""" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "driver_BSSN_T4UU.c")] = outstr.replace("BaikalETK", ThornName)
def GiRaFFE_Higher_Order_v2(): #Step 1.0: Set the spatial dimension parameter to 3. par.set_parval_from_str("grid::DIM", 3) DIM = par.parval_from_str("grid::DIM") # Step 1.1: Set the finite differencing order to 4. #par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", 4) thismodule = "GiRaFFE_NRPy" # M_PI will allow the C code to substitute the correct value M_PI = par.Cparameters("#define",thismodule,"M_PI","") # ADMBase defines the 4-metric in terms of the 3+1 spacetime metric quantities gamma_{ij}, beta^i, and alpha gammaDD = ixp.register_gridfunctions_for_single_rank2("AUX","gammaDD", "sym01",DIM=3) betaU = ixp.register_gridfunctions_for_single_rank1("AUX","betaU",DIM=3) alpha = gri.register_gridfunctions("AUX","alpha") # GiRaFFE uses the Valencia 3-velocity and A_i, which are defined in the initial data module(GiRaFFEfood) ValenciavU = ixp.register_gridfunctions_for_single_rank1("AUX","ValenciavU",DIM=3) AD = ixp.register_gridfunctions_for_single_rank1("EVOL","AD",DIM=3) # B^i must be computed at each timestep within GiRaFFE so that the Valencia 3-velocity can be evaluated BU = ixp.register_gridfunctions_for_single_rank1("AUX","BU",DIM=3) # <a id='step3'></a> # # ## Step 1.2: Build the four metric $g_{\mu\nu}$, its inverse $g^{\mu\nu}$ and spatial derivatives $g_{\mu\nu,i}$ from ADM 3+1 quantities $\gamma_{ij}$, $\beta^i$, and $\alpha$ \[Back to [top](#top)\] # # Notice that the time evolution equation for $\tilde{S}_i$ # $$ # \partial_t \tilde{S}_i = - \partial_j \left( \alpha \sqrt{\gamma} T^j_{{\rm EM} i} \right) + \frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu} # $$ # contains $\partial_i g_{\mu \nu} = g_{\mu\nu,i}$. We will now focus on evaluating this term. # # The four-metric $g_{\mu\nu}$ is related to the three-metric $\gamma_{ij}$, index-lowered shift $\beta_i$, and lapse $\alpha$ by # $$ # g_{\mu\nu} = \begin{pmatrix} # -\alpha^2 + \beta^k \beta_k & \beta_j \\ # \beta_i & \gamma_{ij} # \end{pmatrix}. # $$ # This tensor and its inverse have already been built by the u0_smallb_Poynting__Cartesian.py module ([documented here](Tutorial-u0_smallb_Poynting-Cartesian.ipynb)), so we can simply load the module and import the variables. # $$\label{step3}$$ # Step 1.2: import u0_smallb_Poynting__Cartesian.py to set # the four metric and its inverse. This module also sets b^2 and u^0. import u0_smallb_Poynting__Cartesian.u0_smallb_Poynting__Cartesian as u0b u0b.compute_u0_smallb_Poynting__Cartesian(gammaDD,betaU,alpha,ValenciavU,BU) betaD = ixp.zerorank1() for i in range(DIM): for j in range(DIM): betaD[i] += gammaDD[i][j] * betaU[j] # Error check: fixed = to += # We will now pull in the four metric and its inverse. import BSSN.ADMBSSN_tofrom_4metric as AB4m # NRPy+: ADM/BSSN <-> 4-metric conversions AB4m.g4DD_ito_BSSN_or_ADM("ADM") g4DD = AB4m.g4DD AB4m.g4UU_ito_BSSN_or_ADM("ADM") g4UU = AB4m.g4UU # Next we compute spatial derivatives of the metric, $\partial_i g_{\mu\nu} = g_{\mu\nu,i}$, written in terms of the three-metric, shift, and lapse. Simply taking the derivative of the expression for $g_{\mu\nu}$ above, we find # $$ # g_{\mu\nu,l} = \begin{pmatrix} # -2\alpha \alpha_{,l} + \beta^k_{\ ,l} \beta_k + \beta^k \beta_{k,l} & \beta_{j,l} \\ # \beta_{i,l} & \gamma_{ij,l} # \end{pmatrix}. # $$ # # Notice the derivatives of the shift vector with its indexed lowered, $\beta_{i,j} = \partial_j \beta_i$. This can be easily computed in terms of the given ADMBase quantities $\beta^i$ and $\gamma_{ij}$ via: # \begin{align} # \beta_{i,j} &= \partial_j \beta_i \\ # &= \partial_j (\gamma_{ik} \beta^k) \\ # &= \gamma_{ik} \partial_j\beta^k + \beta^k \partial_j \gamma_{ik} \\ # \beta_{i,j} &= \gamma_{ik} \beta^k_{\ ,j} + \beta^k \gamma_{ik,j}. # \end{align} # # Since this expression mixes Greek and Latin indices, we will declare this as a 4 dimensional quantity, but only set the three spatial components of its last index (that is, leaving $l=0$ unset). # # So, we will first set # $$ g_{00,l} = \underbrace{-2\alpha \alpha_{,l}}_{\rm Term\ 1} + \underbrace{\beta^k_{\ ,l} \beta_k}_{\rm Term\ 2} + \underbrace{\beta^k \beta_{k,l}}_{\rm Term\ 3} $$ # Step 1.2, cont'd: Build spatial derivatives of the four metric # Step 1.2.a: Declare derivatives of grid functions. These will be handled by FD_outputC alpha_dD = ixp.declarerank1("alpha_dD") betaU_dD = ixp.declarerank2("betaU_dD","nosym") gammaDD_dD = ixp.declarerank3("gammaDD_dD","sym01") # Step 1.2.b: These derivatives will be constructed analytically. betaDdD = ixp.zerorank2() g4DDdD = ixp.zerorank3(DIM=4) for i in range(DIM): for j in range(DIM): for k in range(DIM): # \gamma_{ik} \beta^k_{,j} + \beta^k \gamma_{ik,j} betaDdD[i][j] += gammaDD[i][k] * betaU_dD[k][j] + betaU[k] * gammaDD_dD[i][k][j] #Error check: changed = to += above # Step 1.2.c: Set the 00 components # Step 1.2.c.i: Term 1: -2\alpha \alpha_{,l} for l in range(DIM): g4DDdD[0][0][l+1] = -2*alpha*alpha_dD[l] # Step 1.2.c.ii: Term 2: \beta^k_{\ ,l} \beta_k for l in range(DIM): for k in range(DIM): g4DDdD[0][0][l+1] += betaU_dD[k][l] * betaD[k] # Step 1.2.c.iii: Term 3: \beta^k \beta_{k,l} for l in range(DIM): for k in range(DIM): g4DDdD[0][0][l+1] += betaU[k] * betaDdD[k][l] # Now we will contruct the other components of $g_{\mu\nu,l}$. We will first construct # $$ g_{i0,l} = g_{0i,l} = \beta_{i,l}, $$ # then # $$ g_{ij,l} = \gamma_{ij,l} $$ # Step 1.2.d: Set the i0 and 0j components for l in range(DIM): for i in range(DIM): # \beta_{i,l} g4DDdD[i+1][0][l+1] = g4DDdD[0][i+1][l+1] = betaDdD[i][l] #Step 1.2.e: Set the ij components for l in range(DIM): for i in range(DIM): for j in range(DIM): # \gamma_{ij,l} g4DDdD[i+1][j+1][l+1] = gammaDD_dD[i][j][l] # <a id='step4'></a> # # # $T^{\mu\nu}_{\rm EM}$ and its derivatives # # Now that the metric and its derivatives are out of the way, let's return to the evolution equation for $\tilde{S}_i$, # $$ # \partial_t \tilde{S}_i = - \partial_j \left( \alpha \sqrt{\gamma} T^j_{{\rm EM} i} \right) + \frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu}. # $$ # We turn our focus now to $T^j_{{\rm EM} i}$ and its derivatives. To this end, we start by computing $T^{\mu \nu}_{\rm EM}$ (from eq. 27 of [Paschalidis & Shapiro's paper on their GRFFE code](https://arxiv.org/pdf/1310.3274.pdf)): # # $$\boxed{T^{\mu \nu}_{\rm EM} = b^2 u^{\mu} u^{\nu} + \frac{b^2}{2} g^{\mu \nu} - b^{\mu} b^{\nu}.}$$ # # Notice that $T^{\mu\nu}_{\rm EM}$ is written in terms of # # * $b^\mu$, the 4-component magnetic field vector, related to the comoving magnetic field vector $B^i_{(u)}$ # * $u^\mu$, the 4-velocity # * $g^{\mu \nu}$, the inverse 4-metric # # However, $\texttt{GiRaFFE}$ has access to only the following quantities, requiring in the following sections that we write the above quantities in terms of the following ones: # # * $\gamma_{ij}$, the 3-metric # * $\alpha$, the lapse # * $\beta^i$, the shift # * $A_i$, the vector potential # * $B^i$, the magnetic field (we assume only in the grid interior, not the ghost zones) # * $\left[\sqrt{\gamma}\Phi\right]$, the zero-component of the vector potential $A_\mu$, times the square root of the determinant of the 3-metric # * $v_{(n)}^i$, the Valencia 3-velocity # * $u^0$, the zero-component of the 4-velocity # # ## Step 2.0: $u^i$ and $b^i$ and related quantities \[Back to [top](#top)\] # # We begin by importing what we can from u0_smallb_Poynting__Cartesian.py. We will need the four-velocity $u^\mu$, which is related to the Valencia 3-velocity $v^i_{(n)}$ used directly by $\texttt{GiRaFFE}$ (see also [Duez, et al, eqs. 53 and 56](https://arxiv.org/pdf/astro-ph/0503420.pdf)) # \begin{align} # u^i &= u^0 (\alpha v^i_{(n)} - \beta^i), \\ # u_j &= \alpha u^0 \gamma_{ij} v^i_{(n)}, # \end{align} # where $v^i_{(n)}$ is the Valencia three-velocity. These have already been constructed in terms of the Valencia 3-velocity and other 3+1 ADM quantities by the u0_smallb_Poynting__Cartesian.py module, so we can simply import these variables: # $$\label{step4}$$ # Step 2.0: u^i, b^i, and related quantities # Step 2.0.a: import the four-velocity, as written in terms of the Valencia 3-velocity uD = ixp.zerorank1() uU = ixp.zerorank1() u4upperZero = gri.register_gridfunctions("AUX","u4upperZero") for i in range(DIM): uD[i] = u0b.uD[i].subs(u0b.u0,u4upperZero) uU[i] = u0b.uU[i].subs(u0b.u0,u4upperZero) # We also need the magnetic field 4-vector $b^{\mu}$, which is related to the magnetic field by [eqs. 23, 24, and 31 in Duez, et al](https://arxiv.org/pdf/astro-ph/0503420.pdf): # \begin{align} # b^0 &= \frac{1}{\sqrt{4\pi}} B^0_{\rm (u)} = \frac{u_j B^j}{\sqrt{4\pi}\alpha}, \\ # b^i &= \frac{1}{\sqrt{4\pi}} B^i_{\rm (u)} = \frac{B^i + (u_j B^j) u^i}{\sqrt{4\pi}\alpha u^0}, \\ # \end{align} # where $B^i$ is the variable tracked by the HydroBase thorn in the Einstein Toolkit. Again, these have already been built by the u0_smallb_Poynting__Cartesian.py module, so we can simply import the variables. # Step 2.0.b: import the small b terms smallb4U = ixp.zerorank1(DIM=4) smallb4D = ixp.zerorank1(DIM=4) for mu in range(4): smallb4U[mu] = u0b.smallb4U[mu].subs(u0b.u0,u4upperZero) smallb4D[mu] = u0b.smallb4D[mu].subs(u0b.u0,u4upperZero) smallb2 = u0b.smallb2etk.subs(u0b.u0,u4upperZero) # <a id='step5'></a> # # ## Step 2.1: Construct all components of the electromagnetic stress-energy tensor $T^{\mu \nu}_{\rm EM}$ \[Back to [top](#top)\] # # We now have all the pieces to calculate the stress-energy tensor, # $$T^{\mu \nu}_{\rm EM} = \underbrace{b^2 u^{\mu} u^{\nu}}_{\rm Term\ 1} + # \underbrace{\frac{b^2}{2} g^{\mu \nu}}_{\rm Term\ 2} # - \underbrace{b^{\mu} b^{\nu}}_{\rm Term\ 3}.$$ # Because $u^0$ is a separate variable, we could build the $00$ component separately, then the $\mu0$ and $0\nu$ components, and finally the $\mu\nu$ components. Alternatively, for clarity, we could create a temporary variable $u^\mu=\left( u^0, u^i \right)$ # $$\label{step5}$$ # Step 2.1: Construct the electromagnetic stress-energy tensor # Step 2.1.a: Set up the four-velocity vector u4U = ixp.zerorank1(DIM=4) u4U[0] = u4upperZero for i in range(DIM): u4U[i+1] = uU[i] # Step 2.1.b: Build T4EMUU itself T4EMUU = ixp.zerorank2(DIM=4) for mu in range(4): for nu in range(4): # Term 1: b^2 u^{\mu} u^{\nu} T4EMUU[mu][nu] = smallb2*u4U[mu]*u4U[nu] for mu in range(4): for nu in range(4): # Term 2: b^2 / 2 g^{\mu \nu} T4EMUU[mu][nu] += smallb2*g4UU[mu][nu]/2 for mu in range(4): for nu in range(4): # Term 3: -b^{\mu} b^{\nu} T4EMUU[mu][nu] += -smallb4U[mu]*smallb4U[nu] # # # Evolution equation for $\tilde{S}_i$ # <a id='step7'></a> # # ## Step 3.0: Construct the evolution equation for $\tilde{S}_i$ \[Back to [top](#top)\] # # Finally, we will return our attention to the time evolution equation (from eq. 13 of the [original paper](https://arxiv.org/pdf/1704.00599.pdf)), # \begin{align} # \partial_t \tilde{S}_i &= - \partial_j \underbrace{\left( \alpha \sqrt{\gamma} T^j_{{\rm EM} i} \right)}_{\rm SevolParenUD} + \frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu} \\ # &= - \partial_j{\rm SevolParenUD[i][j]} + \frac{1}{2} \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu} . # \end{align} # We will first take construct ${\rm SevolParenUD}$, then use its derivatives to construct the evolution equation. Note that # \begin{align} # {\rm SevolParenUD[i][j]} &= \alpha \sqrt{\gamma} T^j_{{\rm EM} i} \\ # &= \alpha \sqrt{\gamma} g_{\mu i} T^{\mu j}_{\rm EM}. # \end{align} # $$\label{step7}$$ # Step 3.0: Construct the evolution equation for \tilde{S}_i # Here, we set up the necessary machinery to take FD derivatives of alpha * sqrt(gamma) global gammaUU,gammadet gammaUU = ixp.register_gridfunctions_for_single_rank2("AUX","gammaUU","sym01") gammadet = gri.register_gridfunctions("AUX","gammadet") gammaUU, gammadet = ixp.symm_matrix_inverter3x3(gammaDD) alpsqrtgam = alpha*sp.sqrt(gammadet) global SevolParenUD SevolParenUD = ixp.register_gridfunctions_for_single_rank2("AUX","SevolParenUD","nosym") SevolParenUD = ixp.zerorank2() for i in range(DIM): for j in range (DIM): for mu in range(4): SevolParenUD[j][i] += alpsqrtgam * g4DD[mu][i+1] * T4EMUU[mu][j+1] SevolParenUD_dD = ixp.declarerank3("SevolParenUD_dD","nosym") global Stilde_rhsD Stilde_rhsD = ixp.zerorank1() # The first term: \alpha \sqrt{\gamma} \partial_j T^j_{{\rm EM} i} for i in range(DIM): for j in range(DIM): Stilde_rhsD[i] += -SevolParenUD_dD[j][i][j] # The second term: \alpha \sqrt{\gamma} T^{\mu \nu}_{\rm EM} \partial_i g_{\mu \nu} / 2 for i in range(DIM): for mu in range(4): for nu in range(4): Stilde_rhsD[i] += alpsqrtgam * T4EMUU[mu][nu] * g4DDdD[mu][nu][i+1] / 2 # # Evolution equations for $A_i$ and $\Phi$ # <a id='step8'></a> # # ## Step 4.0: Construct the evolution equations for $A_i$ and $[\sqrt{\gamma}\Phi]$ \[Back to [top](#top)\] # # We will also need to evolve the vector potential $A_i$. This evolution is given as eq. 17 in the [$\texttt{GiRaFFE}$](https://arxiv.org/pdf/1704.00599.pdf) paper: # $$\boxed{\partial_t A_i = \epsilon_{ijk} v^j B^k - \partial_i (\underbrace{\alpha \Phi - \beta^j A_j}_{\rm AevolParen}),}$$ # where $\epsilon_{ijk} = [ijk] \sqrt{\gamma}$ is the antisymmetric Levi-Civita tensor, the drift velocity $v^i = u^i/u^0$, $\gamma$ is the determinant of the three metric, $B^k$ is the magnetic field, $\alpha$ is the lapse, and $\beta$ is the shift. # The scalar electric potential $\Phi$ is also evolved by eq. 19: # $$\boxed{\partial_t [\sqrt{\gamma} \Phi] = -\partial_j (\underbrace{\alpha\sqrt{\gamma} \gamma^{ij} A_i - \beta^j [\sqrt{\gamma} \Phi]}_{\rm PevolParenU[j]}) - \xi \alpha [\sqrt{\gamma} \Phi],}$$ # with $\xi$ chosen as a damping factor. # # ### Step 4.0.a: Construct some useful auxiliary gridfunctions for the other evolution equations # # After declaring a some needed quantities, we will also define the parenthetical terms (underbrace above) that we need to take derivatives of. That way, we can take finite-difference derivatives easily. Note that the above equations incorporate the fact that $\gamma^{ij}$ is the appropriate raising operator for $A_i$: $A^j = \gamma^{ij} A_i$. This is correct since $n_\mu A^\mu = 0$, where $n_\mu$ is a normal to the hypersurface, so $A^0=0$ (according to Sec. II, subsection C of [the "Improved EM gauge condition" paper of Etienne *et al*](https://arxiv.org/pdf/1110.4633.pdf)). # $$\label{step8}$$ # Step 4.0: Construct the evolution equations for A_i and sqrt(gamma)Phi # Step 4.0.a: Construct some useful auxiliary gridfunctions for the other evolution equations xi = par.Cparameters("REAL",thismodule,"xi", 0.1) # The (dimensionful) Lorenz damping factor. Recommendation: set to ~1.5/max(delta t). # Define sqrt(gamma)Phi as psi6Phi psi6Phi = gri.register_gridfunctions("EVOL","psi6Phi") Phi = psi6Phi / sp.sqrt(gammadet) # We'll define a few extra gridfunctions to avoid complicated derivatives global AevolParen,PevolParenU AevolParen = gri.register_gridfunctions("AUX","AevolParen") PevolParenU = ixp.register_gridfunctions_for_single_rank1("AUX","PevolParenU") # {\rm AevolParen} = \alpha \Phi - \beta^j A_j AevolParen = alpha*Phi for j in range(DIM): AevolParen += -betaU[j] * AD[j] # {\rm PevolParenU[j]} = \alpha\sqrt{\gamma} \gamma^{ij} A_i - \beta^j [\sqrt{\gamma} \Phi] for j in range(DIM): PevolParenU[j] = -betaU[j] * psi6Phi for i in range(DIM): PevolParenU[j] += alpsqrtgam * gammaUU[i][j] * AD[i] AevolParen_dD = ixp.declarerank1("AevolParen_dD") PevolParenU_dD = ixp.declarerank2("PevolParenU_dD","nosym") # ### Step 4.0.b: Complete the construction of the evolution equations for $A_i$ and $[\sqrt{\gamma}\Phi]$ # # Now to set the evolution equations ([eqs. 17 and 19](https://arxiv.org/pdf/1704.00599.pdf)), recalling that the drift velocity $v^i = u^i/u^0$: # \begin{align} # \partial_t A_i &= \epsilon_{ijk} v^j B^k - \partial_i (\alpha \Phi - \beta^j A_j) \\ # &= \epsilon_{ijk} \frac{u^j}{u^0} B^k - {\rm AevolParen\_dD[i]} \\ # \partial_t [\sqrt{\gamma} \Phi] &= -\partial_j \left(\left(\alpha\sqrt{\gamma}\right)A^j - \beta^j [\sqrt{\gamma} \Phi]\right) - \xi \alpha [\sqrt{\gamma} \Phi] \\ # &= -{\rm PevolParenU\_dD[j][j]} - \xi \alpha [\sqrt{\gamma} \Phi]. \\ # \end{align} # Step 4.0.b: Construct the actual evolution equations for A_i and sqrt(gamma)Phi global A_rhsD,psi6Phi_rhs A_rhsD = ixp.zerorank1() psi6Phi_rhs = sp.sympify(0) # We already have a handy function to define the Levi-Civita symbol in WeylScalars import WeylScal4NRPy.WeylScalars_Cartesian as weyl # Initialize the Levi-Civita tensor by setting it equal to the Levi-Civita symbol LeviCivitaSymbolDDD = weyl.define_LeviCivitaSymbol_rank3() LeviCivitaTensorDDD = ixp.zerorank3() #LeviCivitaTensorUUU = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): LeviCivitaTensorDDD[i][j][k] = LeviCivitaSymbolDDD[i][j][k] * sp.sqrt(gammadet) #LeviCivitaTensorUUU[i][j][k] = LeviCivitaSymbolDDD[i][j][k] / sp.sqrt(gammadet) for i in range(DIM): A_rhsD[i] = -AevolParen_dD[i] for j in range(DIM): for k in range(DIM): A_rhsD[i] += LeviCivitaTensorDDD[i][j][k]*(uU[j]/u4upperZero)*BU[k] psi6Phi_rhs = -xi*alpha*psi6Phi for j in range(DIM): psi6Phi_rhs += -PevolParenU_dD[j][j]
def add_psi4_part_to_Cfunction_dict(includes=None, rel_path_to_Cparams=os.path.join("."), whichpart=0, setPsi4tozero=False, OMP_pragma_on="i2"): starttime = print_msg_with_timing("psi4, part " + str(whichpart), msg="Ccodegen", startstop="start") # Set up the C function for psi4 if includes is None: includes = [] includes += ["NRPy_function_prototypes.h"] desc = "Compute psi4 at all interior gridpoints, part " + str(whichpart) name = "psi4_part" + str(whichpart) params = """const paramstruct *restrict params, const REAL *restrict in_gfs, REAL *restrict xx[3], REAL *restrict aux_gfs""" body = "" gri.register_gridfunctions("AUX", [ "psi4_part" + str(whichpart) + "re", "psi4_part" + str(whichpart) + "im" ]) FD_outCparams = "outCverbose=False,enable_SIMD=False,CSE_sorting=none" if not setPsi4tozero: # Set the body of the function # First compute the symbolic expressions psi4.Psi4(specify_tetrad=False) # We really don't want to store these "Cparameters" permanently; they'll be set via function call... # so we make a copy of the original par.glb_Cparams_list (sans tetrad vectors) and restore it below Cparams_list_orig = par.glb_Cparams_list.copy() par.Cparameters("REAL", __name__, ["mre4U0", "mre4U1", "mre4U2", "mre4U3"], [0, 0, 0, 0]) par.Cparameters("REAL", __name__, ["mim4U0", "mim4U1", "mim4U2", "mim4U3"], [0, 0, 0, 0]) par.Cparameters("REAL", __name__, ["n4U0", "n4U1", "n4U2", "n4U3"], [0, 0, 0, 0]) body += """ REAL mre4U0,mre4U1,mre4U2,mre4U3,mim4U0,mim4U1,mim4U2,mim4U3,n4U0,n4U1,n4U2,n4U3; psi4_tetrad(params, in_gfs[IDX4S(CFGF, i0,i1,i2)], in_gfs[IDX4S(HDD00GF, i0,i1,i2)], in_gfs[IDX4S(HDD01GF, i0,i1,i2)], in_gfs[IDX4S(HDD02GF, i0,i1,i2)], in_gfs[IDX4S(HDD11GF, i0,i1,i2)], in_gfs[IDX4S(HDD12GF, i0,i1,i2)], in_gfs[IDX4S(HDD22GF, i0,i1,i2)], &mre4U0,&mre4U1,&mre4U2,&mre4U3,&mim4U0,&mim4U1,&mim4U2,&mim4U3,&n4U0,&n4U1,&n4U2,&n4U3, xx, i0,i1,i2); """ body += "REAL xCart_rel_to_globalgrid_center[3];\n" body += "xx_to_Cart(params, xx, i0, i1, i2, xCart_rel_to_globalgrid_center);\n" body += "int ignore_Cart_to_i0i1i2[3]; REAL xx_rel_to_globalgridorigin[3];\n" body += "Cart_to_xx_and_nearest_i0i1i2_global_grid_center(params, xCart_rel_to_globalgrid_center,xx_rel_to_globalgridorigin,ignore_Cart_to_i0i1i2);\n" for i in range(3): body += "const REAL xx" + str( i) + "=xx_rel_to_globalgridorigin[" + str(i) + "];\n" body += fin.FD_outputC("returnstring", [ lhrh(lhs=gri.gfaccess("in_gfs", "psi4_part" + str(whichpart) + "re"), rhs=psi4.psi4_re_pt[whichpart]), lhrh(lhs=gri.gfaccess("in_gfs", "psi4_part" + str(whichpart) + "im"), rhs=psi4.psi4_im_pt[whichpart]) ], params=FD_outCparams) par.glb_Cparams_list = Cparams_list_orig.copy() elif setPsi4tozero: body += fin.FD_outputC("returnstring", [ lhrh(lhs=gri.gfaccess("in_gfs", "psi4_part" + str(whichpart) + "re"), rhs=sp.sympify(0)), lhrh(lhs=gri.gfaccess("in_gfs", "psi4_part" + str(whichpart) + "im"), rhs=sp.sympify(0)) ], params=FD_outCparams) enable_SIMD = False enable_rfm_precompute = False print_msg_with_timing("psi4, part " + str(whichpart), msg="Ccodegen", startstop="stop", starttime=starttime) add_to_Cfunction_dict(includes=includes, desc=desc, name=name, params=params, body=body, loopopts=get_loopopts("InteriorPoints", enable_SIMD, enable_rfm_precompute, OMP_pragma_on, enable_xxs=False), rel_path_to_Cparams=rel_path_to_Cparams) return pickle_NRPy_env()
def WeylScalars_Cartesian(): # We do not need the barred or hatted quantities calculated when using Cartesian coordinates. # Instead, we declare the PHYSICAL metric and extrinsic curvature as grid functions. gammaDD = ixp.register_gridfunctions_for_single_rank2( "EVOL", "gammaDD", "sym01") kDD = ixp.register_gridfunctions_for_single_rank2("EVOL", "kDD", "sym01") gammaUU, detgamma = ixp.symm_matrix_inverter3x3(gammaDD) output_scalars = par.parval_from_str("output_scalars") global psi4r, psi4i, psi3r, psi3i, psi2r, psi2i, psi1r, psi1i, psi0r, psi0i # if output_scalars is "all_psis_and_invariants": # psi4r,psi4i,psi3r,psi3i,psi2r,psi2i,psi1r,psi1i,psi0r,psi0i = sp.symbols("psi4r psi4i\ # psi3r psi3i\ # psi2r psi2i\ # psi1r psi1i\ # psi0r psi0i") # elif output_scalars is "all_psis": psi4r,psi4i,psi3r,psi3i,psi2r,psi2i,psi1r,psi1i,psi0r,psi0i = gri.register_gridfunctions("AUX",["psi4r","psi4i",\ "psi3r","psi3i",\ "psi2r","psi2i",\ "psi1r","psi1i",\ "psi0r","psi0i"]) # Step 2a: Set spatial dimension (must be 3 for BSSN) DIM = 3 par.set_parval_from_str("grid::DIM", DIM) # Step 2b: Set the coordinate system to Cartesian x, y, z = gri.register_gridfunctions("AUX", ["x", "y", "z"]) # Step 2c: Set which tetrad is used; at the moment, only one supported option if par.parval_from_str("WeylScal4NRPy.WeylScalars_Cartesian::TetradChoice" ) == "Approx_QuasiKinnersley": # Step 3a: Choose 3 orthogonal vectors. Here, we choose one in the azimuthal # direction, one in the radial direction, and the cross product of the two. # Eqs 5.6, 5.7 in https://arxiv.org/pdf/gr-qc/0104063.pdf: # v_1^a &= [-y,x,0] \\ # v_2^a &= [x,y,z] \\ # v_3^a &= {\rm det}(g)^{1/2} g^{ad} \epsilon_{dbc} v_1^b v_2^c, v1U = ixp.zerorank1() v2U = ixp.zerorank1() v3U = ixp.zerorank1() v1U[0] = -y v1U[1] = x v1U[2] = sp.sympify(0) v2U[0] = x v2U[1] = y v2U[2] = z LeviCivitaSymbol_rank3 = define_LeviCivitaSymbol_rank3() for a in range(DIM): for b in range(DIM): for c in range(DIM): for d in range(DIM): v3U[a] += sp.sqrt( detgamma) * gammaUU[a][d] * LeviCivitaSymbol_rank3[ d][b][c] * v1U[b] * v2U[c] # Step 3b: Gram-Schmidt orthonormalization of the vectors. # The w_i^a vectors here are used to temporarily hold values on the way to the final vectors e_i^a # e_1^a &= \frac{v_1^a}{\omega_{11}} \\ # e_2^a &= \frac{v_2^a - \omega_{12} e_1^a}{\omega_{22}} \\ # e_3^a &= \frac{v_3^a - \omega_{13} e_1^a - \omega_{23} e_2^a}{\omega_{33}}, \\ # Normalize the first vector w1U = ixp.zerorank1() for a in range(DIM): w1U[a] = v1U[a] omega11 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega11 += w1U[a] * w1U[b] * gammaDD[a][b] e1U = ixp.zerorank1() for a in range(DIM): e1U[a] = w1U[a] / sp.sqrt(omega11) # Subtract off the portion of the first vector along the second, then normalize omega12 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega12 += e1U[a] * v2U[b] * gammaDD[a][b] w2U = ixp.zerorank1() for a in range(DIM): w2U[a] = v2U[a] - omega12 * e1U[a] omega22 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega22 += w2U[a] * w2U[b] * gammaDD[a][b] e2U = ixp.zerorank1() for a in range(DIM): e2U[a] = w2U[a] / sp.sqrt(omega22) # Subtract off the portion of the first and second vectors along the third, then normalize omega13 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega13 += e1U[a] * v3U[b] * gammaDD[a][b] omega23 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega23 += e2U[a] * v3U[b] * gammaDD[a][b] w3U = ixp.zerorank1() for a in range(DIM): w3U[a] = v3U[a] - omega13 * e1U[a] - omega23 * e2U[a] omega33 = sp.sympify(0) for a in range(DIM): for b in range(DIM): omega33 += w3U[a] * w3U[b] * gammaDD[a][b] e3U = ixp.zerorank1() for a in range(DIM): e3U[a] = w3U[a] / sp.sqrt(omega33) # Step 3c: Construct the tetrad itself. # Eqs. 5.6: # l^a &= \frac{1}{\sqrt{2}} e_2^a \\ # n^a &= -\frac{1}{\sqrt{2}} e_2^a \\ # m^a &= \frac{1}{\sqrt{2}} (e_3^a + i e_1^a) \\ # \overset{*}{m}{}^a &= \frac{1}{\sqrt{2}} (e_3^a - i e_1^a) isqrt2 = 1 / sp.sqrt(2) ltetU = ixp.zerorank1() ntetU = ixp.zerorank1() #mtetU = ixp.zerorank1() #mtetccU = ixp.zerorank1() remtetU = ixp.zerorank1( ) # SymPy does not like trying to take the real/imaginary parts of such a immtetU = ixp.zerorank1( ) # complicated expression as the Weyl scalars, so we will do it ourselves. for i in range(DIM): ltetU[i] = isqrt2 * e2U[i] ntetU[i] = -isqrt2 * e2U[i] remtetU[i] = isqrt2 * e3U[i] immtetU[i] = isqrt2 * e1U[i] nn = isqrt2 else: print("Error: TetradChoice == " + par.parval_from_str("TetradChoice") + " unsupported!") exit(1) gammaDD_dD = ixp.declarerank3("gammaDD_dD", "sym01") # Define the Christoffel symbols GammaUDD = ixp.zerorank3(DIM) for i in range(DIM): for k in range(DIM): for l in range(DIM): for m in range(DIM): GammaUDD[i][k][l] += (sp.Rational(1,2))*gammaUU[i][m]*\ (gammaDD_dD[m][k][l] + gammaDD_dD[m][l][k] - gammaDD_dD[k][l][m]) # Step 4b: Declare and construct the Riemann curvature tensor: # R_{abcd} = \frac{1}{2} (\gamma_{ad,cb}+\gamma_{bc,da}-\gamma_{ac,bd}-\gamma_{bd,ac}) # + \gamma_{je} \Gamma^{j}_{bc}\Gamma^{e}_{ad} - \gamma_{je} \Gamma^{j}_{bd} \Gamma^{e}_{ac} gammaDD_dDD = ixp.declarerank4("gammaDD_dDD", "sym01_sym23") RiemannDDDD = ixp.zerorank4() for a in range(DIM): for b in range(DIM): for c in range(DIM): for d in range(DIM): RiemannDDDD[a][b][c][d] = (gammaDD_dDD[a][d][c][b] + \ gammaDD_dDD[b][c][d][a] - \ gammaDD_dDD[a][c][b][d] - \ gammaDD_dDD[b][d][a][c]) / 2 for e in range(DIM): for j in range(DIM): RiemannDDDD[a][b][c][d] += gammaDD[j][e] * GammaUDD[j][b][c] * GammaUDD[e][a][d] - \ gammaDD[j][e] * GammaUDD[j][b][d] * GammaUDD[e][a][c] # Step 4c: We also need the extrinsic curvature tensor $K_{ij}$. # In Cartesian coordinates, we already made the components gridfunctions. # We will, however, need to calculate the trace of K seperately: trK = sp.sympify(0) for i in range(DIM): for j in range(DIM): trK += gammaUU[i][j] * kDD[i][j] # Step 5: Build the formula for \psi_4. # Gauss equation: involving the Riemann tensor and extrinsic curvature. # GaussDDDD[i][j][k][l] =& R_{ijkl} + 2K_{i[k}K_{l]j} GaussDDDD = ixp.zerorank4() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): GaussDDDD[i][j][k][l] = RiemannDDDD[i][j][k][ l] + kDD[i][k] * kDD[l][j] - kDD[i][l] * kDD[k][j] # Codazzi equation: involving partial derivatives of the extrinsic curvature. # We will first need to declare derivatives of kDD # CodazziDDD[j][k][l] =& -2 (K_{j[k,l]} + \Gamma^p_{j[k} K_{l]p}) kDD_dD = ixp.declarerank3("kDD_dD", "sym01") CodazziDDD = ixp.zerorank3() for j in range(DIM): for k in range(DIM): for l in range(DIM): CodazziDDD[j][k][l] = kDD_dD[j][l][k] - kDD_dD[j][k][l] for p in range(DIM): CodazziDDD[j][k][l] += GammaUDD[p][j][l] * kDD[k][ p] - GammaUDD[p][j][k] * kDD[l][p] # Another piece. While not associated with any particular equation, # this is still useful for organizational purposes. # RojoDD[j][l]} = & R_{jl} - K_{jp} K^p_l + KK_{jl} \\ # = & \gamma^{pd} R_{jpld} - K_{jp} K^p_l + KK_{jl} RojoDD = ixp.zerorank2() for j in range(DIM): for l in range(DIM): RojoDD[j][l] = trK * kDD[j][l] for p in range(DIM): for d in range(DIM): RojoDD[j][l] += gammaUU[p][d] * RiemannDDDD[j][p][l][ d] - kDD[j][p] * gammaUU[p][d] * kDD[d][l] # Now we can calculate $\psi_4$ itself! We assume l^0 = n^0 = \frac{1}{\sqrt{2}} # and m^0 = \overset{*}{m}{}^0 = 0 to simplify these equations. # We calculate the Weyl scalars as defined in https://arxiv.org/abs/gr-qc/0104063 # In terms of the above-defined quantites, the psis are defined as: # \psi_4 =&\ (\text{GaussDDDD[i][j][k][l]}) n^i \overset{*}{m}{}^j n^k \overset{*}{m}{}^l \\ # &+2 (\text{CodazziDDD[j][k][l]}) n^{0} \overset{*}{m}{}^{j} n^k \overset{*}{m}{}^l \\ # &+ (\text{RojoDD[j][l]}) n^{0} \overset{*}{m}{}^{j} n^{0} \overset{*}{m}{}^{l}. # \psi_3 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i n^j \overset{*}{m}{}^k n^l \\ # &+ (\text{CodazziDDD[j][k][l]}) (l^{0} n^{j} \overset{*}{m}{}^k n^l - l^{j} n^{0} \overset{*}{m}{}^k n^l - l^k n^j\overset{*}{m}{}^l n^0) \\ # &- (\text{RojoDD[j][l]}) l^{0} n^{j} \overset{*}{m}{}^l n^0 - l^{j} n^{0} \overset{*}{m}{}^l n^0 \\ # \psi_2 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i m^j \overset{*}{m}{}^k n^l \\ # &+ (\text{CodazziDDD[j][k][l]}) (l^{0} m^{j} \overset{*}{m}{}^k n^l - l^{j} m^{0} \overset{*}{m}{}^k n^l - l^k m^l \overset{*}{m}{}^l n^0) \\ # &- (\text{RojoDD[j][l]}) l^0 m^j \overset{*}{m}{}^l n^0 \\ # \psi_1 =&\ (\text{GaussDDDD[i][j][k][l]}) n^i l^j m^k l^l \\ # &+ (\text{CodazziDDD[j][k][l]}) (n^{0} l^{j} m^k l^l - n^{j} l^{0} m^k l^l - n^k l^l m^j l^0) \\ # &- (\text{RojoDD[j][l]}) (n^{0} l^{j} m^l l^0 - n^{j} l^{0} m^l l^0) \\ # \psi_0 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i m^j l^k m^l \\ # &+2 (\text{CodazziDDD[j][k][l]}) (l^0 m^j l^k m^l + l^k m^l l^0 m^j) \\ # &+ (\text{RojoDD[j][l]}) l^0 m^j l^0 m^j. \\ psi4r = sp.sympify(0) psi4i = sp.sympify(0) psi3r = sp.sympify(0) psi3i = sp.sympify(0) psi2r = sp.sympify(0) psi2i = sp.sympify(0) psi1r = sp.sympify(0) psi1i = sp.sympify(0) psi0r = sp.sympify(0) psi0i = sp.sympify(0) for l in range(DIM): for j in range(DIM): psi4r += RojoDD[j][l] * nn * nn * (remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi4i += RojoDD[j][l] * nn * nn * (-remtetU[j] * immtetU[l] - immtetU[j] * remtetU[l]) psi3r += -RojoDD[j][l] * nn * nn * (ntetU[j] - ltetU[j]) * remtetU[l] psi3i += RojoDD[j][l] * nn * nn * (ntetU[j] - ltetU[j]) * immtetU[l] psi2r += -RojoDD[j][l] * nn * nn * (remtetU[l] * remtetU[j] + immtetU[j] * immtetU[l]) psi2i += -RojoDD[j][l] * nn * nn * (immtetU[l] * remtetU[j] - remtetU[j] * immtetU[l]) psi1r += RojoDD[j][l] * nn * nn * (ntetU[j] * remtetU[l] - ltetU[j] * remtetU[l]) psi1i += RojoDD[j][l] * nn * nn * (ntetU[j] * immtetU[l] - ltetU[j] * immtetU[l]) psi0r += RojoDD[j][l] * nn * nn * (remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi0i += RojoDD[j][l] * nn * nn * (remtetU[j] * immtetU[l] + immtetU[j] * remtetU[l]) for l in range(DIM): for j in range(DIM): for k in range(DIM): psi4r += 2 * CodazziDDD[j][k][l] * ntetU[k] * nn * ( remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi4i += 2 * CodazziDDD[j][k][l] * ntetU[k] * nn * ( -remtetU[j] * immtetU[l] - immtetU[j] * remtetU[l]) psi3r += 1 * CodazziDDD[j][k][l] * nn * ( (ntetU[j] - ltetU[j]) * remtetU[k] * ntetU[l] - remtetU[j] * ltetU[k] * ntetU[l]) psi3i += -1 * CodazziDDD[j][k][l] * nn * ( (ntetU[j] - ltetU[j]) * immtetU[k] * ntetU[l] - immtetU[j] * ltetU[k] * ntetU[l]) psi2r += 1 * CodazziDDD[j][k][l] * nn * ( ntetU[l] * (remtetU[j] * remtetU[k] + immtetU[j] * immtetU[k]) - ltetU[k] * (remtetU[j] * remtetU[l] + immtetU[j] * immtetU[l])) psi2i += 1 * CodazziDDD[j][k][l] * nn * ( ntetU[l] * (immtetU[j] * remtetU[k] - remtetU[j] * immtetU[k]) - ltetU[k] * (remtetU[j] * immtetU[l] - immtetU[j] * remtetU[l])) psi1r += 1 * CodazziDDD[j][k][l] * nn * ( ltetU[j] * remtetU[k] * ltetU[l] - remtetU[j] * ntetU[k] * ltetU[l] - ntetU[j] * remtetU[k] * ltetU[l]) psi1i += 1 * CodazziDDD[j][k][l] * nn * ( ltetU[j] * immtetU[k] * ltetU[l] - immtetU[j] * ntetU[k] * ltetU[l] - ntetU[j] * immtetU[k] * ltetU[l]) psi0r += 2 * CodazziDDD[j][k][l] * nn * ltetU[k] * ( remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi0i += 2 * CodazziDDD[j][k][l] * nn * ltetU[k] * ( remtetU[j] * immtetU[l] + immtetU[j] * remtetU[l]) for l in range(DIM): for j in range(DIM): for k in range(DIM): for i in range(DIM): psi4r += GaussDDDD[i][j][k][l] * ntetU[i] * ntetU[k] * ( remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi4i += GaussDDDD[i][j][k][l] * ntetU[i] * ntetU[k] * ( -remtetU[j] * immtetU[l] - immtetU[j] * remtetU[l]) psi3r += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[ j] * remtetU[k] * ntetU[l] psi3i += -GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[ j] * immtetU[k] * ntetU[l] psi2r += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[l] * ( remtetU[j] * remtetU[k] + immtetU[j] * immtetU[k]) psi2i += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[l] * ( immtetU[j] * remtetU[k] - remtetU[j] * immtetU[k]) psi1r += GaussDDDD[i][j][k][l] * ntetU[i] * ltetU[ j] * remtetU[k] * ltetU[l] psi1i += GaussDDDD[i][j][k][l] * ntetU[i] * ltetU[ j] * immtetU[k] * ltetU[l] psi0r += GaussDDDD[i][j][k][l] * ltetU[i] * ltetU[k] * ( remtetU[j] * remtetU[l] - immtetU[j] * immtetU[l]) psi0i += GaussDDDD[i][j][k][l] * ltetU[i] * ltetU[k] * ( remtetU[j] * immtetU[l] + immtetU[j] * remtetU[l])
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. outCparams = "outCverbose=False,CSE_sorting=none" xi_damping = par.Cparameters("REAL",thismodule,"xi_damping",0.1) # Default Kreiss-Oliger dissipation strength default_KO_strength = 0.1 diss_strength = par.Cparameters("REAL", thismodule, "diss_strength", default_KO_strength) import GRHD.equations as GRHD # NRPy+: Generate general relativistic hydrodynamics equations 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") ixp.register_gridfunctions_for_single_rank1("EVOL","AD") BU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","BU") ixp.register_gridfunctions_for_single_rank1("AUXEVOL","BstaggerU") ValenciavU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","ValenciavU") gri.register_gridfunctions("EVOL","psi6Phi") StildeD = ixp.register_gridfunctions_for_single_rank1("EVOL","StildeD") gri.register_gridfunctions("AUXEVOL","psi6_temp") gri.register_gridfunctions("AUXEVOL","psi6center") gri.register_gridfunctions("AUXEVOL","cmax_x") gri.register_gridfunctions("AUXEVOL","cmin_x") gri.register_gridfunctions("AUXEVOL","cmax_y") gri.register_gridfunctions("AUXEVOL","cmin_y") gri.register_gridfunctions("AUXEVOL","cmax_z") gri.register_gridfunctions("AUXEVOL","cmin_z")
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") PhievolParenU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","PhievolParenU",DIM=3) AevolParen = gri.register_gridfunctions("AUXEVOL","AevolParen") GRHD.compute_sqrtgammaDET(gammaDD) GRFFE.compute_AD_source_term_parenthetical_for_FD(GRHD.sqrtgammaDET,betaU,alpha,psi6Phi,AD) GRFFE.compute_psi6Phi_rhs_parenthetical(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_parentheticals_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="outCverbose=False").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="outCverbose=False").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) # Declare all the Cparameters we will need metricderivDDD = ixp.declarerank3("metricderivDDD","sym01",DIM=3) shiftderivUD = ixp.declarerank2("shiftderivUD","nosym",DIM=3) lapsederivD = ixp.declarerank1("lapsederivD",DIM=3) general_access = """const REAL gammaDD00 = auxevol_gfs[IDX4S(GAMMADD00GF,i0,i1,i2)]; const REAL gammaDD01 = auxevol_gfs[IDX4S(GAMMADD01GF,i0,i1,i2)]; const REAL gammaDD02 = auxevol_gfs[IDX4S(GAMMADD02GF,i0,i1,i2)]; const REAL gammaDD11 = auxevol_gfs[IDX4S(GAMMADD11GF,i0,i1,i2)]; const REAL gammaDD12 = auxevol_gfs[IDX4S(GAMMADD12GF,i0,i1,i2)]; const REAL gammaDD22 = auxevol_gfs[IDX4S(GAMMADD22GF,i0,i1,i2)]; const REAL betaU0 = auxevol_gfs[IDX4S(BETAU0GF,i0,i1,i2)]; const REAL betaU1 = auxevol_gfs[IDX4S(BETAU1GF,i0,i1,i2)]; const REAL betaU2 = auxevol_gfs[IDX4S(BETAU2GF,i0,i1,i2)]; const REAL alpha = auxevol_gfs[IDX4S(ALPHAGF,i0,i1,i2)]; const REAL ValenciavU0 = auxevol_gfs[IDX4S(VALENCIAVU0GF,i0,i1,i2)]; const REAL ValenciavU1 = auxevol_gfs[IDX4S(VALENCIAVU1GF,i0,i1,i2)]; const REAL ValenciavU2 = auxevol_gfs[IDX4S(VALENCIAVU2GF,i0,i1,i2)]; const REAL BU0 = auxevol_gfs[IDX4S(BU0GF,i0,i1,i2)]; const REAL BU1 = auxevol_gfs[IDX4S(BU1GF,i0,i1,i2)]; const REAL BU2 = auxevol_gfs[IDX4S(BU2GF,i0,i1,i2)]; """ metric_deriv_access = ixp.zerorank1(DIM=3) metric_deriv_access[0] = """const REAL metricderivDDD000 = (auxevol_gfs[IDX4S(GAMMA_FACEDD00GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD00GF,i0,i1,i2)])/dxx0; const REAL metricderivDDD010 = (auxevol_gfs[IDX4S(GAMMA_FACEDD01GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD01GF,i0,i1,i2)])/dxx0; const REAL metricderivDDD020 = (auxevol_gfs[IDX4S(GAMMA_FACEDD02GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD02GF,i0,i1,i2)])/dxx0; const REAL metricderivDDD110 = (auxevol_gfs[IDX4S(GAMMA_FACEDD11GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD11GF,i0,i1,i2)])/dxx0; const REAL metricderivDDD120 = (auxevol_gfs[IDX4S(GAMMA_FACEDD12GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD12GF,i0,i1,i2)])/dxx0; const REAL metricderivDDD220 = (auxevol_gfs[IDX4S(GAMMA_FACEDD22GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD22GF,i0,i1,i2)])/dxx0; const REAL shiftderivUD00 = (auxevol_gfs[IDX4S(BETA_FACEU0GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(BETA_FACEU0GF,i0,i1,i2)])/dxx0; const REAL shiftderivUD10 = (auxevol_gfs[IDX4S(BETA_FACEU1GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(BETA_FACEU1GF,i0,i1,i2)])/dxx0; const REAL shiftderivUD20 = (auxevol_gfs[IDX4S(BETA_FACEU2GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(BETA_FACEU2GF,i0,i1,i2)])/dxx0; const REAL lapsederivD0 = (auxevol_gfs[IDX4S(ALPHA_FACEGF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(ALPHA_FACEGF,i0,i1,i2)])/dxx0; REAL Stilde_rhsD0; """ metric_deriv_access[1] = """const REAL metricderivDDD001 = (auxevol_gfs[IDX4S(GAMMA_FACEDD00GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD00GF,i0,i1,i2)])/dxx1; const REAL metricderivDDD011 = (auxevol_gfs[IDX4S(GAMMA_FACEDD01GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD01GF,i0,i1,i2)])/dxx1; const REAL metricderivDDD021 = (auxevol_gfs[IDX4S(GAMMA_FACEDD02GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD02GF,i0,i1,i2)])/dxx1; const REAL metricderivDDD111 = (auxevol_gfs[IDX4S(GAMMA_FACEDD11GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD11GF,i0,i1,i2)])/dxx1; const REAL metricderivDDD121 = (auxevol_gfs[IDX4S(GAMMA_FACEDD12GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD12GF,i0,i1,i2)])/dxx1; const REAL metricderivDDD221 = (auxevol_gfs[IDX4S(GAMMA_FACEDD22GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD22GF,i0,i1,i2)])/dxx1; const REAL shiftderivUD01 = (auxevol_gfs[IDX4S(BETA_FACEU0GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(BETA_FACEU0GF,i0,i1,i2)])/dxx1; const REAL shiftderivUD11 = (auxevol_gfs[IDX4S(BETA_FACEU1GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(BETA_FACEU1GF,i0,i1,i2)])/dxx1; const REAL shiftderivUD21 = (auxevol_gfs[IDX4S(BETA_FACEU2GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(BETA_FACEU2GF,i0,i1,i2)])/dxx1; const REAL lapsederivD1 = (auxevol_gfs[IDX4S(ALPHA_FACEGF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(ALPHA_FACEGF,i0,i1,i2)])/dxx1; REAL Stilde_rhsD1; """ metric_deriv_access[2] = """const REAL metricderivDDD002 = (auxevol_gfs[IDX4S(GAMMA_FACEDD00GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(GAMMA_FACEDD00GF,i0,i1,i2)])/dxx2; const REAL metricderivDDD012 = (auxevol_gfs[IDX4S(GAMMA_FACEDD01GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(GAMMA_FACEDD01GF,i0,i1,i2)])/dxx2; const REAL metricderivDDD022 = (auxevol_gfs[IDX4S(GAMMA_FACEDD02GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(GAMMA_FACEDD02GF,i0,i1,i2)])/dxx2; const REAL metricderivDDD112 = (auxevol_gfs[IDX4S(GAMMA_FACEDD11GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(GAMMA_FACEDD11GF,i0,i1,i2)])/dxx2; const REAL metricderivDDD122 = (auxevol_gfs[IDX4S(GAMMA_FACEDD12GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(GAMMA_FACEDD12GF,i0,i1,i2)])/dxx2; const REAL metricderivDDD222 = (auxevol_gfs[IDX4S(GAMMA_FACEDD22GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(GAMMA_FACEDD22GF,i0,i1,i2)])/dxx2; const REAL shiftderivUD02 = (auxevol_gfs[IDX4S(BETA_FACEU0GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(BETA_FACEU0GF,i0,i1,i2)])/dxx2; const REAL shiftderivUD12 = (auxevol_gfs[IDX4S(BETA_FACEU1GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(BETA_FACEU1GF,i0,i1,i2)])/dxx2; const REAL shiftderivUD22 = (auxevol_gfs[IDX4S(BETA_FACEU2GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(BETA_FACEU2GF,i0,i1,i2)])/dxx2; const REAL lapsederivD2 = (auxevol_gfs[IDX4S(ALPHA_FACEGF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(ALPHA_FACEGF,i0,i1,i2)])/dxx2; REAL Stilde_rhsD2; """ write_final_quantity = ixp.zerorank1(DIM=3) write_final_quantity[0] = """rhs_gfs[IDX4S(STILDED0GF,i0,i1,i2)] += Stilde_rhsD0; """ write_final_quantity[1] = """rhs_gfs[IDX4S(STILDED1GF,i0,i1,i2)] += Stilde_rhsD1; """ write_final_quantity[2] = """rhs_gfs[IDX4S(STILDED2GF,i0,i1,i2)] += Stilde_rhsD2; """ # Declare this symbol: sqrt4pi = par.Cparameters("REAL",thismodule,"sqrt4pi","sqrt(4.0*M_PI)") # 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.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, metricderivDDD,shiftderivUD,lapsederivD) 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(out_dir,subdir,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="outCverbose=False").replace("IDX4","IDX4S")\ +write_final_quantity[i], loopopts ="InteriorPoints", rel_path_for_Cparams=os.path.join("../")) 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) Memory_Read = """const double alpha_face = auxevol_gfs[IDX4S(ALPHA_FACEGF, i0,i1,i2)]; const double gamma_faceDD00 = auxevol_gfs[IDX4S(GAMMA_FACEDD00GF, i0,i1,i2)]; const double gamma_faceDD01 = auxevol_gfs[IDX4S(GAMMA_FACEDD01GF, i0,i1,i2)]; const double gamma_faceDD02 = auxevol_gfs[IDX4S(GAMMA_FACEDD02GF, i0,i1,i2)]; const double gamma_faceDD11 = auxevol_gfs[IDX4S(GAMMA_FACEDD11GF, i0,i1,i2)]; const double gamma_faceDD12 = auxevol_gfs[IDX4S(GAMMA_FACEDD12GF, i0,i1,i2)]; const double gamma_faceDD22 = auxevol_gfs[IDX4S(GAMMA_FACEDD22GF, i0,i1,i2)]; const double beta_faceU0 = auxevol_gfs[IDX4S(BETA_FACEU0GF, i0,i1,i2)]; const double beta_faceU1 = auxevol_gfs[IDX4S(BETA_FACEU1GF, i0,i1,i2)]; const double beta_faceU2 = auxevol_gfs[IDX4S(BETA_FACEU2GF, i0,i1,i2)]; const double Valenciav_rU0 = auxevol_gfs[IDX4S(VALENCIAV_RU0GF, i0,i1,i2)]; const double Valenciav_rU1 = auxevol_gfs[IDX4S(VALENCIAV_RU1GF, i0,i1,i2)]; const double Valenciav_rU2 = auxevol_gfs[IDX4S(VALENCIAV_RU2GF, i0,i1,i2)]; const double B_rU0 = auxevol_gfs[IDX4S(B_RU0GF, i0,i1,i2)]; const double B_rU1 = auxevol_gfs[IDX4S(B_RU1GF, i0,i1,i2)]; const double B_rU2 = auxevol_gfs[IDX4S(B_RU2GF, i0,i1,i2)]; const double Valenciav_lU0 = auxevol_gfs[IDX4S(VALENCIAV_LU0GF, i0,i1,i2)]; const double Valenciav_lU1 = auxevol_gfs[IDX4S(VALENCIAV_LU1GF, i0,i1,i2)]; const double Valenciav_lU2 = auxevol_gfs[IDX4S(VALENCIAV_LU2GF, i0,i1,i2)]; const double B_lU0 = auxevol_gfs[IDX4S(B_LU0GF, i0,i1,i2)]; const double B_lU1 = auxevol_gfs[IDX4S(B_LU1GF, i0,i1,i2)]; const double B_lU2 = auxevol_gfs[IDX4S(B_LU2GF, i0,i1,i2)]; REAL A_rhsD0 = 0; REAL A_rhsD1 = 0; REAL A_rhsD2 = 0; """ Memory_Write = """rhs_gfs[IDX4S(AD0GF,i0,i1,i2)] += A_rhsD0; rhs_gfs[IDX4S(AD1GF,i0,i1,i2)] += A_rhsD1; rhs_gfs[IDX4S(AD2GF,i0,i1,i2)] += A_rhsD2; """ indices = ["i0","i1","i2"] indicesp1 = ["i0+1","i1+1","i2+1"] subdir = "RHSs" for flux_dirn in range(3): Af.calculate_E_i_flux(flux_dirn,True,alpha_face,gamma_faceDD,beta_faceU,\ Valenciav_rU,B_rU,Valenciav_lU,B_lU) E_field_to_print = [\ sp.Rational(1,4)*Af.E_fluxD[(flux_dirn+1)%3], sp.Rational(1,4)*Af.E_fluxD[(flux_dirn+2)%3], ] E_field_names = [\ "A_rhsD"+str((flux_dirn+1)%3), "A_rhsD"+str((flux_dirn+2)%3), ] desc = "Calculate the electric flux on the left face in direction " + str(flux_dirn) + "." name = "calculate_E_field_D" + str(flux_dirn) + "_right" outCfunction( outfile = os.path.join(out_dir,subdir,name+".h"), desc=desc, name=name, params ="const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs", body = Memory_Read \ +outputC(E_field_to_print,E_field_names,"returnstring",params="outCverbose=False").replace("IDX4","IDX4S")\ +Memory_Write, loopopts ="InteriorPoints", rel_path_for_Cparams=os.path.join("../")) desc = "Calculate the electric flux on the left face in direction " + str(flux_dirn) + "." name = "calculate_E_field_D" + str(flux_dirn) + "_left" outCfunction( outfile = os.path.join(out_dir,subdir,name+".h"), desc=desc, name=name, params ="const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs", body = Memory_Read.replace(indices[flux_dirn],indicesp1[flux_dirn]) \ +outputC(E_field_to_print,E_field_names,"returnstring",params="outCverbose=False").replace("IDX4","IDX4S")\ +Memory_Write, loopopts ="InteriorPoints", rel_path_for_Cparams=os.path.join("../")) Memory_Read = """const double alpha_face = auxevol_gfs[IDX4S(ALPHA_FACEGF, i0,i1,i2)]; const double gamma_faceDD00 = auxevol_gfs[IDX4S(GAMMA_FACEDD00GF, i0,i1,i2)]; const double gamma_faceDD01 = auxevol_gfs[IDX4S(GAMMA_FACEDD01GF, i0,i1,i2)]; const double gamma_faceDD02 = auxevol_gfs[IDX4S(GAMMA_FACEDD02GF, i0,i1,i2)]; const double gamma_faceDD11 = auxevol_gfs[IDX4S(GAMMA_FACEDD11GF, i0,i1,i2)]; const double gamma_faceDD12 = auxevol_gfs[IDX4S(GAMMA_FACEDD12GF, i0,i1,i2)]; const double gamma_faceDD22 = auxevol_gfs[IDX4S(GAMMA_FACEDD22GF, i0,i1,i2)]; const double beta_faceU0 = auxevol_gfs[IDX4S(BETA_FACEU0GF, i0,i1,i2)]; const double beta_faceU1 = auxevol_gfs[IDX4S(BETA_FACEU1GF, i0,i1,i2)]; const double beta_faceU2 = auxevol_gfs[IDX4S(BETA_FACEU2GF, i0,i1,i2)]; const double Valenciav_rU0 = auxevol_gfs[IDX4S(VALENCIAV_RU0GF, i0,i1,i2)]; const double Valenciav_rU1 = auxevol_gfs[IDX4S(VALENCIAV_RU1GF, i0,i1,i2)]; const double Valenciav_rU2 = auxevol_gfs[IDX4S(VALENCIAV_RU2GF, i0,i1,i2)]; const double B_rU0 = auxevol_gfs[IDX4S(B_RU0GF, i0,i1,i2)]; const double B_rU1 = auxevol_gfs[IDX4S(B_RU1GF, i0,i1,i2)]; const double B_rU2 = auxevol_gfs[IDX4S(B_RU2GF, i0,i1,i2)]; const double Valenciav_lU0 = auxevol_gfs[IDX4S(VALENCIAV_LU0GF, i0,i1,i2)]; const double Valenciav_lU1 = auxevol_gfs[IDX4S(VALENCIAV_LU1GF, i0,i1,i2)]; const double Valenciav_lU2 = auxevol_gfs[IDX4S(VALENCIAV_LU2GF, i0,i1,i2)]; const double B_lU0 = auxevol_gfs[IDX4S(B_LU0GF, i0,i1,i2)]; const double B_lU1 = auxevol_gfs[IDX4S(B_LU1GF, i0,i1,i2)]; const double B_lU2 = auxevol_gfs[IDX4S(B_LU2GF, i0,i1,i2)]; REAL Stilde_fluxD0 = 0; REAL Stilde_fluxD1 = 0; REAL Stilde_fluxD2 = 0; """ Memory_Write = """rhs_gfs[IDX4S(STILDED0GF, i0, i1, i2)] += invdx0*Stilde_fluxD0; rhs_gfs[IDX4S(STILDED1GF, i0, i1, i2)] += invdx0*Stilde_fluxD1; rhs_gfs[IDX4S(STILDED2GF, i0, i1, i2)] += invdx0*Stilde_fluxD2; """ indices = ["i0","i1","i2"] indicesp1 = ["i0+1","i1+1","i2+1"] assignment = "+=" assignmentp1 = "-=" invdx = ["invdx0","invdx1","invdx2"] for flux_dirn in range(3): Sf.calculate_Stilde_flux(flux_dirn,True,alpha_face,gamma_faceDD,beta_faceU,\ Valenciav_rU,B_rU,Valenciav_lU,B_lU,sqrt4pi) Stilde_flux_to_print = [\ Sf.Stilde_fluxD[0],\ Sf.Stilde_fluxD[1],\ Sf.Stilde_fluxD[2],\ ] Stilde_flux_names = [\ "Stilde_fluxD0",\ "Stilde_fluxD1",\ "Stilde_fluxD2",\ ] desc = "Compute the flux of all 3 components of tilde{S}_i on the right face in the " + str(flux_dirn) + "." name = "calculate_Stilde_flux_D" + str(flux_dirn) + "_right" outCfunction( outfile = os.path.join(out_dir,subdir,name+".h"), desc=desc, name=name, params ="const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs", body = Memory_Read \ +outputC(Stilde_flux_to_print,Stilde_flux_names,"returnstring",params="outCverbose=False").replace("IDX4","IDX4S")\ +Memory_Write.replace(invdx[0],invdx[flux_dirn]), loopopts ="InteriorPoints", rel_path_for_Cparams=os.path.join("../")) desc = "Compute the flux of all 3 components of tilde{S}_i on the left face in the " + str(flux_dirn) + "." name = "calculate_Stilde_flux_D" + str(flux_dirn) + "_left" outCfunction( outfile = os.path.join(out_dir,subdir,name+".h"), desc=desc, name=name, params ="const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs", body = Memory_Read.replace(indices[flux_dirn],indicesp1[flux_dirn]) \ +outputC(Stilde_flux_to_print,Stilde_flux_names,"returnstring",params="outCverbose=False").replace("IDX4","IDX4S")\ +Memory_Write.replace(invdx[0],invdx[flux_dirn]).replace(assignment,assignmentp1), loopopts ="InteriorPoints", rel_path_for_Cparams=os.path.join("../")) 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="outCverbose=False").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="outCverbose=False").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("""// 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_parentheticals_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 "RHSs/calculate_E_field_D0_right.h" // #include "RHSs/calculate_E_field_D0_left.h" // #include "RHSs/calculate_E_field_D1_right.h" // #include "RHSs/calculate_E_field_D1_left.h" // #include "RHSs/calculate_E_field_D2_right.h" // #include "RHSs/calculate_E_field_D2_left.h" #include "../calculate_E_field_flat_all_in_one.h" #include "RHSs/calculate_Stilde_flux_D0_right.h" #include "RHSs/calculate_Stilde_flux_D0_left.h" #include "RHSs/calculate_Stilde_flux_D1_right.h" #include "RHSs/calculate_Stilde_flux_D1_left.h" #include "RHSs/calculate_Stilde_flux_D2_right.h" #include "RHSs/calculate_Stilde_flux_D2_left.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_parentheticals_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); calculate_E_field_flat_all_in_one(params,auxevol_gfs,rhs_gfs,flux_dirn); // Finally, we calculate the flux of StildeD and add the appropriate finite-differences // to the RHSs. calculate_Stilde_flux_D0_right(params,auxevol_gfs,rhs_gfs); calculate_Stilde_flux_D0_left(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_E_field_flat_all_in_one(params,auxevol_gfs,rhs_gfs,flux_dirn); calculate_Stilde_flux_D1_right(params,auxevol_gfs,rhs_gfs); calculate_Stilde_flux_D1_left(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_E_field_flat_all_in_one(params,auxevol_gfs,rhs_gfs,flux_dirn); calculate_Stilde_flux_D2_right(params,auxevol_gfs,rhs_gfs); calculate_Stilde_flux_D2_left(params,auxevol_gfs,rhs_gfs); } } } 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); } """)