def test_viscous_stress_tensor(actx_factory, transport_model): """Test tau data structure and values against exact.""" actx = actx_factory() dim = 3 nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(1.0, ) * dim, b=(2.0, ) * dim, nelements_per_axis=(nel_1d, ) * dim) order = 1 discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) zeros = discr.zeros(actx) ones = zeros + 1.0 # assemble velocities for simple, unique grad components velocity_x = nodes[0] + 2 * nodes[1] + 3 * nodes[2] velocity_y = 4 * nodes[0] + 5 * nodes[1] + 6 * nodes[2] velocity_z = 7 * nodes[0] + 8 * nodes[1] + 9 * nodes[2] velocity = make_obj_array([velocity_x, velocity_y, velocity_z]) mass = 2 * ones energy = zeros + 2.5 mom = mass * velocity cv = make_conserved(dim, mass=mass, energy=energy, momentum=mom) grad_cv = op.local_grad(discr, cv) if transport_model: tv_model = SimpleTransport(bulk_viscosity=1.0, viscosity=0.5) else: tv_model = PowerLawTransport() eos = IdealSingleGas() gas_model = GasModel(eos=eos, transport=tv_model) fluid_state = make_fluid_state(cv, gas_model) mu = tv_model.viscosity(eos, cv) lam = tv_model.volume_viscosity(eos, cv) # Exact answer for tau exp_grad_v = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) exp_grad_v_t = np.array([[1, 4, 7], [2, 5, 8], [3, 6, 9]]) exp_div_v = 15 exp_tau = (mu * (exp_grad_v + exp_grad_v_t) + lam * exp_div_v * np.eye(3)) from mirgecom.viscous import viscous_stress_tensor tau = viscous_stress_tensor(fluid_state, grad_cv) # The errors come from grad_v assert actx.to_numpy(discr.norm(tau - exp_tau, np.inf)) < 1e-12
def test_local_max_species_diffusivity(actx_factory, dim, array_valued): """Test the local maximum species diffusivity.""" actx = actx_factory() nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(1.0, ) * dim, b=(2.0, ) * dim, nelements_per_axis=(nel_1d, ) * dim) order = 1 discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) zeros = discr.zeros(actx) ones = zeros + 1.0 vel = .32 velocity = make_obj_array([zeros + vel for _ in range(dim)]) massval = 1 mass = massval * ones energy = zeros + 1.0 / (1.4 * .4) mom = mass * velocity species_mass = np.array([1., 2., 3.], dtype=object) cv = make_conserved(dim, mass=mass, energy=energy, momentum=mom, species_mass=species_mass) d_alpha_input = np.array([.1, .2, .3]) if array_valued: f = 1 + 0.1 * actx.np.sin(nodes[0]) d_alpha_input *= f tv_model = SimpleTransport(species_diffusivity=d_alpha_input) eos = IdealSingleGas() d_alpha = tv_model.species_diffusivity(eos, cv) from mirgecom.viscous import get_local_max_species_diffusivity expected = .3 * ones if array_valued: expected *= f calculated = get_local_max_species_diffusivity(actx, d_alpha) assert actx.to_numpy(discr.norm(calculated - expected, np.inf)) == 0
def test_viscous_timestep(actx_factory, dim, mu, vel): """Test timestep size.""" actx = actx_factory() nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(1.0, ) * dim, b=(2.0, ) * dim, nelements_per_axis=(nel_1d, ) * dim) order = 1 discr = EagerDGDiscretization(actx, mesh, order=order) zeros = discr.zeros(actx) ones = zeros + 1.0 velocity = make_obj_array([zeros + vel for _ in range(dim)]) massval = 1 mass = massval * ones # I *think* this energy should yield c=1.0 energy = zeros + 1.0 / (1.4 * .4) mom = mass * velocity species_mass = None cv = make_conserved(dim, mass=mass, energy=energy, momentum=mom, species_mass=species_mass) from grudge.dt_utils import characteristic_lengthscales chlen = characteristic_lengthscales(actx, discr) from grudge.op import nodal_min chlen_min = nodal_min(discr, "vol", chlen) mu = mu * chlen_min if mu < 0: mu = 0 tv_model = None else: tv_model = SimpleTransport(viscosity=mu) eos = IdealSingleGas() gas_model = GasModel(eos=eos, transport=tv_model) fluid_state = make_fluid_state(cv, gas_model) from mirgecom.viscous import get_viscous_timestep dt_field = get_viscous_timestep(discr, fluid_state) speed_total = fluid_state.wavespeed dt_expected = chlen / (speed_total + (mu / chlen)) error = (dt_expected - dt_field) / dt_expected assert actx.to_numpy(discr.norm(error, np.inf)) == 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()
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()
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()
def test_diffusive_heat_flux(actx_factory): """Test diffusive heat flux and values against exact.""" actx = actx_factory() dim = 3 nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(1.0, ) * dim, b=(2.0, ) * dim, nelements_per_axis=(nel_1d, ) * dim) order = 1 discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) zeros = discr.zeros(actx) ones = zeros + 1.0 # assemble velocities for simple, unique grad components velocity_x = nodes[0] + 2 * nodes[1] + 3 * nodes[2] velocity_y = 4 * nodes[0] + 5 * nodes[1] + 6 * nodes[2] velocity_z = 7 * nodes[0] + 8 * nodes[1] + 9 * nodes[2] velocity = make_obj_array([velocity_x, velocity_y, velocity_z]) # assemble y so that each one has simple, but unique grad components nspecies = 2 * dim y = make_obj_array([ones for _ in range(nspecies)]) for idim in range(dim): ispec = 2 * idim y[ispec] = (ispec + 1) * (idim * dim + 1) * sum( [(iidim + 1) * nodes[iidim] for iidim in range(dim)]) y[ispec + 1] = -y[ispec] massval = 2 mass = massval * ones energy = zeros + 2.5 mom = mass * velocity species_mass = mass * y cv = make_conserved(dim, mass=mass, energy=energy, momentum=mom, species_mass=species_mass) grad_cv = op.local_grad(discr, cv) mu_b = 1.0 mu = 0.5 kappa = 5.0 # assemble d_alpha so that every species has a unique j d_alpha = np.array([(ispec + 1) for ispec in range(nspecies)]) tv_model = SimpleTransport(bulk_viscosity=mu_b, viscosity=mu, thermal_conductivity=kappa, species_diffusivity=d_alpha) eos = IdealSingleGas() gas_model = GasModel(eos=eos, transport=tv_model) fluid_state = make_fluid_state(cv, gas_model) from mirgecom.viscous import diffusive_flux j = diffusive_flux(fluid_state, grad_cv) def inf_norm(x): return actx.to_numpy(discr.norm(x, np.inf)) tol = 1e-10 for idim in range(dim): ispec = 2 * idim exact_dy = np.array([((ispec + 1) * (idim * dim + 1)) * (iidim + 1) for iidim in range(dim)]) exact_j = -massval * d_alpha[ispec] * exact_dy assert inf_norm(j[ispec] - exact_j) < tol exact_j = massval * d_alpha[ispec + 1] * exact_dy assert inf_norm(j[ispec + 1] - exact_j) < tol
def test_poiseuille_fluxes(actx_factory, order, kappa): """Test the viscous fluxes using a Poiseuille input state.""" actx = actx_factory() dim = 2 from pytools.convergence import EOCRecorder e_eoc_rec = EOCRecorder() p_eoc_rec = EOCRecorder() base_pressure = 100000.0 pressure_ratio = 1.001 mu = 42 # arbitrary left_boundary_location = 0 right_boundary_location = 0.1 ybottom = 0. ytop = .02 nspecies = 0 spec_diffusivity = 0 * np.ones(nspecies) transport_model = SimpleTransport(viscosity=mu, thermal_conductivity=kappa, species_diffusivity=spec_diffusivity) xlen = right_boundary_location - left_boundary_location p_low = base_pressure p_hi = pressure_ratio * base_pressure dpdx = (p_low - p_hi) / xlen rho = 1.0 eos = IdealSingleGas() gas_model = GasModel(eos=eos, transport=transport_model) from mirgecom.initializers import PlanarPoiseuille initializer = PlanarPoiseuille(density=rho, mu=mu) def _elbnd_flux(discr, compute_interior_flux, compute_boundary_flux, int_tpair, boundaries): return (compute_interior_flux(int_tpair) + sum(compute_boundary_flux(btag) for btag in boundaries)) from mirgecom.flux import gradient_flux_central def cv_flux_interior(int_tpair): normal = thaw(actx, discr.normal(int_tpair.dd)) flux_weak = gradient_flux_central(int_tpair, normal) dd_all_faces = int_tpair.dd.with_dtag("all_faces") return discr.project(int_tpair.dd, dd_all_faces, flux_weak) def cv_flux_boundary(btag): boundary_discr = discr.discr_from_dd(btag) bnd_nodes = thaw(actx, boundary_discr.nodes()) cv_bnd = initializer(x_vec=bnd_nodes, eos=eos) bnd_nhat = thaw(actx, discr.normal(btag)) from grudge.trace_pair import TracePair bnd_tpair = TracePair(btag, interior=cv_bnd, exterior=cv_bnd) flux_weak = gradient_flux_central(bnd_tpair, bnd_nhat) dd_all_faces = bnd_tpair.dd.with_dtag("all_faces") return discr.project(bnd_tpair.dd, dd_all_faces, flux_weak) for nfac in [1, 2, 4]: npts_axis = nfac * (11, 21) box_ll = (left_boundary_location, ybottom) box_ur = (right_boundary_location, ytop) mesh = _get_box_mesh(2, a=box_ll, b=box_ur, n=npts_axis) logger.info(f"Number of {dim}d elements: {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) def inf_norm(x): return actx.to_numpy(discr.norm(x, np.inf)) # compute max element size from grudge.dt_utils import h_max_from_volume h_max = h_max_from_volume(discr) # form exact cv cv = initializer(x_vec=nodes, eos=eos) cv_int_tpair = interior_trace_pair(discr, cv) boundaries = [BTAG_ALL] cv_flux_bnd = _elbnd_flux(discr, cv_flux_interior, cv_flux_boundary, cv_int_tpair, boundaries) from mirgecom.operators import grad_operator from grudge.dof_desc import as_dofdesc dd_vol = as_dofdesc("vol") dd_faces = as_dofdesc("all_faces") grad_cv = grad_operator(discr, dd_vol, dd_faces, cv, cv_flux_bnd) xp_grad_cv = initializer.exact_grad(x_vec=nodes, eos=eos, cv_exact=cv) xp_grad_v = 1 / cv.mass * xp_grad_cv.momentum xp_tau = mu * (xp_grad_v + xp_grad_v.transpose()) # sanity check the gradient: relerr_scale_e = 1.0 / inf_norm(xp_grad_cv.energy) relerr_scale_p = 1.0 / inf_norm(xp_grad_cv.momentum) graderr_e = inf_norm(grad_cv.energy - xp_grad_cv.energy) graderr_p = inf_norm(grad_cv.momentum - xp_grad_cv.momentum) graderr_e *= relerr_scale_e graderr_p *= relerr_scale_p assert graderr_e < 5e-7 assert graderr_p < 5e-11 zeros = discr.zeros(actx) ones = zeros + 1 pressure = eos.pressure(cv) # grad of p should be dp/dx xp_grad_p = make_obj_array([dpdx * ones, zeros]) grad_p = op.local_grad(discr, pressure) dpscal = 1.0 / np.abs(dpdx) temperature = eos.temperature(cv) tscal = rho * eos.gas_const() * dpscal xp_grad_t = xp_grad_p / (cv.mass * eos.gas_const()) grad_t = op.local_grad(discr, temperature) # sanity check assert inf_norm(grad_p - xp_grad_p) * dpscal < 5e-9 assert inf_norm(grad_t - xp_grad_t) * tscal < 5e-9 fluid_state = make_fluid_state(cv, gas_model) # verify heat flux from mirgecom.viscous import conductive_heat_flux heat_flux = conductive_heat_flux(fluid_state, grad_t) xp_heat_flux = -kappa * xp_grad_t assert inf_norm(heat_flux - xp_heat_flux) < 2e-8 xp_e_flux = np.dot(xp_tau, cv.velocity) - xp_heat_flux xp_mom_flux = xp_tau from mirgecom.viscous import viscous_flux vflux = viscous_flux(fluid_state, grad_cv, grad_t) efluxerr = (inf_norm(vflux.energy - xp_e_flux) / inf_norm(xp_e_flux)) momfluxerr = (inf_norm(vflux.momentum - xp_mom_flux) / inf_norm(xp_mom_flux)) assert inf_norm(vflux.mass) == 0 e_eoc_rec.add_data_point(actx.to_numpy(h_max), efluxerr) p_eoc_rec.add_data_point(actx.to_numpy(h_max), momfluxerr) assert (e_eoc_rec.order_estimate() >= order - 0.5 or e_eoc_rec.max_error() < 3e-9) assert (p_eoc_rec.order_estimate() >= order - 0.5 or p_eoc_rec.max_error() < 2e-12)