def GiRaFFE_NRPy_Afield_flux(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. gammaDD = ixp.declarerank2("gammaDD", "sym01", DIM=3) betaU = ixp.declarerank1("betaU", DIM=3) alpha = sp.sympify("alpha") for flux_dirn in range(3): chsp.find_cmax_cmin(flux_dirn, gammaDD, betaU, alpha) Ccode_kernel = outputC([chsp.cmax, chsp.cmin], ["cmax", "cmin"], "returnstring", params="outCverbose=False,CSE_sorting=none") Ccode_kernel = Ccode_kernel.replace("cmax", "*cmax").replace("cmin", "*cmin") Ccode_kernel = Ccode_kernel.replace("betaU0", "betaUi").replace( "betaU1", "betaUi").replace("betaU2", "betaUi") with open( os.path.join(Ccodesdir, "compute_cmax_cmin_dirn" + str(flux_dirn) + ".h"), "w") as file: file.write(Ccode_kernel) with open(os.path.join(Ccodesdir, name + ".h"), "w") as file: file.write(body)
def GiRaFFE_NRPy_Source_Terms(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. with open( os.path.join(Ccodesdir, "Lorenz_psi6phi_rhs__add_gauge_terms_to_A_i_rhs.h"), "w") as file: file.write(body)
def enforce_detgammabar_eq_detgammahat__generate_symbolic_expressions_and_C_code(): ###################################### # START: 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) # 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). cmd.mkdir(os.path.join(outdir,"rfm_files/")) 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() # Restore original finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order) # 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_FD_order_"+str(FD_order)+".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("Finished gamma constraint C codegen (FD_order="+str(FD_order)+") in " + str(end - start) + " seconds.")
def GiRaFFE_NRPy_FCVAL(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. with open( os.path.join(Ccodesdir, "interpolate_metric_gfs_to_cell_faces.h"), "w") as file: file.write(prefunc) desc = "Interpolate metric gridfunctions to cell faces" name = "interpolate_metric_gfs_to_cell_faces" interp_Cfunc = outCfunction( outfile="returnstring", desc=desc, name=name, params= "const paramstruct *params,REAL *auxevol_gfs,const int flux_dirn", preloop=""" int in_gf,out_gf; REAL Qm2,Qm1,Qp0,Qp1; """, body=""" for(int gf = 0;gf < num_metric_gfs;gf++) { in_gf = metric_gfs_list[gf]; out_gf = metric_gfs_face_list[gf]; for (int i2 = 2;i2 < Nxx_plus_2NGHOSTS2-1;i2++) { for (int i1 = 2;i1 < Nxx_plus_2NGHOSTS1-1;i1++) { for (int i0 = 2;i0 < Nxx_plus_2NGHOSTS0-1;i0++) { Qm2 = auxevol_gfs[IDX4S(in_gf,i0-2*kronecker_delta[flux_dirn][0],i1-2*kronecker_delta[flux_dirn][1],i2-2*kronecker_delta[flux_dirn][2])]; Qm1 = auxevol_gfs[IDX4S(in_gf,i0-kronecker_delta[flux_dirn][0],i1-kronecker_delta[flux_dirn][1],i2-kronecker_delta[flux_dirn][2])]; Qp0 = auxevol_gfs[IDX4S(in_gf,i0,i1,i2)]; Qp1 = auxevol_gfs[IDX4S(in_gf,i0+kronecker_delta[flux_dirn][0],i1+kronecker_delta[flux_dirn][1],i2+kronecker_delta[flux_dirn][2])]; auxevol_gfs[IDX4S(out_gf,i0,i1,i2)] = COMPUTE_FCVAL(Qm2,Qm1,Qp0,Qp1); } } } } """, rel_path_to_Cparams=os.path.join("../")) with open( os.path.join(Ccodesdir, "interpolate_metric_gfs_to_cell_faces.h"), "a") as file: file.write(interp_Cfunc)
def BaikalETK_C_kernels_codegen_onepart(NRPyDir=os.path.join(".."), 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] == "WhichParamSet": WhichParamSet = int(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) # Create directory for BaikalETK thorn & subdirectories in case they don't exist. outrootdir = ThornName cmd.mkdir(os.path.join(outrootdir)) outdir = os.path.join(outrootdir,"src") # Main C code output directory # Copy SIMD/SIMD_intrinsics.h to $outdir/SIMD/SIMD_intrinsics.h cmd.mkdir(os.path.join(outdir,"SIMD")) shutil.copy(os.path.join(NRPyDir,"SIMD/")+"SIMD_intrinsics.h",os.path.join(outdir,"SIMD/")) # 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. REAL = "CCTK_REAL" # Set REAL to CCTK_REAL, the ETK data type for # floating point precision (typically `double`) # 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: H = gri.register_gridfunctions("AUX","H") MU = ixp.register_gridfunctions_for_single_rank1("AUX", "MU") def BSSN_RHSs_Ricci__generate_symbolic_expressions(): ###################################### # START: 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) print("Generating symbolic expressions for BSSN RHSs and 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). cmd.mkdir(os.path.join(outdir,"rfm_files/")) 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 # Next compute Ricci tensor par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","False") 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("Finished BSSN symbolic expressions in "+str(end-start)+" seconds.") # Restore original finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order) # 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]) ] 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 [betaU,BSSN_RHSs_SymbExpressions,Ricci_SymbExpressions] def BSSN_RHSs_Ricci__generate_Ccode(which_expressions, all_RHSs_Ricci_exprs_list): betaU = all_RHSs_Ricci_exprs_list[0] BSSN_RHSs_SymbExpressions = all_RHSs_Ricci_exprs_list[1] Ricci_SymbExpressions = all_RHSs_Ricci_exprs_list[2] if(which_expressions == "BSSN_RHSs"): 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() BSSN_RHSs_string = fin.FD_outputC("returnstring",BSSN_RHSs_SymbExpressions, params="outCverbose=False,SIMD_enable=True", upwindcontrolvec=betaU) with open(os.path.join(outdir,"BSSN_RHSs_FD_order_"+str(FD_order)+"_enable_Tmunu_"+str(enable_stress_energy_source_terms)+".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","SIMD_width"], ["#pragma omp parallel for", "#include \"rfm_files/rfm_struct__SIMD_outer_read2.h\"", "#include \"rfm_files/rfm_struct__SIMD_outer_read1.h\""],"", "#include \"rfm_files/rfm_struct__SIMD_inner_read0.h\"\n"+BSSN_RHSs_string)) end = time.time() print("Finished BSSN_RHS C codegen (FD_order="+str(FD_order)+",Tmunu="+str(enable_stress_energy_source_terms)+") in " + str(end - start) + " seconds.") elif(which_expressions == "Ricci"): print("Generating C code for Ricci tensor (FD_order="+str(FD_order)+") in "+par.parval_from_str("reference_metric::CoordSystem")+" coordinates.") start = time.time() Ricci_string = fin.FD_outputC("returnstring", Ricci_SymbExpressions, params="outCverbose=False,SIMD_enable=True") with open(os.path.join(outdir,"BSSN_Ricci_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","SIMD_width"], ["#pragma omp parallel for", "#include \"rfm_files/rfm_struct__SIMD_outer_read2.h\"", "#include \"rfm_files/rfm_struct__SIMD_outer_read1.h\""],"", "#include \"rfm_files/rfm_struct__SIMD_inner_read0.h\"\n"+Ricci_string)) end = time.time() print("Finished Ricci C codegen (FD_order="+str(FD_order)+") in " + str(end - start) + " seconds.") else: print("Error: unexpected argument, "+str(which_expressions)+" to BSSN_RHSs_Ricci__generate_Ccode()") def BSSN_constraints__generate_symbolic_expressions_and_C_code(): ###################################### # START: GENERATE SYMBOLIC EXPRESSIONS ###################################### # START: 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) # 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] # Restore original finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order) # END: GENERATE SYMBOLIC EXPRESSIONS ###################################### 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_FD_order_"+str(FD_order)+"_enable_Tmunu_"+str(enable_stress_energy_source_terms)+".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)) end = time.time() print("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 # 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) # 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). cmd.mkdir(os.path.join(outdir,"rfm_files/")) 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() # Restore original finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order) # 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_FD_order_"+str(FD_order)+".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("Finished gamma constraint C codegen (FD_order="+str(FD_order)+") in " + str(end - start) + " seconds.") if WhichPart=="BSSN_RHSs": BSSN_RHSs_Ricci__generate_Ccode("BSSN_RHSs", BSSN_RHSs_Ricci__generate_symbolic_expressions()) elif WhichPart=="Ricci": BSSN_RHSs_Ricci__generate_Ccode("Ricci", BSSN_RHSs_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 # 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)) return outstr
def BSSN_RHSs_Ricci__generate_symbolic_expressions(): ###################################### # START: 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) print("Generating symbolic expressions for BSSN RHSs and 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). cmd.mkdir(os.path.join(outdir,"rfm_files/")) 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 # Next compute Ricci tensor par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","False") 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("Finished BSSN symbolic expressions in "+str(end-start)+" seconds.") # Restore original finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order) # 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]) ] 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 [betaU,BSSN_RHSs_SymbExpressions,Ricci_SymbExpressions]
import os, sys # Python module: used for system and OS specific commands import sympy as sp # Python module: used for symbolic expressions # Register NRPy+ root directory to the path nrpy_dir_path = os.path.join("..", "..") if nrpy_dir_path not in sys.path: sys.path.append(nrpy_dir_path) # Load NRPy+ modules from outputC import * # NRPy+ module: used to output sympy expressions to C import indexedexp as ixp # NRPy+ module: used to generate indexed expressions (e.g. g_{\mu\nu}) import cmdline_helper as cmd # NRPy+ module: used for command line features # Create the NRPy+ header file directory, if it doesn't already exist IGM_src_dir_path = os.path.join("..", "src") cmd.mkdir(os.path.join(IGM_src_dir_path, "NRPy_generated_headers")) NRPy_headers_dir_path = os.path.join(IGM_src_dir_path, "NRPy_generated_headers") # Set up a neat function to output the expressions to NRPy+ generated files def NRPy_IGM_write_to_file(filepath, filename, contents, precontents="", postcontents=""): with open(filepath, "w") as file: file.write(""" /* .-----------------------------------------------------------------------. * | This file was generated by NRPy+ for IllinoisGRMHD, as documented in: | * | Tutorial-IllinoisGRMHD__NRPyfied_IGM_expressions.ipynb |
def GiRaFFE_NRPy_A2B(outdir, gammaDD, AD, BU): cmd.mkdir(outdir) # Set spatial dimension (must be 3 for BSSN) DIM = 3 par.set_parval_from_str("grid::DIM", DIM) # Compute the sqrt of the three metric determinant. import GRHD.equations as gh gh.compute_sqrtgammaDET(gammaDD) # Import the Levi-Civita symbol and build the corresponding tensor. # We already have a handy function to define the Levi-Civita symbol in indexedexp.py LeviCivitaUUU = ixp.LeviCivitaTensorUUU_dim3_rank3(gh.sqrtgammaDET) AD_dD = ixp.declarerank2("AD_dD", "nosym") BU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j] # Write the code to compute derivatives with shifted stencils as needed. with open(os.path.join(outdir, "driver_AtoB.h"), "w") as file: file.write(prefunc) # Now, we'll also write some more auxiliary functions to handle the order-lowering method for A2B with open(os.path.join(outdir, "driver_AtoB.h"), "a") as file: file.write("""REAL relative_error(REAL a, REAL b) { if((a+b)!=0.0) { return 2.0*fabs(a-b)/fabs(a+b); } else { return 0.0; } } #define M2 0 #define M1 1 #define P0 2 #define P1 3 #define P2 4 #define CN4 0 #define CN2 1 #define UP2 2 #define DN2 3 #define UP1 4 #define DN1 5 void compute_Bx_pointwise(REAL *Bx, const REAL invdy, const REAL *Ay, const REAL invdz, const REAL *Az) { REAL dz_Ay,dy_Az; dz_Ay = invdz*((Ay[P1]-Ay[M1])*2.0/3.0 - (Ay[P2]-Ay[M2])/12.0); dy_Az = invdy*((Az[P1]-Az[M1])*2.0/3.0 - (Az[P2]-Az[M2])/12.0); Bx[CN4] = dy_Az - dz_Ay; dz_Ay = invdz*(Ay[P1]-Ay[M1])/2.0; dy_Az = invdy*(Az[P1]-Az[M1])/2.0; Bx[CN2] = dy_Az - dz_Ay; dz_Ay = invdz*(-1.5*Ay[P0]+2.0*Ay[P1]-0.5*Ay[P2]); dy_Az = invdy*(-1.5*Az[P0]+2.0*Az[P1]-0.5*Az[P2]); Bx[UP2] = dy_Az - dz_Ay; dz_Ay = invdz*(1.5*Ay[P0]-2.0*Ay[M1]+0.5*Ay[M2]); dy_Az = invdy*(1.5*Az[P0]-2.0*Az[M1]+0.5*Az[M2]); Bx[DN2] = dy_Az - dz_Ay; dz_Ay = invdz*(Ay[P1]-Ay[P0]); dy_Az = invdy*(Az[P1]-Az[P0]); Bx[UP1] = dy_Az - dz_Ay; dz_Ay = invdz*(Ay[P0]-Ay[M1]); dy_Az = invdy*(Az[P0]-Az[M1]); Bx[DN1] = dy_Az - dz_Ay; } #define TOLERANCE_A2B 1.0e-4 REAL find_accepted_Bx_order(REAL *Bx) { REAL accepted_val = Bx[CN4]; REAL Rel_error_o2_vs_o4 = relative_error(Bx[CN2],Bx[CN4]); REAL Rel_error_oCN2_vs_oDN2 = relative_error(Bx[CN2],Bx[DN2]); REAL Rel_error_oCN2_vs_oUP2 = relative_error(Bx[CN2],Bx[UP2]); if(Rel_error_o2_vs_o4 > TOLERANCE_A2B) { accepted_val = Bx[CN2]; if(Rel_error_o2_vs_o4 > Rel_error_oCN2_vs_oDN2 || Rel_error_o2_vs_o4 > Rel_error_oCN2_vs_oUP2) { // Should we use AND or OR in if statement? if(relative_error(Bx[UP2],Bx[UP1]) < relative_error(Bx[DN2],Bx[DN1])) { accepted_val = Bx[UP2]; } else { accepted_val = Bx[DN2]; } } } return accepted_val; } """) # order_lowering_body = """REAL AD0_1[5],AD0_2[5],AD1_2[5],AD1_0[5],AD2_0[5],AD2_1[5]; # const double gammaDD00 = auxevol_gfs[IDX4S(GAMMADD00GF, i0,i1,i2)]; # const double gammaDD01 = auxevol_gfs[IDX4S(GAMMADD01GF, i0,i1,i2)]; # const double gammaDD02 = auxevol_gfs[IDX4S(GAMMADD02GF, i0,i1,i2)]; # const double gammaDD11 = auxevol_gfs[IDX4S(GAMMADD11GF, i0,i1,i2)]; # const double gammaDD12 = auxevol_gfs[IDX4S(GAMMADD12GF, i0,i1,i2)]; # const double gammaDD22 = auxevol_gfs[IDX4S(GAMMADD22GF, i0,i1,i2)]; # AD0_2[M2] = in_gfs[IDX4S(AD0GF, i0,i1,i2-2)]; # AD0_2[M1] = in_gfs[IDX4S(AD0GF, i0,i1,i2-1)]; # AD0_1[M2] = in_gfs[IDX4S(AD0GF, i0,i1-2,i2)]; # AD0_1[M1] = in_gfs[IDX4S(AD0GF, i0,i1-1,i2)]; # AD0_1[P0] = AD0_2[P0] = in_gfs[IDX4S(AD0GF, i0,i1,i2)]; # AD0_1[P1] = in_gfs[IDX4S(AD0GF, i0,i1+1,i2)]; # AD0_1[P2] = in_gfs[IDX4S(AD0GF, i0,i1+2,i2)]; # AD0_2[P1] = in_gfs[IDX4S(AD0GF, i0,i1,i2+1)]; # AD0_2[P2] = in_gfs[IDX4S(AD0GF, i0,i1,i2+2)]; # AD1_2[M2] = in_gfs[IDX4S(AD1GF, i0,i1,i2-2)]; # AD1_2[M1] = in_gfs[IDX4S(AD1GF, i0,i1,i2-1)]; # AD1_0[M2] = in_gfs[IDX4S(AD1GF, i0-2,i1,i2)]; # AD1_0[M1] = in_gfs[IDX4S(AD1GF, i0-1,i1,i2)]; # AD1_2[P0] = AD1_0[P0] = in_gfs[IDX4S(AD1GF, i0,i1,i2)]; # AD1_0[P1] = in_gfs[IDX4S(AD1GF, i0+1,i1,i2)]; # AD1_0[P2] = in_gfs[IDX4S(AD1GF, i0+2,i1,i2)]; # AD1_2[P1] = in_gfs[IDX4S(AD1GF, i0,i1,i2+1)]; # AD1_2[P2] = in_gfs[IDX4S(AD1GF, i0,i1,i2+2)]; # AD2_1[M2] = in_gfs[IDX4S(AD2GF, i0,i1-2,i2)]; # AD2_1[M1] = in_gfs[IDX4S(AD2GF, i0,i1-1,i2)]; # AD2_0[M2] = in_gfs[IDX4S(AD2GF, i0-2,i1,i2)]; # AD2_0[M1] = in_gfs[IDX4S(AD2GF, i0-1,i1,i2)]; # AD2_0[P0] = AD2_1[P0] = in_gfs[IDX4S(AD2GF, i0,i1,i2)]; # AD2_0[P1] = in_gfs[IDX4S(AD2GF, i0+1,i1,i2)]; # AD2_0[P2] = in_gfs[IDX4S(AD2GF, i0+2,i1,i2)]; # AD2_1[P1] = in_gfs[IDX4S(AD2GF, i0,i1+1,i2)]; # AD2_1[P2] = in_gfs[IDX4S(AD2GF, i0,i1+2,i2)]; # const double invsqrtg = 1.0/sqrt(gammaDD00*gammaDD11*gammaDD22 # - gammaDD00*gammaDD12*gammaDD12 # + 2*gammaDD01*gammaDD02*gammaDD12 # - gammaDD11*gammaDD02*gammaDD02 # - gammaDD22*gammaDD01*gammaDD01); # REAL BU0[4],BU1[4],BU2[4]; # compute_Bx_pointwise(BU0,invdx2,AD1_2,invdx1,AD2_1); # compute_Bx_pointwise(BU1,invdx0,AD2_0,invdx2,AD0_2); # compute_Bx_pointwise(BU2,invdx1,AD0_1,invdx0,AD1_0); # auxevol_gfs[IDX4S(BU0GF, i0,i1,i2)] = find_accepted_Bx_order(BU0)*invsqrtg; # auxevol_gfs[IDX4S(BU1GF, i0,i1,i2)] = find_accepted_Bx_order(BU1)*invsqrtg; # auxevol_gfs[IDX4S(BU2GF, i0,i1,i2)] = find_accepted_Bx_order(BU2)*invsqrtg; # """ # Here, we'll use the outCfunction() function to output a function that will compute the magnetic field # on the interior. Then, we'll add postloop code to handle the ghostzones. desc = "Compute the magnetic field from the vector potential everywhere, including ghostzones" name = "driver_A_to_B" driver_Ccode = outCfunction( outfile="returnstring", desc=desc, name=name, params= "const paramstruct *restrict params,REAL *restrict in_gfs,REAL *restrict auxevol_gfs", body=fin.FD_outputC("returnstring", [ lhrh(lhs=gri.gfaccess("out_gfs", "BU0"), rhs=BU[0]), lhrh(lhs=gri.gfaccess("out_gfs", "BU1"), rhs=BU[1]), lhrh(lhs=gri.gfaccess("out_gfs", "BU2"), rhs=BU[2]) ]), # body = order_lowering_body, postloop=""" int imin[3] = { NGHOSTS_A2B, NGHOSTS_A2B, NGHOSTS_A2B }; int imax[3] = { NGHOSTS+Nxx0, NGHOSTS+Nxx1, NGHOSTS+Nxx2 }; // Now, we loop over the ghostzones to calculate the magnetic field there. for(int which_gz = 0; which_gz < NGHOSTS_A2B; which_gz++) { // After updating each face, adjust imin[] and imax[] // to reflect the newly-updated face extents. compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2]); imin[0]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2]); imax[0]++; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2]); imin[1]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2]); imax[1]++; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2]); imin[2]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1); imax[2]++; } """, loopopts="InteriorPoints", rel_path_to_Cparams=os.path.join("../")).replace( "= NGHOSTS", "= NGHOSTS_A2B").replace( "NGHOSTS+Nxx0", "Nxx_plus_2NGHOSTS0-NGHOSTS_A2B").replace( "NGHOSTS+Nxx1", "Nxx_plus_2NGHOSTS1-NGHOSTS_A2B").replace( "NGHOSTS+Nxx2", "Nxx_plus_2NGHOSTS2-NGHOSTS_A2B") with open(os.path.join(outdir, "driver_AtoB.h"), "a") as file: file.write(driver_Ccode)
def GiRaFFE_NRPy_BCs(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. with open(os.path.join(Ccodesdir,name),"w") as file: file.write(body)
def Set_up_CurviBoundaryConditions(Ccodesdir, verbose=True, Cparamspath=os.path.join("../"), enable_copy_of_static_Ccodes=True, BoundaryCondition="QuadraticExtrapolation"): # Step P0: Check that Ccodesdir is not the same as CurviBoundaryConditions/boundary_conditions, # to prevent trusted versions of these C codes from becoming contaminated. if os.path.join(Ccodesdir) == os.path.join("CurviBoundaryConditions", "boundary_conditions"): print( "Error: Tried to output boundary conditions C code into CurviBoundaryConditions/boundary_conditions," " which is not allowed, to prevent trusted versions of these C codes from becoming contaminated." ) sys.exit(1) # Step P1: Create the C codes output directory & copy static CurviBC files # from CurviBoundaryConditions/boundary_conditions to Ccodesdir/ if enable_copy_of_static_Ccodes: cmd.mkdir(os.path.join(Ccodesdir)) # Choosing boundary condition drivers with in NRPy+ # - current options are Quadratic Polynomial Extrapolation for any coordinate system, # and the Sommerfeld boundary condition for only cartesian coordinates if str(BoundaryCondition) == "QuadraticExtrapolation": for file in [ "apply_bcs_curvilinear.h", "BCs_data_structs.h", "bcstruct_freemem.h", "CurviBC_include_Cfunctions.h", "driver_bcstruct.h", "set_bcstruct.h", "set_up__bc_gz_map_and_parity_condns.h" ]: shutil.copy( os.path.join("CurviBoundaryConditions", "boundary_conditions", file), os.path.join(Ccodesdir)) with open(os.path.join(Ccodesdir, "CurviBC_include_Cfunctions.h"), "a") as file: file.write("\n#include \"apply_bcs_curvilinear.h\"") elif str(BoundaryCondition) == "Sommerfeld": for file in [ "apply_bcs_sommerfeld.h", "BCs_data_structs.h", "bcstruct_freemem.h", "CurviBC_include_Cfunctions.h", "driver_bcstruct.h", "set_bcstruct.h", "set_up__bc_gz_map_and_parity_condns.h" ]: shutil.copy( os.path.join("CurviBoundaryConditions", "boundary_conditions", file), os.path.join(Ccodesdir)) with open(os.path.join(Ccodesdir, "CurviBC_include_Cfunctions.h"), "a") as file: file.write("\n#include \"apply_bcs_sommerfeld.h\"") elif str(BoundaryCondition) == "QuadraticExtrapolation&Sommerfeld": for file in [ "apply_bcs_curvilinear.h", "apply_bcs_sommerfeld.h", "BCs_data_structs.h", "bcstruct_freemem.h", "CurviBC_include_Cfunctions.h", "driver_bcstruct.h", "set_bcstruct.h", "set_up__bc_gz_map_and_parity_condns.h" ]: shutil.copy( os.path.join("CurviBoundaryConditions", "boundary_conditions", file), os.path.join(Ccodesdir)) with open(os.path.join(Ccodesdir, "CurviBC_include_Cfunctions.h"), "a") as file: file.write("\n#include \"apply_bcs_sommerfeld.h\"" + "\n#include \"apply_bcs_curvilinear.h\"") else: print( "ERROR: Only Quadratic Polynomial Extrapolation (QuadraticExtrapolation) and Sommerfeld boundary conditions are currently supported\n" ) sys.exit(1) # Step P2: Output correct #include for set_Cparameters.h to # Ccodesdir/boundary_conditions/RELATIVE_PATH__set_Cparameters.h with open(os.path.join(Ccodesdir, "RELATIVE_PATH__set_Cparameters.h"), "w") as file: file.write( "#include \"" + Cparamspath + "/set_Cparameters.h\"\n" ) # #include's may include forward slashes for paths, even in Windows. # Step 0: Set up reference metric in case it hasn't already been set up. # (Doing it twice hurts nothing). rfm.reference_metric() # Step 1: Set unit-vector dot products (=parity) for each of the 10 parity condition types parity = ixp.zerorank1(DIM=10) UnitVectors_inner = ixp.zerorank2() xx0_inbounds, xx1_inbounds, xx2_inbounds = sp.symbols( "xx0_inbounds xx1_inbounds xx2_inbounds", real=True) for i in range(3): for j in range(3): UnitVectors_inner[i][j] = rfm.UnitVectors[i][j].subs( rfm.xx[0], xx0_inbounds).subs(rfm.xx[1], xx1_inbounds).subs(rfm.xx[2], xx2_inbounds) # Type 0: scalar parity[0] = sp.sympify(1) # Type 1: i0-direction vector or one-form # Type 2: i1-direction vector or one-form # Type 3: i2-direction vector or one-form for i in range(3): for Type in range(1, 4): parity[Type] += rfm.UnitVectors[Type - 1][i] * UnitVectors_inner[Type - 1][i] # Type 4: i0i0-direction rank-2 tensor # parity[4] = parity[1]*parity[1] # Type 5: i0i1-direction rank-2 tensor # Type 6: i0i2-direction rank-2 tensor # Type 7: i1i1-direction rank-2 tensor # Type 8: i1i2-direction rank-2 tensor # Type 9: i2i2-direction rank-2 tensor count = 4 for i in range(3): for j in range(i, 3): parity[count] = parity[i + 1] * parity[j + 1] count = count + 1 lhs_strings = [] for i in range(10): lhs_strings.append("parity[" + str(i) + "]") outputC( parity, lhs_strings, os.path.join(Ccodesdir, "parity_conditions_symbolic_dot_products.h")) # Step 2.a: Generate Ccodesdir/gridfunction_defines.h file, # containing human-readable gridfunction aliases evolved_variables_list, auxiliary_variables_list, auxevol_variables_list = gri.output__gridfunction_defines_h__return_gf_lists( Ccodesdir) # Step 2.b: set the parity conditions on all gridfunctions in gf_list, # based on how many digits are at the end of their names def set_parity_types(list_of_gf_names): parity_type = [] for name in list_of_gf_names: for gf in gri.glb_gridfcs_list: if gf.name == name: parity_type__orig_len = len(parity_type) if gf.DIM < 3 or gf.DIM > 4: print( "Error: Cannot currently specify parity conditions on gridfunctions with DIM<3 or >4." ) sys.exit(1) if gf.rank == 0: parity_type.append(0) elif gf.rank == 1: if gf.DIM == 3: parity_type.append( int(gf.name[-1]) + 1 ) # = 1 for e.g., beta^0; = 2 for e.g., beta^1, etc. elif gf.DIM == 4: parity_type.append( int(gf.name[-1]) ) # = 0 for e.g., b4^0; = 1 for e.g., beta^1, etc. elif gf.rank == 2: if gf.DIM == 3: # element of a list; a[-2] the # second-to-last element, etc. idx0 = gf.name[-2] idx1 = gf.name[-1] if idx0 == "0" and idx1 == "0": parity_type.append(4) elif (idx0 == "0" and idx1 == "1") or (idx0 == "1" and idx1 == "0"): parity_type.append(5) elif (idx0 == "0" and idx1 == "2") or (idx0 == "2" and idx1 == "0"): parity_type.append(6) elif idx0 == "1" and idx1 == "1": parity_type.append(7) elif (idx0 == "1" and idx1 == "2") or (idx0 == "2" and idx1 == "1"): parity_type.append(8) elif idx0 == "2" and idx1 == "2": parity_type.append(9) elif gf.DIM == 4: idx0 = gf.name[-2] idx1 = gf.name[-1] # g4DD00 = g_{tt} : parity type = 0 # g4DD01 = g_{tx} : parity type = 1 # g4DD02 = g_{ty} : parity type = 2 # g4DD0a = g_{ta} : parity type = a if idx0 == "0": parity_type.append(int(idx1)) elif idx1 == "0": parity_type.append(int(idx0)) if idx0 == "1" and idx1 == "1": parity_type.append(4) elif (idx0 == "1" and idx1 == "2") or (idx0 == "2" and idx1 == "1"): parity_type.append(5) elif (idx0 == "1" and idx1 == "3") or (idx0 == "3" and idx1 == "1"): parity_type.append(6) elif idx0 == "2" and idx1 == "2": parity_type.append(7) elif (idx0 == "2" and idx1 == "3") or (idx0 == "3" and idx1 == "2"): parity_type.append(8) elif idx0 == "3" and idx1 == "3": parity_type.append(9) if len(parity_type) == parity_type__orig_len: print( "Error: Could not figure out parity type for " + gf.gftype + " gridfunction: " + gf.name, gf.DIM, gf.name[-2], gf.name[-1], gf.rank) sys.exit(1) if len(parity_type) != len(list_of_gf_names): print( "Error: For some reason the length of the parity types list did not match the length of the gf list." ) sys.exit(1) return parity_type evol_parity_type = set_parity_types(evolved_variables_list) aux_parity_type = set_parity_types(auxiliary_variables_list) auxevol_parity_type = set_parity_types(auxevol_variables_list) # Step 2.c: Output all gridfunctions to Ccodesdir+"/gridfunction_defines.h" # ... then append to the file the parity type for each gridfunction. with open(os.path.join(Ccodesdir, "gridfunction_defines.h"), "a") as file: file.write("\n\n/* PARITY TYPES FOR ALL GRIDFUNCTIONS.\n") file.write( " SEE \"Tutorial-Start_to_Finish-Curvilinear_BCs.ipynb\" FOR DEFINITIONS. */\n" ) if len(evolved_variables_list) > 0: file.write("const int8_t evol_gf_parity[" + str(len(evolved_variables_list)) + "] = { ") for i in range(len(evolved_variables_list) - 1): file.write(str(evol_parity_type[i]) + ", ") file.write( str(evol_parity_type[len(evolved_variables_list) - 1]) + " };\n") if len(auxiliary_variables_list) > 0: file.write("const int8_t aux_gf_parity[" + str(len(auxiliary_variables_list)) + "] = { ") for i in range(len(auxiliary_variables_list) - 1): file.write(str(aux_parity_type[i]) + ", ") file.write( str(aux_parity_type[len(auxiliary_variables_list) - 1]) + " };\n") if len(auxevol_variables_list) > 0: file.write("const int8_t auxevol_gf_parity[" + str(len(auxevol_variables_list)) + "] = { ") for i in range(len(auxevol_variables_list) - 1): file.write(str(auxevol_parity_type[i]) + ", ") file.write( str(auxevol_parity_type[len(auxevol_variables_list) - 1]) + " };\n") if verbose == True: import textwrap wrapper = textwrap.TextWrapper(initial_indent="", subsequent_indent=" ", width=75) def print_parity_list(gf_type, variable_names, parity_types): outstr = "" if len(variable_names) != 0: outstr += gf_type + " parity: ( " for i in range(len(variable_names)): outstr += variable_names[i] + ":" + str(parity_types[i]) if i != len(variable_names) - 1: outstr += ", " outstr += " )" print(wrapper.fill(outstr)) print_parity_list("Evolved", evolved_variables_list, evol_parity_type) print_parity_list("Auxiliary", auxiliary_variables_list, aux_parity_type) print_parity_list("AuxEvol", auxevol_variables_list, auxevol_parity_type) # Step 3: Find the Eigen-Coordinate and set up the Eigen-Coordinate's reference metric: CoordSystem_orig = par.parval_from_str("reference_metric::CoordSystem") par.set_parval_from_str("reference_metric::CoordSystem", rfm.get_EigenCoord()) rfm.reference_metric() # Step 4: Output C code for the Eigen-Coordinate mapping from xx->Cartesian: rfm.xxCart_h("EigenCoord_xxCart", os.path.join(Cparamspath, "set_Cparameters.h"), os.path.join(Ccodesdir, "EigenCoord_xxCart.h")) # Step 5: Output the Eigen-Coordinate mapping from Cartesian->xx: # Step 5.a: Sanity check: First make sure that rfm.Cart_to_xx has been set. Error out if not! if rfm.Cart_to_xx[0] == 0 or rfm.Cart_to_xx[1] == 0 or rfm.Cart_to_xx[ 2] == 0: print( "ERROR: rfm.Cart_to_xx[], which maps Cartesian -> xx, has not been set for" ) print(" reference_metric::CoordSystem = " + par.parval_from_str("reference_metric::CoordSystem")) print( " Boundary conditions in curvilinear coordinates REQUIRE this be set." ) sys.exit(1) # Step 5.b: Output C code for the Eigen-Coordinate mapping from Cartesian->xx: outputC([rfm.Cart_to_xx[0], rfm.Cart_to_xx[1], rfm.Cart_to_xx[2]], [ "Cart_to_xx0_inbounds", "Cart_to_xx1_inbounds", "Cart_to_xx2_inbounds" ], os.path.join(Ccodesdir, "EigenCoord_Cart_to_xx.h")) # Step 6: Restore reference_metric::CoordSystem back to the original CoordSystem par.set_parval_from_str("reference_metric::CoordSystem", CoordSystem_orig) rfm.reference_metric()
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); } """)
def GiRaFFE_NRPy_A2B(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. with open(os.path.join(Ccodesdir, "compute_B_and_Bstagger_from_A.h"), "w") as file: file.write(body)
paramslist.sort() # Sort the list alphabetically. ############################### ############################### # Step 2: Generate all C-code kernels for Baikal and BaikalVacuum, # in parallel if supported by this OS. nrpy_dir_path = os.path.join(".") if nrpy_dir_path not in sys.path: sys.path.append(nrpy_dir_path) # Create all output directories if they do not yet exist import cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface for ThornName in ["Baikal", "BaikalVacuum"]: outrootdir = ThornName cmd.mkdir(os.path.join(outrootdir)) outdir = os.path.join(outrootdir, "src") # Main C code output directory # Copy SIMD/SIMD_intrinsics.h to $outdir/SIMD/SIMD_intrinsics.h, replacing # the line "#define REAL_SIMD_ARRAY REAL" with "#define REAL_SIMD_ARRAY CCTK_REAL" # (since REAL is undefined in the ETK, but CCTK_REAL takes its place) cmd.mkdir(os.path.join(outdir, "SIMD")) import fileinput f = fileinput.input( os.path.join(nrpy_dir_path, "SIMD", "SIMD_intrinsics.h")) with open(os.path.join(outdir, "SIMD", "SIMD_intrinsics.h"), "w") as outfile: for line in f: outfile.write( line.replace("#define REAL_SIMD_ARRAY REAL", "#define REAL_SIMD_ARRAY CCTK_REAL"))
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_Afield_flux(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. with open(os.path.join(Ccodesdir,"A_i_rhs_no_gauge_terms.h"),"w") as file: file.write(r"""/* Compute the part of A_i_rhs that excludes the gauge terms. I.e., we set * A_i_rhs = \partial_t A_i = \psi^{6} (v^z B^x - v^x B^z) here. */ static void A_i_rhs_no_gauge_terms(const int A_dirn,const paramstruct *params,gf_and_gz_struct *out_prims_r,gf_and_gz_struct *out_prims_l, REAL *psi6_pointer,REAL *cmax_1,REAL *cmin_1,REAL *cmax_2,REAL *cmin_2, REAL *A3_rhs) { #include "../set_Cparameters.h" // If A_dirn=1, then v1_offset=1 (v1=VY) and v2_offset=2 (v2=VZ) // If A_dirn=2, then v1_offset=2 (v1=VZ) and v2_offset=0 (v2=VX) // If A_dirn=3, then v1_offset=0 (v1=VX) and v2_offset=1 (v2=VY) const int v1_offset = ((A_dirn-1)+1)%3, v2_offset = ((A_dirn-1)+2)%3; const REAL *v1rr=out_prims_r[VXR+v1_offset].gf, *v2rr=out_prims_r[VXR+v2_offset].gf; const REAL *v1rl=out_prims_l[VXR+v1_offset].gf, *v2rl=out_prims_l[VXR+v2_offset].gf; const REAL *v1lr=out_prims_r[VXL+v1_offset].gf, *v2lr=out_prims_r[VXL+v2_offset].gf; const REAL *v1ll=out_prims_l[VXL+v1_offset].gf, *v2ll=out_prims_l[VXL+v2_offset].gf; const REAL *B1r=out_prims_r[BX_STAGGER+v1_offset].gf, *B1l=out_prims_l[BX_STAGGER+v1_offset].gf; const REAL *B2r=out_prims_r[BX_STAGGER+v2_offset].gf, *B2l=out_prims_l[BX_STAGGER+v2_offset].gf; /**** V DEPENDENCIES ****/ /* In the case of Ax_rhs, we need v{y,z}{r,l} at (i,j+1/2,k+1/2). * However, v{y,z}{r,l}{r,l} are defined at (i,j-1/2,k-1/2), so * v{y,z}{r,l} at (i,j+1/2,k+1/2) is stored at v{y,z}{r,l}{r,l}(i,j+1,k+1). * In the case of Ay_rhs, we need v{x,z}{r,l} at (i+1/2,j,k+1/2). * However, v{x,z}{r,l}{r,l} are defined at (i-1/2,j,k-1/2), so * v{x,z}{r,l} at (i+1/2,j,k+1/2) is stored at v{x,z}{r,l}{r,l}(i+1,j,k+1). * In the case of Az_rhs, we need v{x,y}{r,l} at (i+1/2,j+1/2,k). * However, v{x,y}{r,l}{r,l} are defined at (i-1/2,j-1/2,k), so * v{x,y}{r,l} at (i+1/2,j+1/2,k) is stored at v{x,y}{r,l}{r,l}(i+1,j+1,k). */ static const int vs_ijk_offset[4][3] = { {0,0,0} , {0,1,1} , {1,0,1} , {1,1,0} }; // Note that vs_ijk_offset[0] is UNUSED; we choose a 1-offset for convenience. /**** B DEPENDENCIES ****/ /* In the case of Ax_rhs, we need B{y,z}{r,l} at (i,j+1/2,k+1/2). * However, By_stagger{r,l} is defined at (i,j+1/2,k-1/2), and * Bz_stagger{r,l} is defined at (i,j-1/2,k+1/2), so * By_stagger{r,l} at (i,j+1/2,k+1/2) is stored at By_stagger{r,l}(i,j,k+1), and * Bz_stagger{r,l} at (i,j+1/2,k+1/2) is stored at Bz_stagger{r,l}(i,j+1,k). * In the case of Ay_rhs, we need B{z,x}_stagger{r,l} at (i+1/2,j,k+1/2). * However, Bz_stagger{r,l} is defined at (i-1/2,j,k+1/2), and * Bx_stagger{r,l} is defined at (i+1/2,j,k-1/2), so * Bz_stagger{r,l} at (i+1/2,j,k+1/2) is stored at Bz_stagger{r,l}(i+1,j,k), and * Bx_stagger{r,l} at (i+1/2,j,k+1/2) is stored at Bx_stagger{r,l}(i,j,k+1). * In the case of Az_rhs, we need B{x,y}_stagger{r,l} at (i+1/2,j+1/2,k). * However, Bx_stagger{r,l} is defined at (i+1/2,j-1/2,k), and * By_stagger{r,l} is defined at (i-1/2,j+1/2,k), so * Bx_stagger{r,l} at (i+1/2,j+1/2,k) is stored at Bx_stagger{r,l}(i,j+1,k), and * By_stagger{r,l} at (i+1/2,j+1/2,k) is stored at By_stagger{r,l}(i+1,j,k). */ static const int B1_ijk_offset[4][3] = { {0,0,0} , {0,0,1} , {1,0,0} , {0,1,0} }; // Note that B1_ijk_offset[0] is UNUSED; we choose a 1-offset for convenience. static const int B2_ijk_offset[4][3] = { {0,0,0} , {0,1,0} , {0,0,1} , {1,0,0} }; // Note that B2_ijk_offset[0] is UNUSED; we choose a 1-offset for convenience. #pragma omp parallel for for(int k=NGHOSTS;k<Nxx_plus_2NGHOSTS2-NGHOSTS;k++) for(int j=NGHOSTS;j<Nxx_plus_2NGHOSTS1-NGHOSTS;j++) for(int i=NGHOSTS;i<Nxx_plus_2NGHOSTS0-NGHOSTS;i++) { const int index=IDX3S(i,j,k); // The following lines set the indices appropriately. See justification in exorbitant comments above. const int index_v =IDX3S(i+vs_ijk_offset[A_dirn][0],j+vs_ijk_offset[A_dirn][1],k+vs_ijk_offset[A_dirn][2]); const int index_B1=IDX3S(i+B1_ijk_offset[A_dirn][0],j+B1_ijk_offset[A_dirn][1],k+B1_ijk_offset[A_dirn][2]); const int index_B2=IDX3S(i+B2_ijk_offset[A_dirn][0],j+B2_ijk_offset[A_dirn][1],k+B2_ijk_offset[A_dirn][2]); // Stores 1/sqrt(gamma)==exp(6 phi) at (i+1/2,j+1/2,k) for Az, (i+1/2,j,k+1/2) for Ay, and (i,j+1/2,k+1/2) for Az. const REAL psi6_interped=psi6_pointer[index]; const REAL B1lL = B1l[index_B1]; const REAL B1rL = B1r[index_B1]; const REAL B2lL = B2l[index_B2]; const REAL B2rL = B2r[index_B2]; const REAL A3_rhs_rr = psi6_interped*(v1rr[index_v]*B2rL - v2rr[index_v]*B1rL); const REAL A3_rhs_rl = psi6_interped*(v1rl[index_v]*B2rL - v2rl[index_v]*B1lL); const REAL A3_rhs_lr = psi6_interped*(v1lr[index_v]*B2lL - v2lr[index_v]*B1rL); const REAL A3_rhs_ll = psi6_interped*(v1ll[index_v]*B2lL - v2ll[index_v]*B1lL); // All variables for the A_i_rhs computation are now at the appropriate staggered point, // so it's time to compute the HLL flux! // Note that with PPM, cmin and cmax are defined between ijk=3 and ijk<cctk_lsh[]-2 for all directions. const REAL cmax_1L = cmax_1[index_B2]; const REAL cmin_1L = cmin_1[index_B2]; const REAL cmax_2L = cmax_2[index_B1]; const REAL cmin_2L = cmin_2[index_B1]; const REAL B1tilder_minus_B1tildel = psi6_interped*( B1rL - B1lL ); const REAL B2tilder_minus_B2tildel = psi6_interped*( B2rL - B2lL ); /*--------------------------- * Implement 2D HLL flux * [see Del Zanna, Bucciantini & Londrillo A&A 400, 397 (2003), Eq. (44)] * * Note that cmax/cmin (\alpha^{\pm} as defined in that paper) is at a slightly DIFFERENT * point than that described in the Del Zanna et al paper (e.g., (i+1/2,j,k) instead of * (i+1/2,j+1/2,k) for F3). Yuk Tung Liu discussed this point with M. Shibata, * who found that the effect is negligible. ---------------------------*/ A3_rhs[index] = (cmax_1L*cmax_2L*A3_rhs_ll + cmax_1L*cmin_2L*A3_rhs_lr + cmin_1L*cmax_2L*A3_rhs_rl + cmin_1L*cmin_2L*A3_rhs_rr) /( (cmax_1L+cmin_1L)*(cmax_2L+cmin_2L) ) - cmax_1L*cmin_1L*(B2tilder_minus_B2tildel)/(cmax_1L+cmin_1L) + cmax_2L*cmin_2L*(B1tilder_minus_B1tildel)/(cmax_2L+cmin_2L); } } """)
from outputC import outCfunction, lhrh, add_to_Cfunction_dict, outC_function_outdir_dict, outC_function_dict, outC_function_prototype_dict, outC_function_master_list, outC_function_element # NRPy+: Core C code output module import finite_difference as fin # NRPy+: Finite difference C code generation module import NRPy_param_funcs as par # NRPy+: Parameter interface import grid as gri # NRPy+: Functions having to do with numerical grids import loop as lp # NRPy+: Generate C code loops import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support import reference_metric as rfm # NRPy+: Reference metric support import cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface thismodule = __name__ par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER",2) out_dir = os.path.join("GiRaFFE_standalone_Ccodes") cmd.mkdir(out_dir) 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. 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
def GiRaFFE_NRPy_Afield_flux(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. with open(os.path.join(Ccodesdir, "A_i_rhs_no_gauge_terms.h"), "w") as file: file.write(body)
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 construct_Makefile_from_outC_function_dict(Ccodesrootdir, exec_name, uses_free_parameters_h=False, compiler_opt_option="fastdebug", addl_CFLAGS=None, addl_libraries=None, mkdir_Ccodesrootdir=True, use_make=True, CC="gcc"): if "main" not in outC_function_dict: print("construct_Makefile_from_outC_function_dict() error: C codes will not compile if main() function not defined!") print(" Make sure that the main() function registered to outC_function_dict has name \"main\".") sys.exit(1) if not os.path.isdir(Ccodesrootdir): if not mkdir_Ccodesrootdir: print("Error (in construct_Makefile_from_outC_function_dict): Directory \"" + Ccodesrootdir + "\" does not exist.") sys.exit(1) else: import cmdline_helper as cmd cmd.mkdir(Ccodesrootdir) Makefile_list_of_files = [] def add_to_Makefile(Ccodesrootdir, path_and_file): Makefile_list_of_files.append(path_and_file) return os.path.join(Ccodesrootdir, path_and_file) for key, item in outC_function_dict.items(): # Convention: Output all C files ending in _gridN into the gridN/ subdirectory. if "grid" in key.split("_")[-1] and not "grids" in key: subdir = key.split("_")[-1] with open(add_to_Makefile(Ccodesrootdir, os.path.join(subdir, key+".c")), "w") as file: file.write(item) elif outC_function_outdir_dict[key] != "default": subdir = outC_function_outdir_dict[key] with open(add_to_Makefile(Ccodesrootdir, os.path.join(subdir, key+".c")), "w") as file: file.write(item) else: with open(add_to_Makefile(Ccodesrootdir, os.path.join(key+".c")), "w") as file: file.write(item) CFLAGS = " -O2 -march=native -g -fopenmp -Wall -Wno-unused-variable" DEBUGCFLAGS = " -O2 -g -Wall -Wno-unused-variable -Wno-unknown-pragmas" # OpenMP requires -fopenmp, and when disabling # -fopenmp, unknown pragma warnings appear. # -Wunknown-pragmas silences these warnings FASTCFLAGS = " -O2 -march=native -fopenmp -Wall -Wno-unused-variable" if CC == "gcc": CFLAGS += " -std=gnu99" DEBUGCFLAGS += " -std=gnu99" FASTCFLAGS += " -std=gnu99" CHOSEN_CFLAGS = CFLAGS if compiler_opt_option == "debug": CHOSEN_CFLAGS = DEBUGCFLAGS elif compiler_opt_option == "fast": CHOSEN_CFLAGS = FASTCFLAGS if addl_CFLAGS is not None: if not isinstance(addl_CFLAGS, list): print("Error: construct_Makefile_from_outC_function_dict(): addl_CFLAGS must be a list!") sys.exit(1) for FLAG in addl_CFLAGS: CHOSEN_CFLAGS += " "+FLAG all_str = exec_name + " " dep_list = [] compile_list = [] for c_file in Makefile_list_of_files: object_file = c_file.replace(".c", ".o") all_str += " " + object_file addl_headers = "" if uses_free_parameters_h: if c_file == "main.c": addl_headers += " free_parameters.h" dep_list.append(object_file + ": " + c_file + addl_headers) compile_list.append("\t$(CC) $(CFLAGS) -c " + c_file + " -o " + object_file) linked_libraries = " -lm" if addl_libraries is not None: if not isinstance(addl_libraries, list): print("Error: construct_Makefile_from_outC_function_dict(): addl_libraries must be a list!") sys.exit(1) for lib in addl_libraries: linked_libraries += " " + lib if use_make: with open(os.path.join(Ccodesrootdir, "Makefile"), "w") as Makefile: Makefile.write("""CC = """ + CC + """ CFLAGS = """ + CHOSEN_CFLAGS + """ #CFLAGS = """ + CFLAGS + """ #CFLAGS = """ + DEBUGCFLAGS + """ #CFLAGS = """ + FASTCFLAGS + "\n") Makefile.write("all: " + all_str + "\n") for idx, dep in enumerate(dep_list): Makefile.write(dep + "\n") Makefile.write(compile_list[idx] + "\n\n") Makefile.write(exec_name + ": " + all_str.replace(exec_name, "") + "\n") Makefile.write("\t$(CC) $(CFLAGS) main.c " + all_str.replace(exec_name, "").replace("main.o", "") + " -o " + exec_name + linked_libraries + "\n") Makefile.write("\nclean:\n\trm -f *.o */*.o *~ */*~ ./#* *.txt *.dat *.avi *.png " + exec_name + "\n") else: with open(os.path.join(Ccodesrootdir, "backup_script_nomake.sh"), "w") as backup: for compile_line in compile_list: backup.write(compile_line.replace("$(CC)", CC).replace("$(CFLAGS)", CFLAGS).replace("\t", "") + "\n") backup.write(CC + " " + CFLAGS + " main.c " + all_str.replace(exec_name, "").replace("main.o", "") + " -o " + exec_name + linked_libraries + "\n") os.chmod(os.path.join(Ccodesrootdir, "backup_script_nomake.sh"), stat.S_IRWXU)
def GiRaFFE_NRPy_A2B(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. with open(os.path.join(Ccodesdir, "compute_B_and_Bstagger_from_A.h"), "w") as file: file.write("""#define LOOP_DEFINE_SIMPLE \\ _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++) void GiRaFFE_compute_B_and_Bstagger_from_A(const paramstruct *params, const REAL *gxx, const REAL *gxy, const REAL *gxz, const REAL *gyy, const REAL *gyz,const REAL *gzz, REAL *psi3_bssn, const REAL *Ax, const REAL *Ay, const REAL *Az, REAL *Bx, REAL *By, REAL *Bz, REAL *Bx_stagger, REAL *By_stagger, REAL *Bz_stagger) { #include "../set_Cparameters.h" LOOP_DEFINE_SIMPLE { const int index=IDX3S(i,j,k); psi3_bssn[index] = sqrt(sqrt( gxx[index]*gyy[index]*gzz[index] - gxx[index]*gyz[index]*gyz[index] +2*gxy[index]*gxz[index]*gyz[index] - gyy[index]*gxz[index]*gxz[index] - gzz[index]*gxy[index]*gxy[index])); } LOOP_DEFINE_SIMPLE { // Look Mom, no if() statements! const int shiftedim1 = (i-1)*(i!=0); // This way, i=0 yields shiftedim1=0 and shiftedi=1, used below for our COPY boundary condition. const int shiftedi = shiftedim1+1; const int shiftedjm1 = (j-1)*(j!=0); const int shiftedj = shiftedjm1+1; const int shiftedkm1 = (k-1)*(k!=0); const int shiftedk = shiftedkm1+1; int index,indexim1,indexjm1,indexkm1; const int actual_index = IDX3S(i,j,k); const REAL Psim3 = 1.0/psi3_bssn[actual_index]; // For the lower boundaries, the following applies a "copy" // boundary condition on Bi_stagger where needed. // E.g., Bx_stagger(i,jmin,k) = Bx_stagger(i,jmin+1,k) // We find the copy BC works better than extrapolation. // For the upper boundaries, we do the following copy: // E.g., Psi(imax+1,j,k)=Psi(imax,j,k) /**************/ /* Bx_stagger */ /**************/ index = IDX3S(i,shiftedj,shiftedk); indexjm1 = IDX3S(i,shiftedjm1,shiftedk); indexkm1 = IDX3S(i,shiftedj,shiftedkm1); // Set Bx_stagger = \partial_y A_z - partial_z A_y // "Grid" Ax(i,j,k) is actually Ax(i,j+1/2,k+1/2) // "Grid" Ay(i,j,k) is actually Ay(i+1/2,j,k+1/2) // "Grid" Az(i,j,k) is actually Ay(i+1/2,j+1/2,k) // Therefore, the 2nd order derivative \partial_z A_y at (i+1/2,j,k) is: // ["Grid" Ay(i,j,k) - "Grid" Ay(i,j,k-1)]/dZ Bx_stagger[actual_index] = (Az[index]-Az[indexjm1])*invdx1 - (Ay[index]-Ay[indexkm1])*invdx2; // Now multiply Bx_stagger by 1/sqrt(gamma(i+1/2,j,k)]) = 1/sqrt(1/2 [gamma + gamma_ip1]) = exp(-6 x 1/2 [phi + phi_ip1] ) const int imax_minus_i = (Nxx_plus_2NGHOSTS0-1)-i; const int indexip1jk = IDX3S(i + ( (imax_minus_i > 0) - (0 > imax_minus_i) ),j,k); Bx_stagger[actual_index] *= Psim3/psi3_bssn[indexip1jk]; /**************/ /* By_stagger */ /**************/ index = IDX3S(shiftedi,j,shiftedk); indexim1 = IDX3S(shiftedim1,j,shiftedk); indexkm1 = IDX3S(shiftedi,j,shiftedkm1); // Set By_stagger = \partial_z A_x - \partial_x A_z By_stagger[actual_index] = (Ax[index]-Ax[indexkm1])*invdx2 - (Az[index]-Az[indexim1])*invdx0; // Now multiply By_stagger by 1/sqrt(gamma(i,j+1/2,k)]) = 1/sqrt(1/2 [gamma + gamma_jp1]) = exp(-6 x 1/2 [phi + phi_jp1] ) const int jmax_minus_j = (Nxx_plus_2NGHOSTS1-1)-j; const int indexijp1k = IDX3S(i,j + ( (jmax_minus_j > 0) - (0 > jmax_minus_j) ),k); By_stagger[actual_index] *= Psim3/psi3_bssn[indexijp1k]; /**************/ /* Bz_stagger */ /**************/ index = IDX3S(shiftedi,shiftedj,k); indexim1 = IDX3S(shiftedim1,shiftedj,k); indexjm1 = IDX3S(shiftedi,shiftedjm1,k); // Set Bz_stagger = \partial_x A_y - \partial_y A_x Bz_stagger[actual_index] = (Ay[index]-Ay[indexim1])*invdx0 - (Ax[index]-Ax[indexjm1])*invdx1; // Now multiply Bz_stagger by 1/sqrt(gamma(i,j,k+1/2)]) = 1/sqrt(1/2 [gamma + gamma_kp1]) = exp(-6 x 1/2 [phi + phi_kp1] ) const int kmax_minus_k = (Nxx_plus_2NGHOSTS2-1)-k; const int indexijkp1 = IDX3S(i,j,k + ( (kmax_minus_k > 0) - (0 > kmax_minus_k) )); Bz_stagger[actual_index] *= Psim3/psi3_bssn[indexijkp1]; } LOOP_DEFINE_SIMPLE { // Look Mom, no if() statements! const int shiftedim1 = (i-1)*(i!=0); // This way, i=0 yields shiftedim1=0 and shiftedi=1, used below for our COPY boundary condition. const int shiftedi = shiftedim1+1; const int shiftedjm1 = (j-1)*(j!=0); const int shiftedj = shiftedjm1+1; const int shiftedkm1 = (k-1)*(k!=0); const int shiftedk = shiftedkm1+1; int index,indexim1,indexjm1,indexkm1; const int actual_index = IDX3S(i,j,k); // For the lower boundaries, the following applies a "copy" // boundary condition on Bi and Bi_stagger where needed. // E.g., Bx(imin,j,k) = Bx(imin+1,j,k) // We find the copy BC works better than extrapolation. /******/ /* Bx */ /******/ index = IDX3S(shiftedi,j,k); indexim1 = IDX3S(shiftedim1,j,k); // Set Bx = 0.5 ( Bx_stagger + Bx_stagger_im1 ) // "Grid" Bx_stagger(i,j,k) is actually Bx_stagger(i+1/2,j,k) Bx[actual_index] = 0.5 * ( Bx_stagger[index] + Bx_stagger[indexim1] ); /******/ /* By */ /******/ index = IDX3S(i,shiftedj,k); indexjm1 = IDX3S(i,shiftedjm1,k); // Set By = 0.5 ( By_stagger + By_stagger_im1 ) // "Grid" By_stagger(i,j,k) is actually By_stagger(i,j+1/2,k) By[actual_index] = 0.5 * ( By_stagger[index] + By_stagger[indexjm1] ); /******/ /* Bz */ /******/ index = IDX3S(i,j,shiftedk); indexkm1 = IDX3S(i,j,shiftedkm1); // Set Bz = 0.5 ( Bz_stagger + Bz_stagger_im1 ) // "Grid" Bz_stagger(i,j,k) is actually Bz_stagger(i,j+1/2,k) Bz[actual_index] = 0.5 * ( Bz_stagger[index] + Bz_stagger[indexkm1] ); } } """)
def GiRaFFE_NRPy_Afield_flux(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. gammaDD = ixp.declarerank2("gammaDD", "sym01", DIM=3) betaU = ixp.declarerank1("betaU", DIM=3) alpha = sp.sympify("alpha") for flux_dirn in range(3): chsp.find_cmax_cmin(flux_dirn, gammaDD, betaU, alpha) Ccode_kernel = outputC([chsp.cmax, chsp.cmin], ["cmax", "cmin"], "returnstring", params="outCverbose=False,CSE_sorting=none") Ccode_kernel = Ccode_kernel.replace("cmax", "*cmax").replace("cmin", "*cmin") Ccode_kernel = Ccode_kernel.replace("betaU0", "betaUi").replace( "betaU1", "betaUi").replace("betaU2", "betaUi") with open( os.path.join(Ccodesdir, "compute_cmax_cmin_dirn" + str(flux_dirn) + ".h"), "w") as file: file.write(Ccode_kernel) with open(os.path.join(Ccodesdir, "calculate_E_field_flat_all_in_one.h"), "w") as file: file.write( r"""void find_cmax_cmin(const REAL gammaDD00, const REAL gammaDD01, const REAL gammaDD02, const REAL gammaDD11, const REAL gammaDD12, const REAL gammaDD22, const REAL betaUi, const REAL alpha, const int flux_dirn, REAL *cmax, REAL *cmin) { switch(flux_dirn) { case 0: #include "compute_cmax_cmin_dirn0.h" break; case 1: #include "compute_cmax_cmin_dirn1.h" break; case 2: #include "compute_cmax_cmin_dirn2.h" break; default: printf("Invalid parameter flux_dirn!"); *cmax = 1.0/0.0; *cmin = 1.0/0.0; break; } } REAL HLLE_solve(REAL F0B1_r, REAL F0B1_l, REAL U_r, REAL U_l, REAL cmin, REAL cmax) { // Eq. 3.15 of https://epubs.siam.org/doi/abs/10.1137/1025002?journalCode=siread // F_HLLE = (c_min F_R + c_max F_L - c_min c_max (U_R-U_L)) / (c_min + c_max) return (cmin*F0B1_r + cmax*F0B1_l - cmin*cmax*(U_r-U_l)) / (cmin+cmax); } /* Calculate the electric flux on both faces in the input direction. The input count is an integer that is either 0 or 1. If it is 0, this implies that the components are input in order of a backwards permutation and the final results will need to be multiplied by -1.0. If it is 1, then the permutation is forwards. */ void calculate_E_field_flat_all_in_one(const paramstruct *params, const REAL *Vr0,const REAL *Vr1, const REAL *Vl0,const REAL *Vl1, const REAL *Br0,const REAL *Br1, const REAL *Bl0,const REAL *Bl1, const REAL *Brflux_dirn, const REAL *Blflux_dirn, const REAL *gamma_faceDD00, const REAL *gamma_faceDD01, const REAL *gamma_faceDD02, const REAL *gamma_faceDD11, const REAL *gamma_faceDD12, const REAL *gamma_faceDD22, const REAL *beta_faceU0, const REAL *beta_faceU1, const REAL *alpha_face, REAL *A2_rhs,const REAL SIGN,const int flux_dirn) { // This function is written to be generic and compute the contribution for all three AD RHSs. // However, for convenience, the notation used in the function itself is for the contribution // to AD2, specifically the [F_HLL^x(B^y)]_z term, with reconstructions in the x direction. This // corresponds to flux_dirn=0 and count=1 (which corresponds to SIGN=+1.0). // Thus, Az(i,j,k) += 0.25 ( [F_HLL^x(B^y)]_z(i+1/2,j,k)+[F_HLL^x(B^y)]_z(i-1/2,j,k)) are solved here. // The other terms are computed by cyclically permuting the indices when calling this function. #include "../set_Cparameters.h" #pragma omp parallel for for(int i2=NGHOSTS; i2<NGHOSTS+Nxx2; i2++) { for(int i1=NGHOSTS; i1<NGHOSTS+Nxx1; i1++) { for(int i0=NGHOSTS; i0<NGHOSTS+Nxx0; i0++) { // First, we set the index from which we will read memory. indexp1 is incremented by // one point in the direction of reconstruction. These correspond to the faces at at // i-1/2 and i+1/2, respectively. // Now, we read in memory. We need the x and y components of velocity and magnetic field on both // the left and right sides of the interface at *both* faces. // Here, the point (i0,i1,i2) corresponds to the point (i-1/2,j,k) const int index = IDX3S(i0,i1,i2); const double alpha = alpha_face[index]; const double betaU0 = beta_faceU0[index]; const double betaU1 = beta_faceU1[index]; const double v_rU0 = alpha*Vr0[index]-betaU0; const double v_rU1 = alpha*Vr1[index]-betaU1; const double B_rU0 = Br0[index]; const double B_rU1 = Br1[index]; const double B_rflux_dirn = Brflux_dirn[index]; const double v_lU0 = alpha*Vl0[index]-betaU0; const double v_lU1 = alpha*Vl1[index]-betaU1; const double B_lU0 = Bl0[index]; const double B_lU1 = Bl1[index]; const double B_lflux_dirn = Blflux_dirn[index]; // We will also need need the square root of the metric determinant here at this point: const REAL gxx = gamma_faceDD00[index]; const REAL gxy = gamma_faceDD01[index]; const REAL gxz = gamma_faceDD02[index]; const REAL gyy = gamma_faceDD11[index]; const REAL gyz = gamma_faceDD12[index]; const REAL gzz = gamma_faceDD22[index]; const REAL sqrtgammaDET = sqrt( gxx*gyy*gzz - gxx*gyz*gyz +2*gxy*gxz*gyz - gyy*gxz*gxz - gzz*gxy*gxy ); // ******************************* // REPEAT ABOVE, but at i+1, which corresponds to point (i+1/2,j,k) // Recall that the documentation here assumes flux_dirn==0, but the // algorithm is generalized so that any flux_dirn or velocity/magnetic // field component can be computed via permuting the inputs into this // function. const int indexp1 = IDX3S(i0+(flux_dirn==0),i1+(flux_dirn==1),i2+(flux_dirn==2)); const double alpha_p1 = alpha_face[indexp1]; const double betaU0_p1 = beta_faceU0[indexp1]; const double betaU1_p1 = beta_faceU1[indexp1]; const double v_rU0_p1 = alpha_p1*Vr0[indexp1]-betaU0_p1; const double v_rU1_p1 = alpha_p1*Vr1[indexp1]-betaU1_p1; const double B_rU0_p1 = Br0[indexp1]; const double B_rU1_p1 = Br1[indexp1]; const double B_rflux_dirn_p1 = Brflux_dirn[indexp1]; const double v_lU0_p1 = alpha_p1*Vl0[indexp1]-betaU0_p1; const double v_lU1_p1 = alpha_p1*Vl1[indexp1]-betaU1_p1; const double B_lU0_p1 = Bl0[indexp1]; const double B_lU1_p1 = Bl1[indexp1]; const double B_lflux_dirn_p1 = Blflux_dirn[indexp1]; // We will also need need the square root of the metric determinant here at this point: const REAL gxx_p1 = gamma_faceDD00[indexp1]; const REAL gxy_p1 = gamma_faceDD01[indexp1]; const REAL gxz_p1 = gamma_faceDD02[indexp1]; const REAL gyy_p1 = gamma_faceDD11[indexp1]; const REAL gyz_p1 = gamma_faceDD12[indexp1]; const REAL gzz_p1 = gamma_faceDD22[indexp1]; const REAL sqrtgammaDET_p1 = sqrt( gxx_p1*gyy_p1*gzz_p1 - gxx_p1*gyz_p1*gyz_p1 +2*gxy_p1*gxz_p1*gyz_p1 - gyy_p1*gxz_p1*gxz_p1 - gzz_p1*gxy_p1*gxy_p1 ); // ******************************* // DEBUGGING: // if(flux_dirn==0 && SIGN>0 && i1==Nxx_plus_2NGHOSTS1/2 && i2==Nxx_plus_2NGHOSTS2/2) { // printf("index=%d & indexp1=%d\n",index,indexp1); // } // Since we are computing A_z, the relevant equation here is: // -E_z(x_i,y_j,z_k) = 0.25 ( [F_HLL^x(B^y)]_z(i+1/2,j,k)+[F_HLL^x(B^y)]_z(i-1/2,j,k) // -[F_HLL^y(B^x)]_z(i,j+1/2,k)-[F_HLL^y(B^x)]_z(i,j-1/2,k) ) // We will construct the above sum one half at a time, first with SIGN=+1, which // corresponds to flux_dirn = 0, count=1, and // takes care of the terms: // [F_HLL^x(B^y)]_z(i+1/2,j,k)+[F_HLL^x(B^y)]_z(i-1/2,j,k) // ( Note that we will repeat the above with flux_dirn = 1, count = 0, with SIGN=-1 // AND with the input components switched (x->y,y->x) so that we get the term // -[F_HLL^y(B^x)]_z(i,j+1/2,k)-[F_HLL^y(B^x)]_z(i,j-1/2,k) // thus completing the above sum. ) // Here, [F_HLL^i(B^j)]_k = (v^i B^j - v^j B^i) in general. // Calculate the flux vector on each face for each component of the E-field: // The F(B) terms are as Eq. 6 in Giacomazzo: https://arxiv.org/pdf/1009.2468.pdf // [F^i(B^j)]_k = \sqrt{\gamma} (v^i B^j - v^j B^i) // Therefore since we want [F_HLL^x(B^y)]_z, // we will code (v^x B^y - v^y B^x) on both left and right faces. const REAL F0B1_r = sqrtgammaDET*(v_rU0*B_rU1 - v_rU1*B_rU0); const REAL F0B1_l = sqrtgammaDET*(v_lU0*B_lU1 - v_lU1*B_lU0); // Compute the state vector for these terms: const REAL U_r = B_rflux_dirn; const REAL U_l = B_lflux_dirn; REAL cmin,cmax; // Basic HLLE solver: find_cmax_cmin(gxx,gxy,gxz, gyy,gyz,gzz, betaU0,alpha,flux_dirn, &cmax, &cmin); const REAL FHLL_0B1 = HLLE_solve(F0B1_r, F0B1_l, U_r, U_l, cmin, cmax); // ************************************ // ************************************ // REPEAT ABOVE, but at point i+1 // Calculate the flux vector on each face for each component of the E-field: const REAL F0B1_r_p1 = sqrtgammaDET_p1*(v_rU0_p1*B_rU1_p1 - v_rU1_p1*B_rU0_p1); const REAL F0B1_l_p1 = sqrtgammaDET_p1*(v_lU0_p1*B_lU1_p1 - v_lU1_p1*B_lU0_p1); // Compute the state vector for this flux direction const REAL U_r_p1 = B_rflux_dirn_p1; const REAL U_l_p1 = B_lflux_dirn_p1; //const REAL U_r_p1 = B_rU1_p1; //const REAL U_l_p1 = B_lU1_p1; // Basic HLLE solver, but at the next point: find_cmax_cmin(gxx_p1,gxy_p1,gxz_p1, gyy_p1,gyz_p1,gzz_p1, betaU0_p1,alpha_p1,flux_dirn, &cmax, &cmin); const REAL FHLL_0B1p1 = HLLE_solve(F0B1_r_p1, F0B1_l_p1, U_r_p1, U_l_p1, cmin, cmax); // ************************************ // ************************************ // With the Riemann problem solved, we add the contributions to the RHSs: // -E_z(x_i,y_j,z_k) &= 0.25 ( [F_HLL^x(B^y)]_z(i+1/2,j,k)+[F_HLL^x(B^y)]_z(i-1/2,j,k) // -[F_HLL^y(B^x)]_z(i,j+1/2,k)-[F_HLL^y(B^x)]_z(i,j-1/2,k) ) // (Eq. 11 in https://arxiv.org/pdf/1009.2468.pdf) // This code, as written, solves the first two terms for flux_dirn=0. Calling this function for count=0 // and flux_dirn=1 flips x for y to solve the latter two, switching to SIGN=-1 as well. // Here, we finally add together the output of the HLLE solver at i-1/2 and i+1/2 // We also multiply by the SIGN dictated by the order of the input vectors and divide by 4. A2_rhs[index] += SIGN*0.25*(FHLL_0B1 + FHLL_0B1p1); // flux dirn = 0 ===================> i-1/2 i+1/2 // Eq 11 in Giacomazzo: // -FxBy(avg over i-1/2 and i+1/2) + FyBx(avg over j-1/2 and j+1/2) // Eq 6 in Giacomazzo: // FxBy = vxBy - vyBx // -> // FHLL_0B1 = vyBx - vxBy } // END LOOP: for(int i0=NGHOSTS; i0<NGHOSTS+Nxx0; i0++) } // END LOOP: for(int i1=NGHOSTS; i1<NGHOSTS+Nxx1; i1++) } // END LOOP: for(int i2=NGHOSTS; i2<NGHOSTS+Nxx2; i2++) } """)
def GiRaFFE_NRPy_BCs(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. with open(os.path.join(Ccodesdir,"GiRaFFE_boundary_conditions.h"),"w") as file: file.write("""// Currently, we're using basic Cartesian boundary conditions, pending fixes by Zach. // Part P8a: Declare boundary condition FACE_UPDATE macro, // which updates a single face of the 3D grid cube // using quadratic polynomial extrapolation. // Basic extrapolation boundary conditions #define FACE_UPDATE(which_gf, i0min,i0max, i1min,i1max, i2min,i2max, FACEX0,FACEX1,FACEX2) \\ for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) { \\ gfs[IDX4S(which_gf,i0,i1,i2)] = \\ +2.0*gfs[IDX4S(which_gf,i0+1*FACEX0,i1+1*FACEX1,i2+1*FACEX2)] \\ -1.0*gfs[IDX4S(which_gf,i0+2*FACEX0,i1+2*FACEX1,i2+2*FACEX2)]; \\ } // +1.0*gfs[IDX4S(which_gf,i0+3*FACEX0,i1+3*FACEX1,i2+3*FACEX2)]; \\ // Basic Copy boundary conditions #define FACE_UPDATE_COPY(which_gf, i0min,i0max, i1min,i1max, i2min,i2max, FACEX0,FACEX1,FACEX2) \\ for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) { \\ gfs[IDX4S(which_gf,i0,i1,i2)] = gfs[IDX4S(which_gf,i0+1*FACEX0,i1+1*FACEX1,i2+1*FACEX2)]; \\ } // Part P8b: Boundary condition driver routine: Apply BCs to all six // boundary faces of the cube, filling in the innermost // ghost zone first, and moving outward. const int MAXFACE = -1; const int NUL = +0; const int MINFACE = +1; // This macro acts differently in that it acts on an entire 3-vector of gfs, instead of 1. // which_gf_0 corresponds to the zeroth component of that vector. The if statements only // evaluate true if the velocity is directed inwards on the face in consideration. #define FACE_UPDATE_OUTFLOW(which_gf, i0min,i0max, i1min,i1max, i2min,i2max, FACEX0,FACEX1,FACEX2) \\ for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) { \\ aux_gfs[IDX4S(which_gf_0,i0,i1,i2)] = \\ aux_gfs[IDX4S(which_gf_0,i0+FACEX0,i1+FACEX1,i2+FACEX2)]; \\ aux_gfs[IDX4S(which_gf_0+1,i0,i1,i2)] = \\ aux_gfs[IDX4S(which_gf_0+1,i0+FACEX0,i1+FACEX1,i2+FACEX2)]; \\ aux_gfs[IDX4S(which_gf_0+2,i0,i1,i2)] = \\ aux_gfs[IDX4S(which_gf_0+2,i0+FACEX0,i1+FACEX1,i2+FACEX2)]; \\ } /* if(FACEX0*aux_gfs[IDX4S(which_gf_0+0,i0,i1,i2)] > 0.0) { \\ aux_gfs[IDX4S(which_gf_0+0,i0,i1,i2)] = 0.0; \\ } \\ if(FACEX1*aux_gfs[IDX4S(which_gf_0+1,i0,i1,i2)] > 0.0) { \\ aux_gfs[IDX4S(which_gf_0+1,i0,i1,i2)] = 0.0; \\ } \\ if(FACEX2*aux_gfs[IDX4S(which_gf_0+2,i0,i1,i2)] > 0.0) { \\ aux_gfs[IDX4S(which_gf_0+2,i0,i1,i2)] = 0.0; \\ } \\ */ void apply_bcs_potential(const paramstruct *restrict params,REAL *gfs) { #include "../set_Cparameters.h" // First, we apply extrapolation boundary conditions to AD #pragma omp parallel for for(int which_gf=0;which_gf<NUM_EVOL_GFS;which_gf++) { if(which_gf < STILDED0GF || which_gf > STILDED2GF) { int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS }; int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS }; for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) { // After updating each face, adjust imin[] and imax[] // to reflect the newly-updated face extents. FACE_UPDATE(which_gf, imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2], MINFACE,NUL,NUL); imin[0]--; FACE_UPDATE(which_gf, imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2], MAXFACE,NUL,NUL); imax[0]++; FACE_UPDATE(which_gf, imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2], NUL,MINFACE,NUL); imin[1]--; FACE_UPDATE(which_gf, imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2], NUL,MAXFACE,NUL); imax[1]++; FACE_UPDATE(which_gf, imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2], NUL,NUL,MINFACE); imin[2]--; FACE_UPDATE(which_gf, imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1, NUL,NUL,MAXFACE); imax[2]++; } } } // Then, we apply copy boundary conditions to StildeD and psi6Phi /*#pragma omp parallel for for(int which_gf=3;which_gf<NUM_EVOL_GFS;which_gf++) { int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS }; int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS }; for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) { // After updating each face, adjust imin[] and imax[] // to reflect the newly-updated face extents. FACE_UPDATE_COPY(which_gf, imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2], MINFACE,NUL,NUL); imin[0]--; FACE_UPDATE_COPY(which_gf, imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2], MAXFACE,NUL,NUL); imax[0]++; FACE_UPDATE_COPY(which_gf, imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2], NUL,MINFACE,NUL); imin[1]--; FACE_UPDATE_COPY(which_gf, imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2], NUL,MAXFACE,NUL); imax[1]++; FACE_UPDATE_COPY(which_gf, imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2], NUL,NUL,MINFACE); imin[2]--; FACE_UPDATE_COPY(which_gf, imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1, NUL,NUL,MAXFACE); imax[2]++; } }*/ } void apply_bcs_velocity(const paramstruct *restrict params,REAL *aux_gfs) { #include "../set_Cparameters.h" // Apply outflow/copy boundary conditions to ValenciavU by passing VALENCIAVU0 as which_gf_0 // for(int which_gf=VALENCIAVU0GF;which_gf<=VALENCIAVU2GF;which_gf++) { const int which_gf_0 = VALENCIAVU0GF; int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS }; int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS }; for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) { FACE_UPDATE_OUTFLOW(which_gf_0, imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2], MINFACE,NUL,NUL); imin[0]--; FACE_UPDATE_OUTFLOW(which_gf_0, imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2], MAXFACE,NUL,NUL); imax[0]++; FACE_UPDATE_OUTFLOW(which_gf_0, imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2], NUL,MINFACE,NUL); imin[1]--; FACE_UPDATE_OUTFLOW(which_gf_0, imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2], NUL,MAXFACE,NUL); imax[1]++; FACE_UPDATE_OUTFLOW(which_gf_0, imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2], NUL,NUL,MINFACE); imin[2]--; FACE_UPDATE_OUTFLOW(which_gf_0, imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1, NUL,NUL,MAXFACE); imax[2]++; } // } } /*// A supplement to the boundary conditions for debugging. This will overwrite data with exact conditions void FACE_UPDATE_EXACT(const paramstruct *restrict params,REAL *restrict xx[3], const int n, const REAL dt,REAL *out_gfs,REAL *aux_gfs, const int i0min,const int i0max, const int i1min,const int i1max, const int i2min,const int i2max, const int FACEX0,const int FACEX1,const int FACEX2) { #include "../set_Cparameters.h" for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) { REAL xx0 = xx[0][i0]-n*dt; REAL xx1 = xx[1][i1]; REAL xx2 = xx[2][i2]; if(xx0<=lbound) { #include "../GiRaFFEfood_A_v_1D_tests_left.h" } else if (xx0<rbound) { #include "../GiRaFFEfood_A_v_1D_tests_center.h" } else { #include "../GiRaFFEfood_A_v_1D_tests_right.h" } out_gfs[IDX4S(PSI6PHIGF, i0,i1,i2)] = 0.0; } } void apply_bcs_EXACT(const paramstruct *restrict params,REAL *restrict xx[3], const int n, const REAL dt, REAL *out_gfs,REAL *aux_gfs) { #include "../set_Cparameters.h" int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS }; int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS }; for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) { // After updating each face, adjust imin[] and imax[] // to reflect the newly-updated face extents. // Right now, we only want to update the xmin and xmax faces with the exact data. FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2], MINFACE,NUL,NUL); imin[0]--; FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2], MAXFACE,NUL,NUL); imax[0]++; FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2], NUL,MINFACE,NUL); imin[1]--; FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2], NUL,MAXFACE,NUL); imax[1]++; FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2], NUL,NUL,MINFACE); imin[2]--; FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1, NUL,NUL,MAXFACE); imax[2]++; } } // A supplement to the boundary conditions for debugging. This will overwrite data with exact conditions void FACE_UPDATE_EXACT_StildeD(const paramstruct *restrict params,REAL *restrict xx[3], REAL *out_gfs,REAL *out_gfs_exact, const int i0min,const int i0max, const int i1min,const int i1max, const int i2min,const int i2max, const int FACEX0,const int FACEX1,const int FACEX2) { #include "../set_Cparameters.h" // This is currently modified to calculate more exact boundary conditions for StildeD. Rename if it works. for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) { #include "../GiRaFFEfood_NRPy_Stilde.h" } idx = IDX3(i0,i1,i2); out_gfs[IDX4ptS(STILDED0GF,idx)] = out_gfs_exact[IDX4ptS(STILDED0GF,idx)]; out_gfs[IDX4ptS(STILDED1GF,idx)] = out_gfs_exact[IDX4ptS(STILDED1GF,idx)]; out_gfs[IDX4ptS(STILDED2GF,idx)] = out_gfs_exact[IDX4ptS(STILDED2GF,idx)]; } void apply_bcs_EXACT_StildeD(const paramstruct *restrict params,REAL *restrict xx[3], REAL *out_gfs,REAL *out_gfs_exact) { #include "../set_Cparameters.h" int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS }; int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS }; for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) { // After updating each face, adjust imin[] and imax[] // to reflect the newly-updated face extents. // Right now, we only want to update the xmin and xmax faces with the exact data. FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2], MINFACE,NUL,NUL); imin[0]--; FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2], MAXFACE,NUL,NUL); imax[0]++; //FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2], NUL,MINFACE,NUL); imin[1]--; //FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2], NUL,MAXFACE,NUL); imax[1]++; //FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2], NUL,NUL,MINFACE); imin[2]--; //FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1, NUL,NUL,MAXFACE); imax[2]++; } }*/ """)
# Step 0: Add NRPy's directory to the path # https://stackoverflow.com/questions/16780014/import-file-from-parent-directory import os, sys nrpy_dir_path = os.path.join("..") if nrpy_dir_path not in sys.path: sys.path.append(nrpy_dir_path) import cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface Ccodesdir = "GiRaFFE_standalone_Ccodes/A2B" cmd.mkdir(os.path.join(Ccodesdir)) def GiRaFFE_NRPy_A2B(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. with open(os.path.join(Ccodesdir, "compute_B_and_Bstagger_from_A.h"), "w") as file: file.write("""#define LOOP_DEFINE_SIMPLE \\ _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++) void GiRaFFE_compute_B_and_Bstagger_from_A(const paramstruct *params, const REAL *gxx, const REAL *gxy, const REAL *gxz, const REAL *gyy, const REAL *gyz,const REAL *gzz, REAL *psi3_bssn, const REAL *Ax, const REAL *Ay, const REAL *Az, REAL *Bx, REAL *By, REAL *Bz, REAL *Bx_stagger, REAL *By_stagger, REAL *Bz_stagger) { #include "../set_Cparameters.h" LOOP_DEFINE_SIMPLE { const int index=IDX3S(i,j,k);
def GiRaFFE_NRPy_Source_Terms(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. with open( os.path.join(Ccodesdir, "Lorenz_psi6phi_rhs__add_gauge_terms_to_A_i_rhs.h"), "w") as file: file.write( """static inline REAL avg(const REAL f[PLUS2+1][PLUS2+1][PLUS2+1],const int imin,const int imax, const int jmin,const int jmax, const int kmin,const int kmax); #define MINUS2 0 #define MINUS1 1 #define PLUS0 2 #define PLUS1 3 #define PLUS2 4 // The "I" suffix denotes interpolation. In other words, these // definitions are used for interpolation ONLY. The order here // matters as well! static const int SHIFTXI=0,SHIFTYI=1,SHIFTZI=2,GUPXXI=3,GUPXYI=4,GUPXZI=5,GUPYYI=6,GUPYZI=7,GUPZZI=8, PSII=9,LAPM1I=10,A_XI=11,A_YI=12,A_ZI=13,LAPSE_PSI2I=14,LAPSE_OVER_PSI6I=15; #define MAXNUMINTERP 16 // GiRaFFE_NRPy does not store the inverse metric. So, the actual inputs to the function will be // just the metric, which we can invert in an early step. To keep things consistent, we'll label the // components with the same labels as the inverse: static const int GXXI=3,GXYI=4,GXZI=5,GYYI=6,GYZI=7,GZZI=8; static void Lorenz_psi6phi_rhs__add_gauge_terms_to_A_i_rhs(const paramstruct *params,REAL **in_vars,const REAL *psi6phi, /* TEMPS: */ REAL *shiftx_iphjphkph,REAL *shifty_iphjphkph,REAL *shiftz_iphjphkph, REAL *alpha_iphjphkph,REAL *alpha_Phi_minus_betaj_A_j_iphjphkph,REAL *alpha_sqrtg_Ax_interp, REAL *alpha_sqrtg_Ay_interp,REAL *alpha_sqrtg_Az_interp, /* END TEMPS, 8 total! */ REAL *psi6phi_rhs,REAL *Ax_rhs,REAL *Ay_rhs,REAL *Az_rhs) { #include "../set_Cparameters.h" /* Compute * \\partial_t psi6phi = -\\partial_j ( \\alpha \\sqrt{\\gamma} A^j - \\beta^j psi6phi) * (Eq 13 of http://arxiv.org/pdf/1110.4633.pdf), using Lorenz gauge. * Note that the RHS consists of a shift advection term on psi6phi and * a term depending on the vector potential. * psi6phi is defined at (i+1/2,j+1/2,k+1/2), but instead of reconstructing * to compute the RHS of \\partial_t psi6phi, we instead use standard * interpolations. */ // The stencil here is {-1,1},{-1,1},{-1,1} for x,y,z directions, respectively. // Note that ALL input variables are defined at ALL gridpoints, so no // worries about ghostzones. #pragma omp parallel for for(int k=1;k<Nxx_plus_2NGHOSTS2-1;k++) for(int j=1;j<Nxx_plus_2NGHOSTS1-1;j++) for(int i=1;i<Nxx_plus_2NGHOSTS0-1;i++) { const int index=IDX3S(i,j,k); REAL INTERP_VARS[MAXNUMINTERP][PLUS2+1][PLUS2+1][PLUS2+1]; // First compute \\partial_j \\alpha \\sqrt{\\gamma} A^j (RHS of \\partial_i psi6phi) // FIXME: Would be much cheaper & easier to unstagger A_i, raise, then interpolate A^i. // However, we keep it this way to be completely compatible with the original // Illinois GRMHD thorn, called mhd_evolve. // //Step 1) j=x: Need to raise A_i, but to do that, we must have all variables at the same gridpoints: // The goal is to compute \\partial_j (\\alpha \\sqrt{\\gamma} A^j) at (i+1/2,j+1/2,k+1/2) // We do this by first interpolating (RHS1x) = (\\alpha \\sqrt{\\gamma} A^x) at // (i,j+1/2,k+1/2)and (i+1,j+1/2,k+1/2), then taking \\partial_x (RHS1x) = // [ RHS1x(i+1,j+1/2,k+1/2) - RHS1x(i,j+1/2,k+1/2) ]/dX. // First bring gup's, psi, and alpha to (i,j+1/2,k+1/2): int num_vars_to_interp; int vars_to_interpolate[MAXNUMINTERP] = {GUPXXI,GUPXYI,GUPXZI,GUPYYI,GUPYZI,GUPZZI,LAPM1I,PSII,SHIFTXI,SHIFTYI,SHIFTZI}; num_vars_to_interp = 11; // We may set interp_limits to be more general than we need. int interp_limits[6] = {-1,1,-1,1,-1,1}; SET_INDEX_ARRAYS_NRPY_3DBLOCK(interp_limits); //SET_INDEX_ARRAYS_NRPY_3DBLOCK(interp_limits); // for(int ww=0;ww<num_vars_to_interp;ww++) { // int whichvar=vars_to_interpolate[ww]; // // Read in variable at interp. stencil points from main memory, store in INTERP_VARS. // for(int kk=PLUS0;kk<=PLUS1;kk++) for(int jj=PLUS0;jj<=PLUS1;jj++) for(int ii=PLUS0;ii<=PLUS1;ii++) { // INTERP_VARS[whichvar][kk][jj][ii] = in_vars[whichvar][index_arr_3DB[kk][jj][ii]]; } // } // Major change here to invert the metric on the spot! // Read in variable at interp. stencil points from main memory, store in INTERP_VARS. for(int kk=PLUS0;kk<=PLUS1;kk++) for(int jj=PLUS0;jj<=PLUS1;jj++) for(int ii=PLUS0;ii<=PLUS1;ii++) { // First, we will read in each component of the metric, then find the determinant. // We write the twelfth root to psi_bssn. Then, we invert the metric and store these values. const REAL gammaDD00 = in_vars[GXXI][index_arr_3DB[kk][jj][ii]]; const REAL gammaDD01 = in_vars[GXYI][index_arr_3DB[kk][jj][ii]]; const REAL gammaDD02 = in_vars[GXZI][index_arr_3DB[kk][jj][ii]]; const REAL gammaDD11 = in_vars[GYYI][index_arr_3DB[kk][jj][ii]]; const REAL gammaDD12 = in_vars[GYZI][index_arr_3DB[kk][jj][ii]]; const REAL gammaDD22 = in_vars[GZZI][index_arr_3DB[kk][jj][ii]]; // Generated by NRPy+: /* * NRPy+ Finite Difference Code Generation, Step 2 of 1: Evaluate SymPy expressions and write to main memory: */ const double FDPart3_0 = cbrt(gammaDD00*gammaDD11*gammaDD22 - gammaDD00*((gammaDD12)*(gammaDD12)) - ((gammaDD01)*(gammaDD01))*gammaDD22 + 2*gammaDD01*gammaDD02*gammaDD12 - ((gammaDD02)*(gammaDD02))*gammaDD11); const double FDPart3_1 = (1.0/(FDPart3_0)); const REAL gamma_bssnDD00 = FDPart3_1*gammaDD00; const REAL gamma_bssnDD01 = FDPart3_1*gammaDD01; const REAL gamma_bssnDD02 = FDPart3_1*gammaDD02; const REAL gamma_bssnDD11 = FDPart3_1*gammaDD11; const REAL gamma_bssnDD12 = FDPart3_1*gammaDD12; const REAL gamma_bssnDD22 = FDPart3_1*gammaDD22; const double tmp_5 = gamma_bssnDD00*gamma_bssnDD11*gamma_bssnDD22 - gamma_bssnDD00*((gamma_bssnDD12)*(gamma_bssnDD12)) - ((gamma_bssnDD01)*(gamma_bssnDD01))*gamma_bssnDD22 + 2*gamma_bssnDD01*gamma_bssnDD02*gamma_bssnDD12 - ((gamma_bssnDD02)*(gamma_bssnDD02))*gamma_bssnDD11; const double tmp_6 = (1.0/(tmp_5)); INTERP_VARS[GUPXXI][kk][jj][ii] = tmp_6*(gamma_bssnDD11*gamma_bssnDD22 - ((gamma_bssnDD12)*(gamma_bssnDD12))); INTERP_VARS[GUPXYI][kk][jj][ii] = tmp_6*(-gamma_bssnDD01*gamma_bssnDD22 + gamma_bssnDD02*gamma_bssnDD12); INTERP_VARS[GUPXZI][kk][jj][ii] = tmp_6*(gamma_bssnDD01*gamma_bssnDD12 - gamma_bssnDD02*gamma_bssnDD11); INTERP_VARS[GUPYYI][kk][jj][ii] = tmp_6*(gamma_bssnDD00*gamma_bssnDD22 - ((gamma_bssnDD02)*(gamma_bssnDD02))); INTERP_VARS[GUPYZI][kk][jj][ii] = tmp_6*(-gamma_bssnDD00*gamma_bssnDD12 + gamma_bssnDD01*gamma_bssnDD02); INTERP_VARS[GUPZZI][kk][jj][ii] = tmp_6*(gamma_bssnDD00*gamma_bssnDD11 - ((gamma_bssnDD01)*(gamma_bssnDD01))); INTERP_VARS[PSII][kk][jj][ii] = pow(FDPart3_0,1.0/4.0); // Now, we read in the lapse function. int whichvar=vars_to_interpolate[6]; INTERP_VARS[whichvar][kk][jj][ii] = in_vars[whichvar][index_arr_3DB[kk][jj][ii]]-1.0; // Input alpha, expect alpha-1 // Finally, we read in the shift vector into the array. for(int ww=8;ww<num_vars_to_interp;ww++) { int whichvar=vars_to_interpolate[ww]; INTERP_VARS[whichvar][kk][jj][ii] = in_vars[whichvar][index_arr_3DB[kk][jj][ii]]; } } // Next set \\alpha at (i+1/2,j+1/2,k+1/2). Will come in handy when computing damping term later. alpha_iphjphkph[index] = avg(INTERP_VARS[LAPM1I] , PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS1)+1.0; //A_x needs a stencil s.t. interp_limits={0,1,-1,1,-1,1}: for(int kk=MINUS1;kk<=PLUS1;kk++) for(int jj=MINUS1;jj<=PLUS1;jj++) for(int ii=PLUS0;ii<=PLUS1;ii++) { INTERP_VARS[A_XI][kk][jj][ii] = in_vars[A_XI][index_arr_3DB[kk][jj][ii]]; } //A_y needs a stencil s.t. interp_limits={-1,1,0,1,-1,1}: for(int kk=MINUS1;kk<=PLUS1;kk++) for(int jj=PLUS0;jj<=PLUS1;jj++) for(int ii=MINUS1;ii<=PLUS1;ii++) { INTERP_VARS[A_YI][kk][jj][ii] = in_vars[A_YI][index_arr_3DB[kk][jj][ii]]; } //A_z needs a stencil s.t. interp_limits={-1,1,-1,1,0,1}: for(int kk=PLUS0;kk<=PLUS1;kk++) for(int jj=MINUS1;jj<=PLUS1;jj++) for(int ii=MINUS1;ii<=PLUS1;ii++) { INTERP_VARS[A_ZI][kk][jj][ii] = in_vars[A_ZI][index_arr_3DB[kk][jj][ii]]; } // FIRST DO A^X TERM (interpolate to (i,j+1/2,k+1/2) ) // \\alpha \\sqrt{\\gamma} A^x = \\alpha psi^6 A^x (RHS of \\partial_i psi6phi) // Note that gupij is \\tilde{\\gamma}^{ij}, so we need to multiply by \\psi^{-4}. const REAL gupxx_jphkph = avg(INTERP_VARS[GUPXXI], PLUS0,PLUS0, PLUS0,PLUS1, PLUS0,PLUS1); const REAL gupxy_jphkph = avg(INTERP_VARS[GUPXYI], PLUS0,PLUS0, PLUS0,PLUS1, PLUS0,PLUS1); const REAL gupxz_jphkph = avg(INTERP_VARS[GUPXZI], PLUS0,PLUS0, PLUS0,PLUS1, PLUS0,PLUS1); for(int kk=PLUS0;kk<=PLUS1;kk++) for(int jj=PLUS0;jj<=PLUS1;jj++) for(int ii=PLUS0;ii<=PLUS1;ii++) { const REAL Psi2 = INTERP_VARS[PSII][kk][jj][ii]*INTERP_VARS[PSII][kk][jj][ii]; const REAL alpha = INTERP_VARS[LAPM1I][kk][jj][ii]+1.0; INTERP_VARS[LAPSE_PSI2I][kk][jj][ii]=alpha*Psi2; INTERP_VARS[LAPSE_OVER_PSI6I][kk][jj][ii]=alpha/(Psi2*Psi2*Psi2); } const REAL lapse_Psi2_jphkph = avg(INTERP_VARS[LAPSE_PSI2I], PLUS0,PLUS0, PLUS0,PLUS1, PLUS0,PLUS1); const REAL A_x_jphkph = avg(INTERP_VARS[A_XI], PLUS0,PLUS0, PLUS0,PLUS0, PLUS0,PLUS0); // @ (i,j+1/2,k+1/2) const REAL A_y_jphkph = avg(INTERP_VARS[A_YI],MINUS1,PLUS0, PLUS0,PLUS1, PLUS0,PLUS0); // @ (i+1/2,j,k+1/2) const REAL A_z_jphkph = avg(INTERP_VARS[A_ZI],MINUS1,PLUS0, PLUS0,PLUS0, PLUS0,PLUS1); // @ (i+1/2,j+1/2,k) alpha_sqrtg_Ax_interp[index] = lapse_Psi2_jphkph* ( gupxx_jphkph*A_x_jphkph + gupxy_jphkph*A_y_jphkph + gupxz_jphkph*A_z_jphkph ); // DO A^Y TERM (interpolate to (i+1/2,j,k+1/2) ) // \\alpha \\sqrt{\\gamma} A^y = \\alpha psi^6 A^y (RHS of \\partial_i psi6phi) // Note that gupij is \\tilde{\\gamma}^{ij}, so we need to multiply by \\psi^{-4}. const REAL gupxy_iphkph = avg(INTERP_VARS[GUPXYI], PLUS0,PLUS1, PLUS0,PLUS0, PLUS0,PLUS1); const REAL gupyy_iphkph = avg(INTERP_VARS[GUPYYI], PLUS0,PLUS1, PLUS0,PLUS0, PLUS0,PLUS1); const REAL gupyz_iphkph = avg(INTERP_VARS[GUPYZI], PLUS0,PLUS1, PLUS0,PLUS0, PLUS0,PLUS1); const REAL lapse_Psi2_iphkph = avg(INTERP_VARS[LAPSE_PSI2I], PLUS0,PLUS1, PLUS0,PLUS0, PLUS0,PLUS1); //REAL lapse_iphkph = avg(INTERP_VARS[LAPM1I], PLUS0,PLUS1, PLUS0,PLUS0, PLUS0,PLUS1)+1.0; //REAL psi_iphkph = avg(INTERP_VARS[PSII ], PLUS0,PLUS1, PLUS0,PLUS0, PLUS0,PLUS1); const REAL A_x_iphkph = avg(INTERP_VARS[A_XI], PLUS0,PLUS1,MINUS1,PLUS0, PLUS0,PLUS0); // @ (i,j+1/2,k+1/2) const REAL A_y_iphkph = avg(INTERP_VARS[A_YI], PLUS0,PLUS0, PLUS0,PLUS0, PLUS0,PLUS0); // @ (i+1/2,j,k+1/2) const REAL A_z_iphkph = avg(INTERP_VARS[A_ZI], PLUS0,PLUS0,MINUS1,PLUS0, PLUS0,PLUS1); // @ (i+1/2,j+1/2,k) alpha_sqrtg_Ay_interp[index] = lapse_Psi2_iphkph* ( gupxy_iphkph*A_x_iphkph + gupyy_iphkph*A_y_iphkph + gupyz_iphkph*A_z_iphkph ); // DO A^Z TERM (interpolate to (i+1/2,j+1/2,k) ) // \\alpha \\sqrt{\\gamma} A^z = \\alpha psi^6 A^z (RHS of \\partial_i psi6phi) // Note that gupij is \\tilde{\\gamma}^{ij}, so we need to multiply by \\psi^{-4}. const REAL gupxz_iphjph = avg(INTERP_VARS[GUPXZI], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS0); const REAL gupyz_iphjph = avg(INTERP_VARS[GUPYZI], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS0); const REAL gupzz_iphjph = avg(INTERP_VARS[GUPZZI], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS0); //REAL lapse_iphjph = avg(INTERP_VARS[LAPM1I], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS0)+1.0; //REAL psi_iphjph = avg(INTERP_VARS[PSII ], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS0); const REAL lapse_Psi2_iphjph = avg(INTERP_VARS[LAPSE_PSI2I], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS0); const REAL A_x_iphjph = avg(INTERP_VARS[A_XI], PLUS0,PLUS1, PLUS0,PLUS0,MINUS1,PLUS0); // @ (i,j+1/2,k+1/2) const REAL A_y_iphjph = avg(INTERP_VARS[A_YI], PLUS0,PLUS0, PLUS0,PLUS1,MINUS1,PLUS0); // @ (i+1/2,j,k+1/2) const REAL A_z_iphjph = avg(INTERP_VARS[A_ZI], PLUS0,PLUS0, PLUS0,PLUS0, PLUS0,PLUS0); // @ (i+1/2,j+1/2,k) alpha_sqrtg_Az_interp[index] = lapse_Psi2_iphjph* ( gupxz_iphjph*A_x_iphjph + gupyz_iphjph*A_y_iphjph + gupzz_iphjph*A_z_iphjph ); // Next set \\alpha \\Phi - \\beta^j A_j at (i+1/2,j+1/2,k+1/2): // We add a "L" suffix to shifti_iphjphkph to denote "LOCAL", as we set // shifti_iphjphkph[] gridfunction below. const REAL shiftx_iphjphkphL = avg(INTERP_VARS[SHIFTXI], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS1); const REAL shifty_iphjphkphL = avg(INTERP_VARS[SHIFTYI], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS1); const REAL shiftz_iphjphkphL = avg(INTERP_VARS[SHIFTZI], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS1); const REAL lapse_over_Psi6_iphjphkphL = avg(INTERP_VARS[LAPSE_OVER_PSI6I], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS1); //REAL psi_iphjphkph = avg(INTERP_VARS[PSII ], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS1); //REAL psi2_iphjphkph= psi_iphjphkph*psi_iphjphkph; //REAL psi6_iphjphkph= psi2_iphjphkph*psi2_iphjphkph*psi2_iphjphkph; const REAL A_x_iphjphkph = avg(INTERP_VARS[A_XI], PLUS0,PLUS1, PLUS0,PLUS0, PLUS0,PLUS0); // @ (i,j+1/2,k+1/2) const REAL A_y_iphjphkph = avg(INTERP_VARS[A_YI], PLUS0,PLUS0, PLUS0,PLUS1, PLUS0,PLUS0); // @ (i+1/2,j,k+1/2) const REAL A_z_iphjphkph = avg(INTERP_VARS[A_ZI], PLUS0,PLUS0, PLUS0,PLUS0, PLUS0,PLUS1); // @ (i+1/2,j+1/2,k) alpha_Phi_minus_betaj_A_j_iphjphkph[index] = psi6phi[index]*lapse_over_Psi6_iphjphkphL - (shiftx_iphjphkphL*A_x_iphjphkph + shifty_iphjphkphL*A_y_iphjphkph + shiftz_iphjphkphL*A_z_iphjphkph); // Finally, save shifti_iphjphkph, for \\partial_j \\beta^j psi6phi shiftx_iphjphkph[index]=shiftx_iphjphkphL; shifty_iphjphkph[index]=shifty_iphjphkphL; shiftz_iphjphkph[index]=shiftz_iphjphkphL; } // This loop requires two additional ghostzones in every direction. Hence the following loop definition: #pragma omp parallel for for(int k=NGHOSTS;k<Nxx_plus_2NGHOSTS2-NGHOSTS;k++) for(int j=NGHOSTS;j<Nxx_plus_2NGHOSTS1-NGHOSTS;j++) for(int i=NGHOSTS;i<Nxx_plus_2NGHOSTS0-NGHOSTS;i++) { const int index = IDX3S(i,j,k); // \\partial_t A_i = [reconstructed stuff] + [gauge stuff], // where [gauge stuff] = -\\partial_i (\\alpha \\Phi - \\beta^j A_j) const REAL alpha_Phi_minus_betaj_A_j_iphjphkphL = alpha_Phi_minus_betaj_A_j_iphjphkph[index]; // - partial_i -> - (A_{i} - A_{i-1})/dX = (A_{i-1} - A_{i})/dX, for Ax Ax_rhs[index] += invdx0*(alpha_Phi_minus_betaj_A_j_iphjphkph[IDX3S(i-1,j,k)] - alpha_Phi_minus_betaj_A_j_iphjphkphL); Ay_rhs[index] += invdx1*(alpha_Phi_minus_betaj_A_j_iphjphkph[IDX3S(i,j-1,k)] - alpha_Phi_minus_betaj_A_j_iphjphkphL); Az_rhs[index] += invdx2*(alpha_Phi_minus_betaj_A_j_iphjphkph[IDX3S(i,j,k-1)] - alpha_Phi_minus_betaj_A_j_iphjphkphL); // \\partial_t psi6phi = [shift advection term] + \\partial_j (\\alpha \\sqrt{\\gamma} A^j) // Here we compute [shift advection term] = \\partial_j (\\beta^j psi6phi) // Cache misses are likely more expensive than branch mispredictions here, // which is why we use if() statements and array lookups inside the if()'s. REAL psi6phi_rhsL=0.0; const REAL psi6phiL=psi6phi[index]; const REAL shiftx_iphjphkphL=shiftx_iphjphkph[index]; const REAL shifty_iphjphkphL=shifty_iphjphkph[index]; const REAL shiftz_iphjphkphL=shiftz_iphjphkph[index]; // \\partial_x (\\beta^x psi6phi) : if(shiftx_iphjphkphL < 0.0) { psi6phi_rhsL+=0.5*invdx0*(+ shiftx_iphjphkph[IDX3S(i-2,j,k)]*psi6phi[IDX3S(i-2,j,k)] -4.0*shiftx_iphjphkph[IDX3S(i-1,j,k)]*psi6phi[IDX3S(i-1,j,k)] +3.0*shiftx_iphjphkphL* psi6phiL); } else { psi6phi_rhsL+=0.5*invdx0*(- shiftx_iphjphkph[IDX3S(i+2,j,k)]*psi6phi[IDX3S(i+2,j,k)] +4.0*shiftx_iphjphkph[IDX3S(i+1,j,k)]*psi6phi[IDX3S(i+1,j,k)] -3.0*shiftx_iphjphkphL* psi6phiL); } // \\partial_y (\\beta^y psi6phi) : if(shifty_iphjphkphL < 0.0) { psi6phi_rhsL+=0.5*invdx1*(+ shifty_iphjphkph[IDX3S(i,j-2,k)]*psi6phi[IDX3S(i,j-2,k)] -4.0*shifty_iphjphkph[IDX3S(i,j-1,k)]*psi6phi[IDX3S(i,j-1,k)] +3.0*shifty_iphjphkphL* psi6phiL); } else { psi6phi_rhsL+=0.5*invdx1*(- shifty_iphjphkph[IDX3S(i,j+2,k)]*psi6phi[IDX3S(i,j+2,k)] +4.0*shifty_iphjphkph[IDX3S(i,j+1,k)]*psi6phi[IDX3S(i,j+1,k)] -3.0*shifty_iphjphkphL* psi6phiL); } // \\partial_z (\\beta^z psi6phi) : if(shiftz_iphjphkphL < 0.0) { psi6phi_rhsL+=0.5*invdx2*(+ shiftz_iphjphkph[IDX3S(i,j,k-2)]*psi6phi[IDX3S(i,j,k-2)] -4.0*shiftz_iphjphkph[IDX3S(i,j,k-1)]*psi6phi[IDX3S(i,j,k-1)] +3.0*shiftz_iphjphkphL* psi6phiL); } else { psi6phi_rhsL+=0.5*invdx2*(- shiftz_iphjphkph[IDX3S(i,j,k+2)]*psi6phi[IDX3S(i,j,k+2)] +4.0*shiftz_iphjphkph[IDX3S(i,j,k+1)]*psi6phi[IDX3S(i,j,k+1)] -3.0*shiftz_iphjphkphL* psi6phiL); } // Next we add \\partial_j (\\alpha \\sqrt{\\gamma} A^j) to \\partial_t psi6phi: psi6phi_rhsL+=invdx0*(alpha_sqrtg_Ax_interp[index] - alpha_sqrtg_Ax_interp[IDX3S(i+1,j,k)]) + invdx1*(alpha_sqrtg_Ay_interp[index] - alpha_sqrtg_Ay_interp[IDX3S(i,j+1,k)]) + invdx2*(alpha_sqrtg_Az_interp[index] - alpha_sqrtg_Az_interp[IDX3S(i,j,k+1)]); // *GENERALIZED* LORENZ GAUGE: // Finally, add damping factor to \\partial_t psi6phi //subtract lambda * alpha psi^6 Phi psi6phi_rhsL+=-xi_damping*alpha_iphjphkph[index]*psi6phiL; psi6phi_rhs[index] = psi6phi_rhsL; } } static inline REAL avg(const REAL f[PLUS2+1][PLUS2+1][PLUS2+1],const int imin,const int imax, const int jmin,const int jmax, const int kmin,const int kmax) { REAL retval=0.0,num_in_sum=0.0; for(int kk=kmin;kk<=kmax;kk++) for(int jj=jmin;jj<=jmax;jj++) for(int ii=imin;ii<=imax;ii++) { retval+=f[kk][jj][ii]; num_in_sum++; } return retval/num_in_sum; } """)
def GiRaFFE_NRPy_A2B(outdir,gammaDD,AD,BU): cmd.mkdir(outdir) # Set spatial dimension (must be 3 for BSSN) DIM = 3 par.set_parval_from_str("grid::DIM",DIM) # Compute the sqrt of the three metric determinant. import GRHD.equations as gh gh.compute_sqrtgammaDET(gammaDD) # Import the Levi-Civita symbol and build the corresponding tensor. # We already have a handy function to define the Levi-Civita symbol in WeylScalars import WeylScal4NRPy.WeylScalars_Cartesian as weyl LeviCivitaDDD = weyl.define_LeviCivitaSymbol_rank3() LeviCivitaUUU = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): LCijk = LeviCivitaDDD[i][j][k] #LeviCivitaDDD[i][j][k] = LCijk * sp.sqrt(gho.gammadet) LeviCivitaUUU[i][j][k] = LCijk / gh.sqrtgammaDET AD_dD = ixp.declarerank2("AD_dD","nosym") BU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j] # Write the code to compute derivatives with shifted stencils as needed. with open(os.path.join(outdir,"driver_AtoB.h"),"w") as file: file.write("""void compute_A2B_in_ghostzones(const paramstruct *restrict params,REAL *restrict in_gfs,REAL *restrict auxevol_gfs, const int i0min,const int i0max, const int i1min,const int i1max, const int i2min,const int i2max) { #include "../set_Cparameters.h" for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) { REAL dx_Ay,dx_Az,dy_Ax,dy_Az,dz_Ax,dz_Ay; // Check to see if we're on the +x or -x face. If so, use a downwinded- or upwinded-stencil, respectively. // Otherwise, use a centered stencil. if (i0 > 0 && i0 < Nxx_plus_2NGHOSTS0-1) { dx_Ay = invdx0*(-1.0/2.0*in_gfs[IDX4S(AD1GF, i0-1,i1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD1GF, i0+1,i1,i2)]); dx_Az = invdx0*(-1.0/2.0*in_gfs[IDX4S(AD2GF, i0-1,i1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD2GF, i0+1,i1,i2)]); } else if (i0==0) { dx_Ay = invdx0*(-3.0/2.0*in_gfs[IDX4S(AD1GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD1GF, i0+1,i1,i2)] - 1.0/2.0*in_gfs[IDX4S(AD1GF, i0+2,i1,i2)]); dx_Az = invdx0*(-3.0/2.0*in_gfs[IDX4S(AD2GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD2GF, i0+1,i1,i2)] - 1.0/2.0*in_gfs[IDX4S(AD2GF, i0+2,i1,i2)]); } else { dx_Ay = invdx0*((3.0/2.0)*in_gfs[IDX4S(AD1GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD1GF, i0-1,i1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD1GF, i0-2,i1,i2)]); dx_Az = invdx0*((3.0/2.0)*in_gfs[IDX4S(AD2GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD2GF, i0-1,i1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD2GF, i0-2,i1,i2)]); } // As above, but in the y direction. if (i1 > 0 && i1 < Nxx_plus_2NGHOSTS1-1) { dy_Ax = invdx1*(-1.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1-1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1+1,i2)]); dy_Az = invdx1*(-1.0/2.0*in_gfs[IDX4S(AD2GF, i0,i1-1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD2GF, i0,i1+1,i2)]); } else if (i1==0) { dy_Ax = invdx1*(-3.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD0GF, i0,i1+1,i2)] - 1.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1+2,i2)]); dy_Az = invdx1*(-3.0/2.0*in_gfs[IDX4S(AD2GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD2GF, i0,i1+1,i2)] - 1.0/2.0*in_gfs[IDX4S(AD2GF, i0,i1+2,i2)]); } else { dy_Ax = invdx1*((3.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD0GF, i0,i1-1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1-2,i2)]); dy_Az = invdx1*((3.0/2.0)*in_gfs[IDX4S(AD2GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD2GF, i0,i1-1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD2GF, i0,i1-2,i2)]); } // As above, but in the z direction. if (i2 > 0 && i2 < Nxx_plus_2NGHOSTS2-1) { dz_Ax = invdx2*(-1.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1,i2-1)] + (1.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1,i2+1)]); dz_Ay = invdx2*(-1.0/2.0*in_gfs[IDX4S(AD1GF, i0,i1,i2-1)] + (1.0/2.0)*in_gfs[IDX4S(AD1GF, i0,i1,i2+1)]); } else if (i2==0) { dz_Ax = invdx2*(-3.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD0GF, i0,i1,i2+1)] - 1.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1,i2+2)]); dz_Ay = invdx2*(-3.0/2.0*in_gfs[IDX4S(AD1GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD1GF, i0,i1,i2+1)] - 1.0/2.0*in_gfs[IDX4S(AD1GF, i0,i1,i2+2)]); } else { dz_Ax = invdx2*((3.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD0GF, i0,i1,i2-1)] + (1.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1,i2-2)]); dz_Ay = invdx2*((3.0/2.0)*in_gfs[IDX4S(AD1GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD1GF, i0,i1,i2-1)] + (1.0/2.0)*in_gfs[IDX4S(AD1GF, i0,i1,i2-2)]); } // Compute the magnetic field in the normal way, using the previously calculated derivatives. const double gammaDD00 = auxevol_gfs[IDX4S(GAMMADD00GF, i0,i1,i2)]; const double gammaDD01 = auxevol_gfs[IDX4S(GAMMADD01GF, i0,i1,i2)]; const double gammaDD02 = auxevol_gfs[IDX4S(GAMMADD02GF, i0,i1,i2)]; const double gammaDD11 = auxevol_gfs[IDX4S(GAMMADD11GF, i0,i1,i2)]; const double gammaDD12 = auxevol_gfs[IDX4S(GAMMADD12GF, i0,i1,i2)]; const double gammaDD22 = auxevol_gfs[IDX4S(GAMMADD22GF, i0,i1,i2)]; /* * NRPy+ Finite Difference Code Generation, Step 2 of 1: Evaluate SymPy expressions and write to main memory: */ const double invsqrtg = 1.0/sqrt(gammaDD00*gammaDD11*gammaDD22 - gammaDD00*gammaDD12*gammaDD12 + 2*gammaDD01*gammaDD02*gammaDD12 - gammaDD11*gammaDD02*gammaDD02 - gammaDD22*gammaDD01*gammaDD01); auxevol_gfs[IDX4S(BU0GF, i0,i1,i2)] = (dy_Az-dz_Ay)*invsqrtg; auxevol_gfs[IDX4S(BU1GF, i0,i1,i2)] = (dz_Ax-dx_Az)*invsqrtg; auxevol_gfs[IDX4S(BU2GF, i0,i1,i2)] = (dx_Ay-dy_Ax)*invsqrtg; } } """) # Now, we'll also write some more auxiliary functions to handle the order-lowering method for A2B with open(os.path.join(outdir,"driver_AtoB.h"),"a") as file: file.write("""REAL relative_error(REAL a, REAL b) { if((a+b)!=0.0) { return 2.0*fabs(a-b)/fabs(a+b); } else { return 0.0; } } #define M2 0 #define M1 1 #define P0 2 #define P1 3 #define P2 4 #define CN4 0 #define CN2 1 #define UP2 2 #define DN2 3 #define UP1 4 #define DN1 5 void compute_Bx_pointwise(REAL *Bx, const REAL invdy, const REAL *Ay, const REAL invdz, const REAL *Az) { REAL dz_Ay,dy_Az; dz_Ay = invdz*((Ay[P1]-Ay[M1])*2.0/3.0 - (Ay[P2]-Ay[M2])/12.0); dy_Az = invdy*((Az[P1]-Az[M1])*2.0/3.0 - (Az[P2]-Az[M2])/12.0); Bx[CN4] = dy_Az - dz_Ay; dz_Ay = invdz*(Ay[P1]-Ay[M1])/2.0; dy_Az = invdy*(Az[P1]-Az[M1])/2.0; Bx[CN2] = dy_Az - dz_Ay; dz_Ay = invdz*(-1.5*Ay[P0]+2.0*Ay[P1]-0.5*Ay[P2]); dy_Az = invdy*(-1.5*Az[P0]+2.0*Az[P1]-0.5*Az[P2]); Bx[UP2] = dy_Az - dz_Ay; dz_Ay = invdz*(1.5*Ay[P0]-2.0*Ay[M1]+0.5*Ay[M2]); dy_Az = invdy*(1.5*Az[P0]-2.0*Az[M1]+0.5*Az[M2]); Bx[DN2] = dy_Az - dz_Ay; dz_Ay = invdz*(Ay[P1]-Ay[P0]); dy_Az = invdy*(Az[P1]-Az[P0]); Bx[UP1] = dy_Az - dz_Ay; dz_Ay = invdz*(Ay[P0]-Ay[M1]); dy_Az = invdy*(Az[P0]-Az[M1]); Bx[DN1] = dy_Az - dz_Ay; } #define TOLERANCE_A2B 1.0e-4 REAL find_accepted_Bx_order(REAL *Bx) { REAL accepted_val = Bx[CN4]; REAL Rel_error_o2_vs_o4 = relative_error(Bx[CN2],Bx[CN4]); REAL Rel_error_oCN2_vs_oDN2 = relative_error(Bx[CN2],Bx[DN2]); REAL Rel_error_oCN2_vs_oUP2 = relative_error(Bx[CN2],Bx[UP2]); if(Rel_error_o2_vs_o4 > TOLERANCE_A2B) { accepted_val = Bx[CN2]; if(Rel_error_o2_vs_o4 > Rel_error_oCN2_vs_oDN2 || Rel_error_o2_vs_o4 > Rel_error_oCN2_vs_oUP2) { // Should we use AND or OR in if statement? if(relative_error(Bx[UP2],Bx[UP1]) < relative_error(Bx[DN2],Bx[DN1])) { accepted_val = Bx[UP2]; } else { accepted_val = Bx[DN2]; } } } return accepted_val; } """) order_lowering_body = """REAL AD0_1[5],AD0_2[5],AD1_2[5],AD1_0[5],AD2_0[5],AD2_1[5]; const double gammaDD00 = auxevol_gfs[IDX4S(GAMMADD00GF, i0,i1,i2)]; const double gammaDD01 = auxevol_gfs[IDX4S(GAMMADD01GF, i0,i1,i2)]; const double gammaDD02 = auxevol_gfs[IDX4S(GAMMADD02GF, i0,i1,i2)]; const double gammaDD11 = auxevol_gfs[IDX4S(GAMMADD11GF, i0,i1,i2)]; const double gammaDD12 = auxevol_gfs[IDX4S(GAMMADD12GF, i0,i1,i2)]; const double gammaDD22 = auxevol_gfs[IDX4S(GAMMADD22GF, i0,i1,i2)]; AD0_2[M2] = in_gfs[IDX4S(AD0GF, i0,i1,i2-2)]; AD0_2[M1] = in_gfs[IDX4S(AD0GF, i0,i1,i2-1)]; AD0_1[M2] = in_gfs[IDX4S(AD0GF, i0,i1-2,i2)]; AD0_1[M1] = in_gfs[IDX4S(AD0GF, i0,i1-1,i2)]; AD0_1[P0] = AD0_2[P0] = in_gfs[IDX4S(AD0GF, i0,i1,i2)]; AD0_1[P1] = in_gfs[IDX4S(AD0GF, i0,i1+1,i2)]; AD0_1[P2] = in_gfs[IDX4S(AD0GF, i0,i1+2,i2)]; AD0_2[P1] = in_gfs[IDX4S(AD0GF, i0,i1,i2+1)]; AD0_2[P2] = in_gfs[IDX4S(AD0GF, i0,i1,i2+2)]; AD1_2[M2] = in_gfs[IDX4S(AD1GF, i0,i1,i2-2)]; AD1_2[M1] = in_gfs[IDX4S(AD1GF, i0,i1,i2-1)]; AD1_0[M2] = in_gfs[IDX4S(AD1GF, i0-2,i1,i2)]; AD1_0[M1] = in_gfs[IDX4S(AD1GF, i0-1,i1,i2)]; AD1_2[P0] = AD1_0[P0] = in_gfs[IDX4S(AD1GF, i0,i1,i2)]; AD1_0[P1] = in_gfs[IDX4S(AD1GF, i0+1,i1,i2)]; AD1_0[P2] = in_gfs[IDX4S(AD1GF, i0+2,i1,i2)]; AD1_2[P1] = in_gfs[IDX4S(AD1GF, i0,i1,i2+1)]; AD1_2[P2] = in_gfs[IDX4S(AD1GF, i0,i1,i2+2)]; AD2_1[M2] = in_gfs[IDX4S(AD2GF, i0,i1-2,i2)]; AD2_1[M1] = in_gfs[IDX4S(AD2GF, i0,i1-1,i2)]; AD2_0[M2] = in_gfs[IDX4S(AD2GF, i0-2,i1,i2)]; AD2_0[M1] = in_gfs[IDX4S(AD2GF, i0-1,i1,i2)]; AD2_0[P0] = AD2_1[P0] = in_gfs[IDX4S(AD2GF, i0,i1,i2)]; AD2_0[P1] = in_gfs[IDX4S(AD2GF, i0+1,i1,i2)]; AD2_0[P2] = in_gfs[IDX4S(AD2GF, i0+2,i1,i2)]; AD2_1[P1] = in_gfs[IDX4S(AD2GF, i0,i1+1,i2)]; AD2_1[P2] = in_gfs[IDX4S(AD2GF, i0,i1+2,i2)]; const double invsqrtg = 1.0/sqrt(gammaDD00*gammaDD11*gammaDD22 - gammaDD00*gammaDD12*gammaDD12 + 2*gammaDD01*gammaDD02*gammaDD12 - gammaDD11*gammaDD02*gammaDD02 - gammaDD22*gammaDD01*gammaDD01); REAL BU0[4],BU1[4],BU2[4]; compute_Bx_pointwise(BU0,invdx2,AD1_2,invdx1,AD2_1); compute_Bx_pointwise(BU1,invdx0,AD2_0,invdx2,AD0_2); compute_Bx_pointwise(BU2,invdx1,AD0_1,invdx0,AD1_0); auxevol_gfs[IDX4S(BU0GF, i0,i1,i2)] = find_accepted_Bx_order(BU0)*invsqrtg; auxevol_gfs[IDX4S(BU1GF, i0,i1,i2)] = find_accepted_Bx_order(BU1)*invsqrtg; auxevol_gfs[IDX4S(BU2GF, i0,i1,i2)] = find_accepted_Bx_order(BU2)*invsqrtg; """ # Here, we'll use the outCfunction() function to output a function that will compute the magnetic field # on the interior. Then, we'll add postloop code to handle the ghostzones. desc="Compute the magnetic field from the vector potential everywhere, including ghostzones" name="driver_A_to_B" driver_Ccode = outCfunction( outfile = "returnstring", desc=desc, name=name, params = "const paramstruct *restrict params,REAL *restrict in_gfs,REAL *restrict auxevol_gfs", body = fin.FD_outputC("returnstring",[lhrh(lhs=gri.gfaccess("out_gfs","BU0"),rhs=BU[0]),\ lhrh(lhs=gri.gfaccess("out_gfs","BU1"),rhs=BU[1]),\ lhrh(lhs=gri.gfaccess("out_gfs","BU2"),rhs=BU[2])]).replace("IDX4","IDX4S"), # body = order_lowering_body, postloop = """ int imin[3] = { NGHOSTS_A2B, NGHOSTS_A2B, NGHOSTS_A2B }; int imax[3] = { NGHOSTS+Nxx0, NGHOSTS+Nxx1, NGHOSTS+Nxx2 }; // Now, we loop over the ghostzones to calculate the magnetic field there. for(int which_gz = 0; which_gz < NGHOSTS_A2B; which_gz++) { // After updating each face, adjust imin[] and imax[] // to reflect the newly-updated face extents. compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2]); imin[0]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2]); imax[0]++; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2]); imin[1]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2]); imax[1]++; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2]); imin[2]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1); imax[2]++; } """, loopopts="InteriorPoints", rel_path_for_Cparams=os.path.join("../")).replace("=NGHOSTS","=NGHOSTS_A2B").replace("NGHOSTS+Nxx0","Nxx_plus_2NGHOSTS0-NGHOSTS_A2B").replace("NGHOSTS+Nxx1","Nxx_plus_2NGHOSTS1-NGHOSTS_A2B").replace("NGHOSTS+Nxx2","Nxx_plus_2NGHOSTS2-NGHOSTS_A2B") with open(os.path.join(outdir,"driver_AtoB.h"),"a") as file: file.write(driver_Ccode)
def GiRaFFE_NRPy_FCVAL(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. with open(os.path.join(Ccodesdir,"interpolate_metric_gfs_to_cell_faces.h"),"w") as file: file.write("""// Side note: the following values could be used for cell averaged gfs: // am2=-1.0/12.0, am1=7.0/12.0, a0=7.0/12.0, a1=-1.0/12.0 // However, since the metric gfs store the grid point values instead of the cell average, // the following coefficients should be used: // am2 = -1/16, am1 = 9/16, a0 = 9/16, a1 = -1/16 // This will yield the third-order-accurate face values at m-1/2, // using values specified at {m-2,m-1,m,m+1} #define AM2 -0.0625 #define AM1 0.5625 #define A0 0.5625 #define A1 -0.0625 #define COMPUTE_FCVAL(METRICm2,METRICm1,METRIC,METRICp1) (AM2*(METRICm2) + AM1*(METRICm1) + A0*(METRIC) + A1*(METRICp1)) const int metric_gfs_list[10] = {GAMMADD00GF, GAMMADD01GF, GAMMADD02GF, GAMMADD11GF, GAMMADD12GF, GAMMADD22GF, BETAU0GF, BETAU1GF, BETAU2GF, ALPHAGF}; const int metric_gfs_face_list[10] = {GAMMA_FACEDD00GF, GAMMA_FACEDD01GF, GAMMA_FACEDD02GF, GAMMA_FACEDD11GF, GAMMA_FACEDD12GF, GAMMA_FACEDD22GF, BETA_FACEU0GF, BETA_FACEU1GF, BETA_FACEU2GF, ALPHA_FACEGF}; const int num_metric_gfs = 10; """) desc = "Interpolate metric gridfunctions to cell faces" name = "interpolate_metric_gfs_to_cell_faces" interp_Cfunc = outCfunction( outfile = "returnstring", desc=desc, name=name, params ="const paramstruct *params,REAL *auxevol_gfs,const int flux_dirn", preloop =""" int in_gf,out_gf; REAL Qm2,Qm1,Qp0,Qp1; """ , body =""" for(int gf = 0;gf < num_metric_gfs;gf++) { in_gf = metric_gfs_list[gf]; out_gf = metric_gfs_face_list[gf]; for (int i2 = 2;i2 < Nxx_plus_2NGHOSTS2-1;i2++) { for (int i1 = 2;i1 < Nxx_plus_2NGHOSTS1-1;i1++) { for (int i0 = 2;i0 < Nxx_plus_2NGHOSTS0-1;i0++) { Qm2 = auxevol_gfs[IDX4S(in_gf,i0-2*kronecker_delta[flux_dirn][0],i1-2*kronecker_delta[flux_dirn][1],i2-2*kronecker_delta[flux_dirn][2])]; Qm1 = auxevol_gfs[IDX4S(in_gf,i0-kronecker_delta[flux_dirn][0],i1-kronecker_delta[flux_dirn][1],i2-kronecker_delta[flux_dirn][2])]; Qp0 = auxevol_gfs[IDX4S(in_gf,i0,i1,i2)]; Qp1 = auxevol_gfs[IDX4S(in_gf,i0+kronecker_delta[flux_dirn][0],i1+kronecker_delta[flux_dirn][1],i2+kronecker_delta[flux_dirn][2])]; auxevol_gfs[IDX4S(out_gf,i0,i1,i2)] = COMPUTE_FCVAL(Qm2,Qm1,Qp0,Qp1); } } } } """, rel_path_to_Cparams=os.path.join("../")) with open(os.path.join(Ccodesdir,"interpolate_metric_gfs_to_cell_faces.h"),"a") as file: file.write(interp_Cfunc)
def GiRaFFE_NRPy_PPM(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. with open( os.path.join(Ccodesdir, "reconstruct_set_of_prims_PPM_GRFFE_NRPy.c"), "w") as file: file.write("""/***************************************** * PPM Reconstruction Interface. * Zachariah B. Etienne (2013) * * This version of PPM implements the standard * Colella & Woodward PPM, but in the GRFFE * limit, where P=rho=0. Thus, e.g., ftilde=0. *****************************************/ #define MINUS2 0 #define MINUS1 1 #define PLUS0 2 #define PLUS1 3 #define PLUS2 4 #define MAXNUMINDICES 5 // ^^^^^^^^^^^^^ Be _sure_ to define MAXNUMINDICES appropriately! #define MIN(a,b) ( ((a) < (b)) ? (a) : (b) ) #define MAX(a,b) ( ((a) > (b)) ? (a) : (b) ) #define SQR(x) ((x) * (x)) // FIXME: Should make this zero-offset for NRPy+ standards. Probably a wrapper function for compatibility with a minimum of other changes? const int kronecker_delta[4][3] = { { 0,0,0 }, { 1,0,0 }, { 0,1,0 }, { 0,0,1 } }; // You'll find the #define's for LOOP_DEFINE and SET_INDEX_ARRAYS_NRPY inside: #include "loop_defines_reconstruction_NRPy.h" static inline REAL slope_limit_NRPy(const REAL dU,const REAL dUp1); static inline void monotonize_NRPy(const REAL U,REAL Ur,REAL Ul); static void reconstruct_set_of_prims_PPM_GRFFE_NRPy(const paramstruct *params,REAL *auxevol_gfs,const int flux_dirn, const int num_prims_to_reconstruct,const int *which_prims_to_reconstruct, const gf_and_gz_struct *in_prims,gf_and_gz_struct *out_prims_r, gf_and_gz_struct *out_prims_l,REAL *temporary) { #include "set_Cparameters.h" const int Nxx_plus_2NGHOSTS[3] = {Nxx_plus_2NGHOSTS0,Nxx_plus_2NGHOSTS1,Nxx_plus_2NGHOSTS2}; REAL U[NUM_RECONSTRUCT_GFS][MAXNUMINDICES],dU[NUM_RECONSTRUCT_GFS][MAXNUMINDICES],slope_lim_dU[NUM_RECONSTRUCT_GFS][MAXNUMINDICES], Ur[NUM_RECONSTRUCT_GFS][MAXNUMINDICES],Ul[NUM_RECONSTRUCT_GFS][MAXNUMINDICES]; int ijkgz_lo_hi[4][2]; for(int ww=0;ww<num_prims_to_reconstruct;ww++) { const int whichvar=which_prims_to_reconstruct[ww]; if(in_prims[whichvar].gz_lo[flux_dirn]!=0 || in_prims[whichvar].gz_hi[flux_dirn]!=0) { printf("TOO MANY GZ'S! WHICHVAR=%d: %d %d %d : %d %d %d DIRECTION %d",whichvar, in_prims[whichvar].gz_lo[1],in_prims[whichvar].gz_lo[2],in_prims[whichvar].gz_lo[3], in_prims[whichvar].gz_hi[1],in_prims[whichvar].gz_hi[2],in_prims[whichvar].gz_hi[3],flux_dirn); exit(0); } // *** LOOP 1: Interpolate to Ur and Ul, which are face values *** // You will find that Ur depends on U at MINUS1,PLUS0, PLUS1,PLUS2, and // Ul depends on U at MINUS2,MINUS1,PLUS0,PLUS1. // However, we define the below loop from MINUS2 to PLUS2. Why not split // this up and get additional points? Maybe we should. In GRMHD, the // reason is that later on, Ur and Ul depend on ftilde, which is // defined from MINUS2 to PLUS2, so we would lose those points anyway. // But in GRFFE, ftilde is set to zero, so there may be a potential // for boosting performance here. LOOP_DEFINE(2,2, Nxx_plus_2NGHOSTS,flux_dirn, ijkgz_lo_hi,in_prims[whichvar].gz_lo,in_prims[whichvar].gz_hi) { SET_INDEX_ARRAYS_NRPY(-2,2,flux_dirn); /* *** LOOP 1a: READ INPUT *** */ // Read in a primitive at all gridpoints between m = MINUS2 & PLUS2, where m's direction is given by flux_dirn. Store to U. for(int ii=MINUS2;ii<=PLUS2;ii++) U[whichvar][ii] = in_prims[whichvar].gf[index_arr[flux_dirn][ii]]; /* *** LOOP 1b: DO COMPUTATION *** */ /* First, compute simple dU = U(i) - U(i-1), where direction of i * is given by flux_dirn, and U is a primitive variable: * {vx,vy,vz,Bx,By,Bz}. */ // Note that for Ur and Ul at i, we must compute dU(i-1),dU(i),dU(i+1), // and dU(i+2) dU[whichvar][MINUS1] = U[whichvar][MINUS1]- U[whichvar][MINUS2]; dU[whichvar][PLUS0] = U[whichvar][PLUS0] - U[whichvar][MINUS1]; dU[whichvar][PLUS1] = U[whichvar][PLUS1] - U[whichvar][PLUS0]; dU[whichvar][PLUS2] = U[whichvar][PLUS2] - U[whichvar][PLUS1]; // Then, compute slope-limited dU, using MC slope limiter: slope_lim_dU[whichvar][MINUS1]=slope_limit_NRPy(dU[whichvar][MINUS1],dU[whichvar][PLUS0]); slope_lim_dU[whichvar][PLUS0] =slope_limit_NRPy(dU[whichvar][PLUS0], dU[whichvar][PLUS1]); slope_lim_dU[whichvar][PLUS1] =slope_limit_NRPy(dU[whichvar][PLUS1], dU[whichvar][PLUS2]); // Finally, compute face values Ur and Ul based on the PPM prescription // (Eq. A1 in http://arxiv.org/pdf/astro-ph/0503420.pdf, but using standard 1/6=(1.0/6.0) coefficient) // Ur[PLUS0] represents U(i+1/2) // We applied a simplification to the following line: Ur=U+0.5*(U(i+1)-U) + ... = 0.5*(U(i+1)+U) + ... Ur[whichvar][PLUS0] = 0.5*(U[whichvar][PLUS1] + U[whichvar][PLUS0] ) + (1.0/6.0)*(slope_lim_dU[whichvar][PLUS0] - slope_lim_dU[whichvar][PLUS1]); // Ul[PLUS0] represents U(i-1/2) // We applied a simplification to the following line: Ul=U(i-1)+0.5*(U-U(i-1)) + ... = 0.5*(U+U(i-1)) + ... Ul[whichvar][PLUS0] = 0.5*(U[whichvar][PLUS0] + U[whichvar][MINUS1]) + (1.0/6.0)*(slope_lim_dU[whichvar][MINUS1] - slope_lim_dU[whichvar][PLUS0]); /* *** LOOP 1c: WRITE OUTPUT *** */ // Store right face values to {vxr,vyr,vzr,Bxr,Byr,Bzr}, // and left face values to {vxl,vyl,vzl,Bxl,Byl,Bzl} out_prims_r[whichvar].gf[index_arr[flux_dirn][PLUS0]] = Ur[whichvar][PLUS0]; out_prims_l[whichvar].gf[index_arr[flux_dirn][PLUS0]] = Ul[whichvar][PLUS0]; } // *** LOOP 2 (REMOVED): STEEPEN RHOB. RHOB DOES NOT EXIST IN GRFFE EQUATIONS *** } // *** LOOP 3: FLATTEN BASED ON FTILDE AND MONOTONIZE *** for(int ww=0;ww<num_prims_to_reconstruct;ww++) { const int whichvar=which_prims_to_reconstruct[ww]; // ftilde() depends on P(MINUS2,MINUS1,PLUS1,PLUS2), THUS IS SET TO ZERO IN GRFFE LOOP_DEFINE(2,2, Nxx_plus_2NGHOSTS,flux_dirn, ijkgz_lo_hi,in_prims[whichvar].gz_lo,in_prims[whichvar].gz_hi) { SET_INDEX_ARRAYS_NRPY(0,0,flux_dirn); U[whichvar][PLUS0] = in_prims[whichvar].gf[index_arr[flux_dirn][PLUS0]]; Ur[whichvar][PLUS0] = out_prims_r[whichvar].gf[index_arr[flux_dirn][PLUS0]]; Ul[whichvar][PLUS0] = out_prims_l[whichvar].gf[index_arr[flux_dirn][PLUS0]]; // ftilde_gf was computed in the function compute_ftilde_gf(), called before this routine //REAL ftilde = ftilde_gf[index_arr[flux_dirn][PLUS0]]; // ...and then flatten (local operation) Ur[whichvar][PLUS0] = Ur[whichvar][PLUS0]; Ul[whichvar][PLUS0] = Ul[whichvar][PLUS0]; // Then monotonize monotonize_NRPy(U[whichvar][PLUS0],Ur[whichvar][PLUS0],Ul[whichvar][PLUS0]); out_prims_r[whichvar].gf[index_arr[flux_dirn][PLUS0]] = Ur[whichvar][PLUS0]; out_prims_l[whichvar].gf[index_arr[flux_dirn][PLUS0]] = Ul[whichvar][PLUS0]; } // Note: ftilde=0 in GRFFE. Ur depends on ftilde, which depends on points of U between MINUS2 and PLUS2 out_prims_r[whichvar].gz_lo[flux_dirn]+=2; out_prims_r[whichvar].gz_hi[flux_dirn]+=2; // Note: ftilde=0 in GRFFE. Ul depends on ftilde, which depends on points of U between MINUS2 and PLUS2 out_prims_l[whichvar].gz_lo[flux_dirn]+=2; out_prims_l[whichvar].gz_hi[flux_dirn]+=2; } // *** LOOP 4: SHIFT Ur AND Ul *** /* Currently face values are set so that * a) Ur(i) represents U(i+1/2), and * b) Ul(i) represents U(i-1/2) * Here, we shift so that the indices are consistent: * a) U(i-1/2+epsilon) = oldUl(i) = newUr(i) * b) U(i-1/2-epsilon) = oldUr(i-1) = newUl(i) * Note that this step is not strictly necessary if you keep * track of indices when computing the flux. */ for(int ww=0;ww<num_prims_to_reconstruct;ww++) { const int whichvar=which_prims_to_reconstruct[ww]; LOOP_DEFINE(3,2, Nxx_plus_2NGHOSTS,flux_dirn, ijkgz_lo_hi,in_prims[whichvar].gz_lo,in_prims[whichvar].gz_hi) { SET_INDEX_ARRAYS_NRPY(-1,0,flux_dirn); temporary[index_arr[flux_dirn][PLUS0]] = out_prims_r[whichvar].gf[index_arr[flux_dirn][MINUS1]]; } LOOP_DEFINE(3,2, Nxx_plus_2NGHOSTS,flux_dirn, ijkgz_lo_hi,in_prims[whichvar].gz_lo,in_prims[whichvar].gz_hi) { SET_INDEX_ARRAYS_NRPY(0,0,flux_dirn); // Then shift so that Ur represents the gridpoint at i-1/2+epsilon, // and Ul represents the gridpoint at i-1/2-epsilon. // Ur(i-1/2) = Ul(i-1/2) = U(i-1/2+epsilon) // Ul(i-1/2) = Ur(i+1/2 - 1) = U(i-1/2-epsilon) out_prims_r[whichvar].gf[index_arr[flux_dirn][PLUS0]] = out_prims_l[whichvar].gf[index_arr[flux_dirn][PLUS0]]; out_prims_l[whichvar].gf[index_arr[flux_dirn][PLUS0]] = temporary[index_arr[flux_dirn][PLUS0]]; } // Ul was just shifted, so we lost another ghostzone. out_prims_l[whichvar].gz_lo[flux_dirn]+=1; out_prims_l[whichvar].gz_hi[flux_dirn]+=0; // As for Ur, we didn't need to get rid of another ghostzone, // but we did ... seems wasteful! out_prims_r[whichvar].gz_lo[flux_dirn]+=1; out_prims_r[whichvar].gz_hi[flux_dirn]+=0; } } // Set SLOPE_LIMITER_COEFF = 2.0 for MC, 1 for minmod #define SLOPE_LIMITER_COEFF 2.0 //Eq. 60 in JOURNAL OF COMPUTATIONAL PHYSICS 123, 1-14 (1996) // [note the factor of 2 missing in the |a_{j+1} - a_{j}| term]. // Recall that dU = U_{i} - U_{i-1}. static inline REAL slope_limit_NRPy(const REAL dU,const REAL dUp1) { if(dU*dUp1 > 0.0) { //delta_m_U=0.5 * [ (u_(i+1)-u_i) + (u_i-u_(i-1)) ] = (u_(i+1) - u_(i-1))/2 <-- first derivative, second-order; this should happen most of the time (smooth flows) const REAL delta_m_U = 0.5*(dU + dUp1); // EXPLANATION OF BELOW LINE OF CODE. // In short, sign_delta_a_j = sign(delta_m_U) = (0.0 < delta_m_U) - (delta_m_U < 0.0). // If delta_m_U>0, then (0.0 < delta_m_U)==1, and (delta_m_U < 0.0)==0, so sign_delta_a_j=+1 // If delta_m_U<0, then (0.0 < delta_m_U)==0, and (delta_m_U < 0.0)==1, so sign_delta_a_j=-1 // If delta_m_U==0,then (0.0 < delta_m_U)==0, and (delta_m_U < 0.0)==0, so sign_delta_a_j=0 const int sign_delta_m_U = (0.0 < delta_m_U) - (delta_m_U < 0.0); //Decide whether to use 2nd order derivative or first-order derivative, limiting slope. return sign_delta_m_U*MIN(fabs(delta_m_U),MIN(SLOPE_LIMITER_COEFF*fabs(dUp1),SLOPE_LIMITER_COEFF*fabs(dU))); } return 0.0; } static inline void monotonize_NRPy(const REAL U,REAL Ur,REAL Ul) { const REAL dU = Ur - Ul; const REAL mU = 0.5*(Ur+Ul); if ( (Ur-U)*(U-Ul) <= 0.0) { Ur = U; Ul = U; return; } if ( dU*(U-mU) > (1.0/6.0)*SQR(dU)) { Ul = 3.0*U - 2.0*Ur; return; } if ( dU*(U-mU) < -(1.0/6.0)*SQR(dU)) { Ur = 3.0*U - 2.0*Ul; return; } } """) with open(os.path.join(Ccodesdir, "loop_defines_reconstruction_NRPy.h"), "w") as file: file.write("""#ifndef loop_defines_reconstruction_NRPy_H_ #define loop_defines_reconstruction_NRPy_H_ #define LOOP_DEFINE(gz_shift_lo,gz_shift_hi, ext,flux_dirn, ijkgz_lo_hi,gz_lo,gz_hi) \\ for(int rr=1;rr<=3;rr++) { \\ ijkgz_lo_hi[rr][0]= gz_lo[rr]; \\ ijkgz_lo_hi[rr][1]=ext[rr-1]-gz_hi[rr]; \\ } \\ ijkgz_lo_hi[flux_dirn][0] += gz_shift_lo; \\ ijkgz_lo_hi[flux_dirn][1] -= gz_shift_hi; \\ /* The following line is valid C99 */ \\ _Pragma("omp parallel for private(U,dU,slope_lim_dU,Ur,Ul)") \\ for(int k=ijkgz_lo_hi[3][0];k<ijkgz_lo_hi[3][1];k++) \\ for(int j=ijkgz_lo_hi[2][0];j<ijkgz_lo_hi[2][1];j++) \\ for(int i=ijkgz_lo_hi[1][0];i<ijkgz_lo_hi[1][1];i++) // This define only sets indices. // FIXME: benchmark with and without the if() statement. // FIXME: try without index_arr being defined in all directions. #define SET_INDEX_ARRAYS_NRPY(IMIN,IMAX,flux_dirn) \\ const int max_shift=(MAXNUMINDICES/2); \\ /* DEBUGGING ONLY: if(IMIN<-max_shift || IMAX>max_shift) CCTK_VError(VERR_DEF_PARAMS,"FIX MAXNUMINDICES!"); */ \\ int index_arr[4][MAXNUMINDICES]; \\ for(int idx=IMIN;idx<=IMAX;idx++) { \\ index_arr[flux_dirn][idx+max_shift]= \\ IDX3S( \\ i+idx*kronecker_delta[flux_dirn][0], \\ j+idx*kronecker_delta[flux_dirn][1], \\ k+idx*kronecker_delta[flux_dirn][2]); \\ } #define SET_INDEX_ARRAYS_NRPY_3DBLOCK(IJKLOHI) \\ const int max_shift=(MAXNUMINDICES/2); \\ int index_arr_3DB[MAXNUMINDICES][MAXNUMINDICES][MAXNUMINDICES]; \\ for(int idx_k=IJKLOHI[4];idx_k<=IJKLOHI[5];idx_k++) for(int idx_j=IJKLOHI[2];idx_j<=IJKLOHI[3];idx_j++) for(int idx_i=IJKLOHI[0];idx_i<=IJKLOHI[1];idx_i++) { \\ index_arr_3DB[idx_k+max_shift][idx_j+max_shift][idx_i+max_shift]=CCTK_GFINDEX3D(cctkGH,i+idx_i,j+idx_j,k+idx_k); \\ } #endif /* loop_defines_reconstruction_NRPy_H_ */ """)
def GiRaFFE_NRPy_A2B(outdir): cmd.mkdir(outdir) # Set spatial dimension (must be 3 for BSSN) DIM = 3 par.set_parval_from_str("grid::DIM", DIM) # Register the gridfunction gammadet. This determinant will be calculated separately gammadet = gri.register_gridfunctions("AUXEVOL", "gammadet") # Import the Levi-Civita symbol and build the corresponding tensor. # We already have a handy function to define the Levi-Civita symbol in WeylScalars import WeylScal4NRPy.WeylScalars_Cartesian as weyl LeviCivitaDDD = weyl.define_LeviCivitaSymbol_rank3() LeviCivitaUUU = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): LCijk = LeviCivitaDDD[i][j][k] #LeviCivitaDDD[i][j][k] = LCijk * sp.sqrt(gho.gammadet) LeviCivitaUUU[i][j][k] = LCijk / sp.sqrt(gammadet) # Step 1.a: A useful function AD = ixp.register_gridfunctions_for_single_rank1("EVOL", "AD") BU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "BU") AD_dD = ixp.declarerank2("AD_dD", "nosym") BU = ixp.zerorank1( ) # BU is already registered as a gridfunction, but we need to zero its values and declare it in this scope. for i in range(DIM): for j in range(DIM): for k in range(DIM): BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j] # Write the code to compute derivatives with shifted stencils as needed. with open(os.path.join(outdir, "driver_AtoB.h"), "w") as file: file.write( """void compute_A2B_in_ghostzones(const paramstruct *restrict params,REAL *restrict in_gfs,REAL *restrict auxevol_gfs, const int i0min,const int i0max, const int i1min,const int i1max, const int i2min,const int i2max) { #include "set_Cparameters.h" for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) { REAL dx_Ay,dx_Az,dy_Ax,dy_Az,dz_Ax,dz_Ay; // Check to see if we're on the +x or -x face. If so, use a downwinded- or upwinded-stencil, respectively. // Otherwise, use a centered stencil. if (i0 > 0 && i0 < Nxx_plus_2NGHOSTS0-1) { dx_Ay = invdx0*(-1.0/2.0*in_gfs[IDX4S(AD1GF, i0-1,i1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD1GF, i0+1,i1,i2)]); dx_Az = invdx0*(-1.0/2.0*in_gfs[IDX4S(AD2GF, i0-1,i1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD2GF, i0+1,i1,i2)]); } else if (i0==0) { dx_Ay = invdx0*(-3.0/2.0*in_gfs[IDX4S(AD1GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD1GF, i0+1,i1,i2)] - 1.0/2.0*in_gfs[IDX4S(AD1GF, i0+2,i1,i2)]); dx_Az = invdx0*(-3.0/2.0*in_gfs[IDX4S(AD2GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD2GF, i0+1,i1,i2)] - 1.0/2.0*in_gfs[IDX4S(AD2GF, i0+2,i1,i2)]); } else { dx_Ay = invdx0*((3.0/2.0)*in_gfs[IDX4S(AD1GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD1GF, i0-1,i1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD1GF, i0-2,i1,i2)]); dx_Az = invdx0*((3.0/2.0)*in_gfs[IDX4S(AD2GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD2GF, i0-1,i1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD2GF, i0-2,i1,i2)]); } // As above, but in the y direction. if (i1 > 0 && i1 < Nxx_plus_2NGHOSTS1-1) { dy_Ax = invdx1*(-1.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1-1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1+1,i2)]); dy_Az = invdx1*(-1.0/2.0*in_gfs[IDX4S(AD2GF, i0,i1-1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD2GF, i0,i1+1,i2)]); } else if (i1==0) { dy_Ax = invdx1*(-3.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD0GF, i0,i1+1,i2)] - 1.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1+2,i2)]); dy_Az = invdx1*(-3.0/2.0*in_gfs[IDX4S(AD2GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD2GF, i0,i1+1,i2)] - 1.0/2.0*in_gfs[IDX4S(AD2GF, i0,i1+2,i2)]); } else { dy_Ax = invdx1*((3.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD0GF, i0,i1-1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1-2,i2)]); dy_Az = invdx1*((3.0/2.0)*in_gfs[IDX4S(AD2GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD2GF, i0,i1-1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD2GF, i0,i1-2,i2)]); } // As above, but in the z direction. if (i2 > 0 && i2 < Nxx_plus_2NGHOSTS2-1) { dz_Ax = invdx2*(-1.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1,i2-1)] + (1.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1,i2+1)]); dz_Ay = invdx2*(-1.0/2.0*in_gfs[IDX4S(AD1GF, i0,i1,i2-1)] + (1.0/2.0)*in_gfs[IDX4S(AD1GF, i0,i1,i2+1)]); } else if (i2==0) { dz_Ax = invdx2*(-3.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD0GF, i0,i1,i2+1)] - 1.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1,i2+2)]); dz_Ay = invdx2*(-3.0/2.0*in_gfs[IDX4S(AD1GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD1GF, i0,i1,i2+1)] - 1.0/2.0*in_gfs[IDX4S(AD1GF, i0,i1,i2+2)]); } else { dz_Ax = invdx2*((3.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD0GF, i0,i1,i2-1)] + (1.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1,i2-2)]); dz_Ay = invdx2*((3.0/2.0)*in_gfs[IDX4S(AD1GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD1GF, i0,i1,i2-1)] + (1.0/2.0)*in_gfs[IDX4S(AD1GF, i0,i1,i2-2)]); } // Compute the magnetic field in the normal way, using the previously calculated derivatives. const REAL sqrtgammadet = sqrt(auxevol_gfs[IDX4S(GAMMADETGF, i0,i1,i2)]); auxevol_gfs[IDX4S(BU0GF, i0,i1,i2)] = (dy_Az-dz_Ay)/sqrtgammadet; auxevol_gfs[IDX4S(BU1GF, i0,i1,i2)] = (dz_Ax-dx_Az)/sqrtgammadet; auxevol_gfs[IDX4S(BU2GF, i0,i1,i2)] = (dx_Ay-dy_Ax)/sqrtgammadet; } } """) # Here, we'll use the outCfunction() function to output a function that will compute the magnetic field # on the interior. Then, we'll add postloop code to handle the ghostzones. desc = "Compute the magnetic field from the vector potential everywhere, including ghostzones" name = "driver_A_to_B" driver_Ccode = outCfunction( outfile = "returnstring", desc=desc, name=name, params = "const paramstruct *restrict params,REAL *restrict in_gfs,REAL *restrict auxevol_gfs", body = fin.FD_outputC("returnstring",[lhrh(lhs=gri.gfaccess("out_gfs","BU0"),rhs=BU[0]),\ lhrh(lhs=gri.gfaccess("out_gfs","BU1"),rhs=BU[1]),\ lhrh(lhs=gri.gfaccess("out_gfs","BU2"),rhs=BU[2])], params="outCverbose=False").replace("IDX4","IDX4S"), postloop = """ int imin[3] = { NGHOSTS_A2B, NGHOSTS_A2B, NGHOSTS_A2B }; int imax[3] = { NGHOSTS+Nxx0, NGHOSTS+Nxx1, NGHOSTS+Nxx2 }; // Now, we loop over the ghostzones to calculate the magnetic field there. for(int which_gz = 0; which_gz < NGHOSTS_A2B; which_gz++) { // After updating each face, adjust imin[] and imax[] // to reflect the newly-updated face extents. compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2]); imin[0]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2]); imax[0]++; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2]); imin[1]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2]); imax[1]++; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2]); imin[2]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1); imax[2]++; } """, loopopts="InteriorPoints").replace("=NGHOSTS","=NGHOSTS_A2B").replace("NGHOSTS+Nxx0","Nxx_plus_2NGHOSTS0-NGHOSTS_A2B").replace("NGHOSTS+Nxx1","Nxx_plus_2NGHOSTS1-NGHOSTS_A2B").replace("NGHOSTS+Nxx2","Nxx_plus_2NGHOSTS2-NGHOSTS_A2B") with open(os.path.join(outdir, "driver_AtoB.h"), "a") as file: file.write(driver_Ccode)