#!/usr/bin/env python # coding: utf-8 # # # # # C code generation of GR HD Equations # # ## Authors: Phil Chang & Zach Etienne # ### Formatting improvements courtesy Brandon Clark # # $\newcommand{\be}{\begin{equation}}$ # $\newcommand{\ee}{\end{equation}}$ # $\newcommand{\grad}{{\boldsymbol{\nabla}}}$ # $\newcommand{\vel}{{\boldsymbol{v}}}$ # $\newcommand{\mom}{{\boldsymbol{p}}}$ # $\newcommand{\ddt}[1]{{\frac{\partial #1}{\partial t}}}$ # $\newcommand{\ddx}[1]{{\frac{\partial #1}{\partial x}}}$ # $\newcommand{\state}{{\boldsymbol{\mathcal{U}}}}$ # $\newcommand{\charge}{{\boldsymbol{U}}}$ # $\newcommand{\psicharge}{{\boldsymbol{\psi}}}$ # $\newcommand{\lapse}{\alpha}$ # $\newcommand{\shift}{\boldsymbol{\beta}}$ # $\newcommand{\rhostar}{{\rho_*}}$ # $\newcommand{\tautilde}{{\tilde{\tau}}}$ # $\newcommand{\Svectilde}{{\tilde{\boldsymbol{S}}}}$ # $\newcommand{\rtgamma}{{\sqrt{\gamma}}}$ # $\newcommand{\T}[2]{{T^{#1 #2}}}$ # $\newcommand{\uvec}{{\boldsymbol{u}}}$ # $\newcommand{\Vvec}{{\boldsymbol{\mathcal{V}}}}$ # $\newcommand{\vfluid}{{\boldsymbol{v}_{\rm f}}}$ # $\newcommand{\vVal}{{\tilde{\boldsymbol{v}}}}$ # # $\newcommand{\flux}{{\boldsymbol{\mathcal{F}}}}$ # $\newcommand{\fluxV}{{\boldsymbol{F}}}$ # $\newcommand{\source}{{\boldsymbol{\mathcal{S}}}}$ # $\newcommand{\sourceV}{{\boldsymbol{S}}}$ # # $\newcommand{\area}{{\boldsymbol{A}}}$ # $\newcommand{\normal}{{\hat{\boldsymbol{n}}}}$ # $\newcommand{\pt}{{\boldsymbol{p}}}$ # $\newcommand{\nb}{{\boldsymbol{n}}}$ # $\newcommand{\meshv}{{\boldsymbol{w}}}$ # $\newcommand{\facev}{{\boldsymbol{\tilde{w}}_{ij}}}$ # $\newcommand{\facer}{{\boldsymbol{\tilde{r}}_{ij}}}$ # $\newcommand{\meshr}{{\boldsymbol{r}}}$ # $\newcommand{\cmr}{{\boldsymbol{c}}}$ # # ## Introduction: # We start out with the ** GRHD ** equations in conservative form with the state vector $\state=(\rhostar, \Svectilde, \tautilde)$: # \begin{equation} # \ddt{\state} + \grad\cdot\flux = \source, # \end{equation} # where $\rhostar = \lapse\rho\rtgamma u^0$, $\Svectilde = \rhostar h \uvec$, $\tautilde = \lapse^2\rtgamma \T00 - \rhostar$. The associated set of primitive variables are $(\rho, \vel, \epsilon)$, which are the rest mass density, fluid 3-velocity, and internal energy (measured in the rest frame). # # The flux, $\flux$ is given by # \begin{equation} # \flux=(\rhostar \vel, \lapse\rtgamma\T{j}{\beta}g_{\beta i}, \lapse^2\rtgamma\T0j - \rhostar\vel # \end{equation} # where $\vel$ is the 3-velocity, and $\source = (0, \frac 1 2 \lapse\rtgamma \T{\lapse}{\beta}g_{\lapse\beta,i}, s)$ is the source function, and # \begin{equation} # s = \lapse\rtgamma\left[\left(\T00\beta^i\beta^j + 2\T0i\beta^j\right)K_{ij} - \left(\T00\beta^i + \T0i\right)\partial_i\lapse\right] # \end{equation} # The stress energy tensor for a perfect fluid is written as # \begin{equation} # \T{\mu}{\nu} = \rho h u^{\mu} u^{\nu} + P g^{\mu\nu}, # \end{equation} # where $h = 1 + \epsilon + P/\rho$ is the specific enthalpy and $u^{\mu}$ are the respective components of the four velocity. # # Noting that the mass $\flux$ is defined in terms of $\rhostar$ and $\vel$, we need to first find a mapping between $\vel$ and $u$. # # ### Alternative formulation # # The Athena++ folks have an alternative formulations that might be superior. # Begin with the continuity equation # \begin{equation} # \grad_{\mu}\rho u^{\mu} = 0, # \end{equation} # where $\grad$ is the covariant derivative. This can be mapped directly to # \begin{equation} # \partial_{0} \sqrt{-g}\rho u^0 + \partial_i\sqrt{-g} \rho u^0 v^i = 0 # \end{equation} # which we can identify with $\rhostar = \alpha\rtgamma \rho u^0$ because $\sqrt{-g} = \alpha\rtgamma$. # # Now the second equation is conservation of energy-momentum which we write as # \begin{equation} # \grad_{\nu}T^{\nu}_{\mu} = 0 # \end{equation} # writing this out we have # \begin{equation} # \partial_0 g_{\mu\alpha}T^{\alpha 0} + \partial_i g_{\mu\alpha}T^{\alpha i} - \Gamma_{\mu\alpha}^{\gamma} g_{\gamma\beta}T^{\alpha\beta} = 0 # \end{equation} # Noting that # \begin{equation} # \Gamma^{\alpha}_{\beta\gamma} = \frac 1 2 g^{\alpha\delta}\left(\partial_{\gamma}g_{\beta\delta} + \partial_{\beta}g_{\gamma\delta} - \partial_{\delta}g_{\beta\gamma}\right) # \end{equation} # Writing this all out, we note the last term is # \begin{equation} # \Gamma_{\mu\alpha}^{\gamma} g_{\gamma\beta}T^{\alpha\beta} = # \frac 1 2 g^{\gamma\delta}\left(\partial_{\alpha}g_{\mu\delta} + \partial_{\mu}g_{\alpha \delta} - \partial_{\delta}g_{\mu \alpha}\right) T_{\gamma}^{\alpha} = # \frac 1 2 \left(\partial_{\alpha}g_{\mu\delta} + \partial_{\mu}g_{\alpha \delta} - \partial_{\delta}g_{\mu \alpha}\right) # T^{\alpha\delta} # \end{equation} # We sum over $\alpha$ and $\delta$, but noting that we are antisymmetric in first and last terms in $\alpha$ and $\delta$ in the () but symmetric in $T_{\alpha\delta}$ so we have # \begin{equation} # \Gamma_{\mu\alpha}^{\gamma} g_{\gamma\beta}T^{\alpha\beta} = \frac 1 2 \partial_{\mu}g_{\alpha \delta} T^{\alpha\delta} # \end{equation} # # Thus we have # \begin{equation} # \partial_0 T^{0}_{\mu} + \partial_i T^{i}_{\mu} = \frac 1 2 \partial_{\mu}g_{\alpha \delta} T^{\alpha\delta} # \end{equation} # The $\mu = (1,2,3)$, we almost get back the equations in the standard formulation # \begin{equation} # \partial_0 \rho h u^0 u_i + \partial_j T^j_i = \frac 1 2 \partial_{i}g_{\alpha \delta} T^{\alpha\delta}, # \end{equation} # which modulo a factors of $\lapse\rtgamma$ in front is the same as the "standard" equations. # # The $T^0_0$ term is more interesting. Here we have # \begin{equation} # \partial_0 (\rho h u^0 u_0 + + \partial_j T^j_i = \frac 1 2 \partial_{0}g_{\alpha \delta} T^{\alpha\delta}, # \end{equation} # # However the disadvantage is that we need the time derivative of the metric. # # # # Table of Contents # $$\label{toc}$$ # # This notebook is organized as follows # # 1. [Step 1](#mapping): Primitive to Conservative Mapping # 1. [Step 2](#zach): Compute $u^0$ from the Valencia 3-velocity (Zach step) # 1. [Step 3](#flux): Compute the flux # 1. [Step 4](#source): Source Terms # 1. [Step 5](#rotation): Rotation # 1. [Step 6](#solver): Conservative to Primitive Solver # 1. [Step 7](#lorentz): Lorentz Boosts # 1. [Step 8](#TmunuSph): Compute $T^{\mu\nu}$ in Cartesian Coordinates # 1. [Step 9](#latex_pdf_output): Output this notebook to $\LaTeX$-formatted PDF file # # # # Step 1: Primitive to Conservative Mapping # $$\label{mapping}$$ # # We want to make a mapping from the primitives to conserved variables following Zach notebook: # \begin{equation} # (\rho, \vel, \epsilon) \rightarrow (\rhostar = \lapse\rho\rtgamma u^0, \Svectilde = \rhostar h \uvec, \tautilde = \lapse^2\rtgamma \T00 - \rhostar). # \end{equation} # # In[1]: import GRHD.equations as Ge # NRPy: Implementation of GRHD equations in Cartesian coordinates import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends from outputC import outputC # NRPy+: Basic C code output functionality # declare gammaDD gammaDD = ixp.zerorank2() components = ["xx", "xy", "xz", "yy", "yz", "zz"] names = "" for comp in components : names = names + "mi.gamDD{0} ".format(comp) gxx, gxy, gxz, gyy, gyz, gzz = sp.symbols( names) gammaDD[0][0] = gxx gammaDD[0][1] = gxy gammaDD[0][2] = gxz gammaDD[1][0] = gxy gammaDD[1][1] = gyy gammaDD[1][2] = gyz gammaDD[2][0] = gxz gammaDD[2][1] = gyz gammaDD[2][2] = gzz #declare alpha alpha = sp.symbols( "mi.alpha") #declare beta betaU = ixp.zerorank1() for i, comp in enumerate(["X", "Y", "Z"]) : betaU[i] = sp.symbols( "mi.beta{0}".format(comp), real=True) #now get the primitives rho_b, epsilon, P = sp.symbols("rho ie p") #get the 3-velocities vU = ixp.zerorank1() for i, comp in enumerate( ["vx", "vy", "vz"]) : vU[i] = sp.symbols("{0}".format(comp)) Ge.u4U_in_terms_of_vU__rescale_vU_by_applying_speed_limit(alpha,betaU,gammaDD, vU) u4U = Ge.u4U_ito_vU # Zach says: Probably want to adopt speed-limited vU[i], Ge.rescaledvU[i], here, a la: # for i in range(3): # ... vU[i] = Ge.rescaledvU[i] # First compute stress-energy tensor T4UU and T4UD: Ge.compute_T4UU(gammaDD,betaU,alpha, rho_b,P,epsilon,u4U) Ge.compute_T4UD(gammaDD,betaU,alpha, Ge.T4UU) # Next sqrt(gamma) Ge.compute_sqrtgammaDET(gammaDD) # Compute conservative variables in terms of primitive variables Ge.compute_rho_star( alpha, Ge.sqrtgammaDET, rho_b, u4U) Ge.compute_tau_tilde(alpha, Ge.sqrtgammaDET, Ge.T4UU,Ge.rho_star) Ge.compute_S_tildeD( alpha, Ge.sqrtgammaDET, Ge.T4UD) # Zach says: Why only output u^x? Debugging reasons? outputC([u4U[1], Ge.rho_star, Ge.S_tildeD[0], Ge.S_tildeD[1], Ge.S_tildeD[2], Ge.tau_tilde], ["u4U1", "con[iRhoStar]", "con[iSx]", "con[iSy]", "con[iSz]", "con[iTau]"], filename="NRPY+prim2Con.h", params="outCverbose=False") get_ipython().system('cat NRPY+prim2Con.h') outputC([Ge.sqrtgammaDET*alpha], ["detg"], filename="NRPY+detg.h") get_ipython().system('cat NRPY+detg.h') gammaUU, gammabarDet = ixp.symm_matrix_inverter3x3(gammaDD) outputC([gammaUU[0][0],gammaUU[0][1],gammaUU[0][2],gammaUU[1][1],gammaUU[1][2],gammaUU[2][2]], [ "gamUUxx", "gamUUxy", "gamUUxz", "gamUUyy", "gamUUyz", "gamUUzz"], filename="NRPY+gamUU.h") get_ipython().system('cat NRPY+gamUU.h') # # # # Step 3: Compute the flux # $$\label{flux}$$ # # The fluxes are as follows # \begin{equation} # \frac{\partial}{\partial t} # \begin{pmatrix} # \rhostar\\ # \Svectilde\\ # \tautilde # \end{pmatrix} + \frac{\partial}{\partial x^j}\begin{pmatrix} \rhostar v^j\\ # \lapse\rtgamma T^j_i\\ \lapse^2\rtgamma T^{0j} - \rhostar v^j # \end{pmatrix} = \begin{pmatrix} 0 \\ \frac 1 2 \lapse\rtgamma T^{\alpha\beta}g_{\alpha\beta,i} \\ s \end{pmatrix} # \end{equation} # so the flux is # \begin{equation} # \mathcal{F} = \begin{pmatrix} \rhostar v^i \\ \lapse\rtgamma T^i_k \\ \lapse^2\rtgamma T^{0i} - \rhostar v^i # \end{pmatrix} # \end{equation} # In the moving-mesh formalism, the flux is just taken along the x directions so we have # \begin{equation} # \mathcal{F} = \begin{pmatrix} \rhostar v^1 \\ \lapse\rtgamma T^1_k \\ \lapse^2\rtgamma T^{01} - \rhostar v^1 # \end{pmatrix} # \end{equation} # Note that we will need to rotate $T^{\mu\nu}$ and $g_{\mu\nu}$ to get the right orientation. # In order to do this, we must first compute the stress energy tensor: # \begin{equation} # T^{\mu\nu} = \rho h u^{\mu}u^{\nu} + Pg^{\mu\nu} = \rho h (u^0)^2v^iv^j + P g^{\mu\nu} # \end{equation} # # In[2]: # Next compute fluxes of conservative variables Ge.compute_rho_star_fluxU( vU, Ge.rho_star) Ge.compute_tau_tilde_fluxU(alpha, Ge.sqrtgammaDET, vU,Ge.T4UU,Ge.rho_star) Ge.compute_S_tilde_fluxUD( alpha, Ge.sqrtgammaDET, Ge.T4UD) normD = ixp.zerorank1() normD[0], normD[1], normD[2] = sp.symbols("norm[0] norm[1] norm[2]", real=True) faceVelU = ixp.zerorank1() faceVelU[0], faceVelU[1], faceVelU[2] = sp.symbols("faceVelocity[0] faceVelocity[1] faceVelocity[2]", real=True) # Zach says: don't forget to limit the velocities after they are computed! faceVelNorm = sp.sympify(0) for i in range(3) : faceVelNorm += normD[i]*faceVelU[i] exprArray = [] nameArray = [] exprArray.append( Ge.rho_star) nameArray.append( "temp_rho_star") exprArray.append( Ge.T4UU[0][1]) nameArray.append( "temp_T4UU01") rho_star_flux = sp.sympify(0) for i in range(3) : rho_star_flux += Ge.rho_star_fluxU[i]*normD[i] rho_star_flux -= Ge.rho_star*faceVelNorm exprArray.append( rho_star_flux) nameArray.append( "flux[iRhoStar]") tau_tilde_flux = sp.sympify(0) for i in range(3) : tau_tilde_flux += Ge.tau_tilde_fluxU[i]*normD[i] tau_tilde_flux -= Ge.tau_tilde*faceVelNorm S_tilde_fluxD = ixp.zerorank1() for i in range(3) : S_tilde_fluxD[i] -= Ge.S_tildeD[i]*faceVelNorm for j in range(3) : S_tilde_fluxD[i] += Ge.S_tilde_fluxUD[j][i]*normD[j] for j, comp in enumerate(["x","y", "z"]) : exprArray.append( S_tilde_fluxD[j]) nameArray.append( "flux[iS{0}]".format(comp)) exprArray.append( tau_tilde_flux) nameArray.append( "flux[iTau]") #for expr, name in zip( exprArray, nameArray) : # print( name) outputC(exprArray, nameArray, filename="NRPY+calFlux.h", params="outCverbose=False") get_ipython().system('cat NRPY+calFlux.h') # # # # Step 4: Source Terms # $$\label{source}$$ # # The sources terms are for mass, momentum and energy are: # \begin{equation} # \source = (0, \frac 1 2 \lapse\rtgamma \T{\alpha}{\beta}g_{\alpha\beta,i}, s), # \end{equation} # For a time stationary metric $s\neq 0$, so we will ignore this until the next section. As for the rest, we need to define derivatives of the metric. Suppose I have done this already. Then the code for the source terms is: # # In[3]: # FIXME: Assume static spacetime with KDD = betaU = betaU_dD = 0 KDD = ixp.zerorank2() betaU = ixp.zerorank1() betaU_dD = ixp.zerorank2() # Set+evaluate derivatives of alpha, performing 2nd-order finite difference alpha_dD = ixp.zerorank1() h = sp.symbols("h") for i in range(3) : alpha_plus, alpha_minus = sp.symbols("mi_plus[{0}].alpha mi_minus[{0}].alpha".format(i)) alpha_dD[i] = (alpha_plus - alpha_minus)/(2*h) # Set+evaluate derivatives of gamma_{ij}, performing 2nd-order finite difference gammaDD_dD = ixp.zerorank3() components = ["xx", "xy", "xz", "yy", "yz", "zz"] for i in range(3) : names_plus = "" names_minus = "" for comp in components : names_plus = names_plus + "mi_plus[{0}].gamDD{1} ".format(i, comp) names_minus = names_minus + "mi_minus[{0}].gamDD{1} ".format(i, comp) gxx_plus, gxy_plus, gxz_plus, gyy_plus, gyz_plus, gzz_plus = sp.symbols( names_plus) gxx_minus, gxy_minus, gxz_minus, gyy_minus, gyz_minus, gzz_minus = sp.symbols( names_minus) gammaDD_dD[0][0][i] = (gxx_plus - gxx_minus)/(2*h) gammaDD_dD[0][1][i] = (gxy_plus - gxy_minus)/(2*h) gammaDD_dD[0][2][i] = (gxz_plus - gxz_minus)/(2*h) gammaDD_dD[1][0][i] = (gxy_plus - gxy_minus)/(2*h) gammaDD_dD[1][1][i] = (gyy_plus - gyy_minus)/(2*h) gammaDD_dD[1][2][i] = (gyz_plus - gyz_minus)/(2*h) gammaDD_dD[2][0][i] = (gxz_plus - gxz_minus)/(2*h) gammaDD_dD[2][1][i] = (gyz_plus - gyz_minus)/(2*h) gammaDD_dD[2][2][i] = (gzz_plus - gzz_minus)/(2*h) # Compute g_{mu nu, i} based on ADM quantities & derivatives defined above Ge.compute_g4DD_zerotimederiv_dD(gammaDD,betaU,alpha, gammaDD_dD,betaU_dD,alpha_dD) # Compute source terms for tau tilde & S tilde: Ge.compute_s_source_term(KDD,betaU,alpha, Ge.sqrtgammaDET,alpha_dD, Ge.T4UU) Ge.compute_S_tilde_source_termD( alpha, Ge.sqrtgammaDET,Ge.g4DD_zerotimederiv_dD, Ge.T4UU) exprArray = [] nameArray = [] #momentum terms for i in range(3) : exprArray.append( Ge.S_tilde_source_termD[i]) nameArray.append( "vSource[{0}]".format(i)) #tau term exprArray.append( Ge.s_source_term) nameArray.append( "eSource") outputC( exprArray, nameArray, filename="NRPY+calSources.h", params="outCverbose=False") get_ipython().system('cat NRPY+calSources.h') # # # # Step 6: Conservative to Primitive Solver # $$\label{solver}$$ # # We now discuss the reverse mapping from conservative to primitive variables. # Given the lapse, shift vector and $\rtgamma$, the mapping between primitive and conserved variable is straightforward. However, the reverse is not as simple. In GRMHD, the conservative to primitive solver is amplified by the inclusion of the magnetic field, leading to rather sophisticated root finding strategies. The failure rates of these algorithms are low (??), but since this algorithm may be executed several times per timestep for every gridpoint, even a low failure can give unacceptable collective failure rates. However, for purely polytropic equations of state, e.g., $P\propto\rho^{\Gamma_1}$, the convervative to primitive variable solver is greatly simplified. # # To construct the conservative-to-primitive variable solver, we restrict ourselves to polytropic equations of states # \begin{equation} # P = P_0\left(\frac{\rho}{\rho_0}\right)^{\Gamma_1} \quad\textrm{and}\quad \epsilon = \epsilon_0\left(\frac{\rho}{\rho_0}\right)^{\Gamma_1-1}, # \end{equation} # where $P_0$, $\rho_0$, and $\epsilon_0$ are the fiducial pressure, density, and internal energy, and we have used the relation $P = (\Gamma_1 - 1)\rho\epsilon$. # # For such a polytropic equation of state, the energy equation is redundant and effectively we are only concerned with the continuity and momentum equations. The conservative variables of concern are $\rhostar$ and $\Svectilde$. Noting that the shift, $\alpha$, and $\rtgamma$ are provided by the Einsteins field equation solver, we can write # \begin{equation} # u^0 = \frac{\rhostar}{\alpha\rtgamma\rho} = u^0(\rho) \quad\textrm{and}\quad \uvec = \frac{\Svectilde}{\alpha\rtgamma\rho h} = \uvec(\rho). # \end{equation} # Noting that the four velocity $u^2 = g_{\mu\nu}u^{\mu}u^{\nu} = g^{00}u^0u^0 + 2g^{0i}u^0\uvec^i + g_{ij}\uvec^i\uvec^j = -1$, we have # \begin{equation} # 0 = f(\rho)\equiv \alpha^2\gamma\rho^2h^2 + \left(-\lapse^2 + \shift\cdot\shift\right)\rhostar^2h^2 + 2h\rhostar\shift\cdot\Svectilde + \Svectilde\cdot\Svectilde, # \end{equation} # which is an implicit equation of either $\rho$ or $u^0$, where $h(\rho = \rhostar/(\alpha\rtgamma u^0)) = 1 + \gamma_1 \epsilon$ which can be inverted by standard nonlinear root finding algorithms, e.g., Newton-raphson. # # We put this all together to define a function, $f(\rho)$, whose root is zero that we will find via Newton-raphson. # # Several checks must be performed: # # 1. $\rhostar > 0$ : This check is performed at the very beginning # # 2. $\rho > \rho_{\rm min}$ : This check is performed after the fact # # 3. $u_0 < \alpha^{-1}\Gamma_{\rm max}$ : This check is performed after the fact as well # In[4]: DIM = 3 # Declare rank-1 contravariant ("v") vector vU = ixp.declarerank1("vU") shiftU = ixp.zerorank1() rho, gamma1 = sp.symbols("rho gamma") Sx, Sy, Sz = sp.symbols("con[iSx] con[iSy] con[iSz]") p0, rho0, rhostar = sp.symbols("p_0 rho_0 rhostar") # Declare rank-2 covariant gmunu #gammaDD = ixp.declarerank2("gammaDD","sym01") StildeD = ixp.declarerank1("StildeD") lapse, beta_x, beta_y, beta_z = sp.symbols( "mi.alpha mi.betaX mi.betaY mi.betaZ") rtgamma = Ge.sqrtgammaDET shiftU[0] = beta_x shiftU[1] = beta_y shiftU[2] = beta_z StildeD[0] = Sx StildeD[1] = Sy StildeD[2] = Sz # gamma = rtgamma*rtgamma <- unused lapse2 = lapse*lapse uU0 = rhostar/(lapse*rtgamma*rho) epsilon = p0/rho0*(rho/rho0)**(gamma1 - 1)/(gamma1 - 1) h = 1 + gamma1*epsilon beta2 = 0. for i in range(DIM) : for j in range(DIM) : beta2 += gammaDD[i][j] * shiftU[i]*shiftU[j] betaDotStilde = 0 for i in range(DIM) : betaDotStilde += shiftU[i]*StildeD[i] # Note that this is |Stilde|^2, where the absolute value denotes # that this is not a proper contraction of Stilde_i, as # Stilde^i is NOT equal to gamma^{ij} Stilde_j (to understand # why this is, notice that Stilde_i is proportional to the # *4D* stress-energy tensor.) Stilde2 = 0 for i in range(DIM) : for j in range(DIM) : Stilde2 += gammaUU[i][j] * StildeD[i]*StildeD[j] f = rhostar**2*h**2 + (-lapse2 + beta2)*rhostar**2.*h**2.*uU0**2 + 2.*h*rhostar*betaDotStilde*uU0 + Stilde2 outputC(f,"rootRho",filename="NRPY+rhoRoot.h") outputC(Stilde2, "Stilde2", filename="NRPY+Stilde2.h") get_ipython().system('cat NRPY+rhoRoot.h') get_ipython().system('cat NRPY+Stilde2.h') # The root solve above finds $\rho$, which then allows us to get # \begin{equation} # u^0 = \frac{\rhostar}{\alpha\rtgamma\rho}\quad\textrm{and}\quad \vel = \frac{\uvec}{u^0} = \frac{\Svectilde}{\rhostar h(\rho)}. # \end{equation} # and thus we can find the rest of the primitives. # In[5]: #rhostar = sp.symbols("rhostar") #StildeU = ixp.declarerank1("StildeU") velU = ixp.zerorank1() #lapse, rtgamma, rho, gamma1, c = sp.symbols("lapse rtgamma rho gamma1 c") rho, rhostar = sp.symbols("testPrim[iRho] con[iRhoStar]") u0 = rhostar/(lapse*rtgamma*rho) epsilon = p0/rho0*(rho/rho0)**(gamma1 - 1)/(gamma1 - 1) h = 1. + gamma1*epsilon for i in range(DIM) : for j in range(DIM) : velU[i] += gammaUU[i][j]*StildeD[j]/(rhostar * h)/u0 outputC([h,u0,velU[0],velU[1],velU[2]], ["h", "u0","testPrim[ivx]", "testPrim[ivy]", "testPrim[ivz]"],filename="NRPY+getv.h") # # # # Step 7: Lorentz Boosts # $$\label{lorentz}$$ # # We need to boost to the frame of the moving face. The boost is # \begin{equation} # B(\beta) =\begin{pmatrix} # \gamma & -\beta\gamma n_x & -\beta\gamma n_y & -\beta\gamma n_z \\ # -\beta\gamma n_x & 1 + (\gamma-1)n_x^2 & (\gamma-1)n_x n_y & (\gamma-1)n_x n_z\\ # -\beta\gamma n_x & (\gamma-1)n_y n_x & 1 + (\gamma-1)n_y^2 & (\gamma-1)n_y n_z\\ # -\beta\gamma n_x & (\gamma-1) n_z n_x & (\gamma-1)n_z n_x & 1 + (\gamma-1)n_z^2 # \end{pmatrix} # \end{equation} # And the boost is $X' = B(\beta) X$, where $X'$ and $X$ are four vectors. # # So the rest of this is straightforward. # # # # Step 8: Compute $T^{\mu\nu}$ in Cartesian Coordinates # $$\label{TmunuSph}$$ # # # In[6]: # declare gammaDD gammaDD = ixp.zerorank2() components = ["xx", "xy", "xz", "yy", "yz", "zz"] names = "" for comp in components : names = names + "mi.gamDD{0} ".format(comp) g11, g12, g13, g22, g23, g33 = sp.symbols( names) gammaDD[0][0] = g11 gammaDD[0][1] = g12 gammaDD[0][2] = g13 gammaDD[1][0] = g12 gammaDD[1][1] = g22 gammaDD[1][2] = g23 gammaDD[2][0] = g13 gammaDD[2][1] = g23 gammaDD[2][2] = g33 #declare alpha alpha = sp.symbols( "mi.alpha") #declare beta betaU = ixp.zerorank1() for i, comp in enumerate(["X", "Y", "Z"]) : betaU[i] = 0 #NEED A BETTER WAY sp.symbols( "mi.beta{0}".format(comp), real=True) #now get the primitives rho_b, epsilon, P = sp.symbols("rho ie press") #get the 3-velocities vU = ixp.zerorank1() for i, comp in enumerate( ["vx", "vy", "vz"]) : vU[i] = sp.symbols("{0}".format(comp)) Ge.u4U_in_terms_of_vU__rescale_vU_by_applying_speed_limit(alpha,betaU,gammaDD, vU) u4U = Ge.u4U_ito_vU # First compute stress-energy tensor T4UU and T4UD in Spherical Coordinates: Ge.compute_T4UU(gammaDD,betaU,alpha, rho_b,P,epsilon,u4U) Ge.compute_T4UD(gammaDD,betaU,alpha, Ge.T4UU) outputC([Ge.T4UU[0][0],Ge.T4UU[1][1],Ge.T4UU[2][2],Ge.T4UU[3][3] ], ["T4UU_diag[0]", "T4UU_diag[1]","T4UU_diag[2]", "T4UU_diag[3]"],filename="NRPY+getT4UU.h") # # # # Step 9: Output this notebook to $\LaTeX$-formatted PDF file \[Back to [top](#toc)\] # $$\label{latex_pdf_output}$$ # # The following code cell converts this Jupyter notebook into a proper, clickable $\LaTeX$-formatted PDF file. After the cell is successfully run, the generated PDF may be found in the root NRPy+ tutorial directory, with filename # [Tutorial-GRHD_Equations-Cartesian-c-code.pdf](Tutorial-GRHD_Equations-Cartesian-c-code.pdf) (Note that clicking on this link may not work; you may need to open the PDF file through another means.) # In[7]: import cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface cmd.output_Jupyter_notebook_to_LaTeXed_PDF("Tutorial-GRHD_Equations-Cartesian-c-code")