Example #1
0
def single_RK_substep(commentblock, RHS_str, RHS_input_str, RHS_output_str, RK_lhss_list, RK_rhss_list,
                      post_RHS_list, post_RHS_output_list, indent="  "):
    addl_indent = ""
    return_str  = commentblock + "\n"
    if not isinstance(RK_lhss_list, list):
        RK_lhss_list = [RK_lhss_list]
    if not isinstance(RK_rhss_list, list):
        RK_rhss_list = [RK_rhss_list]

    if not isinstance(post_RHS_list, list):
        post_RHS_list = [post_RHS_list]
    if not isinstance(post_RHS_output_list, list):
        post_RHS_output_list = [post_RHS_output_list]

    # Part 1: RHS evaluation:
    return_str += indent_Ccode(RHS_str.replace("RK_INPUT_GFS",  RHS_input_str).
                                       replace("RK_OUTPUT_GFS", RHS_output_str)+"\n", indent=addl_indent)

    # Part 2: RK update
    return_str += addl_indent + "LOOP_ALL_GFS_GPS(i) {\n"
    for lhs, rhs in zip(RK_lhss_list, RK_rhss_list):
        return_str += addl_indent + indent + lhs + "[i] = " + rhs + ";\n"
    return_str += addl_indent + "}\n"

    # Part 3: Call post-RHS functions
    for post_RHS, post_RHS_output in zip(post_RHS_list, post_RHS_output_list):
        return_str += indent_Ccode(post_RHS.replace("RK_OUTPUT_GFS", post_RHS_output), indent=addl_indent)

    return return_str
Example #2
0
def add_to_Cfunction_dict_MoL_malloc(MoL_method, which_gfs):
    includes = ["NRPy_basic_defines.h", "NRPy_function_prototypes.h"]
    desc = "Method of Lines (MoL) for \"" + MoL_method + "\" method: Allocate memory for \"" + which_gfs + "\" gridfunctions\n"
    desc += "   * y_n_gfs are used to store data for the vector of gridfunctions y_i at t_n, at the start of each MoL timestep\n"
    desc += "   * non_y_n_gfs are needed for intermediate (e.g., k_i) storage in chosen MoL method\n"
    c_type = "void"

    y_n_gridfunctions, non_y_n_gridfunctions_list, diagnostic_gridfunctions_point_to = \
        generate_gridfunction_names(MoL_method = MoL_method)

    gridfunctions_list = []
    if which_gfs == "y_n_gfs":
        gridfunctions_list = [y_n_gridfunctions]
    elif which_gfs == "non_y_n_gfs":
        gridfunctions_list = non_y_n_gridfunctions_list
    else:
        print("ERROR: which_gfs = \"" + which_gfs + "\" unrecognized.")
        sys.exit(1)
    name = "MoL_malloc_" + which_gfs
    params = "const paramstruct *restrict params, MoL_gridfunctions_struct *restrict gridfuncs"
    body = "const int Nxx_plus_2NGHOSTS_tot = Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2;\n"
    for gridfunctions in gridfunctions_list:
        num_gfs = "NUM_EVOL_GFS"
        if gridfunctions == "auxevol_gfs":
            num_gfs = "NUM_AUXEVOL_GFS"
        body += "gridfuncs->" + gridfunctions + " = (REAL *restrict)malloc(sizeof(REAL) * " + num_gfs + " * Nxx_plus_2NGHOSTS_tot);\n"
    body += "\ngridfuncs->diagnostic_output_gfs = gridfuncs->" + diagnostic_gridfunctions_point_to + ";\n"
    add_to_Cfunction_dict(includes=includes,
                          desc=desc,
                          c_type=c_type,
                          name=name,
                          params=params,
                          body=indent_Ccode(body, "  "),
                          rel_path_to_Cparams=os.path.join("."))
