Esempio n. 1
0
    def __init__(
        self,
        array_context: ArrayContext,
        mesh: Mesh,
        order=None,
        discr_tag_to_group_factory=None,
        mpi_communicator=None,
        # FIXME: `quad_tag_to_group_factory` is deprecated
        quad_tag_to_group_factory=None):
        """
        :arg discr_tag_to_group_factory: A mapping from discretization tags
            (typically one of: :class:`grudge.dof_desc.DISCR_TAG_BASE`,
            :class:`grudge.dof_desc.DISCR_TAG_MODAL`, or
            :class:`grudge.dof_desc.DISCR_TAG_QUAD`) to a
            :class:`~meshmode.discretization.poly_element.ElementGroupFactory`
            indicating with which type of discretization the operations are
            to be carried out, or *None* to indicate that operations with this
            discretization tag should be carried out with the standard volume
            discretization.
        """

        if (quad_tag_to_group_factory is not None
                and discr_tag_to_group_factory is not None):
            raise ValueError(
                "Both `quad_tag_to_group_factory` and `discr_tag_to_group_factory` "
                "are specified. Use `discr_tag_to_group_factory` instead.")

        # FIXME: `quad_tag_to_group_factory` is deprecated
        if (quad_tag_to_group_factory is not None
                and discr_tag_to_group_factory is None):
            warn(
                "`quad_tag_to_group_factory` is a deprecated kwarg and will "
                "be dropped in version 2022.x. Use `discr_tag_to_group_factory` "
                "instead.",
                DeprecationWarning,
                stacklevel=2)
            discr_tag_to_group_factory = quad_tag_to_group_factory

        self._setup_actx = array_context.clone()

        from meshmode.discretization.poly_element import \
                default_simplex_group_factory

        if discr_tag_to_group_factory is None:
            if order is None:
                raise TypeError(
                    "one of 'order' and 'discr_tag_to_group_factory' must be given"
                )

            discr_tag_to_group_factory = {
                DISCR_TAG_BASE:
                default_simplex_group_factory(base_dim=mesh.dim, order=order)
            }
        else:
            if order is not None:
                discr_tag_to_group_factory = discr_tag_to_group_factory.copy()
                if DISCR_TAG_BASE in discr_tag_to_group_factory:
                    raise ValueError(
                        "if 'order' is given, 'discr_tag_to_group_factory' must "
                        "not have a key of DISCR_TAG_BASE")

                discr_tag_to_group_factory[DISCR_TAG_BASE] = \
                        default_simplex_group_factory(base_dim=mesh.dim, order=order)

        # Modal discr should always come from the base discretization
        discr_tag_to_group_factory[DISCR_TAG_MODAL] = \
            _generate_modal_group_factory(
                discr_tag_to_group_factory[DISCR_TAG_BASE]
            )

        self.discr_tag_to_group_factory = discr_tag_to_group_factory

        from meshmode.discretization import Discretization

        self._volume_discr = Discretization(
            array_context, mesh,
            self.group_factory_for_discretization_tag(DISCR_TAG_BASE))

        # NOTE: Can be removed when symbolics are completely removed
        # {{{ management of discretization-scoped common subexpressions

        from pytools import UniqueNameGenerator
        self._discr_scoped_name_gen = UniqueNameGenerator()

        self._discr_scoped_subexpr_to_name = {}
        self._discr_scoped_subexpr_name_to_value = {}

        # }}}

        self._dist_boundary_connections = \
                self._set_up_distributed_communication(
                        mpi_communicator, array_context)

        self.mpi_communicator = mpi_communicator
