# 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()
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)

    # Let's choose alpha = 1/psi**2 (psi = BSSN conformal factor) for these initial data,
    # where psi = exp(phi); chi = 1/psi**4; W = 1/psi**2
    if par.parval_from_str("EvolvedConformalFactor_cf") == "phi":
        alpha = sp.exp(-2 * cf)
    elif par.parval_from_str("EvolvedConformalFactor_cf") == "chi":
        alpha = sp.sqrt(cf)
    elif par.parval_from_str("EvolvedConformalFactor_cf") == "W":
        alpha = cf
    else:
        print("Error EvolvedConformalFactor_cf type = \"" +
              par.parval_from_str("EvolvedConformalFactor_cf") + "\" unknown.")
        sys.exit(1)

    import BSSN.BSSN_ID_function_string as bIDf
    # Generates initial_data() C function & stores to outC_function_dict["initial_data"]
    bIDf.BSSN_ID_function_string(cf, hDD, lambdaU, aDD, trK, alpha, vetU, betU)
Exemple #3
0
from outputC import nrpyAbs  # NRPy+: Core C code output module
import sympy as sp  # SymPy: The Python computer algebra package upon which NRPy+ depends
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 reference_metric as rfm  # NRPy+: Reference metric support
import GRHD.equations as GRHD  # NRPy+: Generate general relativistic hydrodynamics equations
import GRFFE.equations as GRFFE  # NRPy+: Generate general relativisitic force-free electrodynamics equations

thismodule = __name__

# There are several C parameters that we will need in this module:
M_PI = par.Cparameters("#define", thismodule, ["M_PI"], "")
GAMMA_SPEED_LIMIT = par.Cparameters("REAL", thismodule, "GAMMA_SPEED_LIMIT",
                                    10.0)  # Default value based on
# IllinoisGRMHD.
# GiRaFFE default = 2000.0

# There are three fixes in this module; we might not always want to do all (or even any!) of them.
# So, we initialize some NRPy+ parameters to control this behavior
par.initialize_param(
    par.glb_param(type="bool",
                  module=thismodule,
                  parname="enforce_orthogonality_StildeD_BtildeU",
                  defaultval=True))
par.initialize_param(
    par.glb_param(type="bool",
                  module=thismodule,
                  parname="enforce_speed_limit_StildeD",
                  defaultval=True))