Example #3
0
def add_to_Cfunction_dict_MoL_free_memory(MoL_method, which_gfs):
    includes = ["NRPy_basic_defines.h", "NRPy_function_prototypes.h"]
    desc = "Method of Lines (MoL) for \"" + MoL_method + "\" method: Free memory for \"" + which_gfs + "\" gridfunctions\n"
    desc += "   - y_n_gfs are used to store data for the vector of gridfunctions y_i at t_n, at the start of each MoL timestep\n"
    desc += "   - non_y_n_gfs are needed for intermediate (e.g., k_i) storage in chosen MoL method\n"
    c_type = "void"

    y_n_gridfunctions, non_y_n_gridfunctions_list, diagnostic_gridfunctions_point_to = \
        generate_gridfunction_names(MoL_method=MoL_method)

    gridfunctions_list = []
    if which_gfs == "y_n_gfs":
        gridfunctions_list = [y_n_gridfunctions]
    elif which_gfs == "non_y_n_gfs":
        gridfunctions_list = non_y_n_gridfunctions_list
    else:
        print("ERROR: which_gfs = \"" + which_gfs + "\" unrecognized.")
        sys.exit(1)
    name = "MoL_free_memory_" + which_gfs
    params = "const paramstruct *restrict params, MoL_gridfunctions_struct *restrict gridfuncs"
    body = ""
    for gridfunctions in gridfunctions_list:
        body += "    free(gridfuncs->" + gridfunctions + ");\n"
    add_to_Cfunction_dict(includes=includes,
                          desc=desc,
                          c_type=c_type,
                          name=name,
                          params=params,
                          body=indent_Ccode(body, "  "),
                          rel_path_to_Cparams=os.path.join("."))
