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:
$$\left\{\gamma_{ij}, K_{ij}, \alpha, \beta^i\right\}$$into BSSN variables
$$\left\{\bar{\gamma}_{i j},\bar{A}_{i j},\phi, K, \bar{\Lambda}^{i}, \alpha, \beta^i, B^i\right\},$$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.): $$ \bar{\gamma}_{i j} = \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \gamma_{ij}, $$ where we always make the choice $\bar{\gamma} = \hat{\gamma}$.
After constructing $\bar{\gamma}_{ij}$, we rescale to get $h_{ij}$ according to the prescription described in the the covariant BSSN formulation tutorial (also Ruchlin et al.):
$$ h_{ij} = (\bar{\gamma}_{ij} - \hat{\gamma}_{ij})/\text{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 $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.): \begin{align} K &= \gamma^{ij} K_{ij} \\ \bar{A}_{ij} &= \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \left(K_{ij} - \frac{1}{3} \gamma_{ij} K \right) \end{align}
After constructing $\bar{A}_{ij}$, we rescale to get $a_{ij}$ according to the prescription described in the the covariant BSSN formulation tutorial (also Ruchlin et al.):
$$ a_{ij} = \bar{A}_{ij}/\text{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 $\bar{\Lambda}^i$; rescale to compute $\lambda^i$ [Back to top]¶To define $\bar{\Lambda}^i$ we implement Eqs. 4 and 5 of Baumgarte et al.: $$ \bar{\Lambda}^i = \bar{\gamma}^{jk}\left(\bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk}\right). $$
The reference_metric.py module provides us with exact, closed-form expressions for $\hat{\Gamma}^i_{jk}$, so here we need only compute exact expressions for $\bar{\Gamma}^i_{jk}$, based on $\gamma_{ij}$ given as an explicit function of (xx0,xx1,xx2)
. This is particularly useful when setting up initial data.
After constructing $\bar{\Lambda}^i$, we rescale to get $\lambda^i$ according to the prescription described in the the covariant BSSN formulation tutorial (also Ruchlin et al.):
$$ \lambda^i = \bar{\Lambda}^i/\text{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
$$
\chi = e^{-4 \phi} = \exp\left(-4 \frac{1}{12} \left(\frac{\gamma}{\bar{\gamma}}\right)\right)
= \exp\left(-\frac{1}{3} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/3}.
$$
Finally if "BSSN_quantities::EvolvedConformalFactor_cf"
is set to "W"
, then
$$
W = e^{-2 \phi} = \exp\left(-2 \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) =
\exp\left(-\frac{1}{6} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) =
\left(\frac{\gamma}{\bar{\gamma}}\right)^{-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 $\beta^i$ and $B^i$ according to the prescription described in the the covariant BSSN formulation tutorial (also Ruchlin et al.): \begin{align} \mathcal{V}^i &= \beta^i/\text{ReU[i]}\\ \mathcal{B}^i &= B^i/\text{ReU[i]}. \end{align}
# 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