Пример #1
0
def betaU_derivs():
    # Step 8.i: Declare as globals all expressions that may be used
    #           outside this function, declare BSSN gridfunctions
    #           if not defined already, and set DIM=3.
    global betaU_dD, betaU_dupD, betaU_dDD
    hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha = declare_BSSN_gridfunctions_if_not_declared_already(
    )
    DIM = 3

    # Step 8.ii: Compute the unrescaled shift vector beta^i = ReU[i]*vet^i
    vetU_dD = ixp.declarerank2("vetU_dD", "nosym")
    vetU_dupD = ixp.declarerank2("vetU_dupD",
                                 "nosym")  # Needed for upwinded \beta^i_{,j}
    vetU_dDD = ixp.declarerank3("vetU_dDD", "sym12")  # Needed for \beta^i_{,j}
    betaU_dD = ixp.zerorank2()
    betaU_dupD = ixp.zerorank2()  # Needed for, e.g., \beta^i RHS
    betaU_dDD = ixp.zerorank3()  # Needed for, e.g., \bar{\Lambda}^i RHS
    for i in range(DIM):
        for j in range(DIM):
            betaU_dD[i][
                j] = vetU_dD[i][j] * rfm.ReU[i] + vetU[i] * rfm.ReUdD[i][j]
            betaU_dupD[i][j] = vetU_dupD[i][j] * rfm.ReU[i] + vetU[
                i] * rfm.ReUdD[i][j]  # Needed for \beta^i RHS
            for k in range(DIM):
                # Needed for, e.g., \bar{\Lambda}^i RHS:
                betaU_dDD[i][j][k] = vetU_dDD[i][j][k] * rfm.ReU[i] + vetU_dD[i][j] * rfm.ReUdD[i][k] + \
                                     vetU_dD[i][k] * rfm.ReUdD[i][j] + vetU[i] * rfm.ReUdDD[i][j][k]
Пример #2
0
def declare_BSSN_gridfunctions_if_not_declared_already():
    # Step 2: Register all needed BSSN gridfunctions.

    # Declare as globals all variables that may be
    # used outside this function
    global hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha

    #   Check to see if this function has already been called.
    #   If so, do not register the gridfunctions again!
    for i in range(len(gri.glb_gridfcs_list)):
        if "hDD00" in gri.glb_gridfcs_list[i].name:
            hDD = ixp.declarerank2("hDD", "sym01")
            aDD = ixp.declarerank2("aDD", "sym01")
            lambdaU = ixp.declarerank1("lambdaU")
            vetU = ixp.declarerank1("vetU")
            betU = ixp.declarerank1("betU")
            trK, cf, alpha = sp.symbols('trK cf alpha', real=True)
            return hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha

    # Step 2.a: Register indexed quantities, using ixp.register_... functions
    hDD = ixp.register_gridfunctions_for_single_rank2("EVOL", "hDD", "sym01")
    aDD = ixp.register_gridfunctions_for_single_rank2("EVOL", "aDD", "sym01")
    lambdaU = ixp.register_gridfunctions_for_single_rank1("EVOL", "lambdaU")
    vetU = ixp.register_gridfunctions_for_single_rank1("EVOL", "vetU")
    betU = ixp.register_gridfunctions_for_single_rank1("EVOL", "betU")

    # Step 2.b: Register scalar quantities, using gri.register_gridfunctions()
    trK, cf, alpha = gri.register_gridfunctions("EVOL", ["trK", "cf", "alpha"])

    return hDD, aDD, lambdaU, vetU, betU, trK, cf, alpha
def BSSN_source_terms_for_BSSN_constraints(custom_T4UU=None):
    global sourceterm_H, sourceterm_MU

    # Step 4.a: Call BSSN_source_terms_ito_T4UU to get SDD, SD, S, & rho
    if custom_T4UU == "unrescaled BSSN source terms already given":
        # SDD and S unused, so we ignore their return values from ixp.declarerankN() below
        ixp.declarerank2("SDD", "sym01")
        SD = ixp.declarerank1("SD")
        sp.symbols("S", real=True)
        rho = sp.symbols("rho", real=True)
    else:
        _SDD, SD, _S, rho = stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars(
            "BSSN", custom_T4UU)  #_SDD,_S unused.
    PI = par.Cparameters("REAL", thismodule, ["PI"],
                         "3.14159265358979323846264338327950288")

    # Step 4.b: Add source term to the Hamiltonian constraint H
    sourceterm_H = -16 * PI * rho

    # Step 4.c: Add source term to the momentum constraint M^i
    # Step 4.c.i: Compute gammaUU in terms of BSSN quantities
    import BSSN.ADM_in_terms_of_BSSN as AitoB
    AitoB.ADM_in_terms_of_BSSN()  # Provides gammaUU
    # Step 4.c.ii: Raise S_i
    SU = ixp.zerorank1()
    for i in range(3):
        for j in range(3):
            SU[i] += AitoB.gammaUU[i][j] * SD[j]
    # Step 4.c.iii: Add source term to momentum constraint & rescale:
    sourceterm_MU = ixp.zerorank1()
    for i in range(3):
        sourceterm_MU[i] = -8 * PI * SU[i] / rfm.ReU[i]
def trK_AbarDD_aDD(gammaDD, KDD):
    global trK, AbarDD, aDD

    if gammaDD == None:
        gammaDD = ixp.declarerank2("gammaDD", "sym01")
    if KDD == None:
        KDD = ixp.declarerank2("KDD", "sym01")

    if rfm.have_already_called_reference_metric_function == False:
        print(
            "BSSN.BSSN_in_terms_of_ADM.trK_AbarDD(): Must call reference_metric() first!"
        )
        sys.exit(1)
    # \bar{gamma}_{ij} = (\frac{\bar{gamma}}{gamma})^{1/3}*gamma_{ij}.
    gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD)
    # K = gamma^{ij} K_{ij}, and
    # \bar{A}_{ij} &= (\frac{\bar{gamma}}{gamma})^{1/3}*(K_{ij} - \frac{1}{3}*gamma_{ij}*K)
    trK = sp.sympify(0)
    for i in range(DIM):
        for j in range(DIM):
            trK += gammaUU[i][j] * KDD[i][j]

    AbarDD = ixp.zerorank2()
    aDD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            AbarDD[i][j] = (rfm.detgammahat / gammaDET)**(sp.Rational(
                1, 3)) * (KDD[i][j] - sp.Rational(1, 3) * gammaDD[i][j] * trK)
            aDD[i][j] = AbarDD[i][j] / rfm.ReDD[i][j]
Пример #5
0
def generate_everything_for_UnitTesting():
    # First define hydrodynamical quantities
    u4U = ixp.declarerank1("u4U", DIM=4)
    rho_b, P, epsilon = sp.symbols('rho_b P epsilon', real=True)

    # Then ADM quantities
    gammaDD = ixp.declarerank2("gammaDD", "sym01", DIM=3)
    KDD = ixp.declarerank2("KDD", "sym01", DIM=3)
    betaU = ixp.declarerank1("betaU", DIM=3)
    alpha = sp.symbols('alpha', real=True)

    # First compute stress-energy tensor T4UU and T4UD:
    compute_T4UU(gammaDD, betaU, alpha, rho_b, P, epsilon, u4U)
    compute_T4UD(gammaDD, betaU, alpha, T4UU)

    # Next sqrt(gamma)
    compute_sqrtgammaDET(gammaDD)

    # Compute conservative variables in terms of primitive variables
    compute_rho_star(alpha, sqrtgammaDET, rho_b, u4U)
    compute_tau_tilde(alpha, sqrtgammaDET, T4UU, rho_star)
    compute_S_tildeD(alpha, sqrtgammaDET, T4UD)

    # Then compute v^i from u^mu
    compute_vU_from_u4U__no_speed_limit(u4U)

    # Next compute fluxes of conservative variables
    compute_rho_star_fluxU(vU, rho_star)
    compute_tau_tilde_fluxU(alpha, sqrtgammaDET, vU, T4UU, rho_star)
    compute_S_tilde_fluxUD(alpha, sqrtgammaDET, T4UD)

    # Then declare derivatives & compute g4DD_zerotimederiv_dD
    gammaDD_dD = ixp.declarerank3("gammaDD_dD", "sym01", DIM=3)
    betaU_dD = ixp.declarerank2("betaU_dD", "nosym", DIM=3)
    alpha_dD = ixp.declarerank1("alpha_dD", DIM=3)
    compute_g4DD_zerotimederiv_dD(gammaDD, betaU, alpha, gammaDD_dD, betaU_dD,
                                  alpha_dD)

    # Then compute source terms on tau_tilde and S_tilde equations
    compute_s_source_term(KDD, betaU, alpha, sqrtgammaDET, alpha_dD, T4UU)
    compute_S_tilde_source_termD(alpha, sqrtgammaDET, g4DD_zerotimederiv_dD,
                                 T4UU)

    # Then compute the 4-velocities in terms of an input Valencia 3-velocity testValenciavU[i]
    testValenciavU = ixp.declarerank1("testValenciavU", DIM=3)
    u4U_in_terms_of_ValenciavU__rescale_ValenciavU_by_applying_speed_limit(
        alpha, betaU, gammaDD, testValenciavU)

    # Finally compute the 4-velocities in terms of an input 3-velocity testvU[i] = u^i/u^0
    testvU = ixp.declarerank1("testvU", DIM=3)
    u4U_in_terms_of_vU__rescale_vU_by_applying_speed_limit(
        alpha, betaU, gammaDD, testvU)
Пример #6
0
def BSSN_or_ADM_ito_g4DD(inputvars, g4DD=None):
    # Step 0: Declare output variables as globals, to make interfacing with other modules/functions easier
    if inputvars == "ADM":
        global gammaDD, betaU, alpha
    elif inputvars == "BSSN":
        global hDD, cf, vetU, alpha
    else:
        print("inputvars = " + str(inputvars) +
              " not supported. Please choose ADM or BSSN.")
        sys.exit(1)

    # Step 1: declare g4DD as symmetric rank-4 tensor:
    g4DD_is_input_into_this_function = True
    if g4DD == None:
        g4DD = ixp.declarerank2("g4DD", "sym01", DIM=4)
        g4DD_is_input_into_this_function = False

    # Step 2: Compute gammaDD & betaD
    betaD = ixp.zerorank1()
    gammaDD = ixp.zerorank2()
    for i in range(3):
        betaD[i] = g4DD[0][i]
        for j in range(3):
            gammaDD[i][j] = g4DD[i + 1][j + 1]

    # Step 3: Compute betaU
    # Step 3.a: Compute gammaUU based on provided gammaDD
    gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD)

    # Step 3.b: Use gammaUU to raise betaU
    betaU = ixp.zerorank1()
    for i in range(3):
        for j in range(3):
            betaU[i] += gammaUU[i][j] * betaD[j]

    # Step 4:   Compute alpha = sqrt(beta^2 - g_{00}):
    # Step 4.a: Compute beta^2 = beta^k beta_k:
    beta_squared = sp.sympify(0)
    for k in range(3):
        beta_squared += betaU[k] * betaD[k]

    # Step 4.b: alpha = sqrt(beta^2 - g_{00}):
    if g4DD_is_input_into_this_function == False:
        alpha = sp.sqrt(sp.simplify(beta_squared) - g4DD[0][0])
    else:
        alpha = sp.sqrt(beta_squared - g4DD[0][0])

    # Step 5: If inputvars == "ADM", we are finished. Return.
    if inputvars == "ADM":
        return

    # Step 6: If inputvars == "BSSN", convert ADM to BSSN
    import BSSN.BSSN_in_terms_of_ADM as BitoA
    dummyBU = ixp.zerorank1()
    BitoA.gammabarDD_hDD(gammaDD)
    BitoA.cf_from_gammaDD(gammaDD)
    BitoA.betU_vetU(betaU, dummyBU)
    hDD = BitoA.hDD
    cf = BitoA.cf
    vetU = BitoA.vetU
def cf_from_gammaDD(gammaDD):
    global cf

    if gammaDD == None:
        gammaDD = ixp.declarerank2("gammaDD", "sym01")

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

    cf = sp.sympify(0)

    if par.parval_from_str("EvolvedConformalFactor_cf") == "phi":
        # phi = \frac{1}{12} log(\frac{gamma}{\bar{gamma}}).
        cf = sp.Rational(1, 12) * sp.log(gammaDET / gammabarDET)
    elif par.parval_from_str("EvolvedConformalFactor_cf") == "chi":
        # chi = exp(-4*phi) = exp(-4*\frac{1}{12}*(\frac{gamma}{\bar{gamma}}))
        #      = exp(-\frac{1}{3}*log(\frac{gamma}{\bar{gamma}})) = (\frac{gamma}{\bar{gamma}})^{-1/3}.
        #
        cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 3))
    elif par.parval_from_str("EvolvedConformalFactor_cf") == "W":
        # W = exp(-2*phi) = exp(-2*\frac{1}{12}*log(\frac{gamma}{\bar{gamma}}))
        #   = exp(-\frac{1}{6}*log(\frac{gamma}{\bar{gamma}})) = (\frac{gamma}{bar{gamma}})^{-1/6}.
        cf = (gammaDET / gammabarDET)**(-sp.Rational(1, 6))
    else:
        print("Error EvolvedConformalFactor_cf type = \"" +
              par.parval_from_str("EvolvedConformalFactor_cf") + "\" unknown.")
        sys.exit(1)