par.initialize_param(
Exemple #4
0
# $$\label{preliminaries}$$
#
# \[Back to [top](#top)\]
#
# Here, we will import the NRPy+ core modules and set the reference metric to Cartesian, set commonly used NRPy+ parameters, and set C parameters that will be set from outside the code eventually generated from these expressions. We will also set up a parameter to determine what initial data is set up, although it won't do much yet.

# Step 0: Import the NRPy+ core modules and set the reference metric to Cartesian
import NRPy_param_funcs as par
import indexedexp as ixp
import grid as gri
import finite_difference as fin
from outputC import *
import loop

import reference_metric as rfm
par.set_parval_from_str("reference_metric::CoordSystem", "Cartesian")
rfm.reference_metric()

# Step 1a: Set commonly used parameters.
thismodule = "GiRaFFEfood_HO_Aligned_Rotator"
# Set the spatial dimension parameter to 3.
par.set_parval_from_str("grid::DIM", 3)
DIM = par.parval_from_str("grid::DIM")

B_p_aligned_rotator, R_NS_aligned_rotator = par.Cparameters(
    "REAL", thismodule, ["B_p_aligned_rotator", "R_NS_aligned_rotator"]
)  # A constant defining the intensity of the magnetic field and the Neutron star Radius

# <a id='step2'></a>
#
# ### Step 2: Set the vectors A in Spherical coordinates
def FishboneMoncriefID(CoordSystem="Cartesian"):
    par.set_parval_from_str("reference_metric::CoordSystem", CoordSystem)
    rfm.reference_metric()
    #Set the spatial dimension parameter to 3.
    par.set_parval_from_str("grid::DIM", 3)
    DIM = par.parval_from_str("grid::DIM")

    gPhys4UU = ixp.register_gridfunctions_for_single_rank2("AUX",
                                                           "gPhys4UU",
                                                           "sym01",
                                                           DIM=4)
    KDD = ixp.register_gridfunctions_for_single_rank2("EVOL", "KDD", "sym01")

    # Variables needed for initial data given in spherical basis
    global r_in, r_at_max_density, a, M, r, th, ph
    r, th, ph = gri.register_gridfunctions("AUX", ["r", "th", "ph"])

    r_in, r_at_max_density, a, M = par.Cparameters(
        "REAL", thismodule, ["r_in", "r_at_max_density", "a", "M"],
        [6.0, 12.0, 0.9375, 1.0])

    kappa, gamma = par.Cparameters("REAL", thismodule, ["kappa", "gamma"],
                                   [1.0e-3, 4.0 / 3.0])

    # The return value from gri.register_gridfunctions("AUX","LorentzFactor") is unused, so we ignore it here:
    gri.register_gridfunctions("AUX", "LorentzFactor")

    def calculate_l_at_r(r):
        l = sp.sqrt(M / r**3) * (r**4 + r**2 * a**2 - 2 * M * r * a**2 -
                                 a * sp.sqrt(M * r) * (r**2 - a**2))
        l /= r**2 - 3 * M * r + 2 * a * sp.sqrt(M * r)
        return l

    # First compute angular momentum at r_at_max_density, TAKING POSITIVE ROOT. This way disk is co-rotating with black hole
    # Eq 3.8:
    l = calculate_l_at_r(r_at_max_density)

    # Eq 3.6:
    # First compute the radially-independent part of the log of the enthalpy, ln_h_const
    Delta = r**2 - 2 * M * r + a**2
    Sigma = r**2 + a**2 * sp.cos(th)**2
    A = (r**2 + a**2)**2 - Delta * a**2 * sp.sin(th)**2

    # Next compute the radially-dependent part of log(enthalpy), ln_h
    tmp3 = sp.sqrt(1 + 4 * l**2 * Sigma**2 * Delta / (A * sp.sin(th))**2)
    # Term 1 of Eq 3.6
    ln_h = sp.Rational(1, 2) * sp.log((1 + tmp3) / (Sigma * Delta / A))
    # Term 2 of Eq 3.6
    ln_h -= sp.Rational(1, 2) * tmp3
    # Term 3 of Eq 3.6
    ln_h -= 2 * a * M * r * l / A

    # Next compute the radially-INdependent part of log(enthalpy), ln_h
    # Note that there is some typo in the expression for these terms given in Eq 3.6, so we opt to just evaluate
    #   negative of the first three terms at r=r_in and th=pi/2 (the integration constant), as described in
    #   the text below Eq. 3.6, basically just copying the above lines of code.
    # Delin = Delta_in ; Sigin = Sigma_in ; Ain = A_in .
    Delin = r_in**2 - 2 * M * r_in + a**2
    Sigin = r_in**2 + a**2 * sp.cos(sp.pi / 2)**2
    Ain = (r_in**2 + a**2)**2 - Delin * a**2 * sp.sin(sp.pi / 2)**2

    tmp3in = sp.sqrt(1 + 4 * l**2 * Sigin**2 * Delin /
                     (Ain * sp.sin(sp.pi / 2))**2)
    # Term 4 of Eq 3.6
    mln_h_in = -sp.Rational(1, 2) * sp.log(
        (1 + tmp3in) / (Sigin * Delin / Ain))
    # Term 5 of Eq 3.6
    mln_h_in += sp.Rational(1, 2) * tmp3in
    # Term 6 of Eq 3.6
    mln_h_in += 2 * a * M * r_in * l / Ain

    global hm1
    hm1 = sp.exp(ln_h + mln_h_in) - 1

    global rho_initial
    rho_initial, Pressure_initial = gri.register_gridfunctions(
        "AUX", ["rho_initial", "Pressure_initial"])

    # Python 3.4 + sympy 1.0.0 has a serious problem taking the power here, hangs forever.
    # so instead we use the identity x^{1/y} = exp( [1/y] * log(x) )
    # Original expression (works with Python 2.7 + sympy 0.7.4.1):
    # rho_initial = ( hm1*(gamma-1)/(kappa*gamma) )**(1/(gamma - 1))
    # New expression (workaround):
    rho_initial = sp.exp(
        (1 / (gamma - 1)) * sp.log(hm1 * (gamma - 1) / (kappa * gamma)))
    Pressure_initial = kappa * rho_initial**gamma
    # Eq 3.3: First compute exp(-2 chi), assuming Boyer-Lindquist coordinates
    #    Eq 2.16: chi = psi - nu, so
    #    Eq 3.5 -> exp(-2 chi) = exp(-2 (psi - nu)) = exp(2 nu)/exp(2 psi)
    exp2nu = Sigma * Delta / A
    exp2psi = A * sp.sin(th)**2 / Sigma
    expm2chi = exp2nu / exp2psi

    # Eq 3.3: Next compute u_(phi).
    u_pphip = sp.sqrt((-1 + sp.sqrt(1 + 4 * l**2 * expm2chi)) / 2)
    # Eq 2.13: Compute u_(t)
    u_ptp = -sp.sqrt(1 + u_pphip**2)

    # Next compute spatial components of 4-velocity in Boyer-Lindquist coordinates:
    global uBL4D
    uBL4D = ixp.zerorank1(DIM=4)  # Components 1 and 2: u_r = u_theta = 0
    # Eq 2.12 (typo): u_(phi) = e^(-psi) u_phi -> u_phi = e^(psi) u_(phi)
    uBL4D[3] = sp.sqrt(exp2psi) * u_pphip

    # Assumes Boyer-Lindquist coordinates:
    omega = 2 * a * M * r / A
    # Eq 2.13: u_(t) = 1/sqrt(exp2nu) * ( u_t + omega*u_phi )
    #     -->  u_t = u_(t) * sqrt(exp2nu) - omega*u_phi
    #     -->  u_t = u_ptp * sqrt(exp2nu) - omega*uBL4D[3]
    uBL4D[0] = u_ptp * sp.sqrt(exp2nu) - omega * uBL4D[3]
    # Eq. 3.5:
    # w = 2*a*M*r/A;
    # Eqs. 3.5 & 2.1:
    # gtt = -Sig*Del/A + w^2*Sin[th]^2*A/Sig;
    # gtp = w*Sin[th]^2*A/Sig;
    # gpp = Sin[th]^2*A/Sig;
    # FullSimplify[Inverse[{{gtt,gtp},{gtp,gpp}}]]
    gPhys4BLUU = ixp.zerorank2(DIM=4)
    gPhys4BLUU[0][0] = -A / (Delta * Sigma)
    # DO NOT NEED TO SET gPhys4BLUU[1][1] or gPhys4BLUU[2][2]!
    gPhys4BLUU[0][3] = gPhys4BLUU[3][0] = -2 * a * M * r / (Delta * Sigma)
    gPhys4BLUU[3][3] = -4 * a**2 * M**2 * r**2 / (
        Delta * A * Sigma) + Sigma**2 / (A * Sigma * sp.sin(th)**2)

    global uBL4U
    uBL4U = ixp.zerorank1(DIM=4)
    for i in range(4):
        for j in range(4):
            uBL4U[i] += gPhys4BLUU[i][j] * uBL4D[j]

    # https://github.com/atchekho/harmpi/blob/master/init.c
    # Next transform Boyer-Lindquist velocity to Kerr-Schild basis:
    transformBLtoKS = ixp.zerorank2(DIM=4)
    for i in range(4):
        transformBLtoKS[i][i] = 1
    transformBLtoKS[0][1] = 2 * r / (r**2 - 2 * r + a * a)
    transformBLtoKS[3][1] = a / (r**2 - 2 * r + a * a)
    global uKS4U
    uKS4U = ixp.zerorank1(DIM=4)
    for i in range(4):
        for j in range(4):
            uKS4U[i] += transformBLtoKS[i][j] * uBL4U[j]

    # Adopt the Kerr-Schild metric for Fishbone-Moncrief disks
    # http://gravity.psu.edu/numrel/jclub/jc/Cook___LivRev_2000-5.pdf
    # Alternatively, Appendix of https://arxiv.org/pdf/1704.00599.pdf
    rhoKS2 = r**2 + a**2 * sp.cos(
        th
    )**2  # Eq 79 of Cook's Living Review article (note that definition above Eq A.1 in
    #                                     https://arxiv.org/pdf/1704.00599.pdf should be rho^2, not rho)
    alphaKS = 1 / sp.sqrt(1 + 2 * M * r / rhoKS2)
    betaKSU = ixp.zerorank1()
    betaKSU[0] = alphaKS**2 * 2 * M * r / rhoKS2
    gammaKSDD = ixp.zerorank2()
    gammaKSDD[0][0] = 1 + 2 * M * r / rhoKS2
    gammaKSDD[0][2] = gammaKSDD[2][0] = -(1 + 2 * M * r / rhoKS2) * a * sp.sin(
        th)**2
    gammaKSDD[1][1] = rhoKS2
    gammaKSDD[2][2] = (r**2 + a**2 + 2 * M * r / rhoKS2 * a**2 *
                       sp.sin(th)**2) * sp.sin(th)**2

    AA = a**2 * sp.cos(2 * th) + a**2 + 2 * r**2
    BB = AA + 4 * M * r
    DD = sp.sqrt(2 * M * r / (a**2 * sp.cos(th)**2 + r**2) + 1)
    KDD[0][0] = DD * (AA + 2 * M * r) / (AA**2 * BB) * (
        4 * M * (a**2 * sp.cos(2 * th) + a**2 - 2 * r**2))
    KDD[0][1] = KDD[1][0] = DD / (
        AA * BB) * 8 * a**2 * M * r * sp.sin(th) * sp.cos(th)
    KDD[0][2] = KDD[2][0] = DD / AA**2 * (
        -2 * a * M * sp.sin(th)**2 * (a**2 * sp.cos(2 * th) + a**2 - 2 * r**2))
    KDD[1][1] = DD / BB * 4 * M * r**2
    KDD[1][2] = KDD[2][1] = DD / (AA * BB) * (-8 * a**3 * M * r *
                                              sp.sin(th)**3 * sp.cos(th))
    KDD[2][2] = DD/(AA**2*BB) * \
                (2*M*r*sp.sin(th)**2 * (a**4*(r-M)*sp.cos(4*th) + a**4*(M+3*r) +
                 4*a**2*r**2*(2*r-M) + 4*a**2*r*sp.cos(2*th)*(a**2 + r*(M+2*r)) + 8*r**5))

    # For compatibility, we must compute gPhys4UU
    gammaKSUU, gammaKSDET = ixp.symm_matrix_inverter3x3(gammaKSDD)
    # See, e.g., Eq. 4.49 of https://arxiv.org/pdf/gr-qc/0703035.pdf , where N = alpha
    gPhys4UU[0][0] = -1 / alphaKS**2
    for i in range(1, 4):
        if i > 0:
            # if the quantity does not have a "4", then it is assumed to be a 3D quantity.
            #  E.g., betaKSU[] is a spatial vector, with indices ranging from 0 to 2:
            gPhys4UU[0][i] = gPhys4UU[i][0] = betaKSU[i - 1] / alphaKS**2
    for i in range(1, 4):
        for j in range(1, 4):
            # if the quantity does not have a "4", then it is assumed to be a 3D quantity.
            #  E.g., betaKSU[] is a spatial vector, with indices ranging from 0 to 2,
            #    and gammaKSUU[][] is a spatial tensor, with indices again ranging from 0 to 2.
            gPhys4UU[i][j] = gPhys4UU[j][i] = gammaKSUU[i - 1][
                j - 1] - betaKSU[i - 1] * betaKSU[j - 1] / alphaKS**2

    A_b = par.Cparameters("REAL", thismodule, "A_b", 1.0)

    A_3vecpotentialD = ixp.zerorank1()
    # Set A_phi = A_b*rho_initial FIXME: why is there a sign error?
    A_3vecpotentialD[2] = -A_b * rho_initial

    BtildeU = ixp.register_gridfunctions_for_single_rank1("EVOL", "BtildeU")
    # Eq 15 of https://arxiv.org/pdf/1501.07276.pdf:
    # B = curl A -> B^r = d_th A_ph - d_ph A_th
    BtildeU[0] = sp.diff(A_3vecpotentialD[2], th) - sp.diff(
        A_3vecpotentialD[1], ph)
    # B = curl A -> B^th = d_ph A_r - d_r A_ph
    BtildeU[1] = sp.diff(A_3vecpotentialD[0], ph) - sp.diff(
        A_3vecpotentialD[2], r)
    # B = curl A -> B^ph = d_r A_th - d_th A_r
    BtildeU[2] = sp.diff(A_3vecpotentialD[1], r) - sp.diff(
        A_3vecpotentialD[0], th)

    # Construct spacetime metric in 3+1 form:
    # See, e.g., Eq. 4.49 of https://arxiv.org/pdf/gr-qc/0703035.pdf , where N = alpha
    # The return values from gri.register_gridfunctions() & ixp.register_gridfunctions_for_single_rank1() are
    #   unused, so we ignore them below:
    gri.register_gridfunctions("EVOL", ["alpha"])
    ixp.register_gridfunctions_for_single_rank1("EVOL", "betaU")

    alpha = sp.sqrt(1 / (-gPhys4UU[0][0]))
    betaU = ixp.zerorank1()
    for i in range(3):
        betaU[i] = alpha**2 * gPhys4UU[0][i + 1]
    gammaUU = ixp.zerorank2()
    for i in range(3):
        for j in range(3):
            gammaUU[i][j] = gPhys4UU[i + 1][j +
                                            1] + betaU[i] * betaU[j] / alpha**2

    # The return value from ixp.register_gridfunctions_for_single_rank2() is unused so we ignore it below:
    ixp.register_gridfunctions_for_single_rank2("EVOL", "gammaDD", "sym01")
    gammaDD, igammaDET = ixp.symm_matrix_inverter3x3(gammaUU)
    gammaDET = 1 / igammaDET

    ###############
    # Next compute g_{\alpha \beta} from lower 3-metric, using
    # Eq 4.47 of https://arxiv.org/pdf/gr-qc/0703035.pdf
    betaD = ixp.zerorank1()
    for i in range(3):
        for j in range(3):
            betaD[i] += gammaDD[i][j] * betaU[j]

    beta2 = sp.sympify(0)
    for i in range(3):
        beta2 += betaU[i] * betaD[i]

    global gPhys4DD
    gPhys4DD = ixp.zerorank2(DIM=4)
    gPhys4DD[0][0] = -alpha**2 + beta2
    for i in range(3):
        gPhys4DD[0][i + 1] = gPhys4DD[i + 1][0] = betaD[i]
        for j in range(3):
            gPhys4DD[i + 1][j + 1] = gammaDD[i][j]

    ###############
    # Next compute b^{\mu} using Eqs 23 and 31 of https://arxiv.org/pdf/astro-ph/0503420.pdf
    uKS4D = ixp.zerorank1(DIM=4)
    for i in range(4):
        for j in range(4):
            uKS4D[i] += gPhys4DD[i][j] * uKS4U[j]

    # Eq 27 of https://arxiv.org/pdf/astro-ph/0503420.pdf
    BU = ixp.zerorank1()
    for i in range(3):
        BU[i] = BtildeU[i] / sp.sqrt(gammaDET)

    # Eq 23 of https://arxiv.org/pdf/astro-ph/0503420.pdf
    BU0_u = sp.sympify(0)
    for i in range(3):
        BU0_u += uKS4D[i + 1] * BU[i] / alpha

    smallbU = ixp.zerorank1(DIM=4)
    smallbU[0] = BU0_u / sp.sqrt(4 * sp.pi)
    # Eqs 24 and 31 of https://arxiv.org/pdf/astro-ph/0503420.pdf
    for i in range(3):
        smallbU[i +
                1] = (BU[i] / alpha +
                      BU0_u * uKS4U[i + 1]) / (sp.sqrt(4 * sp.pi) * uKS4U[0])

    smallbD = ixp.zerorank1(DIM=4)
    for i in range(4):
        for j in range(4):
            smallbD[i] += gPhys4DD[i][j] * smallbU[j]

    smallb2 = sp.sympify(0)
    for i in range(4):
        smallb2 += smallbU[i] * smallbD[i]

    ###############
    LorentzFactor = alpha * uKS4U[0]
    # Define Valencia 3-velocity v^i_(n), which sets the 3-velocity as measured by normal observers to the spatial slice:
    #  v^i_(n) = u^i/(u^0*alpha) + beta^i/alpha. See eq 11 of https://arxiv.org/pdf/1501.07276.pdf
    Valencia3velocityU = ixp.zerorank1()
    for i in range(3):
        Valencia3velocityU[i] = uKS4U[i + 1] / (alpha *
                                                uKS4U[0]) + betaU[i] / alpha

    # sqrtgamma4DET = sp.sqrt(gammaDET)*alpha

    alpha = alpha.subs(r,
                       rfm.xxSph[0]).subs(th,
                                          rfm.xxSph[1]).subs(ph, rfm.xxSph[2])
    for i in range(DIM):
        betaU[i] = betaU[i].subs(r, rfm.xxSph[0]).subs(th, rfm.xxSph[1]).subs(
            ph, rfm.xxSph[2])
        for j in range(DIM):
            gammaDD[i][j] = gammaDD[i][j].subs(r, rfm.xxSph[0]).subs(
                th, rfm.xxSph[1]).subs(ph, rfm.xxSph[2])
            KDD[i][j] = KDD[i][j].subs(r, rfm.xxSph[0]).subs(
                th, rfm.xxSph[1]).subs(ph, rfm.xxSph[2])

    # GRMHD variables:
    # Density and pressure:
    hm1 = hm1.subs(r, rfm.xxSph[0]).subs(th,
                                         rfm.xxSph[1]).subs(ph, rfm.xxSph[2])
    rho_initial = rho_initial.subs(r, rfm.xxSph[0]).subs(th,
                                                         rfm.xxSph[1]).subs(
                                                             ph, rfm.xxSph[2])

    # "Valencia" three-velocity
    for i in range(DIM):
        BtildeU[i] = BtildeU[i].subs(r,
                                     rfm.xxSph[0]).subs(th, rfm.xxSph[1]).subs(
                                         ph, rfm.xxSph[2])
        uKS4U[i + 1] = uKS4U[i + 1].subs(r, rfm.xxSph[0]).subs(
            th, rfm.xxSph[1]).subs(ph, rfm.xxSph[2])
        uBL4U[i + 1] = uBL4U[i + 1].subs(r, rfm.xxSph[0]).subs(
            th, rfm.xxSph[1]).subs(ph, rfm.xxSph[2])
        Valencia3velocityU[i] = Valencia3velocityU[i].subs(
            r, rfm.xxSph[0]).subs(th, rfm.xxSph[1]).subs(ph, rfm.xxSph[2])

    # Transform initial data to our coordinate system:
    # First compute Jacobian and its inverse
    drrefmetric__dx_0UDmatrix = sp.Matrix([[
        sp.diff(rfm.xxSph[0], rfm.xx[0]),
        sp.diff(rfm.xxSph[0], rfm.xx[1]),
        sp.diff(rfm.xxSph[0], rfm.xx[2])
    ],
                                           [
                                               sp.diff(rfm.xxSph[1],
                                                       rfm.xx[0]),
                                               sp.diff(rfm.xxSph[1],
                                                       rfm.xx[1]),
                                               sp.diff(rfm.xxSph[1], rfm.xx[2])
                                           ],
                                           [
                                               sp.diff(rfm.xxSph[2],
                                                       rfm.xx[0]),
                                               sp.diff(rfm.xxSph[2],
                                                       rfm.xx[1]),
                                               sp.diff(rfm.xxSph[2], rfm.xx[2])
                                           ]])
    dx__drrefmetric_0UDmatrix = drrefmetric__dx_0UDmatrix.inv()

    # Declare as gridfunctions the final quantities we will output for the initial data
    global IDalpha, IDgammaDD, IDKDD, IDbetaU, IDValencia3velocityU
    IDalpha = gri.register_gridfunctions("EVOL", "IDalpha")
    IDgammaDD = ixp.register_gridfunctions_for_single_rank2(
        "EVOL", "IDgammaDD", "sym01")
    IDKDD = ixp.register_gridfunctions_for_single_rank2(
        "EVOL", "IDKDD", "sym01")
    IDbetaU = ixp.register_gridfunctions_for_single_rank1("EVOL", "IDbetaU")
    IDValencia3velocityU = ixp.register_gridfunctions_for_single_rank1(
        "EVOL", "IDValencia3velocityU")

    IDalpha = alpha
    for i in range(3):
        IDbetaU[i] = 0
        IDValencia3velocityU[i] = 0
        for j in range(3):
            # Matrices are stored in row, column format, so (i,j) <-> (row,column)
            IDbetaU[i] += dx__drrefmetric_0UDmatrix[(i, j)] * betaU[j]
            IDValencia3velocityU[i] += dx__drrefmetric_0UDmatrix[
                (i, j)] * Valencia3velocityU[j]
            IDgammaDD[i][j] = 0
            IDKDD[i][j] = 0
            for k in range(3):
                for l in range(3):
                    IDgammaDD[i][j] += drrefmetric__dx_0UDmatrix[(
                        k, i)] * drrefmetric__dx_0UDmatrix[(l,
                                                            j)] * gammaDD[k][l]
                    IDKDD[i][j] += drrefmetric__dx_0UDmatrix[
                        (k, i)] * drrefmetric__dx_0UDmatrix[(l, j)] * KDD[k][l]
Exemple #6
0
      (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):
    if sys.version_info[0] == 3:
def add_FD_func_to_outC_function_dict(list_of_deriv_vars,
                                      list_of_base_gridfunction_names_in_derivs, list_of_deriv_operators,
                                      fdcoeffs, fdstencl):
    # Step 5.a.ii.A: First construct a list of all the unique finite difference functions
    list_of_uniq_deriv_operators = superfast_uniq(list_of_deriv_operators)
    c_type = "REAL"
    if par.parval_from_str("grid::GridFuncMemAccess") == "ETK":
        c_type = "CCTK_REAL"
    func_prefix = "order_"+str(FDparams.FD_CD_order)+"_"
    if FDparams.enable_SIMD == "True":
        c_type = "REAL_SIMD_ARRAY"
        func_prefix = "SIMD_"+func_prefix

    # Stores the needed calls to the functions we're adding to outC_function_dict:
    FDfunccall_list = []
    for op in list_of_uniq_deriv_operators:
        which_op_idx = find_which_op_idx(op, list_of_deriv_operators)

        rhs_expr = sp.sympify(0)
        for j in range(len(fdcoeffs[which_op_idx])):
            var = sp.sympify("f" + varsuffix(fdstencl[which_op_idx][j], FDparams))
            rhs_expr += fdcoeffs[which_op_idx][j] * var

        # Multiply each expression by the appropriate power
        #   of 1/dx[i]
        invdx = []
        used_invdx = [False, False, False, False]
        for d in range(FDparams.DIM):
            invdx.append(sp.sympify("invdx" + str(d)))
        # First-order or Kreiss-Oliger derivatives:
        if ( (len(op) == 5 and "dKOD" in op) or
             (len(op) == 3 and   "dD" in op) or
             (len(op) == 5 and ("dupD" in op or "ddnD" in op)) ):
            dirn = int(op[len(op) - 1])
            rhs_expr *= invdx[dirn]
            used_invdx[dirn] = True
        # Second-order derivs:
        elif len(op) == 5 and "dDD" in op:
            dirn1 = int(op[len(op) - 2])
            dirn2 = int(op[len(op) - 1])
            used_invdx[dirn1] = used_invdx[dirn2] = True
            rhs_expr *= invdx[dirn1]*invdx[dirn2]
        else:
            print("Error: was unable to parse derivative operator: ", op)
            sys.exit(1)

        outfunc_params = ""
        for d in range(FDparams.DIM):
            if used_invdx[d]:
                outfunc_params += "const " + c_type + " invdx" + str(d) + ","

        for j in range(len(fdcoeffs[which_op_idx])):
            var = sp.sympify("f" + varsuffix(fdstencl[which_op_idx][j], FDparams))
            outfunc_params += "const " + c_type + " " + str(var)
            if j != len(fdcoeffs[which_op_idx])-1:
                outfunc_params += ","

        for i in range(len(list_of_deriv_operators)):
            # print("comparing ",list_of_deriv_operators[i],op)
            if list_of_deriv_operators[i] == op:
                funccall = type__var(list_of_deriv_vars[i], FDparams) + " = " + func_prefix + "f_" + str(op) + "("
                for d in range(FDparams.DIM):
                    if used_invdx[d]:
                        funccall += "invdx" + str(d) + ","
                gfname = list_of_base_gridfunction_names_in_derivs[i]
                for j in range(len(fdcoeffs[which_op_idx])):
                    funccall += gfname + varsuffix(fdstencl[which_op_idx][j], FDparams)
                    if j != len(fdcoeffs[which_op_idx])-1:
                        funccall += ","
                funccall += ");"
                FDfunccall_list.append(funccall)

        # If the function already exists in the outC_function_dict, then do not add it; move to the next op.
        if func_prefix + "f_" + str(op) not in outC_function_dict:
            p = "preindent=1,enable_SIMD="+FDparams.enable_SIMD+",outCverbose=False,CSE_preprocess=True,includebraces=False"
            outFDstr = outputC(rhs_expr, "retval", "returnstring", params=p)
            outFDstr = outFDstr.replace("retval = ", "return ")
            add_to_Cfunction_dict(desc=" * (__FD_OPERATOR_FUNC__) Finite difference operator for "+str(op).replace("dDD", "second derivative: ").
                                  replace("dD", "first derivative: ").replace("dKOD", "Kreiss-Oliger derivative: ").
                                  replace("dupD", "upwinded derivative: ").replace("ddnD", "downwinded derivative: ") + " direction. In Cartesian coordinates, directions 0,1,2 correspond to x,y,z directions, respectively.",
                                  c_type="static " + c_type + " _NOINLINE _UNUSED",
                                  name=func_prefix+"f_" + str(op), enableCparameters=False,
                                  params=outfunc_params, preloop="", body=outFDstr)
    return FDfunccall_list
Exemple #8
0
def GiRaFFE_HO_A2B(outdir):
    # Register the gridfunction gammadet. This determinant will be calculated separately
    gammadet = gri.register_gridfunctions("AUXEVOL", "gammadet")
    # Import the Levi-Civita symbol and build the corresponding tensor.
    # We already have a handy function to define the Levi-Civita symbol in WeylScalars
    import WeylScal4NRPy.WeylScalars_Cartesian as weyl
    LeviCivitaDDD = weyl.define_LeviCivitaSymbol_rank3()
    LeviCivitaUUU = ixp.zerorank3()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                LCijk = LeviCivitaDDD[i][j][k]
                #LeviCivitaDDD[i][j][k] = LCijk * sp.sqrt(gho.gammadet)
                LeviCivitaUUU[i][j][k] = LCijk / sp.sqrt(gammadet)

    # We can use this function to compactly reset to expressions to print at each FD order.
    def set_BU_to_print():
        return [lhrh(lhs=gri.gfaccess("out_gfs","BU0"),rhs=BU[0]),\
                lhrh(lhs=gri.gfaccess("out_gfs","BU1"),rhs=BU[1]),\
                lhrh(lhs=gri.gfaccess("out_gfs","BU2"),rhs=BU[2])]

    AD = ixp.register_gridfunctions_for_single_rank1("EVOL", "AD")
    BU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "BU")
    AD_dD = ixp.declarerank2("AD_dD", "nosym")
    BU = ixp.zerorank1(
    )  # BU is already registered as a gridfunction, but we need to zero its values and declare it in this scope.

    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j]

    par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", 10)
    fin.FD_outputC(os.path.join(outdir, "B_from_A_order10.h"),
                   set_BU_to_print(),
                   params="outCverbose=False")

    par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", 8)
    fin.FD_outputC(os.path.join(outdir, "B_from_A_order8.h"),
                   set_BU_to_print(),
                   params="outCverbose=False")

    par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", 6)
    fin.FD_outputC(os.path.join(outdir, "B_from_A_order6.h"),
                   set_BU_to_print(),
                   params="outCverbose=False")

    par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", 4)
    fin.FD_outputC(os.path.join(outdir, "B_from_A_order4.h"),
                   set_BU_to_print(),
                   params="outCverbose=False")

    par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", 2)
    fin.FD_outputC(os.path.join(outdir, "B_from_A_order2.h"),
                   set_BU_to_print(),
                   params="outCverbose=False")

    # For the outermost points, we'll need a separate file for each face.
    # These will correspond to an upwinded and a downwinded file for each direction.
    AD_ddnD = ixp.declarerank2("AD_ddnD", "nosym")
    for i in range(DIM):
        BU[i] = 0
        for j in range(DIM):
            for k in range(DIM):
                if j == 0:
                    BU[i] += LeviCivitaUUU[i][j][k] * AD_ddnD[k][j]
                else:
                    BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j]

    fin.FD_outputC(os.path.join(outdir, "B_from_A_order2_dirx0_dnwind.h"),
                   set_BU_to_print(),
                   params="outCverbose=False")

    AD_dupD = ixp.declarerank2("AD_dupD", "nosym")
    for i in range(DIM):
        BU[i] = 0
        for j in range(DIM):
            for k in range(DIM):
                if j == 0:
                    BU[i] += LeviCivitaUUU[i][j][k] * AD_dupD[k][j]
                else:
                    BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j]

    fin.FD_outputC(os.path.join(outdir, "B_from_A_order2_dirx0_upwind.h"),
                   set_BU_to_print(),
                   params="outCverbose=False")

    for i in range(DIM):
        BU[i] = 0
        for j in range(DIM):
            for k in range(DIM):
                if j == 1:
                    BU[i] += LeviCivitaUUU[i][j][k] * AD_ddnD[k][j]
                else:
                    BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j]

    fin.FD_outputC(os.path.join(outdir, "B_from_A_order2_dirx1_dnwind.h"),
                   set_BU_to_print(),
                   params="outCverbose=False")
    for i in range(DIM):
        BU[i] = 0
        for j in range(DIM):
            for k in range(DIM):
                if j == 1:
                    BU[i] += LeviCivitaUUU[i][j][k] * AD_dupD[k][j]
                else:
                    BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j]

    fin.FD_outputC(os.path.join(outdir, "B_from_A_order2_dirx1_upwind.h"),
                   set_BU_to_print(),
                   params="outCverbose=False")

    for i in range(DIM):
        BU[i] = 0
        for j in range(DIM):
            for k in range(DIM):
                if j == 2:
                    BU[i] += LeviCivitaUUU[i][j][k] * AD_ddnD[k][j]
                else:
                    BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j]

    fin.FD_outputC(os.path.join(outdir, "B_from_A_order2_dirx2_dnwind.h"),
                   set_BU_to_print(),
                   params="outCverbose=False")
    for i in range(DIM):
        BU[i] = 0
        for j in range(DIM):
            for k in range(DIM):
                if j == 2:
                    BU[i] += LeviCivitaUUU[i][j][k] * AD_dupD[k][j]
                else:
                    BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j]

    fin.FD_outputC(os.path.join(outdir, "B_from_A_order2_dirx2_upwind.h"),
                   set_BU_to_print(),
                   params="outCverbose=False")
