Beispiel #1
0
def main(ctx_factory=cl.create_some_context,
         casename="flame1d",
         user_input_file=None,
         snapshot_pattern="{casename}-{step:06d}-{rank:04d}.pkl",
         restart_step=None,
         restart_name=None,
         use_profiling=False,
         use_logmgr=False,
         use_lazy_eval=False):
    """Drive the 1D Flame example."""

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

    if restart_name is None:
        restart_name = casename
    """logging and profiling"""
    logmgr = initialize_logmgr(use_logmgr,
                               filename=(f"{casename}.sqlite"),
                               mode="wo",
                               mpi_comm=comm)

    cl_ctx = ctx_factory()
    if use_profiling:
        if use_lazy_eval:
            raise RuntimeError("Cannot run lazy with profiling.")
        queue = cl.CommandQueue(
            cl_ctx, properties=cl.command_queue_properties.PROFILING_ENABLE)
        actx = PyOpenCLProfilingArrayContext(
            queue,
            allocator=cl_tools.MemoryPool(cl_tools.ImmediateAllocator(queue)),
            logmgr=logmgr)
    else:
        queue = cl.CommandQueue(cl_ctx)
        if use_lazy_eval:
            actx = PytatoArrayContext(queue)
        else:
            actx = PyOpenCLArrayContext(
                queue,
                allocator=cl_tools.MemoryPool(
                    cl_tools.ImmediateAllocator(queue)))

    # default input values that will be read from input (if they exist)
    nviz = 100
    nrestart = 100
    nhealth = 100
    nstatus = 1
    current_dt = 1e-9
    t_final = 1.e-3
    order = 1
    integrator = "rk4"

    if user_input_file:
        if rank == 0:
            with open(user_input_file) as f:
                input_data = yaml.load(f, Loader=yaml.FullLoader)
        else:
            input_data = None
        input_data = comm.bcast(input_data, root=0)
        #print(input_data)
        try:
            nviz = int(input_data["nviz"])
        except KeyError:
            pass
        try:
            nrestart = int(input_data["nrestart"])
        except KeyError:
            pass
        try:
            nhealth = int(input_data["nhealth"])
        except KeyError:
            pass
        try:
            nstatus = int(input_data["nstatus"])
        except KeyError:
            pass
        try:
            current_dt = float(input_data["current_dt"])
        except KeyError:
            pass
        try:
            t_final = float(input_data["t_final"])
        except KeyError:
            pass
        try:
            order = int(input_data["order"])
        except KeyError:
            pass
        try:
            integrator = input_data["integrator"]
        except KeyError:
            pass

    # param sanity check
    allowed_integrators = ["rk4", "euler", "lsrk54", "lsrk144"]
    if (integrator not in allowed_integrators):
        error_message = "Invalid time integrator: {}".format(integrator)
        raise RuntimeError(error_message)

    timestepper = rk4_step
    if integrator == "euler":
        timestepper = euler_step
    if integrator == "lsrk54":
        timestepper = lsrk54_step
    if integrator == "lsrk144":
        timestepper = lsrk144_step

    if (rank == 0):
        print(f'#### Simluation control data: ####')
        print(f'\tnviz = {nviz}')
        print(f'\tnrestart = {nrestart}')
        print(f'\tnhealth = {nhealth}')
        print(f'\tnstatus = {nstatus}')
        print(f'\tcurrent_dt = {current_dt}')
        print(f'\tt_final = {t_final}')
        print(f'\torder = {order}')
        print(f"\tTime integration {integrator}")
        print(f'#### Simluation control data: ####')

    restart_path = 'restart_data/'
    viz_path = 'viz_data/'
    #if(rank == 0):
    #if not os.path.exists(restart_path):
    #os.makedirs(restart_path)
    #if not os.path.exists(viz_path):
    #os.makedirs(viz_path)

    dim = 2
    exittol = .09
    current_cfl = 1.0
    current_t = 0
    constant_cfl = False
    checkpoint_t = current_t
    current_step = 0

    vel_burned = np.zeros(shape=(dim, ))
    vel_unburned = np.zeros(shape=(dim, ))

    # {{{  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.
    fuel = "H2"
    allowed_fuels = ["H2", "C2H4"]
    if (fuel not in allowed_fuels):
        error_message = "Invalid fuel selection: {}".format(fuel)
        raise RuntimeError(error_message)

    if rank == 0:
        print(f"Fuel: {fuel}")

    from mirgecom.mechanisms import get_mechanism_cti
    if fuel == "C2H4":
        mech_cti = get_mechanism_cti("uiuc")
    elif fuel == "H2":
        mech_cti = get_mechanism_cti("sanDiego")

    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.
    temp_unburned = 300.0
    temp_ignition = 1500.0
    # Parameters for calculating the amounts of fuel, oxidizer, and inert species
    if fuel == "C2H4":
        stoich_ratio = 3.0
    if fuel == "H2":
        stoich_ratio = 0.5
    equiv_ratio = 1.0
    ox_di_ratio = 0.21
    # Grab the array indices for the specific species, ethylene, oxygen, and nitrogen
    i_fu = cantera_soln.species_index(fuel)
    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
    pres_unburned = one_atm

    # Let the user know about how Cantera is being initilized
    print(f"Input state (T,P,X) = ({temp_unburned}, {pres_unburned}, {x}")
    # Set Cantera internal gas temperature, pressure, and mole fractios
    cantera_soln.TPX = temp_unburned, pres_unburned, x
    # Pull temperature, total density, mass fractions, and pressure from Cantera
    # We need total density, and mass fractions to initialize the fluid/gas state.
    y_unburned = np.zeros(nspecies)
    can_t, rho_unburned, y_unburned = 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.

    # now find the conditions for the burned gas
    cantera_soln.equilibrate('TP')
    temp_burned, rho_burned, y_burned = cantera_soln.TDY
    pres_burned = cantera_soln.P

    pyrometheus_mechanism = pyro.get_thermochem_class(cantera_soln)(actx.np)

    kappa = 1.6e-5  # Pr = mu*rho/alpha = 0.75
    mu = 1.e-5
    species_diffusivity = 1.e-5 * np.ones(nspecies)
    transport_model = SimpleTransport(viscosity=mu,
                                      thermal_conductivity=kappa,
                                      species_diffusivity=species_diffusivity)

    eos = PyrometheusMixture(pyrometheus_mechanism,
                             temperature_guess=temp_unburned,
                             transport_model=transport_model)
    species_names = pyrometheus_mechanism.species_names

    print(f"Pyrometheus mechanism species names {species_names}")
    print(
        f"Unburned state (T,P,Y) = ({temp_unburned}, {pres_unburned}, {y_unburned}"
    )
    print(f"Burned state (T,P,Y) = ({temp_burned}, {pres_burned}, {y_burned}")

    flame_start_loc = 0.10
    flame_speed = 1000

    # use the burned conditions with a lower temperature
    #bulk_init = PlanarDiscontinuity(dim=dim, disc_location=flame_start_loc, sigma=0.01, nspecies=nspecies,
    #temperature_left=temp_ignition, temperature_right=temp_unburned,
    #pressure_left=pres_burned, pressure_right=pres_unburned,
    #velocity_left=vel_burned, velocity_right=vel_unburned,
    #species_mass_left=y_burned, species_mass_right=y_unburned)
    bulk_init = PlanarDiscontinuity(dim=dim,
                                    disc_location=flame_start_loc,
                                    sigma=0.0005,
                                    nspecies=nspecies,
                                    temperature_right=temp_ignition,
                                    temperature_left=temp_unburned,
                                    pressure_right=pres_burned,
                                    pressure_left=pres_unburned,
                                    velocity_right=vel_burned,
                                    velocity_left=vel_unburned,
                                    species_mass_right=y_burned,
                                    species_mass_left=y_unburned)

    inflow_init = MixtureInitializer(dim=dim,
                                     nspecies=nspecies,
                                     pressure=pres_burned,
                                     temperature=temp_ignition,
                                     massfractions=y_burned,
                                     velocity=vel_burned)
    outflow_init = MixtureInitializer(dim=dim,
                                      nspecies=nspecies,
                                      pressure=pres_unburned,
                                      temperature=temp_unburned,
                                      massfractions=y_unburned,
                                      velocity=vel_unburned)

    def symmetry(nodes, eos, cv=None, **kwargs):
        dim = len(nodes)

        if cv is not None:
            #cv = split_conserved(dim, q)
            mass = cv.mass
            momentum = cv.momentum
            momentum[1] = -1.0 * momentum[1]
            ke = .5 * np.dot(cv.momentum, cv.momentum) / cv.mass
            energy = cv.energy
            species_mass = cv.species_mass
            return make_conserved(dim=dim,
                                  mass=mass,
                                  momentum=momentum,
                                  energy=energy,
                                  species_mass=species_mass)

    def dummy(nodes, eos, cv=None, **kwargs):
        dim = len(nodes)

        if cv is not None:
            #cv = split_conserved(dim, q)
            mass = cv.mass
            momentum = cv.momentum
            ke = .5 * np.dot(cv.momentum, cv.momentum) / cv.mass
            energy = cv.energy
            species_mass = cv.species_mass
            return make_conserved(dim=dim,
                                  mass=mass,
                                  momentum=momentum,
                                  energy=energy,
                                  species_mass=species_mass)

    inflow = PrescribedViscousBoundary(q_func=inflow_init)
    outflow = PrescribedViscousBoundary(q_func=outflow_init)
    wall_symmetry = PrescribedViscousBoundary(q_func=symmetry)
    wall_dummy = PrescribedViscousBoundary(q_func=dummy)
    wall = PrescribedViscousBoundary(
    )  # essentially a "dummy" use the interior solution for the exterior

    boundaries = {
        DTAG_BOUNDARY("Inflow"): inflow,
        DTAG_BOUNDARY("Outflow"): outflow,
        #DTAG_BOUNDARY("Wall"): wall_dummy}
        DTAG_BOUNDARY("Wall"): wall_symmetry
    }

    if restart_step is None:
        char_len = 0.0001
        box_ll = (0.0, 0.0)
        box_ur = (0.2, 0.00125)
        num_elements = (int((box_ur[0] - box_ll[0]) / char_len),
                        int((box_ur[1] - box_ll[1]) / char_len))

        from meshmode.mesh.generation import generate_regular_rect_mesh
        generate_mesh = partial(generate_regular_rect_mesh,
                                a=box_ll,
                                b=box_ur,
                                n=num_elements,
                                boundary_tag_to_face={
                                    "Inflow": ["+x"],
                                    "Outflow": ["-x"],
                                    "Wall": ["+y", "-y"]
                                })
        local_mesh, global_nelements = generate_and_distribute_mesh(
            comm, generate_mesh)
        local_nelements = local_mesh.nelements

    else:  # Restart
        from mirgecom.restart import read_restart_data
        restart_file = restart_path + snapshot_pattern.format(
            casename=restart_name, step=restart_step, rank=rank)
        restart_data = read_restart_data(actx, restart_file)

        local_mesh = restart_data["local_mesh"]
        local_nelements = local_mesh.nelements
        global_nelements = restart_data["global_nelements"]

        assert comm.Get_size() == restart_data["num_parts"]

    if rank == 0:
        logging.info("Making discretization")
    discr = EagerDGDiscretization(actx,
                                  local_mesh,
                                  order=order,
                                  mpi_communicator=comm)
    nodes = thaw(actx, discr.nodes())

    if restart_step is None:
        if rank == 0:
            logging.info("Initializing soln.")
        # for Discontinuity initial conditions
        current_state = bulk_init(x_vec=nodes, eos=eos, time=0.)
        # for uniform background initial condition
        #current_state = bulk_init(nodes, eos=eos)
    else:
        current_t = restart_data["t"]
        current_step = restart_step
        #current_state = make_fluid_restart_state(actx, discr.discr_from_dd("vol"), restart_data["state"])
        current_state = restart_data["state"]

    vis_timer = None
    log_cfl = LogUserQuantity(name="cfl", value=current_cfl)

    if logmgr:
        logmgr_add_cl_device_info(logmgr, queue)
        logmgr_add_many_discretization_quantities(logmgr, discr, dim,
                                                  extract_vars_for_logging,
                                                  units_for_logging)
        logmgr_set_time(logmgr, current_step, current_t)
        logmgr.add_quantity(log_cfl, interval=nstatus)
        #logmgr_add_package_versions(logmgr)

        logmgr.add_watches([
            ("step.max", "step = {value}, "),
            ("t_sim.max", "sim time: {value:1.6e} s, "),
            ("cfl.max", "cfl = {value:1.4f}\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")
        ])

        try:
            logmgr.add_watches(["memory_usage.max"])
        except KeyError:
            pass

        if use_profiling:
            logmgr.add_watches(["pyopencl_array_time.max"])

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

    visualizer = make_visualizer(discr)

    initname = "flame1d"
    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 (
            ns_operator(discr, cv=state, t=t, boundaries=boundaries, eos=eos) +
            eos.get_species_source_terms(cv=state))

    def my_checkpoint(step, t, dt, state, force=False):
        do_health = force or check_step(step, nhealth) and step > 0
        do_viz = force or check_step(step, nviz)
        do_restart = force or check_step(step, nrestart)
        do_status = force or check_step(step, nstatus)

        if do_viz or do_health:
            dv = eos.dependent_vars(state)

        errors = False
        if do_health:
            health_message = ""
            if check_naninf_local(discr, "vol", dv.pressure):
                errors = True
                health_message += "Invalid pressure data found.\n"
            elif check_range_local(discr,
                                   "vol",
                                   dv.pressure,
                                   min_value=1,
                                   max_value=2.e6):
                errors = True
                health_message += "Pressure data failed health check.\n"

        errors = comm.allreduce(errors, MPI.LOR)
        if errors:
            if rank == 0:
                logger.info("Fluid solution failed health check.")
            if health_message:
                logger.info(f"{rank=}:  {health_message}")

        #if check_step(step, nrestart) and step != restart_step and not errors:
        if do_restart or errors:
            filename = restart_path + snapshot_pattern.format(
                step=step, rank=rank, casename=casename)
            restart_dictionary = {
                "local_mesh": local_mesh,
                "order": order,
                "state": state,
                "t": t,
                "step": step,
                "global_nelements": global_nelements,
                "num_parts": nparts
            }
            write_restart_file(actx, restart_dictionary, filename, comm)

        if do_status or do_viz or errors:
            local_cfl = get_inviscid_cfl(discr, eos=eos, dt=dt, cv=state)
            max_cfl = nodal_max(discr, "vol", local_cfl)
            log_cfl.set_quantity(max_cfl)

        #if ((check_step(step, nviz) and step != restart_step) or errors):
        if do_viz or errors:

            def loc_fn(t):
                return flame_start_loc + flame_speed * t

            #exact_soln =  PlanarDiscontinuity(dim=dim, disc_location=loc_fn,
            #sigma=0.0000001, nspecies=nspecies,
            #temperature_left=temp_ignition, temperature_right=temp_unburned,
            #pressure_left=pres_burned, pressure_right=pres_unburned,
            #velocity_left=vel_burned, velocity_right=vel_unburned,
            #species_mass_left=y_burned, species_mass_right=y_unburned)

            reaction_rates = eos.get_production_rates(cv=state)

            # conserved quantities
            viz_fields = [
                #("cv", state),
                ("CV_rho", state.mass),
                ("CV_rhoU", state.momentum[0]),
                ("CV_rhoV", state.momentum[1]),
                ("CV_rhoE", state.energy)
            ]
            # species mass fractions
            viz_fields.extend(
                ("Y_" + species_names[i], state.species_mass[i] / state.mass)
                for i in range(nspecies))
            # dependent variables
            viz_fields.extend([
                ("DV", eos.dependent_vars(state)),
                #("exact_soln", exact_soln),
                ("reaction_rates", reaction_rates),
                ("cfl", local_cfl)
            ])

            write_visfile(discr,
                          viz_fields,
                          visualizer,
                          vizname=viz_path + casename,
                          step=step,
                          t=t,
                          overwrite=True,
                          vis_timer=vis_timer)

        if errors:
            raise RuntimeError("Error detected by user checkpoint, exiting.")

    if rank == 0:
        logging.info("Stepping.")

    (current_step, current_t, current_state) = \
        advance_state(rhs=my_rhs, timestepper=timestepper,
                      checkpoint=my_checkpoint,
                      get_timestep=get_timestep, state=current_state,
                      t_final=t_final, t=current_t, istep=current_step,
                      logmgr=logmgr,eos=eos,dim=dim)

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

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

    exit()
