Notebook Status: Self-Validated
Validation Notes: This tutorial notebook has been confirmed to be self-consistent with its corresponding NRPy+ module, as documented below. Additional validation tests may have been performed, but are as yet, undocumented. (TODO)
These C functions take as input the ADM variables
{γij,Kij,α,βi,Bi}and optionally stress-energy tensor
Tμνin the spherical or Cartesian basis. It is up to the user to either provide the needed function for reading in these data or use one provided within NRPy+. The function takes the form
void ID_function(const paramstruct *params, const REAL xCart[3],
const ID_persist_struct *restrict ID_persist,
initial_data_struct *restrict initial_data)
where
params
provides NRPy+ input parametersxCart[3]
is the input 3-element array storing the Cartesian coordinate x,y,z
at which ID_function()
will set the initial dataID_persist
is an input struct
storing any data needed by the initial data solver/interpolator. For example if ID_function()
performs interpolations on pseudospectral grids, ID_persist
might store the pseudospectral coefficients.initial_data
is the struct that ID_function()
outputs, storing the ADM variables and optionally the stress-energy tensor in either the spherical or Cartesian basis.ID_function()
is called from the main driver routine for reading and setting initial data:
initial_data_reader__convert_to_BSSN_from_ADM_spherical()
, andinitial_data_reader__convert_to_BSSN_from_ADM_Cartesian()
.These driver routines loop over all gridpoints, and at each gridpoint:
ID_function()
is first called, setting {γij,Kij,α,βi,Bi}, and optionally Tμν in the spherical or Cartesian basis.ADM_SphorCart_to_Cart()
then converts ADM variables from the spherical or Cartesian basis to the Cartesian basis.ADM_Cart_to_BSSN_Cart()
converts ADM variables in the Cartesian basis to BSSN variables in the Cartesian basis.initial_data_BSSN_basis_transform_Cartesian_to_rfm()
converts BSSN vectors/tensors from Cartesian to reference-metric basisThe above steps will set all the BSSN quantities needed for full evolutions, at all gridpoints, except the derived BSSN quantity λi. λi is set in terms of finite-difference derivatives of other BSSN quantities.
As such, after the other BSSN quantities are set at all gridpoints, the function initial_data_lambdaU_grid_interior()
is called to compute λi at all points in the grid interior.
It is up to the user to then call the inner boundary and extrapolation-outer boundary condition routine from the Curvilinear boundary conditions driver, so that all BSSN quantities are set at all points on the numerical grid, including the grid boundaries.
This notebook is organized as follows
Step 1: Initialize core Python/NRPy+ modules
Step 2: Overview of initial_data_reader__convert_ADM_..._to_BSSN()
: Driver routine that computes/reads ADM variables at all points on all grids, and converts them to BSSN quantities in chosen curvilinear reference metric
Step 2.a: Example ID_function()
generator, for "exact" initial data (i.e., initial data in which all quantities are specified with closed-form expressions)
Step 2.b: ADM_SphorCart_to_Cart()
: Convert ADM variables from the spherical or Cartesian basis to the Cartesian basis
Step 2.c ADM_Cart_to_BSSN_Cart()
: Convert ADM variables in the Cartesian basis to BSSN variables in the Cartesian basis
Step 2.d: BSSN_Cart_to_rescaled_BSSN_rfm()
: Convert Cartesian-basis BSSN vectors/tensors except λi, to the basis specified by reference_metric::CoordSystem
, then rescale these BSSN quantities.
Step 2.e: initial_data_lambdaU_grid_interior()
: Compute λi from finite-difference derivatives of rescaled metric quantities
Step 2.f: Putting it all together: Register initial_data_reader__convert_ADM_..._to_BSSN()
C function
Step 3: register_NRPy_basic_defines()
: Register ID_data_struct
within NRPy_basic_defines.h
Step 4:Code Validation against BSSN.ADM_Initial_Data_Reader__BSSN_Converter
NRPy+ module
Step 5: Output this notebook to LATEX-formatted PDF file
# Step 1: Initialize core Python/NRPy+ modules
from outputC import outputC,lhrh,add_to_Cfunction_dict, Cfunction # NRPy+: Core C code output module
from outputC import outC_NRPy_basic_defines_h_dict
import NRPy_param_funcs as par # NRPy+: Parameter interface
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 cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface
from pickling import pickle_NRPy_env # NRPy+: Pickle/unpickle NRPy+ environment, for parallel codegen
import os, sys # Standard Python modules for multiplatform OS-level functions
--------------------------------------------------------------------------- ModuleNotFoundError Traceback (most recent call last) Cell In[1], line 2 1 # Step 1: Initialize core Python/NRPy+ modules ----> 2 from outputC import outputC,lhrh,add_to_Cfunction_dict, Cfunction # NRPy+: Core C code output module 3 from outputC import outC_NRPy_basic_defines_h_dict 4 import NRPy_param_funcs as par # NRPy+: Parameter interface File ~/Documents/VS_Code/Etienne/nrpytutorial/outputC.py:18 12 __all__ = ['lhrh', 'outCparams', 'nrpyAbs', 'superfast_uniq', 'check_if_string__error_if_not', 13 'outputC', 'parse_outCparams_string', 14 'outC_NRPy_basic_defines_h_dict', 15 'outC_function_prototype_dict', 'outC_function_dict', 'Cfunction', 'add_to_Cfunction_dict', 'outCfunction'] 17 import loop as lp # NRPy+: C code loop interface ---> 18 import NRPy_param_funcs as par # NRPy+: parameter interface 19 from SIMD import expr_convert_to_SIMD_intrins # NRPy+: SymPy expression => SIMD intrinsics interface 20 from cse_helpers import cse_preprocess,cse_postprocess # NRPy+: CSE preprocessing and postprocessing File ~/Documents/VS_Code/Etienne/nrpytutorial/NRPy_param_funcs.py:10 1 # As documented in the NRPy+ tutorial module 2 # Tutorial-Coutput__Parameter_Interface.ipynb 3 # this core NRPy+ module is used for (...) 7 # Author: Zachariah B. Etienne 8 # zachetie **at** gmail **dot* com ---> 10 import sympy as sp # Import SymPy 11 import sys # Standard Python: OS-independent system functions 12 from collections import namedtuple # Standard Python: Enable namedtuple data type ModuleNotFoundError: No module named 'sympy'
initial_data_reader__convert_ADM_..._to_BSSN()
: Driver routine that computes/reads ADM variables at all points on all grids, and converts them to BSSN quantities in chosen curvilinear reference metric [Back to top]¶Initial data for constructing spacetimes in NRPy+ are provided in either the spherical or Cartesian basis, and the corresponding reader routines are initial_data_reader__convert_ADM_spherical_to_BSSN()
or initial_data_reader__convert_ADM_Cartesian_to_BSSN()
, respectively. These routines do the following:
ID_function()
: Read or compute ADM initial data at Cartesian point xCart[3]
=(x,y,z), in the spherical basis inside initial_data_reader__convert_to_BSSN_from_ADM_Spherical()
or Cartesian basis inside initial_data_reader__convert_to_BSSN_from_ADM_Cartesian()
.ADM_SphorCart_to_Cart()
: Convert ADM variables from the spherical or Cartesian basis to the Cartesian basis.ADM_Cart_to_BSSN_Cart()
: Convert ADM variables in the Cartesian basis to BSSN variables in the Cartesian basis.BSSN_Cart_to_rescaled_BSSN_rfm()
: Convert all BSSN vectors/tensors except λi in the Cartesian basis, to the basis specified by reference_metric::CoordSystem
. Rescale BSSN quantities.initial_data_lambdaU_grid_interior()
to compute λi in the reference_metric::CoordSystem
basis, in the grid interior only.Important Note: After initial_data_reader__convert_to_BSSN_rfm_from_ADM_sph_or_Cart()
is called, inner/outer boundary conditions must be applied to λi to ensure it is specified on the grid boundaries.
ID_function()
generator, for "exact" initial data (i.e., initial data in which all quantities are specified with closed-form expressions) [Back to top]¶This function converts closed-form SymPy expressions for ADM quantities α, βi, Bi, γij and Kij in the spherical or Cartesian basis at a given point (x,y,z) to optimized C code. It is meant to be passed as the ID_function()
argument into initial_data_reader__convert_ADM_..._to_BSSN()
. By not calling it ID_function()
, we can easily have multiple initial data functions within the same C executable.
Regarding the input quantities, a number of initial data sets are provided within NRPy+'s BSSN
module, including for example:
BSSN.UIUCBlackHole
for spinning single black hole initial data, in which the coordinate size of the BH does not shrink to zero in the limit of maximum spinBSSN.ShiftedKerrSchild
for spinning single black hole initial dataBSSN.StaticTrumpet
for static trumpet nonspinning black hole initial dataBSSN.BrillLindquist
for initial data of two nonspinning black holes at restWhen any of these initial data set functions is called, it will export the ADM quantities as global variables. For example
import BSSN.UIUCBlackHole as UIB
UIB.UIUCBlackHole()
pickled_NRPy_env = \
add_to_Cfunction_dict_exact_ADM_ID_function("UIUCBlackHole", "Spherical", UIB.alphaSph, UIB.betaSphU,
UIB.BSphU, UIB.betaSphU, UIB.gammaSphDD, UIB.KSphDD)
where the returned pickled NRPy+ environment pickled_NRPy_env
can be used to run this function in parallel with other functions (parallelized codegen).
def add_to_Cfunction_dict_exact_ADM_ID_function(IDtype, IDCoordSystem, alpha, betaU, BU, gammaDD, KDD):
includes = ["NRPy_basic_defines.h", "NRPy_function_prototypes.h"]
desc = IDtype + " initial data"
c_type = "void"
name = IDtype
params = "const paramstruct *params, const REAL xCart[3], const ID_persist_struct *restrict ID_persist, initial_data_struct *restrict initial_data"
desired_rfm_coord = par.parval_from_str("reference_metric::CoordSystem")
par.set_parval_from_str("reference_metric::CoordSystem", IDCoordSystem)
rfm.reference_metric()
body = ""
if IDCoordSystem == "Spherical":
body += r"""
REAL xx0,xx1,xx2 __attribute__((unused)); // xx2 might be unused in the case of axisymmetric initial data.
{
int unused_Cart_to_i0i1i2[3];
REAL xx[3];
Cart_to_xx_and_nearest_i0i1i2(params, xCart, xx, unused_Cart_to_i0i1i2);
xx0=xx[0]; xx1=xx[1]; xx2=xx[2];
}
const REAL r = xx0; // Some ID only specify r,th,ph.
const REAL th = xx1;
const REAL ph = xx2;
"""
elif IDCoordSystem == "Cartesian":
body += r""" const REAL Cartxyz0=xCart[0], Cartxyz1=xCart[1], Cartxyz2=xCart[2];
"""
else:
print("add_to_Cfunction_dict_exact_ADM_ID_function() Error: IDCoordSystem == " + IDCoordSystem + " unsupported")
sys.exit(1)
list_of_output_exprs = [alpha]
list_of_output_varnames = ["initial_data->alpha"]
for i in range(3):
list_of_output_exprs += [betaU[i]]
list_of_output_varnames += ["initial_data->betaSphorCartU" + str(i)]
list_of_output_exprs += [BU[i]]
list_of_output_varnames += ["initial_data->BSphorCartU" + str(i)]
for j in range(i, 3):
list_of_output_exprs += [gammaDD[i][j]]
list_of_output_varnames += ["initial_data->gammaSphorCartDD" + str(i) + str(j)]
list_of_output_exprs += [KDD[i][j]]
list_of_output_varnames += ["initial_data->KSphorCartDD" + str(i) + str(j)]
# Sort the outputs before calling outputC()
# https://stackoverflow.com/questions/9764298/is-it-possible-to-sort-two-listswhich-reference-each-other-in-the-exact-same-w
list_of_output_varnames, list_of_output_exprs = (list(t) for t in zip(*sorted(zip(list_of_output_varnames, list_of_output_exprs))))
body += outputC(list_of_output_exprs, list_of_output_varnames,
filename="returnstring", params="outCverbose=False,includebraces=False,preindent=1")
# Restore CoordSystem:
par.set_parval_from_str("reference_metric::CoordSystem", desired_rfm_coord)
rfm.reference_metric()
add_to_Cfunction_dict(
includes=includes,
desc=desc, c_type=c_type, name=name, params=params,
body=body,
enableCparameters=True)
return pickle_NRPy_env()
def Cfunction_ADM_SphorCart_to_Cart(input_Coord="Spherical", include_T4UU=False):
includes = []
desired_rfm_basis = par.parval_from_str("reference_metric::CoordSystem")
desc = "Convert ADM variables from the spherical or Cartesian basis to the Cartesian basis"
c_type = "static void"
name = "ADM_SphorCart_to_Cart"
params = """paramstruct *restrict params, const REAL xCart[3], const initial_data_struct *restrict initial_data,
ADM_Cart_basis_struct *restrict ADM_Cart_basis"""
body = r"""
// Unpack initial_data for ADM vectors/tensors
"""
for i in ["betaSphorCartU", "BSphorCartU"]:
for j in range(3):
varname = i + str(j)
body += " const REAL " + varname + " = initial_data->" + varname + ";\n"
body += "\n"
for i in ["gammaSphorCartDD", "KSphorCartDD"]:
for j in range(3):
for k in range(j, 3):
varname = i + str(j) + str(k)
body += " const REAL " + varname + " = initial_data->" + varname + ";\n"
body += "\n"
# Read stress-energy tensor in spherical or Cartesian basis if desired.
if include_T4UU:
for mu in range(4):
for nu in range(mu, 4):
varname = "T4SphorCartUU" + str(mu) + str(nu)
body += " const REAL " + varname + " = initial_data->" + varname + ";\n"
body += "\n"
# Set reference_metric to the input_Coord
par.set_parval_from_str("reference_metric::CoordSystem", input_Coord)
rfm.reference_metric()
body += r"""
// Perform the basis transform on ADM vectors/tensors from """ + input_Coord + """ to Cartesian:
// Set destination xx[3] based on desired xCart[3]
REAL xx0,xx1,xx2;
""" + outputC(rfm.Cart_to_xx[0:3], ["xx0", "xx1", "xx2"],
filename='returnstring', params="includebraces=True").\
replace("Cartx","xCart[0]").\
replace("Carty","xCart[1]").\
replace("Cartz","xCart[2]")
# Define the input variables:
gammaSphorCartDD = ixp.declarerank2("gammaSphorCartDD", "sym01")
KSphorCartDD = ixp.declarerank2("KSphorCartDD", "sym01")
betaSphorCartU = ixp.declarerank1("betaSphorCartU")
BSphorCartU = ixp.declarerank1("BSphorCartU")
T4SphorCartUU = ixp.declarerank2("T4SphorCartUU", "sym01", DIM=4)
# Compute Jacobian to convert to Cartesian coordinates
Jac_dUCart_dDrfmUD, Jac_dUrfm_dDCartUD = rfm.compute_Jacobian_and_inverseJacobian_tofrom_Cartesian()
gammaCartDD = rfm.basis_transform_tensorDD_from_rfmbasis_to_Cartesian(Jac_dUrfm_dDCartUD, gammaSphorCartDD)
KCartDD = rfm.basis_transform_tensorDD_from_rfmbasis_to_Cartesian(Jac_dUrfm_dDCartUD, KSphorCartDD)
betaCartU = rfm.basis_transform_vectorU_from_rfmbasis_to_Cartesian(Jac_dUCart_dDrfmUD, betaSphorCartU)
BCartU = rfm.basis_transform_vectorU_from_rfmbasis_to_Cartesian(Jac_dUCart_dDrfmUD, BSphorCartU)
T4CartUU = ixp.zerorank2(DIM=4)
if include_T4UU:
T4CartUU = rfm.basis_transform_4tensorUU_from_time_indep_rfmbasis_to_Cartesian(Jac_dUCart_dDrfmUD,
T4SphorCartUU)
alpha = sp.symbols("initial_data->alpha", real=True)
list_of_output_exprs = [alpha]
list_of_output_varnames = ["ADM_Cart_basis->alpha"]
for i in range(3):
list_of_output_exprs += [betaCartU[i]]
list_of_output_varnames += ["ADM_Cart_basis->betaU" + str(i)]
list_of_output_exprs += [BCartU[i]]
list_of_output_varnames += ["ADM_Cart_basis->BU" + str(i)]
for j in range(i, 3):
list_of_output_exprs += [gammaCartDD[i][j]]
list_of_output_varnames += ["ADM_Cart_basis->gammaDD" + str(i) + str(j)]
list_of_output_exprs += [KCartDD[i][j]]
list_of_output_varnames += ["ADM_Cart_basis->KDD" + str(i) + str(j)]
if include_T4UU:
for mu in range(4):
for nu in range(mu, 4):
list_of_output_exprs += [T4CartUU[mu][nu]]
list_of_output_varnames += ["ADM_Cart_basis->T4UU" + str(mu) + str(nu)]
# Sort the outputs before calling outputC()
# https://stackoverflow.com/questions/9764298/is-it-possible-to-sort-two-listswhich-reference-each-other-in-the-exact-same-w
list_of_output_varnames, list_of_output_exprs = (list(t) for t in zip(*sorted(zip(list_of_output_varnames, list_of_output_exprs))))
body += outputC(list_of_output_exprs, list_of_output_varnames,
filename="returnstring", params="outCverbose=False,includebraces=False,preindent=1")
# Restore reference metric globals to coordsystem on grid.
par.set_parval_from_str("reference_metric::CoordSystem", desired_rfm_basis)
rfm.reference_metric()
_func_prototype, func = Cfunction(
includes=includes,
desc=desc,
c_type=c_type, name=name, params=params,
body=body,
enableCparameters=False)
return func
def Cfunction_ADM_Cart_to_BSSN_Cart(include_T4UU=False):
includes = []
desired_rfm_basis = par.parval_from_str("reference_metric::CoordSystem")
desc = "Convert ADM variables in the Cartesian basis to BSSN variables in the Cartesian basis"
c_type = "static void"
name = "ADM_Cart_to_BSSN_Cart"
params = """paramstruct *restrict params, const REAL xCart[3], const ADM_Cart_basis_struct *restrict ADM_Cart_basis,
BSSN_Cart_basis_struct *restrict BSSN_Cart_basis"""
# Extract desired rfm basis from reference_metric::CoordSystem
desired_rfm_basis = par.parval_from_str("reference_metric::CoordSystem")
# Set CoordSystem to Cartesian
par.set_parval_from_str("reference_metric::CoordSystem", "Cartesian")
rfm.reference_metric()
gammaCartDD = ixp.declarerank2("ADM_Cart_basis->gammaDD", "sym01")
KCartDD = ixp.declarerank2("ADM_Cart_basis->KDD", "sym01")
import BSSN.BSSN_in_terms_of_ADM as BitoA
BitoA.trK_AbarDD_aDD(gammaCartDD, KCartDD)
BitoA.gammabarDD_hDD(gammaCartDD)
BitoA.cf_from_gammaDD(gammaCartDD)
body = r"""
// *In the Cartesian basis*, convert ADM quantities gammaDD & KDD
// into BSSN gammabarDD, AbarDD, cf, and trK.
BSSN_Cart_basis->alpha = ADM_Cart_basis->alpha;
BSSN_Cart_basis->betaU0 = ADM_Cart_basis->betaU0;
BSSN_Cart_basis->betaU1 = ADM_Cart_basis->betaU1;
BSSN_Cart_basis->betaU2 = ADM_Cart_basis->betaU2;
BSSN_Cart_basis->BU0 = ADM_Cart_basis->BU0;
BSSN_Cart_basis->BU1 = ADM_Cart_basis->BU1;
BSSN_Cart_basis->BU2 = ADM_Cart_basis->BU2;
"""
list_of_output_exprs = [BitoA.cf, BitoA.trK]
list_of_output_varnames = ["BSSN_Cart_basis->cf", "BSSN_Cart_basis->trK"]
for i in range(3):
for j in range(i, 3):
list_of_output_exprs += [BitoA.gammabarDD[i][j]]
list_of_output_varnames += ["BSSN_Cart_basis->gammabarDD" + str(i) + str(j)]
list_of_output_exprs += [BitoA.AbarDD[i][j]]
list_of_output_varnames += ["BSSN_Cart_basis->AbarDD" + str(i) + str(j)]
if include_T4UU:
T4CartUU = ixp.declarerank2("ADM_Cart_basis->T4UU", "sym01", DIM=4)
for mu in range(4):
for nu in range(mu, 4):
list_of_output_exprs += [T4CartUU[mu][nu]]
list_of_output_varnames += ["BSSN_Cart_basis->T4UU" + str(mu) + str(nu)]
# Sort the outputs before calling outputC()
# https://stackoverflow.com/questions/9764298/is-it-possible-to-sort-two-listswhich-reference-each-other-in-the-exact-same-w
list_of_output_varnames, list_of_output_exprs = (list(t) for t in zip(*sorted(zip(list_of_output_varnames, list_of_output_exprs))))
body += outputC(list_of_output_exprs, list_of_output_varnames,
filename="returnstring", params="outCverbose=False,includebraces=False,preindent=1")
# Restore reference metric globals to desired reference metric.
par.set_parval_from_str("reference_metric::CoordSystem", desired_rfm_basis)
rfm.reference_metric()
_func_prototype, func = Cfunction(
includes=includes,
desc=desc,
c_type=c_type, name=name, params=params,
body=body,
enableCparameters=False)
return func
BSSN_Cart_to_rescaled_BSSN_rfm()
: Convert Cartesian-basis BSSN vectors/tensors except λi, to the basis specified by reference_metric::CoordSystem
, then rescale these BSSN quantities. [Back to top]¶By the time this function is called, all BSSN tensors and vectors are in the Cartesian coordinate basis xiCart=(x,y,z), but we need them in the curvilinear coordinate basis xirfm=(xx0,xx1,xx2)
set by the "reference_metric::CoordSystem"
variable. Empirically speaking, it is far easier to write (x(xx0,xx1,xx2),y(xx0,xx1,xx2),z(xx0,xx1,xx2))
than the inverse, so we will compute the Jacobian matrix
via exact differentiation (courtesy SymPy), and the inverse Jacobian Jac_dUrfm_dDCartUD[i][j]=∂xirfm∂xjCart,
using NRPy+'s generic_matrix_inverter3x3()
function. In terms of these, the transformation of BSSN tensors from Spherical to "reference_metric::CoordSystem"
coordinates may be written:
The above basis transforms are included in functions basis_transform_tensorDD_from_Cartesian_to_rfmbasis()
and basis_transform_vectorU_from_Cartesian_to_rfmbasis()
in reference_metric.py
, and we use them below.
After the basis transform has been performed, we perform tensor rescalings to compute the evolved variables hij, aij, veti, and beti:
ˉγij is rescaled hij according to the prescription described in the the covariant BSSN formulation tutorial (also Ruchlin et al.):
hij=(ˉγij−ˆγij)/ReDD[i][j].Further ˉAij, βi, Bi are rescaled to aij, veti, and beti respectively via the standard formulas (found in the covariant BSSN formulation tutorial; also Ruchlin et al.):
aij=ˉAij/ReDD[i][j]veti=βi/ReU[i]beti=Bi/ReU[i].Finally, functions depending on the stress-energy tensor Tμν all assume it to be unrescaled. Thus we do not rescale Tμν.
def Cfunction_BSSN_Cart_to_rescaled_BSSN_rfm(rel_path_to_Cparams=os.path.join("."), include_T4UU=False):
includes = []
desc = r"""Convert Cartesian-basis BSSN vectors/tensors *except* lambda^i,
to the basis specified by `reference_metric::CoordSystem`, then rescale these BSSN quantities"""
c_type = "static void"
name = "BSSN_Cart_to_rescaled_BSSN_rfm"
params = """paramstruct *restrict params, const REAL xCart[3],
const BSSN_Cart_basis_struct *restrict BSSN_Cart_basis,
rescaled_BSSN_rfm_basis_struct *restrict rescaled_BSSN_rfm_basis"""
body = r"""
REAL xx0,xx1,xx2 __attribute__((unused)); // xx2 might be unused in the case of axisymmetric initial data.
{
int unused_Cart_to_i0i1i2[3];
REAL xx[3];
Cart_to_xx_and_nearest_i0i1i2(params, xCart, xx, unused_Cart_to_i0i1i2);
xx0=xx[0]; xx1=xx[1]; xx2=xx[2];
}
"""
# Define the input variables:
gammabarCartDD = ixp.declarerank2("BSSN_Cart_basis->gammabarDD", "sym01")
AbarCartDD = ixp.declarerank2("BSSN_Cart_basis->AbarDD", "sym01")
betaCartU = ixp.declarerank1("BSSN_Cart_basis->betaU")
BCartU = ixp.declarerank1("BSSN_Cart_basis->BU")
# Compute Jacobian to convert to Cartesian coordinates
Jac_dUCart_dDrfmUD, Jac_dUrfm_dDCartUD = rfm.compute_Jacobian_and_inverseJacobian_tofrom_Cartesian()
gammabarDD = rfm.basis_transform_tensorDD_from_Cartesian_to_rfmbasis(Jac_dUCart_dDrfmUD, gammabarCartDD)
AbarDD = rfm.basis_transform_tensorDD_from_Cartesian_to_rfmbasis(Jac_dUCart_dDrfmUD, AbarCartDD)
betaU = rfm.basis_transform_vectorU_from_Cartesian_to_rfmbasis(Jac_dUrfm_dDCartUD, betaCartU)
BU = rfm.basis_transform_vectorU_from_Cartesian_to_rfmbasis(Jac_dUrfm_dDCartUD, BCartU)
# Next rescale:
vetU = ixp.zerorank1()
betU = ixp.zerorank1()
hDD = ixp.zerorank2()
aDD = ixp.zerorank2()
for i in range(3):
vetU[i] = betaU[i] / rfm.ReU[i]
betU[i] = BU[i] / rfm.ReU[i]
for j in range(3):
hDD[i][j] = (gammabarDD[i][j] - rfm.ghatDD[i][j]) / rfm.ReDD[i][j]
aDD[i][j] = AbarDD[i][j] / rfm.ReDD[i][j]
if include_T4UU:
T4CartUU = ixp.declarerank2("BSSN_Cart_basis->T4UU", "sym01", DIM=4)
T4UU = rfm.basis_transform_4tensorUU_from_Cartesian_to_time_indep_rfmbasis(Jac_dUrfm_dDCartUD, T4CartUU)
alpha, cf, trK = sp.symbols('BSSN_Cart_basis->alpha BSSN_Cart_basis->cf BSSN_Cart_basis->trK', real=True)
list_of_output_exprs = [alpha, cf, trK]
list_of_output_varnames = ["rescaled_BSSN_rfm_basis->alpha",
"rescaled_BSSN_rfm_basis->cf",
"rescaled_BSSN_rfm_basis->trK"]
for i in range(3):
list_of_output_exprs += [vetU[i]]
list_of_output_varnames += ["rescaled_BSSN_rfm_basis->vetU" + str(i)]
list_of_output_exprs += [betU[i]]
list_of_output_varnames += ["rescaled_BSSN_rfm_basis->betU" + str(i)]
for j in range(i, 3):
list_of_output_exprs += [hDD[i][j]]
list_of_output_varnames += ["rescaled_BSSN_rfm_basis->hDD" + str(i) + str(j)]
list_of_output_exprs += [aDD[i][j]]
list_of_output_varnames += ["rescaled_BSSN_rfm_basis->aDD" + str(i) + str(j)]
if include_T4UU:
for mu in range(4):
for nu in range(mu, 4):
# T4UU IS ASSUMED NOT RESCALED; RESCALINGS ARE HANDLED WITHIN BSSN RHSs, etc.
list_of_output_exprs += [T4UU[mu][nu]]
list_of_output_varnames += ["rescaled_BSSN_rfm_basis->T4UU" + str(mu) + str(nu)]
# Sort the outputs before calling outputC()
# https://stackoverflow.com/questions/9764298/is-it-possible-to-sort-two-listswhich-reference-each-other-in-the-exact-same-w
list_of_output_varnames, list_of_output_exprs = (list(t) for t in zip(*sorted(zip(list_of_output_varnames, list_of_output_exprs))))
body += outputC(list_of_output_exprs, list_of_output_varnames,
filename="returnstring", params="outCverbose=False,includebraces=False,preindent=1")
_func_prototype, func = Cfunction(
includes=includes,
desc=desc,
c_type=c_type, name=name, params=params,
body=body,
enableCparameters=True, rel_path_to_Cparams=rel_path_to_Cparams)
return func
initial_data_lambdaU_grid_interior()
: Compute λi from finite-difference derivatives of rescaled metric quantities [Back to top]¶We compute ˉΛi (Eqs. 4 and 5 of Baumgarte et al.), from finite-difference derivatives of rescaled metric quantities hij:
ˉΛi=ˉγjk(ˉΓijk−ˆΓijk).The reference_metric.py module provides us with analytic expressions for ˆΓijk, so here we need only compute finite-difference expressions for ˉΓijk, based on the values for hij provided in the initial data. Once ˉΛi has been computed, we apply the usual rescaling procedure: λi=ˉΛi/ReU[i],
# initial_data_lambdaU_grid_interior() computes lambdaU from
# finite-difference derivatives of rescaled metric quantities
def Cfunction_initial_data_lambdaU_grid_interior():
includes = []
c_type = "static void"
output_Coord = par.parval_from_str("reference_metric::CoordSystem")
desc = "Compute lambdaU in " + output_Coord + " coordinates"
name = "initial_data_lambdaU_grid_interior"
params = """const paramstruct *restrict params, REAL *restrict xx[3], REAL *restrict in_gfs"""
# Step 7: Compute $\bar{\Lambda}^i$ from finite-difference derivatives of rescaled metric quantities
# 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(3):
for j in range(3):
for k in range(3):
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(3):
lambdaU[i] = LambdabarU[i] / rfm.ReU[i]
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])]
body = fin.FD_outputC("returnstring", lambdaU_expressions,
params="outCverbose=False,includebraces=False,preindent=0")
_func_prototype, func = Cfunction(
includes=includes,
desc=desc,
c_type=c_type, name=name, params=params,
body=body,
loopopts="InteriorPoints,Read_xxs",
enableCparameters=True)
return func
initial_data_reader__convert_ADM_..._to_BSSN()
C function [Back to top]¶As discussed above, the function initial_data_reader__convert_ADM_..._to_BSSN()
is our core driver function, generally called directly by main()
to read/construct initial data in terms of ADM quantities in the spherical/Cartesian basis, and convert these quantities to rescaled BSSN variables. As such, it incorporates all the above functions just described.
def add_to_Cfunction_dict_initial_data_reader__convert_ADM_Sph_or_Cart_to_BSSN(addl_includes=None,
rel_path_to_Cparams=os.path.join("."),
input_Coord="Spherical",
include_T4UU=False):
includes = ["NRPy_basic_defines.h", "NRPy_function_prototypes.h"]
if par.parval_from_str("finite_difference::enable_FD_functions"):
includes += ["finite_difference_functions.h"]
if addl_includes is not None:
if not isinstance(addl_includes, list):
print("Error: addl_includes must be a list.")
sys.exit(1)
includes += addl_includes
def T4UU_prettyprint():
return r"""
REAL T4UU00,T4UU01,T4UU02,T4UU03;
REAL T4UU11,T4UU12,T4UU13;
REAL T4UU22,T4UU23;
REAL T4UU33;
"""
prefunc = """
// ADM variables in the Cartesian basis:
typedef struct __ADM_Cart_basis_struct__ {
REAL alpha, betaU0,betaU1,betaU2, BU0,BU1,BU2;
REAL gammaDD00,gammaDD01,gammaDD02,gammaDD11,gammaDD12,gammaDD22;
REAL KDD00,KDD01,KDD02,KDD11,KDD12,KDD22;
"""
if include_T4UU:
prefunc += T4UU_prettyprint()
prefunc += "} ADM_Cart_basis_struct;\n"
##############
prefunc += """
// BSSN variables in the Cartesian basis:
typedef struct __BSSN_Cart_basis_struct__ {
REAL alpha, betaU0,betaU1,betaU2, BU0,BU1,BU2;
REAL cf, trK;
REAL gammabarDD00,gammabarDD01,gammabarDD02,gammabarDD11,gammabarDD12,gammabarDD22;
REAL AbarDD00,AbarDD01,AbarDD02,AbarDD11,AbarDD12,AbarDD22;
"""
if include_T4UU:
prefunc += T4UU_prettyprint()
prefunc += "} BSSN_Cart_basis_struct;\n"
##############
prefunc += """
// Rescaled BSSN variables in the rfm basis:
typedef struct __rescaled_BSSN_rfm_basis_struct__ {
REAL alpha, vetU0,vetU1,vetU2, betU0,betU1,betU2;
REAL cf, trK;
REAL hDD00,hDD01,hDD02,hDD11,hDD12,hDD22;
REAL aDD00,aDD01,aDD02,aDD11,aDD12,aDD22;
"""
if include_T4UU:
prefunc += T4UU_prettyprint()
prefunc += "} rescaled_BSSN_rfm_basis_struct;\n"
##############
##############
prefunc += Cfunction_ADM_SphorCart_to_Cart(input_Coord=input_Coord, include_T4UU=include_T4UU)
prefunc += Cfunction_ADM_Cart_to_BSSN_Cart( include_T4UU=include_T4UU)
prefunc += Cfunction_BSSN_Cart_to_rescaled_BSSN_rfm(rel_path_to_Cparams=rel_path_to_Cparams,
include_T4UU=include_T4UU)
prefunc += Cfunction_initial_data_lambdaU_grid_interior()
output_Coord = par.parval_from_str("reference_metric::CoordSystem")
desc = "Read in ADM initial data in the " + input_Coord + " basis, and convert to BSSN data in " + output_Coord + " coordinates"
c_type = "void"
name = "initial_data_reader__convert_ADM_" + input_Coord + "_to_BSSN"
params = """griddata_struct *restrict griddata, ID_persist_struct *restrict ID_persist,
void ID_function(const paramstruct *params, const REAL xCart[3],
const ID_persist_struct *restrict ID_persist,
initial_data_struct *restrict initial_data)"""
body = r"""
const int Nxx_plus_2NGHOSTS0 = griddata->params.Nxx_plus_2NGHOSTS0;
const int Nxx_plus_2NGHOSTS1 = griddata->params.Nxx_plus_2NGHOSTS1;
const int Nxx_plus_2NGHOSTS2 = griddata->params.Nxx_plus_2NGHOSTS2;
LOOP_OMP("omp parallel for", i0,0,Nxx_plus_2NGHOSTS0, i1,0,Nxx_plus_2NGHOSTS1, i2,0,Nxx_plus_2NGHOSTS2) {
// xCart is the global Cartesian coordinate, which accounts for any grid offsets from the origin.
REAL xCart[3]; xx_to_Cart(&griddata->params, griddata->xx, i0,i1,i2, xCart);
// Read or compute initial data at destination point xCart
initial_data_struct initial_data;
ID_function(&griddata->params, xCart, ID_persist, &initial_data);
ADM_Cart_basis_struct ADM_Cart_basis;
ADM_SphorCart_to_Cart(&griddata->params, xCart, &initial_data, &ADM_Cart_basis);
BSSN_Cart_basis_struct BSSN_Cart_basis;
ADM_Cart_to_BSSN_Cart(&griddata->params, xCart, &ADM_Cart_basis, &BSSN_Cart_basis);
rescaled_BSSN_rfm_basis_struct rescaled_BSSN_rfm_basis;
BSSN_Cart_to_rescaled_BSSN_rfm(&griddata->params, xCart, &BSSN_Cart_basis, &rescaled_BSSN_rfm_basis);
const int idx3 = IDX3S(i0,i1,i2);
// Output data to gridfunctions
"""
gf_list = ["alpha", "trK", "cf"]
for i in range(3):
gf_list += ["vetU"+str(i), "betU"+str(i)]
for j in range(i, 3):
gf_list += ["hDD"+str(i)+str(j), "aDD"+str(i)+str(j)]
for gf in sorted(gf_list):
body += " griddata->gridfuncs.y_n_gfs[IDX4ptS("+gf.upper()+"GF, idx3)] = rescaled_BSSN_rfm_basis."+gf+";\n"
if include_T4UU:
for mu in range(4):
for nu in range(mu, 4):
gf = "T4UU" + str(mu) + str(nu)
body += " griddata->gridfuncs.auxevol_gfs[IDX4ptS("+gf.upper()+"GF, idx3)] = rescaled_BSSN_rfm_basis."+gf+";\n"
body += """
} // END LOOP over all gridpoints on given grid
initial_data_lambdaU_grid_interior(&griddata->params, griddata->xx, griddata->gridfuncs.y_n_gfs);
"""
add_to_Cfunction_dict(
includes=includes,
prefunc=prefunc,
desc=desc,
c_type=c_type, name=name, params=params,
body=body,
enableCparameters=False)
return pickle_NRPy_env()
register_NRPy_basic_defines()
: Register ID_data_struct
within NRPy_basic_defines.h
[Back to top]¶Other than its core use as a means to store ADM input quantities, ID_data_struct
is designed to be extensible. For example, it may be used to store e.g., pseudospectral coefficients for TwoPunctures, initial data gridfunctions from NRPyElliptic, pointers to TOV 1D data from the TOV solver, etc.
# Other than its core use as a means to store ADM input quantities,
# `initial_data_struct` is designed to be extensible. For example, it may be
# used to store e.g., pseudospectral coefficients for TwoPunctures,
# initial data gridfunctions from NRPyElliptic, pointers to TOV 1D data
# from the TOV solver, etc.
def register_NRPy_basic_defines(ID_persist_struct_contents_str="", include_T4UU=False):
Nbd = r"""typedef struct __initial_data_struct__ {
REAL alpha;
REAL betaSphorCartU0, betaSphorCartU1, betaSphorCartU2;
REAL BSphorCartU0, BSphorCartU1, BSphorCartU2;
REAL gammaSphorCartDD00, gammaSphorCartDD01, gammaSphorCartDD02;
REAL gammaSphorCartDD11, gammaSphorCartDD12, gammaSphorCartDD22;
REAL KSphorCartDD00, KSphorCartDD01, KSphorCartDD02;
REAL KSphorCartDD11, KSphorCartDD12, KSphorCartDD22;
"""
if include_T4UU:
Nbd += """
REAL T4SphorCartUU00,T4SphorCartUU01,T4SphorCartUU02,T4SphorCartUU03;
REAL T4SphorCartUU11,T4SphorCartUU12,T4SphorCartUU13;
REAL T4SphorCartUU22,T4SphorCartUU23;
REAL T4SphorCartUU33;
"""
Nbd += """
} initial_data_struct;
"""
Nbd += "typedef struct __ID_persist_struct__ {\n"
Nbd += ID_persist_struct_contents_str + "\n"
Nbd += "} ID_persist_struct;\n"
outC_NRPy_basic_defines_h_dict["BSSN_initial_data"] = Nbd
BSSN.ADM_Initial_Data_Reader__BSSN_Converter
NRPy+ module [Back to top]¶Here, as a code validation check, we verify that functions within
are identical. This notebook serves as documentation for the Python module, and the Python module is meant to be called from external routines. Thus this validation test ensures the documentation remains consistent with the Python module.
import BSSN.ADM_Initial_Data_Reader__BSSN_Converter as AID
funclist = [(add_to_Cfunction_dict_exact_ADM_ID_function, AID.add_to_Cfunction_dict_exact_ADM_ID_function),
(Cfunction_ADM_SphorCart_to_Cart, AID.Cfunction_ADM_SphorCart_to_Cart),
(Cfunction_ADM_Cart_to_BSSN_Cart, AID.Cfunction_ADM_Cart_to_BSSN_Cart),
(Cfunction_BSSN_Cart_to_rescaled_BSSN_rfm, AID.Cfunction_BSSN_Cart_to_rescaled_BSSN_rfm),
(Cfunction_initial_data_lambdaU_grid_interior, AID.Cfunction_initial_data_lambdaU_grid_interior),
(add_to_Cfunction_dict_initial_data_reader__convert_ADM_Sph_or_Cart_to_BSSN, AID.add_to_Cfunction_dict_initial_data_reader__convert_ADM_Sph_or_Cart_to_BSSN),
(register_NRPy_basic_defines, AID.register_NRPy_basic_defines)
]
import inspect
for func in funclist:
# https://stackoverflow.com/questions/20059011/check-if-two-python-functions-are-equal
if inspect.getsource(func[0]) != inspect.getsource(func[1]):
with open(func[0].__name__ + "_Jupyter_notebook_version.c", "w") as file:
file.write(inspect.getsource(func[0]))
with open(func[1].__name__ + "_Python_module_version.c", "w") as file:
file.write(inspect.getsource(func[1]))
print("ERROR: function " + func[0].__name__ + " is not the same as the Ccodegen_library version!")
print(" For more info, try this:")
print("diff " + func[0].__name__ + "_Jupyter_notebook_version.c" + " " + func[1].__name__ + "_Python_module_version.c")
sys.exit(1)
print("PASS! ALL FUNCTIONS ARE IDENTICAL")
PASS! ALL FUNCTIONS ARE IDENTICAL
Once the following code finishes running, the generated PDF may be found at the following location within the directory you have the NRPy+ tutorial saved: Tutorial-ADM_Initial_Data_Reader__BSSN_Converter.pdf
import cmdline_helper as cmd # NRPy+: Multi-platform Python command-line interface
cmd.output_Jupyter_notebook_to_LaTeXed_PDF("Tutorial-ADM_Initial_Data_Reader__BSSN_Converter")
Created Tutorial-ADM_Initial_Data_Reader__BSSN_Converter.tex, and compiled LaTeX file to PDF file Tutorial- ADM_Initial_Data_Reader__BSSN_Converter.pdf