# 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")) # 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!")
        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,
def GiRaFFE_NRPy_A2B(outdir,gammaDD,AD,BU):
    cmd.mkdir(outdir)
    # Set spatial dimension (must be 3 for BSSN)
    DIM = 3
    par.set_parval_from_str("grid::DIM",DIM)
    # Compute the sqrt of the three metric determinant.
    import GRHD.equations as gh
    gh.compute_sqrtgammaDET(gammaDD)

    # Import the Levi-Civita symbol and build the corresponding tensor.
    # We already have a handy function to define the Levi-Civita symbol in indexedexp.py
    LeviCivitaUUU = ixp.LeviCivitaTensorUUU_dim3_rank3(gh.sqrtgammaDET)

    AD_dD = ixp.declarerank2("AD_dD","nosym")
    BU = ixp.zerorank1()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                BU[i] += LeviCivitaUUU[i][j][k] * AD_dD[k][j]

    # Write the code to compute derivatives with shifted stencils as needed.
    with open(os.path.join(outdir,"driver_AtoB.h"),"w") as file:
        file.write("""void compute_A2B_in_ghostzones(const paramstruct *restrict params,REAL *restrict in_gfs,REAL *restrict auxevol_gfs,
                                      const int i0min,const int i0max,
                                      const int i1min,const int i1max,
                                      const int i2min,const int i2max) {
#include "../set_Cparameters.h"
    for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) {
        REAL dx_Ay,dx_Az,dy_Ax,dy_Az,dz_Ax,dz_Ay;
        // Check to see if we're on the +x or -x face. If so, use a downwinded- or upwinded-stencil, respectively.
        // Otherwise, use a centered stencil.
        if (i0 > 0 && i0 < Nxx_plus_2NGHOSTS0-1) {
            dx_Ay = invdx0*(-1.0/2.0*in_gfs[IDX4S(AD1GF, i0-1,i1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD1GF, i0+1,i1,i2)]);
            dx_Az = invdx0*(-1.0/2.0*in_gfs[IDX4S(AD2GF, i0-1,i1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD2GF, i0+1,i1,i2)]);
        }
        else if (i0==0) {
            dx_Ay = invdx0*(-3.0/2.0*in_gfs[IDX4S(AD1GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD1GF, i0+1,i1,i2)] - 1.0/2.0*in_gfs[IDX4S(AD1GF, i0+2,i1,i2)]);
            dx_Az = invdx0*(-3.0/2.0*in_gfs[IDX4S(AD2GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD2GF, i0+1,i1,i2)] - 1.0/2.0*in_gfs[IDX4S(AD2GF, i0+2,i1,i2)]);
        }
        else {
            dx_Ay = invdx0*((3.0/2.0)*in_gfs[IDX4S(AD1GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD1GF, i0-1,i1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD1GF, i0-2,i1,i2)]);
            dx_Az = invdx0*((3.0/2.0)*in_gfs[IDX4S(AD2GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD2GF, i0-1,i1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD2GF, i0-2,i1,i2)]);
        }
        // As above, but in the y direction.
        if (i1 > 0 && i1 < Nxx_plus_2NGHOSTS1-1) {
            dy_Ax = invdx1*(-1.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1-1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1+1,i2)]);
            dy_Az = invdx1*(-1.0/2.0*in_gfs[IDX4S(AD2GF, i0,i1-1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD2GF, i0,i1+1,i2)]);
        }
        else if (i1==0) {
            dy_Ax = invdx1*(-3.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD0GF, i0,i1+1,i2)] - 1.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1+2,i2)]);
            dy_Az = invdx1*(-3.0/2.0*in_gfs[IDX4S(AD2GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD2GF, i0,i1+1,i2)] - 1.0/2.0*in_gfs[IDX4S(AD2GF, i0,i1+2,i2)]);
        }
        else {
            dy_Ax = invdx1*((3.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD0GF, i0,i1-1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1-2,i2)]);
            dy_Az = invdx1*((3.0/2.0)*in_gfs[IDX4S(AD2GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD2GF, i0,i1-1,i2)] + (1.0/2.0)*in_gfs[IDX4S(AD2GF, i0,i1-2,i2)]);
        }
        // As above, but in the z direction.
        if (i2 > 0 && i2 < Nxx_plus_2NGHOSTS2-1) {
            dz_Ax = invdx2*(-1.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1,i2-1)] + (1.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1,i2+1)]);
            dz_Ay = invdx2*(-1.0/2.0*in_gfs[IDX4S(AD1GF, i0,i1,i2-1)] + (1.0/2.0)*in_gfs[IDX4S(AD1GF, i0,i1,i2+1)]);
        }
        else if (i2==0) {
            dz_Ax = invdx2*(-3.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD0GF, i0,i1,i2+1)] - 1.0/2.0*in_gfs[IDX4S(AD0GF, i0,i1,i2+2)]);
            dz_Ay = invdx2*(-3.0/2.0*in_gfs[IDX4S(AD1GF, i0,i1,i2)] + 2*in_gfs[IDX4S(AD1GF, i0,i1,i2+1)] - 1.0/2.0*in_gfs[IDX4S(AD1GF, i0,i1,i2+2)]);
        }
        else {
            dz_Ax = invdx2*((3.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD0GF, i0,i1,i2-1)] + (1.0/2.0)*in_gfs[IDX4S(AD0GF, i0,i1,i2-2)]);
            dz_Ay = invdx2*((3.0/2.0)*in_gfs[IDX4S(AD1GF, i0,i1,i2)] - 2*in_gfs[IDX4S(AD1GF, i0,i1,i2-1)] + (1.0/2.0)*in_gfs[IDX4S(AD1GF, i0,i1,i2-2)]);
        }
        // Compute the magnetic field in the normal way, using the previously calculated derivatives.
        const double gammaDD00 = auxevol_gfs[IDX4S(GAMMADD00GF, i0,i1,i2)];
        const double gammaDD01 = auxevol_gfs[IDX4S(GAMMADD01GF, i0,i1,i2)];
        const double gammaDD02 = auxevol_gfs[IDX4S(GAMMADD02GF, i0,i1,i2)];
        const double gammaDD11 = auxevol_gfs[IDX4S(GAMMADD11GF, i0,i1,i2)];
        const double gammaDD12 = auxevol_gfs[IDX4S(GAMMADD12GF, i0,i1,i2)];
        const double gammaDD22 = auxevol_gfs[IDX4S(GAMMADD22GF, i0,i1,i2)];
        /*
        * NRPy+ Finite Difference Code Generation, Step 2 of 1: Evaluate SymPy expressions and write to main memory:
        */
        const double invsqrtg = 1.0/sqrt(gammaDD00*gammaDD11*gammaDD22
                                       - gammaDD00*gammaDD12*gammaDD12
                                       + 2*gammaDD01*gammaDD02*gammaDD12
                                       - gammaDD11*gammaDD02*gammaDD02
                                       - gammaDD22*gammaDD01*gammaDD01);
        auxevol_gfs[IDX4S(BU0GF, i0,i1,i2)] = (dy_Az-dz_Ay)*invsqrtg;
        auxevol_gfs[IDX4S(BU1GF, i0,i1,i2)] = (dz_Ax-dx_Az)*invsqrtg;
        auxevol_gfs[IDX4S(BU2GF, i0,i1,i2)] = (dx_Ay-dy_Ax)*invsqrtg;
    }
}
""")

    # Now, we'll also write some more auxiliary functions to handle the order-lowering method for A2B
    with open(os.path.join(outdir,"driver_AtoB.h"),"a") as file:
        file.write("""REAL relative_error(REAL a, REAL b) {
    if((a+b)!=0.0) {
        return 2.0*fabs(a-b)/fabs(a+b);
    }
    else {
        return 0.0;
    }
}

#define M2 0
#define M1 1
#define P0 2
#define P1 3
#define P2 4
#define CN4 0
#define CN2 1
#define UP2 2
#define DN2 3
#define UP1 4
#define DN1 5
void compute_Bx_pointwise(REAL *Bx, const REAL invdy, const REAL *Ay, const REAL invdz, const REAL *Az) {
    REAL dz_Ay,dy_Az;
    dz_Ay = invdz*((Ay[P1]-Ay[M1])*2.0/3.0 - (Ay[P2]-Ay[M2])/12.0);
    dy_Az = invdy*((Az[P1]-Az[M1])*2.0/3.0 - (Az[P2]-Az[M2])/12.0);
    Bx[CN4] = dy_Az - dz_Ay;

    dz_Ay = invdz*(Ay[P1]-Ay[M1])/2.0;
    dy_Az = invdy*(Az[P1]-Az[M1])/2.0;
    Bx[CN2] = dy_Az - dz_Ay;

    dz_Ay = invdz*(-1.5*Ay[P0]+2.0*Ay[P1]-0.5*Ay[P2]);
    dy_Az = invdy*(-1.5*Az[P0]+2.0*Az[P1]-0.5*Az[P2]);
    Bx[UP2] = dy_Az - dz_Ay;

    dz_Ay = invdz*(1.5*Ay[P0]-2.0*Ay[M1]+0.5*Ay[M2]);
    dy_Az = invdy*(1.5*Az[P0]-2.0*Az[M1]+0.5*Az[M2]);
    Bx[DN2] = dy_Az - dz_Ay;

    dz_Ay = invdz*(Ay[P1]-Ay[P0]);
    dy_Az = invdy*(Az[P1]-Az[P0]);
    Bx[UP1] = dy_Az - dz_Ay;

    dz_Ay = invdz*(Ay[P0]-Ay[M1]);
    dy_Az = invdy*(Az[P0]-Az[M1]);
    Bx[DN1] = dy_Az - dz_Ay;
}

#define TOLERANCE_A2B 1.0e-4
REAL find_accepted_Bx_order(REAL *Bx) {
    REAL accepted_val = Bx[CN4];
    REAL Rel_error_o2_vs_o4 = relative_error(Bx[CN2],Bx[CN4]);
    REAL Rel_error_oCN2_vs_oDN2 = relative_error(Bx[CN2],Bx[DN2]);
    REAL Rel_error_oCN2_vs_oUP2 = relative_error(Bx[CN2],Bx[UP2]);

    if(Rel_error_o2_vs_o4 > TOLERANCE_A2B) {
        accepted_val = Bx[CN2];
        if(Rel_error_o2_vs_o4 > Rel_error_oCN2_vs_oDN2 || Rel_error_o2_vs_o4 > Rel_error_oCN2_vs_oUP2) {
            // Should we use AND or OR in if statement?
            if(relative_error(Bx[UP2],Bx[UP1]) < relative_error(Bx[DN2],Bx[DN1])) {
                accepted_val = Bx[UP2];
            }
            else {
                accepted_val = Bx[DN2];
            }
        }
    }
    return accepted_val;
}
""")

    order_lowering_body = """REAL AD0_1[5],AD0_2[5],AD1_2[5],AD1_0[5],AD2_0[5],AD2_1[5];
const double gammaDD00 = auxevol_gfs[IDX4S(GAMMADD00GF, i0,i1,i2)];
const double gammaDD01 = auxevol_gfs[IDX4S(GAMMADD01GF, i0,i1,i2)];
const double gammaDD02 = auxevol_gfs[IDX4S(GAMMADD02GF, i0,i1,i2)];
const double gammaDD11 = auxevol_gfs[IDX4S(GAMMADD11GF, i0,i1,i2)];
const double gammaDD12 = auxevol_gfs[IDX4S(GAMMADD12GF, i0,i1,i2)];
const double gammaDD22 = auxevol_gfs[IDX4S(GAMMADD22GF, i0,i1,i2)];
AD0_2[M2] = in_gfs[IDX4S(AD0GF, i0,i1,i2-2)];
AD0_2[M1] = in_gfs[IDX4S(AD0GF, i0,i1,i2-1)];
AD0_1[M2] = in_gfs[IDX4S(AD0GF, i0,i1-2,i2)];
AD0_1[M1] = in_gfs[IDX4S(AD0GF, i0,i1-1,i2)];
AD0_1[P0] = AD0_2[P0] = in_gfs[IDX4S(AD0GF, i0,i1,i2)];
AD0_1[P1] = in_gfs[IDX4S(AD0GF, i0,i1+1,i2)];
AD0_1[P2] = in_gfs[IDX4S(AD0GF, i0,i1+2,i2)];
AD0_2[P1] = in_gfs[IDX4S(AD0GF, i0,i1,i2+1)];
AD0_2[P2] = in_gfs[IDX4S(AD0GF, i0,i1,i2+2)];
AD1_2[M2] = in_gfs[IDX4S(AD1GF, i0,i1,i2-2)];
AD1_2[M1] = in_gfs[IDX4S(AD1GF, i0,i1,i2-1)];
AD1_0[M2] = in_gfs[IDX4S(AD1GF, i0-2,i1,i2)];
AD1_0[M1] = in_gfs[IDX4S(AD1GF, i0-1,i1,i2)];
AD1_2[P0] = AD1_0[P0] = in_gfs[IDX4S(AD1GF, i0,i1,i2)];
AD1_0[P1] = in_gfs[IDX4S(AD1GF, i0+1,i1,i2)];
AD1_0[P2] = in_gfs[IDX4S(AD1GF, i0+2,i1,i2)];
AD1_2[P1] = in_gfs[IDX4S(AD1GF, i0,i1,i2+1)];
AD1_2[P2] = in_gfs[IDX4S(AD1GF, i0,i1,i2+2)];
AD2_1[M2] = in_gfs[IDX4S(AD2GF, i0,i1-2,i2)];
AD2_1[M1] = in_gfs[IDX4S(AD2GF, i0,i1-1,i2)];
AD2_0[M2] = in_gfs[IDX4S(AD2GF, i0-2,i1,i2)];
AD2_0[M1] = in_gfs[IDX4S(AD2GF, i0-1,i1,i2)];
AD2_0[P0] = AD2_1[P0] = in_gfs[IDX4S(AD2GF, i0,i1,i2)];
AD2_0[P1] = in_gfs[IDX4S(AD2GF, i0+1,i1,i2)];
AD2_0[P2] = in_gfs[IDX4S(AD2GF, i0+2,i1,i2)];
AD2_1[P1] = in_gfs[IDX4S(AD2GF, i0,i1+1,i2)];
AD2_1[P2] = in_gfs[IDX4S(AD2GF, i0,i1+2,i2)];
const double invsqrtg = 1.0/sqrt(gammaDD00*gammaDD11*gammaDD22
                               - gammaDD00*gammaDD12*gammaDD12
                               + 2*gammaDD01*gammaDD02*gammaDD12
                               - gammaDD11*gammaDD02*gammaDD02
                               - gammaDD22*gammaDD01*gammaDD01);

REAL BU0[4],BU1[4],BU2[4];
compute_Bx_pointwise(BU0,invdx2,AD1_2,invdx1,AD2_1);
compute_Bx_pointwise(BU1,invdx0,AD2_0,invdx2,AD0_2);
compute_Bx_pointwise(BU2,invdx1,AD0_1,invdx0,AD1_0);

auxevol_gfs[IDX4S(BU0GF, i0,i1,i2)] = find_accepted_Bx_order(BU0)*invsqrtg;
auxevol_gfs[IDX4S(BU1GF, i0,i1,i2)] = find_accepted_Bx_order(BU1)*invsqrtg;
auxevol_gfs[IDX4S(BU2GF, i0,i1,i2)] = find_accepted_Bx_order(BU2)*invsqrtg;
"""

    # Here, we'll use the outCfunction() function to output a function that will compute the magnetic field
    # on the interior. Then, we'll add postloop code to handle the ghostzones.
    desc="Compute the magnetic field from the vector potential everywhere, including ghostzones"
    name="driver_A_to_B"
    driver_Ccode = outCfunction(
        outfile  = "returnstring", desc=desc, name=name,
        params   = "const paramstruct *restrict params,REAL *restrict in_gfs,REAL *restrict auxevol_gfs",
        body     = fin.FD_outputC("returnstring",[lhrh(lhs=gri.gfaccess("out_gfs","BU0"),rhs=BU[0]),\
                                                  lhrh(lhs=gri.gfaccess("out_gfs","BU1"),rhs=BU[1]),\
                                                  lhrh(lhs=gri.gfaccess("out_gfs","BU2"),rhs=BU[2])]).replace("IDX4","IDX4S"),
#         body     = order_lowering_body,
        postloop = """
    int imin[3] = { NGHOSTS_A2B, NGHOSTS_A2B, NGHOSTS_A2B };
    int imax[3] = { NGHOSTS+Nxx0, NGHOSTS+Nxx1, NGHOSTS+Nxx2 };
    // Now, we loop over the ghostzones to calculate the magnetic field there.
    for(int which_gz = 0; which_gz < NGHOSTS_A2B; which_gz++) {
        // After updating each face, adjust imin[] and imax[]
        //   to reflect the newly-updated face extents.
        compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2]); imin[0]--;
        compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2]); imax[0]++;

        compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2]); imin[1]--;
        compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2]); imax[1]++;

        compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2]); imin[2]--;
        compute_A2B_in_ghostzones(params,in_gfs,auxevol_gfs,imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1); imax[2]++;
    }
""",
        loopopts="InteriorPoints",
        rel_path_for_Cparams=os.path.join("../")).replace("= NGHOSTS","= NGHOSTS_A2B").replace("NGHOSTS+Nxx0","Nxx_plus_2NGHOSTS0-NGHOSTS_A2B").replace("NGHOSTS+Nxx1","Nxx_plus_2NGHOSTS1-NGHOSTS_A2B").replace("NGHOSTS+Nxx2","Nxx_plus_2NGHOSTS2-NGHOSTS_A2B")

    with open(os.path.join(outdir,"driver_AtoB.h"),"a") as file:
        file.write(driver_Ccode)
