def _euler_flow_stepper(actx, parameters): logging.basicConfig(format="%(message)s", level=logging.INFO) mesh = parameters["mesh"] t = parameters["time"] order = parameters["order"] t_final = parameters["tfinal"] initializer = parameters["initializer"] exittol = parameters["exittol"] casename = parameters["casename"] boundaries = parameters["boundaries"] eos = parameters["eos"] cfl = parameters["cfl"] dt = parameters["dt"] constantcfl = parameters["constantcfl"] nstepstatus = parameters["nstatus"] if t_final <= t: return (0.0) rank = 0 dim = mesh.dim istep = 0 discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) cv = initializer(nodes) sdt = cfl * get_inviscid_timestep(discr, eos=eos, cv=cv) initname = initializer.__class__.__name__ eosname = eos.__class__.__name__ logger.info(f"Num {dim}d order-{order} elements: {mesh.nelements}\n" f"Timestep: {dt}\n" f"Final time: {t_final}\n" f"Status freq: {nstepstatus}\n" f"Initialization: {initname}\n" f"EOS: {eosname}") vis = make_visualizer(discr, order) def write_soln(state, write_status=True): dv = eos.dependent_vars(cv=state) expected_result = initializer(nodes, t=t) result_resid = (state - expected_result).join() maxerr = [ np.max(np.abs(result_resid[i].get())) for i in range(dim + 2) ] mindv = [np.min(dvfld.get()) for dvfld in dv] maxdv = [np.max(dvfld.get()) for dvfld in dv] if write_status is True: statusmsg = (f"Status: Step({istep}) Time({t})\n" f"------ P({mindv[0]},{maxdv[0]})\n" f"------ T({mindv[1]},{maxdv[1]})\n" f"------ dt,cfl = ({dt},{cfl})\n" f"------ Err({maxerr})") logger.info(statusmsg) io_fields = ["cv", state] io_fields += eos.split_fields(dim, dv) io_fields.append(("exact_soln", expected_result)) io_fields.append(("residual", result_resid)) nameform = casename + "-{iorank:04d}-{iostep:06d}.vtu" visfilename = nameform.format(iorank=rank, iostep=istep) vis.write_vtk_file(visfilename, io_fields) return maxerr def rhs(t, q): return euler_operator(discr, eos=eos, boundaries=boundaries, cv=q, time=t) filter_order = 8 eta = .5 alpha = -1.0 * np.log(np.finfo(float).eps) nummodes = int(1) for _ in range(dim): nummodes *= int(order + dim + 1) nummodes /= math.factorial(int(dim)) cutoff = int(eta * order) from mirgecom.filter import (exponential_mode_response_function as xmrfunc, filter_modally) frfunc = partial(xmrfunc, alpha=alpha, filter_order=filter_order) while t < t_final: if constantcfl is True: dt = sdt else: cfl = dt / sdt if nstepstatus > 0: if istep % nstepstatus == 0: write_soln(state=cv) cv = rk4_step(cv, t, dt, rhs) cv = make_conserved(dim, q=filter_modally(discr, "vol", cutoff, frfunc, cv.join())) t += dt istep += 1 sdt = cfl * get_inviscid_timestep(discr, eos=eos, cv=cv) if nstepstatus > 0: logger.info("Writing final dump.") maxerr = max(write_soln(cv, False)) else: expected_result = initializer(nodes, time=t) maxerr = discr.norm((cv - expected_result).join(), np.inf) logger.info(f"Max Error: {maxerr}") if maxerr > exittol: raise ValueError("Solution failed to follow expected result.") return (maxerr)
def test_filter_function(actx_factory, dim, order, do_viz=False): """ Test the stand-alone procedural interface to spectral filtering. Tests that filtered fields have expected attenuated higher modes. """ actx = actx_factory() logger = logging.getLogger(__name__) filter_order = 1 nel_1d = 1 eta = .5 # filter half the modes # Alpha value suggested by: # JSH/TW Nodal DG Methods, Seciton 5.3 # DOI: 10.1007/978-0-387-72067-8 alpha = -1.0 * np.log(np.finfo(float).eps) from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(0.0, ) * dim, b=(1.0, ) * dim, nelements_per_axis=(nel_1d, ) * dim) discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) # number of modes see e.g.: # JSH/TW Nodal DG Methods, Section 10.1 # DOI: 10.1007/978-0-387-72067-8 nummodes = int(1) for _ in range(dim): nummodes *= int(order + dim + 1) nummodes /= math.factorial(int(dim)) cutoff = int(eta * order) from mirgecom.filter import exponential_mode_response_function as xmrfunc frfunc = partial(xmrfunc, alpha=alpha, filter_order=filter_order) # First test a uniform field, which should pass through # the filter unharmed. from mirgecom.initializers import Uniform initr = Uniform(dim=dim) uniform_soln = initr(t=0, x_vec=nodes).join() from mirgecom.filter import filter_modally filtered_soln = filter_modally(discr, "vol", cutoff, frfunc, uniform_soln) soln_resid = uniform_soln - filtered_soln max_errors = [discr.norm(v, np.inf) for v in soln_resid] tol = 1e-14 logger.info(f"Max Errors (uniform field) = {max_errors}") assert (np.max(max_errors) < tol) # construct polynomial field: # a0 + a1*x + a2*x*x + .... def polyfn(coeff): # , x_vec): # r = actx.np.sqrt(np.dot(nodes, nodes)) r = nodes[0] result = 0 for n, a in enumerate(coeff): result += a * r**n return make_obj_array([result]) # Any order {cutoff} and below fields should be unharmed tol = 1e-14 field_order = int(cutoff) coeff = [1.0 / (i + 1) for i in range(field_order + 1)] field = polyfn(coeff=coeff) filtered_field = filter_modally(discr, "vol", cutoff, frfunc, field) soln_resid = field - filtered_field max_errors = [discr.norm(v, np.inf) for v in soln_resid] logger.info(f"Field = {field}") logger.info(f"Filtered = {filtered_field}") logger.info(f"Max Errors (poly) = {max_errors}") assert (np.max(max_errors) < tol) # Any order > cutoff fields should have higher modes attenuated threshold = 1e-3 tol = 1e-1 if do_viz is True: from grudge.shortcuts import make_visualizer vis = make_visualizer(discr, discr.order) from grudge.dof_desc import DD_VOLUME_MODAL, DD_VOLUME modal_map = discr.connection_from_dds(DD_VOLUME, DD_VOLUME_MODAL) for field_order in range(cutoff + 1, cutoff + 4): coeff = [1.0 / (i + 1) for i in range(field_order + 1)] field = polyfn(coeff=coeff) filtered_field = filter_modally(discr, "vol", cutoff, frfunc, field) unfiltered_spectrum = modal_map(field) filtered_spectrum = modal_map(filtered_field) if do_viz is True: spectrum_resid = unfiltered_spectrum - filtered_spectrum io_fields = [("unfiltered", field), ("filtered", filtered_field), ("unfiltered_spectrum", unfiltered_spectrum), ("filtered_spectrum", filtered_spectrum), ("residual", spectrum_resid)] vis.write_vtk_file(f"filter_test_{field_order}.vtu", io_fields) field_resid = unfiltered_spectrum - filtered_spectrum max_errors = [discr.norm(v, np.inf) for v in field_resid] # fields should be different, but not too different assert (tol > np.max(max_errors) > threshold)