Example #1
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)
def GiRaFFE_NRPy_Source_Terms(Ccodesdir):
    cmd.mkdir(Ccodesdir)
    # Write out the code to a file.
    with open(
            os.path.join(Ccodesdir,
                         "Lorenz_psi6phi_rhs__add_gauge_terms_to_A_i_rhs.h"),
            "w") as file:
        file.write(body)
Example #3
0
    def enforce_detgammabar_eq_detgammahat__generate_symbolic_expressions_and_C_code():
        ######################################
        # START: 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)

        # 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).
        cmd.mkdir(os.path.join(outdir,"rfm_files/"))
        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()

        # Restore original finite-differencing order:
        par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order)
        # 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_FD_order_"+str(FD_order)+".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("Finished gamma constraint C codegen (FD_order="+str(FD_order)+") in " + str(end - start) + " seconds.")
Example #4
0
def GiRaFFE_NRPy_FCVAL(Ccodesdir):
    cmd.mkdir(Ccodesdir)
    # Write out the code to a file.
    with open(
            os.path.join(Ccodesdir, "interpolate_metric_gfs_to_cell_faces.h"),
            "w") as file:
        file.write(prefunc)

    desc = "Interpolate metric gridfunctions to cell faces"
    name = "interpolate_metric_gfs_to_cell_faces"
    interp_Cfunc = outCfunction(
        outfile="returnstring",
        desc=desc,
        name=name,
        params=
        "const paramstruct *params,REAL *auxevol_gfs,const int flux_dirn",
        preloop="""    int in_gf,out_gf;
    REAL Qm2,Qm1,Qp0,Qp1;

""",
        body="""    for(int gf = 0;gf < num_metric_gfs;gf++) {
        in_gf  = metric_gfs_list[gf];
        out_gf = metric_gfs_face_list[gf];
        for (int i2 = 2;i2 < Nxx_plus_2NGHOSTS2-1;i2++) {
            for (int i1 = 2;i1 < Nxx_plus_2NGHOSTS1-1;i1++) {
                for (int i0 = 2;i0 < Nxx_plus_2NGHOSTS0-1;i0++) {
                    Qm2 = auxevol_gfs[IDX4S(in_gf,i0-2*kronecker_delta[flux_dirn][0],i1-2*kronecker_delta[flux_dirn][1],i2-2*kronecker_delta[flux_dirn][2])];
                    Qm1 = auxevol_gfs[IDX4S(in_gf,i0-kronecker_delta[flux_dirn][0],i1-kronecker_delta[flux_dirn][1],i2-kronecker_delta[flux_dirn][2])];
                    Qp0 = auxevol_gfs[IDX4S(in_gf,i0,i1,i2)];
                    Qp1 = auxevol_gfs[IDX4S(in_gf,i0+kronecker_delta[flux_dirn][0],i1+kronecker_delta[flux_dirn][1],i2+kronecker_delta[flux_dirn][2])];
                    auxevol_gfs[IDX4S(out_gf,i0,i1,i2)] = COMPUTE_FCVAL(Qm2,Qm1,Qp0,Qp1);
                }
            }
        }
    }
""",
        rel_path_to_Cparams=os.path.join("../"))

    with open(
            os.path.join(Ccodesdir, "interpolate_metric_gfs_to_cell_faces.h"),
            "a") as file:
        file.write(interp_Cfunc)
