def ValenciavU_func_FW(**params): # B^x(0,x) = 1.0 # B^y(0,x) = 1.0 if x <= -0.1 # 1.0-1.5(x+0.1) if -0.1 < x <= 0.1 # 0.7 if x > 0.1 # B^z(0,x) = 0 x = rfm.xx_to_Cart[0] y = rfm.xx_to_Cart[1] Byleft = sp.sympify(1) Bycenter = sp.sympify(1) - sp.Rational(15, 10) * (x + sp.Rational(1, 10)) Byright = sp.Rational(7, 10) BU = ixp.zerorank1() BU[0] = sp.sympify(1) BU[1] = noif.coord_leq_bound(x,-bound)*Byleft\ +noif.coord_greater_bound(x,-bound)*noif.coord_leq_bound(x,bound)*Bycenter\ +noif.coord_greater_bound(x,bound)*Byright BU[2] = 0 # E^x(0,x) = 0.0 , E^y(x) = 0.0 , E^z(x) = -B^y(0,x) EU = ixp.zerorank1() EU[0] = sp.sympify(0) EU[1] = sp.sympify(0) EU[2] = -BU[1] # In flat space, ED and EU are identical, so we can still use this function. return gfcf.compute_ValenciavU_from_ED_and_BU(EU, BU)
def ValenciavU_func_AW(**params): x = rfm.xx_to_Cart[0] Bzleft = sp.sympify(1) Bzcenter = sp.sympify(1) + sp.Rational(15, 100) * f_AW(x) Bzright = sp.Rational(13, 10) BpU = ixp.zerorank1() BpU[0] = sp.sympify(1) BpU[1] = sp.sympify(1) BpU[2] = noif.coord_leq_bound(x,-bound)*Bzleft\ +noif.coord_greater_bound(x,-bound)*noif.coord_leq_bound(x,bound)*Bzcenter\ +noif.coord_greater_bound(x,bound)*Bzright EpU = ixp.zerorank1() EpU[0] = -BpU[2] EpU[1] = sp.sympify(0) EpU[2] = sp.sympify(1) BU = ixp.zerorank1() BU[0] = BpU[0] BU[1] = gammamu * (BpU[1] - mu_AW * EpU[2]) BU[2] = gammamu * (BpU[2] + mu_AW * EpU[1]) EU = ixp.zerorank1() EU[0] = EpU[0] EU[1] = gammamu * (EpU[1] + mu_AW * BpU[2]) EU[2] = gammamu * (EpU[2] - mu_AW * BpU[1]) # In flat space, ED and EU are identical, so we can still use this function. return gfcf.compute_ValenciavU_from_ED_and_BU(EU, BU)
def ValenciavU_func_EW(**params): M = params["M"] gammaDD = params["gammaDD"] # Note that this must use a Cartesian basis! sqrtgammaDET = params["sqrtgammaDET"] KerrSchild_radial_shift = params["KerrSchild_radial_shift"] r = rfm.xxSph[ 0] + KerrSchild_radial_shift # We are setting the data up in Shifted Kerr-Schild coordinates theta = rfm.xxSph[1] LeviCivitaTensorUUU = ixp.LeviCivitaTensorUUU_dim3_rank3(sqrtgammaDET) AD = gfcf.Axyz_func_spherical(Ar_EW, Ath_EW, Aph_EW, False, **params) # For the initial data, we can analytically take the derivatives of A_i ADdD = ixp.zerorank2() for i in range(3): for j in range(3): ADdD[i][j] = sp.simplify(sp.diff(AD[i], rfm.xx_to_Cart[j])) BU = ixp.zerorank1() for i in range(3): for j in range(3): for k in range(3): BU[i] += LeviCivitaTensorUUU[i][j][k] * ADdD[k][j] EsphD = ixp.zerorank1() # 2 M ( 1+ 2M/r )^{-1/2} \sin^2 \theta EsphD[2] = 2 * M * sp.sin(theta)**2 / sp.sqrt(1 + 2 * M / r) ED = rfm.basis_transform_vectorD_from_rfmbasis_to_Cartesian( gfcf.Jac_dUrfm_dDCartUD, EsphD) return gfcf.compute_ValenciavU_from_ED_and_BU(ED, BU, gammaDD)
def LambdabarU_lambdaU__exact_gammaDD(gammaDD): global LambdabarU, lambdaU if gammaDD == None: gammaDD = ixp.declarerank2("gammaDD", "sym01") # \bar{Lambda}^i = \bar{gamma}^{jk}(\bar{Gamma}^i_{jk} - \hat{Gamma}^i_{jk}). gammabarDD_hDD(gammaDD) gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD) # First compute Christoffel symbols \bar{Gamma}^i_{jk}, with respect to barred metric: GammabarUDD = ixp.zerorank3() for i in range(DIM): for j in range(DIM): for k in range(DIM): for l in range(DIM): GammabarUDD[i][j][k] += sp.Rational( 1, 2) * gammabarUU[i][l] * ( sp.diff(gammabarDD[l][j], rfm.xx[k]) + sp.diff(gammabarDD[l][k], rfm.xx[j]) - sp.diff(gammabarDD[j][k], rfm.xx[l])) # Next evaluate \bar{Lambda}^i, based on GammabarUDD above and GammahatUDD # (from the reference metric): LambdabarU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): LambdabarU[i] += gammabarUU[j][k] * (GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k]) lambdaU = ixp.zerorank1() for i in range(DIM): lambdaU[i] = LambdabarU[i] / rfm.ReU[i]
def ValenciavU_func_SM(**params): M = params["M"] a = params["a"] alpha = params["alpha"] betaU = params["betaU"] # Note that this must use a spherical basis! gammaDD = params["gammaDD"] # Note that this must use a Cartesian basis! sqrtgammaDET = params["sqrtgammaDET"] KerrSchild_radial_shift = params["KerrSchild_radial_shift"] r = rfm.xxSph[0] + KerrSchild_radial_shift # We are setting the data up in Shifted Kerr-Schild coordinates theta = rfm.xxSph[1] z = rfm.xx_to_Cart[2] split_C_SM = noif.coord_geq_bound(z,sp.sympify(0))*C_SM - noif.coord_less_bound(z,sp.sympify(0))*C_SM BsphU = ixp.zerorank1() BsphU[0] = split_C_SM*alpha*M*M/(r*r) + \ split_C_SM*alpha*a*a*M*M*sp.Rational(1,2)/(r**4)*(-sp.sympify(2)*sp.cos(theta) + (r/M)**2*(sp.sympify(1)+sp.sympify(3)*sp.cos(sp.sympify(2)*theta))*f_of_r(r,M)) BsphU[1] = -split_C_SM*alpha*a*a/(r*r) * sp.sin(theta)*sp.cos(theta)*fp_of_r(r,M) BsphU[2] = -split_C_SM*alpha*a*M*sp.Rational(1,8)/(r*r)*(sp.sympify(1)+sp.sympify(4)*M/r) EsphD = ixp.zerorank1() EsphD[0] = -split_C_SM*a**3/(sp.sympify(8)*alpha*M**3)*fp_of_r(r,M)*sp.cos(theta)*sp.sin(theta)**2 EsphD[1] = -split_C_SM*a*sp.Rational(1,8)/alpha*(sp.sin(theta) + a*a*f_of_r(r,M)*sp.sin(theta)*(sp.sympify(2)*sp.cos(theta)**2-sp.sin(theta)**2)) - \ betaU[0]*sqrtgammaDET*a*split_C_SM*sp.Rational(1,8)/(r*r)*(sp.sympify(1)+sp.sympify(4)*M/r) EsphD[2] = betaU[0]/(alpha*M)*split_C_SM*a*a*fp_of_r(r,M)*sp.cos(theta)*sp.sin(theta)**2 ED = gfcf.change_basis_spherical_to_Cartesian_D(EsphD) BU = gfcf.change_basis_spherical_to_Cartesian_U(BsphU) return gfcf.compute_ValenciavU_from_ED_and_BU(ED, BU, gammaDD)
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 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 ValenciavU_FW(**params): # B^x(0,x) = 1.0 # B^y(0,x) = 1.0 if x <= -0.1 # 1.0-1.5(x+0.1) if -0.1 < x <= 0.1 # 0.7 if x > 0.1 # B^z(0,x) = 0 Byleft = sp.sympify(1) Bycenter = sp.sympify(1) - sp.Rational(15, 10) * (x + sp.Rational(1, 10)) Byright = sp.Rational(7, 10) global BU BU = ixp.zerorank1() BU[0] = sp.sympify(1) BU[1] = noif.coord_leq_bound(x,-bound)*Byleft\ +noif.coord_greater_bound(x,-bound)*noif.coord_leq_bound(x,bound)*Bycenter\ +noif.coord_greater_bound(x,bound)*Byright BU[2] = sp.sympify(0) # E^x(0,x) = 0.0 , E^y(x) = 0.0 , E^z(x) = -B^y(0,x) EU = ixp.zerorank1() EU[0] = sp.sympify(0) EU[1] = sp.sympify(0) EU[2] = -BU[1] return compute_ValenciavU_from_EU_and_BU(EU, BU)
def detgammabar_and_derivs(): # Step 5.a: Declare as globals all expressions that may be used # outside this function, declare BSSN gridfunctions # if not defined already, and set DIM=3. global detgammabar,detgammabar_dD,detgammabar_dDD hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already() DIM = 3 detgbarOverdetghat = sp.sympify(1) detgbarOverdetghat_dD = ixp.zerorank1() detgbarOverdetghat_dDD = ixp.zerorank2() if par.parval_from_str(thismodule + "::detgbarOverdetghat_equals_one") == "False": print("Error: detgbarOverdetghat_equals_one=\"False\" is not fully implemented yet.") exit(1) ## Approach for implementing detgbarOverdetghat_equals_one=False: # detgbarOverdetghat = gri.register_gridfunctions("AUX", ["detgbarOverdetghat"]) # detgbarOverdetghatInitial = gri.register_gridfunctions("AUX", ["detgbarOverdetghatInitial"]) # detgbarOverdetghat_dD = ixp.declarerank1("detgbarOverdetghat_dD") # detgbarOverdetghat_dDD = ixp.declarerank2("detgbarOverdetghat_dDD", "sym01") # Step 8d: Define detgammabar, detgammabar_dD, and detgammabar_dDD (needed for \partial_t \bar{\Lambda}^i below) detgammabar = detgbarOverdetghat * rfm.detgammahat detgammabar_dD = ixp.zerorank1() for i in range(DIM): detgammabar_dD[i] = detgbarOverdetghat_dD[i] * rfm.detgammahat + detgbarOverdetghat * rfm.detgammahatdD[i] detgammabar_dDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): detgammabar_dDD[i][j] = detgbarOverdetghat_dDD[i][j] * rfm.detgammahat + \ detgbarOverdetghat_dD[i] * rfm.detgammahatdD[j] + \ detgbarOverdetghat_dD[j] * rfm.detgammahatdD[i] + \ detgbarOverdetghat * rfm.detgammahatdDD[i][j]
def compute_ValenciavU_from_ED_and_BU(ED, BU, gammaDD=None): # Now, we calculate v^i = ([ijk] E_j B_k) / B^2, # where [ijk] is the Levi-Civita symbol and B^2 = \gamma_{ij} B^i B^j$ is a trivial dot product in flat space. # In flat spacetime, use the Minkowski metric; otherwise, use the input metric. if gammaDD is None: gammaDD = ixp.zerorank2() for i in range(3): gammaDD[i][i] = sp.sympify(1) unused_gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD) sqrtgammaDET = sp.sqrt(gammaDET) LeviCivitaTensorUUU = ixp.LeviCivitaTensorUUU_dim3_rank3(sqrtgammaDET) BD = ixp.zerorank1() for i in range(3): for j in range(3): BD[i] += gammaDD[i][j] * BU[j] B2 = sp.sympify(0) for i in range(3): B2 += BU[i] * BD[i] ValenciavU = ixp.zerorank1() for i in range(3): for j in range(3): for k in range(3): ValenciavU[ i] += LeviCivitaTensorUUU[i][j][k] * ED[j] * BD[k] / B2 return ValenciavU
def BSSN_basic_tensors(): # Step 3.a: Declare as globals all variables that may be used # outside this function, declare BSSN gridfunctions # if not defined already, and set DIM=3. global gammabarDD, AbarDD, LambdabarU, betaU, BU hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already( ) DIM = 3 # Step 3.a.i: gammabarDD and AbarDD: gammabarDD = ixp.zerorank2() AbarDD = ixp.zerorank2() for i in range(DIM): for j in range(DIM): # gammabar_{ij} = h_{ij}*ReDD[i][j] + gammahat_{ij} gammabarDD[i][j] = hDD[i][j] * rfm.ReDD[i][j] + rfm.ghatDD[i][j] # Abar_{ij} = a_{ij}*ReDD[i][j] AbarDD[i][j] = aDD[i][j] * rfm.ReDD[i][j] # Step 3.a.ii: LambdabarU, betaU, and BU: LambdabarU = ixp.zerorank1() betaU = ixp.zerorank1() BU = ixp.zerorank1() for i in range(DIM): LambdabarU[i] = lambdaU[i] * rfm.ReU[i] betaU[i] = vetU[i] * rfm.ReU[i] BU[i] = betU[i] * rfm.ReU[i]
def BSSN_or_ADM_ito_g4DD(inputvars, g4DD=None): # Step 0: Declare output variables as globals, to make interfacing with other modules/functions easier if inputvars == "ADM": global gammaDD, betaU, alpha elif inputvars == "BSSN": global hDD, cf, vetU, alpha else: print("inputvars = " + str(inputvars) + " not supported. Please choose ADM or BSSN.") sys.exit(1) # Step 1: declare g4DD as symmetric rank-4 tensor: g4DD_is_input_into_this_function = True if g4DD == None: g4DD = ixp.declarerank2("g4DD", "sym01", DIM=4) g4DD_is_input_into_this_function = False # Step 2: Compute gammaDD & betaD betaD = ixp.zerorank1() gammaDD = ixp.zerorank2() for i in range(3): betaD[i] = g4DD[0][i] for j in range(3): gammaDD[i][j] = g4DD[i + 1][j + 1] # Step 3: Compute betaU # Step 3.a: Compute gammaUU based on provided gammaDD gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD) # Step 3.b: Use gammaUU to raise betaU betaU = ixp.zerorank1() for i in range(3): for j in range(3): betaU[i] += gammaUU[i][j] * betaD[j] # Step 4: Compute alpha = sqrt(beta^2 - g_{00}): # Step 4.a: Compute beta^2 = beta^k beta_k: beta_squared = sp.sympify(0) for k in range(3): beta_squared += betaU[k] * betaD[k] # Step 4.b: alpha = sqrt(beta^2 - g_{00}): if g4DD_is_input_into_this_function == False: alpha = sp.sqrt(sp.simplify(beta_squared) - g4DD[0][0]) else: alpha = sp.sqrt(beta_squared - g4DD[0][0]) # Step 5: If inputvars == "ADM", we are finished. Return. if inputvars == "ADM": return # Step 6: If inputvars == "BSSN", convert ADM to BSSN import BSSN.BSSN_in_terms_of_ADM as BitoA dummyBU = ixp.zerorank1() BitoA.gammabarDD_hDD(gammaDD) BitoA.cf_from_gammaDD(gammaDD) BitoA.betU_vetU(betaU, dummyBU) hDD = BitoA.hDD cf = BitoA.cf vetU = BitoA.vetU
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 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 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 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) import BSSN.BSSN_ID_function_string as bIDf # Generates initial_data() C function & stores to outC_function_dict["initial_data"] bIDf.BSSN_ID_function_string( cf, hDD, lambdaU, aDD, trK, alpha, vetU, betU, include_NRPy_basic_defines=include_NRPy_basic_defines_and_pickle) if include_NRPy_basic_defines_and_pickle: return pickle_NRPy_env()
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 compute_psi6Phi_rhs_flux_term_operand(gammaDD,sqrtgammaDET,betaU,alpha,AD,psi6Phi): gammaUU,_gammaDET = ixp.symm_matrix_inverter3x3(gammaDD) # _gammaDET unused. AU = ixp.zerorank1() # Raise the index on A in the usual way: for i in range(3): for j in range(3): AU[i] += gammaUU[i][j] * AD[j] global PhievolParenU PhievolParenU = ixp.zerorank1(DIM=3) for j in range(3): # \alpha\sqrt{\gamma}A^j - \beta^j [\sqrt{\gamma} \Phi] PhievolParenU[j] += alpha*sqrtgammaDET*AU[j] - betaU[j]*psi6Phi
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 GiRaFFE_NRPy_P2C(gammaDD,betaU,alpha, ValenciavU,BU, sqrt4pi): # After recalculating the 3-velocity, we need to update the poynting flux: # We'll reset the Valencia velocity, since this will be part of a second call to outCfunction. # First compute stress-energy tensor T4UU and T4UD: GRHD.compute_sqrtgammaDET(gammaDD) # GRHD.u4U_in_terms_of_ValenciavU__rescale_ValenciavU_by_applying_speed_limit(alpha, betaU, gammaDD, ValenciavU) R = sp.sympify(0) for i in range(3): for j in range(3): R += gammaDD[i][j] * ValenciavU[i] * ValenciavU[j] u4U_ito_ValenciavU = ixp.zerorank1(DIM=4) u4U_ito_ValenciavU[0] = 1 / (alpha * sp.sqrt(1 - R)) # 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 * ValenciavU[i] - betaU[i]) GRFFE.compute_smallb4U_with_driftvU_for_FFE(gammaDD, betaU, alpha, u4U_ito_ValenciavU, BU, sqrt4pi) GRFFE.compute_smallbsquared(gammaDD, betaU, alpha, GRFFE.smallb4_with_driftv_for_FFE_U) GRFFE.compute_TEM4UU(gammaDD, betaU, alpha, GRFFE.smallb4_with_driftv_for_FFE_U, GRFFE.smallbsquared, u4U_ito_ValenciavU) GRFFE.compute_TEM4UD(gammaDD, betaU, alpha, GRFFE.TEM4UU) # Compute conservative variables in terms of primitive variables GRHD.compute_S_tildeD(alpha, GRHD.sqrtgammaDET, GRFFE.TEM4UD) global StildeD StildeD = GRHD.S_tildeD
def g4DD_ito_BSSN_or_ADM(inputvars, gammaDD=None, betaU=None, alpha=None): # Step 0: Declare g4DD as globals, to make interfacing with other modules/functions easier global g4DD # Step 1: Set gammaDD, betaU, and alpha if not already input. if gammaDD == None and betaU == None and alpha == None: gammaDD, betaU, alpha = setup_ADM_quantities(inputvars) # Step 2: Compute g4DD = g_{mu nu}: # To get \gamma_{\mu \nu} = gamma4DD[mu][nu], we'll need to construct the 4-metric, using Eq. 2.122 in B&S: g4DD = ixp.zerorank2(DIM=4) # Step 2.a: Compute beta_i via Eq. 2.121 in B&S betaD = ixp.zerorank1() for i in range(3): for j in range(3): betaD[i] += gammaDD[i][j] * betaU[j] # Step 2.b: Compute beta_i beta^i, the beta contraction. beta2 = sp.sympify(0) for i in range(3): beta2 += betaU[i] * betaD[i] # Step 2.c: Construct g4DD via Eq. 2.122 in B&S g4DD[0][0] = -alpha**2 + beta2 for mu in range(1, 4): g4DD[mu][0] = g4DD[0][mu] = betaD[mu - 1] for mu in range(1, 4): for nu in range(1, 4): g4DD[mu][nu] = gammaDD[mu - 1][nu - 1]
def add_to_Cfunction_dict__GiRaFFE_NRPy_A2B(gammaDD, AD, BU, includes=None): # Set spatial dimension (must be 3 for BSSN) DIM = 3 par.set_parval_from_str("grid::DIM", DIM) # Compute the sqrt of the three metric determinant. import GRHD.equations as gh gh.compute_sqrtgammaDET(gammaDD) # Import the Levi-Civita symbol and build the corresponding tensor. # We already have a handy function to define the Levi-Civita symbol in indexedexp.py LeviCivitaUUU = ixp.LeviCivitaTensorUUU_dim3_rank3(gh.sqrtgammaDET) AD_dD = ixp.declarerank2("AD_dD", "nosym") BU = ixp.zerorank1() for i in range(DIM): for j in range(DIM): for k in range(DIM): BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j] # Here, we'll use the add_to_Cfunction_dict() function to output a function that will compute the magnetic field # on the interior. Then, we'll add postloop code to handle the ghostzones. desc = "Compute the magnetic field from the vector potential everywhere, including ghostzones" name = "driver_A_to_B" params = "const paramstruct *restrict params,REAL *restrict in_gfs,REAL *restrict auxevol_gfs" body = fin.FD_outputC("returnstring", [ lhrh(lhs=gri.gfaccess("out_gfs", "BU0"), rhs=BU[0]), lhrh(lhs=gri.gfaccess("out_gfs", "BU1"), rhs=BU[1]), lhrh(lhs=gri.gfaccess("out_gfs", "BU2"), rhs=BU[2]) ]) postloop = """ int imin[3] = { NGHOSTS_A2B, NGHOSTS_A2B, NGHOSTS_A2B }; int imax[3] = { NGHOSTS+Nxx0, NGHOSTS+Nxx1, NGHOSTS+Nxx2 }; // Now, we loop over the ghostzones to calculate the magnetic field there. for(int which_gz = 0; which_gz < NGHOSTS_A2B; which_gz++) { // After updating each face, adjust imin[] and imax[] // to reflect the newly-updated face extents. compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2]); imin[0]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2]); imax[0]++; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2]); imin[1]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2]); imax[1]++; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2]); imin[2]--; compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1); imax[2]++; } """ loopopts = "InteriorPoints" add_to_Cfunction_dict(includes=includes, desc=desc, name=name, prefunc=prefunc, params=params, body=body, loopopts=loopopts, postloop=postloop) outC_function_dict[name] = outC_function_dict[name].replace( "= NGHOSTS", "= NGHOSTS_A2B").replace( "NGHOSTS+Nxx0", "Nxx_plus_2NGHOSTS0-NGHOSTS_A2B").replace( "NGHOSTS+Nxx1", "Nxx_plus_2NGHOSTS1-NGHOSTS_A2B").replace( "NGHOSTS+Nxx2", "Nxx_plus_2NGHOSTS2-NGHOSTS_A2B").replace( "../set_Cparameters.h", "set_Cparameters.h")
def HS2011_Omega_SO_3p5PN_pt6(m1, m2, n12U, p1U, p2U, r12): Omega1 = ixp.zerorank1() for i in range(3): Omega1[i] = (+(-(8 / m1 + 9 * m2 / (2 * m1**2)) * dot(n12U, p1U) + (59 / (4 * m1) + 27 / (2 * m2)) * dot(n12U, p2U)) * cross(p1U, p2U)[i]) / r12**3 return Omega1
def ScalarField_Tmunu(): global T4UU # Step 1.c: 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.d: Given the chosen coordinate system, set up # corresponding reference metric and needed # reference metric quantities # The following function call sets up the reference metric # and related quantities, including rescaling matrices ReDD, # ReU, and hatted quantities. rfm.reference_metric() # Step 1.e: Import all basic (unrescaled) BSSN scalars & tensors Bq.BSSN_basic_tensors() alpha = Bq.alpha betaU = Bq.betaU # Step 1.g: Define ADM quantities in terms of BSSN quantities BtoA.ADM_in_terms_of_BSSN() gammaDD = BtoA.gammaDD gammaUU = BtoA.gammaUU # Step 1.h: Define scalar field quantitites sf_dD = ixp.declarerank1("sf_dD") Pi = sp.Symbol("sfM", real=True) # Step 2a: Set up \partial^{t}\varphi = Pi/alpha sf4dU = ixp.zerorank1(DIM=4) sf4dU[0] = Pi / alpha # Step 2b: Set up \partial^{i}\varphi = -Pi*beta^{i}/alpha + gamma^{ij}\partial_{j}\varphi for i in range(DIM): sf4dU[i + 1] = -Pi * betaU[i] / alpha for j in range(DIM): sf4dU[i + 1] += gammaUU[i][j] * sf_dD[j] # Step 2c: Set up \partial^{i}\varphi\partial_{i}\varphi = -Pi**2 + gamma^{ij}\partial_{i}\varphi\partial_{j}\varphi sf4d2 = -Pi**2 for i in range(DIM): for j in range(DIM): sf4d2 += gammaUU[i][j] * sf_dD[i] * sf_dD[j] # Step 3a: Setting up g^{\mu\nu} ADMg.g4UU_ito_BSSN_or_ADM("ADM", gammaDD=gammaDD, betaU=betaU, alpha=alpha, gammaUU=gammaUU) g4UU = ADMg.g4UU # Step 3b: Setting up T^{\mu\nu} for a massless scalar field T4UU = ixp.zerorank2(DIM=4) for mu in range(4): for nu in range(4): T4UU[mu][nu] = sf4dU[mu] * sf4dU[nu] - g4UU[mu][nu] * sf4d2 / 2
def compute_S_tilde_source_termD(alpha, sqrtgammaDET,g4DD_zerotimederiv_dD, T4UU): global S_tilde_source_termD S_tilde_source_termD = ixp.zerorank1(DIM=3) for i in range(3): for mu in range(4): for nu in range(4): S_tilde_source_termD[i] += sp.Rational(1,2)*alpha*sqrtgammaDET*T4UU[mu][nu]*g4DD_zerotimederiv_dD[mu][nu][i+1]
def compute_g4DD_zerotimederiv_dD(gammaDD,betaU,alpha, gammaDD_dD,betaU_dD,alpha_dD): global g4DD_zerotimederiv_dD # Eq. 2.121 in B&S betaD = ixp.zerorank1() for i in range(3): for j in range(3): betaD[i] += gammaDD[i][j]*betaU[j] # gammaDD_dD = ixp.declarerank3("gammaDD_dDD","sym12",DIM=3) # betaU_dD = ixp.declarerank2("betaU_dD" ,"nosym",DIM=3) betaDdD = ixp.zerorank2() for i in range(3): for j in range(3): for k in range(3): # Recall that betaD[i] = gammaDD[i][j]*betaU[j] (Eq. 2.121 in B&S) betaDdD[i][k] += gammaDD_dD[i][j][k]*betaU[j] + gammaDD[i][j]*betaU_dD[j][k] # Eq. 2.122 in B&S g4DD_zerotimederiv_dD = ixp.zerorank3(DIM=4) for k in range(3): # Recall that g4DD[0][0] = -alpha^2 + betaU[j]*betaD[j] g4DD_zerotimederiv_dD[0][0][k+1] += -2*alpha*alpha_dD[k] for j in range(3): g4DD_zerotimederiv_dD[0][0][k+1] += betaU_dD[j][k]*betaD[j] + betaU[j]*betaDdD[j][k] for i in range(3): for k in range(3): # Recall that g4DD[i][0] = g4DD[0][i] = betaD[i] g4DD_zerotimederiv_dD[i+1][0][k+1] = g4DD_zerotimederiv_dD[0][i+1][k+1] = betaDdD[i][k] for i in range(3): for j in range(3): for k in range(3): # Recall that g4DD[i][j] = gammaDD[i][j] g4DD_zerotimederiv_dD[i+1][j+1][k+1] = gammaDD_dD[i][j][k]
def u4U_in_terms_of_vU__rescale_vU_by_applying_speed_limit(alpha, betaU, gammaDD, vU): ValenciavU = ixp.zerorank1() for i in range(3): ValenciavU[i] = (vU[i] + betaU[i]) / alpha u4U_in_terms_of_ValenciavU__rescale_ValenciavU_by_applying_speed_limit(alpha, betaU, gammaDD, ValenciavU) # Since ValenciavU is written in terms of vU, # u4U_ito_ValenciavU is actually u4U_ito_vU global u4U_ito_vU u4U_ito_vU = ixp.zerorank1(DIM=4) for mu in range(4): u4U_ito_vU[mu] = u4U_ito_ValenciavU[mu] # Finally compute the rescaled (speed-limited) vU global rescaledvU rescaledvU = ixp.zerorank1(DIM=3) for i in range(3): rescaledvU[i] = alpha * rescaledValenciavU[i] - betaU[i]
def ValenciavU_func_FB(**params): x = rfm.xx_to_Cart[0] BU = ixp.zerorank1(DIM=3) BU[0] = sp.sympify(1) BU[1] = BU[2] = noif.coord_leq_bound(x,boundL) * sp.sympify(1)\ +noif.coord_greater_bound(x,boundL)*noif.coord_leq_bound(x,boundR)* zfunc(x)\ +noif.coord_greater_bound(x,boundR) * -sp.sympify(1) EU = ixp.zerorank1() EU[0] = sp.sympify(0) EU[1] = sp.Rational(1,2) EU[2] = -sp.Rational(1,2) # In flat space, ED and EU are identical, so we can still use this function. return gfcf.compute_ValenciavU_from_ED_and_BU(EU, BU)
def compute_smallb4U_with_driftvU_for_FFE(gammaDD,betaU,alpha, u4U,B_notildeU, sqrt4pi): global smallb4_with_driftv_for_FFE_U import BSSN.ADMBSSN_tofrom_4metric as AB4m AB4m.g4DD_ito_BSSN_or_ADM("ADM",gammaDD,betaU,alpha) u4D = ixp.zerorank1(DIM=4) for mu in range(4): for nu in range(4): u4D[mu] += AB4m.g4DD[mu][nu]*u4U[nu] smallb4_with_driftv_for_FFE_U = ixp.zerorank1(DIM=4) # b^0 = 0 smallb4_with_driftv_for_FFE_U[0] = 0 # b^i = B^i / [alpha * u^0 * sqrt(4 pi)] for i in range(3): smallb4_with_driftv_for_FFE_U[i+1] = B_notildeU[i] / (alpha*u4U[0]*sqrt4pi)
def ADM_to_four_metric(gammaDD, betaU, alpha, returng4DD=True, returng4UU=False): # The ADM formulation decomposes Einstein's 4D equations into 3+1 dimensional form, with # the 3 spatial dimensions separated from the 1 temporal dimension. DIM here refers to # the spatial dimension. DIM = 3 # Eq 4.47 in [Gourgoulhon](https://arxiv.org/pdf/gr-qc/0703035.pdf): # g_{tt} = -\alpha^2 + \beta^k \beta_k # g_{ti} = \beta_i # g_{ij} = \gamma_{ij} # Eq. 2.121 in B&S betaD = ixp.zerorank1() for i in range(DIM): for j in range(DIM): betaD[i] += gammaDD[i][j] * betaU[j] # Now compute the beta contraction. beta2 = sp.sympify(0) for i in range(DIM): beta2 += betaU[i] * betaD[i] g4DD = ixp.zerorank2(DIM=4) g4DD[0][0] = -alpha**2 + beta2 for i in range(DIM): g4DD[i + 1][0] = g4DD[0][i + 1] = betaD[i] for j in range(DIM): g4DD[i + 1][j + 1] = gammaDD[i][j] if returng4DD == True and returng4UU == False: return g4DD gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD) # Eq. 4.49 in [Gourgoulhon](https://arxiv.org/pdf/gr-qc/0703035.pdf): # g^{tt} = -1 / alpha^2 # g^{ti} = beta^i / alpha^2 # g^{ij} = gamma^{ij} - beta^i beta^j / alpha^2 g4UU = ixp.zerorank2(DIM=4) g4UU[0][0] = -1 / alpha**2 for i in range(DIM): g4UU[i + 1][0] = g4UU[0][i + 1] = betaU[i] / alpha**2 for j in range(DIM): g4UU[i + 1][j + 1] = gammaUU[i][j] - betaU[i] * betaU[j] / alpha**2 if returng4DD == True and returng4UU == True: return g4DD, g4UU if returng4DD == False and returng4UU == True: return g4UU print( "Error: ADM_to_four_metric() called without requesting anything being returned!" ) exit(1)
def f_H_SS_2PN(m1, m2, S1U, S2U, nU, q): S0U = ixp.zerorank1() for i in range(3): S0U[i] = (1 + m2 / m1) * S1U[i] + (1 + m1 / m2) * S2U[i] global H_SS_2PN mu = m1 * m2 / (m1 + m2) H_SS_2PN = mu / (m1 + m2) * (3 * dot(S0U, nU)**2 - dot(S0U, S0U)) / (2 * q**3)