def add_to_Cfunction_dict__GiRaFFE_NRPy_A2B(gammaDD, AD, BU, includes=None): # Set spatial dimension (must be 3 for BSSN) DIM = 3 par.set_parval_from_str("grid::DIM", DIM) # Compute the sqrt of the three metric determinant. import GRHD.equations as gh gh.compute_sqrtgammaDET(gammaDD) # Import the Levi-Civita symbol and build the corresponding tensor. # We already have a handy function to define the Levi-Civita symbol in indexedexp.py LeviCivitaUUU = ixp.LeviCivitaTensorUUU_dim3_rank3(gh.sqrtgammaDET) AD_dD = ixp.declarerank2("AD_dD", "nosym") BU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j] # Here, we'll use the add_to_Cfunction_dict() function to output a function that will compute the magnetic field # on the interior. Then, we'll add postloop code to handle the ghostzones. desc = "Compute the magnetic field from the vector potential everywhere, including ghostzones" name = "driver_A_to_B" params = "const paramstruct *restrict params,REAL *restrict in_gfs,REAL *restrict auxevol_gfs" body = fin.FD_outputC("returnstring", [ lhrh(lhs=gri.gfaccess("out_gfs", "BU0"), rhs=BU[0]), lhrh(lhs=gri.gfaccess("out_gfs", "BU1"), rhs=BU[1]), lhrh(lhs=gri.gfaccess("out_gfs", "BU2"), rhs=BU[2]) ]) postloop = """ int imin[3] = { NGHOSTS_A2B, NGHOSTS_A2B, NGHOSTS_A2B }; int imax[3] = { NGHOSTS+Nxx0, NGHOSTS+Nxx1, NGHOSTS+Nxx2 }; // Now, we loop over the ghostzones to calculate the magnetic field there. for(int which_gz = 0; which_gz < NGHOSTS_A2B; which_gz++) { // After updating each face, adjust imin[] and imax[] // to reflect the newly-updated face extents. compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2]); imin[0]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2]); imax[0]++; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2]); imin[1]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2]); imax[1]++; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2]); imin[2]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1); imax[2]++; } """ loopopts = "InteriorPoints" add_to_Cfunction_dict(includes=includes, desc=desc, name=name, prefunc=prefunc, params=params, body=body, loopopts=loopopts, postloop=postloop) outC_function_dict[name] = outC_function_dict[name].replace( "= NGHOSTS", "= NGHOSTS_A2B").replace( "NGHOSTS+Nxx0", "Nxx_plus_2NGHOSTS0-NGHOSTS_A2B").replace( "NGHOSTS+Nxx1", "Nxx_plus_2NGHOSTS1-NGHOSTS_A2B").replace( "NGHOSTS+Nxx2", "Nxx_plus_2NGHOSTS2-NGHOSTS_A2B").replace( "../set_Cparameters.h", "set_Cparameters.h")
def ScalarWave_RHSs(): # Step 1: Get the spatial dimension, defined in the # NRPy+ "grid" module. DIM = par.parval_from_str("DIM") # Step 2: Register gridfunctions that are needed as input # to the scalar wave RHS expressions. uu, vv = gri.register_gridfunctions("EVOL", ["uu", "vv"]) # Step 3: Declare the rank-2 indexed expression \partial_{ij} u, # which is symmetric about interchange of indices i and j # Derivative variables like these must have an underscore # in them, so the finite difference module can parse the # variable name properly. uu_dDD = ixp.declarerank2("uu_dDD", "sym01") # Step 4: Specify RHSs as global variables, # to enable access outside this # function (e.g., for C code output) global uu_rhs, vv_rhs # Step 5: Define right-hand sides for the evolution. uu_rhs = vv vv_rhs = 0 for i in range(DIM): vv_rhs += wavespeed * wavespeed * uu_dDD[i][i] # # Step 5: Generate C code for scalarwave evolution equations, # # print output to the screen (standard out, or stdout). fin.FD_outputC("simple_c.py", [ lhrh(lhs=gri.gfaccess("out_gfs", "UU_rhs"), rhs=uu_rhs), lhrh(lhs=gri.gfaccess("out_gfs", "VV_rhs"), rhs=vv_rhs) ])
def Ricci__generate_Ccode(Ricci_exprs_list): Ricci_SymbExpressions = Ricci_exprs_list[0] print("Generating C code for Ricci tensor (FD_order="+str(FD_order)+") in "+par.parval_from_str("reference_metric::CoordSystem")+" coordinates.") start = time.time() # Store original finite-differencing order: FD_order_orig = par.parval_from_str("finite_difference::FD_CENTDERIVS_ORDER") # Set new finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order) Ricci_string = fin.FD_outputC("returnstring", Ricci_SymbExpressions, params="outCverbose=False,SIMD_enable=True,GoldenKernelsEnable=True") filename = "BSSN_Ricci_FD_order_"+str(FD_order)+".h" with open(os.path.join(outdir, filename), "w") as file: file.write(lp.loop(["i2","i1","i0"],["cctk_nghostzones[2]","cctk_nghostzones[1]","cctk_nghostzones[0]"], ["cctk_lsh[2]-cctk_nghostzones[2]","cctk_lsh[1]-cctk_nghostzones[1]","cctk_lsh[0]-cctk_nghostzones[0]"], ["1","1","SIMD_width"], ["#pragma omp parallel for", "#include \"rfm_files/rfm_struct__SIMD_outer_read2.h\"", r""" #include "rfm_files/rfm_struct__SIMD_outer_read1.h" #if (defined __INTEL_COMPILER && __INTEL_COMPILER_BUILD_DATE >= 20180804) #pragma ivdep // Forces Intel compiler (if Intel compiler used) to ignore certain SIMD vector dependencies #pragma vector always // Forces Intel compiler (if Intel compiler used) to vectorize #endif"""],"", "#include \"rfm_files/rfm_struct__SIMD_inner_read0.h\"\n"+Ricci_string)) # Restore original finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order_orig) end = time.time() print("(BENCH) Finished Ricci C codegen (FD_order="+str(FD_order)+") in " + str(end - start) + " seconds.")
def enforce_detgammabar_eq_detgammahat__generate_symbolic_expressions_and_C_code( ): ###################################### # START: GENERATE SYMBOLIC EXPRESSIONS # Enable rfm_precompute infrastructure, which results in # BSSN RHSs that are free of transcendental functions, # even in curvilinear coordinates, so long as # ConformalFactor is set to "W" (default). par.set_parval_from_str("reference_metric::enable_rfm_precompute", "True") par.set_parval_from_str( "reference_metric::rfm_precompute_Ccode_outdir", os.path.join(outdir, "rfm_files/")) import BSSN.Enforce_Detgammabar_Constraint as EGC enforce_detg_constraint_symb_expressions = EGC.Enforce_Detgammabar_Constraint_symb_expressions( ) # Now that we are finished with all the rfm hatted # quantities in generic precomputed functional # form, let's restore them to their closed- # form expressions. par.set_parval_from_str( "reference_metric::enable_rfm_precompute", "False") # Reset to False to disable rfm_precompute. rfm.ref_metric__hatted_quantities() # END: GENERATE SYMBOLIC EXPRESSIONS ###################################### start = time.time() print( "Generating optimized C code (FD_order=" + str(FD_order) + ") for gamma constraint. May take a while, depending on CoordSystem." ) enforce_gammadet_string = fin.FD_outputC( "returnstring", enforce_detg_constraint_symb_expressions, params="outCverbose=False,preindent=0,includebraces=False") with open(os.path.join(outdir, "enforcedetgammabar_constraint.h"), "w") as file: file.write( lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], ["cctk_lsh[2]", "cctk_lsh[1]", "cctk_lsh[0]"], ["1", "1", "1"], [ "#pragma omp parallel for", "#include \"rfm_files/rfm_struct__read2.h\"", "#include \"rfm_files/rfm_struct__read1.h\"" ], "", "#include \"rfm_files/rfm_struct__read0.h\"\n" + enforce_gammadet_string)) end = time.time() print("(BENCH) Finished gamma constraint C codegen (FD_order=" + str(FD_order) + ") in " + str(end - start) + " seconds.")
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 output_C__MomentumConstraint_h(outdir="BSSN/", add_T4UUmunu_source_terms=False, enable_verbose=True): # Step 0: Check if outdir is string; error out if not. check_if_string__error_if_not(outdir, "outdir") # Calling BSSN_constraints() (defined above) computes H and MU: BSSN_constraints(add_T4UUmunu_source_terms) if add_T4UUmunu_source_terms == True: print( "ERROR: MOMENTUM CONSTRAINT DOES NOT YET ADD T4UUmunu source terms." ) exit(1) import time start = time.time() if enable_verbose: print( "Generating optimized C code for Momentum constraint. May take a while, depending on CoordSystem." ) MomentumConstraintString = fin.FD_outputC("returnstring", [ lhrh(lhs=gri.gfaccess("aux_gfs", "MU0"), rhs=MU[0]), lhrh(lhs=gri.gfaccess("aux_gfs", "MU1"), rhs=MU[1]), lhrh(lhs=gri.gfaccess("aux_gfs", "MU2"), rhs=MU[2]) ], params="outCverbose=False") end = time.time() if enable_verbose: print("Finished in " + str(end - start) + " seconds.") import loop as lp with open(outdir + "MomentumConstraint.h", "w") as file: file.write( lp.loop(["i2", "i1", "i0"], ["NGHOSTS", "NGHOSTS", "NGHOSTS"], [ "NGHOSTS+Nxx[2]", "NGHOSTS+Nxx[1]", "NGHOSTS+Nxx[0]" ], ["1", "1", "1"], [ "const REAL invdx0 = 1.0/dxx[0];\n" + "const REAL invdx1 = 1.0/dxx[1];\n" + "const REAL invdx2 = 1.0/dxx[2];\n" + "#pragma omp parallel for", " const REAL xx2 = xx[2][i2];", " const REAL xx1 = xx[1][i1];" ], "", "const REAL xx0 = xx[0][i0];\n" + MomentumConstraintString)) print("Output C implementation of Momentum constraint to " + outdir + "MomentumConstraint.h")
def add_to_Cfunction_dict__prims_to_cons(gammaDD,betaU,alpha, ValenciavU,BU, sqrt4pi, includes=None): 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" params ="const paramstruct *params,REAL *auxevol_gfs,REAL *in_gfs" body = fin.FD_outputC("returnstring",values_to_print,params=outCparams) loopopts ="AllPoints" add_to_Cfunction_dict( includes=includes, desc=desc, name=name, params=params, body=body, loopopts=loopopts)
def BSSN_constraints__generate_symbolic_expressions_and_C_code(): ###################################### # START: GENERATE SYMBOLIC EXPRESSIONS # Define the Hamiltonian constraint and output the optimized C code. import BSSN.BSSN_constraints as bssncon bssncon.BSSN_constraints(add_T4UUmunu_source_terms=False) if T4UU != None: import BSSN.BSSN_stress_energy_source_terms as Bsest Bsest.BSSN_source_terms_for_BSSN_RHSs(T4UU) Bsest.BSSN_source_terms_for_BSSN_constraints(T4UU) bssncon.H += Bsest.sourceterm_H for i in range(3): bssncon.MU[i] += Bsest.sourceterm_MU[i] # END: GENERATE SYMBOLIC EXPRESSIONS ###################################### # Store original finite-differencing order: FD_order_orig = par.parval_from_str("finite_difference::FD_CENTDERIVS_ORDER") # Set new finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order) start = time.time() print("Generating optimized C code for Ham. & mom. constraints. May take a while, depending on CoordSystem.") Ham_mom_string = fin.FD_outputC("returnstring", [lhrh(lhs=gri.gfaccess("aux_gfs", "H"), rhs=bssncon.H), lhrh(lhs=gri.gfaccess("aux_gfs", "MU0"), rhs=bssncon.MU[0]), lhrh(lhs=gri.gfaccess("aux_gfs", "MU1"), rhs=bssncon.MU[1]), lhrh(lhs=gri.gfaccess("aux_gfs", "MU2"), rhs=bssncon.MU[2])], params="outCverbose=False") with open(os.path.join(outdir,"BSSN_constraints_enable_Tmunu_"+str(enable_stress_energy_source_terms)+"_FD_order_"+str(FD_order)+".h"), "w") as file: file.write(lp.loop(["i2","i1","i0"],["cctk_nghostzones[2]","cctk_nghostzones[1]","cctk_nghostzones[0]"], ["cctk_lsh[2]-cctk_nghostzones[2]","cctk_lsh[1]-cctk_nghostzones[1]","cctk_lsh[0]-cctk_nghostzones[0]"], ["1","1","1"],["#pragma omp parallel for","",""], "", Ham_mom_string)) # Restore original finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order_orig) end = time.time() print("(BENCH) Finished Hamiltonian & momentum constraint C codegen (FD_order="+str(FD_order)+",Tmunu="+str(enable_stress_energy_source_terms)+") in " + str(end - start) + " seconds.")
def add_to_Cfunction_dict__AD_gauge_term_psi6Phi_fin_diff(includes=None): xi_damping = par.Cparameters("REAL",thismodule,"xi_damping",0.1) GRFFE.compute_psi6Phi_rhs_damping_term(alpha,psi6Phi,xi_damping) AevolParen_dD = ixp.declarerank1("AevolParen_dD",DIM=3) PhievolParenU_dD = ixp.declarerank2("PhievolParenU_dD","nosym",DIM=3) A_rhsD = ixp.zerorank1() psi6Phi_rhs = GRFFE.psi6Phi_damping for i in range(3): A_rhsD[i] += -AevolParen_dD[i] psi6Phi_rhs += -PhievolParenU_dD[i][i] # Add Kreiss-Oliger dissipation to the GRFFE RHSs: # psi6Phi_dKOD = ixp.declarerank1("psi6Phi_dKOD") # AD_dKOD = ixp.declarerank2("AD_dKOD","nosym") # for i in range(3): # psi6Phi_rhs += diss_strength*psi6Phi_dKOD[i]*rfm.ReU[i] # ReU[i] = 1/scalefactor_orthog_funcform[i] # for j in range(3): # A_rhsD[j] += diss_strength*AD_dKOD[j][i]*rfm.ReU[i] # ReU[i] = 1/scalefactor_orthog_funcform[i] RHSs_to_print = [ lhrh(lhs=gri.gfaccess("rhs_gfs","AD0"),rhs=A_rhsD[0]), lhrh(lhs=gri.gfaccess("rhs_gfs","AD1"),rhs=A_rhsD[1]), lhrh(lhs=gri.gfaccess("rhs_gfs","AD2"),rhs=A_rhsD[2]), lhrh(lhs=gri.gfaccess("rhs_gfs","psi6Phi"),rhs=psi6Phi_rhs), ] desc = "Calculate AD gauge term and psi6Phi RHSs" name = "calculate_AD_gauge_psi6Phi_RHSs" params ="const paramstruct *params,const REAL *in_gfs,const REAL *auxevol_gfs,REAL *rhs_gfs" body = fin.FD_outputC("returnstring",RHSs_to_print,params=outCparams) loopopts ="InteriorPoints" add_to_Cfunction_dict( includes=includes, desc=desc, name=name, params=params, body=body, loopopts=loopopts) outC_function_dict[name] = outC_function_dict[name].replace("= NGHOSTS","= NGHOSTS_A2B").replace("NGHOSTS+Nxx0","Nxx_plus_2NGHOSTS0-NGHOSTS_A2B").replace("NGHOSTS+Nxx1","Nxx_plus_2NGHOSTS1-NGHOSTS_A2B").replace("NGHOSTS+Nxx2","Nxx_plus_2NGHOSTS2-NGHOSTS_A2B")
def add_to_Cfunction_dict__cons_to_prims(StildeD,BU,gammaDD,betaU,alpha, includes=None): 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]) ] desc = "Apply fixes to \tilde{S}_i and recompute the velocity to match with current sheet prescription." name = "GiRaFFE_NRPy_cons_to_prims" 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" add_to_Cfunction_dict( includes=includes, desc=desc, name=name, params=params, body=body, loopopts=loopopts)
def output_Enforce_Detgammabar_Constraint_Ccode(outdir="BSSN/"): # Step 0: Check if outdir is string; error out if not. check_if_string__error_if_not(outdir,"outdir") enforce_detg_constraint_symb_expressions = Enforce_Detgammabar_Constraint_symb_expressions() enforce_gammadet_string = fin.FD_outputC("returnstring", enforce_detg_constraint_symb_expressions, params="outCverbose=False,preindent=0,includebraces=False") with open(outdir+"enforce_detgammabar_constraint.h", "w") as file: indent = " " file.write( "void enforce_detgammabar_constraint(const int Nxx_plus_2NGHOSTS[3],REAL *xx[3], REAL *in_gfs) {\n\n") file.write(lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], ["Nxx_plus_2NGHOSTS[2]", "Nxx_plus_2NGHOSTS[1]", "Nxx_plus_2NGHOSTS[0]"], ["1", "1", "1"], ["#pragma omp parallel for", " const REAL xx2 = xx[2][i2];", " const REAL xx1 = xx[1][i1];"], "", "const REAL xx0 = xx[0][i0];\n" + enforce_gammadet_string)) file.write("}\n") print("Output C implementation of det(gammabar) constraint to file "+outdir+"enforce_detgammabar_constraint.h")
def output_Enforce_Detgammabar_Constraint_Ccode(outdir="BSSN/", exprs="", Read_xxs=False): # Step 0: Check if outdir is string; error out if not. check_if_string__error_if_not(outdir, "outdir") desc = "Enforce det(gammabar) = det(gammahat) constraint." name = "enforce_detgammabar_constraint" params = "const rfm_struct *restrict rfmstruct,const paramstruct *restrict params, REAL *restrict in_gfs" loopopts = "AllPoints,Enable_rfm_precompute" if Read_xxs: params = "const paramstruct *restrict params, REAL *restrict xx[3], REAL *restrict in_gfs" loopopts = "AllPoints,Read_xxs" outCfunction(outfile=os.path.join(outdir, name + ".h"), desc=desc, name=name, params=params, body=fin.FD_outputC( "returnstring", exprs, params="outCverbose=False,preindent=1,includebraces=False" ).replace("IDX4", "IDX4S"), loopopts=loopopts)
def add_to_Cfunction_dict__AD_gauge_term_psi6Phi_flux_term(includes=None): 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]), ] desc = "Calculate quantities to be finite-differenced for the GRFFE RHSs" name = "calculate_AD_gauge_term_psi6Phi_flux_term_for_RHSs" params = "const paramstruct *restrict params,const REAL *restrict in_gfs,REAL *restrict auxevol_gfs" body = fin.FD_outputC("returnstring",parens_to_print,params=outCparams) loopopts = "AllPoints" rel_path_to_Cparams=os.path.join("../") add_to_Cfunction_dict( includes=includes, desc=desc, name=name, params=params, body=body, loopopts=loopopts)
def output_C__Hamiltonian_h(add_T4UUmunu_source_terms=False, enable_verbose=True): # Calling BSSN_constraints() (defined above) computes H and MU: BSSN_constraints(add_T4UUmunu_source_terms) import time start = time.time() if enable_verbose: print( "Generating optimized C code for Hamiltonian constraint. May take a while, depending on CoordSystem." ) Hamiltonianstring = fin.FD_outputC("returnstring", lhrh(lhs=gri.gfaccess("aux_gfs", "H"), rhs=H), params="outCverbose=False") end = time.time() if enable_verbose: print("Finished in " + str(end - start) + " seconds.") import loop as lp with open("BSSN/Hamiltonian.h", "w") as file: file.write( lp.loop(["i2", "i1", "i0"], ["NGHOSTS", "NGHOSTS", "NGHOSTS"], ["NGHOSTS+Nxx[2]", "NGHOSTS+Nxx[1]", "NGHOSTS+Nxx[0]"], ["1", "1", "1"], [ "const REAL invdx0 = 1.0/dxx[0];\n" + "const REAL invdx1 = 1.0/dxx[1];\n" + "const REAL invdx2 = 1.0/dxx[2];\n" + "#pragma omp parallel for", " const REAL xx2 = xx[2][i2];", " const REAL xx1 = xx[1][i1];" ], "", "const REAL xx0 = xx[0][i0];\n" + Hamiltonianstring)) print( "Output C implementation of Hamiltonian constraint to BSSN/Hamiltonian.h" )
def Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear(CoordType_in, ADM_input_function_name, Ccodesdir = "BSSN", pointer_to_ID_inputs=False,loopopts=",oldloops"): # The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations. # To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in # the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3. # Step 0: Set spatial dimension (must be 3 for BSSN) DIM = 3 # Step 1: All ADM initial data quantities are now functions of xx0,xx1,xx2, but # they are still in the Spherical or Cartesian basis. We can now directly apply # Jacobian transformations to get them in the correct xx0,xx1,xx2 basis: # All input quantities are in terms of r,th,ph or x,y,z. We want them in terms # of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace # r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2 # as defined for this particular reference metric in reference_metric.py's # xxSph[] or xx_to_Cart[], respectively: # Define the input variables: gammaSphorCartDD = ixp.declarerank2("gammaSphorCartDD", "sym01") KSphorCartDD = ixp.declarerank2("KSphorCartDD", "sym01") alphaSphorCart = sp.symbols("alphaSphorCart") betaSphorCartU = ixp.declarerank1("betaSphorCartU") BSphorCartU = ixp.declarerank1("BSphorCartU") # Make sure that rfm.reference_metric() has been called. # We'll need the variables it defines throughout this module. if rfm.have_already_called_reference_metric_function == False: print("Error. Called Convert_Spherical_ADM_to_BSSN_curvilinear() without") print(" first setting up reference metric, by calling rfm.reference_metric().") sys.exit(1) r_th_ph_or_Cart_xyz_oID_xx = [] if CoordType_in == "Spherical": r_th_ph_or_Cart_xyz_oID_xx = rfm.xxSph elif CoordType_in == "Cartesian": r_th_ph_or_Cart_xyz_oID_xx = rfm.xx_to_Cart else: print("Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords.") sys.exit(1) # Step 2: All ADM initial data quantities are now functions of xx0,xx1,xx2, but # they are still in the Spherical or Cartesian basis. We can now directly apply # Jacobian transformations to get them in the correct xx0,xx1,xx2 basis: # alpha is a scalar, so no Jacobian transformation is necessary. alpha = alphaSphorCart Jac_dUSphorCart_dDrfmUD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff(r_th_ph_or_Cart_xyz_oID_xx[i], rfm.xx[j]) Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3(Jac_dUSphorCart_dDrfmUD) betaU = ixp.zerorank1() BU = ixp.zerorank1() gammaDD = ixp.zerorank2() KDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j] BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j] for k in range(DIM): for l in range(DIM): gammaDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * \ gammaSphorCartDD[k][l] KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l] # Step 3: All ADM quantities were input into this function in the Spherical or Cartesian # basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above, # we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2. # Here we convert ADM quantities in the "rfm" basis to their BSSN Curvilinear # counterparts, for all BSSN quantities *except* lambda^i: import BSSN.BSSN_in_terms_of_ADM as BitoA BitoA.gammabarDD_hDD(gammaDD) BitoA.trK_AbarDD_aDD(gammaDD, KDD) BitoA.cf_from_gammaDD(gammaDD) BitoA.betU_vetU(betaU, BU) hDD = BitoA.hDD trK = BitoA.trK aDD = BitoA.aDD cf = BitoA.cf vetU = BitoA.vetU betU = BitoA.betU # Step 4: Compute $\bar{\Lambda}^i$ (Eqs. 4 and 5 of # [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)), # from finite-difference derivatives of rescaled metric # quantities $h_{ij}$: # \bar{\Lambda}^i = \bar{\gamma}^{jk}\left(\bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk}\right). # The reference_metric.py module provides us with analytic expressions for # $\hat{\Gamma}^i_{jk}$, so here we need only compute # finite-difference expressions for $\bar{\Gamma}^i_{jk}$, based on # the values for $h_{ij}$ provided in the initial data. Once # $\bar{\Lambda}^i$ has been computed, we apply the usual rescaling # procedure: # \lambda^i = \bar{\Lambda}^i/\text{ReU[i]}, # and then output the result to a C file using the NRPy+ # finite-difference C output routine. # We will need all BSSN gridfunctions to be defined, as well as # expressions for gammabarDD_dD in terms of exact derivatives of # the rescaling matrix and finite-difference derivatives of # hDD's. This functionality is provided by BSSN.BSSN_unrescaled_and_barred_vars, # which we call here to overwrite above definitions of gammabarDD,gammabarUU, etc. Bq.gammabar__inverse_and_derivs() # Provides gammabarUU and GammabarUDD gammabarUU = Bq.gammabarUU GammabarUDD = Bq.GammabarUDD # Next evaluate \bar{\Lambda}^i, based on GammabarUDD above and GammahatUDD # (from the reference metric): LambdabarU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): LambdabarU[i] += gammabarUU[j][k] * (GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k]) # Finally apply rescaling: # lambda^i = Lambdabar^i/\text{ReU[i]} lambdaU = ixp.zerorank1() for i in range(DIM): lambdaU[i] = LambdabarU[i] / rfm.ReU[i] if ADM_input_function_name == "DoNotOutputADMInputFunction": return hDD,aDD,trK,vetU,betU,alpha,cf,lambdaU # Step 5.A: Output files containing finite-differenced lambdas. outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" lambdaU_expressions = [lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU0"), rhs=lambdaU[0]), lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU1"), rhs=lambdaU[1]), lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU2"), rhs=lambdaU[2])] desc = "Output lambdaU[i] for BSSN, built using finite-difference derivatives." name = "ID_BSSN_lambdas" params = "const paramstruct *restrict params,REAL *restrict xx[3],REAL *restrict in_gfs" preloop = "" enableCparameters=True if "oldloops" in loopopts: params = "const int Nxx[3],const int Nxx_plus_2NGHOSTS[3],REAL *xx[3],const REAL dxx[3],REAL *in_gfs" enableCparameters=False preloop = """ const REAL invdx0 = 1.0/dxx[0]; const REAL invdx1 = 1.0/dxx[1]; const REAL invdx2 = 1.0/dxx[2]; """ outCfunction( outfile=os.path.join(Ccodesdir, name + ".h"), desc=desc, name=name, params=params, preloop=preloop, body=fin.FD_outputC("returnstring", lambdaU_expressions, outCparams), loopopts="InteriorPoints,Read_xxs"+loopopts, enableCparameters=enableCparameters) # Step 5: Output all ADM-to-BSSN expressions to a C function. This function # must first call the ID_ADM_SphorCart() defined above. Using these # Spherical or Cartesian data, it sets up all quantities needed for # BSSNCurvilinear initial data, *except* $\lambda^i$, which must be # computed from numerical data using finite-difference derivatives. ID_inputs_param = "ID_inputs other_inputs," if pointer_to_ID_inputs == True: ID_inputs_param = "ID_inputs *other_inputs," desc = "Write BSSN variables in terms of ADM variables at a given point xx0,xx1,xx2" name = "ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs" enableCparameters=True params = "const paramstruct *restrict params, " if "oldloops" in loopopts: enableCparameters=False params = "" params += "const int i0i1i2[3], const REAL xx0xx1xx2[3]," + ID_inputs_param + """ REAL *hDD00,REAL *hDD01,REAL *hDD02,REAL *hDD11,REAL *hDD12,REAL *hDD22, REAL *aDD00,REAL *aDD01,REAL *aDD02,REAL *aDD11,REAL *aDD12,REAL *aDD22, REAL *trK, REAL *vetU0,REAL *vetU1,REAL *vetU2, REAL *betU0,REAL *betU1,REAL *betU2, REAL *alpha, REAL *cf""" outCparams = "preindent=1,outCverbose=False,includebraces=False" outCfunction( outfile=os.path.join(Ccodesdir, name + ".h"), desc=desc, name=name, params=params, body=""" REAL gammaSphorCartDD00,gammaSphorCartDD01,gammaSphorCartDD02, gammaSphorCartDD11,gammaSphorCartDD12,gammaSphorCartDD22; REAL KSphorCartDD00,KSphorCartDD01,KSphorCartDD02, KSphorCartDD11,KSphorCartDD12,KSphorCartDD22; REAL alphaSphorCart,betaSphorCartU0,betaSphorCartU1,betaSphorCartU2; REAL BSphorCartU0,BSphorCartU1,BSphorCartU2; const REAL xx0 = xx0xx1xx2[0]; const REAL xx1 = xx0xx1xx2[1]; const REAL xx2 = xx0xx1xx2[2]; REAL xyz_or_rthph[3];\n""" + outputC(r_th_ph_or_Cart_xyz_oID_xx[0:3], ["xyz_or_rthph[0]", "xyz_or_rthph[1]", "xyz_or_rthph[2]"], "returnstring", outCparams + ",CSE_enable=False") + " " + ADM_input_function_name + """(params,i0i1i2, xyz_or_rthph, other_inputs, &gammaSphorCartDD00,&gammaSphorCartDD01,&gammaSphorCartDD02, &gammaSphorCartDD11,&gammaSphorCartDD12,&gammaSphorCartDD22, &KSphorCartDD00,&KSphorCartDD01,&KSphorCartDD02, &KSphorCartDD11,&KSphorCartDD12,&KSphorCartDD22, &alphaSphorCart,&betaSphorCartU0,&betaSphorCartU1,&betaSphorCartU2, &BSphorCartU0,&BSphorCartU1,&BSphorCartU2); // Next compute all rescaled BSSN curvilinear quantities:\n""" + outputC([hDD[0][0], hDD[0][1], hDD[0][2], hDD[1][1], hDD[1][2], hDD[2][2], aDD[0][0], aDD[0][1], aDD[0][2], aDD[1][1], aDD[1][2], aDD[2][2], trK, vetU[0], vetU[1], vetU[2], betU[0], betU[1], betU[2], alpha, cf], ["*hDD00", "*hDD01", "*hDD02", "*hDD11", "*hDD12", "*hDD22", "*aDD00", "*aDD01", "*aDD02", "*aDD11", "*aDD12", "*aDD22", "*trK", "*vetU0", "*vetU1", "*vetU2", "*betU0", "*betU1", "*betU2", "*alpha", "*cf"], "returnstring", params=outCparams), enableCparameters=enableCparameters) # Step 5.a: Output the driver function for the above # function ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs() # Next write the driver function for ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(): desc = """Driver function for ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(), which writes BSSN variables in terms of ADM variables at a given point xx0,xx1,xx2""" name = "ID_BSSN__ALL_BUT_LAMBDAs" params = "const paramstruct *restrict params,REAL *restrict xx[3]," + ID_inputs_param + "REAL *in_gfs" enableCparameters = True funccallparams = "params, " idx3replace = "IDX3S" idx4ptreplace = "IDX4ptS" if "oldloops" in loopopts: params = "const int Nxx_plus_2NGHOSTS[3],REAL *xx[3]," + ID_inputs_param + "REAL *in_gfs" enableCparameters = False funccallparams = "" idx3replace = "IDX3" idx4ptreplace = "IDX4pt" outCfunction( outfile=os.path.join(Ccodesdir, name + ".h"), desc=desc, name=name, params=params, body=""" const int idx = IDX3(i0,i1,i2); const int i0i1i2[3] = {i0,i1,i2}; const REAL xx0xx1xx2[3] = {xx0,xx1,xx2}; ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(""".replace("IDX3",idx3replace)+funccallparams+"""i0i1i2,xx0xx1xx2,other_inputs, &in_gfs[IDX4pt(HDD00GF,idx)],&in_gfs[IDX4pt(HDD01GF,idx)],&in_gfs[IDX4pt(HDD02GF,idx)], &in_gfs[IDX4pt(HDD11GF,idx)],&in_gfs[IDX4pt(HDD12GF,idx)],&in_gfs[IDX4pt(HDD22GF,idx)], &in_gfs[IDX4pt(ADD00GF,idx)],&in_gfs[IDX4pt(ADD01GF,idx)],&in_gfs[IDX4pt(ADD02GF,idx)], &in_gfs[IDX4pt(ADD11GF,idx)],&in_gfs[IDX4pt(ADD12GF,idx)],&in_gfs[IDX4pt(ADD22GF,idx)], &in_gfs[IDX4pt(TRKGF,idx)], &in_gfs[IDX4pt(VETU0GF,idx)],&in_gfs[IDX4pt(VETU1GF,idx)],&in_gfs[IDX4pt(VETU2GF,idx)], &in_gfs[IDX4pt(BETU0GF,idx)],&in_gfs[IDX4pt(BETU1GF,idx)],&in_gfs[IDX4pt(BETU2GF,idx)], &in_gfs[IDX4pt(ALPHAGF,idx)],&in_gfs[IDX4pt(CFGF,idx)]); """.replace("IDX4pt",idx4ptreplace), loopopts="AllPoints,Read_xxs"+loopopts, enableCparameters=enableCparameters)
def add_Ricci_eval_to_Cfunction_dict( includes=None, rel_path_to_Cparams=os.path.join("."), enable_rfm_precompute=True, enable_golden_kernels=False, enable_SIMD=True, enable_split_for_optimizations_doesnt_help=False, OMP_pragma_on="i2", func_name_suffix=""): if includes is None: includes = [] if enable_SIMD: includes += [os.path.join("SIMD", "SIMD_intrinsics.h")] enable_FD_functions = bool( par.parval_from_str("finite_difference::enable_FD_functions")) if enable_FD_functions: includes += ["finite_difference_functions.h"] # Set up the C function for the 3-Ricci tensor desc = "Evaluate the 3-Ricci tensor" name = "Ricci_eval" + func_name_suffix params = "const paramstruct *restrict params, " if enable_rfm_precompute: params += "const rfm_struct *restrict rfmstruct, " else: params += "REAL *xx[3], " params += "const REAL *restrict in_gfs, REAL *restrict auxevol_gfs" # Construct body: Ricci_SymbExpressions = Ricci__generate_symbolic_expressions() FD_outCparams = "outCverbose=False,enable_SIMD=" + str(enable_SIMD) FD_outCparams += ",GoldenKernelsEnable=" + str(enable_golden_kernels) loopopts = get_loopopts("InteriorPoints", enable_SIMD, enable_rfm_precompute, OMP_pragma_on) FDorder = par.parval_from_str("finite_difference::FD_CENTDERIVS_ORDER") starttime = print_msg_with_timing("3-Ricci tensor (FD order=" + str(FDorder) + ")", msg="Ccodegen", startstop="start") # Construct body: preloop = "" enableCparameters = True # Set up preloop in case we're outputting code for the Einstein Toolkit (ETK) if par.parval_from_str("grid::GridFuncMemAccess") == "ETK": params, preloop = set_ETK_func_params_preloop(func_name_suffix) enableCparameters = False if enable_split_for_optimizations_doesnt_help and FDorder >= 8: loopopts += ",DisableOpenMP" Ricci_SymbExpressions_pt1 = [] Ricci_SymbExpressions_pt2 = [] for lhsrhs in Ricci_SymbExpressions: if "RBARDD00" in lhsrhs.lhs or "RBARDD11" in lhsrhs.lhs or "RBARDD22" in lhsrhs.lhs: Ricci_SymbExpressions_pt1.append( lhrh(lhs=lhsrhs.lhs, rhs=lhsrhs.rhs)) else: Ricci_SymbExpressions_pt2.append( lhrh(lhs=lhsrhs.lhs, rhs=lhsrhs.rhs)) preloop = """#pragma omp parallel { #pragma omp for """ preloopbody = fin.FD_outputC("returnstring", Ricci_SymbExpressions_pt1, params=FD_outCparams) preloop += lp.simple_loop(loopopts, preloopbody) preloop += "#pragma omp for\n" body = fin.FD_outputC("returnstring", Ricci_SymbExpressions_pt2, params=FD_outCparams) postloop = "\n } // END #pragma omp parallel\n" else: body = fin.FD_outputC("returnstring", Ricci_SymbExpressions, params=FD_outCparams) postloop = "" print_msg_with_timing("3-Ricci tensor (FD order=" + str(FDorder) + ")", msg="Ccodegen", startstop="stop", starttime=starttime) add_to_Cfunction_dict(includes=includes, desc=desc, name=name, params=params, preloop=preloop, body=body, loopopts=loopopts, postloop=postloop, rel_path_to_Cparams=rel_path_to_Cparams, enableCparameters=enableCparameters) return pickle_NRPy_env()
def add_rhs_eval_to_Cfunction_dict( includes=None, rel_path_to_Cparams=os.path.join("."), enable_rfm_precompute=True, enable_golden_kernels=False, enable_SIMD=True, enable_split_for_optimizations_doesnt_help=False, LapseCondition="OnePlusLog", ShiftCondition="GammaDriving2ndOrder_Covariant", enable_KreissOliger_dissipation=False, enable_stress_energy_source_terms=False, leave_Ricci_symbolic=True, OMP_pragma_on="i2", func_name_suffix=""): if includes is None: includes = [] if enable_SIMD: includes += [os.path.join("SIMD", "SIMD_intrinsics.h")] enable_FD_functions = bool( par.parval_from_str("finite_difference::enable_FD_functions")) if enable_FD_functions: includes += ["finite_difference_functions.h"] # Set up the C function for the BSSN RHSs desc = "Evaluate the BSSN RHSs" name = "rhs_eval" + func_name_suffix params = "const paramstruct *restrict params, " if enable_rfm_precompute: params += "const rfm_struct *restrict rfmstruct, " else: params += "REAL *xx[3], " params += """ const REAL *restrict auxevol_gfs,const REAL *restrict in_gfs,REAL *restrict rhs_gfs""" betaU, BSSN_RHSs_SymbExpressions = \ BSSN_RHSs__generate_symbolic_expressions(LapseCondition=LapseCondition, ShiftCondition=ShiftCondition, enable_KreissOliger_dissipation=enable_KreissOliger_dissipation, enable_stress_energy_source_terms=enable_stress_energy_source_terms, leave_Ricci_symbolic=leave_Ricci_symbolic) # Construct body: preloop = "" enableCparameters = True # Set up preloop in case we're outputting code for the Einstein Toolkit (ETK) if par.parval_from_str("grid::GridFuncMemAccess") == "ETK": params, preloop = set_ETK_func_params_preloop(func_name_suffix) enableCparameters = False FD_outCparams = "outCverbose=False,enable_SIMD=" + str(enable_SIMD) FD_outCparams += ",GoldenKernelsEnable=" + str(enable_golden_kernels) loopopts = get_loopopts("InteriorPoints", enable_SIMD, enable_rfm_precompute, OMP_pragma_on) FDorder = par.parval_from_str("finite_difference::FD_CENTDERIVS_ORDER") starttime = print_msg_with_timing("BSSN_RHSs (FD order=" + str(FDorder) + ")", msg="Ccodegen", startstop="start") if enable_split_for_optimizations_doesnt_help and FDorder == 6: loopopts += ",DisableOpenMP" BSSN_RHSs_SymbExpressions_pt1 = [] BSSN_RHSs_SymbExpressions_pt2 = [] for lhsrhs in BSSN_RHSs_SymbExpressions: if "BETU" in lhsrhs.lhs or "LAMBDAU" in lhsrhs.lhs: BSSN_RHSs_SymbExpressions_pt1.append( lhrh(lhs=lhsrhs.lhs, rhs=lhsrhs.rhs)) else: BSSN_RHSs_SymbExpressions_pt2.append( lhrh(lhs=lhsrhs.lhs, rhs=lhsrhs.rhs)) preloop += """#pragma omp parallel { """ preloopbody = fin.FD_outputC("returnstring", BSSN_RHSs_SymbExpressions_pt1, params=FD_outCparams, upwindcontrolvec=betaU) preloop += "\n#pragma omp for\n" + lp.simple_loop( loopopts, preloopbody) preloop += "\n#pragma omp for\n" body = fin.FD_outputC("returnstring", BSSN_RHSs_SymbExpressions_pt2, params=FD_outCparams, upwindcontrolvec=betaU) postloop = "\n } // END #pragma omp parallel\n" else: preloop += "" body = fin.FD_outputC("returnstring", BSSN_RHSs_SymbExpressions, params=FD_outCparams, upwindcontrolvec=betaU) postloop = "" print_msg_with_timing("BSSN_RHSs (FD order=" + str(FDorder) + ")", msg="Ccodegen", startstop="stop", starttime=starttime) add_to_Cfunction_dict(includes=includes, desc=desc, name=name, params=params, preloop=preloop, body=body, loopopts=loopopts, postloop=postloop, rel_path_to_Cparams=rel_path_to_Cparams, enableCparameters=enableCparameters) return pickle_NRPy_env()
def add_BSSN_constraints_to_Cfunction_dict( includes=None, rel_path_to_Cparams=os.path.join("."), enable_rfm_precompute=True, enable_golden_kernels=False, enable_SIMD=True, enable_stress_energy_source_terms=False, leave_Ricci_symbolic=True, output_H_only=False, OMP_pragma_on="i2", func_name_suffix=""): if includes is None: includes = [] if enable_SIMD: includes += [os.path.join("SIMD", "SIMD_intrinsics.h")] enable_FD_functions = bool( par.parval_from_str("finite_difference::enable_FD_functions")) if enable_FD_functions: includes += ["finite_difference_functions.h"] # Set up the C function for the BSSN constraints desc = "Evaluate the BSSN constraints" name = "BSSN_constraints" + func_name_suffix params = "const paramstruct *restrict params, " if enable_rfm_precompute: params += "const rfm_struct *restrict rfmstruct, " else: params += "REAL *xx[3], " params += """ const REAL *restrict in_gfs, const REAL *restrict auxevol_gfs, REAL *restrict aux_gfs""" # Construct body: BSSN_constraints_SymbExpressions = BSSN_constraints__generate_symbolic_expressions( enable_stress_energy_source_terms, leave_Ricci_symbolic=leave_Ricci_symbolic, output_H_only=output_H_only) preloop = "" enableCparameters = True # Set up preloop in case we're outputting code for the Einstein Toolkit (ETK) if par.parval_from_str("grid::GridFuncMemAccess") == "ETK": params, preloop = set_ETK_func_params_preloop(func_name_suffix) enableCparameters = False FD_outCparams = "outCverbose=False,enable_SIMD=" + str(enable_SIMD) FD_outCparams += ",GoldenKernelsEnable=" + str(enable_golden_kernels) FDorder = par.parval_from_str("finite_difference::FD_CENTDERIVS_ORDER") starttime = print_msg_with_timing("BSSN constraints (FD order=" + str(FDorder) + ")", msg="Ccodegen", startstop="start") body = fin.FD_outputC("returnstring", BSSN_constraints_SymbExpressions, params=FD_outCparams) print_msg_with_timing("BSSN constraints (FD order=" + str(FDorder) + ")", msg="Ccodegen", startstop="stop", starttime=starttime) add_to_Cfunction_dict(includes=includes, desc=desc, name=name, params=params, preloop=preloop, body=body, loopopts=get_loopopts("InteriorPoints", enable_SIMD, enable_rfm_precompute, OMP_pragma_on), rel_path_to_Cparams=rel_path_to_Cparams, enableCparameters=enableCparameters) return pickle_NRPy_env()
def output_Enforce_Detgammabar_Constraint_Ccode(): # Set spatial dimension (must be 3 for BSSN) DIM = 3 # Then we set the coordinate system for the numerical grid rfm.reference_metric( ) # Create ReU, ReDD needed for rescaling B-L initial data, generating BSSN RHSs, etc. # We will need the h_{ij} quantities defined within BSSN_RHSs # below when we enforce the gammahat=gammabar constraint # Step 1: All barred quantities are defined in terms of BSSN rescaled gridfunctions, # which we declare here in case they haven't yet been declared elsewhere. Bq.declare_BSSN_gridfunctions_if_not_declared_already() hDD = Bq.hDD Bq.BSSN_basic_tensors() gammabarDD = Bq.gammabarDD # First define the Kronecker delta: KroneckerDeltaDD = ixp.zerorank2() for i in range(DIM): KroneckerDeltaDD[i][i] = sp.sympify(1) # The detgammabar in BSSN_RHSs is set to detgammahat when BSSN_RHSs::detgbarOverdetghat_equals_one=True (default), # so we manually compute it here: dummygammabarUU, detgammabar = ixp.symm_matrix_inverter3x3(gammabarDD) # Next apply the constraint enforcement equation above. hprimeDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): hprimeDD[i][j] = \ (sp.Abs(rfm.detgammahat) / detgammabar) ** (sp.Rational(1, 3)) * (KroneckerDeltaDD[i][j] + hDD[i][j]) \ - KroneckerDeltaDD[i][j] enforce_detg_constraint_vars = [ \ lhrh(lhs=gri.gfaccess("in_gfs", "hDD00"), rhs=hprimeDD[0][0]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD01"), rhs=hprimeDD[0][1]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD02"), rhs=hprimeDD[0][2]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD11"), rhs=hprimeDD[1][1]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD12"), rhs=hprimeDD[1][2]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD22"), rhs=hprimeDD[2][2])] enforce_gammadet_string = fin.FD_outputC( "returnstring", enforce_detg_constraint_vars, params="outCverbose=False,preindent=0,includebraces=False") with open("BSSN/enforce_detgammabar_constraint.h", "w") as file: indent = " " file.write( "void enforce_detgammabar_constraint(const int Nxx_plus_2NGHOSTS[3],REAL *xx[3], REAL *in_gfs) {\n\n" ) file.write( lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], [ "Nxx_plus_2NGHOSTS[2]", "Nxx_plus_2NGHOSTS[1]", "Nxx_plus_2NGHOSTS[0]" ], ["1", "1", "1"], [ "#pragma omp parallel for", " const REAL xx2 = xx[2][i2];", " const REAL xx1 = xx[1][i1];" ], "", "const REAL xx0 = xx[0][i0];\n" + enforce_gammadet_string)) file.write("}\n") print( "Output C implementation of det(gammabar) constraint to file BSSN/enforce_detgammabar_constraint.h" )
def add_enforce_detgammahat_constraint_to_Cfunction_dict( includes=None, rel_path_to_Cparams=os.path.join("."), enable_rfm_precompute=True, enable_golden_kernels=False, OMP_pragma_on="i2", func_name_suffix=""): # This function disables SIMD, as it includes cbrt() and abs() functions. if includes is None: includes = [] # This function does not use finite differences! # enable_FD_functions = bool(par.parval_from_str("finite_difference::enable_FD_functions")) # if enable_FD_functions: # includes += ["finite_difference_functions.h"] # Set up the C function for enforcing the det(gammabar) = det(gammahat) BSSN algebraic constraint desc = "Enforce the det(gammabar) = det(gammahat) (algebraic) constraint" name = "enforce_detgammahat_constraint" + func_name_suffix params = "const paramstruct *restrict params, " if enable_rfm_precompute: params += "const rfm_struct *restrict rfmstruct, " else: params += "REAL *xx[3], " params += "REAL *restrict in_gfs" # Construct body: enforce_detg_constraint_symb_expressions = EGC.Enforce_Detgammahat_Constraint_symb_expressions( ) preloop = "" enableCparameters = True # Set up preloop in case we're outputting code for the Einstein Toolkit (ETK) if par.parval_from_str("grid::GridFuncMemAccess") == "ETK": params, preloop = set_ETK_func_params_preloop(func_name_suffix, enable_SIMD=False) enableCparameters = False FD_outCparams = "outCverbose=False,enable_SIMD=False" FD_outCparams += ",GoldenKernelsEnable=" + str(enable_golden_kernels) starttime = print_msg_with_timing( "Enforcing det(gammabar)=det(gammahat) constraint", msg="Ccodegen", startstop="start") body = fin.FD_outputC("returnstring", enforce_detg_constraint_symb_expressions, params=FD_outCparams) print_msg_with_timing("Enforcing det(gammabar)=det(gammahat) constraint", msg="Ccodegen", startstop="stop", starttime=starttime) enable_SIMD = False add_to_Cfunction_dict(includes=includes, desc=desc, name=name, params=params, preloop=preloop, body=body, loopopts=get_loopopts("AllPoints", enable_SIMD, enable_rfm_precompute, OMP_pragma_on), rel_path_to_Cparams=rel_path_to_Cparams, enableCparameters=enableCparameters) return pickle_NRPy_env()
def add_psi4_part_to_Cfunction_dict(includes=None, rel_path_to_Cparams=os.path.join("."), whichpart=0, setPsi4tozero=False, OMP_pragma_on="i2"): starttime = print_msg_with_timing("psi4, part " + str(whichpart), msg="Ccodegen", startstop="start") # Set up the C function for psi4 if includes is None: includes = [] includes += ["NRPy_function_prototypes.h"] desc = "Compute psi4 at all interior gridpoints, part " + str(whichpart) name = "psi4_part" + str(whichpart) params = """const paramstruct *restrict params, const REAL *restrict in_gfs, REAL *restrict xx[3], REAL *restrict aux_gfs""" body = "" gri.register_gridfunctions("AUX", [ "psi4_part" + str(whichpart) + "re", "psi4_part" + str(whichpart) + "im" ]) FD_outCparams = "outCverbose=False,enable_SIMD=False,CSE_sorting=none" if not setPsi4tozero: # Set the body of the function # First compute the symbolic expressions psi4.Psi4(specify_tetrad=False) # We really don't want to store these "Cparameters" permanently; they'll be set via function call... # so we make a copy of the original par.glb_Cparams_list (sans tetrad vectors) and restore it below Cparams_list_orig = par.glb_Cparams_list.copy() par.Cparameters("REAL", __name__, ["mre4U0", "mre4U1", "mre4U2", "mre4U3"], [0, 0, 0, 0]) par.Cparameters("REAL", __name__, ["mim4U0", "mim4U1", "mim4U2", "mim4U3"], [0, 0, 0, 0]) par.Cparameters("REAL", __name__, ["n4U0", "n4U1", "n4U2", "n4U3"], [0, 0, 0, 0]) body += """ REAL mre4U0,mre4U1,mre4U2,mre4U3,mim4U0,mim4U1,mim4U2,mim4U3,n4U0,n4U1,n4U2,n4U3; psi4_tetrad(params, in_gfs[IDX4S(CFGF, i0,i1,i2)], in_gfs[IDX4S(HDD00GF, i0,i1,i2)], in_gfs[IDX4S(HDD01GF, i0,i1,i2)], in_gfs[IDX4S(HDD02GF, i0,i1,i2)], in_gfs[IDX4S(HDD11GF, i0,i1,i2)], in_gfs[IDX4S(HDD12GF, i0,i1,i2)], in_gfs[IDX4S(HDD22GF, i0,i1,i2)], &mre4U0,&mre4U1,&mre4U2,&mre4U3,&mim4U0,&mim4U1,&mim4U2,&mim4U3,&n4U0,&n4U1,&n4U2,&n4U3, xx, i0,i1,i2); """ body += "REAL xCart_rel_to_globalgrid_center[3];\n" body += "xx_to_Cart(params, xx, i0, i1, i2, xCart_rel_to_globalgrid_center);\n" body += "int ignore_Cart_to_i0i1i2[3]; REAL xx_rel_to_globalgridorigin[3];\n" body += "Cart_to_xx_and_nearest_i0i1i2_global_grid_center(params, xCart_rel_to_globalgrid_center,xx_rel_to_globalgridorigin,ignore_Cart_to_i0i1i2);\n" for i in range(3): body += "const REAL xx" + str( i) + "=xx_rel_to_globalgridorigin[" + str(i) + "];\n" body += fin.FD_outputC("returnstring", [ lhrh(lhs=gri.gfaccess("in_gfs", "psi4_part" + str(whichpart) + "re"), rhs=psi4.psi4_re_pt[whichpart]), lhrh(lhs=gri.gfaccess("in_gfs", "psi4_part" + str(whichpart) + "im"), rhs=psi4.psi4_im_pt[whichpart]) ], params=FD_outCparams) par.glb_Cparams_list = Cparams_list_orig.copy() elif setPsi4tozero: body += fin.FD_outputC("returnstring", [ lhrh(lhs=gri.gfaccess("in_gfs", "psi4_part" + str(whichpart) + "re"), rhs=sp.sympify(0)), lhrh(lhs=gri.gfaccess("in_gfs", "psi4_part" + str(whichpart) + "im"), rhs=sp.sympify(0)) ], params=FD_outCparams) enable_SIMD = False enable_rfm_precompute = False print_msg_with_timing("psi4, part " + str(whichpart), msg="Ccodegen", startstop="stop", starttime=starttime) add_to_Cfunction_dict(includes=includes, desc=desc, name=name, params=params, body=body, loopopts=get_loopopts("InteriorPoints", enable_SIMD, enable_rfm_precompute, OMP_pragma_on, enable_xxs=False), rel_path_to_Cparams=rel_path_to_Cparams) return pickle_NRPy_env()
def driver_C_codes(Csrcdict, ThornName, rhs_list, evol_gfs_list, aux_gfs_list, auxevol_gfs_list, LapseCondition="OnePlusLog", enable_stress_energy_source_terms=False): # First the ETK banner code, proudly showing the NRPy+ banner import NRPy_logo as logo outstr = """ #include <stdio.h> void BaikalETK_Banner() { """ logostr = logo.print_logo(print_to_stdout=False) outstr += "printf(\"BaikalETK: another Einstein Toolkit thorn generated by\\n\");\n" for line in logostr.splitlines(): outstr += " printf(\"" + line + "\\n\");\n" outstr += "}\n" # Finally add C code string to dictionaries (Python dictionaries are immutable) # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("Banner.c")] = outstr.replace( "BaikalETK", ThornName) # Then the RegisterSlicing() function, needed for other ETK thorns outstr = """ #include "cctk.h" #include "Slicing.h" int BaikalETK_RegisterSlicing (void) { Einstein_RegisterSlicing ("BaikalETK"); return 0; }""" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "RegisterSlicing.c")] = outstr.replace("BaikalETK", ThornName) # Next BaikalETK_Symmetry_registration(): Register symmetries full_gfs_list = [] full_gfs_list.extend(evol_gfs_list) full_gfs_list.extend(auxevol_gfs_list) full_gfs_list.extend(aux_gfs_list) outstr = """ #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" #include "Symmetry.h" void BaikalETK_Symmetry_registration(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; // Stores gridfunction parity across x=0, y=0, and z=0 planes, respectively int sym[3]; // Next register parities for each gridfunction based on its name // (to ensure this algorithm is robust, gridfunctions with integers // in their base names are forbidden in NRPy+). """ outstr += "" for gf in full_gfs_list: # Do not add T4UU gridfunctions if enable_stress_energy_source_terms==False: if not (enable_stress_energy_source_terms == False and "T4UU" in gf): outstr += """ // Default to scalar symmetry: sym[0] = 1; sym[1] = 1; sym[2] = 1; // Now modify sym[0], sym[1], and/or sym[2] as needed // to account for gridfunction parity across // x=0, y=0, and/or z=0 planes, respectively """ # If gridfunction name does not end in a digit, by NRPy+ syntax, it must be a scalar if gf[len(gf) - 1].isdigit() == False: pass # Scalar = default elif len(gf) > 2: # Rank-1 indexed expression (e.g., vector) if gf[len(gf) - 2].isdigit() == False: if int(gf[-1]) > 2: print("Error: Found invalid gridfunction name: " + gf) sys.exit(1) symidx = gf[-1] outstr += " sym[" + symidx + "] = -1;\n" # Rank-2 indexed expression elif gf[len(gf) - 2].isdigit() == True: if len(gf) > 3 and gf[len(gf) - 3].isdigit() == True: print("Error: Found a Rank-3 or above gridfunction: " + gf + ", which is at the moment unsupported.") print("It should be easy to support this if desired.") sys.exit(1) symidx0 = gf[-2] outstr += " sym[" + symidx0 + "] *= -1;\n" symidx1 = gf[-1] outstr += " sym[" + symidx1 + "] *= -1;\n" else: print( "Don't know how you got this far with a gridfunction named " + gf + ", but I'll take no more of this nonsense.") print( " Please follow best-practices and rename your gridfunction to be more descriptive" ) sys.exit(1) outstr += " SetCartSymVN(cctkGH, sym, \"BaikalETK::" + gf + "\");\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("Symmetry_registration_oldCartGrid3D.c")] = \ outstr.replace("BaikalETK",ThornName) # Next set RHSs to zero outstr = """ #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" #include "Symmetry.h" void BaikalETK_zero_rhss(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; """ set_rhss_to_zero = "" for gf in rhs_list: set_rhss_to_zero += gf + "[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)] = 0.0;\n" outstr += lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], ["cctk_lsh[2]", "cctk_lsh[1]", "cctk_lsh[0]"], ["1", "1", "1"], [ "#pragma omp parallel for", "", "", ], "", set_rhss_to_zero) outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("zero_rhss.c")] = outstr.replace( "BaikalETK", ThornName) # Next registration with the Method of Lines thorn outstr = """ //-------------------------------------------------------------------------- // Register with the Method of Lines time stepper // (MoL thorn, found in arrangements/CactusBase/MoL) // MoL documentation located in arrangements/CactusBase/MoL/doc //-------------------------------------------------------------------------- #include <stdio.h> #include "cctk.h" #include "cctk_Parameters.h" #include "cctk_Arguments.h" #include "Symmetry.h" void BaikalETK_MoL_registration(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; CCTK_INT ierr = 0, group, rhs; // Register evolution & RHS gridfunction groups with MoL, so it knows group = CCTK_GroupIndex("BaikalETK::evol_variables"); rhs = CCTK_GroupIndex("BaikalETK::evol_variables_rhs"); ierr += MoLRegisterEvolvedGroup(group, rhs); if (ierr) CCTK_ERROR("Problems registering with MoL"); } """ # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "MoL_registration.c")] = outstr.replace("BaikalETK", ThornName) # Next register with the boundary conditions thorns. # PART 1: Set BC type to "none" for all variables # Since we choose NewRad boundary conditions, we must register all # gridfunctions to have boundary type "none". This is because # NewRad is seen by the rest of the Toolkit as a modification to the # RHSs. # This code is based on Kranc's McLachlan/ML_BSSN/src/Boundaries.cc code. outstr = """ #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" #include "cctk_Faces.h" #include "util_Table.h" #include "Symmetry.h" // Set `none` boundary conditions on BSSN RHSs, as these are set via NewRad. void BaikalETK_BoundaryConditions_evolved_gfs(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; CCTK_INT ierr CCTK_ATTRIBUTE_UNUSED = 0; """ for gf in evol_gfs_list: outstr += """ ierr = Boundary_SelectVarForBC(cctkGH, CCTK_ALL_FACES, 1, -1, "BaikalETK::""" + gf + """", "none"); if (ierr < 0) CCTK_ERROR("Failed to register BC for BaikalETK::""" + gf + """!"); """ outstr += """ } // Set `flat` boundary conditions on BSSN constraints, similar to what Lean does. void BaikalETK_BoundaryConditions_aux_gfs(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; CCTK_INT ierr CCTK_ATTRIBUTE_UNUSED = 0; """ for gf in aux_gfs_list: outstr += """ ierr = Boundary_SelectVarForBC(cctkGH, CCTK_ALL_FACES, cctk_nghostzones[0], -1, "BaikalETK::""" + gf + """", "flat"); if (ierr < 0) CCTK_ERROR("Failed to register BC for BaikalETK::""" + gf + """!"); """ outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "BoundaryConditions.c")] = outstr.replace("BaikalETK", ThornName) # PART 2: Set C code for calling NewRad BCs # As explained in lean_public/LeanBSSNMoL/src/calc_bssn_rhs.F90, # the function NewRad_Apply takes the following arguments: # NewRad_Apply(cctkGH, var, rhs, var0, v0, radpower), # which implement the boundary condition: # var = var_at_infinite_r + u(r-var_char_speed*t)/r^var_radpower # Obviously for var_radpower>0, var_at_infinite_r is the value of # the variable at r->infinity. var_char_speed is the propagation # speed at the outer boundary, and var_radpower is the radial # falloff rate. outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_NewRad(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; """ for gf in evol_gfs_list: var_at_infinite_r = "0.0" var_char_speed = "1.0" var_radpower = "1.0" if gf == "alpha": var_at_infinite_r = "1.0" if LapseCondition == "OnePlusLog": var_char_speed = "sqrt(2.0)" else: pass # 1.0 (default) is fine if "aDD" in gf or "trK" in gf: # consistent with Lean code. var_radpower = "2.0" outstr += " NewRad_Apply(cctkGH, " + gf + ", " + gf.replace( "GF", "" ) + "_rhsGF, " + var_at_infinite_r + ", " + var_char_speed + ", " + var_radpower + ");\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "BoundaryCondition_NewRad.c")] = outstr.replace( "BaikalETK", ThornName) # First we convert from ADM to BSSN, as is required to convert initial data # (given using) ADM quantities, to the BSSN evolved variables import BSSN.ADM_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear as atob IDhDD,IDaDD,IDtrK,IDvetU,IDbetU,IDalpha,IDcf,IDlambdaU = \ atob.Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear("Cartesian","DoNotOutputADMInputFunction",os.path.join(ThornName,"src")) # Store the original list of registered gridfunctions; we'll want to unregister # all the *SphorCart* gridfunctions after we're finished with them below. orig_glb_gridfcs_list = [] for gf in gri.glb_gridfcs_list: orig_glb_gridfcs_list.append(gf) alphaSphorCart = gri.register_gridfunctions("AUXEVOL", "alphaSphorCart") betaSphorCartU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "betaSphorCartU") BSphorCartU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "BSphorCartU") gammaSphorCartDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "gammaSphorCartDD", "sym01") KSphorCartDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "KSphorCartDD", "sym01") # ADM to BSSN conversion, used for converting ADM initial data into a form readable by this thorn. # ADM to BSSN, Part 1: Set up function call and pointers to ADM gridfunctions outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_ADM_to_BSSN(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; CCTK_REAL *alphaSphorCartGF = alp; """ # It's ugly if we output code in the following ordering, so we'll first # output to a string and then sort the string to beautify the code a bit. outstrtmp = [] for i in range(3): outstrtmp.append(" CCTK_REAL *betaSphorCartU" + str(i) + "GF = beta" + chr(ord('x') + i) + ";\n") outstrtmp.append(" CCTK_REAL *BSphorCartU" + str(i) + "GF = dtbeta" + chr(ord('x') + i) + ";\n") for j in range(i, 3): outstrtmp.append(" CCTK_REAL *gammaSphorCartDD" + str(i) + str(j) + "GF = g" + chr(ord('x') + i) + chr(ord('x') + j) + ";\n") outstrtmp.append(" CCTK_REAL *KSphorCartDD" + str(i) + str(j) + "GF = k" + chr(ord('x') + i) + chr(ord('x') + j) + ";\n") outstrtmp.sort() for line in outstrtmp: outstr += line # ADM to BSSN, Part 2: Set up ADM to BSSN conversions for BSSN gridfunctions that do not require # finite-difference derivatives (i.e., all gridfunctions except lambda^i (=Gamma^i # in non-covariant BSSN)): # h_{ij}, a_{ij}, trK, vet^i=beta^i,bet^i=B^i, cf (conformal factor), and alpha all_but_lambdaU_expressions = [ lhrh(lhs=gri.gfaccess("in_gfs", "hDD00"), rhs=IDhDD[0][0]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD01"), rhs=IDhDD[0][1]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD02"), rhs=IDhDD[0][2]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD11"), rhs=IDhDD[1][1]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD12"), rhs=IDhDD[1][2]), lhrh(lhs=gri.gfaccess("in_gfs", "hDD22"), rhs=IDhDD[2][2]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD00"), rhs=IDaDD[0][0]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD01"), rhs=IDaDD[0][1]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD02"), rhs=IDaDD[0][2]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD11"), rhs=IDaDD[1][1]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD12"), rhs=IDaDD[1][2]), lhrh(lhs=gri.gfaccess("in_gfs", "aDD22"), rhs=IDaDD[2][2]), lhrh(lhs=gri.gfaccess("in_gfs", "trK"), rhs=IDtrK), lhrh(lhs=gri.gfaccess("in_gfs", "vetU0"), rhs=IDvetU[0]), lhrh(lhs=gri.gfaccess("in_gfs", "vetU1"), rhs=IDvetU[1]), lhrh(lhs=gri.gfaccess("in_gfs", "vetU2"), rhs=IDvetU[2]), lhrh(lhs=gri.gfaccess("in_gfs", "betU0"), rhs=IDbetU[0]), lhrh(lhs=gri.gfaccess("in_gfs", "betU1"), rhs=IDbetU[1]), lhrh(lhs=gri.gfaccess("in_gfs", "betU2"), rhs=IDbetU[2]), lhrh(lhs=gri.gfaccess("in_gfs", "alpha"), rhs=IDalpha), lhrh(lhs=gri.gfaccess("in_gfs", "cf"), rhs=IDcf) ] outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" all_but_lambdaU_outC = fin.FD_outputC("returnstring", all_but_lambdaU_expressions, outCparams) outstr += lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], ["cctk_lsh[2]", "cctk_lsh[1]", "cctk_lsh[0]"], ["1", "1", "1"], ["#pragma omp parallel for", "", ""], " ", all_but_lambdaU_outC) # ADM to BSSN, Part 3: Set up ADM to BSSN conversions for BSSN gridfunctions defined from # finite-difference derivatives: lambda^i, which is Gamma^i in non-covariant BSSN): outstr += """ const CCTK_REAL invdx0 = 1.0/CCTK_DELTA_SPACE(0); const CCTK_REAL invdx1 = 1.0/CCTK_DELTA_SPACE(1); const CCTK_REAL invdx2 = 1.0/CCTK_DELTA_SPACE(2); """ path = os.path.join(ThornName, "src") BSSN_RHS_FD_orders_output = [] for root, dirs, files in os.walk(path): for file in files: if "BSSN_RHSs_FD_order" in file: array = file.replace(".", "_").split("_") BSSN_RHS_FD_orders_output.append(int(array[4])) for current_FD_order in BSSN_RHS_FD_orders_output: # Store original finite-differencing order: orig_FD_order = par.parval_from_str( "finite_difference::FD_CENTDERIVS_ORDER") # Set new finite-differencing order: par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", current_FD_order) outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" lambdaU_expressions = [ lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU0"), rhs=IDlambdaU[0]), lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU1"), rhs=IDlambdaU[1]), lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU2"), rhs=IDlambdaU[2]) ] lambdaU_expressions_FDout = fin.FD_outputC("returnstring", lambdaU_expressions, outCparams) lambdafile = "ADM_to_BSSN__compute_lambdaU_FD_order_" + str( current_FD_order) + ".h" with open(os.path.join(ThornName, "src", lambdafile), "w") as file: file.write( lp.loop(["i2", "i1", "i0"], [ "cctk_nghostzones[2]", "cctk_nghostzones[1]", "cctk_nghostzones[0]" ], [ "cctk_lsh[2]-cctk_nghostzones[2]", "cctk_lsh[1]-cctk_nghostzones[1]", "cctk_lsh[0]-cctk_nghostzones[0]" ], ["1", "1", "1"], ["#pragma omp parallel for", "", ""], "", lambdaU_expressions_FDout)) outstr += " if(FD_order == " + str(current_FD_order) + ") {\n" outstr += " #include \"" + lambdafile + "\"\n" outstr += " }\n" # Restore original FD order par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", orig_FD_order) outstr += """ ExtrapolateGammas(cctkGH,lambdaU0GF); ExtrapolateGammas(cctkGH,lambdaU1GF); ExtrapolateGammas(cctkGH,lambdaU2GF); } """ # Unregister the *SphorCartGF's. gri.glb_gridfcs_list = orig_glb_gridfcs_list # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("ADM_to_BSSN.c")] = outstr.replace( "BaikalETK", ThornName) import BSSN.ADM_in_terms_of_BSSN as btoa import BSSN.BSSN_quantities as Bq btoa.ADM_in_terms_of_BSSN() Bq.BSSN_basic_tensors() # Gives us betaU & BU outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_BSSN_to_ADM(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; """ btoa_lhrh = [] for i in range(3): for j in range(i, 3): btoa_lhrh.append( lhrh(lhs="g" + chr(ord('x') + i) + chr(ord('x') + j) + "[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)]", rhs=btoa.gammaDD[i][j])) for i in range(3): for j in range(i, 3): btoa_lhrh.append( lhrh(lhs="k" + chr(ord('x') + i) + chr(ord('x') + j) + "[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)]", rhs=btoa.KDD[i][j])) btoa_lhrh.append( lhrh(lhs="alp[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)]", rhs=Bq.alpha)) for i in range(3): btoa_lhrh.append( lhrh(lhs="beta" + chr(ord('x') + i) + "[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)]", rhs=Bq.betaU[i])) for i in range(3): btoa_lhrh.append( lhrh(lhs="dtbeta" + chr(ord('x') + i) + "[CCTK_GFINDEX3D(cctkGH,i0,i1,i2)]", rhs=Bq.BU[i])) outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" bssn_to_adm_Ccode = fin.FD_outputC("returnstring", btoa_lhrh, outCparams) outstr += lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], ["cctk_lsh[2]", "cctk_lsh[1]", "cctk_lsh[0]"], ["1", "1", "1"], ["#pragma omp parallel for", "", ""], "", bssn_to_adm_Ccode) outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("BSSN_to_ADM.c")] = outstr.replace( "BaikalETK", ThornName) # Next, the driver for computing the Ricci tensor: outstr = """ #include <math.h> #include "SIMD/SIMD_intrinsics.h" #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_driver_pt1_BSSN_Ricci(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; const CCTK_INT *FD_order = CCTK_ParameterGet("FD_order","BaikalETK",NULL); const CCTK_REAL NOSIMDinvdx0 = 1.0/CCTK_DELTA_SPACE(0); const REAL_SIMD_ARRAY invdx0 = ConstSIMD(NOSIMDinvdx0); const CCTK_REAL NOSIMDinvdx1 = 1.0/CCTK_DELTA_SPACE(1); const REAL_SIMD_ARRAY invdx1 = ConstSIMD(NOSIMDinvdx1); const CCTK_REAL NOSIMDinvdx2 = 1.0/CCTK_DELTA_SPACE(2); const REAL_SIMD_ARRAY invdx2 = ConstSIMD(NOSIMDinvdx2); """ path = os.path.join(ThornName, "src") for root, dirs, files in os.walk(path): for file in files: if "BSSN_Ricci_FD_order" in file: array = file.replace(".", "_").split("_") outstr += " if(*FD_order == " + str(array[4]) + ") {\n" outstr += " #include \"" + file + "\"\n" outstr += " }\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "driver_pt1_BSSN_Ricci.c")] = outstr.replace("BaikalETK", ThornName) def SIMD_declare_C_params(): SIMD_declare_C_params_str = "" for i in range(len(par.glb_Cparams_list)): # keep_param is a boolean indicating whether we should accept or reject # the parameter. singleparstring will contain the string indicating # the variable type. keep_param, singleparstring = ccl.keep_param__return_type( par.glb_Cparams_list[i]) if (keep_param) and ("CCTK_REAL" in singleparstring): parname = par.glb_Cparams_list[i].parname SIMD_declare_C_params_str += " const "+singleparstring + "*NOSIMD"+parname+\ " = CCTK_ParameterGet(\""+parname+"\",\"BaikalETK\",NULL);\n" SIMD_declare_C_params_str += " const REAL_SIMD_ARRAY " + parname + " = ConstSIMD(*NOSIMD" + parname + ");\n" return SIMD_declare_C_params_str # Next, the driver for computing the BSSN RHSs: outstr = """ #include <math.h> #include "SIMD/SIMD_intrinsics.h" #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_driver_pt2_BSSN_RHSs(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; const CCTK_INT *FD_order = CCTK_ParameterGet("FD_order","BaikalETK",NULL); const CCTK_REAL NOSIMDinvdx0 = 1.0/CCTK_DELTA_SPACE(0); const REAL_SIMD_ARRAY invdx0 = ConstSIMD(NOSIMDinvdx0); const CCTK_REAL NOSIMDinvdx1 = 1.0/CCTK_DELTA_SPACE(1); const REAL_SIMD_ARRAY invdx1 = ConstSIMD(NOSIMDinvdx1); const CCTK_REAL NOSIMDinvdx2 = 1.0/CCTK_DELTA_SPACE(2); const REAL_SIMD_ARRAY invdx2 = ConstSIMD(NOSIMDinvdx2); """ + SIMD_declare_C_params() path = os.path.join(ThornName, "src") for root, dirs, files in os.walk(path): for file in files: if "BSSN_RHSs_FD_order" in file: array = file.replace(".", "_").split("_") outstr += " if(*FD_order == " + str(array[4]) + ") {\n" outstr += " #include \"" + file + "\"\n" outstr += " }\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "driver_pt2_BSSN_RHSs.c")] = outstr.replace("BaikalETK", ThornName) # Next, the driver for enforcing detgammabar = detgammahat constraint: outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_enforce_detgammabar_constraint(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; """ path = os.path.join(ThornName, "src") for root, dirs, files in os.walk(path): for file in files: if "enforcedetgammabar_constraint_FD_order" in file: array = file.replace(".", "_").split("_") outstr += " if(FD_order == " + str(array[4]) + ") {\n" outstr += " #include \"" + file + "\"\n" outstr += " }\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list("driver_enforcedetgammabar_constraint.c")] = \ outstr.replace("BaikalETK",ThornName) # Next, the driver for computing the BSSN Hamiltonian & momentum constraints outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" void BaikalETK_BSSN_constraints(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; const CCTK_REAL invdx0 = 1.0/CCTK_DELTA_SPACE(0); const CCTK_REAL invdx1 = 1.0/CCTK_DELTA_SPACE(1); const CCTK_REAL invdx2 = 1.0/CCTK_DELTA_SPACE(2); """ path = os.path.join(ThornName, "src") for root, dirs, files in os.walk(path): for file in files: if "BSSN_constraints_FD_order" in file: array = file.replace(".", "_").split("_") outstr += " if(FD_order == " + str(array[4]) + ") {\n" outstr += " #include \"" + file + "\"\n" outstr += " }\n" outstr += "}\n" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "driver_BSSN_constraints.c")] = outstr.replace("BaikalETK", ThornName) if enable_stress_energy_source_terms == True: # Declare T4DD as a set of gridfunctions. These won't # actually appear in interface.ccl, as interface.ccl # was set above. Thus before calling the code output # by FD_outputC(), we'll have to set pointers # to the actual gridfunctions they reference. # (In this case the eTab's.) T4DD = ixp.register_gridfunctions_for_single_rank2("AUXEVOL", "T4DD", "sym01", DIM=4) import BSSN.ADMBSSN_tofrom_4metric as AB4m AB4m.g4UU_ito_BSSN_or_ADM("BSSN") T4UUraised = ixp.zerorank2(DIM=4) for mu in range(4): for nu in range(4): for delta in range(4): for gamma in range(4): T4UUraised[mu][nu] += AB4m.g4UU[mu][delta] * AB4m.g4UU[ nu][gamma] * T4DD[delta][gamma] T4UU_expressions = [ lhrh(lhs=gri.gfaccess("in_gfs", "T4UU00"), rhs=T4UUraised[0][0]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU01"), rhs=T4UUraised[0][1]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU02"), rhs=T4UUraised[0][2]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU03"), rhs=T4UUraised[0][3]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU11"), rhs=T4UUraised[1][1]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU12"), rhs=T4UUraised[1][2]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU13"), rhs=T4UUraised[1][3]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU22"), rhs=T4UUraised[2][2]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU23"), rhs=T4UUraised[2][3]), lhrh(lhs=gri.gfaccess("in_gfs", "T4UU33"), rhs=T4UUraised[3][3]) ] outCparams = "outCverbose=False,includebraces=False,preindent=2,SIMD_enable=True" T4UUstr = fin.FD_outputC("returnstring", T4UU_expressions, outCparams) T4UUstr_loop = lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], ["cctk_lsh[2]", "cctk_lsh[1]", "cctk_lsh[0]"], ["1", "1", "SIMD_width"], ["#pragma omp parallel for", "", ""], "", T4UUstr) outstr = """ #include <math.h> #include "cctk.h" #include "cctk_Arguments.h" #include "cctk_Parameters.h" #include "SIMD/SIMD_intrinsics.h" void BaikalETK_driver_BSSN_T4UU(CCTK_ARGUMENTS) { DECLARE_CCTK_ARGUMENTS; DECLARE_CCTK_PARAMETERS; const CCTK_REAL *restrict T4DD00GF = eTtt; const CCTK_REAL *restrict T4DD01GF = eTtx; const CCTK_REAL *restrict T4DD02GF = eTty; const CCTK_REAL *restrict T4DD03GF = eTtz; const CCTK_REAL *restrict T4DD11GF = eTxx; const CCTK_REAL *restrict T4DD12GF = eTxy; const CCTK_REAL *restrict T4DD13GF = eTxz; const CCTK_REAL *restrict T4DD22GF = eTyy; const CCTK_REAL *restrict T4DD23GF = eTyz; const CCTK_REAL *restrict T4DD33GF = eTzz; """ + T4UUstr_loop + """ }\n""" # Add C code string to dictionary (Python dictionaries are immutable) Csrcdict[append_to_make_code_defn_list( "driver_BSSN_T4UU.c")] = outstr.replace("BaikalETK", ThornName)
def GiRaFFE_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 Psi4_tetrads_Ccode_function(tetrad_Ccode_filename="BSSN/Psi4_tetrad.h", TetradChoice="QuasiKinnersley", version=2): # Step 1.b: Given the chosen coordinate system, set up # corresponding reference metric and needed # reference metric quantities # The following function call sets up the reference metric # and related quantities, including rescaling matrices ReDD, # ReU, and hatted quantities. rfm.reference_metric() # Step 1.c: Import the tetrad module if version == 1: import BSSN.Psi4_tetrads as BP4T par.set_parval_from_str("BSSN.Psi4_tetrads::TetradChoice", TetradChoice) else: import BSSN.Psi4_tetradsv2 as BP4T par.set_parval_from_str("BSSN.Psi4_tetradsv2::TetradChoice", TetradChoice) # Step 2: Construct the C function header and # convert (xx0,xx1,xx2) to the corresponding # (x,y,z), as both are needed for the tetrad # expressions. outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" with open(tetrad_Ccode_filename, "w") as file: file.write(""" // Taking as input (xx0,xx1,xx2), this function outputs // the chosen Psi4 tetrad in the (xx0,xx1,xx2) basis void Psi4_tetrad(const REAL xx0,const REAL xx1,const REAL xx2, const REAL *in_gfs, const int i0,const int i1,const int i2, REAL n4U[4],REAL mre4U[4],REAL mim4U[4]) { """) outputC([rfm.xxCart[0], rfm.xxCart[1], rfm.xxCart[2]], ["REAL x", "REAL y", "REAL z"], tetrad_Ccode_filename, outCparams + ",CSE_enable=False") # Step 3: Output the tetrad in the reference-metric basis. # Step 3.a: BP4T.Psi4_tetrads() to construct the symbolic # expressions for the tetrad vectors $n^\mu$, # $\Re m^\mu$, and $\Im m^\mu$, which are needed # to construct $\Psi_4$. if version == 1: BP4T.Psi4_tetrads() else: BP4T.Psi4_tetradsv2() Psi4_tetrad_vecs = [] # Step 3.b: As the tetrad expressions depend on BSSN # gridfunctions, we pass the expressions into # fin.FD_outputC() so that the needed gridfunction # values are read in from memory as appropriate. for i in range(4): Psi4_tetrad_vecs.append( lhrh(lhs="n4U[" + str(i) + "]", rhs=BP4T.n4U[i])) Psi4_tetrad_vecs.append( lhrh(lhs="mre4U[" + str(i) + "]", rhs=BP4T.mre4U[i])) Psi4_tetrad_vecs.append( lhrh(lhs="mim4U[" + str(i) + "]", rhs=BP4T.mim4U[i])) fin.FD_outputC(tetrad_Ccode_filename, Psi4_tetrad_vecs, outCparams) with open(tetrad_Ccode_filename, "a") as file: file.write("}\n")
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_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_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_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 generate_C_code_for_Stilde_flux( out_dir, inputs_provided=False, alpha_face=None, gamma_faceDD=None, beta_faceU=None, Valenciav_rU=None, B_rU=None, Valenciav_lU=None, B_lU=None, sqrt4pi=None, outCparams="outCverbose=False,CSE_sorting=none", write_cmax_cmin=False): if not inputs_provided: # We will pass values of the gridfunction on the cell faces into the function. This requires us # to declare them as C parameters in NRPy+. We will denote this with the _face infix/suffix. alpha_face = gri.register_gridfunctions("AUXEVOL", "alpha_face") gamma_faceDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "gamma_faceDD", "sym01") beta_faceU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "beta_faceU") # We'll need some more gridfunctions, now, to represent the reconstructions of BU and ValenciavU # on the right and left faces Valenciav_rU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_rU", DIM=3) B_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_rU", DIM=3) Valenciav_lU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_lU", DIM=3) B_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_lU", DIM=3) sqrt4pi = par.Cparameters("REAL", thismodule, "sqrt4pi", "sqrt(4.0*M_PI)") # We'll also need to store the results of the HLLE step between functions. ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "Stilde_flux_HLLED") input_params_for_Stilde_flux = "const paramstruct *params,REAL *auxevol_gfs,REAL *rhs_gfs" if write_cmax_cmin: name_suffixes = ["_x", "_y", "_z"] for flux_dirn in range(3): calculate_Stilde_flux(flux_dirn,alpha_face,gamma_faceDD,beta_faceU,\ Valenciav_rU,B_rU,Valenciav_lU,B_lU,sqrt4pi) Stilde_flux_to_print = [ lhrh(lhs=gri.gfaccess("out_gfs", "Stilde_flux_HLLED0"), rhs=Stilde_fluxD[0]), lhrh(lhs=gri.gfaccess("out_gfs", "Stilde_flux_HLLED1"), rhs=Stilde_fluxD[1]), lhrh(lhs=gri.gfaccess("out_gfs", "Stilde_flux_HLLED2"), rhs=Stilde_fluxD[2]) ] if write_cmax_cmin: Stilde_flux_to_print = Stilde_flux_to_print \ +[ lhrh(lhs=gri.gfaccess("out_gfs","cmax"+name_suffixes[flux_dirn]),rhs=chsp.cmax), lhrh(lhs=gri.gfaccess("out_gfs","cmin"+name_suffixes[flux_dirn]),rhs=chsp.cmin) ] desc = "Compute the flux term of all 3 components of tilde{S}_i on the left face in the " + str( flux_dirn) + "direction for all components." name = "calculate_Stilde_flux_D" + str(flux_dirn) Ccode_function = outCfunction( outfile="returnstring", desc=desc, name=name, params=input_params_for_Stilde_flux, body=fin.FD_outputC("returnstring", Stilde_flux_to_print, params=outCparams), loopopts="InteriorPoints", rel_path_to_Cparams=os.path.join("../")).replace( "NGHOSTS+Nxx0", "NGHOSTS+Nxx0+1").replace( "NGHOSTS+Nxx1", "NGHOSTS+Nxx1+1").replace("NGHOSTS+Nxx2", "NGHOSTS+Nxx2+1") with open(os.path.join(out_dir, name + ".h"), "w") as file: file.write(Ccode_function) pre_body = """// Notice in the loop below that we go from 3 to cctk_lsh-3 for i, j, AND k, even though // we are only computing the flux in one direction. This is because in the end, // we only need the rhs's from 3 to cctk_lsh-3 for i, j, and k. const REAL invdxi[4] = {1e100,invdx0,invdx1,invdx2}; const REAL invdx = invdxi[flux_dirn];""" FD_body = """const int index = IDX3S(i0,i1,i2); const int indexp1 = IDX3S(i0+kronecker_delta[flux_dirn][0],i1+kronecker_delta[flux_dirn][1],i2+kronecker_delta[flux_dirn][2]); rhs_gfs[IDX4ptS(STILDED0GF,index)] += (auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED0GF,index)] - auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED0GF,indexp1)] ) * invdx; rhs_gfs[IDX4ptS(STILDED1GF,index)] += (auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED1GF,index)] - auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED1GF,indexp1)] ) * invdx; rhs_gfs[IDX4ptS(STILDED2GF,index)] += (auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED2GF,index)] - auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED2GF,indexp1)] ) * invdx;""" desc = "Compute the difference in the flux of StildeD on the opposite faces in flux_dirn for all components." name = "calculate_Stilde_rhsD" outCfunction( outfile=os.path.join(out_dir, name + ".h"), desc=desc, name=name, params= "const int flux_dirn,const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs", preloop=pre_body, body=FD_body, loopopts="InteriorPoints", rel_path_to_Cparams=os.path.join("../"))
def Tmunu_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear( CoordType_in, Tmunu_input_function_name, pointer_to_ID_inputs=False): # The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations. # To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in # the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3. # Step 0: Set spatial dimension (must be 3 for BSSN) DIM = 3 # Step 1: Define the input variables: the 4D stress-energy tensor, and the ADM 3-metric, lapse, & shift: T4SphorCartUU = ixp.declarerank2("T4SphorCartUU", "sym01", DIM=4) gammaSphorCartDD = ixp.declarerank2("gammaSphorCartDD", "sym01") alphaSphorCart = sp.symbols("alphaSphorCart") betaSphorCartU = ixp.declarerank1("betaSphorCartU") # Step 2: All Tmunu initial data quantities are functions of xx0,xx1,xx2, but # in the Spherical or Cartesian basis. # We first define the BSSN stress-energy source terms in the Spherical # or Cartesian basis, respectively. # To get \gamma_{\mu \nu} = gammabar4DD[mu][nu], we'll need to construct the 4-metric, using Eq. 2.122 in B&S: # S_{ij} = \gamma_{i \mu} \gamma_{j \nu} T^{\mu \nu} # S_{i} = -\gamma_{i\mu} n_\nu T^{\mu\nu} # S = \gamma^{ij} S_{ij} # rho = n_\mu n_\nu T^{\mu\nu}, # where # \gamma_{\mu\nu} = g_{\mu\nu} + n_\mu n_\nu # and # n_mu = {-\alpha,0,0,0}, # Step 2.1: Construct the 4-metric based on the input ADM quantities. # This is provided by Eq 4.47 in [Gourgoulhon](https://arxiv.org/pdf/gr-qc/0703035.pdf): # g_{tt} = -\alpha^2 + \beta^k \beta_k # g_{ti} = \beta_i # g_{ij} = \gamma_{ij} # Eq. 2.121 in B&S betaSphorCartD = ixp.zerorank1() for i in range(DIM): for j in range(DIM): betaSphorCartD[i] += gammaSphorCartDD[i][j] * betaSphorCartU[j] # Now compute the beta contraction. beta2 = sp.sympify(0) for i in range(DIM): beta2 += betaSphorCartU[i] * betaSphorCartD[i] # Eq. 2.122 in B&S g4SphorCartDD = ixp.zerorank2(DIM=4) g4SphorCartDD[0][0] = -alphaSphorCart**2 + beta2 for i in range(DIM): g4SphorCartDD[i + 1][0] = g4SphorCartDD[0][i + 1] = betaSphorCartD[i] for i in range(DIM): for j in range(DIM): g4SphorCartDD[i + 1][j + 1] = gammaSphorCartDD[i][j] # Step 2.2: Construct \gamma_{mu nu} = g_{mu nu} + n_mu n_nu: n4SphorCartD = ixp.zerorank1(DIM=4) n4SphorCartD[0] = -alphaSphorCart gamma4SphorCartDD = ixp.zerorank2(DIM=4) for mu in range(4): for nu in range(4): gamma4SphorCartDD[mu][nu] = g4SphorCartDD[mu][ nu] + n4SphorCartD[mu] * n4SphorCartD[nu] # Step 2.3: We now have all we need to construct the BSSN source # terms in the current basis (Spherical or Cartesian): # S_{ij} = \gamma_{i \mu} \gamma_{j \nu} T^{\mu \nu} # S_{i} = -\gamma_{i\mu} n_\nu T^{\mu\nu} # S = \gamma^{ij} S_{ij} # rho = n_\mu n_\nu T^{\mu\nu}, SSphorCartDD = ixp.zerorank2() SSphorCartD = ixp.zerorank1() SSphorCart = sp.sympify(0) rhoSphorCart = sp.sympify(0) for i in range(DIM): for j in range(DIM): for mu in range(4): for nu in range(4): SSphorCartDD[i][j] += gamma4SphorCartDD[ i + 1][mu] * gamma4SphorCartDD[ j + 1][nu] * T4SphorCartUU[mu][nu] for i in range(DIM): for mu in range(4): for nu in range(4): SSphorCartD[i] += -gamma4SphorCartDD[ i + 1][mu] * n4SphorCartD[nu] * T4SphorCartUU[mu][nu] gammaSphorCartUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaSphorCartDD) for i in range(DIM): for j in range(DIM): SSphorCart += gammaSphorCartUU[i][j] * SSphorCartDD[i][j] for mu in range(4): for nu in range(4): rhoSphorCart += n4SphorCartD[mu] * n4SphorCartD[ nu] * T4SphorCartUU[mu][nu] # Step 3: Perform basis conversion to # Make sure that rfm.reference_metric() has been called. # We'll need the variables it defines throughout this module. if rfm.have_already_called_reference_metric_function == False: print( "Error. Called Tmunu_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear() without" ) print( " first setting up reference metric, by calling rfm.reference_metric()." ) exit(1) # Step 1: All input quantities are in terms of r,th,ph or x,y,z. We want them in terms # of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace # r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2 # as defined for this particular reference metric in reference_metric.py's # xxSph[] or xxCart[], respectively: r_th_ph_or_Cart_xyz_oID_xx = [] if CoordType_in == "Spherical": r_th_ph_or_Cart_xyz_oID_xx = rfm.xxSph elif CoordType_in == "Cartesian": r_th_ph_or_Cart_xyz_oID_xx = rfm.xxCart else: print( "Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords." ) exit(1) # Next apply Jacobian transformations to convert into the (xx0,xx1,xx2) basis # alpha is a scalar, so no Jacobian transformation is necessary. alpha = alphaSphorCart Jac_dUSphorCart_dDrfmUD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff( r_th_ph_or_Cart_xyz_oID_xx[i], rfm.xx[j]) Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3( Jac_dUSphorCart_dDrfmUD) betaU = ixp.zerorank1() BU = ixp.zerorank1() gammaSphorCartDD = ixp.zerorank2() KDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j] BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j] for k in range(DIM): for l in range(DIM): gammaSphorCartDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * \ gammaSphorCartDD[k][l] KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][ i] * Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l] # Step 3: All ADM quantities were input into this function in the Spherical or Cartesian # basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above, # we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2. # Here we convert ADM quantities to their BSSN Curvilinear counterparts: # Step 3.1: Convert ADM $\gamma_{ij}$ to BSSN $\bar{\gamma}_{ij}$: # We have (Eqs. 2 and 3 of [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)): # \bar{\gamma}_{i j} = \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \gamma_{ij}. gammaSphorCartUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaSphorCartDD) gammabarDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): gammabarDD[i][j] = (rfm.detgammahat / gammaDET)**(sp.Rational( 1, 3)) * gammaSphorCartDD[i][j] # Step 3.2: Convert the extrinsic curvature $K_{ij}$ to the trace-free extrinsic # curvature $\bar{A}_{ij}$, plus the trace of the extrinsic curvature $K$, # where (Eq. 3 of [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)): # K = \gamma^{ij} K_{ij}, and # \bar{A}_{ij} &= \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \left(K_{ij} - \frac{1}{3} \gamma_{ij} K \right) trK = sp.sympify(0) for i in range(DIM): for j in range(DIM): trK += gammaSphorCartUU[i][j] * KDD[i][j] AbarDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): AbarDD[i][j] = (rfm.detgammahat / gammaDET)**(sp.Rational( 1, 3)) * (KDD[i][j] - sp.Rational(1, 3) * gammaSphorCartDD[i][j] * trK) # Step 3.3: Set the conformal factor variable $\texttt{cf}$, which is set # by the "BSSN_RHSs::ConformalFactor" parameter. For example if # "ConformalFactor" is set to "phi", we can use Eq. 3 of # [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf), # which in arbitrary coordinates is written: # \phi = \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right). # Alternatively if "BSSN_RHSs::ConformalFactor" is set to "chi", then # \chi = e^{-4 \phi} = \exp\left(-4 \frac{1}{12} \left(\frac{\gamma}{\bar{\gamma}}\right)\right) # = \exp\left(-\frac{1}{3} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/3}. # # Finally if "BSSN_RHSs::ConformalFactor" is set to "W", then # W = e^{-2 \phi} = \exp\left(-2 \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = # \exp\left(-\frac{1}{6} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = # \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/6}. # First compute gammabarDET: gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD) cf = sp.sympify(0) if par.parval_from_str("ConformalFactor") == "phi": cf = sp.Rational(1, 12) * sp.log(gammaDET / gammabarDET) elif par.parval_from_str("ConformalFactor") == "chi": cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 3)) elif par.parval_from_str("ConformalFactor") == "W": cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 6)) else: print("Error ConformalFactor type = \"" + par.parval_from_str("ConformalFactor") + "\" unknown.") exit(1) # Step 4: Rescale tensorial quantities according to the prescription described in # the [BSSN in curvilinear coordinates tutorial module](Tutorial-BSSNCurvilinear.ipynb) # (also [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)): # # h_{ij} &= (\bar{\gamma}_{ij} - \hat{\gamma}_{ij})/\text{ReDD[i][j]}\\ # a_{ij} &= \bar{A}_{ij}/\text{ReDD[i][j]}\\ # \lambda^i &= \bar{\Lambda}^i/\text{ReU[i]}\\ # \mathcal{V}^i &= \beta^i/\text{ReU[i]}\\ # \mathcal{B}^i &= B^i/\text{ReU[i]}\\ hDD = ixp.zerorank2() aDD = ixp.zerorank2() vetU = ixp.zerorank1() betU = ixp.zerorank1() for i in range(DIM): vetU[i] = betaU[i] / rfm.ReU[i] betU[i] = BU[i] / rfm.ReU[i] for j in range(DIM): hDD[i][j] = (gammabarDD[i][j] - rfm.ghatDD[i][j]) / rfm.ReDD[i][j] aDD[i][j] = AbarDD[i][j] / rfm.ReDD[i][j] # Step 5: Output all ADM-to-BSSN expressions to a C function. This function # must first call the ID_ADM_SphorCart() defined above. Using these # Spherical or Cartesian data, it sets up all quantities needed for # BSSNCurvilinear initial data, *except* $\lambda^i$, which must be # computed from numerical data using finite-difference derivatives. with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", "w") as file: file.write( "void ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(const REAL xx0xx1xx2[3]," ) if pointer_to_ID_inputs == True: file.write("ID_inputs *other_inputs,") else: file.write("ID_inputs other_inputs,") file.write(""" REAL *hDD00,REAL *hDD01,REAL *hDD02,REAL *hDD11,REAL *hDD12,REAL *hDD22, REAL *aDD00,REAL *aDD01,REAL *aDD02,REAL *aDD11,REAL *aDD12,REAL *aDD22, REAL *trK, REAL *vetU0,REAL *vetU1,REAL *vetU2, REAL *betU0,REAL *betU1,REAL *betU2, REAL *alpha, REAL *cf) { REAL gammaSphorCartDD00,gammaSphorCartDD01,gammaSphorCartDD02, gammaSphorCartDD11,gammaSphorCartDD12,gammaSphorCartDD22; REAL KSphorCartDD00,KSphorCartDD01,KSphorCartDD02, KSphorCartDD11,KSphorCartDD12,KSphorCartDD22; REAL alphaSphorCart,betaSphorCartU0,betaSphorCartU1,betaSphorCartU2; REAL BSphorCartU0,BSphorCartU1,BSphorCartU2; const REAL xx0 = xx0xx1xx2[0]; const REAL xx1 = xx0xx1xx2[1]; const REAL xx2 = xx0xx1xx2[2]; REAL xyz_or_rthph[3];\n""") outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" outputC(r_th_ph_or_Cart_xyz_oID_xx[0:3], ["xyz_or_rthph[0]", "xyz_or_rthph[1]", "xyz_or_rthph[2]"], "BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", outCparams + ",CSE_enable=False") with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", "a") as file: file.write(" " + ADM_input_function_name + """(xyz_or_rthph, other_inputs, &gammaSphorCartDD00,&gammaSphorCartDD01,&gammaSphorCartDD02, &gammaSphorCartDD11,&gammaSphorCartDD12,&gammaSphorCartDD22, &KSphorCartDD00,&KSphorCartDD01,&KSphorCartDD02, &KSphorCartDD11,&KSphorCartDD12,&KSphorCartDD22, &alphaSphorCart,&betaSphorCartU0,&betaSphorCartU1,&betaSphorCartU2, &BSphorCartU0,&BSphorCartU1,&BSphorCartU2); // Next compute all rescaled BSSN curvilinear quantities:\n""") outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" outputC([ hDD[0][0], hDD[0][1], hDD[0][2], hDD[1][1], hDD[1][2], hDD[2][2], aDD[0][0], aDD[0][1], aDD[0][2], aDD[1][1], aDD[1][2], aDD[2][2], trK, vetU[0], vetU[1], vetU[2], betU[0], betU[1], betU[2], alpha, cf ], [ "*hDD00", "*hDD01", "*hDD02", "*hDD11", "*hDD12", "*hDD22", "*aDD00", "*aDD01", "*aDD02", "*aDD11", "*aDD12", "*aDD22", "*trK", "*vetU0", "*vetU1", "*vetU2", "*betU0", "*betU1", "*betU2", "*alpha", "*cf" ], "BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", params=outCparams) with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", "a") as file: file.write("}\n") # Step 5.A: Output the driver function for the above # function ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs() # Next write the driver function for ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(): with open("BSSN/ID_BSSN__ALL_BUT_LAMBDAs.h", "w") as file: file.write( "void ID_BSSN__ALL_BUT_LAMBDAs(const int Nxx_plus_2NGHOSTS[3],REAL *xx[3]," ) if pointer_to_ID_inputs == True: file.write("ID_inputs *other_inputs,") else: file.write("ID_inputs other_inputs,") file.write("REAL *in_gfs) {\n") file.write( lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], [ "Nxx_plus_2NGHOSTS[2]", "Nxx_plus_2NGHOSTS[1]", "Nxx_plus_2NGHOSTS[0]" ], ["1", "1", "1"], [ "#pragma omp parallel for", " const REAL xx2 = xx[2][i2];", " const REAL xx1 = xx[1][i1];" ], "", """const REAL xx0 = xx[0][i0]; const int idx = IDX3(i0,i1,i2); const REAL xx0xx1xx2[3] = {xx0,xx1,xx2}; ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(xx0xx1xx2,other_inputs, &in_gfs[IDX4pt(HDD00GF,idx)],&in_gfs[IDX4pt(HDD01GF,idx)],&in_gfs[IDX4pt(HDD02GF,idx)], &in_gfs[IDX4pt(HDD11GF,idx)],&in_gfs[IDX4pt(HDD12GF,idx)],&in_gfs[IDX4pt(HDD22GF,idx)], &in_gfs[IDX4pt(ADD00GF,idx)],&in_gfs[IDX4pt(ADD01GF,idx)],&in_gfs[IDX4pt(ADD02GF,idx)], &in_gfs[IDX4pt(ADD11GF,idx)],&in_gfs[IDX4pt(ADD12GF,idx)],&in_gfs[IDX4pt(ADD22GF,idx)], &in_gfs[IDX4pt(TRKGF,idx)], &in_gfs[IDX4pt(VETU0GF,idx)],&in_gfs[IDX4pt(VETU1GF,idx)],&in_gfs[IDX4pt(VETU2GF,idx)], &in_gfs[IDX4pt(BETU0GF,idx)],&in_gfs[IDX4pt(BETU1GF,idx)],&in_gfs[IDX4pt(BETU2GF,idx)], &in_gfs[IDX4pt(ALPHAGF,idx)],&in_gfs[IDX4pt(CFGF,idx)]); """)) file.write("}\n") # Step 6: Compute $\bar{\Lambda}^i$ (Eqs. 4 and 5 of # [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)), # from finite-difference derivatives of rescaled metric # quantities $h_{ij}$: # \bar{\Lambda}^i = \bar{\gamma}^{jk}\left(\bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk}\right). # The reference_metric.py module provides us with analytic expressions for # $\hat{\Gamma}^i_{jk}$, so here we need only compute # finite-difference expressions for $\bar{\Gamma}^i_{jk}$, based on # the values for $h_{ij}$ provided in the initial data. Once # $\bar{\Lambda}^i$ has been computed, we apply the usual rescaling # procedure: # \lambda^i = \bar{\Lambda}^i/\text{ReU[i]}, # and then output the result to a C file using the NRPy+ # finite-difference C output routine. # We will need all BSSN gridfunctions to be defined, as well as # expressions for gammabarDD_dD in terms of exact derivatives of # the rescaling matrix and finite-difference derivatives of # hDD's. gammabarDD = bssnrhs.gammabarDD gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD) gammabarDD_dD = bssnrhs.gammabarDD_dD # Next compute Christoffel symbols \bar{\Gamma}^i_{jk}: GammabarUDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): GammabarUDD[i][j][k] += sp.Rational( 1, 2) * gammabarUU[i][l] * (gammabarDD_dD[l][j][k] + gammabarDD_dD[l][k][j] - gammabarDD_dD[j][k][l]) # Next evaluate \bar{\Lambda}^i, based on GammabarUDD above and GammahatUDD # (from the reference metric): LambdabarU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): LambdabarU[i] += gammabarUU[j][k] * ( GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k]) # Finally apply rescaling: # lambda^i = Lambdabar^i/\text{ReU[i]} lambdaU = ixp.zerorank1() for i in range(DIM): lambdaU[i] = LambdabarU[i] / rfm.ReU[i] outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False" lambdaU_expressions = [ lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU0"), rhs=lambdaU[0]), lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU1"), rhs=lambdaU[1]), lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU2"), rhs=lambdaU[2]) ] lambdaU_expressions_FDout = fin.FD_outputC("returnstring", lambdaU_expressions, outCparams) with open("BSSN/ID_BSSN_lambdas.h", "w") as file: file.write(""" void ID_BSSN_lambdas(const int Nxx[3],const int Nxx_plus_2NGHOSTS[3],REAL *xx[3],const REAL dxx[3],REAL *in_gfs) {\n""" ) file.write( lp.loop(["i2", "i1", "i0"], ["NGHOSTS", "NGHOSTS", "NGHOSTS"], ["NGHOSTS+Nxx[2]", "NGHOSTS+Nxx[1]", "NGHOSTS+Nxx[0]"], ["1", "1", "1"], [ "const REAL invdx0 = 1.0/dxx[0];\n" + "const REAL invdx1 = 1.0/dxx[1];\n" + "const REAL invdx2 = 1.0/dxx[2];\n" + "#pragma omp parallel for", " const REAL xx2 = xx[2][i2];", " const REAL xx1 = xx[1][i1];" ], "", "const REAL xx0 = xx[0][i0];\n" + lambdaU_expressions_FDout)) file.write("}\n")