예제 #1
0
def test_mrab_scheme_explainers(order=3,
                                step_ratio=3,
                                explainer=TextualSchemeExplainer()):
    method = MultiRateMultiStepMethodBuilder(order, (
        (
            'dt',
            'fast',
            '=',
            MRHistory(1, "<func>f", (
                "fast",
                "slow",
            )),
        ),
        (
            'dt',
            'slow',
            '=',
            MRHistory(step_ratio,
                      "<func>s", (
                          "fast",
                          "slow",
                      ),
                      rhs_policy=rhs_policy.late),
        ),
    ))

    method.generate(explainer=explainer)
    print(explainer)
예제 #2
0
def test_mrab_with_derived_state_scheme_explainers(
    order=3, step_ratio=3, explainer=TextualSchemeExplainer()):
    method = MultiRateMultiStepMethodBuilder(order, (
        (
            "dt",
            "fast",
            "=",
            MRHistory(1, "<func>f", (
                "fast",
                "slow",
            )),
        ),
        (
            "dt",
            "slow",
            "=",
            MRHistory(step_ratio,
                      "<func>s", ("fast", "slow", "derived"),
                      rhs_policy=rhs_policy.late),
        ),
        (
            "derived",
            "=",
            MRHistory(step_ratio,
                      "<func>compute_derived", (
                          "fast",
                          "slow",
                      ),
                      rhs_policy=rhs_policy.late),
        ),
    ))

    code = method.generate(explainer=explainer)
    print(code)
    print()
    print(explainer)
예제 #3
0
def test_dependent_state(order=3, step_ratio=3):
    # Solve
    # f' = f+s
    # s' = -f+s

    def true_f(t):
        return np.exp(t) * np.sin(t)

    def true_s(t):
        return np.exp(t) * np.cos(t)

    method = MultiRateMultiStepMethodBuilder(order, (
        (
            "dt",
            "fast",
            "=",
            MRHistory(1, "<func>f", (
                "two_fast",
                "slow",
            )),
        ),
        ("dt", "slow", "=", MRHistory(step_ratio, "<func>s",
                                      ("fast", "slow"))),
        (
            "two_fast",
            "=",
            MRHistory(step_ratio, "<func>twice", ("fast", )),
        ),
    ),
                                             static_dt=True)

    code = method.generate()
    print(code)

    from pytools.convergence import EOCRecorder
    eocrec = EOCRecorder()

    from dagrt.codegen import PythonCodeGenerator
    codegen = PythonCodeGenerator(class_name="Method")

    stepper_cls = codegen.get_class(code)

    for n in range(4, 7):
        t = 0
        dt = 2**(-n)
        final_t = 10

        stepper = stepper_cls(
            function_map={
                "<func>f": lambda t, two_fast, slow: 0.5 * two_fast + slow,
                "<func>s": lambda t, fast, slow: -fast + slow,
                "<func>twice": lambda t, fast: 2 * fast,
            })

        stepper.set_up(t_start=t,
                       dt_start=dt,
                       context={
                           "fast": true_f(t),
                           "slow": true_s(t),
                       })

        f_times = []
        f_values = []
        s_times = []
        s_values = []
        for event in stepper.run(t_end=final_t):
            if isinstance(event, stepper_cls.StateComputed):
                if event.component_id == "fast":
                    f_times.append(event.t)
                    f_values.append(event.state_component)
                elif event.component_id == "slow":
                    s_times.append(event.t)
                    s_values.append(event.state_component)
                else:
                    assert False, event.component_id

        f_times = np.array(f_times)
        s_times = np.array(s_times)
        f_values_true = true_f(f_times)
        s_values_true = true_s(s_times)

        f_err = f_values - f_values_true
        s_err = s_values - s_values_true

        error = (
            la.norm(f_err) / la.norm(f_values_true) +  # noqa: W504
            la.norm(s_err) / la.norm(s_values_true))

        eocrec.add_data_point(dt, error)

    print(eocrec.pretty_print())

    orderest = eocrec.estimate_order_of_convergence()[0, 1]
    assert orderest > 3 * 0.95