Пример #8
0
def LambdabarU_lambdaU__exact_gammaDD(gammaDD):
    global LambdabarU, lambdaU

    if gammaDD == None:
        gammaDD = ixp.declarerank2("gammaDD", "sym01")

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

    # First compute Christoffel symbols \bar{Gamma}^i_{jk}, with respect to barred metric:
    GammabarUDD = ixp.zerorank3()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                for l in range(DIM):
                    GammabarUDD[i][j][k] += sp.Rational(
                        1, 2) * gammabarUU[i][l] * (
                            sp.diff(gammabarDD[l][j], rfm.xx[k]) +
                            sp.diff(gammabarDD[l][k], rfm.xx[j]) -
                            sp.diff(gammabarDD[j][k], rfm.xx[l]))
    # Next evaluate \bar{Lambda}^i, based on GammabarUDD above and GammahatUDD
    #       (from the reference metric):
    LambdabarU = ixp.zerorank1()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                LambdabarU[i] += gammabarUU[j][k] * (GammabarUDD[i][j][k] -
                                                     rfm.GammahatUDD[i][j][k])
    lambdaU = ixp.zerorank1()
    for i in range(DIM):
        lambdaU[i] = LambdabarU[i] / rfm.ReU[i]
def write_out_functions_for_StildeD_source_term(outdir,outCparams,gammaDD,betaU,alpha,ValenciavU,BU,sqrt4pi):
    generate_memory_access_code()
    # First, we declare some dummy tensors that we will use for the codegen.
    gammaDDdD  = ixp.declarerank3("gammaDDdD","sym01",DIM=3)
    betaUdD = ixp.declarerank2("betaUdD","nosym",DIM=3)
    alphadD = ixp.declarerank1("alphadD",DIM=3)

    # We need to rerun a few of these functions with the reset lists to make sure these functions
    # don't cheat by using analytic expressions
    GRHD.compute_sqrtgammaDET(gammaDD)
    GRHD.u4U_in_terms_of_ValenciavU__rescale_ValenciavU_by_applying_speed_limit(alpha, betaU, gammaDD, ValenciavU)
    GRFFE.compute_smallb4U(gammaDD, betaU, alpha, GRHD.u4U_ito_ValenciavU, BU, sqrt4pi)
    GRFFE.compute_smallbsquared(gammaDD, betaU, alpha, GRFFE.smallb4U)
    GRFFE.compute_TEM4UU(gammaDD,betaU,alpha, GRFFE.smallb4U, GRFFE.smallbsquared,GRHD.u4U_ito_ValenciavU)
    GRHD.compute_g4DD_zerotimederiv_dD(gammaDD,betaU,alpha, gammaDDdD,betaUdD,alphadD)
    GRHD.compute_S_tilde_source_termD(alpha, GRHD.sqrtgammaDET,GRHD.g4DD_zerotimederiv_dD, GRFFE.TEM4UU)
    for i in range(3):
        desc = "Adds the source term to StildeD"+str(i)+"."
        name = "calculate_StildeD"+str(i)+"_source_term"
        outCfunction(
            outfile  = os.path.join(outdir,name+".h"), desc=desc, name=name,
            params   ="const paramstruct *params,const REAL *auxevol_gfs, REAL *rhs_gfs",
            body     = general_access \
                      +metric_deriv_access[i]\
                      +outputC(GRHD.S_tilde_source_termD[i],"Stilde_rhsD"+str(i),"returnstring",params=outCparams).replace("IDX4","IDX4S")\
                      +write_final_quantity[i],
            loopopts ="InteriorPoints",
            rel_path_for_Cparams=os.path.join("../"))
Пример #10
0
def GiRaFFE_NRPy_Afield_flux(Ccodesdir):
    cmd.mkdir(Ccodesdir)
    # Write out the code to a file.

    gammaDD = ixp.declarerank2("gammaDD", "sym01", DIM=3)
    betaU = ixp.declarerank1("betaU", DIM=3)
    alpha = sp.sympify("alpha")

    for flux_dirn in range(3):
        chsp.find_cmax_cmin(flux_dirn, gammaDD, betaU, alpha)
        Ccode_kernel = outputC([chsp.cmax, chsp.cmin], ["cmax", "cmin"],
                               "returnstring",
                               params="outCverbose=False,CSE_sorting=none")
        Ccode_kernel = Ccode_kernel.replace("cmax",
                                            "*cmax").replace("cmin", "*cmin")
        Ccode_kernel = Ccode_kernel.replace("betaU0", "betaUi").replace(
            "betaU1", "betaUi").replace("betaU2", "betaUi")

        with open(
                os.path.join(Ccodesdir,
                             "compute_cmax_cmin_dirn" + str(flux_dirn) + ".h"),
                "w") as file:
            file.write(Ccode_kernel)

    with open(os.path.join(Ccodesdir, name + ".h"), "w") as file:
        file.write(body)
Пример #11
0
def add_to_Cfunction_dict__GiRaFFE_NRPy_A2B(gammaDD, AD, BU, includes=None):
    # 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]
    # Here, we'll use the add_to_Cfunction_dict() 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"
    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])
    ])
    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"
    add_to_Cfunction_dict(includes=includes,
                          desc=desc,
                          name=name,
                          prefunc=prefunc,
                          params=params,
                          body=body,
                          loopopts=loopopts,
                          postloop=postloop)
    outC_function_dict[name] = outC_function_dict[name].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").replace(
                        "../set_Cparameters.h", "set_Cparameters.h")
Пример #12
0
def ScalarWave_RHSs():
    # Step 1: Get the spatial dimension, defined in the
    #         NRPy+ "grid" module.
    DIM = par.parval_from_str("DIM")

    # Step 2: Register gridfunctions that are needed as input
    #         to the scalar wave RHS expressions.
    uu, vv = gri.register_gridfunctions("EVOL", ["uu", "vv"])

    # Step 3: Declare the rank-2 indexed expression \partial_{ij} u,
    #         which is symmetric about interchange of indices i and j
    #         Derivative variables like these must have an underscore
    #         in them, so the finite difference module can parse the
    #         variable name properly.
    uu_dDD = ixp.declarerank2("uu_dDD", "sym01")

    # Step 4: Specify RHSs as global variables,
    #         to enable access outside this
    #         function (e.g., for C code output)
    global uu_rhs, vv_rhs

    # Step 5: Define right-hand sides for the evolution.
    uu_rhs = vv
    vv_rhs = 0
    for i in range(DIM):
        vv_rhs += wavespeed * wavespeed * uu_dDD[i][i]

    # # Step 5: Generate C code for scalarwave evolution equations,
    # #         print output to the screen (standard out, or stdout).
    fin.FD_outputC("simple_c.py", [
        lhrh(lhs=gri.gfaccess("out_gfs", "UU_rhs"), rhs=uu_rhs),
        lhrh(lhs=gri.gfaccess("out_gfs", "VV_rhs"), rhs=vv_rhs)
    ])
Пример #13
0
def generate_everything_for_UnitTesting():
    # First define hydrodynamical quantities
    u4U = ixp.declarerank1("u4U", DIM=4)
    B_tildeU = ixp.declarerank1("B_tildeU", DIM=3)

    # Then ADM quantities
    gammaDD = ixp.declarerank2("gammaDD", "sym01", DIM=3)
    betaU = ixp.declarerank1("betaU", DIM=3)
    alpha = sp.symbols('alpha', real=True)

    # Then numerical constant
    sqrt4pi = sp.symbols('sqrt4pi', real=True)

    # First compute stress-energy tensor T4UU and T4UD:
    import GRHD.equations as GHeq
    GHeq.compute_sqrtgammaDET(gammaDD)
    compute_B_notildeU(GHeq.sqrtgammaDET, B_tildeU)
    compute_smallb4U(gammaDD, betaU, alpha, u4U, B_notildeU, sqrt4pi)
    compute_smallb4U_with_driftvU_for_FFE(gammaDD, betaU, alpha, u4U,
                                          B_notildeU, sqrt4pi)
    compute_smallbsquared(gammaDD, betaU, alpha, smallb4U)

    compute_TEM4UU(gammaDD, betaU, alpha, smallb4U, smallbsquared, u4U)
    compute_TEM4UD(gammaDD, betaU, alpha, TEM4UU)

    # Compute conservative variables in terms of primitive variables
    GHeq.compute_S_tildeD(alpha, GHeq.sqrtgammaDET, TEM4UD)
    global S_tildeD
    S_tildeD = GHeq.S_tildeD

    # Next compute fluxes of conservative variables
    GHeq.compute_S_tilde_fluxUD(alpha, GHeq.sqrtgammaDET, TEM4UD)
    global S_tilde_fluxUD
    S_tilde_fluxUD = GHeq.S_tilde_fluxUD

    # Then declare derivatives & compute g4DDdD
    gammaDD_dD = ixp.declarerank3("gammaDD_dD", "sym01", DIM=3)
    betaU_dD = ixp.declarerank2("betaU_dD", "nosym", DIM=3)
    alpha_dD = ixp.declarerank1("alpha_dD", DIM=3)
    GHeq.compute_g4DD_zerotimederiv_dD(gammaDD, betaU, alpha, gammaDD_dD,
                                       betaU_dD, alpha_dD)

    # Finally compute source terms on tau_tilde and S_tilde equations
    GHeq.compute_S_tilde_source_termD(alpha, GHeq.sqrtgammaDET,
                                      GHeq.g4DD_zerotimederiv_dD, TEM4UU)
    global S_tilde_source_termD
    S_tilde_source_termD = GHeq.S_tilde_source_termD
def ScalarWaveCurvilinear_RHSs():
    # Step 1: Get the spatial dimension, defined in the
    #         NRPy+ "grid" module. With reference metrics,
    #         this must be set to 3 or fewer.
    DIM = par.parval_from_str("DIM")

    # Step 2: Set up the reference metric and
    #         quantities derived from the
    #         reference metric.
    rfm.reference_metric()

    # Step 3: Compute the contracted Christoffel symbols:
    contractedGammahatU = ixp.zerorank1()
    for k in range(DIM):
        for i in range(DIM):
            for j in range(DIM):
                contractedGammahatU[k] += rfm.ghatUU[i][j] * rfm.GammahatUDD[k][i][j]

    # Step 4: Register gridfunctions that are needed as input
    #         to the scalar wave RHS expressions.
    uu, vv = gri.register_gridfunctions("EVOL",["uu","vv"])

    # Step 5a: Declare the rank-1 indexed expression \partial_{i} u,
    #          Derivative variables like these must have an underscore
    #          in them, so the finite difference module can parse the
    #          variable name properly.
    uu_dD = ixp.declarerank1("uu_dD")

    # Step 5b: Declare the rank-2 indexed expression \partial_{ij} u,
    #          which is symmetric about interchange of indices i and j
    #          Derivative variables like these must have an underscore
    #          in them, so the finite difference module can parse the
    #          variable name properly.
    uu_dDD = ixp.declarerank2("uu_dDD","sym01")

    # Step 7: Specify RHSs as global variables,
    #         to enable access outside this
    #         function (e.g., for C code output)
    global uu_rhs,vv_rhs

    # Step 6: Define right-hand sides for the evolution.
    # Step 6a: uu_rhs = vv:
    uu_rhs = vv
    # Step 6b: The right-hand side of the \partial_t v equation
    #          is given by:
    #          \hat{g}^{ij} \partial_i \partial_j u - \hat{\Gamma}^i \partial_i u.
    #          ^^^^^^^^^^^^ PART 1 ^^^^^^^^^^^^^^^^ ^^^^^^^^^^ PART 2 ^^^^^^^^^^^
    vv_rhs = 0
    for i in range(DIM):
        # PART 2:
        vv_rhs -= contractedGammahatU[i]*uu_dD[i]
        for j in range(DIM):
            # PART 1:
            vv_rhs += rfm.ghatUU[i][j]*uu_dDD[i][j]

    vv_rhs *= wavespeed*wavespeed