Example #5
0
def BaikalETK_C_kernels_codegen_onepart(NRPyDir=os.path.join(".."),
       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] == "WhichParamSet":
                WhichParamSet = int(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)

    # Create directory for BaikalETK thorn & subdirectories in case they don't exist.
    outrootdir = ThornName
    cmd.mkdir(os.path.join(outrootdir))
    outdir = os.path.join(outrootdir,"src") # Main C code output directory

    # Copy SIMD/SIMD_intrinsics.h to $outdir/SIMD/SIMD_intrinsics.h
    cmd.mkdir(os.path.join(outdir,"SIMD"))
    shutil.copy(os.path.join(NRPyDir,"SIMD/")+"SIMD_intrinsics.h",os.path.join(outdir,"SIMD/"))

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

    REAL      = "CCTK_REAL" # Set REAL to CCTK_REAL, the ETK data type for 
                            # floating point precision (typically `double`)

    # 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:
        H  = gri.register_gridfunctions("AUX","H")
        MU = ixp.register_gridfunctions_for_single_rank1("AUX", "MU")

    def BSSN_RHSs_Ricci__generate_symbolic_expressions():
        ######################################
        # START: 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)


        print("Generating symbolic expressions for BSSN RHSs and 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).
        cmd.mkdir(os.path.join(outdir,"rfm_files/"))
        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

        # Next compute Ricci tensor
        par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","False")
        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("Finished BSSN symbolic expressions in "+str(end-start)+" seconds.")
        # Restore original finite-differencing order:
        par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order)
        # 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]) ]

        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 [betaU,BSSN_RHSs_SymbExpressions,Ricci_SymbExpressions]

    def BSSN_RHSs_Ricci__generate_Ccode(which_expressions, all_RHSs_Ricci_exprs_list):
        betaU                     = all_RHSs_Ricci_exprs_list[0]
        BSSN_RHSs_SymbExpressions = all_RHSs_Ricci_exprs_list[1]
        Ricci_SymbExpressions     = all_RHSs_Ricci_exprs_list[2]

        if(which_expressions == "BSSN_RHSs"):
            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()
            BSSN_RHSs_string = fin.FD_outputC("returnstring",BSSN_RHSs_SymbExpressions, 
                                              params="outCverbose=False,SIMD_enable=True",
                                              upwindcontrolvec=betaU)

            with open(os.path.join(outdir,"BSSN_RHSs_FD_order_"+str(FD_order)+"_enable_Tmunu_"+str(enable_stress_energy_source_terms)+".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","SIMD_width"],
                                    ["#pragma omp parallel for",
                                                 "#include \"rfm_files/rfm_struct__SIMD_outer_read2.h\"",
                                                 "#include \"rfm_files/rfm_struct__SIMD_outer_read1.h\""],"",
                                                 "#include \"rfm_files/rfm_struct__SIMD_inner_read0.h\"\n"+BSSN_RHSs_string))
            end = time.time()
            print("Finished BSSN_RHS C codegen (FD_order="+str(FD_order)+",Tmunu="+str(enable_stress_energy_source_terms)+") in " + str(end - start) + " seconds.")

        elif(which_expressions == "Ricci"):
            print("Generating C code for Ricci tensor (FD_order="+str(FD_order)+") in "+par.parval_from_str("reference_metric::CoordSystem")+" coordinates.")
            start = time.time()
            Ricci_string = fin.FD_outputC("returnstring", Ricci_SymbExpressions,
                                           params="outCverbose=False,SIMD_enable=True")
            with open(os.path.join(outdir,"BSSN_Ricci_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","SIMD_width"],
                                    ["#pragma omp parallel for",
                                                 "#include \"rfm_files/rfm_struct__SIMD_outer_read2.h\"",
                                                 "#include \"rfm_files/rfm_struct__SIMD_outer_read1.h\""],"",
                                                 "#include \"rfm_files/rfm_struct__SIMD_inner_read0.h\"\n"+Ricci_string))
            end = time.time()
            print("Finished Ricci C codegen (FD_order="+str(FD_order)+") in " + str(end - start) + " seconds.")
        else:
            print("Error: unexpected argument, "+str(which_expressions)+" to BSSN_RHSs_Ricci__generate_Ccode()")

    def BSSN_constraints__generate_symbolic_expressions_and_C_code():
        ######################################
        # START: GENERATE SYMBOLIC EXPRESSIONS
        ######################################
        # START: 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)

        # 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]
        # Restore original finite-differencing order:
        par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order)
        # END: GENERATE SYMBOLIC EXPRESSIONS
        ######################################

        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_FD_order_"+str(FD_order)+"_enable_Tmunu_"+str(enable_stress_energy_source_terms)+".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))
        end = time.time()
        print("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
        # 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)

        # 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).
        cmd.mkdir(os.path.join(outdir,"rfm_files/"))
        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()

        # Restore original finite-differencing order:
        par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order)
        # 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_FD_order_"+str(FD_order)+".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("Finished gamma constraint C codegen (FD_order="+str(FD_order)+") in " + str(end - start) + " seconds.")

    if WhichPart=="BSSN_RHSs":
        BSSN_RHSs_Ricci__generate_Ccode("BSSN_RHSs", BSSN_RHSs_Ricci__generate_symbolic_expressions())
    elif WhichPart=="Ricci":
        BSSN_RHSs_Ricci__generate_Ccode("Ricci", BSSN_RHSs_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
    # 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))
    return outstr
Example #6
0
    def BSSN_RHSs_Ricci__generate_symbolic_expressions():
        ######################################
        # START: 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)


        print("Generating symbolic expressions for BSSN RHSs and 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).
        cmd.mkdir(os.path.join(outdir,"rfm_files/"))
        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

        # Next compute Ricci tensor
        par.set_parval_from_str("BSSN.BSSN_quantities::LeaveRicciSymbolic","False")
        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("Finished BSSN symbolic expressions in "+str(end-start)+" seconds.")
        # Restore original finite-differencing order:
        par.set_parval_from_str("finite_difference::FD_CENTDERIVS_ORDER", FD_order)
        # 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]) ]

        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 [betaU,BSSN_RHSs_SymbExpressions,Ricci_SymbExpressions]
import os, sys  # Python module: used for system and OS specific commands
import sympy as sp  # Python module: used for symbolic expressions

# Register NRPy+ root directory to the path
nrpy_dir_path = os.path.join("..", "..")
if nrpy_dir_path not in sys.path:
    sys.path.append(nrpy_dir_path)

# Load NRPy+ modules
from outputC import *  # NRPy+ module: used to output sympy expressions to C
import indexedexp as ixp  # NRPy+ module: used to generate indexed expressions (e.g. g_{\mu\nu})
import cmdline_helper as cmd  # NRPy+ module: used for command line features

# Create the NRPy+ header file directory, if it doesn't already exist
IGM_src_dir_path = os.path.join("..", "src")
cmd.mkdir(os.path.join(IGM_src_dir_path, "NRPy_generated_headers"))
NRPy_headers_dir_path = os.path.join(IGM_src_dir_path,
                                     "NRPy_generated_headers")


# Set up a neat function to output the expressions to NRPy+ generated files
def NRPy_IGM_write_to_file(filepath,
                           filename,
                           contents,
                           precontents="",
                           postcontents=""):
    with open(filepath, "w") as file:
        file.write("""
/* .-----------------------------------------------------------------------.
 * | This file was generated by NRPy+ for IllinoisGRMHD, as documented in: |
 * |        Tutorial-IllinoisGRMHD__NRPyfied_IGM_expressions.ipynb         |
Example #8
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)
def GiRaFFE_NRPy_BCs(Ccodesdir):
    cmd.mkdir(Ccodesdir)
    # Write out the code to a file.
    with open(os.path.join(Ccodesdir,name),"w") as file:
        file.write(body)
Example #10
0
def Set_up_CurviBoundaryConditions(Ccodesdir,
                                   verbose=True,
                                   Cparamspath=os.path.join("../"),
                                   enable_copy_of_static_Ccodes=True,
                                   BoundaryCondition="QuadraticExtrapolation"):
    # Step P0: Check that Ccodesdir is not the same as CurviBoundaryConditions/boundary_conditions,
    #          to prevent trusted versions of these C codes from becoming contaminated.
    if os.path.join(Ccodesdir) == os.path.join("CurviBoundaryConditions",
                                               "boundary_conditions"):
        print(
            "Error: Tried to output boundary conditions C code into CurviBoundaryConditions/boundary_conditions,"
            "       which is not allowed, to prevent trusted versions of these C codes from becoming contaminated."
        )
        sys.exit(1)

    # Step P1: Create the C codes output directory & copy static CurviBC files
    #          from CurviBoundaryConditions/boundary_conditions to Ccodesdir/
    if enable_copy_of_static_Ccodes:
        cmd.mkdir(os.path.join(Ccodesdir))

        # Choosing boundary condition drivers with in NRPy+
        #  - current options are Quadratic Polynomial Extrapolation for any coordinate system,
        #    and the Sommerfeld boundary condition for only cartesian coordinates
        if str(BoundaryCondition) == "QuadraticExtrapolation":
            for file in [
                    "apply_bcs_curvilinear.h", "BCs_data_structs.h",
                    "bcstruct_freemem.h", "CurviBC_include_Cfunctions.h",
                    "driver_bcstruct.h", "set_bcstruct.h",
                    "set_up__bc_gz_map_and_parity_condns.h"
            ]:
                shutil.copy(
                    os.path.join("CurviBoundaryConditions",
                                 "boundary_conditions", file),
                    os.path.join(Ccodesdir))

            with open(os.path.join(Ccodesdir, "CurviBC_include_Cfunctions.h"),
                      "a") as file:
                file.write("\n#include \"apply_bcs_curvilinear.h\"")

        elif str(BoundaryCondition) == "Sommerfeld":
            for file in [
                    "apply_bcs_sommerfeld.h", "BCs_data_structs.h",
                    "bcstruct_freemem.h", "CurviBC_include_Cfunctions.h",
                    "driver_bcstruct.h", "set_bcstruct.h",
                    "set_up__bc_gz_map_and_parity_condns.h"
            ]:
                shutil.copy(
                    os.path.join("CurviBoundaryConditions",
                                 "boundary_conditions", file),
                    os.path.join(Ccodesdir))

            with open(os.path.join(Ccodesdir, "CurviBC_include_Cfunctions.h"),
                      "a") as file:
                file.write("\n#include \"apply_bcs_sommerfeld.h\"")

        elif str(BoundaryCondition) == "QuadraticExtrapolation&Sommerfeld":
            for file in [
                    "apply_bcs_curvilinear.h", "apply_bcs_sommerfeld.h",
                    "BCs_data_structs.h", "bcstruct_freemem.h",
                    "CurviBC_include_Cfunctions.h", "driver_bcstruct.h",
                    "set_bcstruct.h", "set_up__bc_gz_map_and_parity_condns.h"
            ]:
                shutil.copy(
                    os.path.join("CurviBoundaryConditions",
                                 "boundary_conditions", file),
                    os.path.join(Ccodesdir))

            with open(os.path.join(Ccodesdir, "CurviBC_include_Cfunctions.h"),
                      "a") as file:
                file.write("\n#include \"apply_bcs_sommerfeld.h\"" +
                           "\n#include \"apply_bcs_curvilinear.h\"")

        else:
            print(
                "ERROR: Only Quadratic Polynomial Extrapolation (QuadraticExtrapolation) and Sommerfeld boundary conditions are currently supported\n"
            )
            sys.exit(1)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    # Step 6: Restore reference_metric::CoordSystem back to the original CoordSystem
    par.set_parval_from_str("reference_metric::CoordSystem", CoordSystem_orig)
    rfm.reference_metric()
Example #11
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")

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

    GRHD.compute_sqrtgammaDET(gammaDD)
    GRFFE.compute_AD_source_term_parenthetical_for_FD(GRHD.sqrtgammaDET,betaU,alpha,psi6Phi,AD)
    GRFFE.compute_psi6Phi_rhs_parenthetical(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_parentheticals_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="outCverbose=False").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="outCverbose=False").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)
    
    # Declare all the Cparameters we will need
    metricderivDDD = ixp.declarerank3("metricderivDDD","sym01",DIM=3)
    shiftderivUD = ixp.declarerank2("shiftderivUD","nosym",DIM=3)
    lapsederivD = ixp.declarerank1("lapsederivD",DIM=3)

    general_access = """const REAL gammaDD00 = auxevol_gfs[IDX4S(GAMMADD00GF,i0,i1,i2)];
const REAL gammaDD01 = auxevol_gfs[IDX4S(GAMMADD01GF,i0,i1,i2)];
const REAL gammaDD02 = auxevol_gfs[IDX4S(GAMMADD02GF,i0,i1,i2)];
const REAL gammaDD11 = auxevol_gfs[IDX4S(GAMMADD11GF,i0,i1,i2)];
const REAL gammaDD12 = auxevol_gfs[IDX4S(GAMMADD12GF,i0,i1,i2)];
const REAL gammaDD22 = auxevol_gfs[IDX4S(GAMMADD22GF,i0,i1,i2)];
const REAL betaU0 = auxevol_gfs[IDX4S(BETAU0GF,i0,i1,i2)];
const REAL betaU1 = auxevol_gfs[IDX4S(BETAU1GF,i0,i1,i2)];
const REAL betaU2 = auxevol_gfs[IDX4S(BETAU2GF,i0,i1,i2)];
const REAL alpha = auxevol_gfs[IDX4S(ALPHAGF,i0,i1,i2)];
const REAL ValenciavU0 = auxevol_gfs[IDX4S(VALENCIAVU0GF,i0,i1,i2)];
const REAL ValenciavU1 = auxevol_gfs[IDX4S(VALENCIAVU1GF,i0,i1,i2)];
const REAL ValenciavU2 = auxevol_gfs[IDX4S(VALENCIAVU2GF,i0,i1,i2)];
const REAL BU0 = auxevol_gfs[IDX4S(BU0GF,i0,i1,i2)];
const REAL BU1 = auxevol_gfs[IDX4S(BU1GF,i0,i1,i2)];
const REAL BU2 = auxevol_gfs[IDX4S(BU2GF,i0,i1,i2)];
"""
    metric_deriv_access = ixp.zerorank1(DIM=3)
    metric_deriv_access[0] = """const REAL metricderivDDD000 = (auxevol_gfs[IDX4S(GAMMA_FACEDD00GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD00GF,i0,i1,i2)])/dxx0;
const REAL metricderivDDD010 = (auxevol_gfs[IDX4S(GAMMA_FACEDD01GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD01GF,i0,i1,i2)])/dxx0;
const REAL metricderivDDD020 = (auxevol_gfs[IDX4S(GAMMA_FACEDD02GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD02GF,i0,i1,i2)])/dxx0;
const REAL metricderivDDD110 = (auxevol_gfs[IDX4S(GAMMA_FACEDD11GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD11GF,i0,i1,i2)])/dxx0;
const REAL metricderivDDD120 = (auxevol_gfs[IDX4S(GAMMA_FACEDD12GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD12GF,i0,i1,i2)])/dxx0;
const REAL metricderivDDD220 = (auxevol_gfs[IDX4S(GAMMA_FACEDD22GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD22GF,i0,i1,i2)])/dxx0;
const REAL shiftderivUD00 = (auxevol_gfs[IDX4S(BETA_FACEU0GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(BETA_FACEU0GF,i0,i1,i2)])/dxx0;
const REAL shiftderivUD10 = (auxevol_gfs[IDX4S(BETA_FACEU1GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(BETA_FACEU1GF,i0,i1,i2)])/dxx0;
const REAL shiftderivUD20 = (auxevol_gfs[IDX4S(BETA_FACEU2GF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(BETA_FACEU2GF,i0,i1,i2)])/dxx0;
const REAL lapsederivD0 = (auxevol_gfs[IDX4S(ALPHA_FACEGF,i0+1,i1,i2)]-auxevol_gfs[IDX4S(ALPHA_FACEGF,i0,i1,i2)])/dxx0;
REAL Stilde_rhsD0;
"""
    metric_deriv_access[1] = """const REAL metricderivDDD001 = (auxevol_gfs[IDX4S(GAMMA_FACEDD00GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD00GF,i0,i1,i2)])/dxx1;
const REAL metricderivDDD011 = (auxevol_gfs[IDX4S(GAMMA_FACEDD01GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD01GF,i0,i1,i2)])/dxx1;
const REAL metricderivDDD021 = (auxevol_gfs[IDX4S(GAMMA_FACEDD02GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD02GF,i0,i1,i2)])/dxx1;
const REAL metricderivDDD111 = (auxevol_gfs[IDX4S(GAMMA_FACEDD11GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD11GF,i0,i1,i2)])/dxx1;
const REAL metricderivDDD121 = (auxevol_gfs[IDX4S(GAMMA_FACEDD12GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD12GF,i0,i1,i2)])/dxx1;
const REAL metricderivDDD221 = (auxevol_gfs[IDX4S(GAMMA_FACEDD22GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(GAMMA_FACEDD22GF,i0,i1,i2)])/dxx1;
const REAL shiftderivUD01 = (auxevol_gfs[IDX4S(BETA_FACEU0GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(BETA_FACEU0GF,i0,i1,i2)])/dxx1;
const REAL shiftderivUD11 = (auxevol_gfs[IDX4S(BETA_FACEU1GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(BETA_FACEU1GF,i0,i1,i2)])/dxx1;
const REAL shiftderivUD21 = (auxevol_gfs[IDX4S(BETA_FACEU2GF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(BETA_FACEU2GF,i0,i1,i2)])/dxx1;
const REAL lapsederivD1 = (auxevol_gfs[IDX4S(ALPHA_FACEGF,i0,i1+1,i2)]-auxevol_gfs[IDX4S(ALPHA_FACEGF,i0,i1,i2)])/dxx1;
REAL Stilde_rhsD1;
"""
    metric_deriv_access[2] = """const REAL metricderivDDD002 = (auxevol_gfs[IDX4S(GAMMA_FACEDD00GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(GAMMA_FACEDD00GF,i0,i1,i2)])/dxx2;
const REAL metricderivDDD012 = (auxevol_gfs[IDX4S(GAMMA_FACEDD01GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(GAMMA_FACEDD01GF,i0,i1,i2)])/dxx2;
const REAL metricderivDDD022 = (auxevol_gfs[IDX4S(GAMMA_FACEDD02GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(GAMMA_FACEDD02GF,i0,i1,i2)])/dxx2;
const REAL metricderivDDD112 = (auxevol_gfs[IDX4S(GAMMA_FACEDD11GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(GAMMA_FACEDD11GF,i0,i1,i2)])/dxx2;
const REAL metricderivDDD122 = (auxevol_gfs[IDX4S(GAMMA_FACEDD12GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(GAMMA_FACEDD12GF,i0,i1,i2)])/dxx2;
const REAL metricderivDDD222 = (auxevol_gfs[IDX4S(GAMMA_FACEDD22GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(GAMMA_FACEDD22GF,i0,i1,i2)])/dxx2;
const REAL shiftderivUD02 = (auxevol_gfs[IDX4S(BETA_FACEU0GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(BETA_FACEU0GF,i0,i1,i2)])/dxx2;
const REAL shiftderivUD12 = (auxevol_gfs[IDX4S(BETA_FACEU1GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(BETA_FACEU1GF,i0,i1,i2)])/dxx2;
const REAL shiftderivUD22 = (auxevol_gfs[IDX4S(BETA_FACEU2GF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(BETA_FACEU2GF,i0,i1,i2)])/dxx2;
const REAL lapsederivD2 = (auxevol_gfs[IDX4S(ALPHA_FACEGF,i0,i1,i2+1)]-auxevol_gfs[IDX4S(ALPHA_FACEGF,i0,i1,i2)])/dxx2;
REAL Stilde_rhsD2;
"""
    write_final_quantity = ixp.zerorank1(DIM=3)
    write_final_quantity[0] = """rhs_gfs[IDX4S(STILDED0GF,i0,i1,i2)] += Stilde_rhsD0;
"""
    write_final_quantity[1] = """rhs_gfs[IDX4S(STILDED1GF,i0,i1,i2)] += Stilde_rhsD1;
"""
    write_final_quantity[2] = """rhs_gfs[IDX4S(STILDED2GF,i0,i1,i2)] += Stilde_rhsD2;
"""

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

    # 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.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, metricderivDDD,shiftderivUD,lapsederivD)
    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(out_dir,subdir,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="outCverbose=False").replace("IDX4","IDX4S")\
                      +write_final_quantity[i],
            loopopts ="InteriorPoints",
            rel_path_for_Cparams=os.path.join("../"))

    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)

    Memory_Read = """const double alpha_face = auxevol_gfs[IDX4S(ALPHA_FACEGF, i0,i1,i2)];
const double gamma_faceDD00 = auxevol_gfs[IDX4S(GAMMA_FACEDD00GF, i0,i1,i2)];
const double gamma_faceDD01 = auxevol_gfs[IDX4S(GAMMA_FACEDD01GF, i0,i1,i2)];
const double gamma_faceDD02 = auxevol_gfs[IDX4S(GAMMA_FACEDD02GF, i0,i1,i2)];
const double gamma_faceDD11 = auxevol_gfs[IDX4S(GAMMA_FACEDD11GF, i0,i1,i2)];
const double gamma_faceDD12 = auxevol_gfs[IDX4S(GAMMA_FACEDD12GF, i0,i1,i2)];
const double gamma_faceDD22 = auxevol_gfs[IDX4S(GAMMA_FACEDD22GF, i0,i1,i2)];
const double beta_faceU0 = auxevol_gfs[IDX4S(BETA_FACEU0GF, i0,i1,i2)];
const double beta_faceU1 = auxevol_gfs[IDX4S(BETA_FACEU1GF, i0,i1,i2)];
const double beta_faceU2 = auxevol_gfs[IDX4S(BETA_FACEU2GF, i0,i1,i2)];
const double Valenciav_rU0 = auxevol_gfs[IDX4S(VALENCIAV_RU0GF, i0,i1,i2)];
const double Valenciav_rU1 = auxevol_gfs[IDX4S(VALENCIAV_RU1GF, i0,i1,i2)];
const double Valenciav_rU2 = auxevol_gfs[IDX4S(VALENCIAV_RU2GF, i0,i1,i2)];
const double B_rU0 = auxevol_gfs[IDX4S(B_RU0GF, i0,i1,i2)];
const double B_rU1 = auxevol_gfs[IDX4S(B_RU1GF, i0,i1,i2)];
const double B_rU2 = auxevol_gfs[IDX4S(B_RU2GF, i0,i1,i2)];
const double Valenciav_lU0 = auxevol_gfs[IDX4S(VALENCIAV_LU0GF, i0,i1,i2)];
const double Valenciav_lU1 = auxevol_gfs[IDX4S(VALENCIAV_LU1GF, i0,i1,i2)];
const double Valenciav_lU2 = auxevol_gfs[IDX4S(VALENCIAV_LU2GF, i0,i1,i2)];
const double B_lU0 = auxevol_gfs[IDX4S(B_LU0GF, i0,i1,i2)];
const double B_lU1 = auxevol_gfs[IDX4S(B_LU1GF, i0,i1,i2)];
const double B_lU2 = auxevol_gfs[IDX4S(B_LU2GF, i0,i1,i2)];
REAL A_rhsD0 = 0; REAL A_rhsD1 = 0; REAL A_rhsD2 = 0;
"""
    Memory_Write = """rhs_gfs[IDX4S(AD0GF,i0,i1,i2)] += A_rhsD0;
rhs_gfs[IDX4S(AD1GF,i0,i1,i2)] += A_rhsD1;
rhs_gfs[IDX4S(AD2GF,i0,i1,i2)] += A_rhsD2;
"""

    indices = ["i0","i1","i2"]
    indicesp1 = ["i0+1","i1+1","i2+1"]

    subdir = "RHSs"
    for flux_dirn in range(3):
        Af.calculate_E_i_flux(flux_dirn,True,alpha_face,gamma_faceDD,beta_faceU,\
                              Valenciav_rU,B_rU,Valenciav_lU,B_lU)

        E_field_to_print = [\
                            sp.Rational(1,4)*Af.E_fluxD[(flux_dirn+1)%3],
                            sp.Rational(1,4)*Af.E_fluxD[(flux_dirn+2)%3],
                           ]
        E_field_names = [\
                         "A_rhsD"+str((flux_dirn+1)%3),
                         "A_rhsD"+str((flux_dirn+2)%3),
                        ]

        desc = "Calculate the electric flux on the left face in direction " + str(flux_dirn) + "."
        name = "calculate_E_field_D" + str(flux_dirn) + "_right"
        outCfunction(
            outfile  = os.path.join(out_dir,subdir,name+".h"), desc=desc, name=name,
            params   ="const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs",
            body     =  Memory_Read \
                       +outputC(E_field_to_print,E_field_names,"returnstring",params="outCverbose=False").replace("IDX4","IDX4S")\
                       +Memory_Write,
            loopopts ="InteriorPoints",
            rel_path_for_Cparams=os.path.join("../"))

        desc = "Calculate the electric flux on the left face in direction " + str(flux_dirn) + "."
        name = "calculate_E_field_D" + str(flux_dirn) + "_left"
        outCfunction(
            outfile  = os.path.join(out_dir,subdir,name+".h"), desc=desc, name=name,
            params   ="const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs",
            body     =  Memory_Read.replace(indices[flux_dirn],indicesp1[flux_dirn]) \
                       +outputC(E_field_to_print,E_field_names,"returnstring",params="outCverbose=False").replace("IDX4","IDX4S")\
                       +Memory_Write,
            loopopts ="InteriorPoints",
            rel_path_for_Cparams=os.path.join("../"))


    Memory_Read = """const double alpha_face = auxevol_gfs[IDX4S(ALPHA_FACEGF, i0,i1,i2)];
const double gamma_faceDD00 = auxevol_gfs[IDX4S(GAMMA_FACEDD00GF, i0,i1,i2)];
const double gamma_faceDD01 = auxevol_gfs[IDX4S(GAMMA_FACEDD01GF, i0,i1,i2)];
const double gamma_faceDD02 = auxevol_gfs[IDX4S(GAMMA_FACEDD02GF, i0,i1,i2)];
const double gamma_faceDD11 = auxevol_gfs[IDX4S(GAMMA_FACEDD11GF, i0,i1,i2)];
const double gamma_faceDD12 = auxevol_gfs[IDX4S(GAMMA_FACEDD12GF, i0,i1,i2)];
const double gamma_faceDD22 = auxevol_gfs[IDX4S(GAMMA_FACEDD22GF, i0,i1,i2)];
const double beta_faceU0 = auxevol_gfs[IDX4S(BETA_FACEU0GF, i0,i1,i2)];
const double beta_faceU1 = auxevol_gfs[IDX4S(BETA_FACEU1GF, i0,i1,i2)];
const double beta_faceU2 = auxevol_gfs[IDX4S(BETA_FACEU2GF, i0,i1,i2)];
const double Valenciav_rU0 = auxevol_gfs[IDX4S(VALENCIAV_RU0GF, i0,i1,i2)];
const double Valenciav_rU1 = auxevol_gfs[IDX4S(VALENCIAV_RU1GF, i0,i1,i2)];
const double Valenciav_rU2 = auxevol_gfs[IDX4S(VALENCIAV_RU2GF, i0,i1,i2)];
const double B_rU0 = auxevol_gfs[IDX4S(B_RU0GF, i0,i1,i2)];
const double B_rU1 = auxevol_gfs[IDX4S(B_RU1GF, i0,i1,i2)];
const double B_rU2 = auxevol_gfs[IDX4S(B_RU2GF, i0,i1,i2)];
const double Valenciav_lU0 = auxevol_gfs[IDX4S(VALENCIAV_LU0GF, i0,i1,i2)];
const double Valenciav_lU1 = auxevol_gfs[IDX4S(VALENCIAV_LU1GF, i0,i1,i2)];
const double Valenciav_lU2 = auxevol_gfs[IDX4S(VALENCIAV_LU2GF, i0,i1,i2)];
const double B_lU0 = auxevol_gfs[IDX4S(B_LU0GF, i0,i1,i2)];
const double B_lU1 = auxevol_gfs[IDX4S(B_LU1GF, i0,i1,i2)];
const double B_lU2 = auxevol_gfs[IDX4S(B_LU2GF, i0,i1,i2)];
REAL Stilde_fluxD0 = 0; REAL Stilde_fluxD1 = 0; REAL Stilde_fluxD2 = 0;
"""
    Memory_Write = """rhs_gfs[IDX4S(STILDED0GF, i0, i1, i2)] += invdx0*Stilde_fluxD0;
rhs_gfs[IDX4S(STILDED1GF, i0, i1, i2)] += invdx0*Stilde_fluxD1;
rhs_gfs[IDX4S(STILDED2GF, i0, i1, i2)] += invdx0*Stilde_fluxD2;
"""

    indices = ["i0","i1","i2"]
    indicesp1 = ["i0+1","i1+1","i2+1"]
    assignment = "+="
    assignmentp1 = "-="
    invdx = ["invdx0","invdx1","invdx2"]

    for flux_dirn in range(3):
        Sf.calculate_Stilde_flux(flux_dirn,True,alpha_face,gamma_faceDD,beta_faceU,\
                                 Valenciav_rU,B_rU,Valenciav_lU,B_lU,sqrt4pi)
        Stilde_flux_to_print = [\
                                Sf.Stilde_fluxD[0],\
                                Sf.Stilde_fluxD[1],\
                                Sf.Stilde_fluxD[2],\
                               ]
        Stilde_flux_names = [\
                             "Stilde_fluxD0",\
                             "Stilde_fluxD1",\
                             "Stilde_fluxD2",\
                            ]

        desc = "Compute the flux of all 3 components of tilde{S}_i on the right face in the " + str(flux_dirn) + "."
        name = "calculate_Stilde_flux_D" + str(flux_dirn) + "_right"
        outCfunction(
            outfile  = os.path.join(out_dir,subdir,name+".h"), desc=desc, name=name,
            params   ="const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs",
            body     =  Memory_Read \
                   +outputC(Stilde_flux_to_print,Stilde_flux_names,"returnstring",params="outCverbose=False").replace("IDX4","IDX4S")\
                       +Memory_Write.replace(invdx[0],invdx[flux_dirn]),
            loopopts ="InteriorPoints",
            rel_path_for_Cparams=os.path.join("../"))

        desc = "Compute the flux of all 3 components of tilde{S}_i on the left face in the " + str(flux_dirn) + "."
        name = "calculate_Stilde_flux_D" + str(flux_dirn) + "_left"
        outCfunction(
            outfile  = os.path.join(out_dir,subdir,name+".h"), desc=desc, name=name,
            params   ="const paramstruct *params,const REAL *auxevol_gfs,REAL *rhs_gfs",
            body     =  Memory_Read.replace(indices[flux_dirn],indicesp1[flux_dirn]) \
                   +outputC(Stilde_flux_to_print,Stilde_flux_names,"returnstring",params="outCverbose=False").replace("IDX4","IDX4S")\
                       +Memory_Write.replace(invdx[0],invdx[flux_dirn]).replace(assignment,assignmentp1),
            loopopts ="InteriorPoints",
            rel_path_for_Cparams=os.path.join("../"))

    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="outCverbose=False").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="outCverbose=False").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("""// 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_parentheticals_for_RHSs.h"
#include "RHSs/calculate_AD_gauge_psi6Phi_RHSs.h"
#include "PPM/reconstruct_set_of_prims_PPM_GRFFE_NRPy.c"
#include "FCVAL/interpolate_metric_gfs_to_cell_faces.h"
#include "RHSs/calculate_StildeD0_source_term.h"
#include "RHSs/calculate_StildeD1_source_term.h"
#include "RHSs/calculate_StildeD2_source_term.h"
// #include "RHSs/calculate_E_field_D0_right.h"
// #include "RHSs/calculate_E_field_D0_left.h"
// #include "RHSs/calculate_E_field_D1_right.h"
// #include "RHSs/calculate_E_field_D1_left.h"
// #include "RHSs/calculate_E_field_D2_right.h"
// #include "RHSs/calculate_E_field_D2_left.h"
#include "../calculate_E_field_flat_all_in_one.h"
#include "RHSs/calculate_Stilde_flux_D0_right.h"
#include "RHSs/calculate_Stilde_flux_D0_left.h"
#include "RHSs/calculate_Stilde_flux_D1_right.h"
#include "RHSs/calculate_Stilde_flux_D1_left.h"
#include "RHSs/calculate_Stilde_flux_D2_right.h"
#include "RHSs/calculate_Stilde_flux_D2_left.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_parentheticals_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);
            calculate_E_field_flat_all_in_one(params,auxevol_gfs,rhs_gfs,flux_dirn);
            // Finally, we calculate the flux of StildeD and add the appropriate finite-differences 
            // to the RHSs.
            calculate_Stilde_flux_D0_right(params,auxevol_gfs,rhs_gfs);
            calculate_Stilde_flux_D0_left(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_E_field_flat_all_in_one(params,auxevol_gfs,rhs_gfs,flux_dirn);
            calculate_Stilde_flux_D1_right(params,auxevol_gfs,rhs_gfs);
            calculate_Stilde_flux_D1_left(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_E_field_flat_all_in_one(params,auxevol_gfs,rhs_gfs,flux_dirn);
            calculate_Stilde_flux_D2_right(params,auxevol_gfs,rhs_gfs);
            calculate_Stilde_flux_D2_left(params,auxevol_gfs,rhs_gfs);
        }
    }
}

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);
}
""")
Example #12
0
def GiRaFFE_NRPy_A2B(Ccodesdir):
    cmd.mkdir(Ccodesdir)
    # Write out the code to a file.
    with open(os.path.join(Ccodesdir, "compute_B_and_Bstagger_from_A.h"),
              "w") as file:
        file.write(body)
Example #13
0
paramslist.sort()  # Sort the list alphabetically.
###############################

###############################
# Step 2: Generate all C-code kernels for Baikal and BaikalVacuum,
#         in parallel if supported by this OS.
nrpy_dir_path = os.path.join(".")
if nrpy_dir_path not in sys.path:
    sys.path.append(nrpy_dir_path)

# Create all output directories if they do not yet exist
import cmdline_helper as cmd  # NRPy+: Multi-platform Python command-line interface
for ThornName in ["Baikal", "BaikalVacuum"]:
    outrootdir = ThornName
    cmd.mkdir(os.path.join(outrootdir))
    outdir = os.path.join(outrootdir, "src")  # Main C code output directory

    # Copy SIMD/SIMD_intrinsics.h to $outdir/SIMD/SIMD_intrinsics.h, replacing
    #   the line "#define REAL_SIMD_ARRAY REAL" with "#define REAL_SIMD_ARRAY CCTK_REAL"
    #   (since REAL is undefined in the ETK, but CCTK_REAL takes its place)
    cmd.mkdir(os.path.join(outdir, "SIMD"))
    import fileinput
    f = fileinput.input(
        os.path.join(nrpy_dir_path, "SIMD", "SIMD_intrinsics.h"))
    with open(os.path.join(outdir, "SIMD", "SIMD_intrinsics.h"),
              "w") as outfile:
        for line in f:
            outfile.write(
                line.replace("#define REAL_SIMD_ARRAY REAL",
                             "#define REAL_SIMD_ARRAY CCTK_REAL"))
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")
    ixp.register_gridfunctions_for_single_rank1("EVOL", "AD")
    BU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "BU")
    ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "BstaggerU")
    ValenciavU = ixp.register_gridfunctions_for_single_rank1(
        "AUXEVOL", "ValenciavU")
    gri.register_gridfunctions("EVOL", "psi6Phi")
    StildeD = ixp.register_gridfunctions_for_single_rank1("EVOL", "StildeD")

    gri.register_gridfunctions("AUXEVOL", "psi6_temp")
    gri.register_gridfunctions("AUXEVOL", "psi6center")
    gri.register_gridfunctions("AUXEVOL", "cmax_x")
    gri.register_gridfunctions("AUXEVOL", "cmin_x")
    gri.register_gridfunctions("AUXEVOL", "cmax_y")
    gri.register_gridfunctions("AUXEVOL", "cmin_y")
    gri.register_gridfunctions("AUXEVOL", "cmax_z")
    gri.register_gridfunctions("AUXEVOL", "cmin_z")

    subdir = "RHSs"
    stgsrc.GiRaFFE_NRPy_Source_Terms(os.path.join(out_dir, subdir))

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

    cmd.mkdir(os.path.join(out_dir, subdir))
    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)

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

    ixp.register_gridfunctions_for_single_rank1("AUXEVOL",
                                                "Valenciav_rrU",
                                                DIM=3)
    ixp.register_gridfunctions_for_single_rank1("AUXEVOL",
                                                "Valenciav_rlU",
                                                DIM=3)
    ixp.register_gridfunctions_for_single_rank1("AUXEVOL",
                                                "Valenciav_lrU",
                                                DIM=3)
    ixp.register_gridfunctions_for_single_rank1("AUXEVOL",
                                                "Valenciav_llU",
                                                DIM=3)
    ixp.register_gridfunctions_for_single_rank1("AUXEVOL",
                                                "Bstagger_rU",
                                                DIM=3)
    ixp.register_gridfunctions_for_single_rank1("AUXEVOL",
                                                "Bstagger_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,
                                       write_cmax_cmin=True)

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

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

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

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

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

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

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

    # Write out the main driver itself:
    with open(os.path.join(out_dir, "GiRaFFE_NRPy_Main_Driver.h"),
              "w") as file:
        file.write(r"""// Structure to track ghostzones for PPM:
typedef struct __gf_and_gz_struct__ {
  REAL *gf;
  int gz_lo[4],gz_hi[4];
} gf_and_gz_struct;
// Some additional constants needed for PPM:
static const int VX=0,VY=1,VZ=2,
  BX_CENTER=3,BY_CENTER=4,BZ_CENTER=5,BX_STAGGER=6,BY_STAGGER=7,BZ_STAGGER=8,
  VXR=9,VYR=10,VZR=11,VXL=12,VYL=13,VZL=14;  //<-- Be _sure_ to define MAXNUMVARS appropriately!
const int NUM_RECONSTRUCT_GFS = 15;

#define WORKAROUND_ENABLED

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

void workaround_Valencia_to_Drift_velocity(const paramstruct *params, REAL *vU0, const REAL *alpha, const REAL *betaU0, const REAL flux_dirn) {
#include "set_Cparameters.h"
    // Converts Valencia 3-velocities to Drift 3-velocities for testing. The variable argument
    // vu0 is any Valencia 3-velocity component or reconstruction thereof.
#pragma omp parallel for
    for (int i2 = 2*(flux_dirn==3);i2 < Nxx_plus_2NGHOSTS2-1*(flux_dirn==3);i2++) for (int i1 = 2*(flux_dirn==2);i1 < Nxx_plus_2NGHOSTS1-1*(flux_dirn==2);i1++) for (int i0 = 2*(flux_dirn==1);i0 < Nxx_plus_2NGHOSTS0-1*(flux_dirn==1);i0++) {
        int ii = IDX3S(i0,i1,i2);
        // Here, we modify the velocity in place.
        vU0[ii] = alpha[ii]*vU0[ii]-betaU0[ii];
    }
}

void workaround_Drift_to_Valencia_velocity(const paramstruct *params, REAL *vU0, const REAL *alpha, const REAL *betaU0, const REAL flux_dirn) {
#include "set_Cparameters.h"
    // Converts Drift 3-velocities to Valencia 3-velocities for testing. The variable argument
    // vu0 is any drift (i.e. IllinoisGRMHD's definition) 3-velocity component or reconstruction thereof.
#pragma omp parallel for
    for (int i2 = 2*(flux_dirn==3);i2 < Nxx_plus_2NGHOSTS2-1*(flux_dirn==3);i2++) for (int i1 = 2*(flux_dirn==2);i1 < Nxx_plus_2NGHOSTS1-1*(flux_dirn==2);i1++) for (int i0 = 2*(flux_dirn==1);i0 < Nxx_plus_2NGHOSTS0-1*(flux_dirn==1);i0++) {
        int ii = IDX3S(i0,i1,i2);
        // Here, we modify the velocity in place.
        vU0[ii] = (vU0[ii]+betaU0[ii])/alpha[ii];
    }
}

void GiRaFFE_NRPy_RHSs(const paramstruct *restrict params,REAL *restrict auxevol_gfs,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;
    }

    // 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*PSI6_TEMPGF; // Using dedicated temporary variables for the staggered grid
    REAL *psi6center = auxevol_gfs + Nxxp2NG012*PSI6CENTERGF; // Because the prescription requires more acrobatics.
    // 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++;
    in_prims[ww].gf      = auxevol_gfs + Nxxp2NG012*BSTAGGERU0GF;
      out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*BSTAGGER_RU0GF;
      out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*BSTAGGER_LU0GF;
    ww++;
    in_prims[ww].gf      = auxevol_gfs + Nxxp2NG012*BSTAGGERU1GF;
      out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*BSTAGGER_RU1GF;
      out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*BSTAGGER_LU1GF;
    ww++;
    in_prims[ww].gf      = auxevol_gfs + Nxxp2NG012*BSTAGGERU2GF;
      out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*BSTAGGER_RU2GF;
      out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*BSTAGGER_LU2GF;
    ww++;
    in_prims[ww].gf      = auxevol_gfs + Nxxp2NG012*VALENCIAV_RU0GF;
      out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RRU0GF;
      out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RLU0GF;
    ww++;
    in_prims[ww].gf      = auxevol_gfs + Nxxp2NG012*VALENCIAV_RU1GF;
      out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RRU1GF;
      out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RLU1GF;
    ww++;
    in_prims[ww].gf      = auxevol_gfs + Nxxp2NG012*VALENCIAV_RU2GF;
      out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RRU2GF;
      out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_RLU2GF;
    ww++;
    in_prims[ww].gf      = auxevol_gfs + Nxxp2NG012*VALENCIAV_LU0GF;
      out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LRU0GF;
      out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LLU0GF;
    ww++;
    in_prims[ww].gf      = auxevol_gfs + Nxxp2NG012*VALENCIAV_LU1GF;
      out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LRU1GF;
      out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LLU1GF;
    ww++;
    in_prims[ww].gf      = auxevol_gfs + Nxxp2NG012*VALENCIAV_LU2GF;
      out_prims_r[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LRU2GF;
      out_prims_l[ww].gf = auxevol_gfs + Nxxp2NG012*VALENCIAV_LLU2GF;
    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; }

  int flux_dirn;
  flux_dirn=0;
  interpolate_metric_gfs_to_cell_faces(params,auxevol_gfs,flux_dirn+1);
  // ftilde = 0 in GRFFE, since P=rho=0.

  /* There are two stories going on here:
   * 1) Computation of \partial_x on RHS of \partial_t {mhd_st_{x,y,z}},
   *    via PPM reconstruction onto (i-1/2,j,k), so that
   *    \partial_y F = [ F(i+1/2,j,k) - F(i-1/2,j,k) ] / dx
   * 2) Computation of \partial_t A_i, where A_i are *staggered* gridfunctions,
   *    where A_x is defined at (i,j+1/2,k+1/2), A_y at (i+1/2,j,k+1/2), etc.
   *    Ai_rhs = \partial_t A_i = \epsilon_{ijk} \psi^{6} (v^j B^k - v^j B^k),
   *    where \epsilon_{ijk} is the flat-space antisymmetric operator.
   * 2A) Az_rhs is defined at (i+1/2,j+1/2,k), and it depends on {Bx,By,vx,vy},
   *     so the trick is to reconstruct {Bx,By,vx,vy} cleverly to get to these
   *     staggered points. For example:
   * 2Aa) vx and vy are at (i,j,k), and we reconstruct them to (i-1/2,j,k) below. After
   *      this, we'll reconstruct again in the y-dir'n to get {vx,vy} at (i-1/2,j-1/2,k)
   * 2Ab) By_stagger is at (i,j+1/2,k), and we reconstruct below to (i-1/2,j+1/2,k). */
  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_CENTER; ww++;
  which_prims_to_reconstruct[ww]=BY_CENTER; ww++;
  which_prims_to_reconstruct[ww]=BZ_CENTER; ww++;
  which_prims_to_reconstruct[ww]=BY_STAGGER;ww++;
  num_prims_to_reconstruct=ww;
  // This function is housed in the file: "reconstruct_set_of_prims_PPM_GRFFE.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);
  //Right and left face values of BI_CENTER are used in GRFFE__S_i__flux computation (first to compute b^a).
  //   Instead of reconstructing, we simply set B^x face values to be consistent with BX_STAGGER.
#pragma omp parallel for
  for(int k=0;k<Nxx_plus_2NGHOSTS2;k++) for(int j=0;j<Nxx_plus_2NGHOSTS1;j++) for(int i=0;i<Nxx_plus_2NGHOSTS0;i++) {
        const int index=IDX3S(i,j,k), indexim1=IDX3S(i-1+(i==0),j,k); /* indexim1=0 when i=0 */
        out_prims_r[BX_CENTER].gf[index]=out_prims_l[BX_CENTER].gf[index]=in_prims[BX_STAGGER].gf[indexim1]; }
  // Then add fluxes to RHS for hydro variables {vx,vy,vz}:
  // This function is housed in the file: "add_fluxes_and_source_terms_to_hydro_rhss.C"
  calculate_StildeD0_source_term(params,auxevol_gfs,rhs_gfs);
  calculate_Stilde_flux_D0(params,auxevol_gfs,rhs_gfs);
  calculate_Stilde_rhsD(flux_dirn+1,params,auxevol_gfs,rhs_gfs);

  // Note that we have already reconstructed vx and vy along the x-direction,
  //   at (i-1/2,j,k). That result is stored in v{x,y}{r,l}.  Bx_stagger data
  //   are defined at (i+1/2,j,k).
  // Next goal: reconstruct Bx, vx and vy at (i+1/2,j+1/2,k).
  flux_dirn=1;
  // ftilde = 0 in GRFFE, since P=rho=0.

  // in_prims[{VXR,VXL,VYR,VYL}].gz_{lo,hi} ghostzones are set to all zeros, which
  //    is incorrect. We fix this below.
  // [Note that this is a cheap operation, copying only 8 integers and a pointer.]
  in_prims[VXR]=out_prims_r[VX];
  in_prims[VXL]=out_prims_l[VX];
  in_prims[VYR]=out_prims_r[VY];
  in_prims[VYL]=out_prims_l[VY];

  /* There are two stories going on here:
   * 1) Computation of \partial_y on RHS of \partial_t {mhd_st_{x,y,z}},
   *    via PPM reconstruction onto (i,j-1/2,k), so that
   *    \partial_y F = [ F(i,j+1/2,k) - F(i,j-1/2,k) ] / dy
   * 2) Computation of \partial_t A_i, where A_i are *staggered* gridfunctions,
   *    where A_x is defined at (i,j+1/2,k+1/2), A_y at (i+1/2,j,k+1/2), etc.
   *    Ai_rhs = \partial_t A_i = \epsilon_{ijk} \psi^{6} (v^j B^k - v^j B^k),
   *    where \epsilon_{ijk} is the flat-space antisymmetric operator.
   * 2A) Az_rhs is defined at (i+1/2,j+1/2,k), and it depends on {Bx,By,vx,vy},
   *     so the trick is to reconstruct {Bx,By,vx,vy} cleverly to get to these
   *     staggered points. For example:
   * 2Aa) VXR = [right-face of vx reconstructed along x-direction above] is at (i-1/2,j,k),
   *      and we reconstruct it to (i-1/2,j-1/2,k) below. Similarly for {VXL,VYR,VYL}
   * 2Ab) Bx_stagger is at (i+1/2,j,k), and we reconstruct to (i+1/2,j-1/2,k) below
   * 2Ac) By_stagger is at (i-1/2,j+1/2,k) already for Az_rhs, from the previous step.
   * 2B) Ax_rhs is defined at (i,j+1/2,k+1/2), and it depends on {By,Bz,vy,vz}.
   *     Again the trick is to reconstruct these onto these staggered points.
   * 2Ba) Bz_stagger is at (i,j,k+1/2), and we reconstruct to (i,j-1/2,k+1/2) below */
  ww=0;
  // NOTE! The order of variable reconstruction is important here,
  //   as we don't want to overwrite {vxr,vxl,vyr,vyl}!
  which_prims_to_reconstruct[ww]=VXR;       ww++;
  which_prims_to_reconstruct[ww]=VYR;       ww++;
  which_prims_to_reconstruct[ww]=VXL;       ww++;
  which_prims_to_reconstruct[ww]=VYL;       ww++;
  num_prims_to_reconstruct=ww;
#ifdef WORKAROUND_ENABLED
  workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU0GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU0GF,flux_dirn+1);
  workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU1GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU1GF,flux_dirn+1);
  workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU0GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU0GF,flux_dirn+1);
  workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU1GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU1GF,flux_dirn+1);
#endif /*WORKAROUND_ENABLED*/
  // This function is housed in the file: "reconstruct_set_of_prims_PPM_GRFFE.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);
#ifdef WORKAROUND_ENABLED
  workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU0GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU0GF,flux_dirn+1);
  workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU1GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU1GF,flux_dirn+1);
  workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU0GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU0GF,flux_dirn+1);
  workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU1GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU1GF,flux_dirn+1);
#endif /*WORKAROUND_ENABLED*/
  interpolate_metric_gfs_to_cell_faces(params,auxevol_gfs,flux_dirn+1);
  ww=0;
  // Reconstruct other primitives last!
  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_CENTER; ww++;
  //which_prims_to_reconstruct[ww]=BY_CENTER; ww++;
  which_prims_to_reconstruct[ww]=BZ_CENTER; ww++;
  which_prims_to_reconstruct[ww]=BX_STAGGER;ww++;
  which_prims_to_reconstruct[ww]=BZ_STAGGER;ww++;
  num_prims_to_reconstruct=ww;
  // This function is housed in the file: "reconstruct_set_of_prims_PPM_GRFFE.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);
  //Right and left face values of BI_CENTER are used in GRFFE__S_i__flux computation (first to compute b^a).
  //   Instead of reconstructing, we simply set B^y face values to be consistent with BY_STAGGER.
#pragma omp parallel for
  for(int k=0;k<Nxx_plus_2NGHOSTS2;k++) for(int j=0;j<Nxx_plus_2NGHOSTS1;j++) for(int i=0;i<Nxx_plus_2NGHOSTS0;i++) {
        const int index=IDX3S(i,j,k), indexjm1=IDX3S(i,j-1+(j==0),k); /* indexjm1=0 when j=0 */
        out_prims_r[BY_CENTER].gf[index]=out_prims_l[BY_CENTER].gf[index]=in_prims[BY_STAGGER].gf[indexjm1]; }
  // Then add fluxes to RHS for hydro variables {vx,vy,vz}:
  // This function is housed in the file: "add_fluxes_and_source_terms_to_hydro_rhss.C"
  calculate_StildeD1_source_term(params,auxevol_gfs,rhs_gfs);
  calculate_Stilde_flux_D1(params,auxevol_gfs,rhs_gfs);
  calculate_Stilde_rhsD(flux_dirn+1,params,auxevol_gfs,rhs_gfs);

  /*****************************************
   * COMPUTING RHS OF A_z, BOOKKEEPING NOTE:
   * We want to compute
   * \partial_t A_z - [gauge terms] = \psi^{6} (v^x B^y - v^y B^x).
   * A_z is defined at (i+1/2,j+1/2,k).
   * ==========================
   * Where defined  | Variables
   * (i-1/2,j-1/2,k)| {vxrr,vxrl,vxlr,vxll,vyrr,vyrl,vylr,vyll}
   * (i+1/2,j-1/2,k)| {Bx_stagger_r,Bx_stagger_l} (see Table 1 in arXiv:1007.2848)
   * (i-1/2,j+1/2,k)| {By_stagger_r,By_stagger_l} (see Table 1 in arXiv:1007.2848)
   * (i,j,k)        | {phi}
   * ==========================
   ******************************************/
  // Interpolates to i+1/2
#define IPH(METRICm1,METRICp0,METRICp1,METRICp2) (-0.0625*((METRICm1) + (METRICp2)) + 0.5625*((METRICp0) + (METRICp1)))
  // Next compute sqrt(gamma)=psi^6 at (i+1/2,j+1/2,k):
  // To do so, we first compute the sqrt of the metric determinant at all points:
#pragma omp parallel for
  for(int k=0;k<Nxx_plus_2NGHOSTS2;k++) for(int j=0;j<Nxx_plus_2NGHOSTS1;j++) for(int i=0;i<Nxx_plus_2NGHOSTS0;i++) {
        const int index=IDX3S(i,j,k);
        const REAL gxx = auxevol_gfs[IDX4ptS(GAMMADD00GF,index)];
        const REAL gxy = auxevol_gfs[IDX4ptS(GAMMADD01GF,index)];
        const REAL gxz = auxevol_gfs[IDX4ptS(GAMMADD02GF,index)];
        const REAL gyy = auxevol_gfs[IDX4ptS(GAMMADD11GF,index)];
        const REAL gyz = auxevol_gfs[IDX4ptS(GAMMADD12GF,index)];
        const REAL gzz = auxevol_gfs[IDX4ptS(GAMMADD22GF,index)];
        psi6center[index] = sqrt( gxx*gyy*gzz
                               -  gxx*gyz*gyz
                               +2*gxy*gxz*gyz
                               -  gyy*gxz*gxz
                               -  gzz*gxy*gxy );
      }
#pragma omp parallel for
  for(int k=0;k<Nxx_plus_2NGHOSTS2;k++) for(int j=1;j<Nxx_plus_2NGHOSTS1-2;j++) for(int i=1;i<Nxx_plus_2NGHOSTS0-2;i++) {
        temporary[IDX3S(i,j,k)]=
          IPH(IPH(psi6center[IDX3S(i-1,j-1,k)],psi6center[IDX3S(i,j-1,k)],psi6center[IDX3S(i+1,j-1,k)],psi6center[IDX3S(i+2,j-1,k)]),
              IPH(psi6center[IDX3S(i-1,j  ,k)],psi6center[IDX3S(i,j  ,k)],psi6center[IDX3S(i+1,j  ,k)],psi6center[IDX3S(i+2,j  ,k)]),
              IPH(psi6center[IDX3S(i-1,j+1,k)],psi6center[IDX3S(i,j+1,k)],psi6center[IDX3S(i+1,j+1,k)],psi6center[IDX3S(i+2,j+1,k)]),
              IPH(psi6center[IDX3S(i-1,j+2,k)],psi6center[IDX3S(i,j+2,k)],psi6center[IDX3S(i+1,j+2,k)],psi6center[IDX3S(i+2,j+2,k)]));
      }

  int A_directionz=3;
  A_i_rhs_no_gauge_terms(A_directionz,params,out_prims_r,out_prims_l,temporary,
                         auxevol_gfs+Nxxp2NG012*CMAX_XGF,
                         auxevol_gfs+Nxxp2NG012*CMIN_XGF,
                         auxevol_gfs+Nxxp2NG012*CMAX_YGF,
                         auxevol_gfs+Nxxp2NG012*CMIN_YGF,
                         rhs_gfs+Nxxp2NG012*AD2GF);

  // in_prims[{VYR,VYL,VZR,VZL}].gz_{lo,hi} ghostzones are not correct, so we fix
  //    this below.
  // [Note that this is a cheap operation, copying only 8 integers and a pointer.]
  in_prims[VYR]=out_prims_r[VY];
  in_prims[VYL]=out_prims_l[VY];
  in_prims[VZR]=out_prims_r[VZ];
  in_prims[VZL]=out_prims_l[VZ];

  flux_dirn=2;
  // ftilde = 0 in GRFFE, since P=rho=0.

  /* There are two stories going on here:
   * 1) Single reconstruction to (i,j,k-1/2) for {vx,vy,vz,Bx,By,Bz} to compute
   *    z-dir'n advection terms in \partial_t {mhd_st_{x,y,z}} at (i,j,k)
   * 2) Multiple reconstructions for *staggered* gridfunctions A_i:
   *    \partial_t A_i = \epsilon_{ijk} \psi^{6} (v^j B^k - v^j B^k),
   *    where \epsilon_{ijk} is the flat-space antisymmetric operator.
   * 2A) Ax_rhs is defined at (i,j+1/2,k+1/2), depends on v{y,z} and B{y,z}
   * 2Aa) v{y,z}{r,l} are at (i,j-1/2,k), so we reconstruct here to (i,j-1/2,k-1/2)
   * 2Ab) Bz_stagger{r,l} are at (i,j-1/2,k+1/2) already.
   * 2Ac) By_stagger is at (i,j+1/2,k), and below we reconstruct its value at (i,j+1/2,k-1/2)
   * 2B) Ay_rhs is defined at (i+1/2,j,k+1/2), depends on v{z,x} and B{z,x}.
   * 2Ba) v{x,z} are reconstructed to (i,j,k-1/2). Later we'll reconstruct again to (i-1/2,j,k-1/2).
   * 2Bb) Bz_stagger is at (i,j,k+1/2). Later we will reconstruct to (i-1/2,j,k+1/2).
   * 2Bc) Bx_stagger is at (i+1/2,j,k), and below we reconstruct its value at (i+1/2,j,k-1/2)
   */
  ww=0;
  // NOTE! The order of variable reconstruction is important here,
  //   as we don't want to overwrite {vxr,vxl,vyr,vyl}!
  which_prims_to_reconstruct[ww]=VYR;       ww++;
  which_prims_to_reconstruct[ww]=VZR;       ww++;
  which_prims_to_reconstruct[ww]=VYL;       ww++;
  which_prims_to_reconstruct[ww]=VZL;       ww++;
  num_prims_to_reconstruct=ww;
#ifdef WORKAROUND_ENABLED
  workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU1GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU1GF,flux_dirn+1);
  workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU2GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU2GF,flux_dirn+1);
  workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU1GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU1GF,flux_dirn+1);
  workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU2GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU2GF,flux_dirn+1);
#endif /*WORKAROUND_ENABLED*/
  // This function is housed in the file: "reconstruct_set_of_prims_PPM_GRFFE.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);
#ifdef WORKAROUND_ENABLED
  workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU1GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU1GF,flux_dirn+1);
  workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU2GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU2GF,flux_dirn+1);
  workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU1GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU1GF,flux_dirn+1);
  workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU2GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU2GF,flux_dirn+1);
