Notebook Status: Self-Validated
Validation Notes: This tutorial notebook has been confirmed to be self-consistent with its corresponding NRPy+ module, as documented below. Additional validation tests may have been performed, but are as yet, undocumented. (TODO)
This module documents the conversion of ADM variables:
{γij,Kij,α,βi}into BSSN variables
{ˉγij,ˉAij,ϕ,K,ˉΛi,α,βi,Bi},in the desired curvilinear basis (given by reference_metric::CoordSystem
). Then it rescales the resulting BSSNCurvilinear variables (as defined in the covariant BSSN formulation tutorial) into the form needed for solving Einstein's equations with the BSSN formulation:
This notebook is organized as follows
BSSN.BSSN_in_terms_of_ADM
NRPy+ module# Step 1: Import needed core 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 reference_metric as rfm # NRPy+: Reference metric support
import sys # Standard Python modules for multiplatform OS-level functions
import BSSN.BSSN_quantities as Bq # NRPy+: This module depends on the parameter EvolvedConformalFactor_cf,
# which is defined in BSSN.BSSN_quantities
# Step 1.a: Set DIM=3, as we're using a 3+1 decomposition of Einstein's equations
DIM=3
We have (Eqs. 2 and 3 of Ruchlin et al.): ˉγij=(ˉγγ)1/3γij,
After constructing ˉγij, we rescale to get hij according to the prescription described in the the covariant BSSN formulation tutorial (also Ruchlin et al.):
hij=(ˉγij−ˆγij)/ReDD[i][j].# Step 2: All ADM quantities were input into this function in the Spherical or Cartesian
# basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above,
# we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2.
# Here we convert ADM quantities to their BSSN Curvilinear counterparts:
# Step 2.a: Convert ADM $\gamma_{ij}$ to BSSN $\bar{gamma}_{ij}$:
# We have (Eqs. 2 and 3 of [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)):
def gammabarDD_hDD(gammaDD):
global gammabarDD,hDD
if rfm.have_already_called_reference_metric_function == False:
print("BSSN.BSSN_in_terms_of_ADM.hDD_given_ADM(): Must call reference_metric() first!")
sys.exit(1)
# \bar{gamma}_{ij} = (\frac{\bar{gamma}}{gamma})^{1/3}*gamma_{ij}.
_gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD)
gammabarDD = ixp.zerorank2()
hDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
gammabarDD[i][j] = (rfm.detgammahat/gammaDET)**(sp.Rational(1,3))*gammaDD[i][j]
hDD[i][j] = (gammabarDD[i][j] - rfm.ghatDD[i][j]) / rfm.ReDD[i][j]
Convert the ADM extrinsic curvature Kij to the trace-free extrinsic curvature ˉAij, plus the trace of the extrinsic curvature K, where (Eq. 3 of Baumgarte et al.): K=γijKijˉAij=(ˉγγ)1/3(Kij−13γijK)
After constructing ˉAij, we rescale to get aij according to the prescription described in the the covariant BSSN formulation tutorial (also Ruchlin et al.):
aij=ˉAij/ReDD[i][j].# Step 2.b: Convert the extrinsic curvature K_{ij} to the trace-free extrinsic
# curvature \bar{A}_{ij}, plus the trace of the extrinsic curvature K,
# where (Eq. 3 of [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)):
def trK_AbarDD_aDD(gammaDD,KDD):
global trK,AbarDD,aDD
if rfm.have_already_called_reference_metric_function == False:
print("BSSN.BSSN_in_terms_of_ADM.trK_AbarDD(): Must call reference_metric() first!")
sys.exit(1)
# \bar{gamma}_{ij} = (\frac{\bar{gamma}}{gamma})^{1/3}*gamma_{ij}.
gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD)
# K = gamma^{ij} K_{ij}, and
# \bar{A}_{ij} &= (\frac{\bar{gamma}}{gamma})^{1/3}*(K_{ij} - \frac{1}{3}*gamma_{ij}*K)
trK = sp.sympify(0)
for i in range(DIM):
for j in range(DIM):
trK += gammaUU[i][j]*KDD[i][j]
AbarDD = ixp.zerorank2()
aDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
AbarDD[i][j] = (rfm.detgammahat/gammaDET)**(sp.Rational(1,3))*(KDD[i][j] - sp.Rational(1,3)*gammaDD[i][j]*trK)
aDD[i][j] = AbarDD[i][j] / rfm.ReDD[i][j]
(xx0,xx1,xx2)
, convert to BSSN ˉΛi; rescale to compute λi [Back to top]¶To define ˉΛi we implement Eqs. 4 and 5 of Baumgarte et al.: ˉΛi=ˉγjk(ˉΓijk−ˆΓijk).
The reference_metric.py module provides us with exact, closed-form expressions for ˆΓijk, so here we need only compute exact expressions for ˉΓijk, based on γij given as an explicit function of (xx0,xx1,xx2)
. This is particularly useful when setting up initial data.
After constructing ˉΛi, we rescale to get λi according to the prescription described in the the covariant BSSN formulation tutorial (also Ruchlin et al.):
λi=ˉΛi/ReU[i].# Step 2.c: Define \bar{Lambda}^i (Eqs. 4 and 5 of [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)):
def LambdabarU_lambdaU__exact_gammaDD(gammaDD):
global LambdabarU,lambdaU
# \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])
for i in range(DIM):
# We evaluate LambdabarU[i] here to ensure proper cancellations. If these cancellations
# are not applied, certain expressions (e.g., lambdaU[0] in StaticTrumpet) will
# cause SymPy's (v1.5+) CSE algorithm to hang
LambdabarU[i] = LambdabarU[i].doit()
lambdaU = ixp.zerorank1()
for i in range(DIM):
lambdaU[i] = LambdabarU[i] / rfm.ReU[i]
cf
[Back to top]¶We define the conformal factor variable cf
based on the setting of the "BSSN_quantities::EvolvedConformalFactor_cf"
parameter.
For example if "BSSN_quantities::EvolvedConformalFactor_cf"
is set to "phi"
, we can use Eq. 3 of Ruchlin et al., which in arbitrary coordinates is written:
Alternatively if "BSSN_quantities::EvolvedConformalFactor_cf"
is set to "chi"
, then
χ=e−4ϕ=exp(−4112(γˉγ))=exp(−13log(γˉγ))=(γˉγ)−1/3.
Finally if "BSSN_quantities::EvolvedConformalFactor_cf"
is set to "W"
, then
W=e−2ϕ=exp(−2112log(γˉγ))=exp(−16log(γˉγ))=(γˉγ)−1/6.
# Step 2.d: Set the conformal factor variable cf, which is set
# by the "BSSN_quantities::EvolvedConformalFactor_cf" parameter. For example if
# "EvolvedConformalFactor_cf" is set to "phi", we can use Eq. 3 of
# [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf),
# which in arbitrary coordinates is written:
def cf_from_gammaDD(gammaDD):
global cf
# \bar{Lambda}^i = \bar{gamma}^{jk}(\bar{Gamma}^i_{jk} - \hat{Gamma}^i_{jk}).
gammabarDD_hDD(gammaDD)
_gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD)
_gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD)
cf = sp.sympify(0)
if par.parval_from_str("EvolvedConformalFactor_cf") == "phi":
# phi = \frac{1}{12} log(\frac{gamma}{\bar{gamma}}).
cf = sp.Rational(1,12)*sp.log(gammaDET/gammabarDET)
elif par.parval_from_str("EvolvedConformalFactor_cf") == "chi":
# chi = exp(-4*phi) = exp(-4*\frac{1}{12}*(\frac{gamma}{\bar{gamma}}))
# = exp(-\frac{1}{3}*log(\frac{gamma}{\bar{gamma}})) = (\frac{gamma}{\bar{gamma}})^{-1/3}.
#
cf = (gammaDET/gammabarDET)**(-sp.Rational(1,3))
elif par.parval_from_str("EvolvedConformalFactor_cf") == "W":
# W = exp(-2*phi) = exp(-2*\frac{1}{12}*log(\frac{gamma}{\bar{gamma}}))
# = exp(-\frac{1}{6}*log(\frac{gamma}{\bar{gamma}})) = (\frac{gamma}{bar{gamma}})^{-1/6}.
cf = (gammaDET/gammabarDET)**(-sp.Rational(1,6))
else:
print("Error EvolvedConformalFactor_cf type = \""+par.parval_from_str("EvolvedConformalFactor_cf")+"\" unknown.")
sys.exit(1)
We rescale βi and Bi according to the prescription described in the the covariant BSSN formulation tutorial (also Ruchlin et al.): Vi=βi/ReU[i]Bi=Bi/ReU[i].
# Step 2.e: Rescale beta^i and B^i according to the prescription described in
# the [BSSN in curvilinear coordinates tutorial notebook](Tutorial-BSSNCurvilinear.ipynb)
# (also [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)):
#
# \mathcal{V}^i &= beta^i/(ReU[i])
# \mathcal{B}^i &= B^i/(ReU[i])
def betU_vetU(betaU,BU):
global vetU,betU
if rfm.have_already_called_reference_metric_function == False:
print("BSSN.BSSN_in_terms_of_ADM.bet_vet(): Must call reference_metric() first!")
sys.exit(1)
vetU = ixp.zerorank1()
betU = ixp.zerorank1()
for i in range(DIM):
vetU[i] = betaU[i] / rfm.ReU[i]
betU[i] = BU[i] / rfm.ReU[i]
BSSN.BSSN_in_terms_of_ADM
module [Back to top]¶Here, as a code validation check, we verify agreement in the SymPy expressions for UIUC initial data between
As no basis transformation is performed, we analyze these expressions in their native, Spherical coordinates.
# Step 3.a: Set the desired *output* coordinate system to Spherical:
par.set_parval_from_str("reference_metric::CoordSystem","Spherical")
rfm.reference_metric()
# Step 3.b: Set up initial data; assume UIUC spinning black hole initial data
import BSSN.UIUCBlackHole as uibh
uibh.UIUCBlackHole()
# Step 3.c: Call above functions to convert ADM to BSSN curvilinear
gammabarDD_hDD( uibh.gammaDD)
trK_AbarDD_aDD( uibh.gammaDD,uibh.KDD)
LambdabarU_lambdaU__exact_gammaDD(uibh.gammaDD)
cf_from_gammaDD( uibh.gammaDD)
betU_vetU( uibh.betaU,uibh.BU)
# Step 3.d: Now load the BSSN_in_terms_of_ADM module and perform the same conversion
import BSSN.BSSN_in_terms_of_ADM as BitoA
BitoA.gammabarDD_hDD( uibh.gammaDD)
BitoA.trK_AbarDD_aDD( uibh.gammaDD,uibh.KDD)
BitoA.LambdabarU_lambdaU__exact_gammaDD(uibh.gammaDD)
BitoA.cf_from_gammaDD( uibh.gammaDD)
BitoA.betU_vetU( uibh.betaU,uibh.BU)
# Step 3.e: Perform the consistency check
def compare(q1, q2, q1name, q2name):
if sp.simplify(q1 - q2) != 0:
print("Error: "+q1name+" - "+q2name+" = "+str(sp.simplify(q1 - q2)))
sys.exit(1)
print("Consistency check between this tutorial notebook and BSSN.BSSN_in_terms_of_ADM NRPy+ module:")
compare(cf, BitoA.cf, "cf", "BitoA.cf")
compare(trK, BitoA.trK, "trK", "BitoA.trK")
# alpha is the only variable that remains unchanged:
# print("alpha - BitoA.alpha = " + str(alpha - BitoA.alpha))
for i in range(DIM):
compare(vetU[i], BitoA.vetU[i], "vetU"+str(i), "BitoA.vetU"+str(i))
compare(betU[i], BitoA.betU[i], "betU"+str(i), "BitoA.betU"+str(i))
compare(lambdaU[i], BitoA.lambdaU[i], "lambdaU"+str(i), "BitoA.lambdaU"+str(i))
for j in range(DIM):
compare(hDD[i][j], BitoA.hDD[i][j], "hDD"+str(i)+str(j), "BitoA.hDD"+str(i)+str(j))
compare(aDD[i][j], BitoA.aDD[i][j], "aDD"+str(i)+str(j), "BitoA.aDD"+str(i)+str(j))
print("ALL TESTS PASS")
Consistency check between this tutorial notebook and BSSN.BSSN_in_terms_of_ADM NRPy+ module: ALL TESTS PASS
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-BSSN_in_terms_of_ADM.pdf (Note that clicking on this link may not work; you may need to open the PDF file through another means.)
import cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface
cmd.output_Jupyter_notebook_to_LaTeXed_PDF("Tutorial-BSSN_in_terms_of_ADM")
Created Tutorial-BSSN_in_terms_of_ADM.tex, and compiled LaTeX file to PDF file Tutorial-BSSN_in_terms_of_ADM.pdf