Exemple #1
0
def main(ctx_factory=cl.create_some_context, use_logmgr=True,
         use_leap=False, use_profiling=False, casename=None,
         rst_filename=None, actx_class=PyOpenCLArrayContext,
         log_dependent=True):
    """Drive example."""
    cl_ctx = ctx_factory()

    if casename is None:
        casename = "mirgecom"

    from mpi4py import MPI
    comm = MPI.COMM_WORLD
    rank = comm.Get_rank()
    nparts = comm.Get_size()

    from mirgecom.simutil import global_reduce as _global_reduce
    global_reduce = partial(_global_reduce, comm=comm)

    logmgr = initialize_logmgr(use_logmgr,
        filename=f"{casename}.sqlite", mode="wu", mpi_comm=comm)

    if use_profiling:
        queue = cl.CommandQueue(
            cl_ctx, properties=cl.command_queue_properties.PROFILING_ENABLE)
    else:
        queue = cl.CommandQueue(cl_ctx)

    actx = actx_class(
        queue,
        allocator=cl_tools.MemoryPool(cl_tools.ImmediateAllocator(queue)))

    # timestepping control
    if use_leap:
        from leap.rk import RK4MethodBuilder
        timestepper = RK4MethodBuilder("state")
    else:
        timestepper = rk4_step
    t_final = 1e-8
    current_cfl = 1.0
    current_dt = 1e-9
    current_t = 0
    current_step = 0
    constant_cfl = False

    # some i/o frequencies
    nstatus = 1
    nhealth = 1
    nrestart = 5
    nviz = 1

    dim = 2
    rst_path = "restart_data/"
    rst_pattern = (
        rst_path + "{cname}-{step:04d}-{rank:04d}.pkl"
    )
    if rst_filename:  # read the grid from restart data
        rst_filename = f"{rst_filename}-{rank:04d}.pkl"
        from mirgecom.restart import read_restart_data
        restart_data = read_restart_data(actx, rst_filename)
        local_mesh = restart_data["local_mesh"]
        local_nelements = local_mesh.nelements
        global_nelements = restart_data["global_nelements"]
        assert restart_data["num_parts"] == nparts
    else:  # generate the grid from scratch
        nel_1d = 16
        box_ll = -5.0
        box_ur = 5.0
        from meshmode.mesh.generation import generate_regular_rect_mesh
        generate_mesh = partial(generate_regular_rect_mesh, a=(box_ll,)*dim,
                                b=(box_ur,) * dim, nelements_per_axis=(nel_1d,)*dim)
        local_mesh, global_nelements = generate_and_distribute_mesh(comm,
                                                                    generate_mesh)
        local_nelements = local_mesh.nelements

    order = 3
    discr = EagerDGDiscretization(
        actx, local_mesh, order=order, mpi_communicator=comm
    )
    nodes = thaw(discr.nodes(), actx)

    vis_timer = None

    if logmgr:
        logmgr_add_device_name(logmgr, queue)
        logmgr_add_device_memory_usage(logmgr, queue)

        vis_timer = IntervalTimer("t_vis", "Time spent visualizing")
        logmgr.add_quantity(vis_timer)

        logmgr.add_watches([
            ("step.max", "step = {value}, "),
            ("t_sim.max", "sim time: {value:1.6e} s\n"),
            ("t_step.max", "------- step walltime: {value:6g} s, "),
            ("t_log.max", "log walltime: {value:6g} s")
        ])

        if log_dependent:
            logmgr_add_many_discretization_quantities(logmgr, discr, dim,
                                                      extract_vars_for_logging,
                                                      units_for_logging)
            logmgr.add_watches([
                ("min_pressure", "\n------- P (min, max) (Pa) = ({value:1.9e}, "),
                ("max_pressure",    "{value:1.9e})\n"),
                ("min_temperature", "------- T (min, max) (K)  = ({value:7g}, "),
                ("max_temperature",    "{value:7g})\n")])

    # Pyrometheus initialization
    from mirgecom.mechanisms import get_mechanism_cti
    mech_cti = get_mechanism_cti("uiuc")
    sol = cantera.Solution(phase_id="gas", source=mech_cti)
    from mirgecom.thermochemistry import make_pyrometheus_mechanism_class
    pyrometheus_mechanism = make_pyrometheus_mechanism_class(sol)(actx.np)

    nspecies = pyrometheus_mechanism.num_species
    eos = PyrometheusMixture(pyrometheus_mechanism)
    from mirgecom.gas_model import GasModel, make_fluid_state
    gas_model = GasModel(eos=eos)
    from pytools.obj_array import make_obj_array

    y0s = np.zeros(shape=(nspecies,))
    for i in range(nspecies-1):
        y0s[i] = 1.0 / (10.0 ** (i + 1))
    spec_sum = sum([y0s[i] for i in range(nspecies-1)])
    y0s[nspecies-1] = 1.0 - spec_sum

    # Mixture defaults to STP (p, T) = (1atm, 300K)
    velocity = np.zeros(shape=(dim,)) + 1.0
    initializer = MixtureInitializer(dim=dim, nspecies=nspecies,
                                     massfractions=y0s, velocity=velocity)

    def boundary_solution(discr, btag, gas_model, state_minus, **kwargs):
        actx = state_minus.array_context
        bnd_discr = discr.discr_from_dd(btag)
        nodes = thaw(bnd_discr.nodes(), actx)
        return make_fluid_state(initializer(x_vec=nodes, eos=gas_model.eos,
                                            **kwargs), gas_model,
                                temperature_seed=state_minus.temperature)

    boundaries = {
        BTAG_ALL: PrescribedFluidBoundary(boundary_state_func=boundary_solution)
    }

    if rst_filename:
        current_t = restart_data["t"]
        current_step = restart_data["step"]
        current_cv = restart_data["cv"]
        tseed = restart_data["temperature_seed"]
        if logmgr:
            from mirgecom.logging_quantities import logmgr_set_time
            logmgr_set_time(logmgr, current_step, current_t)
    else:
        # Set the current state from time 0
        current_cv = initializer(x_vec=nodes, eos=eos)
        tseed = 300.0

    current_state = make_fluid_state(current_cv, gas_model, temperature_seed=tseed)

    visualizer = make_visualizer(discr)
    initname = initializer.__class__.__name__
    eosname = eos.__class__.__name__
    init_message = make_init_message(dim=dim, order=order,
                                     nelements=local_nelements,
                                     global_nelements=global_nelements,
                                     dt=current_dt, t_final=t_final, nstatus=nstatus,
                                     nviz=nviz, cfl=current_cfl,
                                     constant_cfl=constant_cfl, initname=initname,
                                     eosname=eosname, casename=casename)
    if rank == 0:
        logger.info(init_message)

    def my_write_status(component_errors, dv=None):
        from mirgecom.simutil import allsync
        status_msg = (
            "------- errors="
            + ", ".join("%.3g" % en for en in component_errors))
        if ((dv is not None) and (not log_dependent)):
            temp = dv.temperature
            press = dv.pressure

            from grudge.op import nodal_min_loc, nodal_max_loc
            tmin = allsync(actx.to_numpy(nodal_min_loc(discr, "vol", temp)),
                           comm=comm, op=MPI.MIN)
            tmax = allsync(actx.to_numpy(nodal_max_loc(discr, "vol", temp)),
                           comm=comm, op=MPI.MAX)
            pmin = allsync(actx.to_numpy(nodal_min_loc(discr, "vol", press)),
                           comm=comm, op=MPI.MIN)
            pmax = allsync(actx.to_numpy(nodal_max_loc(discr, "vol", press)),
                           comm=comm, op=MPI.MAX)
            dv_status_msg = f"\nP({pmin}, {pmax}), T({tmin}, {tmax})"
            status_msg = status_msg + dv_status_msg

        if rank == 0:
            logger.info(status_msg)
        if rank == 0:
            logger.info(status_msg)

    def my_write_viz(step, t, state, dv, exact=None, resid=None):
        if exact is None:
            exact = initializer(x_vec=nodes, eos=eos, time=t)
        if resid is None:
            resid = state - exact
        viz_fields = [("cv", state), ("dv", dv)]
        from mirgecom.simutil import write_visfile
        write_visfile(discr, viz_fields, visualizer, vizname=casename,
                      step=step, t=t, overwrite=True, vis_timer=vis_timer)

    def my_write_restart(step, t, state, tseed):
        rst_fname = rst_pattern.format(cname=casename, step=step, rank=rank)
        if rst_fname != rst_filename:
            rst_data = {
                "local_mesh": local_mesh,
                "cv": state,
                "temperature_seed": tseed,
                "t": t,
                "step": step,
                "order": order,
                "global_nelements": global_nelements,
                "num_parts": nparts
            }
            from mirgecom.restart import write_restart_file
            write_restart_file(actx, rst_data, rst_fname, comm)

    def my_health_check(dv, component_errors):
        health_error = False
        from mirgecom.simutil import check_naninf_local, check_range_local
        if check_naninf_local(discr, "vol", dv.pressure) \
           or check_range_local(discr, "vol", dv.pressure, 1e5, 1.1e5):
            health_error = True
            logger.info(f"{rank=}: Invalid pressure data found.")

        exittol = .09
        if max(component_errors) > exittol:
            health_error = True
            if rank == 0:
                logger.info("Solution diverged from exact soln.")

        return health_error

    def my_pre_step(step, t, dt, state):
        cv, tseed = state
        fluid_state = make_fluid_state(cv, gas_model, temperature_seed=tseed)
        dv = fluid_state.dv

        try:
            exact = None
            component_errors = None

            if logmgr:
                logmgr.tick_before()

            from mirgecom.simutil import check_step
            do_viz = check_step(step=step, interval=nviz)
            do_restart = check_step(step=step, interval=nrestart)
            do_health = check_step(step=step, interval=nhealth)
            do_status = check_step(step=step, interval=nstatus)

            if do_health:
                exact = initializer(x_vec=nodes, eos=eos, time=t)
                from mirgecom.simutil import compare_fluid_solutions
                component_errors = compare_fluid_solutions(discr, cv, exact)
                health_errors = global_reduce(
                    my_health_check(dv, component_errors), op="lor")
                if health_errors:
                    if rank == 0:
                        logger.info("Fluid solution failed health check.")
                    raise MyRuntimeError("Failed simulation health check.")

            if do_restart:
                my_write_restart(step=step, t=t, state=cv, tseed=tseed)

            if do_viz:
                if exact is None:
                    exact = initializer(x_vec=nodes, eos=eos, time=t)
                resid = state - exact
                my_write_viz(step=step, t=t, state=cv, dv=dv, exact=exact,
                             resid=resid)

            if do_status:
                if component_errors is None:
                    if exact is None:
                        exact = initializer(x_vec=nodes, eos=eos, time=t)
                    from mirgecom.simutil import compare_fluid_solutions
                    component_errors = compare_fluid_solutions(discr, cv, exact)
                my_write_status(component_errors, dv=dv)

        except MyRuntimeError:
            if rank == 0:
                logger.info("Errors detected; attempting graceful exit.")
            my_write_viz(step=step, t=t, state=cv, dv=dv)
            my_write_restart(step=step, t=t, state=cv, tseed=tseed)
            raise

        dt = get_sim_timestep(discr, fluid_state, t, dt, current_cfl, t_final,
                              constant_cfl)
        return state, dt

    def my_post_step(step, t, dt, state):
        cv, tseed = state
        fluid_state = make_fluid_state(cv, gas_model, temperature_seed=tseed)
        tseed = fluid_state.temperature
        # Logmgr needs to know about EOS, dt, dim?
        # imo this is a design/scope flaw
        if logmgr:
            set_dt(logmgr, dt)
            set_sim_state(logmgr, dim, cv, eos)
            logmgr.tick_after()
        return make_obj_array([fluid_state.cv, tseed]), dt

    def my_rhs(t, state):
        cv, tseed = state
        fluid_state = make_fluid_state(cv, gas_model, temperature_seed=tseed)
        return make_obj_array(
            [euler_operator(discr, state=fluid_state, time=t,
                            boundaries=boundaries, gas_model=gas_model),
             0*tseed])

    current_dt = get_sim_timestep(discr, current_state, current_t, current_dt,
                                  current_cfl, t_final, constant_cfl)

    current_step, current_t, advanced_state = \
        advance_state(rhs=my_rhs, timestepper=timestepper,
                      pre_step_callback=my_pre_step,
                      post_step_callback=my_post_step, dt=current_dt,
                      state=make_obj_array([current_state.cv,
                                            current_state.temperature]),
                      t=current_t, t_final=t_final, eos=eos, dim=dim)

    # Dump the final data
    if rank == 0:
        logger.info("Checkpointing final state ...")

    current_cv, tseed = advanced_state
    current_state = make_fluid_state(current_cv, gas_model, temperature_seed=tseed)
    final_dv = current_state.dv
    final_exact = initializer(x_vec=nodes, eos=eos, time=current_t)
    final_resid = current_state.cv - final_exact
    my_write_viz(step=current_step, t=current_t, state=current_cv, dv=final_dv,
                 exact=final_exact, resid=final_resid)
    my_write_restart(step=current_step, t=current_t, state=current_state.cv,
                     tseed=tseed)

    if logmgr:
        logmgr.close()
    elif use_profiling:
        print(actx.tabulate_profiling_data())

    finish_tol = 1e-16
    assert np.abs(current_t - t_final) < finish_tol