Beispiel #2
0
def main(ctx_factory=cl.create_some_context,
         snapshot_pattern="flame1d-{step:06d}-{rank:04d}.pkl",
         restart_step=None,
         use_profiling=False,
         use_logmgr=False):
    """Drive the Y0 example."""

    from mpi4py import MPI
    comm = MPI.COMM_WORLD
    rank = 0
    rank = comm.Get_rank()
    nparts = comm.Get_size()
    """logging and profiling"""
    logmgr = initialize_logmgr(use_logmgr,
                               filename="flame1d.sqlite",
                               mode="wo",
                               mpi_comm=comm)

    cl_ctx = ctx_factory()
    if use_profiling:
        queue = cl.CommandQueue(
            cl_ctx, properties=cl.command_queue_properties.PROFILING_ENABLE)
        actx = PyOpenCLProfilingArrayContext(
            queue,
            allocator=cl_tools.MemoryPool(cl_tools.ImmediateAllocator(queue)),
            logmgr=logmgr)
    else:
        queue = cl.CommandQueue(cl_ctx)
        actx = PyOpenCLArrayContext(queue,
                                    allocator=cl_tools.MemoryPool(
                                        cl_tools.ImmediateAllocator(queue)))

    #nviz = 500
    #nrestart = 500
    nviz = 1
    nrestart = 3
    #current_dt = 5.0e-8 # stable with euler
    current_dt = 5.0e-8  # stable with rk4
    #current_dt = 4e-7 # stable with lrsrk144
    t_final = 1.5e-7

    dim = 2
    order = 1
    exittol = 1000000000000
    #t_final = 0.001
    current_cfl = 1.0
    current_t = 0
    constant_cfl = False
    nstatus = 10000000000
    rank = 0
    checkpoint_t = current_t
    current_step = 0
    vel_burned = np.zeros(shape=(dim, ))
    vel_unburned = np.zeros(shape=(dim, ))

    # {{{  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
    # uiuc C2H4
    #mech_cti = get_mechanism_cti("uiuc")
    # sanDiego H2
    mech_cti = get_mechanism_cti("sanDiego")

    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.
    temp_unburned = 300.0
    temp_ignition = 1500.0
    # Parameters for calculating the amounts of fuel, oxidizer, and inert species
    equiv_ratio = 1.0
    ox_di_ratio = 0.21
    # H2
    stoich_ratio = 0.5
    #C2H4
    #stoich_ratio = 3.0
    # Grab the array indices for the specific species, ethylene, oxygen, and nitrogen
    # C2H4
    #i_fu = cantera_soln.species_index("C2H4")
    # H2
    i_fu = cantera_soln.species_index("H2")
    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
    pres_unburned = one_atm

    # Let the user know about how Cantera is being initilized
    print(f"Input state (T,P,X) = ({temp_unburned}, {pres_unburned}, {x}")
    # Set Cantera internal gas temperature, pressure, and mole fractios
    cantera_soln.TPX = temp_unburned, pres_unburned, x
    # Pull temperature, total density, mass fractions, and pressure from Cantera
    # We need total density, and mass fractions to initialize the fluid/gas state.
    y_unburned = np.zeros(nspecies)
    can_t, rho_unburned, y_unburned = 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.

    # now find the conditions for the burned gas
    cantera_soln.equilibrate('TP')
    temp_burned, rho_burned, y_burned = cantera_soln.TDY
    pres_burned = cantera_soln.P

    casename = "flame1d"
    pyrometheus_mechanism = pyro.get_thermochem_class(cantera_soln)(actx.np)

    # C2H4
    mu = 1.e-5
    kappa = 1.6e-5  # Pr = mu*rho/alpha = 0.75
    # H2
    mu = 1.e-5
    kappa = mu * 0.08988 / 0.75  # Pr = mu*rho/alpha = 0.75

    species_diffusivity = 1.e-5 * np.ones(nspecies)
    transport_model = SimpleTransport(viscosity=mu,
                                      thermal_conductivity=kappa,
                                      species_diffusivity=species_diffusivity)

    eos = PyrometheusMixture(pyrometheus_mechanism,
                             temperature_guess=temp_unburned,
                             transport_model=transport_model)
    species_names = pyrometheus_mechanism.species_names

    print(f"Pyrometheus mechanism species names {species_names}")
    print(
        f"Unburned state (T,P,Y) = ({temp_unburned}, {pres_unburned}, {y_unburned}"
    )
    print(f"Burned state (T,P,Y) = ({temp_burned}, {pres_burned}, {y_burned}")

    flame_start_loc = 0.05
    flame_speed = 1000

    # use the burned conditions with a lower temperature
    bulk_init = PlanarDiscontinuity(dim=dim,
                                    disc_location=flame_start_loc,
                                    sigma=0.01,
                                    nspecies=nspecies,
                                    temperature_left=temp_ignition,
                                    temperature_right=temp_unburned,
                                    pressure_left=pres_burned,
                                    pressure_right=pres_unburned,
                                    velocity_left=vel_burned,
                                    velocity_right=vel_unburned,
                                    species_mass_left=y_burned,
                                    species_mass_right=y_unburned)

    inflow_init = MixtureInitializer(dim=dim,
                                     nspecies=nspecies,
                                     pressure=pres_burned,
                                     temperature=temp_ignition,
                                     massfractions=y_burned,
                                     velocity=vel_burned)
    outflow_init = MixtureInitializer(dim=dim,
                                      nspecies=nspecies,
                                      pressure=pres_unburned,
                                      temperature=temp_unburned,
                                      massfractions=y_unburned,
                                      velocity=vel_unburned)

    inflow = PrescribedViscousBoundary(q_func=inflow_init)
    outflow = PrescribedViscousBoundary(q_func=outflow_init)
    wall = PrescribedViscousBoundary(
    )  # essentially a "dummy" use the interior solution for the exterior

    from grudge import sym
    boundaries = {
        sym.DTAG_BOUNDARY("Inflow"): inflow,
        sym.DTAG_BOUNDARY("Outflow"): outflow,
        sym.DTAG_BOUNDARY("Wall"): wall
    }

    if restart_step is None:
        char_len = 0.001
        box_ll = (0.0, 0.0)
        box_ur = (0.25, 0.01)
        num_elements = (int((box_ur[0] - box_ll[0]) / char_len),
                        int((box_ur[1] - box_ll[1]) / char_len))

        from meshmode.mesh.generation import generate_regular_rect_mesh
        generate_mesh = partial(generate_regular_rect_mesh,
                                a=box_ll,
                                b=box_ur,
                                n=num_elements,
                                mesh_type="X",
                                boundary_tag_to_face={
                                    "Inflow": ["-x"],
                                    "Outflow": ["+x"],
                                    "Wall": ["+y", "-y"]
                                })
        local_mesh, global_nelements = generate_and_distribute_mesh(
            comm, generate_mesh)
        local_nelements = local_mesh.nelements

    else:  # Restart
        with open(snapshot_pattern.format(step=restart_step, rank=rank),
                  "rb") as f:
            restart_data = pickle.load(f)

        local_mesh = restart_data["local_mesh"]
        local_nelements = local_mesh.nelements
        global_nelements = restart_data["global_nelements"]

        assert comm.Get_size() == restart_data["num_parts"]

    if rank == 0:
        logging.info("Making discretization")
    discr = EagerDGDiscretization(actx,
                                  local_mesh,
                                  order=order,
                                  mpi_communicator=comm)
    nodes = thaw(actx, discr.nodes())

    if restart_step is None:
        if rank == 0:
            logging.info("Initializing soln.")
        # for Discontinuity initial conditions
        current_state = bulk_init(t=0., x_vec=nodes, eos=eos)
        # for uniform background initial condition
        #current_state = bulk_init(nodes, eos=eos)
    else:
        current_t = restart_data["t"]
        current_step = restart_step

        current_state = unflatten(
            actx, discr.discr_from_dd("vol"),
            obj_array_vectorize(actx.from_numpy, restart_data["state"]))

    vis_timer = None

    if logmgr:
        logmgr_add_cl_device_info(logmgr, queue)
        logmgr_add_many_discretization_quantities(logmgr, discr, dim,
                                                  extract_vars_for_logging,
                                                  units_for_logging)
        logmgr.add_watches([
            "step.max", "t_sim.max", "t_step.max", "t_log.max", "min_pressure",
            "max_pressure", "min_temperature", "max_temperature"
        ])

        try:
            logmgr.add_watches(
                ["memory_usage_python.max", "memory_usage_gpu.max"])
        except KeyError:
            pass

        if use_profiling:
            logmgr.add_watches(["pyopencl_array_time.max"])

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

    visualizer = make_visualizer(discr, order)
    #    initname = initializer.__class__.__name__
    initname = "flame1d"
    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)

    #timestepper = rk4_step
    #timestepper = lsrk54_step
    #timestepper = lsrk144_step
    timestepper = euler_step

    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):
        # check for some troublesome output types
        inf_exists = not np.isfinite(discr.norm(state, np.inf))
        if inf_exists:
            if rank == 0:
                logging.info(
                    "Non-finite values detected in simulation, exiting...")
            # dump right now
            sim_checkpoint(discr=discr,
                           visualizer=visualizer,
                           eos=eos,
                           q=state,
                           vizname=casename,
                           step=999999999,
                           t=t,
                           dt=current_dt,
                           nviz=1,
                           exittol=exittol,
                           constant_cfl=constant_cfl,
                           comm=comm,
                           vis_timer=vis_timer,
                           overwrite=True,
                           s0=s0_sc,
                           kappa=kappa_sc)
            exit()

        cv = split_conserved(dim=dim, q=state)
        return (
            ns_operator(discr, q=state, t=t, boundaries=boundaries, eos=eos) +
            eos.get_species_source_terms(cv))

    def my_checkpoint(step, t, dt, state):

        write_restart = (check_step(step, nrestart)
                         if step != restart_step else False)
        if write_restart is True:
            with open(snapshot_pattern.format(step=step, rank=rank),
                      "wb") as f:
                pickle.dump(
                    {
                        "local_mesh": local_mesh,
                        "state": obj_array_vectorize(actx.to_numpy,
                                                     flatten(state)),
                        "t": t,
                        "step": step,
                        "global_nelements": global_nelements,
                        "num_parts": nparts,
                    }, f)

        def loc_fn(t):
            return flame_start_loc + flame_speed * t

        exact_soln = PlanarDiscontinuity(dim=dim,
                                         disc_location=loc_fn,
                                         sigma=0.0000001,
                                         nspecies=nspecies,
                                         temperature_left=temp_ignition,
                                         temperature_right=temp_unburned,
                                         pressure_left=pres_burned,
                                         pressure_right=pres_unburned,
                                         velocity_left=vel_burned,
                                         velocity_right=vel_unburned,
                                         species_mass_left=y_burned,
                                         species_mass_right=y_unburned)

        cv = split_conserved(dim, state)
        reaction_rates = eos.get_production_rates(cv)
        viz_fields = [("reaction_rates", reaction_rates)]

        return sim_checkpoint(discr=discr,
                              visualizer=visualizer,
                              eos=eos,
                              q=state,
                              vizname=casename,
                              step=step,
                              t=t,
                              dt=dt,
                              nstatus=nstatus,
                              nviz=nviz,
                              exittol=exittol,
                              constant_cfl=constant_cfl,
                              comm=comm,
                              vis_timer=vis_timer,
                              overwrite=True,
                              exact_soln=exact_soln,
                              viz_fields=viz_fields)

    if rank == 0:
        logging.info("Stepping.")

    (current_step, current_t, current_state) = \
        advance_state(rhs=my_rhs, timestepper=timestepper,
                      checkpoint=my_checkpoint,
                      get_timestep=get_timestep, state=current_state,
                      t_final=t_final, t=current_t, istep=current_step,
                      logmgr=logmgr,eos=eos,dim=dim)

    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")

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

    exit()