Esempio n. 2
0
def main(ctx_factory=cl.create_some_context, use_logmgr=True,
         use_overintegration=False,
         use_leap=False, use_profiling=False, casename=None,
         rst_filename=None, actx_class=PyOpenCLArrayContext):
    """Drive the example."""
    cl_ctx = ctx_factory()

    if casename is None:
        casename = "mirgecom"

    from mpi4py import MPI
    comm = MPI.COMM_WORLD
    rank = comm.Get_rank()
    num_parts = 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
    current_step = 0
    if use_leap:
        from leap.rk import RK4MethodBuilder
        timestepper = RK4MethodBuilder("state")
    else:
        timestepper = rk4_step
    t_final = 0.1
    current_cfl = 1.0
    current_dt = .01
    current_t = 0
    constant_cfl = False

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

    dim = 3
    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"] == num_parts
    else:  # generate the grid from scratch
        from meshmode.mesh.generation import generate_regular_rect_mesh
        box_ll = -1
        box_ur = 1
        nel_1d = 16
        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

    from grudge.dof_desc import DISCR_TAG_BASE, DISCR_TAG_QUAD
    from meshmode.discretization.poly_element import \
        default_simplex_group_factory, QuadratureSimplexGroupFactory

    order = 1
    discr = EagerDGDiscretization(
        actx, local_mesh,
        discr_tag_to_group_factory={
            DISCR_TAG_BASE: default_simplex_group_factory(
                base_dim=local_mesh.dim, order=order),
            DISCR_TAG_QUAD: QuadratureSimplexGroupFactory(2*order + 1)
        },
        mpi_communicator=comm
    )
    nodes = thaw(discr.nodes(), actx)

    if use_overintegration:
        quadrature_tag = DISCR_TAG_QUAD
    else:
        quadrature_tag = None

    vis_timer = None

    if logmgr:
        logmgr_add_cl_device_info(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"),
            ("t_step.max", "------- step walltime: {value:6g} s, "),
            ("t_log.max", "log walltime: {value:6g} s")
        ])

    eos = IdealSingleGas()
    gas_model = GasModel(eos=eos)
    vel = np.zeros(shape=(dim,))
    orig = np.zeros(shape=(dim,))
    initializer = Lump(dim=dim, center=orig, velocity=vel, rhoamp=0.0)
    wall = AdiabaticSlipBoundary()
    boundaries = {BTAG_ALL: wall}
    uniform_state = initializer(nodes)
    acoustic_pulse = AcousticPulse(dim=dim, amplitude=1.0, width=.1,
                                   center=orig)
    if rst_filename:
        current_t = restart_data["t"]
        current_step = restart_data["step"]
        current_cv = restart_data["cv"]
        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 = acoustic_pulse(x_vec=nodes, cv=uniform_state, eos=eos)

    current_state = make_fluid_state(current_cv, gas_model)

    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)

    def my_write_viz(step, t, state, dv=None):
        if dv is None:
            dv = eos.dependent_vars(state)
        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):
        rst_fname = rst_pattern.format(cname=casename, step=step, rank=rank)
        if rst_fname != rst_filename:
            rst_data = {
                "local_mesh": local_mesh,
                "cv": state,
                "t": t,
                "step": step,
                "order": order,
                "global_nelements": global_nelements,
                "num_parts": num_parts
            }
            from mirgecom.restart import write_restart_file
            write_restart_file(actx, rst_data, rst_fname, comm)

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

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

        try:

            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)

            if do_health:
                health_errors = global_reduce(my_health_check(dv.pressure), 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=state)

            if do_viz:
                my_write_viz(step=step, t=t, state=state, dv=dv)

        except MyRuntimeError:
            if rank == 0:
                logger.info("Errors detected; attempting graceful exit.")
            my_write_viz(step=step, t=t, state=state)
            my_write_restart(step=step, t=t, state=state)
            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):
        # 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):
        fluid_state = make_fluid_state(cv=state, gas_model=gas_model)
        return euler_operator(discr, state=fluid_state, time=t,
                              boundaries=boundaries,
                              gas_model=gas_model,
                              quadrature_tag=quadrature_tag)

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

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

    # Dump the final data
    if rank == 0:
        logger.info("Checkpointing final state ...")
    final_state = make_fluid_state(current_cv, gas_model)
    final_dv = final_state.dv

    my_write_viz(step=current_step, t=current_t, state=current_cv, dv=final_dv)
    my_write_restart(step=current_step, t=current_t, state=current_cv)

    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
Esempio n. 3
0
def main(ctx_factory, dim=2, order=3, visualize=False):
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(
        queue,
        allocator=cl_tools.MemoryPool(cl_tools.ImmediateAllocator(queue)),
        force_device_scalars=True,
    )

    nel_1d = 16
    from meshmode.mesh.generation import generate_regular_rect_mesh
    mesh = generate_regular_rect_mesh(
            a=(-0.5,)*dim,
            b=(0.5,)*dim,
            nelements_per_axis=(nel_1d,)*dim)

    logger.info("%d elements", mesh.nelements)

    from meshmode.discretization.poly_element import \
            QuadratureSimplexGroupFactory, \
            default_simplex_group_factory
    dcoll = DiscretizationCollection(
        actx, mesh,
        discr_tag_to_group_factory={
            DISCR_TAG_BASE: default_simplex_group_factory(base_dim=dim, order=order),
            DISCR_TAG_QUAD: QuadratureSimplexGroupFactory(3*order),
        }
    )

    # bounded above by 1
    c = 0.2 + 0.8*bump(actx, dcoll, center=np.zeros(3), width=0.5)
    dt = 0.5 * estimate_rk4_timestep(actx, dcoll, c=1)

    fields = flat_obj_array(
            bump(actx, dcoll, ),
            [dcoll.zeros(actx) for i in range(dcoll.dim)]
            )

    vis = make_visualizer(dcoll)

    def rhs(t, w):
        return wave_operator(dcoll, c=c, w=w)

    logger.info("dt = %g", dt)

    t = 0
    t_final = 3
    istep = 0
    while t < t_final:
        fields = rk4_step(fields, t, dt, rhs)

        if istep % 10 == 0:
            logger.info(f"step: {istep} t: {t} "
                        f"L2: {op.norm(dcoll, fields[0], 2)} "
                        f"Linf: {op.norm(dcoll, fields[0], np.inf)} "
                        f"sol max: {op.nodal_max(dcoll, 'vol', fields[0])} "
                        f"sol min: {op.nodal_min(dcoll, 'vol', fields[0])}")
            if visualize:
                vis.write_vtk_file(
                    f"fld-wave-eager-var-velocity-{istep:04d}.vtu",
                    [
                        ("c", c),
                        ("u", fields[0]),
                        ("v", fields[1:]),
                    ]
                )

        t += dt
        istep += 1

        # NOTE: These are here to ensure the solution is bounded for the
        # time interval specified
        assert op.norm(dcoll, fields[0], 2) < 1