Пример #15
0
def BSSN_source_terms_for_BSSN_RHSs(custom_T4UU=None):
    global sourceterm_trK_rhs, sourceterm_a_rhsDD, sourceterm_lambda_rhsU, sourceterm_Lambdabar_rhsU

    # Step 3.a: Call BSSN_source_terms_ito_T4UU to get SDD, SD, S, & rho

    if custom_T4UU == "unrescaled BSSN source terms already given":
        SDD = ixp.declarerank2("SDD", "sym01")
        SD = ixp.declarerank1("SD")
        S = sp.symbols("S", real=True)
        rho = sp.symbols("rho", real=True)
    else:
        SDD, SD, S, rho = stress_energy_source_terms_ito_T4UU_and_ADM_or_BSSN_metricvars(
            "BSSN", custom_T4UU)
    PI = par.Cparameters("REAL", thismodule, ["PI"],
                         "3.14159265358979323846264338327950288")
    alpha = sp.symbols("alpha", real=True)

    # Step 3.b: trK_rhs
    sourceterm_trK_rhs = 4 * PI * alpha * (rho + S)

    # Step 3.c: Abar_rhsDD:
    # Step 3.c.i: Compute trace-free part of S_{ij}:
    import BSSN.BSSN_quantities as Bq
    Bq.BSSN_basic_tensors()  # Sets gammabarDD
    gammabarUU, dummydet = ixp.symm_matrix_inverter3x3(
        Bq.gammabarDD)  # Set gammabarUU
    tracefree_SDD = ixp.zerorank2()
    for i in range(3):
        for j in range(3):
            tracefree_SDD[i][j] = SDD[i][j]
    for i in range(3):
        for j in range(3):
            for k in range(3):
                for m in range(3):
                    tracefree_SDD[i][j] += -sp.Rational(1, 3) * Bq.gammabarDD[
                        i][j] * gammabarUU[k][m] * SDD[k][m]
    # Step 3.c.ii: Define exp_m4phi = e^{-4 phi}
    Bq.phi_and_derivs()
    # Step 3.c.iii: Evaluate stress-energy part of AbarDD's RHS
    sourceterm_a_rhsDD = ixp.zerorank2()
    for i in range(3):
        for j in range(3):
            Abar_rhsDDij = -8 * PI * alpha * Bq.exp_m4phi * tracefree_SDD[i][j]
            sourceterm_a_rhsDD[i][j] = Abar_rhsDDij / rfm.ReDD[i][j]

    # Step 3.d: Stress-energy part of Lambdabar_rhsU = stressenergy_Lambdabar_rhsU
    sourceterm_Lambdabar_rhsU = ixp.zerorank1()
    for i in range(3):
        for j in range(3):
            sourceterm_Lambdabar_rhsU[
                i] += -16 * PI * alpha * gammabarUU[i][j] * SD[j]
    sourceterm_lambda_rhsU = ixp.zerorank1()
    for i in range(3):
        sourceterm_lambda_rhsU[i] = sourceterm_Lambdabar_rhsU[i] / rfm.ReU[i]
Пример #16
0
def register_stress_energy_source_terms_return_T4UU(
        enable_stress_energy_source_terms):
    if enable_stress_energy_source_terms:
        registered_already = False
        for i in range(len(gri.glb_gridfcs_list)):
            if gri.glb_gridfcs_list[i].name == "T4UU00":
                registered_already = True
        if not registered_already:
            return ixp.register_gridfunctions_for_single_rank2("AUXEVOL",
                                                               "T4UU",
                                                               "sym01",
                                                               DIM=4)
        else:
            return ixp.declarerank2("T4UU", "sym01", DIM=4)
    return None
def gammabarDD_hDD(gammaDD):
    global gammabarDD, hDD

    if gammaDD == None:
        gammaDD = ixp.declarerank2("gammaDD", "sym01")

    if rfm.have_already_called_reference_metric_function == False:
        print(
            "BSSN.BSSN_in_terms_of_ADM.hDD_given_ADM(): Must call reference_metric() first!"
        )
        sys.exit(1)
    # \bar{gamma}_{ij} = (\frac{\bar{gamma}}{gamma})^{1/3}*gamma_{ij}.
    gammaUU, gammaDET = ixp.symm_matrix_inverter3x3(gammaDD)
    gammabarDD = ixp.zerorank2()
    hDD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            gammabarDD[i][j] = (rfm.detgammahat / gammaDET)**(sp.Rational(
                1, 3)) * gammaDD[i][j]
            hDD[i][j] = (gammabarDD[i][j] - rfm.ghatDD[i][j]) / rfm.ReDD[i][j]
def setup_ADM_quantities(inputvars):
    if inputvars == "ADM":
        gammaDD = ixp.declarerank2("gammaDD", "sym01")
        betaU = ixp.declarerank1("betaU")
        alpha = sp.symbols("alpha", real=True)
    elif inputvars == "BSSN":
        import BSSN.ADM_in_terms_of_BSSN as AitoB

        # Construct gamma_{ij} in terms of cf & gammabar_{ij}
        AitoB.ADM_in_terms_of_BSSN()
        gammaDD = AitoB.gammaDD
        # Next construct beta^i in terms of vet^i and reference metric quantities
        import BSSN.BSSN_quantities as Bq

        Bq.BSSN_basic_tensors()
        betaU = Bq.betaU
        alpha = sp.symbols("alpha", real=True)
    else:
        print("inputvars = " + str(inputvars) + " not supported. Please choose ADM or BSSN.")
        sys.exit(1)
    return gammaDD,betaU,alpha
def add_to_Cfunction_dict__AD_gauge_term_psi6Phi_fin_diff(includes=None):
    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"
    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"
    add_to_Cfunction_dict(
        includes=includes,
        desc=desc,
        name=name, params=params,
        body=body, loopopts=loopopts)
    outC_function_dict[name] = outC_function_dict[name].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")
def LambdabarU_lambdaU__exact_gammaDD(gammaDD):
    global LambdabarU, lambdaU

    if gammaDD is None:  # Use "is None" instead of "==None", as the former is more correct.
        gammaDD = ixp.declarerank2("gammaDD", "sym01")

    # \bar{Lambda}^i = \bar{gamma}^{jk}(\bar{Gamma}^i_{jk} - \hat{Gamma}^i_{jk}).
    gammabarDD_hDD(gammaDD)
    gammabarUU, _gammabarDET = ixp.symm_matrix_inverter3x3(
        gammabarDD)  # _gammabarDET unused.

    # First compute Christoffel symbols \bar{Gamma}^i_{jk}, with respect to barred metric:
    GammabarUDD = ixp.zerorank3()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                for l in range(DIM):
                    GammabarUDD[i][j][k] += sp.Rational(
                        1, 2) * gammabarUU[i][l] * (
                            sp.diff(gammabarDD[l][j], rfm.xx[k]) +
                            sp.diff(gammabarDD[l][k], rfm.xx[j]) -
                            sp.diff(gammabarDD[j][k], rfm.xx[l]))
    # Next evaluate \bar{Lambda}^i, based on GammabarUDD above and GammahatUDD
    #       (from the reference metric):
    LambdabarU = ixp.zerorank1()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                LambdabarU[i] += gammabarUU[j][k] * (GammabarUDD[i][j][k] -
                                                     rfm.GammahatUDD[i][j][k])
    for i in range(DIM):
        # We evaluate LambdabarU[i] here to ensure proper cancellations. If these cancellations
        #   are not applied, certain expressions (e.g., lambdaU[0] in StaticTrumpet) will
        #   cause SymPy's (v1.5+) CSE algorithm to hang
        LambdabarU[i] = LambdabarU[i].doit()
    lambdaU = ixp.zerorank1()
    for i in range(DIM):
        lambdaU[i] = LambdabarU[i] / rfm.ReU[i]
Пример #21
0
def calculate_E_i_flux(flux_dirn,inputs_provided=True,alpha_face=None,gamma_faceDD=None,beta_faceU=None,\
                       Valenciav_rU=None,B_rU=None,Valenciav_lU=None,B_lU=None):
    if not inputs_provided:
        # declare all variables
        alpha_face = sp.symbols(alpha_face)
        beta_faceU = ixp.declarerank1("beta_faceU")
        gamma_faceDD = ixp.declarerank2("gamma_faceDD", "sym01")
        Valenciav_rU = ixp.declarerank1("Valenciav_rU")
        B_rU = ixp.declarerank1("B_rU")
        Valenciav_lU = ixp.declarerank1("Valenciav_lU")
        B_lU = ixp.declarerank1("B_lU")
    global E_fluxD
    E_fluxD = ixp.zerorank1()
    for field_comp in range(3):
        find_cmax_cmin(field_comp, gamma_faceDD, beta_faceU, alpha_face)
        calculate_flux_and_state_for_Induction(field_comp,flux_dirn, gamma_faceDD,beta_faceU,alpha_face,\
                                               Valenciav_rU,B_rU)
        Fr = F
        Ur = U
        calculate_flux_and_state_for_Induction(field_comp,flux_dirn, gamma_faceDD,beta_faceU,alpha_face,\
                                               Valenciav_lU,B_lU)
        Fl = F
        Ul = U
        E_fluxD[field_comp] += HLLE_solver(cmax, cmin, Fr, Fl, Ur, Ul)
Пример #22
0
def GiRaFFEfood_NRPy_Exact_Wald():

    # <a id='step2'></a>
    #
    # ### Step 2: Set the vectors A and E in Spherical coordinates
    # $$\label{step2}$$
    #
    # \[Back to [top](#top)\]
    #
    # We will first build the fundamental vectors $A_i$ and $E_i$ in spherical coordinates (see [Table 3](https://arxiv.org/pdf/1704.00599.pdf)). Note that we use reference_metric.py to set $r$ and $\theta$ in terms of Cartesian coordinates; this will save us a step later when we convert to Cartesian coordinates. Since $C_0 = 1$,
    # \begin{align}
    # A_{\phi} &= \frac{1}{2} r^2 \sin^2 \theta \\
    # E_{\phi} &= 2 M \left( 1+ \frac {2M}{r} \right)^{-1/2} \sin^2 \theta. \\
    # \end{align}
    # While we have $E_i$ set as a variable in NRPy+, note that the final C code won't store these values.

    # Step 2: Set the vectors A and E in Spherical coordinates

    r = rfm.xxSph[
        0] + KerrSchild_radial_shift  # We are setting the data up in Shifted Kerr-Schild coordinates
    theta = rfm.xxSph[1]

    # Initialize all components of A and E in the *spherical basis* to zero
    ASphD = ixp.zerorank1()
    ESphD = ixp.zerorank1()
    ASphD[2] = (r * r * sp.sin(theta)**2) / 2
    ESphD[2] = 2 * M * sp.sin(theta)**2 / sp.sqrt(1 + 2 * M / r)

    # <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 both $A_i$ and $E_i$ have one lower index, both 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")
    ED = ixp.zerorank1()

    for i in range(3):
        for j in range(3):
            AD[i] = drrefmetric__dx_0UDmatrix[(j, i)] * ASphD[j]
            ED[i] = drrefmetric__dx_0UDmatrix[(j, i)] * ESphD[j]

    #Step 4: Declare the basic spacetime quantities
    alpha = sp.symbols("alpha", real=True)
    betaU = ixp.declarerank1("betaU", DIM=3)
    gammaDD = ixp.declarerank2("gammaDD", "sym01", DIM=3)

    import GRHD.equations as GRHD
    GRHD.compute_sqrtgammaDET(gammaDD)

    # <a id='step4'></a>
    #
    # ### Step 4: Calculate $v^i$ from $A_i$ and $E_i$
    # $$\label{step4}$$
    #
    # \[Back to [top](#top)\]
    #
    # We will now find the magnetic field using equation 18 in [the original paper](https://arxiv.org/pdf/1704.00599.pdf) $$B^i = \frac{[ijk]}{\sqrt{\gamma}} \partial_j A_k. $$ We will need the metric quantites: the lapse $\alpha$, the shift $\beta^i$, and the three-metric $\gamma_{ij}$. We will also need the Levi-Civita symbol, provided by $\text{WeylScal4NRPy}$.

    # Step 4: Calculate v^i from A_i and E_i
    # Step 4a: Calculate the magnetic field B^i
    # Here, we build the Levi-Civita tensor from the Levi-Civita symbol.
    # Here, we build the Levi-Civita tensor from the Levi-Civita symbol.
    import WeylScal4NRPy.WeylScalars_Cartesian as weyl
    LeviCivitaSymbolDDD = weyl.define_LeviCivitaSymbol_rank3()
    LeviCivitaTensorUUU = ixp.zerorank3()
    for i in range(3):
        for j in range(3):
            for k in range(3):
                LeviCivitaTensorUUU[i][j][
                    k] = LeviCivitaSymbolDDD[i][j][k] / GRHD.sqrtgammaDET

    # For the initial data, we can analytically take the derivatives of A_i
    ADdD = ixp.zerorank2()
    for i in range(3):
        for j in range(3):
            ADdD[i][j] = sp.simplify(sp.diff(AD[i], rfm.xxCart[j]))

    #global BU
    BU = ixp.zerorank1()
    for i in range(3):
        for j in range(3):
            for k in range(3):
                BU[i] += LeviCivitaTensorUUU[i][j][k] * ADdD[k][j]

    # We will now build the initial velocity using equation 152 in [this paper,](https://arxiv.org/pdf/1310.3274v2.pdf) cited in the original $\texttt{GiRaFFE}$ code: $$ v^i = \alpha \frac{\epsilon^{ijk} E_j B_k}{B^2} -\beta^i. $$
    # However, our code needs the Valencia 3-velocity while this expression is for the drift velocity. So, we will need to transform it to the Valencia 3-velocity using the rule $\bar{v}^i = \frac{1}{\alpha} \left(v^i +\beta^i \right)$.
    # Thus, $$\bar{v}^i = \frac{\epsilon^{ijk} E_j B_k}{B^2}$$

    # Step 4b: Calculate B^2 and B_i
    # B^2 is an inner product defined in the usual way:
    B2 = sp.sympify(0)
    for i in range(3):
        for j in range(3):
            B2 += gammaDD[i][j] * BU[i] * BU[j]

    # Lower the index on B^i
    BD = ixp.zerorank1()
    for i in range(3):
        for j in range(3):
            BD[i] += gammaDD[i][j] * BU[j]

    # Step 4c: Calculate the Valencia 3-velocity
    global ValenciavU
    ValenciavU = ixp.zerorank1()
    for i in range(3):
        for j in range(3):
            for k in range(3):
                ValenciavU[
                    i] += LeviCivitaTensorUUU[i][j][k] * ED[j] * BD[k] / B2