Beispiel #3
0
def test_pyrometheus_mechanisms(ctx_factory, mechname, rate_tol, y0):
    """Test known pyrometheus mechanisms.

    This test reproduces a pyrometheus-native test in the MIRGE context.

    Tests that the Pyrometheus mechanism code  gets the same thermo properties as the
    corresponding mechanism in Cantera.
    """
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    dim = 1
    nel_1d = 2

    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)

    order = 4

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

    discr = EagerDGDiscretization(actx, mesh, order=order)

    # Pyrometheus initialization
    mech_cti = get_mechanism_cti(mechname)
    sol = cantera.Solution(phase_id="gas", source=mech_cti)
    prometheus_mechanism = pyro.get_thermochem_class(sol)(actx.np)

    nspecies = prometheus_mechanism.num_species
    print(f"PyrometheusMixture::NumSpecies = {nspecies}")

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

    for fac in range(1, 11):
        pressin = fac * press0
        tempin = fac * temp0

        print(f"Testing (t,P) = ({tempin}, {pressin})")
        cantera_soln = cantera.Solution(phase_id="gas", source=mech_cti)
        cantera_soln.TPY = tempin, pressin, y0s
        cantera_soln.equilibrate("UV")
        can_t, can_rho, can_y = cantera_soln.TDY
        can_p = cantera_soln.P
        can_e = cantera_soln.int_energy_mass
        can_k = cantera_soln.forward_rate_constants
        can_c = cantera_soln.concentrations

        # Chemistry functions for testing pyro chem
        can_r = cantera_soln.net_rates_of_progress
        can_omega = cantera_soln.net_production_rates

        ones = discr.zeros(actx) + 1.0
        tin = can_t * ones
        pin = can_p * ones
        yin = make_obj_array([can_y[i] * ones for i in range(nspecies)])

        prom_rho = prometheus_mechanism.get_density(pin, tin, yin)
        prom_e = prometheus_mechanism.get_mixture_internal_energy_mass(
            tin, yin)
        prom_t = prometheus_mechanism.get_temperature(prom_e, tin, yin, True)
        prom_p = prometheus_mechanism.get_pressure(prom_rho, tin, yin)
        prom_c = prometheus_mechanism.get_concentrations(prom_rho, yin)
        prom_k = prometheus_mechanism.get_fwd_rate_coefficients(prom_t, prom_c)

        # Pyro chemistry functions
        prom_r = prometheus_mechanism.get_net_rates_of_progress(prom_t, prom_c)
        prom_omega = prometheus_mechanism.get_net_production_rates(
            prom_rho, prom_t, yin)

        print(f"can(rho, y, p, t, e, k) = ({can_rho}, {can_y}, "
              f"{can_p}, {can_t}, {can_e}, {can_k})")
        print(f"prom(rho, y, p, t, e, k) = ({prom_rho}, {y0s}, "
              f"{prom_p}, {prom_t}, {prom_e}, {prom_k})")

        # For pyro chem testing
        print(f"can_r = {can_r}")
        print(f"prom_r = {prom_r}")
        print(f"can_omega = {can_omega}")
        print(f"prom_omega = {prom_omega}")

        assert discr.norm((prom_c - can_c) / can_c, np.inf) < 1e-14
        assert discr.norm((prom_t - can_t) / can_t, np.inf) < 1e-14
        assert discr.norm((prom_rho - can_rho) / can_rho, np.inf) < 1e-14
        assert discr.norm((prom_p - can_p) / can_p, np.inf) < 1e-14
        assert discr.norm((prom_e - can_e) / can_e, np.inf) < 1e-6
        assert discr.norm((prom_k - can_k) / can_k, np.inf) < 1e-10

        # Pyro chem test comparisons
        for i, rate in enumerate(can_r):
            assert discr.norm((prom_r[i] - rate), np.inf) < rate_tol
        for i, rate in enumerate(can_omega):
            assert discr.norm((prom_omega[i] - rate), np.inf) < rate_tol