Example #4
0
def add_to_Cfunction_dict_MoL_step_forward_in_time(MoL_method,
                                                   RHS_string="",
                                                   post_RHS_string="",
                                                   post_post_RHS_string="",
                                                   enable_rfm=False,
                                                   enable_curviBCs=False,
                                                   enable_SIMD=False,
                                                   enable_griddata=False):
    includes = ["NRPy_basic_defines.h", "NRPy_function_prototypes.h"]
    if enable_SIMD:
        includes += [os.path.join("SIMD", "SIMD_intrinsics.h")]
    desc = "Method of Lines (MoL) for \"" + MoL_method + "\" method: Step forward one full timestep.\n"
    c_type = "void"
    name = "MoL_step_forward_in_time"
    if enable_griddata:
        params = "griddata_struct *restrict griddata, const REAL dt"
    else:
        params = "const paramstruct *restrict params, "
        if enable_rfm:
            params += "const rfm_struct *restrict rfmstruct, "
        else:
            params += "REAL *restrict xx[3], "
        if enable_curviBCs:
            params += "const bc_struct *restrict bcstruct, "
        params += "MoL_gridfunctions_struct *restrict gridfuncs, const REAL dt"

    indent = ""  # We don't bother with an indent here.

    body = indent + "// C code implementation of -={ " + MoL_method + " }=- Method of Lines timestepping.\n\n"

    y_n_gridfunctions, non_y_n_gridfunctions_list, _throwaway = generate_gridfunction_names(
        MoL_method)
    if enable_griddata:
        gf_prefix = "griddata->gridfuncs."
    else:
        gf_prefix = "gridfuncs->"
    gf_aliases = """// Set gridfunction aliases from gridfuncs struct
REAL *restrict """ + y_n_gridfunctions + " = " + gf_prefix + y_n_gridfunctions + """;  // y_n gridfunctions
// Temporary timelevel & AUXEVOL gridfunctions:\n"""
    for gf in non_y_n_gridfunctions_list:
        gf_aliases += "REAL *restrict " + gf + " = " + gf_prefix + gf + ";\n"
    if enable_griddata:
        gf_aliases += "paramstruct *restrict params = &griddata->params;\n"
        if enable_rfm:
            gf_aliases += "const rfm_struct *restrict rfmstruct = &griddata->rfmstruct;\n"
        else:
            gf_aliases += "REAL * xx[3]; for(int ww=0;ww<3;ww++) xx[ww] = griddata->xx[ww];\n"
        gf_aliases += "const bc_struct *restrict bcstruct = &griddata->bcstruct;\n"
        for i in ["0", "1", "2"]:
            gf_aliases += "const int Nxx_plus_2NGHOSTS" + i + " = griddata->params.Nxx_plus_2NGHOSTS" + i + ";\n"

    if not enable_griddata:
        body += gf_aliases

    # Implement Method of Lines (MoL) Timestepping
    Butcher = Butcher_dict[MoL_method][
        0]  # Get the desired Butcher table from the dictionary
    num_steps = len(
        Butcher) - 1  # Specify the number of required steps to update solution

    # Diagonal RK3 only!!!
    dt = sp.Symbol("dt", real=True)
    if diagonal(MoL_method) and "RK3" in MoL_method:
        #  In a diagonal RK3 method, only 3 gridfunctions need be defined. Below implements this approach.
        y_n_gfs = sp.Symbol("y_n_gfsL", real=True)
        k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs = sp.Symbol(
            "k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfsL", real=True)
        k2_or_y_nplus_a32_k2_gfs = sp.Symbol("k2_or_y_nplus_a32_k2_gfsL",
                                             real=True)
        # k_1
        body += """
    // In a diagonal RK3 method like this one, only 3 gridfunctions need be defined. Below implements this approach.
    // Using y_n_gfs as input, k1 and apply boundary conditions\n"""
        body += single_RK_substep_input_symbolic(
            commentblock="""// -={ START k1 substep }=-
    // RHS evaluation:
    //  1. We will store k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs now as
    //     ...  the update for the next rhs evaluation y_n + a21*k1*dt
    // Post-RHS evaluation:
    //  1. Apply post-RHS to y_n + a21*k1*dt""",
            RHS_str=RHS_string,
            RHS_input_str=y_n_gfs,
            RHS_output_str=k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs,
            RK_lhss_list=[k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs],
            RK_rhss_list=[
                Butcher[1][1] *
                k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs * dt +
                y_n_gfs
            ],
            post_RHS_list=[post_RHS_string],
            post_RHS_output_list=[
                k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs
            ],
            enable_SIMD=enable_SIMD,
            enable_griddata=enable_griddata,
            gf_aliases=gf_aliases,
            post_post_RHS_string=post_post_RHS_string
        ) + "// -={ END k1 substep }=-\n\n"

        # k_2
        body += single_RK_substep_input_symbolic(
            commentblock="""// -={ START k2 substep }=-
    // RHS evaluation:
    //    1. Reassign k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs to be the running total y_{n+1}; a32*k2*dt to the running total
    //    2. Store k2_or_y_nplus_a32_k2_gfs now as y_n + a32*k2*dt
    // Post-RHS evaluation:
    //    1. Apply post-RHS to both y_n + a32*k2 (stored in k2_or_y_nplus_a32_k2_gfs)
    //       ... and the y_{n+1} running total, as they have not been applied yet to k2-related gridfunctions""",
            RHS_str=RHS_string,
            RHS_input_str=k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs,
            RHS_output_str=k2_or_y_nplus_a32_k2_gfs,
            RK_lhss_list=[
                k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs,
                k2_or_y_nplus_a32_k2_gfs
            ],
            RK_rhss_list=[
                Butcher[3][1] *
                (k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs - y_n_gfs)
                / Butcher[1][1] + y_n_gfs +
                Butcher[3][2] * k2_or_y_nplus_a32_k2_gfs * dt,
                Butcher[2][2] * k2_or_y_nplus_a32_k2_gfs * dt + y_n_gfs
            ],
            post_RHS_list=[post_RHS_string, post_RHS_string],
            post_RHS_output_list=[
                k2_or_y_nplus_a32_k2_gfs,
                k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs
            ],
            enable_SIMD=enable_SIMD,
            enable_griddata=enable_griddata,
            gf_aliases=gf_aliases,
            post_post_RHS_string=post_post_RHS_string
        ) + "// -={ END k2 substep }=-\n\n"

        # k_3
        body += single_RK_substep_input_symbolic(
            commentblock="""// -={ START k3 substep }=-
        // RHS evaluation:
        //    1. Add k3 to the running total and save to y_n
        // Post-RHS evaluation:
        //    1. Apply post-RHS to y_n""",
            RHS_str=RHS_string,
            RHS_input_str=k2_or_y_nplus_a32_k2_gfs,
            RHS_output_str=y_n_gfs,
            RK_lhss_list=[y_n_gfs],
            RK_rhss_list=[
                k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs +
                Butcher[3][3] * y_n_gfs * dt
            ],
            post_RHS_list=[post_RHS_string],
            post_RHS_output_list=[y_n_gfs],
            enable_SIMD=enable_SIMD,
            enable_griddata=enable_griddata,
            gf_aliases=gf_aliases,
            post_post_RHS_string=post_post_RHS_string
        ) + "// -={ END k3 substep }=-\n\n"
    else:
        y_n = sp.Symbol("y_n_gfsL", real=True)
        if not diagonal(MoL_method):
            for s in range(num_steps):
                next_y_input = sp.Symbol("next_y_input_gfsL", real=True)

                # If we're on the first step (s=0), we use y_n gridfunction as input.
                #      Otherwise next_y_input is input. Output is just the reverse.
                if s == 0:  # If on first step:
                    RHS_input = y_n
                else:  # If on second step or later:
                    RHS_input = next_y_input
                RHS_output = sp.Symbol("k" + str(s + 1) + "_gfs", real=True)
                if s == num_steps - 1:  # If on final step:
                    RK_lhs = y_n
                else:  # If on anything but the final step:
                    RK_lhs = next_y_input
                RK_rhs = y_n
                for m in range(s + 1):
                    k_mp1_gfs = sp.Symbol("k" + str(m + 1) + "_gfsL")
                    if Butcher[s + 1][m + 1] != 0:
                        if Butcher[s + 1][m + 1] != 1:
                            RK_rhs += dt * k_mp1_gfs * Butcher[s + 1][m + 1]
                        else:
                            RK_rhs += dt * k_mp1_gfs

                post_RHS = post_RHS_string
                if s == num_steps - 1:  # If on final step:
                    post_RHS_output = y_n
                else:  # If on anything but the final step:
                    post_RHS_output = next_y_input

                body += single_RK_substep_input_symbolic(
                    commentblock="// -={ START k" + str(s + 1) +
                    " substep }=-",
                    RHS_str=RHS_string,
                    RHS_input_str=RHS_input,
                    RHS_output_str=RHS_output,
                    RK_lhss_list=[RK_lhs],
                    RK_rhss_list=[RK_rhs],
                    post_RHS_list=[post_RHS],
                    post_RHS_output_list=[post_RHS_output],
                    enable_SIMD=enable_SIMD,
                    enable_griddata=enable_griddata,
                    gf_aliases=gf_aliases,
                    post_post_RHS_string=post_post_RHS_string
                ) + "// -={ END k" + str(s + 1) + " substep }=-\n\n"
        else:
            y_n = sp.Symbol("y_n_gfsL", real=True)
            y_nplus1_running_total = sp.Symbol("y_nplus1_running_total_gfsL",
                                               real=True)
            if MoL_method == 'Euler':  # Euler's method doesn't require any k_i, and gets its own unique algorithm
                body += single_RK_substep_input_symbolic(
                    commentblock=indent +
                    "// ***Euler timestepping only requires one RHS evaluation***",
                    RHS_str=RHS_string,
                    RHS_input_str=y_n,
                    RHS_output_str=y_nplus1_running_total,
                    RK_lhss_list=[y_n],
                    RK_rhss_list=[y_n + y_nplus1_running_total * dt],
                    post_RHS_list=[post_RHS_string],
                    post_RHS_output_list=[y_n],
                    enable_SIMD=enable_SIMD,
                    enable_griddata=enable_griddata,
                    gf_aliases=gf_aliases,
                    post_post_RHS_string=post_post_RHS_string)
            else:
                for s in range(num_steps):
                    # If we're on the first step (s=0), we use y_n gridfunction as input.
                    # and k_odd as output.
                    if s == 0:
                        RHS_input = sp.Symbol("y_n_gfsL", real=True)
                        RHS_output = sp.Symbol("k_odd_gfsL", real=True)
                    # For the remaining steps the inputs and ouputs alternate between k_odd and k_even
                    elif s % 2 == 0:
                        RHS_input = sp.Symbol("k_even_gfsL", real=True)
                        RHS_output = sp.Symbol("k_odd_gfsL", real=True)
                    else:
                        RHS_input = sp.Symbol("k_odd_gfsL", real=True)
                        RHS_output = sp.Symbol("k_even_gfsL", real=True)
                    RK_lhs_list = []
                    RK_rhs_list = []
                    if s != num_steps - 1:  # For anything besides the final step
                        if s == 0:  # The first RK step
                            RK_lhs_list.append(y_nplus1_running_total)
                            RK_rhs_list.append(RHS_output * dt *
                                               Butcher[num_steps][s + 1])

                            RK_lhs_list.append(RHS_output)
                            RK_rhs_list.append(y_n + RHS_output * dt *
                                               Butcher[s + 1][s + 1])
                        else:
                            if Butcher[num_steps][s + 1] != 0:
                                RK_lhs_list.append(y_nplus1_running_total)
                                if Butcher[num_steps][s + 1] != 1:
                                    RK_rhs_list.append(
                                        y_nplus1_running_total + RHS_output *
                                        dt * Butcher[num_steps][s + 1])
                                else:
                                    RK_rhs_list.append(y_nplus1_running_total +
                                                       RHS_output * dt)
                            if Butcher[s + 1][s + 1] != 0:
                                RK_lhs_list.append(RHS_output)
                                if Butcher[s + 1][s + 1] != 1:
                                    RK_rhs_list.append(y_n + RHS_output * dt *
                                                       Butcher[s + 1][s + 1])
                                else:
                                    RK_rhs_list.append(y_n + RHS_output * dt)
                        post_RHS_output = RHS_output
                    if s == num_steps - 1:  # If on the final step
                        if Butcher[num_steps][s + 1] != 0:
                            RK_lhs_list.append(y_n)
                            if Butcher[num_steps][s + 1] != 1:
                                RK_rhs_list.append(y_n +
                                                   y_nplus1_running_total +
                                                   RHS_output * dt *
                                                   Butcher[num_steps][s + 1])
                            else:
                                RK_rhs_list.append(y_n +
                                                   y_nplus1_running_total +
                                                   RHS_output * dt)
                        post_RHS_output = y_n
                    body += single_RK_substep_input_symbolic(
                        commentblock=indent + "// -={ START k" + str(s + 1) +
                        " substep }=-",
                        RHS_str=RHS_string,
                        RHS_input_str=RHS_input,
                        RHS_output_str=RHS_output,
                        RK_lhss_list=RK_lhs_list,
                        RK_rhss_list=RK_rhs_list,
                        post_RHS_list=[post_RHS_string],
                        post_RHS_output_list=[post_RHS_output],
                        enable_SIMD=enable_SIMD,
                        enable_griddata=enable_griddata,
                        gf_aliases=gf_aliases,
                        post_post_RHS_string=post_post_RHS_string
                    ) + "// -={ END k" + str(s + 1) + " substep }=-\n\n"

    enableCparameters = True
    if enable_griddata:
        enableCparameters = False
    add_to_Cfunction_dict(includes=includes,
                          desc=desc,
                          c_type=c_type,
                          name=name,
                          params=params,
                          body=indent_Ccode(body, "  "),
                          enableCparameters=enableCparameters,
                          rel_path_to_Cparams=os.path.join("."))