Exemple #11
0
# The A-to-B driver
from outputC import *
import os
import grid as gri
import indexedexp as ixp
import NRPy_param_funcs as par
import finite_difference as fin

# Set spatial dimension (must be 3 for BSSN)
DIM = 3
par.set_parval_from_str("grid::DIM", DIM)


def GiRaFFE_HO_A2B(outdir):
    # Register the gridfunction gammadet. This determinant will be calculated separately
    gammadet = gri.register_gridfunctions("AUXEVOL", "gammadet")
    # Import the Levi-Civita symbol and build the corresponding tensor.
    # We already have a handy function to define the Levi-Civita symbol in WeylScalars
    import WeylScal4NRPy.WeylScalars_Cartesian as weyl
    LeviCivitaDDD = weyl.define_LeviCivitaSymbol_rank3()
    LeviCivitaUUU = ixp.zerorank3()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                LCijk = LeviCivitaDDD[i][j][k]
                #LeviCivitaDDD[i][j][k] = LCijk * sp.sqrt(gho.gammadet)
                LeviCivitaUUU[i][j][k] = LCijk / sp.sqrt(gammadet)

    # We can use this function to compactly reset to expressions to print at each FD order.
    def set_BU_to_print():
        return [lhrh(lhs=gri.gfaccess("out_gfs","BU0"),rhs=BU[0]),\
#
# ### Steps 0-1: Preliminaries \[Back to [top](#top)\]
#
# Here, we will import the NRPy+ core modules and set the reference metric to Cartesian, set commonly used NRPy+ parameters, and set C parameters that will be set from outside the code eventually generated from these expressions. We will also set up a parameter to determine what initial data is set up, although it won't do much yet.
# $$\label{preliminaries}$$

# Step 0: Import the NRPy+ core modules and set the reference metric to Cartesian
import NRPy_param_funcs as par
import indexedexp as ixp
import grid as gri
import finite_difference as fin
from outputC import *
import loop

import reference_metric as rfm
par.set_parval_from_str("reference_metric::CoordSystem", "Cartesian")
rfm.reference_metric()

# Step 1a: Set commonly used parameters.
thismodule = "GiRaFFEfood_NRPy_1D"

# <a id='step2'></a>
# ### Set the vector $A_k$
# The vector potential is given as
# \begin{align}
# A_x &= 0 \\
# A_y &= \left \{ \begin{array}{lll}\gamma_\mu x - 0.015 & \mbox{if} & x \leq -0.1/\gamma_\mu \\
# 1.15 \gamma_\mu x - 0.03g(x) & \mbox{if} & -0.1/\gamma_\mu \leq x \leq 0.1/\gamma_\mu \\
# 1.3 \gamma_\mu x - 0.015 & \mbox{if} & x \geq 0.1/\gamma_\mu \end{array} \right. , \\
# A_z &= y - \gamma_\mu (1-\mu)x .
# \end{align}
Exemple #13
0
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 + "\"")
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: 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 not in ('Frozen', 'GammaDriving2ndOrder_NoCovariant', 'GammaDriving2ndOrder_Covariant',
                               'GammaDriving2ndOrder_Covariant__Hatted', 'GammaDriving1stOrder_Covariant',
                               '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]
import GiRaFFE_NRPy.GiRaFFE_NRPy_Metric_Face_Values as FCVAL
import GiRaFFE_NRPy.GiRaFFE_NRPy_PPM as PPM
import GiRaFFE_NRPy.GiRaFFE_NRPy_Afield_flux_handwritten as Af
import GiRaFFE_NRPy.Stilde_flux as Sf
import GiRaFFE_NRPy.GiRaFFE_NRPy_BCs as BC
import GiRaFFE_NRPy.GiRaFFE_NRPy_A2B as A2B
import GiRaFFE_NRPy.GiRaFFE_NRPy_C2P_P2C as C2P_P2C
import GiRaFFE_NRPy.GiRaFFE_NRPy_Source_Terms as source

thismodule = "GiRaFFE_NRPy_Main_Driver"

CoordSystem = "Cartesian"

outCparams = "outCverbose=False,CSE_sorting=none"

par.set_parval_from_str("reference_metric::CoordSystem",CoordSystem)
rfm.reference_metric() # Create ReU, ReDD needed for rescaling B-L initial data, generating BSSN RHSs, etc.

# Default Kreiss-Oliger dissipation strength
default_KO_strength = 0.1
diss_strength = par.Cparameters("REAL", thismodule, "diss_strength", default_KO_strength)

def GiRaFFE_NRPy_Main_Driver_generate_all(out_dir):
    cmd.mkdir(out_dir)

    gammaDD = ixp.register_gridfunctions_for_single_rank2("AUXEVOL","gammaDD","sym01",DIM=3)
    betaU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","betaU",DIM=3)
    alpha = gri.register_gridfunctions("AUXEVOL","alpha")
    AD = ixp.register_gridfunctions_for_single_rank1("EVOL","AD")
    BU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","BU")
    ValenciavU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","ValenciavU")
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 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!")
            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("REAL",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]
def GiRaFFE_NRPy_Main_Driver_generate_all(out_dir):
    cmd.mkdir(out_dir)

    gammaDD = ixp.register_gridfunctions_for_single_rank2("AUXEVOL","gammaDD","sym01",DIM=3)
    betaU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","betaU",DIM=3)
    alpha = gri.register_gridfunctions("AUXEVOL","alpha")
    AD = ixp.register_gridfunctions_for_single_rank1("EVOL","AD")
    BU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","BU")
    ValenciavU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","ValenciavU")
    psi6Phi = gri.register_gridfunctions("EVOL","psi6Phi")
    StildeD = ixp.register_gridfunctions_for_single_rank1("EVOL","StildeD")

    ixp.register_gridfunctions_for_single_rank1("AUXEVOL","PhievolParenU",DIM=3)
    gri.register_gridfunctions("AUXEVOL","AevolParen")

    # Declare this symbol:
    sqrt4pi = par.Cparameters("REAL",thismodule,"sqrt4pi","sqrt(4.0*M_PI)")

    GRHD.compute_sqrtgammaDET(gammaDD)
    GRFFE.compute_AD_source_term_operand_for_FD(GRHD.sqrtgammaDET,betaU,alpha,psi6Phi,AD)
    GRFFE.compute_psi6Phi_rhs_flux_term_operand(gammaDD,GRHD.sqrtgammaDET,betaU,alpha,AD,psi6Phi)

    parens_to_print = [\
                       lhrh(lhs=gri.gfaccess("auxevol_gfs","AevolParen"),rhs=GRFFE.AevolParen),\
                       lhrh(lhs=gri.gfaccess("auxevol_gfs","PhievolParenU0"),rhs=GRFFE.PhievolParenU[0]),\
                       lhrh(lhs=gri.gfaccess("auxevol_gfs","PhievolParenU1"),rhs=GRFFE.PhievolParenU[1]),\
                       lhrh(lhs=gri.gfaccess("auxevol_gfs","PhievolParenU2"),rhs=GRFFE.PhievolParenU[2]),\
                      ]

    subdir = "RHSs"
    cmd.mkdir(os.path.join(out_dir, subdir))
    desc = "Calculate quantities to be finite-differenced for the GRFFE RHSs"
    name = "calculate_AD_gauge_term_psi6Phi_flux_term_for_RHSs"
    outCfunction(
        outfile  = os.path.join(out_dir,subdir,name+".h"), desc=desc, name=name,
        params   ="const paramstruct *restrict params,const REAL *restrict in_gfs,REAL *restrict auxevol_gfs",
        body     = fin.FD_outputC("returnstring",parens_to_print,params=outCparams),
        loopopts ="AllPoints",
        rel_path_to_Cparams=os.path.join("../"))

    xi_damping = par.Cparameters("REAL",thismodule,"xi_damping",0.1)
    GRFFE.compute_psi6Phi_rhs_damping_term(alpha,psi6Phi,xi_damping)

    AevolParen_dD = ixp.declarerank1("AevolParen_dD",DIM=3)
    PhievolParenU_dD = ixp.declarerank2("PhievolParenU_dD","nosym",DIM=3)

    A_rhsD = ixp.zerorank1()
    psi6Phi_rhs = GRFFE.psi6Phi_damping

    for i in range(3):
        A_rhsD[i] += -AevolParen_dD[i]
        psi6Phi_rhs += -PhievolParenU_dD[i][i]

    # Add Kreiss-Oliger dissipation to the GRFFE RHSs:
#     psi6Phi_dKOD = ixp.declarerank1("psi6Phi_dKOD")
#     AD_dKOD    = ixp.declarerank2("AD_dKOD","nosym")
#     for i in range(3):
#         psi6Phi_rhs += diss_strength*psi6Phi_dKOD[i]*rfm.ReU[i] # ReU[i] = 1/scalefactor_orthog_funcform[i]
#         for j in range(3):
#             A_rhsD[j] += diss_strength*AD_dKOD[j][i]*rfm.ReU[i] # ReU[i] = 1/scalefactor_orthog_funcform[i]

    RHSs_to_print = [\
                     lhrh(lhs=gri.gfaccess("rhs_gfs","AD0"),rhs=A_rhsD[0]),\
                     lhrh(lhs=gri.gfaccess("rhs_gfs","AD1"),rhs=A_rhsD[1]),\
                     lhrh(lhs=gri.gfaccess("rhs_gfs","AD2"),rhs=A_rhsD[2]),\
                     lhrh(lhs=gri.gfaccess("rhs_gfs","psi6Phi"),rhs=psi6Phi_rhs),\
                    ]

    desc = "Calculate AD gauge term and psi6Phi RHSs"
    name = "calculate_AD_gauge_psi6Phi_RHSs"
    source_Ccode = outCfunction(
        outfile  = "returnstring", desc=desc, name=name,
        params   ="const paramstruct *params,const REAL *in_gfs,const REAL *auxevol_gfs,REAL *rhs_gfs",
        body     = fin.FD_outputC("returnstring",RHSs_to_print,params=outCparams),
        loopopts ="InteriorPoints",
        rel_path_to_Cparams=os.path.join("../")).replace("= NGHOSTS","= NGHOSTS_A2B").replace("NGHOSTS+Nxx0","Nxx_plus_2NGHOSTS0-NGHOSTS_A2B").replace("NGHOSTS+Nxx1","Nxx_plus_2NGHOSTS1-NGHOSTS_A2B").replace("NGHOSTS+Nxx2","Nxx_plus_2NGHOSTS2-NGHOSTS_A2B")
    # Note the above .replace() functions. These serve to expand the loop range into the ghostzones, since
    # the second-order FD needs fewer than some other algorithms we use do.
    with open(os.path.join(out_dir,subdir,name+".h"),"w") as file:
        file.write(source_Ccode)

    source.write_out_functions_for_StildeD_source_term(os.path.join(out_dir,subdir),outCparams,gammaDD,betaU,alpha,
                                                       ValenciavU,BU,sqrt4pi)

    subdir = "FCVAL"
    cmd.mkdir(os.path.join(out_dir, subdir))
    FCVAL.GiRaFFE_NRPy_FCVAL(os.path.join(out_dir,subdir))

    subdir = "PPM"
    cmd.mkdir(os.path.join(out_dir, subdir))
    PPM.GiRaFFE_NRPy_PPM(os.path.join(out_dir,subdir))


    # We will pass values of the gridfunction on the cell faces into the function. This requires us
    # to declare them as C parameters in NRPy+. We will denote this with the _face infix/suffix.
    alpha_face = gri.register_gridfunctions("AUXEVOL","alpha_face")
    gamma_faceDD = ixp.register_gridfunctions_for_single_rank2("AUXEVOL","gamma_faceDD","sym01")
    beta_faceU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","beta_faceU")

    # We'll need some more gridfunctions, now, to represent the reconstructions of BU and ValenciavU
    # on the right and left faces
    Valenciav_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","Valenciav_rU",DIM=3)
    B_rU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","B_rU",DIM=3)
    Valenciav_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","Valenciav_lU",DIM=3)
    B_lU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL","B_lU",DIM=3)

    subdir = "RHSs"
    Af.GiRaFFE_NRPy_Afield_flux(os.path.join(out_dir,subdir))

    ixp.register_gridfunctions_for_single_rank1("AUXEVOL","Stilde_flux_HLLED")

    Sf.generate_C_code_for_Stilde_flux(os.path.join(out_dir,subdir), True, alpha_face,gamma_faceDD,beta_faceU,
                                       Valenciav_rU,B_rU,Valenciav_lU,B_lU,sqrt4pi)

    subdir = "boundary_conditions"
    cmd.mkdir(os.path.join(out_dir,subdir))
    BC.GiRaFFE_NRPy_BCs(os.path.join(out_dir,subdir))

    subdir = "A2B"
    cmd.mkdir(os.path.join(out_dir,subdir))
    A2B.GiRaFFE_NRPy_A2B(os.path.join(out_dir,subdir),gammaDD,AD,BU)

    C2P_P2C.GiRaFFE_NRPy_C2P(StildeD,BU,gammaDD,betaU,alpha)

    values_to_print = [
                       lhrh(lhs=gri.gfaccess("in_gfs","StildeD0"),rhs=C2P_P2C.outStildeD[0]),
                       lhrh(lhs=gri.gfaccess("in_gfs","StildeD1"),rhs=C2P_P2C.outStildeD[1]),
                       lhrh(lhs=gri.gfaccess("in_gfs","StildeD2"),rhs=C2P_P2C.outStildeD[2]),
                       lhrh(lhs=gri.gfaccess("auxevol_gfs","ValenciavU0"),rhs=C2P_P2C.ValenciavU[0]),
                       lhrh(lhs=gri.gfaccess("auxevol_gfs","ValenciavU1"),rhs=C2P_P2C.ValenciavU[1]),
                       lhrh(lhs=gri.gfaccess("auxevol_gfs","ValenciavU2"),rhs=C2P_P2C.ValenciavU[2])
                      ]

    subdir = "C2P"
    cmd.mkdir(os.path.join(out_dir,subdir))
    desc = "Apply fixes to \tilde{S}_i and recompute the velocity to match with current sheet prescription."
    name = "GiRaFFE_NRPy_cons_to_prims"
    outCfunction(
        outfile  = os.path.join(out_dir,subdir,name+".h"), desc=desc, name=name,
        params   ="const paramstruct *params,REAL *xx[3],REAL *auxevol_gfs,REAL *in_gfs",
        body     = fin.FD_outputC("returnstring",values_to_print,params=outCparams),
        loopopts ="AllPoints,Read_xxs",
        rel_path_to_Cparams=os.path.join("../"))

    C2P_P2C.GiRaFFE_NRPy_P2C(gammaDD,betaU,alpha,  ValenciavU,BU, sqrt4pi)

    values_to_print = [
                       lhrh(lhs=gri.gfaccess("in_gfs","StildeD0"),rhs=C2P_P2C.StildeD[0]),
                       lhrh(lhs=gri.gfaccess("in_gfs","StildeD1"),rhs=C2P_P2C.StildeD[1]),
                       lhrh(lhs=gri.gfaccess("in_gfs","StildeD2"),rhs=C2P_P2C.StildeD[2]),
                      ]

    desc = "Recompute StildeD after current sheet fix to Valencia 3-velocity to ensure consistency between conservative & primitive variables."
    name = "GiRaFFE_NRPy_prims_to_cons"
    outCfunction(
        outfile  = os.path.join(out_dir,subdir,name+".h"), desc=desc, name=name,
        params   ="const paramstruct *params,REAL *auxevol_gfs,REAL *in_gfs",
        body     = fin.FD_outputC("returnstring",values_to_print,params=outCparams),
        loopopts ="AllPoints",
        rel_path_to_Cparams=os.path.join("../"))

    # Write out the main driver itself:
    with open(os.path.join(out_dir,"GiRaFFE_NRPy_Main_Driver.h"),"w") as file:
        file.write(r"""// Structure to track ghostzones for PPM:
typedef struct __gf_and_gz_struct__ {
  REAL *gf;
  int gz_lo[4],gz_hi[4];
} gf_and_gz_struct;
// Some additional constants needed for PPM:
const int VX=0,VY=1,VZ=2,BX=3,BY=4,BZ=5;
const int NUM_RECONSTRUCT_GFS = 6;

// Include ALL functions needed for evolution
#include "RHSs/calculate_AD_gauge_term_psi6Phi_flux_term_for_RHSs.h"
#include "RHSs/calculate_AD_gauge_psi6Phi_RHSs.h"
#include "PPM/reconstruct_set_of_prims_PPM_GRFFE_NRPy.c"
#include "FCVAL/interpolate_metric_gfs_to_cell_faces.h"
#include "RHSs/calculate_StildeD0_source_term.h"
#include "RHSs/calculate_StildeD1_source_term.h"
#include "RHSs/calculate_StildeD2_source_term.h"
#include "RHSs/calculate_E_field_flat_all_in_one.h"
#include "RHSs/calculate_Stilde_flux_D0.h"
#include "RHSs/calculate_Stilde_flux_D1.h"
#include "RHSs/calculate_Stilde_flux_D2.h"
#include "RHSs/calculate_Stilde_rhsD.h"
#include "boundary_conditions/GiRaFFE_boundary_conditions.h"
#include "A2B/driver_AtoB.h"
#include "C2P/GiRaFFE_NRPy_cons_to_prims.h"
#include "C2P/GiRaFFE_NRPy_prims_to_cons.h"

void GiRaFFE_NRPy_RHSs(const paramstruct *restrict params,REAL *restrict auxevol_gfs,const REAL *restrict in_gfs,REAL *restrict rhs_gfs) {
#include "set_Cparameters.h"
    // First thing's first: initialize the RHSs to zero!
#pragma omp parallel for
    for(int ii=0;ii<Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2*NUM_EVOL_GFS;ii++) {
        rhs_gfs[ii] = 0.0;
    }
    // Next calculate the easier source terms that don't require flux directions
    // This will also reset the RHSs for each gf at each new timestep.
    calculate_AD_gauge_term_psi6Phi_flux_term_for_RHSs(params,in_gfs,auxevol_gfs);
    calculate_AD_gauge_psi6Phi_RHSs(params,in_gfs,auxevol_gfs,rhs_gfs);

    // Now, we set up a bunch of structs of pointers to properly guide the PPM algorithm.
    // They also count the number of ghostzones available.
    gf_and_gz_struct in_prims[NUM_RECONSTRUCT_GFS], out_prims_r[NUM_RECONSTRUCT_GFS], out_prims_l[NUM_RECONSTRUCT_GFS];
    int which_prims_to_reconstruct[NUM_RECONSTRUCT_GFS],num_prims_to_reconstruct;
    const int Nxxp2NG012 = Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2;

    REAL *temporary = auxevol_gfs + Nxxp2NG012*AEVOLPARENGF; //We're not using this anymore
    // This sets pointers to the portion of auxevol_gfs containing the relevant gridfunction.
    int ww=0;
    in_prims[ww].gf      = auxevol_gfs + Nxxp2NG012*VALENCIAVU0GF;
      out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RU0GF;
      out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LU0GF;
    ww++;
    in_prims[ww].gf      = auxevol_gfs + Nxxp2NG012*VALENCIAVU1GF;
      out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RU1GF;
      out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LU1GF;
    ww++;
    in_prims[ww].gf      = auxevol_gfs + Nxxp2NG012*VALENCIAVU2GF;
      out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RU2GF;
      out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LU2GF;
    ww++;
    in_prims[ww].gf      = auxevol_gfs + Nxxp2NG012*BU0GF;
      out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*B_RU0GF;
      out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*B_LU0GF;
    ww++;
    in_prims[ww].gf      = auxevol_gfs + Nxxp2NG012*BU1GF;
      out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*B_RU1GF;
      out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*B_LU1GF;
    ww++;
    in_prims[ww].gf      = auxevol_gfs + Nxxp2NG012*BU2GF;
      out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*B_RU2GF;
      out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*B_LU2GF;
    ww++;

    // Prims are defined AT ALL GRIDPOINTS, so we set the # of ghostzones to zero:
    for(int i=0;i<NUM_RECONSTRUCT_GFS;i++) for(int j=1;j<=3;j++) { in_prims[i].gz_lo[j]=0; in_prims[i].gz_hi[j]=0; }
    // Left/right variables are not yet defined, yet we set the # of gz's to zero by default:
    for(int i=0;i<NUM_RECONSTRUCT_GFS;i++) for(int j=1;j<=3;j++) { out_prims_r[i].gz_lo[j]=0; out_prims_r[i].gz_hi[j]=0; }
    for(int i=0;i<NUM_RECONSTRUCT_GFS;i++) for(int j=1;j<=3;j++) { out_prims_l[i].gz_lo[j]=0; out_prims_l[i].gz_hi[j]=0; }

    ww=0;
    which_prims_to_reconstruct[ww]=VX; ww++;
    which_prims_to_reconstruct[ww]=VY; ww++;
    which_prims_to_reconstruct[ww]=VZ; ww++;
    which_prims_to_reconstruct[ww]=BX; ww++;
    which_prims_to_reconstruct[ww]=BY; ww++;
    which_prims_to_reconstruct[ww]=BZ; ww++;
    num_prims_to_reconstruct=ww;

    // In each direction, perform the PPM reconstruction procedure.
    // Then, add the fluxes to the RHS as appropriate.
    for(int flux_dirn=0;flux_dirn<3;flux_dirn++) {
        // In each direction, interpolate the metric gfs (gamma,beta,alpha) to cell faces.
        interpolate_metric_gfs_to_cell_faces(params,auxevol_gfs,flux_dirn+1);
        // Then, reconstruct the primitive variables on the cell faces.
        // This function is housed in the file: "reconstruct_set_of_prims_PPM_GRFFE_NRPy.c"
        reconstruct_set_of_prims_PPM_GRFFE_NRPy(params, auxevol_gfs, flux_dirn+1, num_prims_to_reconstruct,
                                                which_prims_to_reconstruct, in_prims, out_prims_r, out_prims_l, temporary);
        // For example, if flux_dirn==0, then at gamma_faceDD00(i,j,k) represents gamma_{xx}
        // at (i-1/2,j,k), Valenciav_lU0(i,j,k) is the x-component of the velocity at (i-1/2-epsilon,j,k),
        // and Valenciav_rU0(i,j,k) is the x-component of the velocity at (i-1/2+epsilon,j,k).

        if(flux_dirn==0) {
            // Next, we calculate the source term for StildeD. Again, this also resets the rhs_gfs array at
            // each new timestep.
            calculate_StildeD0_source_term(params,auxevol_gfs,rhs_gfs);
            // Now, compute the electric field on each face of a cell and add it to the RHSs as appropriate
            //calculate_E_field_D0_right(params,auxevol_gfs,rhs_gfs);
            //calculate_E_field_D0_left(params,auxevol_gfs,rhs_gfs);
            // Finally, we calculate the flux of StildeD and add the appropriate finite-differences
            // to the RHSs.
            calculate_Stilde_flux_D0(params,auxevol_gfs,rhs_gfs);
        }
        else if(flux_dirn==1) {
            calculate_StildeD1_source_term(params,auxevol_gfs,rhs_gfs);
            //calculate_E_field_D1_right(params,auxevol_gfs,rhs_gfs);
            //calculate_E_field_D1_left(params,auxevol_gfs,rhs_gfs);
            calculate_Stilde_flux_D1(params,auxevol_gfs,rhs_gfs);
        }
        else {
            calculate_StildeD2_source_term(params,auxevol_gfs,rhs_gfs);
            //calculate_E_field_D2_right(params,auxevol_gfs,rhs_gfs);
            //calculate_E_field_D2_left(params,auxevol_gfs,rhs_gfs);
            calculate_Stilde_flux_D2(params,auxevol_gfs,rhs_gfs);
        }
        calculate_Stilde_rhsD(flux_dirn+1,params,auxevol_gfs,rhs_gfs);
        for(int count=0;count<=1;count++) {
            // This function is written to be general, using notation that matches the forward permutation added to AD2,
            // i.e., [F_HLL^x(B^y)]_z corresponding to flux_dirn=0, count=1.
            // The SIGN parameter is necessary because
            // -E_z(x_i,y_j,z_k) = 0.25 ( [F_HLL^x(B^y)]_z(i+1/2,j,k)+[F_HLL^x(B^y)]_z(i-1/2,j,k)
            //                           -[F_HLL^y(B^x)]_z(i,j+1/2,k)-[F_HLL^y(B^x)]_z(i,j-1/2,k) )
            // Note the negative signs on the reversed permutation terms!

            // By cyclically permuting with flux_dirn, we
            // get contributions to the other components, and by incrementing count, we get the backward permutations:
            // Let's suppose flux_dirn = 0. Then we will need to update Ay (count=0) and Az (count=1):
            //     flux_dirn=count=0 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (0+1+0)%3=AD1GF <- Updating Ay!
            //        (flux_dirn)%3 = (0)%3 = 0               Vx
            //        (flux_dirn-count+2)%3 = (0-0+2)%3 = 2   Vz .  Inputs Vx, Vz -> SIGN = -1 ; 2.0*((REAL)count)-1.0=-1 check!
            //     flux_dirn=0,count=1 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (0+1+1)%3=AD2GF <- Updating Az!
            //        (flux_dirn)%3 = (0)%3 = 0               Vx
            //        (flux_dirn-count+2)%3 = (0-1+2)%3 = 1   Vy .  Inputs Vx, Vy -> SIGN = +1 ; 2.0*((REAL)count)-1.0=2-1=+1 check!
            // Let's suppose flux_dirn = 1. Then we will need to update Az (count=0) and Ax (count=1):
            //     flux_dirn=1,count=0 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (1+1+0)%3=AD2GF <- Updating Az!
            //        (flux_dirn)%3 = (1)%3 = 1               Vy
            //        (flux_dirn-count+2)%3 = (1-0+2)%3 = 0   Vx .  Inputs Vy, Vx -> SIGN = -1 ; 2.0*((REAL)count)-1.0=-1 check!
            //     flux_dirn=count=1 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (1+1+1)%3=AD0GF <- Updating Ax!
            //        (flux_dirn)%3 = (1)%3 = 1               Vy
            //        (flux_dirn-count+2)%3 = (1-1+2)%3 = 2   Vz .  Inputs Vy, Vz -> SIGN = +1 ; 2.0*((REAL)count)-1.0=2-1=+1 check!
            // Let's suppose flux_dirn = 2. Then we will need to update Ax (count=0) and Ay (count=1):
            //     flux_dirn=2,count=0 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (2+1+0)%3=AD0GF <- Updating Ax!
            //        (flux_dirn)%3 = (2)%3 = 2               Vz
            //        (flux_dirn-count+2)%3 = (2-0+2)%3 = 1   Vy .  Inputs Vz, Vy -> SIGN = -1 ; 2.0*((REAL)count)-1.0=-1 check!
            //     flux_dirn=2,count=1 -> AD0GF+(flux_dirn+1+count)%3 = AD0GF + (2+1+1)%3=AD1GF <- Updating Ay!
            //        (flux_dirn)%3 = (2)%3 = 2               Vz
            //        (flux_dirn-count+2)%3 = (2-1+2)%3 = 0   Vx .  Inputs Vz, Vx -> SIGN = +1 ; 2.0*((REAL)count)-1.0=2-1=+1 check!
            calculate_E_field_flat_all_in_one(params,
              &auxevol_gfs[IDX4ptS(VALENCIAV_RU0GF+(flux_dirn)%3, 0)],&auxevol_gfs[IDX4ptS(VALENCIAV_RU0GF+(flux_dirn-count+2)%3, 0)],
              &auxevol_gfs[IDX4ptS(VALENCIAV_LU0GF+(flux_dirn)%3, 0)],&auxevol_gfs[IDX4ptS(VALENCIAV_LU0GF+(flux_dirn-count+2)%3, 0)],
              &auxevol_gfs[IDX4ptS(B_RU0GF        +(flux_dirn)%3, 0)],&auxevol_gfs[IDX4ptS(B_RU0GF        +(flux_dirn-count+2)%3, 0)],
              &auxevol_gfs[IDX4ptS(B_LU0GF        +(flux_dirn)%3, 0)],&auxevol_gfs[IDX4ptS(B_LU0GF        +(flux_dirn-count+2)%3, 0)],
              &auxevol_gfs[IDX4ptS(B_RU0GF        +(flux_dirn-count+2)%3, 0)],
              &auxevol_gfs[IDX4ptS(B_LU0GF        +(flux_dirn-count+2)%3, 0)],
              &auxevol_gfs[IDX4ptS(GAMMA_FACEDD00GF,0)],&auxevol_gfs[IDX4ptS(GAMMA_FACEDD01GF,0)],&auxevol_gfs[IDX4ptS(GAMMA_FACEDD02GF,0)],
              &auxevol_gfs[IDX4ptS(GAMMA_FACEDD11GF,0)],&auxevol_gfs[IDX4ptS(GAMMA_FACEDD12GF,0)],&auxevol_gfs[IDX4ptS(GAMMA_FACEDD22GF,0)],
              &auxevol_gfs[IDX4ptS(BETA_FACEU0GF+flux_dirn,0)],&auxevol_gfs[IDX4ptS(BETA_FACEU0GF+(flux_dirn-count+2)%3,0)],&auxevol_gfs[IDX4ptS(ALPHA_FACEGF,0)],
              &rhs_gfs[IDX4ptS(AD0GF+(flux_dirn+1+count)%3,0)], 2.0*((REAL)count)-1.0, flux_dirn);
        }
    }
}

void GiRaFFE_NRPy_post_step(const paramstruct *restrict params,REAL *xx[3],REAL *restrict auxevol_gfs,REAL *restrict evol_gfs,const int n) {
    // First, apply BCs to AD and psi6Phi. Then calculate BU from AD
    apply_bcs_potential(params,evol_gfs);
    driver_A_to_B(params,evol_gfs,auxevol_gfs);
    //override_BU_with_old_GiRaFFE(params,auxevol_gfs,n);
    // Apply fixes to StildeD, then recompute the velocity at the new timestep.
    // Apply the current sheet prescription to the velocities
    GiRaFFE_NRPy_cons_to_prims(params,xx,auxevol_gfs,evol_gfs);
    // Then, recompute StildeD to be consistent with the new velocities
    //GiRaFFE_NRPy_prims_to_cons(params,auxevol_gfs,evol_gfs);
    // Finally, apply outflow boundary conditions to the velocities.
    apply_bcs_velocity(params,auxevol_gfs);
}
""")
Exemple #18
0
# 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 :
Exemple #19
0
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 TYPE not in ('double', ''):
            print(
                "SIMD output currently only supports double precision or typeless. Sorry!"
            )
            sys.exit(1)
        if TYPE == "double":
            TYPE = "REAL_SIMD_ARRAY"

    # 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
    sympyexpr = sympyexpr[:]  # pass-by-value (copy list)

    # 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_RATIONAL_decls = RATIONAL_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 = []

        varprefix = '' if outCparams.CSE_varprefix == 'tmp' else outCparams.CSE_varprefix
        if outCparams.CSE_preprocess == "True" or outCparams.SIMD_enable == "True":
            # If CSE_preprocess == True, then perform partial factorization
            # If SIMD_enable == True, then declare _NegativeOne_ in preprocessing
            factor_negative = eval(outCparams.SIMD_enable) and eval(
                outCparams.SIMD_find_more_subs)
            sympyexpr, map_sym_to_rat = cse_preprocess(sympyexpr,prefix=varprefix,\
                declare=eval(outCparams.SIMD_enable),negative=factor_negative,factor=eval(outCparams.CSE_preprocess))
            for v in map_sym_to_rat:
                p, q = float(map_sym_to_rat[v].p), float(map_sym_to_rat[v].q)
                if outCparams.SIMD_enable == "False":
                    RATIONAL_decls += outCparams.preindent + indent + 'const double ' + str(
                        v) + ' = '
                    # Since Integer is a subclass of Rational in SymPy, we need only check whether
                    # the denominator q = 1 to determine if a rational is an integer.
                    if q != 1: RATIONAL_decls += str(p) + '/' + str(q) + ';\n'
                    else: RATIONAL_decls += str(p) + ';\n'

        sympy_version = sp.__version__.replace('rc', '...').replace('b', '...')
        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 < 3):
            print('Warning: SymPy version', sympy_version,
                  'does not support CSE postprocessing.')
            CSE_results = sp.cse(sympyexpr, sp.numbered_symbols(outCparams.CSE_varprefix + '_'), \
                                 order=outCparams.CSE_sorting)
        else:
            CSE_results = cse_postprocess(sp.cse(sympyexpr, sp.numbered_symbols(outCparams.CSE_varprefix + '_'), \
                                                 order=outCparams.CSE_sorting))

        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],map_sym_to_rat,varprefix,outCparams.SIMD_find_more_FMAsFMSs)) + ";\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,map_sym_to_rat,varprefix,outCparams.SIMD_find_more_FMAsFMSs)) + ";\n"
            else:
                outstring += outtypestring + ccode_postproc(
                    sp.ccode(result,
                             output_varname_str[i],
                             user_functions=custom_functions_for_SymPy_ccode)
                ) + "\n"
        # 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).
        if outCparams.SIMD_enable == "True":
            for v in map_sym_to_rat:
                p, q = float(map_sym_to_rat[v].p), float(map_sym_to_rat[v].q)
                SIMD_const_varnms.extend([str(v)])
                if q != 1: SIMD_const_values.extend([str(p) + '/' + str(q)])
                else: SIMD_const_values.extend([str(p)])

        # 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_RATIONAL_decls += outCparams.preindent + indent + SIMD_const_varnms[
                        i] + " = " + SIMD_const_values[i] + ";"
                else:
                    SIMD_RATIONAL_decls += outCparams.preindent + indent + "const double " + "tmp" + SIMD_const_varnms[
                        i] + " = " + SIMD_const_values[i] + ";\n"
                    SIMD_RATIONAL_decls += outCparams.preindent + indent + "const REAL_SIMD_ARRAY " + SIMD_const_varnms[
                        i] + " = ConstSIMD(" + "tmp" + SIMD_const_varnms[
                            i] + ");\n"
                SIMD_RATIONAL_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 + RATIONAL_decls + SIMD_RATIONAL_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 + "\"")