Пример #23
0
def BSSN_RHSs():
    # Step 1.c: Given the chosen coordinate system, set up
    #           corresponding reference metric and needed
    #           reference metric quantities
    # The following function call sets up the reference metric
    #    and related quantities, including rescaling matrices ReDD,
    #    ReU, and hatted quantities.
    rfm.reference_metric()

    global have_already_called_BSSN_RHSs_function  # setting to global enables other modules to see updated value.
    have_already_called_BSSN_RHSs_function = True

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

    # Step 1.e: Import all basic (unrescaled) BSSN scalars & tensors
    import BSSN.BSSN_quantities as Bq
    Bq.BSSN_basic_tensors()
    gammabarDD = Bq.gammabarDD
    AbarDD = Bq.AbarDD
    LambdabarU = Bq.LambdabarU
    trK = Bq.trK
    alpha = Bq.alpha
    betaU = Bq.betaU

    # Step 1.f: Import all neeeded rescaled BSSN tensors:
    aDD = Bq.aDD
    cf = Bq.cf
    lambdaU = Bq.lambdaU

    # Step 2.a.i: Import derivative expressions for betaU defined in the BSSN.BSSN_quantities module:
    Bq.betaU_derivs()
    betaU_dD = Bq.betaU_dD
    betaU_dDD = Bq.betaU_dDD
    # Step 2.a.ii: Import derivative expression for gammabarDD
    Bq.gammabar__inverse_and_derivs()
    gammabarDD_dupD = Bq.gammabarDD_dupD

    # Step 2.a.iii: First term of \partial_t \bar{\gamma}_{i j} right-hand side:
    # \beta^k \bar{\gamma}_{ij,k} + \beta^k_{,i} \bar{\gamma}_{kj} + \beta^k_{,j} \bar{\gamma}_{ik}
    gammabar_rhsDD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                gammabar_rhsDD[i][j] += betaU[k] * gammabarDD_dupD[i][j][k] + betaU_dD[k][i] * gammabarDD[k][j] \
                                        + betaU_dD[k][j] * gammabarDD[i][k]

    # Step 2.b.i: First import \bar{A}_{ij} = AbarDD[i][j], and its contraction trAbar = \bar{A}^k_k
    #           from BSSN.BSSN_quantities
    Bq.AbarUU_AbarUD_trAbar_AbarDD_dD()
    trAbar = Bq.trAbar

    # Step 2.b.ii: Import detgammabar quantities from BSSN.BSSN_quantities:
    Bq.detgammabar_and_derivs()
    detgammabar = Bq.detgammabar
    detgammabar_dD = Bq.detgammabar_dD

    # Step 2.b.ii: Compute the contraction \bar{D}_k \beta^k = \beta^k_{,k} + \frac{\beta^k \bar{\gamma}_{,k}}{2 \bar{\gamma}}
    Dbarbetacontraction = sp.sympify(0)
    for k in range(DIM):
        Dbarbetacontraction += betaU_dD[k][
            k] + betaU[k] * detgammabar_dD[k] / (2 * detgammabar)

    # Step 2.b.iii: Second term of \partial_t \bar{\gamma}_{i j} right-hand side:
    # \frac{2}{3} \bar{\gamma}_{i j} \left (\alpha \bar{A}_{k}^{k} - \bar{D}_{k} \beta^{k}\right )
    for i in range(DIM):
        for j in range(DIM):
            gammabar_rhsDD[i][j] += sp.Rational(2, 3) * gammabarDD[i][j] * (
                alpha * trAbar - Dbarbetacontraction)

    # Step 2.c: Third term of \partial_t \bar{\gamma}_{i j} right-hand side:
    # -2 \alpha \bar{A}_{ij}
    for i in range(DIM):
        for j in range(DIM):
            gammabar_rhsDD[i][j] += -2 * alpha * AbarDD[i][j]

    # Step 3.a: First term of \partial_t \bar{A}_{i j}:
    # \beta^k \partial_k \bar{A}_{ij} + \partial_i \beta^k \bar{A}_{kj} + \partial_j \beta^k \bar{A}_{ik}

    # First define AbarDD_dupD:
    AbarDD_dupD = Bq.AbarDD_dupD  # From Bq.AbarUU_AbarUD_trAbar_AbarDD_dD()

    Abar_rhsDD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                Abar_rhsDD[i][j] += betaU[k] * AbarDD_dupD[i][j][k] + betaU_dD[k][i] * AbarDD[k][j] \
                                    + betaU_dD[k][j] * AbarDD[i][k]

    # Step 3.b: Second term of \partial_t \bar{A}_{i j}:
    # - (2/3) \bar{A}_{i j} \bar{D}_{k} \beta^{k} - 2 \alpha \bar{A}_{i k} {\bar{A}^{k}}_{j} + \alpha \bar{A}_{i j} K
    gammabarUU = Bq.gammabarUU  # From Bq.gammabar__inverse_and_derivs()
    AbarUD = Bq.AbarUD  # From Bq.AbarUU_AbarUD_trAbar()
    for i in range(DIM):
        for j in range(DIM):
            Abar_rhsDD[i][j] += -sp.Rational(2, 3) * AbarDD[i][
                j] * Dbarbetacontraction + alpha * AbarDD[i][j] * trK
            for k in range(DIM):
                Abar_rhsDD[i][j] += -2 * alpha * AbarDD[i][k] * AbarUD[k][j]

    # Step 3.c.i: Define partial derivatives of \phi in terms of evolved quantity "cf":
    Bq.phi_and_derivs()
    phi_dD = Bq.phi_dD
    phi_dupD = Bq.phi_dupD
    phi_dDD = Bq.phi_dDD
    exp_m4phi = Bq.exp_m4phi
    phi_dBarD = Bq.phi_dBarD  # phi_dBarD = Dbar_i phi = phi_dD (since phi is a scalar)
    phi_dBarDD = Bq.phi_dBarDD  # phi_dBarDD = Dbar_i Dbar_j phi (covariant derivative)

    # Step 3.c.ii: Define RbarDD
    Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU()
    RbarDD = Bq.RbarDD

    # Step 3.c.iii: Define first and second derivatives of \alpha, as well as
    #         \bar{D}_i \bar{D}_j \alpha, which is defined just like phi
    alpha_dD = ixp.declarerank1("alpha_dD")
    alpha_dDD = ixp.declarerank2("alpha_dDD", "sym01")
    alpha_dBarD = alpha_dD
    alpha_dBarDD = ixp.zerorank2()
    GammabarUDD = Bq.GammabarUDD  # Defined in Bq.gammabar__inverse_and_derivs()
    for i in range(DIM):
        for j in range(DIM):
            alpha_dBarDD[i][j] = alpha_dDD[i][j]
            for k in range(DIM):
                alpha_dBarDD[i][j] += -GammabarUDD[k][i][j] * alpha_dD[k]

    # Step 3.c.iv: Define the terms in curly braces:
    curlybrackettermsDD = ixp.zerorank2()
    for i in range(DIM):
        for j in range(DIM):
            curlybrackettermsDD[i][j] = -2 * alpha * phi_dBarDD[i][j] + 4 * alpha * phi_dBarD[i] * phi_dBarD[j] \
                                        + 2 * alpha_dBarD[i] * phi_dBarD[j] \
                                        + 2 * alpha_dBarD[j] * phi_dBarD[i] \
                                        - alpha_dBarDD[i][j] + alpha * RbarDD[i][j]

    # Step 3.c.v: Compute the trace:
    curlybracketterms_trace = sp.sympify(0)
    for i in range(DIM):
        for j in range(DIM):
            curlybracketterms_trace += gammabarUU[i][j] * curlybrackettermsDD[
                i][j]

    # Step 3.c.vi: Third and final term of Abar_rhsDD[i][j]:
    for i in range(DIM):
        for j in range(DIM):
            Abar_rhsDD[i][j] += exp_m4phi * (
                curlybrackettermsDD[i][j] -
                sp.Rational(1, 3) * gammabarDD[i][j] * curlybracketterms_trace)

    # Step 4: Right-hand side of conformal factor variable "cf". Supported
    #          options include: cf=phi, cf=W=e^(-2*phi) (default), and cf=chi=e^(-4*phi)
    # \partial_t phi = \left[\beta^k \partial_k \phi \right] <- TERM 1
    #                  + \frac{1}{6} \left (\bar{D}_{k} \beta^{k} - \alpha K \right ) <- TERM 2
    global cf_rhs
    cf_rhs = sp.Rational(1, 6) * (Dbarbetacontraction - alpha * trK)  # Term 2
    for k in range(DIM):
        cf_rhs += betaU[k] * phi_dupD[k]  # Term 1

    # Next multiply to convert phi_rhs to cf_rhs.
    if par.parval_from_str(
            "BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "phi":
        pass  # do nothing; cf_rhs = phi_rhs
    elif par.parval_from_str(
            "BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "W":
        cf_rhs *= -2 * cf  # cf_rhs = -2*cf*phi_rhs
    elif par.parval_from_str(
            "BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "chi":
        cf_rhs *= -4 * cf  # cf_rhs = -4*cf*phi_rhs
    else:
        print("Error: EvolvedConformalFactor_cf == " + par.parval_from_str(
            "BSSN.BSSN_quantities::EvolvedConformalFactor_cf") +
              " unsupported!")
        exit(1)

    # Step 5: right-hand side of trK (trace of extrinsic curvature):
    # \partial_t K = \beta^k \partial_k K <- TERM 1
    #           + \frac{1}{3} \alpha K^{2} <- TERM 2
    #           + \alpha \bar{A}_{i j} \bar{A}^{i j} <- TERM 3
    #           - - e^{-4 \phi} (\bar{D}_{i} \bar{D}^{i} \alpha + 2 \bar{D}^{i} \alpha \bar{D}_{i} \phi ) <- TERM 4
    global trK_rhs
    # TERM 2:
    trK_rhs = sp.Rational(1, 3) * alpha * trK * trK
    trK_dupD = ixp.declarerank1("trK_dupD")
    for i in range(DIM):
        # TERM 1:
        trK_rhs += betaU[i] * trK_dupD[i]
    for i in range(DIM):
        for j in range(DIM):
            # TERM 4:
            trK_rhs += -exp_m4phi * gammabarUU[i][j] * (
                alpha_dBarDD[i][j] + 2 * alpha_dBarD[j] * phi_dBarD[i])
    AbarUU = Bq.AbarUU  # From Bq.AbarUU_AbarUD_trAbar()
    for i in range(DIM):
        for j in range(DIM):
            # TERM 3:
            trK_rhs += alpha * AbarDD[i][j] * AbarUU[i][j]

    # Step 6: right-hand side of \partial_t \bar{\Lambda}^i:
    # \partial_t \bar{\Lambda}^i = \beta^k \partial_k \bar{\Lambda}^i - \partial_k \beta^i \bar{\Lambda}^k <- TERM 1
    #                            + \bar{\gamma}^{j k} \hat{D}_{j} \hat{D}_{k} \beta^{i} <- TERM 2
    #                            + \frac{2}{3} \Delta^{i} \bar{D}_{j} \beta^{j} <- TERM 3
    #                            + \frac{1}{3} \bar{D}^{i} \bar{D}_{j} \beta^{j} <- TERM 4
    #                            - 2 \bar{A}^{i j} (\partial_{j} \alpha - 6 \partial_{j} \phi) <- TERM 5
    #                            + 2 \alpha \bar{A}^{j k} \Delta_{j k}^{i} <- TERM 6
    #                            - \frac{4}{3} \alpha \bar{\gamma}^{i j} \partial_{j} K <- TERM 7

    # Step 6.a: Term 1 of \partial_t \bar{\Lambda}^i: \beta^k \partial_k \bar{\Lambda}^i - \partial_k \beta^i \bar{\Lambda}^k
    # First we declare \bar{\Lambda}^i and \bar{\Lambda}^i_{,j} in terms of \lambda^i and \lambda^i_{,j}
    global LambdabarU_dupD  # Used on the RHS of the Gamma-driving shift conditions
    LambdabarU_dupD = ixp.zerorank2()
    lambdaU_dupD = ixp.declarerank2("lambdaU_dupD", "nosym")
    for i in range(DIM):
        for j in range(DIM):
            LambdabarU_dupD[i][j] = lambdaU_dupD[i][j] * rfm.ReU[i] + lambdaU[
                i] * rfm.ReUdD[i][j]

    global Lambdabar_rhsU  # Used on the RHS of the Gamma-driving shift conditions
    Lambdabar_rhsU = ixp.zerorank1()
    for i in range(DIM):
        for k in range(DIM):
            Lambdabar_rhsU[i] += betaU[k] * LambdabarU_dupD[i][k] - betaU_dD[
                i][k] * LambdabarU[k]  # Term 1

    # Step 6.b: Term 2 of \partial_t \bar{\Lambda}^i = \bar{\gamma}^{jk} (Term 2a + Term 2b + Term 2c)
    # Term 2a: \bar{\gamma}^{jk} \beta^i_{,kj}
    Term2aUDD = ixp.zerorank3()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                Term2aUDD[i][j][k] += betaU_dDD[i][k][j]
    # Term 2b: \hat{\Gamma}^i_{mk,j} \beta^m + \hat{\Gamma}^i_{mk} \beta^m_{,j}
    #          + \hat{\Gamma}^i_{dj}\beta^d_{,k} - \hat{\Gamma}^d_{kj} \beta^i_{,d}
    Term2bUDD = ixp.zerorank3()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                for m in range(DIM):
                    Term2bUDD[i][j][k] += rfm.GammahatUDDdD[i][m][k][j] * betaU[m] \
                                          + rfm.GammahatUDD[i][m][k] * betaU_dD[m][j] \
                                          + rfm.GammahatUDD[i][m][j] * betaU_dD[m][k] \
                                          - rfm.GammahatUDD[m][k][j] * betaU_dD[i][m]
    # Term 2c: \hat{\Gamma}^i_{dj}\hat{\Gamma}^d_{mk} \beta^m - \hat{\Gamma}^d_{kj} \hat{\Gamma}^i_{md} \beta^m
    Term2cUDD = ixp.zerorank3()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                for m in range(DIM):
                    for d in range(DIM):
                        Term2cUDD[i][j][k] += (rfm.GammahatUDD[i][d][j] * rfm.GammahatUDD[d][m][k] \
                                               - rfm.GammahatUDD[d][k][j] * rfm.GammahatUDD[i][m][d]) * betaU[m]

    Lambdabar_rhsUpieceU = ixp.zerorank1()

    # Put it all together to get Term 2:
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                Lambdabar_rhsU[i] += gammabarUU[j][k] * (Term2aUDD[i][j][k] +
                                                         Term2bUDD[i][j][k] +
                                                         Term2cUDD[i][j][k])
                Lambdabar_rhsUpieceU[i] += gammabarUU[j][k] * (
                    Term2aUDD[i][j][k] + Term2bUDD[i][j][k] +
                    Term2cUDD[i][j][k])

    # Step 6.c: Term 3 of \partial_t \bar{\Lambda}^i:
    #    \frac{2}{3} \Delta^{i} \bar{D}_{j} \beta^{j}
    DGammaU = Bq.DGammaU  # From Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU()
    for i in range(DIM):
        Lambdabar_rhsU[i] += sp.Rational(
            2, 3) * DGammaU[i] * Dbarbetacontraction  # Term 3

    # Step 6.d: Term 4 of \partial_t \bar{\Lambda}^i:
    #           \frac{1}{3} \bar{D}^{i} \bar{D}_{j} \beta^{j}
    detgammabar_dDD = Bq.detgammabar_dDD  # From Bq.detgammabar_and_derivs()
    Dbarbetacontraction_dBarD = ixp.zerorank1()
    for k in range(DIM):
        for m in range(DIM):
            Dbarbetacontraction_dBarD[m] += betaU_dDD[k][k][m] + \
                                            (betaU_dD[k][m] * detgammabar_dD[k] +
                                             betaU[k] * detgammabar_dDD[k][m]) / (2 * detgammabar) \
                                            - betaU[k] * detgammabar_dD[k] * detgammabar_dD[m] / (
                                                        2 * detgammabar * detgammabar)
    for i in range(DIM):
        for m in range(DIM):
            Lambdabar_rhsU[i] += sp.Rational(
                1, 3) * gammabarUU[i][m] * Dbarbetacontraction_dBarD[m]

    # Step 6.e: Term 5 of \partial_t \bar{\Lambda}^i:
    #           - 2 \bar{A}^{i j} (\partial_{j} \alpha - 6 \alpha \partial_{j} \phi)
    for i in range(DIM):
        for j in range(DIM):
            Lambdabar_rhsU[i] += -2 * AbarUU[i][j] * (alpha_dD[j] -
                                                      6 * alpha * phi_dD[j])

    # Step 6.f: Term 6 of \partial_t \bar{\Lambda}^i:
    #           2 \alpha \bar{A}^{j k} \Delta^{i}_{j k}
    DGammaUDD = Bq.DGammaUDD  # From RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                Lambdabar_rhsU[
                    i] += 2 * alpha * AbarUU[j][k] * DGammaUDD[i][j][k]

    # Step 6.g: Term 7 of \partial_t \bar{\Lambda}^i:
    #           -\frac{4}{3} \alpha \bar{\gamma}^{i j} \partial_{j} K
    trK_dD = ixp.declarerank1("trK_dD")
    for i in range(DIM):
        for j in range(DIM):
            Lambdabar_rhsU[i] += -sp.Rational(
                4, 3) * alpha * gammabarUU[i][j] * trK_dD[j]

    # Step 7: Rescale the RHS quantities so that the evolved
    #         variables are smooth across coord singularities
    global h_rhsDD, a_rhsDD, lambda_rhsU
    h_rhsDD = ixp.zerorank2()
    a_rhsDD = ixp.zerorank2()
    lambda_rhsU = ixp.zerorank1()
    for i in range(DIM):
        lambda_rhsU[i] = Lambdabar_rhsU[i] / rfm.ReU[i]
        for j in range(DIM):
            h_rhsDD[i][j] = gammabar_rhsDD[i][j] / rfm.ReDD[i][j]
            a_rhsDD[i][j] = Abar_rhsDD[i][j] / rfm.ReDD[i][j]
Пример #24
0
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(prefunc)

    # 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])
        ]),
        #         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_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")

    with open(os.path.join(outdir, "driver_AtoB.h"), "a") as file:
        file.write(driver_Ccode)