Exemple #2
0
        SSPRK22MethodBuilder,
        SSPRK33MethodBuilder,
    )
    from leap.rk.imex import KennedyCarpenterIMEXARK4MethodBuilder
    from mirgecom.steppers import advance_state

    @pytest.mark.parametrize(("method", "method_order"), [
        (ODE23MethodBuilder("y", use_high_order=False), 2),
        (ODE23MethodBuilder("y", use_high_order=True), 3),
        (ODE45MethodBuilder("y", use_high_order=False), 4),
        (ODE45MethodBuilder("y", use_high_order=True), 5),
        (ForwardEulerMethodBuilder("y"), 1),
        (MidpointMethodBuilder("y"), 2),
        (HeunsMethodBuilder("y"), 2),
        (RK3MethodBuilder("y"), 3),
        (RK4MethodBuilder("y"), 4),
        (RK5MethodBuilder("y"), 5),
        (LSRK4MethodBuilder("y"), 4),
        (KennedyCarpenterIMEXARK4MethodBuilder(
            "y", use_implicit=False, explicit_rhs_name="y"), 4),
        (SSPRK22MethodBuilder("y"), 2),
        (SSPRK33MethodBuilder("y"), 3),
    ])
    def test_leapgen_integration_order(method, method_order):
        """Test that time integrators have correct order."""
        def exact_soln(t):
            return np.exp(-t)

        def rhs(t, y):
            return -np.exp(-t)