#endif /*WORKAROUND_ENABLED*/
  interpolate_metric_gfs_to_cell_faces(params,auxevol_gfs,flux_dirn+1);
  // Reconstruct other primitives last!
  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_CENTER; ww++;
  which_prims_to_reconstruct[ww]=BY_CENTER; ww++;
  //which_prims_to_reconstruct[ww]=BZ_CENTER; ww++;
  which_prims_to_reconstruct[ww]=BX_STAGGER; ww++;
  which_prims_to_reconstruct[ww]=BY_STAGGER; ww++;
  num_prims_to_reconstruct=ww;
  // This function is housed in the file: "reconstruct_set_of_prims_PPM_GRFFE.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);
  //Right and left face values of BI_CENTER are used in GRFFE__S_i__flux computation (first to compute b^a).
  //   Instead of reconstructing, we simply set B^z face values to be consistent with BZ_STAGGER.
#pragma omp parallel for
  for(int k=0;k<Nxx_plus_2NGHOSTS2;k++) for(int j=0;j<Nxx_plus_2NGHOSTS1;j++) for(int i=0;i<Nxx_plus_2NGHOSTS0;i++) {
        const int index=IDX3S(i,j,k), indexkm1=IDX3S(i,j,k-1+(k==0)); /* indexkm1=0 when k=0 */
        out_prims_r[BZ_CENTER].gf[index]=out_prims_l[BZ_CENTER].gf[index]=in_prims[BZ_STAGGER].gf[indexkm1]; }
  // Then add fluxes to RHS for hydro variables {vx,vy,vz}:
  // This function is housed in the file: "add_fluxes_and_source_terms_to_hydro_rhss.C"
  calculate_StildeD2_source_term(params,auxevol_gfs,rhs_gfs);
  calculate_Stilde_flux_D2(params,auxevol_gfs,rhs_gfs);
  calculate_Stilde_rhsD(flux_dirn+1,params,auxevol_gfs,rhs_gfs);

  // in_prims[{VYR,VYL,VZR,VZL}].gz_{lo,hi} ghostzones are not set correcty.
  //    We fix this below.
  // [Note that this is a cheap operation, copying only 8 integers and a pointer.]
  in_prims[VXR]=out_prims_r[VX];
  in_prims[VZR]=out_prims_r[VZ];
  in_prims[VXL]=out_prims_l[VX];
  in_prims[VZL]=out_prims_l[VZ];
  // FIXME: lines above seem to be inconsistent with lines below.... Possible bug, not major enough to affect evolutions though.
  in_prims[VZR].gz_lo[1]=in_prims[VZR].gz_hi[1]=0;
  in_prims[VXR].gz_lo[1]=in_prims[VXR].gz_hi[1]=0;
  in_prims[VZL].gz_lo[1]=in_prims[VZL].gz_hi[1]=0;
  in_prims[VXL].gz_lo[1]=in_prims[VXL].gz_hi[1]=0;
  /*****************************************
   * COMPUTING RHS OF A_x, BOOKKEEPING NOTE:
   * We want to compute
   * \partial_t A_x - [gauge terms] = \psi^{6} (v^y B^z - v^z B^y).
   * A_x is defined at (i,j+1/2,k+1/2).
   * ==========================
   * Where defined  | Variables
   * (i,j-1/2,k-1/2)| {vyrr,vyrl,vylr,vyll,vzrr,vzrl,vzlr,vzll}
   * (i,j+1/2,k-1/2)| {By_stagger_r,By_stagger_l} (see Table 1 in arXiv:1007.2848)
   * (i,j-1/2,k+1/2)| {Bz_stagger_r,Bz_stagger_l} (see Table 1 in arXiv:1007.2848)
   * (i,j,k)        | {phi}
   * ==========================
   ******************************************/
  // Next compute phi at (i,j+1/2,k+1/2):