Exemple #20
0
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])
def GiRaFFE_NRPy_C2P(StildeD, BU, gammaDD, gammaUU, gammadet, betaU, alpha):
    sqrtgammadet = sp.sqrt(gammadet)
    BtildeU = ixp.zerorank1()
    for i in range(3):
        # \tilde{B}^i = B^i \sqrt{\gamma}
        BtildeU[i] = sqrtgammadet * BU[i]

    BtildeD = ixp.zerorank1()
    for i in range(3):
        for j in range(3):
            BtildeD[j] += gammaDD[i][j] * BtildeU[i]

    Btilde2 = sp.sympify(0)
    for i in range(3):
        Btilde2 += BtildeU[i] * BtildeD[i]

    global outStildeD
    outStildeD = StildeD
    # Then, enforce the orthogonality:
    if par.parval_from_str("enforce_orthogonality_StildeD_BtildeU"):
        for i in range(3):
            for j in range(3):
                # {\tilde S}_i = {\tilde S}_i - ({\tilde S}_j {\tilde B}^j) {\tilde B}_i/{\tilde B}^2
                outStildeD[i] -= (StildeD[j] *
                                  BtildeU[j]) * BtildeD[i] / Btilde2

    # Calculate \tilde{S}^2:
    Stilde2 = sp.sympify(0)
    for i in range(3):
        for j in range(3):
            Stilde2 += gammaUU[i][j] * outStildeD[i] * outStildeD[j]

    # First we need to compute the factor f:
    # f = \sqrt{(1-\Gamma_{\max}^{-2}){\tilde B}^4/(16 \pi^2 \gamma {\tilde S}^2)}
    speed_limit_factor = sp.sqrt((1-GAMMA_SPEED_LIMIT**(-2))\
                                 *Btilde2*Btilde2/(16*M_PI*M_PI*sqrtgammadet*Stilde2*Stilde2))

    def min_noif(a, b):
        # This returns the minimum of a and b
        # If a>b, then we get 0.5*(a+b-a+b) = b
        # If b>a, then we get 0.5*(a+b+a-b) = a
        return sp.Rational(1, 2) * (a + b - nrpyAbs(a - b))

    # Calculate B^2
    B2 = sp.sympify(0)
    for i in range(3):
        for j in range(3):
            B2 += gammaDD[i][j] * BU[i] * BU[j]

    # Enforce the speed limit on StildeD:
    if par.parval_from_str("enforce_speed_limit_StildeD"):
        for i in range(3):
            outStildeD[i] *= min_noif(1, speed_limit_factor)

    global ValenciavU
    ValenciavU = ixp.zerorank1()
    if par.parval_from_str(
            "enforce_orthogonality_StildeD_BtildeU") or par.parval_from_str(
                "enforce_speed_limit_StildeD"):
        # Recompute 3-velocity:
        for i in range(3):
            for j in range(3):
                # \bar{v}^i = 4 \pi \gamma^{ij} {\tilde S}_j / (\sqrt{\gamma} B^2)
                ValenciavU[i] = sp.sympify(
                    4.0) * M_PI * gammaUU[i][j] * StildeD[j] / (sqrtgammadet *
                                                                B2)

    # We will use once more the trick from above with min and max without if. However, we we'll need a function
    # that returns either 0 or 1, so as to choose between two otherwise mathetmatically unrelated branches.
    def max_normal0(a):
        # If a>0, return 1. Otherwise, return 0. This defines a 'less than' branch.
        # WILL BREAK if a = 0.
        return (a + nrpyAbs(a)) / (2 * a)

    def min_normal0(a):
        # If a>0, return 1. Otherwise, return 0. This defines a 'greater than' branch.
        # WILL BREAK if a = 0.
        return (a - nrpyAbs(a)) / (2 * a)

    # This number determines how far away (in grid points) we will apply the fix.
    grid_points_from_z_plane = par.Cparameters("REAL", thismodule,
                                               "grid_points_from_z_plane", 4.0)
    # Set the Cartesian normal vector. This can be expanded later to arbitrary sheets and coordinate systems.
    nU = ixp.zerorank1()
    nU[2] = 1
    # Lower the index, as usual:
    nD = ixp.zerorank1()
    for i in range(3):
        for j in range(3):
            nD[i] = gammaDD[i][j] * nU[j]

    if par.parval_from_str("enforce_current_sheet_prescription"):
        # Calculate the drift velocity
        driftvU = ixp.declarerank1("driftvU")

        inner_product = sp.sympify(0)
        for i in range(3):
            inner_product += driftvU[i] * nD[
                i]  # This is the portion of the drift velocity normal to the z plane
            # In flat space, this is just v^z
        # We'll use a sympy utility to solve for v^z. This should make it easier to generalize later
        newdriftvU2 = sp.solve(inner_product, driftvU[2])
        newdriftvU2 = newdriftvU2[0]  # In flat space this reduces to v^z=0
        for i in range(3):
            # Now, we substitute drift velocity in terms of our preferred Valencia velocity
            newdriftvU2 = newdriftvU2.subs(driftvU[i],
                                           alpha * ValenciavU[i] - betaU[i])
        # Now that we have the z component, it's time to substitute its Valencia form in.
        # Remember, we only do this if abs(z) < (k+0.01)*dz. Note that we add 0.01; this helps
        # avoid floating point errors and division by zero. This is the same as abs(z) - (k+0.01)*dz<0
        boundary = nrpyAbs(rfm.xx[2]) - (grid_points_from_z_plane +
                                         sp.sympify(0.01)) * gri.dxx[2]
        ValenciavU[2] = max_normal0(boundary)*(newdriftvU2+betaU[2])/alpha \
                         + min_normal0(boundary)*ValenciavU[2]
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])
Exemple #23
0
def GiRaFFEfood_HO_Aligned_Rotator():
    r = rfm.xxSph[0]
    varpi = sp.sqrt(rfm.xxCart[0]**2 + rfm.xxCart[1]**2)

    mu = B_p_aligned_rotator * R_NS_aligned_rotator**3 / 2

    ASphD = ixp.zerorank1()

    ASphD[2] = mu * varpi**2 / (
        r**3)  # The other components were already declared to be 0.

    # <a id='step3'></a>
    #
    # ### Step 3: Use the Jacobian matrix to transform the vectors to Cartesian coordinates.
    # $$\label{step3}$$
    #
    # \[Back to [top](#top)\]
    #
    # Now, we will use the coordinate transformation definitions provided by reference_metric.py to build the Jacobian
    # $$
    # \frac{\partial x_{\rm Sph}^j}{\partial x_{\rm Cart}^i},
    # $$
    # where $x_{\rm Sph}^j \in \{r,\theta,\phi\}$ and $x_{\rm Cart}^i \in \{x,y,z\}$. We would normally compute its inverse, but since none of the quantities we need to transform have upper indices, it is not necessary. Then, since $A_i$ and has one lower index, it will need to be multiplied by the Jacobian:
    #
    # $$
    # A_i^{\rm Cart} = A_j^{\rm Sph} \frac{\partial x_{\rm Sph}^j}{\partial x_{\rm Cart}^i},
    # $$

    # Step 3: Use the Jacobian matrix to transform the vectors to Cartesian coordinates.
    drrefmetric__dx_0UDmatrix = sp.Matrix([[
        sp.diff(rfm.xxSph[0], rfm.xx[0]),
        sp.diff(rfm.xxSph[0], rfm.xx[1]),
        sp.diff(rfm.xxSph[0], rfm.xx[2])
    ],
                                           [
                                               sp.diff(rfm.xxSph[1],
                                                       rfm.xx[0]),
                                               sp.diff(rfm.xxSph[1],
                                                       rfm.xx[1]),
                                               sp.diff(rfm.xxSph[1], rfm.xx[2])
                                           ],
                                           [
                                               sp.diff(rfm.xxSph[2],
                                                       rfm.xx[0]),
                                               sp.diff(rfm.xxSph[2],
                                                       rfm.xx[1]),
                                               sp.diff(rfm.xxSph[2], rfm.xx[2])
                                           ]])
    #dx__drrefmetric_0UDmatrix = drrefmetric__dx_0UDmatrix.inv() # We don't actually need this in this case.

    global AD
    AD = ixp.register_gridfunctions_for_single_rank1("EVOL", "AD")
    for i in range(DIM):
        for j in range(DIM):
            AD[i] = drrefmetric__dx_0UDmatrix[(j, i)] * ASphD[j]

    # <a id='step4'></a>
    #
    # ### Step 4: Calculate $v^i$
    # $$\label{step4}$$
    #
    # \[Back to [top](#top)\]
    #
    # Here, we will calculate the drift velocity $v^i = \Omega \textbf{e}_z \times \textbf{r} = [ijk] \Omega \textbf{e}^j_z x^k$, where $[ijk]$ is the Levi-Civita permutation symbol and $\textbf{e}^i_z = (0,0,1)$. Conveniently, in flat space, the drift velocity reduces to the Valencia velocity because $\alpha = 1$ and $\beta^i = 0$.

    # Step 4: Calculate v^i
    # Here, we build the Levi-Civita tensor from the Levi-Civita symbol.
    import WeylScal4NRPy.WeylScalars_Cartesian as weyl
    LeviCivitaSymbolDDD = weyl.define_LeviCivitaSymbol_rank3()

    Omega_aligned_rotator = par.Cparameters(
        "REAL", thismodule,
        "Omega_aligned_rotator")  # The angular velocity of the NS
    unit_zU = ixp.zerorank1()
    unit_zU[2] = 1

    global ValenciavU
    ValenciavU = ixp.zerorank1()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                ValenciavU[i] += LeviCivitaSymbolDDD[i][j][
                    k] * Omega_aligned_rotator * unit_zU[j] * rfm.xx[k]
