def write_out_functions_for_StildeD_source_term(outdir,outCparams,gammaDD,betaU,alpha,ValenciavU,BU,sqrt4pi): generate_memory_access_code() # First, we declare some dummy tensors that we will use for the codegen. gammaDDdD = ixp.declarerank3("gammaDDdD","sym01",DIM=3) betaUdD = ixp.declarerank2("betaUdD","nosym",DIM=3) alphadD = ixp.declarerank1("alphadD",DIM=3) # We need to rerun a few of these functions with the reset lists to make sure these functions # don't cheat by using analytic expressions GRHD.compute_sqrtgammaDET(gammaDD) GRHD.u4U_in_terms_of_ValenciavU__rescale_ValenciavU_by_applying_speed_limit(alpha, betaU, gammaDD, ValenciavU) GRFFE.compute_smallb4U(gammaDD, betaU, alpha, GRHD.u4U_ito_ValenciavU, BU, sqrt4pi) GRFFE.compute_smallbsquared(gammaDD, betaU, alpha, GRFFE.smallb4U) GRFFE.compute_TEM4UU(gammaDD,betaU,alpha, GRFFE.smallb4U, GRFFE.smallbsquared,GRHD.u4U_ito_ValenciavU) GRHD.compute_g4DD_zerotimederiv_dD(gammaDD,betaU,alpha, gammaDDdD,betaUdD,alphadD) GRHD.compute_S_tilde_source_termD(alpha, GRHD.sqrtgammaDET,GRHD.g4DD_zerotimederiv_dD, GRFFE.TEM4UU) for i in range(3): desc = "Adds the source term to StildeD"+str(i)+"." name = "calculate_StildeD"+str(i)+"_source_term" outCfunction( outfile = os.path.join(outdir,name+".h"), desc=desc, name=name, params ="const paramstruct *params,const REAL *auxevol_gfs, REAL *rhs_gfs", body = general_access \ +metric_deriv_access[i]\ +outputC(GRHD.S_tilde_source_termD[i],"Stilde_rhsD"+str(i),"returnstring",params=outCparams).replace("IDX4","IDX4S")\ +write_final_quantity[i], loopopts ="InteriorPoints", rel_path_for_Cparams=os.path.join("../"))
def ID_scalarfield(Ccodesdir=".", new_way=False): includes = ["NRPy_basic_defines.h", "NRPy_function_prototypes.h"] desc = """(c) 2021 Leo Werneck This is the scalar field initial data driver functiono. """ c_type = "void" name = "ID_scalarfield" params = """const paramstruct *restrict params,REAL *restrict xx[3], ID_inputs other_inputs,REAL *restrict in_gfs""" body = """ const int idx = IDX3S(i0,i1,i2); const REAL xx0xx1xx2[3] = {xx0,xx1,xx2}; ID_scalarfield_xx0xx1xx2_to_BSSN_xx0xx1xx2(params,xx0xx1xx2,other_inputs, &in_gfs[IDX4ptS(SFGF,idx)], &in_gfs[IDX4ptS(SFMGF,idx)]); """ loopopts = "AllPoints,Read_xxs" if new_way == True: outC.add_to_Cfunction_dict(includes=includes, desc=desc, c_type=c_type, name=name, params=params, body=body, loopopts=loopopts) else: outfile = os.path.join(Ccodesdir, "ID_scalarfield.h") outC.outCfunction(outfile=outfile, includes=None, desc=desc, c_type=c_type, name=name, params=params, body=body, loopopts=loopopts)
def generate_C_code_for_Stilde_flux(out_dir,inputs_provided = False, alpha_face=None, gamma_faceDD=None, beta_faceU=None, Valenciav_rU=None, B_rU=None, Valenciav_lU=None, B_lU=None, sqrt4pi=None, outCparams = "outCverbose=False,CSE_sorting=none"): if not inputs_provided: # We will pass values of the gridfunction on the cell faces into the function. This requires us # to declare them as C parameters in NRPy+. We will denote this with the _face infix/suffix. alpha_face = gri.register_gridfunctions("AUXEVOL","alpha_face") gamma_faceDD = ixp.register_gridfunctions_for_single_rank2("AUXEVOL","gamma_faceDD","sym01") beta_faceU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","beta_faceU") # We'll need some more gridfunctions, now, to represent the reconstructions of BU and ValenciavU # on the right and left faces Valenciav_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","Valenciav_rU",DIM=3) B_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","B_rU",DIM=3) Valenciav_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","Valenciav_lU",DIM=3) B_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","B_lU",DIM=3) sqrt4pi = par.Cparameters("REAL",thismodule,"sqrt4pi","sqrt(4.0*M_PI)") for flux_dirn in range(3): calculate_Stilde_flux(flux_dirn,alpha_face,gamma_faceDD,beta_faceU,\ Valenciav_rU,B_rU,Valenciav_lU,B_lU,sqrt4pi) Stilde_flux_to_print = [\ Stilde_fluxD[0],\ Stilde_fluxD[1],\ Stilde_fluxD[2],\ ] Stilde_flux_names = [\ "Stilde_fluxD0",\ "Stilde_fluxD1",\ "Stilde_fluxD2",\ ] desc = "Compute the flux of all 3 components of tilde{S}_i on the right face in the " + str(flux_dirn) + "." name = "calculate_Stilde_flux_D" + str(flux_dirn) + "_right" outCfunction( outfile = os.path.join(out_dir,name+".h"), desc=desc, name=name, params ="const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs", body = Memory_Read \ +outputC(Stilde_flux_to_print,Stilde_flux_names,"returnstring",params=outCparams).replace("IDX4","IDX4S")\ +Memory_Write.replace(invdx[0],invdx[flux_dirn]), loopopts ="InteriorPoints", rel_path_for_Cparams=os.path.join("../")) desc = "Compute the flux of all 3 components of tilde{S}_i on the left face in the " + str(flux_dirn) + "." name = "calculate_Stilde_flux_D" + str(flux_dirn) + "_left" outCfunction( outfile = os.path.join(out_dir,name+".h"), desc=desc, name=name, params ="const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs", body = Memory_Read.replace(indices[flux_dirn],indicesp1[flux_dirn]) \ +outputC(Stilde_flux_to_print,Stilde_flux_names,"returnstring",params=outCparams).replace("IDX4","IDX4S")\ +Memory_Write.replace(invdx[0],invdx[flux_dirn]).replace(assignment,assignmentp1), loopopts ="InteriorPoints", rel_path_for_Cparams=os.path.join("../"))
def ID_scalarfield_xx0xx1xx2_to_BSSN_xx0xx1xx2(Ccodesdir=".", pointer_to_ID_inputs=False, new_way=False): rfm.reference_metric() rthph = outC.outputC(rfm.xxSph[0:3], ["rthph[0]", "rthph[1]", "rthph[2]"], "returnstring", "includebraces=False,outCverbose=False,preindent=1") includes = ["NRPy_basic_defines.h", "NRPy_function_prototypes.h"] desc = """(c) 2021 Leo Werneck This function takes as input either (x,y,z) or (r,th,ph) and outputs all scalar field quantities in the Cartesian or Spherical basis, respectively. """ c_type = "void" name = "ID_scalarfield_xx0xx1xx2_to_BSSN_xx0xx1xx2" params = "const paramstruct *restrict params,const REAL xx0xx1xx2[3],\n" if pointer_to_ID_inputs == True: params += "ID_inputs *other_inputs,\n" else: params += "ID_inputs other_inputs,\n" params += "REAL *restrict sf, REAL *restrict sfM" body = """ const REAL xx0 = xx0xx1xx2[0]; const REAL xx1 = xx0xx1xx2[1]; const REAL xx2 = xx0xx1xx2[2]; REAL rthph[3]; """ + rthph + """ ID_scalarfield_spherical(rthph,other_inputs,sf,sfM); """ if new_way == True: outC.add_to_Cfunction_dict(includes=includes, desc=desc, c_type=c_type, name=name, params=params, body=body) else: outfile = os.path.join(Ccodesdir, "ID_scalarfield_xx0xx1xx2_to_BSSN_xx0xx1xx2.h") outC.outCfunction(outfile=outfile, includes=None, desc=desc, c_type=c_type, name=name, params=params, body=body)
def ID_scalarfield_spherical(Ccodesdir=".", new_way=False): includes = ["NRPy_basic_defines.h", "NRPy_function_prototypes.h"] desc = """(c) 2021 Leo Werneck This function takes as input either (x,y,z) or (r,th,ph) and outputs all scalar field quantities in the Cartesian or Spherical basis, respectively. """ c_type = "void" name = "ID_scalarfield_spherical" params = "const REAL xyz_or_rthph[3],const ID_inputs other_inputs,REAL *restrict sf,REAL *restrict sfM" body = """ const REAL r = xyz_or_rthph[0]; const REAL th = xyz_or_rthph[1]; const REAL ph = xyz_or_rthph[2]; REAL sf_star,psi4_star,alpha_star; scalarfield_interpolate_1D(r, other_inputs.interp_stencil_size, other_inputs.numlines_in_file, other_inputs.r_arr, other_inputs.sf_arr, other_inputs.psi4_arr, other_inputs.alpha_arr, &sf_star,&psi4_star,&alpha_star); // Update varphi *sf = sf_star; // Update Pi *sfM = 0; """ if new_way == True: outC.add_to_Cfunction_dict(includes=includes, desc=desc, c_type=c_type, name=name, params=params, body=body, enableCparameters=False) else: outfile = os.path.join(Ccodesdir, "ID_scalarfield_spherical.h") outC.outCfunction(outfile=outfile, includes=None, desc=desc, c_type=c_type, name=name, params=params, body=body, enableCparameters=False)
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 GiRaFFE_NRPy_FCVAL(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. with open( os.path.join(Ccodesdir, "interpolate_metric_gfs_to_cell_faces.h"), "w") as file: file.write(prefunc) desc = "Interpolate metric gridfunctions to cell faces" name = "interpolate_metric_gfs_to_cell_faces" interp_Cfunc = outCfunction( outfile="returnstring", desc=desc, name=name, params= "const paramstruct *params,REAL *auxevol_gfs,const int flux_dirn", preloop=""" int in_gf,out_gf; REAL Qm2,Qm1,Qp0,Qp1; """, body=""" for(int gf = 0;gf < num_metric_gfs;gf++) { in_gf = metric_gfs_list[gf]; out_gf = metric_gfs_face_list[gf]; for (int i2 = 2;i2 < Nxx_plus_2NGHOSTS2-1;i2++) { for (int i1 = 2;i1 < Nxx_plus_2NGHOSTS1-1;i1++) { for (int i0 = 2;i0 < Nxx_plus_2NGHOSTS0-1;i0++) { Qm2 = auxevol_gfs[IDX4S(in_gf,i0-2*kronecker_delta[flux_dirn][0],i1-2*kronecker_delta[flux_dirn][1],i2-2*kronecker_delta[flux_dirn][2])]; Qm1 = auxevol_gfs[IDX4S(in_gf,i0-kronecker_delta[flux_dirn][0],i1-kronecker_delta[flux_dirn][1],i2-kronecker_delta[flux_dirn][2])]; Qp0 = auxevol_gfs[IDX4S(in_gf,i0,i1,i2)]; Qp1 = auxevol_gfs[IDX4S(in_gf,i0+kronecker_delta[flux_dirn][0],i1+kronecker_delta[flux_dirn][1],i2+kronecker_delta[flux_dirn][2])]; auxevol_gfs[IDX4S(out_gf,i0,i1,i2)] = COMPUTE_FCVAL(Qm2,Qm1,Qp0,Qp1); } } } } """, rel_path_to_Cparams=os.path.join("../")) with open( os.path.join(Ccodesdir, "interpolate_metric_gfs_to_cell_faces.h"), "a") as file: file.write(interp_Cfunc)
def 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 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") ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "PhievolParenU", DIM=3) gri.register_gridfunctions("AUXEVOL", "AevolParen") # Declare this symbol: sqrt4pi = par.Cparameters("REAL", thismodule, "sqrt4pi", "sqrt(4.0*M_PI)") GRHD.compute_sqrtgammaDET(gammaDD) GRFFE.compute_AD_source_term_operand_for_FD(GRHD.sqrtgammaDET, betaU, alpha, psi6Phi, AD) GRFFE.compute_psi6Phi_rhs_flux_term_operand(gammaDD, GRHD.sqrtgammaDET, betaU, alpha, AD, psi6Phi) parens_to_print = [\ lhrh(lhs=gri.gfaccess("auxevol_gfs","AevolParen"),rhs=GRFFE.AevolParen),\ lhrh(lhs=gri.gfaccess("auxevol_gfs","PhievolParenU0"),rhs=GRFFE.PhievolParenU[0]),\ lhrh(lhs=gri.gfaccess("auxevol_gfs","PhievolParenU1"),rhs=GRFFE.PhievolParenU[1]),\ lhrh(lhs=gri.gfaccess("auxevol_gfs","PhievolParenU2"),rhs=GRFFE.PhievolParenU[2]),\ ] subdir = "RHSs" cmd.mkdir(os.path.join(out_dir, subdir)) desc = "Calculate quantities to be finite-differenced for the GRFFE RHSs" name = "calculate_AD_gauge_term_psi6Phi_flux_term_for_RHSs" outCfunction( outfile=os.path.join(out_dir, subdir, name + ".h"), desc=desc, name=name, params= "const paramstruct *restrict params,const REAL *restrict in_gfs,REAL *restrict auxevol_gfs", body=fin.FD_outputC("returnstring", parens_to_print, params=outCparams).replace("IDX4", "IDX4S"), loopopts="AllPoints", rel_path_for_Cparams=os.path.join("../")) xi_damping = par.Cparameters("REAL", thismodule, "xi_damping", 0.1) GRFFE.compute_psi6Phi_rhs_damping_term(alpha, psi6Phi, xi_damping) AevolParen_dD = ixp.declarerank1("AevolParen_dD", DIM=3) PhievolParenU_dD = ixp.declarerank2("PhievolParenU_dD", "nosym", DIM=3) A_rhsD = ixp.zerorank1() psi6Phi_rhs = GRFFE.psi6Phi_damping for i in range(3): A_rhsD[i] += -AevolParen_dD[i] psi6Phi_rhs += -PhievolParenU_dD[i][i] # Add Kreiss-Oliger dissipation to the GRFFE RHSs: # psi6Phi_dKOD = ixp.declarerank1("psi6Phi_dKOD") # AD_dKOD = ixp.declarerank2("AD_dKOD","nosym") # for i in range(3): # psi6Phi_rhs += diss_strength*psi6Phi_dKOD[i]*rfm.ReU[i] # ReU[i] = 1/scalefactor_orthog_funcform[i] # for j in range(3): # A_rhsD[j] += diss_strength*AD_dKOD[j][i]*rfm.ReU[i] # ReU[i] = 1/scalefactor_orthog_funcform[i] RHSs_to_print = [\ lhrh(lhs=gri.gfaccess("rhs_gfs","AD0"),rhs=A_rhsD[0]),\ lhrh(lhs=gri.gfaccess("rhs_gfs","AD1"),rhs=A_rhsD[1]),\ lhrh(lhs=gri.gfaccess("rhs_gfs","AD2"),rhs=A_rhsD[2]),\ lhrh(lhs=gri.gfaccess("rhs_gfs","psi6Phi"),rhs=psi6Phi_rhs),\ ] desc = "Calculate AD gauge term and psi6Phi RHSs" name = "calculate_AD_gauge_psi6Phi_RHSs" source_Ccode = outCfunction( outfile="returnstring", desc=desc, name=name, params= "const paramstruct *params,const REAL *in_gfs,const REAL *auxevol_gfs,REAL *rhs_gfs", body=fin.FD_outputC("returnstring", RHSs_to_print, params=outCparams).replace("IDX4", "IDX4S"), loopopts="InteriorPoints", rel_path_for_Cparams=os.path.join("../")).replace( "= NGHOSTS", "= NGHOSTS_A2B").replace( "NGHOSTS+Nxx0", "Nxx_plus_2NGHOSTS0-NGHOSTS_A2B").replace( "NGHOSTS+Nxx1", "Nxx_plus_2NGHOSTS1-NGHOSTS_A2B").replace( "NGHOSTS+Nxx2", "Nxx_plus_2NGHOSTS2-NGHOSTS_A2B") # Note the above .replace() functions. These serve to expand the loop range into the ghostzones, since # the second-order FD needs fewer than some other algorithms we use do. with open(os.path.join(out_dir, subdir, name + ".h"), "w") as file: file.write(source_Ccode) source.write_out_functions_for_StildeD_source_term( os.path.join(out_dir, subdir), outCparams, gammaDD, betaU, alpha, ValenciavU, BU, sqrt4pi) subdir = "FCVAL" cmd.mkdir(os.path.join(out_dir, subdir)) FCVAL.GiRaFFE_NRPy_FCVAL(os.path.join(out_dir, subdir)) subdir = "PPM" cmd.mkdir(os.path.join(out_dir, subdir)) PPM.GiRaFFE_NRPy_PPM(os.path.join(out_dir, subdir)) # We will pass values of the gridfunction on the cell faces into the function. This requires us # to declare them as C parameters in NRPy+. We will denote this with the _face infix/suffix. alpha_face = gri.register_gridfunctions("AUXEVOL", "alpha_face") gamma_faceDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "gamma_faceDD", "sym01") beta_faceU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "beta_faceU") # We'll need some more gridfunctions, now, to represent the reconstructions of BU and ValenciavU # on the right and left faces Valenciav_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "Valenciav_rU", DIM=3) B_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_rU", DIM=3) Valenciav_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "Valenciav_lU", DIM=3) B_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_lU", DIM=3) subdir = "RHSs" Af.GiRaFFE_NRPy_Afield_flux(os.path.join(out_dir, subdir)) Sf.generate_C_code_for_Stilde_flux(os.path.join(out_dir, subdir), True, alpha_face, gamma_faceDD, beta_faceU, Valenciav_rU, B_rU, Valenciav_lU, B_lU, sqrt4pi) subdir = "boundary_conditions" cmd.mkdir(os.path.join(out_dir, subdir)) BC.GiRaFFE_NRPy_BCs(os.path.join(out_dir, subdir)) subdir = "A2B" cmd.mkdir(os.path.join(out_dir, subdir)) A2B.GiRaFFE_NRPy_A2B(os.path.join(out_dir, subdir), gammaDD, AD, BU) C2P_P2C.GiRaFFE_NRPy_C2P(StildeD, BU, gammaDD, betaU, alpha) values_to_print = [\ lhrh(lhs=gri.gfaccess("in_gfs","StildeD0"),rhs=C2P_P2C.outStildeD[0]),\ lhrh(lhs=gri.gfaccess("in_gfs","StildeD1"),rhs=C2P_P2C.outStildeD[1]),\ lhrh(lhs=gri.gfaccess("in_gfs","StildeD2"),rhs=C2P_P2C.outStildeD[2]),\ lhrh(lhs=gri.gfaccess("auxevol_gfs","ValenciavU0"),rhs=C2P_P2C.ValenciavU[0]),\ lhrh(lhs=gri.gfaccess("auxevol_gfs","ValenciavU1"),rhs=C2P_P2C.ValenciavU[1]),\ lhrh(lhs=gri.gfaccess("auxevol_gfs","ValenciavU2"),rhs=C2P_P2C.ValenciavU[2])\ ] subdir = "C2P" cmd.mkdir(os.path.join(out_dir, subdir)) desc = "Apply fixes to \tilde{S}_i and recompute the velocity to match with current sheet prescription." name = "GiRaFFE_NRPy_cons_to_prims" outCfunction( outfile=os.path.join(out_dir, subdir, name + ".h"), desc=desc, name=name, params= "const paramstruct *params,REAL *xx[3],REAL *auxevol_gfs,REAL *in_gfs", body=fin.FD_outputC("returnstring", values_to_print, params=outCparams).replace("IDX4", "IDX4S"), loopopts="AllPoints,Read_xxs", rel_path_for_Cparams=os.path.join("../")) C2P_P2C.GiRaFFE_NRPy_P2C(gammaDD, betaU, alpha, ValenciavU, BU, sqrt4pi) values_to_print = [\ lhrh(lhs=gri.gfaccess("in_gfs","StildeD0"),rhs=C2P_P2C.StildeD[0]),\ lhrh(lhs=gri.gfaccess("in_gfs","StildeD1"),rhs=C2P_P2C.StildeD[1]),\ lhrh(lhs=gri.gfaccess("in_gfs","StildeD2"),rhs=C2P_P2C.StildeD[2]),\ ] desc = "Recompute StildeD after current sheet fix to Valencia 3-velocity to ensure consistency between conservative & primitive variables." name = "GiRaFFE_NRPy_prims_to_cons" outCfunction( outfile=os.path.join(out_dir, subdir, name + ".h"), desc=desc, name=name, params="const paramstruct *params,REAL *auxevol_gfs,REAL *in_gfs", body=fin.FD_outputC("returnstring", values_to_print, params=outCparams).replace("IDX4", "IDX4S"), loopopts="AllPoints", rel_path_for_Cparams=os.path.join("../")) # Write out the main driver itself: with open(os.path.join(out_dir, "GiRaFFE_NRPy_Main_Driver.h"), "w") as file: file.write(r"""// Structure to track ghostzones for PPM: typedef struct __gf_and_gz_struct__ { REAL *gf; int gz_lo[4],gz_hi[4]; } gf_and_gz_struct; // Some additional constants needed for PPM: const int VX=0,VY=1,VZ=2,BX=3,BY=4,BZ=5; const int NUM_RECONSTRUCT_GFS = 6; // Include ALL functions needed for evolution #include "RHSs/calculate_AD_gauge_term_psi6Phi_flux_term_for_RHSs.h" #include "RHSs/calculate_AD_gauge_psi6Phi_RHSs.h" #include "PPM/reconstruct_set_of_prims_PPM_GRFFE_NRPy.c" #include "FCVAL/interpolate_metric_gfs_to_cell_faces.h" #include "RHSs/calculate_StildeD0_source_term.h" #include "RHSs/calculate_StildeD1_source_term.h" #include "RHSs/calculate_StildeD2_source_term.h" #include "../calculate_E_field_flat_all_in_one.h" #include "RHSs/calculate_Stilde_flux_D0.h" #include "RHSs/calculate_Stilde_flux_D1.h" #include "RHSs/calculate_Stilde_flux_D2.h" #include "boundary_conditions/GiRaFFE_boundary_conditions.h" #include "A2B/driver_AtoB.h" #include "C2P/GiRaFFE_NRPy_cons_to_prims.h" #include "C2P/GiRaFFE_NRPy_prims_to_cons.h" void override_BU_with_old_GiRaFFE(const paramstruct *restrict params,REAL *restrict auxevol_gfs,const int n) { #include "set_Cparameters.h" char filename[100]; sprintf(filename,"BU0_override-%08d.bin",n); FILE *out2D = fopen(filename, "rb"); fread(auxevol_gfs+BU0GF*Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2, sizeof(double),Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2,out2D); fclose(out2D); sprintf(filename,"BU1_override-%08d.bin",n); out2D = fopen(filename, "rb"); fread(auxevol_gfs+BU1GF*Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2, sizeof(double),Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2,out2D); fclose(out2D); sprintf(filename,"BU2_override-%08d.bin",n); out2D = fopen(filename, "rb"); fread(auxevol_gfs+BU2GF*Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2, sizeof(double),Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2,out2D); fclose(out2D); } void GiRaFFE_NRPy_RHSs(const paramstruct *restrict params,REAL *restrict auxevol_gfs,const REAL *restrict in_gfs,REAL *restrict rhs_gfs) { #include "set_Cparameters.h" // First thing's first: initialize the RHSs to zero! #pragma omp parallel for for(int ii=0;ii<Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2*NUM_EVOL_GFS;ii++) { rhs_gfs[ii] = 0.0; } // Next calculate the easier source terms that don't require flux directions // This will also reset the RHSs for each gf at each new timestep. calculate_AD_gauge_term_psi6Phi_flux_term_for_RHSs(params,in_gfs,auxevol_gfs); calculate_AD_gauge_psi6Phi_RHSs(params,in_gfs,auxevol_gfs,rhs_gfs); // Now, we set up a bunch of structs of pointers to properly guide the PPM algorithm. // They also count the number of ghostzones available. gf_and_gz_struct in_prims[NUM_RECONSTRUCT_GFS], out_prims_r[NUM_RECONSTRUCT_GFS], out_prims_l[NUM_RECONSTRUCT_GFS]; int which_prims_to_reconstruct[NUM_RECONSTRUCT_GFS],num_prims_to_reconstruct; const int Nxxp2NG012 = Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2; REAL *temporary = auxevol_gfs + Nxxp2NG012*AEVOLPARENGF; //We're not using this anymore // This sets pointers to the portion of auxevol_gfs containing the relevant gridfunction. int ww=0; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAVU0GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RU0GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LU0GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAVU1GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RU1GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LU1GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAVU2GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RU2GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LU2GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*BU0GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*B_RU0GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*B_LU0GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*BU1GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*B_RU1GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*B_LU1GF; ww++; in_prims[ww].gf = auxevol_gfs + Nxxp2NG012*BU2GF; out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*B_RU2GF; out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*B_LU2GF; ww++; // Prims are defined AT ALL GRIDPOINTS, so we set the # of ghostzones to zero: for(int i=0;i<NUM_RECONSTRUCT_GFS;i++) for(int j=1;j<=3;j++) { in_prims[i].gz_lo[j]=0; in_prims[i].gz_hi[j]=0; } // Left/right variables are not yet defined, yet we set the # of gz's to zero by default: for(int i=0;i<NUM_RECONSTRUCT_GFS;i++) for(int j=1;j<=3;j++) { out_prims_r[i].gz_lo[j]=0; out_prims_r[i].gz_hi[j]=0; } for(int i=0;i<NUM_RECONSTRUCT_GFS;i++) for(int j=1;j<=3;j++) { out_prims_l[i].gz_lo[j]=0; out_prims_l[i].gz_hi[j]=0; } ww=0; which_prims_to_reconstruct[ww]=VX; ww++; which_prims_to_reconstruct[ww]=VY; ww++; which_prims_to_reconstruct[ww]=VZ; ww++; which_prims_to_reconstruct[ww]=BX; ww++; which_prims_to_reconstruct[ww]=BY; ww++; which_prims_to_reconstruct[ww]=BZ; ww++; num_prims_to_reconstruct=ww; // In each direction, perform the PPM reconstruction procedure. // Then, add the fluxes to the RHS as appropriate. for(int flux_dirn=0;flux_dirn<3;flux_dirn++) { // In each direction, interpolate the metric gfs (gamma,beta,alpha) to cell faces. interpolate_metric_gfs_to_cell_faces(params,auxevol_gfs,flux_dirn+1); // Then, reconstruct the primitive variables on the cell faces. // This function is housed in the file: "reconstruct_set_of_prims_PPM_GRFFE_NRPy.c" reconstruct_set_of_prims_PPM_GRFFE_NRPy(params, auxevol_gfs, flux_dirn+1, num_prims_to_reconstruct, which_prims_to_reconstruct, in_prims, out_prims_r, out_prims_l, temporary); // For example, if flux_dirn==0, then at gamma_faceDD00(i,j,k) represents gamma_{xx} // at (i-1/2,j,k), Valenciav_lU0(i,j,k) is the x-component of the velocity at (i-1/2-epsilon,j,k), // and Valenciav_rU0(i,j,k) is the x-component of the velocity at (i-1/2+epsilon,j,k). if(flux_dirn==0) { // Next, we calculate the source term for StildeD. Again, this also resets the rhs_gfs array at // each new timestep. calculate_StildeD0_source_term(params,auxevol_gfs,rhs_gfs); // Now, compute the electric field on each face of a cell and add it to the RHSs as appropriate //calculate_E_field_D0_right(params,auxevol_gfs,rhs_gfs); //calculate_E_field_D0_left(params,auxevol_gfs,rhs_gfs); // Finally, we calculate the flux of StildeD and add the appropriate finite-differences // to the RHSs. calculate_Stilde_flux_D0(params,auxevol_gfs,rhs_gfs); } else if(flux_dirn==1) { calculate_StildeD1_source_term(params,auxevol_gfs,rhs_gfs); //calculate_E_field_D1_right(params,auxevol_gfs,rhs_gfs); //calculate_E_field_D1_left(params,auxevol_gfs,rhs_gfs); calculate_Stilde_flux_D1(params,auxevol_gfs,rhs_gfs); } else { calculate_StildeD2_source_term(params,auxevol_gfs,rhs_gfs); //calculate_E_field_D2_right(params,auxevol_gfs,rhs_gfs); //calculate_E_field_D2_left(params,auxevol_gfs,rhs_gfs); calculate_Stilde_flux_D2(params,auxevol_gfs,rhs_gfs); } for(int count=0;count<=1;count++) { // This function is written to be general, using notation that matches the forward permutation added to AD2, // i.e., [F_HLL^x(B^y)]_z corresponding to flux_dirn=0, count=1. // The SIGN parameter is necessary because // -E_z(x_i,y_j,z_k) = 0.25 ( [F_HLL^x(B^y)]_z(i+1/2,j,k)+[F_HLL^x(B^y)]_z(i-1/2,j,k) // -[F_HLL^y(B^x)]_z(i,j+1/2,k)-[F_HLL^y(B^x)]_z(i,j-1/2,k) ) // Note the negative signs on the reversed permutation terms! // By cyclically permuting with flux_dirn, we // get contributions to the other components, and by incrementing count, we get the backward permutations: // Let's suppose flux_dirn = 0. Then we will need to update Ay (count=0) and Az (count=1): // flux_dirn=count=0 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (0+1+0)%3=AD1GF <- Updating Ay! // (flux_dirn)%3 = (0)%3 = 0 Vx // (flux_dirn-count+2)%3 = (0-0+2)%3 = 2 Vz . Inputs Vx, Vz -> SIGN = -1 ; 2.0*((REAL)count)-1.0=-1 check! // flux_dirn=0,count=1 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (0+1+1)%3=AD2GF <- Updating Az! // (flux_dirn)%3 = (0)%3 = 0 Vx // (flux_dirn-count+2)%3 = (0-1+2)%3 = 1 Vy . Inputs Vx, Vy -> SIGN = +1 ; 2.0*((REAL)count)-1.0=2-1=+1 check! // Let's suppose flux_dirn = 1. Then we will need to update Az (count=0) and Ax (count=1): // flux_dirn=1,count=0 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (1+1+0)%3=AD2GF <- Updating Az! // (flux_dirn)%3 = (1)%3 = 1 Vy // (flux_dirn-count+2)%3 = (1-0+2)%3 = 0 Vx . Inputs Vy, Vx -> SIGN = -1 ; 2.0*((REAL)count)-1.0=-1 check! // flux_dirn=count=1 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (1+1+1)%3=AD0GF <- Updating Ax! // (flux_dirn)%3 = (1)%3 = 1 Vy // (flux_dirn-count+2)%3 = (1-1+2)%3 = 2 Vz . Inputs Vy, Vz -> SIGN = +1 ; 2.0*((REAL)count)-1.0=2-1=+1 check! // Let's suppose flux_dirn = 2. Then we will need to update Ax (count=0) and Ay (count=1): // flux_dirn=2,count=0 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (2+1+0)%3=AD0GF <- Updating Ax! // (flux_dirn)%3 = (2)%3 = 2 Vz // (flux_dirn-count+2)%3 = (2-0+2)%3 = 1 Vy . Inputs Vz, Vy -> SIGN = -1 ; 2.0*((REAL)count)-1.0=-1 check! // flux_dirn=2,count=1 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (2+1+1)%3=AD1GF <- Updating Ay! // (flux_dirn)%3 = (2)%3 = 2 Vz // (flux_dirn-count+2)%3 = (2-1+2)%3 = 0 Vx . Inputs Vz, Vx -> SIGN = +1 ; 2.0*((REAL)count)-1.0=2-1=+1 check! calculate_E_field_flat_all_in_one(params, &auxevol_gfs[IDX4ptS(VALENCIAV_RU0GF+(flux_dirn)%3, 0)],&auxevol_gfs[IDX4ptS(VALENCIAV_RU0GF+(flux_dirn-count+2)%3, 0)], &auxevol_gfs[IDX4ptS(VALENCIAV_LU0GF+(flux_dirn)%3, 0)],&auxevol_gfs[IDX4ptS(VALENCIAV_LU0GF+(flux_dirn-count+2)%3, 0)], &auxevol_gfs[IDX4ptS(B_RU0GF +(flux_dirn)%3, 0)],&auxevol_gfs[IDX4ptS(B_RU0GF +(flux_dirn-count+2)%3, 0)], &auxevol_gfs[IDX4ptS(B_LU0GF +(flux_dirn)%3, 0)],&auxevol_gfs[IDX4ptS(B_LU0GF +(flux_dirn-count+2)%3, 0)], &auxevol_gfs[IDX4ptS(B_RU0GF +(flux_dirn-count+2)%3, 0)], &auxevol_gfs[IDX4ptS(B_LU0GF +(flux_dirn-count+2)%3, 0)], &rhs_gfs[IDX4ptS(AD0GF+(flux_dirn+1+count)%3,0)], 2.0*((REAL)count)-1.0, flux_dirn); } } } void GiRaFFE_NRPy_post_step(const paramstruct *restrict params,REAL *xx[3],REAL *restrict auxevol_gfs,REAL *restrict evol_gfs,const int n) { // First, apply BCs to AD and psi6Phi. Then calculate BU from AD apply_bcs_potential(params,evol_gfs); driver_A_to_B(params,evol_gfs,auxevol_gfs); //override_BU_with_old_GiRaFFE(params,auxevol_gfs,n); // Apply fixes to StildeD, then recompute the velocity at the new timestep. // Apply the current sheet prescription to the velocities GiRaFFE_NRPy_cons_to_prims(params,xx,auxevol_gfs,evol_gfs); // Then, recompute StildeD to be consistent with the new velocities //GiRaFFE_NRPy_prims_to_cons(params,auxevol_gfs,evol_gfs); // Finally, apply outflow boundary conditions to the velocities. apply_bcs_velocity(params,auxevol_gfs); } """)
def GiRaFFE_NRPy_FCVAL(Ccodesdir): cmd.mkdir(Ccodesdir) # Write out the code to a file. with open(os.path.join(Ccodesdir,"interpolate_metric_gfs_to_cell_faces.h"),"w") as file: file.write("""// Side note: the following values could be used for cell averaged gfs: // am2=-1.0/12.0, am1=7.0/12.0, a0=7.0/12.0, a1=-1.0/12.0 // However, since the metric gfs store the grid point values instead of the cell average, // the following coefficients should be used: // am2 = -1/16, am1 = 9/16, a0 = 9/16, a1 = -1/16 // This will yield the third-order-accurate face values at m-1/2, // using values specified at {m-2,m-1,m,m+1} #define AM2 -0.0625 #define AM1 0.5625 #define A0 0.5625 #define A1 -0.0625 #define COMPUTE_FCVAL(METRICm2,METRICm1,METRIC,METRICp1) (AM2*(METRICm2) + AM1*(METRICm1) + A0*(METRIC) + A1*(METRICp1)) const int metric_gfs_list[10] = {GAMMADD00GF, GAMMADD01GF, GAMMADD02GF, GAMMADD11GF, GAMMADD12GF, GAMMADD22GF, BETAU0GF, BETAU1GF, BETAU2GF, ALPHAGF}; const int metric_gfs_face_list[10] = {GAMMA_FACEDD00GF, GAMMA_FACEDD01GF, GAMMA_FACEDD02GF, GAMMA_FACEDD11GF, GAMMA_FACEDD12GF, GAMMA_FACEDD22GF, BETA_FACEU0GF, BETA_FACEU1GF, BETA_FACEU2GF, ALPHA_FACEGF}; const int num_metric_gfs = 10; """) desc = "Interpolate metric gridfunctions to cell faces" name = "interpolate_metric_gfs_to_cell_faces" interp_Cfunc = outCfunction( outfile = "returnstring", desc=desc, name=name, params ="const paramstruct *params,REAL *auxevol_gfs,const int flux_dirn", preloop =""" int in_gf,out_gf; REAL Qm2,Qm1,Qp0,Qp1; """ , body =""" for(int gf = 0;gf < num_metric_gfs;gf++) { in_gf = metric_gfs_list[gf]; out_gf = metric_gfs_face_list[gf]; for (int i2 = 2;i2 < Nxx_plus_2NGHOSTS2-1;i2++) { for (int i1 = 2;i1 < Nxx_plus_2NGHOSTS1-1;i1++) { for (int i0 = 2;i0 < Nxx_plus_2NGHOSTS0-1;i0++) { Qm2 = auxevol_gfs[IDX4S(in_gf,i0-2*kronecker_delta[flux_dirn][0],i1-2*kronecker_delta[flux_dirn][1],i2-2*kronecker_delta[flux_dirn][2])]; Qm1 = auxevol_gfs[IDX4S(in_gf,i0-kronecker_delta[flux_dirn][0],i1-kronecker_delta[flux_dirn][1],i2-kronecker_delta[flux_dirn][2])]; Qp0 = auxevol_gfs[IDX4S(in_gf,i0,i1,i2)]; Qp1 = auxevol_gfs[IDX4S(in_gf,i0+kronecker_delta[flux_dirn][0],i1+kronecker_delta[flux_dirn][1],i2+kronecker_delta[flux_dirn][2])]; auxevol_gfs[IDX4S(out_gf,i0,i1,i2)] = COMPUTE_FCVAL(Qm2,Qm1,Qp0,Qp1); } } } } """, rel_path_to_Cparams=os.path.join("../")) with open(os.path.join(Ccodesdir,"interpolate_metric_gfs_to_cell_faces.h"),"a") as file: file.write(interp_Cfunc)
def generate_C_code_for_Stilde_flux( out_dir, inputs_provided=False, alpha_face=None, gamma_faceDD=None, beta_faceU=None, Valenciav_rU=None, B_rU=None, Valenciav_lU=None, B_lU=None, sqrt4pi=None, outCparams="outCverbose=False,CSE_sorting=none", write_cmax_cmin=False): if not inputs_provided: # We will pass values of the gridfunction on the cell faces into the function. This requires us # to declare them as C parameters in NRPy+. We will denote this with the _face infix/suffix. alpha_face = gri.register_gridfunctions("AUXEVOL", "alpha_face") gamma_faceDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "gamma_faceDD", "sym01") beta_faceU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "beta_faceU") # We'll need some more gridfunctions, now, to represent the reconstructions of BU and ValenciavU # on the right and left faces Valenciav_rU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_rU", DIM=3) B_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_rU", DIM=3) Valenciav_lU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_lU", DIM=3) B_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_lU", DIM=3) sqrt4pi = par.Cparameters("REAL", thismodule, "sqrt4pi", "sqrt(4.0*M_PI)") Memory_Read = write_C_Code_to_read_memory(write_cmax_cmin) Memory_Write = write_C_code_to_write_results(write_cmax_cmin) if write_cmax_cmin: # In the staggered case, we will also want to output cmax and cmin # If we want to write cmax and cmin, we will need to be able to change auxevol_gfs: input_params_for_Stilde_flux = "const paramstruct *params,REAL *auxevol_gfs,REAL *rhs_gfs" else: input_params_for_Stilde_flux = "const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs" for flux_dirn in range(3): calculate_Stilde_flux(flux_dirn,alpha_face,gamma_faceDD,beta_faceU,\ Valenciav_rU,B_rU,Valenciav_lU,B_lU,sqrt4pi) Stilde_flux_to_print = [ Stilde_fluxD[0], Stilde_fluxD[1], Stilde_fluxD[2] ] Stilde_flux_names = ["Stilde_fluxD0", "Stilde_fluxD1", "Stilde_fluxD2"] if write_cmax_cmin: Stilde_flux_to_print = Stilde_flux_to_print + [ chsp.cmax, chsp.cmin ] Stilde_flux_names = Stilde_flux_names + ["cmax", "cmin"] desc = "Compute the flux term of all 3 components of tilde{S}_i on the right face in the " + str( flux_dirn) + "direction." name = "calculate_Stilde_flux_D" + str(flux_dirn) Ccode_function = outCfunction( outfile = "returnstring", desc=desc, name=name, params = input_params_for_Stilde_flux, body = Memory_Read \ +outputC(Stilde_flux_to_print,Stilde_flux_names,"returnstring",params=outCparams).replace("IDX4","IDX4S")\ +Memory_Write[flux_dirn], loopopts ="InteriorPoints", rel_path_for_Cparams=os.path.join("../")).replace("NGHOSTS+Nxx0","NGHOSTS+Nxx0+1").replace("NGHOSTS+Nxx1","NGHOSTS+Nxx1+1").replace("NGHOSTS+Nxx2","NGHOSTS+Nxx2+1") with open(os.path.join(out_dir, name + ".h"), "w") as file: file.write(Ccode_function)
def 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 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 generate_Afield_flux_function_files(out_dir,subdir,alpha_face,gamma_faceDD,beta_faceU,\ Valenciav_rU,B_rU,Valenciav_lU,B_lU,inputs_provided=True): if not inputs_provided: # declare all variables alpha_face = sp.symbols(alpha_face) beta_faceU = ixp.declarerank1("beta_faceU") gamma_faceDD = ixp.declarerank2("gamma_faceDD","sym01") Valenciav_rU = ixp.declarerank1("Valenciav_rU") B_rU = ixp.declarerank1("B_rU") Valenciav_lU = ixp.declarerank1("Valenciav_lU") B_lU = ixp.declarerank1("B_lU") 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"] for flux_dirn in range(3): calculate_E_i_flux(flux_dirn,alpha_face,gamma_faceDD,beta_faceU,\ Valenciav_rU,B_rU,Valenciav_lU,B_lU) E_field_to_print = [\ sp.Rational(1,4)*E_fluxD[(flux_dirn+1)%3], sp.Rational(1,4)*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("../"))
def ID_scalarfield_ADM_quantities(Ccodesdir=".", new_way=False): includes = ["NRPy_basic_defines.h", "NRPy_function_prototypes.h"] desc = """(c) 2021 Leo Werneck This function takes as input either (x,y,z) or (r,th,ph) and outputs all ADM quantities in the Cartesian or Spherical basis, respectively. """ c_type = "void" name = "ID_scalarfield_ADM_quantities" params = """const REAL xyz_or_rthph[3],const ID_inputs other_inputs, REAL *restrict gammaDD00,REAL *restrict gammaDD01,REAL *restrict gammaDD02, REAL *restrict gammaDD11,REAL *restrict gammaDD12,REAL *restrict gammaDD22, REAL *restrict KDD00,REAL *restrict KDD01,REAL *restrict KDD02, REAL *restrict KDD11,REAL *restrict KDD12,REAL *restrict KDD22, REAL *restrict alpha, REAL *restrict betaU0,REAL *restrict betaU1,REAL *restrict betaU2, REAL *restrict BU0,REAL *restrict BU1,REAL *restrict BU2""" body = """ const REAL r = xyz_or_rthph[0]; const REAL th = xyz_or_rthph[1]; const REAL ph = xyz_or_rthph[2]; REAL sf_star,psi4_star,alpha_star; scalarfield_interpolate_1D(r, other_inputs.interp_stencil_size, other_inputs.numlines_in_file, other_inputs.r_arr, other_inputs.sf_arr, other_inputs.psi4_arr, other_inputs.alpha_arr, &sf_star,&psi4_star,&alpha_star); // Update alpha *alpha = alpha_star; // gamma_{rr} = psi^4 *gammaDD00 = psi4_star; // gamma_{thth} = psi^4 r^2 *gammaDD11 = psi4_star*r*r; // gamma_{phph} = psi^4 r^2 sin^2(th) *gammaDD22 = psi4_star*r*r*sin(th)*sin(th); // All other quantities ARE ZERO: *gammaDD01 = 0.0; *gammaDD02 = 0.0; /**/ *gammaDD12 = 0.0; *KDD00 = 0.0; *KDD01 = 0.0; *KDD02 = 0.0; /**/ *KDD11 = 0.0; *KDD12 = 0.0; /**/ *KDD22 = 0.0; *betaU0 = 0.0; *betaU1 = 0.0; *betaU2 = 0.0; *BU0 = 0.0; *BU1 = 0.0; *BU2 = 0.0; """ if new_way == True: outC.add_to_Cfunction_dict(includes=includes, desc=desc, c_type=c_type, name=name, params=params, body=body, enableCparameters=False) else: outfile = os.path.join(Ccodesdir, "ID_scalarfield_ADM_quantities.h") outC.outCfunction(outfile=outfile, includes=None, desc=desc, c_type=c_type, name=name, params=params, body=body, enableCparameters=False)