#pragma omp parallel for
  for(int k=1;k<Nxx_plus_2NGHOSTS2-2;k++) for(int j=1;j<Nxx_plus_2NGHOSTS1-2;j++) for(int i=0;i<Nxx_plus_2NGHOSTS0;i++) {
        temporary[IDX3S(i,j,k)]=
          IPH(IPH(psi6center[IDX3S(i,j-1,k-1)],psi6center[IDX3S(i,j,k-1)],psi6center[IDX3S(i,j+1,k-1)],psi6center[IDX3S(i,j+2,k-1)]),
              IPH(psi6center[IDX3S(i,j-1,k  )],psi6center[IDX3S(i,j,k  )],psi6center[IDX3S(i,j+1,k  )],psi6center[IDX3S(i,j+2,k  )]),
              IPH(psi6center[IDX3S(i,j-1,k+1)],psi6center[IDX3S(i,j,k+1)],psi6center[IDX3S(i,j+1,k+1)],psi6center[IDX3S(i,j+2,k+1)]),
              IPH(psi6center[IDX3S(i,j-1,k+2)],psi6center[IDX3S(i,j,k+2)],psi6center[IDX3S(i,j+1,k+2)],psi6center[IDX3S(i,j+2,k+2)]));
      }

  int A_directionx=1;
  A_i_rhs_no_gauge_terms(A_directionx,params,out_prims_r,out_prims_l,temporary,
                         auxevol_gfs+Nxxp2NG012*CMAX_YGF,
                         auxevol_gfs+Nxxp2NG012*CMIN_YGF,
                         auxevol_gfs+Nxxp2NG012*CMAX_ZGF,
                         auxevol_gfs+Nxxp2NG012*CMIN_ZGF,
                         rhs_gfs+Nxxp2NG012*AD0GF);

  // We reprise flux_dirn=0 to finish up computations of Ai_rhs's!
  flux_dirn=0;
  // ftilde = 0 in GRFFE, since P=rho=0.

  ww=0;
  // NOTE! The order of variable reconstruction is important here,
  //   as we don't want to overwrite {vxr,vxl,vyr,vyl}!
  which_prims_to_reconstruct[ww]=VXR;       ww++;
  which_prims_to_reconstruct[ww]=VZR;       ww++;
  which_prims_to_reconstruct[ww]=VXL;       ww++;
  which_prims_to_reconstruct[ww]=VZL;       ww++;
  which_prims_to_reconstruct[ww]=BZ_STAGGER;ww++;
  num_prims_to_reconstruct=ww;
#ifdef WORKAROUND_ENABLED
  workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU0GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU0GF,flux_dirn+1);
  workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU2GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU2GF,flux_dirn+1);
  workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU0GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU0GF,flux_dirn+1);
  workaround_Valencia_to_Drift_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU2GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU2GF,flux_dirn+1);
#endif /*WORKAROUND_ENABLED*/
  // This function is housed in the file: "reconstruct_set_of_prims_PPM_GRFFE.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);
#ifdef WORKAROUND_ENABLED
  workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU0GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU0GF,flux_dirn+1);
  workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_RU2GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU2GF,flux_dirn+1);
  workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU0GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU0GF,flux_dirn+1);
  workaround_Drift_to_Valencia_velocity(params,auxevol_gfs+Nxxp2NG012*VALENCIAV_LU2GF,auxevol_gfs+Nxxp2NG012*ALPHA_FACEGF,auxevol_gfs+Nxxp2NG012*BETA_FACEU2GF,flux_dirn+1);
#endif /*WORKAROUND_ENABLED*/

  /*****************************************
   * COMPUTING RHS OF A_y, BOOKKEEPING NOTE:
   * We want to compute
   * \partial_t A_y - [gauge terms] = \psi^{6} (v^z B^x - v^x B^z).
   * A_y is defined at (i+1/2,j,k+1/2).
   * ==========================
   * Where defined  | Variables
   * (i-1/2,j,k-1/2)| {vyrr,vyrl,vylr,vyll,vzrr,vzrl,vzlr,vzll}
   * (i+1/2,j,k-1/2)| {By_stagger_r,By_stagger_l} (see Table 1 in arXiv:1007.2848)
   * (i-1/2,j,k+1/2)| {Bz_stagger_r,Bz_stagger_l} (see Table 1 in arXiv:1007.2848)
   * (i,j,k)        | {phi}
   * ==========================
   ******************************************/
  // Next compute phi at (i+1/2,j,k+1/2):
#pragma omp parallel for
  for(int k=1;k<Nxx_plus_2NGHOSTS2-2;k++) for(int j=0;j<Nxx_plus_2NGHOSTS1;j++) for(int i=1;i<Nxx_plus_2NGHOSTS0-2;i++) {
        temporary[IDX3S(i,j,k)]=
          IPH(IPH(psi6center[IDX3S(i-1,j,k-1)],psi6center[IDX3S(i,j,k-1)],psi6center[IDX3S(i+1,j,k-1)],psi6center[IDX3S(i+2,j,k-1)]),
              IPH(psi6center[IDX3S(i-1,j,k  )],psi6center[IDX3S(i,j,k  )],psi6center[IDX3S(i+1,j,k  )],psi6center[IDX3S(i+2,j,k  )]),
              IPH(psi6center[IDX3S(i-1,j,k+1)],psi6center[IDX3S(i,j,k+1)],psi6center[IDX3S(i+1,j,k+1)],psi6center[IDX3S(i+2,j,k+1)]),
              IPH(psi6center[IDX3S(i-1,j,k+2)],psi6center[IDX3S(i,j,k+2)],psi6center[IDX3S(i+1,j,k+2)],psi6center[IDX3S(i+2,j,k+2)]));
      }

  int A_directiony=2;
  A_i_rhs_no_gauge_terms(A_directiony,params,out_prims_r,out_prims_l,temporary,
                         auxevol_gfs+Nxxp2NG012*CMAX_ZGF,
                         auxevol_gfs+Nxxp2NG012*CMIN_ZGF,
                         auxevol_gfs+Nxxp2NG012*CMAX_XGF,
                         auxevol_gfs+Nxxp2NG012*CMIN_XGF,
                         rhs_gfs+Nxxp2NG012*AD1GF);

  // Next compute psi6phi_rhs, and add gauge terms to A_i_rhs terms!
  //   Note that in the following function, we don't bother with reconstruction, instead interpolating.
  // We need A^i, but only have A_i. So we add gtupij to the list of input variables.
  REAL *interp_vars[MAXNUMINTERP];
  ww=0;
  interp_vars[ww]=auxevol_gfs+Nxxp2NG012*BETAU0GF;   ww++;
  interp_vars[ww]=auxevol_gfs+Nxxp2NG012*BETAU1GF;   ww++;
  interp_vars[ww]=auxevol_gfs+Nxxp2NG012*BETAU2GF;   ww++;
  interp_vars[ww]=auxevol_gfs+Nxxp2NG012*GAMMADD00GF;  ww++;
  interp_vars[ww]=auxevol_gfs+Nxxp2NG012*GAMMADD01GF;  ww++;
  interp_vars[ww]=auxevol_gfs+Nxxp2NG012*GAMMADD02GF;  ww++;
  interp_vars[ww]=auxevol_gfs+Nxxp2NG012*GAMMADD11GF;  ww++;
  interp_vars[ww]=auxevol_gfs+Nxxp2NG012*GAMMADD12GF;  ww++;
  interp_vars[ww]=auxevol_gfs+Nxxp2NG012*GAMMADD22GF;  ww++;
  interp_vars[ww]=temporary;ww++;
  interp_vars[ww]=auxevol_gfs+Nxxp2NG012*ALPHAGF;   ww++;
  interp_vars[ww]=in_gfs+Nxxp2NG012*AD0GF;      ww++;
  interp_vars[ww]=in_gfs+Nxxp2NG012*AD1GF;      ww++;
  interp_vars[ww]=in_gfs+Nxxp2NG012*AD2GF;      ww++;
  const int max_num_interp_variables=ww;
//   if(max_num_interp_variables>MAXNUMINTERP) {CCTK_VError(VERR_DEF_PARAMS,"Error: Didn't allocate enough space for interp_vars[]."); }
  // We are FINISHED with v{x,y,z}{r,l} and P{r,l} so we use these 8 gridfunctions' worth of space as temp storage.
  Lorenz_psi6phi_rhs__add_gauge_terms_to_A_i_rhs(params,interp_vars,
                                                 in_gfs+Nxxp2NG012*PSI6PHIGF,
                                                 auxevol_gfs+Nxxp2NG012*VALENCIAV_RU0GF,  // WARNING:
                                                 auxevol_gfs+Nxxp2NG012*VALENCIAV_RU1GF,  // ALL VARIABLES
                                                 auxevol_gfs+Nxxp2NG012*VALENCIAV_RU2GF,  // ON THESE LINES
                                                 auxevol_gfs+Nxxp2NG012*VALENCIAV_LU0GF,  // ARE OVERWRITTEN
                                                 auxevol_gfs+Nxxp2NG012*VALENCIAV_LU1GF,  // FOR TEMP STORAGE
                                                 auxevol_gfs+Nxxp2NG012*VALENCIAV_LU2GF,  // .
                                                 auxevol_gfs+Nxxp2NG012*VALENCIAV_RRU0GF, // .
                                                 auxevol_gfs+Nxxp2NG012*VALENCIAV_RLU0GF, // .
                                                 rhs_gfs+Nxxp2NG012*PSI6PHIGF,
                                                 rhs_gfs+Nxxp2NG012*AD0GF,
                                                 rhs_gfs+Nxxp2NG012*AD1GF,
                                                 rhs_gfs+Nxxp2NG012*AD2GF);
/*#pragma omp parallel for
  for(int k=0;k<Nxx_plus_2NGHOSTS2;k++) for(int j=0;j<Nxx_plus_2NGHOSTS1;j++) for(int i=0;i<Nxx_plus_2NGHOSTS0;i++) {
        REAL x = xx[0][i];
        REAL y = xx[1][j];
        REAL z = xx[2][k];
        if(sqrt(x*x+y*y+z*z)<min_radius_inside_of_which_conserv_to_prims_FFE_and_FFE_evolution_is_DISABLED) {
          rhs_gfs[IDX4S(STILDED0GF,i,j,k)] = 0.0;
          rhs_gfs[IDX4S(STILDED1GF,i,j,k)] = 0.0;
          rhs_gfs[IDX4S(STILDED2GF,i,j,k)] = 0.0;

          rhs_gfs[IDX4S(PSI6PHIGF,i,j,k)] = 0.0;
          rhs_gfs[IDX4S(AD0GF,i,j,k)] = 0.0;
          rhs_gfs[IDX4S(AD1GF,i,j,k)] = 0.0;
          rhs_gfs[IDX4S(AD2GF,i,j,k)] = 0.0;
        }
      }*/
}

void GiRaFFE_NRPy_post_step(const paramstruct *restrict params,REAL *xx[3],REAL *restrict auxevol_gfs,REAL *restrict evol_gfs,const int n) {
#include "set_Cparameters.h"
    // First, apply BCs to AD and psi6Phi. Then calculate BU from AD
    apply_bcs_potential(params,evol_gfs);
    const int Nxxp2NG012 = Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2;
    GiRaFFE_compute_B_and_Bstagger_from_A(params,
                                          auxevol_gfs+Nxxp2NG012*GAMMADD00GF,
                                          auxevol_gfs+Nxxp2NG012*GAMMADD01GF,
                                          auxevol_gfs+Nxxp2NG012*GAMMADD02GF,
                                          auxevol_gfs+Nxxp2NG012*GAMMADD11GF,
                                          auxevol_gfs+Nxxp2NG012*GAMMADD12GF,
                                          auxevol_gfs+Nxxp2NG012*GAMMADD22GF,
                                          auxevol_gfs + Nxxp2NG012*PSI6_TEMPGF, /* Temporary storage */
                                          evol_gfs+Nxxp2NG012*AD0GF,
                                          evol_gfs+Nxxp2NG012*AD1GF,
                                          evol_gfs+Nxxp2NG012*AD2GF,
                                          auxevol_gfs+Nxxp2NG012*BU0GF,
                                          auxevol_gfs+Nxxp2NG012*BU1GF,
                                          auxevol_gfs+Nxxp2NG012*BU2GF,
                                          auxevol_gfs+Nxxp2NG012*BSTAGGERU0GF,
                                          auxevol_gfs+Nxxp2NG012*BSTAGGERU1GF,
                                          auxevol_gfs+Nxxp2NG012*BSTAGGERU2GF);
    //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);
}
""")
Example #15
0
def GiRaFFE_NRPy_Afield_flux(Ccodesdir):
    cmd.mkdir(Ccodesdir)
    # Write out the code to a file.
    with open(os.path.join(Ccodesdir,"A_i_rhs_no_gauge_terms.h"),"w") as file:
        file.write(r"""/* Compute the part of A_i_rhs that excludes the gauge terms. I.e., we set
 *   A_i_rhs = \partial_t A_i = \psi^{6} (v^z B^x - v^x B^z)   here.
 */