예제 #4
0
def test_single_rate_identical(order=3, hist_length=3):
    from leap.multistep import AdamsBashforthMethodBuilder
    from dagrt.exec_numpy import NumpyInterpreter

    from multirate_test_systems import Full
    ode = Full()

    t_start = 0
    dt = 0.1

    # {{{ single rate

    single_rate_method = AdamsBashforthMethodBuilder("y",
                                                     order=order,
                                                     hist_length=hist_length)
    single_rate_code = single_rate_method.generate()

    def single_rate_rhs(t, y):
        f, s = y
        return np.array([
            ode.f2f_rhs(t, f, s) + ode.s2f_rhs(t, f, s),
            ode.f2s_rhs(t, f, s) + ode.s2s_rhs(t, f, s),
        ])

    single_rate_interp = NumpyInterpreter(
        single_rate_code, function_map={"<func>y": single_rate_rhs})

    single_rate_interp.set_up(
        t_start=t_start,
        dt_start=dt,
        context={"y": np.array([
            ode.soln_0(t_start),
            ode.soln_1(t_start),
        ])})

    single_rate_values = {}

    nsteps = 20

    for event in single_rate_interp.run():
        if isinstance(event, single_rate_interp.StateComputed):
            single_rate_values[event.t] = event.state_component

            if len(single_rate_values) == nsteps:
                break

    # }}}

    # {{{ two rate

    multi_rate_method = MultiRateMultiStepMethodBuilder(
        order, (
            (
                'dt',
                'fast',
                '=',
                MRHistory(
                    1, "<func>f", (
                        "fast",
                        "slow",
                    ), hist_length=hist_length),
            ),
            (
                'dt',
                'slow',
                '=',
                MRHistory(1,
                          "<func>s", (
                              "fast",
                              "slow",
                          ),
                          rhs_policy=rhs_policy.late,
                          hist_length=hist_length),
            ),
        ),
        hist_consistency_threshold=1e-8,
        early_hist_consistency_threshold=dt**order)

    multi_rate_code = multi_rate_method.generate()

    def rhs_fast(t, fast, slow):
        return ode.f2f_rhs(t, fast, slow) + ode.s2f_rhs(t, fast, slow)

    def rhs_slow(t, fast, slow):
        return ode.f2s_rhs(t, fast, slow) + ode.s2s_rhs(t, fast, slow)

    multi_rate_interp = NumpyInterpreter(multi_rate_code,
                                         function_map={
                                             "<func>f": rhs_fast,
                                             "<func>s": rhs_slow
                                         })

    multi_rate_interp.set_up(t_start=t_start,
                             dt_start=dt,
                             context={
                                 "fast": ode.soln_0(t_start),
                                 "slow": ode.soln_1(t_start),
                             })

    multi_rate_values = {}

    for event in multi_rate_interp.run():
        if isinstance(event, single_rate_interp.StateComputed):
            idx = {"fast": 0, "slow": 1}[event.component_id]
            if event.t not in multi_rate_values:
                multi_rate_values[event.t] = [None, None]

            multi_rate_values[event.t][idx] = event.state_component

            if len(multi_rate_values) > nsteps:
                break

    # }}}

    times = sorted(single_rate_values)
    single_rate_values = np.array([single_rate_values[t] for t in times])
    multi_rate_values = np.array([multi_rate_values[t] for t in times])
    print(single_rate_values)
    print(multi_rate_values)

    diff = la.norm((single_rate_values - multi_rate_values).reshape(-1))

    assert diff < 1e-13
