5MZ436MULWYB6UYDJ26MWB7SUPJEN4NOXW5B5HEDIINWN32HINDAC
% *======================================================================*
% Cactus Thorn template for ThornGuide documentation
% Author: Ian Kelley
% Date: Sun Jun 02, 2002
% $Header$
%
% Thorn documentation in the latex file doc/documentation.tex
% will be included in ThornGuides built with the Cactus make system.
% The scripts employed by the make system automatically include
% pages about variables, parameters and scheduling parsed from the
% relevant thorn CCL files.
%
% This template contains guidelines which help to assure that your
% documentation will be correctly added to ThornGuides. More
% information is available in the Cactus UsersGuide.
%
% Guidelines:
% - Do not change anything before the line
% % START CACTUS THORNGUIDE",
% except for filling in the title, author, date, etc. fields.
% - Each of these fields should only be on ONE line.
% - Author names should be separated with a \\ or a comma.
% - You can define your own macros, but they must appear after
% the START CACTUS THORNGUIDE line, and must not redefine standard
% latex commands.
% - To avoid name clashes with other thorns, 'labels', 'citations',
% 'references', and 'image' names should conform to the following
% convention:
% ARRANGEMENT_THORN_LABEL
% For example, an image wave.eps in the arrangement CactusWave and
% thorn WaveToyC should be renamed to CactusWave_WaveToyC_wave.eps
% - Graphics should only be included using the graphicx package.
% More specifically, with the "\includegraphics" command. Do
% not specify any graphic file extensions in your .tex file. This
% will allow us to create a PDF version of the ThornGuide
% via pdflatex.
% - References should be included with the latex "\bibitem" command.
% - Use \begin{abstract}...\end{abstract} instead of \abstract{...}
% - Do not use \appendix, instead include any appendices you need as
% standard sections.
% - For the benefit of our Perl scripts, and for future extensions,
% please use simple latex.
%
% *======================================================================*
%
% Example of including a graphic image:
% \begin{figure}[ht]
% \begin{center}
% \includegraphics[width=6cm]{MyArrangement_MyThorn_MyFigure}
% \end{center}
% \caption{Illustration of this and that}
% \label{MyArrangement_MyThorn_MyLabel}
% \end{figure}
%
% Example of using a label:
% \label{MyArrangement_MyThorn_MyLabel}
%
% Example of a citation:
% \cite{MyArrangement_MyThorn_Author99}
%
% Example of including a reference
% \bibitem{MyArrangement_MyThorn_Author99}
% {J. Author, {\em The Title of the Book, Journal, or periodical}, 1 (1999),
% 1--16. {\tt http://www.nowhere.com/}}
%
% *======================================================================*
% If you are using CVS use this line to give version information
% $Header$
\documentclass{article}
% Use the Cactus ThornGuide style file
% (Automatically used from Cactus distribution, if you have a
% thorn without the Cactus Flesh download this from the Cactus
% homepage at www.cactuscode.org)
\usepackage{../../../../doc/latex/cactus}
\begin{document}
% The author of the documentation
\author{Roland Haas \textless rhaas@illinois.edu\textgreater}
% The title of the document (not necessarily the name of the Thorn)
\title{BaikalX}
% the date your document was last changed, if your document is in CVS,
% please use:
% \date{$ $Date$ $}
\date{December 16 2019}
\maketitle
% Do not delete next line
% START CACTUS THORNGUIDE
% Add all definitions used in this documentation here
% \def\mydef etc
% Add an abstract for this thorn's documentation
\begin{abstract}
\end{abstract}
% The following sections are suggestive only.
% Remove them or add your own.
\section{Introduction}
\section{Physical System}
\section{Numerical Implementation}
\section{Using This Thorn}
\subsection{Obtaining This Thorn}
\subsection{Basic Usage}
\subsection{Special Behaviour}
\subsection{Interaction With Other Thorns}
\subsection{Examples}
\subsection{Support and Feedback}
\section{History}
\subsection{Thorn Source Code}
\subsection{Thorn Documentation}
\subsection{Acknowledgements}
\begin{thebibliography}{9}
\end{thebibliography}
% Do not delete next line
% END CACTUS THORNGUIDE
\end{document}
# As documented in the NRPy+ tutorial module
# Tutorial-ADMBSSN_tofrom_4metric.ipynb,
# this module will construct expressions for
# ADM or BSSN quantities in terms of the
# 4-metric g4DD, and g4DD/g4UU in terms of
# ADM/BSSN quantities.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import sys # Standard Python modules for multiplatform OS-level functions
def setup_ADM_quantities(inputvars):
if inputvars == "ADM":
gammaDD = ixp.declarerank2("gammaDD", "sym01")
betaU = ixp.declarerank1("betaU")
alpha = sp.symbols("alpha", real=True)
elif inputvars == "BSSN":
import BSSN.ADM_in_terms_of_BSSN as AitoB
# Construct gamma_{ij} in terms of cf & gammabar_{ij}
AitoB.ADM_in_terms_of_BSSN()
gammaDD = AitoB.gammaDD
# Next construct beta^i in terms of vet^i and reference metric quantities
import BSSN.BSSN_quantities as Bq
Bq.BSSN_basic_tensors()
betaU = Bq.betaU
alpha = sp.symbols("alpha", real=True)
else:
print("inputvars = " + str(inputvars) + " not supported. Please choose ADM or BSSN.")
sys.exit(1)
return gammaDD,betaU,alpha
# g_{mu nu} in terms of BSSN (if inputvars=="BSSN") or ADM (if inputvars=="ADM") variables.
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]
# g^{mu nu} in terms of BSSN (if inputvars=="BSSN") or ADM (if inputvars=="ADM") variables.
def g4UU_ito_BSSN_or_ADM(inputvars,gammaDD=None,betaU=None,alpha=None, gammaUU=None):
# Step 0: Declare g4UU as globals, to make interfacing with other modules/functions easier
global g4UU
# 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 g4UU = g_{mu nu}:
# To get \gamma^{\mu \nu} = gamma4UU[mu][nu], we'll need to use Eq. 2.119 in B&S.
g4UU = ixp.zerorank2(DIM=4)
# Step 3: Construct g4UU = g^{mu nu}
# Step 3.a: Compute gammaUU based on provided gammaDD:
if gammaUU==None:
gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD)
# Then evaluate g4UU:
g4UU = ixp.zerorank2(DIM=4)
g4UU[0][0] = -1 / alpha ** 2
for mu in range(1, 4):
g4UU[0][mu] = g4UU[mu][0] = betaU[mu - 1] / alpha ** 2
for mu in range(1, 4):
for nu in range(1, 4):
g4UU[mu][nu] = gammaUU[mu - 1][nu - 1] - betaU[mu - 1] * betaU[nu - 1] / alpha ** 2
# BSSN (if inputvars=="BSSN") or ADM (if inputvars=="ADM") metric variables in terms of g_{mu nu}
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
# This module performs the conversion between ADM
# spacetime variables in Spherical or Cartesian coordinates
# given as *exact* SymPy expressions (i.e., direct functions
# of r,th,ph or x,y,z), to rescaled BSSN-in-curvilinear
# coordinate quantities, as defined in BSSN_RHSs.py
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step P0: Import needed Python/NRPy+ modules
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
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 module for multiplatform OS-level functions
def Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear(CoordType_in, Sph_r_th_ph_or_Cart_xyz,
gammaDD_inSphorCart, KDD_inSphorCart, alpha_inSphorCart, betaU_inSphorCart, BU_inSphorCart):
# This routine converts the ADM variables
# $$\left\{\gamma_{ij}, K_{ij}, \alpha, \beta^i\right\}$$
# in Spherical or Cartesian basis+coordinates, first to the BSSN variables
# in the chosen reference_metric::CoordSystem coordinate system+basis:
# $$\left\{\bar{\gamma}_{i j},\bar{A}_{i j},\phi, K, \bar{\Lambda}^{i}, \alpha, \beta^i, B^i\right\},$$
# and then to the rescaled variables:
# $$\left\{h_{i j},a_{i j},\phi, K, \lambda^{i}, \alpha, \mathcal{V}^i, \mathcal{B}^i\right\}.$$
# The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations.
# To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in
# the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3.
# Step P1: Set spatial dimension (must be 3 for BSSN)
DIM = 3
# Step P2: Copy gammaSphDD_in to gammaSphDD, KSphDD_in to KSphDD, etc.
# This ensures that the input arrays are not modified below;
# modifying them would result in unexpected effects outside
# this function.
alphaSphorCart = alpha_inSphorCart
betaSphorCartU = ixp.zerorank1()
BSphorCartU = ixp.zerorank1()
gammaSphorCartDD = ixp.zerorank2()
KSphorCartDD = ixp.zerorank2()
for i in range(DIM):
betaSphorCartU[i] = betaU_inSphorCart[i]
BSphorCartU[i] = BU_inSphorCart[i]
for j in range(DIM):
gammaSphorCartDD[i][j] = gammaDD_inSphorCart[i][j]
KSphorCartDD[i][j] = KDD_inSphorCart[i][j]
# Make sure that rfm.reference_metric() has been called.
# We'll need the variables it defines throughout this module.
if rfm.have_already_called_reference_metric_function == False:
print("Error. Called Convert_Spherical_ADM_to_BSSN_curvilinear() without")
print(" first setting up reference metric, by calling rfm.reference_metric().")
sys.exit(1)
# Step 1: All input quantitiefs are in terms of r,th,ph or x,y,z. We want them in terms
# of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace
# r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2
# as defined for this particular reference metric in reference_metric.py's
# xxSph[] or xxCart[], respectively:
# Note that substitution only works when the variable is not an integer. Hence the
# if isinstance(...,...) stuff:
def sympify_integers__replace_rthph_or_Cartxyz(obj, rthph_or_xyz, rthph_or_xyz_of_xx):
if isinstance(obj, int):
return sp.sympify(obj)
else:
return obj.subs(rthph_or_xyz[0], rthph_or_xyz_of_xx[0]).\
subs(rthph_or_xyz[1], rthph_or_xyz_of_xx[1]).\
subs(rthph_or_xyz[2], rthph_or_xyz_of_xx[2])
r_th_ph_or_Cart_xyz_of_xx = []
if CoordType_in == "Spherical":
r_th_ph_or_Cart_xyz_of_xx = rfm.xxSph
elif CoordType_in == "Cartesian":
r_th_ph_or_Cart_xyz_of_xx = rfm.xxCart
else:
print("Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords.")
sys.exit(1)
alphaSphorCart = sympify_integers__replace_rthph_or_Cartxyz(
alphaSphorCart, Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx)
for i in range(DIM):
betaSphorCartU[i] = sympify_integers__replace_rthph_or_Cartxyz(
betaSphorCartU[i], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx)
BSphorCartU[i] = sympify_integers__replace_rthph_or_Cartxyz(
BSphorCartU[i], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx)
for j in range(DIM):
gammaSphorCartDD[i][j] = sympify_integers__replace_rthph_or_Cartxyz(
gammaSphorCartDD[i][j], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx)
KSphorCartDD[i][j] = sympify_integers__replace_rthph_or_Cartxyz(
KSphorCartDD[i][j], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx)
# Step 2: All ADM initial data quantities are now functions of xx0,xx1,xx2, but
# they are still in the Spherical or Cartesian basis. We can now directly apply
# Jacobian transformations to get them in the correct xx0,xx1,xx2 basis:
# alpha is a scalar, so no Jacobian transformation is necessary.
alpha = alphaSphorCart
Jac_dUSphorCart_dDrfmUD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff(r_th_ph_or_Cart_xyz_of_xx[i],rfm.xx[j])
Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3(Jac_dUSphorCart_dDrfmUD)
betaU = ixp.zerorank1()
BU = ixp.zerorank1()
gammaDD = ixp.zerorank2()
KDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j]
BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j]
for k in range(DIM):
for l in range(DIM):
gammaDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i]*Jac_dUSphorCart_dDrfmUD[l][j] * gammaSphorCartDD[k][l]
KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i]*Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l]
# Step 3: 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 in the "rfm" basis to their BSSN Curvilinear
# counterparts:
import BSSN.BSSN_in_terms_of_ADM as BitoA
BitoA.gammabarDD_hDD(gammaDD)
BitoA.trK_AbarDD_aDD(gammaDD, KDD)
BitoA.LambdabarU_lambdaU__exact_gammaDD(gammaDD)
BitoA.cf_from_gammaDD(gammaDD)
BitoA.betU_vetU(betaU, BU)
# Step 4: Return the BSSN Curvilinear variables in the desired xx0,xx1,xx2
# basis, and as functions of the consistent xx0,xx1,xx2 coordinates.
return BitoA.cf, BitoA.hDD, BitoA.lambdaU, BitoA.aDD, BitoA.trK, alpha, BitoA.vetU, BitoA.betU
# This module performs the conversion between ADM
# spacetime variables in Spherical or Cartesian coordinates
# given as *numerical* expressions (i.e., ADM quantities
# are only given to floating-point precision; e.g., in the
# case of an initial data solver), to rescaled BSSN-in-curvilinear
# coordinate quantities, as defined in BSSN_RHSs.py
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step P1: Initialize core Python/NRPy+ modules
from outputC import * # NRPy+: Core C code output module
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import finite_difference as fin # NRPy+: Finite difference C code generation module
import grid as gri # NRPy+: Functions having to do with numerical grids
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
import BSSN.BSSN_quantities as Bq # NRPy+: Computes useful BSSN quantities; e.g., gammabarUU & GammabarUDD needed below
import os, sys # Standard Python modules for multiplatform OS-level functions
def Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear(CoordType_in, ADM_input_function_name,
Ccodesdir = "BSSN", pointer_to_ID_inputs=False,loopopts=",oldloops"):
# The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations.
# To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in
# the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3.
# Step 0: Set spatial dimension (must be 3 for BSSN)
DIM = 3
# Step 1: All ADM initial data quantities are now functions of xx0,xx1,xx2, but
# they are still in the Spherical or Cartesian basis. We can now directly apply
# Jacobian transformations to get them in the correct xx0,xx1,xx2 basis:
# All input quantities are in terms of r,th,ph or x,y,z. We want them in terms
# of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace
# r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2
# as defined for this particular reference metric in reference_metric.py's
# xxSph[] or xxCart[], respectively:
# Define the input variables:
gammaSphorCartDD = ixp.declarerank2("gammaSphorCartDD", "sym01")
KSphorCartDD = ixp.declarerank2("KSphorCartDD", "sym01")
alphaSphorCart = sp.symbols("alphaSphorCart")
betaSphorCartU = ixp.declarerank1("betaSphorCartU")
BSphorCartU = ixp.declarerank1("BSphorCartU")
# Make sure that rfm.reference_metric() has been called.
# We'll need the variables it defines throughout this module.
if rfm.have_already_called_reference_metric_function == False:
print("Error. Called Convert_Spherical_ADM_to_BSSN_curvilinear() without")
print(" first setting up reference metric, by calling rfm.reference_metric().")
sys.exit(1)
r_th_ph_or_Cart_xyz_oID_xx = []
if CoordType_in == "Spherical":
r_th_ph_or_Cart_xyz_oID_xx = rfm.xxSph
elif CoordType_in == "Cartesian":
r_th_ph_or_Cart_xyz_oID_xx = rfm.xxCart
else:
print("Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords.")
sys.exit(1)
# Step 2: All ADM initial data quantities are now functions of xx0,xx1,xx2, but
# they are still in the Spherical or Cartesian basis. We can now directly apply
# Jacobian transformations to get them in the correct xx0,xx1,xx2 basis:
# alpha is a scalar, so no Jacobian transformation is necessary.
alpha = alphaSphorCart
Jac_dUSphorCart_dDrfmUD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff(r_th_ph_or_Cart_xyz_oID_xx[i], rfm.xx[j])
Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3(Jac_dUSphorCart_dDrfmUD)
betaU = ixp.zerorank1()
BU = ixp.zerorank1()
gammaDD = ixp.zerorank2()
KDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j]
BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j]
for k in range(DIM):
for l in range(DIM):
gammaDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * \
gammaSphorCartDD[k][l]
KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l]
# Step 3: 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 in the "rfm" basis to their BSSN Curvilinear
# counterparts, for all BSSN quantities *except* lambda^i:
import BSSN.BSSN_in_terms_of_ADM as BitoA
BitoA.gammabarDD_hDD(gammaDD)
BitoA.trK_AbarDD_aDD(gammaDD, KDD)
BitoA.cf_from_gammaDD(gammaDD)
BitoA.betU_vetU(betaU, BU)
hDD = BitoA.hDD
trK = BitoA.trK
aDD = BitoA.aDD
cf = BitoA.cf
vetU = BitoA.vetU
betU = BitoA.betU
# Step 4: Compute $\bar{\Lambda}^i$ (Eqs. 4 and 5 of
# [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)),
# from finite-difference derivatives of rescaled metric
# quantities $h_{ij}$:
# \bar{\Lambda}^i = \bar{\gamma}^{jk}\left(\bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk}\right).
# The reference_metric.py module provides us with analytic expressions for
# $\hat{\Gamma}^i_{jk}$, so here we need only compute
# finite-difference expressions for $\bar{\Gamma}^i_{jk}$, based on
# the values for $h_{ij}$ provided in the initial data. Once
# $\bar{\Lambda}^i$ has been computed, we apply the usual rescaling
# procedure:
# \lambda^i = \bar{\Lambda}^i/\text{ReU[i]},
# and then output the result to a C file using the NRPy+
# finite-difference C output routine.
# We will need all BSSN gridfunctions to be defined, as well as
# expressions for gammabarDD_dD in terms of exact derivatives of
# the rescaling matrix and finite-difference derivatives of
# hDD's. This functionality is provided by BSSN.BSSN_unrescaled_and_barred_vars,
# which we call here to overwrite above definitions of gammabarDD,gammabarUU, etc.
Bq.gammabar__inverse_and_derivs() # Provides gammabarUU and GammabarUDD
gammabarUU = Bq.gammabarUU
GammabarUDD = Bq.GammabarUDD
# 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])
# Finally apply rescaling:
# lambda^i = Lambdabar^i/\text{ReU[i]}
lambdaU = ixp.zerorank1()
for i in range(DIM):
lambdaU[i] = LambdabarU[i] / rfm.ReU[i]
if ADM_input_function_name == "DoNotOutputADMInputFunction":
return hDD,aDD,trK,vetU,betU,alpha,cf,lambdaU
# Step 5.A: Output files containing finite-differenced lambdas.
outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False"
lambdaU_expressions = [lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU0"), rhs=lambdaU[0]),
lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU1"), rhs=lambdaU[1]),
lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU2"), rhs=lambdaU[2])]
desc = "Output lambdaU[i] for BSSN, built using finite-difference derivatives."
name = "ID_BSSN_lambdas"
params = "const paramstruct *restrict params,REAL *restrict xx[3],REAL *restrict in_gfs"
preloop = ""
opts = ""
idx4replace = "IDX4S"
if "oldloops" in loopopts:
params = "const int Nxx[3],const int Nxx_plus_2NGHOSTS[3],REAL *xx[3],const REAL dxx[3],REAL *in_gfs"
opts = "DisableCparameters"
idx4replace = "IDX4"
preloop = """
const REAL invdx0 = 1.0/dxx[0];
const REAL invdx1 = 1.0/dxx[1];
const REAL invdx2 = 1.0/dxx[2];
"""
outCfunction(
outfile=os.path.join(Ccodesdir, name + ".h"), desc=desc, name=name, params=params,
preloop=preloop,
body=fin.FD_outputC("returnstring", lambdaU_expressions, outCparams).replace("IDX4",idx4replace),
loopopts="InteriorPoints,Read_xxs"+loopopts, opts=opts)
# Step 5: Output all ADM-to-BSSN expressions to a C function. This function
# must first call the ID_ADM_SphorCart() defined above. Using these
# Spherical or Cartesian data, it sets up all quantities needed for
# BSSNCurvilinear initial data, *except* $\lambda^i$, which must be
# computed from numerical data using finite-difference derivatives.
ID_inputs_param = "ID_inputs other_inputs,"
if pointer_to_ID_inputs == True:
ID_inputs_param = "ID_inputs *other_inputs,"
desc = "Write BSSN variables in terms of ADM variables at a given point xx0,xx1,xx2"
name = "ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs"
opts = ""
params = "const paramstruct *restrict params, "
if "oldloops" in loopopts:
opts = "DisableCparameters"
params = ""
params += "const REAL xx0xx1xx2[3]," + ID_inputs_param + """
REAL *hDD00,REAL *hDD01,REAL *hDD02,REAL *hDD11,REAL *hDD12,REAL *hDD22,
REAL *aDD00,REAL *aDD01,REAL *aDD02,REAL *aDD11,REAL *aDD12,REAL *aDD22,
REAL *trK,
REAL *vetU0,REAL *vetU1,REAL *vetU2,
REAL *betU0,REAL *betU1,REAL *betU2,
REAL *alpha, REAL *cf"""
outCparams = "preindent=1,outCverbose=False,includebraces=False"
outCfunction(
outfile=os.path.join(Ccodesdir, name + ".h"), desc=desc, name=name, params=params,
body="""
REAL gammaSphorCartDD00,gammaSphorCartDD01,gammaSphorCartDD02,
gammaSphorCartDD11,gammaSphorCartDD12,gammaSphorCartDD22;
REAL KSphorCartDD00,KSphorCartDD01,KSphorCartDD02,
KSphorCartDD11,KSphorCartDD12,KSphorCartDD22;
REAL alphaSphorCart,betaSphorCartU0,betaSphorCartU1,betaSphorCartU2;
REAL BSphorCartU0,BSphorCartU1,BSphorCartU2;
const REAL xx0 = xx0xx1xx2[0];
const REAL xx1 = xx0xx1xx2[1];
const REAL xx2 = xx0xx1xx2[2];
REAL xyz_or_rthph[3];\n""" +
outputC(r_th_ph_or_Cart_xyz_oID_xx[0:3], ["xyz_or_rthph[0]", "xyz_or_rthph[1]", "xyz_or_rthph[2]"],
"returnstring",
outCparams + ",CSE_enable=False") + " " + ADM_input_function_name + """(xyz_or_rthph, other_inputs,
&gammaSphorCartDD00,&gammaSphorCartDD01,&gammaSphorCartDD02,
&gammaSphorCartDD11,&gammaSphorCartDD12,&gammaSphorCartDD22,
&KSphorCartDD00,&KSphorCartDD01,&KSphorCartDD02,
&KSphorCartDD11,&KSphorCartDD12,&KSphorCartDD22,
&alphaSphorCart,&betaSphorCartU0,&betaSphorCartU1,&betaSphorCartU2,
&BSphorCartU0,&BSphorCartU1,&BSphorCartU2);
// Next compute all rescaled BSSN curvilinear quantities:\n""" +
outputC([hDD[0][0], hDD[0][1], hDD[0][2], hDD[1][1], hDD[1][2], hDD[2][2],
aDD[0][0], aDD[0][1], aDD[0][2], aDD[1][1], aDD[1][2], aDD[2][2],
trK, vetU[0], vetU[1], vetU[2], betU[0], betU[1], betU[2],
alpha, cf],
["*hDD00", "*hDD01", "*hDD02", "*hDD11", "*hDD12", "*hDD22",
"*aDD00", "*aDD01", "*aDD02", "*aDD11", "*aDD12", "*aDD22",
"*trK", "*vetU0", "*vetU1", "*vetU2", "*betU0", "*betU1", "*betU2",
"*alpha", "*cf"], "returnstring", params=outCparams),
opts = opts)
# Step 5.a: Output the driver function for the above
# function ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs()
# Next write the driver function for ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs():
desc = """Driver function for ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(),
which writes BSSN variables in terms of ADM variables at a given point xx0,xx1,xx2"""
name = "ID_BSSN__ALL_BUT_LAMBDAs"
params = "const paramstruct *restrict params,REAL *restrict xx[3]," + ID_inputs_param + "REAL *in_gfs"
opts = ""
funccallparams = "params, "
idx3replace = "IDX3S"
idx4ptreplace = "IDX4ptS"
if "oldloops" in loopopts:
params = "const int Nxx_plus_2NGHOSTS[3],REAL *xx[3]," + ID_inputs_param + "REAL *in_gfs"
opts = "DisableCparameters"
funccallparams = ""
idx3replace = "IDX3"
idx4ptreplace = "IDX4pt"
outCfunction(
outfile=os.path.join(Ccodesdir, name + ".h"), desc=desc, name=name, params=params,
body="""
const int idx = IDX3(i0,i1,i2);
const REAL xx0xx1xx2[3] = {xx0,xx1,xx2};
ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(""".replace("IDX3",idx3replace)+funccallparams+"""xx0xx1xx2,other_inputs,
&in_gfs[IDX4pt(HDD00GF,idx)],&in_gfs[IDX4pt(HDD01GF,idx)],&in_gfs[IDX4pt(HDD02GF,idx)],
&in_gfs[IDX4pt(HDD11GF,idx)],&in_gfs[IDX4pt(HDD12GF,idx)],&in_gfs[IDX4pt(HDD22GF,idx)],
&in_gfs[IDX4pt(ADD00GF,idx)],&in_gfs[IDX4pt(ADD01GF,idx)],&in_gfs[IDX4pt(ADD02GF,idx)],
&in_gfs[IDX4pt(ADD11GF,idx)],&in_gfs[IDX4pt(ADD12GF,idx)],&in_gfs[IDX4pt(ADD22GF,idx)],
&in_gfs[IDX4pt(TRKGF,idx)],
&in_gfs[IDX4pt(VETU0GF,idx)],&in_gfs[IDX4pt(VETU1GF,idx)],&in_gfs[IDX4pt(VETU2GF,idx)],
&in_gfs[IDX4pt(BETU0GF,idx)],&in_gfs[IDX4pt(BETU1GF,idx)],&in_gfs[IDX4pt(BETU2GF,idx)],
&in_gfs[IDX4pt(ALPHAGF,idx)],&in_gfs[IDX4pt(CFGF,idx)]);
""".replace("IDX4pt",idx4ptreplace),
loopopts="AllPoints,Read_xxs"+loopopts, opts=opts)
# As documented in the NRPy+ tutorial module
# Tutorial-ADM_in_terms_of_BSSN.ipynb,
# this module will construct expressions for ADM
# quantities in terms of BSSN quantities.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1.a: import all needed modules from NRPy+:
from outputC import * # NRPy+: Core C code output module
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
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 module for multiplatform OS-level functions
def ADM_in_terms_of_BSSN():
global gammaDD, gammaDDdD, gammaDDdDD, gammaUU, detgamma, GammaUDD, KDD, KDDdD
# Step 1.c: 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.d: 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.e: Import all basic (unrescaled) BSSN scalars & tensors
import BSSN.BSSN_quantities as Bq
Bq.BSSN_basic_tensors()
gammabarDD = Bq.gammabarDD
cf = Bq.cf
AbarDD = Bq.AbarDD
trK = Bq.trK
Bq.gammabar__inverse_and_derivs()
gammabarDD_dD = Bq.gammabarDD_dD
gammabarDD_dDD = Bq.gammabarDD_dDD
Bq.AbarUU_AbarUD_trAbar_AbarDD_dD()
AbarDD_dD = Bq.AbarDD_dD
# Step 2: The ADM three-metric gammaDD and its
# derivatives in terms of BSSN quantities.
gammaDD = ixp.zerorank2()
exp4phi = sp.sympify(0)
if par.parval_from_str("EvolvedConformalFactor_cf") == "phi":
exp4phi = sp.exp(4 * cf)
elif par.parval_from_str("EvolvedConformalFactor_cf") == "chi":
exp4phi = (1 / cf)
elif par.parval_from_str("EvolvedConformalFactor_cf") == "W":
exp4phi = (1 / cf ** 2)
else:
print("Error EvolvedConformalFactor_cf type = \"" + par.parval_from_str("EvolvedConformalFactor_cf") + "\" unknown.")
sys.exit(1)
for i in range(DIM):
for j in range(DIM):
gammaDD[i][j] = exp4phi * gammabarDD[i][j]
# Step 2.a: Derivatives of $e^{4\phi}$
phidD = ixp.zerorank1()
phidDD = ixp.zerorank2()
cf_dD = ixp.declarerank1("cf_dD")
cf_dDD = ixp.declarerank2("cf_dDD","sym01")
if par.parval_from_str("EvolvedConformalFactor_cf") == "phi":
for i in range(DIM):
phidD[i] = cf_dD[i]
for j in range(DIM):
phidDD[i][j] = cf_dDD[i][j]
elif par.parval_from_str("EvolvedConformalFactor_cf") == "chi":
for i in range(DIM):
phidD[i] = -sp.Rational(1,4)*exp4phi*cf_dD[i]
for j in range(DIM):
phidDD[i][j] = sp.Rational(1,4)*( exp4phi**2*cf_dD[i]*cf_dD[j] - exp4phi*cf_dDD[i][j] )
elif par.parval_from_str("EvolvedConformalFactor_cf") == "W":
exp2phi = (1 / cf)
for i in range(DIM):
phidD[i] = -sp.Rational(1,2)*exp2phi*cf_dD[i]
for j in range(DIM):
phidDD[i][j] = sp.Rational(1,2)*( exp4phi*cf_dD[i]*cf_dD[j] - exp2phi*cf_dDD[i][j] )
else:
print("Error EvolvedConformalFactor_cf type = \""+par.parval_from_str("EvolvedConformalFactor_cf")+"\" unknown.")
sys.exit(1)
exp4phidD = ixp.zerorank1()
exp4phidDD = ixp.zerorank2()
for i in range(DIM):
exp4phidD[i] = 4*exp4phi*phidD[i]
for j in range(DIM):
exp4phidDD[i][j] = 16*exp4phi*phidD[i]*phidD[j] + 4*exp4phi*phidDD[i][j]
# Step 2.b: Derivatives of gammaDD, the ADM three-metric
gammaDDdD = ixp.zerorank3()
gammaDDdDD = ixp.zerorank4()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
gammaDDdD[i][j][k] = exp4phidD[k] * gammabarDD[i][j] + exp4phi * gammabarDD_dD[i][j][k]
for l in range(DIM):
gammaDDdDD[i][j][k][l] = exp4phidDD[k][l] * gammabarDD[i][j] + \
exp4phidD[k] * gammabarDD_dD[i][j][l] + \
exp4phidD[l] * gammabarDD_dD[i][j][k] + \
exp4phi * gammabarDD_dDD[i][j][k][l]
# Step 2.c: 3-Christoffel symbols associated with ADM 3-metric gammaDD
# Step 2.c.i: First compute the inverse 3-metric gammaUU:
gammaUU, detgamma = ixp.symm_matrix_inverter3x3(gammaDD)
GammaUDD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
GammaUDD[i][j][k] += sp.Rational(1,2)*gammaUU[i][l]* \
(gammaDDdD[l][j][k] + gammaDDdD[l][k][j] - gammaDDdD[j][k][l])
# Step 3: Define ADM extrinsic curvature KDD and
# its first spatial derivatives KDDdD
# in terms of BSSN quantities
KDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
KDD[i][j] = exp4phi * AbarDD[i][j] + sp.Rational(1, 3) * gammaDD[i][j] * trK
KDDdD = ixp.zerorank3()
trK_dD = ixp.declarerank1("trK_dD")
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
KDDdD[i][j][k] = exp4phidD[k] * AbarDD[i][j] + exp4phi * AbarDD_dD[i][j][k] + \
sp.Rational(1, 3) * (gammaDDdD[i][j][k] * trK + gammaDD[i][j] * trK_dD[k])
# This module sets up a standard initial data function used for
# setting up SENR initial data at all gridpoints.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
from outputC import *
def BSSN_ID_function_string(cf,hDD,lambdaU,aDD,trK,alpha,vetU,betU):
rhss = [trK,alpha,cf]
lhss = ["in_gfs[IDX4S(TRKGF,i0,i1,i2)]","in_gfs[IDX4S(ALPHAGF,i0,i1,i2)]","in_gfs[IDX4S(CFGF,i0,i1,i2)]"]
for i in range(3):
rhss.append(lambdaU[i])
lhss.append("in_gfs[IDX4S(LAMBDAU"+str(i)+"GF,i0,i1,i2)]")
rhss.append(vetU[i])
lhss.append("in_gfs[IDX4S(VETU"+str(i)+"GF,i0,i1,i2)]")
rhss.append(betU[i])
lhss.append("in_gfs[IDX4S(BETU"+str(i)+"GF,i0,i1,i2)]")
for j in range(i,3):
rhss.append(hDD[i][j])
lhss.append("in_gfs[IDX4S(HDD" + str(i) + str(j) + "GF,i0,i1,i2)]")
rhss.append(aDD[i][j])
lhss.append("in_gfs[IDX4S(ADD" + str(i) + str(j) + "GF,i0,i1,i2)]")
# Sort the lhss list alphabetically, and rhss to match:
lhss,rhss = [list(x) for x in zip(*sorted(zip(lhss, rhss), key=lambda pair: pair[0]))]
body = outputC(rhss, lhss, filename="returnstring",
params="preindent=1,CSE_enable=True,outCverbose=False", # outCverbose=False to prevent
# enormous output files.
prestring="", poststring="")
desc = "Set up the initial data at all points on the numerical grid."
add_to_Cfunction_dict(
desc =desc,
name ="initial_data",
params ="const paramstruct *restrict params,REAL *restrict xx[3], REAL *restrict in_gfs",
body =body,
loopopts="AllPoints,Read_xxs")
# As documented in the NRPy+ tutorial module
# Tutorial-BSSN_time_evolution-BSSN_RHSs.ipynb,
# this module will construct the right-hand sides (RHSs)
# expressions of the BSSN time evolution equations.
#
# Time-evolution equations for the BSSN gauge conditions are
# specified in the BSSN_gauge_RHSs module and documented in
# the Tutorial-BSSN_time_evolution-BSSN_gauge_RHSs.ipynb
# NRPy+ tutorial module.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1.a: import all needed modules from NRPy+:
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
have_already_called_BSSN_RHSs_function = False
# Step 1.b: Set the coordinate system for the numerical grid:
# DO NOT SET IN STANDALONE PYTHON MODULE
# par.set_parval_from_str("reference_metric::CoordSystem","Spherical")
def BSSN_RHSs():
# Step 1.c: 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()
global have_already_called_BSSN_RHSs_function # setting to global enables other modules to see updated value.
have_already_called_BSSN_RHSs_function = True
# Step 1.d: 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.e: Import all basic (unrescaled) BSSN scalars & tensors
import BSSN.BSSN_quantities as Bq
Bq.BSSN_basic_tensors()
gammabarDD = Bq.gammabarDD
AbarDD = Bq.AbarDD
LambdabarU = Bq.LambdabarU
trK = Bq.trK
alpha = Bq.alpha
betaU = Bq.betaU
# Step 1.f: Import all neeeded rescaled BSSN tensors:
aDD = Bq.aDD
cf = Bq.cf
lambdaU = Bq.lambdaU
# Step 2.a.i: Import derivative expressions for betaU defined in the BSSN.BSSN_quantities module:
Bq.betaU_derivs()
betaU_dD = Bq.betaU_dD
betaU_dDD = Bq.betaU_dDD
# Step 2.a.ii: Import derivative expression for gammabarDD
Bq.gammabar__inverse_and_derivs()
gammabarDD_dupD = Bq.gammabarDD_dupD
# Step 2.a.iii: First term of \partial_t \bar{\gamma}_{i j} right-hand side:
# \beta^k \bar{\gamma}_{ij,k} + \beta^k_{,i} \bar{\gamma}_{kj} + \beta^k_{,j} \bar{\gamma}_{ik}
gammabar_rhsDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
gammabar_rhsDD[i][j] += betaU[k] * gammabarDD_dupD[i][j][k] + betaU_dD[k][i] * gammabarDD[k][j] \
+ betaU_dD[k][j] * gammabarDD[i][k]
# Step 2.b.i: First import \bar{A}_{ij} = AbarDD[i][j], and its contraction trAbar = \bar{A}^k_k
# from BSSN.BSSN_quantities
Bq.AbarUU_AbarUD_trAbar_AbarDD_dD()
trAbar = Bq.trAbar
# Step 2.b.ii: Import detgammabar quantities from BSSN.BSSN_quantities:
Bq.detgammabar_and_derivs()
detgammabar = Bq.detgammabar
detgammabar_dD = Bq.detgammabar_dD
# Step 2.b.ii: Compute the contraction \bar{D}_k \beta^k = \beta^k_{,k} + \frac{\beta^k \bar{\gamma}_{,k}}{2 \bar{\gamma}}
Dbarbetacontraction = sp.sympify(0)
for k in range(DIM):
Dbarbetacontraction += betaU_dD[k][k] + betaU[k] * detgammabar_dD[k] / (2 * detgammabar)
# Step 2.b.iii: Second term of \partial_t \bar{\gamma}_{i j} right-hand side:
# \frac{2}{3} \bar{\gamma}_{i j} \left (\alpha \bar{A}_{k}^{k} - \bar{D}_{k} \beta^{k}\right )
for i in range(DIM):
for j in range(DIM):
gammabar_rhsDD[i][j] += sp.Rational(2, 3) * gammabarDD[i][j] * (alpha * trAbar - Dbarbetacontraction)
# Step 2.c: Third term of \partial_t \bar{\gamma}_{i j} right-hand side:
# -2 \alpha \bar{A}_{ij}
for i in range(DIM):
for j in range(DIM):
gammabar_rhsDD[i][j] += -2 * alpha * AbarDD[i][j]
# Step 3.a: First term of \partial_t \bar{A}_{i j}:
# \beta^k \partial_k \bar{A}_{ij} + \partial_i \beta^k \bar{A}_{kj} + \partial_j \beta^k \bar{A}_{ik}
# First define AbarDD_dupD:
AbarDD_dupD = Bq.AbarDD_dupD # From Bq.AbarUU_AbarUD_trAbar_AbarDD_dD()
Abar_rhsDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
Abar_rhsDD[i][j] += betaU[k] * AbarDD_dupD[i][j][k] + betaU_dD[k][i] * AbarDD[k][j] \
+ betaU_dD[k][j] * AbarDD[i][k]
# Step 3.b: Second term of \partial_t \bar{A}_{i j}:
# - (2/3) \bar{A}_{i j} \bar{D}_{k} \beta^{k} - 2 \alpha \bar{A}_{i k} {\bar{A}^{k}}_{j} + \alpha \bar{A}_{i j} K
gammabarUU = Bq.gammabarUU # From Bq.gammabar__inverse_and_derivs()
AbarUD = Bq.AbarUD # From Bq.AbarUU_AbarUD_trAbar()
for i in range(DIM):
for j in range(DIM):
Abar_rhsDD[i][j] += -sp.Rational(2, 3) * AbarDD[i][j] * Dbarbetacontraction + alpha * AbarDD[i][j] * trK
for k in range(DIM):
Abar_rhsDD[i][j] += -2 * alpha * AbarDD[i][k] * AbarUD[k][j]
# Step 3.c.i: Define partial derivatives of \phi in terms of evolved quantity "cf":
Bq.phi_and_derivs()
phi_dD = Bq.phi_dD
phi_dupD = Bq.phi_dupD
phi_dDD = Bq.phi_dDD
exp_m4phi = Bq.exp_m4phi
phi_dBarD = Bq.phi_dBarD # phi_dBarD = Dbar_i phi = phi_dD (since phi is a scalar)
phi_dBarDD = Bq.phi_dBarDD # phi_dBarDD = Dbar_i Dbar_j phi (covariant derivative)
# Step 3.c.ii: Define RbarDD
Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU()
RbarDD = Bq.RbarDD
# Step 3.c.iii: Define first and second derivatives of \alpha, as well as
# \bar{D}_i \bar{D}_j \alpha, which is defined just like phi
alpha_dD = ixp.declarerank1("alpha_dD")
alpha_dDD = ixp.declarerank2("alpha_dDD", "sym01")
alpha_dBarD = alpha_dD
alpha_dBarDD = ixp.zerorank2()
GammabarUDD = Bq.GammabarUDD # Defined in Bq.gammabar__inverse_and_derivs()
for i in range(DIM):
for j in range(DIM):
alpha_dBarDD[i][j] = alpha_dDD[i][j]
for k in range(DIM):
alpha_dBarDD[i][j] += - GammabarUDD[k][i][j] * alpha_dD[k]
# Step 3.c.iv: Define the terms in curly braces:
curlybrackettermsDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
curlybrackettermsDD[i][j] = -2 * alpha * phi_dBarDD[i][j] + 4 * alpha * phi_dBarD[i] * phi_dBarD[j] \
+ 2 * alpha_dBarD[i] * phi_dBarD[j] \
+ 2 * alpha_dBarD[j] * phi_dBarD[i] \
- alpha_dBarDD[i][j] + alpha * RbarDD[i][j]
# Step 3.c.v: Compute the trace:
curlybracketterms_trace = sp.sympify(0)
for i in range(DIM):
for j in range(DIM):
curlybracketterms_trace += gammabarUU[i][j] * curlybrackettermsDD[i][j]
# Step 3.c.vi: Third and final term of Abar_rhsDD[i][j]:
for i in range(DIM):
for j in range(DIM):
Abar_rhsDD[i][j] += exp_m4phi * (curlybrackettermsDD[i][j] -
sp.Rational(1, 3) * gammabarDD[i][j] * curlybracketterms_trace)
# Step 4: Right-hand side of conformal factor variable "cf". Supported
# options include: cf=phi, cf=W=e^(-2*phi) (default), and cf=chi=e^(-4*phi)
# \partial_t phi = \left[\beta^k \partial_k \phi \right] <- TERM 1
# + \frac{1}{6} \left (\bar{D}_{k} \beta^{k} - \alpha K \right ) <- TERM 2
global cf_rhs
cf_rhs = sp.Rational(1, 6) * (Dbarbetacontraction - alpha * trK) # Term 2
for k in range(DIM):
cf_rhs += betaU[k] * phi_dupD[k] # Term 1
# Next multiply to convert phi_rhs to cf_rhs.
if par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "phi":
pass # do nothing; cf_rhs = phi_rhs
elif par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "W":
cf_rhs *= -2 * cf # cf_rhs = -2*cf*phi_rhs
elif par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "chi":
cf_rhs *= -4 * cf # cf_rhs = -4*cf*phi_rhs
else:
print("Error: EvolvedConformalFactor_cf == " +
par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") + " unsupported!")
sys.exit(1)
# Step 5: right-hand side of trK (trace of extrinsic curvature):
# \partial_t K = \beta^k \partial_k K <- TERM 1
# + \frac{1}{3} \alpha K^{2} <- TERM 2
# + \alpha \bar{A}_{i j} \bar{A}^{i j} <- TERM 3
# - - e^{-4 \phi} (\bar{D}_{i} \bar{D}^{i} \alpha + 2 \bar{D}^{i} \alpha \bar{D}_{i} \phi ) <- TERM 4
global trK_rhs
# TERM 2:
trK_rhs = sp.Rational(1, 3) * alpha * trK * trK
trK_dupD = ixp.declarerank1("trK_dupD")
for i in range(DIM):
# TERM 1:
trK_rhs += betaU[i] * trK_dupD[i]
for i in range(DIM):
for j in range(DIM):
# TERM 4:
trK_rhs += -exp_m4phi * gammabarUU[i][j] * (alpha_dBarDD[i][j] + 2 * alpha_dBarD[j] * phi_dBarD[i])
AbarUU = Bq.AbarUU # From Bq.AbarUU_AbarUD_trAbar()
for i in range(DIM):
for j in range(DIM):
# TERM 3:
trK_rhs += alpha * AbarDD[i][j] * AbarUU[i][j]
# Step 6: right-hand side of \partial_t \bar{\Lambda}^i:
# \partial_t \bar{\Lambda}^i = \beta^k \partial_k \bar{\Lambda}^i - \partial_k \beta^i \bar{\Lambda}^k <- TERM 1
# + \bar{\gamma}^{j k} \hat{D}_{j} \hat{D}_{k} \beta^{i} <- TERM 2
# + \frac{2}{3} \Delta^{i} \bar{D}_{j} \beta^{j} <- TERM 3
# + \frac{1}{3} \bar{D}^{i} \bar{D}_{j} \beta^{j} <- TERM 4
# - 2 \bar{A}^{i j} (\partial_{j} \alpha - 6 \partial_{j} \phi) <- TERM 5
# + 2 \alpha \bar{A}^{j k} \Delta_{j k}^{i} <- TERM 6
# - \frac{4}{3} \alpha \bar{\gamma}^{i j} \partial_{j} K <- TERM 7
# Step 6.a: Term 1 of \partial_t \bar{\Lambda}^i: \beta^k \partial_k \bar{\Lambda}^i - \partial_k \beta^i \bar{\Lambda}^k
# First we declare \bar{\Lambda}^i and \bar{\Lambda}^i_{,j} in terms of \lambda^i and \lambda^i_{,j}
global LambdabarU_dupD # Used on the RHS of the Gamma-driving shift conditions
LambdabarU_dupD = ixp.zerorank2()
lambdaU_dupD = ixp.declarerank2("lambdaU_dupD", "nosym")
for i in range(DIM):
for j in range(DIM):
LambdabarU_dupD[i][j] = lambdaU_dupD[i][j] * rfm.ReU[i] + lambdaU[i] * rfm.ReUdD[i][j]
global Lambdabar_rhsU # Used on the RHS of the Gamma-driving shift conditions
Lambdabar_rhsU = ixp.zerorank1()
for i in range(DIM):
for k in range(DIM):
Lambdabar_rhsU[i] += betaU[k] * LambdabarU_dupD[i][k] - betaU_dD[i][k] * LambdabarU[k] # Term 1
# Step 6.b: Term 2 of \partial_t \bar{\Lambda}^i = \bar{\gamma}^{jk} (Term 2a + Term 2b + Term 2c)
# Term 2a: \bar{\gamma}^{jk} \beta^i_{,kj}
Term2aUDD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
Term2aUDD[i][j][k] += betaU_dDD[i][k][j]
# Term 2b: \hat{\Gamma}^i_{mk,j} \beta^m + \hat{\Gamma}^i_{mk} \beta^m_{,j}
# + \hat{\Gamma}^i_{dj}\beta^d_{,k} - \hat{\Gamma}^d_{kj} \beta^i_{,d}
Term2bUDD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for m in range(DIM):
Term2bUDD[i][j][k] += rfm.GammahatUDDdD[i][m][k][j] * betaU[m] \
+ rfm.GammahatUDD[i][m][k] * betaU_dD[m][j] \
+ rfm.GammahatUDD[i][m][j] * betaU_dD[m][k] \
- rfm.GammahatUDD[m][k][j] * betaU_dD[i][m]
# Term 2c: \hat{\Gamma}^i_{dj}\hat{\Gamma}^d_{mk} \beta^m - \hat{\Gamma}^d_{kj} \hat{\Gamma}^i_{md} \beta^m
Term2cUDD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for m in range(DIM):
for d in range(DIM):
Term2cUDD[i][j][k] += (rfm.GammahatUDD[i][d][j] * rfm.GammahatUDD[d][m][k] \
- rfm.GammahatUDD[d][k][j] * rfm.GammahatUDD[i][m][d]) * betaU[m]
Lambdabar_rhsUpieceU = ixp.zerorank1()
# Put it all together to get Term 2:
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
Lambdabar_rhsU[i] += gammabarUU[j][k] * (Term2aUDD[i][j][k] + Term2bUDD[i][j][k] + Term2cUDD[i][j][k])
Lambdabar_rhsUpieceU[i] += gammabarUU[j][k] * (
Term2aUDD[i][j][k] + Term2bUDD[i][j][k] + Term2cUDD[i][j][k])
# Step 6.c: Term 3 of \partial_t \bar{\Lambda}^i:
# \frac{2}{3} \Delta^{i} \bar{D}_{j} \beta^{j}
DGammaU = Bq.DGammaU # From Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU()
for i in range(DIM):
Lambdabar_rhsU[i] += sp.Rational(2, 3) * DGammaU[i] * Dbarbetacontraction # Term 3
# Step 6.d: Term 4 of \partial_t \bar{\Lambda}^i:
# \frac{1}{3} \bar{D}^{i} \bar{D}_{j} \beta^{j}
detgammabar_dDD = Bq.detgammabar_dDD # From Bq.detgammabar_and_derivs()
Dbarbetacontraction_dBarD = ixp.zerorank1()
for k in range(DIM):
for m in range(DIM):
Dbarbetacontraction_dBarD[m] += betaU_dDD[k][k][m] + \
(betaU_dD[k][m] * detgammabar_dD[k] +
betaU[k] * detgammabar_dDD[k][m]) / (2 * detgammabar) \
- betaU[k] * detgammabar_dD[k] * detgammabar_dD[m] / (
2 * detgammabar * detgammabar)
for i in range(DIM):
for m in range(DIM):
Lambdabar_rhsU[i] += sp.Rational(1, 3) * gammabarUU[i][m] * Dbarbetacontraction_dBarD[m]
# Step 6.e: Term 5 of \partial_t \bar{\Lambda}^i:
# - 2 \bar{A}^{i j} (\partial_{j} \alpha - 6 \alpha \partial_{j} \phi)
for i in range(DIM):
for j in range(DIM):
Lambdabar_rhsU[i] += -2 * AbarUU[i][j] * (alpha_dD[j] - 6 * alpha * phi_dD[j])
# Step 6.f: Term 6 of \partial_t \bar{\Lambda}^i:
# 2 \alpha \bar{A}^{j k} \Delta^{i}_{j k}
DGammaUDD = Bq.DGammaUDD # From RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
Lambdabar_rhsU[i] += 2 * alpha * AbarUU[j][k] * DGammaUDD[i][j][k]
# Step 6.g: Term 7 of \partial_t \bar{\Lambda}^i:
# -\frac{4}{3} \alpha \bar{\gamma}^{i j} \partial_{j} K
trK_dD = ixp.declarerank1("trK_dD")
for i in range(DIM):
for j in range(DIM):
Lambdabar_rhsU[i] += -sp.Rational(4, 3) * alpha * gammabarUU[i][j] * trK_dD[j]
# Step 7: Rescale the RHS quantities so that the evolved
# variables are smooth across coord singularities
global h_rhsDD,a_rhsDD,lambda_rhsU
h_rhsDD = ixp.zerorank2()
a_rhsDD = ixp.zerorank2()
lambda_rhsU = ixp.zerorank1()
for i in range(DIM):
lambda_rhsU[i] = Lambdabar_rhsU[i] / rfm.ReU[i]
for j in range(DIM):
h_rhsDD[i][j] = gammabar_rhsDD[i][j] / rfm.ReDD[i][j]
a_rhsDD[i][j] = Abar_rhsDD[i][j] / rfm.ReDD[i][j]
# print(str(Abar_rhsDD[2][2]).replace("**","^").replace("_","").replace("xx","x").replace("sin(x2)","Sin[x2]").replace("sin(2*x2)","Sin[2*x2]").replace("cos(x2)","Cos[x2]").replace("detgbaroverdetghat","detg"))
# print(str(Dbarbetacontraction).replace("**","^").replace("_","").replace("xx","x").replace("sin(x2)","Sin[x2]").replace("detgbaroverdetghat","detg"))
# print(betaU_dD)
# print(str(trK_rhs).replace("xx2","xx3").replace("xx1","xx2").replace("xx0","xx1").replace("**","^").replace("_","").replace("sin(xx2)","Sinx2").replace("xx","x").replace("sin(2*x2)","Sin2x2").replace("cos(x2)","Cosx2").replace("detgbaroverdetghat","detg"))
# print(str(bet_rhsU[0]).replace("xx2","xx3").replace("xx1","xx2").replace("xx0","xx1").replace("**","^").replace("_","").replace("sin(xx2)","Sinx2").replace("xx","x").replace("sin(2*x2)","Sin2x2").replace("cos(x2)","Cosx2").replace("detgbaroverdetghat","detg"))
# This module registers rescaled BSSN T^{mu nu} source term variables
# as AUX (i.e., not evolved) gridfunctions
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step P1: import all needed modules from NRPy+:
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import grid as gri # NRPy+: Functions having to do with numerical grids
def define_BSSN_T4UUmunu_rescaled_source_terms():
# Step 0: First check to see if this function has already been called.
# If so, do not register the gridfunctions again!
for i in range(len(gri.glb_gridfcs_list)):
if "sDD00" in gri.glb_gridfcs_list[i].name:
return
# Step 1: Declare as globals all quantities declared in this function.
global rho,S,sD,sDD
# Step 2: Register all needed *evolved* gridfunctions.
# Step 2a: Register indexed quantities, using ixp.register_... functions
sDD = ixp.register_gridfunctions_for_single_rank2("AUX", "sDD", "sym01")
sD = ixp.register_gridfunctions_for_single_rank1("AUX", "sD")
# Step 2b: Register scalar quantities, using gri.register_gridfunctions()
rho, S = gri.register_gridfunctions("AUX",["rho","S"])
# As documented in the NRPy+ tutorial module
# Tutorial-BSSN_constraints.ipynb,
# this module will construct expressions for the
# BSSN Hamiltonian and momentum constraint equations.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1: Initialize needed 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 reference_metric as rfm # NRPy+: Reference metric support
import BSSN.BSSN_quantities as Bq # NRPy+: Computes useful BSSN quantities
import BSSN.BSSN_T4UUmunu_vars as BTmunu
thismodule = __name__
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]
# Next 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]
# As documented in the NRPy+ tutorial module
# Tutorial-BSSN_time_evolution-BSSN_gauge_RHSs.ipynb
# this module will construct the right-hand sides (RHSs)
# expressions for the time evolution equations of the
# BSSN gauge quantities alpha and beta^i (i.e., the
# lapse and shift)
#
# Non-gauge BSSN time-evolution equations are documented in the
# NRPy+ tutorial module
# Tutorial-BSSN_time_evolution-BSSN_RHSs.ipynb,
# and separately constructed in the BSSN.BSSN_RHSs
# Python module.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1: Import all needed modules from NRPy+:
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 BSSN.BSSN_quantities as Bq # NRPy+: Computes useful BSSN quantities
import BSSN.BSSN_RHSs as Brhs # NRPy+: Constructs BSSN right-hand-side expressions
import sys # Standard Python modules for multiplatform OS-level functions
# Step 1.a: Declare/initialize parameters for this module
thismodule = __name__
par.initialize_param(par.glb_param("char", thismodule, "LapseEvolutionOption", "OnePlusLog"))
par.initialize_param(par.glb_param("char", thismodule, "ShiftEvolutionOption", "GammaDriving2ndOrder_Covariant"))
def BSSN_gauge_RHSs():
# Step 1.d: 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.e: 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.f: Define needed BSSN quantities:
# Declare scalars & tensors (in terms of rescaled BSSN quantities)
Bq.BSSN_basic_tensors()
Bq.betaU_derivs()
# Declare BSSN_RHSs (excluding the time evolution equations for the gauge conditions),
# if they haven't already been declared.
if Brhs.have_already_called_BSSN_RHSs_function == False:
print("BSSN_gauge_RHSs() Error: You must call BSSN_RHSs() before calling BSSN_gauge_RHSs().")
sys.exit(1)
# Step 2: Lapse conditions
LapseEvolOption = par.parval_from_str(thismodule + "::LapseEvolutionOption")
# Step 2.a: The 1+log lapse condition:
# \partial_t \alpha = \beta^i \alpha_{,i} - 2*\alpha*K
# First import expressions from BSSN_quantities
cf = Bq.cf
trK = Bq.trK
alpha = Bq.alpha
betaU = Bq.betaU
# Implement the 1+log lapse condition
global alpha_rhs
alpha_rhs = sp.sympify(0)
if LapseEvolOption == "OnePlusLog":
alpha_rhs = -2 * alpha * trK
alpha_dupD = ixp.declarerank1("alpha_dupD")
for i in range(DIM):
alpha_rhs += betaU[i] * alpha_dupD[i]
# Step 2.b: Implement the harmonic slicing lapse condition
elif LapseEvolOption == "HarmonicSlicing":
if par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "W":
alpha_rhs = -3 * cf ** (-4) * Brhs.cf_rhs
elif par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "phi":
alpha_rhs = 6 * sp.exp(6 * cf) * Brhs.cf_rhs
else:
print("Error LapseEvolutionOption==HarmonicSlicing unsupported for EvolvedConformalFactor_cf!=(W or phi)")
sys.exit(1)
# Step 2.c: Frozen lapse
# \partial_t \alpha = 0
elif LapseEvolOption == "Frozen":
alpha_rhs = sp.sympify(0)
else:
print("Error: "+thismodule + "::LapseEvolutionOption == "+LapseEvolOption+" not supported!")
sys.exit(1)
# Step 3.a: Set \partial_t \beta^i
# First check that ShiftEvolutionOption parameter choice is supported.
ShiftEvolOption = par.parval_from_str(thismodule + "::ShiftEvolutionOption")
if ShiftEvolOption != "Frozen" and \
ShiftEvolOption != "GammaDriving2ndOrder_NoCovariant" and \
ShiftEvolOption != "GammaDriving2ndOrder_Covariant" and \
ShiftEvolOption != "GammaDriving2ndOrder_Covariant__Hatted" and \
ShiftEvolOption != "GammaDriving1stOrder_Covariant" and \
ShiftEvolOption != "GammaDriving1stOrder_Covariant__Hatted":
print("Error: ShiftEvolutionOption == " + ShiftEvolOption + " unsupported!")
sys.exit(1)
# Next import expressions from BSSN_quantities
BU = Bq.BU
betU = Bq.betU
betaU_dupD = Bq.betaU_dupD
# Define needed quantities
beta_rhsU = ixp.zerorank1()
B_rhsU = ixp.zerorank1()
# In the case of Frozen shift condition, we
# explicitly set the betaU and BU RHS's to zero
# instead of relying on the ixp.zerorank1()'s above,
# for safety.
if ShiftEvolOption == "Frozen":
for i in range(DIM):
beta_rhsU[i] = sp.sympify(0)
BU[i] = sp.sympify(0)
if ShiftEvolOption == "GammaDriving2ndOrder_NoCovariant":
# Step 3.a.i: Compute right-hand side of beta^i
# * \partial_t \beta^i = \beta^j \beta^i_{,j} + B^i
for i in range(DIM):
beta_rhsU[i] += BU[i]
for j in range(DIM):
beta_rhsU[i] += betaU[j] * betaU_dupD[i][j]
# Compute right-hand side of B^i:
eta = par.Cparameters("REAL", thismodule, ["eta"],2.0)
# Step 3.a.ii: Compute right-hand side of B^i
# * \partial_t B^i = \beta^j B^i_{,j} + 3/4 * \partial_0 \Lambda^i - eta B^i
# Step 3.a.iii: Define BU_dupD, in terms of derivative of rescaled variable \bet^i
BU_dupD = ixp.zerorank2()
betU_dupD = ixp.declarerank2("betU_dupD", "nosym")
for i in range(DIM):
for j in range(DIM):
BU_dupD[i][j] = betU_dupD[i][j] * rfm.ReU[i] + betU[i] * rfm.ReUdD[i][j]
# Step 3.a.iv: Compute \partial_0 \bar{\Lambda}^i = (\partial_t - \beta^i \partial_i) \bar{\Lambda}^j
Lambdabar_partial0 = ixp.zerorank1()
for i in range(DIM):
Lambdabar_partial0[i] = Brhs.Lambdabar_rhsU[i]
for i in range(DIM):
for j in range(DIM):
Lambdabar_partial0[j] += -betaU[i] * Brhs.LambdabarU_dupD[j][i]
# Step 3.a.v: Evaluate RHS of B^i:
for i in range(DIM):
B_rhsU[i] += sp.Rational(3, 4) * Lambdabar_partial0[i] - eta * BU[i]
for j in range(DIM):
B_rhsU[i] += betaU[j] * BU_dupD[i][j]
# Step 3.b: The right-hand side of the \partial_t \beta^i equation
if "GammaDriving2ndOrder_Covariant" in ShiftEvolOption:
# Step 3.b Option 2: \partial_t \beta^i = \left[\beta^j \bar{D}_j \beta^i\right] + B^{i}
# First we need GammabarUDD, defined in Bq.gammabar__inverse_and_derivs()
Bq.gammabar__inverse_and_derivs()
ConnectionUDD = Bq.GammabarUDD
# If instead we wish to use the Hatted covariant derivative, we replace
# ConnectionUDD with GammahatUDD:
if ShiftEvolOption == "GammaDriving2ndOrder_Covariant__Hatted":
ConnectionUDD = rfm.GammahatUDD
# Then compute right-hand side:
# Term 1: \beta^j \beta^i_{,j}
for i in range(DIM):
for j in range(DIM):
beta_rhsU[i] += betaU[j] * betaU_dupD[i][j]
# Term 2: \beta^j \bar{\Gamma}^i_{mj} \beta^m
for i in range(DIM):
for j in range(DIM):
for m in range(DIM):
beta_rhsU[i] += betaU[j] * ConnectionUDD[i][m][j] * betaU[m]
# Term 3: B^i
for i in range(DIM):
beta_rhsU[i] += BU[i]
if "GammaDriving2ndOrder_Covariant" in ShiftEvolOption:
ConnectionUDD = Bq.GammabarUDD
# If instead we wish to use the Hatted covariant derivative, we replace
# ConnectionUDD with GammahatUDD:
if ShiftEvolOption == "GammaDriving2ndOrder_Covariant__Hatted":
ConnectionUDD = rfm.GammahatUDD
# Step 3.c: Covariant option:
# \partial_t B^i = \beta^j \bar{D}_j B^i
# + \frac{3}{4} ( \partial_t \bar{\Lambda}^{i} - \beta^j \bar{D}_j \bar{\Lambda}^{i} )
# - \eta B^{i}
# = \beta^j B^i_{,j} + \beta^j \bar{\Gamma}^i_{mj} B^m
# + \frac{3}{4}[ \partial_t \bar{\Lambda}^{i}
# - \beta^j (\bar{\Lambda}^i_{,j} + \bar{\Gamma}^i_{mj} \bar{\Lambda}^m)]
# - \eta B^{i}
# Term 1, part a: First compute B^i_{,j} using upwinded derivative
BU_dupD = ixp.zerorank2()
betU_dupD = ixp.declarerank2("betU_dupD", "nosym")
for i in range(DIM):
for j in range(DIM):
BU_dupD[i][j] = betU_dupD[i][j] * rfm.ReU[i] + betU[i] * rfm.ReUdD[i][j]
# Term 1: \beta^j B^i_{,j}
for i in range(DIM):
for j in range(DIM):
B_rhsU[i] += betaU[j] * BU_dupD[i][j]
# Term 2: \beta^j \bar{\Gamma}^i_{mj} B^m
for i in range(DIM):
for j in range(DIM):
for m in range(DIM):
B_rhsU[i] += betaU[j] * ConnectionUDD[i][m][j] * BU[m]
# Term 3: \frac{3}{4}\partial_t \bar{\Lambda}^{i}
for i in range(DIM):
B_rhsU[i] += sp.Rational(3, 4) * Brhs.Lambdabar_rhsU[i]
# Term 4: -\frac{3}{4}\beta^j \bar{\Lambda}^i_{,j}
for i in range(DIM):
for j in range(DIM):
B_rhsU[i] += -sp.Rational(3, 4) * betaU[j] * Brhs.LambdabarU_dupD[i][j]
# Term 5: -\frac{3}{4}\beta^j \bar{\Gamma}^i_{mj} \bar{\Lambda}^m
for i in range(DIM):
for j in range(DIM):
for m in range(DIM):
B_rhsU[i] += -sp.Rational(3, 4) * betaU[j] * ConnectionUDD[i][m][j] * Bq.LambdabarU[m]
# Term 6: - \eta B^i
# eta is a free parameter; we declare it here:
eta = par.Cparameters("REAL", thismodule, ["eta"],2.0)
for i in range(DIM):
B_rhsU[i] += -eta * BU[i]
if "GammaDriving1stOrder_Covariant" in ShiftEvolOption:
# Step 3.c: \partial_t \beta^i = \left[\beta^j \bar{D}_j \beta^i\right] + 3/4 Lambdabar^i - eta*beta^i
# First set \partial_t B^i = 0:
B_rhsU = ixp.zerorank1() # \partial_t B^i = 0
# Second, set \partial_t beta^i RHS:
# Compute covariant advection term:
# We need GammabarUDD, defined in Bq.gammabar__inverse_and_derivs()
Bq.gammabar__inverse_and_derivs()
ConnectionUDD = Bq.GammabarUDD
# If instead we wish to use the Hatted covariant derivative, we replace
# ConnectionUDD with GammahatUDD:
if ShiftEvolOption == "GammaDriving1stOrder_Covariant__Hatted":
ConnectionUDD = rfm.GammahatUDD
# Term 1: \beta^j \beta^i_{,j}
for i in range(DIM):
for j in range(DIM):
beta_rhsU[i] += betaU[j] * betaU_dupD[i][j]
# Term 2: \beta^j \bar{\Gamma}^i_{mj} \beta^m
for i in range(DIM):
for j in range(DIM):
for m in range(DIM):
beta_rhsU[i] += betaU[j] * ConnectionUDD[i][m][j] * betaU[m]
# Term 3: 3/4 Lambdabar^i - eta*beta^i
eta = par.Cparameters("REAL", thismodule, ["eta"], 2.0)
for i in range(DIM):
beta_rhsU[i] += sp.Rational(3, 4) * Bq.LambdabarU[i] - eta * betaU[i]
# Step 4: Rescale the BSSN gauge RHS quantities so that the evolved
# variables may remain smooth across coord singularities
global vet_rhsU,bet_rhsU
vet_rhsU = ixp.zerorank1()
bet_rhsU = ixp.zerorank1()
for i in range(DIM):
vet_rhsU[i] = beta_rhsU[i] / rfm.ReU[i]
bet_rhsU[i] = B_rhsU[i] / rfm.ReU[i]
# print(str(Abar_rhsDD[2][2]).replace("**","^").replace("_","").replace("xx","x").replace("sin(x2)","Sin[x2]").replace("sin(2*x2)","Sin[2*x2]").replace("cos(x2)","Cos[x2]").replace("detgbaroverdetghat","detg"))
# print(str(Dbarbetacontraction).replace("**","^").replace("_","").replace("xx","x").replace("sin(x2)","Sin[x2]").replace("detgbaroverdetghat","detg"))
# print(betaU_dD)
# print(str(trK_rhs).replace("xx2","xx3").replace("xx1","xx2").replace("xx0","xx1").replace("**","^").replace("_","").replace("sin(xx2)","Sinx2").replace("xx","x").replace("sin(2*x2)","Sin2x2").replace("cos(x2)","Cosx2").replace("detgbaroverdetghat","detg"))
# print(str(bet_rhsU[0]).replace("xx2","xx3").replace("xx1","xx2").replace("xx0","xx1").replace("**","^").replace("_","").replace("sin(xx2)","Sinx2").replace("xx","x").replace("sin(2*x2)","Sin2x2").replace("cos(x2)","Cosx2").replace("detgbaroverdetghat","detg"))
# As documented in the NRPy+ tutorial module
# Tutorial-BSSN_in_terms_of_ADM.ipynb,
# this module will construct expressions for BSSN
# quantities in terms of ADM quantities.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1: Import needed core NRPy+ modules
from outputC import * # NRPy+: Core C code output module
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
# 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 gammaDD == None:
gammaDD = ixp.declarerank2("gammaDD","sym01")
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]
# 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 gammaDD == None:
gammaDD = ixp.declarerank2("gammaDD","sym01")
if KDD == None:
KDD = ixp.declarerank2("KDD","sym01")
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]
# 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
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]
# 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
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)
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)
# Step 2.e: Rescale beta^i and B^i according to the prescription described in
# the [BSSN in curvilinear coordinates tutorial module](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 betaU == None:
betaU = ixp.declarerank1("betaU")
if BU == None:
BU = ixp.declarerank1("BU")
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]
# This module provides functions that declare and define useful BSSN quantities
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1: Import all needed modules from NRPy+:
import NRPy_param_funcs as par # NRPy+: Parameter interface
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import grid as gri # NRPy+: Functions having to do with numerical grids
import reference_metric as rfm # NRPy+: Reference metric support
import sys # Standard Python modules for multiplatform OS-level functions
# Step 1.a: Set the coordinate system for the numerical grid
# DO NOT SET IN STANDALONE PYTHON MODULE
# par.set_parval_from_str("reference_metric::CoordSystem","Spherical")
# 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.
# DO NOT CALL IN STANDALONE PYTHON MODULE
# rfm.reference_metric()
# 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)
# DO NOT CALL IN STANDALONE PYTHON MODULE
# DIM = 3
# par.set_parval_from_str("grid::DIM",DIM)
# Step 1.d: Declare/initialize parameters for this module
thismodule = __name__
par.initialize_param(par.glb_param("char", thismodule, "EvolvedConformalFactor_cf", "W"))
par.initialize_param(par.glb_param("bool", thismodule, "detgbarOverdetghat_equals_one", "True"))
par.initialize_param(par.glb_param("bool", thismodule, "LeaveRicciSymbolic", "False"))
def declare_BSSN_gridfunctions_if_not_declared_already():
# Step 2: Register all needed BSSN gridfunctions.
# Declare as globals all variables that may be
# used outside this function
global hDD,aDD,lambdaU,vetU,betU,trK,cf,alpha
# Check to see if this function has already been called.
# If so, do not register the gridfunctions again!
for i in range(len(gri.glb_gridfcs_list)):
if "hDD00" in gri.glb_gridfcs_list[i].name:
hDD = ixp.declarerank2("hDD", "sym01")
aDD = ixp.declarerank2("aDD", "sym01")
lambdaU = ixp.declarerank1("lambdaU")
vetU = ixp.declarerank1("vetU")
betU = ixp.declarerank1("betU")
trK, cf, alpha = sp.symbols('trK cf alpha', real=True)
return hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha
# Step 2.a: Register indexed quantities, using ixp.register_... functions
hDD = ixp.register_gridfunctions_for_single_rank2("EVOL", "hDD", "sym01")
aDD = ixp.register_gridfunctions_for_single_rank2("EVOL", "aDD", "sym01")
lambdaU = ixp.register_gridfunctions_for_single_rank1("EVOL", "lambdaU")
vetU = ixp.register_gridfunctions_for_single_rank1("EVOL", "vetU")
betU = ixp.register_gridfunctions_for_single_rank1("EVOL", "betU")
# Step 2.b: Register scalar quantities, using gri.register_gridfunctions()
trK, cf, alpha = gri.register_gridfunctions("EVOL", ["trK", "cf", "alpha"])
return hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha
# Step 3: Define all basic conformal BSSN tensors
# gammabarDD,AbarDD,LambdabarU,betaU,BU
# in terms of BSSN gridfunctions.
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]
# Step 4: gammabarUU and spatial derivatives of gammabarDD,
# including GammabarUDD
def gammabar__inverse_and_derivs():
# Step 4.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 gammabarUU, gammabarDD_dD, gammabarDD_dupD, gammabarDD_dDD, GammabarUDD
hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already()
DIM = 3
# This function needs gammabarDD, defined in BSSN_basic_tensors()
BSSN_basic_tensors()
# Step 4.a.i: gammabarUU:
gammabarUU, dummydet = ixp.symm_matrix_inverter3x3(gammabarDD)
# Step 4.b.i: gammabarDDdD[i][j][k]
# = \hat{\gamma}_{ij,k} + h_{ij,k} \text{ReDD[i][j]} + h_{ij} \text{ReDDdD[i][j][k]}.
gammabarDD_dD = ixp.zerorank3()
gammabarDD_dupD = ixp.zerorank3()
hDD_dD = ixp.declarerank3("hDD_dD", "sym01")
hDD_dupD = ixp.declarerank3("hDD_dupD", "sym01")
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
gammabarDD_dD[i][j][k] = rfm.ghatDDdD[i][j][k] + \
hDD_dD[i][j][k] * rfm.ReDD[i][j] + hDD[i][j] * rfm.ReDDdD[i][j][k]
# Compute associated upwinded derivative, needed for the \bar{\gamma}_{ij} RHS
gammabarDD_dupD[i][j][k] = rfm.ghatDDdD[i][j][k] + \
hDD_dupD[i][j][k] * rfm.ReDD[i][j] + hDD[i][j] * rfm.ReDDdD[i][j][k]
# Step 4.b.ii: Compute gammabarDD_dDD in terms of the rescaled BSSN quantity hDD
# and its derivatives, as well as the reference metric and rescaling
# matrix, and its derivatives (expression given below):
hDD_dDD = ixp.declarerank4("hDD_dDD", "sym01_sym23")
gammabarDD_dDD = ixp.zerorank4()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
# gammabar_{ij,kl} = gammahat_{ij,kl}
# + h_{ij,kl} ReDD[i][j]
# + h_{ij,k} ReDDdD[i][j][l] + h_{ij,l} ReDDdD[i][j][k]
# + h_{ij} ReDDdDD[i][j][k][l]
gammabarDD_dDD[i][j][k][l] = rfm.ghatDDdDD[i][j][k][l]
gammabarDD_dDD[i][j][k][l] += hDD_dDD[i][j][k][l] * rfm.ReDD[i][j]
gammabarDD_dDD[i][j][k][l] += hDD_dD[i][j][k] * rfm.ReDDdD[i][j][l] + \
hDD_dD[i][j][l] * rfm.ReDDdD[i][j][k]
gammabarDD_dDD[i][j][k][l] += hDD[i][j] * rfm.ReDDdDD[i][j][k][l]
# Step 4.b.iii: Define barred Christoffel symbol \bar{\Gamma}^{i}_{kl} = GammabarUDD[i][k][l] (see expression below)
GammabarUDD = ixp.zerorank3()
for i in range(DIM):
for k in range(DIM):
for l in range(DIM):
for m in range(DIM):
# Gammabar^i_{kl} = 1/2 * gammabar^{im} ( gammabar_{mk,l} + gammabar_{ml,k} - gammabar_{kl,m}):
GammabarUDD[i][k][l] += sp.Rational(1, 2) * gammabarUU[i][m] * \
(gammabarDD_dD[m][k][l] + gammabarDD_dD[m][l][k] - gammabarDD_dD[k][l][m])
# Step 5: det(gammabarDD) and its derivatives
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.")
sys.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 5.b: 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]
# Step 6: Quantities related to conformal traceless
# extrinsic curvature AbarDD:
# AbarUU, AbarUD, and trAbar
def AbarUU_AbarUD_trAbar_AbarDD_dD():
# Step 6.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 AbarUU,AbarUD,trAbar,AbarDD_dD,AbarDD_dupD
hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already()
DIM = 3
# Define AbarDD and gammabarDD in terms of BSSN gridfunctions
BSSN_basic_tensors()
# Define gammabarUU in terms of BSSN gridfunctions
gammabar__inverse_and_derivs()
# Step 6.a.i: Compute Abar^{ij} in terms of Abar_{ij} and gammabar^{ij}
AbarUU = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
# Abar^{ij} = gammabar^{ik} gammabar^{jl} Abar_{kl}
AbarUU[i][j] += gammabarUU[i][k] * gammabarUU[j][l] * AbarDD[k][l]
# Step 6.a.ii: Compute Abar^i_j in terms of Abar_{ij} and gammabar^{ij}
AbarUD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
# Abar^i_j = gammabar^{ik} Abar_{kj}
AbarUD[i][j] += gammabarUU[i][k] * AbarDD[k][j]
# Step 6.a.iii: Compute Abar^k_k = trace of Abar:
trAbar = sp.sympify(0)
for k in range(DIM):
for j in range(DIM):
# Abar^k_k = gammabar^{kj} Abar_{jk}
trAbar += gammabarUU[k][j] * AbarDD[j][k]
# Step 6.a.iv: Compute Abar_{ij,k}
AbarDD_dD = ixp.zerorank3()
AbarDD_dupD = ixp.zerorank3()
aDD_dD = ixp.declarerank3("aDD_dD" ,"sym01")
aDD_dupD = ixp.declarerank3("aDD_dupD","sym01")
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
AbarDD_dupD[i][j][k] = rfm.ReDDdD[i][j][k]*aDD[i][j] + rfm.ReDD[i][j]*aDD_dupD[i][j][k]
AbarDD_dD[i][j][k] = rfm.ReDDdD[i][j][k]*aDD[i][j] + rfm.ReDD[i][j]*aDD_dD[ i][j][k]
# Step 7: The conformal ("barred") Ricci tensor RbarDD
# and associated quantities
def RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU():
# Step 7.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 RbarDD,DGammaUDD,gammabarDD_dHatD,DGammaU
hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already()
DIM = 3
# GammabarUDD is used below, defined in
# gammabar__inverse_and_derivs()
gammabar__inverse_and_derivs()
# Step 7.a.i: Define \varepsilon_{ij} = epsDD[i][j]
epsDD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
epsDD[i][j] = hDD[i][j] * rfm.ReDD[i][j]
# Step 7.a.ii: Define epsDD_dD[i][j][k]
hDD_dD = ixp.declarerank3("hDD_dD", "sym01")
epsDD_dD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
epsDD_dD[i][j][k] = hDD_dD[i][j][k] * rfm.ReDD[i][j] + hDD[i][j] * rfm.ReDDdD[i][j][k]
# Step 7.a.iii: Define epsDD_dDD[i][j][k][l]
hDD_dDD = ixp.declarerank4("hDD_dDD", "sym01_sym23")
epsDD_dDD = ixp.zerorank4()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
epsDD_dDD[i][j][k][l] = hDD_dDD[i][j][k][l] * rfm.ReDD[i][j] + \
hDD_dD[i][j][k] * rfm.ReDDdD[i][j][l] + \
hDD_dD[i][j][l] * rfm.ReDDdD[i][j][k] + \
hDD[i][j] * rfm.ReDDdDD[i][j][k][l]
# Step 7.a.iv: DhatgammabarDDdD[i][j][l] = \bar{\gamma}_{ij;\hat{l}}
# \bar{\gamma}_{ij;\hat{l}} = \varepsilon_{i j,l}
# - \hat{\Gamma}^m_{i l} \varepsilon_{m j}
# - \hat{\Gamma}^m_{j l} \varepsilon_{i m}
gammabarDD_dHatD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for l in range(DIM):
gammabarDD_dHatD[i][j][l] = epsDD_dD[i][j][l]
for m in range(DIM):
gammabarDD_dHatD[i][j][l] += - rfm.GammahatUDD[m][i][l] * epsDD[m][j] \
- rfm.GammahatUDD[m][j][l] * epsDD[i][m]
# Step 7.a.v: \bar{\gamma}_{ij;\hat{l},k} = DhatgammabarDD_dHatD_dD[i][j][l][k]:
# \bar{\gamma}_{ij;\hat{l},k} = \varepsilon_{ij,lk}
# - \hat{\Gamma}^m_{i l,k} \varepsilon_{m j}
# - \hat{\Gamma}^m_{i l} \varepsilon_{m j,k}
# - \hat{\Gamma}^m_{j l,k} \varepsilon_{i m}
# - \hat{\Gamma}^m_{j l} \varepsilon_{i m,k}
gammabarDD_dHatD_dD = ixp.zerorank4()
for i in range(DIM):
for j in range(DIM):
for l in range(DIM):
for k in range(DIM):
gammabarDD_dHatD_dD[i][j][l][k] = epsDD_dDD[i][j][l][k]
for m in range(DIM):
gammabarDD_dHatD_dD[i][j][l][k] += -rfm.GammahatUDDdD[m][i][l][k] * epsDD[m][j] \
- rfm.GammahatUDD[m][i][l] * epsDD_dD[m][j][k] \
- rfm.GammahatUDDdD[m][j][l][k] * epsDD[i][m] \
- rfm.GammahatUDD[m][j][l] * epsDD_dD[i][m][k]
# Step 7.a.vi: \bar{\gamma}_{ij;\hat{l}\hat{k}} = DhatgammabarDD_dHatDD[i][j][l][k]
# \bar{\gamma}_{ij;\hat{l}\hat{k}} = \partial_k \hat{D}_{l} \varepsilon_{i j}
# - \hat{\Gamma}^m_{lk} \left(\hat{D}_{m} \varepsilon_{i j}\right)
# - \hat{\Gamma}^m_{ik} \left(\hat{D}_{l} \varepsilon_{m j}\right)
# - \hat{\Gamma}^m_{jk} \left(\hat{D}_{l} \varepsilon_{i m}\right)
gammabarDD_dHatDD = ixp.zerorank4()
for i in range(DIM):
for j in range(DIM):
for l in range(DIM):
for k in range(DIM):
gammabarDD_dHatDD[i][j][l][k] = gammabarDD_dHatD_dD[i][j][l][k]
for m in range(DIM):
gammabarDD_dHatDD[i][j][l][k] += - rfm.GammahatUDD[m][l][k] * gammabarDD_dHatD[i][j][m] \
- rfm.GammahatUDD[m][i][k] * gammabarDD_dHatD[m][j][l] \
- rfm.GammahatUDD[m][j][k] * gammabarDD_dHatD[i][m][l]
# Step 7.b: Second term of RhatDD: compute \hat{D}_{j} \bar{\Lambda}^{k} = LambarU_dHatD[k][j]
lambdaU_dD = ixp.declarerank2("lambdaU_dD", "nosym")
LambarU_dHatD = ixp.zerorank2()
for j in range(DIM):
for k in range(DIM):
LambarU_dHatD[k][j] = lambdaU_dD[k][j] * rfm.ReU[k] + lambdaU[k] * rfm.ReUdD[k][j]
for m in range(DIM):
LambarU_dHatD[k][j] += rfm.GammahatUDD[k][m][j] * lambdaU[m] * rfm.ReU[m]
# Step 7.c: Conformal Ricci tensor, part 3: The \Delta^{k} \Delta_{(i j) k}
# + \bar{\gamma}^{k l}*(2 \Delta_{k(i}^{m} \Delta_{j) m l}
# + \Delta_{i k}^{m} \Delta_{m j l}) terms
# Step 7.c.i: Define \Delta^i_{jk} = \bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk} = DGammaUDD[i][j][k]
DGammaUDD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
DGammaUDD[i][j][k] = GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k]
# Step 7.c.ii: Define \Delta^i = \bar{\gamma}^{jk} \Delta^i_{jk}
DGammaU = ixp.zerorank1()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
DGammaU[i] += gammabarUU[j][k] * DGammaUDD[i][j][k]
# Step 7.c.iii: Define \Delta_{ijk} = \bar{\gamma}_{im} \Delta^m_{jk}
DGammaDDD = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for m in range(DIM):
DGammaDDD[i][j][k] += gammabarDD[i][m] * DGammaUDD[m][j][k]
if par.parval_from_str(thismodule+"::LeaveRicciSymbolic") == "True":
for i in range(len(gri.glb_gridfcs_list)):
if "RbarDD00" in gri.glb_gridfcs_list[i].name:
return
RbarDD = ixp.register_gridfunctions_for_single_rank2("AUXEVOL","RbarDD","sym01")
return
# Step 7.d: Summing the terms and defining \bar{R}_{ij}
# Step 7.d.i: Add the first term to RbarDD:
# Rbar_{ij} += - \frac{1}{2} \bar{\gamma}^{k l} \hat{D}_{k} \hat{D}_{l} \bar{\gamma}_{i j}
RbarDD = ixp.zerorank2()
RbarDDpiece = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
RbarDD[i][j] += -sp.Rational(1, 2) * gammabarUU[k][l] * gammabarDD_dHatDD[i][j][l][k]
RbarDDpiece[i][j] += -sp.Rational(1, 2) * gammabarUU[k][l] * gammabarDD_dHatDD[i][j][l][k]
# Step 7.d.ii: Add the second term to RbarDD:
# Rbar_{ij} += (1/2) * (gammabar_{ki} Lambar^k_{;\hat{j}} + gammabar_{kj} Lambar^k_{;\hat{i}})
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
RbarDD[i][j] += sp.Rational(1, 2) * ( gammabarDD[k][i] * LambarU_dHatD[k][j] +
gammabarDD[k][j] * LambarU_dHatD[k][i] )
# Step 7.d.iii: Add the remaining term to RbarDD:
# Rbar_{ij} += \Delta^{k} \Delta_{(i j) k} = 1/2 \Delta^{k} (\Delta_{i j k} + \Delta_{j i k})
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
RbarDD[i][j] += sp.Rational(1, 2) * DGammaU[k] * (DGammaDDD[i][j][k] + DGammaDDD[j][i][k])
# Step 7.d.iv: Add the final term to RbarDD:
# Rbar_{ij} += \bar{\gamma}^{k l} (\Delta^{m}_{k i} \Delta_{j m l}
# + \Delta^{m}_{k j} \Delta_{i m l}
# + \Delta^{m}_{i k} \Delta_{m j l})
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
for m in range(DIM):
RbarDD[i][j] += gammabarUU[k][l] * (DGammaUDD[m][k][i] * DGammaDDD[j][m][l] +
DGammaUDD[m][k][j] * DGammaDDD[i][m][l] +
DGammaUDD[m][i][k] * DGammaDDD[m][j][l])
# Step 8: The unrescaled shift vector betaU spatial derivatives:
# betaUdD & betaUdDD, written in terms of the
# rescaled shift vector vetU
def betaU_derivs():
# Step 8.i: Declare as globals all expressions that may be used
# outside this function, declare BSSN gridfunctions
# if not defined already, and set DIM=3.
global betaU_dD,betaU_dupD,betaU_dDD
hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already()
DIM = 3
# Step 8.ii: Compute the unrescaled shift vector beta^i = ReU[i]*vet^i
vetU_dD = ixp.declarerank2("vetU_dD", "nosym")
vetU_dupD = ixp.declarerank2("vetU_dupD", "nosym") # Needed for upwinded \beta^i_{,j}
vetU_dDD = ixp.declarerank3("vetU_dDD", "sym12") # Needed for \beta^i_{,j}
betaU_dD = ixp.zerorank2()
betaU_dupD = ixp.zerorank2() # Needed for, e.g., \beta^i RHS
betaU_dDD = ixp.zerorank3() # Needed for, e.g., \bar{\Lambda}^i RHS
for i in range(DIM):
for j in range(DIM):
betaU_dD[i][j] = vetU_dD[i][j] * rfm.ReU[i] + vetU[i] * rfm.ReUdD[i][j]
betaU_dupD[i][j] = vetU_dupD[i][j] * rfm.ReU[i] + vetU[i] * rfm.ReUdD[i][j] # Needed for \beta^i RHS
for k in range(DIM):
# Needed for, e.g., \bar{\Lambda}^i RHS:
betaU_dDD[i][j][k] = vetU_dDD[i][j][k] * rfm.ReU[i] + vetU_dD[i][j] * rfm.ReUdD[i][k] + \
vetU_dD[i][k] * rfm.ReUdD[i][j] + vetU[i] * rfm.ReUdDD[i][j][k]
# Step 9: Standard BSSN conformal factor phi,
# and its partial and covariant derivatives,
# all in terms of BSSN gridfunctions like cf
def phi_and_derivs():
# Step 9.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 phi_dD,phi_dupD,phi_dDD,exp_m4phi,phi_dBarD,phi_dBarDD
hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already()
DIM = 3
# GammabarUDD is used below, defined in
# gammabar__inverse_and_derivs()
gammabar__inverse_and_derivs()
# Step 9.a.i: Define partial derivatives of \phi in terms of evolved quantity "cf":
cf_dD = ixp.declarerank1("cf_dD")
cf_dupD = ixp.declarerank1("cf_dupD") # Needed for \partial_t \phi next.
cf_dDD = ixp.declarerank2("cf_dDD", "sym01")
phi_dD = ixp.zerorank1()
phi_dupD = ixp.zerorank1()
phi_dDD = ixp.zerorank2()
exp_m4phi = sp.sympify(0)
# Step 9.a.ii: Assuming cf=phi, define exp_m4phi, phi_dD,
# phi_dupD (upwind finite-difference version of phi_dD), and phi_DD
if par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "phi":
for i in range(DIM):
phi_dD[i] = cf_dD[i]
phi_dupD[i] = cf_dupD[i]
for j in range(DIM):
phi_dDD[i][j] = cf_dDD[i][j]
exp_m4phi = sp.exp(-4 * cf)
# Step 9.a.iii: Assuming cf=W=e^{-2 phi}, define exp_m4phi, phi_dD,
# phi_dupD (upwind finite-difference version of phi_dD), and phi_DD
if par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "W":
# \partial_i W = \partial_i (e^{-2 phi}) = -2 e^{-2 phi} \partial_i phi
# -> \partial_i phi = -\partial_i cf / (2 cf)
for i in range(DIM):
phi_dD[i] = - cf_dD[i] / (2 * cf)
phi_dupD[i] = - cf_dupD[i] / (2 * cf)
for j in range(DIM):
# \partial_j \partial_i phi = - \partial_j [\partial_i cf / (2 cf)]
# = - cf_{,ij} / (2 cf) + \partial_i cf \partial_j cf / (2 cf^2)
phi_dDD[i][j] = (- cf_dDD[i][j] + cf_dD[i] * cf_dD[j] / cf) / (2 * cf)
exp_m4phi = cf * cf
# Step 9.a.iv: Assuming cf=chi=e^{-4 phi}, define exp_m4phi, phi_dD,
# phi_dupD (upwind finite-difference version of phi_dD), and phi_DD
if par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "chi":
# \partial_i chi = \partial_i (e^{-4 phi}) = -4 e^{-4 phi} \partial_i phi
# -> \partial_i phi = -\partial_i cf / (4 cf)
for i in range(DIM):
phi_dD[i] = - cf_dD[i] / (4 * cf)
phi_dupD[i] = - cf_dupD[i] / (4 * cf)
for j in range(DIM):
# \partial_j \partial_i phi = - \partial_j [\partial_i cf / (4 cf)]
# = - cf_{,ij} / (4 cf) + \partial_i cf \partial_j cf / (4 cf^2)
phi_dDD[i][j] = (- cf_dDD[i][j] + cf_dD[i] * cf_dD[j] / cf) / (4 * cf)
exp_m4phi = cf
# Step 9.a.v: Error out if unsupported EvolvedConformalFactor_cf choice is made:
cf_choice = par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf")
if not (cf_choice == "phi" or cf_choice == "W" or cf_choice == "chi"):
print("Error: EvolvedConformalFactor_cf == " + par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") + " unsupported!")
sys.exit(1)
# Step 9.b: Define phi_dBarD = phi_dD (since phi is a scalar) and phi_dBarDD (covariant derivative)
# \bar{D}_i \bar{D}_j \phi = \phi_{;\bar{i}\bar{j}} = \bar{D}_i \phi_{,j}
# = \phi_{,ij} - \bar{\Gamma}^k_{ij} \phi_{,k}
phi_dBarD = phi_dD
phi_dBarDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
phi_dBarDD[i][j] = phi_dDD[i][j]
for k in range(DIM):
phi_dBarDD[i][j] += - GammabarUDD[k][i][j] * phi_dD[k]
# As documented in the NRPy+ tutorial module
# Tutorial-BSSN_stress_energy_source_terms.ipynb,
# this module will construct expressions for
# BSSN stress-energy source terms, in terms of
# elements of T^{mu nu}.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
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 BSSN.ADMBSSN_tofrom_4metric as AB4m # NRPy+: ADM/BSSN <-> 4-metric conversions
import sys # Standard Python modules for multiplatform OS-level functions
thismodule = __name__
# Define BSSN source terms in terms of T^{mu nu} or T_{mu nu}
def stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars(inputvars,custom_T4UU=None):
# Step 1: Check if rfm.reference_metric() already called. If not, BSSN
# quantities are not yet defined, so cannot proceed!
if rfm.have_already_called_reference_metric_function == False:
print("BSSN_source_terms_ito_T4UU(): Must call reference_metric() first!")
sys.exit(1)
# Step 2.a: Define gamma4DD[mu][nu] = g_{mu nu} + n_{mu} n_{nu}
alpha = sp.symbols("alpha", real=True)
zero = sp.sympify(0)
n4D = [-alpha, zero, zero, zero]
AB4m.g4DD_ito_BSSN_or_ADM(inputvars)
gamma4DD = ixp.zerorank2(DIM=4)
for mu in range(4):
for nu in range(4):
gamma4DD[mu][nu] = AB4m.g4DD[mu][nu] + n4D[mu] * n4D[nu]
# Step 2.b: If expression for components of T4UU not given, declare T4UU here
if custom_T4UU == None:
T4UU = ixp.declarerank2("T4UU","sym01",DIM=4)
else:
T4UU = custom_T4UU
# Step 2.c: Define BSSN source terms
global SDD,SD,S,rho
# Step 2.c.i: S_{ij} = gamma_{i mu} gamma_{j nu} T^{mu nu}
SDD = ixp.zerorank2()
for i in range(3):
for j in range(3):
for mu in range(4):
for nu in range(4):
SDD[i][j] += gamma4DD[i+1][mu] * gamma4DD[j+1][nu] * T4UU[mu][nu]
# Step 2.c.ii: S_{i} = -gamma_{i mu} n_{nu} T^{mu nu}
SD = ixp.zerorank1()
for i in range(3):
for mu in range(4):
for nu in range(4):
SD[i] += - gamma4DD[i+1][mu] * n4D[nu] * T4UU[mu][nu]
# Step 2.c.iii: S = gamma^{ij} S_{ij}
if inputvars == "ADM":
gammaDD = ixp.declarerank2("gammaDD", "sym01")
gammaUU, dummydet = ixp.symm_matrix_inverter3x3(gammaDD) # Set gammaUU
elif inputvars == "BSSN":
import BSSN.ADM_in_terms_of_BSSN as AitoB # NRPy+: ADM quantities in terms of BSSN quantities
AitoB.ADM_in_terms_of_BSSN()
gammaUU = AitoB.gammaUU
S = zero
for i in range(3):
for j in range(3):
S += gammaUU[i][j] * SDD[i][j]
# Step 2.c.iv: rho = n_{mu} n_{nu} T^{mu nu}
rho = zero
for mu in range(4):
for nu in range(4):
rho += n4D[mu] * n4D[nu] * T4UU[mu][nu]
return SDD,SD,S,rho
# Step 3: Add BSSN stress-energy source terms to BSSN RHSs
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]
# Step 4: Add BSSN stress-energy source terms to BSSN constraints
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]
# This module sets up Brill-Lindquist initial data in terms of
# the variables used in BSSN_RHSs.py
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# # Setting up Two Black Hole Initial Data, in Curvilinear Coordinates
#
# ## This module sets up initial data for two black holes at rest in spherical coordinates, converts the initial data/basis to the desired CoordSystem, and then rescales all quantities according to the BSSNCurvilinear prescription.
#
# ### Brill-Lindquist initial data ([Brill & Lindquist, Phys. Rev. 131, 471, 1963](https://journals.aps.org/pr/abstract/10.1103/PhysRev.131.471); see also Eq. 1 of [Brandt & Br\"ugmann, arXiv:gr-qc/9711015v1](https://arxiv.org/pdf/gr-qc/9711015v1.pdf)) may be written in terms of the BSSN conformal factor and ADM extrinsic curvature as
#
# $$\psi = e^{\phi} = 1 + \sum_{i=1}^N \frac{m_{(i)}}{2 \left|\vec{r}_{(i)} - \vec{r}\right|};\quad K_{ij}=0.$$
#
# These data consist of $N$ nonspinning black holes initially at rest. This module restricts to the case of two such black holes, positioned along either the $x$ or $z$ axis. Here, we implement $N=2$.
#
# **Inputs for $\psi$**:
# * The position and (bare) mass of black hole 1: $\left(x_{(1)},y_{(1)},z_{(1)}\right)$ and $m_{(1)}$, respectively
# * The position and (bare) mass of black hole 2: $\left(x_{(2)},y_{(2)},z_{(2)}\right)$ and $m_{(2)}$, respectively
#
# **Additional variables needed for spacetime evolution**:
# * Desired coordinate system
# * 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
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):
global Cartxyz,gammaCartDD, KCartDD, alphaCart, betaCartU, BCartU
# 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
global returnfunction
returnfunction = bIDf.BSSN_ID_function_string(cf, hDD, lambdaU, aDD, trK, alpha, vetU, betU)
# This module implements the gammabar = gammahat constraint.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step P1: import all needed modules from NRPy+:
from outputC import * # NRPy+: Core C code output module
import finite_difference as fin # NRPy+: Finite difference C code generation module
import grid as gri # NRPy+: Functions having to do with numerical grids
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import BSSN.BSSN_quantities as Bq # NRPy+: Computes useful BSSN quantities
import os # Standard Python modules for multiplatform OS-level functions
def Enforce_Detgammabar_Constraint_symb_expressions():
# Set spatial dimension (must be 3 for BSSN)
DIM = 3
# Then we set the coordinate system for the numerical grid
rfm.reference_metric() # Create ReU, ReDD needed for rescaling B-L initial data, generating BSSN RHSs, etc.
# We will need the h_{ij} quantities defined within BSSN_RHSs
# below when we enforce the gammahat=gammabar constraint
# Step 1: All barred quantities are defined in terms of BSSN rescaled gridfunctions,
# which we declare here in case they haven't yet been declared elsewhere.
Bq.declare_BSSN_gridfunctions_if_not_declared_already()
hDD = Bq.hDD
Bq.BSSN_basic_tensors()
gammabarDD = Bq.gammabarDD
# First define the Kronecker delta:
KroneckerDeltaDD = ixp.zerorank2()
for i in range(DIM):
KroneckerDeltaDD[i][i] = sp.sympify(1)
# The detgammabar in BSSN_RHSs is set to detgammahat when BSSN_RHSs::detgbarOverdetghat_equals_one=True (default),
# so we manually compute it here:
dummygammabarUU, detgammabar = ixp.symm_matrix_inverter3x3(gammabarDD)
# Next apply the constraint enforcement equation above.
hprimeDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
hprimeDD[i][j] = \
(nrpyAbs(rfm.detgammahat) / detgammabar) ** (sp.Rational(1, 3)) * (KroneckerDeltaDD[i][j] + hDD[i][j]) \
- KroneckerDeltaDD[i][j]
enforce_detg_constraint_symb_expressions = [
lhrh(lhs=gri.gfaccess("in_gfs", "hDD00"), rhs=hprimeDD[0][0]),
lhrh(lhs=gri.gfaccess("in_gfs", "hDD01"), rhs=hprimeDD[0][1]),
lhrh(lhs=gri.gfaccess("in_gfs", "hDD02"), rhs=hprimeDD[0][2]),
lhrh(lhs=gri.gfaccess("in_gfs", "hDD11"), rhs=hprimeDD[1][1]),
lhrh(lhs=gri.gfaccess("in_gfs", "hDD12"), rhs=hprimeDD[1][2]),
lhrh(lhs=gri.gfaccess("in_gfs", "hDD22"), rhs=hprimeDD[2][2])]
return enforce_detg_constraint_symb_expressions
def output_Enforce_Detgammabar_Constraint_Ccode(outdir="BSSN/", exprs="", Read_xxs=False):
# Step 0: Check if outdir is string; error out if not.
check_if_string__error_if_not(outdir,"outdir")
desc = "Enforce det(gammabar) = det(gammahat) constraint."
name = "enforce_detgammabar_constraint"
params = "const rfm_struct *restrict rfmstruct,const paramstruct *restrict params, REAL *restrict in_gfs"
loopopts = "AllPoints,Enable_rfm_precompute"
if Read_xxs:
params = "const paramstruct *restrict params, REAL *restrict xx[3], REAL *restrict in_gfs"
loopopts = "AllPoints,Read_xxs"
outCfunction(
outfile=os.path.join(outdir, name + ".h"), desc=desc, name=name, params=params,
body=fin.FD_outputC("returnstring", exprs,
params="outCverbose=False,preindent=1,includebraces=False").replace("IDX4", "IDX4S"),
loopopts=loopopts)
// This function takes as input either (x,y,z) or (r,th,ph) and outputs
// all ADM quantities in the Cartesian or Spherical basis, respectively.
void ID_ADM_SphorCart(const REAL xyz_or_rthph[3],
REAL *gammaDD00,REAL *gammaDD01,REAL *gammaDD02,REAL *gammaDD11,REAL *gammaDD12,REAL *gammaDD22,
REAL *KDD00,REAL *KDD01,REAL *KDD02,REAL *KDD11,REAL *KDD12,REAL *KDD22,
REAL *alpha,
REAL *betaU0,REAL *betaU1,REAL *betaU2,
REAL *BU0,REAL *BU1,REAL *BU2) {
const REAL r = xyz_or_rthph[0];
const REAL th = xyz_or_rthph[1];
const REAL ph = xyz_or_rthph[2];
const double tmp0 = (1.0/4.0)*M;
const double tmp1 = pow(M, 2);
const double tmp2 = pow(chi, 2)*tmp1;
const double tmp3 = -tmp2;
const double tmp4 = sqrt(tmp1 + tmp3);
const double tmp5 = (1.0/4.0)*tmp4;
const double tmp6 = cos(th);
const double tmp7 = 1.0/r;
const double tmp8 = (1.0/4.0)*tmp7*(M + tmp4) + 1;
const double tmp9 = pow(r, 2)*pow(tmp8, 4);
const double tmp10 = tmp2*pow(tmp6, 2) + tmp9;
const double tmp11 = r*pow(tmp8, 2);
const double tmp12 = -M + tmp11 + tmp4;
const double tmp13 = tmp2 + tmp9;
const double tmp14 = 2*tmp11;
const double tmp15 = sin(th);
const double tmp16 = pow(tmp15, 2);
const double tmp17 = tmp16*tmp2;
const double tmp18 = pow(tmp13, 2) - tmp17*(-M*tmp14 + tmp13);
const double tmp19 = 1.0/tmp10;
const double tmp20 = tmp16*tmp19;
const double tmp21 = pow(tmp10*tmp18, -1.0/2.0);
const double tmp22 = pow(M, 4);
*gammaDD00 = tmp10*pow(r + tmp0 + tmp5, 2)/(pow(r, 3)*tmp12);
*gammaDD01 = 0;
*gammaDD02 = 0;
*gammaDD11 = tmp10;
*gammaDD12 = 0;
*gammaDD22 = tmp18*tmp20;
*KDD00 = 0;
*KDD01 = 0;
*KDD02 = chi*tmp1*tmp20*tmp21*tmp8*(-pow(chi, 4)*tmp22 + 3*pow(r, 4)*pow(tmp8, 8) - tmp17*(tmp3 + tmp9) + 2*tmp2*tmp9)/sqrt(r*tmp12);
*KDD11 = 0;
*KDD12 = -pow(chi, 3)*tmp14*pow(tmp15, 3)*tmp19*tmp21*tmp22*tmp6*sqrt(tmp12*tmp7)*(r - tmp0 - tmp5);
*KDD22 = 0;
*alpha = 1;
*betaU0 = 0;
*betaU1 = 0;
*betaU2 = 0;
*BU0 = 0;
*BU1 = 0;
*BU2 = 0;
}
# As documented in the NRPy+ tutorial module
# Tutorial-Psi4.ipynb,
# this module will construct a generic
# expression for \psi_4
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step 1.a: import all needed modules from NRPy+:
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
def Psi4(specify_tetrad=True):
global psi4_im_pt, psi4_re_pt
# 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 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: Import all ADM quantities as written in terms of BSSN quantities
import BSSN.ADM_in_terms_of_BSSN as AB
AB.ADM_in_terms_of_BSSN()
# Step 1.e: Set up tetrad vectors
if specify_tetrad==True:
import BSSN.Psi4_tetrads as BP4t
BP4t.Psi4_tetrads()
mre4U = BP4t.mre4U
mim4U = BP4t.mim4U
n4U = BP4t.n4U
else:
# For code validation against NRPy+ psi_4 tutorial module (Tutorial-Psi4.ipynb);
# ensures a more complete code validation.
mre4U = ixp.declarerank1("mre4U", DIM=4)
mim4U = ixp.declarerank1("mim4U", DIM=4)
n4U = ixp.declarerank1("n4U", DIM=4)
# Step 2: Construct the (rank-4) Riemann curvature tensor associated with the ADM 3-metric:
RDDDD = ixp.zerorank4()
gammaDDdDD = AB.gammaDDdDD
for i in range(DIM):
for k in range(DIM):
for l in range(DIM):
for m in range(DIM):
RDDDD[i][k][l][m] = sp.Rational(1, 2) * \
(gammaDDdDD[i][m][k][l] + gammaDDdDD[k][l][i][m] - gammaDDdDD[i][l][k][m] -
gammaDDdDD[k][m][i][l])
# ... then we add the term on the right:
gammaDD = AB.gammaDD
GammaUDD = AB.GammaUDD
for i in range(DIM):
for k in range(DIM):
for l in range(DIM):
for m in range(DIM):
for n in range(DIM):
for p in range(DIM):
RDDDD[i][k][l][m] += gammaDD[n][p] * \
(GammaUDD[n][k][l] * GammaUDD[p][i][m] - GammaUDD[n][k][m] * GammaUDD[p][i][l])
# Step 3: Construct the (rank-4) tensor in term 1 of psi_4 (referring to Eq 5.1 in
# Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf
rank4term1DDDD = ixp.zerorank4()
KDD = AB.KDD
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
rank4term1DDDD[i][j][k][l] = RDDDD[i][j][k][l] + KDD[i][k] * KDD[l][j] - KDD[i][l] * KDD[k][j]
# Step 4: Construct the (rank-3) tensor in term 2 of psi_4 (referring to Eq 5.1 in
# Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf
rank3term2DDD = ixp.zerorank3()
KDDdD = AB.KDDdD
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
rank3term2DDD[j][k][l] = sp.Rational(1, 2) * (KDDdD[j][k][l] - KDDdD[j][l][k])
# ... then we construct the second term in this sum:
# \Gamma^{p}_{j[k} K_{l]p} = \frac{1}{2} (\Gamma^{p}_{jk} K_{lp}-\Gamma^{p}_{jl} K_{kp}):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
for p in range(DIM):
rank3term2DDD[j][k][l] += sp.Rational(1, 2) * (
GammaUDD[p][j][k] * KDD[l][p] - GammaUDD[p][j][l] * KDD[k][p])
# Finally, we multiply the term by $-8$:
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
rank3term2DDD[j][k][l] *= sp.sympify(-8)
# Step 5: Construct the (rank-2) tensor in term 3 of psi_4 (referring to Eq 5.1 in
# Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf
# Step 5.1: Construct 3-Ricci tensor R_{ij} = gamma^{im} R_{ijml}
RDD = ixp.zerorank2()
gammaUU = AB.gammaUU
for j in range(DIM):
for l in range(DIM):
for i in range(DIM):
for m in range(DIM):
RDD[j][l] += gammaUU[i][m]*RDDDD[i][j][m][l]
# Step 5.2: Construct K^p_l = gamma^{pi} K_{il}
KUD = ixp.zerorank2()
for p in range(DIM):
for l in range(DIM):
for i in range(DIM):
KUD[p][l] += gammaUU[p][i]*KDD[i][l]
# Step 5.3: Construct trK = gamma^{ij} K_{ij}
trK = sp.sympify(0)
for i in range(DIM):
for j in range(DIM):
trK += gammaUU[i][j]*KDD[i][j]
# Next we put these terms together to construct the entire term in parentheses:
# +4 \left(R_{jl} - K_{jp} K^p_l + K K_{jl} \right),
rank2term3DD = ixp.zerorank2()
for j in range(DIM):
for l in range(DIM):
rank2term3DD[j][l] = RDD[j][l] + trK*KDD[j][l]
for p in range(DIM):
rank2term3DD[j][l] += - KDD[j][p]*KUD[p][l]
# Finally we multiply by +4:
for j in range(DIM):
for l in range(DIM):
rank2term3DD[j][l] *= sp.sympify(4)
# Step 6: Construct real & imaginary parts of psi_4
# by contracting constituent rank 2, 3, and 4
# tensors with input tetrads mre4U, mim4U, & n4U.
def tetrad_product__Real_psi4(n, Mre, Mim, mu, nu, eta, delta):
return +n[mu] * Mre[nu] * n[eta] * Mre[delta] - n[mu] * Mim[nu] * n[eta] * Mim[delta]
def tetrad_product__Imag_psi4(n, Mre, Mim, mu, nu, eta, delta):
return -n[mu] * Mre[nu] * n[eta] * Mim[delta] - n[mu] * Mim[nu] * n[eta] * Mre[delta]
# We split psi_4 into three pieces, to expedite & possibly parallelize C code generation.
psi4_re_pt = [sp.sympify(0),sp.sympify(0),sp.sympify(0)]
psi4_im_pt = [sp.sympify(0),sp.sympify(0),sp.sympify(0)]
# First term:
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
psi4_re_pt[0] += rank4term1DDDD[i][j][k][l] * tetrad_product__Real_psi4(n4U, mre4U, mim4U,
i + 1, j + 1,k + 1, l + 1)
psi4_im_pt[0] += rank4term1DDDD[i][j][k][l] * tetrad_product__Imag_psi4(n4U, mre4U, mim4U,
i + 1, j + 1, k + 1, l + 1)
# Second term:
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
psi4_re_pt[1] += rank3term2DDD[j][k][l] * \
sp.Rational(1, 2) * (+tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, k + 1, l + 1)
- tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, k + 1, l + 1))
psi4_im_pt[1] += rank3term2DDD[j][k][l] * \
sp.Rational(1, 2) * (+tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, k + 1, l + 1)
- tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, k + 1, l + 1))
# Third term:
for j in range(DIM):
for l in range(DIM):
psi4_re_pt[2] += rank2term3DD[j][l] * \
(sp.Rational(1, 4) * (+tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, 0, l + 1)
- tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, 0, l + 1)
- tetrad_product__Real_psi4(n4U, mre4U, mim4U, 0, j + 1, l + 1, 0)
+ tetrad_product__Real_psi4(n4U, mre4U, mim4U, j + 1, 0, l + 1, 0)))
psi4_im_pt[2] += rank2term3DD[j][l] * \
(sp.Rational(1, 4) * (+tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, 0, l + 1)
- tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, 0, l + 1)
- tetrad_product__Imag_psi4(n4U, mre4U, mim4U, 0, j + 1, l + 1, 0)
+ tetrad_product__Imag_psi4(n4U, mre4U, mim4U, j + 1, 0, l + 1, 0)))
# As documented in the NRPy+ tutorial module
# Tutorial-Psi4_tetrads.ipynb,
# this module will construct tetrads
# needed to compute \psi_4 (as well as other
# Weyl scalars and invariants in principle)
# Authors: Zachariah B. Etienne
# (zachetie **at** gmail **dot* com),
# and Patrick Nelson
# Step 1.a: import all needed modules from NRPy+:
import sympy as sp
import NRPy_param_funcs as par
import indexedexp as ixp
import grid as gri
import finite_difference as fin
import reference_metric as rfm
# Step 1.b: Initialize TetradChoice parameter
thismodule = __name__
# Current option: QuasiKinnersley = choice made in Baker, Campanelli, and Lousto. PRD 65, 044001 (2002)
par.initialize_param(par.glb_param("char", thismodule, "TetradChoice", "QuasiKinnersley"))
par.initialize_param(par.glb_param("char", thismodule, "UseCorrectUnitNormal", "False"))
def Psi4_tetrads():
global l4U, n4U, mre4U, mim4U
# Step 1.c: Check if tetrad choice is implemented:
if par.parval_from_str(thismodule+"::TetradChoice") != "QuasiKinnersley":
print("ERROR: "+thismodule+"::TetradChoice = "+par.parval_from_str("TetradChoice")+" currently unsupported!")
exit(1)
# 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: 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.f: Import all ADM quantities as written in terms of BSSN quantities
# import BSSN.ADM_in_terms_of_BSSN as AB
# AB.ADM_in_terms_of_BSSN()
# Step 2.a: Declare the Cartesian x,y,z as input parameters
# and v_1^a, v_2^a, and v_3^a tetrads,
# as well as detgamma and gammaUU from
# BSSN.ADM_in_terms_of_BSSN
x,y,z = gri.register_gridfunctions("AUX",["x","y","z"])
# x, y, z = par.Cparameters("REAL", thismodule, ["x", "y", "z"])
v1UCart = ixp.zerorank1()
v2UCart = ixp.zerorank1()
gammaDD = ixp.declarerank2("gammaDD","sym01")
gammaUU,detgamma = ixp.symm_matrix_inverter3x3(gammaDD)
# detgamma = AB.detgamma
# gammaUU = AB.gammaUU
# Step 2.b: Define v1U and v2U
v1UCart = [-y, x, sp.sympify(0)]
v2UCart = [x, y, z]
v1U = ixp.zerorank1()
v2U = ixp.zerorank1()
for i in range(DIM):
v1U[i] = v1UCart[i]
v2U[i] = v2UCart[i]
# # Step 2.c: Construct the Jacobian d x_Cart^i / d xx^j
# Jac_dUCart_dDrfmUD = ixp.zerorank2()
# for i in range(DIM):
# for j in range(DIM):
# Jac_dUCart_dDrfmUD[i][j] = sp.diff(rfm.xxCart[i], rfm.xx[j])
#
# # Step 2.d: Invert above Jacobian to get needed d xx^j / d x_Cart^i
# Jac_dUrfm_dDCartUD, dummyDET = ixp.generic_matrix_inverter3x3(Jac_dUCart_dDrfmUD)
#
# # Step 2.e: Transform v1U and v2U from the Cartesian to the xx^i basis
# v1U = ixp.zerorank1()
# v2U = ixp.zerorank1()
# for i in range(DIM):
# for j in range(DIM):
# v1U[i] += Jac_dUrfm_dDCartUD[i][j] * v1UCart[j]
# v2U[i] += Jac_dUrfm_dDCartUD[i][j] * v2UCart[j]
# Step 2.f: Define the rank-3 version of the Levi-Civita symbol. Amongst
# other uses, this is needed for the construction of the approximate
# quasi-Kinnersley tetrad.
def define_LeviCivitaSymbol_rank3(DIM=-1):
if DIM == -1:
DIM = par.parval_from_str("DIM")
LeviCivitaSymbol = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
# From https://codegolf.stackexchange.com/questions/160359/levi-civita-symbol :
LeviCivitaSymbol[i][j][k] = (i - j) * (j - k) * (k - i) / 2
return LeviCivitaSymbol
# Step 2.g: Define v3U
v3U = ixp.zerorank1()
LeviCivitaSymbolDDD = define_LeviCivitaSymbol_rank3(DIM=3)
for a in range(DIM):
for b in range(DIM):
for c in range(DIM):
for d in range(DIM):
v3U[a] += sp.sqrt(detgamma) * gammaUU[a][d] * LeviCivitaSymbolDDD[d][b][c] * v1U[b] * v2U[c]
# Step 2.h: Define omega_{ij}
omegaDD = ixp.zerorank2()
#gammaDD = AB.gammaDD
def v_vectorDU(v1U,v2U,v3U, i,a):
if i==0:
return v1U[a]
elif i==1:
return v2U[a]
elif i==2:
return v3U[a]
else:
print("ERROR: unknown vector!")
exit(1)
def update_omega(omegaDD, i,j, v1U,v2U,v3U,gammaDD):
omegaDD[i][j] = sp.sympify(0)
for a in range(DIM):
for b in range(DIM):
omegaDD[i][j] += v_vectorDU(v1U,v2U,v3U, i,a)*v_vectorDU(v1U,v2U,v3U, j,b)*gammaDD[a][b]
# Step 2.i: Define e^a_i. Note that:
# omegaDD[0][0] = \omega_{11} above;
# omegaDD[1][1] = \omega_{22} above, etc.
# First e_1^a: Orthogonalize & normalize:
e1U = ixp.zerorank1()
update_omega(omegaDD, 0,0, v1U,v2U,v3U,gammaDD)
for a in range(DIM):
e1U[a] = v1U[a]/sp.sqrt(omegaDD[0][0])
# Next e_2^a: First orthogonalize:
e2U = ixp.zerorank1()
update_omega(omegaDD, 0,1, e1U,v2U,v3U,gammaDD)
for a in range(DIM):
e2U[a] = (v2U[a] - omegaDD[0][1]*e1U[a])
# Then normalize:
update_omega(omegaDD, 1,1, e1U,e2U,v3U,gammaDD)
for a in range(DIM):
e2U[a] /= sp.sqrt(omegaDD[1][1])
# Next e_3^a: First orthogonalize:
e3U = ixp.zerorank1()
update_omega(omegaDD, 0,2, e1U,e2U,v3U,gammaDD)
update_omega(omegaDD, 1,2, e1U,e2U,v3U,gammaDD)
for a in range(DIM):
e3U[a] = (v3U[a] - omegaDD[0][2]*e1U[a] - omegaDD[1][2]*e2U[a])
# Then normalize:
update_omega(omegaDD, 2,2, e1U,e2U,e3U,gammaDD)
for a in range(DIM):
e3U[a] /= sp.sqrt(omegaDD[2][2])
# Step 2.j: Construct l^mu, n^mu, and m^mu, based on r^mu, theta^mu, phi^mu, and u^mu:
r4U = ixp.zerorank1(DIM=4)
u4U = ixp.zerorank1(DIM=4)
theta4U = ixp.zerorank1(DIM=4)
phi4U = ixp.zerorank1(DIM=4)
for a in range(DIM):
r4U[ a+1] = e2U[a]
theta4U[a+1] = e3U[a]
phi4U[ a+1] = e1U[a]
# FIXME? assumes alpha=1, beta^i = 0
if par.parval_from_str(thismodule+"::UseCorrectUnitNormal") == "False":
u4U[0] = 1
else:
# Eq. 2.116 in Baumgarte & Shapiro:
# n^mu = {1/alpha, -beta^i/alpha}. Note that n_mu = {alpha,0}, so n^mu n_mu = -1.
import BSSN.BSSN_quantities as Bq
Bq.declare_BSSN_gridfunctions_if_not_declared_already()
Bq.BSSN_basic_tensors()
u4U[0] = 1/Bq.alpha
for i in range(DIM):
u4U[i+1] = -Bq.betaU[i]/Bq.alpha
l4U = ixp.zerorank1(DIM=4)
n4U = ixp.zerorank1(DIM=4)
mre4U = ixp.zerorank1(DIM=4)
mim4U = ixp.zerorank1(DIM=4)
isqrt2 = 1/sp.sqrt(2)
for mu in range(4):
l4U[mu] = isqrt2*(u4U[mu] + r4U[mu])
n4U[mu] = isqrt2*(u4U[mu] - r4U[mu])
mre4U[mu] = isqrt2*theta4U[mu]
mim4U[mu] = isqrt2* phi4U[mu]
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<script async src=\"https://www.googletagmanager.com/gtag/js?id=UA-59152712-8\"></script>\n",
"<script>\n",
" window.dataLayer = window.dataLayer || [];\n",
" function gtag(){dataLayer.push(arguments);}\n",
" gtag('js', new Date());\n",
"\n",
" gtag('config', 'UA-59152712-8');\n",
"</script>\n",
"\n",
"# Start-to-Finish validation of $\\psi_4$ in curvilinear coordinates against Cartesian formulation provided by [Patrick Nelson's Weyl scalars & invariants in Cartesian coordinates module](../../Tutorial-WeylScalarsInvariants-Cartesian.ipynb)\n",
"\n",
"### Author: Zach Etienne\n",
"\n",
"<font color='blue'>**This module exists as a modification of [the NRPy+ $\\psi_4$ in curvilinear coordinates module](../../Tutorial-Psi4.ipynb), writing all spacetime quantities in terms of ADM variables and their derivatives directly.**</font>\n",
"\n",
"## A Note on Notation\n",
"\n",
"As is standard in NRPy+, \n",
"\n",
"* Greek indices range from 0 to 3, inclusive, with the zeroth component denoting the temporal (time) component.\n",
"* Latin indices range from 0 to 2, inclusive, with the zeroth component denoting the first spatial component.\n",
"\n",
"As a corollary, any expressions involving mixed Greek and Latin indices will need to offset one set of indices by one: A Latin index in a four-vector will be incremented and a Greek index in a three-vector will be decremented (however, the latter case does not occur in this tutorial module).\n",
"\n",
"<a id='toc'></a>\n",
"\n",
"# Introduction, Table of Contents\n",
"$$\\label{toc}$$\n",
"\n",
"This module constructs $\\psi_4$, a quantity that is immensely useful when extracting gravitational wave content from a numerical relativity simulation. $\\psi_4$ is related to the gravitational wave strain via\n",
"\n",
"$$\n",
"\\psi_4 = \\ddot{h}_+ - i \\ddot{h}_\\times.\n",
"$$\n",
"\n",
"We construct $\\psi_4$ from the standard ADM spatial metric $\\gamma_{ij}$ and extrinsic curvature $K_{ij}$, and their derivatives. The full expression is given by Eq. 5.1 in [Baker, Campanelli, Lousto (2001)](https://arxiv.org/pdf/gr-qc/0104063.pdf):\n",
"\n",
"\\begin{align}\n",
"\\psi_4 &= \\left[ {R}_{ijkl}+2K_{i[k}K_{l]j}\\right]\n",
"{n}^i\\bar{m}^j{n}^k\\bar{m}^l \\\\\n",
"& -8\\left[ K_{j[k,l]}+{\\Gamma }_{j[k}^pK_{l]p}\\right]\n",
"{n}^{[0}\\bar{m}^{j]}{n}^k\\bar{m}^l \\\\\n",
"& +4\\left[ {R}_{jl}-K_{jp}K_l^p+KK_{jl}\\right]\n",
"{n}^{[0}\\bar{m}^{j]}{n}^{[0}\\bar{m}^{l]},\n",
"\\end{align}\n",
"\n",
"Note that $\\psi_4$ is imaginary, with the imaginary components originating from the tetrad vector $m^\\mu$. This module does not specify a tetrad; instead it only constructs the above expression leaving $m^\\mu$ and $n^\\mu$ unspecified. The [next module on tetrads defines these tetrad quantities](Tutorial-Psi4_tetrads.ipynb) (currently only a quasi-Kinnersley tetrad is supported).\n",
"\n",
"**This tutorial module is organized as follows:**\n",
"\n",
"1. [Step 1](#initializenrpy): Initialize needed NRPy+ modules\n",
"1. [Step 2](#riemann): Constructing the 3-Riemann tensor $R_{ik\\ell m}$\n",
"1. [Step 3](#termone): Constructing the rank-4 tensor in Term 1 of $\\psi_4$: $R_{ijkl} + 2 K_{i[k} K_{l]j}$\n",
"1. [Step 4](#termtwo): Constructing the rank-3 tensor in Term 2 of $\\psi_4$: $-8 \\left(K_{j[k,l]} + \\Gamma^{p}_{j[k} K_{l]p} \\right)$\n",
"1. [Step 5](#termthree): Constructing the rank-2 tensor in term 3 of $\\psi_4$: $+4 \\left(R_{jl} - K_{jp} K^p_l + K K_{jl} \\right)$\n",
"1. [Step 6](#psifour): Constructing $\\psi_4$ through contractions of the above terms with arbitrary tetrad vectors $n^\\mu$ and $m^\\mu$\n",
"1. [Step 7](#code_validation): Code Validation against BSSN.Psi4 NRPy+ module\n",
"1. [Step 8](#latex_pdf_output): Output this notebook to $\\LaTeX$-formatted PDF"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='initializenrpy'></a>\n",
"\n",
"# Step 1: Initialize core NRPy+ modules \\[Back to [top](#toc)\\]\n",
"$$\\label{initializenrpy}$$\n",
"\n",
"Let's start by importing all the needed modules from NRPy+:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"# Step 1.a: import all needed modules from NRPy+:\n",
"import sympy as sp\n",
"from outputC import *\n",
"import NRPy_param_funcs as par\n",
"import indexedexp as ixp\n",
"import grid as gri\n",
"import finite_difference as fin\n",
"import reference_metric as rfm\n",
"\n",
"# Step 1.b: Set the coordinate system for the numerical grid\n",
"par.set_parval_from_str(\"reference_metric::CoordSystem\",\"Cartesian\")\n",
"\n",
"# Step 1.c: Given the chosen coordinate system, set up \n",
"# corresponding reference metric and needed\n",
"# reference metric quantities\n",
"# The following function call sets up the reference metric\n",
"# and related quantities, including rescaling matrices ReDD,\n",
"# ReU, and hatted quantities.\n",
"rfm.reference_metric()\n",
"\n",
"# Step 1.d: Set spatial dimension (must be 3 for BSSN, as BSSN is \n",
"# a 3+1-dimensional decomposition of the general \n",
"# relativistic field equations)\n",
"DIM = 3\n",
"\n",
"# Step 1.e: Import all ADM quantities as written in terms of BSSN quantities\n",
"# import BSSN.ADM_in_terms_of_BSSN as AB\n",
"# AB.ADM_in_terms_of_BSSN()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='riemann'></a>\n",
"\n",
"# Step 2: Constructing the 3-Riemann tensor $R_{ik\\ell m}$ \\[Back to [top](#toc)\\]\n",
"$$\\label{riemann}$$\n",
"\n",
"Analogously to Christoffel symbols, the Riemann tensor is a measure of the curvature of an $N$-dimensional manifold. Thus the 3-Riemann tensor is not simply a projection of the 4-Riemann tensor (see e.g., Eq. 2.7 of [Campanelli *et al* (1998)](https://arxiv.org/pdf/gr-qc/9803058.pdf) for the relation between 4-Riemann and 3-Riemann), as $N$-dimensional Riemann tensors are meant to define a notion of curvature given only the associated $N$-dimensional metric. \n",
"\n",
"So, given the ADM 3-metric, the Riemann tensor in arbitrary dimension is given by the 3-dimensional version of Eq. 1.19 in Baumgarte & Shapiro's *Numerical Relativity*. I.e.,\n",
"\n",
"$$\n",
"R^i_{jkl} = \\partial_k \\Gamma^{i}_{jl} - \\partial_l \\Gamma^{i}_{jk} + \\Gamma^i_{mk} \\Gamma^m_{jl} - \\Gamma^{i}_{ml} \\Gamma^{m}_{jk},\n",
"$$\n",
"where $\\Gamma^i_{jk}$ is the Christoffel symbol associated with the 3-metric $\\gamma_{ij}$:\n",
"\n",
"$$\n",
"\\Gamma^l_{ij} = \\frac{1}{2} \\gamma^{lk} \\left(\\gamma_{ki,j} + \\gamma_{kj,i} - \\gamma_{ij,k} \\right) \n",
"$$\n",
"\n",
"Notice that this equation for the Riemann tensor is equivalent to the equation given in the Wikipedia article on [Formulas in Riemannian geometry](https://en.wikipedia.org/w/index.php?title=List_of_formulas_in_Riemannian_geometry&oldid=882667524):\n",
"\n",
"$$\n",
"R^\\ell{}_{ijk}=\n",
"\\partial_j \\Gamma^\\ell{}_{ik}-\\partial_k\\Gamma^\\ell{}_{ij}\n",
"+\\Gamma^\\ell{}_{js}\\Gamma_{ik}^s-\\Gamma^\\ell{}_{ks}\\Gamma^s{}_{ij},\n",
"$$\n",
"with the replacements $i\\to \\ell$, $j\\to i$, $k\\to j$, $l\\to k$, and $s\\to m$. Wikipedia also provides a simpler form in terms of second-derivatives of three-metric itself (using the definition of Christoffel symbol), so that we need not define derivatives of the Christoffel symbol:\n",
"\n",
"$$\n",
"R_{ik\\ell m}=\\frac{1}{2}\\left(\n",
"\\gamma_{im,k\\ell} \n",
"+ \\gamma_{k\\ell,im}\n",
"- \\gamma_{i\\ell,km}\n",
"- \\gamma_{km,i\\ell} \\right)\n",
"+\\gamma_{np} \\left(\n",
"\\Gamma^n{}_{k\\ell} \\Gamma^p{}_{im} - \n",
"\\Gamma^n{}_{km} \\Gamma^p{}_{i\\ell} \\right).\n",
"$$\n",
"\n",
"First we construct the term on the left:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# Step 2: Construct the (rank-4) Riemann curvature tensor associated with the ADM 3-metric:\n",
"RDDDD = ixp.zerorank4()\n",
"gammaDD = ixp.register_gridfunctions_for_single_rank2(\"AUX\",\"gammaDD\", \"sym01\") # The AUX or EVOL designation is *not*\n",
" # used in diagnostic modules.\n",
"kDD = ixp.register_gridfunctions_for_single_rank2(\"AUX\",\"kDD\", \"sym01\")\n",
"gammaDD_dD = ixp.declarerank3(\"gammaDD_dD\",\"sym01\")\n",
"gammaDD_dDD = ixp.declarerank4(\"gammaDD_dDD\",\"sym01_sym23\")\n",
"\n",
"# gammaDD_dDD = AB.gammaDD_dDD\n",
"\n",
"for i in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" for m in range(DIM):\n",
" RDDDD[i][k][l][m] = sp.Rational(1,2) * \\\n",
" (gammaDD_dDD[i][m][k][l] + gammaDD_dDD[k][l][i][m] - gammaDD_dDD[i][l][k][m] - gammaDD_dDD[k][m][i][l])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"... then we add the term on the right:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"# ... then we add the term on the right:\n",
"# Define the Christoffel symbols\n",
"GammaUDD = ixp.zerorank3(DIM)\n",
"gammaUU,gammadetdummy = ixp.symm_matrix_inverter3x3(gammaDD)\n",
"for i in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" for m in range(DIM):\n",
" GammaUDD[i][k][l] += (sp.Rational(1,2))*gammaUU[i][m]*\\\n",
" (gammaDD_dD[m][k][l] + gammaDD_dD[m][l][k] - gammaDD_dD[k][l][m])\n",
"\n",
"for i in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" for m in range(DIM):\n",
" for n in range(DIM):\n",
" for p in range(DIM):\n",
" RDDDD[i][k][l][m] += gammaDD[n][p] * \\\n",
" (GammaUDD[n][k][l]*GammaUDD[p][i][m] - GammaUDD[n][k][m]*GammaUDD[p][i][l])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='termone'></a>\n",
"\n",
"# Step 3: Constructing the rank-4 tensor in Term 1 of $\\psi_4$: $R_{ijkl} + 2 K_{i[k} K_{l]j}$ \\[Back to [top](#toc)\\]\n",
"$$\\label{termone}$$\n",
"\n",
"Following Eq. 5.1 in [Baker, Campanelli, Lousto (2001)](https://arxiv.org/pdf/gr-qc/0104063.pdf), the rank-4 tensor in the first term of $\\psi_4$ is given by\n",
"\n",
"$$\n",
"R_{ijkl} + 2 K_{i[k} K_{l]j} = R_{ijkl} + K_{ik} K_{lj} - K_{il} K_{kj}\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"# Step 3: Construct the (rank-4) tensor in term 1 of psi_4 (referring to Eq 5.1 in \n",
"# Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf\n",
"rank4term1 = ixp.zerorank4()\n",
"# kDD = AB.kDD\n",
"\n",
"for i in range(DIM):\n",
" for j in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" rank4term1[i][j][k][l] = RDDDD[i][j][k][l] + kDD[i][k]*kDD[l][j] - kDD[i][l]*kDD[k][j]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='termtwo'></a>\n",
"\n",
"# Step 4: Constructing the rank-3 tensor in Term 2 of $\\psi_4$: $-8 \\left(K_{j[k,l]} + \\Gamma^{p}_{j[k} K_{l]p} \\right)$ \\[Back to [top](#toc)\\]\n",
"$$\\label{termtwo}$$\n",
"\n",
"Following Eq. 5.1 in [Baker, Campanelli, Lousto (2001)](https://arxiv.org/pdf/gr-qc/0104063.pdf), the rank-3 tensor in the second term of $\\psi_4$ is given by\n",
"\n",
"$$\n",
"-8 \\left(K_{j[k,l]} + \\Gamma^{p}_{j[k} K_{l]p} \\right)\n",
"$$\n",
"First let's construct the first term in this sum: $K_{j[k,l]} = \\frac{1}{2} (K_{jk,l} - K_{jl,k})$:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"# Step 4: Construct the (rank-3) tensor in term 2 of psi_4 (referring to Eq 5.1 in \n",
"# Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf\n",
"rank3term2 = ixp.zerorank3()\n",
"# kDD_dD = AB.kDD_dD\n",
"kDD_dD = ixp.declarerank3(\"kDD_dD\",\"sym01\")\n",
"\n",
"for j in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" rank3term2[j][k][l] = sp.Rational(1,2)*(kDD_dD[j][k][l] - kDD_dD[j][l][k])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"... then we construct the second term in this sum: $\\Gamma^{p}_{j[k} K_{l]p} = \\frac{1}{2} (\\Gamma^{p}_{jk} K_{lp}-\\Gamma^{p}_{jl} K_{kp})$:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"# ... then we construct the second term in this sum: \n",
"# \\Gamma^{p}_{j[k} K_{l]p} = \\frac{1}{2} (\\Gamma^{p}_{jk} K_{lp}-\\Gamma^{p}_{jl} K_{kp}):\n",
"for j in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" for p in range(DIM):\n",
" rank3term2[j][k][l] += sp.Rational(1,2)*(GammaUDD[p][j][k]*kDD[l][p] - GammaUDD[p][j][l]*kDD[k][p])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, we multiply the term by $-8$:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"# Finally, we multiply the term by $-8$:\n",
"for j in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" rank3term2[j][k][l] *= sp.sympify(-8)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='termthree'></a>\n",
"\n",
"# Step 5: Constructing the rank-2 tensor in term 3 of $\\psi_4$: $+4 \\left(R_{jl} - K_{jp} K^p_l + K K_{jl} \\right)$ \\[Back to [top](#toc)\\]\n",
"$$\\label{termthree}$$\n",
"\n",
"Following Eq. 5.1 in [Baker, Campanelli, Lousto (2001)](https://arxiv.org/pdf/gr-qc/0104063.pdf), the rank-2 tensor in the third term of $\\psi_4$ is given by\n",
"\n",
"$$\n",
"+4 \\left(R_{jl} - K_{jp} K^p_l + K K_{jl} \\right),\n",
"$$\n",
"where\n",
"\\begin{align}\n",
"R_{jl} &= R^i_{jil} \\\\\n",
"&= \\gamma^{im} R_{ijml} \\\\\n",
"K &= K^i_i \\\\\n",
"&= \\gamma^{im} K_{im}\n",
"\\end{align}\n",
"\n",
"Let's build the components of this term: $R_{jl}$, $K^p_l$, and $K$, as defined above:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"# Step 5: Construct the (rank-2) tensor in term 3 of psi_4 (referring to Eq 5.1 in \n",
"# Baker, Campanelli, Lousto (2001); https://arxiv.org/pdf/gr-qc/0104063.pdf\n",
"\n",
"# Step 5.1: Construct 3-Ricci tensor R_{ij} = gamma^{im} R_{ijml}\n",
"RDD = ixp.zerorank2()\n",
"for j in range(DIM):\n",
" for l in range(DIM):\n",
" for i in range(DIM):\n",
" for m in range(DIM):\n",
" RDD[j][l] += gammaUU[i][m]*RDDDD[i][j][m][l]\n",
"\n",
"# Step 5.2: Construct K^p_l = gamma^{pi} K_{il}\n",
"KUD = ixp.zerorank2()\n",
"for p in range(DIM):\n",
" for l in range(DIM):\n",
" for i in range(DIM):\n",
" KUD[p][l] += gammaUU[p][i]*kDD[i][l]\n",
"\n",
"# Step 5.3: Construct trK = gamma^{ij} K_{ij}\n",
"trK = sp.sympify(0)\n",
"for i in range(DIM):\n",
" for j in range(DIM):\n",
" trK += gammaUU[i][j]*kDD[i][j]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next we put these terms together to construct the entire term:\n",
"$$\n",
"+4 \\left(R_{jl} - K_{jp} K^p_l + K K_{jl} \\right),\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"# Next we put these terms together to construct the entire term in parentheses:\n",
"# +4 \\left(R_{jl} - K_{jp} K^p_l + K K_{jl} \\right),\n",
"rank2term3 = ixp.zerorank2()\n",
"for j in range(DIM):\n",
" for l in range(DIM):\n",
" rank2term3[j][l] = RDD[j][l] + trK*kDD[j][l]\n",
" for p in range(DIM):\n",
" rank2term3[j][l] += - kDD[j][p]*KUD[p][l]\n",
"# Finally we multiply by +4:\n",
"for j in range(DIM):\n",
" for l in range(DIM):\n",
" rank2term3[j][l] *= sp.sympify(4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='psifour'></a>\n",
"\n",
"# Step 6: Constructing $\\psi_4$ through contractions of the above terms with an arbitrary tetrad vectors $m^\\mu$ and $n^\\mu$ \\[Back to [top](#toc)\\]\n",
"$$\\label{psifour}$$\n",
"\n",
"Eq. 5.1 in [Baker, Campanelli, Lousto (2001)](https://arxiv.org/pdf/gr-qc/0104063.pdf) writes $\\psi_4$ (which is complex) as the contraction of each of the above terms with products of tetrad vectors:\n",
"\n",
"\\begin{align}\n",
"\\psi_4 &= \\left[ {R}_{ijkl}+2K_{i[k}K_{l]j}\\right]\n",
"{n}^i\\bar{m}^j{n}^k\\bar{m}^l \\\\\n",
"& -8\\left[ K_{j[k,l]}+{\\Gamma }_{j[k}^pK_{l]p}\\right]\n",
"{n}^{[0}\\bar{m}^{j]}{n}^k\\bar{m}^l \\\\\n",
"& +4\\left[ {R}_{jl}-K_{jp}K_l^p+KK_{jl}\\right]\n",
"{n}^{[0}\\bar{m}^{j]}{n}^{[0}\\bar{m}^{l]},\n",
"\\end{align}\n",
"where $\\bar{m}^\\mu$ is the complex conjugate of $m^\\mu$, and $n^\\mu$ is real. The third term is given by\n",
"\\begin{align}\n",
"{n}^{[0}\\bar{m}^{j]}{n}^{[0}\\bar{m}^{l]}\n",
"&= \\frac{1}{2}({n}^{0}\\bar{m}^{j} - {n}^{j}\\bar{m}^{0} )\\frac{1}{2}({n}^{0}\\bar{m}^{l} - {n}^{l}\\bar{m}^{0} )\\\\\n",
"&= \\frac{1}{4}({n}^{0}\\bar{m}^{j} - {n}^{j}\\bar{m}^{0} )({n}^{0}\\bar{m}^{l} - {n}^{l}\\bar{m}^{0} )\\\\\n",
"&= \\frac{1}{4}({n}^{0}\\bar{m}^{j}{n}^{0}\\bar{m}^{l} - {n}^{j}\\bar{m}^{0}{n}^{0}\\bar{m}^{l} - {n}^{0}\\bar{m}^{j}{n}^{l}\\bar{m}^{0} + {n}^{j}\\bar{m}^{0}{n}^{l}\\bar{m}^{0})\n",
"\\end{align}\n",
"\n",
"Only $m^\\mu$ is complex, so we can separate the real and imaginary parts of $\\psi_4$ by hand, defining $M^\\mu$ to now be the real part of $\\bar{m}^\\mu$ and $\\mathcal{M}^\\mu$ to be the imaginary part. All of the above products are of the form ${n}^\\mu\\bar{m}^\\nu{n}^\\eta\\bar{m}^\\delta$, so let's evalute the real and imaginary parts of this product once, for all such terms:\n",
"\n",
"\\begin{align}\n",
"{n}^\\mu\\bar{m}^\\nu{n}^\\eta\\bar{m}^\\delta\n",
"&= {n}^\\mu(M^\\nu - i \\mathcal{M}^\\nu){n}^\\eta(M^\\delta - i \\mathcal{M}^\\delta) \\\\\n",
"&= \\left({n}^\\mu M^\\nu {n}^\\eta M^\\delta -\n",
"{n}^\\mu \\mathcal{M}^\\nu {n}^\\eta \\mathcal{M}^\\delta \\right)+\n",
"i \\left(\n",
"-{n}^\\mu M^\\nu {n}^\\eta \\mathcal{M}^\\delta\n",
"-{n}^\\mu \\mathcal{M}^\\nu {n}^\\eta M^\\delta\n",
"\\right)\n",
"\\end{align}\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"initialize_param() minor warning: Did nothing; already initialized parameter reference_metric::M_PI\n",
"initialize_param() minor warning: Did nothing; already initialized parameter reference_metric::xmin\n",
"initialize_param() minor warning: Did nothing; already initialized parameter reference_metric::xmax\n",
"initialize_param() minor warning: Did nothing; already initialized parameter reference_metric::ymin\n",
"initialize_param() minor warning: Did nothing; already initialized parameter reference_metric::ymax\n",
"initialize_param() minor warning: Did nothing; already initialized parameter reference_metric::zmin\n",
"initialize_param() minor warning: Did nothing; already initialized parameter reference_metric::zmax\n"
]
}
],
"source": [
"# mre4U = ixp.declarerank1(\"mre4U\",DIM=4)\n",
"# mim4U = ixp.declarerank1(\"mim4U\",DIM=4)\n",
"# n4U = ixp.declarerank1(\"n4U\" ,DIM=4)\n",
"\n",
"import BSSN.Psi4_tetrads as P4t\n",
"P4t.Psi4_tetrads()\n",
"mre4U = P4t.mre4U\n",
"mim4U = P4t.mim4U\n",
"n4U = P4t.n4U\n",
"\n",
"def tetrad_product__Real_psi4(n,Mre,Mim, mu,nu,eta,delta):\n",
" return +n[mu]*Mre[nu]*n[eta]*Mre[delta] - n[mu]*Mim[nu]*n[eta]*Mim[delta]\n",
"\n",
"def tetrad_product__Imag_psi4(n,Mre,Mim, mu,nu,eta,delta):\n",
" return -n[mu]*Mre[nu]*n[eta]*Mim[delta] - n[mu]*Mim[nu]*n[eta]*Mre[delta]\n",
"\n",
"\n",
"psi4_re = sp.sympify(0)\n",
"psi4_im = sp.sympify(0)\n",
"# First term:\n",
"for i in range(DIM):\n",
" for j in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" psi4_re += rank4term1[i][j][k][l]*tetrad_product__Real_psi4(n4U,mre4U,mim4U, i+1,j+1,k+1,l+1)\n",
" psi4_im += rank4term1[i][j][k][l]*tetrad_product__Imag_psi4(n4U,mre4U,mim4U, i+1,j+1,k+1,l+1)\n",
"\n",
"# Second term:\n",
"for j in range(DIM):\n",
" for k in range(DIM):\n",
" for l in range(DIM):\n",
" psi4_re += rank3term2[j][k][l] * \\\n",
" sp.Rational(1,2)*(+tetrad_product__Real_psi4(n4U,mre4U,mim4U, 0,j+1,k+1,l+1)\n",
" -tetrad_product__Real_psi4(n4U,mre4U,mim4U, j+1,0,k+1,l+1) )\n",
" psi4_im += rank3term2[j][k][l] * \\\n",
" sp.Rational(1,2)*(+tetrad_product__Imag_psi4(n4U,mre4U,mim4U, 0,j+1,k+1,l+1)\n",
" -tetrad_product__Imag_psi4(n4U,mre4U,mim4U, j+1,0,k+1,l+1) )\n",
"# Third term:\n",
"for j in range(DIM):\n",
" for l in range(DIM):\n",
" psi4_re += rank2term3[j][l] * \\\n",
" (sp.Rational(1,4)*(+tetrad_product__Real_psi4(n4U,mre4U,mim4U, 0,j+1,0,l+1)\n",
" -tetrad_product__Real_psi4(n4U,mre4U,mim4U, j+1,0,0,l+1)\n",
" -tetrad_product__Real_psi4(n4U,mre4U,mim4U, 0,j+1,l+1,0)\n",
" +tetrad_product__Real_psi4(n4U,mre4U,mim4U, j+1,0,l+1,0)))\n",
" psi4_im += rank2term3[j][l] * \\\n",
" (sp.Rational(1,4)*(+tetrad_product__Imag_psi4(n4U,mre4U,mim4U, 0,j+1,0,l+1)\n",
" -tetrad_product__Imag_psi4(n4U,mre4U,mim4U, j+1,0,0,l+1)\n",
" -tetrad_product__Imag_psi4(n4U,mre4U,mim4U, 0,j+1,l+1,0)\n",
" +tetrad_product__Imag_psi4(n4U,mre4U,mim4U, j+1,0,l+1,0)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='code_validation'></a>\n",
"\n",
"# Step 6: Code validation against BSSN.Psi4 NRPy+ module \\[Back to [top](#toc)\\]\n",
"$$\\label{code_validation}$$\n",
"\n",
"As a code validation check, we verify agreement in the SymPy expressions for the RHSs of the BSSN equations between\n",
"1. this tutorial and \n",
"2. the NRPy+ BSSN.Psi4 module.\n",
"\n",
"By default, we compare all quantities in Spherical coordinates, though other coordinate systems may be chosen."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"STARTING NEW\n",
"Wrote to file \"Psi4_new.h\"\n",
"FINISHED NEW\n",
"STARTING OLD\n",
"Wrote to file \"Psi4_old.h\"\n",
"FINISHED OLD\n"
]
}
],
"source": [
"outCparams = \"preindent=1,outCfileaccess=w,outCverbose=False,includebraces=False\"\n",
"print(\"STARTING NEW\")\n",
"fin.FD_outputC(\"Psi4_new.h\", lhrh(lhs=\"psi4_real\", rhs=psi4_im), outCparams)\n",
"print(\"FINISHED NEW\")\n",
"\n",
"gri.glb_gridfcs_list = []\n",
"\n",
"import WeylScal4NRPy.WeylScalars_Cartesian as W4\n",
"W4.WeylScalars_Cartesian()\n",
"print(\"STARTING OLD\")\n",
"fin.FD_outputC(\"Psi4_old.h\", lhrh(lhs=\"psi4_real\", rhs=W4.psi4i), outCparams)\n",
"print(\"FINISHED OLD\")\n",
"\n",
"# print(\"FullSimplify[\"+str(sp.mathematica_code(psi4_re-W4.psi4r))+\"]\")\n",
"# with open(\"math.txt\",\"w\") as file:\n",
"# file.write(\"FullSimplify[\"+str(sp.mathematica_code(psi4_re-W4.psi4r))+\"]\")\n",
" \n",
"# # Call the BSSN_RHSs() function from within the\n",
"# # BSSN/BSSN_RHSs.py module,\n",
"# # which should do exactly the same as in Steps 1-16 above.\n",
"# print(\"vvv Ignore the minor warnings below. vvv\")\n",
"# import BSSN.Psi4 as BP4\n",
"# BP4.Psi4()\n",
"# print(\"^^^ Ignore the minor warnings above. ^^^\\n\")\n",
"\n",
"# print(\"Consistency check between this tutorial and BSSN.Psi4 NRPy+ module: ALL SHOULD BE ZERO.\")\n",
"\n",
"# print(\"psi4_im - BP4.psi4_im = \" + str(psi4_im - BP4.psi4_im))\n",
"# print(\"psi4_re - BP4.psi4_re = \" + str(psi4_re - BP4.psi4_re))"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"GOOD: 9.858968807130199e-16 : 1.678026634904849e-08 1.678026634904847e-08\r\n"
]
}
],
"source": [
"!gcc -O2 psi4_tester.c -o psi4_tester -lm\n",
"!./psi4_tester 4 4 4"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='latex_pdf_output'></a>\n",
"\n",
"# Step 7: Output this notebook to $\\LaTeX$-formatted PDF file \\[Back to [top](#toc)\\]\n",
"$$\\label{latex_pdf_output}$$"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[NbConvertApp] Converting notebook Tutorial-Psi4.ipynb to latex\n",
"[NbConvertApp] Writing 61056 bytes to Tutorial-Psi4.tex\n",
"This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=pdflatex)\n",
" restricted \\write18 enabled.\n",
"entering extended mode\n",
"This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=pdflatex)\n",
" restricted \\write18 enabled.\n",
"entering extended mode\n",
"This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=pdflatex)\n",
" restricted \\write18 enabled.\n",
"entering extended mode\n"
]
}
],
"source": [
"!jupyter nbconvert --to latex --template latex_nrpy_style.tplx --log-level='WARN' Tutorial-Psi4.ipynb\n",
"!pdflatex -interaction=batchmode Tutorial-Psi4.tex\n",
"!pdflatex -interaction=batchmode Tutorial-Psi4.tex\n",
"!pdflatex -interaction=batchmode Tutorial-Psi4.tex\n",
"!rm -f Tut*.out Tut*.aux Tut*.log"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
# This code calculates the Weyl scalars psi0, psi1, psi2, psi3, and psi4. It does so by following the paper
# Baker, Campanelli, and Lousto. PRD 65, 044001 (2002), gr-qc/0104063 and the example set by the Kranc-
# generated ETK thorn which can be found at https://bitbucket.org/einsteintoolkit/einsteinanalysis/src
# Step 1: import all needed modules from NRPy+:
import NRPy_param_funcs as par
import indexedexp as ixp
import grid as gri
import finite_difference as fin
from outputC import *
import sympy as sp
# Step 2: Initialize WeylScalars parameters
thismodule = __name__
# Current option: Approx_QuasiKinnersley = choice made in Baker, Campanelli, and Lousto. PRD 65, 044001 (2002)
par.initialize_param(par.glb_param("char *", thismodule, "TetradChoice", "Approx_QuasiKinnersley"))
# This controls what gets output. Acceptable values are "psi4_only", "all_psis", and "all_psis_and_invariants"
par.initialize_param(par.glb_param("char *", thismodule, "output_scalars", "all_psis_and_invariants"))
# Step 3: Define the rank-3 version of the Levi-Civita symbol. Amongst
# other possible uses, this is needed for the construction of the approximate
# quasi-Kinnersley tetrad.
def define_LeviCivitaSymbol_rank3(DIM=-1):
if DIM == -1:
DIM = par.parval_from_str("DIM")
LeviCivitaSymbol = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
# From https://codegolf.stackexchange.com/questions/160359/levi-civita-symbol :
LeviCivitaSymbol[i][j][k] = (i - j) * (j - k) * (k - i) / 2
return LeviCivitaSymbol
# Step 1: Call BSSNs. This module computes many different quantities related to the metric,
# many of which will be essential here. We must first change to our desired coordinate
# system, however.
def WeylScalars_Cartesian():
# We do not need the barred or hatted quantities calculated when using Cartesian coordinates.
# Instead, we declare the PHYSICAL metric and extrinsic curvature as grid functions.
gammaDD = ixp.register_gridfunctions_for_single_rank2("AUX","gammaDD", "sym01")
kDD = ixp.register_gridfunctions_for_single_rank2("AUX","kDD", "sym01")
gammaUU, detgamma = ixp.symm_matrix_inverter3x3(gammaDD)
output_scalars = par.parval_from_str("output_scalars")
global psi4r,psi4i,psi3r,psi3i,psi2r,psi2i,psi1r,psi1i,psi0r,psi0i
# if output_scalars is "all_psis_and_invariants":
# psi4r,psi4i,psi3r,psi3i,psi2r,psi2i,psi1r,psi1i,psi0r,psi0i = sp.symbols("psi4r psi4i\
# psi3r psi3i\
# psi2r psi2i\
# psi1r psi1i\
# psi0r psi0i")
# elif output_scalars is "all_psis":
psi4r,psi4i,psi3r,psi3i,psi2r,psi2i,psi1r,psi1i,psi0r,psi0i = gri.register_gridfunctions("AUX",["psi4r","psi4i",\
"psi3r","psi3i",\
"psi2r","psi2i",\
"psi1r","psi1i",\
"psi0r","psi0i"])
# Step 2a: Set spatial dimension (must be 3 for BSSN)
DIM = 3
par.set_parval_from_str("grid::DIM",DIM)
# Step 2b: Set the coordinate system to Cartesian
x,y,z = gri.register_gridfunctions("AUX",["x","y","z"])
# Step 2c: Set which tetrad is used; at the moment, only one supported option
if par.parval_from_str("WeylScal4NRPy.WeylScalars_Cartesian::TetradChoice") == "Approx_QuasiKinnersley":
# Step 3a: Choose 3 orthogonal vectors. Here, we choose one in the azimuthal
# direction, one in the radial direction, and the cross product of the two.
# Eqs 5.6, 5.7 in https://arxiv.org/pdf/gr-qc/0104063.pdf:
# v_1^a &= [-y,x,0] \\
# v_2^a &= [x,y,z] \\
# v_3^a &= {\rm det}(g)^{1/2} g^{ad} \epsilon_{dbc} v_1^b v_2^c,
v1U = ixp.zerorank1()
v2U = ixp.zerorank1()
v3U = ixp.zerorank1()
v1U[0] = -y
v1U[1] = x
v1U[2] = sp.sympify(0)
v2U[0] = x
v2U[1] = y
v2U[2] = z
LeviCivitaSymbol_rank3 = define_LeviCivitaSymbol_rank3()
for a in range(DIM):
for b in range(DIM):
for c in range(DIM):
for d in range(DIM):
v3U[a] += sp.sqrt(detgamma) * gammaUU[a][d] * LeviCivitaSymbol_rank3[d][b][c] * v1U[b] *v2U[c]
# Step 3b: Gram-Schmidt orthonormalization of the vectors.
# The w_i^a vectors here are used to temporarily hold values on the way to the final vectors e_i^a
# e_1^a &= \frac{v_1^a}{\omega_{11}} \\
# e_2^a &= \frac{v_2^a - \omega_{12} e_1^a}{\omega_{22}} \\
# e_3^a &= \frac{v_3^a - \omega_{13} e_1^a - \omega_{23} e_2^a}{\omega_{33}}, \\
# Normalize the first vector
w1U = ixp.zerorank1()
for a in range(DIM):
w1U[a] = v1U[a]
omega11 = sp.sympify(0)
for a in range(DIM):
for b in range(DIM):
omega11 += w1U[a] * w1U[b] * gammaDD[a][b]
e1U = ixp.zerorank1()
for a in range(DIM):
e1U[a] = w1U[a] / sp.sqrt(omega11)
# Subtract off the portion of the first vector along the second, then normalize
omega12 = sp.sympify(0)
for a in range(DIM):
for b in range(DIM):
omega12 += e1U[a] * v2U[b] * gammaDD[a][b]
w2U = ixp.zerorank1()
for a in range(DIM):
w2U[a] = v2U[a] - omega12*e1U[a]
omega22 = sp.sympify(0)
for a in range(DIM):
for b in range(DIM):
omega22 += w2U[a] * w2U[b] *gammaDD[a][b]
e2U = ixp.zerorank1()
for a in range(DIM):
e2U[a] = w2U[a] / sp.sqrt(omega22)
# Subtract off the portion of the first and second vectors along the third, then normalize
omega13 = sp.sympify(0)
for a in range(DIM):
for b in range(DIM):
omega13 += e1U[a] * v3U[b] * gammaDD[a][b]
omega23 = sp.sympify(0)
for a in range(DIM):
for b in range(DIM):
omega23 += e2U[a] * v3U[b] * gammaDD[a][b]
w3U = ixp.zerorank1()
for a in range(DIM):
w3U[a] = v3U[a] - omega13*e1U[a] - omega23*e2U[a]
omega33 = sp.sympify(0)
for a in range(DIM):
for b in range(DIM):
omega33 += w3U[a] * w3U[b] * gammaDD[a][b]
e3U = ixp.zerorank1()
for a in range(DIM):
e3U[a] = w3U[a] / sp.sqrt(omega33)
# Step 3c: Construct the tetrad itself.
# Eqs. 5.6:
# l^a &= \frac{1}{\sqrt{2}} e_2^a \\
# n^a &= -\frac{1}{\sqrt{2}} e_2^a \\
# m^a &= \frac{1}{\sqrt{2}} (e_3^a + i e_1^a) \\
# \overset{*}{m}{}^a &= \frac{1}{\sqrt{2}} (e_3^a - i e_1^a)
isqrt2 = 1/sp.sqrt(2)
ltetU = ixp.zerorank1()
ntetU = ixp.zerorank1()
#mtetU = ixp.zerorank1()
#mtetccU = ixp.zerorank1()
remtetU = ixp.zerorank1() # SymPy does not like trying to take the real/imaginary parts of such a
immtetU = ixp.zerorank1() # complicated expression as the Weyl scalars, so we will do it ourselves.
for i in range(DIM):
ltetU[i] = isqrt2 * e2U[i]
ntetU[i] = -isqrt2 * e2U[i]
remtetU[i] = isqrt2 * e3U[i]
immtetU[i] = isqrt2 * e1U[i]
nn = isqrt2
else:
print("Error: TetradChoice == "+par.parval_from_str("TetradChoice")+" unsupported!")
exit(1)
gammaDD_dD = ixp.declarerank3("gammaDD_dD","sym01")
# Define the Christoffel symbols
GammaUDD = ixp.zerorank3(DIM)
for i in range(DIM):
for k in range(DIM):
for l in range(DIM):
for m in range(DIM):
GammaUDD[i][k][l] += (sp.Rational(1,2))*gammaUU[i][m]*\
(gammaDD_dD[m][k][l] + gammaDD_dD[m][l][k] - gammaDD_dD[k][l][m])
# Step 4b: Declare and construct the Riemann curvature tensor:
# R_{abcd} = \frac{1}{2} (\gamma_{ad,cb}+\gamma_{bc,da}-\gamma_{ac,bd}-\gamma_{bd,ac})
# + \gamma_{je} \Gamma^{j}_{bc}\Gamma^{e}_{ad} - \gamma_{je} \Gamma^{j}_{bd} \Gamma^{e}_{ac}
gammaDD_dDD = ixp.declarerank4("gammaDD_dDD","sym01_sym23")
RiemannDDDD = ixp.zerorank4()
for a in range(DIM):
for b in range(DIM):
for c in range(DIM):
for d in range(DIM):
RiemannDDDD[a][b][c][d] = (gammaDD_dDD[a][d][c][b] + \
gammaDD_dDD[b][c][d][a] - \
gammaDD_dDD[a][c][b][d] - \
gammaDD_dDD[b][d][a][c]) / 2
for e in range(DIM):
for j in range(DIM):
RiemannDDDD[a][b][c][d] += gammaDD[j][e] * GammaUDD[j][b][c] * GammaUDD[e][a][d] - \
gammaDD[j][e] * GammaUDD[j][b][d] * GammaUDD[e][a][c]
# Step 4c: We also need the extrinsic curvature tensor $K_{ij}$.
# In Cartesian coordinates, we already made the components gridfunctions.
# We will, however, need to calculate the trace of K seperately:
trK = sp.sympify(0)
for i in range(DIM):
for j in range(DIM):
trK += gammaUU[i][j] * kDD[i][j]
# Step 5: Build the formula for \psi_4.
# Gauss equation: involving the Riemann tensor and extrinsic curvature.
# GaussDDDD[i][j][k][l] =& R_{ijkl} + 2K_{i[k}K_{l]j}
GaussDDDD = ixp.zerorank4()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
GaussDDDD[i][j][k][l] = RiemannDDDD[i][j][k][l] + kDD[i][k]*kDD[l][j] - kDD[i][l]*kDD[k][j]
# Codazzi equation: involving partial derivatives of the extrinsic curvature.
# We will first need to declare derivatives of kDD
# CodazziDDD[j][k][l] =& -2 (K_{j[k,l]} + \Gamma^p_{j[k} K_{l]p})
kDD_dD = ixp.declarerank3("kDD_dD","sym01")
CodazziDDD = ixp.zerorank3()
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
CodazziDDD[j][k][l] = kDD_dD[j][l][k] - kDD_dD[j][k][l]
for p in range(DIM):
CodazziDDD[j][k][l] += GammaUDD[p][j][l]*kDD[k][p] - GammaUDD[p][j][k]*kDD[l][p]
# Another piece. While not associated with any particular equation,
# this is still useful for organizational purposes.
# RojoDD[j][l]} = & R_{jl} - K_{jp} K^p_l + KK_{jl} \\
# = & \gamma^{pd} R_{jpld} - K_{jp} K^p_l + KK_{jl}
RojoDD = ixp.zerorank2()
for j in range(DIM):
for l in range(DIM):
RojoDD[j][l] = trK*kDD[j][l]
for p in range(DIM):
for d in range(DIM):
RojoDD[j][l] += gammaUU[p][d]*RiemannDDDD[j][p][l][d] - kDD[j][p]*gammaUU[p][d]*kDD[d][l]
# Now we can calculate $\psi_4$ itself! We assume l^0 = n^0 = \frac{1}{\sqrt{2}}
# and m^0 = \overset{*}{m}{}^0 = 0 to simplify these equations.
# We calculate the Weyl scalars as defined in https://arxiv.org/abs/gr-qc/0104063
# In terms of the above-defined quantites, the psis are defined as:
# \psi_4 =&\ (\text{GaussDDDD[i][j][k][l]}) n^i \overset{*}{m}{}^j n^k \overset{*}{m}{}^l \\
# &+2 (\text{CodazziDDD[j][k][l]}) n^{0} \overset{*}{m}{}^{j} n^k \overset{*}{m}{}^l \\
# &+ (\text{RojoDD[j][l]}) n^{0} \overset{*}{m}{}^{j} n^{0} \overset{*}{m}{}^{l}.
# \psi_3 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i n^j \overset{*}{m}{}^k n^l \\
# &+ (\text{CodazziDDD[j][k][l]}) (l^{0} n^{j} \overset{*}{m}{}^k n^l - l^{j} n^{0} \overset{*}{m}{}^k n^l - l^k n^j\overset{*}{m}{}^l n^0) \\
# &- (\text{RojoDD[j][l]}) l^{0} n^{j} \overset{*}{m}{}^l n^0 - l^{j} n^{0} \overset{*}{m}{}^l n^0 \\
# \psi_2 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i m^j \overset{*}{m}{}^k n^l \\
# &+ (\text{CodazziDDD[j][k][l]}) (l^{0} m^{j} \overset{*}{m}{}^k n^l - l^{j} m^{0} \overset{*}{m}{}^k n^l - l^k m^l \overset{*}{m}{}^l n^0) \\
# &- (\text{RojoDD[j][l]}) l^0 m^j \overset{*}{m}{}^l n^0 \\
# \psi_1 =&\ (\text{GaussDDDD[i][j][k][l]}) n^i l^j m^k l^l \\
# &+ (\text{CodazziDDD[j][k][l]}) (n^{0} l^{j} m^k l^l - n^{j} l^{0} m^k l^l - n^k l^l m^j l^0) \\
# &- (\text{RojoDD[j][l]}) (n^{0} l^{j} m^l l^0 - n^{j} l^{0} m^l l^0) \\
# \psi_0 =&\ (\text{GaussDDDD[i][j][k][l]}) l^i m^j l^k m^l \\
# &+2 (\text{CodazziDDD[j][k][l]}) (l^0 m^j l^k m^l + l^k m^l l^0 m^j) \\
# &+ (\text{RojoDD[j][l]}) l^0 m^j l^0 m^j. \\
psi4r = sp.sympify(0)
psi4i = sp.sympify(0)
psi3r = sp.sympify(0)
psi3i = sp.sympify(0)
psi2r = sp.sympify(0)
psi2i = sp.sympify(0)
psi1r = sp.sympify(0)
psi1i = sp.sympify(0)
psi0r = sp.sympify(0)
psi0i = sp.sympify(0)
for l in range(DIM):
for j in range(DIM):
psi4r += RojoDD[j][l] * nn * nn * (remtetU[j]*remtetU[l]-immtetU[j]*immtetU[l])
psi4i += RojoDD[j][l] * nn * nn * (-remtetU[j]*immtetU[l]-immtetU[j]*remtetU[l])
psi3r +=-RojoDD[j][l] * nn * nn * (ntetU[j]-ltetU[j]) * remtetU[l]
psi3i += RojoDD[j][l] * nn * nn * (ntetU[j]-ltetU[j]) * immtetU[l]
psi2r +=-RojoDD[j][l] * nn * nn * (remtetU[l]*remtetU[j]+immtetU[j]*immtetU[l])
psi2i +=-RojoDD[j][l] * nn * nn * (immtetU[l]*remtetU[j]-remtetU[j]*immtetU[l])
psi1r += RojoDD[j][l] * nn * nn * (ntetU[j]*remtetU[l]-ltetU[j]*remtetU[l])
psi1i += RojoDD[j][l] * nn * nn * (ntetU[j]*immtetU[l]-ltetU[j]*immtetU[l])
psi0r += RojoDD[j][l] * nn * nn * (remtetU[j]*remtetU[l]-immtetU[j]*immtetU[l])
psi0i += RojoDD[j][l] * nn * nn * (remtetU[j]*immtetU[l]+immtetU[j]*remtetU[l])
for l in range(DIM):
for j in range(DIM):
for k in range(DIM):
psi4r += 2 * CodazziDDD[j][k][l] * ntetU[k] * nn * (remtetU[j]*remtetU[l]-immtetU[j]*immtetU[l])
psi4i += 2 * CodazziDDD[j][k][l] * ntetU[k] * nn * (-remtetU[j]*immtetU[l]-immtetU[j]*remtetU[l])
psi3r += 1 * CodazziDDD[j][k][l] * nn * ((ntetU[j]-ltetU[j])*remtetU[k]*ntetU[l]-remtetU[j]*ltetU[k]*ntetU[l])
psi3i +=-1 * CodazziDDD[j][k][l] * nn * ((ntetU[j]-ltetU[j])*immtetU[k]*ntetU[l]-immtetU[j]*ltetU[k]*ntetU[l])
psi2r += 1 * CodazziDDD[j][k][l] * nn * (ntetU[l]*(remtetU[j]*remtetU[k]+immtetU[j]*immtetU[k])-ltetU[k]*(remtetU[j]*remtetU[l]+immtetU[j]*immtetU[l]))
psi2i += 1 * CodazziDDD[j][k][l] * nn * (ntetU[l]*(immtetU[j]*remtetU[k]-remtetU[j]*immtetU[k])-ltetU[k]*(remtetU[j]*immtetU[l]-immtetU[j]*remtetU[l]))
psi1r += 1 * CodazziDDD[j][k][l] * nn * (ltetU[j]*remtetU[k]*ltetU[l]-remtetU[j]*ntetU[k]*ltetU[l]-ntetU[j]*remtetU[k]*ltetU[l])
psi1i += 1 * CodazziDDD[j][k][l] * nn * (ltetU[j]*immtetU[k]*ltetU[l]-immtetU[j]*ntetU[k]*ltetU[l]-ntetU[j]*immtetU[k]*ltetU[l])
psi0r += 2 * CodazziDDD[j][k][l] * nn * ltetU[k]*(remtetU[j]*remtetU[l]-immtetU[j]*immtetU[l])
psi0i += 2 * CodazziDDD[j][k][l] * nn * ltetU[k]*(remtetU[j]*immtetU[l]+immtetU[j]*remtetU[l])
for l in range(DIM):
for j in range(DIM):
for k in range(DIM):
for i in range(DIM):
psi4r += GaussDDDD[i][j][k][l] * ntetU[i] * ntetU[k] * (remtetU[j]*remtetU[l]-immtetU[j]*immtetU[l])
psi4i += GaussDDDD[i][j][k][l] * ntetU[i] * ntetU[k] * (-remtetU[j]*immtetU[l]-immtetU[j]*remtetU[l])
psi3r += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[j] * remtetU[k] * ntetU[l]
psi3i +=-GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[j] * immtetU[k] * ntetU[l]
psi2r += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[l] * (remtetU[j]*remtetU[k]+immtetU[j]*immtetU[k])
psi2i += GaussDDDD[i][j][k][l] * ltetU[i] * ntetU[l] * (immtetU[j]*remtetU[k]-remtetU[j]*immtetU[k])
psi1r += GaussDDDD[i][j][k][l] * ntetU[i] * ltetU[j] * remtetU[k] * ltetU[l]
psi1i += GaussDDDD[i][j][k][l] * ntetU[i] * ltetU[j] * immtetU[k] * ltetU[l]
psi0r += GaussDDDD[i][j][k][l] * ltetU[i] * ltetU[k] * (remtetU[j]*remtetU[l]-immtetU[j]*immtetU[l])
psi0i += GaussDDDD[i][j][k][l] * ltetU[i] * ltetU[k] * (remtetU[j]*immtetU[l]+immtetU[j]*remtetU[l])
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <sys/time.h>
/* EVOLVED VARIABLES: */
#define NUM_AUX_GFS 15
#define KDD00GF 0
#define KDD01GF 1
#define KDD02GF 2
#define KDD11GF 3
#define KDD12GF 4
#define KDD22GF 5
#define GAMMADD00GF 6
#define GAMMADD01GF 7
#define GAMMADD02GF 8
#define GAMMADD11GF 9
#define GAMMADD12GF 10
#define GAMMADD22GF 11
#define XGF 12
#define YGF 13
#define ZGF 14
#define REAL double
#define NGHOSTS 2
// Cartesian coordinates parameters
const REAL xmin = -10.,xmax=10.;
const REAL ymin = -10.,ymax=10.;
const REAL zmin = -10.,zmax=10.;
#define IDX4(g,i,j,k) \
( (i) + Nxx_plus_2NGHOSTS[0] * ( (j) + Nxx_plus_2NGHOSTS[1] * ( (k) + Nxx_plus_2NGHOSTS[2] * (g) ) ) )
#define IDX3(i,j,k) ( (i) + Nxx_plus_2NGHOSTS[0] * ( (j) + Nxx_plus_2NGHOSTS[1] * (k) ) )
// Assuming idx = IDX3(i,j,k). Much faster if idx can be reused over and over:
#define IDX4pt(g,idx) ( (idx) + (Nxx_plus_2NGHOSTS[0]*Nxx_plus_2NGHOSTS[1]*Nxx_plus_2NGHOSTS[2]) * (g) )
int main(int argc, const char *argv[]) {
// Step 0a: Read command-line input, error out if nonconformant
if((argc != 4) || atoi(argv[1]) < NGHOSTS || atoi(argv[2]) < NGHOSTS || atoi(argv[3]) < 2 /* FIXME; allow for axisymmetric sims */) {
fprintf(stderr,"Error: Expected three command-line arguments: ./BrillLindquist_Playground Nx0 Nx1 Nx2,\n");
fprintf(stderr,"where Nx[0,1,2] is the number of grid points in the 0, 1, and 2 directions.\n");
fprintf(stderr,"Nx[] MUST BE larger than NGHOSTS (= %d)\n",NGHOSTS);
exit(1);
}
// Step 0b: Set up numerical grid structure, first in space...
const int Nxx[3] = { atoi(argv[1]), atoi(argv[2]), atoi(argv[3]) };
if(Nxx[0]%2 != 0 || Nxx[1]%2 != 0 || Nxx[2]%2 != 0) {
fprintf(stderr,"Error: Cannot guarantee a proper cell-centered grid if number of grid cells not set to even number.\n");
fprintf(stderr," For example, in case of angular directions, proper symmetry zones will not exist.\n");
exit(1);
}
const int Nxx_plus_2NGHOSTS[3] = { Nxx[0]+2*NGHOSTS, Nxx[1]+2*NGHOSTS, Nxx[2]+2*NGHOSTS };
const int Nxx_plus_2NGHOSTS_tot = Nxx_plus_2NGHOSTS[0]*Nxx_plus_2NGHOSTS[1]*Nxx_plus_2NGHOSTS[2];
REAL *aux_gfs_new = (REAL *)malloc(sizeof(REAL) * NUM_AUX_GFS * Nxx_plus_2NGHOSTS_tot);
REAL *aux_gfs_old = (REAL *)malloc(sizeof(REAL) * NUM_AUX_GFS * Nxx_plus_2NGHOSTS_tot);
// Step 0d: Set up space and time coordinates
// Step 0d.i: Set \Delta x^i on uniform grids.
REAL xxmin[3],xxmax[3];
xxmin[0] = xmin;
xxmin[1] = ymin;
xxmin[2] = zmin;
xxmax[0] = xmax;
xxmax[1] = ymax;
xxmax[2] = zmax;
REAL dxx[3];
for(int i=0;i<3;i++) dxx[i] = (xxmax[i] - xxmin[i]) / ((REAL)Nxx[i]);
// Step 0d.ii: Set up uniform coordinate grids
REAL *xx[3];
for(int i=0;i<3;i++) {
xx[i] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS[i]);
for(int j=0;j<Nxx_plus_2NGHOSTS[i];j++) {
xx[i][j] = xxmin[i] + ((REAL)(j-NGHOSTS) + (1.0/2.0))*dxx[i]; // Cell-centered grid.
}
}
struct timeval time;
gettimeofday(&time,NULL);
// microsecond has 1 000 000
// Assuming you did not need quite that accuracy
// Also do not assume the system clock has that accuracy.
srand48((time.tv_sec * 1000) + (time.tv_usec / 1000));
/*
time_t t;
srand48((unsigned) time(&t));
*/
#define PERT_SIZE 1e-6
#define LOOP_REGION(i0min,i0max, i1min,i1max, i2min,i2max) \
for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++)
LOOP_REGION(0,Nxx_plus_2NGHOSTS[0], 0,Nxx_plus_2NGHOSTS[1], 0,Nxx_plus_2NGHOSTS[2]) {
for(int gf=KDD00GF;gf<=KDD22GF;gf++) aux_gfs_new[IDX4(gf,i0,i1,i2)] = aux_gfs_old[IDX4(gf,i0,i1,i2)] = PERT_SIZE*(0.5-drand48());
for(int gf=GAMMADD00GF;gf<=GAMMADD22GF;gf++) aux_gfs_new[IDX4(gf,i0,i1,i2)] = aux_gfs_old[IDX4(gf,i0,i1,i2)] = PERT_SIZE*(0.5-drand48());
aux_gfs_new[IDX4(GAMMADD00GF,i0,i1,i2)] = aux_gfs_old[IDX4(GAMMADD00GF,i0,i1,i2)] = 1.0 + PERT_SIZE*(0.5-drand48());
aux_gfs_new[IDX4(GAMMADD11GF,i0,i1,i2)] = aux_gfs_old[IDX4(GAMMADD11GF,i0,i1,i2)] = 1.0 + PERT_SIZE*(0.5-drand48());
aux_gfs_new[IDX4(GAMMADD22GF,i0,i1,i2)] = aux_gfs_old[IDX4(GAMMADD22GF,i0,i1,i2)] = 1.0 + PERT_SIZE*(0.5-drand48());
aux_gfs_new[IDX4(XGF,i0,i1,i2)] = aux_gfs_old[IDX4(XGF,i0,i1,i2)] = xx[0][i0];
aux_gfs_new[IDX4(YGF,i0,i1,i2)] = aux_gfs_old[IDX4(YGF,i0,i1,i2)] = xx[1][i1];
aux_gfs_new[IDX4(ZGF,i0,i1,i2)] = aux_gfs_old[IDX4(ZGF,i0,i1,i2)] = xx[2][i2];
}
REAL psi4_real_new,psi4_real_old;
LOOP_REGION(NGHOSTS,Nxx_plus_2NGHOSTS[0]-NGHOSTS, NGHOSTS,Nxx_plus_2NGHOSTS[1]-NGHOSTS, NGHOSTS,Nxx_plus_2NGHOSTS[2]-NGHOSTS) {
REAL *aux_gfs = aux_gfs_new;
const REAL xx0 = xx[0][i0], xx1 = xx[1][i1], xx2 = xx[2][i2];
const REAL invdx0 = 1.0/dxx[0];
const REAL invdx1 = 1.0/dxx[1];
const REAL invdx2 = 1.0/dxx[2];
REAL psi4_real;
#include "Psi4_new.h"
if(i0==Nxx_plus_2NGHOSTS[0]/2 && i1==Nxx_plus_2NGHOSTS[1]/2 && i2==Nxx_plus_2NGHOSTS[2]/2) {
//printf("%.15e\n",psi4_real);
psi4_real_new = psi4_real;
}
//printf("%d %d %d %.15e\n",i0,i1,i2,psi4_real);
}
LOOP_REGION(NGHOSTS,Nxx_plus_2NGHOSTS[0]-NGHOSTS, NGHOSTS,Nxx_plus_2NGHOSTS[1]-NGHOSTS, NGHOSTS,Nxx_plus_2NGHOSTS[2]-NGHOSTS) {
REAL *aux_gfs = aux_gfs_old;
const REAL xx0 = xx[0][i0], xx1 = xx[1][i1], xx2 = xx[2][i2];
const REAL invdx0 = 1.0/dxx[0];
const REAL invdx1 = 1.0/dxx[1];
const REAL invdx2 = 1.0/dxx[2];
REAL psi4_real;
#include "Psi4_old.h"
if(i0==Nxx_plus_2NGHOSTS[0]/2 && i1==Nxx_plus_2NGHOSTS[1]/2 && i2==Nxx_plus_2NGHOSTS[2]/2) {
psi4_real_old = psi4_real;
//printf("%.15e\n",psi4_real);
}
}
REAL relerror = fabs(psi4_real_new - psi4_real_old) / (0.5* ( fabs(psi4_real_new) + fabs(psi4_real_old) ));
if(relerror < 1e-13) {
printf("GOOD: %.15e : %.15e %.15e\n",relerror, psi4_real_new,psi4_real_old);
} else {
printf("BAD: %.15e : %.15e %.15e %ld\n",relerror, psi4_real_new,psi4_real_old, (time.tv_sec * 1000) + (time.tv_usec / 1000));
}
for(int i=0;i<3;i++) free(xx[i]);
free(aux_gfs_new);
free(aux_gfs_old);
return 0;
}
# As documented in the NRPy+ tutorial module
# Tutorial-Psi4_tetrads.ipynb,
# this module will construct tetrads
# needed to compute \psi_4 (as well as other
# Weyl scalars and invariants in principle)
# Authors: Zachariah B. Etienne
# (zachetie **at** gmail **dot* com),
# and Patrick Nelson
# Step 1.a: import all needed modules from NRPy+:
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
# Step 1.b: Initialize TetradChoice parameter
thismodule = __name__
# Current option: QuasiKinnersley = choice made in Baker, Campanelli, and Lousto. PRD 65, 044001 (2002)
par.initialize_param(par.glb_param("char", thismodule, "TetradChoice", "QuasiKinnersley"))
par.initialize_param(par.glb_param("char", thismodule, "UseCorrectUnitNormal", "False")) # False = consistent with WeylScal4 ETK thorn.
def Psi4_tetrads():
global l4U, n4U, mre4U, mim4U
# Step 1.c: Check if tetrad choice is implemented:
if par.parval_from_str(thismodule+"::TetradChoice") != "QuasiKinnersley":
print("ERROR: "+thismodule+"::TetradChoice = "+par.parval_from_str("TetradChoice")+" currently unsupported!")
sys.exit(1)
# 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: 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.f: Import all ADM quantities as written in terms of BSSN quantities
import BSSN.ADM_in_terms_of_BSSN as AB
AB.ADM_in_terms_of_BSSN()
# Step 2.a: Declare the Cartesian x,y,z in terms of
# xx0,xx1,xx2.
x = rfm.xxCart[0]
y = rfm.xxCart[1]
z = rfm.xxCart[2]
# Step 2.b: Declare v_1^a, v_2^a, and v_3^a tetrads,
# as well as detgamma and gammaUU from
# BSSN.ADM_in_terms_of_BSSN
v1UCart = ixp.zerorank1()
v2UCart = ixp.zerorank1()
detgamma = AB.detgamma
gammaUU = AB.gammaUU
# Step 2.c: Define v1U and v2U
v1UCart = [-y, x, sp.sympify(0)]
v2UCart = [x, y, z]
# Step 2.d: Construct the Jacobian d x_Cart^i / d xx^j
Jac_dUCart_dDrfmUD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
Jac_dUCart_dDrfmUD[i][j] = sp.simplify(sp.diff(rfm.xxCart[i], rfm.xx[j]))
# Step 2.e: Invert above Jacobian to get needed d xx^j / d x_Cart^i
Jac_dUrfm_dDCartUD, dummyDET = ixp.generic_matrix_inverter3x3(Jac_dUCart_dDrfmUD)
# Step 2.e.i: Simplify expressions for d xx^j / d x_Cart^i:
for i in range(DIM):
for j in range(DIM):
Jac_dUrfm_dDCartUD[i][j] = sp.simplify(Jac_dUrfm_dDCartUD[i][j])
# Step 2.f: Transform v1U and v2U from the Cartesian to the xx^i basis
v1U = ixp.zerorank1()
v2U = ixp.zerorank1()
for i in range(DIM):
for j in range(DIM):
v1U[i] += Jac_dUrfm_dDCartUD[i][j] * v1UCart[j]
v2U[i] += Jac_dUrfm_dDCartUD[i][j] * v2UCart[j]
# Step 2.g: Define the rank-3 version of the Levi-Civita symbol. Amongst
# other uses, this is needed for the construction of the approximate
# quasi-Kinnersley tetrad.
def define_LeviCivitaSymbol_rank3(DIM=-1):
if DIM == -1:
DIM = par.parval_from_str("DIM")
LeviCivitaSymbol = ixp.zerorank3()
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
# From https://codegolf.stackexchange.com/questions/160359/levi-civita-symbol :
LeviCivitaSymbol[i][j][k] = (i - j) * (j - k) * (k - i) / 2
return LeviCivitaSymbol
# Step 2.h: Define v3U
v3U = ixp.zerorank1()
LeviCivitaSymbolDDD = define_LeviCivitaSymbol_rank3(DIM=3)
for a in range(DIM):
for b in range(DIM):
for c in range(DIM):
for d in range(DIM):
v3U[a] += sp.sqrt(detgamma) * gammaUU[a][d] * LeviCivitaSymbolDDD[d][b][c] * v1U[b] * v2U[c]
# Step 2.h.i: Simplify expressions for v1U,v2U,v3U. This greatly expedites the C code generation (~10x faster)
for a in range(DIM):
v1U[a] = sp.simplify(v1U[a])
v2U[a] = sp.simplify(v2U[a])
v3U[a] = sp.simplify(v3U[a])
# Step 2.i: Define omega_{ij}
omegaDD = ixp.zerorank2()
gammaDD = AB.gammaDD
def v_vectorDU(v1U,v2U,v3U, i,a):
if i==0:
return v1U[a]
elif i==1:
return v2U[a]
elif i==2:
return v3U[a]
else:
print("ERROR: unknown vector!")
sys.exit(1)
def update_omega(omegaDD, i,j, v1U,v2U,v3U,gammaDD):
omegaDD[i][j] = sp.sympify(0)
for a in range(DIM):
for b in range(DIM):
omegaDD[i][j] += v_vectorDU(v1U,v2U,v3U, i,a)*v_vectorDU(v1U,v2U,v3U, j,b)*gammaDD[a][b]
# Step 2.j: Define e^a_i. Note that:
# omegaDD[0][0] = \omega_{11} above;
# omegaDD[1][1] = \omega_{22} above, etc.
# First e_1^a: Orthogonalize & normalize:
e1U = ixp.zerorank1()
update_omega(omegaDD, 0,0, v1U,v2U,v3U,gammaDD)
for a in range(DIM):
e1U[a] = v1U[a]/sp.sqrt(omegaDD[0][0])
# Next e_2^a: First orthogonalize:
e2U = ixp.zerorank1()
update_omega(omegaDD, 0,1, e1U,v2U,v3U,gammaDD)
for a in range(DIM):
e2U[a] = (v2U[a] - omegaDD[0][1]*e1U[a])
# Then normalize:
update_omega(omegaDD, 1,1, e1U,e2U,v3U,gammaDD)
for a in range(DIM):
e2U[a] /= sp.sqrt(omegaDD[1][1])
# Next e_3^a: First orthogonalize:
e3U = ixp.zerorank1()
update_omega(omegaDD, 0,2, e1U,e2U,v3U,gammaDD)
update_omega(omegaDD, 1,2, e1U,e2U,v3U,gammaDD)
for a in range(DIM):
e3U[a] = (v3U[a] - omegaDD[0][2]*e1U[a] - omegaDD[1][2]*e2U[a])
# Then normalize:
update_omega(omegaDD, 2,2, e1U,e2U,e3U,gammaDD)
for a in range(DIM):
e3U[a] /= sp.sqrt(omegaDD[2][2])
# Step 2.k: Construct l^mu, n^mu, and m^mu, based on r^mu, theta^mu, phi^mu, and u^mu:
r4U = ixp.zerorank1(DIM=4)
u4U = ixp.zerorank1(DIM=4)
theta4U = ixp.zerorank1(DIM=4)
phi4U = ixp.zerorank1(DIM=4)
for a in range(DIM):
r4U[ a+1] = e2U[a]
theta4U[a+1] = e3U[a]
phi4U[ a+1] = e1U[a]
# FIXME? assumes alpha=1, beta^i = 0
if par.parval_from_str(thismodule+"::UseCorrectUnitNormal") == "False":
u4U[0] = 1
else:
# Eq. 2.116 in Baumgarte & Shapiro:
# n^mu = {1/alpha, -beta^i/alpha}. Note that n_mu = {alpha,0}, so n^mu n_mu = -1.
import BSSN.BSSN_quantities as Bq
Bq.declare_BSSN_gridfunctions_if_not_declared_already()
Bq.BSSN_basic_tensors()
u4U[0] = 1/Bq.alpha
for i in range(DIM):
u4U[i+1] = -Bq.betaU[i]/Bq.alpha
l4U = ixp.zerorank1(DIM=4)
n4U = ixp.zerorank1(DIM=4)
mre4U = ixp.zerorank1(DIM=4)
mim4U = ixp.zerorank1(DIM=4)
# M_SQRT1_2 = 1 / sqrt(2) (defined in math.h on Linux)
M_SQRT1_2 = par.Cparameters("#define",thismodule,"M_SQRT1_2","")
isqrt2 = M_SQRT1_2 #1/sp.sqrt(2) <- SymPy drops precision to 15 sig. digits in unit tests
for mu in range(4):
l4U[mu] = isqrt2*(u4U[mu] + r4U[mu])
n4U[mu] = isqrt2*(u4U[mu] - r4U[mu])
mre4U[mu] = isqrt2*theta4U[mu]
mim4U[mu] = isqrt2* phi4U[mu]
# This module sets up Shifted Kerr-Schild initial data in terms of
# the variables used in BSSN_RHSs.py
# Authors: George Vopal, gvopal **at** gmail **dot** com
# Zachariah B. Etienne, zachetie **at** gmail **dot** com
# ### NRPy+ Source Code for this module: [BSSN/ShiftedKerrSchild.py](../edit/BSSN/BrillLindquist.py)
#
# WARNING: This module has not yet undergone code testing.
# **Inputs for initial data**:
#
# * The black hole mass, M
# * The black hole spin parameter, a
# 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, 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)
# Step 1: Define rho^2, alpha, beta^(r_{KS}), beta^(theta), beta^(phi), gamma_{r_{KS}theta}, gamma_{theta\phi}
# 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()
# Step 2: Define and construct nonzero components gamma_{r_{KS}r_{KS}}$, gamma_{r_{KS}phi},
# gamma_{thetatheta}, gamma_{phiphi}
# 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
# Step 3: Define useful quantities A, B, C
# 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)
# Step 4: Define 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)
import BSSN.BSSN_ID_function_string as bIDf
global returnfunction
returnfunction = bIDf.BSSN_ID_function_string(cf, hDD, lambdaU, aDD, trK, alpha, vetU, betU)
# This module sets up Static Trumpet initial data in terms of
# the variables used in BSSN_RHSs.py
# Authors: Terrence Pierre Jacques, terrencepierrej **at** gmail **dot** com
# Zachariah B. Etienne, zachetie **at** gmail **dot** com
# Ian Ruchlin
# ## This module sets up initial data for a static trumpet geometry in spherical coordinates. We can convert from spherical to any coordinate system defined in [reference_metric.py](../edit/reference_metric.py) (e.g., SinhSpherical, Cylindrical, Cartesian, etc.) using the [Exact ADM Spherical-to-BSSNCurvilinear converter module](Tutorial-ADM_Initial_Data-Converting_Exact_ADM_Spherical_or_Cartesian_to_BSSNCurvilinear.ipynb)
#
# ### NRPy+ Source Code for this module: [BSSN/BrillLindquist.py](../edit/BSSN/BrillLindquist.py)
#
# <font color='green'>**All quantities have been validated against the [original SENR code](https://bitbucket.org/zach_etienne/nrpy).**</font>
# ### Here we set up Static Trumpet initial data ([Dennison and Baumgarte, 2014](https://arxiv.org/abs/1403.5484)):
#
# Description of Static Trumpet geometry.
#
# **Inputs for initial data**:
#
# * The black hole mass $M$.
#
# **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:
# Auxiliary variables:
psi0 = sp.symbols('psi0', real=True)
# *** The StaticTrumpet conformal factor ***
# Dennison and Baumgarte (2014) Eq. 13
# https://arxiv.org/pdf/1403.5484.pdf
# psi = sqrt{1 + M/r }
psi0 = sp.sqrt(1 + M/r)
# *** The physical spatial metric in spherical basis ***
# Set the upper-triangle of the matrix...
# Eq. 15
# gamma_{ij} = psi^4 * eta_{ij}
# eta_00 = 1, eta_11 = r^2, eta_22 = r^2 * sin^2 (theta)
gammaSphDD = ixp.zerorank2()
gammaSphDD[0][0] = psi0**4
gammaSphDD[1][1] = psi0**4 * r**2
gammaSphDD[2][2] = psi0**4 * r**2*sp.sin(th)**2
# ... then apply symmetries to get the other components
# *** The physical trace-free extrinsic curvature in spherical basis ***
# Set the upper-triangle of the matrix...
# Eq.19 and 20
KSphDD = ixp.zerorank2()
# K_{rr} = M / r^2
KSphDD[0][0] = -M / r**2
# K_{theta theta} = K_{phi phi} / sin^2 theta = M
KSphDD[1][1] = M
KSphDD[2][2] = M * sp.sin(th)**2
# ... then apply symmetries to get the other components
# Lapse function and shift vector
# Eq. 15
# alpha = r / (r+M)
alphaSph = r / (r + M)
betaSphU = ixp.zerorank1()
# beta^r = Mr / (r + M)^2
betaSphU[0] = M*r / (r + M)**2
BSphU = ixp.zerorank1()
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)
import BSSN.BSSN_ID_function_string as bIDf
global returnfunction
returnfunction = bIDf.BSSN_ID_function_string(cf, hDD, lambdaU, aDD, trK, alpha, vetU, betU)
# This module performs the conversion of T^{mu nu}
# in Spherical or Cartesian coordinates
# given as *numerical* expressions (i.e., given as
# numerical values with fixed floating-point precision;
# e.g., in the case of an initial data solver), to
# rescaled BSSN stress-energy source terms.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
# Step P1: Import needed modules
from outputC import * # NRPy+: Core C code output module
import finite_difference as fin # NRPy+: Finite difference C code generation module
import grid as gri # NRPy+: Functions having to do with numerical grids
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm # NRPy+: Reference metric support
import sympy as sp # SymPy: The Python computer algebra package upon which NRPy+ depends
import NRPy_param_funcs as par # NRPy+: Parameter interface
import BSSN.BSSN_quantities as Bq # NRPy+: This module depends on the parameter EvolvedConformalFactor_cf,
# which is defined in BSSN.BSSN_quantities
import sys # Standard Python modules for multiplatform OS-level functions
import BSSN.BSSN_RHSs as bssnrhs # NRPy+: BSSN RHS quantities
import loop as lp # NRPy+: Helper module for writing C-code loops
def T4UUmunu_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear(CoordType_in,
Tmunu_input_function_name,
pointer_to_ID_inputs=False):
# The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations.
# To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in
# the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3.
# Step 0: Set spatial dimension (must be 3 for BSSN)
DIM = 3
# Step 1: Define the input variables: the 4D stress-energy tensor, and the ADM 3-metric, lapse, & shift:
T4SphorCartUU = ixp.declarerank2("T4SphorCartUU", "sym01", DIM=4)
gammaSphorCartDD = ixp.declarerank2("gammaSphorCartDD", "sym01")
alphaSphorCart = sp.symbols("alphaSphorCart")
betaSphorCartU = ixp.declarerank1("betaSphorCartU")
# Step 2: All Tmunu initial data quantities are functions of xx0,xx1,xx2, but
# in the Spherical or Cartesian basis.
# We first define the BSSN stress-energy source terms in the Spherical
# or Cartesian basis, respectively.
# To get \gamma_{\mu \nu} = gammabar4DD[mu][nu], we'll need to construct the 4-metric, using Eq. 2.122 in B&S:
# S_{ij} = \gamma_{i \mu} \gamma_{j \nu} T^{\mu \nu}
# S_{i} = -\gamma_{i\mu} n_\nu T^{\mu\nu}
# S = \gamma^{ij} S_{ij}
# rho = n_\mu n_\nu T^{\mu\nu},
# where
# \gamma_{\mu\nu} = g_{\mu\nu} + n_\mu n_\nu
# and
# n_mu = {-\alpha,0,0,0},
# Step 2.1: Construct the 4-metric based on the input ADM quantities.
# This is provided by 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
betaSphorCartD = ixp.zerorank1()
for i in range(DIM):
for j in range(DIM):
betaSphorCartD[i] += gammaSphorCartDD[i][j] * betaSphorCartU[j]
# Now compute the beta contraction.
beta2 = sp.sympify(0)
for i in range(DIM):
beta2 += betaSphorCartU[i] * betaSphorCartD[i]
# Eq. 2.122 in B&S
g4SphorCartDD = ixp.zerorank2(DIM=4)
g4SphorCartDD[0][0] = -alphaSphorCart ** 2 + beta2
for i in range(DIM):
g4SphorCartDD[i + 1][0] = g4SphorCartDD[0][i + 1] = betaSphorCartD[i]
for i in range(DIM):
for j in range(DIM):
g4SphorCartDD[i + 1][j + 1] = gammaSphorCartDD[i][j]
# Step 2.2: Construct \gamma_{mu nu} = g_{mu nu} + n_mu n_nu:
n4SphorCartD = ixp.zerorank1(DIM=4)
n4SphorCartD[0] = -alphaSphorCart
gamma4SphorCartDD = ixp.zerorank2(DIM=4)
for mu in range(4):
for nu in range(4):
gamma4SphorCartDD[mu][nu] = g4SphorCartDD[mu][nu] + n4SphorCartD[mu] * n4SphorCartD[nu]
# Step 2.3: We now have all we need to construct the BSSN source
# terms in the current basis (Spherical or Cartesian):
# S_{ij} = \gamma_{i \mu} \gamma_{j \nu} T^{\mu \nu}
# S_{i} = -\gamma_{i\mu} n_\nu T^{\mu\nu}
# S = \gamma^{ij} S_{ij}
# rho = n_\mu n_\nu T^{\mu\nu},
SSphorCartDD = ixp.zerorank2()
SSphorCartD = ixp.zerorank1()
SSphorCart = sp.sympify(0)
rhoSphorCart = sp.sympify(0)
for i in range(DIM):
for j in range(DIM):
for mu in range(4):
for nu in range(4):
SSphorCartDD[i][j] += gamma4SphorCartDD[i + 1][mu] * gamma4SphorCartDD[j + 1][nu] * T4SphorCartUU[mu][nu]
for i in range(DIM):
for mu in range(4):
for nu in range(4):
SSphorCartD[i] += -gamma4SphorCartDD[i + 1][mu] * n4SphorCartD[nu] * T4SphorCartUU[mu][nu]
gammaSphorCartUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaSphorCartDD)
for i in range(DIM):
for j in range(DIM):
SSphorCart += gammaSphorCartUU[i][j] * SSphorCartDD[i][j]
for mu in range(4):
for nu in range(4):
rhoSphorCart += n4SphorCartD[mu] * n4SphorCartD[nu] * T4SphorCartUU[mu][nu]
# Step 3: Perform basis conversion to
# Make sure that rfm.reference_metric() has been called.
# We'll need the variables it defines throughout this module.
if rfm.have_already_called_reference_metric_function == False:
print("Error. Called Tmunu_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear() without")
print(" first setting up reference metric, by calling rfm.reference_metric().")
sys.exit(1)
# Step 1: All input quantities are in terms of r,th,ph or x,y,z. We want them in terms
# of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace
# r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2
# as defined for this particular reference metric in reference_metric.py's
# xxSph[] or xxCart[], respectively:
r_th_ph_or_Cart_xyz_oID_xx = []
if CoordType_in == "Spherical":
r_th_ph_or_Cart_xyz_oID_xx = rfm.xxSph
elif CoordType_in == "Cartesian":
r_th_ph_or_Cart_xyz_oID_xx = rfm.xxCart
else:
print("Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords.")
sys.exit(1)
# Next apply Jacobian transformations to convert into the (xx0,xx1,xx2) basis
# alpha is a scalar, so no Jacobian transformation is necessary.
alpha = alphaSphorCart
Jac_dUSphorCart_dDrfmUD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff(r_th_ph_or_Cart_xyz_oID_xx[i], rfm.xx[j])
Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3(Jac_dUSphorCart_dDrfmUD)
betaU = ixp.zerorank1()
BU = ixp.zerorank1()
gammaSphorCartDD = ixp.zerorank2()
KDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j]
BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j]
for k in range(DIM):
for l in range(DIM):
gammaSphorCartDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * \
gammaSphorCartDD[k][l]
KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l]
# Step 3: 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 3.1: 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)):
# \bar{\gamma}_{i j} = \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \gamma_{ij}.
gammaSphorCartUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaSphorCartDD)
gammabarDD = ixp.zerorank2()
for i in range(DIM):
for j in range(DIM):
gammabarDD[i][j] = (rfm.detgammahat / gammaDET) ** (sp.Rational(1, 3)) * gammaSphorCartDD[i][j]
# Step 3.2: 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)):
# K = \gamma^{ij} K_{ij}, and
# \bar{A}_{ij} &= \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \left(K_{ij} - \frac{1}{3} \gamma_{ij} K \right)
trK = sp.sympify(0)
for i in range(DIM):
for j in range(DIM):
trK += gammaSphorCartUU[i][j] * KDD[i][j]
AbarDD = 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) * gammaSphorCartDD[i][j] * trK)
# Step 3.3: Set the conformal factor variable $\texttt{cf}$, which is set
# by the "BSSN_RHSs::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:
# \phi = \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right).
# Alternatively if "BSSN_RHSs::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_RHSs::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}.
# First compute gammabarDET:
gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD)
cf = sp.sympify(0)
if par.parval_from_str("EvolvedConformalFactor_cf") == "phi":
cf = sp.Rational(1, 12) * sp.log(gammaDET / gammabarDET)
elif par.parval_from_str("EvolvedConformalFactor_cf") == "chi":
cf = (gammaDET / gammabarDET) ** (-sp.Rational(1, 3))
elif par.parval_from_str("EvolvedConformalFactor_cf") == "W":
cf = (gammaDET / gammabarDET) ** (-sp.Rational(1, 6))
else:
print("Error EvolvedConformalFactor_cf type = \"" + par.parval_from_str("EvolvedConformalFactor_cf") + "\" unknown.")
sys.exit(1)
# Step 4: Rescale tensorial quantities according to the prescription described in
# the [BSSN in curvilinear coordinates tutorial module](Tutorial-BSSNCurvilinear.ipynb)
# (also [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)):
#
# h_{ij} &= (\bar{\gamma}_{ij} - \hat{\gamma}_{ij})/\text{ReDD[i][j]}\\
# a_{ij} &= \bar{A}_{ij}/\text{ReDD[i][j]}\\
# \lambda^i &= \bar{\Lambda}^i/\text{ReU[i]}\\
# \mathcal{V}^i &= \beta^i/\text{ReU[i]}\\
# \mathcal{B}^i &= B^i/\text{ReU[i]}\\
hDD = ixp.zerorank2()
aDD = ixp.zerorank2()
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]
for j in range(DIM):
hDD[i][j] = (gammabarDD[i][j] - rfm.ghatDD[i][j]) / rfm.ReDD[i][j]
aDD[i][j] = AbarDD[i][j] / rfm.ReDD[i][j]
# Step 5: Output all ADM-to-BSSN expressions to a C function. This function
# must first call the ID_ADM_SphorCart() defined above. Using these
# Spherical or Cartesian data, it sets up all quantities needed for
# BSSNCurvilinear initial data, *except* $\lambda^i$, which must be
# computed from numerical data using finite-difference derivatives.
with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", "w") as file:
file.write("void ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(const REAL xx0xx1xx2[3],")
if pointer_to_ID_inputs == True:
file.write("ID_inputs *other_inputs,")
else:
file.write("ID_inputs other_inputs,")
file.write("""
REAL *hDD00,REAL *hDD01,REAL *hDD02,REAL *hDD11,REAL *hDD12,REAL *hDD22,
REAL *aDD00,REAL *aDD01,REAL *aDD02,REAL *aDD11,REAL *aDD12,REAL *aDD22,
REAL *trK,
REAL *vetU0,REAL *vetU1,REAL *vetU2,
REAL *betU0,REAL *betU1,REAL *betU2,
REAL *alpha, REAL *cf) {
REAL gammaSphorCartDD00,gammaSphorCartDD01,gammaSphorCartDD02,
gammaSphorCartDD11,gammaSphorCartDD12,gammaSphorCartDD22;
REAL KSphorCartDD00,KSphorCartDD01,KSphorCartDD02,
KSphorCartDD11,KSphorCartDD12,KSphorCartDD22;
REAL alphaSphorCart,betaSphorCartU0,betaSphorCartU1,betaSphorCartU2;
REAL BSphorCartU0,BSphorCartU1,BSphorCartU2;
const REAL xx0 = xx0xx1xx2[0];
const REAL xx1 = xx0xx1xx2[1];
const REAL xx2 = xx0xx1xx2[2];
REAL xyz_or_rthph[3];\n""")
outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False"
outputC(r_th_ph_or_Cart_xyz_oID_xx[0:3], ["xyz_or_rthph[0]", "xyz_or_rthph[1]", "xyz_or_rthph[2]"],
"BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", outCparams + ",CSE_enable=False")
with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", "a") as file:
file.write(" "+ADM_input_function_name+"""(xyz_or_rthph, other_inputs,
&gammaSphorCartDD00,&gammaSphorCartDD01,&gammaSphorCartDD02,
&gammaSphorCartDD11,&gammaSphorCartDD12,&gammaSphorCartDD22,
&KSphorCartDD00,&KSphorCartDD01,&KSphorCartDD02,
&KSphorCartDD11,&KSphorCartDD12,&KSphorCartDD22,
&alphaSphorCart,&betaSphorCartU0,&betaSphorCartU1,&betaSphorCartU2,
&BSphorCartU0,&BSphorCartU1,&BSphorCartU2);
// Next compute all rescaled BSSN curvilinear quantities:\n""")
outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False"
outputC([hDD[0][0], hDD[0][1], hDD[0][2], hDD[1][1], hDD[1][2], hDD[2][2],
aDD[0][0], aDD[0][1], aDD[0][2], aDD[1][1], aDD[1][2], aDD[2][2],
trK, vetU[0], vetU[1], vetU[2], betU[0], betU[1], betU[2],
alpha, cf],
["*hDD00", "*hDD01", "*hDD02", "*hDD11", "*hDD12", "*hDD22",
"*aDD00", "*aDD01", "*aDD02", "*aDD11", "*aDD12", "*aDD22",
"*trK", "*vetU0", "*vetU1", "*vetU2", "*betU0", "*betU1", "*betU2",
"*alpha", "*cf"],
"BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", params=outCparams)
with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h", "a") as file:
file.write("}\n")
# Step 5.A: Output the driver function for the above
# function ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs()
# Next write the driver function for ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs():
with open("BSSN/ID_BSSN__ALL_BUT_LAMBDAs.h", "w") as file:
file.write("void ID_BSSN__ALL_BUT_LAMBDAs(const int Nxx_plus_2NGHOSTS[3],REAL *xx[3],")
if pointer_to_ID_inputs == True:
file.write("ID_inputs *other_inputs,")
else:
file.write("ID_inputs other_inputs,")
file.write("REAL *in_gfs) {\n")
file.write(lp.loop(["i2", "i1", "i0"], ["0", "0", "0"],
["Nxx_plus_2NGHOSTS[2]", "Nxx_plus_2NGHOSTS[1]", "Nxx_plus_2NGHOSTS[0]"],
["1", "1", "1"], ["#pragma omp parallel for",
" const REAL xx2 = xx[2][i2];",
" const REAL xx1 = xx[1][i1];"], "",
"""const REAL xx0 = xx[0][i0];
const int idx = IDX3(i0,i1,i2);
const REAL xx0xx1xx2[3] = {xx0,xx1,xx2};
ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(xx0xx1xx2,other_inputs,
&in_gfs[IDX4pt(HDD00GF,idx)],&in_gfs[IDX4pt(HDD01GF,idx)],&in_gfs[IDX4pt(HDD02GF,idx)],
&in_gfs[IDX4pt(HDD11GF,idx)],&in_gfs[IDX4pt(HDD12GF,idx)],&in_gfs[IDX4pt(HDD22GF,idx)],
&in_gfs[IDX4pt(ADD00GF,idx)],&in_gfs[IDX4pt(ADD01GF,idx)],&in_gfs[IDX4pt(ADD02GF,idx)],
&in_gfs[IDX4pt(ADD11GF,idx)],&in_gfs[IDX4pt(ADD12GF,idx)],&in_gfs[IDX4pt(ADD22GF,idx)],
&in_gfs[IDX4pt(TRKGF,idx)],
&in_gfs[IDX4pt(VETU0GF,idx)],&in_gfs[IDX4pt(VETU1GF,idx)],&in_gfs[IDX4pt(VETU2GF,idx)],
&in_gfs[IDX4pt(BETU0GF,idx)],&in_gfs[IDX4pt(BETU1GF,idx)],&in_gfs[IDX4pt(BETU2GF,idx)],
&in_gfs[IDX4pt(ALPHAGF,idx)],&in_gfs[IDX4pt(CFGF,idx)]);
"""))
file.write("}\n")
# Step 6: Compute $\bar{\Lambda}^i$ (Eqs. 4 and 5 of
# [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)),
# from finite-difference derivatives of rescaled metric
# quantities $h_{ij}$:
# \bar{\Lambda}^i = \bar{\gamma}^{jk}\left(\bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk}\right).
# The reference_metric.py module provides us with analytic expressions for
# $\hat{\Gamma}^i_{jk}$, so here we need only compute
# finite-difference expressions for $\bar{\Gamma}^i_{jk}$, based on
# the values for $h_{ij}$ provided in the initial data. Once
# $\bar{\Lambda}^i$ has been computed, we apply the usual rescaling
# procedure:
# \lambda^i = \bar{\Lambda}^i/\text{ReU[i]},
# and then output the result to a C file using the NRPy+
# finite-difference C output routine.
# We will need all BSSN gridfunctions to be defined, as well as
# expressions for gammabarDD_dD in terms of exact derivatives of
# the rescaling matrix and finite-difference derivatives of
# hDD's.
gammabarDD = bssnrhs.gammabarDD
gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD)
gammabarDD_dD = bssnrhs.gammabarDD_dD
# Next compute Christoffel symbols \bar{\Gamma}^i_{jk}:
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] * (gammabarDD_dD[l][j][k] +
gammabarDD_dD[l][k][j] -
gammabarDD_dD[j][k][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])
# Finally apply rescaling:
# lambda^i = Lambdabar^i/\text{ReU[i]}
lambdaU = ixp.zerorank1()
for i in range(DIM):
lambdaU[i] = LambdabarU[i] / rfm.ReU[i]
outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False"
lambdaU_expressions = [lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU0"), rhs=lambdaU[0]),
lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU1"), rhs=lambdaU[1]),
lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU2"), rhs=lambdaU[2])]
lambdaU_expressions_FDout = fin.FD_outputC("returnstring", lambdaU_expressions, outCparams)
with open("BSSN/ID_BSSN_lambdas.h", "w") as file:
file.write("""
void ID_BSSN_lambdas(const int Nxx[3],const int Nxx_plus_2NGHOSTS[3],REAL *xx[3],const REAL dxx[3],REAL *in_gfs) {\n""")
file.write(lp.loop(["i2", "i1", "i0"], ["NGHOSTS", "NGHOSTS", "NGHOSTS"],
["NGHOSTS+Nxx[2]", "NGHOSTS+Nxx[1]", "NGHOSTS+Nxx[0]"],
["1", "1", "1"], ["const REAL invdx0 = 1.0/dxx[0];\n" +
"const REAL invdx1 = 1.0/dxx[1];\n" +
"const REAL invdx2 = 1.0/dxx[2];\n" +
"#pragma omp parallel for",
" const REAL xx2 = xx[2][i2];",
" const REAL xx1 = xx[1][i1];"], "",
"const REAL xx0 = xx[0][i0];\n" + lambdaU_expressions_FDout))
file.write("}\n")
# This module sets up UIUC Black Hole initial data in terms of
# the variables used in BSSN_RHSs.py
# Authors: Zachariah B. Etienne, zachetie **at** gmail **dot** com
# Terrence Pierre Jacques, terrencepierrej **at** gmail **dot** com
# Ian Ruchlin
# ## This module sets up initial data for a merging black hole system in spherical coordinates. We can convert from spherical to any coordinate system defined in [reference_metric.py](../edit/reference_metric.py) (e.g., SinhSpherical, Cylindrical, Cartesian, etc.) using the [Exact ADM Spherical-to-BSSNCurvilinear converter module](Tutorial-ADM_Initial_Data-Converting_Exact_ADM_Spherical_or_Cartesian_to_BSSNCurvilinear.ipynb)
#
# ### Here we set up UIUC Black Hole initial data ([Liu, Etienne, & Shapiro, PRD 80 121503, 2009](https://arxiv.org/abs/1001.4077)):
#
# UIUC black holes have the advantage of finite coordinate radius in the maximal spin limit. It is therefore excellent for studying very highly spinning black holes. This module sets the UIUC black hole at the origin.
#
# **Inputs for initial data**:
#
# * The black hole mass $M$.
# * The dimensionless spin parameter $\chi = a/M$
#
# **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 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 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):
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:
# Spin per unit mass
a = M*chi
# Defined under equation 1 in Liu, Etienne, & Shapiro (2009) https://arxiv.org/pdf/1001.4077.pdf
# Boyer - Lindquist outer horizon
rp = M + sp.sqrt(M**2 - a**2)
# Boyer - Lindquist inner horizon
rm = M - sp.sqrt(M**2 - a**2)
# Boyer - Lindquist radius in terms of UIUC radius
# Eq. 11
# r_{BL} = r * ( 1 + r_+ / 4r )^2
rBL = r*(1 + rp / (4*r))**2
# Expressions found below Eq. 2
# Sigma = r_{BL}^2 + a^2 cos^2 theta
SIG = rBL**2 + a**2*sp.cos(th)**2
# Delta = r_{BL}^2 - 2Mr_{BL} + a^2
DEL = rBL**2 - 2*M*rBL + a**2
# A = (r_{BL}^2 + a^2)^2 - Delta a^2 sin^2 theta
AA = (rBL**2 + a**2)**2 - DEL*a**2*sp.sin(th)**2
# *** The ADM 3-metric in spherical basis ***
gammaSphDD = ixp.zerorank2()
# Declare the nonzero components of the 3-metric
# (Eq. 13 of Liu, Etienne, & Shapiro, https://arxiv.org/pdf/1001.4077.pdf):
# ds^2 = Sigma (r + r_+/4)^2 / ( r^3 (r_{BL} - r_- ) * dr^2 +
# Sigma d theta^2 + (A sin^2 theta) / Sigma * d\phi^2
gammaSphDD[0][0] = ((SIG*(r + rp/4)**2)/(r**3*(rBL - rm)))
gammaSphDD[1][1] = SIG
gammaSphDD[2][2] = AA/SIG*sp.sin(th)**2
# *** The physical trace-free extrinsic curvature in spherical basis ***
# Nonzero components of the extrinsic curvature K, given by
# Eq. 14 of Liu, Etienne, & Shapiro, https://arxiv.org/pdf/1001.4077.pdf:
KSphDD = ixp.zerorank2()
# K_{r phi} = K_{phi r} = (Ma sin^2 theta) / (Sigma sqrt{A Sigma}) *
# [3r^4_{BL} + 2a^2 r^2_{BL} - a^4 - a^2 (r^2_{BL} - a^2) sin^2 theta] *
# (1 + r_+ / 4r) (1 / sqrt{r(r_{BL} - r_-)})
KSphDD[0][2] = KSphDD[2][0] = (M*a*sp.sin(th)**2)/(SIG*sp.sqrt(AA*SIG))*\
(3*rBL**4 + 2*a**2*rBL**2 - a**4- a**2*(rBL**2 - a**2)*\
sp.sin(th)**2)*(1 + rp/(4*r))*1/sp.sqrt(r*(rBL - rm))
# Components of the extrinsic curvature K, given by
# Eq. 15 of Liu, Etienne, & Shapiro, https://arxiv.org/pdf/1001.4077.pdf:
# K_{theta phi} = K_{phi theta} = -(2a^3 Mr_{BL} cos theta sin^3 theta) /
# (Sigma sqrt{A Sigma}) x (r - r_+ / 4) sqrt{(r_{BL} - r_-) / r }
KSphDD[1][2] = KSphDD[2][1] = -((2*a**3*M*rBL*sp.cos(th)*sp.sin(th)**3)/ \
(SIG*sp.sqrt(AA*SIG)))*(r - rp/4)*sp.sqrt((rBL - rm)/r)
alphaSph = sp.sympify(1) # We generally choose alpha = 1/psi**2 (psi = BSSN conformal factor) for these initial data
betaSphU = ixp.zerorank1() # We generally choose \beta^i = 0 for these initial data
BSphU = ixp.zerorank1() # We generally choose B^i = 0 for these initial data
if ComputeADMGlobalsOnly == True:
return
# Validated against original SENR: KSphDD[0][2], KSphDD[1][2], gammaSphDD[2][2], gammaSphDD[0][0], gammaSphDD[1][1]
#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)
import BSSN.BSSN_ID_function_string as bIDf
global returnfunction
returnfunction = bIDf.BSSN_ID_function_string(cf, hDD, lambdaU, aDD, trK, alpha, vetU, betU)
from UnitTesting.create_test import create_test
def test_ADMBSSN_tofrom_4metric():
module = 'BSSN.ADMBSSN_tofrom_4metric'
module_name = 'ADMBSSN_tofrom_4metric'
function_and_global_dict = {'g4DD_ito_BSSN_or_ADM(\"ADM\")': ['g4DD'],
'g4UU_ito_BSSN_or_ADM(\"ADM\")': ['g4UU'],
'BSSN_or_ADM_ito_g4DD(\"ADM\")': ['gammaDD', 'betaU', 'alpha']}
create_test(module, module_name, function_and_global_dict)
def test_ADMBSSN_tofrom_4metric_BSSN():
module = 'BSSN.ADMBSSN_tofrom_4metric'
module_name = 'ADMBSSN_tofrom_4metric'
function_and_global_dict = {'g4DD_ito_BSSN_or_ADM(\"BSSN\")': ['g4DD'],
'g4UU_ito_BSSN_or_ADM(\"BSSN\")': ['g4UU'],
'BSSN_or_ADM_ito_g4DD(\"BSSN\")': ['hDD', 'cf', 'vetU', 'alpha']}
rfm_init_string = '''
import BSSN.BSSN_quantities as Bq
import reference_metric as rfm
rfm.reference_metric()
rfm.ref_metric__hatted_quantities()'''
initialization_string_dict = {
'g4DD_ito_BSSN_or_ADM(\"BSSN\")': rfm_init_string,
'g4UU_ito_BSSN_or_ADM(\"BSSN\")': rfm_init_string,
'BSSN_or_ADM_ito_g4DD(\"BSSN\")': rfm_init_string
}
create_test(module, module_name, function_and_global_dict, logging_level='INFO', initialization_string_dict=initialization_string_dict)
def test_BSSN_stress_energy_source_terms():
module = 'BSSN.BSSN_stress_energy_source_terms'
module_name = 'BSSN_stress_energy_source_terms'
function_and_global_dict = {'stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars(\"ADM\")': ['SDD','SD','S','rho'],
'stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars(\"BSSN\")': ['SDD','SD','S','rho'],
'BSSN_source_terms_for_BSSN_RHSs()': ['sourceterm_trK_rhs', 'sourceterm_a_rhsDD', 'sourceterm_lambda_rhsU', 'sourceterm_Lambdabar_rhsU']}
rfm_init_string = '''
import BSSN.BSSN_quantities as Bq
import reference_metric as rfm
rfm.reference_metric()
rfm.ref_metric__hatted_quantities()'''
initialization_string_dict = {
'stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars(\"ADM\")': rfm_init_string,
'stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars(\"BSSN\")': rfm_init_string,
'BSSN_source_terms_for_BSSN_RHSs()': rfm_init_string
}
create_test(module, module_name, function_and_global_dict, logging_level='INFO', initialization_string_dict=initialization_string_dict)
def test_ADM_in_terms_of_BSSN():
module = 'BSSN.ADM_in_terms_of_BSSN'
module_name = 'ADM_in_terms_of_BSSN'
function_and_global_dict = {'ADM_in_terms_of_BSSN()': ['gammaDD', 'gammaDDdD', 'gammaDDdDD', 'gammaUU', 'detgamma',
'GammaUDD', 'KDD', 'KDDdD']}
create_test(module, module_name, function_and_global_dict)
def test_BSSN_in_terms_of_ADM():
module = 'BSSN.BSSN_in_terms_of_ADM'
module_name = 'BSSN_in_terms_of_ADM'
function_and_global_dict = {'gammabarDD_hDD(gammaDD=None)': ['gammabarDD','hDD'],
'trK_AbarDD_aDD(gammaDD=None,KDD=None)': ['trK','AbarDD','aDD'],
'LambdabarU_lambdaU__exact_gammaDD(gammaDD=None)': ['LambdabarU','lambdaU'],
'cf_from_gammaDD(gammaDD=None)': ['cf'],
'betU_vetU(betaU=None,BU=None)': ['vetU','betU']
}
rfm_init_string = '''
import BSSN.BSSN_quantities as Bq
import reference_metric as rfm
rfm.reference_metric()
rfm.ref_metric__hatted_quantities()'''
initialization_string_dict = {
'gammabarDD_hDD(gammaDD=None)': rfm_init_string,
'trK_AbarDD_aDD(gammaDD=None,KDD=None)': rfm_init_string,
'LambdabarU_lambdaU__exact_gammaDD(gammaDD=None)': rfm_init_string,
'cf_from_gammaDD(gammaDD=None)': rfm_init_string,
'betU_vetU(betaU=None,BU=None)': rfm_init_string
}
create_test(module, module_name, function_and_global_dict, logging_level='INFO', initialization_string_dict=initialization_string_dict)
def test_BrillLindquist():
module = 'BSSN.BrillLindquist'
module_name = 'BrillLindquist'
function_and_global_dict = {'BrillLindquist(ComputeADMGlobalsOnly=True)': ['alphaCart', 'betaCartU', 'BCartU',
'gammaCartDD', 'KCartDD']}
create_test(module, module_name, function_and_global_dict)
def test_constraints():
module = 'BSSN.BSSN_constraints'
module_name = 'BSSN_constraints'
function_and_global_dict = {'BSSN_constraints()': ['H', 'MU']}
create_test(module, module_name, function_and_global_dict)
def test_Psi4():
module = 'BSSN.Psi4'
module_name = 'Psi4'
function_and_global_dict = {'Psi4(specify_tetrad=False)': ['psi4_re_pt', 'psi4_im_pt']}
create_test(module, module_name, function_and_global_dict)
def test_Psi4_tetrads():
module = 'BSSN.Psi4_tetrads'
module_name = 'Psi4_tetrads'
function_and_global_dict = {'Psi4_tetrads()': ['l4U', 'n4U', 'mre4U', 'mim4U']}
create_test(module, module_name, function_and_global_dict)
def test_quantities():
module = 'BSSN.BSSN_quantities'
module_name = 'BSSN_quantities'
function_and_global_dict = {'declare_BSSN_gridfunctions_if_not_declared_already()': ['hDD', 'aDD', 'lambdaU', 'vetU', 'betU', 'trK', 'cf', 'alpha'],
'BSSN_basic_tensors()': ['gammabarDD', 'AbarDD', 'LambdabarU', 'betaU', 'BU'],
'gammabar__inverse_and_derivs()': ['gammabarUU', 'gammabarDD_dD', 'gammabarDD_dupD', 'gammabarDD_dDD', 'GammabarUDD'],
'detgammabar_and_derivs()': ['detgammabar', 'detgammabar_dD', 'detgammabar_dDD'],
'AbarUU_AbarUD_trAbar_AbarDD_dD()': ['AbarUU', 'AbarUD', 'trAbar', 'AbarDD_dD', 'AbarDD_dupD'],
'RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU()': ['RbarDD', 'DGammaUDD', 'gammabarDD_dHatD', 'DGammaU'],
'betaU_derivs()': ['betaU_dD', 'betaU_dupD', 'betaU_dDD'],
'phi_and_derivs()': ['phi_dD', 'phi_dupD', 'phi_dDD', 'exp_m4phi', 'phi_dBarD', 'phi_dBarDD']
}
rfm_init_string = '''
import reference_metric as rfm
rfm.reference_metric()
rfm.ref_metric__hatted_quantities()'''
initialization_string_dict = {
'BSSN_basic_tensors()': rfm_init_string,
'gammabar__inverse_and_derivs()': rfm_init_string,
'detgammabar_and_derivs()': rfm_init_string,
'AbarUU_AbarUD_trAbar_AbarDD_dD()': rfm_init_string,
'RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU()': rfm_init_string,
'betaU_derivs()': rfm_init_string,
'phi_and_derivs()': rfm_init_string
}
create_test(module, module_name, function_and_global_dict, logging_level='INFO', initialization_string_dict=initialization_string_dict)
def test_RHSs():
module = 'BSSN.BSSN_RHSs'
module_name = 'BSSN_RHSs'
function_and_global_dict = {'BSSN_RHSs()': ['cf_rhs', 'trK_rhs', 'lambda_rhsU', 'a_rhsDD', 'h_rhsDD']}
create_test(module, module_name, function_and_global_dict)
def test_gauge_RHSs():
module = 'BSSN.BSSN_gauge_RHSs'
module_name = 'gauge_RHSs'
function_and_global_dict = {'BSSN_gauge_RHSs()': ['alpha_rhs', 'bet_rhsU', 'vet_rhsU']}
bssn_rhs_init_string = '''
import BSSN.BSSN_RHSs as rhs
rhs.BSSN_RHSs()
'''
initialization_string_dict = { 'BSSN_gauge_RHSs()': bssn_rhs_init_string }
create_test(module, module_name, function_and_global_dict, initialization_string_dict=initialization_string_dict)
def test_ShiftedKerrSchild():
module = 'BSSN.ShiftedKerrSchild'
module_name = 'ShiftedKerrSchild'
function_and_global_dict = {'ShiftedKerrSchild(ComputeADMGlobalsOnly=True)': ['alphaSph', 'betaSphU', 'BSphU',
'gammaSphDD', 'KSphDD']}
create_test(module, module_name, function_and_global_dict)
def test_StaticTrumpet():
module = 'BSSN.StaticTrumpet'
module_name = 'StaticTrumpet'
function_and_global_dict = {'StaticTrumpet(ComputeADMGlobalsOnly=True)': ['alphaSph', 'betaSphU', 'BSphU',
'gammaSphDD', 'KSphDD']}
create_test(module, module_name, function_and_global_dict)
def test_T4UUmunu_vars():
module = 'BSSN.BSSN_T4UUmunu_vars'
module_name = 'T4UUmunu_vars'
function_and_global_dict = {'define_BSSN_T4UUmunu_rescaled_source_terms()': ['rho', 'S', 'sD', 'sDD']}
create_test(module, module_name, function_and_global_dict)
def test_UIUCBlackHole():
module = 'BSSN.UIUCBlackHole'
module_name = 'UIUCBlackHole'
function_and_global_dict = {'UIUCBlackHole(ComputeADMGlobalsOnly=True)': ['alphaSph', 'betaSphU', 'BSphU',
'gammaSphDD', 'KSphDD']}
create_test(module, module_name, function_and_global_dict)
if __name__ == '__main__':
import sys
if len(sys.argv) <= 3:
failed_functions = []
for fun in dir():
if fun[0:5] == 'test_':
print('\nTesting ' + str(fun) + '...\n')
try:
exec(fun + '()')
except SystemExit:
failed_functions.append(fun)
if failed_functions != []:
import sys, os
with open(os.path.join('UnitTesting', 'failed_tests.txt'), 'a') as file:
for function in failed_functions:
file.write(sys.argv[0] + ': ' + str(function) + '\n')
exit(1)
else:
globals()[sys.argv[4]]()
from mpmath import mpf, mp, mpc
from UnitTesting.standard_constants import precision
mp.dps = precision
trusted_values_dict = {}
# Generated on: 2019-08-09
trusted_values_dict['ADM_in_terms_of_BSSN__ADM_in_terms_of_BSSN__globals'] = {'gammaDD[0][0]': mpf('8.84684305820134422150597858207921'), 'gammaDD[0][1]': mpf('3.350018689599008046165002833204'), 'gammaDD[0][2]': mpf('0.194795767378908960688399288371257'), 'gammaDD[1][0]': mpf('3.350018689599008046165002833204'), 'gammaDD[1][1]': mpf('1.92752387150314689143948714387062'), 'gammaDD[1][2]': mpf('0.617514215490828029162196536408385'), 'gammaDD[2][0]': mpf('0.194795767378908960688399288371257'), 'gammaDD[2][1]': mpf('0.617514215490828029162196536408385'), 'gammaDD[2][2]': mpf('0.440219517017598275317485123438443'), 'gammaDDdD[0][0][0]': mpf('-39.2181788667717063214934687853284'), 'gammaDDdD[0][0][1]': mpf('-43.5347678242827125179031665798782'), 'gammaDDdD[0][0][2]': mpf('-43.5448629253377323761214222661368'), 'gammaDDdD[0][1][0]': mpf('-3.82269337582904325002653701794443'), 'gammaDDdD[0][1][1]': mpf('-15.8371132982064951265352981022794'), 'gammaDDdD[0][1][2]': mpf('-13.0007465813467278947970267824292'), 'gammaDDdD[0][2][0]': mpf('-0.218013883809053119477949934634148'), 'gammaDDdD[0][2][1]': mpf('0.785829885135145904482804685445781'), 'gammaDDdD[0][2][2]': mpf('-0.581351677673311207621301361731265'), 'gammaDDdD[1][0][0]': mpf('-3.82269337582904325002653701794443'), 'gammaDDdD[1][0][1]': mpf('-15.8371132982064951265352981022794'), 'gammaDDdD[1][0][2]': mpf('-13.0007465813467278947970267824292'), 'gammaDDdD[1][1][0]': mpf('1.02077344056659026570129696849467'), 'gammaDDdD[1][1][1]': mpf('-9.20007419968800686893912169288767'), 'gammaDDdD[1][1][2]': mpf('-8.15422339233949609213151393845906'), 'gammaDDdD[1][2][0]': mpf('0.357104131772599372488808535542396'), 'gammaDDdD[1][2][1]': mpf('-1.65079628886562588602648974576529'), 'gammaDDdD[1][2][2]': mpf('-2.86344337379037596725739579107855'), 'gammaDDdD[2][0][0]': mpf('-0.218013883809053119477949934634148'), 'gammaDDdD[2][0][1]': mpf('0.785829885135145904482804685445781'), 'gammaDDdD[2][0][2]': mpf('-0.581351677673311207621301361731265'), 'gammaDDdD[2][1][0]': mpf('0.357104131772599372488808535542396'), 'gammaDDdD[2][1][1]': mpf('-1.65079628886562588602648974576529'), 'gammaDDdD[2][1][2]': mpf('-2.86344337379037596725739579107855'), 'gammaDDdD[2][2][0]': mpf('0.195001221087943638085807308270354'), 'gammaDDdD[2][2][1]': mpf('-0.410311324085430408983791626770926'), 'gammaDDdD[2][2][2]': mpf('-1.92276041454685421458861940256278'), 'gammaDDdDD[0][0][0][0]': mpf('225.398957517527156513326956764229'), 'gammaDDdDD[0][0][0][1]': mpf('288.140070211252996184124719294385'), 'gammaDDdDD[0][0][0][2]': mpf('281.623967129510091655495816474147'), 'gammaDDdDD[0][0][1][0]': mpf('288.140070211252996184124719294385'), 'gammaDDdDD[0][0][1][1]': mpf('337.948619849001134211864646627346'), 'gammaDDdDD[0][0][1][2]': mpf('322.481152355898457651315654723053'), 'gammaDDdDD[0][0][2][0]': mpf('281.623967129510091655495816474147'), 'gammaDDdDD[0][0][2][1]': mpf('322.481152355898457651315654723053'), 'gammaDDdDD[0][0][2][2]': mpf('293.564334950019576720032123379472'), 'gammaDDdDD[0][1][0][0]': mpf('3.64051177720082509234617335007875'), 'gammaDDdDD[0][1][0][1]': mpf('50.4627675517741918630578752181752'), 'gammaDDdDD[0][1][0][2]': mpf('46.1357119642924033983593968867937'), 'gammaDDdDD[0][1][1][0]': mpf('50.4627675517741918630578752181752'), 'gammaDDdDD[0][1][1][1]': mpf('119.02655264345724986788778186216'), 'gammaDDdDD[0][1][1][2]': mpf('100.654984059470512047205484497294'), 'gammaDDdDD[0][1][2][0]': mpf('46.1357119642924033983593968867937'), 'gammaDDdDD[0][1][2][1]': mpf('100.654984059470512047205484497294'), 'gammaDDdDD[0][1][2][2]': mpf('77.760445818040255191531604261198'), 'gammaDDdDD[0][2][0][0]': mpf('0.375532488603708956700281774274923'), 'gammaDDdDD[0][2][0][1]': mpf('0.678340756727625343658227489853017'), 'gammaDDdDD[0][2][0][2]': mpf('2.78948049995862477035881653480892'), 'gammaDDdDD[0][2][1][0]': mpf('0.678340756727625343658227489853017'), 'gammaDDdDD[0][2][1][1]': mpf('-6.12617437316042868416067368255866'), 'gammaDDdDD[0][2][1][2]': mpf('-1.75847734950653146320336447142805'), 'gammaDDdDD[0][2][2][0]': mpf('2.78948049995862477035881653480892'), 'gammaDDdDD[0][2][2][1]': mpf('-1.75847734950653146320336447142805'), 'gammaDDdDD[0][2][2][2]': mpf('2.73521309266304236084886214346027'), 'gammaDDdDD[1][0][0][0]': mpf('3.64051177720082509234617335007875'), 'gammaDDdDD[1][0][0][1]': mpf('50.4627675517741918630578752181752'), 'gammaDDdDD[1][0][0][2]': mpf('46.1357119642924033983593968867937'), 'gammaDDdDD[1][0][1][0]': mpf('50.4627675517741918630578752181752'), 'gammaDDdDD[1][0][1][1]': mpf('119.02655264345724986788778186216'), 'gammaDDdDD[1][0][1][2]': mpf('100.654984059470512047205484497294'), 'gammaDDdDD[1][0][2][0]': mpf('46.1357119642924033983593968867937'), 'gammaDDdDD[1][0][2][1]': mpf('100.654984059470512047205484497294'), 'gammaDDdDD[1][0][2][2]': mpf('77.760445818040255191531604261198'), 'gammaDDdDD[1][1][0][0]': mpf('-9.92106187379982562603610831325235'), 'gammaDDdDD[1][1][0][1]': mpf('15.825454310362637536048355653535'), 'gammaDDdDD[1][1][0][2]': mpf('14.4039518436611112501034205835319'), 'gammaDDdDD[1][1][1][0]': mpf('15.825454310362637536048355653535'), 'gammaDDdDD[1][1][1][1]': mpf('70.6517225129821789456736189152217'), 'gammaDDdDD[1][1][1][2]': mpf('61.9035794546980075683100782914809'), 'gammaDDdDD[1][1][2][0]': mpf('14.4039518436611112501034205835319'), 'gammaDDdDD[1][1][2][1]': mpf('61.9035794546980075683100782914809'), 'gammaDDdDD[1][1][2][2]': mpf('50.8579841651123380242828623654651'), 'gammaDDdDD[1][2][0][0]': mpf('-3.34089527571068728373797749836677'), 'gammaDDdDD[1][2][0][1]': mpf('5.5335349427183318693808176585407'), 'gammaDDdDD[1][2][0][2]': mpf('4.51408779818567722405331676792284'), 'gammaDDdDD[1][2][1][0]': mpf('5.5335349427183318693808176585407'), 'gammaDDdDD[1][2][1][1]': mpf('9.66395575640457553089340537573125'), 'gammaDDdDD[1][2][1][2]': mpf('15.1141997261213981599250387014752'), 'gammaDDdDD[1][2][2][0]': mpf('4.51408779818567722405331676792284'), 'gammaDDdDD[1][2][2][1]': mpf('15.1141997261213981599250387014752'), 'gammaDDdDD[1][2][2][2]': mpf('19.0650383126789942251109383834193'), 'gammaDDdDD[2][0][0][0]': mpf('0.375532488603708956700281774274923'), 'gammaDDdDD[2][0][0][1]': mpf('0.678340756727625343658227489853017'), 'gammaDDdDD[2][0][0][2]': mpf('2.78948049995862477035881653480892'), 'gammaDDdDD[2][0][1][0]': mpf('0.678340756727625343658227489853017'), 'gammaDDdDD[2][0][1][1]': mpf('-6.12617437316042868416067368255866'), 'gammaDDdDD[2][0][1][2]': mpf('-1.75847734950653146320336447142805'), 'gammaDDdDD[2][0][2][0]': mpf('2.78948049995862477035881653480892'), 'gammaDDdDD[2][0][2][1]': mpf('-1.75847734950653146320336447142805'), 'gammaDDdDD[2][0][2][2]': mpf('2.73521309266304236084886214346027'), 'gammaDDdDD[2][1][0][0]': mpf('-3.34089527571068728373797749836677'), 'gammaDDdDD[2][1][0][1]': mpf('5.5335349427183318693808176585407'), 'gammaDDdDD[2][1][0][2]': mpf('4.51408779818567722405331676792284'), 'gammaDDdDD[2][1][1][0]': mpf('5.5335349427183318693808176585407'), 'gammaDDdDD[2][1][1][1]': mpf('9.66395575640457553089340537573125'), 'gammaDDdDD[2][1][1][2]': mpf('15.1141997261213981599250387014752'), 'gammaDDdDD[2][1][2][0]': mpf('4.51408779818567722405331676792284'), 'gammaDDdDD[2][1][2][1]': mpf('15.1141997261213981599250387014752'), 'gammaDDdDD[2][1][2][2]': mpf('19.0650383126789942251109383834193'), 'gammaDDdDD[2][2][0][0]': mpf('-2.52353147414316377574820905559147'), 'gammaDDdDD[2][2][0][1]': mpf('4.53575664377867062493942188091689'), 'gammaDDdDD[2][2][0][2]': mpf('3.56377908834306629097560544110174'), 'gammaDDdDD[2][2][1][0]': mpf('4.5357566437786706249394218809232'), 'gammaDDdDD[2][2][1][1]': mpf('1.84054709164445852274067505481422'), 'gammaDDdDD[2][2][1][2]': mpf('7.16074940759453707759590964424542'), 'gammaDDdDD[2][2][2][0]': mpf('3.56377908834306629097560544110174'), 'gammaDDdDD[2][2][2][1]': mpf('7.16074940759453707759590964424542'), 'gammaDDdDD[2][2][2][2]': mpf('12.0437865836043923259359609593463'), 'gammaUU[0][0]': mpf('-6.28938129956539035233976956863059'), 'gammaUU[0][1]': mpf('18.2330938347990520861338232067731'), 'gammaUU[0][2]': mpf('-22.7932869645533398576110813493934'), 'gammaUU[1][0]': mpf('18.2330938347990520861338232067731'), 'gammaUU[1][1]': mpf('-51.9160225808937703970803496330078'), 'gammaUU[1][2]': mpf('64.7566755861571412059499249935555'), 'gammaUU[2][0]': mpf('-22.7932869645533398576110813493934'), 'gammaUU[2][1]': mpf('64.7566755861571412059499249935555'), 'gammaUU[2][2]': mpf('-78.479328065890663873188546288808'), 'detgamma': mpf('-0.0742854979156755661927481235463981'), 'GammaUDD[0][0][0]': mpf('-40.7797584599095266200402578142104'), 'GammaUDD[0][0][1]': mpf('-14.9811790250568026018422503217171'), 'GammaUDD[0][0][2]': mpf('12.2823492767347497337333129446469'), 'GammaUDD[0][1][0]': mpf('-14.9811790250568026018422503217171'), 'GammaUDD[0][1][1]': mpf('-36.3609505192780257845662292664448'), 'GammaUDD[0][1][2]': mpf('-30.1270719072420555274502961949333'), 'GammaUDD[0][2][0]': mpf('12.2823492767347497337333129446469'), 'GammaUDD[0][2][1]': mpf('-30.1270719072420555274502961949333'), 'GammaUDD[0][2][2]': mpf('-22.2862334550824046529070435685497'), 'GammaUDD[1][0][0]': mpf('106.641099858555951710230668316931'), 'GammaUDD[1][0][1]': mpf('34.5648661443104393199809718733873'), 'GammaUDD[1][0][2]': mpf('-42.0625759749969397716067483012153'), 'GammaUDD[1][1][0]': mpf('34.5648661443104393199809718733873'), 'GammaUDD[1][1][1]': mpf('97.8702478474345566909160962391556'), 'GammaUDD[1][1][2]': mpf('83.7688065067695141694849527164939'), 'GammaUDD[1][2][0]': mpf('-42.0625759749969397716067483012153'), 'GammaUDD[1][2][1]': mpf('83.7688065067695141694849527164939'), 'GammaUDD[1][2][2]': mpf('63.37436120121238471219216463813'), 'GammaUDD[2][0][0]': mpf('-82.5821023178249589056509375957706'), 'GammaUDD[2][0][1]': mpf('-25.7920996188850360970982778501415'), 'GammaUDD[2][0][2]': mpf('53.7895042071558641126267308366467'), 'GammaUDD[2][1][0]': mpf('-25.7920996188850360970982778501415'), 'GammaUDD[2][1][1]': mpf('-115.685453925551790074619499114864'), 'GammaUDD[2][1][2]': mpf('-104.640881703073552107006493456468'), 'GammaUDD[2][2][0]': mpf('53.7895042071558641126267308366467'), 'GammaUDD[2][2][1]': mpf('-104.640881703073552107006493456468'), 'GammaUDD[2][2][2]': mpf('-81.2201272697658198766768439312511'), 'KDD[0][0]': mpf('2.52635313601760623671007177930783'), 'KDD[0][1]': mpf('2.82898873912114767238892405861643'), 'KDD[0][2]': mpf('0.381578303862588400274173947157554'), 'KDD[1][0]': mpf('2.82898873912114767238892405861643'), 'KDD[1][1]': mpf('0.775186746770488740436250426070358'), 'KDD[1][2]': mpf('0.547222495805151738810645787512339'), 'KDD[2][0]': mpf('0.381578303862588400274173947157554'), 'KDD[2][1]': mpf('0.547222495805151738810645787512339'), 'KDD[2][2]': mpf('0.23987159265969208367518412563801'), 'KDDdD[0][0][0]': mpf('-6.98418007348680855492680452748596'), 'KDDdD[0][0][1]': mpf('-4.90357255070945013899614827846758'), 'KDDdD[0][0][2]': mpf('-9.51091715630967778721956190135931'), 'KDDdD[0][1][0]': mpf('-4.23975060495945983382577294248919'), 'KDDdD[0][1][1]': mpf('-13.8610965865853556038884955897668'), 'KDDdD[0][1][2]': mpf('-10.9245220106877969893233753184212'), 'KDDdD[0][2][0]': mpf('0.673882280969017838122158707807608'), 'KDDdD[0][2][1]': mpf('-0.152233642969041739842056712724457'), 'KDDdD[0][2][2]': mpf('-0.869578101320992405108114411360838'), 'KDDdD[1][0][0]': mpf('-4.23975060495945983382577294248919'), 'KDDdD[1][0][1]': mpf('-13.8610965865853556038884955897668'), 'KDDdD[1][0][2]': mpf('-10.9245220106877969893233753184212'), 'KDDdD[1][1][0]': mpf('0.830817678793678957257737618165166'), 'KDDdD[1][1][1]': mpf('-3.44366499994735267064814741173414'), 'KDDdD[1][1][2]': mpf('-3.27432266931077674480141080049683'), 'KDDdD[1][2][0]': mpf('0.708601442458893682779601967212525'), 'KDDdD[1][2][1]': mpf('-1.5539589956391283812811555376677'), 'KDDdD[1][2][2]': mpf('-2.26158238807310704211409034124987'), 'KDDdD[2][0][0]': mpf('0.673882280969017838122158707807608'), 'KDDdD[2][0][1]': mpf('-0.152233642969041739842056712724457'), 'KDDdD[2][0][2]': mpf('-0.869578101320992405108114411360838'), 'KDDdD[2][1][0]': mpf('0.708601442458893682779601967212525'), 'KDDdD[2][1][1]': mpf('-1.5539589956391283812811555376677'), 'KDDdD[2][1][2]': mpf('-2.26158238807310704211409034124987'), 'KDDdD[2][2][0]': mpf('0.424502981369425679158130440942863'), 'KDDdD[2][2][1]': mpf('-0.116489737786019245029640728668932'), 'KDDdD[2][2][2]': mpf('-1.03790663578070485250054725692471')}
# Generated on: 2019-08-09
trusted_values_dict['BrillLindquist__BrillLindquist__ComputeADMGlobalsOnly=True__globals'] = {'alphaCart': mpf('0.343480750893445797669295337903236'), 'betaCartU[0]': mpf('0.0'), 'betaCartU[1]': mpf('0.0'), 'betaCartU[2]': mpf('0.0'), 'BCartU[0]': mpf('0.0'), 'BCartU[1]': mpf('0.0'), 'BCartU[2]': mpf('0.0'), 'gammaCartDD[0][0]': mpf('8.47608284216415538664572854966934'), 'gammaCartDD[0][1]': mpf('0.0'), 'gammaCartDD[0][2]': mpf('0.0'), 'gammaCartDD[1][0]': mpf('0.0'), 'gammaCartDD[1][1]': mpf('8.47608284216415538664572854966934'), 'gammaCartDD[1][2]': mpf('0.0'), 'gammaCartDD[2][0]': mpf('0.0'), 'gammaCartDD[2][1]': mpf('0.0'), 'gammaCartDD[2][2]': mpf('8.47608284216415538664572854966934'), 'KCartDD[0][0]': mpf('0.0'), 'KCartDD[0][1]': mpf('0.0'), 'KCartDD[0][2]': mpf('0.0'), 'KCartDD[1][0]': mpf('0.0'), 'KCartDD[1][1]': mpf('0.0'), 'KCartDD[1][2]': mpf('0.0'), 'KCartDD[2][0]': mpf('0.0'), 'KCartDD[2][1]': mpf('0.0'), 'KCartDD[2][2]': mpf('0.0')}
# Generated on: 2019-08-09
trusted_values_dict['Psi4__Psi4__specify_tetrad=False__globals'] = {'psi4_re_pt[0]': mpf('-11.4627408385133346012203475168749'), 'psi4_re_pt[1]': mpf('-35.6806714906029645727316589574499'), 'psi4_re_pt[2]': mpf('-3374.92496104893019517395960329346'), 'psi4_im_pt[0]': mpf('7.17626585537563385139665194926662'), 'psi4_im_pt[1]': mpf('103.321768247585408574682983348206'), 'psi4_im_pt[2]': mpf('-1785.88265198482805325630868693525')}
# Generated on: 2019-08-09
trusted_values_dict['Psi4_tetrads__Psi4_tetrads__globals'] = {'l4U[0]': mpf('0.707106781186547524400844362104785'), 'l4U[1]': mpf('0.238900425354320344832613106016182'), 'l4U[2]': mpf('0.0'), 'l4U[3]': mpf('-0.105712695337363311625493077156009'), 'n4U[0]': mpf('0.707106781186547524400844362104785'), 'n4U[1]': mpf('-0.238900425354320344832613106016182'), 'n4U[2]': mpf('0.0'), 'n4U[3]': mpf('0.105712695337363311625493077156009'), 'mre4U[0]': mpf('0.0'), 'mre4U[1]': mpc(real='0.0', imag='1.78934738466770903997371533478145'), 'mre4U[2]': mpc(real='0.0', imag='-5.09490051820905343049616931239143'), 'mre4U[3]': mpc(real='0.0', imag='6.3550480872706263113514069118537'), 'mim4U[0]': mpf('0.0'), 'mim4U[1]': mpf('0.0'), 'mim4U[2]': mpf('0.0'), 'mim4U[3]': mpf('1.06573776541005837815518683742699')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_RHSs__BSSN_RHSs__globals'] = {'cf_rhs': mpf('3.77382093789289698643326794257121'), 'trK_rhs': mpf('4897.15010872261926255101979442461'), 'lambda_rhsU[0]': mpf('-932925.380251880280514928387377418'), 'lambda_rhsU[1]': mpf('1067214.31502612160105983520112858'), 'lambda_rhsU[2]': mpf('-634521.485102957628967190217797872'), 'a_rhsDD[0][0]': mpf('140611.691921936258075248177043018'), 'a_rhsDD[0][1]': mpf('129610.009549628960496251001913799'), 'a_rhsDD[0][2]': mpf('12478.2048292408067063007598939149'), 'a_rhsDD[1][0]': mpf('129610.009549628960496251001913218'), 'a_rhsDD[1][1]': mpf('190869.710957991947993273525242575'), 'a_rhsDD[1][2]': mpf('127861.933393387548396211650851467'), 'a_rhsDD[2][0]': mpf('12478.2048292408067063007598938196'), 'a_rhsDD[2][1]': mpf('127861.933393387548396211650851428'), 'a_rhsDD[2][2]': mpf('200379.251226055957975675818299921'), 'h_rhsDD[0][0]': mpf('26.0204338410819173136453286914799'), 'h_rhsDD[0][1]': mpf('25.0575733174708969733897611044168'), 'h_rhsDD[0][2]': mpf('11.7768556092635889801475757434684'), 'h_rhsDD[1][0]': mpf('25.0575733174708969733897611044168'), 'h_rhsDD[1][1]': mpf('40.8921058052569946189333312205232'), 'h_rhsDD[1][2]': mpf('39.8230119150907530884376958638246'), 'h_rhsDD[2][0]': mpf('11.7768556092635889801475757434684'), 'h_rhsDD[2][1]': mpf('39.8230119150907530884376958638246'), 'h_rhsDD[2][2]': mpf('72.3354550710637207030251571354033')}
# Generated on: 2019-08-09
trusted_values_dict['ShiftedKerrSchild__ShiftedKerrSchild__ComputeADMGlobalsOnly=True__globals'] = {'alphaSph': mpf('0.764992222074068488402374263959388'), 'betaSphU[0]': mpf('0.414786900166179080948286822502086'), 'betaSphU[1]': mpf('0.0'), 'betaSphU[2]': mpf('0.0'), 'BSphU[0]': mpf('0.0'), 'BSphU[1]': mpf('0.0'), 'BSphU[2]': mpf('0.0'), 'gammaSphDD[0][0]': mpf('1.70877924688282501671697638255698'), 'gammaSphDD[0][1]': mpf('0.0'), 'gammaSphDD[0][2]': mpf('-0.34407721284779690446451905546302'), 'gammaSphDD[1][0]': mpf('0.0'), 'gammaSphDD[1][1]': mpf('0.83749814889950038479162273698221'), 'gammaSphDD[1][2]': mpf('0.0'), 'gammaSphDD[2][0]': mpf('-0.34407721284779690446451905546302'), 'gammaSphDD[2][1]': mpf('0.0'), 'gammaSphDD[2][2]': mpf('0.428211355345928897234853289336263'), 'KSphDD[0][0]': mpf('-0.608416975836176206520434449342952'), 'KSphDD[0][1]': mpf('0.0707242913409545835190603089766082'), 'KSphDD[0][2]': mpf('0.0772829375209889643435925308431919'), 'KSphDD[1][0]': mpf('0.0707242913409545835190603089766082'), 'KSphDD[1][1]': mpf('0.457311525223526834585984744200641'), 'KSphDD[1][2]': mpf('-0.0142409366743087039035851689662884'), 'KSphDD[2][0]': mpf('0.0772829375209889643435925308431919'), 'KSphDD[2][1]': mpf('-0.0142409366743087039035851689662884'), 'KSphDD[2][2]': mpf('0.189536302720822065090385756720081')}
# Generated on: 2019-08-09
trusted_values_dict['StaticTrumpet__StaticTrumpet__ComputeADMGlobalsOnly=True__globals'] = {'alphaSph': mpf('0.47901157071628237535174768234896'), 'betaSphU[0]': mpf('0.249559485836202384477547321977731'), 'betaSphU[1]': mpf('0.0'), 'betaSphU[2]': mpf('0.0'), 'BSphU[0]': mpf('0.0'), 'BSphU[1]': mpf('0.0'), 'BSphU[2]': mpf('0.0'), 'gammaSphDD[0][0]': mpf('4.35820838377928180149222863568597'), 'gammaSphDD[0][1]': mpf('0.0'), 'gammaSphDD[0][2]': mpf('0.0'), 'gammaSphDD[1][0]': mpf('0.0'), 'gammaSphDD[1][1]': mpf('0.456231514782622374769913065861258'), 'gammaSphDD[1][2]': mpf('0.0'), 'gammaSphDD[2][0]': mpf('0.0'), 'gammaSphDD[2][1]': mpf('0.0'), 'gammaSphDD[2][2]': mpf('0.19552817796951759898685705719116'), 'KSphDD[0][0]': mpf('-3.36157982955715545881983739302993'), 'KSphDD[0][1]': mpf('0.0'), 'KSphDD[0][2]': mpf('0.0'), 'KSphDD[1][0]': mpf('0.0'), 'KSphDD[1][1]': mpf('0.351901176504010315682080545229837'), 'KSphDD[1][2]': mpf('0.0'), 'KSphDD[2][0]': mpf('0.0'), 'KSphDD[2][1]': mpf('0.0'), 'KSphDD[2][2]': mpf('0.150815087598546495698285048292878')}
# Generated on: 2019-08-09
trusted_values_dict['T4UUmunu_vars__define_BSSN_T4UUmunu_rescaled_source_terms__globals'] = {'rho': mpf('0.318351756980246647366072920704028'), 'S': mpf('0.41672759971301631676254828562378'), 'sD[0]': mpf('0.139936033700502671095478035567794'), 'sD[1]': mpf('0.724042932336828037875875452300534'), 'sD[2]': mpf('0.634678894759461820207491200562799'), 'sDD[0][0]': mpf('0.975581143777359494073664336610818'), 'sDD[0][1]': mpf('0.319506043115684978950241657003062'), 'sDD[0][2]': mpf('0.843707417334013110021828651952092'), 'sDD[1][0]': mpf('0.319506043115684978950241657003062'), 'sDD[1][1]': mpf('0.535419446395211107159184393822215'), 'sDD[1][2]': mpf('0.697293983830049590721955610206351'), 'sDD[2][0]': mpf('0.843707417334013110021828651952092'), 'sDD[2][1]': mpf('0.697293983830049590721955610206351'), 'sDD[2][2]': mpf('0.0883241044811280406889864025288261')}
# Generated on: 2019-08-09
trusted_values_dict['UIUCBlackHole__UIUCBlackHole__ComputeADMGlobalsOnly=True__globals'] = {'alphaSph': mpf('1.0'), 'betaSphU[0]': mpf('0.0'), 'betaSphU[1]': mpf('0.0'), 'betaSphU[2]': mpf('0.0'), 'BSphU[0]': mpf('0.0'), 'BSphU[1]': mpf('0.0'), 'BSphU[2]': mpf('0.0'), 'gammaSphDD[0][0]': mpf('5.88800397535091508054696345959799'), 'gammaSphDD[0][1]': mpf('0.0'), 'gammaSphDD[0][2]': mpf('0.0'), 'gammaSphDD[1][0]': mpf('0.0'), 'gammaSphDD[1][1]': mpf('0.49579570354855419371061261468577'), 'gammaSphDD[1][2]': mpf('0.0'), 'gammaSphDD[2][0]': mpf('0.0'), 'gammaSphDD[2][1]': mpf('0.0'), 'gammaSphDD[2][2]': mpf('0.239531368774223457427342816780148'), 'KSphDD[0][0]': mpf('0.0'), 'KSphDD[0][1]': mpf('0.0'), 'KSphDD[0][2]': mpf('0.516384223238703786717270771044344'), 'KSphDD[1][0]': mpf('0.0'), 'KSphDD[1][1]': mpf('0.0'), 'KSphDD[1][2]': mpf('-0.00263738331117854302766878470676251'), 'KSphDD[2][0]': mpf('0.516384223238703786717270771044344'), 'KSphDD[2][1]': mpf('-0.00263738331117854302766878470676251'), 'KSphDD[2][2]': mpf('0.0')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_constraints__BSSN_constraints__globals'] = {'H': mpf('-743404.980107555200155796229460404'), 'MU[0]': mpf('180487.187683651511140228998931702'), 'MU[1]': mpf('-212737.320962133501867845763983593'), 'MU[2]': mpf('117663.393460122555345796445578668')}
# Generated on: 2019-08-09
trusted_values_dict['gauge_RHSs__BSSN_gauge_RHSs__globals'] = {'alpha_rhs': mpf('4.55215595841202287904255791161233'), 'bet_rhsU[0]': mpf('-699763.514938021371061944114376614'), 'bet_rhsU[1]': mpf('800493.882262268190058064264269136'), 'bet_rhsU[2]': mpf('-475937.852585205029810577745084266'), 'vet_rhsU[0]': mpf('-202.50075396040237046126314033238'), 'vet_rhsU[1]': mpf('243.191035932372271446347823729945'), 'vet_rhsU[2]': mpf('-122.639420805832850593976051533138')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_quantities__declare_BSSN_gridfunctions_if_not_declared_already__globals'] = {'hDD[0][0]': mpf('0.00450974412619198350427041077637114'), 'hDD[0][1]': mpf('0.923519781999295741314881524886005'), 'hDD[0][2]': mpf('0.120267555611392129577552623231895'), 'hDD[1][0]': mpf('0.923519781999295741314881524886005'), 'hDD[1][1]': mpf('0.290125263019792467567015137319686'), 'hDD[1][2]': mpf('0.925654825647319157155834545847028'), 'hDD[2][0]': mpf('0.120267555611392129577552623231895'), 'hDD[2][1]': mpf('0.925654825647319157155834545847028'), 'hDD[2][2]': mpf('0.477887576370744082687735954095842'), 'aDD[0][0]': mpf('0.142813553146530880511022587597836'), 'aDD[0][1]': mpf('0.647457955584693545247887414006982'), 'aDD[0][2]': mpf('0.21834217753528206706903347367188'), 'aDD[1][0]': mpf('0.647457955584693545247887414006982'), 'aDD[1][1]': mpf('0.333850909184938648976981312443968'), 'aDD[1][2]': mpf('0.687554902414082569350739504443482'), 'aDD[2][0]': mpf('0.21834217753528206706903347367188'), 'aDD[2][1]': mpf('0.687554902414082569350739504443482'), 'aDD[2][2]': mpf('0.593368548750377144962442343967268'), 'lambdaU[0]': mpf('0.562783861711218968970626974623883'), 'lambdaU[1]': mpf('0.664424004972256732060031936271116'), 'lambdaU[2]': mpf('0.26459724357734237809580690736766'), 'vetU[0]': mpf('0.696603667756184763426574590994278'), 'vetU[1]': mpf('0.602357411405810894144963185681263'), 'vetU[2]': mpf('0.850413289206002676046125543507515'), 'betU[0]': mpf('0.0391630274878592699039359104062896'), 'betU[1]': mpf('0.0690677340046900178904820677416865'), 'betU[2]': mpf('0.210433354840527941931327404745389'), 'trK': mpf('0.430179315304250131646313093369827'), 'cf': mpf('0.336963542644043156926159099384677'), 'alpha': mpf('0.574476527200906694758941739564762')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_quantities__BSSN_basic_tensors__globals'] = {'gammabarDD[0][0]': mpf('1.00450974412619198350427041077637'), 'gammabarDD[0][1]': mpf('0.380375959488448974007020456949004'), 'gammabarDD[0][2]': mpf('0.0221179741925291571131550303027706'), 'gammabarDD[1][0]': mpf('0.380375959488448974007020456949004'), 'gammabarDD[1][1]': mpf('0.218859597510979935870263689587696'), 'gammabarDD[1][2]': mpf('0.0701152990412707885651168831120483'), 'gammabarDD[2][0]': mpf('0.0221179741925291571131550303027706'), 'gammabarDD[2][1]': mpf('0.0701152990412707885651168831120483'), 'gammabarDD[2][2]': mpf('0.0499844737257731260766363781664902'), 'AbarDD[0][0]': mpf('0.142813553146530880511022587597836'), 'AbarDD[0][1]': mpf('0.266672621295452871655360256826897'), 'AbarDD[0][2]': mpf('0.0401545256600238301157730952990965'), 'AbarDD[1][0]': mpf('0.266672621295452871655360256826897'), 'AbarDD[1][1]': mpf('0.056635179317288849494133380201829'), 'AbarDD[1][2]': mpf('0.0520800154164839604018649639497029'), 'AbarDD[2][0]': mpf('0.0401545256600238301157730952990965'), 'AbarDD[2][1]': mpf('0.0520800154164839604018649639497029'), 'AbarDD[2][2]': mpf('0.0200686541445511291805537698984873'), 'LambdabarU[0]': mpf('0.562783861711218968970626974623883'), 'LambdabarU[1]': mpf('1.61316375791018207963945540610151'), 'LambdabarU[2]': mpf('1.43876032359725990511334321483131'), 'betaU[0]': mpf('0.696603667756184763426574590994278'), 'betaU[1]': mpf('1.46247146117037299492483621025343'), 'betaU[2]': mpf('4.6241634365771266519514488292252'), 'BU[0]': mpf('0.0391630274878592699039359104062896'), 'BU[1]': mpf('0.167690457454195833796083765751675'), 'BU[2]': mpf('1.14424155600667335640923075733714')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_quantities__gammabar__inverse_and_derivs__globals'] = {'gammabarUU[0][0]': mpf('-55.3913683921929939892608290454462'), 'gammabarUU[0][1]': mpf('160.581139770070431806627488067463'), 'gammabarUU[0][2]': mpf('-200.743331495864219543840762334583'), 'gammabarUU[1][0]': mpf('160.581139770070431806627488067463'), 'gammabarUU[1][1]': mpf('-457.230909570455507522618494885285'), 'gammabarUU[1][2]': mpf('570.320147944349793491547665601545'), 'gammabarUU[2][0]': mpf('-200.743331495864219543840762334583'), 'gammabarUU[2][1]': mpf('570.320147944349793491547665601545'), 'gammabarUU[2][2]': mpf('-691.177266096096440977423920582226'), 'gammabarDD_dD[0][0][0]': mpf('0.113376527866793974652637189137749'), 'gammabarDD_dD[0][0][1]': mpf('0.597031746016325581827288715430768'), 'gammabarDD_dD[0][0][2]': mpf('0.0326900992587932925914628867758438'), 'gammabarDD_dD[0][1][0]': mpf('1.29509849818179299105146594183329'), 'gammabarDD_dD[0][1][1]': mpf('0.29966757015887334440305520963308'), 'gammabarDD_dD[0][1][2]': mpf('0.408456984468846210692212242298237'), 'gammabarDD_dD[0][2][0]': mpf('0.0757914276030065310563752344719272'), 'gammabarDD_dD[0][2][1]': mpf('0.211213637778013737361842492406609'), 'gammabarDD_dD[0][2][2]': mpf('0.043576970886389692140854474231034'), 'gammabarDD_dD[1][0][0]': mpf('1.29509849818179299105146594183329'), 'gammabarDD_dD[1][0][1]': mpf('0.29966757015887334440305520963308'), 'gammabarDD_dD[1][0][2]': mpf('0.408456984468846210692212242298237'), 'gammabarDD_dD[1][1][0]': mpf('1.11081293166050470517099765479562'), 'gammabarDD_dD[1][1][1]': mpf('0.162456880553023991817285210787008'), 'gammabarDD_dD[1][1][2]': mpf('0.158500072577490769771706912461303'), 'gammabarDD_dD[1][2][0]': mpf('0.359283025128967082047900286877992'), 'gammabarDD_dD[1][2][1]': mpf('0.199267454573993880968860334987007'), 'gammabarDD_dD[1][2][2]': mpf('0.0222668035023729125133168779194776'), 'gammabarDD_dD[2][0][0]': mpf('0.0757914276030065310563752344719272'), 'gammabarDD_dD[2][0][1]': mpf('0.211213637778013737361842492406609'), 'gammabarDD_dD[2][0][2]': mpf('0.043576970886389692140854474231034'), 'gammabarDD_dD[2][1][0]': mpf('0.359283025128967082047900286877992'), 'gammabarDD_dD[2][1][1]': mpf('0.199267454573993880968860334987007'), 'gammabarDD_dD[2][1][2]': mpf('0.0222668035023729125133168779194776'), 'gammabarDD_dD[2][2][0]': mpf('0.249364796420989096924555910811589'), 'gammabarDD_dD[2][2][1]': mpf('0.229090281001197346827970614026702'), 'gammabarDD_dD[2][2][2]': mpf('0.0293354706024628609686096695909266'), 'gammabarDD_dupD[0][0][0]': mpf('0.37728180260573840332227746330318'), 'gammabarDD_dupD[0][0][1]': mpf('0.670267766719132906949596417689463'), 'gammabarDD_dupD[0][0][2]': mpf('0.781414536731840470018539690499892'), 'gammabarDD_dupD[0][1][0]': mpf('1.27559379975272638197786370234481'), 'gammabarDD_dupD[0][1][1]': mpf('0.270519930552051576420169894439394'), 'gammabarDD_dupD[0][1][2]': mpf('0.126974122612752350149982986405959'), 'gammabarDD_dupD[0][2][0]': mpf('0.160014994496667253968983142186692'), 'gammabarDD_dupD[0][2][1]': mpf('0.159417776511127717981289959174267'), 'gammabarDD_dupD[0][2][2]': mpf('0.0914222006910319288079363366980038'), 'gammabarDD_dupD[1][0][0]': mpf('1.27559379975272638197786370234481'), 'gammabarDD_dupD[1][0][1]': mpf('0.270519930552051576420169894439394'), 'gammabarDD_dupD[1][0][2]': mpf('0.126974122612752350149982986405959'), 'gammabarDD_dupD[1][1][0]': mpf('1.11565554836934532567070034274662'), 'gammabarDD_dupD[1][1][1]': mpf('0.0225054491023032484277595044174296'), 'gammabarDD_dupD[1][1][2]': mpf('0.148236212186685765996028318648501'), 'gammabarDD_dupD[1][2][0]': mpf('0.38244111267429014279462120452751'), 'gammabarDD_dupD[1][2][1]': mpf('0.19605291006032265256196166097098'), 'gammabarDD_dupD[1][2][2]': mpf('0.000359393084847625499631216495471881'), 'gammabarDD_dupD[2][0][0]': mpf('0.160014994496667253968983142186692'), 'gammabarDD_dupD[2][0][1]': mpf('0.159417776511127717981289959174267'), 'gammabarDD_dupD[2][0][2]': mpf('0.0914222006910319288079363366980038'), 'gammabarDD_dupD[2][1][0]': mpf('0.38244111267429014279462120452751'), 'gammabarDD_dupD[2][1][1]': mpf('0.19605291006032265256196166097098'), 'gammabarDD_dupD[2][1][2]': mpf('0.000359393084847625499631216495471881'), 'gammabarDD_dupD[2][2][0]': mpf('0.255124908396158921845552323395099'), 'gammabarDD_dupD[2][2][1]': mpf('0.210716113114337146067597109416175'), 'gammabarDD_dupD[2][2][2]': mpf('0.01339819778908501331879521055357'), 'gammabarDD_dDD[0][0][0][0]': mpf('0.185845801018537204463143552857218'), 'gammabarDD_dDD[0][0][0][1]': mpf('0.41265964722127601582712941308273'), 'gammabarDD_dDD[0][0][0][2]': mpf('0.37985477211695328758622736131656'), 'gammabarDD_dDD[0][0][1][0]': mpf('0.41265964722127601582712941308273'), 'gammabarDD_dDD[0][0][1][1]': mpf('0.689211123606880748937442149326671'), 'gammabarDD_dDD[0][0][1][2]': mpf('0.0048337295380591616122956111212261'), 'gammabarDD_dDD[0][0][2][0]': mpf('0.37985477211695328758622736131656'), 'gammabarDD_dDD[0][0][2][1]': mpf('0.0048337295380591616122956111212261'), 'gammabarDD_dDD[0][0][2][2]': mpf('0.538905401817390661278750485507771'), 'gammabarDD_dDD[0][1][0][0]': mpf('2.17695434404486827829573669497839'), 'gammabarDD_dDD[0][1][0][1]': mpf('0.737845818275736881995111390979058'), 'gammabarDD_dDD[0][1][0][2]': mpf('1.27821731904689736052853767370668'), 'gammabarDD_dDD[0][1][1][0]': mpf('0.737845818275736881995111390979058'), 'gammabarDD_dDD[0][1][1][1]': mpf('0.0572033349261599444767919389310414'), 'gammabarDD_dDD[0][1][1][2]': mpf('0.114449372510356573992423216972657'), 'gammabarDD_dDD[0][1][2][0]': mpf('1.27821731904689736052853767370668'), 'gammabarDD_dDD[0][1][2][1]': mpf('0.114449372510356573992423216972657'), 'gammabarDD_dDD[0][1][2][2]': mpf('0.336172859503064373361339781168181'), 'gammabarDD_dDD[0][2][0][0]': mpf('0.149593239036693143279810874442325'), 'gammabarDD_dDD[0][2][0][1]': mpf('0.670365627630913964987374237749722'), 'gammabarDD_dDD[0][2][0][2]': mpf('0.178979216891036880951181191336912'), 'gammabarDD_dDD[0][2][1][0]': mpf('0.670365627630913964987374237749722'), 'gammabarDD_dDD[0][2][1][1]': mpf('0.659480314934221443474116011063672'), 'gammabarDD_dDD[0][2][1][2]': mpf('0.211927554805221158936564646974544'), 'gammabarDD_dDD[0][2][2][0]': mpf('0.178979216891036880951181191336912'), 'gammabarDD_dDD[0][2][2][1]': mpf('0.211927554805221158936564646974544'), 'gammabarDD_dDD[0][2][2][2]': mpf('0.0131768891907001501822079393602128'), 'gammabarDD_dDD[1][0][0][0]': mpf('2.17695434404486827829573669497839'), 'gammabarDD_dDD[1][0][0][1]': mpf('0.737845818275736881995111390979058'), 'gammabarDD_dDD[1][0][0][2]': mpf('1.27821731904689736052853767370668'), 'gammabarDD_dDD[1][0][1][0]': mpf('0.737845818275736881995111390979058'), 'gammabarDD_dDD[1][0][1][1]': mpf('0.0572033349261599444767919389310414'), 'gammabarDD_dDD[1][0][1][2]': mpf('0.114449372510356573992423216972657'), 'gammabarDD_dDD[1][0][2][0]': mpf('1.27821731904689736052853767370668'), 'gammabarDD_dDD[1][0][2][1]': mpf('0.114449372510356573992423216972657'), 'gammabarDD_dDD[1][0][2][2]': mpf('0.336172859503064373361339781168181'), 'gammabarDD_dDD[1][1][0][0]': mpf('3.2125897978005476278879035565663'), 'gammabarDD_dDD[1][1][0][1]': mpf('0.89598315374067949012654219762452'), 'gammabarDD_dDD[1][1][0][2]': mpf('0.820646830703092166810003596680962'), 'gammabarDD_dDD[1][1][1][0]': mpf('0.89598315374067949012654219762452'), 'gammabarDD_dDD[1][1][1][1]': mpf('0.168996792900712596062122767009282'), 'gammabarDD_dDD[1][1][1][2]': mpf('0.0473971971696919594315542625878797'), 'gammabarDD_dDD[1][1][2][0]': mpf('0.820646830703092166810003596680962'), 'gammabarDD_dDD[1][1][2][1]': mpf('0.0473971971696919594315542625878797'), 'gammabarDD_dDD[1][1][2][2]': mpf('0.12968746168617386620955233939796'), 'gammabarDD_dDD[1][2][0][0]': mpf('1.04180796862731772248736459987955'), 'gammabarDD_dDD[1][2][0][1]': mpf('1.02777187626693600280637544109045'), 'gammabarDD_dDD[1][2][0][2]': mpf('0.138815878772543536594426398853965'), 'gammabarDD_dDD[1][2][1][0]': mpf('1.02777187626693600280637544109045'), 'gammabarDD_dDD[1][2][1][1]': mpf('0.205354158028681326149566579067253'), 'gammabarDD_dDD[1][2][1][2]': mpf('0.051702758215965343443390702033079'), 'gammabarDD_dDD[1][2][2][0]': mpf('0.138815878772543536594426398853965'), 'gammabarDD_dDD[1][2][2][1]': mpf('0.051702758215965343443390702033079'), 'gammabarDD_dDD[1][2][2][2]': mpf('0.0737488709653608336887200438662802'), 'gammabarDD_dDD[2][0][0][0]': mpf('0.149593239036693143279810874442325'), 'gammabarDD_dDD[2][0][0][1]': mpf('0.670365627630913964987374237749722'), 'gammabarDD_dDD[2][0][0][2]': mpf('0.178979216891036880951181191336912'), 'gammabarDD_dDD[2][0][1][0]': mpf('0.670365627630913964987374237749722'), 'gammabarDD_dDD[2][0][1][1]': mpf('0.659480314934221443474116011063672'), 'gammabarDD_dDD[2][0][1][2]': mpf('0.211927554805221158936564646974544'), 'gammabarDD_dDD[2][0][2][0]': mpf('0.178979216891036880951181191336912'), 'gammabarDD_dDD[2][0][2][1]': mpf('0.211927554805221158936564646974544'), 'gammabarDD_dDD[2][0][2][2]': mpf('0.0131768891907001501822079393602128'), 'gammabarDD_dDD[2][1][0][0]': mpf('1.04180796862731772248736459987955'), 'gammabarDD_dDD[2][1][0][1]': mpf('1.02777187626693600280637544109045'), 'gammabarDD_dDD[2][1][0][2]': mpf('0.138815878772543536594426398853965'), 'gammabarDD_dDD[2][1][1][0]': mpf('1.02777187626693600280637544109045'), 'gammabarDD_dDD[2][1][1][1]': mpf('0.205354158028681326149566579067253'), 'gammabarDD_dDD[2][1][1][2]': mpf('0.051702758215965343443390702033079'), 'gammabarDD_dDD[2][1][2][0]': mpf('0.138815878772543536594426398853965'), 'gammabarDD_dDD[2][1][2][1]': mpf('0.051702758215965343443390702033079'), 'gammabarDD_dDD[2][1][2][2]': mpf('0.0737488709653608336887200438662802'), 'gammabarDD_dDD[2][2][0][0]': mpf('0.66508879899869765324495129089009'), 'gammabarDD_dDD[2][2][0][1]': mpf('1.15812919014671874295450095368265'), 'gammabarDD_dDD[2][2][0][2]': mpf('0.165896787206516112586624167221097'), 'gammabarDD_dDD[2][2][1][0]': mpf('1.15812919014671874295450095368285'), 'gammabarDD_dDD[2][2][1][1]': mpf('0.533174868302490566724214906488119'), 'gammabarDD_dDD[2][2][1][2]': mpf('0.131976505361826978863691234258533'), 'gammabarDD_dDD[2][2][2][0]': mpf('0.165896787206516112586624167221097'), 'gammabarDD_dDD[2][2][2][1]': mpf('0.131976505361826978863691234258533'), 'gammabarDD_dDD[2][2][2][2]': mpf('0.010262022536939967495395958633956'), 'GammabarUDD[0][0][0]': mpf('144.958869354596952650223295197696'), 'GammabarUDD[0][0][1]': mpf('56.3884081838815055298106227619069'), 'GammabarUDD[0][0][2]': mpf('18.7492848883834673075437947835502'), 'GammabarUDD[0][1][0]': mpf('56.3884081838815055298106227619069'), 'GammabarUDD[0][1][1]': mpf('3.11678651957992149533502697995063'), 'GammabarUDD[0][1][2]': mpf('-17.4797246150434126590734188209953'), 'GammabarUDD[0][2][0]': mpf('18.7492848883834673075437947835502'), 'GammabarUDD[0][2][1]': mpf('-17.4797246150434126590734188209953'), 'GammabarUDD[0][2][2]': mpf('-13.2700699776147156200210831819353'), 'GammabarUDD[1][0][0]': mpf('-412.661847055574875860384479574194'), 'GammabarUDD[1][0][1]': mpf('-159.805737735924117067707092684796'), 'GammabarUDD[1][0][2]': mpf('-53.4969391010037688000184007256669'), 'GammabarUDD[1][1][0]': mpf('-159.805737735924117067707092684796'), 'GammabarUDD[1][1][1]': mpf('-9.75864522872634062530478027925608'), 'GammabarUDD[1][1][2]': mpf('49.998503864403681839928229922025'), 'GammabarUDD[1][2][0]': mpf('-53.4969391010037688000184007256669'), 'GammabarUDD[1][2][1]': mpf('49.998503864403681839928229922025'), 'GammabarUDD[1][2][2]': mpf('37.5338109651112817021709810969423'), 'GammabarUDD[2][0][0]': mpf('515.903374261971431760645349581284'), 'GammabarUDD[2][0][1]': mpf('200.835355938522818161227492367693'), 'GammabarUDD[2][0][2]': mpf('69.2403025140953793071085058377914'), 'GammabarUDD[2][1][0]': mpf('200.835355938522818161227492367693'), 'GammabarUDD[2][1][1]': mpf('14.7107829316252639239815376245426'), 'GammabarUDD[2][1][2]': mpf('-60.1086414978290381532119255024356'), 'GammabarUDD[2][2][0]': mpf('69.2403025140953793071085058377914'), 'GammabarUDD[2][2][1]': mpf('-60.1086414978290381532119255024356'), 'GammabarUDD[2][2][2]': mpf('-46.4848263107765220270562375920812')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_quantities__detgammabar_and_derivs__globals'] = {'detgammabar': mpf('0.00573756266880972544377979111558305'), 'detgammabar_dD[0]': mpf('0.0557212146869903791885764536246544'), 'detgammabar_dD[1]': mpf('0.0229955254712656127315158404877634'), 'detgammabar_dD[2]': mpf('0.0'), 'detgammabar_dDD[0][0]': mpf('0.405858804349815958396163363914466'), 'detgammabar_dDD[0][1]': mpf('0.223324551832801915665195788045795'), 'detgammabar_dDD[0][2]': mpf('0.0'), 'detgammabar_dDD[1][0]': mpf('0.223324551832801915665195788045795'), 'detgammabar_dDD[1][1]': mpf('0.0346066538274459059495911839041824'), 'detgammabar_dDD[1][2]': mpf('0.0'), 'detgammabar_dDD[2][0]': mpf('0.0'), 'detgammabar_dDD[2][1]': mpf('0.0'), 'detgammabar_dDD[2][2]': mpf('0.0')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_quantities__AbarUU_AbarUD_trAbar_AbarDD_dD__globals'] = {'AbarUU[0][0]': mpf('-4501.35489903001232813737257379218'), 'AbarUU[0][1]': mpf('12891.1260926920183373060745066027'), 'AbarUU[0][2]': mpf('-16048.8656589832488120714141642617'), 'AbarUU[1][0]': mpf('12891.1260926920183373060745066027'), 'AbarUU[1][1]': mpf('-36915.8909445128919893699783581937'), 'AbarUU[1][2]': mpf('45960.7552497439495397460051934269'), 'AbarUU[2][0]': mpf('-16048.8656589832488120714141642617'), 'AbarUU[2][1]': mpf('45960.7552497439495397460051934269'), 'AbarUU[2][2]': mpf('-57214.0298282929626166132996415947'), 'AbarUD[0][0]': mpf('26.8512020837293036423215049007785'), 'AbarUD[0][1]': mpf('-16.1315355594965963827373776014512'), 'AbarUD[0][2]': mpf('2.11020561975821049709560254567513'), 'AbarUD[1][0]': mpf('-76.096867038514101536679331070774'), 'AbarUD[1][1]': mpf('46.6295210174394985755149432015637'), 'AbarUD[1][2]': mpf('-5.91897552114475146319310921096151'), 'AbarUD[2][0]': mpf('95.6660051214531525255013492129713'), 'AbarUD[2][1]': mpf('-57.2290892443011388753856943624776'), 'AbarUD[2][2]': mpf('7.77053133578485609602163485965202'), 'trAbar': mpf('81.251254436953658313858082961098'), 'AbarDD_dD[0][0][0]': mpf('0.33721198437911092948837676885887'), 'AbarDD_dD[0][0][1]': mpf('0.679909473427153132618627751071472'), 'AbarDD_dD[0][0][2]': mpf('0.327242304499680503049319213459967'), 'AbarDD_dD[0][1][0]': mpf('0.733454489269375682720410108340373'), 'AbarDD_dD[0][1][1]': mpf('0.0564054522687123879076101957745309'), 'AbarDD_dD[0][1][2]': mpf('0.288952399746149364416592105125356'), 'AbarDD_dD[0][2][0]': mpf('0.259134566883295043505905621463171'), 'AbarDD_dD[0][2][1]': mpf('0.18566392834089806327031028199579'), 'AbarDD_dD[0][2][2]': mpf('0.10947288045690455436180777612326'), 'AbarDD_dD[1][0][0]': mpf('0.733454489269375682720410108340373'), 'AbarDD_dD[1][0][1]': mpf('0.0564054522687123879076101957745309'), 'AbarDD_dD[1][0][2]': mpf('0.288952399746149364416592105125356'), 'AbarDD_dD[1][1][0]': mpf('0.300852467812027766391019475297803'), 'AbarDD_dD[1][1][1]': mpf('0.0145399155791957464680340380309724'), 'AbarDD_dD[1][1][2]': mpf('0.0395378448600819201304333750185868'), 'AbarDD_dD[1][2][0]': mpf('0.300398363998209101604122555846344'), 'AbarDD_dD[1][2][1]': mpf('0.119537195366846939854213812053404'), 'AbarDD_dD[1][2][2]': mpf('0.04721114477346884448153824722088'), 'AbarDD_dD[2][0][0]': mpf('0.259134566883295043505905621463171'), 'AbarDD_dD[2][0][1]': mpf('0.18566392834089806327031028199579'), 'AbarDD_dD[2][0][2]': mpf('0.10947288045690455436180777612326'), 'AbarDD_dD[2][1][0]': mpf('0.300398363998209101604122555846344'), 'AbarDD_dD[2][1][1]': mpf('0.119537195366846939854213812053404'), 'AbarDD_dD[2][1][2]': mpf('0.04721114477346884448153824722088'), 'AbarDD_dD[2][2][0]': mpf('0.128416701185305713723338911303584'), 'AbarDD_dD[2][2][1]': mpf('0.091211183233673576891878621321523'), 'AbarDD_dD[2][2][2]': mpf('0.0124213262600154053715160803941519'), 'AbarDD_dupD[0][0][0]': mpf('0.769930872140056510311012516467599'), 'AbarDD_dupD[0][0][1]': mpf('0.406856967532203017690051183308242'), 'AbarDD_dupD[0][0][2]': mpf('0.034573833670986209298803260026034'), 'AbarDD_dupD[0][1][0]': mpf('0.799078538941273777740809109133389'), 'AbarDD_dupD[0][1][1]': mpf('0.111116568306179455959424903906438'), 'AbarDD_dupD[0][1][2]': mpf('0.377511909918297386621098531539175'), 'AbarDD_dupD[0][2][0]': mpf('0.214500608396807435491964973223518'), 'AbarDD_dupD[0][2][1]': mpf('0.115344958017771695940477015182743'), 'AbarDD_dupD[0][2][2]': mpf('0.122244734709826544219416183615818'), 'AbarDD_dupD[1][0][0]': mpf('0.799078538941273777740809109133389'), 'AbarDD_dupD[1][0][1]': mpf('0.111116568306179455959424903906438'), 'AbarDD_dupD[1][0][2]': mpf('0.377511909918297386621098531539175'), 'AbarDD_dupD[1][1][0]': mpf('0.435501861817190390787863358691289'), 'AbarDD_dupD[1][1][1]': mpf('0.116623980226106608051172387504808'), 'AbarDD_dupD[1][1][2]': mpf('0.0029262421496022085618519737385143'), 'AbarDD_dupD[1][2][0]': mpf('0.319628992953124375855747562860006'), 'AbarDD_dupD[1][2][1]': mpf('0.154649167361595245816006653565866'), 'AbarDD_dupD[1][2][2]': mpf('0.0691883280846439197601862955001518'), 'AbarDD_dupD[2][0][0]': mpf('0.214500608396807435491964973223518'), 'AbarDD_dupD[2][0][1]': mpf('0.115344958017771695940477015182743'), 'AbarDD_dupD[2][0][2]': mpf('0.122244734709826544219416183615818'), 'AbarDD_dupD[2][1][0]': mpf('0.319628992953124375855747562860006'), 'AbarDD_dupD[2][1][1]': mpf('0.154649167361595245816006653565866'), 'AbarDD_dupD[2][1][2]': mpf('0.0691883280846439197601862955001518'), 'AbarDD_dupD[2][2][0]': mpf('0.102626201407641450303174373971461'), 'AbarDD_dupD[2][2][1]': mpf('0.102313858934520263961250056659667'), 'AbarDD_dupD[2][2][2]': mpf('0.00853496939586718772700035856415055')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_quantities__RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU__globals'] = {'RbarDD[0][0]': mpf('-41245.2193607369706612392062712087'), 'RbarDD[0][1]': mpf('-13279.8476666719541384124392490902'), 'RbarDD[0][2]': mpf('-13136.4852727140129778266912775021'), 'RbarDD[1][0]': mpf('-13279.8476666719541384124392490142'), 'RbarDD[1][1]': mpf('17572.3339460500557245822009043625'), 'RbarDD[1][2]': mpf('-4983.05391806650925004378740779'), 'RbarDD[2][0]': mpf('-13136.485272714012977826691277452'), 'RbarDD[2][1]': mpf('-4983.05391806650925004378740779404'), 'RbarDD[2][2]': mpf('-5521.17554758589382532917232982535'), 'DGammaUDD[0][0][0]': mpf('144.958869354596952650223295197696'), 'DGammaUDD[0][0][1]': mpf('56.3884081838815055298106227619069'), 'DGammaUDD[0][0][2]': mpf('18.7492848883834673075437947835502'), 'DGammaUDD[0][1][0]': mpf('56.3884081838815055298106227619069'), 'DGammaUDD[0][1][1]': mpf('3.52866287231487465035648484008052'), 'DGammaUDD[0][1][2]': mpf('-17.4797246150434126590734188209953'), 'DGammaUDD[0][2][0]': mpf('18.7492848883834673075437947835502'), 'DGammaUDD[0][2][1]': mpf('-17.4797246150434126590734188209953'), 'DGammaUDD[0][2][2]': mpf('-13.1879541513368329697779753752397'), 'DGammaUDD[1][0][0]': mpf('-412.661847055574875860384479574194'), 'DGammaUDD[1][0][1]': mpf('-162.233650854411671297209410338331'), 'DGammaUDD[1][0][2]': mpf('-53.4969391010037688000184007256669'), 'DGammaUDD[1][1][0]': mpf('-162.233650854411671297209410338331'), 'DGammaUDD[1][1][1]': mpf('-9.75864522872634062530478027925608'), 'DGammaUDD[1][1][2]': mpf('49.998503864403681839928229922025'), 'DGammaUDD[1][2][0]': mpf('-53.4969391010037688000184007256669'), 'DGammaUDD[1][2][1]': mpf('49.998503864403681839928229922025'), 'DGammaUDD[1][2][2]': mpf('37.9333377581165059513560056676581'), 'DGammaUDD[2][0][0]': mpf('515.903374261971431760645349581284'), 'DGammaUDD[2][0][1]': mpf('200.835355938522818161227492367693'), 'DGammaUDD[2][0][2]': mpf('66.8123893956078250776061881842562'), 'DGammaUDD[2][1][0]': mpf('200.835355938522818161227492367693'), 'DGammaUDD[2][1][1]': mpf('14.7107829316252639239815376245426'), 'DGammaUDD[2][1][2]': mpf('-62.1125869707275593077009803716383'), 'DGammaUDD[2][2][0]': mpf('66.8123893956078250776061881842562'), 'DGammaUDD[2][2][1]': mpf('-62.1125869707275593077009803716383'), 'DGammaUDD[2][2][2]': mpf('-46.4848263107765220270562375920812'), 'gammabarDD_dHatD[0][0][0]': mpf('0.113376527866793974652637189137749'), 'gammabarDD_dHatD[0][0][1]': mpf('-1.25000781798226590080247433434124'), 'gammabarDD_dHatD[0][0][2]': mpf('-0.0747109401340281274447427294117478'), 'gammabarDD_dHatD[0][1][0]': mpf('0.371578716182497249736584416947285'), 'gammabarDD_dHatD[0][1][1]': mpf('0.182029291952476074147915289630397'), 'gammabarDD_dHatD[0][1][2]': mpf('0.193899915867061900813055159582757'), 'gammabarDD_dHatD[0][2][0]': mpf('0.0220909079065958210382724263781314'), 'gammabarDD_dHatD[0][2][1]': mpf('-0.00334343082377057251731459030886539'), 'gammabarDD_dHatD[0][2][2]': mpf('0.156675546280700493851442555008333'), 'gammabarDD_dHatD[1][0][0]': mpf('0.371578716182497249736584416947285'), 'gammabarDD_dHatD[1][0][1]': mpf('0.182029291952476074147915289630397'), 'gammabarDD_dHatD[1][0][2]': mpf('0.193899915867061900813055159582757'), 'gammabarDD_dHatD[1][1][0]': mpf('0.0480687558528761896352673763644066'), 'gammabarDD_dHatD[1][1][1]': mpf('0.475792606277345313962293654458942'), 'gammabarDD_dHatD[1][1][2]': mpf('-0.122514399611870464853979699604412'), 'gammabarDD_dHatD[1][2][0]': mpf('0.0188153164310087189522141894729995'), 'gammabarDD_dHatD[1][2][1]': mpf('0.0678700890196179934569006598920808'), 'gammabarDD_dHatD[1][2][2]': mpf('0.0407758029860071575845246674537359'), 'gammabarDD_dHatD[2][0][0]': mpf('0.0220909079065958210382724263781314'), 'gammabarDD_dHatD[2][0][1]': mpf('-0.00334343082377057251731459030886539'), 'gammabarDD_dHatD[2][0][2]': mpf('0.156675546280700493851442555008333'), 'gammabarDD_dHatD[2][1][0]': mpf('0.0188153164310087189522141894729995'), 'gammabarDD_dHatD[2][1][1]': mpf('0.0678700890196179934569006598920808'), 'gammabarDD_dHatD[2][1][2]': mpf('0.0407758029860071575845246674537359'), 'gammabarDD_dHatD[2][2][0]': mpf('0.00664887746198699878899292414052604'), 'gammabarDD_dHatD[2][2][1]': mpf('0.0287579613252410813496304259029699'), 'gammabarDD_dHatD[2][2][2]': mpf('0.0889938231884100782604806662221182'), 'DGammaU[0]': mpf('-9883.50614138972832359869160635136'), 'DGammaU[1]': mpf('27506.4846155065370543113398270077'), 'DGammaU[2]': mpf('-36345.2254745846641825231815918191')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_quantities__betaU_derivs__globals'] = {'betaU_dD[0][0]': mpf('0.192243539484738046319023396790726'), 'betaU_dD[0][1]': mpf('0.754980728051157679026061941840453'), 'betaU_dD[0][2]': mpf('0.586123078895915305963626451557502'), 'betaU_dD[1][0]': mpf('-3.18830793589382952185971488180311'), 'betaU_dD[1][1]': mpf('0.922655491152517150129828380218527'), 'betaU_dD[1][2]': mpf('2.402141784188005363245692498957'), 'betaU_dD[2][0]': mpf('-8.9043744551685352332500234492703'), 'betaU_dD[2][1]': mpf('-4.62598533129686961830418682100716'), 'betaU_dD[2][2]': mpf('3.76687640853700760943325487483147'), 'betaU_dupD[0][0]': mpf('0.659472189904469763277461424877401'), 'betaU_dupD[0][1]': mpf('0.948628367838908470588421550928615'), 'betaU_dupD[0][2]': mpf('0.669133180747105038577160485147033'), 'betaU_dupD[1][0]': mpf('-1.60394693865713402126230258890783'), 'betaU_dupD[1][1]': mpf('1.41044106421919101151961432862351'), 'betaU_dupD[1][2]': mpf('0.406302118865459317620854482607423'), 'betaU_dupD[2][0]': mpf('-6.34070058470575134851313539411892'), 'betaU_dupD[2][1]': mpf('-8.20689620375936786515281493126901'), 'betaU_dupD[2][2]': mpf('3.95742743877129970876759452390656'), 'betaU_dDD[0][0][0]': mpf('0.693440743164982609236801636143355'), 'betaU_dDD[0][0][1]': mpf('0.474644061200962252833335242030444'), 'betaU_dDD[0][0][2]': mpf('0.248757440072191138291657352965558'), 'betaU_dDD[0][1][0]': mpf('0.474644061200962252833335242030444'), 'betaU_dDD[0][1][1]': mpf('0.0806331535826826373281051019148435'), 'betaU_dDD[0][1][2]': mpf('0.906200949230643648668603873375105'), 'betaU_dDD[0][2][0]': mpf('0.248757440072191138291657352965558'), 'betaU_dDD[0][2][1]': mpf('0.906200949230643648668603873375105'), 'betaU_dDD[0][2][2]': mpf('0.256446105879290064955000616464531'), 'betaU_dDD[1][0][0]': mpf('17.6480807195419839436961496336133'), 'betaU_dDD[1][0][1]': mpf('-0.521955208304153160154395471306198'), 'betaU_dDD[1][0][2]': mpf('-3.59469879181060009166143907294526'), 'betaU_dDD[1][1][0]': mpf('-0.521955208304153160154395471306198'), 'betaU_dDD[1][1][1]': mpf('0.390333580442849126669804861962297'), 'betaU_dDD[1][1][2]': mpf('1.03879140080831086038810101219714'), 'betaU_dDD[1][2][0]': mpf('-3.59469879181060009166143907294526'), 'betaU_dDD[1][2][1]': mpf('1.03879140080831086038810101219714'), 'betaU_dDD[1][2][2]': mpf('0.429021139775372473874225378432791'), 'betaU_dDD[2][0][0]': mpf('47.7279356778735564316328362884232'), 'betaU_dDD[2][0][1]': mpf('11.3305777369963646737013674981541'), 'betaU_dDD[2][0][2]': mpf('-9.09922406127764796777495296898437'), 'betaU_dDD[2][1][0]': mpf('11.3305777369963646737013674981541'), 'betaU_dDD[2][1][1]': mpf('24.3353794441474582057173283509946'), 'betaU_dDD[2][1][2]': mpf('-3.95193536311042053355897439286505'), 'betaU_dDD[2][2][0]': mpf('-9.09922406127764796777495296898437'), 'betaU_dDD[2][2][1]': mpf('-3.95193536311042053355897439286505'), 'betaU_dDD[2][2][2]': mpf('0.749372685256775991946308530989456')}
# Generated on: 2019-08-09
trusted_values_dict['BSSN_quantities__phi_and_derivs__globals'] = {'phi_dD[0]': mpf('-1.13647037353523967950954390927125'), 'phi_dD[1]': mpf('-1.37882238967032079487109408639122'), 'phi_dD[2]': mpf('-1.23865565471203051564572756374677'), 'phi_dupD[0]': mpf('-1.30437304716054772824033366627827'), 'phi_dupD[1]': mpf('-0.387439729438011521402787682064875'), 'phi_dupD[2]': mpf('-1.286536248178407847990777607347'), 'phi_dDD[0][0]': mpf('1.41350298031869520561660429447829'), 'phi_dDD[0][1]': mpf('2.60287661327406578686593533352924'), 'phi_dDD[0][2]': mpf('2.40978752502737233244962940835342'), 'phi_dDD[1][0]': mpf('2.60287661327406578686593533352924'), 'phi_dDD[1][1]': mpf('3.41285382262798172893551850424285'), 'phi_dDD[1][2]': mpf('3.0612083554957324225454264680395'), 'phi_dDD[2][0]': mpf('2.40978752502737233244962940835342'), 'phi_dDD[2][1]': mpf('3.0612083554957324225454264680395'), 'phi_dDD[2][2]': mpf('2.10516420696710843004822415147254'), 'exp_m4phi': mpf('0.113544429071223891132192393151795'), 'phi_dBarD[0]': mpf('-1.13647037353523967950954390927125'), 'phi_dBarD[1]': mpf('-1.37882238967032079487109408639122'), 'phi_dBarD[2]': mpf('-1.23865565471203051564572756374677'), 'phi_dBarDD[0][0]': mpf('236.194201114655255326559793477184'), 'phi_dBarDD[0][1]': mpf('95.1087520363466622986255911257178'), 'phi_dBarDD[0][2]': mpf('35.7199091574093810655803917372351'), 'phi_dBarDD[1][0]': mpf('95.1087520363466622986255911257178'), 'phi_dBarDD[1][1]': mpf('11.7211452920461024221333792078154'), 'phi_dBarDD[1][2]': mpf('-22.3188329171420787008183405055411'), 'phi_dBarDD[2][0]': mpf('35.7199091574093810655803917372351'), 'phi_dBarDD[2][1]': mpf('-22.3188329171420787008183405055411'), 'phi_dBarDD[2][2]': mpf('-18.8021112171325449817326637076808')}
# Generated on: 2019-09-03
trusted_values_dict['BrillLindquist__BrillLindquist__ComputeADMGlobalsOnly_True__globals'] = {'alphaCart': mpf('0.343480750893445797669295337903236'), 'betaCartU[0]': mpf('0.0'), 'betaCartU[1]': mpf('0.0'), 'betaCartU[2]': mpf('0.0'), 'BCartU[0]': mpf('0.0'), 'BCartU[1]': mpf('0.0'), 'BCartU[2]': mpf('0.0'), 'gammaCartDD[0][0]': mpf('8.47608284216415538664572854966934'), 'gammaCartDD[0][1]': mpf('0.0'), 'gammaCartDD[0][2]': mpf('0.0'), 'gammaCartDD[1][0]': mpf('0.0'), 'gammaCartDD[1][1]': mpf('8.47608284216415538664572854966934'), 'gammaCartDD[1][2]': mpf('0.0'), 'gammaCartDD[2][0]': mpf('0.0'), 'gammaCartDD[2][1]': mpf('0.0'), 'gammaCartDD[2][2]': mpf('8.47608284216415538664572854966934'), 'KCartDD[0][0]': mpf('0.0'), 'KCartDD[0][1]': mpf('0.0'), 'KCartDD[0][2]': mpf('0.0'), 'KCartDD[1][0]': mpf('0.0'), 'KCartDD[1][1]': mpf('0.0'), 'KCartDD[1][2]': mpf('0.0'), 'KCartDD[2][0]': mpf('0.0'), 'KCartDD[2][1]': mpf('0.0'), 'KCartDD[2][2]': mpf('0.0')}
# Generated on: 2019-09-03
trusted_values_dict['Psi4__Psi4__specify_tetrad_False__globals'] = {'psi4_re_pt[0]': mpf('-11.4627408385133346012203475168749'), 'psi4_re_pt[1]': mpf('-35.6806714906029645727316589574499'), 'psi4_re_pt[2]': mpf('-3374.92496104893019517395960329346'), 'psi4_im_pt[0]': mpf('7.17626585537563385139665194926662'), 'psi4_im_pt[1]': mpf('103.321768247585408574682983348206'), 'psi4_im_pt[2]': mpf('-1785.88265198482805325630868693525')}
# Generated on: 2019-09-03
trusted_values_dict['ShiftedKerrSchild__ShiftedKerrSchild__ComputeADMGlobalsOnly_True__globals'] = {'alphaSph': mpf('0.764992222074068488402374263959388'), 'betaSphU[0]': mpf('0.414786900166179080948286822502086'), 'betaSphU[1]': mpf('0.0'), 'betaSphU[2]': mpf('0.0'), 'BSphU[0]': mpf('0.0'), 'BSphU[1]': mpf('0.0'), 'BSphU[2]': mpf('0.0'), 'gammaSphDD[0][0]': mpf('1.70877924688282501671697638255698'), 'gammaSphDD[0][1]': mpf('0.0'), 'gammaSphDD[0][2]': mpf('-0.34407721284779690446451905546302'), 'gammaSphDD[1][0]': mpf('0.0'), 'gammaSphDD[1][1]': mpf('0.83749814889950038479162273698221'), 'gammaSphDD[1][2]': mpf('0.0'), 'gammaSphDD[2][0]': mpf('-0.34407721284779690446451905546302'), 'gammaSphDD[2][1]': mpf('0.0'), 'gammaSphDD[2][2]': mpf('0.428211355345928897234853289336263'), 'KSphDD[0][0]': mpf('-0.608416975836176206520434449342952'), 'KSphDD[0][1]': mpf('0.0707242913409545835190603089766082'), 'KSphDD[0][2]': mpf('0.0772829375209889643435925308431919'), 'KSphDD[1][0]': mpf('0.0707242913409545835190603089766082'), 'KSphDD[1][1]': mpf('0.457311525223526834585984744200641'), 'KSphDD[1][2]': mpf('-0.0142409366743087039035851689662884'), 'KSphDD[2][0]': mpf('0.0772829375209889643435925308431919'), 'KSphDD[2][1]': mpf('-0.0142409366743087039035851689662884'), 'KSphDD[2][2]': mpf('0.189536302720822065090385756720081')}
# Generated on: 2019-09-03
trusted_values_dict['StaticTrumpet__StaticTrumpet__ComputeADMGlobalsOnly_True__globals'] = {'alphaSph': mpf('0.47901157071628237535174768234896'), 'betaSphU[0]': mpf('0.249559485836202384477547321977731'), 'betaSphU[1]': mpf('0.0'), 'betaSphU[2]': mpf('0.0'), 'BSphU[0]': mpf('0.0'), 'BSphU[1]': mpf('0.0'), 'BSphU[2]': mpf('0.0'), 'gammaSphDD[0][0]': mpf('4.35820838377928180149222863568597'), 'gammaSphDD[0][1]': mpf('0.0'), 'gammaSphDD[0][2]': mpf('0.0'), 'gammaSphDD[1][0]': mpf('0.0'), 'gammaSphDD[1][1]': mpf('0.456231514782622374769913065861258'), 'gammaSphDD[1][2]': mpf('0.0'), 'gammaSphDD[2][0]': mpf('0.0'), 'gammaSphDD[2][1]': mpf('0.0'), 'gammaSphDD[2][2]': mpf('0.19552817796951759898685705719116'), 'KSphDD[0][0]': mpf('-3.36157982955715545881983739302993'), 'KSphDD[0][1]': mpf('0.0'), 'KSphDD[0][2]': mpf('0.0'), 'KSphDD[1][0]': mpf('0.0'), 'KSphDD[1][1]': mpf('0.351901176504010315682080545229837'), 'KSphDD[1][2]': mpf('0.0'), 'KSphDD[2][0]': mpf('0.0'), 'KSphDD[2][1]': mpf('0.0'), 'KSphDD[2][2]': mpf('0.150815087598546495698285048292878')}
# Generated on: 2019-09-03
trusted_values_dict['UIUCBlackHole__UIUCBlackHole__ComputeADMGlobalsOnly_True__globals'] = {'alphaSph': mpf('1.0'), 'betaSphU[0]': mpf('0.0'), 'betaSphU[1]': mpf('0.0'), 'betaSphU[2]': mpf('0.0'), 'BSphU[0]': mpf('0.0'), 'BSphU[1]': mpf('0.0'), 'BSphU[2]': mpf('0.0'), 'gammaSphDD[0][0]': mpf('5.88800397535091508054696345959799'), 'gammaSphDD[0][1]': mpf('0.0'), 'gammaSphDD[0][2]': mpf('0.0'), 'gammaSphDD[1][0]': mpf('0.0'), 'gammaSphDD[1][1]': mpf('0.49579570354855419371061261468577'), 'gammaSphDD[1][2]': mpf('0.0'), 'gammaSphDD[2][0]': mpf('0.0'), 'gammaSphDD[2][1]': mpf('0.0'), 'gammaSphDD[2][2]': mpf('0.239531368774223457427342816780148'), 'KSphDD[0][0]': mpf('0.0'), 'KSphDD[0][1]': mpf('0.0'), 'KSphDD[0][2]': mpf('0.516384223238703786717270771044344'), 'KSphDD[1][0]': mpf('0.0'), 'KSphDD[1][1]': mpf('0.0'), 'KSphDD[1][2]': mpf('-0.00263738331117854302766878470676251'), 'KSphDD[2][0]': mpf('0.516384223238703786717270771044344'), 'KSphDD[2][1]': mpf('-0.00263738331117854302766878470676251'), 'KSphDD[2][2]': mpf('0.0')}
# Generated on: 2019-10-08
trusted_values_dict['ADMBSSN_tofrom_4metric__g4DD_ito_BSSN_or_ADM___ADM___globals'] = {'g4DD[0][0]': mpf('1.42770464273047624140299713522594'), 'g4DD[0][1]': mpf('0.813388473397507463814385913308194'), 'g4DD[0][2]': mpf('0.652706348793296836714132090802973'), 'g4DD[0][3]': mpf('1.22429414375154980405074869244046'), 'g4DD[1][0]': mpf('0.813388473397507463814385913308194'), 'g4DD[1][1]': mpf('0.657497767033916602485987823456526'), 'g4DD[1][2]': mpf('0.0577387051674528306577371949970257'), 'g4DD[1][3]': mpf('0.391026617743468030141684721456841'), 'g4DD[2][0]': mpf('0.652706348793296836714132090802973'), 'g4DD[2][1]': mpf('0.0577387051674528306577371949970257'), 'g4DD[2][2]': mpf('0.14235077874207879844448143558111'), 'g4DD[2][3]': mpf('0.723120760610660329170684690325288'), 'g4DD[3][0]': mpf('1.22429414375154980405074869244046'), 'g4DD[3][1]': mpf('0.391026617743468030141684721456841'), 'g4DD[3][2]': mpf('0.723120760610660329170684690325288'), 'g4DD[3][3]': mpf('0.919283767179900235255729512573453')}
# Generated on: 2019-10-08
trusted_values_dict['ADMBSSN_tofrom_4metric__g4UU_ito_BSSN_or_ADM___ADM___globals'] = {'g4UU[0][0]': mpf('-3.03008926847944211197781568780977'), 'g4UU[0][1]': mpf('2.25487680174746330097911429618154'), 'g4UU[0][2]': mpf('0.88396408821931067329277382962707'), 'g4UU[0][3]': mpf('2.38097417962378184338842987036543'), 'g4UU[1][0]': mpf('2.25487680174746330097911429618154'), 'g4UU[1][1]': mpf('-0.109478866681308257858746913532804'), 'g4UU[1][2]': mpf('-1.57673807708112257475456728613969'), 'g4UU[1][3]': mpf('-1.71617440778374167644096697698262'), 'g4UU[2][0]': mpf('0.88396408821931067329277382962707'), 'g4UU[2][1]': mpf('-1.57673807708112257475456728613969'), 'g4UU[2][2]': mpf('-2.06437348264308527564608946533953'), 'g4UU[2][3]': mpf('1.11728919891515454886216209391363'), 'g4UU[3][0]': mpf('2.38097417962378184338842987036543'), 'g4UU[3][1]': mpf('-1.71617440778374167644096697698262'), 'g4UU[3][2]': mpf('1.11728919891515454886216209391363'), 'g4UU[3][3]': mpf('-2.23203972375107587678882970577398')}
# Generated on: 2019-10-08
trusted_values_dict['ADMBSSN_tofrom_4metric__BSSN_or_ADM_ito_g4DD___ADM___globals'] = {'gammaDD[0][0]': mpf('0.498904274895148658330867874610703'), 'gammaDD[0][1]': mpf('0.252497897757109623917415319738211'), 'gammaDD[0][2]': mpf('0.501283702587057011790250271587865'), 'gammaDD[1][0]': mpf('0.252497897757109623917415319738211'), 'gammaDD[1][1]': mpf('0.974132307468977720610325832240051'), 'gammaDD[1][2]': mpf('0.788410581529429888014703919907333'), 'gammaDD[2][0]': mpf('0.501283702587057011790250271587865'), 'gammaDD[2][1]': mpf('0.788410581529429888014703919907333'), 'gammaDD[2][2]': mpf('0.909148885640877257863223803724395'), 'betaU[0]': mpf('9.06694959302922007806468735643867'), 'betaU[1]': mpf('6.98967266398671524153764426039564'), 'betaU[2]': mpf('-10.826763784327769115585618700949'), 'alpha': mpf('2.9281812371251576982173791733214')}
# Generated on: 2019-10-08
trusted_values_dict['ADMBSSN_tofrom_4metric__g4DD_ito_BSSN_or_ADM___BSSN___globals'] = {'g4DD[0][0]': mpf('33.9316040782510563053933745757657'), 'g4DD[0][1]': mpf('11.962817515445852455324947838214'), 'g4DD[0][2]': mpf('8.00807061586371404752271503878887'), 'g4DD[0][3]': mpf('3.0744393577022965271968636933869'), 'g4DD[1][0]': mpf('11.962817515445852455324947838214'), 'g4DD[1][1]': mpf('8.84684305820134422150597858207921'), 'g4DD[1][2]': mpf('3.350018689599008046165002833204'), 'g4DD[1][3]': mpf('0.194795767378908960688399288371257'), 'g4DD[2][0]': mpf('8.00807061586371404752271503878887'), 'g4DD[2][1]': mpf('3.350018689599008046165002833204'), 'g4DD[2][2]': mpf('1.92752387150314689143948714387062'), 'g4DD[2][3]': mpf('0.617514215490828029162196536408385'), 'g4DD[3][0]': mpf('3.0744393577022965271968636933869'), 'g4DD[3][1]': mpf('0.194795767378908960688399288371257'), 'g4DD[3][2]': mpf('0.617514215490828029162196536408385'), 'g4DD[3][3]': mpf('0.440219517017598275317485123438443')}
# Generated on: 2019-10-08
trusted_values_dict['ADMBSSN_tofrom_4metric__g4UU_ito_BSSN_or_ADM___BSSN___globals'] = {'g4UU[0][0]': mpf('-3.03008926847944211197781568780977'), 'g4UU[0][1]': mpf('2.11077129805143422596450042497429'), 'g4UU[0][2]': mpf('4.4314190799497963375295703643971'), 'g4UU[0][3]': mpf('14.0116280048673688065179881220934'), 'g4UU[1][0]': mpf('2.11077129805143422596450042497429'), 'g4UU[1][1]': mpf('-7.75975232758250248342057473161043'), 'g4UU[1][2]': mpf('15.146151050341286192891709193053'), 'g4UU[1][3]': mpf('-32.5538384239792224249547239894336'), 'g4UU[2][0]': mpf('4.4314190799497963375295703643971'), 'g4UU[2][1]': mpf('15.146151050341286192891709193053'), 'g4UU[2][2]': mpf('-58.3968465178062189938977785341917'), 'g4UU[2][3]': mpf('44.2650695045030422092937754418837'), 'g4UU[3][0]': mpf('14.0116280048673688065179881220934'), 'g4UU[3][1]': mpf('-32.5538384239792224249547239894336'), 'g4UU[3][2]': mpf('44.2650695045030422092937754418837'), 'g4UU[3][3]': mpf('-143.271385972918464696654248758534')}
# Generated on: 2019-10-08
trusted_values_dict['ADMBSSN_tofrom_4metric__BSSN_or_ADM_ito_g4DD___BSSN___globals'] = {'hDD[0][0]': mpf('-0.707823822038679430924860360088691'), 'hDD[0][1]': mpf('0.359019871168498611582168252151222'), 'hDD[0][2]': mpf('1.59629921830030516363312220121092'), 'hDD[1][0]': mpf('0.359019871168498611582168252151222'), 'hDD[1][1]': mpf('2.36288339604623268350851551662764'), 'hDD[1][2]': mpf('6.09559777405440062117804245902213'), 'hDD[2][0]': mpf('1.59629921830030516363312220121092'), 'hDD[2][1]': mpf('6.09559777405440062117804245902213'), 'hDD[2][2]': mpf('14.7423246171918635049100138961082'), 'cf': mpf('0.765268415331718015781674002095462'), 'vetU[0]': mpf('9.06694959302922007806468735643867'), 'vetU[1]': mpf('2.87888088365405202711159845187769'), 'vetU[2]': mpf('-1.99111124153992461429422443718632'), 'alpha': mpf('2.9281812371251576982173791733214')}
# Generated on: 2019-10-08
trusted_values_dict['BSSN_stress_energy_source_terms__stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars___ADM___globals'] = {'SDD[0][0]': mpf('1.80029784665212934056799078226269'), 'SDD[0][1]': mpf('1.39659583030126451047804795612856'), 'SDD[0][2]': mpf('3.00811244695977724176245476191804'), 'SDD[1][0]': mpf('1.39659583030126451047804795612856'), 'SDD[1][1]': mpf('1.22494659231382375810150001831715'), 'SDD[1][2]': mpf('2.50094687070838058316104348117154'), 'SDD[2][0]': mpf('3.00811244695977724176245476191804'), 'SDD[2][1]': mpf('2.50094687070838058316104348117154'), 'SDD[2][2]': mpf('5.32511755805920028174659119741427'), 'SD[0]': mpf('0.608341733349877488954095315599777'), 'SD[1]': mpf('0.470565344820866964820125587278172'), 'SD[2]': mpf('1.12342071849067535252360437426405'), 'S': mpf('5.51886985348101528899497640423697'), 'rho': mpf('0.128468170910047767321502802016049')}
# Generated on: 2019-10-08
trusted_values_dict['BSSN_stress_energy_source_terms__stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars___BSSN___globals'] = {'SDD[0][0]': mpf('340.54483250614325553458020093047'), 'SDD[0][1]': mpf('193.449779818371076633319750276679'), 'SDD[0][2]': mpf('59.7673665437832411953316595668582'), 'SDD[1][0]': mpf('193.449779818371076633319750276679'), 'SDD[1][1]': mpf('109.367007247314914388974035339596'), 'SDD[1][2]': mpf('33.5362683001178984597875112467598'), 'SDD[2][0]': mpf('59.7673665437832411953316595668582'), 'SDD[2][1]': mpf('33.5362683001178984597875112467598'), 'SDD[2][2]': mpf('10.1607088644086656351007264098463'), 'SD[0]': mpf('8.31158076395254821775662660137348'), 'SD[1]': mpf('4.46081177450846137199550370457638'), 'SD[2]': mpf('1.24975530738365002814654512794839'), 'S': mpf('56.0590719290101215802838899642879'), 'rho': mpf('0.128468170910047767321502802016049')}
# Generated on: 2019-10-08
trusted_values_dict['BSSN_stress_energy_source_terms__BSSN_source_terms_for_BSSN_RHSs__globals'] = {'sourceterm_trK_rhs': mpf('34.481947192059905389281900539522'), 'sourceterm_a_rhsDD[0][0]': mpf('-24.4205232684807096387985845039797'), 'sourceterm_a_rhsDD[0][1]': mpf('-44.274626076606489686511882243972'), 'sourceterm_a_rhsDD[0][2]': mpf('-42.5329213202643698441006620646304'), 'sourceterm_a_rhsDD[1][0]': mpf('-44.274626076606489686511882243972'), 'sourceterm_a_rhsDD[1][1]': mpf('-60.2567558996127088470902295335831'), 'sourceterm_a_rhsDD[1][2]': mpf('-40.4716414381018667155368392378324'), 'sourceterm_a_rhsDD[2][0]': mpf('-42.5329213202643698441006620646304'), 'sourceterm_a_rhsDD[2][1]': mpf('-40.4716414381018667155368392378324'), 'sourceterm_a_rhsDD[2][2]': mpf('-7.97163159314560426073123653904653'), 'sourceterm_lambda_rhsU[0]': mpf('-12.4024163649413169626935281713898'), 'sourceterm_lambda_rhsU[1]': mpf('-7.90926902901598263785587714011605'), 'sourceterm_lambda_rhsU[2]': mpf('-5.32437139197028910868095764281183'), 'sourceterm_Lambdabar_rhsU[0]': mpf('-12.4024163649413169626935281713898'), 'sourceterm_Lambdabar_rhsU[1]': mpf('-19.2030180331952244454789590045448'), 'sourceterm_Lambdabar_rhsU[2]': mpf('-28.9515272468210212098860497720561')}
# Generated on: 2019-10-08
trusted_values_dict['BSSN_in_terms_of_ADM__gammabarDD_hDD__gammaDD_None__globals'] = {'gammabarDD[0][0]': mpc(real='0.0934311617845273723048649117117748', imag='0.161827519220989063386539896782779'), 'gammabarDD[0][1]': mpc(real='0.00820473402984368653523272740812899', imag='0.0142110162022786069846436163288672'), 'gammabarDD[0][2]': mpc(real='0.0555653159846578106884074088611669', imag='0.0962419504240464018796075151840341'), 'gammabarDD[1][0]': mpc(real='0.00820473402984368653523272740812899', imag='0.0142110162022786069846436163288672'), 'gammabarDD[1][1]': mpc(real='0.0202282035098053057653455510944696', imag='0.035036276224825878278235080642844'), 'gammabarDD[1][2]': mpc(real='0.102756261940095613982570910138747', imag='0.177979066476101677185539529091329'), 'gammabarDD[2][0]': mpc(real='0.0555653159846578106884074088611669', imag='0.0962419504240464018796075151840341'), 'gammabarDD[2][1]': mpc(real='0.102756261940095613982570910138747', imag='0.177979066476101677185539529091329'), 'gammabarDD[2][2]': mpc(real='0.1306312426956797445587454831184', imag='0.226259949404778087922451845770411'), 'hDD[0][0]': mpc(real='-0.906568838215472627695135088288225', imag='0.161827519220989063386539896782779'), 'hDD[0][1]': mpc(real='0.0199203813847587431207220021178728', imag='0.0345031126645514094053446285670361'), 'hDD[0][2]': mpc(real='0.302139096107031490667793605098268', imag='0.523320265410314511278500049229478'), 'hDD[1][0]': mpc(real='0.0199203813847587431207220021178728', imag='0.0345031126645514094053446285670361'), 'hDD[1][1]': mpc(real='-0.880759552378340582201587949384702', imag='0.206530513597969589412173263553996'), 'hDD[1][2]': mpc(real='1.35657739510377672864649412076687', imag='2.34966097271918039623983531782869'), 'hDD[2][0]': mpc(real='0.302139096107031490667793605098268', imag='0.523320265410314511278500049229478'), 'hDD[2][1]': mpc(real='1.35657739510377672864649412076687', imag='2.34966097271918039623983531782869'), 'hDD[2][2]': mpc(real='2.86236517613411134419720838195644', imag='6.68981272244899649592753121396527')}
# Generated on: 2019-10-08
trusted_values_dict['BSSN_in_terms_of_ADM__trK_AbarDD_aDD__gammaDD_None_KDD_None__globals'] = {'trK': mpf('1.2095765123256476836242095293423'), 'AbarDD[0][0]': mpc(real='0.0386623707004693575872700250783964', imag='0.0669651903942752452225661841112014'), 'AbarDD[0][1]': mpc(real='0.0197082770012822795779872109278585', imag='0.0341357370958621092471574343107932'), 'AbarDD[0][2]': mpc(real='-0.0116599231388074490306516750592891', imag='-0.0201955792887624809017754756723662'), 'AbarDD[1][0]': mpc(real='0.0197082770012822795779872109278585', imag='0.0341357370958621092471574343107932'), 'AbarDD[1][1]': mpc(real='0.0523135333085628650984233445342397', imag='0.0906096976138776738496360962926701'), 'AbarDD[1][2]': mpc(real='0.0274023107443551947048909767090663', imag='0.0474621944540137424839443269775074'), 'AbarDD[2][0]': mpc(real='-0.0116599231388074490306516750592891', imag='-0.0201955792887624809017754756723662'), 'AbarDD[2][1]': mpc(real='0.0274023107443551947048909767090663', imag='0.0474621944540137424839443269775074'), 'AbarDD[2][2]': mpc(real='0.0773121459494762602870920886743988', imag='0.133908564826673265324430417422263'), 'aDD[0][0]': mpc(real='0.0386623707004693575872700250783964', imag='0.0669651903942752452225661841112014'), 'aDD[0][1]': mpc(real='0.0478499842741998068773590091495862', imag='0.0828786039042858496683763291912328'), 'aDD[0][2]': mpc(real='-0.0634013966339984497144754982400627', imag='-0.109814440240911720403360618547595'), 'aDD[1][0]': mpc(real='0.0478499842741998068773590091495862', imag='0.0828786039042858496683763291912328'), 'aDD[1][1]': mpc(real='0.308375834035874929472953454023809', imag='0.534122612376563332681200790830189'), 'aDD[1][2]': mpc(real='0.361762433038608999247287556499941', imag='0.626590914292604606039560621866258'), 'aDD[2][0]': mpc(real='-0.0634013966339984497144754982400627', imag='-0.109814440240911720403360618547595'), 'aDD[2][1]': mpc(real='0.361762433038608999247287556499941', imag='0.626590914292604606039560621866258'), 'aDD[2][2]': mpc(real='2.28588302495977568895568765583448', imag='3.95926553938956793388115329435095')}
# Generated on: 2019-10-08
trusted_values_dict['BSSN_in_terms_of_ADM__LambdabarU_lambdaU__exact_gammaDD__gammaDD_None__globals'] = {'LambdabarU[0]': mpc(real='-4.74783841908033465983862697612494', imag='8.22349736797463570781019370770082'), 'LambdabarU[1]': mpc(real='12.33620224866062287105705763679', imag='-21.3669290671256320024440356064588'), 'LambdabarU[2]': mpc(real='-15.5391046195871584956194055848755', imag='26.9145187052532079974298540037125'), 'lambdaU[0]': mpc(real='-4.74783841908033465983862697612494', imag='8.22349736797463570781019370770082'), 'lambdaU[1]': mpc(real='5.08098998877906549154204185470007', imag='-8.80053281331416137334144877968356'), 'lambdaU[2]': mpc(real='-2.85774091943449803920884733088315', imag='4.949752467329147975760861299932')}
# Generated on: 2019-10-08
trusted_values_dict['BSSN_in_terms_of_ADM__cf_from_gammaDD__gammaDD_None__globals'] = {'cf': mpc(real='0.461683509914074474700385053438367', imag='-0.266553098729302162528398412177921')}
# Generated on: 2019-10-08
trusted_values_dict['BSSN_in_terms_of_ADM__betU_vetU__betaU_None_BU_None__globals'] = {'vetU[0]': mpf('0.744161838795925789646901193918893'), 'vetU[1]': mpf('0.120156164503744952492307520976668'), 'vetU[2]': mpf('0.144509409106853504997322208612039'), 'betU[0]': mpf('0.53440111094236486533048946512281'), 'betU[1]': mpf('0.216739633922006111369610208272842'), 'betU[2]': mpf('0.00830060552287044152590411578016158')}
BSD 2-Clause License
Copyright (c) 2018, Zachariah Etienne
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
def print_logo(print_to_stdout=True):
logo_str = """
ooooo ooo ooooooooo. ooooooooo. 88
`888b. `8' `888 `Y88. `888 `Y88. 888888
8 `88b. 8 888 .d88' 888 .d88' oooo ooo 88
8 `88b. 8 888ooo88P' 888ooo88P' `88. .8'
8 `88b.8 888`88b. 888 `88..8'
8 `888 888 `88b. 888 `888'
o8o `8 o888o o888o o888o .8'
.o..P'
NRPy+: Python-based Code Generation `Y8P'
for Numerical Relativity... and Beyond!
- homepage: http://blackholesathome.net
- download: https://github.com/zachetienne/nrpytutorial
"""
if print_to_stdout==True:
print(logo_str)
else:
return logo_str
# As documented in the NRPy+ tutorial module
# Tutorial-Coutput__Parameter_Interface.ipynb
# this core NRPy+ module is used for
# initializing, storing, and recalling
# parameters.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
import sympy as sp # Import SymPy
import os, sys # Standard Python: OS-independent system functions
from collections import namedtuple # Standard Python: Enable namedtuple data type
glb_params_list = [] # = where we store NRPy+ parameters and default values of parameters. A list of named tuples
glb_paramsvals_list = [] # = where we store NRPy+ parameter values.
glb_param = namedtuple('glb_param', 'type module parname defaultval')
glb_Cparams_list = [] # = where we store C runtime parameters and default values of parameters. A list of named tuples
glb_Cparam = namedtuple('glb_Cparam','type module parname defaultval')
veryverbose = False
def initialize_param(input):
if get_params_idx(input) == -1:
glb_params_list.append(input)
glb_paramsvals_list.append(input.defaultval)
else:
if veryverbose == True:
print("initialize_param() minor warning: Did nothing; already initialized parameter "+input.module+"::"+input.parname)
def initialize_Cparam(input):
if get_params_idx(input,Cparam=True) == -1:
glb_Cparams_list.append(input)
else:
if veryverbose == True:
print("initialize_Cparam() minor warning: Did nothing; already initialized parameter "+input.module+"::"+input.parname)
# Given the named tuple `input` and list of named tuples `params`,
# defined according to namedtuple('param', 'type module name defaultval'),
# where in the case of `input`, defaultval need not be set,
# return the list index of `params` that matches `input`.
# On error returns -1
def get_params_idx(input,Cparam=False):
# inspired by: https://stackoverflow.com/questions/2917372/how-to-search-a-list-of-tuples-in-python:
if Cparam==False:
list = [i for i, v in enumerate(glb_params_list)
if (input.type=="ignoretype" or input.type==v[0]) and input.module == v[1] and input.parname == v[2]]
else:
list = [i for i, v in enumerate(glb_Cparams_list) if input.parname == v[2]]
if list == []:
return -1 # No match found => error out!
else:
if len(list) > 1:
print("Error: Found multiple parameters matching "+str(input))
sys.exit(1)
return list.pop() # pop() returns the index
def get_params_value(input):
idx = get_params_idx(input)
if idx < 0:
print("Error: could not find a parameter matching:",input)
print("Full list of modules:\n",glb_params_list)
sys.exit(1)
else:
return glb_paramsvals_list[idx]
#
def idx_from_str(varname,modname=""):
if "::" in varname:
splitstring = re.split('::', varname)
modname=splitstring[0]
varname=splitstring[1]
# inspired by: https://stackoverflow.com/questions/2917372/how-to-search-a-list-of-tuples-in-python:
if modname == "":
list = [i for i, v in enumerate(glb_params_list) if v[2] == varname]
else:
list = [i for i, v in enumerate(glb_params_list) if (v[1] == modname and v[2] == varname)]
if list == []:
print("Error: Could not find a parameter matching \""+varname+"\" in ",glb_params_list)
sys.exit(1)
if len(list) > 1:
print("Error: Found more than one parameter named \""+varname+"\". Use get_params_value() instead.")
sys.exit(1)
return list.pop()
def parval_from_str(string):
return glb_paramsvals_list[idx_from_str(string)]
def set_parval_from_str(string,value):
glb_paramsvals_list[idx_from_str(string)] = value
# parse_param_string__set__params_and_paramsvars:
# Summary: This function parses a string like this
# module::variablename=value
# into its component parts: module,variablename,value
# then hunts for module,variablename in the
# params list. When it finds the matching index in the
# list "idx", it sets paramsvals[idx] = value.
# Usage comment: This function parses both parameter file
# inputs as well as parameter inputs from the command
# line. You should set filename = "" if reading from the
# command line. This ensures the error messages are
# appropriate for the context.
import re
def set_paramsvals_value(line,filename="", FindMainModuleMode=False):
MainModuleFound = True
if FindMainModuleMode == True:
MainModuleFound = False
# First remove carriage return and leading whitespace:
stripped_line_of_text = line.strip()
# Only process lines that do NOT start with a hash
if not stripped_line_of_text.startswith("#"):
# Valid lines take the form:
# module::variable = value # Comment here
# Thus, using the delimiters "::", "=", and "#",
# with the regex split command, the first three
# items in single_param_def will be [module, variablename, value]
single_param_def = re.split('::|=|#', stripped_line_of_text)
# First verify that single_param_def has at least 3 parts:
if len(single_param_def) < 3:
if filename != "":
print("Error: the line " + line + " in parameter file " + filename + " is not in the form")
print("\"module::variable = value\"")
else:
print("Error: the command-line argument " + stripped_line_of_text + " is not in the form")
print("\"module::variable=value\" <-- NOTICE NO SPACES ALLOWED!")
sys.exit(1)
# Next remove all leading/trailing whitespace from single_param_def
for i in range(len(single_param_def)):
single_param_def[i] = single_param_def[i].strip()
if FindMainModuleMode == False:
# Next find the parameter in the params list and set
# the corresponding element in paramsvals to the value.
idx = get_params_idx(glb_param("ignoretype", single_param_def[0], single_param_def[1], "ignoredefval"))
# If parameter is not found, print useful error message, then exit:
if idx == -1:
if filename != "":
print("Error: when reading line \"" + line + "\" in parameter file \"" + filename + "\":")
else:
print("Error: when parsing command-line argument \"" + stripped_line_of_text + "\":")
print("\t\tcould not find parameter \""+ single_param_def[1] + "\" in \""+single_param_def[0]+"\" module.")
sys.exit(1)
# If parameter is found at index idx, set paramsval[idx] to the value specified in the file.
partype = glb_params_list[idx].type
if partype == "bool":
if single_param_def[2] == "True":
glb_paramsvals_list[idx] = True
elif single_param_def[2] == "False":
glb_paramsvals_list[idx] = False
else:
print("Error: \"bool\" type can only take values of \"True\" or \"False\"")
sys.exit(1)
elif partype == "int":
glb_paramsvals_list[idx] = int(single_param_def[2])
elif partype == "REAL" or \
partype == "char" or \
partype == "char *":
glb_paramsvals_list[idx] = single_param_def[2]
else:
print("Error: type \""+partype+"\" on variable \""+ glb_params_list[idx].parname +"\" is unsupported.")
print("Supported types include: bool, int, REAL, REALARRAY, char, and char *")
sys.exit(1)
elif FindMainModuleMode == True and MainModuleFound == False:
if single_param_def[0] == "NRPy" and single_param_def[1] == "MainModule":
idx = get_params_idx(glb_param("ignoretype", single_param_def[0], single_param_def[1], "ignoredefval"))
if idx == -1:
print("Critical error: NRPy::MainModule is uninitialized!")
sys.exit(1)
glb_paramsvals_list[idx] = single_param_def[2]
def Cparameters(type,module,names,defaultvals,assumption="Real"):
output = []
# if names is not a list, make it a list, to
# simplify the remainder of this routine.
if not isinstance(names,list):
names = [names]
defaultval_list = []
if not isinstance(defaultvals,list):
for i in range(len(names)):
defaultval_list.append(defaultvals)
else:
# If defaultvals *is* a list, then make sure it has the same number of elements as "names".
if len(defaultvals) != len(names):
print("Error in Cparameters(): Was provided a list of variables:\n"+str(names)+"\n")
print("and a list of their default values:\n"+str(defaultvals)+"\n")
print("but the lists have different lengths ("+str(len(names))+" != "+str(len(defaultvals))+")\n")
sys.exit(1)
defaultval_list = defaultvals
for i in range(len(names)):
initialize_Cparam(glb_Cparam(type, module, names[i], defaultval_list[i]))
if assumption == "Real":
tmp = sp.Symbol(names[i], real=True) # Assumes all Cparameters are real.
elif assumption == "RealPositive":
tmp = sp.Symbol(names[i], real=True, positive=True) # Assumes all Cparameters are real and positive.
else:
print("Error: assumption "+str(assumption)+" not supported.")
sys.exit(1)
output.append(tmp)
if len(names) == 1:
return output[0]
return output
def generate_Cparameters_Ccodes(directory="./"):
# Step 1: Check that Cparams types are supported.
for i in range(len(glb_Cparams_list)):
partype = glb_Cparams_list[i].type
if partype != "bool" and \
partype != "#define" and \
partype != "char" and \
partype != "int" and \
partype != "REAL":
print("Error: parameter "+glb_Cparams_list[i].module+"::"+glb_Cparams_list[i].parname+" has unsupported type: \""
+ glb_Cparams_list[i].type + "\"")
sys.exit(1)
# Step 2: Generate C code to declare C paramstruct;
# output to "declare_Cparameters_struct.h"
with open(os.path.join(directory,"declare_Cparameters_struct.h"), "w") as file:
file.write("typedef struct __paramstruct__ {\n")
for i in range(len(glb_Cparams_list)):
if glb_Cparams_list[i].type != "#define":
if glb_Cparams_list[i].type == "char":
Ctype = "char *"
else:
Ctype = glb_Cparams_list[i].type
file.write(Ctype + " " + glb_Cparams_list[i].parname + ";\n")
file.write("} paramstruct;\n")
# Step 3: Generate C code to set all elements in
# C paramstruct to default values; output to
# "set_Cparameters_default.h"
with open(os.path.join(directory,"set_Cparameters_default.h"), "w") as file:
for i in range(len(glb_Cparams_list)):
if glb_Cparams_list[i].type != "#define":
Coutput = "params." + glb_Cparams_list[i].parname
if isinstance(glb_Cparams_list[i].defaultval, (bool,int,float)):
Coutput += " = " + str(glb_Cparams_list[i].defaultval).lower() + ";\n"
elif glb_Cparams_list[i].type == "char" and isinstance(glb_Cparams_list[i].defaultval, (str)):
Coutput += " = \"" + str(glb_Cparams_list[i].defaultval).lower() + "\";\n"
else:
Coutput += " = " + str(glb_Cparams_list[i].defaultval) + ";\n"
file.write(Coutput)
# Step 4: Generate C code to set C parameter constants
# (i.e., all ints != -12345678 and REALs != 1e300);
# output to filename "set_Cparameters.h" if SIMD_enable==False
# or "set_Cparameters-SIMD.h" if SIMD_enable==True
# Step 4.a: Output non-SIMD version, set_Cparameters.h
def gen_set_Cparameters(pointerEnable=True):
returnstring = ""
for i in range(len(glb_Cparams_list)):
if glb_Cparams_list[i].type == "char":
Ctype = "char *"
else:
Ctype = glb_Cparams_list[i].type
pointer = "->"
if pointerEnable==False:
pointer = "."
if not ((Ctype == "REAL" and glb_Cparams_list[i].defaultval == 1e300) or Ctype == "#define"):
Coutput = "const "+Ctype+" "+glb_Cparams_list[i].parname+" = "+"params"+pointer+glb_Cparams_list[i].parname + ";\n"
returnstring += Coutput
return returnstring
with open(os.path.join(directory,"set_Cparameters.h"), "w") as file:
file.write(gen_set_Cparameters(pointerEnable=True))
with open(os.path.join(directory,"set_Cparameters-nopointer.h"), "w") as file:
file.write(gen_set_Cparameters(pointerEnable=False))
# Step 4.b: Output SIMD version, set_Cparameters-SIMD.h
with open(os.path.join(directory,"set_Cparameters-SIMD.h"), "w") as file:
for i in range(len(glb_Cparams_list)):
if glb_Cparams_list[i].type == "char":
Ctype = "char *"
else:
Ctype = glb_Cparams_list[i].type
parname = glb_Cparams_list[i].parname
if Ctype == "REAL" and glb_Cparams_list[i].defaultval != 1e300:
Coutput = "const REAL NOSIMD" + parname + " = " + "params->" + glb_Cparams_list[i].parname + ";\n"
Coutput += "const REAL_SIMD_ARRAY " + parname + " = ConstSIMD(NOSIMD" + parname + ");\n"
file.write(Coutput)
elif glb_Cparams_list[i].defaultval != 1e300 and Ctype !="#define":
Coutput = "const "+Ctype+" "+parname + " = " + "params->" + glb_Cparams_list[i].parname + ";\n"
file.write(Coutput)
# NRPy+, SENRv2, and the NRPy+ Jupyter Tutorial
[![Build Status](https://travis-ci.org/zachetienne/nrpytutorial.svg?branch=master)](https://travis-ci.org/zachetienne/nrpytutorial)
[![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/zachetienne/nrpytutorial/master?filepath=NRPyPlus_Tutorial.ipynb)
This repository houses
* [The newest version of NRPy+: Python-Based Code Generation for Numerical Relativity... and Beyond](https://arxiv.org/abs/1712.07658),
* The second version of SENR, the Simple, Efficient Numerical Relativity code (see the "Colliding Black Holes" Start-to-Finish tutorial module), and
* The NRPy+ Jupyter Tutorial: An Introduction to Python-Based Code Generation for Numerical Relativity... and Beyond!
To explore the NRPy+ tutorial without downloading, check out the [NRPy+ tutorial mybinder](https://mybinder.org/v2/gh/zachetienne/nrpytutorial/master?filepath=NRPyPlus_Tutorial.ipynb).
If you would like to explore the NRPy+ tutorial on your local computer, you'll need to install Python, Jupyter, Sympy, and Matplotlib. Once they are installed, [you may find this useful](https://jupyter-notebook-beginner-guide.readthedocs.io/en/latest/execute.html)
In certain circumstances, developers may wish to execute one of these Jupyter notebooks from the command line. For example, when the notebook constructs an [Einstein Toolkit](https://einsteintoolkit.org) thorn. In such a case, the following command should be useful:
jupyter nbconvert --to notebook --inplace --execute --ExecutePreprocessor.timeout=-1 [Jupyter notebook file]
// If compiled with AVX512F SIMD instructions enabled:
#ifdef __AVX512F__
#include <immintrin.h>
#define REAL_SIMD_ARRAY __m512d
#define SIMD_width 8 // 8 doubles per loop iteration
#define ReadSIMD(a) _mm512_loadu_pd(a)
#define WriteSIMD(a,b) _mm512_storeu_pd(a,(b))
#define ConstSIMD(a) _mm512_set1_pd(a)
#define AddSIMD(a,b) _mm512_add_pd((a),(b))
#define SubSIMD(a,b) _mm512_sub_pd((a),(b))
#define MulSIMD(a,b) _mm512_mul_pd((a),(b))
#define DivSIMD(a,b) _mm512_div_pd((a),(b))
#define ExpSIMD(a) _mm512_exp_pd((a))
#define SinSIMD(a) _mm512_sin_pd((a))
#define CosSIMD(a) _mm512_cos_pd((a))
// All AVX512 chips have FMA enabled
#define FusedMulAddSIMD(a,b,c) _mm512_fmadd_pd((a),(b),(c))
#define FusedMulSubSIMD(a,b,c) _mm512_fmsub_pd((a),(b),(c))
// In the case of 512-bit SIMD:
// The result from this comparison is: result[i] = (a OP b) ? 1 : 0, stored in an 8-bit mask array.
// Then if result==1 we set upwind = 0+1, and if result==0 we set upwind = 0
#define UPWIND_ALG(a) _mm512_mask_add_pd(upwind_Integer_0, _mm512_cmp_pd_mask( (a) , upwind_Integer_0, _CMP_GT_OQ), upwind_Integer_0 ,upwind_Integer_1)
// If compiled with AVX SIMD instructions enabled:
#elif __AVX__
#include <immintrin.h>
#define REAL_SIMD_ARRAY __m256d
#define SIMD_width 4 // 4 doubles per loop iteration
// Upwind algorithm notes, in the case of 256-bit SIMD:
// Sources: https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_cmp_pd&expand=736
// ...and: https://stackoverflow.com/questions/37099874/is-avx-intrinsic-mm256-cmp-ps-supposed-to-return-nan-when-true
// The result from _mm256_cmp_pd is 0 if a>0 and NaN otherwise.
// Thus if OP is >, then: if a > b then the result is NaN, and if a <= b then the result is 0.
// We want the result to be 1 if a>b and 0 otherwise, so we simply perform a logical AND operation
// on the result, against the number 1, because AND(NaN,1)=1, and AND(0,1)=0,
// where NaN=0xffffff... in double precision.
#define UPWIND_ALG(a) _mm256_and_pd(_mm256_cmp_pd( (a), upwind_Integer_0, _CMP_GT_OQ ), upwind_Integer_1)
#define ReadSIMD(a) _mm256_loadu_pd(a)
#define WriteSIMD(a,b) _mm256_storeu_pd(a,(b))
#define ConstSIMD(a) _mm256_set1_pd(a)
#define AddSIMD(a,b) _mm256_add_pd((a),(b))
#define SubSIMD(a,b) _mm256_sub_pd((a),(b))
#define MulSIMD(a,b) _mm256_mul_pd((a),(b))
#define DivSIMD(a,b) _mm256_div_pd((a),(b))
#define ExpSIMD(a) _mm256_exp_pd((a))
#define SinSIMD(a) _mm256_sin_pd((a))
#define CosSIMD(a) _mm256_cos_pd((a))
#ifdef __FMA__
#define FusedMulAddSIMD(a,b,c) _mm256_fmadd_pd((a),(b),(c))
#define FusedMulSubSIMD(a,b,c) _mm256_fmsub_pd((a),(b),(c))
#else
#define FusedMulAddSIMD(a,b,c) _mm256_add_pd(_mm256_mul_pd((a),(b)), (c)) // a*b+c
#define FusedMulSubSIMD(a,b,c) _mm256_sub_pd(_mm256_mul_pd((a),(b)), (c)) // a*b-c
#endif
// If compiled with SSE2 SIMD instructions enabled:
#elif __SSE2__
#include <emmintrin.h>
#define REAL_SIMD_ARRAY __m128d
#define SIMD_width 2 // 2 doubles per loop iteration
#define ReadSIMD(a) _mm_loadu_pd(a)
#define WriteSIMD(a,b) _mm_storeu_pd(a,(b))
#define ConstSIMD(a) _mm_set1_pd(a)
#define AddSIMD(a,b) _mm_add_pd((a),(b))
#define SubSIMD(a,b) _mm_sub_pd((a),(b))
#define MulSIMD(a,b) _mm_mul_pd((a),(b))
#define DivSIMD(a,b) _mm_div_pd((a),(b))
#define ExpSIMD(a) _mm_exp_pd((a))
#define SinSIMD(a) _mm_sin_pd((a))
#define CosSIMD(a) _mm_cos_pd((a))
// See description above UPWIND_ALG for __AVX__:
#define UPWIND_ALG(a) _mm_and_pd(_mm_cmpgt_pd( (a), upwind_Integer_0 ), upwind_Integer_1)
#ifdef __FMA__ // Unlikely that any SSE2 chip has FMA, but this will work fine.
#define FusedMulAddSIMD(a,b,c) _mm_fmadd_pd((a),(b),(c))
#define FusedMulSubSIMD(a,b,c) _mm_fmsub_pd((a),(b),(c))
#else
#define FusedMulAddSIMD(a,b,c) _mm_add_pd(_mm_mul_pd((a),(b)), (c)) // a*b+c
#define FusedMulSubSIMD(a,b,c) _mm_sub_pd(_mm_mul_pd((a),(b)), (c)) // a*b-c
#endif
#else
// If SIMD instructions unavailable:
#define REAL_SIMD_ARRAY REAL
#define SIMD_width 1 // 1 double per loop iteration
#define ConstSIMD(a) (a)
#define AddSIMD(a,b) ((a)+(b))
#define SubSIMD(a,b) ((a)-(b))
#define MulSIMD(a,b) ((a)*(b))
#define DivSIMD(a,b) ((a)/(b))
#define FusedMulAddSIMD(a,b,c) ((a)*(b) + (c))
#define FusedMulSubSIMD(a,b,c) ((a)*(b) - (c))
#define SqrtSIMD(a) (sqrt(a))
#define ExpSIMD(a) (exp(a))
#define SinSIMD(a) (sin(a))
#define CosSIMD(a) (cos(a))
#define WriteSIMD(a,b) *(a)=(b)
#define ReadSIMD(a) *(a)
// Algorithm for upwinding.
// *NOTE*: This upwinding is backwards from
// usual upwinding algorithms, because the
// upwinding control vector in BSSN (the shift)
// acts like a *negative* velocity.
#define UPWIND_ALG(UpwindVecU) UpwindVecU > 0.0 ? 1.0 : 0.0
#endif
from sympy import Integer,Symbol,symbols,simplify,Rational,sign,Function,srepr,sin,cos,exp,log,Abs,Add,Mul,Pow,preorder_traversal,N,Float,S,var,sympify
import re, sys
# For debugging purposes, Part 1:
# Basic arithmetic operations
def ConstSIMD_check(a):
return Float(a,34)
def AbsSIMD_check(a):
return Abs(a)
def nrpyAbsSIMD_check(a):
return Abs(a)
def AddSIMD_check(a,b):
return a+b
def SubSIMD_check(a,b):
return a-b
def MulSIMD_check(a,b):
return a*b
def FusedMulAddSIMD_check(a,b,c):
return a*b + c
def FusedMulSubSIMD_check(a,b,c):
return a*b - c
def DivSIMD_check(a,b):
return a/b
def signSIMD_check(a):
return sign(a)
# For debugging purposes, Part 2:
# Transcendental operations
def PowSIMD_check(a, b):
return a**b
def SqrtSIMD_check(a):
return a**(Rational(1,2))
def CbrtSIMD_check(a):
return a**(Rational(1,3))
def ExpSIMD_check(a):
return exp(a)
def LogSIMD_check(a):
return log(a)
def SinSIMD_check(a):
return sin(a)
def CosSIMD_check(a):
return cos(a)
# Input: SymPy expression.
# Return value: SymPy expression containing all needed SIMD compiler intrinsics
# Complication: SIMD functions require numerical constants to be stored in SIMD arrays
# Resolution: This function extends lists "SIMD_const_varnms" and "SIMD_const_values",
# which store the name of each constant SIMD array (e.g., _Integer_1) and
# the value of each variable (e.g., 1.0).
def expr_convert_to_SIMD_intrins(expr, SIMD_const_varnms,SIMD_const_values,SIMD_const_suffix="",debug="False"):
# Declare all variables, so we can eval them in the next (AddSIMD & MulSIMD) step
for item in preorder_traversal(expr):
for i in range(len(item.args)):
if isinstance(item.args[i],Symbol):
var(str(item.args[i]))
expr_orig = expr
AbsSIMD = Function("AbsSIMD")
AddSIMD = Function("AddSIMD")
SubSIMD = Function("SubSIMD")
MulSIMD = Function("MulSIMD")
FusedMulAddSIMD = Function("FusedMulAddSIMD")
FusedMulSubSIMD = Function("FusedMulSubSIMD")
DivSIMD = Function("DivSIMD")
SignSIMD = Function("SignSIMD")
PowSIMD = Function("PowSIMD")
SqrtSIMD = Function("SqrtSIMD")
CbrtSIMD = Function("CbrtSIMD")
ExpSIMD = Function("ExpSIMD")
LogSIMD = Function("LogSIMD")
SinSIMD = Function("SinSIMD")
CosSIMD = Function("CosSIMD")
# Step 1: Replace transcendental, power, and division functions with SIMD equivalents
# Note that due to how SymPy expresses rational numbers, the following does not
# affect fractional expressions of integers
for item in preorder_traversal(expr):
if item.func == Abs:
expr = expr.xreplace({item: AbsSIMD(item.args[0])})
elif item.func == exp:
expr = expr.xreplace({item: ExpSIMD(item.args[0])})
elif item.func == log:
expr = expr.xreplace({item: LogSIMD(item.args[0])})
elif item.func == sin:
expr = expr.xreplace({item: SinSIMD(item.args[0])})
elif item.func == cos:
expr = expr.xreplace({item: CosSIMD(item.args[0])})
elif item.func == sign:
expr = expr.xreplace({item: SignSIMD(item.args[0])})
# Fun little recursive function for constructing integer powers:
def IntegerPowSIMD(a, n):
if n == 2:
return MulSIMD(a, a)
elif n > 2:
return MulSIMD(IntegerPowSIMD(a, n - 1), a)
elif n <= -2:
return DivSIMD(1, IntegerPowSIMD(a, -n))
elif n == -1:
return DivSIMD(1, a)
for item in preorder_traversal(expr):
if item.func == Pow:
if item.args[1] == 0.5:
expr = expr.xreplace({item: SqrtSIMD(item.args[0])})
elif item.args[1] == -0.5:
expr = expr.xreplace({item: DivSIMD(1,SqrtSIMD(item.args[0]))})
elif item.args[1] == Rational(1,3):
expr = expr.xreplace({item: CbrtSIMD(item.args[0])})
elif item.args[1] == int(item.args[1]):
expr = expr.xreplace({item: IntegerPowSIMD(item.args[0], item.args[1])})
else:
expr = expr.xreplace({item: PowSIMD(item.args[0], item.args[1])})
# Step 2: Replace all rational numbers (expressed as Rational(a,b))
# and integers with the new functions RationalTMP and
# IntegerTMP, where Rational(a,b) -> RationalTMP(a,b)
# and Integer(a) -> IntegerTMP(a)
RationalTMP = Function("RationalTMP")
IntegerTMP = Function("IntegerTMP")
string = str(srepr(expr))
string2 = re.sub(r'Integer\(([+\-0-9]+)\)',
"(Function('IntegerTMP')('\\1'))", string)
expr = eval(string2)
string = str(srepr(expr))
string2 = re.sub(r'Rational\(([-0-9]+), ([0-9]+)\)',
"(Function('RationalTMP')(('\\1'),('\\2')))", string)
expr = eval(string2)
# Step 3: The pattern Mul(-1,Rational(a,b)) is often seen. Replace with Rational(-a,b).
for item in preorder_traversal(expr):
if item.func == Mul:
idx_has_Negative1 = -10
for i in range(len(item.args)):
if item.args[i].func == IntegerTMP and item.args[i].args[0] == -1:
idx_has_Negative1 = i
if idx_has_Negative1 >= 0:
for i in range(len(item.args)):
if item.args[i].func==RationalTMP:
tempitem_orig = Mul(IntegerTMP(-1),RationalTMP(item.args[i].args[0], item.args[i].args[1]))
tempitem_new = RationalTMP(-item.args[i].args[0], item.args[i].args[1])
expr = expr.subs(tempitem_orig, tempitem_new )
break
# Step 4: SIMD multiplication and addition compiler intrinsics read in
# only two arguments at once, where SymPy's Mul() and Add()
# operators can read an arbitrary number of arguments.
# Here, we split e.g., Mul(a,b,c,d) into
# MulSIMD(a,MulSIMD(b,MulSIMD(c,d))),
# To accomplish this easily, we construct a string
# 'MulSIMD(A,MulSIMD(B,...', where MulSIMD(a,b) is some user-
# defined function that takes in only two arguments, and then
# evaluate the string using the eval() function.
# Implementation detail: If we did not perform Step 2 above, the eval
# function would automatically evaluate all Rational expressions
# as though they were input as integers: e.g., 1/2 evaluates to 0.
# This is undesirable, so we instead define new, temporary
# functions IntegerTMP and RationalTMP that are undisturbed by
# the eval()
for item in preorder_traversal(expr):
if item.func == Mul:
blahtmp = symbols('blahtmp')
tempitem = blahtmp
for i in range(len(item.args)-1):
tempitem = MulSIMD(item.args[i],tempitem)
tempitem = tempitem.xreplace({blahtmp: item.args[len(item.args)-1]})
expr = expr.xreplace({item: tempitem})
for item in preorder_traversal(expr):
if item.func == Add:
blahtmp = symbols('blahtmp')
tempitem = blahtmp
for i in range(len(item.args)-1):
tempitem = AddSIMD(item.args[i],tempitem)
tempitem = tempitem.xreplace({blahtmp: item.args[len(item.args)-1]})
expr = expr.xreplace({item: tempitem})
# Step 5: Simplification patterns:
# Step 5a: Replace the pattern Mul(Div(1,b),a) or Mul(a,Div(1,b)) with Div(a,b):
for item in preorder_traversal(expr):
if item.func == MulSIMD:
if item.func == MulSIMD and (item.args[0].func == DivSIMD and item.args[0].args[0].func == IntegerTMP and item.args[0].args[0].args[0] == 1):
expr = expr.xreplace({item: DivSIMD(item.args[1],item.args[0].args[1])})
if item.func == MulSIMD and (item.args[1].func == DivSIMD and item.args[1].args[0].func == IntegerTMP and item.args[1].args[0].args[0] == 1):
expr = expr.xreplace({item: DivSIMD(item.args[0],item.args[1].args[1])})
# Step 5: Subtraction intrinsics. SymPy replaces all a-b with a + (-b) = Add(a,Mul(-1,b))
# Here, we replace
# a) AddSIMD(a,MulSIMD(-1,b)),
# b) AddSIMD(a,MulSIMD(b,-1)),
# c) AddSIMD(MulSIMD(-1,b),a), and
# d) AddSIMD(MulSIMD(b,-1),a)
# with SubSIMD(a,b)
for item in preorder_traversal(expr):
tempitem = item
# First match patterns a) and b): AddSIMD(a,MULSIMD(.,.)):
if item.func == AddSIMD and item.args[1].func == MulSIMD:
# Pattern a) AddSIMD(a,MulSIMD(-1,b)) --> SubSIMD(a,b):
if item.args[1].args[0].func == IntegerTMP and item.args[1].args[0].args[0] == -1:
tempitem = SubSIMD(item.args[0],item.args[1].args[1])
# Pattern b) AddSIMD(a,MulSIMD(b,-1)) --> SubSIMD(a,b):
elif item.args[1].args[1].func == IntegerTMP and item.args[1].args[1].args[0] == -1:
tempitem = SubSIMD(item.args[0],item.args[1].args[0])
# Next match patterns c) and d): AddSIMD(MulSIMD(.,.),a):
elif item.func == AddSIMD and item.args[0].func == MulSIMD:
# Pattern c) AddSIMD(MulSIMD(-1,b),a) --> SubSIMD(a,b):
if item.args[0].args[0].func == IntegerTMP and item.args[0].args[0].args[0] == -1:
tempitem = SubSIMD(item.args[1],item.args[0].args[1])
# Pattern d) AddSIMD(MulSIMD(b,-1,a)) --> SubSIMD(a,b):
elif item.args[0].args[1].func == IntegerTMP and item.args[0].args[1].args[0] == -1:
tempitem = SubSIMD(item.args[1],item.args[0].args[0])
expr = expr.subs(item,tempitem)
# Step 6: Now that all multiplication and addition functions only take two
# arguments, we can now easily define fused-multiply-add functions,
# where AddSIMD(a,MulSIMD(b,c)) = b*c + a = FusedMulAddSIMD(b,c,a),
# or AddSIMD(MulSIMD(b,c),a) = b*c + a = FusedMulAddSIMD(b,c,a).
# Fused multiply add (FMA3) is standard on Intel CPUs with the AVX2
# instruction set, starting with Haswell processors in 2013:
# https://en.wikipedia.org/wiki/Haswell_(microarchitecture)
for item in preorder_traversal(expr):
tempitem = item
# If the pattern is a*b + c, replace with FMA(a,b,c)
if item.func == AddSIMD and item.args[0].func == MulSIMD:
tempitem = FusedMulAddSIMD(item.args[0].args[0],item.args[0].args[1],item.args[1])
# If the pattern is c + a*b, replace with FMA(a,b,c)
if item.func == AddSIMD and item.args[1].func == MulSIMD:
tempitem = FusedMulAddSIMD(item.args[1].args[0],item.args[1].args[1],item.args[0])
# If the pattern is a*b - c, replace with FMS(a,b,c)
if item.func == SubSIMD and item.args[0].func == MulSIMD:
tempitem = FusedMulSubSIMD(item.args[0].args[0],item.args[0].args[1],item.args[1])
if item != tempitem: expr = expr.subs(item, tempitem)
# Step 7: SIMD intrinsics cannot take integers or rational numbers as arguments.
# Therefore we must declare all integers & rational numbers as
# const vector doubles (e.g., const _m256d ...). To make the code
# more human readable, we adopt the convention
# RationalTMP(1,3) = 1/3 = "Rational_1_3
# RationalTMP(-1,3) = -1/3 = "Rational_m1_3
# TODO: Keep track of all integers and rationals, so repeats are not computed?
# Step 7a: Set all variable names and corresponding values.
for item in preorder_traversal(expr):
if item.func == RationalTMP:
# Set variable name
if item.args[0]*item.args[1] < 0:
SIMD_const_varnms.extend(["_Rational_m"+str(abs(item.args[0]))+"_"+str(abs(item.args[1]))+SIMD_const_suffix])
elif item.args[0] > 0 and item.args[1] > 0:
SIMD_const_varnms.extend(["_Rational_"+str(item.args[0])+"_"+str(item.args[1])+SIMD_const_suffix])
else:
# E.g., doesn't make sense to have -1/-3. SymPy should have simplified this.
print("Found a weird Rational(a,b) expression, where a<0 and b<0. Report to SymPy devels")
print("Specifically, found that a="+str(item.args[0])+" and b="+str(item.args[1]))
sys.exit(1)
# Set variable value, to 34 digits of precision
SIMD_const_values.extend([str(N(Float(item.args[0],34)/Float(item.args[1],34),34))])
elif item.func == IntegerTMP:
# Set variable name
if item.args[0] < 0:
SIMD_const_varnms.extend(["_Integer_m"+str(-item.args[0])+SIMD_const_suffix])
else:
SIMD_const_varnms.extend(["_Integer_" + str(item.args[0])+SIMD_const_suffix])
# Set variable value, to 34 digits of precision
SIMD_const_values.extend([str((Float(item.args[0],34)))])
# Step 7b: Replace all integers and rationals with the appropriate variable names:
for item in preorder_traversal(expr):
tempitem = item
if item.func == RationalTMP:
if item.args[0]*item.args[1] < 0:
tempitem = var("_Rational_m" + str(abs(item.args[0])) + "_" + str(abs(item.args[1]))+SIMD_const_suffix)
elif item.args[0] > 0 and item.args[1] > 0:
tempitem = var("_Rational_" + str(item.args[0]) + "_" + str(item.args[1])+SIMD_const_suffix)
else:
# E.g., doesn't make sense to have -1/-3. SymPy should have simplified this.
print("Found a weird Rational(a,b) expression, where a<0 and b<0. Report to SymPy devels")
print("Specifically, found that a=" + str(item.args[0]) + " and b=" + str(item.args[1]))
sys.exit(1)
elif item.func == IntegerTMP:
if item.args[0] < 0:
tempitem = var("_Integer_m" + str(-item.args[0])+SIMD_const_suffix)
else:
tempitem = var("_Integer_" + str(item.args[0])+SIMD_const_suffix)
if item != tempitem: expr = expr.subs(item, tempitem)
def lookup_name_output_idx(name, list_of_names):
for i in range(len(list_of_names)):
if list_of_names[i] == name:
return i
print("I SHOULDN'T BE HERE!",name,list_of_names)
sys.exit(1)
if debug=="True":
expr_check = expr
if "SIMD" in str(expr):
expr_check = eval(str(expr).replace("SIMD","SIMD_check"))
for item in preorder_traversal(expr_check):
tempitem = item
if item.is_Symbol and str(item)[0]=="_":
if str(item)[:9]=="_Integer_" or str(item)[:10]=="_Rational_":
tempitem = SIMD_const_values[lookup_name_output_idx(str(item), SIMD_const_varnms)]
if item != tempitem: expr_check = expr_check.subs(item, tempitem)
expr_diff = expr_check - expr_orig
# Some variables do not want to cancel in SymPy ~0.7.4. The eval(str(srepr())) below normalizes the expression.
expr_diff = eval(str(srepr(expr_diff)))
for item in preorder_traversal(expr_diff):
if item.func == Float:
if abs(item - Integer(item)) < 1.0e-14:
expr_diff = expr_diff.xreplace({item:Integer(item)})
# Only simplify if expr_diff != 0:
if expr_diff != 0:
simp_expr_diff = simplify(expr_diff)
if simp_expr_diff != 0:
print("Warning: found possible diff",(simp_expr_diff))
return(expr)
# As documented in the NRPy+ tutorial notebook
# Tutorial-cmdline_helper.ipynb, this Python script
# provides a multi-platform means to run executables,
# remove files, and compile code.
# Basic functions:
# check_executable_exists(): Check to see whether an executable exists.
# Error out or return False if not exists;
# return True if executable exists in PATH.
# C_compile(): Compile C code using gcc.
# Execute(): Execute generated executable file, using taskset
# if available. Calls Execute_input_string() to
# redirect output from stdout & stderr to desired
# destinations.
# Execute_input_string(): Executes an input string and redirects
# output from stdout & stderr to desired destinations.
# delete_existing_files(file_or_wildcard):
# Runs del file_or_wildcard in Windows, or
# rm file_or_wildcard in Linux/MacOS
# Authors: Brandon Clark
# Zach Etienne
# zachetie **at** gmail **dot* com
# Kevin Lituchy
import io, os, shlex, subprocess, sys, time, multiprocessing, getpass, platform
# check_executable_exists(): Check to see whether an executable exists.
# Error out or return False if not exists;
# return True if executable exists in PATH.
def check_executable_exists(exec_name,error_if_not_found=True):
cmd = "where" if os.name == "nt" else "which"
try:
subprocess.check_output([cmd, exec_name])
except subprocess.CalledProcessError:
if error_if_not_found:
print("Sorry, cannot execute the command: " + exec_name)
sys.exit(1)
else:
return False
return True
# C_compile(): Write a function to compile the Main C code into an executable file
def C_compile(main_C_output_path, main_C_output_file, compile_mode="optimized", custom_compile_string=""):
print("Compiling executable...")
# Step 1: Check for gcc compiler
check_executable_exists("gcc")
# Step 2: Delete existing version of executable
delete_string = ""
if os.name == "nt":
main_C_output_file += ".exe"
delete_existing_files(main_C_output_file)
# Step 3: Compile the executable
if compile_mode=="safe":
compile_string = "gcc -O2 -g -fopenmp "+str(main_C_output_path)+" -o "+str(main_C_output_file)+" -lm"
Execute_input_string(compile_string, os.devnull)
# Check if executable exists (i.e., compile was successful), if not, try with more conservative compile flags.
if not os.path.isfile(main_C_output_file):
print("Sorry, compilation failed")
sys.exit(1)
elif compile_mode=="icc":
check_executable_exists("icc")
compile_string = "icc -O2 -xHost -qopenmp -unroll "+str(main_C_output_path)+" -o "+str(main_C_output_file)+" -lm"
Execute_input_string(compile_string, os.devnull)
# Check if executable exists (i.e., compile was successful), if not, try with more conservative compile flags.
if not os.path.isfile(main_C_output_file):
print("Sorry, compilation failed")
sys.exit(1)
elif compile_mode=="custom":
Execute_input_string(custom_compile_string, os.devnull)
# Check if executable exists (i.e., compile was successful), if not, try with more conservative compile flags.
if not os.path.isfile(main_C_output_file):
print("Sorry, compilation failed")
sys.exit(1)
elif compile_mode=="optimized":
compile_string = "gcc -Ofast -fopenmp -march=native -funroll-loops "+str(main_C_output_path)+" -o "+str(main_C_output_file)+" -lm"
Execute_input_string(compile_string, os.devnull)
# Check if executable exists (i.e., compile was successful), if not, try with more conservative compile flags.
if not os.path.isfile(main_C_output_file):
# Step 3.A: Revert to more compatible gcc compile option
print("Most optimized compilation failed. Removing -march=native:")
compile_string = "gcc -Ofast -fopenmp -funroll-loops "+str(main_C_output_path)+" -o "+str(main_C_output_file)+" -lm"
Execute_input_string(compile_string, os.devnull)
if not os.path.isfile(main_C_output_file):
# Step 3.B: Revert to maximally compatible gcc compile option
print("Next-to-most optimized compilation failed. Moving to maximally-compatible gcc compile option:")
compile_string = "gcc -O2 "+str(main_C_output_path)+" -o "+str(main_C_output_file)+" -lm"
Execute_input_string(compile_string, os.devnull)
# Step 3.C: If there are still missing components within the compiler, say compilation failed
if not os.path.isfile(main_C_output_file):
print("Sorry, compilation failed")
sys.exit(1)
else:
print("Sorry, compile_mode = \""+compile_mode+"\" unsupported.")
sys.exit(1)
print("Finished compilation.")
# Execute(): Execute generated executable file, using taskset
# if available. Calls Execute_input_string() to
# redirect output from stdout & stderr to desired
# destinations.
def Execute(executable, executable_output_arguments="", file_to_redirect_stdout=os.devnull,verbose=True):
# Step 1: Delete old version of executable file
if file_to_redirect_stdout != os.devnull:
delete_existing_files(file_to_redirect_stdout)
# Step 2: Build the script for executing the desired executable
execute_string = ""
# When in Windows...
# https://stackoverflow.com/questions/1325581/how-do-i-check-if-im-running-on-windows-in-python
if os.name == "nt":
# ... do as the Windows do
# https://stackoverflow.com/questions/49018413/filenotfounderror-subprocess-popendir-windows-7
execute_prefix = "cmd /c " # Run with cmd /c executable [options] on Windows
else:
execute_prefix = "./" # Run with ./executable [options] on Linux & Mac
taskset_exists = check_executable_exists("taskset", error_if_not_found=False)
if taskset_exists:
execute_string += "taskset -c 0"
if getpass.getuser() != "jovyan": # on mybinder, username is jovyan, and taskset -c 0 is the fastest option.
# If not on mybinder and taskset exists:
has_HT_cores = False # Does CPU have hyperthreading cores?
if platform.processor() != '': # If processor string returns null, then assume CPU does not support hyperthreading.
# This will yield correct behavior on ARM (e.g., cell phone) CPUs.
has_HT_cores=True
if has_HT_cores == True:
# NOTE: You will observe a speed-up by using only *PHYSICAL* (as opposed to logical/hyperthreading) cores:
N_cores_to_use = int(multiprocessing.cpu_count()/2) # To account for hyperthreading cores
else:
N_cores_to_use = int(multiprocessing.cpu_count()) # Use all cores if none are hyperthreading cores.
# This will happen on ARM (e.g., cellphone) CPUs
for i in range(N_cores_to_use-1):
execute_string += ","+str(i+1)
execute_string += " "
execute_string += execute_prefix+executable+" "+executable_output_arguments
# Step 3: Execute the desired executable
Execute_input_string(execute_string, file_to_redirect_stdout,verbose)
# Execute_input_string(): Executes an input string and redirects
# output from stdout & stderr to desired destinations.
def Execute_input_string(input_string, file_to_redirect_stdout=os.devnull, verbose=True):
if verbose:
print("Executing `"+input_string+"`...")
start = time.time()
# https://docs.python.org/3/library/subprocess.html
if os.name != 'nt':
args = shlex.split(input_string)
else:
args = input_string
# https://stackoverflow.com/questions/18421757/live-output-from-subprocess-command
filename = "tmp.txt"
with io.open(filename, 'w') as writer, io.open(filename, 'rb', buffering=-1) as reader, io.open(file_to_redirect_stdout, 'wb') as rdirect:
process = subprocess.Popen(args, stdout=rdirect, stderr=writer)
while process.poll() is None:
# https://stackoverflow.com/questions/21689365/python-3-typeerror-must-be-str-not-bytes-with-sys-stdout-write/21689447
sys.stdout.write(reader.read().decode('utf-8'))
time.sleep(0.2)
# Read the remaining
sys.stdout.write(reader.read().decode('utf-8'))
delete_existing_files(filename)
end = time.time()
if verbose:
print("Finished executing in "+str(end-start)+" seconds.")
# delete_existing_files(file_or_wildcard):
# Runs del file_or_wildcard in Windows, or
# rm file_or_wildcard in Linux/MacOS
def delete_existing_files(file_or_wildcard):
delete_string = ""
if os.name == "nt":
delete_string += "del " + file_or_wildcard
else:
delete_string += "rm -f " + file_or_wildcard
os.system(delete_string)
# https://stackoverflow.com/questions/1274405/how-to-create-new-folder
def mkdir(newpath):
if not os.path.exists(os.path.join(newpath)):
os.makedirs(os.path.join(newpath))
# finite_difference.py:
# As documented in the NRPy+ tutorial notebook:
# Tutorial-Finite_Difference_Derivatives.ipynb ,
# This module generates C kernels for numerically
# solving PDEs with finite differences.
#
# Depends on: outputC.py and grid.py.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
from outputC import * # NRPy+: Core C code output module
import grid as gri # NRPy+: Functions having to do with numerical grids
import sys # Standard Python module for multiplatform OS-level functions
from operator import itemgetter
# Step 1: Initialize free parameters for this module:
import NRPy_param_funcs as par
modulename = __name__
# Centered finite difference accuracy order
par.initialize_param(par.glb_param("int", modulename, "FD_CENTDERIVS_ORDER", 4))
par.initialize_param(par.glb_param("int", modulename, "FD_KO_ORDER__CENTDERIVS_PLUS", 2))
def FD_outputC(filename,sympyexpr_list, params="", upwindcontrolvec=""):
outCparams = parse_outCparams_string(params)
# Step 0.a:
# In case sympyexpr_list is a single sympy expression,
# convert it to a list with just one element:
if type(sympyexpr_list) is not list:
sympyexpr_list = [sympyexpr_list]
# Step 0.b:
# finite_difference.py takes control over outCparams.includebraces here,
# which is necessary because outputC() is called twice:
# first for the reads from main memory and finite difference
# stencil expressions, and second for the SymPy expressions and
# writes to main memory.
# If outCparams.includebraces==True, then it will close off the braces
# after the finite difference stencil expressions and start new ones
# for the SymPy expressions and writes to main memory, resulting
# in a non-functioning C code.
# To get around this issue, we create braces around the entire
# string of C output from this function, only if
# outCparams.includebraces==True.
# See Step 6 for corresponding end brace.
if outCparams.includebraces == "True":
Coutput = outCparams.preindent+"{\n"
indent = " "
else:
Coutput = ""
indent = ""
# Step 1a:
# Create a list of free symbols in the sympy expr list
# that are registered neither as gridfunctions nor
# as C parameters. These *must* be derivatives,
# so we call the list "list_of_deriv_vars"
list_of_deriv_vars_with_duplicates = []
for expr in sympyexpr_list:
for var in expr.rhs.free_symbols:
vartype = gri.variable_type(var)
if vartype == "other":
# vartype=="other" should ONLY refer to derivatives, so
# if "_dD" or variants do not appear in a variable classified
# neither as a gridfunction nor a Cparameter, then error out.
if ("_dD" in str(var)) or \
("_dKOD" in str(var)) or \
("_dupD" in str(var)) or \
("_ddnD" in str(var)):
pass
else:
print("Error: Unregistered variable \""+str(var)+"\" in SymPy expression for "+expr.lhs)
print("All variables in SymPy expressions passed to FD_outputC() must be registered")
print("in NRPy+ as either a gridfunction or Cparameter, by calling")
print(str(var)+" = register_gridfunctions...() (in ixp/grid) if \""+str(var)+"\" is a gridfunction, or")
print(str(var)+" = Cparameters() (in par) otherwise (e.g., if it is a free parameter set at C runtime).")
sys.exit(1)
list_of_deriv_vars_with_duplicates.append(var)
# elif vartype == "gridfunction":
# list_of_deriv_vars_with_duplicates.append(var)
list_of_deriv_vars = superfast_uniq(list_of_deriv_vars_with_duplicates)
# Upwinding with respect to a control vector: algorithm description.
# To enable, set the FD_outputC()'s fourth function argument to the
# desired control vector. In BSSN, the betaU vector controls the upwinding.
# See https://arxiv.org/pdf/gr-qc/0206072.pdf for motivation and
# https://arxiv.org/pdf/gr-qc/0109032.pdf for implementation details,
# at second order. Note that the BSSN shift vector behaves like a *negative*
# velocity. See http://www.damtp.cam.ac.uk/user/naweb/ii/advection/advection.php
# for a very basic example motivating this choice.
# Step 1b: For each variable with suffix _dupD, append to
# the list_of_deriv_vars the corresponding _ddnD.
# Both are required for control-vector upwinding. See
# the above print() block for further documentation
# on upwinding--both motivation and implementation
# details.
if upwindcontrolvec != "":
for var in list_of_deriv_vars:
if "_dupD" in str(var):
list_of_deriv_vars.append(sp.sympify(str(var).replace("_dupD","_ddnD")))
# Finally, sort the list_of_deriv_vars. This ensures
# consistency in the C code output, and might even be
# tuned to reduce cache misses.
# Thanks to Aaron Meurer for this nice one-liner!
list_of_deriv_vars = sorted(list_of_deriv_vars,key=sp.default_sort_key)
# Step 2:
# Process list_of_deriv_vars into a list of base gridfunctions
# and a list of derivative operators.
# Step 2a:
# First determine the base gridfunction name from
# "list_of_deriv_vars"
deriv__base_gridfunction_name = []
deriv__operator = []
for var in list_of_deriv_vars:
# Step 2a.1: Check that the number of juxtaposed integers
# at the end of a variable name matches the
# number of U's + D's in the variable name:
varstr = str(var)
num_UDs = 0
for i in range(len(varstr)):
if varstr[i] == 'D' or varstr[i] == 'U':
num_UDs += 1
num_digits = 0
i = len(varstr) - 1
while varstr[i].isdigit():
num_digits += 1
i-=1
if num_UDs != num_digits:
print("Error: "+varstr+" has "+str(num_UDs)+" U's and D's, but ")
print(str(num_digits)+" integers at the end. These must be equal.")
print("Please rename your gridfunction.")
sys.exit(1)
# Step 2a.2: Based on the variable name, find the rank of
# the underlying gridfunction of which we're
# trying to take the derivative.
rank = 0 # rank = "number of juxtaposed U's and D's before the underscore in a derivative expression"
underscore_position = -1
for i in range(len(varstr)-1,-1,-1):
if underscore_position > 0 and (varstr[i] == "U" or varstr[i] == "D"):
rank += 1
if varstr[i] == "_":
underscore_position = i
# Step 2a.3: Based on the variable name, find the order
# of the derivative we're trying to take.
deriv_order = 0 # deriv_order = "number of D's after the underscore in a derivative expression"
for i in range(underscore_position+1,len(varstr)):
if (varstr[i] == "D"):
deriv_order += 1
# Step 2a.4: Based on derivative order and rank,
# store the base gridfunction name in
# deriv__base_gridfunction_name[]
deriv__base_gridfunction_name.append(varstr[0:underscore_position]+
varstr[len(varstr)-deriv_order-rank:len(varstr)-deriv_order])
deriv__operator.append(varstr[underscore_position+1:len(varstr)-deriv_order-rank]+
varstr[len(varstr)-deriv_order:len(varstr)])
# Step 2b:
# Then check each base gridfunction to determine whether
# it is indeed registered as a gridfunction.
# If not, exit with error.
for basegf in deriv__base_gridfunction_name:
is_gf = False
for gf in gri.glb_gridfcs_list:
if basegf == str(gf.name):
is_gf = True
pass
if not is_gf:
print("Error: Attempting to take the derivative of "+basegf+", which is not a registered gridfunction.")
print(" Make sure your gridfunction name does not have any underscores in it!")
sys.exit(1)
# Step 2c:
# Check each derivative operator to make sure it is
# supported. If not, error out.
for i in range(len(deriv__operator)):
found_derivID = False
for derivID in ["dD","dupD","ddnD","dKOD"]:
if derivID in deriv__operator[i]:
found_derivID = True
if not found_derivID:
print("Error: Valid derivative operator in "+deriv__operator[i]+" not found.")
sys.exit(1)
# Step 2d (Upwinded derivatives algorithm, part 1):
# If an upwinding control vector is specified, determine
# which of the elements of the vector will be required.
# This ensures that those elements are read from memory.
# For example, if a symmetry axis is specified,
# upwind derivatives with respect to only
# two of the three dimensions are used. Here
# we find all directions used for upwinding.
if upwindcontrolvec != "":
upwind_directions_unsorted_withdups = []
for deriv_op in deriv__operator:
if "dupD" in deriv_op:
if deriv_op[len(deriv_op)-1].isdigit():
dirn = int(deriv_op[len(deriv_op)-1])
upwind_directions_unsorted_withdups.append(dirn)
else:
print("Error: Derivative operator "+deriv_op+" does not contain a direction")
sys.exit(1)
upwind_directions = []
if len(upwind_directions_unsorted_withdups)>0:
upwind_directions = superfast_uniq(upwind_directions_unsorted_withdups)
upwind_directions = sorted(upwind_directions,key=sp.default_sort_key)
# Step 3:
# Evaluate the finite difference stencil for each
# derivative operator,
# TODO: being careful not to needlessly recompute.
# Note: Each finite difference stencil consists
# of two parts:
# 1) The coefficient, and
# 2) The index corresponding to the coefficient.
# The former is stored as a rational number, and
# the latter as a simple string, such that e.g.,
# in 3D, the empty string corresponds to (i,j,k),
# the string "ip1" corresponds to (i+1,j,k),
# the string "ip1kp1" corresponds to (i+1,j,k+1),
# etc.
fdcoeffs = [[] for i in range(len(deriv__operator))]
fdstencl = [[[] for i in range(4)] for j in range(len(deriv__operator))]
for i in range(len(deriv__operator)):
fdcoeffs[i], fdstencl[i] = compute_fdcoeffs_fdstencl(deriv__operator[i])
# Step 4:
# Create C code to read gridfunctions from memory
# Step 4a: Compile list of points to read from memory
# for each gridfunction i, based on list
# provided in fdstencil[i][].
list_of_points_read_from_memory_with_duplicates = [[] for i in range(len(gri.glb_gridfcs_list))]
for j in range(len(deriv__base_gridfunction_name)):
derivgfname = deriv__base_gridfunction_name[j]
# Next find the corresponding gridfunction index:
for i in range(len(gri.glb_gridfcs_list)):
gfname = gri.glb_gridfcs_list[i].name
# If the gridfunction for the derivative matches, then
# add to the list of points read from memory:
if derivgfname == gfname:
for k in range(len(fdstencl[j])):
list_of_points_read_from_memory_with_duplicates[i].append(str(fdstencl[j][k][0]) + "," + \
str(fdstencl[j][k][1]) + "," + \
str(fdstencl[j][k][2]) + "," + \
str(fdstencl[j][k][3]))
# Step 4b: "Zeroth derivative" case:
# If gridfunction appears in expression not
# as derivative (i.e., by itself), it must
# be read from memory as well.
for expr in range(len(sympyexpr_list)):
for var in sympyexpr_list[expr].rhs.free_symbols:
vartype = gri.variable_type(var)
if vartype == "gridfunction":
for i in range(len(gri.glb_gridfcs_list)):
gfname = gri.glb_gridfcs_list[i].name
if gfname == str(var):
list_of_points_read_from_memory_with_duplicates[i].append("0,0,0,0")
# Step 4c: Remove duplicates when reading from memory;
# do not needlessly read the same variable
# from memory twice.
list_of_points_read_from_memory = [[] for i in range(len(gri.glb_gridfcs_list))]
for i in range(len(gri.glb_gridfcs_list)):
list_of_points_read_from_memory[i] = superfast_uniq(list_of_points_read_from_memory_with_duplicates[i])
# Step 4d: Minimize cache misses:
# Sort the list of points read from
# main memory by how they are stored
# in memory.
# Step 4d.i: Define a function that maps a gridpoint
# index (i,j,k,l) to a unique memory "address",
# which will correspond to the correct ordering
# of actual memory addresses.
#
# Input: a list of 4 indices, e.g., (i,j,k,l)
# corresponding to a gridpoint's *spatial*
# index in memory (thus we support up to
# 4D in space). If spatial dimension is
# less than 4D, then just set latter
# index/indices to zero. E.g., for 2D
# spatial indexing, set (i,j,0,0).
# Output: a single number, which when sorted
# will yield a unique "address" in memory
# such that consecutive addresses are
# consecutive in memory.
def unique_idx(idx4):
# os and sz are set *just for the purposes of ensuring indices are ordered in memory*
# Do not modify the values of os and sz.
os = 50 # offset
sz = 100 # assumed size in each direction
if par.parval_from_str("MemAllocStyle") == "210":
return str(int(idx4[0])+os + sz*( (int(idx4[1])+os) + sz*( (int(idx4[2])+os) + sz*( int(idx4[3])+os ) ) ))
elif par.parval_from_str("MemAllocStyle") == "012":
return str(int(idx4[3])+os + sz*( (int(idx4[2])+os) + sz*( (int(idx4[1])+os) + sz*( int(idx4[0])+os ) ) ))
else:
print("Error: MemAllocStyle = "+par.parval_from_str("MemAllocStyle")+" unsupported.")
sys.exit(1)
# Step 4d.ii: For each gridfunction and
# point read from memory, call unique_idx,
# then sort according to memory "address"
# Input: list_of_points_read_from_memory[gridfunction][point],
# gri.glb_gridfcs_list[gridfunction]
# Output: 1) A list of points to be read from
# memory, sorted according to memory
# "address":
# sorted_list_of_points_read_from_memory[gridfunction][point]
# 2) A list containing the gridfunction
# read at each point, with the number
# of elements corresponding exactly
# to the total number of points read
# from memory for all gridfunctions:
# read_from_memory_gf[]
read_from_memory_gf = []
sorted_list_of_points_read_from_memory = [[] for i in range(len(gri.glb_gridfcs_list))]
for gfidx in range(len(gri.glb_gridfcs_list)):
# Continue only if reading at least one point of gfidx from memory.
# The sorting algorithm at the end of this code block is not
# well-defined (will throw an error) if no points of gfidx are
# read from memory.
if len(list_of_points_read_from_memory[gfidx]) > 0:
read_from_memory_index = []
for idx in list_of_points_read_from_memory[gfidx]:
read_from_memory_gf.append(gri.glb_gridfcs_list[gfidx])
idxsplit = idx.split(',')
idx4 = [int(idxsplit[0]),int(idxsplit[1]),int(idxsplit[2]),int(idxsplit[3])]
read_from_memory_index.append(unique_idx(idx4))
# https://stackoverflow.com/questions/13668393/python-sorting-two-lists
UNUSEDlist, sorted_list_of_points_read_from_memory[gfidx] = \
[list(x) for x in zip(*sorted(zip(read_from_memory_index, list_of_points_read_from_memory[gfidx]),
key=itemgetter(0)))]
# Step 4e: Create the full C code string
# for reading from memory:
# if DIM==4:
# input: [i,j,k,l]
# output: "i0+i,i1+j,i2+k,i3+l"
# if DIM==3:
# input: [i,j,k,l]
# output: "i0+i,i1+j,i2+k"
# etc.
def ijkl_string(idx4):
DIM = par.parval_from_str("DIM")
retstring = ""
for i in range(DIM):
if i>0:
# Add a comma
retstring += ","
retstring += "i"+str(i)+"+"+str(idx4[i])
return retstring.replace("+-", "-").replace("+0", "")
def out__type_var(in_var,AddPrefix_for_UpDownWindVars=True):
varname = str(in_var)
# Disable prefixing upwinded and downwinded variables
# if the upwind control vector algorithm is disabled.
if upwindcontrolvec == "":
AddPrefix_for_UpDownWindVars = False
if AddPrefix_for_UpDownWindVars:
if "_dupD" in varname: # Variables suffixed with "_dupD" are set
# to be the "pure" upwinded derivative,
# before the upwinding algorithm has been
# applied. However, when they are used
# in the RHS expressions, it is assumed
# that the up. algorithm has been applied.
# To ensure consistency we rename all
# _dupD suffixed variables as
# _dupDPUREUPWIND, and use them as input
# into the upwinding algorithm. The output
# will be the original _dupD variable.
varname = "UpwindAlgInput"+varname
if "_ddnD" in varname: # For consistency with _dupD
varname = "UpwindAlgInput"+varname
if outCparams.SIMD_enable == "True":
return "const REAL_SIMD_ARRAY " + varname
else:
TYPE = par.parval_from_str("PRECISION")
return "const "+ TYPE + " " + varname
def varsuffix(idx4):
if idx4 == [0,0,0,0]:
return ""
return "_"+ijkl_string(idx4).replace(",","_").replace("+","p").replace("-","m")
def read_from_memory_Ccode_onept(gfname,idx):
idxsplit = idx.split(',')
idx4 = [int(idxsplit[0]),int(idxsplit[1]),int(idxsplit[2]),int(idxsplit[3])]
gf_array_name = "in_gfs" # Default array name.
gfaccess_str = gri.gfaccess(gf_array_name,gfname,ijkl_string(idx4))
if outCparams.SIMD_enable == "True":
retstring = out__type_var(gfname) + varsuffix(idx4) +" = ReadSIMD(&" + gfaccess_str + ");"
else:
retstring = out__type_var(gfname) + varsuffix(idx4) +" = " + gfaccess_str + ";"
return retstring+"\n"
read_from_memory_Ccode = ""
count = 0
for gfidx in range(len(gri.glb_gridfcs_list)):
for pt in range(len(sorted_list_of_points_read_from_memory[gfidx])):
read_from_memory_Ccode += read_from_memory_Ccode_onept(read_from_memory_gf[count].name,
sorted_list_of_points_read_from_memory[gfidx][pt])
count += 1
# Step 5: Output C code. C code consists of three parts
# a) Read gridfunctions from memory at needed pts.
# b) Perform arithmetic needed for input expressions
# provided in sympyexpr_list[].rhs and associated
# finite differences.
# c) Write output to gridfunctions specified in
# sympyexpr_list[].lhs.
def indent_Ccode(Ccode):
Ccodesplit = Ccode.splitlines()
outstring = ""
for i in range(len(Ccodesplit)):
outstring += outCparams.preindent+indent+Ccodesplit[i]+'\n'
return outstring
# Step 5a: Read gridfunctions from memory at needed pts.
# *** No need to do anything here; already set in
# string "read_from_memory_Ccode". ***
# FIXME: Update these code comments:
# Step 5b: Perform arithmetic needed for finite differences
# associated with input expressions provided in
# sympyexpr_list[].rhs.
# Note that exprs and lhsvarnames contain
# i) finite difference expressions (constructed
# in steps above) and associated variable names,
# and
# ii) Input expressions sympyexpr_list[], which
# in general depend on finite difference
# variables.
exprs = []
lhsvarnames = []
# Step 5b.i: Output finite difference expressions to
# Coutput string
for i in range(len(list_of_deriv_vars)):
exprs.append(sp.sympify(0)) # Append a new element to the list of derivative expressions.
lhsvarnames.append(out__type_var(list_of_deriv_vars[i]))
var = deriv__base_gridfunction_name[i]
for j in range(len(fdcoeffs[i])):
varname = str(var)+varsuffix(fdstencl[i][j])
exprs[i] += fdcoeffs[i][j]*sp.sympify(varname)
# Multiply each expression by the appropriate power
# of 1/dx[i]
invdx = []
for d in range(par.parval_from_str("DIM")):
invdx.append(sp.sympify("invdx"+str(d)))
# First-order or Kreiss-Oliger derivatives:
if (len(deriv__operator[i]) == 5 and "dKOD" in deriv__operator[i]) or \
(len(deriv__operator[i]) == 3 and "dD" in deriv__operator[i]) or \
(len(deriv__operator[i]) == 5 and ("dupD" in deriv__operator[i] or "ddnD" in deriv__operator[i])):
dirn = int(deriv__operator[i][len(deriv__operator[i])-1])
exprs[i] *= invdx[dirn]
# Second-order derivs:
elif len(deriv__operator[i]) == 5 and "dDD" in deriv__operator[i]:
dirn1 = int(deriv__operator[i][len(deriv__operator[i]) - 2])
dirn2 = int(deriv__operator[i][len(deriv__operator[i]) - 1])
exprs[i] *= invdx[dirn1]*invdx[dirn2]
else:
print("Error: was unable to parse derivative operator: ",deriv__operator[i])
sys.exit(1)
# Step 5b.ii: If upwind control vector is specified,
# add upwind control vectors to the
# derivative expression list, so its
# needed elements are read from memory.
if upwindcontrolvec != "":
for i in range(len(upwind_directions)):
exprs.append(upwindcontrolvec[upwind_directions[i]])
lhsvarnames.append(out__type_var("UpwindControlVectorU"+str(upwind_directions[i])))
# Step 5b.iii: Output useful code comment regarding
# which step we are on. *At most* this
# is a 3-step process:
# 1. Read from memory & compute FD stencils,
# 2. Perform upwinding, and
# 3. Evaluate remaining expressions+write
# results to main memory.
NRPy_FD_StepNumber = 1
NRPy_FD__Number_of_Steps = 1
if len(list_of_deriv_vars) > 0:
NRPy_FD__Number_of_Steps += 1
if upwindcontrolvec != "" and len(upwind_directions) > 0:
NRPy_FD__Number_of_Steps += 1
if len(read_from_memory_Ccode) > 0:
Coutput += indent_Ccode("/* \n * NRPy+ Finite Difference Code Generation, Step "
+ str(NRPy_FD_StepNumber) + " of " + str(NRPy_FD__Number_of_Steps)+
": Read from main memory and compute finite difference stencils:\n */\n")
NRPy_FD_StepNumber = NRPy_FD_StepNumber + 1
default_CSE_varprefix = outCparams.CSE_varprefix
# Prefix chosen CSE variables with "FD", for the finite difference coefficients:
Coutput += indent_Ccode(outputC(exprs,lhsvarnames,"returnstring",params=params + ",CSE_varprefix="+default_CSE_varprefix+"FD,includebraces=False,SIMD_const_suffix=_FDcoeff",
prestring=read_from_memory_Ccode))
# Step 5b.iv: Implement control-vector upwinding algorithm.
if upwindcontrolvec != "":
if len(upwind_directions) > 0:
Coutput += indent_Ccode("/* \n * NRPy+ Finite Difference Code Generation, Step "
+ str(NRPy_FD_StepNumber) + " of " + str(NRPy_FD__Number_of_Steps) +
": Implement upwinding algorithm:\n */\n")
NRPy_FD_StepNumber = NRPy_FD_StepNumber + 1
if outCparams.SIMD_enable == "True":
Coutput += """
const double tmp_upwind_Integer_1 = 1.000000000000000000000000000000000;
const REAL_SIMD_ARRAY upwind_Integer_1 = ConstSIMD(tmp_upwind_Integer_1);
const double tmp_upwind_Integer_0 = 0.000000000000000000000000000000000;
const REAL_SIMD_ARRAY upwind_Integer_0 = ConstSIMD(tmp_upwind_Integer_0);
"""
for dirn in upwind_directions:
Coutput += indent_Ccode(out__type_var("UpWind" + str(dirn)) + " = UPWIND_ALG(UpwindControlVectorU" + str(dirn) + ");\n")
upwindU = [sp.sympify(0) for i in range(par.parval_from_str("DIM"))]
for dirn in upwind_directions:
upwindU[dirn] = sp.sympify("UpWind"+str(dirn))
for i in range(len(list_of_deriv_vars)):
if len(deriv__operator[i]) == 5 and ("dupD" in deriv__operator[i]):
var_dupD = sp.sympify("UpwindAlgInput"+str(list_of_deriv_vars[i]))
var_ddnD = sp.sympify("UpwindAlgInput"+str(list_of_deriv_vars[i]).replace("_dupD","_ddnD"))
upwind_dirn = int(deriv__operator[i][len(deriv__operator[i])-1])
upwind_expr = upwindU[upwind_dirn]*(var_dupD - var_ddnD) + var_ddnD
# For convenience, we require out__type_var() above to
# prefix up/downwinded variables with "UpwindAlgInput".
# Here we do not wish to have this prefix.
Coutput += indent_Ccode(outputC(upwind_expr,
out__type_var(str(list_of_deriv_vars[i]),AddPrefix_for_UpDownWindVars=False),
"returnstring",params=params + ",includebraces=False"))
# Step 5b.v: Add input RHS & LHS expressions from
# sympyexpr_list[]
Coutput += indent_Ccode("/* \n * NRPy+ Finite Difference Code Generation, Step "
+ str(NRPy_FD_StepNumber) + " of " + str(NRPy_FD__Number_of_Steps) +
": Evaluate SymPy expressions and write to main memory:\n */\n")
exprs = []
lhsvarnames = []
for i in range(len(sympyexpr_list)):
exprs.append(sympyexpr_list[i].rhs)
if outCparams.SIMD_enable == "True":
lhsvarnames.append("const REAL_SIMD_ARRAY __RHS_exp_"+str(i))
else:
lhsvarnames.append(sympyexpr_list[i].lhs)
# Step 5c: Write output to gridfunctions specified in
# sympyexpr_list[].lhs.
write_to_mem_string = ""
if outCparams.SIMD_enable == "True":
for i in range(len(sympyexpr_list)):
write_to_mem_string += "WriteSIMD(&"+sympyexpr_list[i].lhs+", __RHS_exp_"+str(i)+");\n"
Coutput += indent_Ccode(outputC(exprs,lhsvarnames,"returnstring", params = params+",includebraces=False,preindent=0", prestring="",poststring=write_to_mem_string))
# Step 6: Add consistent indentation to the output end brace.
# See Step 0.b for corresponding start brace.
if outCparams.includebraces == "True":
Coutput += outCparams.preindent+"}\n"
# Step 7: Output the C code in desired format: stdout, string, or file.
if filename == "stdout":
print(Coutput)
elif filename == "returnstring":
return Coutput+'\n'
else:
# Output to the file specified by outCfilename
with open(filename, outCparams.outCfileaccess) as file:
file.write(Coutput)
successstr = ""
if outCparams.outCfileaccess == "a":
successstr = "Appended "
elif outCparams.outCfileaccess == "w":
successstr = "Wrote "
print(successstr + "to file \"" + filename + "\"")
# print(gri.glb_gridfcs_list[1].name,list_of_points_read_from_memory[1])
# Define the to-be-inverted matrix, A.
# We define A row-by-row, according to the prescription
# derived in notes/notes.pdf, via the following pattern
# that applies for arbitrary order.
#
# As an example, consider a 5-point finite difference
# stencil (4th-order accurate), where we wish to compute
# some derivative at the center point.
#
# Then A is given by:
#
# -2^0 -1^0 1 1^0 2^0
# -2^1 -1^1 0 1^1 2^1
# -2^2 -1^2 0 1^2 2^2
# -2^3 -1^3 0 1^3 2^3
# -2^4 -1^4 0 1^4 2^4
#
# Then right-multiplying A^{-1}
# by (1 0 0 0 0)^T will yield 0th deriv. stencil
# by (0 1 0 0 0)^T will yield 1st deriv. stencil
# by (0 0 1 0 0)^T will yield 2nd deriv. stencil
# etc.
#
# Next suppose we want an upwinded, 4th-order accurate
# stencil. For this case, A is given by:
#
# -1^0 1 1^0 2^0 3^0
# -1^1 0 1^1 2^1 3^1
# -1^2 0 1^2 2^2 3^2
# -1^3 0 1^3 2^3 3^3
# -1^4 0 1^4 2^4 3^4
#
# ... and similarly for the downwinded derivative.
#
# Finally, let's consider a 3rd-order accurate
# stencil. This would correspond to an in-place
# upwind stencil with stencil radius of 2 gridpoints,
# where other, centered derivatives are 4th-order
# accurate. For this case, A is given by:
#
# -1^0 1 1^0 2^0
# -1^1 0 1^1 2^1
# -1^2 0 1^2 2^2
# -1^3 0 1^3 2^3
# -1^4 0 1^4 2^4
#
# ... and similarly for the downwinded derivative.
#
# The general pattern is as follows:
#
# 1) The top row is all 1's,
# 2) If the second row has N elements (N must be odd),
# .... then the radius of the stencil is rs = (N-1)/2
# .... and the j'th row e_j = j-rs-1. For example,
# .... for 4th order, we have rs = 2
# .... j | element
# .... 1 | -2
# .... 2 | -1
# .... 3 | 0
# .... 4 | 1
# .... 5 | 2
# 3) The L'th row, L>2 will be the same as the second
# .... row, but with each element e_j -> e_j^(L-1)
# A1 is used later to validate the inverted
# matrix.
def compute_fdcoeffs_fdstencl(derivstring,FDORDER=-1):
# Step 0: Set finite differencing order, stencil size, and up/downwinding
if FDORDER == -1:
FDORDER = par.parval_from_str("FD_CENTDERIVS_ORDER")
if "dKOD" in derivstring:
FDORDER += par.parval_from_str("FD_KO_ORDER__CENTDERIVS_PLUS")
STENCILSIZE = FDORDER+1
UPDOWNWIND = 0
if "dupD" in derivstring:
UPDOWNWIND = 1
elif "ddnD" in derivstring:
UPDOWNWIND = -1
# Step 1: Set up matrix based on the stencil size (FDORDER+1).
# See documentation above for details on how this
# matrix is set up.
M = sp.zeros(STENCILSIZE,STENCILSIZE)
for i in range(STENCILSIZE):
for j in range(STENCILSIZE):
if i == 0:
M[(i,j)] = 1 # Setting n^0 = 1 for all n, including n=0, because this matches the pattern
else:
dist_from_xeq0_col = j - sp.Rational((STENCILSIZE - 1),2) + UPDOWNWIND
if dist_from_xeq0_col==0:
M[(i,j)] = 0
else:
M[(i, j)] = dist_from_xeq0_col**(i)
Minv = sp.zeros(STENCILSIZE,STENCILSIZE)
Minv = M**(-1)
# Step 2:
# Based on the input derivative string,
# pick out the relevant row of the matrix
# inverse, as outlined in the detailed code
# comments prior to this function definition.
derivtype = "FirstDeriv"
matrixrow = 1
if "DDD" in derivstring:
print("Error: Only derivatives up to second order currently supported.")
print(" Feel free to contribute to NRPy+ to extend its functionality!")
sys.exit(1)
elif "DD" in derivstring:
if derivstring[len(derivstring)-1] == derivstring[len(derivstring)-2]:
# Assuming i==j, we call \partial_i \partial_j gf an "unmixed" second derivative,
# or more simply, just "SecondDeriv":
derivtype = "SecondDeriv"
matrixrow = 2
else:
# Assuming i!=j, we call \partial_i \partial_j gf a MIXED second derivative,
# which is computed using a composite of first derivative operations.
derivtype = "MixedSecondDeriv"
elif "dKOD" in derivstring:
derivtype = "KreissOligerDeriv"
matrixrow = STENCILSIZE - 1
else:
# Up/downwinded and first derivs are all of "FirstDeriv" type
pass
# Step 3:
# Set finite difference coefficients
# and stencil points corresponding to
# each finite difference coefficient.
fdcoeffs = []
fdstencl = []
if derivtype != "MixedSecondDeriv":
for i in range(STENCILSIZE):
idx4 = [0, 0, 0, 0]
# First compute finite difference coefficient.
fdcoeff = sp.factorial(matrixrow)*Minv[(i,matrixrow)]
# Do not store fdcoeff or fdstencil if
# finite difference coefficient is zero.
if fdcoeff != 0:
fdcoeffs.append(fdcoeff)
if derivtype == "KreissOligerDeriv":
fdcoeffs[i] *= (-1)**(sp.Rational((STENCILSIZE+1),2))/2**matrixrow
# Next store finite difference stencil point
# corresponding to coefficient.
gridpt_posn = i - int((STENCILSIZE-1)/2) + UPDOWNWIND
if gridpt_posn != 0:
dirn = int(derivstring[len(derivstring)-1])
idx4[dirn] = gridpt_posn
fdstencl.append(idx4)
else:
# Mixed second derivative finite difference coeffs
# consist of products of first deriv coeffs,
# defined in first Minv matrix row.
for i in range(STENCILSIZE):
for j in range(STENCILSIZE):
idx4 = [0, 0, 0, 0]
# First compute finite difference coefficient.
fdcoeff = (sp.factorial(matrixrow)*Minv[(i,matrixrow)]) * \
(sp.factorial(matrixrow)*Minv[(j,matrixrow)])
# Do not store fdcoeff or fdstencil if
# finite difference coefficient is zero.
if fdcoeff != 0:
fdcoeffs.append(fdcoeff)
# Next store finite difference stencil point
# corresponding to coefficient.
gridpt_posn1 = i - int((STENCILSIZE - 1) / 2)
gridpt_posn2 = j - int((STENCILSIZE - 1) / 2)
dirn1 = int(derivstring[len(derivstring) - 1])
dirn2 = int(derivstring[len(derivstring) - 2])
idx4[dirn1] = gridpt_posn1
idx4[dirn2] = gridpt_posn2
fdstencl.append(idx4)
return fdcoeffs,fdstencl
# grid.py: functions & parameters related to numerical grids
# functions: Automatic loop output, output C code needed for gridfunction memory I/O, gridfunction registration
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
import NRPy_param_funcs as par # NRPy+: Parameter interface
import sympy as sp # Import SymPy, a computer algebra system written entirely in Python
from collections import namedtuple # Standard Python `collections` module: defines named tuples data structure
# Initialize globals related to the grid
glb_gridfcs_list = []
glb_gridfc = namedtuple('gridfunction', 'gftype name rank DIM')
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))
Nxx = par.Cparameters("int", thismodule,["Nxx0","Nxx1","Nxx2"],[64,32,64]) # Default to 64x32x64 grid
Nxx_plus_2NGHOSTS = par.Cparameters("int", thismodule,
["Nxx_plus_2NGHOSTS0","Nxx_plus_2NGHOSTS1","Nxx_plus_2NGHOSTS2"],
[ 70, 38, 70]) # Default to 64x32x64 grid w/ NGHOSTS=3
xx = par.Cparameters("REAL",thismodule,[ "xx0", "xx1", "xx2"],1e300) # These are C variables, not parameters, and
# will be overwritten; best to initialize to crazy
# number to ensure they are overwritten!
dxx = par.Cparameters("REAL",thismodule,[ "dxx0", "dxx1", "dxx2"],0.1)
invdx = par.Cparameters("REAL",thismodule,[ "invdx0", "invdx1", "invdx2"],1.0)
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
for paramname in range(len(par.glb_Cparams_list)):
if str(var) == par.glb_Cparams_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.")
sys.exit(1)
if not (var_is_parameter or var_is_gf):
return "other"
if var_is_parameter:
return "Cparameter"
if var_is_gf:
return "gridfunction"
def find_gftype(varname):
for gf in glb_gridfcs_list:
if gf.name == varname:
return gf.gftype
def gfaccess(gfarrayname = "", varname = "", ijklstring = ""):
found_registered_gf = False
for gf in glb_gridfcs_list:
if gf.name == varname:
if found_registered_gf:
print("Error: found duplicate gridfunction name: "+gf.name)
sys.exit(1)
found_registered_gf = True
if not found_registered_gf:
print("Error: gridfunction \""+varname+"\" is not registered!")
sys.exit(1)
gftype = find_gftype(varname)
DIM = par.parval_from_str("DIM")
retstring = ""
if par.parval_from_str("GridFuncMemAccess") == "SENRlike":
if gfarrayname == "":
print("Error: GridFuncMemAccess = SENRlike requires gfarrayname be passed to gfaccess()")
sys.exit(1)
# FIXME: if gftype == "AUX" then override gfarrayname to aux_gfs[].
# This enables expressions containing a mixture of AUX and EVOL
# gridfunctions, though in a slightly hacky way.
if gftype == "AUX":
gfarrayname = "aux_gfs"
elif gftype == "AUXEVOL":
gfarrayname = "auxevol_gfs"
# Return gfarrayname[IDX3(varname,i0)] for DIM=1, gfarrayname[IDX3(varname,i0,i1)] for DIM=2, etc.
retstring += gfarrayname + "[IDX" + str(DIM+1) + "(" + varname.upper()+"GF" + ", "
elif par.parval_from_str("GridFuncMemAccess") == "ETK":
# Return varname[CCTK_GFINDEX3D(i0,i1,i2)] for DIM=3. Error otherwise
if DIM != 3:
print("Error: GridFuncMemAccess = ETK currently requires that gridfunctions be 3D. Can be easily extended.")
sys.exit(1)
if gfarrayname == "rhs_gfs":
retstring += varname + "_rhsGF" + "[CCTK_GFINDEX"+str(DIM)+"D(cctkGH, "
else:
retstring += varname + "GF" + "[CCTK_GFINDEX"+str(DIM)+"D(cctkGH, "
elif par.parval_from_str("GridFuncMemAccess") == "CarpetX":
# Return varname_.ptr[varname_.offset(i0,i1,i2,...)].
if gfarrayname == "rhs_gfs":
retstring += varname + "_rhsGF" + "_.ptr[" + varname + "_rhsGF_.offset(";
else:
retstring += varname + "GF" + "_.ptr[" + varname + "GF_.offset("
else:
print("grid::GridFuncMemAccess = "+par.parval_from_str("GridFuncMemAccess")+" not supported")
sys.exit(1)
if ijklstring == "":
for i in range(DIM):
retstring += "i"+str(i)
if i != DIM-1:
retstring += ', '
else:
retstring += ijklstring
return retstring + ")]"
# Gridfunction basenames cannot end in integers.
# For example, rank-1 gridfunction vecU1 has
# basename "vecU". Thus this gridfunction has
# a valid basename. If we instead defined a
# scalar gridfunction "u2", this would have a
# basename of "u2" -- not a valid basename.
# Being so strict enables us to determine
# quickly what a gridfunction is by its name
# alone, which is useful, e.g., when setting
# up parity boundary conditions.
def verify_gridfunction_basename_is_valid(gf_basename):
# First check for zero-length basenames:
if len(gf_basename) == 0:
print("Error: tried to register gridfunction without a name!")
sys.exit(1)
# https://stackoverflow.com/questions/1303243/how-to-find-out-if-a-python-object-is-a-string
if sys.version_info[0] < 3:
if not isinstance(gf_basename,basestring):
print("ERROR: gf_names must be strings")
sys.exit(1)
else:
if not isinstance(gf_basename, str):
print("ERROR: gf_names must be strings")
sys.exit(1)
if len(gf_basename) > 0 and gf_basename[-1].isdigit():
print("Error: tried to register gridfunction with base name: "+gf_basename)
print(" Gridfunctions with base names ending in an integer is forbidden; pick a new name.")
sys.exit(1)
import sys
def register_gridfunctions(gf_type,gf_names,rank=0,is_indexed=False,DIM=3):
# Step 0: Sanity check
if (rank > 0 and is_indexed == False) or (rank == 0 and is_indexed == True):
print("Error: Attempted to register *indexed* gridfunction(s) with rank 0, or *scalar* gridfunctions with rank>0.")
print(" Gridfunctions = ",gf_names)
sys.exit(1)
# Step 1: convert gf_names to a list if it's not already a list
if type(gf_names) is not list:
gf_namestmp = [gf_names]
gf_names = gf_namestmp
# Step 2: if the gridfunction is not indexed, then
# gf_names == base names. Check that the
# gridfunction basenames are valid:
if is_indexed==False:
for i in range(len(gf_names)):
verify_gridfunction_basename_is_valid(gf_names[i])
# Step 3: Verify that gridfunction type is valid.
if not (gf_type == "EVOL" or gf_type == "AUX" or gf_type == "AUXEVOL"):
print("Error in registering gridfunction(s) with unsupported type "+gf_type+".")
print("Supported types include \"EVOL\" for gridfunctions related to evolved quantities or \"AUX\" for all others.")
sys.exit(1)
# Step 4: Check for duplicate grid function registrations. If:
# a) A duplicate is found, error out. Otherwise
# b) Append to list of gridfunctions, stored in glb_gridfcs_list[].
for i in range(len(gf_names)):
for j in range(len(glb_gridfcs_list)):
if gf_names[i] == glb_gridfcs_list[j].name:
print("Error: Tried to register the gridfunction \""+gf_names[i]+"\" twice (ignored type)\n\n")
sys.exit(1)
# If no duplicate found, append to "gridfunctions" list:
glb_gridfcs_list.append(glb_gridfc(gf_type,gf_names[i],rank,DIM))
# Step 5: Return SymPy object corresponding to symbol or
# list of symbols representing gridfunction in
# SymPy expression
OBJ_TMPS = []
for i in range(len(gf_names)):
OBJ_TMPS.append(sp.symbols(gf_names[i], real=True))
# OBJ_TMPS.append(sp.sympify(gf_names[i]))
if len(gf_names)==1:
return OBJ_TMPS[0]
return OBJ_TMPS
# Given output directory "outdir" as input, the
# following function outputs a file called
# "outdir/gridfunction_defines.h", which
# #define's all the gridfunction aliases, and
# returns two lists, corresponding to the
# names (strings) of the evolved and auxiliary
# gridfunction names respectively.
#
# For example, if we define only two gridfunctions uu and vv,
# which are evolved quantities (i.e., represent
# the solution of the PDEs we are solving and are
# registered with gftype == "EVOL"), then
# this function will create a file with the following
# content:
#
# | /* This file is automatically generated by NRPy+. Do not edit. */
# | /* EVOLVED VARIABLES: */
# | #define NUM_EVOL_GFS 2
# | #define UUGF 0
# | #define VVGF 1
# |
# | /* AUXILIARY VARIABLES: */
# | #define NUM_AUX_GFS 0
#
# The function will return two lists: the lists of
# EVOL and AUX gridfunction names, respectively.
# For this example, the first list (all gridfunctions
# registered as EVOL) will be ["uu","vv"], and the
# second (all gridfunctions registered as AUX) will
# be the empty list: []
def gridfunction_lists():
evolved_variables_list = []
auxiliary_variables_list = []
auxevol_variables_list = []
for i in range(len(glb_gridfcs_list)):
if glb_gridfcs_list[i].gftype == "EVOL":
evolved_variables_list.append(glb_gridfcs_list[i].name)
if glb_gridfcs_list[i].gftype == "AUX":
auxiliary_variables_list.append(glb_gridfcs_list[i].name)
if glb_gridfcs_list[i].gftype == "AUXEVOL":
auxevol_variables_list.append(glb_gridfcs_list[i].name)
# Next we alphabetize the lists
evolved_variables_list.sort()
auxiliary_variables_list.sort()
auxevol_variables_list.sort()
return evolved_variables_list,auxiliary_variables_list,auxevol_variables_list
def output__gridfunction_defines_h__return_gf_lists(outdir):
evolved_variables_list, auxiliary_variables_list, auxevol_variables_list = gridfunction_lists()
# Finally we set up the #define statements:
with open(outdir+"/gridfunction_defines.h", "w") as file:
file.write("/* This file is automatically generated by NRPy+. Do not edit. */\n\n")
file.write("/* EVOLVED VARIABLES: */\n")
file.write("#define NUM_EVOL_GFS "+str(len(evolved_variables_list))+"\n")
for i in range(len(evolved_variables_list)):
file.write("#define "+evolved_variables_list[i].upper()+"GF\t"+str(i)+"\n")
file.write("\n\n /* AUXILIARY VARIABLES: */\n")
file.write("#define NUM_AUX_GFS " + str(len(auxiliary_variables_list)) + "\n")
for i in range(len(auxiliary_variables_list)):
file.write("#define " + auxiliary_variables_list[i].upper() + "GF\t" + str(i) + "\n")
file.write("\n\n /* AUXEVOL VARIABLES: */\n")
file.write("#define NUM_AUXEVOL_GFS "+str(len(auxevol_variables_list))+"\n")
for i in range(len(auxevol_variables_list)):
file.write("#define "+auxevol_variables_list[i].upper()+"GF\t"+str(i)+"\n")
return evolved_variables_list,auxiliary_variables_list,auxevol_variables_list
# indexedexp.py: functions related to indexed expressions,
# including e.g., tensors and pseudotensors:
# Step 1: Load needed modules
import NRPy_param_funcs as par
import grid as gri
import sympy as sp
import sys
thismodule = __name__
par.initialize_param(par.glb_param("char", thismodule, "symmetry_axes", ""))
def zerorank1(DIM=-1):
if DIM == -1:
DIM = par.parval_from_str("DIM")
return [sp.sympify(0) for i in range(DIM)]
def zerorank2(DIM=-1):
if DIM == -1:
DIM = par.parval_from_str("DIM")
return [[sp.sympify(0) for i in range(DIM)] for j in range(DIM)]
def zerorank3(DIM=-1):
if DIM == -1:
DIM = par.parval_from_str("DIM")
return [[[sp.sympify(0) for i in range(DIM)] for j in range(DIM)] for k in range(DIM)]
def zerorank4(DIM=-1):
if DIM == -1:
DIM = par.parval_from_str("DIM")
return [[[[sp.sympify(0) for i in range(DIM)] for j in range(DIM)] for k in range(DIM)] for l in range(DIM)]
def apply_symmetry_condition_to_derivatives(IDX_OBJ):
symmetry_axes = par.parval_from_str("indexedexp::symmetry_axes")
if symmetry_axes == "":
return IDX_OBJ
rank = 1
if isinstance(IDX_OBJ[0], list):
if not isinstance(IDX_OBJ[0][0], list):
rank = 2
elif not isinstance(IDX_OBJ[0][0][0], list):
rank = 3
elif not isinstance(IDX_OBJ[0][0][0][0], list):
rank = 4
else:
print("Error: could not figure out rank for ",IDX_OBJ)
sys.exit(1)
def does_IDXOBJ_perform_derivative_across_symmetry_axis(idxobj_str):
returnval = False
if "_d" in idxobj_str:
# First we find the order of the derivative:
deriv_order = 0
underscore_position = -1000
for i in range(len(idxobj_str)-1):
if idxobj_str[i] == "_" and idxobj_str[i+1]=="d":
# The order of the derivative is given by the number of D's in a row after the _d:
for k in range(i+2,len(idxobj_str)):
if idxobj_str[k] == "D":
deriv_order = deriv_order + 1
if deriv_order > 2:
print("Error. Derivative order > 2 not supported. Found derivative order = "+str(deriv_order))
sys.exit(1)
end_idx_of_idxobj_str = len(idxobj_str)-1
for j in range(end_idx_of_idxobj_str,end_idx_of_idxobj_str-deriv_order,-1):
if idxobj_str[j] in symmetry_axes:
return True
return False
if rank == 1:
DIM = len(IDX_OBJ)
for i0 in range(DIM):
if does_IDXOBJ_perform_derivative_across_symmetry_axis(str(IDX_OBJ[i0])) == True:
IDX_OBJ[i0] = sp.sympify(0)
if rank == 2:
DIM = len(IDX_OBJ[0])
for i0 in range(DIM):
for i1 in range(DIM):
if does_IDXOBJ_perform_derivative_across_symmetry_axis(str(IDX_OBJ[i0][i1])) == True:
IDX_OBJ[i0][i1] = sp.sympify(0)
if rank == 3:
DIM = len(IDX_OBJ[0][0])
for i0 in range(DIM):
for i1 in range(DIM):
for i2 in range(DIM):
if does_IDXOBJ_perform_derivative_across_symmetry_axis(str(IDX_OBJ[i0][i1][i2])) == True:
IDX_OBJ[i0][i1][i2] = sp.sympify(0)
if rank == 4:
DIM = len(IDX_OBJ[0][0][0])
for i0 in range(DIM):
for i1 in range(DIM):
for i2 in range(DIM):
for i3 in range(DIM):
if does_IDXOBJ_perform_derivative_across_symmetry_axis(str(IDX_OBJ[i0][i1][i2][i3])) == True:
IDX_OBJ[i0][i1][i2][i3] = sp.sympify(0)
return IDX_OBJ
def declarerank1(objname, DIM=-1):
if DIM==-1:
DIM = par.parval_from_str("DIM")
IDX_OBJ_TMP = [sp.sympify(objname + str(i)) for i in range(DIM)]
return apply_symmetry_condition_to_derivatives(IDX_OBJ_TMP)
def register_gridfunctions_for_single_rank1(gf_type,gf_basename, DIM=-1):
# Step 0: Verify the gridfunction basename is valid:
gri.verify_gridfunction_basename_is_valid(gf_basename)
# Step 1: Declare a list of SymPy variables,
# where IDX_OBJ_TMP[i] = gf_basename+str(i)
IDX_OBJ_TMP = declarerank1(gf_basename, DIM)
# Step 2: Register each gridfunction
if DIM==-1:
DIM = par.parval_from_str("DIM")
gf_list = []
for i in range(DIM):
gf_list.append(str(IDX_OBJ_TMP[i]))
gri.register_gridfunctions(gf_type, gf_list, rank=1, is_indexed=True, DIM=DIM)
# Step 3: Return array of SymPy variables
return IDX_OBJ_TMP
def declarerank2(objname, symmetry_option, DIM=-1):
if DIM==-1:
DIM = par.parval_from_str("DIM")
IDX_OBJ_TMP = [[sp.sympify(objname + str(i) + str(j)) for j in range(DIM)] for i in range(DIM)]
for i in range(DIM):
for j in range(DIM):
if symmetry_option == "sym01":
if (j < i):
# j<i in g_{ij} would indicate, e.g., g_{21}.
# By this convention, we must set
# g_{21} = g_{12}:
IDX_OBJ_TMP[i][j] = IDX_OBJ_TMP[j][i]
elif symmetry_option == "nosym":
pass
else:
print("Error: symmetry option " + symmetry_option + " unsupported.")
sys.exit(1)
return apply_symmetry_condition_to_derivatives(IDX_OBJ_TMP)
def register_gridfunctions_for_single_rank2(gf_type,gf_basename, symmetry_option, DIM=-1):
# Step 0: Verify the gridfunction basename is valid:
gri.verify_gridfunction_basename_is_valid(gf_basename)
# Step 1: Declare a list of lists of SymPy variables,
# where IDX_OBJ_TMP[i][j] = gf_basename+str(i)+str(j)
IDX_OBJ_TMP = declarerank2(gf_basename,symmetry_option, DIM)
# Step 2: register each gridfunction, being careful not
# not to store duplicates due to rank-2 symmetries.
if DIM==-1:
DIM = par.parval_from_str("DIM")
# Register only unique gridfunctions. Otherwise
# rank-2 symmetries might result in duplicates
gf_list = []
for i in range(DIM):
for j in range(DIM):
save = True
for l in range(len(gf_list)):
if gf_list[l] == str(IDX_OBJ_TMP[i][j]):
save = False
if save == True:
gf_list.append(str(IDX_OBJ_TMP[i][j]))
gri.register_gridfunctions(gf_type,gf_list,rank=2, is_indexed=True, DIM=DIM)
# Step 3: Return array of SymPy variables
return IDX_OBJ_TMP
def declarerank3(objname, symmetry_option, DIM=-1):
if DIM==-1:
DIM = par.parval_from_str("DIM")
IDX_OBJ_TMP = [[[sp.sympify(objname + str(i) + str(j) + str(k)) for k in range(DIM)] for j in range(DIM)] for i in range(DIM)]
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
if symmetry_option == "sym01":
if j < i:
IDX_OBJ_TMP[i][j][k] = IDX_OBJ_TMP[j][i][k]
if symmetry_option == "sym12":
if k < j:
IDX_OBJ_TMP[i][j][k] = IDX_OBJ_TMP[i][k][j]
if not (symmetry_option == "sym01" or symmetry_option == "sym12" or symmetry_option == "nosym"):
print("Error: symmetry option " + symmetry_option + " unsupported.")
sys.exit(1)
return apply_symmetry_condition_to_derivatives(IDX_OBJ_TMP)
def declarerank4(objname, symmetry_option, DIM=-1):
if DIM==-1:
DIM = par.parval_from_str("DIM")
IDX_OBJ_TMP = [[[[sp.sympify(objname + str(i) + str(j) + str(k) + str(l)) for l in range(DIM)] for k in range(DIM)] for j in range(DIM)] for i in range(DIM)]
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
IDX_OBJ_TMP[i][j][k][l] = sp.sympify(objname + str(i) + str(j) + str(k) + str(l))
if symmetry_option == "sym01" or symmetry_option == "sym01_sym23":
if(j < i):
IDX_OBJ_TMP[i][j][k][l] = IDX_OBJ_TMP[j][i][k][l]
if symmetry_option == "sym12":
if(k < j):
IDX_OBJ_TMP[i][j][k][l] = IDX_OBJ_TMP[i][k][j][l]
if symmetry_option == "sym23" or symmetry_option == "sym01_sym23":
if(l < k):
IDX_OBJ_TMP[i][j][k][l] = IDX_OBJ_TMP[i][j][l][k]
if not (symmetry_option=="sym01" or symmetry_option=="sym23" or symmetry_option=="sym01_sym23" or symmetry_option=="none"):
print("Error: symmetry option "+symmetry_option+" unsupported.")
sys.exit(1)
return apply_symmetry_condition_to_derivatives(IDX_OBJ_TMP)
# We use the following functions to evaluate 3-metric inverses
def symm_matrix_inverter3x3(a):
# It is far more efficient to write out the matrix determinant and inverse by hand
# instead of using symmetry_optionPy's built-in functions, since the matrix is symmetric.
outDET = -a[0][2]**2*a[1][1] + 2*a[0][1]*a[0][2]*a[1][2] - \
a[0][0]*a[1][2]**2 - a[0][1]**2*a[2][2] + \
a[0][0]*a[1][1]*a[2][2]
outINV = [[sp.sympify(0) for i in range(3)] for j in range(3)]
# First fill in the upper-triangle of the gPhysINV matrix...
outINV[0][0] = (-a[1][2]**2 + a[1][1]*a[2][2])/outDET
outINV[0][1] = (+a[0][2]*a[1][2] - a[0][1]*a[2][2])/outDET
outINV[0][2] = (-a[0][2]*a[1][1] + a[0][1]*a[1][2])/outDET
outINV[1][1] = (-a[0][2]**2 + a[0][0]*a[2][2])/outDET
outINV[1][2] = (+a[0][1]*a[0][2] - a[0][0]*a[1][2])/outDET
outINV[2][2] = (-a[0][1]**2 + a[0][0]*a[1][1])/outDET
outINV[1][0] = outINV[0][1]
outINV[2][0] = outINV[0][2]
outINV[2][1] = outINV[1][2]
return outINV, outDET
def symm_matrix_inverter4x4(a):
# It is far more efficient to write out the matrix determinant and inverse by hand
# instead of using symmetry_optionPy's built-in functions, since the matrix is symmetric.
outDET = + a[0][2]*a[0][2]*a[1][3]*a[1][3] + a[0][3]*a[0][3]*a[1][2]*a[1][2] + a[0][1]*a[0][1]*a[2][3]*a[2][3] \
- a[0][0]*a[1][3]*a[1][3]*a[2][2] - a[0][3]*a[0][3]*a[1][1]*a[2][2] - a[0][0]*a[1][1]*a[2][3]*a[2][3] \
- 2*(+ a[0][1]*a[0][2]*a[1][3]*a[2][3] - a[0][0]*a[1][2]*a[1][3]*a[2][3] \
- a[0][3]*(- a[0][2]*a[1][2]*a[1][3] + a[0][1]*a[1][3]*a[2][2] \
+ a[0][2]*a[1][1]*a[2][3] - a[0][1]*a[1][2]*a[2][3])) \
- a[3][3] * (+ a[0][2]*a[0][2]*a[1][1] - a[0][1]*a[0][2]*a[1][2] - a[0][1]*a[0][2]*a[1][2] \
+ a[0][0]*a[1][2]*a[1][2] + a[0][1]*a[0][1]*a[2][2] - a[0][0]*a[1][1]*a[2][2])
outINV = [[sp.sympify(0) for i in range(4)] for j in range(4)]
# First fill in the upper-triangle of the gPhysINV matrix...
outINV[0][0] = (-a[1][3]*a[1][3]*a[2][2] + 2*a[1][2]*a[1][3]*a[2][3] - a[1][1]*a[2][3]*a[2][3] - a[1][2]*a[1][2]*a[3][3] + a[1][1]*a[2][2]*a[3][3])/outDET
outINV[1][1] = (-a[0][3]*a[0][3]*a[2][2] + 2*a[0][2]*a[0][3]*a[2][3] - a[0][0]*a[2][3]*a[2][3] - a[0][2]*a[0][2]*a[3][3] + a[0][0]*a[2][2]*a[3][3])/outDET
outINV[2][2] = (-a[0][3]*a[0][3]*a[1][1] + 2*a[0][1]*a[0][3]*a[1][3] - a[0][0]*a[1][3]*a[1][3] - a[0][1]*a[0][1]*a[3][3] + a[0][0]*a[1][1]*a[3][3])/outDET
outINV[3][3] = (-a[0][2]*a[0][2]*a[1][1] + 2*a[0][1]*a[0][2]*a[1][2] - a[0][0]*a[1][2]*a[1][2] - a[0][1]*a[0][1]*a[2][2] + a[0][0]*a[1][1]*a[2][2])/outDET
outINV[0][1] = (+a[0][3]*a[1][3]*a[2][2] - a[0][3]*a[1][2]*a[2][3] - a[0][2]*a[1][3]*a[2][3] + a[0][1]*a[2][3]*a[2][3] + a[0][2]*a[1][2]*a[3][3] - a[0][1]*a[2][2]*a[3][3])/outDET
outINV[0][2] = (-a[0][3]*a[1][2]*a[1][3] + a[0][2]*a[1][3]*a[1][3] + a[0][3]*a[1][1]*a[2][3] - a[0][1]*a[1][3]*a[2][3] - a[0][2]*a[1][1]*a[3][3] + a[0][1]*a[1][2]*a[3][3])/outDET
outINV[0][3] = (-a[0][2]*a[1][2]*a[1][3] + a[0][1]*a[1][3]*a[2][2] + a[0][3]*a[1][2]*a[1][2] - a[0][3]*a[1][1]*a[2][2] + a[0][2]*a[1][1]*a[2][3] - a[0][1]*a[1][2]*a[2][3])/outDET
outINV[1][2] = (+a[0][3]*a[0][3]*a[1][2] + a[0][0]*a[1][3]*a[2][3] - a[0][3]*a[0][2]*a[1][3] - a[0][3]*a[0][1]*a[2][3] + a[0][1]*a[0][2]*a[3][3] - a[0][0]*a[1][2]*a[3][3])/outDET
outINV[1][3] = (+a[0][2]*a[0][2]*a[1][3] + a[0][1]*a[0][3]*a[2][2] - a[0][0]*a[1][3]*a[2][2] + a[0][0]*a[1][2]*a[2][3] - a[0][2]*a[0][3]*a[1][2] - a[0][2]*a[0][1]*a[2][3])/outDET
outINV[2][3] = (+a[0][2]*a[0][3]*a[1][1] - a[0][1]*a[0][3]*a[1][2] - a[0][1]*a[0][2]*a[1][3] + a[0][0]*a[1][2]*a[1][3] + a[0][1]*a[0][1]*a[2][3] - a[0][0]*a[1][1]*a[2][3])/outDET
# Then we fill the lower triangle of the symmetric matrix
outINV[1][0] = outINV[0][1]
outINV[2][0] = outINV[0][2]
outINV[2][1] = outINV[1][2]
outINV[3][0] = outINV[0][3]
outINV[3][1] = outINV[1][3]
outINV[3][2] = outINV[2][3]
return outINV, outDET
# symmetry_optionPy's generic matrix inverter is highly inefficient for 3x3 matrices, so here we have an optimized version.
def generic_matrix_inverter3x3(a):
outDET = -a[0][2]*a[1][1]*a[2][0] + a[0][1]*a[1][2]*a[2][0] + \
a[0][2]*a[1][0]*a[2][1] - a[0][0]*a[1][2]*a[2][1] - \
a[0][1]*a[1][0]*a[2][2] + a[0][0]*a[1][1]*a[2][2]
outINV = [[sp.sympify(0) for i in range(3)] for j in range(3)]
outINV[0][0] = -a[1][2]*a[2][1] + a[1][1]*a[2][2]
outINV[0][1] = a[0][2]*a[2][1] - a[0][1]*a[2][2]
outINV[0][2] = -a[0][2]*a[1][1] + a[0][1]*a[1][2]
outINV[1][0] = a[1][2]*a[2][0] - a[1][0]*a[2][2]
outINV[1][1] = -a[0][2]*a[2][0] + a[0][0]*a[2][2]
outINV[1][2] = a[0][2]*a[1][0] - a[0][0]*a[1][2]
outINV[2][0] = -a[1][1]*a[2][0] + a[1][0]*a[2][1]
outINV[2][1] = a[0][1]*a[2][0] - a[0][0]*a[2][1]
outINV[2][2] = -a[0][1]*a[1][0] + a[0][0]*a[1][1]
for i in range(3):
for j in range(3):
outINV[i][j] /= outDET
return outINV, outDET
def generic_matrix_inverter4x4(a):
# A = {{a00, a01, a02, a03},
# {a10, a11, a12, a13},
# {a20, a21, a22, a23},
# {a30, a31, a32, a33}}
# A // MatrixForm
# CForm[FullSimplify[Det[A]]] >>> t2.txt
# cat t2.txt | sed "s/ //g" |sed "s/ //g;s/\([0-3]\)/[\1]/g"
outDET = a[0][1]*a[1][3]*a[2][2]*a[3][0]-a[0][1]*a[1][2]*a[2][3]*a[3][0]-a[0][0]*a[1][3]*a[2][2]*a[3][1]+ \
a[0][0]*a[1][2]*a[2][3]*a[3][1]-a[0][1]*a[1][3]*a[2][0]*a[3][2]+a[0][0]*a[1][3]*a[2][1]*a[3][2]+ \
a[0][1]*a[1][0]*a[2][3]*a[3][2]-a[0][0]*a[1][1]*a[2][3]*a[3][2]+ \
a[0][3]*(a[1][2]*a[2][1]*a[3][0]-a[1][1]*a[2][2]*a[3][0]-a[1][2]*a[2][0]*a[3][1]+a[1][0]*a[2][2]*a[3][1]+
a[1][1]*a[2][0]*a[3][2]-a[1][0]*a[2][1]*a[3][2])+ \
(a[0][1]*a[1][2]*a[2][0]-a[0][0]*a[1][2]*a[2][1]-a[0][1]*a[1][0]*a[2][2]+a[0][0]*a[1][1]*a[2][2])*a[3][3]+\
a[0][2]*(-(a[1][3]*a[2][1]*a[3][0])+a[1][1]*a[2][3]*a[3][0]+a[1][3]*a[2][0]*a[3][1]-a[1][0]*a[2][3]*a[3][1]-
a[1][1]*a[2][0]*a[3][3]+a[1][0]*a[2][1]*a[3][3])
outINV = [[sp.sympify(0) for i in range(4)] for j in range(4)]
# CForm[FullSimplify[Inverse[A]*Det[A]]] >>> t.txt
# cat t.txt | sed "s/,/\n/g;s/List(//g;s/))/)/g;s/)//g;s/(//g"|grep -v ^$|sed "s/ //g;s/\([0-3]\)/[\1]/g"| awk '{line[NR]=$0}END{count=1;for(i=0;i<4;i++) { for(j=0;j<4;j++) { printf "outINV[%d][%d] = %s\n", i,j,line[count];count++; }}}'
outINV[0][0] = -a[1][3]*a[2][2]*a[3][1]+a[1][2]*a[2][3]*a[3][1]+a[1][3]*a[2][1]*a[3][2]-a[1][1]*a[2][3]*a[3][2]-a[1][2]*a[2][1]*a[3][3]+a[1][1]*a[2][2]*a[3][3]
outINV[0][1] = a[0][3]*a[2][2]*a[3][1]-a[0][2]*a[2][3]*a[3][1]-a[0][3]*a[2][1]*a[3][2]+a[0][1]*a[2][3]*a[3][2]+a[0][2]*a[2][1]*a[3][3]-a[0][1]*a[2][2]*a[3][3]
outINV[0][2] = -a[0][3]*a[1][2]*a[3][1]+a[0][2]*a[1][3]*a[3][1]+a[0][3]*a[1][1]*a[3][2]-a[0][1]*a[1][3]*a[3][2]-a[0][2]*a[1][1]*a[3][3]+a[0][1]*a[1][2]*a[3][3]
outINV[0][3] = a[0][3]*a[1][2]*a[2][1]-a[0][2]*a[1][3]*a[2][1]-a[0][3]*a[1][1]*a[2][2]+a[0][1]*a[1][3]*a[2][2]+a[0][2]*a[1][1]*a[2][3]-a[0][1]*a[1][2]*a[2][3]
outINV[1][0] = a[1][3]*a[2][2]*a[3][0]-a[1][2]*a[2][3]*a[3][0]-a[1][3]*a[2][0]*a[3][2]+a[1][0]*a[2][3]*a[3][2]+a[1][2]*a[2][0]*a[3][3]-a[1][0]*a[2][2]*a[3][3]
outINV[1][1] = -a[0][3]*a[2][2]*a[3][0]+a[0][2]*a[2][3]*a[3][0]+a[0][3]*a[2][0]*a[3][2]-a[0][0]*a[2][3]*a[3][2]-a[0][2]*a[2][0]*a[3][3]+a[0][0]*a[2][2]*a[3][3]
outINV[1][2] = a[0][3]*a[1][2]*a[3][0]-a[0][2]*a[1][3]*a[3][0]-a[0][3]*a[1][0]*a[3][2]+a[0][0]*a[1][3]*a[3][2]+a[0][2]*a[1][0]*a[3][3]-a[0][0]*a[1][2]*a[3][3]
outINV[1][3] = -a[0][3]*a[1][2]*a[2][0]+a[0][2]*a[1][3]*a[2][0]+a[0][3]*a[1][0]*a[2][2]-a[0][0]*a[1][3]*a[2][2]-a[0][2]*a[1][0]*a[2][3]+a[0][0]*a[1][2]*a[2][3]
outINV[2][0] = -a[1][3]*a[2][1]*a[3][0]+a[1][1]*a[2][3]*a[3][0]+a[1][3]*a[2][0]*a[3][1]-a[1][0]*a[2][3]*a[3][1]-a[1][1]*a[2][0]*a[3][3]+a[1][0]*a[2][1]*a[3][3]
outINV[2][1] = a[0][3]*a[2][1]*a[3][0]-a[0][1]*a[2][3]*a[3][0]-a[0][3]*a[2][0]*a[3][1]+a[0][0]*a[2][3]*a[3][1]+a[0][1]*a[2][0]*a[3][3]-a[0][0]*a[2][1]*a[3][3]
outINV[2][2] = -a[0][3]*a[1][1]*a[3][0]+a[0][1]*a[1][3]*a[3][0]+a[0][3]*a[1][0]*a[3][1]-a[0][0]*a[1][3]*a[3][1]-a[0][1]*a[1][0]*a[3][3]+a[0][0]*a[1][1]*a[3][3]
outINV[2][3] = a[0][3]*a[1][1]*a[2][0]-a[0][1]*a[1][3]*a[2][0]-a[0][3]*a[1][0]*a[2][1]+a[0][0]*a[1][3]*a[2][1]+a[0][1]*a[1][0]*a[2][3]-a[0][0]*a[1][1]*a[2][3]
outINV[3][0] = a[1][2]*a[2][1]*a[3][0]-a[1][1]*a[2][2]*a[3][0]-a[1][2]*a[2][0]*a[3][1]+a[1][0]*a[2][2]*a[3][1]+a[1][1]*a[2][0]*a[3][2]-a[1][0]*a[2][1]*a[3][2]
outINV[3][1] = -a[0][2]*a[2][1]*a[3][0]+a[0][1]*a[2][2]*a[3][0]+a[0][2]*a[2][0]*a[3][1]-a[0][0]*a[2][2]*a[3][1]-a[0][1]*a[2][0]*a[3][2]+a[0][0]*a[2][1]*a[3][2]
outINV[3][2] = a[0][2]*a[1][1]*a[3][0]-a[0][1]*a[1][2]*a[3][0]-a[0][2]*a[1][0]*a[3][1]+a[0][0]*a[1][2]*a[3][1]+a[0][1]*a[1][0]*a[3][2]-a[0][0]*a[1][1]*a[3][2]
outINV[3][3] = -a[0][2]*a[1][1]*a[2][0]+a[0][1]*a[1][2]*a[2][0]+a[0][2]*a[1][0]*a[2][1]-a[0][0]*a[1][2]*a[2][1]-a[0][1]*a[1][0]*a[2][2]+a[0][0]*a[1][1]*a[2][2]
for mu in range(4):
for nu in range(4):
outINV[mu][nu] /= outDET
return outINV, outDET
% Based on http://nbviewer.jupyter.org/github/ipython/nbconvert-examples/blob/master/citations/Tutorial.ipynb , authored by Brian E. Granger
((*- extends 'article.tplx' -*))
((* block docclass *))
% Declare the document class
\documentclass[landscape,letterpaper,10pt,english]{article}
((* endblock docclass *))
% ((* block Xauthor *))
% \author{Zachariah Etienne}
% ((* endblock Xauthor *))
% ((* block Xbibliography *))
% \bibliographystyle{unsrt}
% \bibliography{ipython}
% ((* endblock Xbibliography *))
((* block commands *))
% Start the section counter at -1, so the Table of Contents is Section 0
\setcounter{section}{-2}
% Prevent overflowing lines due to hard-to-break entities
\sloppy
% Setup hyperref package
\hypersetup{
breaklinks=true, % so long urls are correctly broken across lines
colorlinks=true,
urlcolor=urlcolor,
linkcolor=linkcolor,
citecolor=citecolor,
}
% Slightly bigger margins than the latex defaults
\geometry{verbose,tmargin=0.5in,bmargin=0.5in,lmargin=0.5in,rmargin=0.5in}
((* endblock commands *))
# This module contains infrastructure for generating
# C-code loops of arbitrary dimension
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
import sys # Standard Python modules for multiplatform OS-level functions
# loop1D() is just a special case of loop(), and in fact is called by loop().
# For documentation on the inputs, see loop()'s documentation below.
def loop1D(idxvar="i0",lower="0",upper="Nx0",incr="1",OpenMPpragma="#pragma omp parallel for",tabprefix=""):
if not (isinstance(idxvar, str) and isinstance(lower, str) and
isinstance(upper, str) and isinstance(incr, str) and isinstance(OpenMPpragma, str)):
print("Error: all inputs to loop1D() must be STRINGS, and to loop() must be LISTS OF STRINGS")
sys.exit(1)
OMPheader = ""
if OpenMPpragma != "":
OMPheader = OpenMPpragma + "\n"
incrstr = "++"
if incr != "1":
incrstr = "+="+incr
loopbody = tabprefix+"for(int "+idxvar+"="+lower+"; "+idxvar+"<"+upper+"; "+idxvar+incrstr+")"
return OMPheader+loopbody+" {\n", tabprefix+"} // END LOOP: "+loopbody.replace(tabprefix,"")+"\n"
# loop() creates a C code loop, taking as input:
# idxvar (string or list of strings): the index variable name or list of names.
# In the case that idxvar is a list of N strings, we adopt the formulation:
# idxvar[0]=outermost loop variable
# idxvar[N-1]=innermost loop variable
# lower (string or list of strings): lower loop index of idxvar or idxvar[i].
# See definition of "idxvar" if lower is a list of strings.
# idxvar[] must have the same length as idxvar.
# upper (string or list of strings): Defined similarly to "lower", except
# this refers to the *upper* loop index of idxvar or idxvar[i].
# incr (string or list of strings): Defined similarly to "lower", except
# this refers to the loop increment of idxvar or idxvar[i]
# (incr[i] = 1 -> idxvar++)
# OpenMPpragma (string or list of strings): Defined similarly to "lower", except
# this refers to the OpenMP pragma corresponding to idxvar or idxvar[i].
# tabprefix (optional; string): Sets the tab stop for the C code.
# loopguts (optional; string): The loop interior.
#
# Example: loop(["i0","i1"],["0","5"],["N","N-5"],["1","5"],["#pragma omp parallel for",""],
# "double a=-2;\n gf[IDX3(GF,i0,i1)]=-2*a;\n"])
# Output:
# #pragma omp parallel for
# for(int i0=0;i0<N;i0++) {
# for(int i1=5;i1<N-5;i1+=5) {
# a=-2;
# gf[IDX3(GF,i0,i1)]=-2*a;
# } // END LOOP: for(int i1=5;i1<N-5;i1+=5)
# } // END LOOP: for(int i0=0;i0<N;i0++)
def loop(idxvar,lower,upper,incr,OpenMPpragma,tabprefix="",loopguts=""):
# Step 1: Check and/or clean input.
# Step 1a: If only strings are passed, then create lists out of them:
if (isinstance(idxvar,str) and isinstance(lower,str) and
isinstance(upper, str) and isinstance(incr, str) and isinstance(OpenMPpragma, str)):
idxvar = [idxvar]
lower = [lower]
upper = [upper]
incr = [incr]
OpenMPpragma = [OpenMPpragma]
# Step 1b: At this point all inputs should be lists. If they are not, then exit.
if not (isinstance(idxvar,list) and isinstance(lower,list) and
isinstance(upper, list) and isinstance(incr, list) and isinstance(OpenMPpragma, list)):
print("Error: loop(idxvar,lower,upper,incr,OpenMPpragma) requires all inputs be lists")
sys.exit(1)
# Step 1c: At this point all inputs should be lists. If they are not the same length, then exit.
if len(idxvar) != len(lower) or len(lower) != len(upper) or len(upper) != len(incr) or len(incr) != len(
OpenMPpragma):
print("Error: loop(idxvar,lower,upper,incr,OpenMPpragma) requires all inputs be lists OF THE SAME LENGTH")
sys.exit(1)
# Step 2: tabprefix will be set according to the loop nesting, so the loop has proper tabination;
# one tab for each nesting of the loop.
# Step 3: header will be the top of the loop
header = ""
# Step 4: footerarray
footerarray = []
for i in range(len(idxvar)):
headerstr,footerstr = loop1D(idxvar[i],lower[i],upper[i],incr[i],OpenMPpragma[i],tabprefix)
header += headerstr
footerarray.append(footerstr)
tabprefix += " "
loopgutsout = ""
if loopguts != "":
loopgutsarray = loopguts.split("\n")
for line in loopgutsarray:
loopgutsout += tabprefix+line+"\n"
footer = ""
for i in range(len(idxvar)-1,-1,-1):
footer += footerarray[i]
if loopguts == "":
return header,footer
return header+loopgutsout+footer
# Automatic generation of C-code loops around an arbitrarily
# defined loop body.
def simple_loop(loopopts, body):
if loopopts == "":
return body
if "AllPoints" in loopopts:
i2i1i0_mins = ["0", "0", "0"]
i2i1i0_maxs = ["Nxx_plus_2NGHOSTS2", "Nxx_plus_2NGHOSTS1", "Nxx_plus_2NGHOSTS0"]
if "oldloops" in loopopts:
i2i1i0_maxs = ["Nxx_plus_2NGHOSTS[2]", "Nxx_plus_2NGHOSTS[1]", "Nxx_plus_2NGHOSTS[0]"]
elif "InteriorPoints" in loopopts:
i2i1i0_mins = ["NGHOSTS","NGHOSTS","NGHOSTS"]
i2i1i0_maxs = ["NGHOSTS+Nxx2","NGHOSTS+Nxx1","NGHOSTS+Nxx0"]
if "oldloops" in loopopts:
i2i1i0_maxs = ["NGHOSTS+Nxx[2]", "NGHOSTS+Nxx[1]", "NGHOSTS+Nxx[0]"]
else:
print("Error: loopopts given, but no points over which to loop were specified.")
sys.exit(1)
Read_1Darrays = ["", "", ""]
if "Read_xxs" in loopopts:
if not "EnableSIMD" in loopopts:
Read_1Darrays = ["const REAL xx0 = xx[0][i0];",
" const REAL xx1 = xx[1][i1];",
" const REAL xx2 = xx[2][i2];", ]
else:
print("Error: No SIMD support on Read_xxs yet.")
sys.exit(1)
if "Enable_rfm_precompute" in loopopts:
if "Read_xxs" in loopopts:
print("Error: Enable_rfm_precompute and Read_xxs cannot both be enabled.")
sys.exit(1)
if "EnableSIMD" in loopopts:
Read_1Darrays = ["#include \"rfm_files/rfm_struct__SIMD_inner_read0.h\"",
"#include \"rfm_files/rfm_struct__SIMD_outer_read1.h\"",
"#include \"rfm_files/rfm_struct__SIMD_outer_read2.h\""]
else:
Read_1Darrays = ["#include \"rfm_files/rfm_struct__read0.h\"",
"#include \"rfm_files/rfm_struct__read1.h\"",
"#include \"rfm_files/rfm_struct__read2.h\""]
OpenMPpragma = "#pragma omp parallel for"
if "DisableOpenMP" in loopopts:
OpenMPpragma = ""
loopincrements = ["1","1","1"]
if "EnableSIMD" in loopopts:
loopincrements = ["1", "1", "SIMD_width"]
return loop(["i2","i1","i0"],i2i1i0_mins,i2i1i0_maxs,loopincrements,
[OpenMPpragma,Read_1Darrays[2],Read_1Darrays[1]],tabprefix=" ",loopguts = Read_1Darrays[0]+"\n"+body)
#loopheader,loopfooter = loop(idxvar=["i0","i1"],lower=["0","0"],upper=["Nx0","Nx1"],incr=["1","1"],
# OpenMPpragma=["", "#pragma omp parallel for"])
#loopheader,loopfooter = loop(idxvar=["i0","i1"],lower=["0","0"],upper=["Nx0","Nx1"],incr=["1","1"],
# OpenMPpragma=["", "#pragma omp parallel for"])
#print(loopheader+loopfooter)
import sys
# Step 0: Check Python version. NRPy+ is untested below Python 2.7-ish, but is compatible with 2.7+ and 3+
PYTHONVERSION3 = False
if sys.version_info[0]==2:
if sys.version_info[1]<4:
print("Sorry, NRPy won't work with Python < 2.4; sorting functs won't work. See https://docs.python.org/3/howto/sorting.html for details.")
sys.exit(1)
if sys.version_info[0]==3:
PYTHONVERSION3 = True
# Step 1: Print logo.
from NRPy_logo import *
#print_logo()
# Step 2: Initialize core parameter NRPy::MainModule,
# which defines the desired main module.
# E.g., scalarwave, BSSN_RHSs, BSSN_InitialData, etc.
# Contains parameter initialization, manipulation, and read-in routines
import NRPy_param_funcs as par
# Used to import needed modules dynamically
import importlib
# Initialize the MainModule parameter.
# This is the ONLY parameter initialized outside of a module!
MainModule = "scalarwave" # Default. To be overwritten later.
par.initialize_param(par.glb_param("char","NRPy","MainModule",MainModule))
# Step 4: Initialize NRPy+ as desired.
# Step 4a: Enter Interactive Mode if NRPy+ is run via
# `python nrpy.py`
if(len(sys.argv) == 1):
print("/* Run `python nrpy.py --help` for other command-line options */")
print("/* Entering interactive mode */\n")
# Print help message if NRPy+ is run via
# `python nrpy.py --help`
elif(len(sys.argv) == 2 and sys.argv[1] == "--help"):
print("\n \033[1m............................................\033[0m ")
print(" -={ \033[1mNRPy+ supports multiple usage modes.\033[0m }=-\n")
print("\033[1mUsage Mode 0\033[0m: `python nrpy.py`\n\t\t initializes interactive session\n")
print("\033[1mUsage Mode 1\033[0m: `python nrpy.py --help`\n\t\t outputs this message\n")
print("\033[1mUsage Mode 2\033[0m: `python nrpy.py --gen-defparam-file`\n\t\t generates default parameter file\n")
print("\033[1mUsage Mode 3\033[0m: `python nrpy.py [PARAMETER FILE]`\n\t\t override default parameters with parameter file.")
print("\t\t Parameter file takes the form of a list;")
print("\t\t each list item has syntax `mainmodule:paramname=value`\n")
print("\033[1mUsage Mode 4\033[0m: `python nrpy.py [PARAMETER FILE] [PARAMETER OVERRIDES]`")
print("\t\t read parameter file, then override parameter file")
print("\t\t settings with command-line parameters, with same")
print("\t\t syntax (`mainmodule:paramname=value`)")
print(" \033[1m............................................\033[0m ")
sys.exit(0)
# Run with parameter file & optional list of parameter overrides if NRPy+ is run via
# `python nrpy.py [PARAMETER FILE] [(optional) PARAMETER OVERRIDES]`:
# Note that
# 1) parameters set in parameter file override parameter defaults, and
# 2) parameters set at command line override both parameter defaults
# *and* parameter settings in parameter file
elif(len(sys.argv) >= 2):
# When not in an interactive mode, the NRPy::MainModule parameter must be set,
# either in the param file (preferred!) or as a command line argument.
with open(sys.argv[1], "r") as file:
for line in file:
par.set_paramsvals_value(line, sys.argv[1], FindMainModuleMode=True)
# Search for NRPy::MainModule in the command line arguments
for i in range(2,len(sys.argv)):
par.set_paramsvals_value(sys.argv[i], "", FindMainModuleMode=True)
# The NRPy::MainModule parameter has already been set,
# and parse_param_string__set__params_and_paramsvars()
# will error out with a "Critical Error" if it has not been set.
idx = par.get_params_idx(par.glb_param("ignoretype", "NRPy", "MainModule", "ignoredefval"))
MainModule = par.glb_paramsvals_list[idx]
if MainModule == "NODEFAULT":
print("Error: Could not find NRPy::MainModule defined in the parameter file \""+sys.argv[1]+"\" or on the command line!")
sys.exit(1)
# Next initialize all of MainModule's parameters.
# Note that MainModule must also initialize parameters for modules
# on which it depends, except outputC (which NRPy+ loads by default).
# https://stackoverflow.com/questions/10675054/how-to-import-a-module-in-python-with-importlib-import-module
importlib.import_module(MainModule+"."+MainModule)
# Next overwrite default parameters with values specified in the parameter file.
with open(sys.argv[1], "r") as file:
for line in file:
par.set_paramsvals_value(line, sys.argv[1])
# Next overwrite default parameters and values specified in the parameter file with command-line parameter assignments.
for i in range(2,len(sys.argv)):
par.set_paramsvals_value(sys.argv[i], "")
# Next load the MainModule, if it hasn't been loaded already.
#importlib.import_module(MainModule+"."+MainModule)
getattr(importlib.import_module(MainModule+"."+MainModule), MainModule)()
# Initialize parameters for the core outputC module,
# which is the default MainModule.
# Call function initparams() from outputC module (in outputC.py):
# getattr(importlib.import_module(MainModule), 'initparams')()
# Step 3b (temporary): Set a SymPy expression to test processing
import sympy as sp
#from outputC import *
#getattr(importlib.import_module("outputC"),'outputC')([sympify("3*a*b**4+c*sin(a*b**4)"),sympify("6*a*b**4")],["output1","output2"])
#getattr(importlib.import_module("outputC"),'outputC')(sp.sympify("3*a*b**4+2*c*sin(a*b**4)"),"output1")
# As documented in the NRPy+ tutorial module
# Tutorial-Coutput__Parameter_Interface.ipynb
# this core NRPy+ module is used for
# generating C code and functions.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
import loop as lp # NRPy+: C code loop interface
import NRPy_param_funcs as par # NRPy+: parameter interface
from SIMD import expr_convert_to_SIMD_intrins # NRPy+: SymPy expression => SIMD intrinsics interface
import sympy as sp # Import SymPy
import re, sys # Standard Python: regular expressions & OS-independent system functions
from collections import namedtuple # Standard Python: Enable namedtuple data type
lhrh = namedtuple('lhrh', 'lhs rhs')
outCparams = namedtuple('outCparams', 'preindent includebraces declareoutputvars outCfileaccess outCverbose CSE_enable CSE_varprefix SIMD_enable SIMD_const_suffix SIMD_debug enable_TYPE')
# Sometimes SymPy has problems evaluating complicated expressions involving absolute
# values, resulting in hangs. So instead of using sp.Abs(), if we instead use
# nrpyAbs, we can sidestep the internal SymPy evaluation and force the C
# codegen to output our desired fabs().
nrpyAbs = sp.Function('nrpyAbs')
custom_functions_for_SymPy_ccode = {
"nrpyAbs": "fabs",
'Pow': [(lambda b, e: e == 0.5, lambda b, e: 'sqrt(%s)' % (b)),
(lambda b, e: e ==-0.5, lambda b, e: '(1.0/sqrt(%s))' % (b)),
(lambda b, e: e == sp.S.One/3, lambda b, e: 'cbrt(%s)' % (b)),
(lambda b, e: e ==-sp.S.One/3, lambda b, e: '(1.0/cbrt(%s))' % (b)),
(lambda b, e: e == 2, lambda b, e: '((%s)*(%s))' % (b,b)),
(lambda b, e: e == 3, lambda b, e: '((%s)*(%s)*(%s))' % (b,b,b)),
(lambda b, e: e == 4, lambda b, e: '((%s)*(%s)*(%s)*(%s))' % (b,b,b,b)),
(lambda b, e: e == 5, lambda b, e: '((%s)*(%s)*(%s)*(%s)*(%s))' % (b,b,b,b,b)),
(lambda b, e: e ==-1, lambda b, e: '(1.0/(%s))' % (b)),
(lambda b, e: e ==-2, lambda b, e: '(1.0/((%s)*(%s)))' % (b,b)),
(lambda b, e: e ==-3, lambda b, e: '(1.0/((%s)*(%s)*(%s)))' % (b,b,b)),
(lambda b, e: e ==-4, lambda b, e: '(1.0/((%s)*(%s)*(%s)*(%s)))' % (b,b,b,b)),
(lambda b, e: e ==-5, lambda b, e: '(1.0/((%s)*(%s)*(%s)*(%s)*(%s)))' % (b,b,b,b,b)),
(lambda b, e: e !=-5, 'pow')]
## (lambda b, e: e != 2, 'pow')]
}
# Parameter initialization is called once, within nrpy.py.
par.initialize_param(par.glb_param("char", __name__, "PRECISION", "double")) # __name__ = "outputC", this module's name.
# par.initialize_param(par.glb_param("bool", thismodule, "SIMD_enable", False))
# super fast 'uniq' function:
# f8() function from https://www.peterbe.com/plog/uniqifiers-benchmark
def superfast_uniq(seq): # Author: Dave Kirby
# Order preserving
seen = set()
return [x for x in seq if x not in seen and not seen.add(x)]
def check_if_string__error_if_not(allegedstring,stringdesc):
import sys
if sys.version_info[0] == 3:
string_types = str
else:
string_types = basestring
if not isinstance(allegedstring, string_types):
print("ERROR: "+str(stringdesc)+" =="+str(allegedstring)+" not a string!")
sys.exit(1)
def ccode_postproc(string):
PRECISION = par.parval_from_str("PRECISION")
# In the C math library, e.g., pow(x,y) assumes x and y are doubles, and returns a double.
# If x and y are floats, then for consistency should use powf(x,y) instead.
# Similarly, in the case of x and y being long doubles, should use powl(x,y) for consistency.
# First we find the appropriate suffix depending on the desired precision:
cmathsuffix = ""
if PRECISION == "double":
pass
elif PRECISION == "long double":
cmathsuffix = "l"
elif PRECISION == "float":
cmathsuffix = "f"
else:
print("Error: "+__name__+"::PRECISION = \""+ PRECISION +"\" not supported")
sys.exit(1)
# ... then we append the above suffix to standard C math library functions:
for func in ['pow', 'sqrt', 'sin', 'cos', 'tan', 'sinh', 'cosh', 'tanh', 'exp', 'log', 'fabs']:
string2 = re.sub(func+r'\(', func + cmathsuffix+"(", string); string = string2
# Finally, SymPy prefers to output Rationals as long-double fractions.
# E.g., Rational(1,3) is output as 1.0L/3.0L.
# The Intel compiler vectorizer complains miserably about this,
# and strictly speaking it is useless when we're in double precision.
# So here we get rid of the "L" suffix on floating point numbers:
if PRECISION!="long double":
string2 = re.sub(r'([0-9.]+)L/([0-9.]+)L', '(\\1 / \\2)', string); string = string2
return string
def parse_outCparams_string(params):
# Default values:
preindent = ""
includebraces = "True"
declareoutputvars = "False"
outCfileaccess = "w"
outCverbose = "True"
CSE_enable = "True"
CSE_varprefix = "tmp"
SIMD_enable = "False"
SIMD_const_suffix = ""
SIMD_debug = "False"
enable_TYPE = "True"
if params != "":
params2 = re.sub("^,","",params)
params = params2.strip()
splitstring = re.split("=|,", params)
if len(splitstring) % 2 != 0:
print("outputC: Invalid params string: "+params)
sys.exit(1)
parnm = []
value = []
for i in range(int(len(splitstring)/2)):
parnm.append(splitstring[2*i])
value.append(splitstring[2*i+1])
for i in range(len(parnm)):
# Clean the string
if value[i] == "true":
value[i] = "True"
if value[i] == "false":
value[i] = "False"
if parnm[i] == "preindent":
if not value[i].isdigit():
print("Error: preindent must be set to an integer (corresponding to the number of tab stops). ")
print(value[i]+" is not an integer.")
sys.exit(1)
preindent = ""
for i in range(int(value[i])):
preindent += " "
elif parnm[i] == "includebraces":
includebraces = value[i]
elif parnm[i] == "declareoutputvars":
declareoutputvars = value[i]
elif parnm[i] == "outCfileaccess":
outCfileaccess = value[i]
elif parnm[i] == "outCverbose":
outCverbose = value[i]
elif parnm[i] == "CSE_enable":
CSE_enable = value[i]
elif parnm[i] == "CSE_varprefix":
CSE_varprefix = value[i]
elif parnm[i] == "SIMD_enable":
SIMD_enable = value[i]
elif parnm[i] == "SIMD_const_suffix":
SIMD_const_suffix = value[i]
elif parnm[i] == "SIMD_debug":
SIMD_debug = value[i]
elif parnm[i] == "enable_TYPE":
enable_TYPE = value[i]
else:
print("Error: outputC parameter name \""+parnm[i]+"\" unrecognized.")
sys.exit(1)
return outCparams(preindent,includebraces,declareoutputvars,outCfileaccess,outCverbose,CSE_enable,CSE_varprefix,SIMD_enable,SIMD_const_suffix,SIMD_debug,enable_TYPE)
import sympy as sp
# Input: sympyexpr = a single SymPy expression *or* a list of SymPy expressions
# output_varname_str = a single output variable name *or* a list of output
# variable names, one per sympyexpr.
# Output: C code, as a string.
def outputC(sympyexpr, output_varname_str, filename = "stdout", params = "", prestring = "", poststring = ""):
outCparams = parse_outCparams_string(params)
preindent = outCparams.preindent
TYPE = par.parval_from_str("PRECISION")
if outCparams.enable_TYPE == "False":
TYPE = ""
# Step 0: Initialize
# commentblock: comment block containing the input SymPy string,
# set only if outCverbose==True
# outstring: the output C code string
commentblock = ""
outstring = ""
# Step 1: If SIMD_enable==True, then check if TYPE=="double". If not, error out.
# Otherwise set TYPE="REAL_SIMD_ARRAY", which should be #define'd
# within the C code. For example for AVX-256, the C code should have
# #define REAL_SIMD_ARRAY __m256d
if outCparams.SIMD_enable == "True":
if not (TYPE == "double" or TYPE == ""):
print("SIMD output currently only supports double precision or typeless. Sorry!")
sys.exit(1)
if TYPE == "double":
TYPE = "REAL_SIMD_ARRAY"
else:
TYPE = ""
# Step 2a: Apply sanity checks when either sympyexpr or
# output_varname_str is a list.
if type(output_varname_str) is list and type(sympyexpr) is not list:
print("Error: Provided a list of output variable names, but only one SymPy expression.")
sys.exit(1)
if type(sympyexpr) is list:
if type(output_varname_str) is not list:
print("Error: Provided a list of SymPy expressions, but no corresponding list of output variable names")
sys.exit(1)
elif len(output_varname_str) != len(sympyexpr):
print("Error: Length of SymPy expressions list ("+str(len(sympyexpr))+
") != Length of corresponding output variable name list ("+str(len(output_varname_str))+")")
sys.exit(1)
# Step 2b: If sympyexpr and output_varname_str are not lists,
# convert them to lists of one element each, to
# simplify proceeding code.
if type(output_varname_str) is not list and type(sympyexpr) is not list:
output_varname_strtmp = [output_varname_str]
output_varname_str = output_varname_strtmp
sympyexprtmp = [sympyexpr]
sympyexpr = sympyexprtmp
# Step 3: If outCparams.verbose = True, then output the original SymPy
# expression(s) in code comments prior to actual C code
if outCparams.outCverbose == "True":
commentblock += preindent+"/*\n"+preindent+" * Original SymPy expression"
if len(output_varname_str)>1:
commentblock += "s"
commentblock += ":\n"
for i in range(len(output_varname_str)):
if i==0:
if len(output_varname_str)!=1:
commentblock += preindent+" * \"["
else:
commentblock += preindent+" * \""
else:
commentblock += preindent+" * "
commentblock += output_varname_str[i] + " = " + str(sympyexpr[i])
if i==len(output_varname_str)-1:
if len(output_varname_str)!=1:
commentblock += "]\"\n"
else:
commentblock += "\"\n"
else:
commentblock += ",\n"
commentblock += preindent+" */\n"
# Step 4: Add proper indentation of C code:
if outCparams.includebraces == "True":
indent = outCparams.preindent+" "
else:
indent = outCparams.preindent+""
# Step 5: Should the output variable, e.g., outvar, be declared?
# If so, start output line with e.g., "double outvar "
outtypestring = ""
if outCparams.declareoutputvars == "True":
outtypestring = outCparams.preindent+indent+TYPE + " "
else:
outtypestring = outCparams.preindent+indent
# Step 6a: If common subexpression elimination (CSE) disabled, then
# just output the SymPy string in the most boring way,
# nearly consistent with SymPy's ccode() function,
# though with support for float & long double types
# as well.
SIMD_decls = ""
if outCparams.CSE_enable == "False":
# If CSE is disabled:
for i in range(len(sympyexpr)):
outstring += outtypestring + ccode_postproc(sp.ccode(sympyexpr[i], output_varname_str[i],
user_functions=custom_functions_for_SymPy_ccode))+"\n"
# Step 6b: If CSE enabled, then perform CSE using SymPy and then
# resulting C code.
else:
# If CSE is enabled:
SIMD_const_varnms = []
SIMD_const_values = []
CSE_results = sp.cse(sympyexpr, sp.numbered_symbols(outCparams.CSE_varprefix), order='canonical')
for commonsubexpression in CSE_results[0]:
FULLTYPESTRING = "const " + TYPE + " "
if outCparams.enable_TYPE == "False":
FULLTYPESTRING = ""
if outCparams.SIMD_enable == "True":
outstring += outCparams.preindent + indent + FULLTYPESTRING + str(commonsubexpression[0]) + " = " + \
str(expr_convert_to_SIMD_intrins(commonsubexpression[1],SIMD_const_varnms,SIMD_const_values,outCparams.SIMD_const_suffix,outCparams.SIMD_debug)) + ";\n"
else:
outstring += outCparams.preindent+indent+FULLTYPESTRING+ccode_postproc(sp.ccode(commonsubexpression[1],commonsubexpression[0],
user_functions=custom_functions_for_SymPy_ccode))+"\n"
for i,result in enumerate(CSE_results[1]):
if outCparams.SIMD_enable == "True":
outstring += outtypestring + output_varname_str[i] + " = " + \
str(expr_convert_to_SIMD_intrins(result,SIMD_const_varnms,SIMD_const_values,outCparams.SIMD_const_suffix,outCparams.SIMD_debug)) + ";\n"
else:
outstring += outtypestring+ccode_postproc(sp.ccode(result,output_varname_str[i],
user_functions=custom_functions_for_SymPy_ccode))+"\n"
# Step 6b.i: If SIMD_enable == True , and
# there is at least one SIMD const variable,
# then declare the SIMD_const_varnms and SIMD_const_values arrays
if outCparams.SIMD_enable == "True" and len(SIMD_const_varnms) != 0:
# Step 6a) Sort the list of definitions. Idea from:
# https://stackoverflow.com/questions/9764298/is-it-possible-to-sort-two-listswhich-reference-each-other-in-the-exact-same-w
SIMD_const_varnms, SIMD_const_values = \
(list(t) for t in zip(*sorted(zip(SIMD_const_varnms, SIMD_const_values))))
# Step 6b) Remove duplicates
uniq_varnms = superfast_uniq(SIMD_const_varnms)
uniq_values = superfast_uniq(SIMD_const_values)
SIMD_const_varnms = uniq_varnms
SIMD_const_values = uniq_values
if len(SIMD_const_varnms) != len(SIMD_const_values):
print("Error: SIMD constant declaration arrays SIMD_const_varnms[] and SIMD_const_values[] have inconsistent sizes!")
sys.exit(1)
for i in range(len(SIMD_const_varnms)):
if outCparams.enable_TYPE == "False":
SIMD_decls += outCparams.preindent + indent + SIMD_const_varnms[i] + " = " + SIMD_const_values[i]+";"
else:
SIMD_decls += outCparams.preindent + indent + "const double " + outCparams.CSE_varprefix + SIMD_const_varnms[i] + " = " + SIMD_const_values[i] + ";\n"
SIMD_decls += outCparams.preindent+indent+ "const REAL_SIMD_ARRAY " + SIMD_const_varnms[i] + " = ConstSIMD("+ outCparams.CSE_varprefix + SIMD_const_varnms[i] + ");\n"
SIMD_decls += "\n"
# Step 7: Construct final output string
final_Ccode_output_str = commentblock
# Step 7a: Output C code in indented curly brackets if
# outCparams.includebraces = True
if outCparams.includebraces == "True": final_Ccode_output_str += outCparams.preindent+"{\n"
final_Ccode_output_str += prestring + SIMD_decls + outstring + poststring
if outCparams.includebraces == "True": final_Ccode_output_str += outCparams.preindent+"}\n"
# Step 8: If filename == "stdout", then output
# C code to standard out (useful for copy-paste or interactive
# mode). Otherwise output to file specified in variable name.
if filename == "stdout":
# Output to standard out (stdout; "the screen")
print(final_Ccode_output_str)
elif filename == "returnstring":
return final_Ccode_output_str
else:
# Output to the file specified by the function input parameter string 'filename':
with open(filename, outCparams.outCfileaccess) as file:
file.write(final_Ccode_output_str)
successstr = ""
if outCparams.outCfileaccess == "a":
successstr = "Appended "
elif outCparams.outCfileaccess == "w":
successstr = "Wrote "
print(successstr + "to file \"" + filename + "\"")
outC_function_prototype_dict = {}
outC_function_dict = {}
def Cfunction(desc="",type="void",name=None,params=None,preloop="",body=None,loopopts="",postloop="",opts=""):
if name == None or params == None or body == None:
print("Cfunction() error: strings must be provided for function name, parameters, and body")
sys.exit(1)
func_prototype = type+" "+name+"("+params+")"
include_Cparams_str = ""
if not "DisableCparameters" in opts:
if "EnableSIMD" in loopopts:
include_Cparams_str = "#include \"set_Cparameters-SIMD.h\"\n"
else:
include_Cparams_str = "#include \"set_Cparameters.h\"\n"
complete_func = ""
if desc != "":
complete_func = "/*\n" + desc + "\n */\n"
complete_func += func_prototype + " {\n"+include_Cparams_str+preloop+"\n"+lp.simple_loop(loopopts,body)+postloop+"}\n"
return func_prototype+";",complete_func
def add_to_Cfunction_dict(desc="",type="void",name=None,params=None,preloop="",body=None,loopopts="",postloop="",opts=""):
outC_function_prototype_dict[name],outC_function_dict[name] = Cfunction(desc,type,name,params,preloop,body,loopopts,postloop,opts)
def outCfunction(outfile="",desc="",type="void",name=None,params=None,preloop="",body=None,loopopts="",postloop="",opts=""):
ignoreprototype,Cfunc = Cfunction(desc,type,name,params,preloop,body,loopopts,postloop,opts)
if outfile == "returnstring":
return Cfunc
with open(outfile,"w") as file:
file.write(Cfunc)
print("Output C function "+name+"() to file "+outfile)
# reference_metric.py: Define all needed quantities
# for a reference metric.
# Given uniform (reference metric) coordinate
# (xx[0],xx[1],xx[2]), you must define:
# 1) xxmin[3],xxmax[3]: Valid ranges for each
# uniform coordinate xx0,xx1,xx2
# 2) xxSph[3]: Spherical coordinate (r,theta,phi),
# in terms of uniform coordinate xx0,xx1,xx2
# 3) xxCart[3]: Cartesian coordinate (x,y,z),
# in terms of uniform coordinate xx0,xx1,xx2
# 4) scalefactor_orthog:
# orthogonal coordinate scale factor
# (positive root of diagonal reference metric
# components)
# 5) Cart_to_xx[3]: Inverse of xxCart:
# xx0,xx1,xx2 as functions of (x,y,z).
# In the case that there exists no closed-form
# expression, then a root finder might be needed
# 6) UnitVectors[3][3]: Unit vectors of reference
# metric.
# Author: Zachariah B. Etienne
# zachetie **at** gmail **dot* com
import sympy as sp # Import SymPy
from outputC import outputC,superfast_uniq,outC_function_dict,add_to_Cfunction_dict # NRPy+: Core C code output module
import NRPy_param_funcs as par # NRPy+: Parameter interface
import grid as gri # NRPy+: Functions having to do with numerical grids
import indexedexp as ixp # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import sys # Standard Python modules for multiplatform OS-level functions
# Step 0a: Initialize parameters
thismodule = __name__
par.initialize_param(par.glb_param("char", thismodule, "CoordSystem", "Spherical"))
par.initialize_param(par.glb_param("char", thismodule, "enable_rfm_precompute", "False"))
par.initialize_param(par.glb_param("char", thismodule, "rfm_precompute_Ccode_outdir", "Ccode"))
# Step 0b: Declare global variables
xx = gri.xx
xxCart = ixp.zerorank1(DIM=4) # Must be set in terms of xx[]s
Cart_to_xx = ixp.zerorank1(DIM=4) # Must be set in terms of xx[]s
Cartx,Carty,Cartz = sp.symbols("Cartx Carty Cartz", real=True)
Cart = [Cartx,Carty,Cartz]
xxSph = ixp.zerorank1(DIM=4) # Must be set in terms of xx[]s
scalefactor_orthog = ixp.zerorank1(DIM=4) # Must be set in terms of xx[]s
scalefactor_orthog_funcform = ixp.zerorank1(DIM=4) # Must be set in terms of generic functions of xx[]s
have_already_called_reference_metric_function = False
def reference_metric(SymPySimplifyExpressions=True):
global f0_of_xx0_funcform, f1_of_xx1_funcform, f2_of_xx0_xx1_funcform, f3_of_xx0_funcform, f4_of_xx2_funcform
global f0_of_xx0, f1_of_xx1, f2_of_xx0_xx1, f3_of_xx0, f4_of_xx2
f0_of_xx0_funcform = sp.Function('f0_of_xx0_funcform')(xx[0])
f1_of_xx1_funcform = sp.Function('f1_of_xx1_funcform')(xx[1])
f2_of_xx0_xx1_funcform = sp.Function('f2_of_xx0_xx1_funcform')(xx[0], xx[1])
f3_of_xx0_funcform = sp.Function('f3_of_xx0_funcform')(xx[0])
f4_of_xx2_funcform = sp.Function('f4_of_xx2_funcform')(xx[2])
f0_of_xx0, f1_of_xx1, f2_of_xx0_xx1, f3_of_xx0, f4_of_xx2 = par.Cparameters("REAL", thismodule,
["f0_of_xx0", "f1_of_xx1", "f2_of_xx0_xx1", "f3_of_xx0", "f4_of_xx2"], 1e300)
# FIXME: Hack
f0_of_xx0__D0, f0_of_xx0__DD00, f0_of_xx0__DDD000 = par.Cparameters("REAL", thismodule,
["f0_of_xx0__D0", "f0_of_xx0__DD00",
"f0_of_xx0__DDD000"], 1e300)
f1_of_xx1__D1, f1_of_xx1__DD11, f1_of_xx1__DDD111 = par.Cparameters("REAL", thismodule,
["f1_of_xx1__D1", "f1_of_xx1__DD11",
"f1_of_xx1__DDD111"], 1e300)
f2_of_xx0_xx1__D0,f2_of_xx0_xx1__D1,f2_of_xx0_xx1__DD00,f2_of_xx0_xx1__DD11 = \
par.Cparameters("REAL", thismodule,
["f2_of_xx0_xx1__D0","f2_of_xx0_xx1__D1","f2_of_xx0_xx1__DD00","f2_of_xx0_xx1__DD11"],
1e300)
f3_of_xx0__D0,f3_of_xx0__DD00 = par.Cparameters("REAL", thismodule,["f3_of_xx0__D0","f3_of_xx0__DD00"], 1e300)
f4_of_xx2__D2,f4_of_xx2__DD22 = par.Cparameters("REAL", thismodule,["f4_of_xx2__D2","f4_of_xx2__DD22"], 1e300)
global have_already_called_reference_metric_function # setting to global enables other modules to see updated value.
have_already_called_reference_metric_function = True
CoordSystem = par.parval_from_str("reference_metric::CoordSystem")
M_PI,M_SQRT1_2 = par.Cparameters("#define",thismodule,["M_PI","M_SQRT1_2"],"")
global xxmin
global xxmax
global UnitVectors
UnitVectors = ixp.zerorank2(DIM=3)
# Set up hatted metric tensor, rescaling matrix, and rescaling vector
#####################################################################
# SPHERICAL-LIKE COORDINATE SYSTEMS WITH & WITHOUT RADIAL RESCALING #
#####################################################################
if CoordSystem == "Spherical" or CoordSystem == "SinhSpherical" or CoordSystem == "SinhSphericalv2":
# Adding assumption real=True can help simplify expressions involving xx[0] & xx[1] below.
xx[0] = sp.symbols("xx0", real=True)
xx[1] = sp.symbols("xx1", real=True)
if CoordSystem == "Spherical":
RMAX = par.Cparameters("REAL", thismodule, ["RMAX"],10.0)
xxmin = [sp.sympify(0), sp.sympify(0), -M_PI]
xxmax = [ RMAX, M_PI, M_PI]
r = xx[0]
th = xx[1]
ph = xx[2]
Cart_to_xx[0] = sp.sqrt(Cartx ** 2 + Carty ** 2 + Cartz ** 2)
Cart_to_xx[1] = sp.acos(Cartz / Cart_to_xx[0])
Cart_to_xx[2] = sp.atan2(Carty, Cartx)
elif CoordSystem == "SinhSpherical":
xxmin = [sp.sympify(0), sp.sympify(0), -M_PI]
xxmax = [sp.sympify(1), M_PI, M_PI]
AMPL, SINHW = par.Cparameters("REAL",thismodule,["AMPL","SINHW"],[10.0,0.2])
# Set SinhSpherical radial coordinate by default; overwrite later if CoordSystem == "SinhSphericalv2".
r = AMPL * (sp.exp(xx[0] / SINHW) - sp.exp(-xx[0] / SINHW)) / \
(sp.exp(1 / SINHW) - sp.exp(-1 / SINHW))
th = xx[1]
ph = xx[2]
Cart_to_xx[0] = SINHW*sp.asinh(sp.sqrt(Cartx ** 2 + Carty ** 2 + Cartz ** 2)*sp.sinh(1/SINHW)/AMPL)
Cart_to_xx[1] = sp.acos(Cartz / sp.sqrt(Cartx ** 2 + Carty ** 2 + Cartz ** 2))
Cart_to_xx[2] = sp.atan2(Carty, Cartx)
# SinhSphericalv2 adds the parameter "const_dr", which allows for a region near xx[0]=0 to have
# constant radial resolution of const_dr, provided the sinh() term does not dominate near xx[0]=0.
elif CoordSystem == "SinhSphericalv2":
xxmin = [sp.sympify(0), sp.sympify(0), -M_PI]
xxmax = [sp.sympify(1), M_PI, M_PI]
AMPL, SINHW = par.Cparameters("REAL",thismodule,["AMPL","SINHW"],[10.0,0.2])
const_dr = par.Cparameters("REAL",thismodule,["const_dr"],0.0625)
r = AMPL*( const_dr*xx[0] + (sp.exp(xx[0] / SINHW) - sp.exp(-xx[0] / SINHW)) /
(sp.exp(1 / SINHW) - sp.exp(-1 / SINHW)) )
th = xx[1]
ph = xx[2]
# NO CLOSED-FORM EXPRESSION FOR RADIAL INVERSION.
# Cart_to_xx[0] = "NewtonRaphson"
# Cart_to_xx[1] = sp.acos(Cartz / sp.sqrt(Cartx ** 2 + Carty ** 2 + Cartz ** 2))
# Cart_to_xx[2] = sp.atan2(Carty, Cartx)
xxSph[0] = r
xxSph[1] = th
xxSph[2] = ph
# Now define xCart, yCart, and zCart in terms of x0,xx[1],xx[2].
# Note that the relation between r and x0 is not necessarily trivial in SinhSpherical coordinates. See above.
xxCart[0] = xxSph[0]*sp.sin(xxSph[1])*sp.cos(xxSph[2])
xxCart[1] = xxSph[0]*sp.sin(xxSph[1])*sp.sin(xxSph[2])
xxCart[2] = xxSph[0]*sp.cos(xxSph[1])
scalefactor_orthog[0] = sp.diff(xxSph[0],xx[0])
scalefactor_orthog[1] = xxSph[0]
scalefactor_orthog[2] = xxSph[0]*sp.sin(xxSph[1])
f0_of_xx0 = xxSph[0]
f1_of_xx1 = sp.sin(xxSph[1])
scalefactor_orthog_funcform[0] = sp.diff(f0_of_xx0_funcform,xx[0])
scalefactor_orthog_funcform[1] = f0_of_xx0_funcform
scalefactor_orthog_funcform[2] = f0_of_xx0_funcform*f1_of_xx1_funcform
# Set the unit vectors
UnitVectors = [[ sp.sin(xxSph[1])*sp.cos(xxSph[2]), sp.sin(xxSph[1])*sp.sin(xxSph[2]), sp.cos(xxSph[1])],
[ sp.cos(xxSph[1])*sp.cos(xxSph[2]), sp.cos(xxSph[1])*sp.sin(xxSph[2]), -sp.sin(xxSph[1])],
[ -sp.sin(xxSph[2]), sp.cos(xxSph[2]), sp.sympify(0) ]]
######################################################################
# SPHERICAL-LIKE COORDINATE SYSTEMS WITH RADIAL AND THETA RESCALINGS #
######################################################################
elif CoordSystem == "NobleSphericalThetaOptionOne" or CoordSystem == "NobleSphericalThetaOptionTwo":
# WARNING: CANNOT BE USED FOR SENR RUNS;
# THESE DO NOT DEFINE xxmin, xxmax, Cart_to_xx
# ALSO THE RADIAL RESCALINGS ARE NOT ODD FUNCTIONS OF xx0,
# MEANING THAT CURVI. BOUNDARY CONDITIONS WILL NOT WORK.
Rin,R0 = par.Cparameters("REAL", thismodule, ["Rin","R0"],[1.08986052555408,0.0])
x0beg = sp.log(Rin-R0)
xx[0] = sp.symbols("xx0", real=True)
r = R0 + sp.exp(x0beg + xx[0])
# 0.053407075111026485 == 0.017*pi
th_c,xi,x1beg = par.Cparameters("REAL", thismodule, ["th_c","xi","x1beg"],[0.053407075111026485,0.25,0.0])
xx[1] = sp.symbols("xx1", real=True)
x1j = x1beg + xx[1]
if CoordSystem == "NobleSphericalThetaOptionOne":
th = th_c + (M_PI - 2*th_c)*x1j + xi*sp.sin(2*M_PI*x1j)
elif CoordSystem == "NobleSphericalThetaOptionTwo":
x1_n_exponent = par.Cparameters("REAL", thismodule, ["x1_n_exponent"],9.0)
th = M_PI/2 * ( 1 + (1 - xi)*(2*x1j - 1) + (xi - 2*th_c/M_PI)*(2*x1j - 1)**x1_n_exponent )
xx[2] = sp.symbols("xx2", real=True)
ph = xx[2]
xxSph[0] = r
xxSph[1] = th
xxSph[2] = ph
# Now define xCart, yCart, and zCart in terms of x0,xx[1],xx[2].
# Note that the relation between r and x0 is not necessarily trivial in SinhSpherical coordinates. See above.
xxCart[0] = xxSph[0]*sp.sin(xxSph[1])*sp.cos(xxSph[2])
xxCart[1] = xxSph[0]*sp.sin(xxSph[1])*sp.sin(xxSph[2])
xxCart[2] = xxSph[0]*sp.cos(xxSph[1])
scalefactor_orthog[0] = sp.diff(xxSph[0],xx[0])
scalefactor_orthog[1] = xxSph[0]
scalefactor_orthog[2] = xxSph[0]*sp.sin(xxSph[1])
# Set the unit vectors
UnitVectors = [[ sp.sin(xxSph[1])*sp.cos(xxSph[2]), sp.sin(xxSph[1])*sp.sin(xxSph[2]), sp.cos(xxSph[1])],
[ sp.cos(xxSph[1])*sp.cos(xxSph[2]), sp.cos(xxSph[1])*sp.sin(xxSph[2]), -sp.sin(xxSph[1])],
[ -sp.sin(xxSph[2]), sp.cos(xxSph[2]), sp.sympify(0) ]]
##########################################################################
# CYLINDRICAL-LIKE COORDINATE SYSTEMS WITH & WITHOUT RADIAL/Z RESCALINGS #
##########################################################################
elif CoordSystem == "Cylindrical" or CoordSystem == "SinhCylindrical" or CoordSystem == "SinhCylindricalv2":
# Assuming the cylindrical radial coordinate
# is positive makes nice simplifications of
# unit vectors possible.
xx[0] = sp.symbols("xx0", real=True)
if CoordSystem == "Cylindrical":
RHOMAX,ZMIN,ZMAX = par.Cparameters("REAL",thismodule,["RHOMAX","ZMIN","ZMAX"],[10.0,-10.0,10.0])
xxmin = [sp.sympify(0), -M_PI, ZMIN]
xxmax = [ RHOMAX, M_PI, ZMAX]
RHOCYL = xx[0]
PHICYL = xx[1]
ZCYL = xx[2]
Cart_to_xx[0] = sp.sqrt(Cartx ** 2 + Carty ** 2)
Cart_to_xx[1] = sp.atan2(Carty, Cartx)
Cart_to_xx[2] = Cartz
elif CoordSystem == "SinhCylindrical":
xxmin = [sp.sympify(0), -M_PI, sp.sympify(-1)]
xxmax = [sp.sympify(1), M_PI, sp.sympify(+1)]
AMPLRHO, SINHWRHO, AMPLZ, SINHWZ = par.Cparameters("REAL",thismodule,
["AMPLRHO","SINHWRHO","AMPLZ","SINHWZ"],
[ 10.0, 0.2, 10.0, 0.2])
# Set SinhCylindrical radial & z coordinates by default; overwrite later if CoordSystem == "SinhCylindricalv2".
RHOCYL = AMPLRHO * (sp.exp(xx[0] / SINHWRHO) - sp.exp(-xx[0] / SINHWRHO)) / (sp.exp(1 / SINHWRHO) - sp.exp(-1 / SINHWRHO))
# phi coordinate remains unchanged.
PHICYL = xx[1]
ZCYL = AMPLZ * (sp.exp(xx[2] / SINHWZ) - sp.exp(-xx[2] / SINHWZ)) / (sp.exp(1 / SINHWZ) - sp.exp(-1 / SINHWZ))
Cart_to_xx[0] = SINHWRHO*sp.asinh(sp.sqrt(Cartx ** 2 + Carty ** 2)*sp.sinh(1/SINHWRHO)/AMPLRHO)
Cart_to_xx[1] = sp.atan2(Carty, Cartx)
Cart_to_xx[2] = SINHWZ*sp.asinh(Cartz*sp.sinh(1/SINHWZ)/AMPLZ)
# SinhCylindricalv2 adds the parameters "const_drho", "const_dz", which allows for regions near xx[0]=0
# and xx[2]=0 to have constant rho and z resolution of const_drho and const_dz, provided the sinh() terms
# do not dominate near xx[0]=0 and xx[2]=0.
elif CoordSystem == "SinhCylindricalv2":
xxmin = [sp.sympify(0), -M_PI, sp.sympify(-1)]
xxmax = [sp.sympify(1), M_PI, sp.sympify(+1)]
AMPLRHO, SINHWRHO, AMPLZ, SINHWZ = par.Cparameters("REAL",thismodule,
["AMPLRHO","SINHWRHO","AMPLZ","SINHWZ"],
[ 10.0, 0.2, 10.0, 0.2])
const_drho, const_dz = par.Cparameters("REAL",thismodule,["const_drho","const_dz"],[0.0625,0.0625])
RHOCYL = AMPLRHO * ( const_drho*xx[0] + (sp.exp(xx[0] / SINHWRHO) - sp.exp(-xx[0] / SINHWRHO)) / (sp.exp(1 / SINHWRHO) - sp.exp(-1 / SINHWRHO)) )
PHICYL = xx[1]
ZCYL = AMPLZ * ( const_dz *xx[2] + (sp.exp(xx[2] / SINHWZ ) - sp.exp(-xx[2] / SINHWZ )) / (sp.exp(1 / SINHWZ ) - sp.exp(-1 / SINHWZ )) )
# NO CLOSED-FORM EXPRESSION FOR RADIAL OR Z INVERSION.
# Cart_to_xx[0] = "NewtonRaphson"
# Cart_to_xx[1] = sp.atan2(Carty, Cartx)
# Cart_to_xx[2] = "NewtonRaphson"
xxCart[0] = RHOCYL*sp.cos(PHICYL)
xxCart[1] = RHOCYL*sp.sin(PHICYL)
xxCart[2] = ZCYL
xxSph[0] = sp.sqrt(RHOCYL**2 + ZCYL**2)
xxSph[1] = sp.acos(ZCYL / xxSph[0])
xxSph[2] = PHICYL
scalefactor_orthog[0] = sp.diff(RHOCYL,xx[0])
scalefactor_orthog[1] = RHOCYL
scalefactor_orthog[2] = sp.diff(ZCYL,xx[2])
f0_of_xx0 = RHOCYL
f4_of_xx2 = sp.diff(ZCYL,xx[2])
scalefactor_orthog_funcform[0] = sp.diff(f0_of_xx0_funcform,xx[0])
scalefactor_orthog_funcform[1] = f0_of_xx0_funcform
scalefactor_orthog_funcform[2] = f4_of_xx2_funcform
# Set the unit vectors
UnitVectors = [[ sp.cos(PHICYL), sp.sin(PHICYL), sp.sympify(0)],
[-sp.sin(PHICYL), sp.cos(PHICYL), sp.sympify(0)],
[ sp.sympify(0), sp.sympify(0), sp.sympify(1)]]
elif CoordSystem == "SymTP" or CoordSystem == "SinhSymTP":
# var1, var2= sp.symbols('var1 var2',real=True)
bScale, SINHWAA, AMAX = par.Cparameters("REAL",thismodule,
["bScale","SINHWAA","AMAX"],
[0.5, 0.2, 10.0 ])
# Assuming xx0, xx1, and bScale
# are positive makes nice simplifications of
# unit vectors possible.
xx[0],xx[1] = sp.symbols("xx0 xx1", real=True)
xxmin = [sp.sympify(0), sp.sympify(0),-M_PI]
xxmax = [ AMAX, M_PI, M_PI]
AA = xx[0]
if CoordSystem == "SinhSymTP":
# With xxmax[0] == AMAX, sinh(xx0/AMAX) will evaluate to a number between 0 and 1.
# Similarly, sinh(xx0/(AMAX*SINHWAA)) / sinh(1/SINHWAA) will also evaluate to a number between 0 and 1.
# Then AA = AMAX*sinh(xx0/(AMAX*SINHWAA)) / sinh(1/SINHWAA) will evaluate to a number between 0 and AMAX.
AA = AMAX * (sp.exp(xx[0] / (AMAX*SINHWAA)) - sp.exp(-xx[0] / (AMAX*SINHWAA))) / (sp.exp(1 / SINHWAA) - sp.exp(-1 / AMAX))
var1 = sp.sqrt(AA**2 + (bScale * sp.sin(xx[1]))**2)
var2 = sp.sqrt(AA**2 + bScale**2)
RHOSYMTP = AA*sp.sin(xx[1])
PHSYMTP = xx[2]
ZSYMTP = var2*sp.cos(xx[1])
xxCart[0] = AA *sp.sin(xx[1])*sp.cos(xx[2])
xxCart[1] = AA *sp.sin(xx[1])*sp.sin(xx[2])
xxCart[2] = ZSYMTP
xxSph[0] = sp.sqrt(RHOSYMTP**2 + ZSYMTP**2)
xxSph[1] = sp.acos(ZSYMTP / xxSph[0])
xxSph[2] = PHSYMTP
if CoordSystem == "SymTP":
rSph = sp.sqrt(Cartx ** 2 + Carty ** 2 + Cartz ** 2)
thSph = sp.acos(Cartz / rSph)
phSph = sp.atan2(Carty, Cartx)
# Mathematica script to compute Cart_to_xx[]
# AA = x1;
# var2 = Sqrt[AA^2 + bScale^2];
# RHOSYMTP = AA*Sin[x2];
# ZSYMTP = var2*Cos[x2];
# Solve[{rSph == Sqrt[RHOSYMTP^2 + ZSYMTP^2],
# thSph == ArcCos[ZSYMTP/Sqrt[RHOSYMTP^2 + ZSYMTP^2]],
# phSph == x3},
# {x1, x2, x3}]
Cart_to_xx[0] = sp.sqrt(-bScale**2 + rSph**2 +
sp.sqrt(bScale**4 + 2*bScale**2*rSph**2 + rSph**4 -
4*bScale**2*rSph**2*sp.cos(thSph)**2))*M_SQRT1_2 # M_SQRT1_2 = 1/sqrt(2); define this way for UnitTesting
# The sign() function in the following expression ensures the correct root is taken.
Cart_to_xx[1] = sp.acos(sp.sign(Cartz)*(
sp.sqrt(1 + rSph**2/bScale**2 -
sp.sqrt(bScale**4 + 2*bScale**2*rSph**2 + rSph**4 -
4*bScale**2*rSph**2*sp.cos(thSph)**2)/bScale**2)*M_SQRT1_2)) # M_SQRT1_2 = 1/sqrt(2); define this way for UnitTesting
Cart_to_xx[2] = phSph
elif CoordSystem == "SinhSymTP":
pass
# Closed form expression for Cart_to_xx in SinhSymTP may exist, but has not yet been found
scalefactor_orthog[0] = sp.diff(AA,xx[0]) * var1 / var2
scalefactor_orthog[1] = var1
scalefactor_orthog[2] = AA * sp.sin(xx[1])
f0_of_xx0 = AA
f1_of_xx1 = sp.sin(xx[1])
f2_of_xx0_xx1 = var1
f3_of_xx0 = var2
scalefactor_orthog_funcform[0] = sp.diff(f0_of_xx0_funcform,xx[0]) * f2_of_xx0_xx1_funcform/f3_of_xx0_funcform
scalefactor_orthog_funcform[1] = f2_of_xx0_xx1_funcform
scalefactor_orthog_funcform[2] = f0_of_xx0_funcform*f1_of_xx1_funcform
# Set the transpose of the matrix of unit vectors
UnitVectors = [[sp.sin(xx[1]) * sp.cos(xx[2]) * var2 / var1,
sp.sin(xx[1]) * sp.sin(xx[2]) * var2 / var1,
AA * sp.cos(xx[1]) / var1],
[AA * sp.cos(xx[1]) * sp.cos(xx[2]) / var1,
AA * sp.cos(xx[1]) * sp.sin(xx[2]) / var1,
-sp.sin(xx[1]) * var2 / var1],
[-sp.sin(xx[2]), sp.cos(xx[2]), sp.sympify(0)]]
elif CoordSystem == "Cartesian":
xmin, xmax, ymin, ymax, zmin, zmax = par.Cparameters("REAL",thismodule,
["xmin","xmax","ymin","ymax","zmin","zmax"],
[ -10.0, 10.0, -10.0, 10.0, -10.0, 10.0])
xxmin = ["xmin", "ymin", "zmin"]
xxmax = ["xmax", "ymax", "zmax"]
xxCart[0] = xx[0]
xxCart[1] = xx[1]
xxCart[2] = xx[2]
xxSph[0] = sp.sqrt(xx[0] ** 2 + xx[1] ** 2 + xx[2] ** 2)
xxSph[1] = sp.acos(xx[2] / xxSph[0])
xxSph[2] = sp.atan2(xx[1], xx[0])
Cart_to_xx[0] = Cartx
Cart_to_xx[1] = Carty
Cart_to_xx[2] = Cartz
scalefactor_orthog[0] = sp.sympify(1)
scalefactor_orthog[1] = sp.sympify(1)
scalefactor_orthog[2] = sp.sympify(1)
scalefactor_orthog_funcform[0] = sp.sympify(1)
scalefactor_orthog_funcform[1] = sp.sympify(1)
scalefactor_orthog_funcform[2] = sp.sympify(1)
# Set the transpose of the matrix of unit vectors
UnitVectors = [[sp.sympify(1), sp.sympify(0), sp.sympify(0)],
[sp.sympify(0), sp.sympify(1), sp.sympify(0)],
[sp.sympify(0), sp.sympify(0), sp.sympify(1)]]
else:
print("CoordSystem == " + CoordSystem + " is not supported.")
sys.exit(1)
# Finally, call ref_metric__hatted_quantities()
# to construct hatted metric, derivs of hatted
# metric, and Christoffel symbols
ref_metric__hatted_quantities(SymPySimplifyExpressions)
# ref_metric__hatted_quantities(scalefactor_orthog_funcform,SymPySimplifyExpressions)
# ref_metric__hatted_quantities(scalefactor_orthog,SymPySimplifyExpressions)
def ref_metric__hatted_quantities(SymPySimplifyExpressions=True):
enable_rfm_precompute = False
if par.parval_from_str(thismodule+"::enable_rfm_precompute") == "True":
enable_rfm_precompute = True
# Step 0: Set dimension DIM
DIM = par.parval_from_str("grid::DIM")
global ReU,ReDD,ghatDD,ghatUU,detgammahat
ReU = ixp.zerorank1()
ReDD = ixp.zerorank2()
ghatDD = ixp.zerorank2()
# Step 1: Compute ghatDD (reference metric), ghatUU
# (inverse reference metric), as well as
# rescaling vector ReU & rescaling matrix ReDD
if enable_rfm_precompute == False:
for i in range(DIM):
scalefactor_orthog[i] = sp.sympify(scalefactor_orthog[i])
ghatDD[i][i] = scalefactor_orthog[i]**2
ReU[i] = 1/scalefactor_orthog[i]
for j in range(DIM):
ReDD[i][j] = scalefactor_orthog[i]*scalefactor_orthog[j]
else:
for i in range(DIM):
scalefactor_orthog_funcform[i] = sp.sympify(scalefactor_orthog_funcform[i])
ghatDD[i][i] = scalefactor_orthog_funcform[i]**2
ReU[i] = 1/scalefactor_orthog_funcform[i]
for j in range(DIM):
ReDD[i][j] = scalefactor_orthog_funcform[i]*scalefactor_orthog_funcform[j]
# Step 1b: Compute ghatUU
ghatUU, detgammahat = ixp.symm_matrix_inverter3x3(ghatDD)
# Step 1c: Sanity check: verify that ReDD, ghatDD,
# and ghatUU are all symmetric rank-2:
for i in range(DIM):
for j in range(DIM):
if ReDD[i][j] != ReDD[j][i]:
print("Error: ReDD["+ str(i) + "][" + str(j) + "] != ReDD["+ str(j) + "][" + str(i) + ": " + str(ReDD[i][j]) + "!=" + str(ReDD[j][i]))
sys.exit(1)
if ghatDD[i][j] != ghatDD[j][i]:
print("Error: ghatDD["+ str(i) + "][" + str(j) + "] != ghatDD["+ str(j) + "][" + str(i) + ": " + str(ghatDD[i][j]) + "!=" + str(ghatDD[j][i]))
sys.exit(1)
if ghatUU[i][j] != ghatUU[j][i]:
print("Error: ghatUU["+ str(i) + "][" + str(j) + "] != ghatUU["+ str(j) + "][" + str(i) + ": " + str(ghatUU[i][j]) + "!=" + str(ghatUU[j][i]))
sys.exit(1)
# Step 2: Compute det(ghat) and its 1st & 2nd derivatives
global detgammahatdD,detgammahatdDD
detgammahatdD = ixp.zerorank1(DIM)
detgammahatdDD = ixp.zerorank2(DIM)
for i in range(DIM):
detgammahatdD[i] = (sp.diff(detgammahat, xx[i]))
for j in range(DIM):
detgammahatdDD[i][j] = sp.diff(detgammahatdD[i], xx[j])
# Step 3a: Compute 1st & 2nd derivatives of rescaling vector.
# (E.g., needed in BSSN for betaUdDD computation)
global ReUdD,ReUdDD
ReUdD = ixp.zerorank2(DIM)
ReUdDD = ixp.zerorank3(DIM)
for i in range(DIM):
for j in range(DIM):
ReUdD[i][j] = sp.diff(ReU[i], xx[j])
for k in range(DIM):
ReUdDD[i][j][k] = sp.diff(ReUdD[i][j], xx[k])
# Step 3b: Compute 1st & 2nd derivatives of rescaling matrix.
global ReDDdD,ReDDdDD
ReDDdD = ixp.zerorank3(DIM)
ReDDdDD = ixp.zerorank4(DIM)
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
ReDDdD[i][j][k] = (sp.diff(ReDD[i][j],xx[k]))
for l in range(DIM):
# Simplifying this doesn't appear to help overall NRPy run time.
ReDDdDD[i][j][k][l] = sp.diff(ReDDdD[i][j][k],xx[l])
# Step 3c: Compute 1st & 2nd derivatives of reference metric.
global ghatDDdD,ghatDDdDD
ghatDDdD = ixp.zerorank3(DIM)
ghatDDdDD = ixp.zerorank4(DIM)
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
if SymPySimplifyExpressions==True:
# ghatDDdD[i][j][k] = sp.trigsimp(sp.diff(ghatDD[i][j],xx[k])) # FIXME: BAD: MUST BE SIMPLIFIED OR ANSWER IS INCORRECT! Must be some bug in sympy...
ghatDDdD[i][j][k] = sp.simplify(sp.diff(ghatDD[i][j],xx[k])) # FIXME: BAD: MUST BE SIMPLIFIED OR ANSWER IS INCORRECT! Must be some bug in sympy...
else:
ghatDDdD[i][j][k] = (sp.diff(ghatDD[i][j],xx[k])) # FIXME: BAD: MUST BE SIMPLIFIED OR ANSWER IS INCORRECT! Must be some bug in sympy...
for l in range(DIM):
ghatDDdDD[i][j][k][l] = (sp.diff(ghatDDdD[i][j][k],xx[l]))
# Step 4a: Compute Christoffel symbols of reference metric.
global GammahatUDD
GammahatUDD = ixp.zerorank3(DIM)
for i in range(DIM):
for k in range(DIM):
for l in range(DIM):
for m in range(DIM):
# GammahatUDD[i][k][l] += sp.trigsimp((sp.Rational(1,2))*ghatUU[i][m]*\
GammahatUDD[i][k][l] += (sp.Rational(1,2))*ghatUU[i][m]*\
(ghatDDdD[m][k][l] + ghatDDdD[m][l][k] - ghatDDdD[k][l][m])
# Step 4b: Compute derivs of Christoffel symbols of reference metric.
global GammahatUDDdD
GammahatUDDdD = ixp.zerorank4(DIM)
for i in range(DIM):
for j in range(DIM):
for k in range(DIM):
for l in range(DIM):
GammahatUDDdD[i][j][k][l] = (sp.diff(GammahatUDD[i][j][k],xx[l]))
# Step 4c: If rfm_precompute is disabled, then we are finished with this function.
# Otherwise continue to Step 5.
if enable_rfm_precompute == False:
return
else:
CoordSystem = par.parval_from_str("reference_metric::CoordSystem")
# if not (("Spherical" in CoordSystem) or ("SymTP" in CoordSystem)):
# if not (( "Spherical" in CoordSystem)):
# print("Error: CoordSystem == "+CoordSystem+" does not yet support rfm precompute infrastructure.")
# sys.exit(1)
# enable_rfm_precompute: precompute and store in memory complicated
# expressions related to the reference metric (a.k.a., "hatted
# quantities")
# The precomputed "hatted quantity" expressions will be stored in
# a C struct called rfmstruct. As these expressions generally
# involve computationally expensive transcendental functions
# of xx0,xx1,or xx2, and xx0,xx1, and xx2 remain fixed across
# most (if not all) of a given simulation, setting up the
# rfmstruct can greatly improve performance.
# The core challenge in setting up the rfmstruct is collecting
# all the information needed to automatically generate it.
# Step 5 and onwards implements this algorithm, using the
# *generic functional form* of the hatted quantities (as
# opposed to the exact closed-form expressions of the
# hatted quantities) computed above.
# Step 5: Now that all hatted quantities are written in terms of generic SymPy functions,
# we will now replace SymPy functions with simple variables using rigid NRPy+ syntax,
# and store these variables to globals defined above.
def make_replacements(expr):
sympy_version = sp.__version__.replace("rc","...").replace("b","...") # Ignore the rc's and b's for release candidates & betas.
sympy_major_version = int(sympy_version.split(".")[0])
sympy_minor_version = int(sympy_version.split(".")[1])
if sympy_major_version < 1 or (sympy_major_version >= 1 and sympy_minor_version < 2):
print("Detected SymPy version "+sympy_version)
print("Sorry, reference metric precomputation unsupported in SymPy < 1.2!")
sys.exit(1)
for item in sp.preorder_traversal(expr):
if item.func == sp.Derivative:
stringfunc = str(item.args[0]).split("_funcform(", 1)[0] # store everything before _funcform(...
stringderv = str(item.args[1]).replace(" ", "") # Ignore whitespace
deriv_wrt = stringderv.split(",")[0].replace("(xx", "")
derivorder = int(stringderv.split(",")[1].replace(")", ""))
derivop = "__D"
for i in range(derivorder - 1):
derivop += "D"
derivop += deriv_wrt
for i in range(derivorder - 1):
derivop += deriv_wrt
expr = expr.xreplace(
{item: sp.sympify(stringfunc + derivop)})
for item in sp.preorder_traversal(expr):
if "_funcform" in str(item.func):
stringfunc = str(item.func).split("_funcform", 1)[0] # store everything before _funcform(...
expr = expr.xreplace({item: sp.sympify(stringfunc)})
return expr
detgammahat = make_replacements(detgammahat)
for i in range(DIM):
ReU[i] = make_replacements(ReU[i])
detgammahatdD[i] = make_replacements(detgammahatdD[i])
for j in range(DIM):
ReDD[i][j] = make_replacements(ReDD[i][j])
ReUdD[i][j] = make_replacements(ReUdD[i][j])
ghatDD[i][j] = make_replacements(ghatDD[i][j])
ghatUU[i][j] = make_replacements(ghatUU[i][j])
detgammahatdDD[i][j] = make_replacements(detgammahatdDD[i][j])
for k in range(DIM):
ReDDdD[i][j][k] = make_replacements(ReDDdD[i][j][k])
ReUdDD[i][j][k] = make_replacements(ReUdDD[i][j][k])
ghatDDdD[i][j][k] = make_replacements(ghatDDdD[i][j][k])
GammahatUDD[i][j][k] = make_replacements(GammahatUDD[i][j][k])
for l in range(DIM):
ReDDdDD[i][j][k][l] = make_replacements(ReDDdDD[i][j][k][l])
ghatDDdDD[i][j][k][l] = make_replacements(ghatDDdDD[i][j][k][l])
GammahatUDDdD[i][j][k][l] = make_replacements(GammahatUDDdD[i][j][k][l])
# Step 6: At this point, each expression is written in terms of the generic functions
# of xx0, xx1, and/or xx2 and their derivatives. Depending on the functions, some
# of these derivatives may be zero. In Step 5 we'll evaluate the function
# derivatives exactly and set the expressions to zero. Otherwise in the C code
# we'd be storing performing arithmetic with zeros -- wasteful!
# Step 6.a: Construct the full list of *unique* NRPy+ variables representing the
# SymPy functions and derivatives, so that all zero derivatives can be
# computed.
freevars = []
freevars.extend(detgammahat.free_symbols)
for i in range(DIM):
freevars.extend(ReU[i].free_symbols)
freevars.extend(detgammahatdD[i].free_symbols)
for j in range(DIM):
freevars.extend(ReDD[i][j].free_symbols)
freevars.extend(ReUdD[i][j].free_symbols)
freevars.extend(ghatDD[i][j].free_symbols)
freevars.extend(ghatUU[i][j].free_symbols)
freevars.extend(detgammahatdDD[i][j].free_symbols)
for k in range(DIM):
freevars.extend(ReDDdD[i][j][k].free_symbols)
freevars.extend(ReUdDD[i][j][k].free_symbols)
freevars.extend(ghatDDdD[i][j][k].free_symbols)
freevars.extend(GammahatUDD[i][j][k].free_symbols)
for l in range(DIM):
freevars.extend(ReDDdDD[i][j][k][l].free_symbols)
freevars.extend(ghatDDdDD[i][j][k][l].free_symbols)
freevars.extend(GammahatUDDdD[i][j][k][l].free_symbols)
freevars_uniq = superfast_uniq(freevars)
freevars_uniq_xx_indep = []
for i in range(len(freevars_uniq)):
freevars_uniq_xx_indep.append(freevars_uniq[i])
# Step 6.b: Using the expressions f?_of_xx? set in reference_metric(),
# evaluate each needed derivative and, in the case it is zero,
# set the corresponding "freevar" variable to zero.
freevars_uniq_vals = []
for i in range(len(freevars_uniq)):
var = freevars_uniq[i]
basename = str(var).split("__")[0].replace("_funcform", "")
derivatv = ""
if "__" in str(var):
derivatv = str(var).split("__")[1].replace("_funcform", "")
if basename == "f0_of_xx0":
basefunc = f0_of_xx0
elif basename == "f1_of_xx1":
basefunc = f1_of_xx1
elif basename == "f2_of_xx0_xx1":
basefunc = f2_of_xx0_xx1
elif basename == "f3_of_xx0":
basefunc = f3_of_xx0
elif basename == "f4_of_xx2":
basefunc = f4_of_xx2
else:
print("Error: function inside " + str(var) + " undefined.")
sys.exit(1)
diff_result = basefunc
if derivatv == "":
pass
else:
derivorder = derivatv.replace("d", "").replace("D", "").replace("0", "0 ").replace("1", "1 ").replace(
"2", "2 ").split(" ")
for derivdirn in derivorder:
if derivdirn != "":
derivwrt = xx[int(derivdirn)]
diff_result = sp.diff(diff_result, derivwrt)
freevars_uniq_vals.append(diff_result)
frees_uniq = superfast_uniq(diff_result.free_symbols)
xx_dep = False
for dirn in range(3):
if gri.xx[dirn] in frees_uniq:
xx_dep = True
if xx_dep == False:
freevars_uniq_xx_indep[i] = diff_result
# Step 6.c: Finally, substitute integers for all functions & derivatives that evaluate to integers
for varidx in range(len(freevars_uniq)):
detgammahat = detgammahat.subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
for i in range(DIM):
ReU[i] = ReU[i].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
detgammahatdD[i] = detgammahatdD[i].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
for j in range(DIM):
ReDD[i][j] = ReDD[i][j].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
ReUdD[i][j] = ReUdD[i][j].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
ghatDD[i][j] = ghatDD[i][j].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
ghatUU[i][j] = ghatUU[i][j].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
detgammahatdDD[i][j] = detgammahatdDD[i][j].subs(freevars_uniq[varidx],
freevars_uniq_xx_indep[varidx])
for k in range(DIM):
ReDDdD[i][j][k] = ReDDdD[i][j][k].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
ReUdDD[i][j][k] = ReUdDD[i][j][k].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
ghatDDdD[i][j][k] = ghatDDdD[i][j][k].subs(freevars_uniq[varidx], freevars_uniq_xx_indep[varidx])
GammahatUDD[i][j][k] = GammahatUDD[i][j][k].subs(freevars_uniq[varidx],
freevars_uniq_xx_indep[varidx])
for l in range(DIM):
ReDDdDD[i][j][k][l] = ReDDdDD[i][j][k][l].subs(freevars_uniq[varidx],
freevars_uniq_xx_indep[varidx])
ghatDDdDD[i][j][k][l] = ghatDDdDD[i][j][k][l].subs(freevars_uniq[varidx],
freevars_uniq_xx_indep[varidx])
GammahatUDDdD[i][j][k][l] = GammahatUDDdD[i][j][k][l].subs(freevars_uniq[varidx],
freevars_uniq_xx_indep[varidx])
# Step 7: Construct needed C code for declaring rfmstruct, allocating storage for
# rfmstruct arrays, defining each element in each array, reading the
# rfmstruct data from memory (both with and without SIMD enabled), and
# freeing allocated memory for the rfmstrcut arrays.
# struct_str: String that declares the rfmstruct struct.
struct_str = "typedef struct __rfmstruct__ {\n"
define_str = ""
# rfmstruct stores pointers to (so far) 1D arrays. The malloc_str string allocates space for the arrays.
malloc_str = "rfm_struct rfmstruct;\n"
freemm_str = ""
# readvr_str reads the arrays from memory as needed
readvr_str = ["", "", ""]
readvr_SIMD_outer_str = ["", "", ""]
readvr_SIMD_inner_str = ["", "", ""]
# Sort freevars_uniq_vals and freevars_uniq_xx_indep, according to alphabetized freevars_uniq_xx_indep.
# Without this step, the ordering of elements in rfmstruct would be random, and would change each time
# this function was called.
if len(freevars_uniq_xx_indep) > 0:
freevars_uniq_xx_indep, freevars_uniq_vals = (list(x) for x in zip(*sorted(zip(freevars_uniq_xx_indep, freevars_uniq_vals),key=str)))
# Tease out how many variables each function in freevars_uniq_vals
which_freevar = 0
for expr in freevars_uniq_vals:
if "_of_xx" in str(freevars_uniq_xx_indep[which_freevar]):
frees = expr.free_symbols
frees_uniq = superfast_uniq(frees)
xx_list = []
malloc_size = 1
for i in range(3):
if gri.xx[i] in frees_uniq:
xx_list.append(gri.xx[i])
malloc_size *= gri.Nxx_plus_2NGHOSTS[i]
struct_str += "\tREAL *restrict " + str(freevars_uniq_xx_indep[which_freevar]) + ";\n"
malloc_str += "rfmstruct." + str(
freevars_uniq_xx_indep[which_freevar]) + " = (REAL *)malloc(sizeof(REAL)*" + str(malloc_size) + ");\n"
freemm_str += "free(rfmstruct." + str(freevars_uniq_xx_indep[which_freevar]) + ");\n"
output_define_and_readvr = False
for dirn in range(3):
if (gri.xx[dirn] in frees_uniq) and not (gri.xx[(dirn+1)%3] in frees_uniq) and not (gri.xx[(dirn+2)%3] in frees_uniq):
define_str += "for(int i"+str(dirn)+"=0;i"+str(dirn)+"<Nxx_plus_2NGHOSTS"+str(dirn)+";i"+str(dirn)+"++) {\n"
define_str += " const REAL xx"+str(dirn)+" = xx["+str(dirn)+"][i"+str(dirn)+"];\n"
define_str += " rfmstruct." + str(freevars_uniq_xx_indep[which_freevar]) + "[i"+str(dirn)+"] = " + str(sp.ccode(freevars_uniq_vals[which_freevar])) + ";\n"
define_str += "}\n\n"
readvr_str[dirn] += "const REAL " + str(freevars_uniq_xx_indep[which_freevar]) + " = rfmstruct->" + \
str(freevars_uniq_xx_indep[which_freevar]) + "[i"+str(dirn)+"];\n"
readvr_SIMD_outer_str[dirn] += "const double NOSIMD" + str(
freevars_uniq_xx_indep[which_freevar]) + " = rfmstruct->" + str(freevars_uniq_xx_indep[which_freevar]) + "[i"+str(dirn)+"]; "
readvr_SIMD_outer_str[dirn] += "const REAL_SIMD_ARRAY " + str(freevars_uniq_xx_indep[which_freevar]) + \
" = ConstSIMD(NOSIMD" + str(freevars_uniq_xx_indep[which_freevar]) + ");\n"
readvr_SIMD_inner_str[dirn] += "const REAL_SIMD_ARRAY " + str(freevars_uniq_xx_indep[which_freevar]) + \
" = ReadSIMD(&rfmstruct->" + str(freevars_uniq_xx_indep[which_freevar]) + "[i"+str(dirn)+"]);\n"
output_define_and_readvr = True
if (output_define_and_readvr == False) and (gri.xx[0] in frees_uniq) and (gri.xx[1] in frees_uniq):
define_str += """
for(int i1=0;i1<Nxx_plus_2NGHOSTS1;i1++) for(int i0=0;i0<Nxx_plus_2NGHOSTS0;i0++) {
const REAL xx0 = xx[0][i0];
const REAL xx1 = xx[1][i1];
rfmstruct.""" + str(freevars_uniq_xx_indep[which_freevar]) + """[i0 + Nxx_plus_2NGHOSTS0*i1] = """ + str(sp.ccode(freevars_uniq_vals[which_freevar])) + """;
}\n\n"""
readvr_str[0] += "const REAL " + str(freevars_uniq_xx_indep[which_freevar]) + " = rfmstruct->" + \
str(freevars_uniq_xx_indep[which_freevar]) + "[i0 + Nxx_plus_2NGHOSTS0*i1];\n"
readvr_SIMD_outer_str[0] += "const double NOSIMD" + str(freevars_uniq_xx_indep[which_freevar]) + \
" = rfmstruct->" + str(freevars_uniq_xx_indep[which_freevar]) + "[i0 + Nxx_plus_2NGHOSTS0*i1]; "
readvr_SIMD_outer_str[0] += "const REAL_SIMD_ARRAY " + str(freevars_uniq_xx_indep[which_freevar]) + \
" = ConstSIMD(NOSIMD" + str(freevars_uniq_xx_indep[which_freevar]) + ");\n"
readvr_SIMD_inner_str[0] += "const REAL_SIMD_ARRAY " + str(freevars_uniq_xx_indep[which_freevar]) + \
" = ReadSIMD(&rfmstruct->" + str(freevars_uniq_xx_indep[which_freevar]) + "[i0 + Nxx_plus_2NGHOSTS0*i1]);\n"
output_define_and_readvr = True
if output_define_and_readvr == False:
print("ERROR: Could not figure out the (xx0,xx1,xx2) dependency within the expression for "+str(freevars_uniq_xx_indep[which_freevar])+":")
print(str(freevars_uniq_vals[which_freevar]))
sys.exit(1)
which_freevar += 1
struct_str += "} rfm_struct;\n\n"
# Step 8: Output needed C code to files
outdir = par.parval_from_str(thismodule+"::rfm_precompute_Ccode_outdir")
with open(outdir + "/rfm_struct__declare.h", "w") as file:
file.write(struct_str)
with open(outdir + "/rfm_struct__malloc.h", "w") as file:
file.write(malloc_str)
with open(outdir + "/rfm_struct__define.h", "w") as file:
file.write(define_str)
for i in range(3):
with open(outdir + "/rfm_struct__read" + str(i) + ".h", "w") as file:
file.write(readvr_str[i])
with open(outdir + "/rfm_struct__SIMD_outer_read" + str(i) + ".h", "w") as file:
file.write(readvr_SIMD_outer_str[i])
with open(outdir + "/rfm_struct__SIMD_inner_read" + str(i) + ".h", "w") as file:
file.write(readvr_SIMD_inner_str[i])
with open(outdir + "/rfm_struct__freemem.h", "w") as file:
file.write(freemm_str)
def get_EigenCoord():
CoordSystem_orig = par.parval_from_str("reference_metric::CoordSystem")
for EigenCoordstr in ["Spherical","Cylindrical","SymTP","Cartesian"]:
if EigenCoordstr in CoordSystem_orig:
return EigenCoordstr
print("Error: Could not find EigenCoord for reference_metric::CoordSystem == "+CoordSystem_orig)
sys.exit(1)
def set_Nxx_dxx_invdx_params__and__xx_h(outdir="."):
import os
with open(os.path.join(outdir,"set_Nxx_dxx_invdx_params__and__xx.h"),"w") as file:
file.write("""
void set_Nxx_dxx_invdx_params__and__xx(const int EigenCoord, const int Nxx[3],
paramstruct *restrict params, REAL *restrict xx[3]) {
// Override parameter defaults with values based on command line arguments and NGHOSTS.
params->Nxx0 = Nxx[0];
params->Nxx1 = Nxx[1];
params->Nxx2 = Nxx[2];
params->Nxx_plus_2NGHOSTS0 = Nxx[0] + 2*NGHOSTS;
params->Nxx_plus_2NGHOSTS1 = Nxx[1] + 2*NGHOSTS;
params->Nxx_plus_2NGHOSTS2 = Nxx[2] + 2*NGHOSTS;
// Step 0d: Set up space and time coordinates
// Step 0d.i: Declare \Delta x^i=dxx{0,1,2} and invdxx{0,1,2}, as well as xxmin[3] and xxmax[3]:
#include "set_Cparameters.h"
REAL xxmin[3],xxmax[3];
if(EigenCoord == 0) {
""")
for i in range(3):
file.write(" xxmin["+str(i)+"] = "+str(xxmin[i])+";\n")
file.write(" xxmax["+str(i)+"] = "+str(xxmax[i])+";\n")
file.write("""
} else if (EigenCoord == 1) {
""")
CoordSystem_orig = par.parval_from_str("reference_metric::CoordSystem")
par.set_parval_from_str("reference_metric::CoordSystem",get_EigenCoord())
reference_metric()
for i in range(3):
file.write(" xxmin["+str(i)+"] = "+str(xxmin[i])+";\n")
file.write(" xxmax["+str(i)+"] = "+str(xxmax[i])+";\n")
par.set_parval_from_str("reference_metric::CoordSystem",CoordSystem_orig)
reference_metric()
file.write("""
}
params->dxx0 = (xxmax[0] - xxmin[0]) / ((REAL)Nxx[0]);
params->dxx1 = (xxmax[1] - xxmin[1]) / ((REAL)Nxx[1]);
params->dxx2 = (xxmax[2] - xxmin[2]) / ((REAL)Nxx[2]);
params->invdx0 = 1.0/params->dxx0;
params->invdx1 = 1.0/params->dxx1;
params->invdx2 = 1.0/params->dxx2;
// Now that params.dxx{0,1,2} and params.invdxx{0,1,2} have been set,
// Step 0d.iii: Set up uniform coordinate grids
xx[0] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS0);
for(int j=0;j<Nxx_plus_2NGHOSTS0;j++)
xx[0][j] = xxmin[0] + ((REAL)(j-NGHOSTS) + (1.0/2.0))*params->dxx0; // Cell-centered grid.
xx[1] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS1);
for(int j=0;j<Nxx_plus_2NGHOSTS1;j++)
xx[1][j] = xxmin[1] + ((REAL)(j-NGHOSTS) + (1.0/2.0))*params->dxx1; // Cell-centered grid.
xx[2] = (REAL *)malloc(sizeof(REAL)*Nxx_plus_2NGHOSTS2);
for(int j=0;j<Nxx_plus_2NGHOSTS2;j++)
xx[2][j] = xxmin[2] + ((REAL)(j-NGHOSTS) + (1.0/2.0))*params->dxx2; // Cell-centered grid.
//fprintf(stderr,"hey inside setxx: %e %e %e | %e %e\\n",xxmin[0],xxmin[1],xxmin[2],xx[0][0],params->dxx0);
}
""")
def xxCart_h(funcname,cparamsloc,outfile):
import outputC
# Arbitrary-coordinate NRPy+ file output, Part 1: output the conversion from (x0,x1,x2) to Cartesian (x,y,z)
Cout = outputC.outputC([xxCart[0],xxCart[1],xxCart[2]],
["xCart[0]","xCart[1]","xCart[2]"],
"returnstring",params="preindent=1")
with open(outfile, "w") as file:
file.write("""
inline void """+funcname+"""(const paramstruct *restrict params, REAL *restrict xx[3],const int i0,const int i1,const int i2, REAL xCart[3]) {
#include """+"\""+cparamsloc+"\""+"""
REAL xx0 = xx[0][i0];
REAL xx1 = xx[1][i1];
REAL xx2 = xx[2][i2];\n"""+Cout+"}\n")
# Compute proper distance in all 3 directions. Used to find the appropriate timestep for the CFL condition.
def ds_dirn(delxx):
ds_dirn = ixp.zerorank1(3)
for i in range(3):
ds_dirn[i] = delxx[i]*scalefactor_orthog[i]
return ds_dirn
# Find the appropriate timestep for the CFL condition.
def add_find_timestep_func_to_dict():
# Compute proper distance in all 3 directions.
delxx = ixp.declarerank1("dxx", DIM=3)
ds_drn = ds_dirn(delxx)
ds_dirn_h = outputC([ds_drn[0], ds_drn[1], ds_drn[2]], ["ds_dirn0", "ds_dirn1", "ds_dirn2"],"returnstring")
desc="Find the CFL-constrained timestep"
add_to_Cfunction_dict(
desc =desc,
type ="REAL",
name ="find_timestep",
params ="const paramstruct *restrict params, REAL *restrict xx[3]",
preloop ="REAL dsmin = 1e38; // Start with a crazy high value... close to the largest number in single precision.",
body ="REAL ds_dirn0, ds_dirn1, ds_dirn2;\n"+ds_dirn_h+"""
#ifndef MIN
#define MIN(A, B) ( ((A) < (B)) ? (A) : (B) )
#endif
// Set dsmin = MIN(dsmin, ds_dirn0, ds_dirn1, ds_dirn2);
dsmin = MIN(dsmin,MIN(ds_dirn0,MIN(ds_dirn1,ds_dirn2)));
""",
loopopts ="InteriorPoints,Read_xxs,DisableOpenMP",
postloop ="return dsmin*CFL_FACTOR/wavespeed;\n")
def out_timestep_func_to_file(outfile):
add_find_timestep_func_to_dict()
with open(outfile, "w") as file:
file.write(outC_function_dict["find_timestep"])
def out_default_free_parameters_for_rfm(free_parameters_file,
domain_size=1.0,sinh_width=0.4,sinhv2_const_dr=0.05,SymTP_bScale=0.5):
CoordSystem = par.parval_from_str("reference_metric::CoordSystem")
with open(free_parameters_file, "a") as file:
file.write("""
// Set free-parameter values.
const REAL domain_size = """ + str(domain_size) + """;
const REAL sinh_width = """ + str(sinh_width) + """;
const REAL sinhv2_const_dr= """ + str(sinhv2_const_dr) + """;
const REAL SymTP_bScale = """ + str(SymTP_bScale) + ";\n")
coordparams = ""
if CoordSystem == "Spherical":
coordparams += """
params.RMAX = domain_size;\n"""
elif "SinhSpherical" in CoordSystem:
coordparams += """
params.AMPL = domain_size;
params.SINHW= sinh_width;\n"""
if CoordSystem == "SinhSphericalv2":
coordparams += " params.const_dr = sinhv2_const_dr;\n"
elif "SymTP" in CoordSystem:
coordparams += """
params.bScale = SymTP_bScale;
params.AMAX = domain_size;\n"""
if CoordSystem == "SinhSymTP":
coordparams += " params.SINHWAA = sinh_width;\n"
elif CoordSystem == "Cartesian":
coordparams += """
params.xmin = -domain_size, params.xmax = domain_size;
params.ymin = -domain_size, params.ymax = domain_size;
params.zmin = -domain_size, params.zmax = domain_size;\n"""
elif CoordSystem == "Cylindrical":
coordparams += """
params.ZMIN = -domain_size;
params.ZMAX = domain_size;
params.RHOMAX = domain_size;\n"""
elif "SinhCylindrical" in CoordSystem:
coordparams += """
params.AMPLRHO = domain_size;
params.SINHWRHO= sinh_width;
params.AMPLZ = domain_size;
params.SINHWZ = sinh_width;\n"""
if CoordSystem == "SinhCylindricalv2":
coordparams += """
params.const_drho = sinhv2_const_dr;
params.const_dz = sinhv2_const_dr;\n"""
file.write(coordparams + "\n")
sympy
matplotlib
#!/bin/bash
if [ -z "$1" ]; then
echo "Correct usage of this script:"
echo "./run_Jupyter_notebook.sh [Jupyter notebook (a file with .ipynb extension)]"
exit
fi
if [ ! -f $1 ]; then
echo "You input: ./run_Jupyter_notebook.sh" $1
echo "Jupyter notebook" \"$1\" "not found!"
exit
fi
# NRPy+ Jupyter notebooks are completely Python 2/3 cross-compatible.
# However `jupyter nbconvert` will refuse to run if the notebook
# was generated using a different kernel. Here we fool Jupyter
# to think the notebook was written using the native python kernel.
PYTHONMAJORVERSION=`python -c "import sys;print(sys.version_info[0])"`
if (( $PYTHONMAJORVERSION == 3 )); then
cat $1 | sed "s/ \"name\": \"python2\"/ \"name\": \"python3\"/g" > $1-tmp ; mv $1-tmp $1
else
cat $1 | sed "s/ \"name\": \"python3\"/ \"name\": \"python2\"/g" > $1-tmp ; mv $1-tmp $1
fi
time jupyter nbconvert --to notebook --inplace --execute --ExecutePreprocessor.timeout=-1 $1