Beispiel #4
0
def main(ctx_factory=cl.create_some_context, use_leap=False):
    """Drive example."""
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue,
                                allocator=cl_tools.MemoryPool(
                                    cl_tools.ImmediateAllocator(queue)))

    dim = 3
    nel_1d = 16
    order = 3
    exittol = 10.0
    t_final = 0.002
    current_cfl = 1.0
    velocity = np.zeros(shape=(dim, ))
    velocity[:dim] = 1.0
    current_dt = .001
    current_t = 0
    constant_cfl = False
    nstatus = 1
    nviz = 1
    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 = -5.0
    box_ur = 5.0
    error_state = 0

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

    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())
    casename = "uiuc_mixture"

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

    nspecies = pyrometheus_mechanism.num_species
    eos = PyrometheusMixture(pyrometheus_mechanism)

    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)
    initializer = MixtureInitializer(dim=dim,
                                     nspecies=nspecies,
                                     massfractions=y0s,
                                     velocity=velocity)

    boundaries = {BTAG_ALL: PrescribedBoundary(initializer)}
    nodes = thaw(actx, discr.nodes())
    current_state = initializer(x_vec=nodes, eos=eos)

    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)

    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):
        global checkpoint_t
        checkpoint_t = t
        return sim_checkpoint(discr,
                              visualizer,
                              eos,
                              cv=state,
                              exact_soln=initializer,
                              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:
        error_state = 1
        current_step = ex.step
        current_t = ex.t
        current_state = ex.state

    if current_t != checkpoint_t:  # This check because !overwrite
        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 = 1

    if error_state:
        raise ValueError("Simulation did not complete successfully.")
Beispiel #5
0
def test_pyrometheus_eos(ctx_factory, mechname, dim, y0, vel):
    """Test PyrometheusMixture EOS for all available mechanisms.

    Tests that the PyrometheusMixture EOS gets the same thermo properties
    (p, T, e) as the Pyrometheus-native mechanism code.
    """
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    nel_1d = 4

    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)

    order = 4

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

    discr = EagerDGDiscretization(actx, mesh, order=order)
    nodes = thaw(actx, discr.nodes())

    # Pyrometheus initialization
    mech_cti = get_mechanism_cti(mechname)
    sol = cantera.Solution(phase_id="gas", source=mech_cti)
    prometheus_mechanism = pyro.get_thermochem_class(sol)(actx.np)

    nspecies = prometheus_mechanism.num_species
    print(f"PrometheusMixture::Mechanism = {mechname}")
    print(f"PrometheusMixture::NumSpecies = {nspecies}")

    press0 = 101500.0
    temp0 = 300.0
    y0s = np.zeros(shape=(nspecies, ))
    for i in range(1, nspecies):
        y0s[i] = y0 / (10.0**i)
    y0s[0] = 1.0 - np.sum(y0s[1:])
    velocity = vel * np.ones(shape=(dim, ))

    for fac in range(1, 11):
        tempin = fac * temp0
        pressin = fac * press0

        print(f"Testing {mechname}(t,P) = ({tempin}, {pressin})")

        ones = discr.zeros(actx) + 1.0
        tin = tempin * ones
        pin = pressin * ones
        yin = y0s * ones
        tguess = 300.0

        pyro_rho = prometheus_mechanism.get_density(pin, tin, yin)
        pyro_e = prometheus_mechanism.get_mixture_internal_energy_mass(
            tin, yin)
        pyro_t = prometheus_mechanism.get_temperature(pyro_e, tguess, yin,
                                                      True)
        pyro_p = prometheus_mechanism.get_pressure(pyro_rho, pyro_t, yin)

        print(f"prom(rho, y, p, t, e) = ({pyro_rho}, {y0s}, "
              f"{pyro_p}, {pyro_t}, {pyro_e})")

        eos = PyrometheusMixture(prometheus_mechanism)
        initializer = MixtureInitializer(dim=dim,
                                         nspecies=nspecies,
                                         pressure=pyro_p,
                                         temperature=pyro_t,
                                         massfractions=y0s,
                                         velocity=velocity)

        cv = initializer(eos=eos, t=0, x_vec=nodes)
        p = eos.pressure(cv)
        temperature = eos.temperature(cv)
        internal_energy = eos.get_internal_energy(tin, yin)
        y = eos.species_fractions(cv)

        print(f"pyro_y = {y}")
        print(f"pyro_eos.p = {p}")
        print(f"pyro_eos.temp = {temperature}")
        print(f"pyro_eos.e = {internal_energy}")

        tol = 1e-14
        assert discr.norm((cv.mass - pyro_rho) / pyro_rho, np.inf) < tol
        assert discr.norm((temperature - pyro_t) / pyro_t, np.inf) < tol
        assert discr.norm((internal_energy - pyro_e) / pyro_e, np.inf) < tol
        assert discr.norm((p - pyro_p) / pyro_p, np.inf) < tol