Exemple #24
0
nrpy_dir_path = os.path.join("..")
if nrpy_dir_path not in sys.path:
    sys.path.append(nrpy_dir_path)

from outputC import outCfunction, lhrh, add_to_Cfunction_dict, outC_function_outdir_dict, outC_function_dict, outC_function_prototype_dict, outC_function_master_list, outC_function_element # NRPy+: Core C code output module
import finite_difference as fin  # NRPy+: Finite difference C code generation module
import NRPy_param_funcs as par   # NRPy+: Parameter interface
import grid as gri               # NRPy+: Functions having to do with numerical grids
import loop as lp                # NRPy+: Generate C code loops
import indexedexp as ixp         # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm   # NRPy+: Reference metric support
import cmdline_helper as cmd     # NRPy+: Multi-platform Python command-line interface

thismodule = __name__

par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER",2)

out_dir = os.path.join("GiRaFFE_standalone_Ccodes")
cmd.mkdir(out_dir)

CoordSystem = "Cartesian"

par.set_parval_from_str("reference_metric::CoordSystem",CoordSystem)
rfm.reference_metric() # Create ReU, ReDD needed for rescaling B-L initial data, generating BSSN RHSs, etc.

outCparams = "outCverbose=False,CSE_sorting=none"

xi_damping = par.Cparameters("REAL",thismodule,"xi_damping",0.1)