static void A_i_rhs_no_gauge_terms(const int A_dirn,const paramstruct *params,gf_and_gz_struct *out_prims_r,gf_and_gz_struct *out_prims_l,
                                   REAL *psi6_pointer,REAL *cmax_1,REAL *cmin_1,REAL *cmax_2,REAL *cmin_2, REAL *A3_rhs) {
  #include "../set_Cparameters.h"

  // If A_dirn=1, then v1_offset=1 (v1=VY) and v2_offset=2 (v2=VZ)
  // If A_dirn=2, then v1_offset=2 (v1=VZ) and v2_offset=0 (v2=VX)
  // If A_dirn=3, then v1_offset=0 (v1=VX) and v2_offset=1 (v2=VY)
  const int v1_offset  = ((A_dirn-1)+1)%3,        v2_offset = ((A_dirn-1)+2)%3;

  const REAL *v1rr=out_prims_r[VXR+v1_offset].gf, *v2rr=out_prims_r[VXR+v2_offset].gf;
  const REAL *v1rl=out_prims_l[VXR+v1_offset].gf, *v2rl=out_prims_l[VXR+v2_offset].gf;
  const REAL *v1lr=out_prims_r[VXL+v1_offset].gf, *v2lr=out_prims_r[VXL+v2_offset].gf;
  const REAL *v1ll=out_prims_l[VXL+v1_offset].gf, *v2ll=out_prims_l[VXL+v2_offset].gf;

  const REAL *B1r=out_prims_r[BX_STAGGER+v1_offset].gf, *B1l=out_prims_l[BX_STAGGER+v1_offset].gf;
  const REAL *B2r=out_prims_r[BX_STAGGER+v2_offset].gf, *B2l=out_prims_l[BX_STAGGER+v2_offset].gf;

  /**** V DEPENDENCIES ****/
  /* In the case of Ax_rhs, we need v{y,z}{r,l} at (i,j+1/2,k+1/2).
   *    However, v{y,z}{r,l}{r,l} are defined at (i,j-1/2,k-1/2), so
   *    v{y,z}{r,l} at (i,j+1/2,k+1/2) is stored at v{y,z}{r,l}{r,l}(i,j+1,k+1).
   * In the case of Ay_rhs, we need v{x,z}{r,l} at (i+1/2,j,k+1/2).
   *    However, v{x,z}{r,l}{r,l} are defined at (i-1/2,j,k-1/2), so
   *    v{x,z}{r,l} at (i+1/2,j,k+1/2) is stored at v{x,z}{r,l}{r,l}(i+1,j,k+1).
   * In the case of Az_rhs, we need v{x,y}{r,l} at (i+1/2,j+1/2,k).
   *    However, v{x,y}{r,l}{r,l} are defined at (i-1/2,j-1/2,k), so
   *    v{x,y}{r,l} at (i+1/2,j+1/2,k) is stored at v{x,y}{r,l}{r,l}(i+1,j+1,k). */
  static const int vs_ijk_offset[4][3] = { {0,0,0} , {0,1,1} , {1,0,1} , {1,1,0} }; // Note that vs_ijk_offset[0] is UNUSED; we choose a 1-offset for convenience.

  /**** B DEPENDENCIES ****/
  /* In the case of Ax_rhs, we need B{y,z}{r,l} at (i,j+1/2,k+1/2).
   *    However, By_stagger{r,l} is defined at (i,j+1/2,k-1/2), and
   *             Bz_stagger{r,l} is defined at (i,j-1/2,k+1/2), so
   *             By_stagger{r,l} at (i,j+1/2,k+1/2) is stored at By_stagger{r,l}(i,j,k+1), and
   *             Bz_stagger{r,l} at (i,j+1/2,k+1/2) is stored at Bz_stagger{r,l}(i,j+1,k).
   * In the case of Ay_rhs, we need B{z,x}_stagger{r,l} at (i+1/2,j,k+1/2).
   *    However, Bz_stagger{r,l} is defined at (i-1/2,j,k+1/2), and
   *             Bx_stagger{r,l} is defined at (i+1/2,j,k-1/2), so
   *             Bz_stagger{r,l} at (i+1/2,j,k+1/2) is stored at Bz_stagger{r,l}(i+1,j,k), and
   *             Bx_stagger{r,l} at (i+1/2,j,k+1/2) is stored at Bx_stagger{r,l}(i,j,k+1).
   * In the case of Az_rhs, we need B{x,y}_stagger{r,l} at (i+1/2,j+1/2,k).
   *    However, Bx_stagger{r,l} is defined at (i+1/2,j-1/2,k), and
   *             By_stagger{r,l} is defined at (i-1/2,j+1/2,k), so
   *             Bx_stagger{r,l} at (i+1/2,j+1/2,k) is stored at Bx_stagger{r,l}(i,j+1,k), and
   *             By_stagger{r,l} at (i+1/2,j+1/2,k) is stored at By_stagger{r,l}(i+1,j,k).
   */
  static const int B1_ijk_offset[4][3] = { {0,0,0} , {0,0,1} , {1,0,0} , {0,1,0} }; // Note that B1_ijk_offset[0] is UNUSED; we choose a 1-offset for convenience.
  static const int B2_ijk_offset[4][3] = { {0,0,0} , {0,1,0} , {0,0,1} , {1,0,0} }; // Note that B2_ijk_offset[0] is UNUSED; we choose a 1-offset for convenience.

#pragma omp parallel for
  for(int k=NGHOSTS;k<Nxx_plus_2NGHOSTS2-NGHOSTS;k++) for(int j=NGHOSTS;j<Nxx_plus_2NGHOSTS1-NGHOSTS;j++) for(int i=NGHOSTS;i<Nxx_plus_2NGHOSTS0-NGHOSTS;i++) {
        const int index=IDX3S(i,j,k);
        // The following lines set the indices appropriately. See justification in exorbitant comments above.
        const int index_v =IDX3S(i+vs_ijk_offset[A_dirn][0],j+vs_ijk_offset[A_dirn][1],k+vs_ijk_offset[A_dirn][2]);
        const int index_B1=IDX3S(i+B1_ijk_offset[A_dirn][0],j+B1_ijk_offset[A_dirn][1],k+B1_ijk_offset[A_dirn][2]);
        const int index_B2=IDX3S(i+B2_ijk_offset[A_dirn][0],j+B2_ijk_offset[A_dirn][1],k+B2_ijk_offset[A_dirn][2]);

        // Stores 1/sqrt(gamma)==exp(6 phi) at (i+1/2,j+1/2,k) for Az, (i+1/2,j,k+1/2) for Ay, and (i,j+1/2,k+1/2) for Az.
        const REAL psi6_interped=psi6_pointer[index];

        const REAL B1lL = B1l[index_B1];
        const REAL B1rL = B1r[index_B1];
        const REAL B2lL = B2l[index_B2];
        const REAL B2rL = B2r[index_B2];

        const REAL A3_rhs_rr = psi6_interped*(v1rr[index_v]*B2rL - v2rr[index_v]*B1rL);
        const REAL A3_rhs_rl = psi6_interped*(v1rl[index_v]*B2rL - v2rl[index_v]*B1lL);
        const REAL A3_rhs_lr = psi6_interped*(v1lr[index_v]*B2lL - v2lr[index_v]*B1rL);
        const REAL A3_rhs_ll = psi6_interped*(v1ll[index_v]*B2lL - v2ll[index_v]*B1lL);


        // All variables for the A_i_rhs computation are now at the appropriate staggered point,
        //   so it's time to compute the HLL flux!

        // Note that with PPM, cmin and cmax are defined between ijk=3 and ijk<cctk_lsh[]-2 for all directions.
        const REAL cmax_1L = cmax_1[index_B2];
        const REAL cmin_1L = cmin_1[index_B2];
        const REAL cmax_2L = cmax_2[index_B1];
        const REAL cmin_2L = cmin_2[index_B1];

        const REAL B1tilder_minus_B1tildel = psi6_interped*( B1rL - B1lL );
        const REAL B2tilder_minus_B2tildel = psi6_interped*( B2rL - B2lL );

        /*---------------------------
         * Implement 2D HLL flux
         * [see Del Zanna, Bucciantini & Londrillo A&A 400, 397 (2003), Eq. (44)]
         *
         * Note that cmax/cmin (\alpha^{\pm}  as defined in that paper) is at a slightly DIFFERENT
         * point than that described in the Del Zanna et al paper (e.g., (i+1/2,j,k) instead of
         * (i+1/2,j+1/2,k) for F3).  Yuk Tung Liu discussed this point with M. Shibata,
         * who found that the effect is negligible.
         ---------------------------*/
        A3_rhs[index] = (cmax_1L*cmax_2L*A3_rhs_ll + cmax_1L*cmin_2L*A3_rhs_lr +
                         cmin_1L*cmax_2L*A3_rhs_rl + cmin_1L*cmin_2L*A3_rhs_rr)
          /( (cmax_1L+cmin_1L)*(cmax_2L+cmin_2L) )
          - cmax_1L*cmin_1L*(B2tilder_minus_B2tildel)/(cmax_1L+cmin_1L)
          + cmax_2L*cmin_2L*(B1tilder_minus_B1tildel)/(cmax_2L+cmin_2L);
      }
}
""")
Example #16
0
from outputC import outCfunction, lhrh, add_to_Cfunction_dict, outC_function_outdir_dict, outC_function_dict, outC_function_prototype_dict, outC_function_master_list, outC_function_element # NRPy+: Core C code output module
import finite_difference as fin  # NRPy+: Finite difference C code generation module
import NRPy_param_funcs as par   # NRPy+: Parameter interface
import grid as gri               # NRPy+: Functions having to do with numerical grids
import loop as lp                # NRPy+: Generate C code loops
import indexedexp as ixp         # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm   # NRPy+: Reference metric support
import cmdline_helper as cmd     # NRPy+: Multi-platform Python command-line interface

thismodule = __name__

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

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

CoordSystem = "Cartesian"

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

outCparams = "outCverbose=False,CSE_sorting=none"

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

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

import GRHD.equations as GRHD    # NRPy+: Generate general relativistic hydrodynamics equations
def GiRaFFE_NRPy_Afield_flux(Ccodesdir):
    cmd.mkdir(Ccodesdir)
    # Write out the code to a file.
    with open(os.path.join(Ccodesdir, "A_i_rhs_no_gauge_terms.h"),
              "w") as file:
        file.write(body)
Example #18
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);
}
""")
Example #19
0
def construct_Makefile_from_outC_function_dict(Ccodesrootdir, exec_name, uses_free_parameters_h=False,
                                               compiler_opt_option="fastdebug", addl_CFLAGS=None,
                                               addl_libraries=None, mkdir_Ccodesrootdir=True, use_make=True, CC="gcc"):
    if "main" not in outC_function_dict:
        print("construct_Makefile_from_outC_function_dict() error: C codes will not compile if main() function not defined!")
        print("    Make sure that the main() function registered to outC_function_dict has name \"main\".")
        sys.exit(1)

    if not os.path.isdir(Ccodesrootdir):
        if not mkdir_Ccodesrootdir:
            print("Error (in construct_Makefile_from_outC_function_dict): Directory \"" + Ccodesrootdir + "\" does not exist.")
            sys.exit(1)
        else:
            import cmdline_helper as cmd
            cmd.mkdir(Ccodesrootdir)

    Makefile_list_of_files = []
    def add_to_Makefile(Ccodesrootdir, path_and_file):
        Makefile_list_of_files.append(path_and_file)
        return os.path.join(Ccodesrootdir, path_and_file)

    for key, item in outC_function_dict.items():
        # Convention: Output all C files ending in _gridN into the gridN/ subdirectory.
        if "grid" in key.split("_")[-1] and not "grids" in key:
            subdir = key.split("_")[-1]
            with open(add_to_Makefile(Ccodesrootdir, os.path.join(subdir, key+".c")), "w") as file:
                file.write(item)
        elif outC_function_outdir_dict[key] != "default":
            subdir = outC_function_outdir_dict[key]
            with open(add_to_Makefile(Ccodesrootdir, os.path.join(subdir, key+".c")), "w") as file:
                file.write(item)
        else:
            with open(add_to_Makefile(Ccodesrootdir, os.path.join(key+".c")), "w") as file:
                file.write(item)
    CFLAGS      = " -O2 -march=native -g -fopenmp -Wall -Wno-unused-variable"
    DEBUGCFLAGS = " -O2 -g -Wall -Wno-unused-variable -Wno-unknown-pragmas"  # OpenMP requires -fopenmp, and when disabling
                                                                             # -fopenmp, unknown pragma warnings appear.
                                                                             # -Wunknown-pragmas silences these warnings
    FASTCFLAGS  = "  -O2 -march=native -fopenmp -Wall -Wno-unused-variable"
    if CC == "gcc":
        CFLAGS      += " -std=gnu99"
        DEBUGCFLAGS += " -std=gnu99"
        FASTCFLAGS   += " -std=gnu99"
    CHOSEN_CFLAGS = CFLAGS
    if compiler_opt_option == "debug":
        CHOSEN_CFLAGS = DEBUGCFLAGS
    elif compiler_opt_option == "fast":
        CHOSEN_CFLAGS = FASTCFLAGS
    if addl_CFLAGS is not None:
        if not isinstance(addl_CFLAGS, list):
            print("Error: construct_Makefile_from_outC_function_dict(): addl_CFLAGS must be a list!")
            sys.exit(1)
        for FLAG in addl_CFLAGS:
            CHOSEN_CFLAGS += " "+FLAG
    all_str = exec_name + " "
    dep_list = []
    compile_list = []
    for c_file in Makefile_list_of_files:
        object_file = c_file.replace(".c", ".o")
        all_str += " " + object_file
        addl_headers = ""
        if uses_free_parameters_h:
            if c_file == "main.c":
                addl_headers += " free_parameters.h"
        dep_list.append(object_file + ": " + c_file + addl_headers)
        compile_list.append("\t$(CC) $(CFLAGS)  -c " + c_file + " -o " + object_file)

    linked_libraries = " -lm"
    if addl_libraries is not None:
        if not isinstance(addl_libraries, list):
            print("Error: construct_Makefile_from_outC_function_dict(): addl_libraries must be a list!")
            sys.exit(1)
        for lib in addl_libraries:
            linked_libraries += " " + lib

    if use_make:
        with open(os.path.join(Ccodesrootdir, "Makefile"), "w") as Makefile:
            Makefile.write("""CC     = """ + CC + """
CFLAGS = """ + CHOSEN_CFLAGS + """
#CFLAGS = """ + CFLAGS + """
#CFLAGS = """ + DEBUGCFLAGS + """
#CFLAGS = """ + FASTCFLAGS + "\n")
            Makefile.write("all: " + all_str + "\n")
            for idx, dep in enumerate(dep_list):
                Makefile.write(dep + "\n")
                Makefile.write(compile_list[idx] + "\n\n")
            Makefile.write(exec_name + ": " + all_str.replace(exec_name, "") + "\n")
            Makefile.write("\t$(CC) $(CFLAGS) main.c " + all_str.replace(exec_name, "").replace("main.o", "") + " -o " + exec_name + linked_libraries + "\n")
            Makefile.write("\nclean:\n\trm -f *.o */*.o *~ */*~ ./#* *.txt *.dat *.avi *.png " + exec_name + "\n")
    else:
        with open(os.path.join(Ccodesrootdir, "backup_script_nomake.sh"), "w") as backup:
            for compile_line in compile_list:
                backup.write(compile_line.replace("$(CC)", CC).replace("$(CFLAGS)", CFLAGS).replace("\t", "") + "\n")
            backup.write(CC + " " + CFLAGS + " main.c " + all_str.replace(exec_name, "").replace("main.o", "") + " -o " + exec_name + linked_libraries + "\n")
        os.chmod(os.path.join(Ccodesrootdir, "backup_script_nomake.sh"), stat.S_IRWXU)
Example #20
0
def GiRaFFE_NRPy_A2B(Ccodesdir):
    cmd.mkdir(Ccodesdir)
    # Write out the code to a file.
    with open(os.path.join(Ccodesdir, "compute_B_and_Bstagger_from_A.h"),
              "w") as file:
        file.write("""#define LOOP_DEFINE_SIMPLE                      \\
  _Pragma("omp parallel for")                   \\
  for(int k=0;k<Nxx_plus_2NGHOSTS2;k++)                \\
    for(int j=0;j<Nxx_plus_2NGHOSTS1;j++)              \\
      for(int i=0;i<Nxx_plus_2NGHOSTS0;i++)

void GiRaFFE_compute_B_and_Bstagger_from_A(const paramstruct *params,
                                           const REAL *gxx, const REAL *gxy, const REAL *gxz, const REAL *gyy, const REAL *gyz,const REAL *gzz,
                                           REAL *psi3_bssn, const REAL *Ax, const REAL *Ay, const REAL *Az,
                                           REAL *Bx, REAL *By, REAL *Bz, REAL *Bx_stagger, REAL *By_stagger, REAL *Bz_stagger) {
#include "../set_Cparameters.h"

  LOOP_DEFINE_SIMPLE {
    const int index=IDX3S(i,j,k);
    psi3_bssn[index] = sqrt(sqrt( gxx[index]*gyy[index]*gzz[index]
                               -  gxx[index]*gyz[index]*gyz[index]
                               +2*gxy[index]*gxz[index]*gyz[index]
                               -  gyy[index]*gxz[index]*gxz[index]
                               -  gzz[index]*gxy[index]*gxy[index]));
  }

  LOOP_DEFINE_SIMPLE {
    // Look Mom, no if() statements!
    const int shiftedim1 = (i-1)*(i!=0); // This way, i=0 yields shiftedim1=0 and shiftedi=1, used below for our COPY boundary condition.
    const int shiftedi   = shiftedim1+1;

    const int shiftedjm1 = (j-1)*(j!=0);
    const int shiftedj   = shiftedjm1+1;

    const int shiftedkm1 = (k-1)*(k!=0);
    const int shiftedk   = shiftedkm1+1;

    int index,indexim1,indexjm1,indexkm1;

    const int actual_index = IDX3S(i,j,k);

    const REAL Psim3 = 1.0/psi3_bssn[actual_index];

    // For the lower boundaries, the following applies a "copy"
    //    boundary condition on Bi_stagger where needed.
    //    E.g., Bx_stagger(i,jmin,k) = Bx_stagger(i,jmin+1,k)
    //    We find the copy BC works better than extrapolation.
    // For the upper boundaries, we do the following copy:
    //    E.g., Psi(imax+1,j,k)=Psi(imax,j,k)
    /**************/
    /* Bx_stagger */
    /**************/

    index    = IDX3S(i,shiftedj,shiftedk);
    indexjm1 = IDX3S(i,shiftedjm1,shiftedk);
    indexkm1 = IDX3S(i,shiftedj,shiftedkm1);
    // Set Bx_stagger = \partial_y A_z - partial_z A_y
    // "Grid" Ax(i,j,k) is actually Ax(i,j+1/2,k+1/2)
    // "Grid" Ay(i,j,k) is actually Ay(i+1/2,j,k+1/2)
    // "Grid" Az(i,j,k) is actually Ay(i+1/2,j+1/2,k)
    // Therefore, the 2nd order derivative \partial_z A_y at (i+1/2,j,k) is:
    //          ["Grid" Ay(i,j,k) - "Grid" Ay(i,j,k-1)]/dZ
    Bx_stagger[actual_index] = (Az[index]-Az[indexjm1])*invdx1 - (Ay[index]-Ay[indexkm1])*invdx2;

    // Now multiply Bx_stagger by 1/sqrt(gamma(i+1/2,j,k)]) = 1/sqrt(1/2 [gamma + gamma_ip1]) = exp(-6 x 1/2 [phi + phi_ip1] )
    const int imax_minus_i = (Nxx_plus_2NGHOSTS0-1)-i;
    const int indexip1jk = IDX3S(i + ( (imax_minus_i > 0) - (0 > imax_minus_i) ),j,k);
    Bx_stagger[actual_index] *= Psim3/psi3_bssn[indexip1jk];

    /**************/
    /* By_stagger */
    /**************/

    index    = IDX3S(shiftedi,j,shiftedk);
    indexim1 = IDX3S(shiftedim1,j,shiftedk);
    indexkm1 = IDX3S(shiftedi,j,shiftedkm1);
    // Set By_stagger = \partial_z A_x - \partial_x A_z
    By_stagger[actual_index] = (Ax[index]-Ax[indexkm1])*invdx2 - (Az[index]-Az[indexim1])*invdx0;

    // Now multiply By_stagger by 1/sqrt(gamma(i,j+1/2,k)]) = 1/sqrt(1/2 [gamma + gamma_jp1]) = exp(-6 x 1/2 [phi + phi_jp1] )
    const int jmax_minus_j = (Nxx_plus_2NGHOSTS1-1)-j;
    const int indexijp1k = IDX3S(i,j + ( (jmax_minus_j > 0) - (0 > jmax_minus_j) ),k);
    By_stagger[actual_index] *= Psim3/psi3_bssn[indexijp1k];

    /**************/
    /* Bz_stagger */
    /**************/

    index    = IDX3S(shiftedi,shiftedj,k);
    indexim1 = IDX3S(shiftedim1,shiftedj,k);
    indexjm1 = IDX3S(shiftedi,shiftedjm1,k);
    // Set Bz_stagger = \partial_x A_y - \partial_y A_x
    Bz_stagger[actual_index] = (Ay[index]-Ay[indexim1])*invdx0 - (Ax[index]-Ax[indexjm1])*invdx1;

    // Now multiply Bz_stagger by 1/sqrt(gamma(i,j,k+1/2)]) = 1/sqrt(1/2 [gamma + gamma_kp1]) = exp(-6 x 1/2 [phi + phi_kp1] )
    const int kmax_minus_k = (Nxx_plus_2NGHOSTS2-1)-k;
    const int indexijkp1 = IDX3S(i,j,k + ( (kmax_minus_k > 0) - (0 > kmax_minus_k) ));
    Bz_stagger[actual_index] *= Psim3/psi3_bssn[indexijkp1];

  }

  LOOP_DEFINE_SIMPLE {
    // Look Mom, no if() statements!
    const int shiftedim1 = (i-1)*(i!=0); // This way, i=0 yields shiftedim1=0 and shiftedi=1, used below for our COPY boundary condition.
    const int shiftedi   = shiftedim1+1;

    const int shiftedjm1 = (j-1)*(j!=0);
    const int shiftedj   = shiftedjm1+1;

    const int shiftedkm1 = (k-1)*(k!=0);
    const int shiftedk   = shiftedkm1+1;

    int index,indexim1,indexjm1,indexkm1;

    const int actual_index = IDX3S(i,j,k);

    // For the lower boundaries, the following applies a "copy"
    //    boundary condition on Bi and Bi_stagger where needed.
    //    E.g., Bx(imin,j,k) = Bx(imin+1,j,k)
    //    We find the copy BC works better than extrapolation.
    /******/
    /* Bx */
    /******/
    index = IDX3S(shiftedi,j,k);
    indexim1 = IDX3S(shiftedim1,j,k);
    // Set Bx = 0.5 ( Bx_stagger + Bx_stagger_im1 )
    // "Grid" Bx_stagger(i,j,k) is actually Bx_stagger(i+1/2,j,k)
    Bx[actual_index] = 0.5 * ( Bx_stagger[index] + Bx_stagger[indexim1] );

    /******/
    /* By */
    /******/
    index = IDX3S(i,shiftedj,k);
    indexjm1 = IDX3S(i,shiftedjm1,k);
    // Set By = 0.5 ( By_stagger + By_stagger_im1 )
    // "Grid" By_stagger(i,j,k) is actually By_stagger(i,j+1/2,k)
    By[actual_index] = 0.5 * ( By_stagger[index] + By_stagger[indexjm1] );

    /******/
    /* Bz */
    /******/
    index = IDX3S(i,j,shiftedk);
    indexkm1 = IDX3S(i,j,shiftedkm1);
    // Set Bz = 0.5 ( Bz_stagger + Bz_stagger_im1 )
    // "Grid" Bz_stagger(i,j,k) is actually Bz_stagger(i,j+1/2,k)
    Bz[actual_index] = 0.5 * ( Bz_stagger[index] + Bz_stagger[indexkm1] );
  }
}
""")
Example #21
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++)
}
""")
Example #22
0
def GiRaFFE_NRPy_BCs(Ccodesdir):
    cmd.mkdir(Ccodesdir)
    # Write out the code to a file.
    with open(os.path.join(Ccodesdir,"GiRaFFE_boundary_conditions.h"),"w") as file:
        file.write("""// Currently, we're using basic Cartesian boundary conditions, pending fixes by Zach.
// Part P8a: Declare boundary condition FACE_UPDATE macro,
//          which updates a single face of the 3D grid cube
//          using quadratic polynomial extrapolation.
// Basic extrapolation boundary conditions
#define  FACE_UPDATE(which_gf, i0min,i0max, i1min,i1max, i2min,i2max, FACEX0,FACEX1,FACEX2) \\
  for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) { \\
        gfs[IDX4S(which_gf,i0,i1,i2)] =                                  \\
          +2.0*gfs[IDX4S(which_gf,i0+1*FACEX0,i1+1*FACEX1,i2+1*FACEX2)]  \\
          -1.0*gfs[IDX4S(which_gf,i0+2*FACEX0,i1+2*FACEX1,i2+2*FACEX2)]; \\
      }
//          +1.0*gfs[IDX4S(which_gf,i0+3*FACEX0,i1+3*FACEX1,i2+3*FACEX2)]; \\
// Basic Copy boundary conditions
#define  FACE_UPDATE_COPY(which_gf, i0min,i0max, i1min,i1max, i2min,i2max, FACEX0,FACEX1,FACEX2) \\
  for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) { \\
        gfs[IDX4S(which_gf,i0,i1,i2)] = gfs[IDX4S(which_gf,i0+1*FACEX0,i1+1*FACEX1,i2+1*FACEX2)]; \\
      }

// Part P8b: Boundary condition driver routine: Apply BCs to all six
//          boundary faces of the cube, filling in the innermost
//          ghost zone first, and moving outward.
const int MAXFACE = -1;
const int NUL     = +0;
const int MINFACE = +1;
// This macro acts differently in that it acts on an entire 3-vector of gfs, instead of 1.
// which_gf_0 corresponds to the zeroth component of that vector. The if statements only
// evaluate true if the velocity is directed inwards on the face in consideration.
#define  FACE_UPDATE_OUTFLOW(which_gf, i0min,i0max, i1min,i1max, i2min,i2max, FACEX0,FACEX1,FACEX2) \\
  for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) { \\
      aux_gfs[IDX4S(which_gf_0,i0,i1,i2)] =                                      \\
          aux_gfs[IDX4S(which_gf_0,i0+FACEX0,i1+FACEX1,i2+FACEX2)];              \\
      aux_gfs[IDX4S(which_gf_0+1,i0,i1,i2)] =                                    \\
          aux_gfs[IDX4S(which_gf_0+1,i0+FACEX0,i1+FACEX1,i2+FACEX2)];            \\
      aux_gfs[IDX4S(which_gf_0+2,i0,i1,i2)] =                                    \\
          aux_gfs[IDX4S(which_gf_0+2,i0+FACEX0,i1+FACEX1,i2+FACEX2)];            \\
  }