Example #5
0
def single_RK_substep_input_symbolic(commentblock,
                                     RHS_str,
                                     RHS_input_str,
                                     RHS_output_str,
                                     RK_lhss_list,
                                     RK_rhss_list,
                                     post_RHS_list,
                                     post_RHS_output_list,
                                     enable_SIMD=False,
                                     enable_griddata=False,
                                     gf_aliases="",
                                     post_post_RHS_string=""):
    return_str = commentblock + "\n"
    if not isinstance(RK_lhss_list, list):
        RK_lhss_list = [RK_lhss_list]
    if not isinstance(RK_rhss_list, list):
        RK_rhss_list = [RK_rhss_list]

    if not isinstance(post_RHS_list, list):
        post_RHS_list = [post_RHS_list]
    if not isinstance(post_RHS_output_list, list):
        post_RHS_output_list = [post_RHS_output_list]

    indent = ""
    if enable_griddata:
        return_str += "{\n" + indent_Ccode(gf_aliases, "  ")
        indent = "  "
    # Part 1: RHS evaluation:
    return_str += indent_Ccode(str(RHS_str).replace(
        "RK_INPUT_GFS",
        str(RHS_input_str).replace("gfsL", "gfs")).replace(
            "RK_OUTPUT_GFS",
            str(RHS_output_str).replace("gfsL", "gfs")) + "\n",
                               indent=indent)

    # Part 2: RK update
    if enable_SIMD:
        return_str += "#pragma omp parallel for\n"
        return_str += indent + "for(int i=0;i<Nxx_plus_2NGHOSTS0*Nxx_plus_2NGHOSTS1*Nxx_plus_2NGHOSTS2*NUM_EVOL_GFS;i+=SIMD_width) {\n"
    else:
        return_str += indent + "LOOP_ALL_GFS_GPS(i) {\n"
    type = "REAL"
    if enable_SIMD:
        type = "REAL_SIMD_ARRAY"
    RK_lhss_str_list = []
    for i, el in enumerate(RK_lhss_list):
        if enable_SIMD:
            RK_lhss_str_list.append(indent +
                                    "const REAL_SIMD_ARRAY __RHS_exp_" +
                                    str(i))
        else:
            RK_lhss_str_list.append(indent + str(el).replace("gfsL", "gfs[i]"))
    read_list = []
    for el in RK_rhss_list:
        for read in list(sp.ordered(el.free_symbols)):
            read_list.append(read)
    read_list_uniq = superfast_uniq(read_list)
    for el in read_list_uniq:
        if str(el) != "dt":
            if enable_SIMD:
                return_str += indent + "  const " + type + " " + str(
                    el) + " = ReadSIMD(&" + str(el).replace("gfsL",
                                                            "gfs[i]") + ");\n"
            else:
                return_str += indent + "  const " + type + " " + str(
                    el) + " = " + str(el).replace("gfsL", "gfs[i]") + ";\n"
    if enable_SIMD:
        return_str += indent + "  const REAL_SIMD_ARRAY DT = ConstSIMD(dt);\n"
    preindent = "1"
    if enable_griddata:
        preindent = "2"
    kernel = outputC(RK_rhss_list,
                     RK_lhss_str_list,
                     filename="returnstring",
                     params="includebraces=False,preindent=" + preindent +
                     ",outCverbose=False,enable_SIMD=" + str(enable_SIMD))
    if enable_SIMD:
        return_str += kernel.replace("dt", "DT")
        for i, el in enumerate(RK_lhss_list):
            return_str += "  WriteSIMD(&" + str(el).replace(
                "gfsL", "gfs[i]") + ", __RHS_exp_" + str(i) + ");\n"
    else:
        return_str += kernel

    return_str += indent + "}\n"

    # Part 3: Call post-RHS functions
    for post_RHS, post_RHS_output in zip(post_RHS_list, post_RHS_output_list):
        return_str += indent_Ccode(
            post_RHS.replace("RK_OUTPUT_GFS",
                             str(post_RHS_output).replace("gfsL", "gfs")))

    if enable_griddata:
        return_str += "}\n"

    for post_RHS, post_RHS_output in zip(post_RHS_list, post_RHS_output_list):
        return_str += indent_Ccode(
            post_post_RHS_string.replace(
                "RK_OUTPUT_GFS",
                str(post_RHS_output).replace("gfsL", "gfs")), "")

    return return_str