Пример #25
0
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])
Пример #26
0
    def BSSN_RHSs__generate_symbolic_expressions():
        ######################################
        # START: GENERATE SYMBOLIC EXPRESSIONS
        print("Generating symbolic expressions for BSSN RHSs...")
        start = time.time()
        # Enable rfm_precompute infrastructure, which results in
        #   BSSN RHSs that are free of transcendental functions,
        #   even in curvilinear coordinates, so long as
        #   ConformalFactor is set to "W" (default).
        par.set_parval_from_str("reference_metric::enable_rfm_precompute","True")
        par.set_parval_from_str("reference_metric::rfm_precompute_Ccode_outdir",os.path.join(outdir,"rfm_files/"))

        # Evaluate BSSN + BSSN gauge RHSs with rfm_precompute enabled:
        import BSSN.BSSN_quantities as Bq
        par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","True")

        rhs.BSSN_RHSs()

        if T4UU != None:
            import BSSN.BSSN_stress_energy_source_terms as Bsest
            Bsest.BSSN_source_terms_for_BSSN_RHSs(T4UU)
            rhs.trK_rhs += Bsest.sourceterm_trK_rhs
            for i in range(3):
                # Needed for Gamma-driving shift RHSs:
                rhs.Lambdabar_rhsU[i] += Bsest.sourceterm_Lambdabar_rhsU[i]
                # Needed for BSSN RHSs:
                rhs.lambda_rhsU[i]    += Bsest.sourceterm_lambda_rhsU[i]
                for j in range(3):
                    rhs.a_rhsDD[i][j] += Bsest.sourceterm_a_rhsDD[i][j]

        gaugerhs.BSSN_gauge_RHSs()

        # Add Kreiss-Oliger dissipation to the BSSN RHSs:
        thismodule = "KO_Dissipation"
        diss_strength = par.Cparameters("REAL", thismodule, "diss_strength", default_KO_strength)

        alpha_dKOD = ixp.declarerank1("alpha_dKOD")
        cf_dKOD    = ixp.declarerank1("cf_dKOD")
        trK_dKOD   = ixp.declarerank1("trK_dKOD")
        betU_dKOD    = ixp.declarerank2("betU_dKOD","nosym")
        vetU_dKOD    = ixp.declarerank2("vetU_dKOD","nosym")
        lambdaU_dKOD = ixp.declarerank2("lambdaU_dKOD","nosym")
        aDD_dKOD = ixp.declarerank3("aDD_dKOD","sym01")
        hDD_dKOD = ixp.declarerank3("hDD_dKOD","sym01")
        for k in range(3):
            gaugerhs.alpha_rhs += diss_strength*alpha_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k]
            rhs.cf_rhs         += diss_strength*   cf_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k]
            rhs.trK_rhs        += diss_strength*  trK_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k]
            for i in range(3):
                if "2ndOrder" in ShiftCondition:
                    gaugerhs.bet_rhsU[i] += diss_strength*   betU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k]
                gaugerhs.vet_rhsU[i]     += diss_strength*   vetU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k]
                rhs.lambda_rhsU[i]       += diss_strength*lambdaU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k]
                for j in range(3):
                    rhs.a_rhsDD[i][j] += diss_strength*aDD_dKOD[i][j][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k]
                    rhs.h_rhsDD[i][j] += diss_strength*hDD_dKOD[i][j][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k]

        # We use betaU as our upwinding control vector:
        Bq.BSSN_basic_tensors()
        betaU = Bq.betaU

        # Now that we are finished with all the rfm hatted
        #           quantities in generic precomputed functional
        #           form, let's restore them to their closed-
        #           form expressions.
        par.set_parval_from_str("reference_metric::enable_rfm_precompute","False") # Reset to False to disable rfm_precompute.
        rfm.ref_metric__hatted_quantities()
        par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","False")
        end = time.time()
        print("(BENCH) Finished BSSN RHS symbolic expressions in "+str(end-start)+" seconds.")
        # END: GENERATE SYMBOLIC EXPRESSIONS
        ######################################

        BSSN_RHSs_SymbExpressions = [lhrh(lhs=gri.gfaccess("rhs_gfs","aDD00"),   rhs=rhs.a_rhsDD[0][0]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","aDD01"),   rhs=rhs.a_rhsDD[0][1]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","aDD02"),   rhs=rhs.a_rhsDD[0][2]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","aDD11"),   rhs=rhs.a_rhsDD[1][1]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","aDD12"),   rhs=rhs.a_rhsDD[1][2]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","aDD22"),   rhs=rhs.a_rhsDD[2][2]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","alpha"),   rhs=gaugerhs.alpha_rhs),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","betU0"),   rhs=gaugerhs.bet_rhsU[0]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","betU1"),   rhs=gaugerhs.bet_rhsU[1]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","betU2"),   rhs=gaugerhs.bet_rhsU[2]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","cf"),      rhs=rhs.cf_rhs),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","hDD00"),   rhs=rhs.h_rhsDD[0][0]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","hDD01")   ,rhs=rhs.h_rhsDD[0][1]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","hDD02"),   rhs=rhs.h_rhsDD[0][2]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","hDD11"),   rhs=rhs.h_rhsDD[1][1]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","hDD12"),   rhs=rhs.h_rhsDD[1][2]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","hDD22"),   rhs=rhs.h_rhsDD[2][2]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU0"),rhs=rhs.lambda_rhsU[0]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU1"),rhs=rhs.lambda_rhsU[1]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU2"),rhs=rhs.lambda_rhsU[2]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","trK"),     rhs=rhs.trK_rhs),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","vetU0"),   rhs=gaugerhs.vet_rhsU[0]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","vetU1"),   rhs=gaugerhs.vet_rhsU[1]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","vetU2"),   rhs=gaugerhs.vet_rhsU[2]) ]

        return [betaU,BSSN_RHSs_SymbExpressions]