Beispiel #6
0
def test_pyrometheus_kinetics(ctx_factory, mechname, rate_tol, y0):
    """Test known pyrometheus reaction mechanisms.

    This test reproduces a pyrometheus-native test in the MIRGE context.

    Tests that the Pyrometheus mechanism code gets the same chemical properties
    and reaction rates as the corresponding mechanism in Cantera. The reactions
    are integrated in time and verified against a homogeneous reactor in
    Cantera.
    """
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    dim = 1
    nel_1d = 4

    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)

    order = 4

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

    discr = EagerDGDiscretization(actx, mesh, order=order)
    ones = discr.zeros(actx) + 1.0

    # Pyrometheus initialization
    mech_cti = get_mechanism_cti(mechname)
    cantera_soln = cantera.Solution(phase_id="gas", source=mech_cti)
    pyro_obj = pyro.get_thermochem_class(cantera_soln)(actx.np)

    nspecies = pyro_obj.num_species
    print(f"PrometheusMixture::NumSpecies = {nspecies}")

    tempin = 1500.0
    pressin = cantera.one_atm
    print(f"Testing (t,P) = ({tempin}, {pressin})")

    # Homogeneous reactor to get test data
    equiv_ratio = 1.0
    ox_di_ratio = 0.21
    stoich_ratio = 0.5
    i_fu = cantera_soln.species_index("H2")
    i_ox = cantera_soln.species_index("O2")
    i_di = cantera_soln.species_index("N2")
    x = np.zeros(shape=(nspecies, ))
    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

    cantera_soln.TPX = tempin, pressin, x
    #    cantera_soln.equilibrate("UV")
    can_t, can_rho, can_y = cantera_soln.TDY
    #    can_p = cantera_soln.P

    reactor = cantera.IdealGasConstPressureReactor(cantera_soln)
    sim = cantera.ReactorNet([reactor])
    time = 0.0
    for _ in range(50):
        time += 1.0e-6
        sim.advance(time)

        # Cantera kinetics
        can_r = reactor.kinetics.net_rates_of_progress
        can_omega = reactor.kinetics.net_production_rates

        # Get state from Cantera
        can_t = reactor.T
        can_rho = reactor.density
        can_y = reactor.Y
        print(f"can_y = {can_y}")

        tin = can_t * ones
        rhoin = can_rho * ones
        yin = can_y * ones

        # Prometheus kinetics
        pyro_c = pyro_obj.get_concentrations(rhoin, yin)
        print(f"pyro_conc = {pyro_c}")

        pyro_r = pyro_obj.get_net_rates_of_progress(tin, pyro_c)
        pyro_omega = pyro_obj.get_net_production_rates(rhoin, tin, yin)

        # Print
        print(f"can_r = {can_r}")
        print(f"pyro_r = {pyro_r}")
        abs_diff = discr.norm(pyro_r - can_r, np.inf)
        if abs_diff > 1e-14:
            min_r = (np.abs(can_r)).min()
            if min_r > 0:
                assert discr.norm((pyro_r - can_r) / can_r, np.inf) < rate_tol
            else:
                assert discr.norm(pyro_r, np.inf) < rate_tol

        print(f"can_omega = {can_omega}")
        print(f"pyro_omega = {pyro_omega}")
        for i, omega in enumerate(can_omega):
            omin = np.abs(omega).min()
            if omin > 1e-12:
                assert discr.norm(
                    (pyro_omega[i] - omega) / omega, np.inf) < 1e-8
            else:
                assert discr.norm(pyro_omega[i], np.inf) < 1e-12
