示例#1
0
def find_cmax_cmin(field_comp, gamma_faceDD, beta_faceU, alpha_face):
    # Inputs:  flux direction field_comp, Inverse metric gamma_faceUU, shift beta_faceU,
    #          lapse alpha_face, metric determinant gammadet_face
    # Outputs: maximum and minimum characteristic speeds cmax and cmin
    # First, we need to find the characteristic speeds on each face
    gamma_faceUU, unusedgammaDET = ixp.generic_matrix_inverter3x3(gamma_faceDD)
    find_cp_cm(alpha_face, beta_faceU[field_comp],
               gamma_faceUU[field_comp][field_comp])
    cpr = cplus
    cmr = cminus
    find_cp_cm(alpha_face, beta_faceU[field_comp],
               gamma_faceUU[field_comp][field_comp])
    cpl = cplus
    cml = cminus

    # The following algorithms have been verified with random floats:

    global cmax, cmin
    # Now, we need to set cmax to the larger of cpr,cpl, and 0

    import Min_Max_and_Piecewise_Expressions as noif
    cmax = noif.max_noif(noif.max_noif(cpr, cpl), sp.sympify(0))

    # And then, set cmin to the smaller of cmr,cml, and 0
    cmin = -noif.min_noif(noif.min_noif(cmr, cml), sp.sympify(0))
示例#2
0
def find_cmax_cmin(flux_dirn, gamma_faceDD, beta_faceU, alpha_face):
    # Inputs:  flux direction flux_dirn, Inverse metric gamma_faceUU, shift beta_faceU,
    #          lapse alpha_face, metric determinant gammadet_face
    # Outputs: maximum and minimum characteristic speeds cmax and cmin
    # First, we need to find the characteristic speeds on each face
    gamma_faceUU, unusedgammaDET = ixp.generic_matrix_inverter3x3(gamma_faceDD)
    find_cp_cm(alpha_face, beta_faceU[flux_dirn],
               gamma_faceUU[flux_dirn][flux_dirn])
    cpr = cplus
    cmr = cminus
    find_cp_cm(alpha_face, beta_faceU[flux_dirn],
               gamma_faceUU[flux_dirn][flux_dirn])
    cpl = cplus
    cml = cminus

    # The following algorithms have been verified with random floats:

    global cmax, cmin
    # Now, we need to set cmax to the larger of cpr,cpl, and 0
    cmax = sp.Rational(1, 2) * (cpr + cpl + nrpyAbs(cpr - cpl))
    cmax = sp.Rational(1, 2) * (cmax + nrpyAbs(cmax))

    # And then, set cmin to the smaller of cmr,cml, and 0
    cmin = sp.Rational(1, 2) * (cmr + cml - nrpyAbs(cmr - cml))
    cmin = -sp.Rational(1, 2) * (cmin - nrpyAbs(cmin))
def Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear(
        CoordType_in, Sph_r_th_ph_or_Cart_xyz, gammaDD_inSphorCart,
        KDD_inSphorCart, alpha_inSphorCart, betaU_inSphorCart, BU_inSphorCart):
    # This routine converts the ADM variables
    #    $$\left\{\gamma_{ij}, K_{ij}, \alpha, \beta^i\right\}$$
    #    in Spherical or Cartesian basis+coordinates, first to the BSSN variables
    #    in the chosen reference_metric::CoordSystem coordinate system+basis:
    # $$\left\{\bar{\gamma}_{i j},\bar{A}_{i j},\phi, K, \bar{\Lambda}^{i}, \alpha, \beta^i, B^i\right\},$$
    #    and then to the rescaled variables:
    # $$\left\{h_{i j},a_{i j},\phi, K, \lambda^{i}, \alpha, \mathcal{V}^i, \mathcal{B}^i\right\}.$$

    # The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations.
    #    To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in
    #    the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3.
    # Step P1: Set spatial dimension (must be 3 for BSSN)
    DIM = 3

    # Step P2: Copy gammaSphDD_in to gammaSphDD, KSphDD_in to KSphDD, etc.
    #    This ensures that the input arrays are not modified below;
    #    modifying them would result in unexpected effects outside
    #    this function.
    alphaSphorCart = alpha_inSphorCart
    betaSphorCartU = ixp.zerorank1()
    BSphorCartU = ixp.zerorank1()
    gammaSphorCartDD = ixp.zerorank2()
    KSphorCartDD = ixp.zerorank2()
    for i in range(DIM):
        betaSphorCartU[i] = betaU_inSphorCart[i]
        BSphorCartU[i] = BU_inSphorCart[i]
        for j in range(DIM):
            gammaSphorCartDD[i][j] = gammaDD_inSphorCart[i][j]
            KSphorCartDD[i][j] = KDD_inSphorCart[i][j]

    # Make sure that rfm.reference_metric() has been called.
    #    We'll need the variables it defines throughout this module.
    if rfm.have_already_called_reference_metric_function == False:
        print(
            "Error. Called Convert_Spherical_ADM_to_BSSN_curvilinear() without"
        )
        print(
            "       first setting up reference metric, by calling rfm.reference_metric()."
        )
        sys.exit(1)

    # Step 1: All input quantitiefs are in terms of r,th,ph or x,y,z. We want them in terms
    #         of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace
    #         r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2
    #         as defined for this particular reference metric in reference_metric.py's
    #         xxSph[] or xx_to_Cart[], respectively:

    # Note that substitution only works when the variable is not an integer. Hence the
    #         if isinstance(...,...) stuff:
    def sympify_integers__replace_rthph_or_Cartxyz(obj, rthph_or_xyz,
                                                   rthph_or_xyz_of_xx):
        if isinstance(obj, int):
            return sp.sympify(obj)
        return obj.subs(rthph_or_xyz[0], rthph_or_xyz_of_xx[0]).\
            subs(rthph_or_xyz[1], rthph_or_xyz_of_xx[1]).\
            subs(rthph_or_xyz[2], rthph_or_xyz_of_xx[2])

    r_th_ph_or_Cart_xyz_of_xx = []
    if CoordType_in == "Spherical":
        r_th_ph_or_Cart_xyz_of_xx = rfm.xxSph
    elif CoordType_in == "Cartesian":
        r_th_ph_or_Cart_xyz_of_xx = rfm.xx_to_Cart
    else:
        print(
            "Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords."
        )
        sys.exit(1)

    alphaSphorCart = sympify_integers__replace_rthph_or_Cartxyz(
        alphaSphorCart, Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx)
    for i in range(DIM):
        betaSphorCartU[i] = sympify_integers__replace_rthph_or_Cartxyz(
            betaSphorCartU[i], Sph_r_th_ph_or_Cart_xyz,
            r_th_ph_or_Cart_xyz_of_xx)
        BSphorCartU[i] = sympify_integers__replace_rthph_or_Cartxyz(
            BSphorCartU[i], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx)
        for j in range(DIM):
            gammaSphorCartDD[i][
                j] = sympify_integers__replace_rthph_or_Cartxyz(
                    gammaSphorCartDD[i][j], Sph_r_th_ph_or_Cart_xyz,
                    r_th_ph_or_Cart_xyz_of_xx)
            KSphorCartDD[i][j] = sympify_integers__replace_rthph_or_Cartxyz(
                KSphorCartDD[i][j], Sph_r_th_ph_or_Cart_xyz,
                r_th_ph_or_Cart_xyz_of_xx)

    # Step 2: All ADM initial data quantities are now functions of xx0,xx1,xx2, but
    #         they are still in the Spherical or Cartesian basis. We can now directly apply
    #         Jacobian transformations to get them in the correct xx0,xx1,xx2 basis:

    # Make these globals so we can recover the ADM quantities in the destination basis if desired.
    global alpha, betaU, BU, gammaDD, KDD

    # alpha is a scalar, so no Jacobian transformation is necessary.
    alpha = alphaSphorCart

    Jac_dUSphorCart_dDrfmUD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            # Converting UIUCBlackHole ID to Cartesian coords takes 66.8s if the following isn't simplified:
            # Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff(r_th_ph_or_Cart_xyz_of_xx[i],rfm.xx[j])
            # ... but when we simplify each term, the total conversion time is reduced to 60.93s
            Jac_dUSphorCart_dDrfmUD[i][j] = sp.simplify(
                sp.diff(r_th_ph_or_Cart_xyz_of_xx[i], rfm.xx[j]))

    Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3(
        Jac_dUSphorCart_dDrfmUD)

    betaU = ixp.zerorank1()
    BU = ixp.zerorank1()
    gammaDD = ixp.zerorank2()
    KDD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j]
            BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j]
            for k in range(DIM):
                for l in range(DIM):
                    gammaDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][
                        i] * Jac_dUSphorCart_dDrfmUD[l][j] * gammaSphorCartDD[
                            k][l]
                    KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][
                        i] * Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l]

    # Step 3: All ADM quantities were input into this function in the Spherical or Cartesian
    #         basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above,
    #         we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2.
    #         Here we convert ADM quantities in the "rfm" basis to their BSSN Curvilinear
    #         counterparts:

    import BSSN.BSSN_in_terms_of_ADM as BitoA
    BitoA.gammabarDD_hDD(gammaDD)
    BitoA.trK_AbarDD_aDD(gammaDD, KDD)
    BitoA.LambdabarU_lambdaU__exact_gammaDD(gammaDD)
    BitoA.cf_from_gammaDD(gammaDD)
    BitoA.betU_vetU(betaU, BU)

    # Step 4: Return the BSSN Curvilinear variables in the desired xx0,xx1,xx2
    #         basis, and as functions of the consistent xx0,xx1,xx2 coordinates.
    return BitoA.cf, BitoA.hDD, BitoA.lambdaU, BitoA.aDD, BitoA.trK, alpha, BitoA.vetU, BitoA.betU
示例#4
0
    def write_dfdr_function(self, Ccodesdir, fd_order=2):
        # 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]
        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_fd_stencil_str(intdirn, fd_order):
            if fd_order == 2:
                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
                elif 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
                elif intdirn == 2:
                    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 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'
            if fd_order == 2:
                return """
// 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;

    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 + """;
}
"""
            else:
                print("Error: fd_order = " + str(fd_order) +
                      " currently unsupported.")
                sys.exit(1)

        contraction_term_func = """

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;