def main(ctx_factory=cl.create_some_context,
         use_logmgr=True,
         use_leap=False,
         use_profiling=False,
         casename=None,
         rst_filename=None,
         actx_class=PyOpenCLArrayContext):
    """Drive example."""
    cl_ctx = ctx_factory()

    if casename is None:
        casename = "mirgecom"

    from mpi4py import MPI
    comm = MPI.COMM_WORLD
    rank = comm.Get_rank()
    nproc = comm.Get_size()

    logmgr = initialize_logmgr(use_logmgr,
                               filename=f"{casename}.sqlite",
                               mode="wu",
                               mpi_comm=comm)

    if use_profiling:
        queue = cl.CommandQueue(
            cl_ctx, properties=cl.command_queue_properties.PROFILING_ENABLE)
    else:
        queue = cl.CommandQueue(cl_ctx)

    actx = actx_class(queue,
                      allocator=cl_tools.MemoryPool(
                          cl_tools.ImmediateAllocator(queue)))

    # Some discretization parameters
    dim = 2
    nel_1d = 8
    order = 1

    # {{{ Time stepping control

    # This example runs only 3 steps by default (to keep CI ~short)
    # With the mixture defined below, equilibrium is achieved at ~40ms
    # To run to equlibrium, set t_final >= 40ms.

    # Time stepper selection
    if use_leap:
        from leap.rk import RK4MethodBuilder
        timestepper = RK4MethodBuilder("state")
    else:
        timestepper = rk4_step

    # Time loop control parameters
    current_step = 0
    t_final = 1e-8
    current_cfl = 1.0
    current_dt = 1e-9
    current_t = 0
    constant_cfl = False

    # i.o frequencies
    nstatus = 1
    nviz = 5
    nhealth = 1
    nrestart = 5

    # }}}  Time stepping control

    debug = False

    rst_path = "restart_data/"
    rst_pattern = (rst_path + "{cname}-{step:04d}-{rank:04d}.pkl")
    if rst_filename:  # read the grid from restart data
        rst_filename = f"{rst_filename}-{rank:04d}.pkl"

        from mirgecom.restart import read_restart_data
        restart_data = read_restart_data(actx, rst_filename)
        local_mesh = restart_data["local_mesh"]
        local_nelements = local_mesh.nelements
        global_nelements = restart_data["global_nelements"]
        assert restart_data["num_parts"] == nproc
        rst_time = restart_data["t"]
        rst_step = restart_data["step"]
        rst_order = restart_data["order"]
    else:  # generate the grid from scratch
        from meshmode.mesh.generation import generate_regular_rect_mesh
        box_ll = -0.005
        box_ur = 0.005
        generate_mesh = partial(generate_regular_rect_mesh,
                                a=(box_ll, ) * dim,
                                b=(box_ur, ) * dim,
                                nelements_per_axis=(nel_1d, ) * dim)
        local_mesh, global_nelements = generate_and_distribute_mesh(
            comm, generate_mesh)
        local_nelements = local_mesh.nelements

    discr = EagerDGDiscretization(actx,
                                  local_mesh,
                                  order=order,
                                  mpi_communicator=comm)
    nodes = thaw(actx, discr.nodes())

    vis_timer = None

    if logmgr:
        logmgr_add_device_name(logmgr, queue)
        logmgr_add_device_memory_usage(logmgr, queue)
        logmgr_add_many_discretization_quantities(logmgr, discr, dim,
                                                  extract_vars_for_logging,
                                                  units_for_logging)

        vis_timer = IntervalTimer("t_vis", "Time spent visualizing")
        logmgr.add_quantity(vis_timer)

        logmgr.add_watches([
            ("step.max", "step = {value}, "),
            ("t_sim.max", "sim time: {value:1.6e} s\n"),
            ("min_pressure", "------- P (min, max) (Pa) = ({value:1.9e}, "),
            ("max_pressure", "{value:1.9e})\n"),
            ("min_temperature", "------- T (min, max) (K)  = ({value:7g}, "),
            ("max_temperature", "{value:7g})\n"),
            ("t_step.max", "------- step walltime: {value:6g} s, "),
            ("t_log.max", "log walltime: {value:6g} s")
        ])

    # {{{  Set up initial state using Cantera

    # Use Cantera for initialization
    # -- Pick up a CTI for the thermochemistry config
    # --- Note: Users may add their own CTI file by dropping it into
    # ---       mirgecom/mechanisms alongside the other CTI files.
    from mirgecom.mechanisms import get_mechanism_cti
    mech_cti = get_mechanism_cti("uiuc")

    cantera_soln = cantera.Solution(phase_id="gas", source=mech_cti)
    nspecies = cantera_soln.n_species

    # Initial temperature, pressure, and mixutre mole fractions are needed to
    # set up the initial state in Cantera.
    init_temperature = 1500.0  # Initial temperature hot enough to burn
    # Parameters for calculating the amounts of fuel, oxidizer, and inert species
    equiv_ratio = 1.0
    ox_di_ratio = 0.21
    stoich_ratio = 3.0
    # Grab the array indices for the specific species, ethylene, oxygen, and nitrogen
    i_fu = cantera_soln.species_index("C2H4")
    i_ox = cantera_soln.species_index("O2")
    i_di = cantera_soln.species_index("N2")
    x = np.zeros(nspecies)
    # Set the species mole fractions according to our desired fuel/air mixture
    x[i_fu] = (ox_di_ratio * equiv_ratio) / (stoich_ratio +
                                             ox_di_ratio * equiv_ratio)
    x[i_ox] = stoich_ratio * x[i_fu] / equiv_ratio
    x[i_di] = (1.0 - ox_di_ratio) * x[i_ox] / ox_di_ratio
    # Uncomment next line to make pylint fail when it can't find cantera.one_atm
    one_atm = cantera.one_atm  # pylint: disable=no-member
    # one_atm = 101325.0

    # Let the user know about how Cantera is being initilized
    print(f"Input state (T,P,X) = ({init_temperature}, {one_atm}, {x}")
    # Set Cantera internal gas temperature, pressure, and mole fractios
    cantera_soln.TPX = init_temperature, one_atm, x
    # Pull temperature, total density, mass fractions, and pressure from Cantera
    # We need total density, and mass fractions to initialize the fluid/gas state.
    can_t, can_rho, can_y = cantera_soln.TDY
    can_p = cantera_soln.P
    # *can_t*, *can_p* should not differ (significantly) from user's initial data,
    # but we want to ensure that we use exactly the same starting point as Cantera,
    # so we use Cantera's version of these data.

    # }}}

    # {{{ Create Pyrometheus thermochemistry object & EOS

    # Create a Pyrometheus EOS with the Cantera soln. Pyrometheus uses Cantera and
    # generates a set of methods to calculate chemothermomechanical properties and
    # states for this particular mechanism.
    pyrometheus_mechanism = pyro.get_thermochem_class(cantera_soln)(actx.np)
    eos = PyrometheusMixture(pyrometheus_mechanism,
                             temperature_guess=init_temperature)

    # }}}

    # {{{ MIRGE-Com state initialization

    # Initialize the fluid/gas state with Cantera-consistent data:
    # (density, pressure, temperature, mass_fractions)
    print(f"Cantera state (rho,T,P,Y) = ({can_rho}, {can_t}, {can_p}, {can_y}")
    velocity = np.zeros(shape=(dim, ))
    initializer = MixtureInitializer(dim=dim,
                                     nspecies=nspecies,
                                     pressure=can_p,
                                     temperature=can_t,
                                     massfractions=can_y,
                                     velocity=velocity)

    my_boundary = AdiabaticSlipBoundary()
    boundaries = {BTAG_ALL: my_boundary}

    if rst_filename:
        current_step = rst_step
        current_t = rst_time
        if logmgr:
            from mirgecom.logging_quantities import logmgr_set_time
            logmgr_set_time(logmgr, current_step, current_t)
        if order == rst_order:
            current_state = restart_data["state"]
        else:
            rst_state = restart_data["state"]
            old_discr = EagerDGDiscretization(actx,
                                              local_mesh,
                                              order=rst_order,
                                              mpi_communicator=comm)
            from meshmode.discretization.connection import make_same_mesh_connection
            connection = make_same_mesh_connection(
                actx, discr.discr_from_dd("vol"),
                old_discr.discr_from_dd("vol"))
            current_state = connection(rst_state)
    else:
        # Set the current state from time 0
        current_state = initializer(eos=eos, x_vec=nodes)

    # Inspection at physics debugging time
    if debug:
        print("Initial MIRGE-Com state:")
        print(f"{current_state=}")
        print(f"Initial DV pressure: {eos.pressure(current_state)}")
        print(f"Initial DV temperature: {eos.temperature(current_state)}")

    # }}}

    visualizer = make_visualizer(discr)
    initname = initializer.__class__.__name__
    eosname = eos.__class__.__name__
    init_message = make_init_message(dim=dim,
                                     order=order,
                                     nelements=local_nelements,
                                     global_nelements=global_nelements,
                                     dt=current_dt,
                                     t_final=t_final,
                                     nstatus=nstatus,
                                     nviz=nviz,
                                     cfl=current_cfl,
                                     constant_cfl=constant_cfl,
                                     initname=initname,
                                     eosname=eosname,
                                     casename=casename)

    # Cantera equilibrate calculates the expected end state @ chemical equilibrium
    # i.e. the expected state after all reactions
    cantera_soln.equilibrate("UV")
    eq_temperature, eq_density, eq_mass_fractions = cantera_soln.TDY
    eq_pressure = cantera_soln.P

    # Report the expected final state to the user
    if rank == 0:
        logger.info(init_message)
        logger.info(f"Expected equilibrium state:"
                    f" {eq_pressure=}, {eq_temperature=},"
                    f" {eq_density=}, {eq_mass_fractions=}")

    def my_write_status(dt, cfl):
        status_msg = f"------ {dt=}" if constant_cfl else f"----- {cfl=}"
        if rank == 0:
            logger.info(status_msg)

    def my_write_viz(step,
                     t,
                     dt,
                     state,
                     ts_field=None,
                     dv=None,
                     production_rates=None,
                     cfl=None):
        if dv is None:
            dv = eos.dependent_vars(state)
        if production_rates is None:
            production_rates = eos.get_production_rates(state)
        if ts_field is None:
            ts_field, cfl, dt = my_get_timestep(t=t, dt=dt, state=state)
        viz_fields = [("cv", state), ("dv", dv),
                      ("production_rates", production_rates),
                      ("dt" if constant_cfl else "cfl", ts_field)]
        write_visfile(discr,
                      viz_fields,
                      visualizer,
                      vizname=casename,
                      step=step,
                      t=t,
                      overwrite=True,
                      vis_timer=vis_timer)

    def my_write_restart(step, t, state):
        rst_fname = rst_pattern.format(cname=casename, step=step, rank=rank)
        if rst_fname == rst_filename:
            if rank == 0:
                logger.info("Skipping overwrite of restart file.")
        else:
            rst_data = {
                "local_mesh": local_mesh,
                "state": state,
                "t": t,
                "step": step,
                "order": order,
                "global_nelements": global_nelements,
                "num_parts": nproc
            }
            from mirgecom.restart import write_restart_file
            write_restart_file(actx, rst_data, rst_fname, comm)

    def my_health_check(dv):
        health_error = False
        from mirgecom.simutil import check_naninf_local, check_range_local
        if check_naninf_local(discr, "vol", dv.pressure) \
           or check_range_local(discr, "vol", dv.pressure, 1e5, 2.4e5):
            health_error = True
            logger.info(f"{rank=}: Invalid pressure data found.")

        if check_range_local(discr, "vol", dv.temperature, 1.498e3, 1.52e3):
            health_error = True
            logger.info(f"{rank=}: Invalid temperature data found.")

        return health_error

    def my_get_timestep(t, dt, state):
        #  richer interface to calculate {dt,cfl} returns node-local estimates
        t_remaining = max(0, t_final - t)
        if constant_cfl:
            from mirgecom.inviscid import get_inviscid_timestep
            ts_field = current_cfl * get_inviscid_timestep(
                discr, eos=eos, cv=state)
            from grudge.op import nodal_min
            dt = nodal_min(discr, "vol", ts_field)
            cfl = current_cfl
        else:
            from mirgecom.inviscid import get_inviscid_cfl
            ts_field = get_inviscid_cfl(discr, eos=eos, dt=dt, cv=state)
            from grudge.op import nodal_max
            cfl = nodal_max(discr, "vol", ts_field)

        return ts_field, cfl, min(t_remaining, dt)

    def my_pre_step(step, t, dt, state):
        try:
            dv = None

            if logmgr:
                logmgr.tick_before()

            from mirgecom.simutil import check_step
            do_viz = check_step(step=step, interval=nviz)
            do_restart = check_step(step=step, interval=nrestart)
            do_health = check_step(step=step, interval=nhealth)
            do_status = check_step(step=step, interval=nstatus)

            if do_health:
                dv = eos.dependent_vars(state)
                from mirgecom.simutil import allsync
                health_errors = allsync(my_health_check(dv), comm, op=MPI.LOR)
                if health_errors:
                    if rank == 0:
                        logger.info("Fluid solution failed health check.")
                    raise MyRuntimeError("Failed simulation health check.")

            ts_field, cfl, dt = my_get_timestep(t=t, dt=dt, state=state)

            if do_status:
                my_write_status(dt, cfl)

            if do_restart:
                my_write_restart(step=step, t=t, state=state)

            if do_viz:
                production_rates = eos.get_production_rates(state)
                if dv is None:
                    dv = eos.dependent_vars(state)
                my_write_viz(step=step,
                             t=t,
                             dt=dt,
                             state=state,
                             dv=dv,
                             production_rates=production_rates,
                             ts_field=ts_field,
                             cfl=cfl)

        except MyRuntimeError:
            if rank == 0:
                logger.info("Errors detected; attempting graceful exit.")
            my_write_viz(step=step, t=t, dt=dt, state=state)
            my_write_restart(step=step, t=t, state=state)
            raise

        return state, dt

    def my_post_step(step, t, dt, state):
        # Logmgr needs to know about EOS, dt, dim?
        # imo this is a design/scope flaw
        if logmgr:
            set_dt(logmgr, dt)
            set_sim_state(logmgr, dim, state, eos)
            logmgr.tick_after()
        return state, dt

    def my_rhs(t, state):
        return (euler_operator(
            discr, cv=state, time=t, boundaries=boundaries, eos=eos) +
                eos.get_species_source_terms(state))

    current_dt = get_sim_timestep(discr, current_state, current_t, current_dt,
                                  current_cfl, eos, t_final, constant_cfl)

    current_step, current_t, current_state = \
        advance_state(rhs=my_rhs, timestepper=timestepper,
                      pre_step_callback=my_pre_step,
                      post_step_callback=my_post_step, dt=current_dt,
                      state=current_state, t=current_t, t_final=t_final)

    # Dump the final data
    if rank == 0:
        logger.info("Checkpointing final state ...")

    final_dv = eos.dependent_vars(current_state)
    final_dm = eos.get_production_rates(current_state)
    ts_field, cfl, dt = my_get_timestep(t=current_t,
                                        dt=current_dt,
                                        state=current_state)
    my_write_viz(step=current_step,
                 t=current_t,
                 dt=dt,
                 state=current_state,
                 dv=final_dv,
                 production_rates=final_dm,
                 ts_field=ts_field,
                 cfl=cfl)
    my_write_status(dt=dt, cfl=cfl)
    my_write_restart(step=current_step, t=current_t, state=current_state)

    if logmgr:
        logmgr.close()
    elif use_profiling:
        print(actx.tabulate_profiling_data())

    finish_tol = 1e-16
    assert np.abs(current_t - t_final) < finish_tol