Пример #27
0
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 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 / 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)
Пример #28
0
def BaikalETK_C_kernels_codegen_onepart(params=
        "WhichPart=BSSN_RHSs,ThornName=Baikal,FD_order=4,enable_stress_energy_source_terms=True"):
    # Set default parameters
    WhichPart = "BSSN_RHSs"
    ThornName = "Baikal"
    FD_order = 4
    enable_stress_energy_source_terms = True
    LapseCondition  = "OnePlusLog" # Set the standard 1+log lapse condition
    # Set the standard, second-order advecting-shift, Gamma-driving shift condition:
    ShiftCondition  = "GammaDriving2ndOrder_NoCovariant"
    # Default Kreiss-Oliger dissipation strength
    default_KO_strength = 0.1

    import re
    if params != "":
        params2 = re.sub("^,","",params)
        params = params2.strip()
        splitstring = re.split("=|,", params)

        if len(splitstring) % 2 != 0:
            print("outputC: Invalid params string: "+params)
            sys.exit(1)

        parnm = []
        value = []
        for i in range(int(len(splitstring)/2)):
            parnm.append(splitstring[2*i])
            value.append(splitstring[2*i+1])

        for i in range(len(parnm)):
            parnm.append(splitstring[2*i])
            value.append(splitstring[2*i+1])

        for i in range(len(parnm)):
            if parnm[i] == "WhichPart":
                WhichPart = value[i]
            elif parnm[i] == "ThornName":
                ThornName = value[i]
            elif parnm[i] == "FD_order":
                FD_order = int(value[i])
            elif parnm[i] == "enable_stress_energy_source_terms":
                enable_stress_energy_source_terms = False
                if value[i] == "True":
                    enable_stress_energy_source_terms = True
            elif parnm[i] == "LapseCondition":
                LapseCondition = value[i]
            elif parnm[i] == "ShiftCondition":
                ShiftCondition = value[i]
            elif parnm[i] == "default_KO_strength":
                default_KO_strength = float(value[i])
            else:
                print("BaikalETK Error: Could not parse input param: "+parnm[i])
                sys.exit(1)

    # Set output directory for C kernels
    outdir = os.path.join(ThornName, "src")  # Main C code output directory

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

    # Step 2: Set some core parameters, including CoordSystem MoL timestepping algorithm,
    #                                 FD order, floating point precision, and CFL factor:
    # Choices are: Spherical, SinhSpherical, SinhSphericalv2, Cylindrical, SinhCylindrical,
    #              SymTP, SinhSymTP
    # NOTE: Only CoordSystem == Cartesian makes sense here; new
    #       boundary conditions are needed within the ETK for
    #       Spherical, etc. coordinates.
    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.

    # Set the gridfunction memory access type to ETK-like, so that finite_difference
    #    knows how to read and write gridfunctions from/to memory.
    par.set_parval_from_str("grid::GridFuncMemAccess", "ETK")

    par.set_parval_from_str("BSSN.BSSN_gauge_RHSs::ShiftEvolutionOption", ShiftCondition)
    par.set_parval_from_str("BSSN.BSSN_gauge_RHSs::LapseEvolutionOption", LapseCondition)

    T4UU = None
    if enable_stress_energy_source_terms == True:
        registered_already = False
        for i in range(len(gri.glb_gridfcs_list)):
            if gri.glb_gridfcs_list[i].name == "T4UU00":
                registered_already = True
        if not registered_already:
            T4UU = ixp.register_gridfunctions_for_single_rank2("AUXEVOL", "T4UU", "sym01", DIM=4)
        else:
            T4UU = ixp.declarerank2("T4UU", "sym01", DIM=4)

    # Register the BSSN constraints (Hamiltonian & momentum constraints) as gridfunctions.
    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:
        # We ignore return values for register_gridfunctions...() calls below
        #    as they are unused.
        gri.register_gridfunctions("AUX", "H")
        ixp.register_gridfunctions_for_single_rank1("AUX", "MU")

    def BSSN_RHSs__generate_symbolic_expressions():
        ######################################
        # START: GENERATE SYMBOLIC EXPRESSIONS
        print("Generating symbolic expressions for BSSN RHSs...")
        start = time.time()
        # Enable rfm_precompute infrastructure, which results in
        #   BSSN RHSs that are free of transcendental functions,
        #   even in curvilinear coordinates, so long as
        #   ConformalFactor is set to "W" (default).
        par.set_parval_from_str("reference_metric::enable_rfm_precompute","True")
        par.set_parval_from_str("reference_metric::rfm_precompute_Ccode_outdir",os.path.join(outdir,"rfm_files/"))

        # Evaluate BSSN + BSSN gauge RHSs with rfm_precompute enabled:
        import BSSN.BSSN_quantities as Bq
        par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","True")

        rhs.BSSN_RHSs()

        if T4UU != None:
            import BSSN.BSSN_stress_energy_source_terms as Bsest
            Bsest.BSSN_source_terms_for_BSSN_RHSs(T4UU)
            rhs.trK_rhs += Bsest.sourceterm_trK_rhs
            for i in range(3):
                # Needed for Gamma-driving shift RHSs:
                rhs.Lambdabar_rhsU[i] += Bsest.sourceterm_Lambdabar_rhsU[i]
                # Needed for BSSN RHSs:
                rhs.lambda_rhsU[i]    += Bsest.sourceterm_lambda_rhsU[i]
                for j in range(3):
                    rhs.a_rhsDD[i][j] += Bsest.sourceterm_a_rhsDD[i][j]

        gaugerhs.BSSN_gauge_RHSs()

        # Add Kreiss-Oliger dissipation to the BSSN RHSs:
        thismodule = "KO_Dissipation"
        diss_strength = par.Cparameters("REAL", thismodule, "diss_strength", default_KO_strength)

        alpha_dKOD = ixp.declarerank1("alpha_dKOD")
        cf_dKOD    = ixp.declarerank1("cf_dKOD")
        trK_dKOD   = ixp.declarerank1("trK_dKOD")
        betU_dKOD    = ixp.declarerank2("betU_dKOD","nosym")
        vetU_dKOD    = ixp.declarerank2("vetU_dKOD","nosym")
        lambdaU_dKOD = ixp.declarerank2("lambdaU_dKOD","nosym")
        aDD_dKOD = ixp.declarerank3("aDD_dKOD","sym01")
        hDD_dKOD = ixp.declarerank3("hDD_dKOD","sym01")
        for k in range(3):
            gaugerhs.alpha_rhs += diss_strength*alpha_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k]
            rhs.cf_rhs         += diss_strength*   cf_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k]
            rhs.trK_rhs        += diss_strength*  trK_dKOD[k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k]
            for i in range(3):
                if "2ndOrder" in ShiftCondition:
                    gaugerhs.bet_rhsU[i] += diss_strength*   betU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k]
                gaugerhs.vet_rhsU[i]     += diss_strength*   vetU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k]
                rhs.lambda_rhsU[i]       += diss_strength*lambdaU_dKOD[i][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k]
                for j in range(3):
                    rhs.a_rhsDD[i][j] += diss_strength*aDD_dKOD[i][j][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k]
                    rhs.h_rhsDD[i][j] += diss_strength*hDD_dKOD[i][j][k]*rfm.ReU[k] # ReU[k] = 1/scalefactor_orthog_funcform[k]

        # We use betaU as our upwinding control vector:
        Bq.BSSN_basic_tensors()
        betaU = Bq.betaU

        # Now that we are finished with all the rfm hatted
        #           quantities in generic precomputed functional
        #           form, let's restore them to their closed-
        #           form expressions.
        par.set_parval_from_str("reference_metric::enable_rfm_precompute","False") # Reset to False to disable rfm_precompute.
        rfm.ref_metric__hatted_quantities()
        par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","False")
        end = time.time()
        print("(BENCH) Finished BSSN RHS symbolic expressions in "+str(end-start)+" seconds.")
        # END: GENERATE SYMBOLIC EXPRESSIONS
        ######################################

        BSSN_RHSs_SymbExpressions = [lhrh(lhs=gri.gfaccess("rhs_gfs","aDD00"),   rhs=rhs.a_rhsDD[0][0]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","aDD01"),   rhs=rhs.a_rhsDD[0][1]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","aDD02"),   rhs=rhs.a_rhsDD[0][2]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","aDD11"),   rhs=rhs.a_rhsDD[1][1]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","aDD12"),   rhs=rhs.a_rhsDD[1][2]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","aDD22"),   rhs=rhs.a_rhsDD[2][2]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","alpha"),   rhs=gaugerhs.alpha_rhs),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","betU0"),   rhs=gaugerhs.bet_rhsU[0]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","betU1"),   rhs=gaugerhs.bet_rhsU[1]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","betU2"),   rhs=gaugerhs.bet_rhsU[2]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","cf"),      rhs=rhs.cf_rhs),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","hDD00"),   rhs=rhs.h_rhsDD[0][0]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","hDD01")   ,rhs=rhs.h_rhsDD[0][1]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","hDD02"),   rhs=rhs.h_rhsDD[0][2]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","hDD11"),   rhs=rhs.h_rhsDD[1][1]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","hDD12"),   rhs=rhs.h_rhsDD[1][2]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","hDD22"),   rhs=rhs.h_rhsDD[2][2]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU0"),rhs=rhs.lambda_rhsU[0]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU1"),rhs=rhs.lambda_rhsU[1]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","lambdaU2"),rhs=rhs.lambda_rhsU[2]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","trK"),     rhs=rhs.trK_rhs),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","vetU0"),   rhs=gaugerhs.vet_rhsU[0]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","vetU1"),   rhs=gaugerhs.vet_rhsU[1]),
                                     lhrh(lhs=gri.gfaccess("rhs_gfs","vetU2"),   rhs=gaugerhs.vet_rhsU[2]) ]

        return [betaU,BSSN_RHSs_SymbExpressions]

    def Ricci__generate_symbolic_expressions():
        ######################################
        # START: GENERATE SYMBOLIC EXPRESSIONS
        print("Generating symbolic expressions for Ricci tensor...")
        start = time.time()
        # Enable rfm_precompute infrastructure, which results in
        #   BSSN RHSs that are free of transcendental functions,
        #   even in curvilinear coordinates, so long as
        #   ConformalFactor is set to "W" (default).
        par.set_parval_from_str("reference_metric::enable_rfm_precompute","True")
        par.set_parval_from_str("reference_metric::rfm_precompute_Ccode_outdir",os.path.join(outdir,"rfm_files/"))

        # Evaluate BSSN + BSSN gauge RHSs with rfm_precompute enabled:
        import BSSN.BSSN_quantities as Bq

        # Next compute Ricci tensor
        # par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","False")
        RbarDD_already_registered = False
        for i in range(len(gri.glb_gridfcs_list)):
            if "RbarDD00" in gri.glb_gridfcs_list[i].name:
                RbarDD_already_registered = True
        if not RbarDD_already_registered:
            # We ignore the return value of ixp.register_gridfunctions_for_single_rank2() below
            #    as it is unused.
            ixp.register_gridfunctions_for_single_rank2("AUXEVOL","RbarDD","sym01")
        rhs.BSSN_RHSs()
        Bq.RicciBar__gammabarDD_dHatD__DGammaUDD__DGammaU()

        # Now that we are finished with all the rfm hatted
        #           quantities in generic precomputed functional
        #           form, let's restore them to their closed-
        #           form expressions.
        par.set_parval_from_str("reference_metric::enable_rfm_precompute","False") # Reset to False to disable rfm_precompute.
        rfm.ref_metric__hatted_quantities()
        end = time.time()
        print("(BENCH) Finished Ricci symbolic expressions in "+str(end-start)+" seconds.")
        # END: GENERATE SYMBOLIC EXPRESSIONS
        ######################################

        Ricci_SymbExpressions = [lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD00"),rhs=Bq.RbarDD[0][0]),
                                 lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD01"),rhs=Bq.RbarDD[0][1]),
                                 lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD02"),rhs=Bq.RbarDD[0][2]),
                                 lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD11"),rhs=Bq.RbarDD[1][1]),
                                 lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD12"),rhs=Bq.RbarDD[1][2]),
                                 lhrh(lhs=gri.gfaccess("auxevol_gfs","RbarDD22"),rhs=Bq.RbarDD[2][2])]

        return [Ricci_SymbExpressions]

    def BSSN_RHSs__generate_Ccode(all_RHSs_Ricci_exprs_list):
        betaU                     = all_RHSs_Ricci_exprs_list[0]
        BSSN_RHSs_SymbExpressions = all_RHSs_Ricci_exprs_list[1]

        print("Generating C code for BSSN RHSs (FD_order="+str(FD_order)+",Tmunu="+str(enable_stress_energy_source_terms)+") in "+par.parval_from_str("reference_metric::CoordSystem")+" coordinates.")
        start = time.time()

        # Store original finite-differencing order:
        FD_order_orig = par.parval_from_str("finite_difference::FD_CENTDERIVS_ORDER")
        # Set new finite-differencing order:
        par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order)

        BSSN_RHSs_string = fin.FD_outputC("returnstring",BSSN_RHSs_SymbExpressions,
                                          params="outCverbose=False,SIMD_enable=True,GoldenKernelsEnable=True",
                                          upwindcontrolvec=betaU)

        filename = "BSSN_RHSs_enable_Tmunu_"+str(enable_stress_energy_source_terms)+"_FD_order_"+str(FD_order)+".h"
        with open(os.path.join(outdir,filename), "w") as file:
            file.write(lp.loop(["i2","i1","i0"],["cctk_nghostzones[2]","cctk_nghostzones[1]","cctk_nghostzones[0]"],
           ["cctk_lsh[2]-cctk_nghostzones[2]","cctk_lsh[1]-cctk_nghostzones[1]","cctk_lsh[0]-cctk_nghostzones[0]"],
                               ["1","1","SIMD_width"],
                                ["#pragma omp parallel for",
                                 "#include \"rfm_files/rfm_struct__SIMD_outer_read2.h\"",
                                 r"""    #include "rfm_files/rfm_struct__SIMD_outer_read1.h"
    #if (defined __INTEL_COMPILER && __INTEL_COMPILER_BUILD_DATE >= 20180804)
        #pragma ivdep         // Forces Intel compiler (if Intel compiler used) to ignore certain SIMD vector dependencies
        #pragma vector always // Forces Intel compiler (if Intel compiler used) to vectorize
    #endif"""],"",
                    "#include \"rfm_files/rfm_struct__SIMD_inner_read0.h\"\n"+BSSN_RHSs_string))

        # Restore original finite-differencing order:
        par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order_orig)

        end = time.time()
        print("(BENCH) Finished BSSN_RHS C codegen (FD_order="+str(FD_order)+",Tmunu="+str(enable_stress_energy_source_terms)+") in " + str(end - start) + " seconds.")

    def Ricci__generate_Ccode(Ricci_exprs_list):
        Ricci_SymbExpressions     = Ricci_exprs_list[0]

        print("Generating C code for Ricci tensor (FD_order="+str(FD_order)+") in "+par.parval_from_str("reference_metric::CoordSystem")+" coordinates.")
        start = time.time()

        # Store original finite-differencing order:
        FD_order_orig = par.parval_from_str("finite_difference::FD_CENTDERIVS_ORDER")
        # Set new finite-differencing order:
        par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order)

        Ricci_string = fin.FD_outputC("returnstring", Ricci_SymbExpressions,
                                       params="outCverbose=False,SIMD_enable=True,GoldenKernelsEnable=True")

        filename = "BSSN_Ricci_FD_order_"+str(FD_order)+".h"
        with open(os.path.join(outdir, filename), "w") as file:
            file.write(lp.loop(["i2","i1","i0"],["cctk_nghostzones[2]","cctk_nghostzones[1]","cctk_nghostzones[0]"],
           ["cctk_lsh[2]-cctk_nghostzones[2]","cctk_lsh[1]-cctk_nghostzones[1]","cctk_lsh[0]-cctk_nghostzones[0]"],
                               ["1","1","SIMD_width"],
                                ["#pragma omp parallel for",
                                 "#include \"rfm_files/rfm_struct__SIMD_outer_read2.h\"",
                                 r"""    #include "rfm_files/rfm_struct__SIMD_outer_read1.h"
    #if (defined __INTEL_COMPILER && __INTEL_COMPILER_BUILD_DATE >= 20180804)
        #pragma ivdep         // Forces Intel compiler (if Intel compiler used) to ignore certain SIMD vector dependencies
        #pragma vector always // Forces Intel compiler (if Intel compiler used) to vectorize
    #endif"""],"",
                    "#include \"rfm_files/rfm_struct__SIMD_inner_read0.h\"\n"+Ricci_string))

        # Restore original finite-differencing order:
        par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order_orig)

        end = time.time()
        print("(BENCH) Finished Ricci C codegen (FD_order="+str(FD_order)+") in " + str(end - start) + " seconds.")

    def BSSN_constraints__generate_symbolic_expressions_and_C_code():
        ######################################
        # START: GENERATE SYMBOLIC EXPRESSIONS
        # Define the Hamiltonian constraint and output the optimized C code.
        import BSSN.BSSN_constraints as bssncon

        bssncon.BSSN_constraints(add_T4UUmunu_source_terms=False)
        if T4UU != None:
            import BSSN.BSSN_stress_energy_source_terms as Bsest
            Bsest.BSSN_source_terms_for_BSSN_RHSs(T4UU)
            Bsest.BSSN_source_terms_for_BSSN_constraints(T4UU)
            bssncon.H += Bsest.sourceterm_H
            for i in range(3):
                bssncon.MU[i] += Bsest.sourceterm_MU[i]
        # END: GENERATE SYMBOLIC EXPRESSIONS
        ######################################

        # Store original finite-differencing order:
        FD_order_orig = par.parval_from_str("finite_difference::FD_CENTDERIVS_ORDER")
        # Set new finite-differencing order:
        par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order)

        start = time.time()
        print("Generating optimized C code for Ham. & mom. constraints. May take a while, depending on CoordSystem.")
        Ham_mom_string = fin.FD_outputC("returnstring",
                                        [lhrh(lhs=gri.gfaccess("aux_gfs", "H"),   rhs=bssncon.H),
                                         lhrh(lhs=gri.gfaccess("aux_gfs", "MU0"), rhs=bssncon.MU[0]),
                                         lhrh(lhs=gri.gfaccess("aux_gfs", "MU1"), rhs=bssncon.MU[1]),
                                         lhrh(lhs=gri.gfaccess("aux_gfs", "MU2"), rhs=bssncon.MU[2])],
                                        params="outCverbose=False")

        with open(os.path.join(outdir,"BSSN_constraints_enable_Tmunu_"+str(enable_stress_energy_source_terms)+"_FD_order_"+str(FD_order)+".h"), "w") as file:
            file.write(lp.loop(["i2","i1","i0"],["cctk_nghostzones[2]","cctk_nghostzones[1]","cctk_nghostzones[0]"],
           ["cctk_lsh[2]-cctk_nghostzones[2]","cctk_lsh[1]-cctk_nghostzones[1]","cctk_lsh[0]-cctk_nghostzones[0]"],
                               ["1","1","1"],["#pragma omp parallel for","",""], "", Ham_mom_string))

        # Restore original finite-differencing order:
        par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order_orig)

        end = time.time()
        print("(BENCH) Finished Hamiltonian & momentum constraint C codegen (FD_order="+str(FD_order)+",Tmunu="+str(enable_stress_energy_source_terms)+") in " + str(end - start) + " seconds.")

    def enforce_detgammabar_eq_detgammahat__generate_symbolic_expressions_and_C_code():
        ######################################
        # START: GENERATE SYMBOLIC EXPRESSIONS

        # Enable rfm_precompute infrastructure, which results in
        #   BSSN RHSs that are free of transcendental functions,
        #   even in curvilinear coordinates, so long as
        #   ConformalFactor is set to "W" (default).
        par.set_parval_from_str("reference_metric::enable_rfm_precompute","True")
        par.set_parval_from_str("reference_metric::rfm_precompute_Ccode_outdir",os.path.join(outdir,"rfm_files/"))

        import BSSN.Enforce_Detgammabar_Constraint as EGC
        enforce_detg_constraint_symb_expressions = EGC.Enforce_Detgammabar_Constraint_symb_expressions()

        # Now that we are finished with all the rfm hatted
        #           quantities in generic precomputed functional
        #           form, let's restore them to their closed-
        #           form expressions.
        par.set_parval_from_str("reference_metric::enable_rfm_precompute","False") # Reset to False to disable rfm_precompute.
        rfm.ref_metric__hatted_quantities()
        # END: GENERATE SYMBOLIC EXPRESSIONS
        ######################################

        start = time.time()
        print("Generating optimized C code (FD_order="+str(FD_order)+") for gamma constraint. May take a while, depending on CoordSystem.")
        enforce_gammadet_string = fin.FD_outputC("returnstring", enforce_detg_constraint_symb_expressions,
                                                 params="outCverbose=False,preindent=0,includebraces=False")

        with open(os.path.join(outdir,"enforcedetgammabar_constraint.h"), "w") as file:
            file.write(lp.loop(["i2","i1","i0"],["0", "0", "0"],
                               ["cctk_lsh[2]","cctk_lsh[1]","cctk_lsh[0]"],
                               ["1","1","1"],
                                ["#pragma omp parallel for",
                                     "#include \"rfm_files/rfm_struct__read2.h\"",
                                     "#include \"rfm_files/rfm_struct__read1.h\""],"",
                                     "#include \"rfm_files/rfm_struct__read0.h\"\n"+enforce_gammadet_string))
        end = time.time()
        print("(BENCH) Finished gamma constraint C codegen (FD_order="+str(FD_order)+") in " + str(end - start) + " seconds.")

    if WhichPart=="BSSN_RHSs":
        BSSN_RHSs__generate_Ccode(BSSN_RHSs__generate_symbolic_expressions())
    elif WhichPart=="Ricci":
        Ricci__generate_Ccode(Ricci__generate_symbolic_expressions())
    elif WhichPart=="BSSN_constraints":
        BSSN_constraints__generate_symbolic_expressions_and_C_code()
    elif WhichPart=="detgammabar_constraint":
        enforce_detgammabar_eq_detgammahat__generate_symbolic_expressions_and_C_code()
    else:
        print("Error: WhichPart = "+WhichPart+" is not recognized.")
        sys.exit(1)

    # Store all NRPy+ environment variables to an output string so NRPy+ environment from within this subprocess can be easily restored
    import pickle  # Standard Python module for converting arbitrary data structures to a uniform format.
    # https://www.pythonforthelab.com/blog/storing-binary-data-and-serializing/
    outstr = []
    outstr.append(pickle.dumps(len(gri.glb_gridfcs_list)))
    for lst in gri.glb_gridfcs_list:
        outstr.append(pickle.dumps(lst.gftype))
        outstr.append(pickle.dumps(lst.name))
        outstr.append(pickle.dumps(lst.rank))
        outstr.append(pickle.dumps(lst.DIM))

    outstr.append(pickle.dumps(len(par.glb_params_list)))
    for lst in par.glb_params_list:
        outstr.append(pickle.dumps(lst.type))
        outstr.append(pickle.dumps(lst.module))
        outstr.append(pickle.dumps(lst.parname))
        outstr.append(pickle.dumps(lst.defaultval))

    outstr.append(pickle.dumps(len(par.glb_Cparams_list)))
    for lst in par.glb_Cparams_list:
        outstr.append(pickle.dumps(lst.type))
        outstr.append(pickle.dumps(lst.module))
        outstr.append(pickle.dumps(lst.parname))
        outstr.append(pickle.dumps(lst.defaultval))

    outstr.append(pickle.dumps(len(outC_function_dict)))
    for Cfuncname, Cfunc in outC_function_dict.items():
        outstr.append(pickle.dumps(Cfuncname))
        outstr.append(pickle.dumps(Cfunc))

    return outstr