"""
        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
"""
        with open(
                os.path.join(Ccodesdir,
                             "boundary_conditions/radial_derivative.h"),
                "w") as file:
            file.write(contraction_term_func)
示例#5
0
def Psi4_tetradsv2():
    global l4U, n4U, mre4U, mim4U

    # Step 1.c: Check if tetrad choice is implemented:
    if par.parval_from_str(thismodule + "::TetradChoice") != "QuasiKinnersley":
        print("ERROR: " + thismodule + "::TetradChoice = " +
              par.parval_from_str("TetradChoice") + " currently unsupported!")
        exit(1)

    # Step 1.d: Given the chosen coordinate system, set up
    #           corresponding reference metric and needed
    #           reference metric quantities
    # The following function call sets up the reference metric
    #    and related quantities, including rescaling matrices ReDD,
    #    ReU, and hatted quantities.
    rfm.reference_metric()

    # Step 1.e: Set spatial dimension (must be 3 for BSSN, as BSSN is
    #           a 3+1-dimensional decomposition of the general
    #           relativistic field equations)
    DIM = 3

    # Step 1.f: Import all ADM quantities as written in terms of BSSN quantities
    import BSSN.ADM_in_terms_of_BSSN as AB
    AB.ADM_in_terms_of_BSSN()

    # Step 2.a: Declare the Cartesian x,y,z as input parameters
    #           and v_1^a, v_2^a, and v_3^a tetrads,
    #           as well as detgamma and gammaUU from
    #           BSSN.ADM_in_terms_of_BSSN
    x, y, z = par.Cparameters("REAL", thismodule, ["x", "y", "z"])

    v1UCart = ixp.zerorank1()
    v2UCart = ixp.zerorank1()

    # detgamma = AB.detgamma
    # gammaUU  = AB.gammaUU

    # Step 2.b: Define v1U and v2U
    v1UCart = [-y, x, sp.sympify(0)]
    v2UCart = [x, y, z]

    # Step 2.c: Construct the Jacobian d x_Cart^i / d xx^j
    Jac_dUCart_dDrfmUD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            Jac_dUCart_dDrfmUD[i][j] = sp.diff(rfm.xxCart[i], rfm.xx[j])

    # Step 2.d: Invert above Jacobian to get needed d xx^j / d x_Cart^i
    Jac_dUrfm_dDCartUD, dummyDET = ixp.generic_matrix_inverter3x3(
        Jac_dUCart_dDrfmUD)

    # Step 2.e: Transform gammaDD to Cartesian basis:
    gammaCartDD = ixp.zerorank2()
    gammaDD = AB.gammaDD
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                for l in range(DIM):
                    gammaCartDD[i][j] += Jac_dUrfm_dDCartUD[k][
                        i] * Jac_dUrfm_dDCartUD[l][j] * gammaDD[k][l]

    gammaCartUU, detgammaCart = ixp.symm_matrix_inverter3x3(gammaCartDD)

    # Step 2.e: Transform v1U and v2U from the Cartesian to the xx^i basis
    v1U = ixp.zerorank1()
    v2U = ixp.zerorank1()
    for i in range(DIM):
        v1U[i] = v1UCart[i]
        v2U[i] = v2UCart[i]
    # for i in range(DIM):
    #     for j in range(DIM):
    #         v1U[i] += Jac_dUrfm_dDCartUD[i][j]*v1UCart[j]
    #         v2U[i] += Jac_dUrfm_dDCartUD[i][j]*v2UCart[j]

    # Step 2.f: Define the rank-3 version of the Levi-Civita symbol. Amongst
    #         other uses, this is needed for the construction of the approximate
    #         quasi-Kinnersley tetrad.
    def define_LeviCivitaSymbol_rank3(DIM=-1):
        if DIM == -1:
            DIM = par.parval_from_str("DIM")

        LeviCivitaSymbol = ixp.zerorank3()

        for i in range(DIM):
            for j in range(DIM):
                for k in range(DIM):
                    # From https://codegolf.stackexchange.com/questions/160359/levi-civita-symbol :
                    LeviCivitaSymbol[i][j][k] = (i - j) * (j - k) * (k - i) / 2
        return LeviCivitaSymbol

    # Step 2.g: Define v3U
    v3U = ixp.zerorank1()
    LeviCivitaSymbolDDD = define_LeviCivitaSymbol_rank3(DIM=3)
    for a in range(DIM):
        for b in range(DIM):
            for c in range(DIM):
                for d in range(DIM):
                    v3U[a] += sp.sqrt(detgammaCart) * gammaCartUU[a][
                        d] * LeviCivitaSymbolDDD[d][b][c] * v1U[b] * v2U[c]

    # Step 2.h: Define omega_{ij}
    omegaDD = ixp.zerorank2()

    # Step 2.i: Define e^a_i. Note that:
    #           omegaDD[0][0] = \omega_{11} above;
    #           omegaDD[1][1] = \omega_{22} above, etc.
    e1U = ixp.zerorank1()
    e2U = ixp.zerorank1()
    e3U = ixp.zerorank1()
    update_omega(omegaDD, v1U, v2U, v3U, gammaCartDD)
    for a in range(DIM):
        e1U[a] = v1U[a] / sp.sqrt(omegaDD[0][0])
    update_omega(omegaDD, e1U, v2U, v3U, gammaCartDD)
    for a in range(DIM):
        e2U[a] = (v2U[a] - omegaDD[0][1] * e1U[a]) / sp.sqrt(omegaDD[1][1])
    update_omega(omegaDD, e1U, e2U, v3U, gammaCartDD)
    for a in range(DIM):
        e3U[a] = (v3U[a] - omegaDD[0][2] * e1U[a] -
                  omegaDD[1][2] * e2U[a]) / sp.sqrt(omegaDD[2][2])

    # Step 2.j: Construct l^mu, n^mu, and m^mu, based on r^mu, theta^mu, phi^mu, and u^mu:
    rCart4U = ixp.zerorank1(DIM=4)
    thetaCart4U = ixp.zerorank1(DIM=4)
    phiCart4U = ixp.zerorank1(DIM=4)

    for a in range(DIM):
        rCart4U[a + 1] = e2U[a]
        thetaCart4U[a + 1] = e3U[a]
        phiCart4U[a + 1] = e1U[a]

    r4U = ixp.zerorank1(DIM=4)
    theta4U = ixp.zerorank1(DIM=4)
    phi4U = ixp.zerorank1(DIM=4)

    for a in range(DIM):
        for b in range(DIM):
            r4U[a + 1] += Jac_dUrfm_dDCartUD[a][b] * rCart4U[b + 1]
            theta4U[a + 1] += Jac_dUrfm_dDCartUD[a][b] * thetaCart4U[b + 1]
            phi4U[a + 1] += Jac_dUrfm_dDCartUD[a][b] * phiCart4U[b + 1]

    u4U = ixp.zerorank1(DIM=4)
    # FIXME? assumes alpha=1, beta^i = 0
    u4U[0] = 1

    l4U = ixp.zerorank1(DIM=4)
    n4U = ixp.zerorank1(DIM=4)
    mre4U = ixp.zerorank1(DIM=4)
    mim4U = ixp.zerorank1(DIM=4)

    isqrt2 = 1 / sp.sqrt(2)
    for mu in range(4):
        l4U[mu] = isqrt2 * (u4U[mu] + r4U[mu])
        n4U[mu] = isqrt2 * (u4U[mu] - r4U[mu])
        mre4U[mu] = isqrt2 * theta4U[mu]
        mim4U[mu] = isqrt2 * phi4U[mu]