Beispiel #7
0
def run_init(
    ctx_factory=cl.create_some_context,
    snapshot_pattern="flame1d-{step:06d}-{rank:04d}.pkl",
):
    """Drive the Y0 example."""

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

    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue,
                                allocator=cl_tools.MemoryPool(
                                    cl_tools.ImmediateAllocator(queue)))

    dim = 2
    order = 1
    vel_burned = np.zeros(shape=(dim, ))
    vel_unburned = np.zeros(shape=(dim, ))

    # {{{  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
    # uiuc C2H4
    #mech_cti = get_mechanism_cti("uiuc")
    # sanDiego H2
    mech_cti = get_mechanism_cti("sanDiego")

    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.
    temp_unburned = 300.0
    temp_ignition = 1500.0
    # Parameters for calculating the amounts of fuel, oxidizer, and inert species
    equiv_ratio = 1.0
    ox_di_ratio = 0.21
    # H2
    stoich_ratio = 0.5
    #C2H4
    #stoich_ratio = 3.0
    # Grab the array indices for the specific species, ethylene, oxygen, and nitrogen
    # C2H4
    #i_fu = cantera_soln.species_index("C2H4")
    # H2
    i_fu = cantera_soln.species_index("H2")
    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
    pres_unburned = one_atm

    # Let the user know about how Cantera is being initilized
    print(f"Input state (T,P,X) = ({temp_unburned}, {pres_unburned}, {x}")
    # Set Cantera internal gas temperature, pressure, and mole fractios
    cantera_soln.TPX = temp_unburned, pres_unburned, x
    # Pull temperature, total density, mass fractions, and pressure from Cantera
    # We need total density, and mass fractions to initialize the fluid/gas state.
    y_unburned = np.zeros(nspecies)
    can_t, rho_unburned, y_unburned = 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.

    # now find the conditions for the burned gas
    cantera_soln.equilibrate('TP')
    temp_burned, rho_burned, y_burned = cantera_soln.TDY
    pres_burned = cantera_soln.P

    casename = "flame1d"
    pyrometheus_mechanism = pyro.get_thermochem_class(cantera_soln)(actx.np)

    # C2H4
    mu = 1.e-5
    kappa = 1.6e-5  # Pr = mu*rho/alpha = 0.75
    # H2
    mu = 1.e-5
    kappa = mu * 0.08988 / 0.75  # Pr = mu*rho/alpha = 0.75

    species_diffusivity = 1.e-5 * np.ones(nspecies)
    transport_model = SimpleTransport(viscosity=mu,
                                      thermal_conductivity=kappa,
                                      species_diffusivity=species_diffusivity)

    eos = PyrometheusMixture(pyrometheus_mechanism,
                             temperature_guess=temp_unburned,
                             transport_model=transport_model)
    species_names = pyrometheus_mechanism.species_names

    print(f"Pyrometheus mechanism species names {species_names}")
    print(
        f"Unburned state (T,P,Y) = ({temp_unburned}, {pres_unburned}, {y_unburned}"
    )
    print(f"Burned state (T,P,Y) = ({temp_burned}, {pres_burned}, {y_burned}")

    flame_start_loc = 0.05
    flame_speed = 1000

    # use the burned conditions with a lower temperature
    bulk_init = PlanarDiscontinuity(dim=dim,
                                    disc_location=flame_start_loc,
                                    sigma=0.01,
                                    nspecies=nspecies,
                                    temperature_left=temp_ignition,
                                    temperature_right=temp_unburned,
                                    pressure_left=pres_burned,
                                    pressure_right=pres_unburned,
                                    velocity_left=vel_burned,
                                    velocity_right=vel_unburned,
                                    species_mass_left=y_burned,
                                    species_mass_right=y_unburned)

    char_len = 0.001
    box_ll = (0.0, 0.0)
    box_ur = (0.25, 0.01)
    num_elements = (int((box_ur[0] - box_ll[0]) / char_len),
                    int((box_ur[1] - box_ll[1]) / char_len))

    from meshmode.mesh.generation import generate_regular_rect_mesh
    generate_mesh = partial(generate_regular_rect_mesh,
                            a=box_ll,
                            b=box_ur,
                            n=num_elements,
                            mesh_type="X",
                            boundary_tag_to_face={
                                "Inflow": ["-x"],
                                "Outflow": ["+x"],
                                "Wall": ["+y", "-y"]
                            })
    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())

    # for Discontinuity initial conditions
    state = bulk_init(t=0., x_vec=nodes, eos=eos)
    # for uniform background initial condition
    #current_state = bulk_init(nodes, eos=eos)

    visualizer = make_visualizer(discr, order)

    with open(snapshot_pattern.format(step=0, rank=rank), "wb") as f:
        pickle.dump(
            {
                "local_mesh": local_mesh,
                "state": obj_array_vectorize(actx.to_numpy, flatten(state)),
                "t": 0.,
                "step": 0,
                "global_nelements": global_nelements,
                "num_parts": nparts,
            }, f)

    cv = split_conserved(dim, state)
    reaction_rates = eos.get_production_rates(cv)
    viz_fields = [("reaction_rates", reaction_rates)]

    sim_checkpoint(discr=discr,
                   visualizer=visualizer,
                   eos=eos,
                   q=state,
                   vizname=casename,
                   nviz=0,
                   comm=comm,
                   overwrite=True,
                   viz_fields=viz_fields)

    exit()
