def test_adaptive_rk_codegen_error(): """Test whether Fortran code generation for the Runge-Kutta timestepper works. """ component_id = 'y' rhs_function = '<func>y' stepper = ODE45MethodBuilder(component_id, use_high_order=False, atol=1e-6) from dagrt.function_registry import ( base_function_registry, register_ode_rhs) freg = register_ode_rhs(base_function_registry, component_id, identifier=rhs_function) freg = freg.register_codegen(rhs_function, "fortran", f.CallCode(""" ${result} = -2*${y} """)) code = stepper.generate() codegen = f.CodeGenerator( "RKMethod", user_type_map={ component_id: f.ArrayType( (2,), f.BuiltinType('real (kind=8)'), ) }, function_registry=freg) run_fortran([ ("rkmethod.f90", codegen(code)), ("test_rk_adaptive_error.f90", read_file("test_rk_adaptive_error.f90")), ])
def test_elementwise_abs(): with CodeBuilder(name="primary") as cb: cb("i", "<builtin>array(20)") cb("i[j]", "-j", loops=(("j", 0, 20), )) # Test new builtin on an array type. cb("k", "<builtin>elementwise_abs(i)") with cb.if_("k[20] > 19"): cb.raise_(AbsFailure) with cb.if_("k[20] < 19"): cb.raise_(AbsFailure) # Test new builtin on a scalar. cb("l", "<builtin>elementwise_abs(-20)") with cb.if_("l > 20"): cb.raise_(AbsFailure) with cb.if_("l < 20"): cb.raise_(AbsFailure) cb("y", "<func>f(0, <state>ytype)") cb("<state>ytype", "y") # Test new builtin on a usertype. cb("<state>ytype", "<builtin>elementwise_abs(<state>ytype)") # (We check this in the outer test code) code = create_DAGCode_with_steady_phase(cb.statements) rhs_function = "<func>f" from dagrt.function_registry import (base_function_registry, register_ode_rhs) freg = register_ode_rhs(base_function_registry, "ytype", identifier=rhs_function, input_names=("y", )) freg = freg.register_codegen( rhs_function, "fortran", f.CallCode(""" ${result} = -2*${y} """)) codegen = f.CodeGenerator( "element_abs_test", function_registry=freg, user_type_map={"ytype": f.ArrayType((100, ), f.BuiltinType("real*8"))}, timing_function="second") code_str = codegen(code) run_fortran([ ("element_abs.f90", code_str), ("test_element_abs.f90", read_file("test_element_abs.f90")), ], fortran_libraries=["lapack", "blas"])
def test_singlerate_squarewave(min_order, hist_length): from leap.multistep import AdamsBashforthMethodBuilder component_id = "y" rhs_function = "<func>y" stepper = AdamsBashforthMethodBuilder("y", min_order, hist_length=hist_length) from dagrt.function_registry import (base_function_registry, register_ode_rhs) freg = register_ode_rhs(base_function_registry, component_id, identifier=rhs_function) freg = freg.register_codegen( rhs_function, "fortran", f.CallCode(""" ${result} = -2*${y} """)) code = stepper.generate() codegen = f.CodeGenerator("ABMethod", user_type_map={ component_id: f.ArrayType( (2, ), f.BuiltinType("real (kind=8)"), ) }, function_registry=freg, module_preamble=""" ! lines copied to the start of the module, e.g. to say: ! use ModStuff """) code_str = codegen(code) with open("abmethod_test.f90", "wt") as outf: outf.write(code_str) run_fortran( [ ("abmethod.f90", code_str), ("test_ab_squarewave.f90", read_file("test_ab_squarewave.f90").replace( "MIN_ORDER", str(min_order - 0.3) + "d0")), ], fortran_libraries=["lapack", "blas"], )
def test_rk_codegen(min_order, stepper, print_code=False): """Test whether Fortran code generation for the Runge-Kutta timestepper works. """ component_id = 'y' rhs_function = '<func>y' from dagrt.function_registry import (base_function_registry, register_ode_rhs) freg = register_ode_rhs(base_function_registry, component_id, identifier=rhs_function) freg = freg.register_codegen( rhs_function, "fortran", f.CallCode(""" ${result} = -2*${y} """)) code = stepper.generate() codegen = f.CodeGenerator('RKMethod', user_type_map={ component_id: f.ArrayType( (2, ), f.BuiltinType('real (kind=8)'), ) }, function_registry=freg, module_preamble=""" ! lines copied to the start of the module, e.g. to say: ! use ModStuff """) code_str = codegen(code) if print_code: print(code_str) run_fortran([ ("rkmethod.f90", code_str), ("test_rk.f90", read_file("test_rk.f90").replace("MIN_ORDER", str(min_order - 0.3) + "d0")), ])
def test_arrays_and_linalg(): from dagrt.function_registry import base_function_registry as freg with CodeBuilder(name="primary") as cb: cb("n", "4") cb("nodes", "`<builtin>array`(n)") cb("vdm", "`<builtin>array`(n*n)") cb("identity", "`<builtin>array`(n*n)") cb("nodes[i]", "i/n", loops=[("i", 0, "n")]) cb("identity[i]", "0", loops=[("i", 0, "n*n")]) cb("identity[i*n + i]", "1", loops=[("i", 0, "n")]) cb("vdm[j*n + i]", "nodes[i]**j", loops=[("i", 0, "n"), ("j", 0, "n")]) cb("vdm_inverse", "`<builtin>linear_solve`(vdm, identity, n, n)") cb("myarray", "`<builtin>matmul`(vdm, vdm_inverse, n, n)") cb("myzero", "myarray - identity") cb((), "`<builtin>print`(myzero)") with cb.if_("`<builtin>norm_2`(myzero) > 10**(-8)"): cb.raise_(MatrixInversionFailure) code = create_DAGCode_with_steady_phase(cb.statements) codegen = f.CodeGenerator("arrays", function_registry=freg, user_type_map={}, emit_instrumentation=True, timing_function="second") code_str = codegen(code) if 0: with open("arrays.f90", "wt") as outf: outf.write(code_str) run_fortran([ ("arrays.f90", code_str), ("test_arrays_and_linalg.f90", read_file("test_arrays_and_linalg.f90")), ], fortran_libraries=["lapack", "blas"])
def test_adaptive_rk_codegen(): """Test whether Fortran code generation for the Runge-Kutta timestepper works. """ component_id = "y" rhs_function = "<func>y" stepper = ODE45MethodBuilder(component_id, use_high_order=False, rtol=1e-6) from dagrt.function_registry import (base_function_registry, register_ode_rhs) freg = register_ode_rhs(base_function_registry, component_id, identifier=rhs_function) freg = freg.register_codegen( rhs_function, "fortran", f.CallCode(""" ${result}(1) = ${y}(2) ${result}(2) = -30*((${y}(1))**2 - 1)*${y}(2) - ${y}(1) """)) code = stepper.generate() codegen = f.CodeGenerator("RKMethod", user_type_map={ "y": f.ArrayType( (2, ), f.BuiltinType("real (kind=8)"), ), }, function_registry=freg) run_fortran([ ("rkmethod.f90", codegen(code)), ("test_rk_adaptive.f90", read_file("test_rk_adaptive.f90")), ])
def test_self_dep_in_loop(): with CodeBuilder(name="primary") as cb: cb("y", "<state>y") cb("y", "<func>f(0, 2*i*<func>f(0, y if i > 2 else 2*y))", loops=(("i", 0, 5), )) cb("<state>y", "y") code = create_DAGCode_with_steady_phase(cb.statements) rhs_function = "<func>f" from dagrt.function_registry import (base_function_registry, register_ode_rhs) freg = register_ode_rhs(base_function_registry, "ytype", identifier=rhs_function, input_names=("y", )) freg = freg.register_codegen( rhs_function, "fortran", f.CallCode(""" ${result} = -2*${y} """)) codegen = f.CodeGenerator( "selfdep", function_registry=freg, user_type_map={"ytype": f.ArrayType((100, ), f.BuiltinType("real*8"))}, timing_function="second") code_str = codegen(code) run_fortran([ ("selfdep.f90", code_str), ("test_selfdep.f90", read_file("test_selfdep.f90")), ], fortran_libraries=["lapack", "blas"])
def test_multirate_squarewave(min_order, hist_length, method_name): stepper = TwoRateAdamsBashforthMethodBuilder(method_name, min_order, 4, hist_consistency_threshold=1e-8, early_hist_consistency_threshold="3.5e3 * <dt>**%d" % min_order, hist_length_slow=hist_length, hist_length_fast=hist_length) # Early consistency threshold checked for convergence # with timestep change - C. Mikida, 2/6/18 (commit hash 0fb6148) # With method 3-4-Ss (limiting case), the following maximum relative # errors were observed: # for dt = 0.0011: 6.96E-08 # for dt = 0.00073: 5.90E-09 # Reported relative errors motivate constant factor of 3.5e3 for early # consistency threshold code = stepper.generate() from dagrt.function_registry import ( base_function_registry, register_ode_rhs) freg = base_function_registry for func_name in [ "<func>s2s", "<func>f2s", "<func>s2f", "<func>f2f", ]: component_id = { "s": "slow", "f": "fast", }[func_name[-1]] freg = register_ode_rhs(freg, identifier=func_name, output_type_id=component_id, input_type_ids=("slow", "fast"), input_names=("s", "f")) freg = freg.register_codegen("<func>s2f", "fortran", f.CallCode(""" ${result} = (sin(2*${t}) - 1)*${s} """)) freg = freg.register_codegen("<func>f2s", "fortran", f.CallCode(""" ${result} = (sin(2*${t}) + 1)*${f} """)) freg = freg.register_codegen("<func>f2f", "fortran", f.CallCode(""" ${result} = cos(2*${t})*${f} """)) freg = freg.register_codegen("<func>s2s", "fortran", f.CallCode(""" ${result} = -cos(2*${t})*${s} """)) codegen = f.CodeGenerator( "MRAB", user_type_map={ "slow": f.ArrayType( (1,), f.BuiltinType('real (kind=8)'), ), "fast": f.ArrayType( (1,), f.BuiltinType('real (kind=8)'), ) }, function_registry=freg, module_preamble=""" ! lines copied to the start of the module, e.g. to say: ! use ModStuff """) code_str = codegen(code) # Build in conditionals to alter the timestep based on order such that all # tests pass if min_order == 2: fac = 200 elif min_order == 3: fac = 100 else: fac = 12 num_trips_one = 100*fac num_trips_two = 150*fac run_fortran([ ("abmethod.f90", code_str), ("test_mrab_squarewave.f90", ( read_file("test_mrab_squarewave.f90") .replace("MIN_ORDER", str(min_order - 0.3)+"d0") .replace("NUM_TRIPS_ONE", str(num_trips_one)) .replace("NUM_TRIPS_TWO", str(num_trips_two)))), ], fortran_libraries=["lapack", "blas"])
def test_multirate_codegen(min_order, method_name): from leap.multistep.multirate import TwoRateAdamsBashforthMethodBuilder stepper = TwoRateAdamsBashforthMethodBuilder( method_name, min_order, 4, slow_state_filter_name="slow_filt", fast_state_filter_name="fast_filt", # should pass with either, let's alternate by order # static_dt=True is 'more finnicky', so use that at min_order=5. static_dt=True if min_order % 2 == 1 else False, hist_consistency_threshold=1e-8, early_hist_consistency_threshold="1.25e3 * <dt>**%d" % min_order) # Early consistency threshold checked for convergence # with timestep change - C. Mikida, 2/6/18 (commit hash 2e6ca077) # With method 5-Fqs (limiting case), the following maximum relative # errors were observed: # for dt = 0.0384: 1.03E-04 # for dt = 0.0128: 5.43E-08 # Reported relative errors motivate constant factor of 1.25e3 for early # consistency threshold code = stepper.generate() from dagrt.function_registry import ( base_function_registry, register_ode_rhs, UserType, register_function) freg = base_function_registry for func_name in [ "<func>s2s", "<func>f2s", "<func>s2f", "<func>f2f", ]: component_id = { "s": "slow", "f": "fast", }[func_name[-1]] freg = register_ode_rhs(freg, identifier=func_name, output_type_id=component_id, input_type_ids=("slow", "fast"), input_names=("s", "f")) freg = freg.register_codegen("<func>s2f", "fortran", f.CallCode(""" ${result} = (sin(2d0*${t}) - 1d0)*${s} """)) freg = freg.register_codegen("<func>f2s", "fortran", f.CallCode(""" ${result} = (sin(2d0*${t}) + 1d0)*${f} """)) freg = freg.register_codegen("<func>f2f", "fortran", f.CallCode(""" ${result} = cos(2d0*${t})*${f} """)) freg = freg.register_codegen("<func>s2s", "fortran", f.CallCode(""" ${result} = -cos(2d0*${t})*${s} """)) freg = register_function(freg, "<func>slow_filt", ("arg",), result_names=("result",), result_kinds=(UserType("slow"),)) freg = freg.register_codegen("<func>slow_filt", "fortran", f.CallCode(""" ! mess with state ${result} = ${arg} """)) freg = register_function(freg, "<func>fast_filt", ("arg",), result_names=("result",), result_kinds=(UserType("fast"),)) freg = freg.register_codegen("<func>fast_filt", "fortran", f.CallCode(""" ! mess with state ${result} = ${arg} """)) codegen = f.CodeGenerator( "MRAB", user_type_map={ "slow": f.ArrayType( (1,), f.BuiltinType('real (kind=8)'), ), "fast": f.ArrayType( (1,), f.BuiltinType('real (kind=8)'), ) }, function_registry=freg, module_preamble=""" ! lines copied to the start of the module, e.g. to say: ! use ModStuff """) code_str = codegen(code) if 0: with open("abmethod.f90", "wt") as outf: outf.write(code_str) fac = 130 if min_order == 5 and method_name in ["Srs", "Ss"]: pytest.xfail("Srs,Ss do not achieve fifth order convergence") num_trips_one = 10*fac num_trips_two = 30*fac run_fortran([ ("abmethod.f90", code_str), ("test_mrab.f90", ( read_file("test_mrab.f90") .replace("MIN_ORDER", str(min_order - 0.3)+"d0") .replace("NUM_TRIPS_ONE", str(num_trips_one)) .replace("NUM_TRIPS_TWO", str(num_trips_two)))), ], fortran_libraries=["lapack", "blas"])
def test_rk_codegen_fancy(): """Test whether Fortran code generation with lots of fancy features for the Runge-Kutta timestepper works. """ component_id = 'y' rhs_function = '<func>y' state_filter_name = 'state_filter_y' stepper = ODE23MethodBuilder(component_id, use_high_order=True, state_filter_name=state_filter_name) from dagrt.function_registry import ( base_function_registry, register_ode_rhs, register_function, UserType) freg = register_ode_rhs(base_function_registry, component_id, identifier=rhs_function) freg = freg.register_codegen(rhs_function, "fortran", f.CallCode(""" <% igrid = declare_new("integer", "igrid") i = declare_new("integer", "i") %> do ${igrid} = 1, region%n_grids do ${i} = 1, region%n_grid_dofs(${igrid}) ${result}(${igrid})%conserved_var(${i}) = & -2*${y}(${igrid})%conserved_var(${i}) end do end do """)) freg = register_function(freg, "notify_pre_state_update", ("updated_component",)) freg = freg.register_codegen("notify_pre_state_update", "fortran", f.CallCode(""" write(*,*) 'before state update' """)) freg = register_function( freg, "notify_post_state_update", ("updated_component",)) freg = freg.register_codegen("notify_post_state_update", "fortran", f.CallCode(""" write(*,*) 'after state update' """)) freg = register_function(freg, "<func>"+state_filter_name, ("y",), result_names=("result",), result_kinds=(UserType("y"),)) freg = freg.register_codegen("<func>"+state_filter_name, "fortran", f.CallCode(""" ! mess with state <% igrid = declare_new("integer", "igrid") i = declare_new("integer", "i") %> do ${igrid} = 1, region%n_grids do ${i} = 1, region%n_grid_dofs(${igrid}) ${result}(${igrid})%conserved_var(${i}) = & 0.95*${y}(${igrid})%conserved_var(${i}) end do end do """)) code = stepper.generate() codegen = f.CodeGenerator( "RKMethod", user_type_map={ component_id: f.ArrayType( "region%n_grids", index_vars="igrid", element_type=f.StructureType( "sim_grid_state_type", ( ("conserved_var", f.PointerType( f.ArrayType( ("region%n_grid_dofs(igrid)",), f.BuiltinType('real (kind=8)')))), ))) }, function_registry=freg, module_preamble=""" use sim_types use timing """, call_before_state_update="notify_pre_state_update", call_after_state_update="notify_post_state_update", extra_arguments="region", extra_argument_decl=""" type(region_type), pointer :: region """, parallel_do_preamble="!dir$ simd", emit_instrumentation=True, timing_function="get_time") code_str = codegen(code) print(code_str) run_fortran([ ("sim_types.f90", read_file("sim_types.f90")), ("timing.f90", read_file("timing.f90")), ("rkmethod.f90", code_str), ("test_fancy_rk.f90", read_file("test_fancy_rk.f90")), ])