示例#6
0
def Tmunu_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear(
        CoordType_in, Tmunu_input_function_name, pointer_to_ID_inputs=False):
    # The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations.
    #    To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in
    #    the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3.

    # Step 0: Set spatial dimension (must be 3 for BSSN)
    DIM = 3

    # Step 1: Define the input variables: the 4D stress-energy tensor, and the ADM 3-metric, lapse, & shift:
    T4SphorCartUU = ixp.declarerank2("T4SphorCartUU", "sym01", DIM=4)
    gammaSphorCartDD = ixp.declarerank2("gammaSphorCartDD", "sym01")
    alphaSphorCart = sp.symbols("alphaSphorCart")
    betaSphorCartU = ixp.declarerank1("betaSphorCartU")

    # Step 2: All Tmunu initial data quantities are functions of xx0,xx1,xx2, but
    #         in the Spherical or Cartesian basis.
    #         We first define the BSSN stress-energy source terms in the Spherical
    #         or Cartesian basis, respectively.
    # To get \gamma_{\mu \nu} = gammabar4DD[mu][nu], we'll need to construct the 4-metric, using Eq. 2.122 in B&S:

    # S_{ij} = \gamma_{i \mu} \gamma_{j \nu} T^{\mu \nu}
    # S_{i}  = -\gamma_{i\mu} n_\nu T^{\mu\nu}
    # S      = \gamma^{ij} S_{ij}
    # rho    = n_\mu n_\nu T^{\mu\nu},

    # where

    # \gamma_{\mu\nu} = g_{\mu\nu} + n_\mu n_\nu

    # and

    # n_mu = {-\alpha,0,0,0},

    # Step 2.1: Construct the 4-metric based on the input ADM quantities.

    # This is provided by Eq 4.47 in [Gourgoulhon](https://arxiv.org/pdf/gr-qc/0703035.pdf):

    # g_{tt} = -\alpha^2 + \beta^k \beta_k
    # g_{ti} = \beta_i
    # g_{ij} = \gamma_{ij}

    # Eq. 2.121 in B&S
    betaSphorCartD = ixp.zerorank1()
    for i in range(DIM):
        for j in range(DIM):
            betaSphorCartD[i] += gammaSphorCartDD[i][j] * betaSphorCartU[j]

    # Now compute the beta contraction.
    beta2 = sp.sympify(0)
    for i in range(DIM):
        beta2 += betaSphorCartU[i] * betaSphorCartD[i]

    # Eq. 2.122 in B&S
    g4SphorCartDD = ixp.zerorank2(DIM=4)
    g4SphorCartDD[0][0] = -alphaSphorCart**2 + beta2
    for i in range(DIM):
        g4SphorCartDD[i + 1][0] = g4SphorCartDD[0][i + 1] = betaSphorCartD[i]
    for i in range(DIM):
        for j in range(DIM):
            g4SphorCartDD[i + 1][j + 1] = gammaSphorCartDD[i][j]

    # Step 2.2: Construct \gamma_{mu nu} = g_{mu nu} + n_mu n_nu:
    n4SphorCartD = ixp.zerorank1(DIM=4)
    n4SphorCartD[0] = -alphaSphorCart

    gamma4SphorCartDD = ixp.zerorank2(DIM=4)
    for mu in range(4):
        for nu in range(4):
            gamma4SphorCartDD[mu][nu] = g4SphorCartDD[mu][
                nu] + n4SphorCartD[mu] * n4SphorCartD[nu]

    # Step 2.3: We now have all we need to construct the BSSN source
    #           terms in the current basis (Spherical or Cartesian):

    # S_{ij} = \gamma_{i \mu} \gamma_{j \nu} T^{\mu \nu}
    # S_{i}  = -\gamma_{i\mu} n_\nu T^{\mu\nu}
    # S      = \gamma^{ij} S_{ij}
    # rho    = n_\mu n_\nu T^{\mu\nu},

    SSphorCartDD = ixp.zerorank2()
    SSphorCartD = ixp.zerorank1()
    SSphorCart = sp.sympify(0)
    rhoSphorCart = sp.sympify(0)

    for i in range(DIM):
        for j in range(DIM):
            for mu in range(4):
                for nu in range(4):
                    SSphorCartDD[i][j] += gamma4SphorCartDD[
                        i + 1][mu] * gamma4SphorCartDD[
                            j + 1][nu] * T4SphorCartUU[mu][nu]

    for i in range(DIM):
        for mu in range(4):
            for nu in range(4):
                SSphorCartD[i] += -gamma4SphorCartDD[
                    i + 1][mu] * n4SphorCartD[nu] * T4SphorCartUU[mu][nu]

    gammaSphorCartUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaSphorCartDD)
    for i in range(DIM):
        for j in range(DIM):
            SSphorCart += gammaSphorCartUU[i][j] * SSphorCartDD[i][j]

    for mu in range(4):
        for nu in range(4):
            rhoSphorCart += n4SphorCartD[mu] * n4SphorCartD[
                nu] * T4SphorCartUU[mu][nu]

    # Step 3: Perform basis conversion to

    # Make sure that rfm.reference_metric() has been called.
    #    We'll need the variables it defines throughout this module.
    if rfm.have_already_called_reference_metric_function == False:
        print(
            "Error. Called Tmunu_Numerical_Spherical_or_Cartesian_to_BSSNCurvilinear() without"
        )
        print(
            "       first setting up reference metric, by calling rfm.reference_metric()."
        )
        exit(1)

    # Step 1: All input quantities are in terms of r,th,ph or x,y,z. We want them in terms
    #         of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace
    #         r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2
    #         as defined for this particular reference metric in reference_metric.py's
    #         xxSph[] or xxCart[], respectively:
    r_th_ph_or_Cart_xyz_oID_xx = []
    if CoordType_in == "Spherical":
        r_th_ph_or_Cart_xyz_oID_xx = rfm.xxSph
    elif CoordType_in == "Cartesian":
        r_th_ph_or_Cart_xyz_oID_xx = rfm.xxCart
    else:
        print(
            "Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords."
        )
        exit(1)

    # Next apply Jacobian transformations to convert into the (xx0,xx1,xx2) basis

    # alpha is a scalar, so no Jacobian transformation is necessary.
    alpha = alphaSphorCart

    Jac_dUSphorCart_dDrfmUD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff(
                r_th_ph_or_Cart_xyz_oID_xx[i], rfm.xx[j])

    Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3(
        Jac_dUSphorCart_dDrfmUD)

    betaU = ixp.zerorank1()
    BU = ixp.zerorank1()
    gammaSphorCartDD = ixp.zerorank2()
    KDD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j]
            BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j]
            for k in range(DIM):
                for l in range(DIM):
                    gammaSphorCartDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * \
                                     gammaSphorCartDD[k][l]
                    KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][
                        i] * Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l]

    # Step 3: All ADM quantities were input into this function in the Spherical or Cartesian
    #         basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above,
    #         we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2.
    #         Here we convert ADM quantities to their BSSN Curvilinear counterparts:

    # Step 3.1: Convert ADM $\gamma_{ij}$ to BSSN $\bar{\gamma}_{ij}$:
    #   We have (Eqs. 2 and 3 of [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)):
    # \bar{\gamma}_{i j} = \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \gamma_{ij}.
    gammaSphorCartUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaSphorCartDD)
    gammabarDD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            gammabarDD[i][j] = (rfm.detgammahat / gammaDET)**(sp.Rational(
                1, 3)) * gammaSphorCartDD[i][j]

    # Step 3.2: Convert the extrinsic curvature $K_{ij}$ to the trace-free extrinsic
    #           curvature $\bar{A}_{ij}$, plus the trace of the extrinsic curvature $K$,
    #           where (Eq. 3 of [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)):

    # K = \gamma^{ij} K_{ij}, and
    # \bar{A}_{ij} &= \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \left(K_{ij} - \frac{1}{3} \gamma_{ij} K \right)
    trK = sp.sympify(0)
    for i in range(DIM):
        for j in range(DIM):
            trK += gammaSphorCartUU[i][j] * KDD[i][j]

    AbarDD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            AbarDD[i][j] = (rfm.detgammahat / gammaDET)**(sp.Rational(
                1, 3)) * (KDD[i][j] -
                          sp.Rational(1, 3) * gammaSphorCartDD[i][j] * trK)

    # Step 3.3: Set the conformal factor variable $\texttt{cf}$, which is set
    #           by the "BSSN_RHSs::ConformalFactor" parameter. For example if
    #           "ConformalFactor" is set to "phi", we can use Eq. 3 of
    #           [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf),
    #           which in arbitrary coordinates is written:

    # \phi = \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right).

    # Alternatively if "BSSN_RHSs::ConformalFactor" is set to "chi", then

    # \chi = e^{-4 \phi} = \exp\left(-4 \frac{1}{12} \left(\frac{\gamma}{\bar{\gamma}}\right)\right)
    #      = \exp\left(-\frac{1}{3} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/3}.
    #
    # Finally if "BSSN_RHSs::ConformalFactor" is set to "W", then

    # W = e^{-2 \phi} = \exp\left(-2 \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) =
    # \exp\left(-\frac{1}{6} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) =
    # \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/6}.

    # First compute gammabarDET:
    gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD)

    cf = sp.sympify(0)

    if par.parval_from_str("ConformalFactor") == "phi":
        cf = sp.Rational(1, 12) * sp.log(gammaDET / gammabarDET)
    elif par.parval_from_str("ConformalFactor") == "chi":
        cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 3))
    elif par.parval_from_str("ConformalFactor") == "W":
        cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 6))
    else:
        print("Error ConformalFactor type = \"" +
              par.parval_from_str("ConformalFactor") + "\" unknown.")
        exit(1)

    # Step 4: Rescale tensorial quantities according to the prescription described in
    #         the [BSSN in curvilinear coordinates tutorial module](Tutorial-BSSNCurvilinear.ipynb)
    #         (also [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)):
    #
    # h_{ij} &= (\bar{\gamma}_{ij} - \hat{\gamma}_{ij})/\text{ReDD[i][j]}\\
    # a_{ij} &= \bar{A}_{ij}/\text{ReDD[i][j]}\\
    # \lambda^i &= \bar{\Lambda}^i/\text{ReU[i]}\\
    # \mathcal{V}^i &= \beta^i/\text{ReU[i]}\\
    # \mathcal{B}^i &= B^i/\text{ReU[i]}\\
    hDD = ixp.zerorank2()
    aDD = ixp.zerorank2()
    vetU = ixp.zerorank1()
    betU = ixp.zerorank1()
    for i in range(DIM):
        vetU[i] = betaU[i] / rfm.ReU[i]
        betU[i] = BU[i] / rfm.ReU[i]
        for j in range(DIM):
            hDD[i][j] = (gammabarDD[i][j] - rfm.ghatDD[i][j]) / rfm.ReDD[i][j]
            aDD[i][j] = AbarDD[i][j] / rfm.ReDD[i][j]

    # Step 5: Output all ADM-to-BSSN expressions to a C function. This function
    #         must first call the ID_ADM_SphorCart() defined above. Using these
    #         Spherical or Cartesian data, it sets up all quantities needed for
    #         BSSNCurvilinear initial data, *except* $\lambda^i$, which must be
    #         computed from numerical data using finite-difference derivatives.
    with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h",
              "w") as file:
        file.write(
            "void ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(const REAL xx0xx1xx2[3],"
        )
        if pointer_to_ID_inputs == True:
            file.write("ID_inputs *other_inputs,")
        else:
            file.write("ID_inputs other_inputs,")
        file.write("""
                    REAL *hDD00,REAL *hDD01,REAL *hDD02,REAL *hDD11,REAL *hDD12,REAL *hDD22,
                    REAL *aDD00,REAL *aDD01,REAL *aDD02,REAL *aDD11,REAL *aDD12,REAL *aDD22,
                    REAL *trK, 
                    REAL *vetU0,REAL *vetU1,REAL *vetU2,
                    REAL *betU0,REAL *betU1,REAL *betU2,
                    REAL *alpha,  REAL *cf) {
      REAL gammaSphorCartDD00,gammaSphorCartDD01,gammaSphorCartDD02,
           gammaSphorCartDD11,gammaSphorCartDD12,gammaSphorCartDD22;
      REAL KSphorCartDD00,KSphorCartDD01,KSphorCartDD02,
           KSphorCartDD11,KSphorCartDD12,KSphorCartDD22;
      REAL alphaSphorCart,betaSphorCartU0,betaSphorCartU1,betaSphorCartU2;
      REAL BSphorCartU0,BSphorCartU1,BSphorCartU2;
      const REAL xx0 = xx0xx1xx2[0];
      const REAL xx1 = xx0xx1xx2[1];
      const REAL xx2 = xx0xx1xx2[2];
      REAL xyz_or_rthph[3];\n""")
    outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False"
    outputC(r_th_ph_or_Cart_xyz_oID_xx[0:3],
            ["xyz_or_rthph[0]", "xyz_or_rthph[1]", "xyz_or_rthph[2]"],
            "BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h",
            outCparams + ",CSE_enable=False")
    with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h",
              "a") as file:
        file.write("      " + ADM_input_function_name +
                   """(xyz_or_rthph, other_inputs,
                      &gammaSphorCartDD00,&gammaSphorCartDD01,&gammaSphorCartDD02,
                      &gammaSphorCartDD11,&gammaSphorCartDD12,&gammaSphorCartDD22,
                      &KSphorCartDD00,&KSphorCartDD01,&KSphorCartDD02,
                      &KSphorCartDD11,&KSphorCartDD12,&KSphorCartDD22,
                      &alphaSphorCart,&betaSphorCartU0,&betaSphorCartU1,&betaSphorCartU2,
                      &BSphorCartU0,&BSphorCartU1,&BSphorCartU2);
        // Next compute all rescaled BSSN curvilinear quantities:\n""")
    outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False"
    outputC([
        hDD[0][0], hDD[0][1], hDD[0][2], hDD[1][1], hDD[1][2], hDD[2][2],
        aDD[0][0], aDD[0][1], aDD[0][2], aDD[1][1], aDD[1][2], aDD[2][2], trK,
        vetU[0], vetU[1], vetU[2], betU[0], betU[1], betU[2], alpha, cf
    ], [
        "*hDD00", "*hDD01", "*hDD02", "*hDD11", "*hDD12", "*hDD22", "*aDD00",
        "*aDD01", "*aDD02", "*aDD11", "*aDD12", "*aDD22", "*trK", "*vetU0",
        "*vetU1", "*vetU2", "*betU0", "*betU1", "*betU2", "*alpha", "*cf"
    ],
            "BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h",
            params=outCparams)
    with open("BSSN/ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs.h",
              "a") as file:
        file.write("}\n")

    # Step 5.A: Output the driver function for the above
    #           function ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs()
    # Next write the driver function for ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs():
    with open("BSSN/ID_BSSN__ALL_BUT_LAMBDAs.h", "w") as file:
        file.write(
            "void ID_BSSN__ALL_BUT_LAMBDAs(const int Nxx_plus_2NGHOSTS[3],REAL *xx[3],"
        )
        if pointer_to_ID_inputs == True:
            file.write("ID_inputs *other_inputs,")
        else:
            file.write("ID_inputs other_inputs,")
        file.write("REAL *in_gfs) {\n")
        file.write(
            lp.loop(["i2", "i1", "i0"], ["0", "0", "0"], [
                "Nxx_plus_2NGHOSTS[2]", "Nxx_plus_2NGHOSTS[1]",
                "Nxx_plus_2NGHOSTS[0]"
            ], ["1", "1", "1"], [
                "#pragma omp parallel for", "    const REAL xx2 = xx[2][i2];",
                "        const REAL xx1 = xx[1][i1];"
            ], "", """const REAL xx0 = xx[0][i0];
const int idx = IDX3(i0,i1,i2);
const REAL xx0xx1xx2[3] = {xx0,xx1,xx2};
ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(xx0xx1xx2,other_inputs,
                    &in_gfs[IDX4pt(HDD00GF,idx)],&in_gfs[IDX4pt(HDD01GF,idx)],&in_gfs[IDX4pt(HDD02GF,idx)],
                    &in_gfs[IDX4pt(HDD11GF,idx)],&in_gfs[IDX4pt(HDD12GF,idx)],&in_gfs[IDX4pt(HDD22GF,idx)],
                    &in_gfs[IDX4pt(ADD00GF,idx)],&in_gfs[IDX4pt(ADD01GF,idx)],&in_gfs[IDX4pt(ADD02GF,idx)],
                    &in_gfs[IDX4pt(ADD11GF,idx)],&in_gfs[IDX4pt(ADD12GF,idx)],&in_gfs[IDX4pt(ADD22GF,idx)],
                    &in_gfs[IDX4pt(TRKGF,idx)],
                    &in_gfs[IDX4pt(VETU0GF,idx)],&in_gfs[IDX4pt(VETU1GF,idx)],&in_gfs[IDX4pt(VETU2GF,idx)],
                    &in_gfs[IDX4pt(BETU0GF,idx)],&in_gfs[IDX4pt(BETU1GF,idx)],&in_gfs[IDX4pt(BETU2GF,idx)],
                    &in_gfs[IDX4pt(ALPHAGF,idx)],&in_gfs[IDX4pt(CFGF,idx)]);
"""))
        file.write("}\n")

        # Step 6: Compute $\bar{\Lambda}^i$ (Eqs. 4 and 5 of
        #         [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)),
        #         from finite-difference derivatives of rescaled metric
        #         quantities $h_{ij}$:

        # \bar{\Lambda}^i = \bar{\gamma}^{jk}\left(\bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk}\right).

        # The reference_metric.py module provides us with analytic expressions for
        #         $\hat{\Gamma}^i_{jk}$, so here we need only compute
        #         finite-difference expressions for $\bar{\Gamma}^i_{jk}$, based on
        #         the values for $h_{ij}$ provided in the initial data. Once
        #         $\bar{\Lambda}^i$ has been computed, we apply the usual rescaling
        #         procedure:

        # \lambda^i = \bar{\Lambda}^i/\text{ReU[i]},

        # and then output the result to a C file using the NRPy+
        #         finite-difference C output routine.
        # We will need all BSSN gridfunctions to be defined, as well as
        #     expressions for gammabarDD_dD in terms of exact derivatives of
        #     the rescaling matrix and finite-difference derivatives of
        #     hDD's.

        gammabarDD = bssnrhs.gammabarDD
        gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD)

        gammabarDD_dD = bssnrhs.gammabarDD_dD

        # Next compute Christoffel symbols \bar{\Gamma}^i_{jk}:
        GammabarUDD = ixp.zerorank3()
        for i in range(DIM):
            for j in range(DIM):
                for k in range(DIM):
                    for l in range(DIM):
                        GammabarUDD[i][j][k] += sp.Rational(
                            1,
                            2) * gammabarUU[i][l] * (gammabarDD_dD[l][j][k] +
                                                     gammabarDD_dD[l][k][j] -
                                                     gammabarDD_dD[j][k][l])
        # Next evaluate \bar{\Lambda}^i, based on GammabarUDD above and GammahatUDD
        #       (from the reference metric):
        LambdabarU = ixp.zerorank1()
        for i in range(DIM):
            for j in range(DIM):
                for k in range(DIM):
                    LambdabarU[i] += gammabarUU[j][k] * (
                        GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k])

        # Finally apply rescaling:
        # lambda^i = Lambdabar^i/\text{ReU[i]}
        lambdaU = ixp.zerorank1()
        for i in range(DIM):
            lambdaU[i] = LambdabarU[i] / rfm.ReU[i]

        outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False"
        lambdaU_expressions = [
            lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU0"), rhs=lambdaU[0]),
            lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU1"), rhs=lambdaU[1]),
            lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU2"), rhs=lambdaU[2])
        ]
        lambdaU_expressions_FDout = fin.FD_outputC("returnstring",
                                                   lambdaU_expressions,
                                                   outCparams)

        with open("BSSN/ID_BSSN_lambdas.h", "w") as file:
            file.write("""
void ID_BSSN_lambdas(const int Nxx[3],const int Nxx_plus_2NGHOSTS[3],REAL *xx[3],const REAL dxx[3],REAL *in_gfs) {\n"""
                       )
            file.write(
                lp.loop(["i2", "i1", "i0"], ["NGHOSTS", "NGHOSTS", "NGHOSTS"],
                        ["NGHOSTS+Nxx[2]", "NGHOSTS+Nxx[1]", "NGHOSTS+Nxx[0]"],
                        ["1", "1", "1"], [
                            "const REAL invdx0 = 1.0/dxx[0];\n" +
                            "const REAL invdx1 = 1.0/dxx[1];\n" +
                            "const REAL invdx2 = 1.0/dxx[2];\n" +
                            "#pragma omp parallel for",
                            "    const REAL xx2 = xx[2][i2];",
                            "        const REAL xx1 = xx[1][i1];"
                        ], "", "const REAL xx0 = xx[0][i0];\n" +
                        lambdaU_expressions_FDout))
            file.write("}\n")