Esempio n. 4
0
def main(ctx_factory=cl.create_some_context,
         use_logmgr=True,
         use_leap=False,
         use_overintegration=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()
    nproc = 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)))

    # 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 equilibrium, 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

    from grudge.dof_desc import DISCR_TAG_BASE, DISCR_TAG_QUAD
    from meshmode.discretization.poly_element import \
        default_simplex_group_factory, QuadratureSimplexGroupFactory

    discr = EagerDGDiscretization(
        actx,
        local_mesh,
        discr_tag_to_group_factory={
            DISCR_TAG_BASE:
            default_simplex_group_factory(base_dim=local_mesh.dim,
                                          order=order),
            DISCR_TAG_QUAD:
            QuadratureSimplexGroupFactory(2 * order + 1)
        },
        mpi_communicator=comm)
    nodes = thaw(discr.nodes(), actx)
    ones = discr.zeros(actx) + 1.0

    if use_overintegration:
        quadrature_tag = DISCR_TAG_QUAD
    else:
        quadrature_tag = None

    ones = discr.zeros(actx) + 1.0

    vis_timer = None

    if logmgr:
        logmgr_add_cl_device_info(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")
            ])

    # {{{  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.
    temperature_seed = 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) = ({temperature_seed}, {one_atm}, {x}")
    # Set Cantera internal gas temperature, pressure, and mole fractios
    cantera_soln.TPX = temperature_seed, 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.
    from mirgecom.thermochemistry import make_pyrometheus_mechanism_class
    pyro_mechanism = make_pyrometheus_mechanism_class(cantera_soln)(actx.np)
    eos = PyrometheusMixture(pyro_mechanism,
                             temperature_guess=temperature_seed)

    gas_model = GasModel(eos=eos)
    from pytools.obj_array import make_obj_array

    def get_temperature_update(cv, temperature):
        y = cv.species_mass_fractions
        e = gas_model.eos.internal_energy(cv) / cv.mass
        return pyro_mechanism.get_temperature_update_energy(e, temperature, y)

    from mirgecom.gas_model import make_fluid_state

    def get_fluid_state(cv, tseed):
        return make_fluid_state(cv=cv,
                                gas_model=gas_model,
                                temperature_seed=tseed)

    compute_temperature_update = actx.compile(get_temperature_update)
    construct_fluid_state = actx.compile(get_fluid_state)

    # }}}

    # {{{ 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_cv = restart_data["cv"]
            temperature_seed = restart_data["temperature_seed"]
        else:
            rst_cv = restart_data["cv"]
            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_cv = connection(rst_cv)
            temperature_seed = connection(restart_data["temperature_seed"])
    else:
        # Set the current state from time 0
        current_cv = initializer(eos=gas_model.eos, x_vec=nodes)
        temperature_seed = temperature_seed * ones

    # The temperature_seed going into this function is:
    # - At time 0: the initial temperature input data (maybe from Cantera)
    # - On restart: the restarted temperature seed from restart file (saving
    #               the *seed* allows restarts to be deterministic
    current_fluid_state = construct_fluid_state(current_cv, temperature_seed)
    current_dv = current_fluid_state.dv
    temperature_seed = current_dv.temperature

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

    # }}}

    visualizer = make_visualizer(discr)
    initname = initializer.__class__.__name__
    eosname = gas_model.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, dv=None):
        status_msg = f"------ {dt=}" if constant_cfl else f"----- {cfl=}"
        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)

    def my_write_viz(step, t, dt, state, ts_field, dv, production_rates, cfl):
        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, temperature_seed):
        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,
                "cv": state.cv,
                "temperature_seed": temperature_seed,
                "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(cv, dv):
        import grudge.op as op
        health_error = False

        pressure = dv.pressure
        temperature = dv.temperature

        from mirgecom.simutil import check_naninf_local, check_range_local
        if check_naninf_local(discr, "vol", pressure):
            health_error = True
            logger.info(f"{rank=}: Invalid pressure data found.")

        if check_range_local(discr, "vol", pressure, 1e5, 2.6e5):
            health_error = True
            logger.info(f"{rank=}: Pressure range violation.")

        if check_naninf_local(discr, "vol", temperature):
            health_error = True
            logger.info(f"{rank=}: Invalid temperature data found.")
        if check_range_local(discr, "vol", temperature, 1.498e3, 1.6e3):
            health_error = True
            logger.info(f"{rank=}: Temperature range violation.")

        # This check is the temperature convergence check
        # The current *temperature* is what Pyrometheus gets
        # after a fixed number of Newton iterations, *n_iter*.
        # Calling `compute_temperature` here with *temperature*
        # input as the guess returns the calculated gas temperature after
        # yet another *n_iter*.
        # The difference between those two temperatures is the
        # temperature residual, which can be used as an indicator of
        # convergence in Pyrometheus `get_temperature`.
        # Note: The local max jig below works around a very long compile
        # in lazy mode.
        temp_resid = compute_temperature_update(cv, temperature) / temperature
        temp_err = (actx.to_numpy(op.nodal_max_loc(discr, "vol", temp_resid)))
        if temp_err > 1e-8:
            health_error = True
            logger.info(
                f"{rank=}: Temperature is not converged {temp_resid=}.")

        return health_error

    from mirgecom.inviscid import get_inviscid_timestep

    def get_dt(state):
        return get_inviscid_timestep(discr, state=state)

    compute_dt = actx.compile(get_dt)

    from mirgecom.inviscid import get_inviscid_cfl

    def get_cfl(state, dt):
        return get_inviscid_cfl(discr, dt=dt, state=state)

    compute_cfl = actx.compile(get_cfl)

    def get_production_rates(cv, temperature):
        return eos.get_production_rates(cv, temperature)

    compute_production_rates = actx.compile(get_production_rates)

    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:
            ts_field = current_cfl * compute_dt(state)
            from grudge.op import nodal_min_loc
            dt = allsync(actx.to_numpy(nodal_min_loc(discr, "vol", ts_field)),
                         comm=comm,
                         op=MPI.MIN)
            cfl = current_cfl
        else:
            ts_field = compute_cfl(state, current_dt)
            from grudge.op import nodal_max_loc
            cfl = allsync(actx.to_numpy(nodal_max_loc(discr, "vol", ts_field)),
                          comm=comm,
                          op=MPI.MAX)
        return ts_field, cfl, min(t_remaining, dt)

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

        try:

            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:
                health_errors = global_reduce(my_health_check(cv, dv),
                                              op="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=fluid_state)

            if do_status:
                my_write_status(dt=dt, cfl=cfl, dv=dv)

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

            if do_viz:
                production_rates = compute_production_rates(
                    fluid_state.cv, fluid_state.temperature)
                my_write_viz(step=step,
                             t=t,
                             dt=dt,
                             state=cv,
                             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=cv)
            # my_write_restart(step=step, t=t, state=fluid_state)
            raise

        return state, dt

    def my_post_step(step, t, dt, state):
        cv, tseed = state
        fluid_state = construct_fluid_state(cv, tseed)

        # 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, gas_model.eos)
            logmgr.tick_after()
        return make_obj_array([cv, fluid_state.temperature]), dt

    def my_rhs(t, state):
        cv, tseed = state
        from mirgecom.gas_model import make_fluid_state
        fluid_state = make_fluid_state(cv=cv,
                                       gas_model=gas_model,
                                       temperature_seed=tseed)
        return make_obj_array([
            euler_operator(discr,
                           state=fluid_state,
                           time=t,
                           boundaries=boundaries,
                           gas_model=gas_model,
                           quadrature_tag=quadrature_tag) +
            eos.get_species_source_terms(cv, fluid_state.temperature),
            0 * tseed
        ])

    current_dt = get_sim_timestep(discr, current_fluid_state, current_t,
                                  current_dt, current_cfl, 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=make_obj_array([current_cv, temperature_seed]),
                      t=current_t, t_final=t_final)

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

    final_cv, tseed = current_state
    final_fluid_state = construct_fluid_state(final_cv, tseed)
    final_dv = final_fluid_state.dv
    final_dm = compute_production_rates(final_cv, final_dv.temperature)
    ts_field, cfl, dt = my_get_timestep(t=current_t,
                                        dt=current_dt,
                                        state=final_fluid_state)
    my_write_viz(step=current_step,
                 t=current_t,
                 dt=dt,
                 state=final_cv,
                 dv=final_dv,
                 production_rates=final_dm,
                 ts_field=ts_field,
                 cfl=cfl)
    my_write_status(dt=dt, cfl=cfl, dv=final_dv)
    my_write_restart(step=current_step,
                     t=current_t,
                     state=final_fluid_state,
                     temperature_seed=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
Esempio n. 5
0
def test_uniform_rhs(actx_factory, nspecies, dim, order, use_overintegration):
    """Test the inviscid rhs using a trivial constant/uniform state.

    This state should yield rhs = 0 to FP.  The test is performed for 1, 2,
    and 3 dimensions, with orders 1, 2, and 3, with and without passive species.
    """
    actx = actx_factory()

    tolerance = 1e-9

    from pytools.convergence import EOCRecorder
    eoc_rec0 = EOCRecorder()
    eoc_rec1 = EOCRecorder()
    # for nel_1d in [4, 8, 12]:
    for nel_1d in [4, 8]:
        from meshmode.mesh.generation import generate_regular_rect_mesh
        mesh = generate_regular_rect_mesh(
            a=(-0.5,) * dim, b=(0.5,) * dim, nelements_per_axis=(nel_1d,) * dim
        )

        logger.info(
            f"Number of {dim}d elements: {mesh.nelements}"
        )

        from grudge.dof_desc import DISCR_TAG_BASE, DISCR_TAG_QUAD
        from meshmode.discretization.poly_element import \
            default_simplex_group_factory, QuadratureSimplexGroupFactory

        discr = EagerDGDiscretization(
            actx, mesh,
            discr_tag_to_group_factory={
                DISCR_TAG_BASE: default_simplex_group_factory(
                    base_dim=dim, order=order),
                DISCR_TAG_QUAD: QuadratureSimplexGroupFactory(2*order + 1)
            }
        )

        if use_overintegration:
            quadrature_tag = DISCR_TAG_QUAD
        else:
            quadrature_tag = None

        zeros = discr.zeros(actx)
        ones = zeros + 1.0

        mass_input = discr.zeros(actx) + 1
        energy_input = discr.zeros(actx) + 2.5

        mom_input = make_obj_array(
            [discr.zeros(actx) for i in range(discr.dim)]
        )

        mass_frac_input = flat_obj_array(
            [ones / ((i + 1) * 10) for i in range(nspecies)]
        )
        species_mass_input = mass_input * mass_frac_input
        num_equations = dim + 2 + len(species_mass_input)

        cv = make_conserved(
            dim, mass=mass_input, energy=energy_input, momentum=mom_input,
            species_mass=species_mass_input)
        gas_model = GasModel(eos=IdealSingleGas())
        fluid_state = make_fluid_state(cv, gas_model)

        expected_rhs = make_conserved(
            dim, q=make_obj_array([discr.zeros(actx)
                                   for i in range(num_equations)])
        )

        boundaries = {BTAG_ALL: DummyBoundary()}
        inviscid_rhs = euler_operator(discr, state=fluid_state, gas_model=gas_model,
                                      boundaries=boundaries, time=0.0,
                                      quadrature_tag=quadrature_tag)

        rhs_resid = inviscid_rhs - expected_rhs

        rho_resid = rhs_resid.mass
        rhoe_resid = rhs_resid.energy
        mom_resid = rhs_resid.momentum
        rhoy_resid = rhs_resid.species_mass

        rho_rhs = inviscid_rhs.mass
        rhoe_rhs = inviscid_rhs.energy
        rhov_rhs = inviscid_rhs.momentum
        rhoy_rhs = inviscid_rhs.species_mass

        logger.info(
            f"rho_rhs  = {rho_rhs}\n"
            f"rhoe_rhs = {rhoe_rhs}\n"
            f"rhov_rhs = {rhov_rhs}\n"
            f"rhoy_rhs = {rhoy_rhs}\n"
        )

        def inf_norm(x):
            return actx.to_numpy(discr.norm(x, np.inf))

        assert inf_norm(rho_resid) < tolerance
        assert inf_norm(rhoe_resid) < tolerance
        for i in range(dim):
            assert inf_norm(mom_resid[i]) < tolerance
        for i in range(nspecies):
            assert inf_norm(rhoy_resid[i]) < tolerance

        err_max = inf_norm(rho_resid)
        eoc_rec0.add_data_point(1.0 / nel_1d, err_max)

        # set a non-zero, but uniform velocity component
        for i in range(len(mom_input)):
            mom_input[i] = discr.zeros(actx) + (-1.0) ** i

        cv = make_conserved(
            dim, mass=mass_input, energy=energy_input, momentum=mom_input,
            species_mass=species_mass_input)
        gas_model = GasModel(eos=IdealSingleGas())
        fluid_state = make_fluid_state(cv, gas_model)

        boundaries = {BTAG_ALL: DummyBoundary()}
        inviscid_rhs = euler_operator(discr, state=fluid_state, gas_model=gas_model,
                                      boundaries=boundaries, time=0.0)
        rhs_resid = inviscid_rhs - expected_rhs

        rho_resid = rhs_resid.mass
        rhoe_resid = rhs_resid.energy
        mom_resid = rhs_resid.momentum
        rhoy_resid = rhs_resid.species_mass

        assert inf_norm(rho_resid) < tolerance
        assert inf_norm(rhoe_resid) < tolerance

        for i in range(dim):
            assert inf_norm(mom_resid[i]) < tolerance
        for i in range(nspecies):
            assert inf_norm(rhoy_resid[i]) < tolerance

        err_max = inf_norm(rho_resid)
        eoc_rec1.add_data_point(1.0 / nel_1d, err_max)

    logger.info(
        f"V == 0 Errors:\n{eoc_rec0}"
        f"V != 0 Errors:\n{eoc_rec1}"
    )

    assert (
        eoc_rec0.order_estimate() >= order - 0.5
        or eoc_rec0.max_error() < 1e-9
    )
    assert (
        eoc_rec1.order_estimate() >= order - 0.5
        or eoc_rec1.max_error() < 1e-9
    )
Esempio n. 6
0
def _euler_flow_stepper(actx, parameters):
    logging.basicConfig(format="%(message)s", level=logging.INFO)

    mesh = parameters["mesh"]
    t = parameters["time"]
    order = parameters["order"]
    t_final = parameters["tfinal"]
    initializer = parameters["initializer"]
    exittol = parameters["exittol"]
    casename = parameters["casename"]
    boundaries = parameters["boundaries"]
    eos = parameters["eos"]
    cfl = parameters["cfl"]
    dt = parameters["dt"]
    constantcfl = parameters["constantcfl"]
    nstepstatus = parameters["nstatus"]
    use_overintegration = parameters["use_overintegration"]

    if t_final <= t:
        return(0.0)

    rank = 0
    dim = mesh.dim
    istep = 0

    from grudge.dof_desc import DISCR_TAG_BASE, DISCR_TAG_QUAD
    from meshmode.discretization.poly_element import \
        default_simplex_group_factory, QuadratureSimplexGroupFactory

    discr = EagerDGDiscretization(
        actx, mesh,
        discr_tag_to_group_factory={
            DISCR_TAG_BASE: default_simplex_group_factory(
                base_dim=dim, order=order),
            DISCR_TAG_QUAD: QuadratureSimplexGroupFactory(2*order + 1)
        }
    )

    if use_overintegration:
        quadrature_tag = DISCR_TAG_QUAD
    else:
        quadrature_tag = None

    nodes = thaw(discr.nodes(), actx)

    cv = initializer(nodes)
    gas_model = GasModel(eos=eos)
    fluid_state = make_fluid_state(cv, gas_model)

    sdt = cfl * get_inviscid_timestep(discr, fluid_state)

    initname = initializer.__class__.__name__
    eosname = eos.__class__.__name__
    logger.info(
        f"Num {dim}d order-{order} elements: {mesh.nelements}\n"
        f"Timestep:        {dt}\n"
        f"Final time:      {t_final}\n"
        f"Status freq:     {nstepstatus}\n"
        f"Initialization:  {initname}\n"
        f"EOS:             {eosname}"
    )

    vis = make_visualizer(discr, order)

    def write_soln(state, write_status=True):
        dv = eos.dependent_vars(cv=state)
        expected_result = initializer(nodes, t=t)
        result_resid = state - expected_result
        maxerr = [np.max(np.abs(result_resid[i].get())) for i in range(dim + 2)]
        mindv = [np.min(dvfld.get()) for dvfld in dv]
        maxdv = [np.max(dvfld.get()) for dvfld in dv]

        if write_status is True:
            statusmsg = (
                f"Status: Step({istep}) Time({t})\n"
                f"------   P({mindv[0]},{maxdv[0]})\n"
                f"------   T({mindv[1]},{maxdv[1]})\n"
                f"------   dt,cfl = ({dt},{cfl})\n"
                f"------   Err({maxerr})"
            )
            logger.info(statusmsg)

        io_fields = ["cv", state]
        io_fields += eos.split_fields(dim, dv)
        io_fields.append(("exact_soln", expected_result))
        io_fields.append(("residual", result_resid))
        nameform = casename + "-{iorank:04d}-{iostep:06d}.vtu"
        visfilename = nameform.format(iorank=rank, iostep=istep)
        vis.write_vtk_file(visfilename, io_fields)

        return maxerr

    def rhs(t, q):
        fluid_state = make_fluid_state(q, gas_model)
        return euler_operator(discr, fluid_state, boundaries=boundaries,
                              gas_model=gas_model, time=t,
                              quadrature_tag=quadrature_tag)

    filter_order = 8
    eta = .5
    alpha = -1.0*np.log(np.finfo(float).eps)
    nummodes = int(1)
    for _ in range(dim):
        nummodes *= int(order + dim + 1)
    nummodes /= math.factorial(int(dim))
    cutoff = int(eta * order)

    from mirgecom.filter import (
        exponential_mode_response_function as xmrfunc,
        filter_modally
    )
    frfunc = partial(xmrfunc, alpha=alpha, filter_order=filter_order)

    while t < t_final:

        if constantcfl is True:
            dt = sdt
        else:
            cfl = dt / sdt

        if nstepstatus > 0:
            if istep % nstepstatus == 0:
                write_soln(state=cv)

        cv = rk4_step(cv, t, dt, rhs)
        cv = filter_modally(discr, "vol", cutoff, frfunc, cv)

        t += dt
        istep += 1

        sdt = cfl * get_inviscid_timestep(discr, fluid_state)

    if nstepstatus > 0:
        logger.info("Writing final dump.")
        maxerr = max(write_soln(cv, False))
    else:
        expected_result = initializer(nodes, time=t)
        maxerr = max_component_norm(discr, cv-expected_result, np.inf)

    logger.info(f"Max Error: {maxerr}")
    if maxerr > exittol:
        raise ValueError("Solution failed to follow expected result.")

    return(maxerr)
Esempio n. 7
0
def test_multilump_rhs(actx_factory, dim, order, v0, use_overintegration):
    """Test the Euler rhs using the non-trivial 1, 2, and 3D mass lump case.

    The case is tested against the analytic expressions of the RHS. Checks several
    different orders and refinement levels to check error behavior.
    """
    actx = actx_factory()
    nspecies = 10
    tolerance = 1e-8
    maxxerr = 0.0

    from pytools.convergence import EOCRecorder

    eoc_rec = EOCRecorder()

    for nel_1d in [4, 8, 12]:
        from meshmode.mesh.generation import (
            generate_regular_rect_mesh,
        )

        mesh = generate_regular_rect_mesh(
            a=(-1,) * dim, b=(1,) * dim, nelements_per_axis=(nel_1d,) * dim,
        )

        logger.info(f"Number of elements: {mesh.nelements}")

        from grudge.dof_desc import DISCR_TAG_BASE, DISCR_TAG_QUAD
        from meshmode.discretization.poly_element import \
            default_simplex_group_factory, QuadratureSimplexGroupFactory

        discr = EagerDGDiscretization(
            actx, mesh,
            discr_tag_to_group_factory={
                DISCR_TAG_BASE: default_simplex_group_factory(
                    base_dim=dim, order=order),
                DISCR_TAG_QUAD: QuadratureSimplexGroupFactory(2*order + 1)
            }
        )

        if use_overintegration:
            quadrature_tag = DISCR_TAG_QUAD
        else:
            quadrature_tag = None

        nodes = thaw(discr.nodes(), actx)

        centers = make_obj_array([np.zeros(shape=(dim,)) for i in range(nspecies)])
        spec_y0s = np.ones(shape=(nspecies,))
        spec_amplitudes = np.ones(shape=(nspecies,))

        velocity = np.zeros(shape=(dim,))
        velocity[0] = v0
        rho0 = 2.0

        lump = MulticomponentLump(dim=dim, nspecies=nspecies, rho0=rho0,
                                  spec_centers=centers, velocity=velocity,
                                  spec_y0s=spec_y0s, spec_amplitudes=spec_amplitudes)

        lump_soln = lump(nodes)
        gas_model = GasModel(eos=IdealSingleGas())
        fluid_state = make_fluid_state(lump_soln, gas_model)

        def _my_boundary(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(lump(x_vec=nodes, **kwargs), gas_model)

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

        inviscid_rhs = euler_operator(
            discr, state=fluid_state, gas_model=gas_model, boundaries=boundaries,
            time=0.0, quadrature_tag=quadrature_tag
        )
        expected_rhs = lump.exact_rhs(discr, cv=lump_soln, time=0)

        print(f"inviscid_rhs = {inviscid_rhs}")
        print(f"expected_rhs = {expected_rhs}")

        err_max = actx.to_numpy(
            discr.norm((inviscid_rhs-expected_rhs), np.inf))
        if err_max > maxxerr:
            maxxerr = err_max

        eoc_rec.add_data_point(1.0 / nel_1d, err_max)

        logger.info(f"Max error: {maxxerr}")

    logger.info(
        f"Error for (dim,order) = ({dim},{order}):\n"
        f"{eoc_rec}"
    )

    assert (
        eoc_rec.order_estimate() >= order - 0.5
        or eoc_rec.max_error() < tolerance
    )
Esempio n. 8
0
def test_vortex_rhs(actx_factory, order, use_overintegration):
    """Test the inviscid rhs using the non-trivial 2D isentropic vortex.

    The case is configured to yield rhs = 0. Checks several different orders
    and refinement levels to check error behavior.
    """
    actx = actx_factory()

    dim = 2

    from pytools.convergence import EOCRecorder
    eoc_rec = EOCRecorder()

    from meshmode.mesh.generation import generate_regular_rect_mesh

    for nel_1d in [32, 48, 64]:

        mesh = generate_regular_rect_mesh(
            a=(-5,) * dim, b=(5,) * dim, nelements_per_axis=(nel_1d,) * dim,
        )

        logger.info(
            f"Number of {dim}d elements:  {mesh.nelements}"
        )

        from grudge.dof_desc import DISCR_TAG_BASE, DISCR_TAG_QUAD
        from meshmode.discretization.poly_element import \
            default_simplex_group_factory, QuadratureSimplexGroupFactory

        discr = EagerDGDiscretization(
            actx, mesh,
            discr_tag_to_group_factory={
                DISCR_TAG_BASE: default_simplex_group_factory(
                    base_dim=dim, order=order),
                DISCR_TAG_QUAD: QuadratureSimplexGroupFactory(2*order + 1)
            }
        )

        if use_overintegration:
            quadrature_tag = DISCR_TAG_QUAD
        else:
            quadrature_tag = None

        nodes = thaw(discr.nodes(), actx)

        # Init soln with Vortex and expected RHS = 0
        vortex = Vortex2D(center=[0, 0], velocity=[0, 0])
        vortex_soln = vortex(nodes)
        gas_model = GasModel(eos=IdealSingleGas())
        fluid_state = make_fluid_state(vortex_soln, gas_model)

        def _vortex_boundary(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(vortex(x_vec=nodes, **kwargs), gas_model)

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

        inviscid_rhs = euler_operator(
            discr, state=fluid_state, gas_model=gas_model, boundaries=boundaries,
            time=0.0, quadrature_tag=quadrature_tag)

        err_max = max_component_norm(discr, inviscid_rhs, np.inf)

        eoc_rec.add_data_point(1.0 / nel_1d, err_max)

    logger.info(
        f"Error for (dim,order) = ({dim},{order}):\n"
        f"{eoc_rec}"
    )

    assert (
        eoc_rec.order_estimate() >= order - 0.5
        or eoc_rec.max_error() < 1e-11
    )
Esempio n. 9
0
def main(ctx_factory, dim=2, order=4, use_quad=False, visualize=False):
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(
        queue,
        allocator=cl_tools.MemoryPool(cl_tools.ImmediateAllocator(queue)),
        force_device_scalars=True,
    )

    # {{{ parameters

    # sphere radius
    radius = 1.0
    # sphere resolution
    resolution = 64 if dim == 2 else 1

    # final time
    final_time = np.pi

    # flux
    flux_type = "lf"

    # }}}

    # {{{ discretization

    if dim == 2:
        from meshmode.mesh.generation import make_curve_mesh, ellipse
        mesh = make_curve_mesh(
                lambda t: radius * ellipse(1.0, t),
                np.linspace(0.0, 1.0, resolution + 1),
                order)
    elif dim == 3:
        from meshmode.mesh.generation import generate_icosphere
        mesh = generate_icosphere(radius, order=4 * order,
                uniform_refinement_rounds=resolution)
    else:
        raise ValueError("unsupported dimension")

    discr_tag_to_group_factory = {}
    if use_quad:
        qtag = dof_desc.DISCR_TAG_QUAD
    else:
        qtag = None

    from meshmode.discretization.poly_element import \
            default_simplex_group_factory, \
            QuadratureSimplexGroupFactory

    discr_tag_to_group_factory[dof_desc.DISCR_TAG_BASE] = \
        default_simplex_group_factory(base_dim=dim-1, order=order)

    if use_quad:
        discr_tag_to_group_factory[qtag] = \
            QuadratureSimplexGroupFactory(order=4*order)

    from grudge import DiscretizationCollection

    dcoll = DiscretizationCollection(
        actx, mesh,
        discr_tag_to_group_factory=discr_tag_to_group_factory
    )

    volume_discr = dcoll.discr_from_dd(dof_desc.DD_VOLUME)
    logger.info("ndofs:     %d", volume_discr.ndofs)
    logger.info("nelements: %d", volume_discr.mesh.nelements)

    # }}}

    # {{{ Surface advection operator

    # velocity field
    x = thaw(dcoll.nodes(), actx)
    c = make_obj_array([-x[1], x[0], 0.0])[:dim]

    def f_initial_condition(x):
        return x[0]

    from grudge.models.advection import SurfaceAdvectionOperator
    adv_operator = SurfaceAdvectionOperator(
        dcoll,
        c,
        flux_type=flux_type,
        quad_tag=qtag
    )

    u0 = f_initial_condition(x)

    def rhs(t, u):
        return adv_operator.operator(t, u)

    # check velocity is tangential
    from grudge.geometry import normal

    surf_normal = normal(actx, dcoll, dd=dof_desc.DD_VOLUME)

    error = op.norm(dcoll, c.dot(surf_normal), 2)
    logger.info("u_dot_n:   %.5e", error)

    # }}}

    # {{{ time stepping

    # FIXME: dt estimate is not necessarily valid for surfaces
    dt = actx.to_numpy(
        0.45 * adv_operator.estimate_rk4_timestep(actx, dcoll, fields=u0))
    nsteps = int(final_time // dt) + 1

    logger.info("dt:        %.5e", dt)
    logger.info("nsteps:    %d", nsteps)

    from grudge.shortcuts import set_up_rk4
    dt_stepper = set_up_rk4("u", dt, u0, rhs)
    plot = Plotter(actx, dcoll, order, visualize=visualize)

    norm_u = actx.to_numpy(op.norm(dcoll, u0, 2))

    step = 0

    event = dt_stepper.StateComputed(0.0, 0, 0, u0)
    plot(event, "fld-surface-%04d" % 0)

    if visualize and dim == 3:
        from grudge.shortcuts import make_visualizer
        vis = make_visualizer(dcoll)
        vis.write_vtk_file(
            "fld-surface-velocity.vtu",
            [
                ("u", c),
                ("n", surf_normal)
            ],
            overwrite=True
        )

        df = dof_desc.DOFDesc(FACE_RESTR_INTERIOR)
        face_discr = dcoll.discr_from_dd(df)
        face_normal = thaw(dcoll.normal(dd=df), actx)

        from meshmode.discretization.visualization import make_visualizer
        vis = make_visualizer(actx, face_discr)
        vis.write_vtk_file("fld-surface-face-normals.vtu", [
            ("n", face_normal)
            ], overwrite=True)

    for event in dt_stepper.run(t_end=final_time, max_steps=nsteps + 1):
        if not isinstance(event, dt_stepper.StateComputed):
            continue

        step += 1
        if step % 10 == 0:
            norm_u = actx.to_numpy(op.norm(dcoll, event.state_component, 2))
            plot(event, "fld-surface-%04d" % step)

        logger.info("[%04d] t = %.5f |u| = %.5e", step, event.t, norm_u)

        # NOTE: These are here to ensure the solution is bounded for the
        # time interval specified
        assert norm_u < 3