Пример #29
0
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).replace("IDX4", "IDX4S"),
        loopopts="AllPoints",
        rel_path_for_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).replace("IDX4", "IDX4S"),
        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")
    # 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))
    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).replace("IDX4", "IDX4S"),
        loopopts="AllPoints,Read_xxs",
        rel_path_for_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).replace("IDX4", "IDX4S"),
        loopopts="AllPoints",
        rel_path_for_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 "../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 "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 override_BU_with_old_GiRaFFE(const paramstruct *restrict params,REAL *restrict auxevol_gfs,const int n) {
#include "set_Cparameters.h"
    char filename[100];
    sprintf(filename,"BU0_override-%08d.bin",n);
    FILE *out2D = fopen(filename, "rb");
    fread(auxevol_gfs+BU0GF*Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2,
          sizeof(double),Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2,out2D);
    fclose(out2D);
    sprintf(filename,"BU1_override-%08d.bin",n);
    out2D = fopen(filename, "rb");
    fread(auxevol_gfs+BU1GF*Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2,
          sizeof(double),Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2,out2D);
    fclose(out2D);
    sprintf(filename,"BU2_override-%08d.bin",n);
    out2D = fopen(filename, "rb");
    fread(auxevol_gfs+BU2GF*Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2,
          sizeof(double),Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2,out2D);
    fclose(out2D);
}

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);
        }
        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)],
              &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);
}
""")
Пример #30
0
def GiRaFFE_NRPy_Afield_flux(Ccodesdir):
    cmd.mkdir(Ccodesdir)
    # Write out the code to a file.

    gammaDD = ixp.declarerank2("gammaDD", "sym01", DIM=3)
    betaU = ixp.declarerank1("betaU", DIM=3)
    alpha = sp.sympify("alpha")

    for flux_dirn in range(3):
        chsp.find_cmax_cmin(flux_dirn, gammaDD, betaU, alpha)
        Ccode_kernel = outputC([chsp.cmax, chsp.cmin], ["cmax", "cmin"],
                               "returnstring",
                               params="outCverbose=False,CSE_sorting=none")
        Ccode_kernel = Ccode_kernel.replace("cmax",
                                            "*cmax").replace("cmin", "*cmin")
        Ccode_kernel = Ccode_kernel.replace("betaU0", "betaUi").replace(
            "betaU1", "betaUi").replace("betaU2", "betaUi")

        with open(
                os.path.join(Ccodesdir,
                             "compute_cmax_cmin_dirn" + str(flux_dirn) + ".h"),
                "w") as file:
            file.write(Ccode_kernel)

    with open(os.path.join(Ccodesdir, "calculate_E_field_flat_all_in_one.h"),
              "w") as file:
        file.write(
            r"""void find_cmax_cmin(const REAL gammaDD00, const REAL gammaDD01, const REAL gammaDD02,
                    const REAL gammaDD11, const REAL gammaDD12, const REAL gammaDD22,
                    const REAL betaUi, const REAL alpha, const int flux_dirn,
                    REAL *cmax, REAL *cmin) {
    switch(flux_dirn) {
        case 0:
#include "compute_cmax_cmin_dirn0.h"
            break;
        case 1:
#include "compute_cmax_cmin_dirn1.h"
            break;
        case 2:
#include "compute_cmax_cmin_dirn2.h"
            break;
        default:
            printf("Invalid parameter flux_dirn!"); *cmax = 1.0/0.0; *cmin = 1.0/0.0;
            break;
    }
}