def Psi4_tetrads():
    global l4U, n4U, mre4U, mim4U

    # Step 1.c: Check if tetrad choice is implemented:
    if par.parval_from_str(thismodule + "::TetradChoice") != "QuasiKinnersley":
        print("ERROR: " + thismodule + "::TetradChoice = " +
              par.parval_from_str("TetradChoice") + " currently unsupported!")
        sys.exit(1)

    # Step 1.d: Given the chosen coordinate system, set up
    #           corresponding reference metric and needed
    #           reference metric quantities
    # The following function call sets up the reference metric
    #    and related quantities, including rescaling matrices ReDD,
    #    ReU, and hatted quantities.
    rfm.reference_metric()

    # Step 1.e: Set spatial dimension (must be 3 for BSSN, as BSSN is
    #           a 3+1-dimensional decomposition of the general
    #           relativistic field equations)
    DIM = 3

    # Step 1.f: Import all ADM quantities as written in terms of BSSN quantities
    import BSSN.ADM_in_terms_of_BSSN as AB
    AB.ADM_in_terms_of_BSSN()

    # Step 2.a: Declare the Cartesian x,y,z in terms of
    #           xx0,xx1,xx2.
    x = rfm.xxCart[0]
    y = rfm.xxCart[1]
    z = rfm.xxCart[2]

    # Step 2.b: Declare detgamma and gammaUU from
    #           BSSN.ADM_in_terms_of_BSSN;
    #           simplify detgamma & gammaUU expressions,
    #           which expedites Psi4 codegen.
    detgamma = sp.simplify(AB.detgamma)
    gammaUU = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            gammaUU[i][j] = sp.simplify(AB.gammaUU[i][j])

    # 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 v3U
    v3U = ixp.zerorank1()
    LeviCivitaSymbolDDD = ixp.LeviCivitaSymbol_dim3_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] * LeviCivitaSymbolDDD[d][b][c] * v1U[b] * v2U[c]

    # Step 2.g.i: Simplify expressions for v1U,v2U,v3U. This greatly expedites the C code generation (~10x faster)
    #             Drat. Simplification with certain versions of SymPy & coord systems results in a hang. Let's just
    #             evaluate the expressions so the most trivial optimizations can be performed.
    for a in range(DIM):
        v1U[a] = v1U[a].doit()  # sp.simplify(v1U[a])
        v2U[a] = v2U[a].doit()  # sp.simplify(v2U[a])
        v3U[a] = v3U[a].doit()  # sp.simplify(v3U[a])

    # Step 2.h: Define omega_{ij}
    omegaDD = ixp.zerorank2()
    gammaDD = AB.gammaDD

    def v_vectorDU(v1U, v2U, v3U, i, a):
        if i == 0:
            return v1U[a]
        if i == 1:
            return v2U[a]
        if i == 2:
            return v3U[a]
        print("ERROR: unknown vector!")
        sys.exit(1)

    def update_omega(omegaDD, i, j, v1U, v2U, v3U, gammaDD):
        omegaDD[i][j] = sp.sympify(0)
        for a in range(DIM):
            for b in range(DIM):
                omegaDD[i][j] += v_vectorDU(v1U, v2U, v3U, i, a) * v_vectorDU(
                    v1U, v2U, v3U, j, b) * gammaDD[a][b]

    # Step 2.i: Define e^a_i. Note that:
    #           omegaDD[0][0] = \omega_{11} above;
    #           omegaDD[1][1] = \omega_{22} above, etc.
    # First e_1^a: Orthogonalize & normalize:
    e1U = ixp.zerorank1()
    update_omega(omegaDD, 0, 0, v1U, v2U, v3U, gammaDD)
    for a in range(DIM):
        e1U[a] = v1U[a] / sp.sqrt(omegaDD[0][0])

    # Next e_2^a: First orthogonalize:
    e2U = ixp.zerorank1()
    update_omega(omegaDD, 0, 1, e1U, v2U, v3U, gammaDD)
    for a in range(DIM):
        e2U[a] = (v2U[a] - omegaDD[0][1] * e1U[a])
    # Then normalize:
    update_omega(omegaDD, 1, 1, e1U, e2U, v3U, gammaDD)
    for a in range(DIM):
        e2U[a] /= sp.sqrt(omegaDD[1][1])

    # Next e_3^a: First orthogonalize:
    e3U = ixp.zerorank1()
    update_omega(omegaDD, 0, 2, e1U, e2U, v3U, gammaDD)
    update_omega(omegaDD, 1, 2, e1U, e2U, v3U, gammaDD)
    for a in range(DIM):
        e3U[a] = (v3U[a] - omegaDD[0][2] * e1U[a] - omegaDD[1][2] * e2U[a])
    # Then normalize:
    update_omega(omegaDD, 2, 2, e1U, e2U, e3U, gammaDD)
    for a in range(DIM):
        e3U[a] /= sp.sqrt(omegaDD[2][2])

    # Step 2.j: Construct l^mu, n^mu, and m^mu, based on r^mu, theta^mu, phi^mu, and u^mu:
    r4U = ixp.zerorank1(DIM=4)
    u4U = ixp.zerorank1(DIM=4)
    theta4U = ixp.zerorank1(DIM=4)
    phi4U = ixp.zerorank1(DIM=4)

    for a in range(DIM):
        r4U[a + 1] = e2U[a]
        theta4U[a + 1] = e3U[a]
        phi4U[a + 1] = e1U[a]

    # FIXME? assumes alpha=1, beta^i = 0
    if par.parval_from_str(thismodule + "::UseCorrectUnitNormal") == "False":
        u4U[0] = 1
    else:
        # Eq. 2.116 in Baumgarte & Shapiro:
        #  n^mu = {1/alpha, -beta^i/alpha}. Note that n_mu = {alpha,0}, so n^mu n_mu = -1.
        import BSSN.BSSN_quantities as Bq
        Bq.declare_BSSN_gridfunctions_if_not_declared_already()
        Bq.BSSN_basic_tensors()
        u4U[0] = 1 / Bq.alpha
        for i in range(DIM):
            u4U[i + 1] = -Bq.betaU[i] / Bq.alpha

    l4U = ixp.zerorank1(DIM=4)
    n4U = ixp.zerorank1(DIM=4)
    mre4U = ixp.zerorank1(DIM=4)
    mim4U = ixp.zerorank1(DIM=4)

    # M_SQRT1_2 = 1 / sqrt(2) (defined in math.h on Linux)
    M_SQRT1_2 = par.Cparameters("#define", thismodule, "M_SQRT1_2", "")
    isqrt2 = M_SQRT1_2  #1/sp.sqrt(2) <- SymPy drops precision to 15 sig. digits in unit tests
    for mu in range(4):
        l4U[mu] = isqrt2 * (u4U[mu] + r4U[mu])
        n4U[mu] = isqrt2 * (u4U[mu] - r4U[mu])
        mre4U[mu] = isqrt2 * theta4U[mu]
        mim4U[mu] = isqrt2 * phi4U[mu]