Example #6
0
def add_to_Cfunction_dict_MoL_step_forward_in_time(MoL_method, RHS_string = "", post_RHS_string = "",
                                                  enable_rfm=False, enable_curviBCs=False):
    includes = ["NRPy_basic_defines.h", "NRPy_function_prototypes.h"]
    desc  = "Method of Lines (MoL) for \"" + MoL_method + "\" method: Step forward one full timestep.\n"
    c_type = "void"
    name = "MoL_step_forward_in_time"
    params  = "const paramstruct *restrict params, "
    if enable_rfm:
        params += "const rfm_struct *restrict rfmstruct, "
    else:
        params += "REAL *xx[3], "
    if enable_curviBCs:
        params += "const bc_struct *restrict bcstruct, "
    params += "MoL_gridfunctions_struct *restrict gridfuncs, const REAL dt"

    indent = ""  # We don't bother with an indent here.

    body = indent + "// C code implementation of -={ " + MoL_method + " }=- Method of Lines timestepping.\n\n"

    y_n_gridfunctions, non_y_n_gridfunctions_list, _throwaway = generate_gridfunction_names(MoL_method)
    body += "// First set gridfunction aliases from gridfuncs struct\n\n"
    body += "// y_n gridfunctions:\n"
    body += "REAL *restrict " + y_n_gridfunctions + " = gridfuncs->" + y_n_gridfunctions + ";\n"
    body += "\n"
    body += "// Temporary timelevel & AUXEVOL gridfunctions:\n"
    for gf in non_y_n_gridfunctions_list:
        body += "REAL *restrict " + gf + " = gridfuncs->" + gf + ";\n"
    body += "\n"
    body += "// Next perform a full step forward in time\n"

    # Implement Method of Lines (MoL) Timestepping
    Butcher = Butcher_dict[MoL_method][0] # Get the desired Butcher table from the dictionary
    num_steps = len(Butcher)-1 # Specify the number of required steps to update solution

    # Diagonal RK3 only!!!
    if diagonal(MoL_method) and "RK3" in MoL_method:
        #  In a diagonal RK3 method, only 3 gridfunctions need be defined. Below implements this approach.

        # k_1
        body += """
// In a diagonal RK3 method like this one, only 3 gridfunctions need be defined. Below implements this approach.
// Using y_n_gfs as input, k1 and apply boundary conditions\n"""

        body += single_RK_substep(
            commentblock = """// -={ START k1 substep }=-
// RHS evaluation:
//  1. We will store k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs now as
//     ...  the update for the next rhs evaluation y_n + a21*k1*dt
// Post-RHS evaluation:
//  1. Apply post-RHS to y_n + a21*k1*dt""",
            RHS_str = RHS_string,
            RHS_input_str = "y_n_gfs", RHS_output_str = "k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs",
            RK_lhss_list = ["k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs"],
            RK_rhss_list = ["("+sp.ccode(Butcher[1][1]).replace("L","")+")*k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs[i]*dt + y_n_gfs[i]"],
            post_RHS_list = [post_RHS_string], post_RHS_output_list = ["k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs"]) + "// -={ END k1 substep }=-\n\n"

        # k_2
        body += single_RK_substep(
            commentblock="""// -={ START k2 substep }=-
// RHS evaluation:
//    1. Reassign k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs to be the running total y_{n+1}; a32*k2*dt to the running total
//    2. Store k2_or_y_nplus_a32_k2_gfs now as y_n + a32*k2*dt
// Post-RHS evaluation:
//    1. Apply post-RHS to both y_n + a32*k2 (stored in k2_or_y_nplus_a32_k2_gfs)
//       ... and the y_{n+1} running total, as they have not been applied yet to k2-related gridfunctions""",
            RHS_str=RHS_string,
            RHS_input_str="k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs", RHS_output_str="k2_or_y_nplus_a32_k2_gfs",
            RK_lhss_list=["k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs","k2_or_y_nplus_a32_k2_gfs"],
            RK_rhss_list=["("+sp.ccode(Butcher[3][1]).replace("L","")+")*(k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs[i] - y_n_gfs[i])/("+sp.ccode(Butcher[1][1]).replace("L","")+") + y_n_gfs[i] + ("+sp.ccode(Butcher[3][2]).replace("L","")+")*k2_or_y_nplus_a32_k2_gfs[i]*dt",
                          "("+sp.ccode(Butcher[2][2]).replace("L","")+")*k2_or_y_nplus_a32_k2_gfs[i]*dt + y_n_gfs[i]"],
            post_RHS_list=[post_RHS_string,post_RHS_string],
            post_RHS_output_list=["k2_or_y_nplus_a32_k2_gfs","k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs"]) + "// -={ END k2 substep }=-\n\n"

        # k_3
        body += single_RK_substep(
            commentblock="""// -={ START k3 substep }=-
// RHS evaluation:
//    1. Add k3 to the running total and save to y_n
// Post-RHS evaluation:
//    1. Apply post-RHS to y_n""",
            RHS_str=RHS_string,
            RHS_input_str="k2_or_y_nplus_a32_k2_gfs", RHS_output_str="y_n_gfs",
            RK_lhss_list=["y_n_gfs","k2_or_y_nplus_a32_k2_gfs"],
            RK_rhss_list=["k1_or_y_nplus_a21_k1_or_y_nplus1_running_total_gfs[i] + ("+sp.ccode(Butcher[3][3]).replace("L","")+")*y_n_gfs[i]*dt"],
            post_RHS_list=[post_RHS_string],
            post_RHS_output_list=["y_n_gfs"]) + "// -={ END k3 substep }=-\n\n"
    else:
        y_n = "y_n_gfs"
        if not diagonal(MoL_method):
            for s in range(num_steps):
                next_y_input = "next_y_input_gfs"

                # If we're on the first step (s=0), we use y_n gridfunction as input.
                #      Otherwise next_y_input is input. Output is just the reverse.
                if s == 0:  # If on first step:
                    RHS_input = y_n
                else:    # If on second step or later:
                    RHS_input = next_y_input
                RHS_output = "k" + str(s + 1) + "_gfs"
                if s == num_steps-1: # If on final step:
                    RK_lhs = y_n
                    RK_rhs = y_n + "[i] + dt*("
                else:                # If on anything but the final step:
                    RK_lhs = next_y_input
                    RK_rhs = y_n + "[i] + dt*("
                for m in range(s+1):
                    if Butcher[s+1][m+1] != 0:
                        if Butcher[s+1][m+1] != 1:
                            RK_rhs += " + k"+str(m+1)+"_gfs[i]*("+sp.ccode(Butcher[s+1][m+1]).replace("L","")+")"
                        else:
                            RK_rhs += " + k"+str(m+1)+"_gfs[i]"
                RK_rhs += " )"

                post_RHS = post_RHS_string
                if s == num_steps-1: # If on final step:
                    post_RHS_output = y_n
                else:                # If on anything but the final step:
                    post_RHS_output = next_y_input

                body += single_RK_substep(
                    commentblock="// -={ START k" + str(s + 1) + " substep }=-",
                    RHS_str=RHS_string,
                    RHS_input_str=RHS_input, RHS_output_str=RHS_output,
                    RK_lhss_list=[RK_lhs],   RK_rhss_list=[RK_rhs],
                    post_RHS_list=[post_RHS],
                    post_RHS_output_list=[post_RHS_output]) + "// -={ END k" + str(s + 1) + " substep }=-\n\n"
        else:  # diagonal case:
            y_nplus1_running_total = "y_nplus1_running_total_gfs"
            if MoL_method == 'Euler': # Euler's method doesn't require any k_i, and gets its own unique algorithm
                body += single_RK_substep(
                    commentblock=indent + "// ***Euler timestepping only requires one RHS evaluation***",
                    RHS_str=RHS_string,
                    RHS_input_str=y_n, RHS_output_str=y_nplus1_running_total,
                    RK_lhss_list=[y_n],   RK_rhss_list=[y_n+"[i] + "+y_nplus1_running_total+"[i]*dt"],
                    post_RHS_list=[post_RHS_string],
                    post_RHS_output_list=[y_n])
            else:
                for s in range(num_steps):
                    # If we're on the first step (s=0), we use y_n gridfunction as input.
                    # and k_odd as output.
                    if s == 0:
                        RHS_input  = "y_n_gfs"
                        RHS_output = "k_odd_gfs"
                    # For the remaining steps the inputs and ouputs alternate between k_odd and k_even
                    elif s % 2 == 0:
                        RHS_input = "k_even_gfs"
                        RHS_output = "k_odd_gfs"
                    else:
                        RHS_input = "k_odd_gfs"
                        RHS_output = "k_even_gfs"

                    RK_lhs_list = []
                    RK_rhs_list = []
                    if s != num_steps-1:  # For anything besides the final step
                        if s == 0:  # The first RK step
                            RK_lhs_list.append(y_nplus1_running_total)
                            RK_rhs_list.append(RHS_output+"[i]*dt*("+sp.ccode(Butcher[num_steps][s+1]).replace("L","")+")")

                            RK_lhs_list.append(RHS_output)
                            RK_rhs_list.append(y_n+"[i] + "+RHS_output+"[i]*dt*("+sp.ccode(Butcher[s+1][s+1]).replace("L","")+")")
                        else:
                            if Butcher[num_steps][s+1] != 0:
                                RK_lhs_list.append(y_nplus1_running_total)
                                if Butcher[num_steps][s+1] != 1:
                                    RK_rhs_list.append(y_nplus1_running_total+"[i] + "+RHS_output+"[i]*dt*("+sp.ccode(Butcher[num_steps][s+1]).replace("L","")+")")
                                else:
                                    RK_rhs_list.append(y_nplus1_running_total+"[i] + "+RHS_output+"[i]*dt")
                            if Butcher[s+1][s+1] != 0:
                                RK_lhs_list.append(RHS_output)
                                if Butcher[s+1][s+1] != 1:
                                    RK_rhs_list.append(y_n+"[i] + "+RHS_output+"[i]*dt*("+sp.ccode(Butcher[s+1][s+1]).replace("L","")+")")
                                else:
                                    RK_rhs_list.append(y_n+"[i] + "+RHS_output+"[i]*dt")
                        post_RHS_output = RHS_output
                    if s == num_steps-1:  # If on the final step
                        if Butcher[num_steps][s+1] != 0:
                            RK_lhs_list.append(y_n)
                            if Butcher[num_steps][s+1] != 1:
                                RK_rhs_list.append(y_n+"[i] + "+y_nplus1_running_total+"[i] + "+RHS_output+"[i]*dt*("+sp.ccode(Butcher[num_steps][s+1]).replace("L","")+")")
                            else:
                                RK_rhs_list.append(y_n+"[i] + "+y_nplus1_running_total+"[i] + "+RHS_output+"[i]*dt)")
                        post_RHS_output = y_n
                    body += single_RK_substep(
                        commentblock=indent + "// -={ START k" + str(s + 1) + " substep }=-",
                        RHS_str=RHS_string,
                        RHS_input_str=RHS_input, RHS_output_str=RHS_output,
                        RK_lhss_list=RK_lhs_list, RK_rhss_list=RK_rhs_list,
                        post_RHS_list=[post_RHS_string],
                        post_RHS_output_list=[post_RHS_output]) + "// -={ END k" + str(s + 1) + " substep }=-\n\n"

    add_to_Cfunction_dict(
        includes=includes,
        desc=desc,
        c_type=c_type, name=name, params=params,
        body=indent_Ccode(body, "  "),
        rel_path_to_Cparams=os.path.join("."))