/*      if(FACEX0*aux_gfs[IDX4S(which_gf_0+0,i0,i1,i2)] > 0.0) {                   \\
          aux_gfs[IDX4S(which_gf_0+0,i0,i1,i2)] = 0.0;                           \\
      }                                                                          \\
      if(FACEX1*aux_gfs[IDX4S(which_gf_0+1,i0,i1,i2)] > 0.0) {                   \\
          aux_gfs[IDX4S(which_gf_0+1,i0,i1,i2)] = 0.0;                           \\
      }                                                                          \\
      if(FACEX2*aux_gfs[IDX4S(which_gf_0+2,i0,i1,i2)] > 0.0) {                   \\
          aux_gfs[IDX4S(which_gf_0+2,i0,i1,i2)] = 0.0;                           \\
      }                                                                          \\
*/

void apply_bcs_potential(const paramstruct *restrict params,REAL *gfs) {
#include "../set_Cparameters.h"
    // First, we apply extrapolation boundary conditions to AD
#pragma omp parallel for
    for(int which_gf=0;which_gf<NUM_EVOL_GFS;which_gf++) {
    if(which_gf < STILDED0GF || which_gf > STILDED2GF) {
    int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS };
    int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS };
    for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) {
      // After updating each face, adjust imin[] and imax[]
      //   to reflect the newly-updated face extents.
      FACE_UPDATE(which_gf, imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2], MINFACE,NUL,NUL); imin[0]--;
      FACE_UPDATE(which_gf, imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2], MAXFACE,NUL,NUL); imax[0]++;

      FACE_UPDATE(which_gf, imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2], NUL,MINFACE,NUL); imin[1]--;
      FACE_UPDATE(which_gf, imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2], NUL,MAXFACE,NUL); imax[1]++;

      FACE_UPDATE(which_gf, imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2], NUL,NUL,MINFACE);
        imin[2]--;
      FACE_UPDATE(which_gf, imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1, NUL,NUL,MAXFACE);
        imax[2]++;
    }
    }
    }
    // Then, we apply copy boundary conditions to StildeD and psi6Phi
/*#pragma omp parallel for
    for(int which_gf=3;which_gf<NUM_EVOL_GFS;which_gf++) {
    int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS };
    int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS };
    for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) {
      // After updating each face, adjust imin[] and imax[]
      //   to reflect the newly-updated face extents.
      FACE_UPDATE_COPY(which_gf, imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2], MINFACE,NUL,NUL); imin[0]--;
      FACE_UPDATE_COPY(which_gf, imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2], MAXFACE,NUL,NUL); imax[0]++;

      FACE_UPDATE_COPY(which_gf, imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2], NUL,MINFACE,NUL); imin[1]--;
      FACE_UPDATE_COPY(which_gf, imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2], NUL,MAXFACE,NUL); imax[1]++;

      FACE_UPDATE_COPY(which_gf, imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2], NUL,NUL,MINFACE); imin[2]--;
      FACE_UPDATE_COPY(which_gf, imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1, NUL,NUL,MAXFACE); imax[2]++;
    }
    }*/
}
void apply_bcs_velocity(const paramstruct *restrict params,REAL *aux_gfs) {
#include "../set_Cparameters.h"
    // Apply outflow/copy boundary conditions to ValenciavU by passing VALENCIAVU0 as which_gf_0
//     for(int which_gf=VALENCIAVU0GF;which_gf<=VALENCIAVU2GF;which_gf++) {
    const int which_gf_0 = VALENCIAVU0GF;
    int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS };
    int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS };
    for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) {
      FACE_UPDATE_OUTFLOW(which_gf_0, imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2], MINFACE,NUL,NUL); imin[0]--;
      FACE_UPDATE_OUTFLOW(which_gf_0, imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2], MAXFACE,NUL,NUL); imax[0]++;

      FACE_UPDATE_OUTFLOW(which_gf_0, imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2], NUL,MINFACE,NUL); imin[1]--;
      FACE_UPDATE_OUTFLOW(which_gf_0, imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2], NUL,MAXFACE,NUL); imax[1]++;

      FACE_UPDATE_OUTFLOW(which_gf_0, imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2], NUL,NUL,MINFACE);
        imin[2]--;
      FACE_UPDATE_OUTFLOW(which_gf_0, imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1, NUL,NUL,MAXFACE);
        imax[2]++;
    }
//     }
}
/*// A supplement to the boundary conditions for debugging. This will overwrite data with exact conditions
void FACE_UPDATE_EXACT(const paramstruct *restrict params,REAL *restrict xx[3],
                       const int n, const REAL dt,REAL *out_gfs,REAL *aux_gfs,
                       const int i0min,const int i0max, const int i1min,const int i1max, const int i2min,const int i2max,
                       const int FACEX0,const int FACEX1,const int FACEX2) {
#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 xx0 = xx[0][i0]-n*dt;
    REAL xx1 = xx[1][i1];
    REAL xx2 = xx[2][i2];
    if(xx0<=lbound) {
#include "../GiRaFFEfood_A_v_1D_tests_left.h"
    }
    else if (xx0<rbound) {
#include "../GiRaFFEfood_A_v_1D_tests_center.h"
    }
    else {
#include "../GiRaFFEfood_A_v_1D_tests_right.h"
    }
    out_gfs[IDX4S(PSI6PHIGF, i0,i1,i2)] = 0.0;
  }
}
void apply_bcs_EXACT(const paramstruct *restrict params,REAL *restrict xx[3],
                     const int n, const REAL dt,
                     REAL *out_gfs,REAL *aux_gfs) {
#include "../set_Cparameters.h"
    int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS };
    int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS };
    for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) {
      // After updating each face, adjust imin[] and imax[]
      //   to reflect the newly-updated face extents.
      // Right now, we only want to update the xmin and xmax faces with the exact data.
      FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2], MINFACE,NUL,NUL);
      imin[0]--;
      FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2], MAXFACE,NUL,NUL);
      imax[0]++;

      FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2], NUL,MINFACE,NUL);
      imin[1]--;
      FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2], NUL,MAXFACE,NUL);
      imax[1]++;

      FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2], NUL,NUL,MINFACE);
      imin[2]--;
      FACE_UPDATE_EXACT(Nxx,Nxx_plus_2NGHOSTS,xx,n,dt,out_gfs,aux_gfs,imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1, NUL,NUL,MAXFACE);
      imax[2]++;
    }
}
// A supplement to the boundary conditions for debugging. This will overwrite data with exact conditions
void FACE_UPDATE_EXACT_StildeD(const paramstruct *restrict params,REAL *restrict xx[3],
                               REAL *out_gfs,REAL *out_gfs_exact,
                               const int i0min,const int i0max, const int i1min,const int i1max, const int i2min,const int i2max,
                               const int FACEX0,const int FACEX1,const int FACEX2) {
#include "../set_Cparameters.h"
    // This is currently modified to calculate more exact boundary conditions for StildeD. Rename if it works.
    for(int i2=i2min;i2<i2max;i2++) for(int i1=i1min;i1<i1max;i1++) for(int i0=i0min;i0<i0max;i0++) {
#include "../GiRaFFEfood_NRPy_Stilde.h"
    }
      idx = IDX3(i0,i1,i2);
      out_gfs[IDX4ptS(STILDED0GF,idx)] = out_gfs_exact[IDX4ptS(STILDED0GF,idx)];
      out_gfs[IDX4ptS(STILDED1GF,idx)] = out_gfs_exact[IDX4ptS(STILDED1GF,idx)];
      out_gfs[IDX4ptS(STILDED2GF,idx)] = out_gfs_exact[IDX4ptS(STILDED2GF,idx)];
}
void apply_bcs_EXACT_StildeD(const paramstruct *restrict params,REAL *restrict xx[3],
                             REAL *out_gfs,REAL *out_gfs_exact) {
#include "../set_Cparameters.h"
    int imin[3] = { NGHOSTS, NGHOSTS, NGHOSTS };
    int imax[3] = { Nxx_plus_2NGHOSTS0-NGHOSTS, Nxx_plus_2NGHOSTS1-NGHOSTS, Nxx_plus_2NGHOSTS2-NGHOSTS };
    for(int which_gz = 0; which_gz < NGHOSTS; which_gz++) {
      // After updating each face, adjust imin[] and imax[]
      //   to reflect the newly-updated face extents.
      // Right now, we only want to update the xmin and xmax faces with the exact data.
      FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imin[0]-1,imin[0], imin[1],imax[1], imin[2],imax[2], MINFACE,NUL,NUL);
      imin[0]--;
      FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imax[0],imax[0]+1, imin[1],imax[1], imin[2],imax[2], MAXFACE,NUL,NUL);
      imax[0]++;

      //FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imin[0],imax[0], imin[1]-1,imin[1], imin[2],imax[2], NUL,MINFACE,NUL);
      imin[1]--;
      //FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imin[0],imax[0], imax[1],imax[1]+1, imin[2],imax[2], NUL,MAXFACE,NUL);
      imax[1]++;

      //FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imin[0],imax[0], imin[1],imax[1], imin[2]-1,imin[2], NUL,NUL,MINFACE);
      imin[2]--;
      //FACE_UPDATE_EXACT_StildeD(Nxx,Nxx_plus_2NGHOSTS,xx,out_gfs,out_gfs_exact,imin[0],imax[0], imin[1],imax[1], imax[2],imax[2]+1, NUL,NUL,MAXFACE);
      imax[2]++;
    }
}*/
""")
Example #23
0
# Step 0: Add NRPy's directory to the path
# https://stackoverflow.com/questions/16780014/import-file-from-parent-directory
import os, sys
nrpy_dir_path = os.path.join("..")
if nrpy_dir_path not in sys.path:
    sys.path.append(nrpy_dir_path)

import cmdline_helper as cmd  # NRPy+: Multi-platform Python command-line interface
Ccodesdir = "GiRaFFE_standalone_Ccodes/A2B"
cmd.mkdir(os.path.join(Ccodesdir))


def GiRaFFE_NRPy_A2B(Ccodesdir):
    cmd.mkdir(Ccodesdir)
    # Write out the code to a file.
    with open(os.path.join(Ccodesdir, "compute_B_and_Bstagger_from_A.h"),
              "w") as file:
        file.write("""#define LOOP_DEFINE_SIMPLE                      \\
  _Pragma("omp parallel for")                   \\
  for(int k=0;k<Nxx_plus_2NGHOSTS2;k++)                \\
    for(int j=0;j<Nxx_plus_2NGHOSTS1;j++)              \\
      for(int i=0;i<Nxx_plus_2NGHOSTS0;i++)