예제 #5
0
def main():
    # order = 4
    # hist_length = 4
    order = 3
    hist_length = 3
    static_dt = True
    stepper = MultiRateMultiStepMethodBuilder(order, (
        (
            'dt',
            'fast',
            '=',
            MRHistory(1,
                      "<func>f", ("fast", "slow"),
                      hist_length=hist_length,
                      is_rhs_implicit=True),
        ),
        (
            'dt',
            'slow',
            '=',
            MRHistory(1,
                      "<func>s", ("fast", "slow"),
                      rhs_policy=rhs_policy.late,
                      hist_length=hist_length,
                      is_rhs_implicit=False),
        ),
    ),
                                              static_dt=static_dt)

    from dagrt.function_registry import (base_function_registry,
                                         register_function, UserType)

    # This stays unchanged from the existing explicit RK4 RHS.
    freg = register_function(base_function_registry,
                             "<func>s", ("t", "fast", "slow"),
                             result_names=("result", ),
                             result_kinds=(UserType("slow"), ))
    freg = freg.register_codegen(
        "<func>s", "cxx",
        cxx.CallCode("""

                // Purely homogeneous chemistry simulation.
                for (int i = 0;i < 4;i++){

                 ${result}[i] = 0.0;

                }

                """))

    # Here, we need to call a new chemistry RHS that loops through
    # all the points *under the hood.*
    freg = register_function(freg,
                             "<func>f", ("t", "fast", "slow"),
                             result_names=("result", ),
                             result_kinds=(UserType("fast"), ))
    freg = freg.register_codegen(
        "<func>f", "cxx",
        cxx.CallCode("""

                // PyJac inputs.
                double jac[(NS+1)*(NS+1)];
                double jac_trans[(NS+1)*(NS+1)];
                double phi[(NS+1)];
                double phi_guess[(NS+1)];
                double phi_old[(NS+1)];
                double dphi[(NS+1)];
                double dphi_old[(NS+1)];
                double corr[(NS+1)];
                double corr_weights[(NS+1)];
                double corr_weighted[(NS+1)];
                double reltol = 1e-6;
                double abstol = 1e-12;
                /* For Lapack */
                int ipiv[NS+1], info;
                int nrhs = 1;
                int nsp_l = NS+1;
                // Dummy time for pyJac.
                double tout = 0;

                // Work array for PyJac.
                double* rwk_dphi = (double*)malloc(245 * sizeof(double));
                memset(rwk_dphi, 0, 245 * sizeof(double));
                double massFractions[NS];
                double mw[NS];

                // FIXME: Assumes the last (inert) species is nitrogen.
                mw[NS-1] = 2*14.00674;
                for (int i = 0; i < NS-1; ++i ){
                  mw[i] = mw[NS-1]*mw_factor[i];
                }

                double rho = 0.2072648773462248;
                double vol = 1.0 / rho;
                double tol = 1e-10;
                double mass_sum = 0.0;
                for (int i = 2; i <= NS; ++i ){
                  massFractions[i-2] = ${fast}[i]*mw[i-2];
                  mass_sum += massFractions[i-2];
                }
                //massFractions[8] = 1 - mass_sum;
                massFractions[8] = 0.0;

                // PyJac converted input state.
                //phi[0] = ${fast}[0];
                //phi[1] = ${fast}[1];
                // Update temperature and pressure using Cantera?
                Cantera::IdealGasMix * GasMixture;
                GasMixture = new Cantera::IdealGasMix("Mechanisms/sanDiego.xml");
                double int_energy = 788261.179011143;
                GasMixture->setMassFractions(massFractions);
                GasMixture->setState_UV(int_energy, vol, tol);
                phi[0] = GasMixture->temperature();
                phi[1] = GasMixture->pressure();
                delete GasMixture;
                for (int j=2;j <= NS; j++) {
                  phi[j] = massFractions[j-2]/mw[j-2];
                }
                // PyJac call for source term
                species_rates (&tout, &vol, phi, dphi, rwk_dphi);

                for (int j=0;j <= NS; j++) {
                  ${result}[j] = dphi[j];
                }

                """))

    # The tricky part - this is going to require a
    # nonlinear solve of some kind.
    freg = register_function(freg,
                             "<func>solver", ("fast", "slow", "coeff", "t"),
                             result_names=("result", ),
                             result_kinds=(UserType("fast"), ))
    freg = freg.register_codegen(
        "<func>solver", "cxx",
        cxx.CallCode("""

                // PyJac inputs.
                double jac[(NS+1)*(NS+1)];
                double jac_trans[(NS+1)*(NS+1)];
                double jac_sub[(NS-1)*(NS-1)];
                double phi[(NS+1)];
                double phi_guess[(NS+1)];
                double phi_old[(NS+1)];
                double dphi[(NS+1)];
                double dphi_sub[(NS-1)];
                double dphi_old[(NS+1)];
                double corr[(NS+1)];
                double corr_weights[(NS+1)];
                double corr_weighted[(NS+1)];
                double reltol = 1e-6;
                double abstol = 1e-12;
                /* For Lapack */
                int ipiv[NS-1], info;
                int nrhs = 1;
                int nsp_l = NS-1;
                int nsp_l_full = NS+1;
                // Dummy time for pyJac.
                double tout = 0;

                // Work array for PyJac.
                double* rwk_dphi = (double*)malloc(245 * sizeof(double));
                memset(rwk_dphi, 0, 245 * sizeof(double));
                double* rwk_jac = (double*)malloc(245 * sizeof(double));
                memset(rwk_jac, 0, 245 * sizeof(double));

                /* 1D point-sized identity matrix */
                double ident[(NS-1)*(NS-1)];
                for (int i=0; i < (NS-1)*(NS-1); i++) {
                    if (i % (NS) == 0) {
                        ident[i] = 1;
                    } else {
                        ident[i] = 0;
                    }
                }

                // THIS IS WHERE ALL OF THE
                // NEWTON/PYJAC STUFF GOES.
                // Get chemical state at this point.

                double massFractions[NS];
                double mw[NS];

                // FIXME: Assumes the last (inert) species is nitrogen.
                mw[NS-1] = 2*14.00674;
                for (int i = 0; i < NS-1; ++i ){
                  mw[i] = mw[NS-1]*mw_factor[i];
                }

                double rho = 0.2072648773462248;
                double mass_sum = 0.0;
                for (int i = 2; i <= NS; ++i ){
                  massFractions[i-2] = ${fast}[i]*mw[i-2];
                  mass_sum += massFractions[i-2];
                }
                //massFractions[8] = 1 - mass_sum;
                massFractions[8] = 0.0;

                double vol = 1.0 / 0.2072648773462248;
                double tol = 1e-10;

                // PyJac converted input state.
                //phi[0] = ${fast}[0];
                //phi[1] = ${fast}[1];
                Cantera::IdealGasMix * GasMixture;
                GasMixture = new Cantera::IdealGasMix("Mechanisms/sanDiego.xml");
                double int_energy = 788261.179011143;
                GasMixture->setMassFractions(massFractions);
                GasMixture->setState_UV(int_energy, vol, tol);
                phi[0] = GasMixture->temperature();
                phi[1] = GasMixture->pressure();
                //delete GasMixture;
                for (int j=2;j <= NS; j++) {
                  phi[j] = massFractions[j-2]/mw[j-2];
                }
                for (int j=0;j <= NS; j++) {
                  phi_old[j] = phi[j];
                }
                for (int j=0;j <= NS; j++) {
                  phi_guess[j] = phi[j];
                }
                // Newton loop within this point (for now).
                double corr_norm = 1.0;
                // PyJac call for source term
                species_rates (&tout, &vol, phi, dphi_old, rwk_dphi);
                //while (abs(corr_norm) >= reltol) {
                while (abs(corr_norm) >= 1e-8) {
                  species_rates (&tout, &vol, phi_guess, dphi, rwk_dphi);
                  // Get Jacobian at this point.
                  jacobian (&tout, &vol, phi_guess, jac, rwk_jac);
                  for (int j=0;j <= NS; j++) {
                    // IMEX AM
                    dphi[j] = phi_guess[j] - phi_old[j] - ${coeff} * dphi[j];
                  }

                  // Take subset of RHS vector.
                  for (int j=2;j <= NS; j++) {
                    dphi_sub[j-2] = dphi[j];
                  }

                  // Transpose the Jacobian.
                  // PYJAC outputs Fortran-ordering
                  for (int i = 0; i < (NS+1); ++i )
                  {
                    for (int j = 0; j < (NS+1); ++j )
                    {
                      // Index in the original matrix.
                      int index1 = i*(NS+1)+j;

                      // Index in the transpose matrix.
                      int index2 = j*(NS+1)+i;

                      jac_trans[index2] = jac[index1];
                    }
                  }

                  for (int i=0; i<(NS+1)*(NS+1); i++) {
                    jac[i] = jac_trans[i];
                  }

                  // Take subset of Jacobian for algebraic changes.
                  for (int i = 0; i < (NS-1); ++i )
                  {
                    for (int j = 0; j < (NS-1); ++j )
                    {
                      jac_sub[i*(NS-1)+j] = jac[(i+2)*(NS+1)+2+j];
                    }
                  }

                  // Make the algebraic changes,
                  // using PyJac routines as needed.
                  // Get internal energies.
                  double int_energies[NS];
                  eval_u(phi_guess, int_energies);
                  // Get CVs.
                  double cvs[NS];
                  eval_cv(phi_guess, cvs);
                  // Get total CV.
                  double mass_sum = 0.0;
                  for (int i = 2; i <= NS; ++i ){
                    massFractions[i-2] = phi_guess[i]*mw[i-2];
                    mass_sum += massFractions[i-2];
                  }
                  //massFractions[8] = 1 - mass_sum;
                  massFractions[8] = 0.0;
                  double cv_total = 0.0;
                  for (int i = 0; i <= NS-1; ++i ){
                    cv_total += cvs[i]*(massFractions[i]/mw[i]);
                  }

                  // Modify the Jacobian for the algebraic constraint.
                  for (int i = 0; i < (NS-1); ++i )
                  {
                    for (int j = 0; j < (NS-1); ++j )
                    {
                      jac_sub[i*(NS-1)+j] -= jac[j+2]*int_energies[i]/cv_total;
                    }
                  }
                  /* Subtract from identity to get jac for Newton */
                  for (int j=0;j < (NS-1)*(NS-1); j++) {
                    jac_sub[j] = ident[j] - ${coeff} * jac_sub[j]; // IMEX AM
                    jac_sub[j] = -jac_sub[j];
                  }
                  // Do the inversion with the assistance of Lapack.
                  dgesv_(&nsp_l, &nrhs, jac_sub, &nsp_l, ipiv, dphi_sub, &nsp_l, &info);
                  // Add the correction to the buffer.
                  mass_sum = 0.0;
                  for (int i = 2; i <= NS; ++i ){
                    massFractions[i-2] = (phi_guess[i] + dphi_sub[i-2])*mw[i-2];
                    mass_sum += massFractions[i-2];
                  }
                  //massFractions[8] = 1 - mass_sum;
                  massFractions[8] = 0.0;
                  GasMixture->setMassFractions(massFractions);
                  GasMixture->setState_UV(int_energy, vol, tol);
                  corr[0] = GasMixture->temperature() - phi_guess[0];
                  corr[1] = GasMixture->pressure() - phi_guess[1];
                  for (int j=2;j <= NS; j++) {
                    corr[j] = dphi_sub[j-2];
                  }
                  for (int j=0;j <= NS; j++) {
                    corr_weights[j] = 1.0 / (reltol * abs(phi_guess[j]) + abstol);
                    corr_weighted[j] = corr[j]*corr_weights[j];
                  }

                  //corr_norm = dnrm2_(&nsp_l, corr, &nrhs);
                  corr_norm = dnrm2_(&nsp_l_full, corr_weighted, &nrhs);
                  //std::cout << "Correction norm: " << corr_norm << std::endl;
                  for (int j=0;j <= NS; j++) {
                    phi_guess[j] = phi_guess[j] + corr[j];
                  }
                }
                // Now outside the Newton loop (presumably having converged),
                // we update the RHS using the actual state.
                species_rates (&tout, &vol, phi_guess, dphi, rwk_dphi);
                for (int j=0;j <= NS; j++) {
                  ${result}[j] = dphi[j];
                }
                delete GasMixture;

                """))

    code = stepper.generate()

    # Implicit solve thingy
    from leap.implicit import replace_AssignImplicit
    code = replace_AssignImplicit(code, {"solve": am_solver_hook})

    # print(code)
    codegen = cxx.CodeGenerator(
        'LeapIMEX',
        user_type_map={
            "fast": cxx.ArrayType(
                (10, ),
                cxx.BuiltinType('double'),
            ),
            "slow": cxx.ArrayType(
                (4, ),
                cxx.BuiltinType('double'),
            ),
        },
        function_registry=freg,
        emit_instrumentation=True,
        timing_function="clock",
        header_preamble="\n#include \"mechanism.hpp\"\n#include " +
        "\"species_rates.hpp\"\n#include " +
        "\"jacobian.hpp\"\n#include \"memcpy_2d.hpp" +
        "\"\n#include \"lapack_kernels.H\"\n#include " +
        "\"cantera/IdealGasMix.h\"\n#include " +
        "\"cantera/thermo.h\"\n#include " +
        "\"cantera/kinetics.h\"\n#include " + "\"cantera/transport.h\"")

    import sys

    # Write out Leap/Dagrt code:
    with open(sys.argv[1], "a") as outf:
        code_str = codegen(code)
        print(code_str, file=outf)