def Toroidal():
    system = par.parval_from_str(thismodule + "::System_to_use")
    DIM = par.parval_from_str("grid::DIM")

    dst_basis = par.parval_from_str("reference_metric::CoordSystem")

    # Set coordinate system to Cartesian
    par.set_parval_from_str("reference_metric::CoordSystem", "Cartesian")
    rfm.reference_metric()

    global AidU, EidU, psi_ID

    x = rfm.xxCart[0]
    y = rfm.xxCart[1]
    z = rfm.xxCart[2]

    AidD_Sph = ixp.zerorank1()
    # Set coordinate transformations:
    r = sp.sqrt(x * x + y * y + z * z)
    sin_theta = z / r

    u = time + r
    v = time - r
    e_lam_u = sp.exp(-lam * u**2)
    e_lam_v = sp.exp(-lam * v**2)

    # Equation 16 from https://arxiv.org/abs/gr-qc/0201051
    AD_phi_hat = (amp*sin_theta)*( ((e_lam_v - e_lam_u)/r**2) - \
                            2*lam*(v*e_lam_v + u*e_lam_u)/r )

    AidD_Sph[2] = AD_phi_hat / (r * sin_theta)

    # Coordinate transformation from spherical to Cartesian
    AidU_Cart = ixp.zerorank1()
    Jac_dxSphU_dxCartD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            Jac_dxSphU_dxCartD[i][j] = sp.diff(rfm.xxSph[i], rfm.xxCart[j])

    #         Jac_dxCartU_dxSphD[i][j] = sp.diff(rfm.xxCart[i],rfm.xx[j])
    Jac_dxCartU_dxSphD, dummy = ixp.generic_matrix_inverter3x3(
        Jac_dxSphU_dxCartD)

    for i in range(DIM):
        for j in range(DIM):
            AidU_Cart[i] += Jac_dxCartU_dxSphD[i][j] * AidD_Sph[j]
    for i in range(DIM):
        AidU_Cart[i] = sp.simplify(AidU_Cart[i])

    # rfm is still defined in Cartesian coordinates
    cart_xx = ixp.declarerank1("cart_xx")
    for i in range(3):
        for k in range(3):
            AidU_Cart[i] = AidU_Cart[i].subs(rfm.xx[k], cart_xx[k])

    # Set coordinate system to dst_basis
    par.set_parval_from_str("reference_metric::CoordSystem", dst_basis)
    rfm.reference_metric()

    for i in range(3):
        for k in range(3):
            AidU_Cart[i] = AidU_Cart[i].subs(cart_xx[k], rfm.xxCart[k])

#     if radial_like_dst_xx0:
#         for j in range(3):
#             AidU_Cart[j] =  sp.refine(sp.simplify(AidU_Cart[j]), sp.Q.positive(rfm.xx[0]))

# Step 3: Transform BSSN tensors in Cartesian basis to destination grid basis, using center of dest. grid as origin

# Step 3.a: Next construct Jacobian and inverse Jacobian matrices:
# _Jac_dUCart_dDrfmUD is unused.
    _Jac_dUCart_dDrfmUD, Jac_dUrfm_dDCartUD = rfm.compute_Jacobian_and_inverseJacobian_tofrom_Cartesian(
    )

    # Step 3.b: Convert basis of all BSSN *vectors* from Cartesian to destination basis
    AidU = rfm.basis_transform_vectorU_from_Cartesian_to_rfmbasis(
        Jac_dUrfm_dDCartUD, AidU_Cart)

    # Define electric field --> E^i = -\partial_t A^i
    EidU = ixp.zerorank1()
    for j in range(DIM):
        EidU[j] = -sp.diff(AidU[j], time)

    psi_ID = sp.sympify(0)

    if system == "System_II":
        global Gamma_ID
        Gamma_ID = sp.sympify(0)
        print('Currently using ' + system + ' initial data')
    elif system == "System_I":
        print('Currently using ' + system + ' initial data')
    else:
        print(
            "Invalid choice of system: System_to_use must be either System_I or System_II"
        )
        sys.exit(1)