# Default Kreiss-Oliger dissipation strength
default_KO_strength = 0.1
# * 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 sys  # Standard Python module for multiplatform OS-level functions
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:
Exemple #26
0
    def dfdr_function(fd_order):
        # function to write c code to calculate dfdr term in Sommerfeld boundary condition

        # Read what # of dimensions being usded
        DIM = par.parval_from_str("grid::DIM")

        # Set up the chosen reference metric from chosen coordinate system, set within NRPy+
        CoordSystem = par.parval_from_str("reference_metric::CoordSystem")
        rfm.reference_metric()

        # Simplifying the results make them easier to interpret.
        do_simplify = True
        if "Sinh" in CoordSystem:
            # Simplification takes too long on Sinh* coordinate systems
            do_simplify = False

        # Construct Jacobian matrix, output Jac_dUSph_dDrfmUD[i][j] = \partial x_{Sph}^i / \partial x^j:
        Jac_dUSph_dDrfmUD = ixp.zerorank2()
        for i in range(3):
            for j in range(3):
                Jac_dUSph_dDrfmUD[i][j] = sp.diff(rfm.xxSph[i],rfm.xx[j])

        # Invert Jacobian matrix, output to Jac_dUrfm_dDSphUD.
        Jac_dUrfm_dDSphUD, dummyDET = ixp.generic_matrix_inverter3x3(Jac_dUSph_dDrfmUD)

        # Jac_dUrfm_dDSphUD[i][0] stores \partial x^i / \partial r
        if do_simplify:
            for i in range(3):
                Jac_dUrfm_dDSphUD[i][0] = sp.simplify(Jac_dUrfm_dDSphUD[i][0])

        # Declare \partial_i f, which is actually computed later on
        fdD = ixp.declarerank1("fdD") # = [fdD0, fdD1, fdD2]
        contraction = sp.sympify(0)
        for i in range(3):
            contraction += fdD[i]*Jac_dUrfm_dDSphUD[i][0]

        if do_simplify:
            contraction = sp.simplify(contraction)

        r_str_and_contraction_str = outputC([rfm.xxSph[0],contraction],
                 ["*_r","*_partial_i_f"],filename="returnstring",params="includebraces=False")


        def gen_central_2oFD_stencil_str(intdirn):
            if intdirn == 0:
                return "(gfs[IDX4S(which_gf,i0+1,i1,i2)]-gfs[IDX4S(which_gf,i0-1,i1,i2)])*0.5"  # Does not include the 1/dx multiplication
            if intdirn == 1:
                return "(gfs[IDX4S(which_gf,i0,i1+1,i2)]-gfs[IDX4S(which_gf,i0,i1-1,i2)])*0.5"  # Does not include the 1/dy multiplication
            return "(gfs[IDX4S(which_gf,i0,i1,i2+1)]-gfs[IDX4S(which_gf,i0,i1,i2-1)])*0.5"  # Does not include the 1/dz multiplication

        def gen_central_4oFD_stencil_str(intdirn):
            if intdirn == 0:
                return """(-c2*gfs[IDX4S(which_gf,i0+2,i1,i2)]
                         +c1*gfs[IDX4S(which_gf,i0+1,i1,i2)]
                         -c1*gfs[IDX4S(which_gf,i0-1,i1,i2)]
                         +c2*gfs[IDX4S(which_gf,i0-2,i1,i2)])"""  # Does not include the 1/dx multiplication
            if intdirn == 1:
                return """(-c2*gfs[IDX4S(which_gf,i0,i1+2,i2)]
                         +c1*gfs[IDX4S(which_gf,i0,i1+1,i2)]
                         -c1*gfs[IDX4S(which_gf,i0,i1-1,i2)]
                         +c2*gfs[IDX4S(which_gf,i0,i1-2,i2)])"""  # Does not include the 1/dy multiplication
            return """(-c2*gfs[IDX4S(which_gf,i0,i1,i2+2)]
                         +c1*gfs[IDX4S(which_gf,i0,i1,i2+1)]
                         -c1*gfs[IDX4S(which_gf,i0,i1,i2-1)]
                         +c2*gfs[IDX4S(which_gf,i0,i1,i2-2)])"""  # Does not include the 1/dz multiplication

        def gen_central_6oFD_stencil_str(intdirn):
            if intdirn == 0:
                return """( c3*gfs[IDX4S(which_gf,i0+3,i1,i2)]
                         -c2*gfs[IDX4S(which_gf,i0+2,i1,i2)]
                         +c1*gfs[IDX4S(which_gf,i0+1,i1,i2)]
                         -c1*gfs[IDX4S(which_gf,i0-1,i1,i2)]
                         +c2*gfs[IDX4S(which_gf,i0-2,i1,i2)]
                         -c3*gfs[IDX4S(which_gf,i0-3,i1,i2)])"""  # Does not include the 1/dx multiplication

            if intdirn == 1:
                return """( c3*gfs[IDX4S(which_gf,i0,i1+3,i2)]
                         -c2*gfs[IDX4S(which_gf,i0,i1+2,i2)]
                         +c1*gfs[IDX4S(which_gf,i0,i1+1,i2)]
                         -c1*gfs[IDX4S(which_gf,i0,i1-1,i2)]
                         +c2*gfs[IDX4S(which_gf,i0,i1-2,i2)]
                         -c3*gfs[IDX4S(which_gf,i0,i1-3,i2)])"""  # Does not include the 1/dy multiplication

            return """( c3*gfs[IDX4S(which_gf,i0,i1,i2+3)]
                         -c2*gfs[IDX4S(which_gf,i0,i1,i2+2)]
                         +c1*gfs[IDX4S(which_gf,i0,i1,i2+1)]
                         -c1*gfs[IDX4S(which_gf,i0,i1,i2-1)]
                         +c2*gfs[IDX4S(which_gf,i0,i1,i2-2)]
                         -c3*gfs[IDX4S(which_gf,i0,i1,i2-3)])"""  # Does not include the 1/dz multiplication

        def gen_central_fd_stencil_str(intdirn, fd_order):
            if fd_order==2:
                return gen_central_2oFD_stencil_str(intdirn)
            if fd_order==4:
                return gen_central_4oFD_stencil_str(intdirn)
            return gen_central_6oFD_stencil_str(intdirn)

        def output_dfdx(intdirn, fd_order):
            dirn = str(intdirn)
            dirnp1 = str((intdirn+1)%3)  # if dirn='0', then we want this to be '1'; '1' then '2'; and '2' then '0'
            dirnp2 = str((intdirn+2)%3)  # if dirn='0', then we want this to be '2'; '1' then '0'; and '2' then '1'

            preface = """
// On a +x"""+dirn+""" or -x"""+dirn+""" face, do up/down winding as appropriate:
if(abs(FACEXi["""+dirn+"""])==1 || i"""+dirn+"""+NGHOSTS >= Nxx_plus_2NGHOSTS"""+dirn+""" || i"""+dirn+"""-NGHOSTS <= 0) {
    int8_t SHIFTSTENCIL"""+dirn+""" = FACEXi["""+dirn+"""];
    if(i"""+dirn+"""+NGHOSTS >= Nxx_plus_2NGHOSTS"""+dirn+""") SHIFTSTENCIL"""+dirn+""" = -1;
    if(i"""+dirn+"""-NGHOSTS <= 0)                  SHIFTSTENCIL"""+dirn+""" = +1;
    SHIFTSTENCIL"""+dirnp1+""" = 0;
    SHIFTSTENCIL"""+dirnp2+""" = 0;
"""
            if fd_order == 2:
                return preface + """

    fdD"""+dirn+"""
        = SHIFTSTENCIL"""+dirn+"""*(-1.5*gfs[IDX4S(which_gf,i0+0*SHIFTSTENCIL0,i1+0*SHIFTSTENCIL1,i2+0*SHIFTSTENCIL2)]
                         +2.*gfs[IDX4S(which_gf,i0+1*SHIFTSTENCIL0,i1+1*SHIFTSTENCIL1,i2+1*SHIFTSTENCIL2)]
                         -0.5*gfs[IDX4S(which_gf,i0+2*SHIFTSTENCIL0,i1+2*SHIFTSTENCIL1,i2+2*SHIFTSTENCIL2)]
                        )*invdx"""+dirn+""";

// Not on a +x"""+dirn+""" or -x"""+dirn+""" face, using centered difference:
} else {
    fdD"""+dirn+""" = """+gen_central_fd_stencil_str(intdirn, 2)+"""*invdx"""+dirn+""";
}
"""
            if fd_order == 4:
                return preface + """

    fdD"""+dirn+"""
        = SHIFTSTENCIL"""+dirn+"""*(u0*gfs[IDX4S(which_gf,i0+0*SHIFTSTENCIL0,i1+0*SHIFTSTENCIL1,i2+0*SHIFTSTENCIL2)]
                         +u1*gfs[IDX4S(which_gf,i0+1*SHIFTSTENCIL0,i1+1*SHIFTSTENCIL1,i2+1*SHIFTSTENCIL2)]
                         +u2*gfs[IDX4S(which_gf,i0+2*SHIFTSTENCIL0,i1+2*SHIFTSTENCIL1,i2+2*SHIFTSTENCIL2)]
                         +u3*gfs[IDX4S(which_gf,i0+3*SHIFTSTENCIL0,i1+3*SHIFTSTENCIL1,i2+3*SHIFTSTENCIL2)]
                         +u4*gfs[IDX4S(which_gf,i0+4*SHIFTSTENCIL0,i1+4*SHIFTSTENCIL1,i2+4*SHIFTSTENCIL2)]
                        )*invdx"""+dirn+""";

// Not on a +x"""+dirn+""" or -x"""+dirn+""" face, using centered difference:
} else {
    fdD"""+dirn+""" = """+gen_central_fd_stencil_str(intdirn, 4)+"""*invdx"""+dirn+""";
}
"""
            if fd_order == 6:
                return preface + """

    fdD"""+dirn+"""
        = SHIFTSTENCIL"""+dirn+"""*(u0*gfs[IDX4S(which_gf,i0+0*SHIFTSTENCIL0,i1+0*SHIFTSTENCIL1,i2+0*SHIFTSTENCIL2)]
                         +u1*gfs[IDX4S(which_gf,i0+1*SHIFTSTENCIL0,i1+1*SHIFTSTENCIL1,i2+1*SHIFTSTENCIL2)]
                         +u2*gfs[IDX4S(which_gf,i0+2*SHIFTSTENCIL0,i1+2*SHIFTSTENCIL1,i2+2*SHIFTSTENCIL2)]
                         +u3*gfs[IDX4S(which_gf,i0+3*SHIFTSTENCIL0,i1+3*SHIFTSTENCIL1,i2+3*SHIFTSTENCIL2)]
                         +u4*gfs[IDX4S(which_gf,i0+4*SHIFTSTENCIL0,i1+4*SHIFTSTENCIL1,i2+4*SHIFTSTENCIL2)]
                         +u5*gfs[IDX4S(which_gf,i0+5*SHIFTSTENCIL0,i1+5*SHIFTSTENCIL1,i2+5*SHIFTSTENCIL2)]
                         +u6*gfs[IDX4S(which_gf,i0+6*SHIFTSTENCIL0,i1+6*SHIFTSTENCIL1,i2+6*SHIFTSTENCIL2)]
                        )*invdx"""+dirn+""";

// Not on a +x"""+dirn+""" or -x"""+dirn+""" face, using centered difference:
} else {
    fdD"""+dirn+""" = """+gen_central_fd_stencil_str(intdirn, 6)+"""*invdx"""+dirn+""";
}
"""

            print("Error: fd_order = "+str(fd_order)+" currently unsupported.")
            sys.exit(1)

        contraction_term_func = """

// Function to calculate the radial derivative of a grid function
void contraction_term(const paramstruct *restrict params, const int which_gf, const REAL *restrict gfs, REAL *restrict xx[3],
           const int8_t FACEXi[3], const int i0, const int i1, const int i2, REAL *restrict _r, REAL *restrict _partial_i_f) {

#include "RELATIVE_PATH__set_Cparameters.h" /* Header file containing correct #include for set_Cparameters.h;
                                             * accounting for the relative path */

// Initialize derivatives to crazy values, to ensure that
//   we will notice in case they aren't set properly.
REAL fdD0=1e100;
REAL fdD1=1e100;
REAL fdD2=1e100;

REAL xx0 = xx[0][i0];
REAL xx1 = xx[1][i1];
REAL xx2 = xx[2][i2];

int8_t SHIFTSTENCIL0;
int8_t SHIFTSTENCIL1;
int8_t SHIFTSTENCIL2;

"""
        if fd_order == 4:
            contraction_term_func +="""
// foward/backward finite difference coefficients
const REAL u0 =-25./12.;
const REAL u1 = 4.;
const REAL u2 = -3.;
const REAL u3 = 4./3.;
const REAL u4 = -1./4.;

// central finite difference coefficients
const REAL c1 = 2./3.;
const REAL c2 = 1./12.;

"""
        if fd_order == 6:
            contraction_term_func +="""
// foward/backward finite difference coefficients
const REAL u0 = -49./20.;
const REAL u1 =  6.;
const REAL u2 = -15./2.;
const REAL u3 =  20./3.;
const REAL u4 = -15./4.;
const REAL u5 =  6./5.;
const REAL u6 = -1./6.;

// central finite difference coefficients
const REAL c1 = 3./4.;
const REAL c2 = 3./20.;
const REAL c3 = 1./60;

"""
        for i in range(DIM):
            if "fdD"+str(i) in r_str_and_contraction_str:
                contraction_term_func += output_dfdx(i, fd_order)

        contraction_term_func += "\n" + r_str_and_contraction_str

        contraction_term_func +="""
} // END contraction_term function
"""
        return contraction_term_func