Exemple #4
0
def main(ctx_factory=cl.create_some_context, use_leap=False):
    """Drive the example."""
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue,
                                allocator=cl_tools.MemoryPool(
                                    cl_tools.ImmediateAllocator(queue)))

    logger = logging.getLogger(__name__)

    dim = 2
    nel_1d = 16
    order = 1
    exittol = 2e-2
    exittol = 100.0
    t_final = 0.1
    current_cfl = 1.0
    vel = np.zeros(shape=(dim, ))
    orig = np.zeros(shape=(dim, ))
    #    vel[:dim] = 1.0
    current_dt = .01
    current_t = 0
    eos = IdealSingleGas()
    initializer = Lump(dim=dim, center=orig, velocity=vel, rhoamp=0.0)
    casename = "pulse"
    boundaries = {BTAG_ALL: PrescribedBoundary(initializer)}
    wall = AdiabaticSlipBoundary()
    boundaries = {BTAG_ALL: wall}
    constant_cfl = False
    nstatus = 10
    nviz = 10
    rank = 0
    checkpoint_t = current_t
    current_step = 0
    if use_leap:
        from leap.rk import RK4MethodBuilder
        timestepper = RK4MethodBuilder("state")
    else:
        timestepper = rk4_step
    box_ll = -0.5
    box_ur = 0.5

    from mpi4py import MPI
    comm = MPI.COMM_WORLD
    nproc = comm.Get_size()
    rank = comm.Get_rank()
    num_parts = nproc

    from meshmode.mesh.generation import generate_regular_rect_mesh
    if num_parts > 1:
        generate_mesh = partial(generate_regular_rect_mesh,
                                a=(box_ll, ) * dim,
                                b=(box_ur, ) * dim,
                                nelements_per_axis=(nel_1d, ) * dim)
        local_mesh, global_nelements = generate_and_distribute_mesh(
            comm, generate_mesh)
    else:
        local_mesh = generate_regular_rect_mesh(a=(box_ll, ) * dim,
                                                b=(box_ur, ) * dim,
                                                nelements_per_axis=(nel_1d, ) *
                                                dim)
        global_nelements = local_mesh.nelements
    local_nelements = local_mesh.nelements

    discr = EagerDGDiscretization(actx,
                                  local_mesh,
                                  order=order,
                                  mpi_communicator=comm)
    nodes = thaw(actx, discr.nodes())
    uniform_state = initializer(nodes)
    acoustic_pulse = AcousticPulse(dim=dim,
                                   amplitude=1.0,
                                   width=.1,
                                   center=orig)
    current_state = acoustic_pulse(x_vec=nodes, cv=uniform_state, eos=eos)

    visualizer = make_visualizer(discr)

    initname = "pulse"
    eosname = eos.__class__.__name__
    init_message = make_init_message(dim=dim,
                                     order=order,
                                     nelements=local_nelements,
                                     global_nelements=global_nelements,
                                     dt=current_dt,
                                     t_final=t_final,
                                     nstatus=nstatus,
                                     nviz=nviz,
                                     cfl=current_cfl,
                                     constant_cfl=constant_cfl,
                                     initname=initname,
                                     eosname=eosname,
                                     casename=casename)
    if rank == 0:
        logger.info(init_message)

    get_timestep = partial(inviscid_sim_timestep,
                           discr=discr,
                           t=current_t,
                           dt=current_dt,
                           cfl=current_cfl,
                           eos=eos,
                           t_final=t_final,
                           constant_cfl=constant_cfl)

    def my_rhs(t, state):
        return euler_operator(discr,
                              cv=state,
                              t=t,
                              boundaries=boundaries,
                              eos=eos)

    def my_checkpoint(step, t, dt, state):
        return sim_checkpoint(discr,
                              visualizer,
                              eos,
                              cv=state,
                              vizname=casename,
                              step=step,
                              t=t,
                              dt=dt,
                              nstatus=nstatus,
                              nviz=nviz,
                              exittol=exittol,
                              constant_cfl=constant_cfl,
                              comm=comm)

    try:
        (current_step, current_t, current_state) = \
            advance_state(rhs=my_rhs, timestepper=timestepper,
                checkpoint=my_checkpoint,
                get_timestep=get_timestep, state=current_state,
                t=current_t, t_final=t_final)
    except ExactSolutionMismatch as ex:
        current_step = ex.step
        current_t = ex.t
        current_state = ex.state

    #    if current_t != checkpoint_t:
    if rank == 0:
        logger.info("Checkpointing final state ...")
    my_checkpoint(current_step,
                  t=current_t,
                  dt=(current_t - checkpoint_t),
                  state=current_state)

    if current_t - t_final < 0:
        raise ValueError("Simulation exited abnormally")