示例#9
0
def generate_C_code_for_Stilde_flux(
        out_dir,
        inputs_provided=False,
        alpha_face=None,
        gamma_faceDD=None,
        beta_faceU=None,
        Valenciav_rU=None,
        B_rU=None,
        Valenciav_lU=None,
        B_lU=None,
        Stilde_flux_HLLED=None,
        sqrt4pi=None,
        outCparams="outCverbose=False,CSE_sorting=none",
        write_cmax_cmin=False,
        gamma_faceUU=None,
        phi_face=None):
    if not inputs_provided:
        # 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)
        sqrt4pi = par.Cparameters("REAL", thismodule, "sqrt4pi",
                                  "sqrt(4.0*M_PI)")

        # We'll also need to store the results of the HLLE step between functions.
        Stilde_flux_HLLED = ixp.register_gridfunctions_for_single_rank1(
            "AUXEVOL", "Stilde_flux_HLLED")

    if write_cmax_cmin:
        # In the staggered case, we will also want to output cmax and cmin
        # If we want to write cmax and cmin, we will need to be able to change auxevol_gfs:
        input_params_for_Stilde_flux = "const paramstruct *params,REAL *auxevol_gfs,REAL *rhs_gfs"
    else:
        input_params_for_Stilde_flux = "const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs"
    if gamma_faceUU is None:
        gamma_faceUU, unusedgammaDET = ixp.generic_matrix_inverter3x3(
            gamma_faceDD)

    if write_cmax_cmin:
        name_suffixes = ["_x", "_y", "_z"]

    for flux_dirn in range(3):
        calculate_Stilde_flux(flux_dirn,alpha_face,gamma_faceDD,beta_faceU,\
                              Valenciav_rU,B_rU,Valenciav_lU,B_lU,sqrt4pi,gamma_faceUU,phi_face)

        Stilde_flux_to_print = [
            lhrh(lhs=gri.gfaccess("out_gfs", "Stilde_flux_HLLED0"),
                 rhs=Stilde_fluxD[0]),
            lhrh(lhs=gri.gfaccess("out_gfs", "Stilde_flux_HLLED1"),
                 rhs=Stilde_fluxD[1]),
            lhrh(lhs=gri.gfaccess("out_gfs", "Stilde_flux_HLLED2"),
                 rhs=Stilde_fluxD[2]),
        ]

        if write_cmax_cmin:
            Stilde_flux_to_print = Stilde_flux_to_print \
                                  +[
                                    lhrh(lhs=gri.gfaccess("out_gfs","cmax"+name_suffixes[flux_dirn]),rhs=chsp.cmax),
                                    lhrh(lhs=gri.gfaccess("out_gfs","cmin"+name_suffixes[flux_dirn]),rhs=chsp.cmin),
                                   ]

        desc = "Compute the flux term of all 3 components of tilde{S}_i on the left face in the " + str(
            flux_dirn) + "direction for all components."
        name = "calculate_Stilde_flux_D" + str(flux_dirn)
        Ccode_function = outCfunction(
            outfile="returnstring",
            desc=desc,
            name=name,
            params=input_params_for_Stilde_flux,
            body=fin.FD_outputC("returnstring",
                                Stilde_flux_to_print,
                                params=outCparams).replace("IDX4", "IDX4S"),
            loopopts="InteriorPoints",
            rel_path_for_Cparams=os.path.join("../")).replace(
                "NGHOSTS+Nxx0", "NGHOSTS+Nxx0+1").replace(
                    "NGHOSTS+Nxx1",
                    "NGHOSTS+Nxx1+1").replace("NGHOSTS+Nxx2", "NGHOSTS+Nxx2+1")

        with open(os.path.join(out_dir, name + ".h"), "w") as file:
            file.write(Ccode_function)

    pre_body = """// Notice in the loop below that we go from 3 to cctk_lsh-3 for i, j, AND k, even though
    //   we are only computing the flux in one direction. This is because in the end,
    //   we only need the rhs's from 3 to cctk_lsh-3 for i, j, and k.
    const REAL invdxi[4] = {1e100,invdx0,invdx1,invdx2};
    const REAL invdx = invdxi[flux_dirn];"""

    FD_body = """const int index = IDX3S(i0,i1,i2);
const int indexp1 = IDX3S(i0+kronecker_delta[flux_dirn][0],i1+kronecker_delta[flux_dirn][1],i2+kronecker_delta[flux_dirn][2]);

rhs_gfs[IDX4ptS(STILDED0GF,index)] += (auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED0GF,index)]     - auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED0GF,indexp1)]    ) * invdx;
rhs_gfs[IDX4ptS(STILDED1GF,index)] += (auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED1GF,index)]     - auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED1GF,indexp1)]    ) * invdx;
rhs_gfs[IDX4ptS(STILDED2GF,index)] += (auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED2GF,index)]     - auxevol_gfs[IDX4ptS(STILDE_FLUX_HLLED2GF,indexp1)]    ) * invdx;"""

    desc = "Compute the difference in the flux of StildeD on the opposite faces in flux_dirn for all components."
    name = "calculate_Stilde_rhsD"
    outCfunction(
        outfile=os.path.join(out_dir, name + ".h"),
        desc=desc,
        name=name,
        params=
        "const int flux_dirn,const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs",
        preloop=pre_body,
        body=FD_body,
        loopopts="InteriorPoints",
        rel_path_for_Cparams=os.path.join("../"))
def Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear(
        CoordType_in, Sph_r_th_ph_or_Cart_xyz, gammaDD_inSphorCart,
        KDD_inSphorCart, alpha_inSphorCart, betaU_inSphorCart, BU_inSphorCart):
    # This routine converts the ADM variables
    #    $$\left\{\gamma_{ij}, K_{ij}, \alpha, \beta^i\right\}$$
    #    in Spherical or Cartesian basis+coordinates, first to the BSSN variables
    #    in the chosen reference_metric::CoordSystem coordinate system+basis:
    # $$\left\{\bar{\gamma}_{i j},\bar{A}_{i j},\phi, K, \bar{\Lambda}^{i}, \alpha, \beta^i, B^i\right\},$$
    #    and then to the rescaled variables:
    # $$\left\{h_{i j},a_{i j},\phi, K, \lambda^{i}, \alpha, \mathcal{V}^i, \mathcal{B}^i\right\}.$$

    # The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations.
    #    To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in
    #    the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3.
    # Step 0: Set spatial dimension (must be 3 for BSSN)
    DIM = 3

    # Step 0: Copy gammaSphDD_in to gammaSphDD, KSphDD_in to KSphDD, etc.
    #    This ensures that the input arrays are not modified below;
    #    modifying them would result in unexpected effects outside
    #    this function.
    alphaSphorCart = alpha_inSphorCart
    betaSphorCartU = ixp.zerorank1()
    BSphorCartU = ixp.zerorank1()
    gammaSphorCartDD = ixp.zerorank2()
    KSphorCartDD = ixp.zerorank2()
    for i in range(DIM):
        betaSphorCartU[i] = betaU_inSphorCart[i]
        BSphorCartU[i] = BU_inSphorCart[i]
        for j in range(DIM):
            gammaSphorCartDD[i][j] = gammaDD_inSphorCart[i][j]
            KSphorCartDD[i][j] = KDD_inSphorCart[i][j]

    # Make sure that rfm.reference_metric() has been called.
    #    We'll need the variables it defines throughout this module.
    if rfm.have_already_called_reference_metric_function == False:
        print(
            "Error. Called Convert_Spherical_ADM_to_BSSN_curvilinear() without"
        )
        print(
            "       first setting up reference metric, by calling rfm.reference_metric()."
        )
        exit(1)

    # Step 1: All input quantities are in terms of r,th,ph or x,y,z. We want them in terms
    #         of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace
    #         r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2
    #         as defined for this particular reference metric in reference_metric.py's
    #         xxSph[] or xxCart[], respectively:
    # Note that substitution only works when the variable is not an integer. Hence the
    #         if isinstance(...,...) stuff:
    def sympify_integers__replace_rthph_or_Cartxyz(obj, rthph_or_xyz,
                                                   rthph_or_xyz_of_xx):
        if isinstance(obj, int):
            return sp.sympify(obj)
        else:
            return obj.subs(rthph_or_xyz[0], rthph_or_xyz_of_xx[0]).\
                subs(rthph_or_xyz[1], rthph_or_xyz_of_xx[1]).\
                subs(rthph_or_xyz[2], rthph_or_xyz_of_xx[2])

    r_th_ph_or_Cart_xyz_of_xx = []
    if CoordType_in == "Spherical":
        r_th_ph_or_Cart_xyz_of_xx = rfm.xxSph
    elif CoordType_in == "Cartesian":
        r_th_ph_or_Cart_xyz_of_xx = rfm.xxCart
    else:
        print(
            "Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords."
        )
        exit(1)

    alphaSphorCart = sympify_integers__replace_rthph_or_Cartxyz(
        alphaSphorCart, Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx)
    for i in range(DIM):
        betaSphorCartU[i] = sympify_integers__replace_rthph_or_Cartxyz(
            betaSphorCartU[i], Sph_r_th_ph_or_Cart_xyz,
            r_th_ph_or_Cart_xyz_of_xx)
        BSphorCartU[i] = sympify_integers__replace_rthph_or_Cartxyz(
            BSphorCartU[i], Sph_r_th_ph_or_Cart_xyz, r_th_ph_or_Cart_xyz_of_xx)
        for j in range(DIM):
            gammaSphorCartDD[i][
                j] = sympify_integers__replace_rthph_or_Cartxyz(
                    gammaSphorCartDD[i][j], Sph_r_th_ph_or_Cart_xyz,
                    r_th_ph_or_Cart_xyz_of_xx)
            KSphorCartDD[i][j] = sympify_integers__replace_rthph_or_Cartxyz(
                KSphorCartDD[i][j], Sph_r_th_ph_or_Cart_xyz,
                r_th_ph_or_Cart_xyz_of_xx)

    # Step 2: All ADM initial data quantities are now functions of xx0,xx1,xx2, but
    #         they are still in the Spherical or Cartesian basis. We can now directly apply
    #         Jacobian transformations to get them in the correct xx0,xx1,xx2 basis:

    # alpha is a scalar, so no Jacobian transformation is necessary.
    alpha = alphaSphorCart

    Jac_dUSphorCart_dDrfmUD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff(
                r_th_ph_or_Cart_xyz_of_xx[i], rfm.xx[j])

    Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3(
        Jac_dUSphorCart_dDrfmUD)

    betaU = ixp.zerorank1()
    BU = ixp.zerorank1()
    gammaDD = ixp.zerorank2()
    KDD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j]
            BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j]
            for k in range(DIM):
                for l in range(DIM):
                    gammaDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][
                        i] * Jac_dUSphorCart_dDrfmUD[l][j] * gammaSphorCartDD[
                            k][l]
                    KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][
                        i] * Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l]

    # Step 3: All ADM quantities were input into this function in the Spherical or Cartesian
    #         basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above,
    #         we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2.
    #         Here we convert ADM quantities to their BSSN Curvilinear counterparts:

    # Step 3.1: Convert ADM $\gamma_{ij}$ to BSSN $\bar{\gamma}_{ij}$:
    #   We have (Eqs. 2 and 3 of [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)):
    # \bar{\gamma}_{i j} = \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \gamma_{ij}.
    gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD)
    gammabarDD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            gammabarDD[i][j] = (rfm.detgammahat / gammaDET)**(sp.Rational(
                1, 3)) * gammaDD[i][j]

    # Step 3.2: Convert the extrinsic curvature $K_{ij}$ to the trace-free extrinsic
    #           curvature $\bar{A}_{ij}$, plus the trace of the extrinsic curvature $K$,
    #           where (Eq. 3 of [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)):

    # K = \gamma^{ij} K_{ij}, and
    # \bar{A}_{ij} &= \left(\frac{\bar{\gamma}}{\gamma}\right)^{1/3} \left(K_{ij} - \frac{1}{3} \gamma_{ij} K \right)
    trK = sp.sympify(0)
    for i in range(DIM):
        for j in range(DIM):
            trK += gammaUU[i][j] * KDD[i][j]

    AbarDD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            AbarDD[i][j] = (rfm.detgammahat / gammaDET)**(sp.Rational(
                1, 3)) * (KDD[i][j] - sp.Rational(1, 3) * gammaDD[i][j] * trK)

    # Step 3.3: Define $\bar{\Lambda}^i$ (Eqs. 4 and 5 of [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)):

    # \bar{\Lambda}^i = \bar{\gamma}^{jk}\left(\bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk}\right).
    gammabarUU, gammabarDET = ixp.symm_matrix_inverter3x3(gammabarDD)

    # First compute \bar{\Gamma}^i_{jk}:
    GammabarUDD = ixp.zerorank3()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                for l in range(DIM):
                    GammabarUDD[i][j][k] += sp.Rational(
                        1, 2) * gammabarUU[i][l] * (
                            sp.diff(gammabarDD[l][j], rfm.xx[k]) +
                            sp.diff(gammabarDD[l][k], rfm.xx[j]) -
                            sp.diff(gammabarDD[j][k], rfm.xx[l]))
    # Next evaluate \bar{\Lambda}^i, based on GammabarUDD above and GammahatUDD
    #       (from the reference metric):
    LambdabarU = ixp.zerorank1()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                LambdabarU[i] += gammabarUU[j][k] * (GammabarUDD[i][j][k] -
                                                     rfm.GammahatUDD[i][j][k])

    # Step 3.4: Set the conformal factor variable $\texttt{cf}$, which is set
    #           by the "BSSN_quantities::ConformalFactor" parameter. For example if
    #           "ConformalFactor" is set to "phi", we can use Eq. 3 of
    #           [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf),
    #           which in arbitrary coordinates is written:

    # \phi = \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right).

    # Alternatively if "BSSN_quantities::ConformalFactor" is set to "chi", then

    # \chi = e^{-4 \phi} = \exp\left(-4 \frac{1}{12} \left(\frac{\gamma}{\bar{\gamma}}\right)\right)
    #      = \exp\left(-\frac{1}{3} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) = \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/3}.
    #
    # Finally if "BSSN_quantities::ConformalFactor" is set to "W", then

    # W = e^{-2 \phi} = \exp\left(-2 \frac{1}{12} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) =
    # \exp\left(-\frac{1}{6} \log\left(\frac{\gamma}{\bar{\gamma}}\right)\right) =
    # \left(\frac{\gamma}{\bar{\gamma}}\right)^{-1/6}.

    cf = sp.sympify(0)

    if par.parval_from_str("ConformalFactor") == "phi":
        cf = sp.Rational(1, 12) * sp.log(gammaDET / gammabarDET)
    elif par.parval_from_str("ConformalFactor") == "chi":
        cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 3))
    elif par.parval_from_str("ConformalFactor") == "W":
        cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 6))
    else:
        print("Error ConformalFactor type = \"" +
              par.parval_from_str("ConformalFactor") + "\" unknown.")
        exit(1)

    # Step 4: Rescale tensorial quantities according to the prescription described in
    #         the [BSSN in curvilinear coordinates tutorial module](Tutorial-BSSNCurvilinear.ipynb)
    #         (also [Ruchlin *et al.*](https://arxiv.org/pdf/1712.07658.pdf)):
    #
    # h_{ij} &= (\bar{\gamma}_{ij} - \hat{\gamma}_{ij})/\text{ReDD[i][j]}\\
    # a_{ij} &= \bar{A}_{ij}/\text{ReDD[i][j]}\\
    # \lambda^i &= \bar{\Lambda}^i/\text{ReU[i]}\\
    # \mathcal{V}^i &= \beta^i/\text{ReU[i]}\\
    # \mathcal{B}^i &= B^i/\text{ReU[i]}\\
    hDD = ixp.zerorank2()
    aDD = ixp.zerorank2()
    lambdaU = ixp.zerorank1()
    vetU = ixp.zerorank1()
    betU = ixp.zerorank1()
    for i in range(DIM):
        lambdaU[i] = LambdabarU[i] / rfm.ReU[i]
        vetU[i] = betaU[i] / rfm.ReU[i]
        betU[i] = BU[i] / rfm.ReU[i]
        for j in range(DIM):
            hDD[i][j] = (gammabarDD[i][j] - rfm.ghatDD[i][j]) / rfm.ReDD[i][j]
            aDD[i][j] = AbarDD[i][j] / rfm.ReDD[i][j]
    #print(sp.mathematica_code(hDD[0][0]))

    # Step 5: Return the BSSN Curvilinear variables in the desired xx0,xx1,xx2
    #         basis, and as functions of the consistent xx0,xx1,xx2 coordinates.
    return cf, hDD, lambdaU, aDD, trK, alpha, vetU, betU