void GiRaFFE_compute_B_and_Bstagger_from_A(const paramstruct *params,
                                           const REAL *gxx, const REAL *gxy, const REAL *gxz, const REAL *gyy, const REAL *gyz,const REAL *gzz,
                                           REAL *psi3_bssn, const REAL *Ax, const REAL *Ay, const REAL *Az,
                                           REAL *Bx, REAL *By, REAL *Bz, REAL *Bx_stagger, REAL *By_stagger, REAL *Bz_stagger) {
#include "../set_Cparameters.h"

  LOOP_DEFINE_SIMPLE {
    const int index=IDX3S(i,j,k);
def GiRaFFE_NRPy_Source_Terms(Ccodesdir):
    cmd.mkdir(Ccodesdir)
    # Write out the code to a file.
    with open(
            os.path.join(Ccodesdir,
                         "Lorenz_psi6phi_rhs__add_gauge_terms_to_A_i_rhs.h"),
            "w") as file:
        file.write(
            """static inline REAL avg(const REAL f[PLUS2+1][PLUS2+1][PLUS2+1],const int imin,const int imax, const int jmin,const int jmax, const int kmin,const int kmax);

#define MINUS2 0
#define MINUS1 1
#define PLUS0  2
#define PLUS1  3
#define PLUS2  4
// The "I" suffix denotes interpolation. In other words, these
//    definitions are used for interpolation ONLY. The order here
//    matters as well!
static const int SHIFTXI=0,SHIFTYI=1,SHIFTZI=2,GUPXXI=3,GUPXYI=4,GUPXZI=5,GUPYYI=6,GUPYZI=7,GUPZZI=8,
  PSII=9,LAPM1I=10,A_XI=11,A_YI=12,A_ZI=13,LAPSE_PSI2I=14,LAPSE_OVER_PSI6I=15;
#define MAXNUMINTERP 16
// GiRaFFE_NRPy does not store the inverse metric. So, the actual inputs to the function will be
// just the metric, which we can invert in an early step. To keep things consistent, we'll label the
// components with the same labels as the inverse:
static const int GXXI=3,GXYI=4,GXZI=5,GYYI=6,GYZI=7,GZZI=8;

static void Lorenz_psi6phi_rhs__add_gauge_terms_to_A_i_rhs(const paramstruct *params,REAL **in_vars,const REAL *psi6phi,
                                                           /* TEMPS: */
                                                           REAL *shiftx_iphjphkph,REAL *shifty_iphjphkph,REAL *shiftz_iphjphkph,
                                                           REAL *alpha_iphjphkph,REAL *alpha_Phi_minus_betaj_A_j_iphjphkph,REAL *alpha_sqrtg_Ax_interp,
                                                           REAL *alpha_sqrtg_Ay_interp,REAL *alpha_sqrtg_Az_interp,
                                                           /* END TEMPS, 8 total! */
                                                           REAL *psi6phi_rhs,REAL *Ax_rhs,REAL *Ay_rhs,REAL *Az_rhs) {
  #include "../set_Cparameters.h"

  /* Compute
   * \\partial_t psi6phi = -\\partial_j ( \\alpha \\sqrt{\\gamma} A^j - \\beta^j psi6phi)
   *    (Eq 13 of http://arxiv.org/pdf/1110.4633.pdf), using Lorenz gauge.
   * Note that the RHS consists of a shift advection term on psi6phi and
   *    a term depending on the vector potential.
   * psi6phi is defined at (i+1/2,j+1/2,k+1/2), but instead of reconstructing
   *    to compute the RHS of \\partial_t psi6phi, we instead use standard
   *    interpolations.
   */

  // The stencil here is {-1,1},{-1,1},{-1,1} for x,y,z directions, respectively.
  //     Note that ALL input variables are defined at ALL gridpoints, so no
  //     worries about ghostzones.
#pragma omp parallel for
  for(int k=1;k<Nxx_plus_2NGHOSTS2-1;k++) for(int j=1;j<Nxx_plus_2NGHOSTS1-1;j++) for(int i=1;i<Nxx_plus_2NGHOSTS0-1;i++) {
        const int index=IDX3S(i,j,k);
        REAL INTERP_VARS[MAXNUMINTERP][PLUS2+1][PLUS2+1][PLUS2+1];

        // First compute \\partial_j \\alpha \\sqrt{\\gamma} A^j (RHS of \\partial_i psi6phi)
        // FIXME: Would be much cheaper & easier to unstagger A_i, raise, then interpolate A^i.
        //        However, we keep it this way to be completely compatible with the original
        //        Illinois GRMHD thorn, called mhd_evolve.
        //
        //Step 1) j=x: Need to raise A_i, but to do that, we must have all variables at the same gridpoints:
        // The goal is to compute \\partial_j (\\alpha \\sqrt{\\gamma} A^j) at (i+1/2,j+1/2,k+1/2)
        //    We do this by first interpolating (RHS1x) = (\\alpha \\sqrt{\\gamma} A^x) at
        //    (i,j+1/2,k+1/2)and (i+1,j+1/2,k+1/2), then taking \\partial_x (RHS1x) =
        //    [ RHS1x(i+1,j+1/2,k+1/2) - RHS1x(i,j+1/2,k+1/2) ]/dX.
        // First bring gup's, psi, and alpha to (i,j+1/2,k+1/2):
        int num_vars_to_interp;
        int vars_to_interpolate[MAXNUMINTERP] = {GUPXXI,GUPXYI,GUPXZI,GUPYYI,GUPYZI,GUPZZI,LAPM1I,PSII,SHIFTXI,SHIFTYI,SHIFTZI};
        num_vars_to_interp = 11;
        // We may set interp_limits to be more general than we need.
        int interp_limits[6] = {-1,1,-1,1,-1,1}; SET_INDEX_ARRAYS_NRPY_3DBLOCK(interp_limits);
        //SET_INDEX_ARRAYS_NRPY_3DBLOCK(interp_limits);
//         for(int ww=0;ww<num_vars_to_interp;ww++) {
//           int whichvar=vars_to_interpolate[ww];
//           // Read in variable at interp. stencil points from main memory, store in INTERP_VARS.
//           for(int kk=PLUS0;kk<=PLUS1;kk++) for(int jj=PLUS0;jj<=PLUS1;jj++) for(int ii=PLUS0;ii<=PLUS1;ii++) {
//                 INTERP_VARS[whichvar][kk][jj][ii] = in_vars[whichvar][index_arr_3DB[kk][jj][ii]]; }
//         }
        // Major change here to invert the metric on the spot!
        // Read in variable at interp. stencil points from main memory, store in INTERP_VARS.
        for(int kk=PLUS0;kk<=PLUS1;kk++) for(int jj=PLUS0;jj<=PLUS1;jj++) for(int ii=PLUS0;ii<=PLUS1;ii++) {
            // First, we will read in each component of the metric, then find the determinant.
            // We write the twelfth root to psi_bssn. Then, we invert the metric and store these values.
            const REAL gammaDD00 = in_vars[GXXI][index_arr_3DB[kk][jj][ii]];
            const REAL gammaDD01 = in_vars[GXYI][index_arr_3DB[kk][jj][ii]];
            const REAL gammaDD02 = in_vars[GXZI][index_arr_3DB[kk][jj][ii]];
            const REAL gammaDD11 = in_vars[GYYI][index_arr_3DB[kk][jj][ii]];
            const REAL gammaDD12 = in_vars[GYZI][index_arr_3DB[kk][jj][ii]];
            const REAL gammaDD22 = in_vars[GZZI][index_arr_3DB[kk][jj][ii]];
            // Generated by NRPy+:
            /*
             * NRPy+ Finite Difference Code Generation, Step 2 of 1: Evaluate SymPy expressions and write to main memory:
             */
            const double FDPart3_0 = cbrt(gammaDD00*gammaDD11*gammaDD22 - gammaDD00*((gammaDD12)*(gammaDD12)) - ((gammaDD01)*(gammaDD01))*gammaDD22 + 2*gammaDD01*gammaDD02*gammaDD12 - ((gammaDD02)*(gammaDD02))*gammaDD11);
            const double FDPart3_1 = (1.0/(FDPart3_0));
            const REAL gamma_bssnDD00 = FDPart3_1*gammaDD00;
            const REAL gamma_bssnDD01 = FDPart3_1*gammaDD01;
            const REAL gamma_bssnDD02 = FDPart3_1*gammaDD02;
            const REAL gamma_bssnDD11 = FDPart3_1*gammaDD11;
            const REAL gamma_bssnDD12 = FDPart3_1*gammaDD12;
            const REAL gamma_bssnDD22 = FDPart3_1*gammaDD22;
            const double tmp_5 = gamma_bssnDD00*gamma_bssnDD11*gamma_bssnDD22 - gamma_bssnDD00*((gamma_bssnDD12)*(gamma_bssnDD12)) - ((gamma_bssnDD01)*(gamma_bssnDD01))*gamma_bssnDD22 + 2*gamma_bssnDD01*gamma_bssnDD02*gamma_bssnDD12 - ((gamma_bssnDD02)*(gamma_bssnDD02))*gamma_bssnDD11;
            const double tmp_6 = (1.0/(tmp_5));
            INTERP_VARS[GUPXXI][kk][jj][ii] = tmp_6*(gamma_bssnDD11*gamma_bssnDD22 - ((gamma_bssnDD12)*(gamma_bssnDD12)));
            INTERP_VARS[GUPXYI][kk][jj][ii] = tmp_6*(-gamma_bssnDD01*gamma_bssnDD22 + gamma_bssnDD02*gamma_bssnDD12);
            INTERP_VARS[GUPXZI][kk][jj][ii] = tmp_6*(gamma_bssnDD01*gamma_bssnDD12 - gamma_bssnDD02*gamma_bssnDD11);
            INTERP_VARS[GUPYYI][kk][jj][ii] = tmp_6*(gamma_bssnDD00*gamma_bssnDD22 - ((gamma_bssnDD02)*(gamma_bssnDD02)));
            INTERP_VARS[GUPYZI][kk][jj][ii] = tmp_6*(-gamma_bssnDD00*gamma_bssnDD12 + gamma_bssnDD01*gamma_bssnDD02);
            INTERP_VARS[GUPZZI][kk][jj][ii] = tmp_6*(gamma_bssnDD00*gamma_bssnDD11 - ((gamma_bssnDD01)*(gamma_bssnDD01)));
            INTERP_VARS[PSII][kk][jj][ii]   = pow(FDPart3_0,1.0/4.0);

            // Now, we read in the lapse function.
            int whichvar=vars_to_interpolate[6];
            INTERP_VARS[whichvar][kk][jj][ii] = in_vars[whichvar][index_arr_3DB[kk][jj][ii]]-1.0; // Input alpha, expect alpha-1
            // Finally, we read in the shift vector into the array.
            for(int ww=8;ww<num_vars_to_interp;ww++) {
                int whichvar=vars_to_interpolate[ww];
                INTERP_VARS[whichvar][kk][jj][ii] = in_vars[whichvar][index_arr_3DB[kk][jj][ii]];
            }
        }

        // Next set \\alpha at (i+1/2,j+1/2,k+1/2). Will come in handy when computing damping term later.
        alpha_iphjphkph[index] = avg(INTERP_VARS[LAPM1I] , PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS1)+1.0;

        //A_x needs a stencil s.t. interp_limits={0,1,-1,1,-1,1}:
        for(int kk=MINUS1;kk<=PLUS1;kk++) for(int jj=MINUS1;jj<=PLUS1;jj++) for(int ii=PLUS0;ii<=PLUS1;ii++) {
              INTERP_VARS[A_XI][kk][jj][ii] = in_vars[A_XI][index_arr_3DB[kk][jj][ii]]; }
        //A_y needs a stencil s.t. interp_limits={-1,1,0,1,-1,1}:
        for(int kk=MINUS1;kk<=PLUS1;kk++) for(int jj=PLUS0;jj<=PLUS1;jj++) for(int ii=MINUS1;ii<=PLUS1;ii++) {
              INTERP_VARS[A_YI][kk][jj][ii] = in_vars[A_YI][index_arr_3DB[kk][jj][ii]]; }
        //A_z needs a stencil s.t. interp_limits={-1,1,-1,1,0,1}:
        for(int kk=PLUS0;kk<=PLUS1;kk++) for(int jj=MINUS1;jj<=PLUS1;jj++) for(int ii=MINUS1;ii<=PLUS1;ii++) {
              INTERP_VARS[A_ZI][kk][jj][ii] = in_vars[A_ZI][index_arr_3DB[kk][jj][ii]]; }

        // FIRST DO A^X TERM (interpolate to (i,j+1/2,k+1/2) )
        // \\alpha \\sqrt{\\gamma} A^x = \\alpha psi^6 A^x (RHS of \\partial_i psi6phi)
        // Note that gupij is \\tilde{\\gamma}^{ij}, so we need to multiply by \\psi^{-4}.
        const REAL gupxx_jphkph = avg(INTERP_VARS[GUPXXI], PLUS0,PLUS0, PLUS0,PLUS1, PLUS0,PLUS1);
        const REAL gupxy_jphkph = avg(INTERP_VARS[GUPXYI], PLUS0,PLUS0, PLUS0,PLUS1, PLUS0,PLUS1);
        const REAL gupxz_jphkph = avg(INTERP_VARS[GUPXZI], PLUS0,PLUS0, PLUS0,PLUS1, PLUS0,PLUS1);

        for(int kk=PLUS0;kk<=PLUS1;kk++) for(int jj=PLUS0;jj<=PLUS1;jj++) for(int ii=PLUS0;ii<=PLUS1;ii++) {
              const REAL Psi2 = INTERP_VARS[PSII][kk][jj][ii]*INTERP_VARS[PSII][kk][jj][ii];
              const REAL alpha = INTERP_VARS[LAPM1I][kk][jj][ii]+1.0;
              INTERP_VARS[LAPSE_PSI2I][kk][jj][ii]=alpha*Psi2;
              INTERP_VARS[LAPSE_OVER_PSI6I][kk][jj][ii]=alpha/(Psi2*Psi2*Psi2);
            }

        const REAL lapse_Psi2_jphkph = avg(INTERP_VARS[LAPSE_PSI2I], PLUS0,PLUS0, PLUS0,PLUS1, PLUS0,PLUS1);

        const REAL A_x_jphkph   = avg(INTERP_VARS[A_XI], PLUS0,PLUS0, PLUS0,PLUS0, PLUS0,PLUS0); // @ (i,j+1/2,k+1/2)
        const REAL A_y_jphkph   = avg(INTERP_VARS[A_YI],MINUS1,PLUS0, PLUS0,PLUS1, PLUS0,PLUS0); // @ (i+1/2,j,k+1/2)
        const REAL A_z_jphkph   = avg(INTERP_VARS[A_ZI],MINUS1,PLUS0, PLUS0,PLUS0, PLUS0,PLUS1); // @ (i+1/2,j+1/2,k)

        alpha_sqrtg_Ax_interp[index] = lapse_Psi2_jphkph*
          ( gupxx_jphkph*A_x_jphkph + gupxy_jphkph*A_y_jphkph + gupxz_jphkph*A_z_jphkph );


        // DO A^Y TERM (interpolate to (i+1/2,j,k+1/2) )
        // \\alpha \\sqrt{\\gamma} A^y = \\alpha psi^6 A^y (RHS of \\partial_i psi6phi)
        // Note that gupij is \\tilde{\\gamma}^{ij}, so we need to multiply by \\psi^{-4}.
        const REAL gupxy_iphkph = avg(INTERP_VARS[GUPXYI], PLUS0,PLUS1, PLUS0,PLUS0, PLUS0,PLUS1);
        const REAL gupyy_iphkph = avg(INTERP_VARS[GUPYYI], PLUS0,PLUS1, PLUS0,PLUS0, PLUS0,PLUS1);
        const REAL gupyz_iphkph = avg(INTERP_VARS[GUPYZI], PLUS0,PLUS1, PLUS0,PLUS0, PLUS0,PLUS1);

        const REAL lapse_Psi2_iphkph = avg(INTERP_VARS[LAPSE_PSI2I], PLUS0,PLUS1, PLUS0,PLUS0, PLUS0,PLUS1);
        //REAL lapse_iphkph = avg(INTERP_VARS[LAPM1I], PLUS0,PLUS1, PLUS0,PLUS0, PLUS0,PLUS1)+1.0;
        //REAL psi_iphkph   = avg(INTERP_VARS[PSII  ], PLUS0,PLUS1, PLUS0,PLUS0, PLUS0,PLUS1);

        const REAL A_x_iphkph   = avg(INTERP_VARS[A_XI], PLUS0,PLUS1,MINUS1,PLUS0, PLUS0,PLUS0); // @ (i,j+1/2,k+1/2)
        const REAL A_y_iphkph   = avg(INTERP_VARS[A_YI], PLUS0,PLUS0, PLUS0,PLUS0, PLUS0,PLUS0); // @ (i+1/2,j,k+1/2)
        const REAL A_z_iphkph   = avg(INTERP_VARS[A_ZI], PLUS0,PLUS0,MINUS1,PLUS0, PLUS0,PLUS1); // @ (i+1/2,j+1/2,k)

        alpha_sqrtg_Ay_interp[index] = lapse_Psi2_iphkph*
          ( gupxy_iphkph*A_x_iphkph + gupyy_iphkph*A_y_iphkph + gupyz_iphkph*A_z_iphkph );

        // DO A^Z TERM (interpolate to (i+1/2,j+1/2,k) )
        // \\alpha \\sqrt{\\gamma} A^z = \\alpha psi^6 A^z (RHS of \\partial_i psi6phi)
        // Note that gupij is \\tilde{\\gamma}^{ij}, so we need to multiply by \\psi^{-4}.
        const REAL gupxz_iphjph = avg(INTERP_VARS[GUPXZI], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS0);
        const REAL gupyz_iphjph = avg(INTERP_VARS[GUPYZI], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS0);
        const REAL gupzz_iphjph = avg(INTERP_VARS[GUPZZI], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS0);
        //REAL lapse_iphjph = avg(INTERP_VARS[LAPM1I], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS0)+1.0;
        //REAL psi_iphjph   = avg(INTERP_VARS[PSII  ], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS0);

        const REAL lapse_Psi2_iphjph = avg(INTERP_VARS[LAPSE_PSI2I], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS0);

        const REAL A_x_iphjph   = avg(INTERP_VARS[A_XI], PLUS0,PLUS1, PLUS0,PLUS0,MINUS1,PLUS0); // @ (i,j+1/2,k+1/2)
        const REAL A_y_iphjph   = avg(INTERP_VARS[A_YI], PLUS0,PLUS0, PLUS0,PLUS1,MINUS1,PLUS0); // @ (i+1/2,j,k+1/2)
        const REAL A_z_iphjph   = avg(INTERP_VARS[A_ZI], PLUS0,PLUS0, PLUS0,PLUS0, PLUS0,PLUS0); // @ (i+1/2,j+1/2,k)

        alpha_sqrtg_Az_interp[index] = lapse_Psi2_iphjph*
          ( gupxz_iphjph*A_x_iphjph + gupyz_iphjph*A_y_iphjph + gupzz_iphjph*A_z_iphjph );


        // Next set \\alpha \\Phi - \\beta^j A_j at (i+1/2,j+1/2,k+1/2):
        //   We add a "L" suffix to shifti_iphjphkph to denote "LOCAL", as we set
        //      shifti_iphjphkph[] gridfunction below.
        const REAL shiftx_iphjphkphL = avg(INTERP_VARS[SHIFTXI], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS1);
        const REAL shifty_iphjphkphL = avg(INTERP_VARS[SHIFTYI], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS1);
        const REAL shiftz_iphjphkphL = avg(INTERP_VARS[SHIFTZI], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS1);
        const REAL lapse_over_Psi6_iphjphkphL = avg(INTERP_VARS[LAPSE_OVER_PSI6I], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS1);
        //REAL psi_iphjphkph = avg(INTERP_VARS[PSII  ], PLUS0,PLUS1, PLUS0,PLUS1, PLUS0,PLUS1);
        //REAL psi2_iphjphkph= psi_iphjphkph*psi_iphjphkph;
        //REAL psi6_iphjphkph= psi2_iphjphkph*psi2_iphjphkph*psi2_iphjphkph;
        const REAL A_x_iphjphkph = avg(INTERP_VARS[A_XI], PLUS0,PLUS1, PLUS0,PLUS0, PLUS0,PLUS0); // @ (i,j+1/2,k+1/2)
        const REAL A_y_iphjphkph = avg(INTERP_VARS[A_YI], PLUS0,PLUS0, PLUS0,PLUS1, PLUS0,PLUS0); // @ (i+1/2,j,k+1/2)
        const REAL A_z_iphjphkph = avg(INTERP_VARS[A_ZI], PLUS0,PLUS0, PLUS0,PLUS0, PLUS0,PLUS1); // @ (i+1/2,j+1/2,k)

        alpha_Phi_minus_betaj_A_j_iphjphkph[index] = psi6phi[index]*lapse_over_Psi6_iphjphkphL
          - (shiftx_iphjphkphL*A_x_iphjphkph + shifty_iphjphkphL*A_y_iphjphkph + shiftz_iphjphkphL*A_z_iphjphkph);

        // Finally, save shifti_iphjphkph, for \\partial_j \\beta^j psi6phi
        shiftx_iphjphkph[index]=shiftx_iphjphkphL;
        shifty_iphjphkph[index]=shifty_iphjphkphL;
        shiftz_iphjphkph[index]=shiftz_iphjphkphL;
      }

  // This loop requires two additional ghostzones in every direction. Hence the following loop definition:
#pragma omp parallel for
  for(int k=NGHOSTS;k<Nxx_plus_2NGHOSTS2-NGHOSTS;k++) for(int j=NGHOSTS;j<Nxx_plus_2NGHOSTS1-NGHOSTS;j++) for(int i=NGHOSTS;i<Nxx_plus_2NGHOSTS0-NGHOSTS;i++) {
        const int index = IDX3S(i,j,k);

        // \\partial_t A_i = [reconstructed stuff] + [gauge stuff],
        //    where [gauge stuff] = -\\partial_i (\\alpha \\Phi - \\beta^j A_j)
        const REAL alpha_Phi_minus_betaj_A_j_iphjphkphL = alpha_Phi_minus_betaj_A_j_iphjphkph[index];
        // - partial_i -> - (A_{i} - A_{i-1})/dX = (A_{i-1} - A_{i})/dX, for Ax
        Ax_rhs[index] += invdx0*(alpha_Phi_minus_betaj_A_j_iphjphkph[IDX3S(i-1,j,k)] - alpha_Phi_minus_betaj_A_j_iphjphkphL);
        Ay_rhs[index] += invdx1*(alpha_Phi_minus_betaj_A_j_iphjphkph[IDX3S(i,j-1,k)] - alpha_Phi_minus_betaj_A_j_iphjphkphL);
        Az_rhs[index] += invdx2*(alpha_Phi_minus_betaj_A_j_iphjphkph[IDX3S(i,j,k-1)] - alpha_Phi_minus_betaj_A_j_iphjphkphL);

        // \\partial_t psi6phi = [shift advection term] + \\partial_j (\\alpha \\sqrt{\\gamma} A^j)
        // Here we compute [shift advection term] = \\partial_j (\\beta^j psi6phi)
        // Cache misses are likely more expensive than branch mispredictions here,
        //       which is why we use if() statements and array lookups inside the if()'s.
        REAL psi6phi_rhsL=0.0;
        const REAL psi6phiL=psi6phi[index];
        const REAL shiftx_iphjphkphL=shiftx_iphjphkph[index];
        const REAL shifty_iphjphkphL=shifty_iphjphkph[index];
        const REAL shiftz_iphjphkphL=shiftz_iphjphkph[index];

        // \\partial_x (\\beta^x psi6phi) :
        if(shiftx_iphjphkphL < 0.0) {
          psi6phi_rhsL+=0.5*invdx0*(+    shiftx_iphjphkph[IDX3S(i-2,j,k)]*psi6phi[IDX3S(i-2,j,k)]
                                  -4.0*shiftx_iphjphkph[IDX3S(i-1,j,k)]*psi6phi[IDX3S(i-1,j,k)]
                                  +3.0*shiftx_iphjphkphL*                               psi6phiL);
        } else {
          psi6phi_rhsL+=0.5*invdx0*(-    shiftx_iphjphkph[IDX3S(i+2,j,k)]*psi6phi[IDX3S(i+2,j,k)]
                                  +4.0*shiftx_iphjphkph[IDX3S(i+1,j,k)]*psi6phi[IDX3S(i+1,j,k)]
                                  -3.0*shiftx_iphjphkphL*                               psi6phiL);
        }

        // \\partial_y (\\beta^y psi6phi) :
        if(shifty_iphjphkphL < 0.0) {
          psi6phi_rhsL+=0.5*invdx1*(+    shifty_iphjphkph[IDX3S(i,j-2,k)]*psi6phi[IDX3S(i,j-2,k)]
                                  -4.0*shifty_iphjphkph[IDX3S(i,j-1,k)]*psi6phi[IDX3S(i,j-1,k)]
                                  +3.0*shifty_iphjphkphL*                               psi6phiL);
        } else {
          psi6phi_rhsL+=0.5*invdx1*(-    shifty_iphjphkph[IDX3S(i,j+2,k)]*psi6phi[IDX3S(i,j+2,k)]
                                  +4.0*shifty_iphjphkph[IDX3S(i,j+1,k)]*psi6phi[IDX3S(i,j+1,k)]
                                  -3.0*shifty_iphjphkphL*                               psi6phiL);
        }

        // \\partial_z (\\beta^z psi6phi) :
        if(shiftz_iphjphkphL < 0.0) {
          psi6phi_rhsL+=0.5*invdx2*(+    shiftz_iphjphkph[IDX3S(i,j,k-2)]*psi6phi[IDX3S(i,j,k-2)]
                                  -4.0*shiftz_iphjphkph[IDX3S(i,j,k-1)]*psi6phi[IDX3S(i,j,k-1)]
                                  +3.0*shiftz_iphjphkphL*                               psi6phiL);
        } else {
          psi6phi_rhsL+=0.5*invdx2*(-    shiftz_iphjphkph[IDX3S(i,j,k+2)]*psi6phi[IDX3S(i,j,k+2)]
                                  +4.0*shiftz_iphjphkph[IDX3S(i,j,k+1)]*psi6phi[IDX3S(i,j,k+1)]
                                  -3.0*shiftz_iphjphkphL*                               psi6phiL);
        }

        // Next we add \\partial_j (\\alpha \\sqrt{\\gamma} A^j) to \\partial_t psi6phi:
        psi6phi_rhsL+=invdx0*(alpha_sqrtg_Ax_interp[index] - alpha_sqrtg_Ax_interp[IDX3S(i+1,j,k)])
          +           invdx1*(alpha_sqrtg_Ay_interp[index] - alpha_sqrtg_Ay_interp[IDX3S(i,j+1,k)])
          +           invdx2*(alpha_sqrtg_Az_interp[index] - alpha_sqrtg_Az_interp[IDX3S(i,j,k+1)]);

        // *GENERALIZED* LORENZ GAUGE:
        // Finally, add damping factor to \\partial_t psi6phi
        //subtract lambda * alpha psi^6 Phi
        psi6phi_rhsL+=-xi_damping*alpha_iphjphkph[index]*psi6phiL;

        psi6phi_rhs[index] = psi6phi_rhsL;
      }
}

static inline REAL avg(const REAL f[PLUS2+1][PLUS2+1][PLUS2+1],const int imin,const int imax, const int jmin,const int jmax, const int kmin,const int kmax) {
  REAL retval=0.0,num_in_sum=0.0;
  for(int kk=kmin;kk<=kmax;kk++) for(int jj=jmin;jj<=jmax;jj++) for(int ii=imin;ii<=imax;ii++) {
        retval+=f[kk][jj][ii]; num_in_sum++;
      }
  return retval/num_in_sum;
}
""")
Example #25
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)
Example #26
0
def GiRaFFE_NRPy_FCVAL(Ccodesdir):
    cmd.mkdir(Ccodesdir)
    # Write out the code to a file.
    with open(os.path.join(Ccodesdir,"interpolate_metric_gfs_to_cell_faces.h"),"w") as file:
        file.write("""// Side note: the following values could be used for cell averaged gfs:
//     am2=-1.0/12.0, am1=7.0/12.0, a0=7.0/12.0, a1=-1.0/12.0
// However, since the metric gfs store the grid point values instead of the cell average,
//     the following coefficients should be used:
//     am2 = -1/16, am1 = 9/16, a0 = 9/16, a1 = -1/16
// This will yield the third-order-accurate face values at m-1/2,
//      using values specified at {m-2,m-1,m,m+1}
#define AM2 -0.0625
#define AM1  0.5625
#define A0   0.5625
#define A1  -0.0625
#define COMPUTE_FCVAL(METRICm2,METRICm1,METRIC,METRICp1) (AM2*(METRICm2) + AM1*(METRICm1) + A0*(METRIC) + A1*(METRICp1))

const int metric_gfs_list[10] = {GAMMADD00GF,
                                 GAMMADD01GF,
                                 GAMMADD02GF,
                                 GAMMADD11GF,
                                 GAMMADD12GF,
                                 GAMMADD22GF,
                                 BETAU0GF,
                                 BETAU1GF,
                                 BETAU2GF,
                                 ALPHAGF};

const int metric_gfs_face_list[10] = {GAMMA_FACEDD00GF,
                                      GAMMA_FACEDD01GF,
                                      GAMMA_FACEDD02GF,
                                      GAMMA_FACEDD11GF,
                                      GAMMA_FACEDD12GF,
                                      GAMMA_FACEDD22GF,
                                      BETA_FACEU0GF,
                                      BETA_FACEU1GF,
                                      BETA_FACEU2GF,
                                      ALPHA_FACEGF};

const int num_metric_gfs = 10;
""")

    desc = "Interpolate metric gridfunctions to cell faces"
    name = "interpolate_metric_gfs_to_cell_faces"
    interp_Cfunc = outCfunction(
        outfile  = "returnstring", desc=desc, name=name,
        params   ="const paramstruct *params,REAL *auxevol_gfs,const int flux_dirn",
        preloop  ="""    int in_gf,out_gf;
    REAL Qm2,Qm1,Qp0,Qp1;

""" ,
        body     ="""    for(int gf = 0;gf < num_metric_gfs;gf++) {
        in_gf  = metric_gfs_list[gf];
        out_gf = metric_gfs_face_list[gf];
        for (int i2 = 2;i2 < Nxx_plus_2NGHOSTS2-1;i2++) {
            for (int i1 = 2;i1 < Nxx_plus_2NGHOSTS1-1;i1++) {
                for (int i0 = 2;i0 < Nxx_plus_2NGHOSTS0-1;i0++) {
                    Qm2 = auxevol_gfs[IDX4S(in_gf,i0-2*kronecker_delta[flux_dirn][0],i1-2*kronecker_delta[flux_dirn][1],i2-2*kronecker_delta[flux_dirn][2])];
                    Qm1 = auxevol_gfs[IDX4S(in_gf,i0-kronecker_delta[flux_dirn][0],i1-kronecker_delta[flux_dirn][1],i2-kronecker_delta[flux_dirn][2])];
                    Qp0 = auxevol_gfs[IDX4S(in_gf,i0,i1,i2)];
                    Qp1 = auxevol_gfs[IDX4S(in_gf,i0+kronecker_delta[flux_dirn][0],i1+kronecker_delta[flux_dirn][1],i2+kronecker_delta[flux_dirn][2])];
                    auxevol_gfs[IDX4S(out_gf,i0,i1,i2)] = COMPUTE_FCVAL(Qm2,Qm1,Qp0,Qp1);
                }
            }
        }
    }
""",
    rel_path_to_Cparams=os.path.join("../"))

    with open(os.path.join(Ccodesdir,"interpolate_metric_gfs_to_cell_faces.h"),"a") as file:
        file.write(interp_Cfunc)
def GiRaFFE_NRPy_PPM(Ccodesdir):
    cmd.mkdir(Ccodesdir)
    # Write out the code to a file.
    with open(
            os.path.join(Ccodesdir,
                         "reconstruct_set_of_prims_PPM_GRFFE_NRPy.c"),
            "w") as file:
        file.write("""/*****************************************
 * PPM Reconstruction Interface.
 * Zachariah B. Etienne (2013)
 *
 * This version of PPM implements the standard 
 * Colella & Woodward PPM, but in the GRFFE
 * limit, where P=rho=0. Thus, e.g., ftilde=0.
 *****************************************/

#define MINUS2 0
#define MINUS1 1
#define PLUS0  2
#define PLUS1  3
#define PLUS2  4
#define MAXNUMINDICES 5
//    ^^^^^^^^^^^^^ Be _sure_ to define MAXNUMINDICES appropriately!

#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) )
#define MAX(a,b) ( ((a) > (b)) ? (a) : (b) )
#define SQR(x) ((x) * (x))
// FIXME: Should make this zero-offset for NRPy+ standards. Probably a wrapper function for compatibility with a minimum of other changes?
const int kronecker_delta[4][3] = { { 0,0,0 },
                                    { 1,0,0 },
                                    { 0,1,0 },
                                    { 0,0,1 } };

// You'll find the #define's for LOOP_DEFINE and SET_INDEX_ARRAYS_NRPY inside:
#include "loop_defines_reconstruction_NRPy.h"

static inline REAL slope_limit_NRPy(const REAL dU,const REAL dUp1);
static inline void monotonize_NRPy(const REAL U,REAL Ur,REAL Ul);


static void reconstruct_set_of_prims_PPM_GRFFE_NRPy(const paramstruct *params,REAL *auxevol_gfs,const int flux_dirn,
                                               const int num_prims_to_reconstruct,const int *which_prims_to_reconstruct,
                                               const gf_and_gz_struct *in_prims,gf_and_gz_struct *out_prims_r,
                                               gf_and_gz_struct *out_prims_l,REAL *temporary) {
#include "set_Cparameters.h"
  const int Nxx_plus_2NGHOSTS[3] = {Nxx_plus_2NGHOSTS0,Nxx_plus_2NGHOSTS1,Nxx_plus_2NGHOSTS2};

  REAL U[NUM_RECONSTRUCT_GFS][MAXNUMINDICES],dU[NUM_RECONSTRUCT_GFS][MAXNUMINDICES],slope_lim_dU[NUM_RECONSTRUCT_GFS][MAXNUMINDICES],
    Ur[NUM_RECONSTRUCT_GFS][MAXNUMINDICES],Ul[NUM_RECONSTRUCT_GFS][MAXNUMINDICES];
  int ijkgz_lo_hi[4][2];

  for(int ww=0;ww<num_prims_to_reconstruct;ww++) {
    const int whichvar=which_prims_to_reconstruct[ww];

    if(in_prims[whichvar].gz_lo[flux_dirn]!=0 || in_prims[whichvar].gz_hi[flux_dirn]!=0) {
      printf("TOO MANY GZ'S! WHICHVAR=%d: %d %d %d : %d %d %d DIRECTION %d",whichvar,
		  in_prims[whichvar].gz_lo[1],in_prims[whichvar].gz_lo[2],in_prims[whichvar].gz_lo[3],
		  in_prims[whichvar].gz_hi[1],in_prims[whichvar].gz_hi[2],in_prims[whichvar].gz_hi[3],flux_dirn);
      exit(0);  
    }
    

    // *** LOOP 1: Interpolate to Ur and Ul, which are face values ***
    //  You will find that Ur depends on U at MINUS1,PLUS0, PLUS1,PLUS2, and
    //                     Ul depends on U at MINUS2,MINUS1,PLUS0,PLUS1.
    //  However, we define the below loop from MINUS2 to PLUS2. Why not split
    //     this up and get additional points? Maybe we should. In GRMHD, the
    //     reason is that later on, Ur and Ul depend on ftilde, which is
    //     defined from MINUS2 to PLUS2, so we would lose those points anyway.
    //     But in GRFFE, ftilde is set to zero, so there may be a potential
    //     for boosting performance here.
    LOOP_DEFINE(2,2,  Nxx_plus_2NGHOSTS,flux_dirn,  ijkgz_lo_hi,in_prims[whichvar].gz_lo,in_prims[whichvar].gz_hi) {
      SET_INDEX_ARRAYS_NRPY(-2,2,flux_dirn);
      /* *** LOOP 1a: READ INPUT *** */
      // Read in a primitive at all gridpoints between m = MINUS2 & PLUS2, where m's direction is given by flux_dirn. Store to U. 
      for(int ii=MINUS2;ii<=PLUS2;ii++) U[whichvar][ii] = in_prims[whichvar].gf[index_arr[flux_dirn][ii]];
      
      /* *** LOOP 1b: DO COMPUTATION *** */
      /* First, compute simple dU = U(i) - U(i-1), where direction of i 
       *         is given by flux_dirn, and U is a primitive variable: 
       *         {vx,vy,vz,Bx,By,Bz}. */
      // Note that for Ur and Ul at i, we must compute dU(i-1),dU(i),dU(i+1), 
      //         and dU(i+2)
      dU[whichvar][MINUS1] = U[whichvar][MINUS1]- U[whichvar][MINUS2];  
      dU[whichvar][PLUS0]  = U[whichvar][PLUS0] - U[whichvar][MINUS1]; 
      dU[whichvar][PLUS1]  = U[whichvar][PLUS1] - U[whichvar][PLUS0];  
      dU[whichvar][PLUS2]  = U[whichvar][PLUS2] - U[whichvar][PLUS1];  

      // Then, compute slope-limited dU, using MC slope limiter:
      slope_lim_dU[whichvar][MINUS1]=slope_limit_NRPy(dU[whichvar][MINUS1],dU[whichvar][PLUS0]);
      slope_lim_dU[whichvar][PLUS0] =slope_limit_NRPy(dU[whichvar][PLUS0], dU[whichvar][PLUS1]);
      slope_lim_dU[whichvar][PLUS1] =slope_limit_NRPy(dU[whichvar][PLUS1], dU[whichvar][PLUS2]);

      // Finally, compute face values Ur and Ul based on the PPM prescription 
      //   (Eq. A1 in http://arxiv.org/pdf/astro-ph/0503420.pdf, but using standard 1/6=(1.0/6.0) coefficient)
      // Ur[PLUS0] represents U(i+1/2)
      // We applied a simplification to the following line: Ur=U+0.5*(U(i+1)-U) + ... = 0.5*(U(i+1)+U) + ...
      Ur[whichvar][PLUS0] = 0.5*(U[whichvar][PLUS1] + U[whichvar][PLUS0] ) + (1.0/6.0)*(slope_lim_dU[whichvar][PLUS0]  - slope_lim_dU[whichvar][PLUS1]);
      // Ul[PLUS0] represents U(i-1/2)
      // We applied a simplification to the following line: Ul=U(i-1)+0.5*(U-U(i-1)) + ... = 0.5*(U+U(i-1)) + ...
      Ul[whichvar][PLUS0] = 0.5*(U[whichvar][PLUS0] + U[whichvar][MINUS1]) + (1.0/6.0)*(slope_lim_dU[whichvar][MINUS1] - slope_lim_dU[whichvar][PLUS0]);

      /* *** LOOP 1c: WRITE OUTPUT *** */
      // Store right face values to {vxr,vyr,vzr,Bxr,Byr,Bzr},
      //    and left face values to {vxl,vyl,vzl,Bxl,Byl,Bzl}
      out_prims_r[whichvar].gf[index_arr[flux_dirn][PLUS0]] = Ur[whichvar][PLUS0];
      out_prims_l[whichvar].gf[index_arr[flux_dirn][PLUS0]] = Ul[whichvar][PLUS0];      
    }

    // *** LOOP 2 (REMOVED): STEEPEN RHOB. RHOB DOES NOT EXIST IN GRFFE EQUATIONS ***
  }

  // *** LOOP 3: FLATTEN BASED ON FTILDE AND MONOTONIZE ***
  for(int ww=0;ww<num_prims_to_reconstruct;ww++) {
    const int whichvar=which_prims_to_reconstruct[ww];
    // ftilde() depends on P(MINUS2,MINUS1,PLUS1,PLUS2), THUS IS SET TO ZERO IN GRFFE
    LOOP_DEFINE(2,2,  Nxx_plus_2NGHOSTS,flux_dirn,  ijkgz_lo_hi,in_prims[whichvar].gz_lo,in_prims[whichvar].gz_hi) {
      SET_INDEX_ARRAYS_NRPY(0,0,flux_dirn);
      
      U[whichvar][PLUS0]  = in_prims[whichvar].gf[index_arr[flux_dirn][PLUS0]];
      Ur[whichvar][PLUS0] = out_prims_r[whichvar].gf[index_arr[flux_dirn][PLUS0]];
      Ul[whichvar][PLUS0] = out_prims_l[whichvar].gf[index_arr[flux_dirn][PLUS0]];

      // ftilde_gf was computed in the function compute_ftilde_gf(), called before this routine
      //REAL ftilde = ftilde_gf[index_arr[flux_dirn][PLUS0]];
      // ...and then flatten (local operation)
      Ur[whichvar][PLUS0]   = Ur[whichvar][PLUS0];
      Ul[whichvar][PLUS0]   = Ul[whichvar][PLUS0];

      // Then monotonize
      monotonize_NRPy(U[whichvar][PLUS0],Ur[whichvar][PLUS0],Ul[whichvar][PLUS0]);

      out_prims_r[whichvar].gf[index_arr[flux_dirn][PLUS0]] = Ur[whichvar][PLUS0];
      out_prims_l[whichvar].gf[index_arr[flux_dirn][PLUS0]] = Ul[whichvar][PLUS0];
    }
    // Note: ftilde=0 in GRFFE. Ur depends on ftilde, which depends on points of U between MINUS2 and PLUS2
    out_prims_r[whichvar].gz_lo[flux_dirn]+=2; 
    out_prims_r[whichvar].gz_hi[flux_dirn]+=2;
    // Note: ftilde=0 in GRFFE. Ul depends on ftilde, which depends on points of U between MINUS2 and PLUS2
    out_prims_l[whichvar].gz_lo[flux_dirn]+=2;
    out_prims_l[whichvar].gz_hi[flux_dirn]+=2;
  }

// *** LOOP 4: SHIFT Ur AND Ul ***
  /* Currently face values are set so that
   *      a) Ur(i) represents U(i+1/2), and
   *      b) Ul(i) represents U(i-1/2)
   *    Here, we shift so that the indices are consistent:
   *      a) U(i-1/2+epsilon) = oldUl(i)   = newUr(i)
   *      b) U(i-1/2-epsilon) = oldUr(i-1) = newUl(i)
   *    Note that this step is not strictly necessary if you keep
   *      track of indices when computing the flux. */
  for(int ww=0;ww<num_prims_to_reconstruct;ww++) {
    const int whichvar=which_prims_to_reconstruct[ww];
    LOOP_DEFINE(3,2,  Nxx_plus_2NGHOSTS,flux_dirn,  ijkgz_lo_hi,in_prims[whichvar].gz_lo,in_prims[whichvar].gz_hi) {
      SET_INDEX_ARRAYS_NRPY(-1,0,flux_dirn);
      temporary[index_arr[flux_dirn][PLUS0]] = out_prims_r[whichvar].gf[index_arr[flux_dirn][MINUS1]];
    }

    LOOP_DEFINE(3,2,  Nxx_plus_2NGHOSTS,flux_dirn,  ijkgz_lo_hi,in_prims[whichvar].gz_lo,in_prims[whichvar].gz_hi) {
      SET_INDEX_ARRAYS_NRPY(0,0,flux_dirn);
      // Then shift so that Ur represents the gridpoint at i-1/2+epsilon, 
      //                and Ul represents the gridpoint at i-1/2-epsilon.
      // Ur(i-1/2) = Ul(i-1/2)     = U(i-1/2+epsilon)
      // Ul(i-1/2) = Ur(i+1/2 - 1) = U(i-1/2-epsilon)
      out_prims_r[whichvar].gf[index_arr[flux_dirn][PLUS0]] = out_prims_l[whichvar].gf[index_arr[flux_dirn][PLUS0]];
      out_prims_l[whichvar].gf[index_arr[flux_dirn][PLUS0]] = temporary[index_arr[flux_dirn][PLUS0]];
    }
    // Ul was just shifted, so we lost another ghostzone.
    out_prims_l[whichvar].gz_lo[flux_dirn]+=1;
    out_prims_l[whichvar].gz_hi[flux_dirn]+=0;
    // As for Ur, we didn't need to get rid of another ghostzone, 
    //    but we did ... seems wasteful!
    out_prims_r[whichvar].gz_lo[flux_dirn]+=1;
    out_prims_r[whichvar].gz_hi[flux_dirn]+=0;

  }
}

// Set SLOPE_LIMITER_COEFF = 2.0 for MC, 1 for minmod
#define SLOPE_LIMITER_COEFF 2.0

//Eq. 60 in JOURNAL OF COMPUTATIONAL PHYSICS 123, 1-14 (1996) 
//   [note the factor of 2 missing in the |a_{j+1} - a_{j}| term]. 
//   Recall that dU = U_{i} - U_{i-1}.
static inline REAL slope_limit_NRPy(const REAL dU,const REAL dUp1) {
  if(dU*dUp1 > 0.0) {
    //delta_m_U=0.5 * [ (u_(i+1)-u_i) + (u_i-u_(i-1)) ] = (u_(i+1) - u_(i-1))/2  <-- first derivative, second-order; this should happen most of the time (smooth flows)
    const REAL delta_m_U = 0.5*(dU + dUp1);
    // EXPLANATION OF BELOW LINE OF CODE.
    // In short, sign_delta_a_j = sign(delta_m_U) = (0.0 < delta_m_U) - (delta_m_U < 0.0).
    //    If delta_m_U>0, then (0.0 < delta_m_U)==1, and (delta_m_U < 0.0)==0, so sign_delta_a_j=+1
    //    If delta_m_U<0, then (0.0 < delta_m_U)==0, and (delta_m_U < 0.0)==1, so sign_delta_a_j=-1
    //    If delta_m_U==0,then (0.0 < delta_m_U)==0, and (delta_m_U < 0.0)==0, so sign_delta_a_j=0
    const int sign_delta_m_U = (0.0 < delta_m_U) - (delta_m_U < 0.0);
    //Decide whether to use 2nd order derivative or first-order derivative, limiting slope.
    return sign_delta_m_U*MIN(fabs(delta_m_U),MIN(SLOPE_LIMITER_COEFF*fabs(dUp1),SLOPE_LIMITER_COEFF*fabs(dU)));
  }
  return 0.0;
}

static inline void monotonize_NRPy(const REAL U,REAL Ur,REAL Ul) {
  const REAL dU = Ur - Ul;
  const REAL mU = 0.5*(Ur+Ul);
  
  if ( (Ur-U)*(U-Ul) <= 0.0) { 
    Ur = U;
    Ul = U;
    return;
  }
  if ( dU*(U-mU) > (1.0/6.0)*SQR(dU)) { 
    Ul = 3.0*U - 2.0*Ur;
    return;
  }
  if ( dU*(U-mU) < -(1.0/6.0)*SQR(dU)) {
    Ur = 3.0*U - 2.0*Ul;
    return;
  }
}
""")

    with open(os.path.join(Ccodesdir, "loop_defines_reconstruction_NRPy.h"),
              "w") as file:
        file.write("""#ifndef loop_defines_reconstruction_NRPy_H_
#define loop_defines_reconstruction_NRPy_H_

#define LOOP_DEFINE(gz_shift_lo,gz_shift_hi,  ext,flux_dirn,  ijkgz_lo_hi,gz_lo,gz_hi) \\
  for(int rr=1;rr<=3;rr++) {                                            \\
    ijkgz_lo_hi[rr][0]=          gz_lo[rr];                             \\
    ijkgz_lo_hi[rr][1]=ext[rr-1]-gz_hi[rr];                             \\
  }                                                                     \\
  ijkgz_lo_hi[flux_dirn][0] += gz_shift_lo;                             \\
  ijkgz_lo_hi[flux_dirn][1] -= gz_shift_hi;                             \\
  /* The following line is valid C99 */                                 \\
  _Pragma("omp parallel for private(U,dU,slope_lim_dU,Ur,Ul)")          \\
  for(int k=ijkgz_lo_hi[3][0];k<ijkgz_lo_hi[3][1];k++)                  \\
    for(int j=ijkgz_lo_hi[2][0];j<ijkgz_lo_hi[2][1];j++)                \\
      for(int i=ijkgz_lo_hi[1][0];i<ijkgz_lo_hi[1][1];i++)

// This define only sets indices. 
// FIXME: benchmark with and without the if() statement.
// FIXME: try without index_arr being defined in all directions.
#define SET_INDEX_ARRAYS_NRPY(IMIN,IMAX,flux_dirn)                           \\
  const int max_shift=(MAXNUMINDICES/2);                                \\
  /* DEBUGGING ONLY:  if(IMIN<-max_shift || IMAX>max_shift) CCTK_VError(VERR_DEF_PARAMS,"FIX MAXNUMINDICES!"); */ \\
  int index_arr[4][MAXNUMINDICES];                                      \\
  for(int idx=IMIN;idx<=IMAX;idx++) {                                   \\
    index_arr[flux_dirn][idx+max_shift]=                                \\
      IDX3S(                                                            \\
            i+idx*kronecker_delta[flux_dirn][0],                        \\
            j+idx*kronecker_delta[flux_dirn][1],                        \\
            k+idx*kronecker_delta[flux_dirn][2]);                       \\
  }

#define SET_INDEX_ARRAYS_NRPY_3DBLOCK(IJKLOHI)                               \\
  const int max_shift=(MAXNUMINDICES/2);                                \\
  int index_arr_3DB[MAXNUMINDICES][MAXNUMINDICES][MAXNUMINDICES];       \\
  for(int idx_k=IJKLOHI[4];idx_k<=IJKLOHI[5];idx_k++) for(int idx_j=IJKLOHI[2];idx_j<=IJKLOHI[3];idx_j++) for(int idx_i=IJKLOHI[0];idx_i<=IJKLOHI[1];idx_i++) { \\
        index_arr_3DB[idx_k+max_shift][idx_j+max_shift][idx_i+max_shift]=CCTK_GFINDEX3D(cctkGH,i+idx_i,j+idx_j,k+idx_k); \\
      }

#endif /* loop_defines_reconstruction_NRPy_H_ */
""")
Example #28
0
def GiRaFFE_NRPy_A2B(outdir):
    cmd.mkdir(outdir)
    # Set spatial dimension (must be 3 for BSSN)
    DIM = 3
    par.set_parval_from_str("grid::DIM", DIM)
    # Register the gridfunction gammadet. This determinant will be calculated separately
    gammadet = gri.register_gridfunctions("AUXEVOL", "gammadet")

    # Import the Levi-Civita symbol and build the corresponding tensor.
    # We already have a handy function to define the Levi-Civita symbol in WeylScalars
    import WeylScal4NRPy.WeylScalars_Cartesian as weyl
    LeviCivitaDDD = weyl.define_LeviCivitaSymbol_rank3()
    LeviCivitaUUU = ixp.zerorank3()
    for i in range(DIM):
        for j in range(DIM):
            for k in range(DIM):
                LCijk = LeviCivitaDDD[i][j][k]
                #LeviCivitaDDD[i][j][k] = LCijk * sp.sqrt(gho.gammadet)
                LeviCivitaUUU[i][j][k] = LCijk / sp.sqrt(gammadet)
    # Step 1.a: A useful function
    AD = ixp.register_gridfunctions_for_single_rank1("EVOL", "AD")
    BU = ixp.register_gridfunctions_for_single_rank1("AUXEVOL", "BU")
    AD_dD = ixp.declarerank2("AD_dD", "nosym")
    BU = ixp.zerorank1(
    )  # BU is already registered as a gridfunction, but we need to zero its values and declare it in this scope.

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

    # 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 REAL sqrtgammadet = sqrt(auxevol_gfs[IDX4S(GAMMADETGF, i0,i1,i2)]);
        auxevol_gfs[IDX4S(BU0GF, i0,i1,i2)] = (dy_Az-dz_Ay)/sqrtgammadet;
        auxevol_gfs[IDX4S(BU1GF, i0,i1,i2)] = (dz_Ax-dx_Az)/sqrtgammadet;
        auxevol_gfs[IDX4S(BU2GF, i0,i1,i2)] = (dx_Ay-dy_Ax)/sqrtgammadet;
    }
}
""")

    # 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])],
                                  params="outCverbose=False").replace("IDX4","IDX4S"),
        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").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)