Beispiel #8
0
def _pyro_thermochem_wrapper_class(cantera_soln, temperature_niter=5):
    """Return a MIRGE-compatible wrapper for a :mod:`pyrometheus` mechanism class.

    Dynamically creates a class that inherits from a
    :class:`pyrometheus.Thermochemistry` class and overrides a couple of the methods
    to adapt it to :mod:`mirgecom`'s needs.

        - get_concentrations: overrides :class:`pyrometheus.Thermochemistry` version
        of  the same function, pinning any negative concentrations due to slightly
        negative massfractions (which are OK) back to 0.
        - get_temperature: MIRGE-specific interface to use a hard-coded Newton solver
        to find a temperature from an input state.

    Parameters
    ----------
    cantera_soln: Cantera solution
        Cantera solution from which to create the thermochemical mechanism
    temperature_niter: integer
        Number of Newton iterations in `get_temperature` (default=5)
    """
    import pyrometheus as pyro
    pyro_class = pyro.get_thermochem_class(cantera_soln)

    class PyroWrapper(pyro_class):

        # This bit disallows negative concentrations and instead
        # pins them to 0. mass_fractions can sometimes be slightly
        # negative and that's ok.
        def get_concentrations(self, rho, mass_fractions):
            concs = self.iwts * rho * mass_fractions
            # ensure non-negative concentrations
            zero = self._pyro_zeros_like(concs[0])
            for i in range(self.num_species):
                concs[i] = self.usr_np.where(self.usr_np.less(concs[i], 0),
                                             zero, concs[i])
            return concs

        # This is the temperature update for *get_temperature*.  Having this
        # separated out allows it to be used in the fluid drivers for evaluating
        # convergence of the temperature calculation.
        def get_temperature_update_energy(self, e_in, t_in, y):
            pv_func = self.get_mixture_specific_heat_cv_mass
            he_func = self.get_mixture_internal_energy_mass
            return (e_in - he_func(t_in, y)) / pv_func(t_in, y)

        # This hard-codes the number of Newton iterations because the convergence
        # check is not compatible with lazy evaluation. Instead, we plan to check
        # the temperature residual at simulation health checking time.
        # FIXME: Occasional convergence check is other-than-ideal; revisit asap.
        # - could adapt dt or num_iter on temperature convergence?
        # - can pass-in num_iter?
        def get_temperature(self, energy, temperature_guess,
                            species_mass_fractions):
            """Compute the temperature of the mixture from thermal energy.

            Parameters
            ----------
            energy: :class:`~meshmode.dof_array.DOFArray`
                The internal (thermal) energy of the mixture.
            temperature_guess: :class:`~meshmode.dof_array.DOFArray`
                An initial starting temperature for the Newton iterations.
            species_mass_fractions: numpy.ndarray
                An object array of :class:`~meshmode.dof_array.DOFArray` with the
                mass fractions of the mixture species.

            Returns
            -------
            :class:`~meshmode.dof_array.DOFArray`
                The mixture temperature after a fixed number of Newton iterations.
            """
            num_iter = temperature_niter
            t_i = temperature_guess
            for _ in range(num_iter):
                t_i = t_i + self.get_temperature_update_energy(
                    energy, t_i, species_mass_fractions)
            return t_i

    return PyroWrapper
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
Beispiel #10
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):
    """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()

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

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

    nspecies = pyrometheus_mechanism.num_species
    eos = PyrometheusMixture(pyrometheus_mechanism)

    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)

    boundaries = {
        BTAG_ALL: PrescribedInviscidBoundary(fluid_solution_func=initializer)
    }
    nodes = thaw(actx, discr.nodes())
    if rst_filename:
        current_t = restart_data["t"]
        current_step = restart_data["step"]
        current_state = restart_data["state"]
        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_state = initializer(x_vec=nodes, eos=eos)

    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):
        status_msg = (
            "------- errors="
            + ", ".join("%.3g" % en for en in component_errors))
        if rank == 0:
            logger.info(status_msg)

    def my_write_viz(step, t, state, dv=None, exact=None, resid=None):
        viz_fields = [("cv", state)]
        if dv is None:
            dv = eos.dependent_vars(state)
        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),
                      ("exact_soln", exact),
                      ("residual", resid)]
        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,
                "state": state,
                "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):
        try:
            dv = None
            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:
                dv = eos.dependent_vars(state)
                exact = initializer(x_vec=nodes, eos=eos, time=t)
                from mirgecom.simutil import compare_fluid_solutions
                component_errors = compare_fluid_solutions(discr, state, exact)
                from mirgecom.simutil import allsync
                health_errors = allsync(my_health_check(dv, component_errors), comm,
                                        op=MPI.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:
                if dv is None:
                    dv = eos.dependent_vars(state)
                if exact is None:
                    exact = initializer(x_vec=nodes, eos=eos, time=t)
                resid = state - exact
                my_write_viz(step=step, t=t, state=state, 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, state, exact)
                my_write_status(component_errors)

        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, state, t, dt, current_cfl, eos, 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):
        return euler_operator(discr, cv=state, time=t,
                              boundaries=boundaries, eos=eos)

    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, eos=eos,
                      dim=dim)

    # Dump the final data
    if rank == 0:
        logger.info("Checkpointing final state ...")
    final_dv = eos.dependent_vars(current_state)
    final_exact = initializer(x_vec=nodes, eos=eos, time=current_t)
    final_resid = current_state - final_exact
    my_write_viz(step=current_step, t=current_t, state=current_state, dv=final_dv,
                 exact=final_exact, resid=final_resid)
    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
Beispiel #11
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.")