def Convert_Spherical_or_Cartesian_ADM_to_BSSN_curvilinear(CoordType_in, ADM_input_function_name,
                                                           Ccodesdir = "BSSN", pointer_to_ID_inputs=False,loopopts=",oldloops"):
    # The ADM & BSSN formalisms only work in 3D; they are 3+1 decompositions of Einstein's equations.
    #    To implement axisymmetry or spherical symmetry, simply set all spatial derivatives in
    #    the relevant angular directions to zero; DO NOT SET DIM TO ANYTHING BUT 3.

    # Step 0: Set spatial dimension (must be 3 for BSSN)
    DIM = 3

    # Step 1: All ADM initial data quantities are now functions of xx0,xx1,xx2, but
    #         they are still in the Spherical or Cartesian basis. We can now directly apply
    #         Jacobian transformations to get them in the correct xx0,xx1,xx2 basis:

    #         All input quantities are in terms of r,th,ph or x,y,z. We want them in terms
    #         of xx0,xx1,xx2, so here we call sympify_integers__replace_rthph() to replace
    #         r,th,ph or x,y,z, respectively, with the appropriate functions of xx0,xx1,xx2
    #         as defined for this particular reference metric in reference_metric.py's
    #         xxSph[] or xx_to_Cart[], respectively:

    # Define the input variables:
    gammaSphorCartDD = ixp.declarerank2("gammaSphorCartDD", "sym01")
    KSphorCartDD = ixp.declarerank2("KSphorCartDD", "sym01")
    alphaSphorCart = sp.symbols("alphaSphorCart")
    betaSphorCartU = ixp.declarerank1("betaSphorCartU")
    BSphorCartU = ixp.declarerank1("BSphorCartU")

    # Make sure that rfm.reference_metric() has been called.
    #    We'll need the variables it defines throughout this module.
    if rfm.have_already_called_reference_metric_function == False:
        print("Error. Called Convert_Spherical_ADM_to_BSSN_curvilinear() without")
        print("       first setting up reference metric, by calling rfm.reference_metric().")
        sys.exit(1)

    r_th_ph_or_Cart_xyz_oID_xx = []
    if CoordType_in == "Spherical":
        r_th_ph_or_Cart_xyz_oID_xx = rfm.xxSph
    elif CoordType_in == "Cartesian":
        r_th_ph_or_Cart_xyz_oID_xx = rfm.xx_to_Cart
    else:
        print("Error: Can only convert ADM Cartesian or Spherical initial data to BSSN Curvilinear coords.")
        sys.exit(1)

    # Step 2: All ADM initial data quantities are now functions of xx0,xx1,xx2, but
    #         they are still in the Spherical or Cartesian basis. We can now directly apply
    #         Jacobian transformations to get them in the correct xx0,xx1,xx2 basis:

    # alpha is a scalar, so no Jacobian transformation is necessary.
    alpha = alphaSphorCart

    Jac_dUSphorCart_dDrfmUD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            Jac_dUSphorCart_dDrfmUD[i][j] = sp.diff(r_th_ph_or_Cart_xyz_oID_xx[i], rfm.xx[j])

    Jac_dUrfm_dDSphorCartUD, dummyDET = ixp.generic_matrix_inverter3x3(Jac_dUSphorCart_dDrfmUD)

    betaU = ixp.zerorank1()
    BU = ixp.zerorank1()
    gammaDD = ixp.zerorank2()
    KDD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            betaU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * betaSphorCartU[j]
            BU[i] += Jac_dUrfm_dDSphorCartUD[i][j] * BSphorCartU[j]
            for k in range(DIM):
                for l in range(DIM):
                    gammaDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * \
                                     gammaSphorCartDD[k][l]
                    KDD[i][j] += Jac_dUSphorCart_dDrfmUD[k][i] * Jac_dUSphorCart_dDrfmUD[l][j] * KSphorCartDD[k][l]

    # Step 3: All ADM quantities were input into this function in the Spherical or Cartesian
    #         basis, as functions of r,th,ph or x,y,z, respectively. In Steps 1 and 2 above,
    #         we converted them to the xx0,xx1,xx2 basis, and as functions of xx0,xx1,xx2.
    #         Here we convert ADM quantities in the "rfm" basis to their BSSN Curvilinear
    #         counterparts, for all BSSN quantities *except* lambda^i:
    import BSSN.BSSN_in_terms_of_ADM as BitoA
    BitoA.gammabarDD_hDD(gammaDD)
    BitoA.trK_AbarDD_aDD(gammaDD, KDD)
    BitoA.cf_from_gammaDD(gammaDD)
    BitoA.betU_vetU(betaU, BU)
    hDD = BitoA.hDD
    trK = BitoA.trK
    aDD = BitoA.aDD
    cf = BitoA.cf
    vetU = BitoA.vetU
    betU = BitoA.betU

    # Step 4: Compute $\bar{\Lambda}^i$ (Eqs. 4 and 5 of
    #         [Baumgarte *et al.*](https://arxiv.org/pdf/1211.6632.pdf)),
    #         from finite-difference derivatives of rescaled metric
    #         quantities $h_{ij}$:

    # \bar{\Lambda}^i = \bar{\gamma}^{jk}\left(\bar{\Gamma}^i_{jk} - \hat{\Gamma}^i_{jk}\right).

    # The reference_metric.py module provides us with analytic expressions for
    #         $\hat{\Gamma}^i_{jk}$, so here we need only compute
    #         finite-difference expressions for $\bar{\Gamma}^i_{jk}$, based on
    #         the values for $h_{ij}$ provided in the initial data. Once
    #         $\bar{\Lambda}^i$ has been computed, we apply the usual rescaling
    #         procedure:

    # \lambda^i = \bar{\Lambda}^i/\text{ReU[i]},

    # and then output the result to a C file using the NRPy+
    #         finite-difference C output routine.

    # We will need all BSSN gridfunctions to be defined, as well as
    #     expressions for gammabarDD_dD in terms of exact derivatives of
    #     the rescaling matrix and finite-difference derivatives of
    #     hDD's. This functionality is provided by BSSN.BSSN_unrescaled_and_barred_vars,
    #     which we call here to overwrite above definitions of gammabarDD,gammabarUU, etc.
    Bq.gammabar__inverse_and_derivs() # Provides gammabarUU and GammabarUDD
    gammabarUU    = Bq.gammabarUU
    GammabarUDD   = Bq.GammabarUDD

    # Next evaluate \bar{\Lambda}^i, based on GammabarUDD above and GammahatUDD
    #       (from the reference metric):
    LambdabarU = ixp.zerorank1()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                LambdabarU[i] += gammabarUU[j][k] * (GammabarUDD[i][j][k] - rfm.GammahatUDD[i][j][k])

    # Finally apply rescaling:
    # lambda^i = Lambdabar^i/\text{ReU[i]}
    lambdaU = ixp.zerorank1()
    for i in range(DIM):
        lambdaU[i] = LambdabarU[i] / rfm.ReU[i]

    if ADM_input_function_name == "DoNotOutputADMInputFunction":
        return hDD,aDD,trK,vetU,betU,alpha,cf,lambdaU

    # Step 5.A: Output files containing finite-differenced lambdas.
    outCparams = "preindent=1,outCfileaccess=a,outCverbose=False,includebraces=False"
    lambdaU_expressions = [lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU0"), rhs=lambdaU[0]),
                           lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU1"), rhs=lambdaU[1]),
                           lhrh(lhs=gri.gfaccess("in_gfs", "lambdaU2"), rhs=lambdaU[2])]

    desc = "Output lambdaU[i] for BSSN, built using finite-difference derivatives."
    name = "ID_BSSN_lambdas"
    params = "const paramstruct *restrict params,REAL *restrict xx[3],REAL *restrict in_gfs"
    preloop = ""
    enableCparameters=True
    if "oldloops" in loopopts:
        params = "const int Nxx[3],const int Nxx_plus_2NGHOSTS[3],REAL *xx[3],const REAL dxx[3],REAL *in_gfs"
        enableCparameters=False
        preloop = """
const REAL invdx0 = 1.0/dxx[0];
const REAL invdx1 = 1.0/dxx[1];
const REAL invdx2 = 1.0/dxx[2];
"""
    outCfunction(
        outfile=os.path.join(Ccodesdir, name + ".h"), desc=desc, name=name, params=params,
        preloop=preloop,
        body=fin.FD_outputC("returnstring", lambdaU_expressions, outCparams),
        loopopts="InteriorPoints,Read_xxs"+loopopts, enableCparameters=enableCparameters)

    # Step 5: Output all ADM-to-BSSN expressions to a C function. This function
    #         must first call the ID_ADM_SphorCart() defined above. Using these
    #         Spherical or Cartesian data, it sets up all quantities needed for
    #         BSSNCurvilinear initial data, *except* $\lambda^i$, which must be
    #         computed from numerical data using finite-difference derivatives.
    ID_inputs_param = "ID_inputs other_inputs,"
    if pointer_to_ID_inputs == True:
        ID_inputs_param = "ID_inputs *other_inputs,"

    desc = "Write BSSN variables in terms of ADM variables at a given point xx0,xx1,xx2"
    name = "ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs"
    enableCparameters=True
    params = "const paramstruct *restrict params, "
    if "oldloops" in loopopts:
        enableCparameters=False
        params = ""
    params += "const int i0i1i2[3], const REAL xx0xx1xx2[3]," + ID_inputs_param + """
                    REAL *hDD00,REAL *hDD01,REAL *hDD02,REAL *hDD11,REAL *hDD12,REAL *hDD22,
                    REAL *aDD00,REAL *aDD01,REAL *aDD02,REAL *aDD11,REAL *aDD12,REAL *aDD22,
                    REAL *trK,
                    REAL *vetU0,REAL *vetU1,REAL *vetU2,
                    REAL *betU0,REAL *betU1,REAL *betU2,
                    REAL *alpha,  REAL *cf"""
    outCparams = "preindent=1,outCverbose=False,includebraces=False"
    outCfunction(
        outfile=os.path.join(Ccodesdir, name + ".h"), desc=desc, name=name, params=params,
        body="""
      REAL gammaSphorCartDD00,gammaSphorCartDD01,gammaSphorCartDD02,
           gammaSphorCartDD11,gammaSphorCartDD12,gammaSphorCartDD22;
      REAL KSphorCartDD00,KSphorCartDD01,KSphorCartDD02,
           KSphorCartDD11,KSphorCartDD12,KSphorCartDD22;
      REAL alphaSphorCart,betaSphorCartU0,betaSphorCartU1,betaSphorCartU2;
      REAL BSphorCartU0,BSphorCartU1,BSphorCartU2;
      const REAL xx0 = xx0xx1xx2[0];
      const REAL xx1 = xx0xx1xx2[1];
      const REAL xx2 = xx0xx1xx2[2];
      REAL xyz_or_rthph[3];\n""" +
             outputC(r_th_ph_or_Cart_xyz_oID_xx[0:3], ["xyz_or_rthph[0]", "xyz_or_rthph[1]", "xyz_or_rthph[2]"],
                     "returnstring",
                     outCparams + ",CSE_enable=False") + "      " + ADM_input_function_name + """(params,i0i1i2, xyz_or_rthph, other_inputs,
                       &gammaSphorCartDD00,&gammaSphorCartDD01,&gammaSphorCartDD02,
                       &gammaSphorCartDD11,&gammaSphorCartDD12,&gammaSphorCartDD22,
                       &KSphorCartDD00,&KSphorCartDD01,&KSphorCartDD02,
                       &KSphorCartDD11,&KSphorCartDD12,&KSphorCartDD22,
                       &alphaSphorCart,&betaSphorCartU0,&betaSphorCartU1,&betaSphorCartU2,
                       &BSphorCartU0,&BSphorCartU1,&BSphorCartU2);
      // Next compute all rescaled BSSN curvilinear quantities:\n""" +
             outputC([hDD[0][0], hDD[0][1], hDD[0][2], hDD[1][1], hDD[1][2], hDD[2][2],
                      aDD[0][0], aDD[0][1], aDD[0][2], aDD[1][1], aDD[1][2], aDD[2][2],
                      trK, vetU[0], vetU[1], vetU[2], betU[0], betU[1], betU[2],
                      alpha, cf],
                     ["*hDD00", "*hDD01", "*hDD02", "*hDD11", "*hDD12", "*hDD22",
                      "*aDD00", "*aDD01", "*aDD02", "*aDD11", "*aDD12", "*aDD22",
                      "*trK", "*vetU0", "*vetU1", "*vetU2", "*betU0", "*betU1", "*betU2",
                      "*alpha", "*cf"], "returnstring", params=outCparams),
        enableCparameters=enableCparameters)

    # Step 5.a: Output the driver function for the above
    #           function ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs()
    # Next write the driver function for ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs():
    desc = """Driver function for ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(),
which writes BSSN variables in terms of ADM variables at a given point xx0,xx1,xx2"""
    name = "ID_BSSN__ALL_BUT_LAMBDAs"
    params = "const paramstruct *restrict params,REAL *restrict xx[3]," + ID_inputs_param + "REAL *in_gfs"
    enableCparameters = True
    funccallparams = "params, "
    idx3replace   = "IDX3S"
    idx4ptreplace = "IDX4ptS"
    if "oldloops" in loopopts:
        params = "const int Nxx_plus_2NGHOSTS[3],REAL *xx[3]," + ID_inputs_param + "REAL *in_gfs"
        enableCparameters = False
        funccallparams = ""
        idx3replace   = "IDX3"
        idx4ptreplace = "IDX4pt"
    outCfunction(
        outfile=os.path.join(Ccodesdir, name + ".h"), desc=desc, name=name, params=params,
        body="""
const int idx = IDX3(i0,i1,i2);
const int i0i1i2[3] = {i0,i1,i2};
const REAL xx0xx1xx2[3] = {xx0,xx1,xx2};
ID_ADM_xx0xx1xx2_to_BSSN_xx0xx1xx2__ALL_BUT_LAMBDAs(""".replace("IDX3",idx3replace)+funccallparams+"""i0i1i2,xx0xx1xx2,other_inputs,
                    &in_gfs[IDX4pt(HDD00GF,idx)],&in_gfs[IDX4pt(HDD01GF,idx)],&in_gfs[IDX4pt(HDD02GF,idx)],
                    &in_gfs[IDX4pt(HDD11GF,idx)],&in_gfs[IDX4pt(HDD12GF,idx)],&in_gfs[IDX4pt(HDD22GF,idx)],
                    &in_gfs[IDX4pt(ADD00GF,idx)],&in_gfs[IDX4pt(ADD01GF,idx)],&in_gfs[IDX4pt(ADD02GF,idx)],
                    &in_gfs[IDX4pt(ADD11GF,idx)],&in_gfs[IDX4pt(ADD12GF,idx)],&in_gfs[IDX4pt(ADD22GF,idx)],
                    &in_gfs[IDX4pt(TRKGF,idx)],
                    &in_gfs[IDX4pt(VETU0GF,idx)],&in_gfs[IDX4pt(VETU1GF,idx)],&in_gfs[IDX4pt(VETU2GF,idx)],
                    &in_gfs[IDX4pt(BETU0GF,idx)],&in_gfs[IDX4pt(BETU1GF,idx)],&in_gfs[IDX4pt(BETU2GF,idx)],
                    &in_gfs[IDX4pt(ALPHAGF,idx)],&in_gfs[IDX4pt(CFGF,idx)]);
""".replace("IDX4pt",idx4ptreplace),
        loopopts="AllPoints,Read_xxs"+loopopts, enableCparameters=enableCparameters)