def Set_up_CurviBoundaryConditions(Ccodesdir,
                                   verbose=True,
                                   Cparamspath=os.path.join("../"),
                                   enable_copy_of_static_Ccodes=True):
    # Step P0: Check that Ccodesdir is not the same as CurviBoundaryConditions/boundary_conditions,
    #          to prevent trusted versions of these C codes from becoming contaminated.
    if os.path.join(Ccodesdir) == os.path.join("CurviBoundaryConditions",
                                               "boundary_conditions"):
        print(
            "Error: Tried to output boundary conditions C code into CurviBoundaryConditions/boundary_conditions,"
            "       which is not allowed, to prevent trusted versions of these C codes from becoming contaminated."
        )
        sys.exit(1)

    # Step P1: Create the C codes output directory & copy static CurviBC files
    #          from CurviBoundaryConditions/boundary_conditions to Ccodesdir/
    if enable_copy_of_static_Ccodes:
        cmd.mkdir(os.path.join(Ccodesdir))
        for file in [
                "apply_bcs_curvilinear.h", "BCs_data_structs.h",
                "bcstruct_freemem.h", "CurviBC_include_Cfunctions.h",
                "driver_bcstruct.h", "set_bcstruct.h",
                "set_up__bc_gz_map_and_parity_condns.h"
        ]:
            shutil.copy(
                os.path.join("CurviBoundaryConditions", "boundary_conditions",
                             file), os.path.join(Ccodesdir))

    # Step P2: Output correct #include for set_Cparameters.h to
    #          Ccodesdir/boundary_conditions/RELATIVE_PATH__set_Cparameters.h
    with open(os.path.join(Ccodesdir, "RELATIVE_PATH__set_Cparameters.h"),
              "w") as file:
        file.write(
            "#include \"" + Cparamspath + "/set_Cparameters.h\"\n"
        )  # #include's may include forward slashes for paths, even in Windows.

    # Step 0: Set up reference metric in case it hasn't already been set up.
    #         (Doing it twice hurts nothing).
    rfm.reference_metric()

    # Step 1: Set unit-vector dot products (=parity) for each of the 10 parity condition types
    parity = ixp.zerorank1(DIM=10)
    UnitVectors_inner = ixp.zerorank2()
    xx0_inbounds, xx1_inbounds, xx2_inbounds = sp.symbols(
        "xx0_inbounds xx1_inbounds xx2_inbounds", real=True)
    for i in range(3):
        for j in range(3):
            UnitVectors_inner[i][j] = rfm.UnitVectors[i][j].subs(
                rfm.xx[0],
                xx0_inbounds).subs(rfm.xx[1],
                                   xx1_inbounds).subs(rfm.xx[2], xx2_inbounds)
    # Type 0: scalar
    parity[0] = sp.sympify(1)
    # Type 1: i0-direction vector or one-form
    # Type 2: i1-direction vector or one-form
    # Type 3: i2-direction vector or one-form
    for i in range(3):
        for Type in range(1, 4):
            parity[Type] += rfm.UnitVectors[Type -
                                            1][i] * UnitVectors_inner[Type -
                                                                      1][i]
    # Type 4: i0i0-direction rank-2 tensor
    # parity[4] = parity[1]*parity[1]
    # Type 5: i0i1-direction rank-2 tensor
    # Type 6: i0i2-direction rank-2 tensor
    # Type 7: i1i1-direction rank-2 tensor
    # Type 8: i1i2-direction rank-2 tensor
    # Type 9: i2i2-direction rank-2 tensor
    count = 4
    for i in range(3):
        for j in range(i, 3):
            parity[count] = parity[i + 1] * parity[j + 1]
            count = count + 1

    lhs_strings = []
    for i in range(10):
        lhs_strings.append("parity[" + str(i) + "]")
    outputC(
        parity, lhs_strings,
        os.path.join(Ccodesdir, "parity_conditions_symbolic_dot_products.h"))

    # Step 2.a: Generate Ccodesdir/gridfunction_defines.h file,
    #       containing human-readable gridfunction aliases
    evolved_variables_list, auxiliary_variables_list, auxevol_variables_list = gri.output__gridfunction_defines_h__return_gf_lists(
        Ccodesdir)

    # Step 2.b: set the parity conditions on all gridfunctions in gf_list,
    #       based on how many digits are at the end of their names
    def set_parity_types(list_of_gf_names):
        parity_type = []
        for name in list_of_gf_names:
            for gf in gri.glb_gridfcs_list:
                if gf.name == name:
                    parity_type__orig_len = len(parity_type)
                    if gf.DIM < 3 or gf.DIM > 4:
                        print(
                            "Error: Cannot currently specify parity conditions on gridfunctions with DIM<3 or >4."
                        )
                        sys.exit(1)
                    if gf.rank == 0:
                        parity_type.append(0)
                    elif gf.rank == 1:
                        if gf.DIM == 3:
                            parity_type.append(
                                int(gf.name[-1]) + 1
                            )  # = 1 for e.g., beta^0; = 2 for e.g., beta^1, etc.
                        elif gf.DIM == 4:
                            parity_type.append(
                                int(gf.name[-1])
                            )  # = 0 for e.g., b4^0; = 1 for e.g., beta^1, etc.
                    elif gf.rank == 2:
                        if gf.DIM == 3:
                            # element of a list; a[-2] the
                            # second-to-last element, etc.
                            idx0 = gf.name[-2]
                            idx1 = gf.name[-1]
                            if idx0 == "0" and idx1 == "0":
                                parity_type.append(4)
                            elif (idx0 == "0"
                                  and idx1 == "1") or (idx0 == "1"
                                                       and idx1 == "0"):
                                parity_type.append(5)
                            elif (idx0 == "0"
                                  and idx1 == "2") or (idx0 == "2"
                                                       and idx1 == "0"):
                                parity_type.append(6)
                            elif idx0 == "1" and idx1 == "1":
                                parity_type.append(7)
                            elif (idx0 == "1"
                                  and idx1 == "2") or (idx0 == "2"
                                                       and idx1 == "1"):
                                parity_type.append(8)
                            elif idx0 == "2" and idx1 == "2":
                                parity_type.append(9)
                        elif gf.DIM == 4:
                            idx0 = gf.name[-2]
                            idx1 = gf.name[-1]
                            # g4DD00 = g_{tt} : parity type = 0
                            # g4DD01 = g_{tx} : parity type = 1
                            # g4DD02 = g_{ty} : parity type = 2
                            # g4DD0a = g_{ta} : parity type = a
                            if idx0 == "0":
                                parity_type.append(int(idx1))
                            elif idx1 == "0":
                                parity_type.append(int(idx0))
                            if idx0 == "1" and idx1 == "1":
                                parity_type.append(4)
                            elif (idx0 == "1"
                                  and idx1 == "2") or (idx0 == "2"
                                                       and idx1 == "1"):
                                parity_type.append(5)
                            elif (idx0 == "1"
                                  and idx1 == "3") or (idx0 == "3"
                                                       and idx1 == "1"):
                                parity_type.append(6)
                            elif idx0 == "2" and idx1 == "2":
                                parity_type.append(7)
                            elif (idx0 == "2"
                                  and idx1 == "3") or (idx0 == "3"
                                                       and idx1 == "2"):
                                parity_type.append(8)
                            elif idx0 == "3" and idx1 == "3":
                                parity_type.append(9)
                    if len(parity_type) == parity_type__orig_len:
                        print(
                            "Error: Could not figure out parity type for " +
                            gf.gftype + " gridfunction: " + gf.name, gf.DIM,
                            gf.name[-2], gf.name[-1], gf.rank)
                        sys.exit(1)
        if len(parity_type) != len(list_of_gf_names):
            print(
                "Error: For some reason the length of the parity types list did not match the length of the gf list."
            )
            sys.exit(1)
        return parity_type

    evol_parity_type = set_parity_types(evolved_variables_list)
    aux_parity_type = set_parity_types(auxiliary_variables_list)
    auxevol_parity_type = set_parity_types(auxevol_variables_list)

    # Step 2.c: Output all gridfunctions to Ccodesdir+"/gridfunction_defines.h"
    # ... then append to the file the parity type for each gridfunction.
    with open(os.path.join(Ccodesdir, "gridfunction_defines.h"), "a") as file:
        file.write("\n\n/* PARITY TYPES FOR ALL GRIDFUNCTIONS.\n")
        file.write(
            "   SEE \"Tutorial-Start_to_Finish-Curvilinear_BCs.ipynb\" FOR DEFINITIONS. */\n"
        )
        if len(evolved_variables_list) > 0:
            file.write("const int8_t evol_gf_parity[" +
                       str(len(evolved_variables_list)) + "] = { ")
            for i in range(len(evolved_variables_list) - 1):
                file.write(str(evol_parity_type[i]) + ", ")
            file.write(
                str(evol_parity_type[len(evolved_variables_list) - 1]) +
                " };\n")

        if len(auxiliary_variables_list) > 0:
            file.write("const int8_t aux_gf_parity[" +
                       str(len(auxiliary_variables_list)) + "] = { ")
            for i in range(len(auxiliary_variables_list) - 1):
                file.write(str(aux_parity_type[i]) + ", ")
            file.write(
                str(aux_parity_type[len(auxiliary_variables_list) - 1]) +
                " };\n")

        if len(auxevol_variables_list) > 0:
            file.write("const int8_t auxevol_gf_parity[" +
                       str(len(auxevol_variables_list)) + "] = { ")
            for i in range(len(auxevol_variables_list) - 1):
                file.write(str(auxevol_parity_type[i]) + ", ")
            file.write(
                str(auxevol_parity_type[len(auxevol_variables_list) - 1]) +
                " };\n")

    if verbose == True:
        import textwrap
        wrapper = textwrap.TextWrapper(initial_indent="",
                                       subsequent_indent="    ",
                                       width=75)

        def print_parity_list(gf_type, variable_names, parity_types):
            outstr = ""
            if len(variable_names) != 0:
                outstr += gf_type + " parity: ( "
                for i in range(len(variable_names)):
                    outstr += variable_names[i] + ":" + str(parity_types[i])
                    if i != len(variable_names) - 1:
                        outstr += ", "
                outstr += " )"
            print(wrapper.fill(outstr))

        print_parity_list("Evolved", evolved_variables_list, evol_parity_type)
        print_parity_list("Auxiliary", auxiliary_variables_list,
                          aux_parity_type)
        print_parity_list("AuxEvol", auxevol_variables_list,
                          auxevol_parity_type)

    # Step 3: Find the Eigen-Coordinate and set up the Eigen-Coordinate's reference metric:
    CoordSystem_orig = par.parval_from_str("reference_metric::CoordSystem")
    par.set_parval_from_str("reference_metric::CoordSystem",
                            rfm.get_EigenCoord())
    rfm.reference_metric()

    # Step 4: Output C code for the Eigen-Coordinate mapping from xx->Cartesian:
    rfm.xxCart_h("EigenCoord_xxCart",
                 os.path.join(Cparamspath, "set_Cparameters.h"),
                 os.path.join(Ccodesdir, "EigenCoord_xxCart.h"))

    # Step 5: Output the Eigen-Coordinate mapping from Cartesian->xx:
    # Step 5.a: Sanity check: First make sure that rfm.Cart_to_xx has been set. Error out if not!
    if rfm.Cart_to_xx[0] == 0 or rfm.Cart_to_xx[1] == 0 or rfm.Cart_to_xx[
            2] == 0:
        print(
            "ERROR: rfm.Cart_to_xx[], which maps Cartesian -> xx, has not been set for"
        )
        print("       reference_metric::CoordSystem = " +
              par.parval_from_str("reference_metric::CoordSystem"))
        print(
            "       Boundary conditions in curvilinear coordinates REQUIRE this be set."
        )
        sys.exit(1)
    # Step 5.b: Output C code for the Eigen-Coordinate mapping from Cartesian->xx:
    outputC([rfm.Cart_to_xx[0], rfm.Cart_to_xx[1], rfm.Cart_to_xx[2]], [
        "Cart_to_xx0_inbounds", "Cart_to_xx1_inbounds", "Cart_to_xx2_inbounds"
    ], os.path.join(Ccodesdir, "EigenCoord_Cart_to_xx.h"))

    # Step 6: Restore reference_metric::CoordSystem back to the original CoordSystem
    par.set_parval_from_str("reference_metric::CoordSystem", CoordSystem_orig)
    rfm.reference_metric()
# $$\label{preliminaries}$$
#
# \[Back to [top](#top)\]
#
# Here, we will import the NRPy+ core modules and set the reference metric to Cartesian, set commonly used NRPy+ parameters, and set C parameters that will be set from outside the code eventually generated from these expressions. We will also set up a parameter to determine what initial data is set up, although it won't do much yet.

# Step 0: Import the NRPy+ core modules and set the reference metric to Cartesian
import NRPy_param_funcs as par
import indexedexp as ixp
import grid as gri
import finite_difference as fin
from outputC import *
import loop

import reference_metric as rfm
par.set_parval_from_str("reference_metric::CoordSystem", "Cartesian")
rfm.reference_metric()

# Step 1a: Set commonly used parameters.
thismodule = "GiRaFFEfood_HO_Aligned_Rotator"
# Set the spatial dimension parameter to 3.
par.set_parval_from_str("grid::DIM", 3)
DIM = par.parval_from_str("grid::DIM")

B_p_aligned_rotator, R_NS_aligned_rotator = par.Cparameters(
    "REAL",
    thismodule,
    # B_p_aligned_rotator = the intensity of the magnetic field and
    # R_NS_aligned_rotator= "Neutron star" radius
    ["B_p_aligned_rotator", "R_NS_aligned_rotator"],
    [1e-5, 1.0])
Exemple #29
0
def GiRaFFE_NRPy_C2P(StildeD, BU, gammaDD, betaU, alpha):
    GRHD.compute_sqrtgammaDET(gammaDD)
    gammaUU, unusedgammadet = ixp.symm_matrix_inverter3x3(gammaDD)
    BtildeU = ixp.zerorank1()
    for i in range(3):
        # \tilde{B}^i = B^i \sqrt{\gamma}
        BtildeU[i] = GRHD.sqrtgammaDET * BU[i]

    BtildeD = ixp.zerorank1()
    for i in range(3):
        for j in range(3):
            BtildeD[j] += gammaDD[i][j] * BtildeU[i]

    Btilde2 = sp.sympify(0)
    for i in range(3):
        Btilde2 += BtildeU[i] * BtildeD[i]

    global outStildeD
    outStildeD = StildeD
    # Then, enforce the orthogonality:
    if par.parval_from_str("enforce_orthogonality_StildeD_BtildeU"):
        StimesB = sp.sympify(0)
        for i in range(3):
            StimesB += StildeD[i] * BtildeU[i]

        for i in range(3):
            # {\tilde S}_i = {\tilde S}_i - ({\tilde S}_j {\tilde B}^j) {\tilde B}_i/{\tilde B}^2
            outStildeD[i] -= StimesB * BtildeD[i] / Btilde2

    # Calculate \tilde{S}^2:
    Stilde2 = sp.sympify(0)
    for i in range(3):
        for j in range(3):
            Stilde2 += gammaUU[i][j] * outStildeD[i] * outStildeD[j]

    # First we need to compute the factor f:
    # f = \sqrt{(1-\Gamma_{\max}^{-2}){\tilde B}^4/(16 \pi^2 \gamma {\tilde S}^2)}
    speed_limit_factor = sp.sqrt((sp.sympify(1)-GAMMA_SPEED_LIMIT**(-2.0))*Btilde2*Btilde2*sp.Rational(1,16)/\
                                 (M_PI*M_PI*GRHD.sqrtgammaDET*GRHD.sqrtgammaDET*Stilde2))

    import Min_Max_and_Piecewise_Expressions as noif

    # Calculate B^2
    B2 = sp.sympify(0)
    for i in range(3):
        for j in range(3):
            B2 += gammaDD[i][j] * BU[i] * BU[j]

    # Enforce the speed limit on StildeD:
    if par.parval_from_str("enforce_speed_limit_StildeD"):
        for i in range(3):
            outStildeD[i] *= noif.min_noif(sp.sympify(1), speed_limit_factor)

    global ValenciavU
    ValenciavU = ixp.zerorank1()
    # Recompute 3-velocity:
    for i in range(3):
        for j in range(3):
            # \bar{v}^i = 4 \pi \gamma^{ij} {\tilde S}_j / (\sqrt{\gamma} B^2)
            ValenciavU[i] += sp.sympify(4) * M_PI * gammaUU[i][j] * outStildeD[
                j] / (GRHD.sqrtgammaDET * B2)

    # This number determines how far away (in grid points) we will apply the fix.
    grid_points_from_z_plane = par.Cparameters("REAL", thismodule,
                                               "grid_points_from_z_plane", 4.0)

    if par.parval_from_str("enforce_current_sheet_prescription"):
        # Calculate the drift velocity
        driftvU = ixp.zerorank1()
        for i in range(3):
            driftvU[i] = alpha * ValenciavU[i] - betaU[i]

        # The direct approach, used by the original GiRaFFE:
        # v^z = -(\gamma_{xz} v^x + \gamma_{yz} v^y) / \gamma_{zz}
        newdriftvU2 = -(gammaDD[0][2] * driftvU[0] +
                        gammaDD[1][2] * driftvU[1]) / gammaDD[2][2]
        # Now that we have the z component, it's time to substitute its Valencia form in.
        # Remember, we only do this if abs(z) < (k+0.01)*dz. Note that we add 0.01; this helps
        # avoid floating point errors and division by zero. This is the same as abs(z) - (k+0.01)*dz<0
        coord = nrpyAbs(rfm.xx[2])
        bound = (grid_points_from_z_plane + sp.Rational(1, 100)) * gri.dxx[2]
        ValenciavU[2] = noif.coord_leq_bound(coord,bound)*(newdriftvU2+betaU[2])/alpha \
                      + noif.coord_greater_bound(coord,bound)*ValenciavU[2]
def BSSN_constraints(add_T4UUmunu_source_terms=False, output_H_only=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 1.c: Register H and MU gridfunctions for
    #           Hamiltonian & momentum constraints,
    #           respectively.
    registered_already = False
    for i in range(len(gri.glb_gridfcs_list)):
        if gri.glb_gridfcs_list[i].name == "H":
            registered_already = True
    if not registered_already:
        _H = gri.register_gridfunctions("AUX", "H")  # _H unused.
        if not output_H_only:
            _MU = ixp.register_gridfunctions_for_single_rank1(
                "AUX", "MU")  # _MU unused.

    # 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]