def PlaneWave(CoordSystem="Cartesian", default_time=0, default_k0=1, default_k1=1, default_k2=1): # Step 1: Set parameters defined in other modules DIM = par.parval_from_str("grid::DIM") # Step 2: Set up Cartesian coordinates in terms of the native CoordSystem we have chosen. # E.g., if CoordSystem="Cartesian", then xxCart = [xx[0],xx[1],xx[2]] # or if CoordSystem="Spherical", then xxCart = [xx[0]*sp.sin(xx[1])*sp.cos(xx[2]), # xx[0]*sp.sin(xx[1])*sp.sin(xx[2]), # xx[0]*sp.cos(xx[1])] par.set_parval_from_str("reference_metric::CoordSystem", CoordSystem) rfm.reference_metric() xxCart = rfm.xxCart # Step 3: Declare free parameters intrinsic to these initial data time = par.Cparameters("REAL", thismodule, "time", default_time) kk = par.Cparameters("REAL", thismodule, ["kk0", "kk1", "kk2"], [default_k0, default_k1, default_k2]) # Step 4: Normalize the k vector kk_norm_factor = sp.sqrt(kk[0]**2 + kk[1]**2 + kk[2]**2) # Step 5: Compute k_norm.x dot_product = sp.sympify(0) for i in range(DIM): dot_product += kk[i] * xxCart[i] dot_product /= kk_norm_factor # Step 6: Set initial data for uu and vv, where vv_ID = \partial_t uu_ID. global uu_ID, vv_ID uu_ID = sp.sin(dot_product - wavespeed * time) + 2 vv_ID = sp.diff(uu_ID, time)
def SphericalGaussian(CoordSystem="Cartesian", default_time=0, default_sigma=3): # Step 1: Set parameters for the wave DIM = par.parval_from_str("grid::DIM") # Step 2: Set up Cartesian coordinates in terms of the native CoordSystem we have chosen. # E.g., if CoordSystem="Cartesian", then xxCart = [xx[0],xx[1],xx[2]] # or if CoordSystem="Spherical", then xxCart = [xx[0]*sp.sin(xx[1])*sp.cos(xx[2]), # xx[0]*sp.sin(xx[1])*sp.sin(xx[2]), # xx[0]*sp.cos(xx[1])] par.set_parval_from_str("reference_metric::CoordSystem", CoordSystem) rfm.reference_metric() # Must call this function to specify rfm.xxCart xxCart = rfm.xxCart # Step 3: Declare free parameters intrinsic to these initial data time = par.Cparameters("REAL", thismodule, "time", default_time) sigma = par.Cparameters("REAL", thismodule, "sigma", default_sigma) # Step 4: Compute r r = sp.sympify(0) for i in range(DIM): r += xxCart[i]**2 r = sp.sqrt(r) # Step 5: Set initial data for uu and vv, where vv_ID = \partial_t uu_ID. global uu_ID, vv_ID # uu_ID = (r - wavespeed*time)/r * sp.exp(- (r - wavespeed*time)**2 / (2*sigma**2) ) uu_ID = (+(r - wavespeed * time) / r * sp.exp(-(r - wavespeed * time)**2 / (2 * sigma**2)) + (r + wavespeed * time) / r * sp.exp(-(r + wavespeed * time)**2 / (2 * sigma**2))) vv_ID = sp.diff(uu_ID, time)
def u4U_in_terms_of_ValenciavU__rescale_ValenciavU_by_applying_speed_limit( alpha, betaU, gammaDD, ValenciavU): # Inputs: Metric lapse alpha, shift betaU, 3-metric gammaDD, Valencia 3-velocity ValenciavU # Outputs (as globals): u4U_ito_ValenciavU, rescaledValenciavU # R = gamma_{ij} v^i v^j R = sp.sympify(0) for i in range(3): for j in range(3): R += gammaDD[i][j] * ValenciavU[i] * ValenciavU[j] thismodule = __name__ # The default value isn't terribly important here, since we can overwrite in the main C code GAMMA_SPEED_LIMIT = par.Cparameters("REAL", thismodule, "GAMMA_SPEED_LIMIT", 10.0) # Default value based on # IllinoisGRMHD. # GiRaFFE default = 2000.0 Rmax = 1 - 1 / (GAMMA_SPEED_LIMIT * GAMMA_SPEED_LIMIT) # Now, we set Rstar = min(Rmax,R): # If R < Rmax, then Rstar = 0.5*(Rmax+R-Rmax+R) = R # If R >= Rmax, then Rstar = 0.5*(Rmax+R+Rmax-R) = Rmax Rstar = sp.Rational(1, 2) * (Rmax + R - nrpyAbs(Rmax - R)) # We add TINYDOUBLE to R below to avoid a 0/0, which occurs when # ValenciavU == 0 for all Valencia 3-velocity components. # "Those tiny *doubles* make me warm all over # with a feeling that I'm gonna love you till the end of time." # - Adapted from Connie Francis' "Tiny Bubbles" TINYDOUBLE = par.Cparameters("#define", thismodule, "TINYDOUBLE", 1e-100) # The rescaled (speed-limited) Valencia 3-velocity # is given by, v_{(n)}^i = sqrt{Rstar/R} v^i global rescaledValenciavU rescaledValenciavU = ixp.zerorank1() for i in range(3): # If R == 0, then Rstar == 0, so sqrt( Rstar/(R+TINYDOUBLE) )=sqrt(0/1e-100) = 0 # If your velocities are of order 1e-100 and this is physically # meaningful, there must be something wrong with your unit conversion. rescaledValenciavU[i] = ValenciavU[i] * sp.sqrt(Rstar / (R + TINYDOUBLE)) # Finally compute u^mu in terms of Valenciav^i # u^0 = 1/(alpha-sqrt(1-R^*)) global u4U_ito_ValenciavU u4U_ito_ValenciavU = ixp.zerorank1(DIM=4) u4U_ito_ValenciavU[0] = 1 / (alpha * sp.sqrt(1 - Rstar)) # u^i = u^0 ( alpha v^i_{(n)} - beta^i ), where v^i_{(n)} is the Valencia 3-velocity for i in range(3): u4U_ito_ValenciavU[i + 1] = u4U_ito_ValenciavU[0] * ( alpha * rescaledValenciavU[i] - betaU[i])
def BrillLindquist(ComputeADMGlobalsOnly=False): global Cartxyz, gammaCartDD, KCartDD, alphaCart, betaCartU, BCartU # Step 2: Setting up Brill-Lindquist initial data thismodule = "Brill-Lindquist" BH1_posn_x, BH1_posn_y, BH1_posn_z = par.Cparameters( "REAL", thismodule, ["BH1_posn_x", "BH1_posn_y", "BH1_posn_z"]) BH1_mass = par.Cparameters("REAL", thismodule, ["BH1_mass"]) BH2_posn_x, BH2_posn_y, BH2_posn_z = par.Cparameters( "REAL", thismodule, ["BH2_posn_x", "BH2_posn_y", "BH2_posn_z"]) BH2_mass = par.Cparameters("REAL", thismodule, ["BH2_mass"]) # Step 2.a: Set spatial dimension (must be 3 for BSSN) DIM = 3 par.set_parval_from_str("grid::DIM", DIM) global Cartxyz, gammaCartDD, KCartDD, alphaCart, betaCartU, BCartU Cartxyz = ixp.declarerank1("Cartxyz") # Step 2.b: Set psi, the conformal factor: psi = sp.sympify(1) psi += BH1_mass / (2 * sp.sqrt((Cartxyz[0] - BH1_posn_x)**2 + (Cartxyz[1] - BH1_posn_y)**2 + (Cartxyz[2] - BH1_posn_z)**2)) psi += BH2_mass / (2 * sp.sqrt((Cartxyz[0] - BH2_posn_x)**2 + (Cartxyz[1] - BH2_posn_y)**2 + (Cartxyz[2] - BH2_posn_z)**2)) # Step 2.c: Set all needed ADM variables in Cartesian coordinates gammaCartDD = ixp.zerorank2() KCartDD = ixp.zerorank2() # K_{ij} = 0 for these initial data for i in range(DIM): gammaCartDD[i][i] = psi**4 alphaCart = 1 / psi**2 betaCartU = ixp.zerorank1( ) # We generally choose \beta^i = 0 for these initial data BCartU = ixp.zerorank1( ) # We generally choose B^i = 0 for these initial data if ComputeADMGlobalsOnly == True: return cf,hDD,lambdaU,aDD,trK,alpha,vetU,betU = \ AtoB.Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear("Cartesian",Cartxyz, gammaCartDD,KCartDD,alphaCart,betaCartU,BCartU) global returnfunction returnfunction = bIDf.BSSN_ID_function_string(cf, hDD, lambdaU, aDD, trK, alpha, vetU, betU)
def MaxwellCartesian_ID(): DIM = par.parval_from_str("grid::DIM") x, y, z = gri.register_gridfunctions("AUX", ["x", "y", "z"]) gammaDD = ixp.register_gridfunctions_for_single_rank2("AUX","gammaDD", "sym01") # The AUX or EVOL designation is *not* # used in diagnostic modules. # Step 1: Declare free parameters intrinsic to these initial data amp,lam = par.Cparameters("REAL",__name__,["amp","lam"], [1.0,1.0]) # __name__ = "MaxwellCartesian_ID", this module's name # Step 2: Set the initial data system = par.parval_from_str("System_to_use") if system == "System_I" or system == "System_II": global AidD,EidD,psi_ID AidD = ixp.zerorank1() EidD = ixp.zerorank1() EidU = ixp.zerorank1() # Set the coordinate transformations: radial = sp.sqrt(x*x + y*y + z*z) polar = sp.atan2(sp.sqrt(x*x + y*y),z) EU_phi = 8*amp*radial*sp.sin(polar)*lam*lam*sp.exp(-lam*radial*radial) EidU[0] = -(y * EU_phi)/sp.sqrt(x*x + y*y) EidU[1] = (x * EU_phi)/sp.sqrt(x*x + y*y) # The z component (2)is zero. for i in range(DIM): for j in range(DIM): EidD[i] += gammaDD[i][j] * EidU[j] psi_ID = sp.sympify(0) if system == "System_II": global Gamma_ID Gamma_ID = sp.sympify(0) else: print("Invalid choice of system: System_to_use must be either System_I or System_II")
def BSSN_source_terms_for_BSSN_constraints(custom_T4UU=None): global sourceterm_H, sourceterm_MU # Step 4.a: Call BSSN_source_terms_ito_T4UU to get SDD, SD, S, & rho if custom_T4UU == "unrescaled BSSN source terms already given": SDD = ixp.declarerank2("SDD", "sym01") SD = ixp.declarerank1("SD") S = sp.symbols("S", real=True) rho = sp.symbols("rho", real=True) else: SDD, SD, S, rho = stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars( "BSSN", custom_T4UU) PI = par.Cparameters("REAL", thismodule, ["PI"], "3.14159265358979323846264338327950288") # Step 4.b: Add source term to the Hamiltonian constraint H sourceterm_H = -16 * PI * rho # Step 4.c: Add source term to the momentum constraint M^i # Step 4.c.i: Compute gammaUU in terms of BSSN quantities import BSSN.ADM_in_terms_of_BSSN as AitoB AitoB.ADM_in_terms_of_BSSN() # Provides gammaUU # Step 4.c.ii: Raise S_i SU = ixp.zerorank1() for i in range(3): for j in range(3): SU[i] += AitoB.gammaUU[i][j] * SD[j] # Step 4.c.iii: Add source term to momentum constraint & rescale: sourceterm_MU = ixp.zerorank1() for i in range(3): sourceterm_MU[i] = -8 * PI * SU[i] / rfm.ReU[i]
def TOV_ADM_T4UUmunu(ComputeADMT4UUmunuGlobalsOnly=False): global Sph_r_th_ph, r, th, ph, gammaSphDD, KSphDD, alphaSph, betaSphU, BSphU, T4UU # All gridfunctions will be written in terms of spherical coordinates (r, th, ph): r, th, ph = sp.symbols('r th ph', real=True) thismodule = "TOV" DIM = 3 par.set_parval_from_str("grid::DIM", DIM) # Input parameters read in from the TOV data file: rbar, expnu, exp4phi, P, rho = par.Cparameters( "REAL", thismodule, ["rbar", "expnu", "exp4phi", "P", "rho"], [1e300, 1e300, 1e300, 1e300, 1e300]) # Must be read from TOV data # file; set to crazy values to ensure this # Step 4.2: Construct ADM quantities: # *** The physical spatial metric in spherical basis *** # In isotropic coordinates, # gamma_{ij} = e^{4 phi} eta_{ij}, # where eta is the flat-space 3-metric in spherical coordinates gammaSphDD = ixp.zerorank2() gammaSphDD[0][0] = exp4phi gammaSphDD[1][1] = exp4phi * rbar**2 gammaSphDD[2][2] = exp4phi * rbar**2 * sp.sin(th)**2 # *** The extrinsic curvature in spherical basis *** # K_{ij} = 0 for the TOV solution KSphDD = ixp.zerorank2() # *** The lapse and shift in spherical basis *** # alpha = exp^{nu/2} for the TOV solution # \beta^i = 0 for the TOV solution alphaSph = sp.sqrt(expnu) betaSphU = ixp.zerorank1() BSphU = ixp.zerorank1() # Step 4.3: Construct T^{mu nu}: T4UU = ixp.zerorank2(DIM=4) # T^tt = e^(-nu) * rho T4UU[0][0] = rho / expnu # T^{ii} = P / gamma_{ii} for i in range(3): T4UU[i + 1][i + 1] = P / gammaSphDD[i][i] if ComputeADMT4UUmunuGlobalsOnly == True: return Sph_r_th_ph = [r, th, ph] cf, hDD, lambdaU, aDD, trK, alpha, vetU, betU = \ AtoB.Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear("Spherical", Sph_r_th_ph, gammaSphDD, KSphDD, alphaSph, betaSphU, BSphU) sDD, sD, S, rho = \ AtoB.Convert_Spherical_or_Cartesian_T4UUmunu_to_BSSN_curvilinear("Spherical", Sph_r_th_ph, T4UU) global returnfunction returnfunction = bIDf.BSSN_ID_function_string(cf, hDD, lambdaU, aDD, trK, alpha, vetU, betU)
def compute_u0_noif(gammaDD, alpha, ValenciavU): # Inputs: Metric gammaDD, lapse alpha, Valencia 3-velocity ValenciavU # Outputs: u^0, speed-limited ValenciavU # R = gamma_{ij} v^i v^j R = par.Cparameters("#define", thismodule, "TINYDOUBLE", 1e-100) # We initialize R to TINYDOUBLE instead of 0 to avoid a 0/0 below, # in the case that ValenciavU == 0 for all Valencia 3-velocities. # "Those tiny *doubles* make me warm all over # with a feeling that I'm gonna love you till the end of time." # - Adapted from Connie Francis' "Tiny Bubbles" for i in range(DIM): for j in range(DIM): R += gammaDD[i][j] * ValenciavU[i] * ValenciavU[j] # The default value isn't terribly important here, since we'll overwrite in the main C code GAMMA_SPEED_LIMIT = par.Cparameters("REAL", thismodule, "GAMMA_SPEED_LIMIT", 10.0) # Default value based on # IllinoisGRMHD. # GiRaFFE default = 2000.0 # Rmax = 1 - 1/Gamma_{\max}^2 Rmax = 1 - 1 / (GAMMA_SPEED_LIMIT * GAMMA_SPEED_LIMIT) # Now, we set Rmax = min(Rmax,R): # If Rmax>R, then Rmax = 0.5*(Rmax+R-Rmax+R) = R # If R>Rmax, then Rmax = 0.5*(Rmax+R+Rmax-R) = Rmax # If R==TINYDOUBLE, then Rmax = 0.5*(Rmax-Rmax) = 0, since, e.g., 10 +/- 1e-100 = 10 exactly in double precision Rmax = sp.Rational(1, 2) * (Rmax + R - sp.Abs(Rmax - R)) # With our rescaled Rmax, v^i = sqrt{Rmax/R} v^i global rescaledValenciavU, rescaledu0 rescaledValenciavU = ixp.zerorank1() for i in range(DIM): # If R == TINYDOUBLE, then Rmax=0,R=1e-100, and we get Rmax/R=0. # If your velocities are of order 1e-100 and this is physically # meaningful, there must be something wrong with your unit conversion. rescaledValenciavU[i] = ValenciavU[i] * sp.sqrt(Rmax / R) # We stick with the "rescaled" version since we redefine Rmax as detailed above # u^0 = 1/(alpha-sqrt(1-R_{\max})) rescaledu0 = 1 / (alpha * sp.sqrt(1 - Rmax))
def BSSN_source_terms_for_BSSN_RHSs(custom_T4UU=None): global sourceterm_trK_rhs, sourceterm_a_rhsDD, sourceterm_lambda_rhsU, sourceterm_Lambdabar_rhsU # Step 3.a: Call BSSN_source_terms_ito_T4UU to get SDD, SD, S, & rho if custom_T4UU == "unrescaled BSSN source terms already given": SDD = ixp.declarerank2("SDD", "sym01") SD = ixp.declarerank1("SD") S = sp.symbols("S", real=True) rho = sp.symbols("rho", real=True) else: SDD, SD, S, rho = stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars( "BSSN", custom_T4UU) PI = par.Cparameters("REAL", thismodule, ["PI"], "3.14159265358979323846264338327950288") alpha = sp.symbols("alpha", real=True) # Step 3.b: trK_rhs sourceterm_trK_rhs = 4 * PI * alpha * (rho + S) # Step 3.c: Abar_rhsDD: # Step 3.c.i: Compute trace-free part of S_{ij}: import BSSN.BSSN_quantities as Bq Bq.BSSN_basic_tensors() # Sets gammabarDD gammabarUU, dummydet = ixp.symm_matrix_inverter3x3( Bq.gammabarDD) # Set gammabarUU tracefree_SDD = ixp.zerorank2() for i in range(3): for j in range(3): tracefree_SDD[i][j] = SDD[i][j] for i in range(3): for j in range(3): for k in range(3): for m in range(3): tracefree_SDD[i][j] += -sp.Rational(1, 3) * Bq.gammabarDD[ i][j] * gammabarUU[k][m] * SDD[k][m] # Step 3.c.ii: Define exp_m4phi = e^{-4 phi} Bq.phi_and_derivs() # Step 3.c.iii: Evaluate stress-energy part of AbarDD's RHS sourceterm_a_rhsDD = ixp.zerorank2() for i in range(3): for j in range(3): Abar_rhsDDij = -8 * PI * alpha * Bq.exp_m4phi * tracefree_SDD[i][j] sourceterm_a_rhsDD[i][j] = Abar_rhsDDij / rfm.ReDD[i][j] # Step 3.d: Stress-energy part of Lambdabar_rhsU = stressenergy_Lambdabar_rhsU sourceterm_Lambdabar_rhsU = ixp.zerorank1() for i in range(3): for j in range(3): sourceterm_Lambdabar_rhsU[ i] += -16 * PI * alpha * gammabarUU[i][j] * SD[j] sourceterm_lambda_rhsU = ixp.zerorank1() for i in range(3): sourceterm_lambda_rhsU[i] = sourceterm_Lambdabar_rhsU[i] / rfm.ReU[i]
def 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 InitialData_PlaneWave(): # Step 1: Set parameters defined in other modules wavespeed = swrhs.wavespeed DIM = par.parval_from_str("grid::DIM") xx = gri.xx # Step 2: Declare free parameters intrinsic to these initial data time = par.Cparameters("REAL", thismodule, "time") kk = par.Cparameters("REAL", thismodule, ["kk0", "kk1", "kk2"]) # Step 3: Normalize the k vector kk_norm = sp.sqrt(kk[0]**2 + kk[1]**2 + kk[2]**2) # Step 4: Compute k.x dot_product = sp.sympify(0) for i in range(DIM): dot_product += xx[i] * kk[i] dot_product /= kk_norm # Step 5: Set initial data for uu and vv, where vv_ID = \partial_t uu_ID. global uu_ID, vv_ID uu_ID = sp.sin(dot_product - wavespeed * time) + 2 vv_ID = sp.diff(uu_ID, time)
def GiRaFFEfood_NRPy_1D_tests(ID_type="DegenAlfvenWave", stagger_enable=False): global AD, ValenciaVU AD = ixp.zerorank1() if ID_type == "DegenAlfvenWave": mu_DAW = par.Cparameters("REAL", thismodule, ["mu_DAW"], -0.5) # The wave speed AD = Axyz_func(Ax_DAW, Ay_DAW, Az_DAW, stagger_enable, gammamu=sp.sympify(1) / sp.sqrt(sp.sympify(1) - mu_AW**2)) ValenciaVU = ValenciavU_DAW(mu_DAW=mu_DAW, gammamu=gammamu) elif ID_type == "FastWave": AD = Axyz_func(Ax_FW, Ay_FW, Az_FW, stagger_enable) ValenciaVU = ValenciavU_FW()
def add_to_Cfunction_dict__AD_gauge_term_psi6Phi_fin_diff(includes=None): xi_damping = par.Cparameters("REAL",thismodule,"xi_damping",0.1) GRFFE.compute_psi6Phi_rhs_damping_term(alpha,psi6Phi,xi_damping) AevolParen_dD = ixp.declarerank1("AevolParen_dD",DIM=3) PhievolParenU_dD = ixp.declarerank2("PhievolParenU_dD","nosym",DIM=3) A_rhsD = ixp.zerorank1() psi6Phi_rhs = GRFFE.psi6Phi_damping for i in range(3): A_rhsD[i] += -AevolParen_dD[i] psi6Phi_rhs += -PhievolParenU_dD[i][i] # Add Kreiss-Oliger dissipation to the GRFFE RHSs: # psi6Phi_dKOD = ixp.declarerank1("psi6Phi_dKOD") # AD_dKOD = ixp.declarerank2("AD_dKOD","nosym") # for i in range(3): # psi6Phi_rhs += diss_strength*psi6Phi_dKOD[i]*rfm.ReU[i] # ReU[i] = 1/scalefactor_orthog_funcform[i] # for j in range(3): # A_rhsD[j] += diss_strength*AD_dKOD[j][i]*rfm.ReU[i] # ReU[i] = 1/scalefactor_orthog_funcform[i] RHSs_to_print = [ lhrh(lhs=gri.gfaccess("rhs_gfs","AD0"),rhs=A_rhsD[0]), lhrh(lhs=gri.gfaccess("rhs_gfs","AD1"),rhs=A_rhsD[1]), lhrh(lhs=gri.gfaccess("rhs_gfs","AD2"),rhs=A_rhsD[2]), lhrh(lhs=gri.gfaccess("rhs_gfs","psi6Phi"),rhs=psi6Phi_rhs), ] desc = "Calculate AD gauge term and psi6Phi RHSs" name = "calculate_AD_gauge_psi6Phi_RHSs" params ="const paramstruct *params,const REAL *in_gfs,const REAL *auxevol_gfs,REAL *rhs_gfs" body = fin.FD_outputC("returnstring",RHSs_to_print,params=outCparams) loopopts ="InteriorPoints" add_to_Cfunction_dict( includes=includes, desc=desc, name=name, params=params, body=body, loopopts=loopopts) outC_function_dict[name] = outC_function_dict[name].replace("= NGHOSTS","= NGHOSTS_A2B").replace("NGHOSTS+Nxx0","Nxx_plus_2NGHOSTS0-NGHOSTS_A2B").replace("NGHOSTS+Nxx1","Nxx_plus_2NGHOSTS1-NGHOSTS_A2B").replace("NGHOSTS+Nxx2","Nxx_plus_2NGHOSTS2-NGHOSTS_A2B")
def calculate_Stilde_flux(flux_dirn,inputs_provided=True,alpha_face=None,gamma_faceDD=None,beta_faceU=None,\ Valenciav_rU=None,B_rU=None,Valenciav_lU=None,B_lU=None,sqrt4pi=None): if not inputs_provided: # We will pass values of the gridfunction on the cell faces into the function. This requires us # to declare them as C parameters in NRPy+. We will denote this with the _face infix/suffix. alpha_face = gri.register_gridfunctions("AUXEVOL", "alpha_face") gamma_faceDD = ixp.register_gridfunctions_for_single_rank2( "AUXEVOL", "gamma_faceDD", "sym01") beta_faceU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "beta_faceU") # We'll need some more gridfunctions, now, to represent the reconstructions of BU and ValenciavU # on the right and left faces Valenciav_rU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_rU", DIM=3) B_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_rU", DIM=3) Valenciav_lU = ixp.register_gridfunctions_for_single_rank1( "AUXEVOL", "Valenciav_lU", DIM=3) B_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "B_lU", DIM=3) sqrt4pi = par.Cparameters("REAL", thismodule, "sqrt4pi", "sqrt(4.0*M_PI)") find_cmax_cmin(flux_dirn, gamma_faceDD, beta_faceU, alpha_face) global Stilde_fluxD Stilde_fluxD = ixp.zerorank3() for mom_comp in range(3): calculate_GRFFE_Tmunu_and_contractions(flux_dirn, mom_comp, gamma_faceDD,beta_faceU,alpha_face,\ Valenciav_rU,B_rU,sqrt4pi) Fr = F Ur = U calculate_GRFFE_Tmunu_and_contractions(flux_dirn, mom_comp, gamma_faceDD,beta_faceU,alpha_face,\ Valenciav_lU,B_lU,sqrt4pi) Fl = F Ul = U Stilde_fluxD[mom_comp] = HLLE_solver(cmax, cmin, Fr, Fl, Ur, Ul)
# License: BSD 2-Clause # COMPLETE DOCUMENTATION (JUPYTER NOTEBOOKS): # START PAGE (start here!): ../NRPy+_Tutorial.ipynb # THIS MODULE: ../Tutorial-VacuumMaxwell_Flat_Cartesian_ID.ipynb # Step P1: Import needed NRPy+ core modules: import NRPy_param_funcs as par # NRPy+: Parameter interface # The name of this module ("CommonParams") is given by __name__: thismodule = __name__ # Parameters common to/needed by all VacuumMaxwell Python modules # Step P2: Define the C parameters amp, lam, time, and wavespeed. # These variables proper SymPy variables, so they can be # used in SymPy expressions. In the C code, it acts # just like a usual parameter, whose value is # specified in the parameter file. # amplitude amp = par.Cparameters("REAL", thismodule, "amp", default_vals=1.0) # lambda lam = par.Cparameters("REAL", thismodule, "lam", default_vals=1.0) time = par.Cparameters("REAL", thismodule, "time", default_vals=0.0) wavespeed = par.Cparameters("REAL", thismodule, "wavespeed", default_vals=1.0)
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); } """)
import GiRaFFE_NRPy.GiRaFFE_NRPy_C2P_P2C as C2P_P2C import GiRaFFE_NRPy.GiRaFFE_NRPy_Source_Terms as source thismodule = "GiRaFFE_NRPy_Main_Driver" CoordSystem = "Cartesian" outCparams = "outCverbose=False,CSE_sorting=none" par.set_parval_from_str("reference_metric::CoordSystem", CoordSystem) rfm.reference_metric( ) # Create ReU, ReDD needed for rescaling B-L initial data, generating BSSN RHSs, etc. # Default Kreiss-Oliger dissipation strength default_KO_strength = 0.1 diss_strength = par.Cparameters("REAL", thismodule, "diss_strength", default_KO_strength) 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")
def BSSN_RHSs__generate_symbolic_expressions(): ###################################### # START: GENERATE SYMBOLIC EXPRESSIONS print("Generating symbolic expressions for BSSN RHSs...") start = time.time() # Enable rfm_precompute infrastructure, which results in # BSSN RHSs that are free of transcendental functions, # even in curvilinear coordinates, so long as # ConformalFactor is set to "W" (default). par.set_parval_from_str("reference_metric::enable_rfm_precompute","True") par.set_parval_from_str("reference_metric::rfm_precompute_Ccode_outdir",os.path.join(outdir,"rfm_files/")) # Evaluate BSSN + BSSN gauge RHSs with rfm_precompute enabled: import BSSN.BSSN_quantities as Bq par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","True") rhs.BSSN_RHSs() if T4UU != None: import BSSN.BSSN_stress_energy_source_terms as Bsest Bsest.BSSN_source_terms_for_BSSN_RHSs(T4UU) rhs.trK_rhs += Bsest.sourceterm_trK_rhs for i in range(3): # Needed for Gamma-driving shift RHSs: rhs.Lambdabar_rhsU[i] += Bsest.sourceterm_Lambdabar_rhsU[i] # Needed for BSSN RHSs: rhs.lambda_rhsU[i] += Bsest.sourceterm_lambda_rhsU[i] for j in range(3): rhs.a_rhsDD[i][j] += Bsest.sourceterm_a_rhsDD[i][j] gaugerhs.BSSN_gauge_RHSs() # Add Kreiss-Oliger dissipation to the BSSN RHSs: thismodule = "KO_Dissipation" diss_strength = par.Cparameters("REAL", thismodule, "diss_strength", default_KO_strength) alpha_dKOD = ixp.declarerank1("alpha_dKOD") cf_dKOD = ixp.declarerank1("cf_dKOD") trK_dKOD = ixp.declarerank1("trK_dKOD") betU_dKOD = ixp.declarerank2("betU_dKOD","nosym") vetU_dKOD = ixp.declarerank2("vetU_dKOD","nosym") lambdaU_dKOD = ixp.declarerank2("lambdaU_dKOD","nosym") aDD_dKOD = ixp.declarerank3("aDD_dKOD","sym01") hDD_dKOD = ixp.declarerank3("hDD_dKOD","sym01") for k in range(3): gaugerhs.alpha_rhs += diss_strength*alpha_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.cf_rhs += diss_strength* cf_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.trK_rhs += diss_strength* trK_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] for i in range(3): if "2ndOrder" in ShiftCondition: gaugerhs.bet_rhsU[i] += diss_strength* betU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] gaugerhs.vet_rhsU[i] += diss_strength* vetU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.lambda_rhsU[i] += diss_strength*lambdaU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] for j in range(3): rhs.a_rhsDD[i][j] += diss_strength*aDD_dKOD[i][j][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] rhs.h_rhsDD[i][j] += diss_strength*hDD_dKOD[i][j][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k] # We use betaU as our upwinding control vector: Bq.BSSN_basic_tensors() betaU = Bq.betaU # Now that we are finished with all the rfm hatted # quantities in generic precomputed functional # form, let's restore them to their closed- # form expressions. par.set_parval_from_str("reference_metric::enable_rfm_precompute","False") # Reset to False to disable rfm_precompute. rfm.ref_metric__hatted_quantities() par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","False") end = time.time() print("(BENCH) Finished BSSN RHS symbolic expressions in "+str(end-start)+" seconds.") # END: GENERATE SYMBOLIC EXPRESSIONS ###################################### BSSN_RHSs_SymbExpressions = [lhrh(lhs=gri.gfaccess("rhs_gfs","aDD00"), rhs=rhs.a_rhsDD[0][0]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD01"), rhs=rhs.a_rhsDD[0][1]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD02"), rhs=rhs.a_rhsDD[0][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD11"), rhs=rhs.a_rhsDD[1][1]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD12"), rhs=rhs.a_rhsDD[1][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","aDD22"), rhs=rhs.a_rhsDD[2][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","alpha"), rhs=gaugerhs.alpha_rhs), lhrh(lhs=gri.gfaccess("rhs_gfs","betU0"), rhs=gaugerhs.bet_rhsU[0]), lhrh(lhs=gri.gfaccess("rhs_gfs","betU1"), rhs=gaugerhs.bet_rhsU[1]), lhrh(lhs=gri.gfaccess("rhs_gfs","betU2"), rhs=gaugerhs.bet_rhsU[2]), lhrh(lhs=gri.gfaccess("rhs_gfs","cf"), rhs=rhs.cf_rhs), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD00"), rhs=rhs.h_rhsDD[0][0]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD01") ,rhs=rhs.h_rhsDD[0][1]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD02"), rhs=rhs.h_rhsDD[0][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD11"), rhs=rhs.h_rhsDD[1][1]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD12"), rhs=rhs.h_rhsDD[1][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","hDD22"), rhs=rhs.h_rhsDD[2][2]), lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU0"),rhs=rhs.lambda_rhsU[0]), lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU1"),rhs=rhs.lambda_rhsU[1]), lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU2"),rhs=rhs.lambda_rhsU[2]), lhrh(lhs=gri.gfaccess("rhs_gfs","trK"), rhs=rhs.trK_rhs), lhrh(lhs=gri.gfaccess("rhs_gfs","vetU0"), rhs=gaugerhs.vet_rhsU[0]), lhrh(lhs=gri.gfaccess("rhs_gfs","vetU1"), rhs=gaugerhs.vet_rhsU[1]), lhrh(lhs=gri.gfaccess("rhs_gfs","vetU2"), rhs=gaugerhs.vet_rhsU[2]) ] return [betaU,BSSN_RHSs_SymbExpressions]
# # **Additional variables needed for spacetime evolution**: # # * Desired coordinate system # * Desired initial lapse $\alpha$ and shift $\beta^i$. We will choose our gauge conditions as $\alpha=1$ and $\beta^i=B^i=0$. $\alpha = \psi^{-2}$ will yield much better behavior, but the conformal factor $\psi$ depends on the desired *destination* coordinate system (which may not be spherical coordinates). # Step 1: Initialize core Python/NRPy+ modules import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends import NRPy_param_funcs as par # NRPy+: Parameter interface import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support import BSSN.ADM_Exact_Spherical_or_Cartesian_to_BSSNCurvilinear as AtoB thismodule = __name__ # Input parameters: M = par.Cparameters("REAL", thismodule, ["M"], [1.0]) # ComputeADMGlobalsOnly == True will only set up the ADM global quantities. # == False will perform the full ADM SphorCart->BSSN Curvi conversion def StaticTrumpet(ComputeADMGlobalsOnly = False): global Sph_r_th_ph,r,th,ph, gammaSphDD, KSphDD, alphaSph, betaSphU, BSphU # All gridfunctions will be written in terms of spherical coordinates (r, th, ph): r,th,ph = sp.symbols('r th ph', real=True) # Step 0: Set spatial dimension (must be 3 for BSSN) DIM = 3 par.set_parval_from_str("grid::DIM",DIM) # Step 1: Set psi, the conformal factor:
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("../"))
from outputC import nrpyAbs # NRPy+: Core C code output module import NRPy_param_funcs as par # NRPy+: parameter interface import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends thismodule = __name__ TINYDOUBLE = par.Cparameters("REAL", thismodule, "TINYDOUBLE", 1e-100) def min_noif(a, b): # Returns the minimum of a and b if a == sp.sympify(0): return sp.Rational(1, 2) * (b - nrpyAbs(b)) if b == sp.sympify(0): return sp.Rational(1, 2) * (a - nrpyAbs(a)) return sp.Rational(1, 2) * (a + b - nrpyAbs(a - b)) def max_noif(a, b): # Returns the maximum of a and b if a == sp.sympify(0): return sp.Rational(1, 2) * (b + nrpyAbs(b)) if b == sp.sympify(0): return sp.Rational(1, 2) * (a + nrpyAbs(a)) return sp.Rational(1, 2) * (a + b + nrpyAbs(a - b)) def coord_leq_bound(x, xstar): # Returns 1.0 if x <= xstar, 0.0 otherwise. # Requires appropriately defined TINYDOUBLE return min_noif(x - xstar - TINYDOUBLE, 0.0) / (x - xstar - TINYDOUBLE)
# grid.py: functions & parameters related to numerical grids: # functions: Automatic loop output, output C code needed for gridfunction memory I/O, gridfunction registration # Initialize globals related to the grid glb_gridfcs_list = [] glb_gridfc = namedtuple('gridfunction', 'gftype name') thismodule = __name__ par.initialize_param( par.glb_param("char", thismodule, "GridFuncMemAccess", "SENRlike")) par.initialize_param(par.glb_param("char", thismodule, "MemAllocStyle", "210")) par.initialize_param(par.glb_param("INT", thismodule, "DIM", 3)) par.initialize_param( par.glb_param("INT", thismodule, "Nx[DIM]", "SetAtCRuntime")) xx = par.Cparameters("REALARRAY", thismodule, ["xx0", "xx1", "xx2", "xx3"]) def variable_type(var): var_is_gf = False for gf in range(len(glb_gridfcs_list)): if str(var) == glb_gridfcs_list[gf].name: var_is_gf = True var_is_parameter = False for paramname in range(len(par.glb_params_list)): if str(var) == par.glb_params_list[paramname].parname: var_is_parameter = True if var_is_parameter and var_is_gf: print("Error: variable " + str(var) + " is registered both as a gridfunction and as a Cparameter.") exit(1)
# <a id='step2'></a> # ### Set the vector $A_k$ # The vector potential is given as # \begin{align} # A_x &= 0 \\ # A_y &= \left \{ \begin{array}{lll}\gamma_\mu x - 0.015 & \mbox{if} & x \leq -0.1/\gamma_\mu \\ # 1.15 \gamma_\mu x - 0.03g(x) & \mbox{if} & -0.1/\gamma_\mu \leq x \leq 0.1/\gamma_\mu \\ # 1.3 \gamma_\mu x - 0.015 & \mbox{if} & x \geq 0.1/\gamma_\mu \end{array} \right. , \\ # A_z &= y - \gamma_\mu (1-\mu)x . # \end{align} # First, however, we must set $$\gamma_\mu = (1-\mu^2)^{-1/2}$$ and $$g(x) = \cos (5\pi \gamma_\mu x)/\pi$$. # $$\label{step2}$$ mu_AW = par.Cparameters("REAL",thismodule,["mu_AW"], -0.5) # The wave speed M_PI = par.Cparameters("#define",thismodule,["M_PI"], "") def GiRaFFEfood_HO_1D_tests(): gammamu = 1/sp.sqrt(1-mu_AW**2) # We'll use reference_metric.py to define x and y x = rfm.xxCart[0] y = rfm.xxCart[1] g_AW = sp.cos(5*M_PI*gammamu*x)/M_PI # Now, we can define the vector potential. We will create three copies of this variable, because the potential is uniquely defined in three zones. Data for $x \leq -0.1/\gamma_\mu$ shall be referred to as "left", data for $-0.1/\gamma_\mu \leq x \leq 0.1/\gamma_\mu$ as "center", and data for $x \geq 0.1/\gamma_\mu$ as "right". # # Starting on the left,
# * The black hole mass, M # * The black hole spin parameter, a # Step P0: Load needed modules import sympy as sp import NRPy_param_funcs as par from outputC import * import indexedexp as ixp import reference_metric as rfm import BSSN.ADM_Exact_Spherical_or_Cartesian_to_BSSNCurvilinear as AtoB import BSSN.BSSN_ID_function_string as bIDf thismodule = __name__ # Input parameters: M, a, r0 = par.Cparameters("REAL", thismodule, ["M", "a", "r0"], [1.0, 0.9, 1.0]) # ComputeADMGlobalsOnly == True will only set up the ADM global quantities. # == False will perform the full ADM SphorCart->BSSN Curvi conversion def ShiftedKerrSchild(ComputeADMGlobalsOnly=False): global Sph_r_th_ph, r, th, ph, rho2, gammaSphDD, KSphDD, alphaSph, betaSphU, BSphU # All gridfunctions will be written in terms of spherical coordinates (r, th, ph): r, th, ph = sp.symbols('r th ph', real=True) DIM = 3 par.set_parval_from_str("grid::DIM", DIM) # Auxiliary variables: rho2 = sp.symbols('rho2', real=True)
import loop import reference_metric as rfm par.set_parval_from_str("reference_metric::CoordSystem", "Cartesian") rfm.reference_metric() # Step 1a: Set commonly used parameters. thismodule = "GiRaFFEfood_NRPy_Aligned_Rotator" # Set the spatial dimension parameter to 3. par.set_parval_from_str("grid::DIM", 3) DIM = par.parval_from_str("grid::DIM") B_p_aligned_rotator, R_NS_aligned_rotator = par.Cparameters( "REAL", thismodule, # B_p_aligned_rotator = the intensity of the magnetic field and # R_NS_aligned_rotator= "Neutron star" radius ["B_p_aligned_rotator", "R_NS_aligned_rotator"], [1e-5, 1.0]) # The angular velocity of the "neutron star" Omega_aligned_rotator = par.Cparameters("REAL", thismodule, "Omega_aligned_rotator", 1e3) # <a id='step2'></a> # # ### Step 2: Set the vectors A in Spherical coordinates # $$\label{step2}$$ # # \[Back to [top](#top)\] #
def BSSN_constraints(add_T4UUmunu_source_terms=False): # Step 1.a: Set spatial dimension (must be 3 for BSSN, as BSSN is # a 3+1-dimensional decomposition of the general # relativistic field equations) DIM = 3 # Step 1.b: Given the chosen coordinate system, set up # corresponding reference metric and needed # reference metric quantities # The following function call sets up the reference metric # and related quantities, including rescaling matrices ReDD, # ReU, and hatted quantities. rfm.reference_metric() # Step 2: Hamiltonian constraint. # First declare all needed variables Bq.declare_BSSN_gridfunctions_if_not_declared_already() # Sets trK Bq.BSSN_basic_tensors() # Sets AbarDD Bq.gammabar__inverse_and_derivs() # Sets gammabarUU Bq.AbarUU_AbarUD_trAbar_AbarDD_dD() # Sets AbarUU and AbarDD_dD Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU() # Sets RbarDD Bq.phi_and_derivs() # Sets phi_dBarD & phi_dBarDD ############################### ############################### # HAMILTONIAN CONSTRAINT ############################### ############################### # Term 1: 2/3 K^2 global H H = sp.Rational(2, 3) * Bq.trK**2 # Term 2: -A_{ij} A^{ij} for i in range(DIM): for j in range(DIM): H += -Bq.AbarDD[i][j] * Bq.AbarUU[i][j] # Term 3a: trace(Rbar) Rbartrace = sp.sympify(0) for i in range(DIM): for j in range(DIM): Rbartrace += Bq.gammabarUU[i][j] * Bq.RbarDD[i][j] # Term 3b: -8 \bar{\gamma}^{ij} \bar{D}_i \phi \bar{D}_j \phi = -8*phi_dBar_times_phi_dBar # Term 3c: -8 \bar{\gamma}^{ij} \bar{D}_i \bar{D}_j \phi = -8*phi_dBarDD_contraction phi_dBar_times_phi_dBar = sp.sympify(0) # Term 3b phi_dBarDD_contraction = sp.sympify(0) # Term 3c for i in range(DIM): for j in range(DIM): phi_dBar_times_phi_dBar += Bq.gammabarUU[i][j] * Bq.phi_dBarD[ i] * Bq.phi_dBarD[j] phi_dBarDD_contraction += Bq.gammabarUU[i][j] * Bq.phi_dBarDD[i][j] # Add Term 3: H += Bq.exp_m4phi * (Rbartrace - 8 * (phi_dBar_times_phi_dBar + phi_dBarDD_contraction)) if add_T4UUmunu_source_terms: M_PI = par.Cparameters("#define", thismodule, "M_PI", "") # M_PI is pi as defined in C BTmunu.define_BSSN_T4UUmunu_rescaled_source_terms() rho = BTmunu.rho H += -16 * M_PI * rho # FIXME: ADD T4UUmunu SOURCE TERMS TO MOMENTUM CONSTRAINT! # Step 3: M^i, the momentum constraint ############################### ############################### # MOMENTUM CONSTRAINT ############################### ############################### # SEE Tutorial-BSSN_constraints.ipynb for full documentation. global MU MU = ixp.zerorank1() # Term 2: 6 A^{ij} \partial_j \phi: for i in range(DIM): for j in range(DIM): MU[i] += 6 * Bq.AbarUU[i][j] * Bq.phi_dD[j] # Term 3: -2/3 \bar{\gamma}^{ij} K_{,j} trK_dD = ixp.declarerank1( "trK_dD") # Not defined in BSSN_RHSs; only trK_dupD is defined there. for i in range(DIM): for j in range(DIM): MU[i] += -sp.Rational(2, 3) * Bq.gammabarUU[i][j] * trK_dD[j] # First define aDD_dD: aDD_dD = ixp.declarerank3("aDD_dD", "sym01") # Then evaluate the conformal covariant derivative \bar{D}_j \bar{A}_{lm} AbarDD_dBarD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): AbarDD_dBarD[i][j][k] = Bq.AbarDD_dD[i][j][k] for l in range(DIM): AbarDD_dBarD[i][j][ k] += -Bq.GammabarUDD[l][k][i] * Bq.AbarDD[l][j] AbarDD_dBarD[i][j][ k] += -Bq.GammabarUDD[l][k][j] * Bq.AbarDD[i][l] # Term 1: Contract twice with the metric to make \bar{D}_{j} \bar{A}^{ij} for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): MU[i] += Bq.gammabarUU[i][k] * Bq.gammabarUU[j][ l] * AbarDD_dBarD[k][l][j] # Finally, we multiply by e^{-4 phi} and rescale the momentum constraint: for i in range(DIM): MU[i] *= Bq.exp_m4phi / rfm.ReU[i]
def ShiftedKerrSchild(ComputeADMGlobalsOnly = False): global Sph_r_th_ph,r,th,ph, rho2, gammaSphDD, KSphDD, alphaSph, betaSphU, BSphU # All gridfunctions will be written in terms of spherical coordinates (r, th, ph): r,th,ph = sp.symbols('r th ph', real=True) thismodule = "ShiftedKerrSchild" DIM = 3 par.set_parval_from_str("grid::DIM",DIM) # Input parameters: M, a, r0 = par.Cparameters("REAL", thismodule, ["M", "a", "r0"]) # Auxiliary variables: rho2 = sp.symbols('rho2', real=True) # r_{KS} = r + r0 rKS = r+r0 # rho^2 = rKS^2 + a^2*cos^2(theta) rho2 = rKS*rKS + a*a*sp.cos(th)**2 # alpha = 1/sqrt{1 + M*rKS/rho^2} alphaSph = 1/(sp.sqrt(1 + 2*M*rKS/rho2)) # Initialize the shift vector, \beta^i, to zero. betaSphU = ixp.zerorank1() # beta^r = alpha^2*2Mr/rho^2 betaSphU[0] = alphaSph*alphaSph*2*M*rKS/rho2 # Time derivative of shift vector beta^i, B^i, is zero. BSphU = ixp.zerorank1() # Initialize \gamma_{ij} to zero. gammaSphDD = ixp.zerorank2() # gammaDD{rKS rKS} = 1 +2M*rKS/rho^2 gammaSphDD[0][0] = 1 + 2*M*rKS/rho2 # gammaDD{rKS phi} = -a*gammaDD{r r}*sin^2(theta) gammaSphDD[0][2] = gammaSphDD[2][0] = -a*gammaSphDD[0][0]*sp.sin(th)**2 # gammaDD{theta theta} = rho^2 gammaSphDD[1][1] = rho2 # gammaDD{phi phi} = (rKS^2 + a^2 + 2Mr/rho^2*a^2*sin^2(theta))*sin^2(theta) gammaSphDD[2][2] = (rKS*rKS + a*a + 2*M*rKS*a*a*sp.sin(th)**2/rho2)*sp.sin(th)**2 # *** Define Useful Quantities A, B, D *** # A = (a^2*cos^2(2theta) + a^2 + 2r^2) A = (a*a*sp.cos(2*th) + a*a + 2*rKS*rKS) # B = A + 4M*rKS B = A + 4*M*rKS # D = \sqrt(2M*rKS/(a^2cos^2(theta) + rKS^2) + 1) D = sp.sqrt(2*M*rKS/(a*a*sp.cos(th)**2 + rKS*rKS) + 1) # *** The extrinsic curvature in spherical polar coordinates *** # Establish the 3x3 zero-matrix KSphDD = ixp.zerorank2() # *** Fill in the nonzero components *** # *** This will create an upper-triangular matrix *** # K_{r r} = D(A+2Mr)/(A^2*B)[4M(a^2*cos(2theta) + a^2 - 2r^2)] KSphDD[0][0] = D*(A+2*M*rKS)/(A*A*B)*(4*M*(a*a*sp.cos(2*th)+a*a-2*rKS*rKS)) # K_{r theta} = D/(AB)[8a^2*Mr*sin(theta)cos(theta)] KSphDD[0][1] = KSphDD[1][0] = D/(A*B)*(8*a*a*M*rKS*sp.sin(th)*sp.cos(th)) # K_{r phi} = D/A^2[-2aMsin^2(theta)(a^2cos(2theta)+a^2-2r^2)] KSphDD[0][2] = KSphDD[2][0] = D/(A*A)*(-2*a*M*sp.sin(th)**2*(a*a*sp.cos(2*th)+a*a-2*rKS*rKS)) # K_{theta theta} = D/B[4Mr^2] KSphDD[1][1] = D/B*(4*M*rKS*rKS) # K_{theta phi} = D/(AB)*(-8*a^3*Mr*sin^3(theta)cos(theta)) KSphDD[1][2] = KSphDD[2][1] = D/(A*B)*(-8*a**3*M*rKS*sp.sin(th)**3*sp.cos(th)) # K_{phi phi} = D/(A^2*B)[2Mr*sin^2(theta)(a^4(M+3r) # +4a^2r^2(2r-M)+4a^2r*cos(2theta)(a^2+r(M+2r))+8r^5)] KSphDD[2][2] = D/(A*A*B)*(2*M*rKS*sp.sin(th)**2*(a**4*(rKS-M)*sp.cos(4*th)\ + a**4*(M+3*rKS)+4*a*a*rKS*rKS*(2*rKS-M)\ + 4*a*a*rKS*sp.cos(2*th)*(a*a + rKS*(M + 2*rKS)) + 8*rKS**5)) if ComputeADMGlobalsOnly == True: return # Validated against original SENR: #print(sp.mathematica_code(gammaSphDD[1][1])) Sph_r_th_ph = [r,th,ph] cf,hDD,lambdaU,aDD,trK,alpha,vetU,betU = \ AtoB.Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear("Spherical", Sph_r_th_ph, gammaSphDD,KSphDD,alphaSph,betaSphU,BSphU) global returnfunction returnfunction = bIDf.BSSN_ID_function_string(cf,hDD,lambdaU,aDD,trK,alpha,vetU,betU)
# * Desired initial lapse $\alpha$ and shift $\beta^i$. We will choose our gauge conditions as $\alpha=1$ and $\beta^i=B^i=0$. $\alpha = \psi^{-2}$ will yield much better behavior, but the conformal factor $\psi$ depends on the desired *destination* coordinate system (which may not be spherical coordinates). # Step P0: Load needed modules import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends import NRPy_param_funcs as par # NRPy+: Parameter interface import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support import sys # Standard Python module for multiplatform OS-level functions from pickling import pickle_NRPy_env # NRPy+: Pickle/unpickle NRPy+ environment, for parallel codegen import BSSN.ADM_Exact_Spherical_or_Cartesian_to_BSSNCurvilinear as AtoB thismodule = __name__ # The UIUC initial data represent a Kerr black hole with mass M # and dimensionless spin chi in UIUC quasi-isotropic coordinates, # see https://arxiv.org/abs/1001.4077 # Input parameters: M, chi = par.Cparameters("REAL", thismodule, ["M", "chi"], [1.0, 0.99]) # ComputeADMGlobalsOnly == True will only set up the ADM global quantities. # == False will perform the full ADM SphorCart->BSSN Curvi conversion def UIUCBlackHole(ComputeADMGlobalsOnly=False, include_NRPy_basic_defines_and_pickle=False): global Sph_r_th_ph, r, th, ph, gammaSphDD, KSphDD, alphaSph, betaSphU, BSphU # All gridfunctions will be written in terms of spherical coordinates (r, th, ph): r, th, ph = sp.symbols('r th ph', real=True) # Step 0: Set spatial dimension (must be 3 for BSSN) DIM = 3 par.set_parval_from_str("grid::DIM", DIM)
# * Desired initial lapse $\alpha$ and shift $\beta^i$ # # **Transformation to curvilinear coordinates**: # * Once the above variables have been set in Cartesian coordinates, we will apply the appropriate coordinate transformations and tensor rescalings ([described in the BSSN NRPy+ tutorial module](Tutorial-BSSNCurvilinear.ipynb)) # Step 1: Initialize core Python/NRPy+ modules import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends import NRPy_param_funcs as par # NRPy+: Parameter interface import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support from pickling import pickle_NRPy_env # NRPy+: Pickle/unpickle NRPy+ environment, for parallel codegen import BSSN.ADM_Exact_Spherical_or_Cartesian_to_BSSNCurvilinear as AtoB thismodule = __name__ BH1_posn_x, BH1_posn_y, BH1_posn_z = par.Cparameters("REAL", thismodule, ["BH1_posn_x", "BH1_posn_y", "BH1_posn_z"], [0.0, 0.0, +0.5]) BH1_mass = par.Cparameters("REAL", thismodule, ["BH1_mass"], 1.0) BH2_posn_x, BH2_posn_y, BH2_posn_z = par.Cparameters("REAL", thismodule, ["BH2_posn_x", "BH2_posn_y", "BH2_posn_z"], [0.0, 0.0, -0.5]) BH2_mass = par.Cparameters("REAL", thismodule, ["BH2_mass"], 1.0) # ComputeADMGlobalsOnly == True will only set up the ADM global quantities. # == False will perform the full ADM SphorCart->BSSN Curvi conversion def BrillLindquist(ComputeADMGlobalsOnly = False, include_NRPy_basic_defines_and_pickle=False): # Step 2: Setting up Brill-Lindquist initial data # Step 2.a: Set spatial dimension (must be 3 for BSSN) DIM = 3
import reference_metric as rfm par.set_parval_from_str("reference_metric::CoordSystem", "Cartesian") rfm.reference_metric() # Step 1a: Set commonly used parameters. thismodule = "GiRaFFEfood_HO" # Set the spatial dimension parameter to 3. par.set_parval_from_str("grid::DIM", 3) DIM = par.parval_from_str("grid::DIM") # Create a parameter to control the initial data choice. For now, this will only have Exact Wald as an option. par.initialize_param( par.glb_param("char", thismodule, "IDchoice", "Exact_Wald")) # Step 1b: Set needed Cparameters M = par.Cparameters("REAL", thismodule, ["M"], 1.0) # The mass of the black hole M_PI = par.Cparameters("#define", thismodule, ["M_PI"], "") # pi, 3.141592... KerrSchild_radial_shift = par.Cparameters("REAL", thismodule, "KerrSchild_radial_shift", 0.4) # Default value for ExactWald def GiRaFFEfood_HO_Exact_Wald(): # <a id='step2'></a> # # ### Step 2: Set the vectors A and E in Spherical coordinates # $$\label{step2}$$ # # \[Back to [top](#top)\] #