REAL HLLE_solve(REAL F0B1_r, REAL F0B1_l, REAL U_r, REAL U_l, REAL cmin, REAL cmax) {
  // Eq. 3.15 of https://epubs.siam.org/doi/abs/10.1137/1025002?journalCode=siread
  // F_HLLE = (c_min F_R + c_max F_L - c_min c_max (U_R-U_L)) / (c_min + c_max)
  return (cmin*F0B1_r + cmax*F0B1_l - cmin*cmax*(U_r-U_l)) / (cmin+cmax);
}

/*
Calculate the electric flux on both faces in the input direction.
The input count is an integer that is either 0 or 1. If it is 0, this implies
that the components are input in order of a backwards permutation  and the final
results will need to be multiplied by -1.0. If it is 1, then the permutation is forwards.
 */
void calculate_E_field_flat_all_in_one(const paramstruct *params,
                                       const REAL *Vr0,const REAL *Vr1,
                                       const REAL *Vl0,const REAL *Vl1,
                                       const REAL *Br0,const REAL *Br1,
                                       const REAL *Bl0,const REAL *Bl1,
                                       const REAL *Brflux_dirn,
                                       const REAL *Blflux_dirn,
                                       const REAL *gamma_faceDD00, const REAL *gamma_faceDD01, const REAL *gamma_faceDD02,
                                       const REAL *gamma_faceDD11, const REAL *gamma_faceDD12, const REAL *gamma_faceDD22,
                                       const REAL *beta_faceU0, const REAL *beta_faceU1, const REAL *alpha_face,
                                       REAL *A2_rhs,const REAL SIGN,const int flux_dirn) {
    // This function is written to be generic and compute the contribution for all three AD RHSs.
    // However, for convenience, the notation used in the function itself is for the contribution
    // to AD2, specifically the [F_HLL^x(B^y)]_z term, with reconstructions in the x direction. This
    // corresponds to flux_dirn=0 and count=1 (which corresponds to SIGN=+1.0).
    // Thus, Az(i,j,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)) are solved here.
    // The other terms are computed by cyclically permuting the indices when calling this function.
#include "../set_Cparameters.h"

#pragma omp parallel for
    for(int i2=NGHOSTS; i2<NGHOSTS+Nxx2; i2++) {
        for(int i1=NGHOSTS; i1<NGHOSTS+Nxx1; i1++) {
            for(int i0=NGHOSTS; i0<NGHOSTS+Nxx0; i0++) {
                // First, we set the index from which we will read memory. indexp1 is incremented by
                // one point in the direction of reconstruction. These correspond to the faces at at
                // i-1/2 and i+1/2, respectively.

                // Now, we read in memory. We need the x and y components of velocity and magnetic field on both
                // the left and right sides of the interface at *both* faces.
                // Here, the point (i0,i1,i2) corresponds to the point (i-1/2,j,k)
                const int index           = IDX3S(i0,i1,i2);
                const double alpha        = alpha_face[index];
                const double betaU0       = beta_faceU0[index];
                const double betaU1       = beta_faceU1[index];
                const double v_rU0        = alpha*Vr0[index]-betaU0;
                const double v_rU1        = alpha*Vr1[index]-betaU1;
                const double B_rU0        = Br0[index];
                const double B_rU1        = Br1[index];
                const double B_rflux_dirn = Brflux_dirn[index];
                const double v_lU0        = alpha*Vl0[index]-betaU0;
                const double v_lU1        = alpha*Vl1[index]-betaU1;
                const double B_lU0        = Bl0[index];
                const double B_lU1        = Bl1[index];
                const double B_lflux_dirn = Blflux_dirn[index];
                // We will also need need the square root of the metric determinant here at this point:
                const REAL gxx = gamma_faceDD00[index];
                const REAL gxy = gamma_faceDD01[index];
                const REAL gxz = gamma_faceDD02[index];
                const REAL gyy = gamma_faceDD11[index];
                const REAL gyz = gamma_faceDD12[index];
                const REAL gzz = gamma_faceDD22[index];
                const REAL sqrtgammaDET = sqrt( gxx*gyy*gzz
                                             -  gxx*gyz*gyz
                                             +2*gxy*gxz*gyz
                                             -  gyy*gxz*gxz
                                             -  gzz*gxy*gxy );

                // *******************************
                // REPEAT ABOVE, but at i+1, which corresponds to point (i+1/2,j,k)
                //     Recall that the documentation here assumes flux_dirn==0, but the
                //     algorithm is generalized so that any flux_dirn or velocity/magnetic
                //     field component can be computed via permuting the inputs into this
                //     function.
                const int indexp1            = IDX3S(i0+(flux_dirn==0),i1+(flux_dirn==1),i2+(flux_dirn==2));
                const double alpha_p1        = alpha_face[indexp1];
                const double betaU0_p1       = beta_faceU0[indexp1];
                const double betaU1_p1       = beta_faceU1[indexp1];
                const double v_rU0_p1        = alpha_p1*Vr0[indexp1]-betaU0_p1;
                const double v_rU1_p1        = alpha_p1*Vr1[indexp1]-betaU1_p1;
                const double B_rU0_p1        = Br0[indexp1];
                const double B_rU1_p1        = Br1[indexp1];
                const double B_rflux_dirn_p1 = Brflux_dirn[indexp1];
                const double v_lU0_p1        = alpha_p1*Vl0[indexp1]-betaU0_p1;
                const double v_lU1_p1        = alpha_p1*Vl1[indexp1]-betaU1_p1;
                const double B_lU0_p1        = Bl0[indexp1];
                const double B_lU1_p1        = Bl1[indexp1];
                const double B_lflux_dirn_p1 = Blflux_dirn[indexp1];
                // We will also need need the square root of the metric determinant here at this point:
                const REAL gxx_p1 = gamma_faceDD00[indexp1];
                const REAL gxy_p1 = gamma_faceDD01[indexp1];
                const REAL gxz_p1 = gamma_faceDD02[indexp1];
                const REAL gyy_p1 = gamma_faceDD11[indexp1];
                const REAL gyz_p1 = gamma_faceDD12[indexp1];
                const REAL gzz_p1 = gamma_faceDD22[indexp1];
                const REAL sqrtgammaDET_p1 = sqrt( gxx_p1*gyy_p1*gzz_p1
                                                -  gxx_p1*gyz_p1*gyz_p1
                                                +2*gxy_p1*gxz_p1*gyz_p1
                                                -  gyy_p1*gxz_p1*gxz_p1
                                                -  gzz_p1*gxy_p1*gxy_p1 );

                // *******************************

                // DEBUGGING:
//                 if(flux_dirn==0 && SIGN>0 && i1==Nxx_plus_2NGHOSTS1/2 && i2==Nxx_plus_2NGHOSTS2/2) {
//                     printf("index=%d & indexp1=%d\n",index,indexp1);
//                 }

                // Since we are computing A_z, the relevant equation here is:
                // -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) )
                // We will construct the above sum one half at a time, first with SIGN=+1, which
                // corresponds to flux_dirn = 0, count=1, and
                //  takes care of the terms:
                //  [F_HLL^x(B^y)]_z(i+1/2,j,k)+[F_HLL^x(B^y)]_z(i-1/2,j,k)

                // ( Note that we will repeat the above with flux_dirn = 1, count = 0, with SIGN=-1
                //   AND with the input components switched (x->y,y->x) so that we get the term
                // -[F_HLL^y(B^x)]_z(i,j+1/2,k)-[F_HLL^y(B^x)]_z(i,j-1/2,k)
                // thus completing the above sum. )

                // Here, [F_HLL^i(B^j)]_k = (v^i B^j - v^j B^i) in general.

                // Calculate the flux vector on each face for each component of the E-field:
                // The F(B) terms are as Eq. 6 in Giacomazzo: https://arxiv.org/pdf/1009.2468.pdf
                // [F^i(B^j)]_k = \sqrt{\gamma} (v^i B^j - v^j B^i)
                // Therefore since we want [F_HLL^x(B^y)]_z,
                // we will code     (v^x           B^y   - v^y           B^x) on both left and right faces.
                const REAL F0B1_r = sqrtgammaDET*(v_rU0*B_rU1 - v_rU1*B_rU0);
                const REAL F0B1_l = sqrtgammaDET*(v_lU0*B_lU1 - v_lU1*B_lU0);

                // Compute the state vector for these terms:
                const REAL U_r = B_rflux_dirn;
                const REAL U_l = B_lflux_dirn;

                REAL cmin,cmax;
                // Basic HLLE solver:
                find_cmax_cmin(gxx,gxy,gxz,
                               gyy,gyz,gzz,
                               betaU0,alpha,flux_dirn,
                               &cmax, &cmin);
                const REAL FHLL_0B1 = HLLE_solve(F0B1_r, F0B1_l, U_r, U_l, cmin, cmax);

                // ************************************
                // ************************************
                // REPEAT ABOVE, but at point i+1
                // Calculate the flux vector on each face for each component of the E-field:
                const REAL F0B1_r_p1 = sqrtgammaDET_p1*(v_rU0_p1*B_rU1_p1 - v_rU1_p1*B_rU0_p1);
                const REAL F0B1_l_p1 = sqrtgammaDET_p1*(v_lU0_p1*B_lU1_p1 - v_lU1_p1*B_lU0_p1);

                // Compute the state vector for this flux direction
                const REAL U_r_p1 = B_rflux_dirn_p1;
                const REAL U_l_p1 = B_lflux_dirn_p1;
                //const REAL U_r_p1 = B_rU1_p1;
                //const REAL U_l_p1 = B_lU1_p1;
                // Basic HLLE solver, but at the next point:
                find_cmax_cmin(gxx_p1,gxy_p1,gxz_p1,
                               gyy_p1,gyz_p1,gzz_p1,
                               betaU0_p1,alpha_p1,flux_dirn,
                               &cmax, &cmin);
                const REAL FHLL_0B1p1 = HLLE_solve(F0B1_r_p1, F0B1_l_p1, U_r_p1, U_l_p1, cmin, cmax);
                // ************************************
                // ************************************


                // With the Riemann problem solved, we add the contributions to the RHSs:
                // -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) )
                // (Eq. 11 in https://arxiv.org/pdf/1009.2468.pdf)
                // This code, as written, solves the first two terms for flux_dirn=0. Calling this function for count=0
                // and flux_dirn=1 flips x for y to solve the latter two, switching to SIGN=-1 as well.

                // Here, we finally add together the output of the HLLE solver at i-1/2 and i+1/2
                // We also multiply by the SIGN dictated by the order of the input vectors and divide by 4.
                A2_rhs[index] += SIGN*0.25*(FHLL_0B1 + FHLL_0B1p1);
                // flux dirn = 0 ===================>   i-1/2       i+1/2
                //               Eq 11 in Giacomazzo:
                //               -FxBy(avg over i-1/2 and i+1/2) + FyBx(avg over j-1/2 and j+1/2)
                //               Eq 6 in Giacomazzo:
                //               FxBy = vxBy - vyBx
                //             ->
                //               FHLL_0B1 = vyBx - vxBy

            } // END LOOP: for(int i0=NGHOSTS; i0<NGHOSTS+Nxx0; i0++)
        } // END LOOP: for(int i1=NGHOSTS; i1<NGHOSTS+Nxx1; i1++)
    } // END LOOP: for(int i2=NGHOSTS; i2<NGHOSTS+Nxx2; i2++)
}
""")