Exemple #5
0
def main(ctx_factory=cl.create_some_context,
         casename="autoignition",
         use_leap=False,
         restart_step=None,
         restart_name=None):
    """Drive example."""
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue,
                                allocator=cl_tools.MemoryPool(
                                    cl_tools.ImmediateAllocator(queue)))

    dim = 2
    nel_1d = 8
    order = 1

    # This example runs only 3 steps by default (to keep CI ~short)
    # With the mixture defined below, equilibrium is achieved at ~40ms
    # To run to equlibrium, set t_final >= 40ms.
    t_final = 1e-8
    current_cfl = 1.0
    velocity = np.zeros(shape=(dim, ))
    current_dt = 1e-9
    current_t = 0
    constant_cfl = False
    nstatus = 1
    nviz = 5
    nrestart = 5
    rank = 0
    checkpoint_t = current_t
    current_step = 0
    if use_leap:
        from leap.rk import RK4MethodBuilder
        timestepper = RK4MethodBuilder("state")
    else:
        timestepper = rk4_step
    box_ll = -0.005
    box_ur = 0.005
    error_state = False
    debug = False

    from mpi4py import MPI
    comm = MPI.COMM_WORLD
    rank = comm.Get_rank()
    nproc = comm.Get_size()

    restart_file_pattern = "{casename}-{step:04d}-{rank:04d}.pkl"
    restart_path = "restart_data/"
    if restart_step:
        if not restart_name:
            restart_name = casename
        rst_filename = (restart_path + restart_file_pattern.format(
            casename=restart_name, step=restart_step, rank=rank))
        from mirgecom.restart import read_restart_data
        restart_data = read_restart_data(actx, rst_filename)
        local_mesh = restart_data["local_mesh"]
        local_nelements = local_mesh.nelements
        global_nelements = restart_data["global_nelements"]
        assert restart_data["nparts"] == nproc
    else:
        from meshmode.mesh.generation import generate_regular_rect_mesh
        generate_mesh = partial(generate_regular_rect_mesh,
                                a=(box_ll, ) * dim,
                                b=(box_ur, ) * dim,
                                nelements_per_axis=(nel_1d, ) * dim)
        local_mesh, global_nelements = generate_and_distribute_mesh(
            comm, generate_mesh)
        local_nelements = local_mesh.nelements

    discr = EagerDGDiscretization(actx,
                                  local_mesh,
                                  order=order,
                                  mpi_communicator=comm)
    nodes = thaw(actx, discr.nodes())

    # {{{  Set up initial state using Cantera

    # Use Cantera for initialization
    # -- Pick up a CTI for the thermochemistry config
    # --- Note: Users may add their own CTI file by dropping it into
    # ---       mirgecom/mechanisms alongside the other CTI files.
    from mirgecom.mechanisms import get_mechanism_cti
    mech_cti = get_mechanism_cti("uiuc")

    cantera_soln = cantera.Solution(phase_id="gas", source=mech_cti)
    nspecies = cantera_soln.n_species

    # Initial temperature, pressure, and mixutre mole fractions are needed to
    # set up the initial state in Cantera.
    init_temperature = 1500.0  # Initial temperature hot enough to burn
    # Parameters for calculating the amounts of fuel, oxidizer, and inert species
    equiv_ratio = 1.0
    ox_di_ratio = 0.21
    stoich_ratio = 3.0
    # Grab the array indices for the specific species, ethylene, oxygen, and nitrogen
    i_fu = cantera_soln.species_index("C2H4")
    i_ox = cantera_soln.species_index("O2")
    i_di = cantera_soln.species_index("N2")
    x = np.zeros(nspecies)
    # Set the species mole fractions according to our desired fuel/air mixture
    x[i_fu] = (ox_di_ratio * equiv_ratio) / (stoich_ratio +
                                             ox_di_ratio * equiv_ratio)
    x[i_ox] = stoich_ratio * x[i_fu] / equiv_ratio
    x[i_di] = (1.0 - ox_di_ratio) * x[i_ox] / ox_di_ratio
    # Uncomment next line to make pylint fail when it can't find cantera.one_atm
    one_atm = cantera.one_atm  # pylint: disable=no-member
    # one_atm = 101325.0

    # Let the user know about how Cantera is being initilized
    print(f"Input state (T,P,X) = ({init_temperature}, {one_atm}, {x}")
    # Set Cantera internal gas temperature, pressure, and mole fractios
    cantera_soln.TPX = init_temperature, one_atm, x
    # Pull temperature, total density, mass fractions, and pressure from Cantera
    # We need total density, and mass fractions to initialize the fluid/gas state.
    can_t, can_rho, can_y = cantera_soln.TDY
    can_p = cantera_soln.P
    # *can_t*, *can_p* should not differ (significantly) from user's initial data,
    # but we want to ensure that we use exactly the same starting point as Cantera,
    # so we use Cantera's version of these data.

    # }}}

    # {{{ Create Pyrometheus thermochemistry object & EOS

    # Create a Pyrometheus EOS with the Cantera soln. Pyrometheus uses Cantera and
    # generates a set of methods to calculate chemothermomechanical properties and
    # states for this particular mechanism.
    pyrometheus_mechanism = pyro.get_thermochem_class(cantera_soln)(actx.np)
    eos = PyrometheusMixture(pyrometheus_mechanism,
                             temperature_guess=init_temperature)

    # }}}

    # {{{ MIRGE-Com state initialization

    # Initialize the fluid/gas state with Cantera-consistent data:
    # (density, pressure, temperature, mass_fractions)
    print(f"Cantera state (rho,T,P,Y) = ({can_rho}, {can_t}, {can_p}, {can_y}")
    initializer = MixtureInitializer(dim=dim,
                                     nspecies=nspecies,
                                     pressure=can_p,
                                     temperature=can_t,
                                     massfractions=can_y,
                                     velocity=velocity)

    my_boundary = AdiabaticSlipBoundary()
    boundaries = {BTAG_ALL: my_boundary}

    if restart_step:
        current_t = restart_data["t"]
        current_step = restart_step
        current_state = restart_data["state"]
    else:
        # Set the current state from time 0
        current_state = initializer(eos=eos, x_vec=nodes, t=0)

    # Inspection at physics debugging time
    if debug:
        print("Initial MIRGE-Com state:")
        print(f"{current_state=}")
        print(f"Initial DV pressure: {eos.pressure(current_state)}")
        print(f"Initial DV temperature: {eos.temperature(current_state)}")

    # }}}

    visualizer = make_visualizer(discr)
    initname = initializer.__class__.__name__
    eosname = eos.__class__.__name__
    init_message = make_init_message(dim=dim,
                                     order=order,
                                     nelements=local_nelements,
                                     global_nelements=global_nelements,
                                     dt=current_dt,
                                     t_final=t_final,
                                     nstatus=nstatus,
                                     nviz=nviz,
                                     cfl=current_cfl,
                                     constant_cfl=constant_cfl,
                                     initname=initname,
                                     eosname=eosname,
                                     casename=casename)

    # Cantera equilibrate calculates the expected end state @ chemical equilibrium
    # i.e. the expected state after all reactions
    cantera_soln.equilibrate("UV")
    eq_temperature, eq_density, eq_mass_fractions = cantera_soln.TDY
    eq_pressure = cantera_soln.P

    # Report the expected final state to the user
    if rank == 0:
        logger.info(init_message)
        logger.info(f"Expected equilibrium state:"
                    f" {eq_pressure=}, {eq_temperature=},"
                    f" {eq_density=}, {eq_mass_fractions=}")

    get_timestep = partial(inviscid_sim_timestep,
                           discr=discr,
                           t=current_t,
                           dt=current_dt,
                           cfl=current_cfl,
                           eos=eos,
                           t_final=t_final,
                           constant_cfl=constant_cfl)

    def my_rhs(t, state):
        return (euler_operator(
            discr, cv=state, t=t, boundaries=boundaries, eos=eos) +
                eos.get_species_source_terms(state))

    def my_checkpoint(step, t, dt, state):
        if check_step(step, nrestart) and step != restart_step:
            rst_filename = (restart_path + restart_file_pattern.format(
                casename=casename, step=step, rank=rank))
            rst_data = {
                "local_mesh": local_mesh,
                "state": current_state,
                "t": t,
                "step": step,
                "global_nelements": global_nelements,
                "num_parts": nproc
            }
            from mirgecom.restart import write_restart_file
            write_restart_file(actx, rst_data, rst_filename, comm)

        # awful - computes potentially expensive viz quantities
        #         regardless of whether it is time to viz
        reaction_rates = eos.get_production_rates(state)
        viz_fields = [("reaction_rates", reaction_rates)]
        return sim_checkpoint(discr,
                              visualizer,
                              eos,
                              cv=state,
                              vizname=casename,
                              step=step,
                              t=t,
                              dt=dt,
                              nstatus=nstatus,
                              nviz=nviz,
                              constant_cfl=constant_cfl,
                              comm=comm,
                              viz_fields=viz_fields)

    try:
        (current_step, current_t, current_state) = \
            advance_state(rhs=my_rhs, timestepper=timestepper,
                checkpoint=my_checkpoint,
                get_timestep=get_timestep, state=current_state,
                t=current_t, t_final=t_final)
    except ExactSolutionMismatch as ex:
        error_state = True
        current_step = ex.step
        current_t = ex.t
        current_state = ex.state

    if not check_step(current_step, nviz):  # If final step not an output step
        if rank == 0:
            logger.info("Checkpointing final state ...")
        my_checkpoint(current_step,
                      t=current_t,
                      dt=(current_t - checkpoint_t),
                      state=current_state)

    if current_t - t_final < 0:
        error_state = True

    if error_state:
        raise ValueError("Simulation did not complete successfully.")