def adaptive_rk_method(tol): """ The difference between the results of Euler's method y_e = y_n + h f(t_n, y_n) and Heun's method y_h = y_n + h / 2 (f(t_n, y_n), f(t_n + dt, y_e)) can be used as an estimate of the high order error term in Euler's method. This allows us to adapt the time step so that the local error falls within a specified tolerance. """ # Set up variables y = var("<state>y") g = var("<func>g") y_e = var("y_e") y_h = var("y_h") dt = var("<dt>") t = var("<t>") dt_old = var("dt_old") # Helpers for expression fragments def norm(val): return var("<builtin>norm_2")(val) def dt_scaling(tol, err): # Return a suitable scaling factor for dt. # Ensure to guard against excessive increase or divide-by-zero. from pymbolic.primitives import Max, Min return Min(((tol / Max((1.0e-16, norm(err))))**(1 / 2), 2.0)) # Code for the main state with CodeBuilder("adaptrk") as cb: # Euler cb(y_e, y + dt * g(t, y)) # Heun cb(y_h, y + dt / 2 * (g(t, y) + g(t + dt, y_e))) # Adaptation cb(dt_old, dt) cb(dt, dt * dt_scaling(tol, y_h - y_e)) # Accept or reject step with cb.if_(norm(y_h - y_e), ">=", tol): cb.fail_step() with cb.else_(): cb(y, y_h) cb(t, t + dt_old) return DAGCode.from_phases_list([cb.as_execution_phase("adaptrk")], "adaptrk")
def generate(self, solver_hook): """Return code that implements the implicit Euler method for the single state component supported.""" with CodeBuilder(name="primary") as cb: self._make_primary(cb) code = DAGCode.from_phases_list( [cb.as_execution_phase(next_phase="primary")], initial_phase="primary") from leap.implicit import replace_AssignImplicit return replace_AssignImplicit(code, {self.SOLVER_EXPRESSION_ID: solver_hook})
def test_CodeBuilder_restart_step(python_method_impl): with CodeBuilder("init") as builder_init: builder_init("<p>x", "0") with CodeBuilder("state1") as builder1: builder1("<p>x", "<p>x + 1") with builder1.if_("<p>x == 1"): builder1.restart_step() with CodeBuilder("state2") as builder2: builder2.yield_state(var("<p>x"), "x", 0, "final") phases = [ builder_init.as_execution_phase(next_phase="state1"), builder1.as_execution_phase(next_phase="state2"), builder2.as_execution_phase(next_phase="state2") ] code = DAGCode.from_phases_list(phases, "init") result = execute_and_return_single_result(python_method_impl, code, max_steps=4) assert result == 2