Ejemplo n.º 1
0
def test_function_symbol_array(ctx_factory, array_type):
    ctx = ctx_factory()
    queue = cl.CommandQueue(ctx)
    actx = PyOpenCLArrayContext(queue)

    from meshmode.mesh.generation import generate_regular_rect_mesh
    dim = 2
    mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim,
                                      b=(0.5, ) * dim,
                                      n=(8, ) * dim,
                                      order=4)
    discr = DGDiscretizationWithBoundaries(actx, mesh, order=4)
    volume_discr = discr.discr_from_dd(sym.DD_VOLUME)
    ndofs = sum(grp.ndofs for grp in volume_discr.groups)

    import pyopencl.clrandom  # noqa: F401
    if array_type == "scalar":
        sym_x = sym.var("x")
        x = unflatten(actx, volume_discr,
                      cl.clrandom.rand(queue, ndofs, dtype=np.float))
    elif array_type == "vector":
        sym_x = sym.make_sym_array("x", dim)
        x = make_obj_array([
            unflatten(actx, volume_discr,
                      cl.clrandom.rand(queue, ndofs, dtype=np.float))
            for _ in range(dim)
        ])
    else:
        raise ValueError("unknown array type")

    norm = bind(discr, sym.norm(2, sym_x))(x=x)
    assert isinstance(norm, float)
Ejemplo n.º 2
0
def test_flatten_unflatten(actx_factory):
    actx = actx_factory()

    ambient_dim = 2
    from meshmode.mesh.generation import generate_regular_rect_mesh
    mesh = generate_regular_rect_mesh(a=(-0.5, ) * ambient_dim,
                                      b=(+0.5, ) * ambient_dim,
                                      n=(3, ) * ambient_dim,
                                      order=1)
    discr = Discretization(actx, mesh, PolynomialWarpAndBlendGroupFactory(3))
    a = np.random.randn(discr.ndofs)

    from meshmode.dof_array import flatten, unflatten
    a_round_trip = actx.to_numpy(
        flatten(unflatten(actx, discr, actx.from_numpy(a))))
    assert np.array_equal(a, a_round_trip)

    from meshmode.dof_array import flatten_to_numpy, unflatten_from_numpy
    a_round_trip = flatten_to_numpy(actx, unflatten_from_numpy(actx, discr, a))
    assert np.array_equal(a, a_round_trip)

    x = thaw(discr.nodes(), actx)
    avg_mass = DOFArray(
        actx,
        tuple([(np.pi + actx.zeros((grp.nelements, 1), a.dtype))
               for grp in discr.groups]))

    c = MyContainer(name="flatten",
                    mass=avg_mass,
                    momentum=make_obj_array([x, x, x]),
                    enthalpy=x)

    from meshmode.dof_array import unflatten_like
    c_round_trip = unflatten_like(actx, flatten(c), c)
    assert flat_norm(c - c_round_trip) < 1.0e-8
Ejemplo n.º 3
0
def unflatten_from_numpy(actx, discr, ary):
    from pytools.obj_array import obj_array_vectorize
    from meshmode.dof_array import unflatten

    ary = obj_array_vectorize(actx.from_numpy, ary)
    if discr is None:
        return ary
    else:
        return unflatten(actx, discr, ary)
Ejemplo n.º 4
0
    def finish(self):
        self.recv_req.Wait()

        actx = self.array_context
        remote_dof_array = unflatten(self.array_context, self.bdry_discr,
                actx.from_numpy(self.remote_data_host))

        bdry_conn = self.discrwb.get_distributed_boundary_swap_connection(
                sym.as_dofdesc(sym.DTAG_BOUNDARY(self.remote_btag)))
        swapped_remote_dof_array = bdry_conn(remote_dof_array)

        self.send_req.Wait()

        return TracePair(self.remote_btag, self.local_dof_array,
                swapped_remote_dof_array)
Ejemplo n.º 5
0
def plot_partition_indices(actx, discr, indices, **kwargs):
    try:
        import matplotlib.pyplot as pt
    except ImportError:
        return

    indices = indices.get(actx.queue)
    args = [
        kwargs.get("tree_kind", "linear").replace("-", "_"),
        kwargs.get("discr_stage", "stage1"), discr.ambient_dim
    ]

    pt.figure(figsize=(10, 8), dpi=300)
    pt.plot(np.diff(indices.ranges))
    pt.savefig("test_partition_{1}_{3}d_ranges_{2}.png".format(*args))
    pt.clf()

    from pytential.utils import flatten_to_numpy
    if discr.ambient_dim == 2:
        sources = flatten_to_numpy(actx, discr.nodes())

        pt.figure(figsize=(10, 8), dpi=300)
        if indices.indices.shape[0] != discr.ndofs:
            pt.plot(sources[0], sources[1], 'ko', alpha=0.5)

        for i in range(indices.nblocks):
            isrc = indices.block_indices(i)
            pt.plot(sources[0][isrc], sources[1][isrc], 'o')

        pt.xlim([-1.5, 1.5])
        pt.ylim([-1.5, 1.5])
        pt.savefig("test_partition_{1}_{3}d_{2}.png".format(*args))
        pt.clf()
    elif discr.ambient_dim == 3:
        from meshmode.discretization.visualization import make_visualizer
        marker = -42.0 * np.ones(discr.ndofs)

        for i in range(indices.nblocks):
            isrc = indices.block_indices(i)
            marker[isrc] = 10.0 * (i + 1.0)

        from meshmode.dof_array import unflatten
        marker = unflatten(actx, discr, actx.from_numpy(marker))

        vis = make_visualizer(actx, discr, 10)

        filename = "test_partition_{0}_{1}_{3}d_{2}.vtu".format(*args)
        vis.write_vtk_file(filename, [("marker", marker)])
Ejemplo n.º 6
0
    def unflatten(self, ary):
        # Convert a flat version of *ary* into a structured version.
        components = []
        for discr, (start, end) in zip(self.discrs, self.starts_and_ends):
            component = ary[start:end]
            from meshmode.discretization import Discretization
            if isinstance(discr, Discretization):
                from meshmode.dof_array import unflatten
                component = unflatten(self.array_context, discr, component)
            components.append(component)

        if self._operator_uses_obj_array:
            from pytools.obj_array import make_obj_array
            return make_obj_array(components)
        else:
            return components[0]
Ejemplo n.º 7
0
    def exec_compute_potential_insn_direct(self, actx: PyOpenCLArrayContext,
            insn, bound_expr, evaluate):
        kernel_args = {}

        from pytential.utils import flatten_if_needed
        from meshmode.dof_array import flatten, thaw, unflatten

        for arg_name, arg_expr in insn.kernel_arguments.items():
            kernel_args[arg_name] = flatten_if_needed(actx, evaluate(arg_expr))

        from pytential import bind, sym
        waa = bind(bound_expr.places, sym.weights_and_area_elements(
            self.ambient_dim, dofdesc=insn.source))(actx)
        strengths = [waa * evaluate(density) for density in insn.densities]
        flat_strengths = [flatten(strength) for strength in strengths]

        results = []
        p2p = None

        for o in insn.outputs:
            target_discr = bound_expr.places.get_discretization(
                    o.target_name.geometry, o.target_name.discr_stage)

            if p2p is None:
                p2p = self.get_p2p(actx, source_kernels=insn.source_kernels,
                    target_kernels=insn.target_kernels)

            evt, output_for_each_kernel = p2p(actx.queue,
                    flatten_if_needed(actx, target_discr.nodes()),
                    flatten(thaw(actx, self.density_discr.nodes())),
                    flat_strengths, **kernel_args)

            from meshmode.discretization import Discretization
            result = output_for_each_kernel[o.target_kernel_index]
            if isinstance(target_discr, Discretization):
                result = unflatten(actx, target_discr, result)

            results.append((o.name, result))

        timing_data = {}
        return results, timing_data
Ejemplo n.º 8
0
def test_array_context_np_workalike(actx_factory):
    actx = actx_factory()

    from meshmode.mesh.generation import generate_regular_rect_mesh
    mesh = generate_regular_rect_mesh(
            a=(-0.5,)*2, b=(0.5,)*2, n=(8,)*2, order=3)

    discr = Discretization(actx, mesh, PolynomialWarpAndBlendGroupFactory(3))

    for sym_name, n_args in [
            ("sin", 1),
            ("exp", 1),
            ("arctan2", 2),
            ("minimum", 2),
            ("maximum", 2),
            ("where", 3),
            ("conj", 1),
            ]:
        args = [np.random.randn(discr.ndofs) for i in range(n_args)]
        ref_result = getattr(np, sym_name)(*args)

        # {{{ test DOFArrays

        actx_args = [unflatten(actx, discr, actx.from_numpy(arg)) for arg in args]

        actx_result = actx.to_numpy(
                flatten(getattr(actx.np, sym_name)(*actx_args)))

        assert np.allclose(actx_result, ref_result)

        # }}}

        # {{{ test object arrays of DOFArrays

        obj_array_args = [make_obj_array([arg]) for arg in actx_args]

        obj_array_result = actx.to_numpy(
                flatten(getattr(actx.np, sym_name)(*obj_array_args)[0]))

        assert np.allclose(obj_array_result, ref_result)
Ejemplo n.º 9
0
    def exec_compute_potential_insn(self, actx, insn, bound_expr, evaluate,
                                    return_timing_data):
        if return_timing_data:
            from warnings import warn
            warn("Timing data collection not supported.",
                 category=UnableToCollectTimingData)

        p2p = None

        kernel_args = {}
        for arg_name, arg_expr in insn.kernel_arguments.items():
            kernel_args[arg_name] = evaluate(arg_expr)

        strengths = evaluate(insn.density)

        # FIXME: Do this all at once
        results = []
        for o in insn.outputs:
            target_discr = bound_expr.places.get_discretization(
                o.target_name.geometry, o.target_name.discr_stage)

            # no on-disk kernel caching
            if p2p is None:
                p2p = self.get_p2p(actx, insn.kernels)

            from pytential.utils import flatten_if_needed
            evt, output_for_each_kernel = p2p(
                actx.queue, flatten_if_needed(actx, target_discr.nodes()),
                self._nodes, [strengths], **kernel_args)

            from meshmode.discretization import Discretization
            result = output_for_each_kernel[o.kernel_index]
            if isinstance(target_discr, Discretization):
                from meshmode.dof_array import unflatten
                result = unflatten(actx, target_discr, result)

            results.append((o.name, result))

        timing_data = {}
        return results, timing_data
def test_sphere_eigenvalues(ctx_factory, mode_m, mode_n, qbx_order,
                            fmm_backend):
    logging.basicConfig(level=logging.INFO)

    special = pytest.importorskip("scipy.special")

    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    target_order = 8

    from meshmode.discretization import Discretization
    from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory
    from pytential.qbx import QBXLayerPotentialSource
    from pytools.convergence import EOCRecorder

    s_eoc_rec = EOCRecorder()
    d_eoc_rec = EOCRecorder()
    sp_eoc_rec = EOCRecorder()
    dp_eoc_rec = EOCRecorder()

    def rel_err(comp, ref):
        return (norm(density_discr, comp - ref) / norm(density_discr, ref))

    for nrefinements in [0, 1]:
        from meshmode.mesh.generation import generate_icosphere
        mesh = generate_icosphere(1, target_order)
        from meshmode.mesh.refinement import Refiner

        refiner = Refiner(mesh)
        for i in range(nrefinements):
            flags = np.ones(mesh.nelements, dtype=bool)
            refiner.refine(flags)
            mesh = refiner.get_current_mesh()

        pre_density_discr = Discretization(
            actx, mesh,
            InterpolatoryQuadratureSimplexGroupFactory(target_order))
        qbx = QBXLayerPotentialSource(
            pre_density_discr,
            4 * target_order,
            qbx_order,
            fmm_order=6,
            fmm_backend=fmm_backend,
        )
        places = GeometryCollection(qbx)

        from meshmode.dof_array import flatten, unflatten, thaw

        density_discr = places.get_discretization(places.auto_source.geometry)
        nodes = thaw(actx, density_discr.nodes())
        r = actx.np.sqrt(nodes[0] * nodes[0] + nodes[1] * nodes[1] +
                         nodes[2] * nodes[2])
        phi = actx.np.arccos(nodes[2] / r)
        theta = actx.np.arctan2(nodes[0], nodes[1])

        ymn = unflatten(
            actx, density_discr,
            actx.from_numpy(
                special.sph_harm(mode_m, mode_n, actx.to_numpy(flatten(theta)),
                                 actx.to_numpy(flatten(phi)))))

        from sumpy.kernel import LaplaceKernel
        lap_knl = LaplaceKernel(3)

        # {{{ single layer

        s_sigma_op = bind(
            places, sym.S(lap_knl, sym.var("sigma"), qbx_forced_limit=+1))
        s_sigma = s_sigma_op(actx, sigma=ymn)
        s_eigval = 1 / (2 * mode_n + 1)

        h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx)
        s_eoc_rec.add_data_point(h_max, rel_err(s_sigma, s_eigval * ymn))

        # }}}

        # {{{ double layer

        d_sigma_op = bind(
            places, sym.D(lap_knl, sym.var("sigma"), qbx_forced_limit="avg"))
        d_sigma = d_sigma_op(actx, sigma=ymn)
        d_eigval = -1 / (2 * (2 * mode_n + 1))
        d_eoc_rec.add_data_point(h_max, rel_err(d_sigma, d_eigval * ymn))

        # }}}

        # {{{ S'

        sp_sigma_op = bind(
            places, sym.Sp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg"))
        sp_sigma = sp_sigma_op(actx, sigma=ymn)
        sp_eigval = -1 / (2 * (2 * mode_n + 1))

        sp_eoc_rec.add_data_point(h_max, rel_err(sp_sigma, sp_eigval * ymn))

        # }}}

        # {{{ D'

        dp_sigma_op = bind(
            places, sym.Dp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg"))
        dp_sigma = dp_sigma_op(actx, sigma=ymn)
        dp_eigval = -(mode_n * (mode_n + 1)) / (2 * mode_n + 1)

        dp_eoc_rec.add_data_point(h_max, rel_err(dp_sigma, dp_eigval * ymn))

        # }}}

    print("Errors for S:")
    print(s_eoc_rec)
    required_order = qbx_order + 1
    assert s_eoc_rec.order_estimate() > required_order - 1.5

    print("Errors for D:")
    print(d_eoc_rec)
    required_order = qbx_order
    assert d_eoc_rec.order_estimate() > required_order - 0.5

    print("Errors for S':")
    print(sp_eoc_rec)
    required_order = qbx_order
    assert sp_eoc_rec.order_estimate() > required_order - 1.5

    print("Errors for D':")
    print(dp_eoc_rec)
    required_order = qbx_order
    assert dp_eoc_rec.order_estimate() > required_order - 1.5
Ejemplo n.º 11
0
def main(mesh_name="ellipsoid"):
    import logging
    logger = logging.getLogger(__name__)
    logging.basicConfig(level=logging.WARNING)  # INFO for more progress info

    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    if mesh_name == "ellipsoid":
        cad_file_name = "geometries/ellipsoid.step"
        h = 0.6
    elif mesh_name == "two-cylinders":
        cad_file_name = "geometries/two-cylinders-smooth.step"
        h = 0.4
    else:
        raise ValueError("unknown mesh name: %s" % mesh_name)

    from meshmode.mesh.io import generate_gmsh, FileSource
    mesh = generate_gmsh(
        FileSource(cad_file_name),
        2,
        order=2,
        other_options=["-string",
                       "Mesh.CharacteristicLengthMax = %g;" % h],
        target_unit="MM")

    from meshmode.mesh.processing import perform_flips
    # Flip elements--gmsh generates inside-out geometry.
    mesh = perform_flips(mesh, np.ones(mesh.nelements))

    from meshmode.mesh.processing import find_bounding_box
    bbox_min, bbox_max = find_bounding_box(mesh)
    bbox_center = 0.5 * (bbox_min + bbox_max)
    bbox_size = max(bbox_max - bbox_min) / 2

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

    from pytential.qbx import QBXLayerPotentialSource
    from meshmode.discretization import Discretization
    from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory

    density_discr = Discretization(
        actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order))

    qbx = QBXLayerPotentialSource(density_discr,
                                  4 * target_order,
                                  qbx_order,
                                  fmm_order=qbx_order + 3,
                                  target_association_tolerance=0.15)

    from pytential.target import PointsTarget
    fplot = FieldPlotter(bbox_center, extent=3.5 * bbox_size, npoints=150)

    from pytential import GeometryCollection
    places = GeometryCollection(
        {
            "qbx": qbx,
            "targets": PointsTarget(fplot.points)
        }, auto_where="qbx")
    density_discr = places.get_discretization("qbx")

    nodes = thaw(actx, density_discr.nodes())
    angle = actx.np.arctan2(nodes[1], nodes[0])

    if k:
        kernel = HelmholtzKernel(3)
    else:
        kernel = LaplaceKernel(3)

    #op = sym.d_dx(sym.S(kernel, sym.var("sigma"), qbx_forced_limit=None))
    op = sym.D(kernel, sym.var("sigma"), qbx_forced_limit=None)
    #op = sym.S(kernel, sym.var("sigma"), qbx_forced_limit=None)

    sigma = actx.np.cos(mode_nr * angle)
    if 0:
        from meshmode.dof_array import flatten, unflatten
        sigma = flatten(0 * angle)
        from random import randrange
        for i in range(5):
            sigma[randrange(len(sigma))] = 1
        sigma = unflatten(actx, density_discr, sigma)

    if isinstance(kernel, HelmholtzKernel):
        for i, elem in np.ndenumerate(sigma):
            sigma[i] = elem.astype(np.complex128)

    fld_in_vol = actx.to_numpy(
        bind(places, op, auto_where=("qbx", "targets"))(actx, sigma=sigma,
                                                        k=k))

    #fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5)
    fplot.write_vtk_file("layerpot-3d-potential.vts",
                         [("potential", fld_in_vol)])

    bdry_normals = bind(places, sym.normal(
        density_discr.ambient_dim))(actx).as_vector(dtype=object)

    from meshmode.discretization.visualization import make_visualizer
    bdry_vis = make_visualizer(actx, density_discr, target_order)
    bdry_vis.write_vtk_file("layerpot-3d-density.vtu", [
        ("sigma", sigma),
        ("bdry_normals", bdry_normals),
    ])
Ejemplo n.º 12
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()
Ejemplo n.º 13
0
def interpolate_from_meshmode(queue,
                              dof_vec,
                              elements_to_sources_lookup,
                              order="tree"):
    """Interpolate a DoF vector from :mod:`meshmode`.

    :arg dof_vec: a DoF vector representing a field in :mod:`meshmode`
        of shape ``(..., nnodes)``.
    :arg elements_to_sources_lookup: a :class:`ElementsToSourcesLookup`.
    :arg order: order of the output potential, either "tree" or "user".

    .. note:: This function currently supports meshes with just one element
        group. Also, the element group must be simplex-based.

    .. note:: This function does some heavy-lifting computation in Python,
        which we intend to optimize in the future. In particular, we plan
        to shift the batched linear solves and basis evaluations to
        :mod:`loopy`.

    TODO: make linear solvers available as :mod:`loopy` callables.
    TODO: make :mod:`modepy` emit :mod:`loopy` callables for basis evaluation.
    """
    if not isinstance(dof_vec, cl.array.Array):
        raise TypeError("non-array passed to interpolator")

    assert len(elements_to_sources_lookup.discr.groups) == 1
    assert len(elements_to_sources_lookup.discr.mesh.groups) == 1
    degroup = elements_to_sources_lookup.discr.groups[0]
    megroup = elements_to_sources_lookup.discr.mesh.groups[0]

    if not degroup.is_affine:
        raise ValueError(
            "interpolation requires global-to-local map, "
            "which is only available for affinely mapped elements")

    mesh = elements_to_sources_lookup.discr.mesh
    dim = elements_to_sources_lookup.discr.dim
    template_simplex = mesh.groups[0].vertex_unit_coordinates().T

    # -------------------------------------------------------
    # Inversely map source points with a global-to-local map.
    #
    # 1. For each element, solve for the affine map.
    #
    # 2. Apply the map to corresponding source points.
    #
    # This step computes `unit_sources`, the list of inversely
    # mapped source points.

    sources_in_element_starts = \
        elements_to_sources_lookup.sources_in_element_starts.get(queue)
    sources_in_element_lists = \
        elements_to_sources_lookup.sources_in_element_lists.get(queue)
    tree = elements_to_sources_lookup.tree.get(queue)

    unit_sources_host = make_obj_array(
        [np.zeros_like(srccrd) for srccrd in tree.sources])

    for iel in range(degroup.nelements):
        vertex_ids = megroup.vertex_indices[iel]
        vertices = mesh.vertices[:, vertex_ids]
        afa, afb = compute_affine_transform(vertices, template_simplex)

        beg = sources_in_element_starts[iel]
        end = sources_in_element_starts[iel + 1]
        source_ids_in_el = sources_in_element_lists[beg:end]
        sources_in_el = np.vstack(
            [tree.sources[iaxis][source_ids_in_el] for iaxis in range(dim)])

        ivmapped_el_sources = afa @ sources_in_el + afb.reshape([dim, 1])
        for iaxis in range(dim):
            unit_sources_host[iaxis][source_ids_in_el] = \
                ivmapped_el_sources[iaxis, :]

    unit_sources = make_obj_array(
        [cl.array.to_device(queue, usc) for usc in unit_sources_host])

    # -----------------------------------------------------
    # Carry out evaluations in the local (template) frames.
    #
    # 1. Assemble a resampling matrix for each element, with
    #    the basis functions and the local source points.
    #
    # 2. For each element, perform matvec on the resampling
    #    matrix and the local DoF coefficients.
    #
    # This step assumes `unit_sources` computed on device, so
    # that the previous step can be swapped with a kernel without
    # interrupting the followed computation.

    mapped_sources = np.vstack([usc.get(queue) for usc in unit_sources])

    basis_funcs = degroup.basis()

    arr_ctx = PyOpenCLArrayContext(queue)
    dof_vec_view = unflatten(arr_ctx, elements_to_sources_lookup.discr,
                             dof_vec)[0]
    dof_vec_view = dof_vec_view.get()

    sym_shape = dof_vec.shape[:-1]
    source_vec = np.zeros(sym_shape + (tree.nsources, ))

    for iel in range(degroup.nelements):
        beg = sources_in_element_starts[iel]
        end = sources_in_element_starts[iel + 1]
        source_ids_in_el = sources_in_element_lists[beg:end]
        mapped_sources_in_el = mapped_sources[:, source_ids_in_el]
        local_dof_vec = dof_vec_view[..., iel, :]

        # resampling matrix built from Vandermonde matrices
        import modepy as mp
        rsplm = mp.resampling_matrix(basis=basis_funcs,
                                     new_nodes=mapped_sources_in_el,
                                     old_nodes=degroup.unit_nodes)

        if len(sym_shape) == 0:
            local_coeffs = local_dof_vec
            source_vec[source_ids_in_el] = rsplm @ local_coeffs
        else:
            from pytools import indices_in_shape
            for sym_id in indices_in_shape(sym_shape):
                source_vec[sym_id + (source_ids_in_el, )] = \
                    rsplm @ local_dof_vec[sym_id]

    source_vec = cl.array.to_device(queue, source_vec)

    if order == "tree":
        pass  # no need to do anything
    elif order == "user":
        source_vec = source_vec[tree.sorted_target_ids]  # into user order
    else:
        raise ValueError(f"order must be 'tree' or 'user' (got {order}).")

    return source_vec
Ejemplo n.º 14
0
def main(ctx_factory=cl.create_some_context,
         snapshot_pattern="y0euler-{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,
                               use_profiling,
                               filename="y0euler.sqlite",
                               mode="wu",
                               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 = 50
    nrestart = 10000000
    current_dt = 1.0e-7
    #t_final = 5.e-7
    t_final = 3e-4

    dim = 2
    order = 1
    exittol = 10000000  # do never exit when comparing to exact solution
    #t_final = 0.001
    current_cfl = 1.0
    vel_init = np.zeros(shape=(dim, ))
    vel_inflow = np.zeros(shape=(dim, ))
    vel_outflow = np.zeros(shape=(dim, ))
    orig = np.zeros(shape=(dim, ))
    #vel[0] = 340.0
    #vel_inflow[0] = 100.0  # m/s
    current_t = 0
    casename = "y0euler"
    constant_cfl = False
    # no internal euler status messages
    nstatus = 1000000000
    checkpoint_t = current_t
    current_step = 0

    # working gas: CO2 #
    #   gamma = 1.289
    #   MW=44.009  g/mol
    #   cp = 37.135 J/mol-K,
    #   rho= 1.977 kg/m^3 @298K
    gamma_CO2 = 1.289
    R_CO2 = 8314.59 / 44.009

    # background
    #   100 Pa
    #   298 K
    #   rho = 1.77619667e-3 kg/m^3
    #   velocity = 0,0,0
    rho_bkrnd = 1.77619667e-3
    pres_bkrnd = 100
    temp_bkrnd = 298
    c_bkrnd = math.sqrt(gamma_CO2 * pres_bkrnd / rho_bkrnd)

    # isentropic shock relations #
    # lab frame, moving shock
    # state 1 is behind (downstream) the shock, state 2 is in front (upstream) of the shock

    mach = 2.0
    pressure_ratio = (2. * gamma_CO2 * mach * mach -
                      (gamma_CO2 - 1.)) / (gamma_CO2 + 1.)
    density_ratio = (gamma_CO2 + 1.) * mach * mach / (
        (gamma_CO2 - 1.) * mach * mach + 2.)
    mach2 = math.sqrt(((gamma_CO2 - 1.) * mach * mach + 2.) /
                      (2. * gamma_CO2 * mach * mach - (gamma_CO2 - 1.)))

    rho1 = rho_bkrnd
    pressure1 = pres_bkrnd
    rho2 = rho1 * density_ratio
    pressure2 = pressure1 * pressure_ratio
    velocity1 = 0.
    velocity2 = -mach * c_bkrnd * (1 / density_ratio - 1)
    c_shkd = math.sqrt(gamma_CO2 * pressure2 / rho2)

    vel_inflow[0] = velocity2

    timestepper = rk4_step
    eos = IdealSingleGas(gamma=gamma_CO2, gas_const=R_CO2)
    bulk_init = Discontinuity(dim=dim,
                              x0=.05,
                              sigma=0.01,
                              rhol=rho2,
                              rhor=rho1,
                              pl=pressure2,
                              pr=pressure1,
                              ul=vel_inflow[0],
                              ur=0.)
    inflow_init = Lump(dim=dim,
                       rho0=rho2,
                       p0=pressure2,
                       center=orig,
                       velocity=vel_inflow,
                       rhoamp=0.0)
    outflow_init = Lump(dim=dim,
                        rho0=rho1,
                        p0=pressure1,
                        center=orig,
                        velocity=vel_outflow,
                        rhoamp=0.0)

    inflow = PrescribedBoundary(inflow_init)
    outflow = PrescribedBoundary(outflow_init)
    wall = AdiabaticSlipBoundary()
    dummy = DummyBoundary()

    # shock capturing parameters
    # sonic conditions
    density_ratio = (gamma_CO2 + 1.) * 1.0 / ((gamma_CO2 - 1.) + 2.)

    density_star = rho1 * density_ratio
    shock_thickness = 20 * 0.001  # on the order of 3 elements, should match what is in mesh generator
    # alpha is ~h/p (spacing/order)
    #alpha_sc = shock_thickness*abs(velocity1-velocity2)*density_star
    alpha_sc = 0.1
    # sigma is ~p^-4
    sigma_sc = -11.0
    # kappa is empirical ...
    kappa_sc = 0.5
    print(
        f"Shock capturing parameters: alpha {alpha_sc}, s0 {sigma_sc}, kappa {kappa_sc}"
    )

    # timestep estimate
    wave_speed = max(mach2 * c_bkrnd, c_shkd + velocity2)
    char_len = 0.001
    area = char_len * char_len / 2
    perimeter = 2 * char_len + math.sqrt(2 * char_len * char_len)
    h = 2 * area / perimeter

    dt_est = 1 / (wave_speed * order * order / h)
    print(f"Time step estimate {dt_est}\n")

    dt_est_visc = 1 / (wave_speed * order * order / h +
                       alpha_sc * order * order * order * order / h / h)
    print(f"Viscous timestep estimate {dt_est_visc}\n")

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

    #local_mesh, global_nelements = create_parallel_grid(comm,
    #get_pseudo_y0_mesh)
    #
    #local_nelements = local_mesh.nelements

    if restart_step is None:
        local_mesh, global_nelements = create_parallel_grid(comm, get_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.")
        current_state = bulk_init(0, 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_device_name(logmgr, queue)
        logmgr_add_discretization_quantities(logmgr, discr, eos, dim)
        #logmgr_add_package_versions(logmgr)

        logmgr.add_watches([
            "step.max", "t_sim.max", "t_step.max", "min_pressure",
            "max_pressure", "min_temperature", "max_temperature"
        ])

        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, discr.order + 3
    #if discr.dim == 2 else discr.order)
    visualizer = make_visualizer(discr, discr.order)

    #    initname = initializer.__class__.__name__
    initname = "pseudoY0"
    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 inviscid_operator(discr, eos=eos, boundaries=boundaries, q=state, t=t)
        return (inviscid_operator(
            discr, q=state, t=t, boundaries=boundaries, eos=eos) +
                artificial_viscosity(discr,
                                     t=t,
                                     r=state,
                                     eos=eos,
                                     boundaries=boundaries,
                                     alpha=alpha_sc,
                                     sigma=sigma_sc,
                                     kappa=kappa_sc))

    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)

        #x0=f(time)
        exact_soln = Discontinuity(dim=dim,
                                   x0=.05,
                                   sigma=0.00001,
                                   rhol=rho2,
                                   rhor=rho1,
                                   pl=pressure2,
                                   pr=pressure1,
                                   ul=vel_inflow[0],
                                   ur=0.,
                                   uc=mach * c_bkrnd)

        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,
                              sigma=sigma_sc,
                              kappa=kappa_sc)

    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())
Ejemplo n.º 15
0
 def __call__(self):
     self.receive_request.Wait()
     actx = self.array_context
     remote_data = unflatten(self.array_context, self.bdry_discr,
                             actx.from_numpy(self.remote_data_host))
     return [(self.insn_name, remote_data)], []
Ejemplo n.º 16
0
    def exec_compute_potential_insn_fmm(self, actx: PyOpenCLArrayContext, insn,
                                        bound_expr, evaluate, fmm_driver):
        """
        :arg fmm_driver: A function that accepts four arguments:
            *wrangler*, *strength*, *geo_data*, *kernel*, *kernel_arguments*
        :returns: a tuple ``(assignments, extra_outputs)``, where *assignments*
            is a list of tuples containing pairs ``(name, value)`` representing
            assignments to be performed in the evaluation context.
            *extra_outputs* is data that *fmm_driver* may return
            (such as timing data), passed through unmodified.
        """
        target_name_and_side_to_number, target_discrs_and_qbx_sides = (
            self.get_target_discrs_and_qbx_sides(insn, bound_expr))

        geo_data = self.qbx_fmm_geometry_data(bound_expr.places,
                                              insn.source.geometry,
                                              target_discrs_and_qbx_sides)

        # FIXME Exert more positive control over geo_data attribute lifetimes using
        # geo_data.<method>.clear_cache(geo_data).

        # FIXME Synthesize "bad centers" around corners and edges that have
        # inadequate QBX coverage.

        # FIXME don't compute *all* output kernels on all targets--respect that
        # some target discretizations may only be asking for derivatives (e.g.)

        from pytential import bind, sym
        waa = bind(
            bound_expr.places,
            sym.weights_and_area_elements(self.ambient_dim,
                                          dofdesc=insn.source))(actx)
        densities = [evaluate(density) for density in insn.densities]
        strengths = [waa * density for density in densities]
        flat_strengths = tuple(flatten(strength) for strength in strengths)

        base_kernel = single_valued(knl.get_base_kernel()
                                    for knl in insn.source_kernels)

        output_and_expansion_dtype = (self.get_fmm_output_and_expansion_dtype(
            insn.source_kernels, flat_strengths[0]))
        kernel_extra_kwargs, source_extra_kwargs = (
            self.get_fmm_expansion_wrangler_extra_kwargs(
                actx, insn.target_kernels + insn.source_kernels,
                geo_data.tree().user_source_ids, insn.kernel_arguments,
                evaluate))

        wrangler = self.expansion_wrangler_code_container(
            target_kernels=insn.target_kernels,
            source_kernels=insn.source_kernels).get_wrangler(
                actx.queue,
                geo_data,
                output_and_expansion_dtype,
                self.qbx_order,
                self.fmm_level_to_order,
                source_extra_kwargs=source_extra_kwargs,
                kernel_extra_kwargs=kernel_extra_kwargs,
                _use_target_specific_qbx=self._use_target_specific_qbx)

        from pytential.qbx.geometry import target_state
        if (actx.thaw(geo_data.user_target_to_center()) == target_state.FAILED
            ).any().get():
            raise RuntimeError("geometry has failed targets")

        # {{{ geometry data inspection hook

        if self.geometry_data_inspector is not None:
            perform_fmm = self.geometry_data_inspector(insn, bound_expr,
                                                       geo_data)
            if not perform_fmm:
                return [(o.name, 0) for o in insn.outputs]

        # }}}

        # Execute global QBX.
        all_potentials_on_every_target, extra_outputs = (fmm_driver(
            wrangler, flat_strengths, geo_data, base_kernel,
            kernel_extra_kwargs))

        results = []

        for o in insn.outputs:
            target_side_number = target_name_and_side_to_number[
                o.target_name, o.qbx_forced_limit]
            target_discr, _ = target_discrs_and_qbx_sides[target_side_number]
            target_slice = slice(*geo_data.target_info(
            ).target_discr_starts[target_side_number:target_side_number + 2])

            result = \
                all_potentials_on_every_target[o.target_kernel_index][target_slice]

            from meshmode.discretization import Discretization
            if isinstance(target_discr, Discretization):
                from meshmode.dof_array import unflatten
                result = unflatten(actx, target_discr, result)

            results.append((o.name, result))

        return results, extra_outputs
Ejemplo n.º 17
0
def test_interpolation(ctx_factory, name, source_discr_stage,
                       target_granularity):
    ctx = ctx_factory()
    queue = cl.CommandQueue(ctx)
    actx = PyOpenCLArrayContext(queue)

    nelements = 32
    target_order = 7
    qbx_order = 4

    where = sym.as_dofdesc("test_interpolation")
    from_dd = sym.DOFDescriptor(geometry=where.geometry,
                                discr_stage=source_discr_stage,
                                granularity=sym.GRANULARITY_NODE)
    to_dd = sym.DOFDescriptor(geometry=where.geometry,
                              discr_stage=sym.QBX_SOURCE_QUAD_STAGE2,
                              granularity=target_granularity)

    mesh = make_curve_mesh(starfish, np.linspace(0.0, 1.0, nelements + 1),
                           target_order)
    discr = Discretization(
        actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order))

    from pytential.qbx import QBXLayerPotentialSource
    qbx = QBXLayerPotentialSource(discr,
                                  fine_order=4 * target_order,
                                  qbx_order=qbx_order,
                                  fmm_order=False)

    from pytential import GeometryCollection
    places = GeometryCollection(qbx, auto_where=where)

    sigma_sym = sym.var("sigma")
    op_sym = sym.sin(sym.interp(from_dd, to_dd, sigma_sym))
    bound_op = bind(places, op_sym, auto_where=where)

    from meshmode.dof_array import thaw, flatten, unflatten

    def discr_and_nodes(stage):
        density_discr = places.get_discretization(where.geometry, stage)
        return density_discr, np.array([
            actx.to_numpy(flatten(axis))
            for axis in thaw(actx, density_discr.nodes())
        ])

    _, target_nodes = discr_and_nodes(sym.QBX_SOURCE_QUAD_STAGE2)
    source_discr, source_nodes = discr_and_nodes(source_discr_stage)

    sigma_target = np.sin(la.norm(target_nodes, axis=0))
    sigma_dev = unflatten(actx, source_discr,
                          actx.from_numpy(la.norm(source_nodes, axis=0)))
    sigma_target_interp = actx.to_numpy(
        flatten(bound_op(actx, sigma=sigma_dev)))

    if name in ("default", "default_explicit", "stage2", "quad"):
        error = la.norm(sigma_target_interp -
                        sigma_target) / la.norm(sigma_target)
        assert error < 1.0e-10
    elif name in ("stage2_center", ):
        assert len(sigma_target_interp) == 2 * len(sigma_target)
    else:
        raise ValueError(f"unknown test case name: {name}")
Ejemplo n.º 18
0
    def exec_compute_potential_insn_fmm(self, actx: PyOpenCLArrayContext,
            insn, bound_expr, evaluate):
        # {{{ gather unique target discretizations used

        target_name_to_index = {}
        targets = []

        for o in insn.outputs:
            assert o.qbx_forced_limit not in (-1, 1)

            if o.target_name in target_name_to_index:
                continue

            target_name_to_index[o.target_name] = len(targets)
            targets.append(bound_expr.places.get_geometry(o.target_name.geometry))

        targets = tuple(targets)

        # }}}

        # {{{ get wrangler

        geo_data = self.fmm_geometry_data(targets)

        from pytential import bind, sym
        waa = bind(bound_expr.places, sym.weights_and_area_elements(
            self.ambient_dim, dofdesc=insn.source))(actx)
        strengths = waa * evaluate(insn.density)

        from meshmode.dof_array import flatten
        flat_strengths = flatten(strengths)

        out_kernels = tuple(knl for knl in insn.kernels)
        fmm_kernel = self.get_fmm_kernel(out_kernels)
        output_and_expansion_dtype = (
                self.get_fmm_output_and_expansion_dtype(fmm_kernel, strengths))
        kernel_extra_kwargs, source_extra_kwargs = (
                self.get_fmm_expansion_wrangler_extra_kwargs(
                    actx, out_kernels, geo_data.tree().user_source_ids,
                    insn.kernel_arguments, evaluate))

        wrangler = self.expansion_wrangler_code_container(
                fmm_kernel, out_kernels).get_wrangler(
                    actx.queue,
                    geo_data.tree(),
                    output_and_expansion_dtype,
                    self.fmm_level_to_order,
                    source_extra_kwargs=source_extra_kwargs,
                    kernel_extra_kwargs=kernel_extra_kwargs)

        # }}}

        from boxtree.fmm import drive_fmm
        all_potentials_on_every_tgt = drive_fmm(
                geo_data.traversal(), wrangler, (flat_strengths,),
                timing_data=None)

        # {{{ postprocess fmm

        results = []

        for o in insn.outputs:
            target_index = target_name_to_index[o.target_name]
            target_slice = slice(*geo_data.target_info().target_discr_starts[
                    target_index:target_index+2])
            target_discr = targets[target_index]

            result = all_potentials_on_every_tgt[o.kernel_index][target_slice]

            from meshmode.discretization import Discretization
            if isinstance(target_discr, Discretization):
                from meshmode.dof_array import unflatten
                result = unflatten(actx, target_discr, result)

            results.append((o.name, result))

        # }}}

        timing_data = {}
        return results, timing_data
Ejemplo n.º 19
0
            if d == 2:
                u = np.log(dist)
                grad_u = diff/dist_squared
            elif d == 3:
                u = 1/dist
                grad_u = -diff/dist**3
            else:
                assert False

        dn_u = 0
        for i in range(d):
            dn_u = dn_u + normal_host[i]*grad_u[i]

        # }}}

        u_dev = unflatten(actx, density_discr, actx.from_numpy(u))
        dn_u_dev = unflatten(actx, density_discr, actx.from_numpy(dn_u))
        from pytools.obj_array import make_obj_array, obj_array_vectorize
        grad_u_dev = unflatten(actx, density_discr,
                obj_array_vectorize(actx.from_numpy, make_obj_array(grad_u)))

        key = (case.qbx_order, case.geometry.mesh_name, resolution,
                case.expr.zero_op_name)

        bound_op = bind(places, case.expr.get_zero_op(k_sym, **knl_kwargs))
        error = bound_op(
                actx, u=u_dev, dn_u=dn_u_dev, grad_u=grad_u_dev, k=case.k)
        if 0:
            pt.plot(error)
            pt.show()
Ejemplo n.º 20
0
def main(ctx_factory=cl.create_some_context,
         snapshot_pattern="y0euler-{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="y0euler.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 = 100
    nrestart = 100
    #current_dt = 2.5e-8 # stable with euler
    current_dt = 4e-7 # stable with lrsrk144
    t_final = 5.e-1

    dim = 3
    order = 1
    exittol = .09
    #t_final = 0.001
    current_cfl = 1.0
    vel_init = np.zeros(shape=(dim,))
    vel_inflow = np.zeros(shape=(dim,))
    vel_outflow = np.zeros(shape=(dim,))
    orig = np.zeros(shape=(dim,))
    orig[0] = 0.83
    orig[2] = 0.001
    #vel[0] = 340.0
    #vel_inflow[0] = 100.0  # m/s
    current_t = 0
    casename = "y0euler"
    constant_cfl = False
    nstatus = 10000000000
    rank = 0
    checkpoint_t = current_t
    current_step = 0

    # working gas: CO2 #
    #   gamma = 1.289
    #   MW=44.009  g/mol
    #   cp = 37.135 J/mol-K,
    #   rho= 1.977 kg/m^3 @298K
    gamma_CO2 = 1.289
    R_CO2 = 8314.59/44.009

    # background
    #   100 Pa
    #   298 K
    #   rho = 1.77619667e-3 kg/m^3
    #   velocity = 0,0,0
    rho_bkrnd=1.77619667e-3
    pres_bkrnd=100
    temp_bkrnd=298
     
    # nozzle inflow #
    # 
    # stagnation tempertuare 298 K
    # stagnation pressure 1.5e Pa
    # 
    # isentropic expansion based on the area ratios between the inlet (r=13e-3m) and the throat (r=6.3e-3)
    #
    #  MJA, this is calculated offline, add some code to do it for us
    # 
    #   Mach number=0.139145
    #   pressure=148142
    #   temperature=297.169
    #   density=2.63872
    #   gamma=1.289

    # calculate the inlet Mach number from the area ratio
    nozzleInletRadius = 13.e-3
    nozzleThroatRadius = 6.3e-3
    nozzleInletArea = math.pi*nozzleInletRadius*nozzleInletRadius
    nozzleThroatArea = math.pi*nozzleThroatRadius*nozzleThroatRadius
    inletAreaRatio = nozzleInletArea/nozzleThroatArea

    def getMachFromAreaRatio(area_ratio, gamma, mach_guess=0.01):
        error=1.e-8
        nextError=1.e8
        g=gamma
        M0=mach_guess
        while nextError > error:
            R = ((2/(g+1)+((g-1)/(g+1)*M0*M0))**(((g+1)/(2*g-2))))/M0-area_ratio
            dRdM = (2*((2/(g+1)+((g-1)/(g+1)*M0*M0))**(((g+1)/(2*g-2))))/
                   (2*g-2)*(g-1)/(2/(g+1)+((g-1)/(g+1)*M0*M0))-
                   ((2/(g+1)+((g-1)/(g+1)*M0*M0))**(((g+1)/(2*g-2))))* M0**(-2))
      
            M1=M0-R/dRdM
            nextError=abs(R)
            M0=M1

        return M1


    def getIsentropicPressure(mach, P0, gamma):
        pressure=(1.+(gamma-1.)*0.5*math.pow(mach,2))
        pressure=P0*math.pow(pressure,(-gamma/(gamma-1.)))
        return pressure

  
    def getIsentropicTemperature(mach, T0, gamma):
      temperature=(1.+(gamma-1.)*0.5*math.pow(mach,2))
      temperature=T0*math.pow(temperature,-1.0)
      return temperature


    inlet_mach = getMachFromAreaRatio(area_ratio = inletAreaRatio, gamma=gamma_CO2, mach_guess = 0.01);
    # ramp the stagnation pressure
    start_ramp_pres = 1000
    ramp_interval = 5.e-3
    t_ramp_start = 1e-5
    pres_inflow = getIsentropicPressure(mach=inlet_mach, P0=start_ramp_pres, gamma=gamma_CO2)
    temp_inflow = getIsentropicTemperature(mach=inlet_mach, T0=298, gamma=gamma_CO2)
    rho_inflow = pres_inflow/temp_inflow/R_CO2

    print(f'inlet Mach number {inlet_mach}')
    print(f'inlet temperature {temp_inflow}')
    print(f'inlet pressure {pres_inflow}')

    end_ramp_pres = 150000
    pres_inflow_final = getIsentropicPressure(mach=inlet_mach, P0=end_ramp_pres, gamma=gamma_CO2)

    print(f'final inlet pressure {pres_inflow_final}')


    #pres_inflow=148142
    #temp_inflow=297.169
    #rho_inflow=2.63872
    #mach_inflow=infloM = 0.139145
    vel_inflow[0] = inlet_mach*math.sqrt(gamma_CO2*pres_inflow/rho_inflow)

    # starting pressure for the inflow ramp

    #timestepper = rk4_step
    #timestepper = lsrk54_step
    timestepper = lsrk144_step
    #timestepper = euler_step
    eos = IdealSingleGas(gamma=gamma_CO2, gas_const=R_CO2)
    bulk_init = Discontinuity(dim=dim, x0=-.30,sigma=0.005,
    #bulk_init = Discontinuity(dim=dim, x0=-.31,sigma=0.04,
                              rhol=rho_inflow, rhor=rho_bkrnd,
                              pl=pres_inflow, pr=pres_bkrnd,
                              ul=vel_inflow, ur=vel_outflow)
    #inflow_init = Lump(dim=dim, rho0=rho_inflow, p0=pres_inflow,
                       #center=orig, velocity=vel_inflow, rhoamp=0.0)
    #outflow_init = Lump(dim=dim, rho0=rho_bkrnd, p0=pres_bkrnd,
                       #center=orig, velocity=vel_outflow, rhoamp=0.0)

    # pressure ramp function
    def inflow_ramp_pressure(t, startP=start_ramp_pres, finalP=end_ramp_pres, 
                             ramp_interval=ramp_interval, t_ramp_start=t_ramp_start):
      if t > t_ramp_start:
          rampPressure = min(finalP, startP+(t-t_ramp_start)/ramp_interval*(finalP-startP))
      else:
          rampPressure = startP
      return rampPressure


    class IsentropicInflow:

        def __init__(self, *, dim=1, direc=0, T0=298, P0=1e5, mach= 0.01, p_fun = None):

            self._P0 = P0
            self._T0 = T0
            self._dim = dim
            self._direc = direc
            self._mach = mach
            if p_fun is not None:
              self._p_fun = p_fun
    
        def __call__(self, x_vec, *, t=0, eos):
    
    
            if self._p_fun is not None:
                P0 = self._p_fun(t)
            else:
                P0 = self._P0
            T0 = self._T0

            gamma = eos.gamma()
            gas_const = eos.gas_const()
            pressure = getIsentropicPressure(mach=self._mach, P0=P0, gamma=gamma)
            temperature = getIsentropicTemperature(mach=self._mach, T0=T0, gamma=gamma)
            rho = pressure/temperature/gas_const

            #print(f'ramp Mach number {self._mach}')
            #print(f'ramp stagnation pressure {P0}')
            #print(f'ramp stagnation temperature {T0}')
            #print(f'ramp pressure {pressure}')
            #print(f'ramp temperature {temperature}')

            velocity = np.zeros(shape=(self._dim,)) 
            velocity[self._direc] = self._mach*math.sqrt(gamma*pressure/rho)
    
            mass = 0.0*x_vec[0] + rho
            mom = velocity*mass
            energy = (pressure/(gamma - 1.0)) + np.dot(mom, mom)/(2.0*mass)
            from mirgecom.euler import join_conserved
            return join_conserved(dim=self._dim, mass=mass, momentum=mom, energy=energy)


    inflow_init = IsentropicInflow(dim=dim, T0=298, P0=start_ramp_pres, 
                                   mach = inlet_mach , p_fun=inflow_ramp_pressure)
    outflow_init = Uniform(dim=dim, rho=rho_bkrnd, p=pres_bkrnd,
                           velocity=vel_outflow)

    inflow = PrescribedBoundary(inflow_init)
    outflow = PrescribedBoundary(outflow_init)
    wall = AdiabaticSlipBoundary()
    dummy = DummyBoundary()

    alpha_sc = 0.5
    # s0 is ~p^-4 
    #s0_sc = -11.0
    s0_sc = -5.0
    # kappa is empirical ...
    kappa_sc = 0.5
    print(f"Shock capturing parameters: alpha {alpha_sc}, s0 {s0_sc}, kappa {kappa_sc}")

    # timestep estimate
    #wave_speed = max(mach2*c_bkrnd,c_shkd+velocity2[0])
    #char_len = 0.001
    #area=char_len*char_len/2
    #perimeter = 2*char_len+math.sqrt(2*char_len*char_len)
    #h = 2*area/perimeter

    #dt_est = 1/(wave_speed*order*order/h)
    #print(f"Time step estimate {dt_est}\n")
#
    #dt_est_visc = 1/(wave_speed*order*order/h+alpha_sc*order*order*order*order/h/h)
    #print(f"Viscous timestep estimate {dt_est_visc}\n")

    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:
        local_mesh, global_nelements = create_parallel_grid(comm, get_pseudo_y0_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(0, 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_device_name(logmgr, queue)
        logmgr_add_many_discretization_quantities(logmgr, discr, dim,
            extract_vars_for_logging, units_for_logging)
        #logmgr_add_package_versions(logmgr)

        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.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, discr.order + 3
                                 if discr.dim == 2 else discr.order)
    #    initname = initializer.__class__.__name__
    initname = "pseudoY0"
    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 inviscid_operator(discr, eos=eos, boundaries=boundaries, q=state, t=t)
        return ( inviscid_operator(discr, q=state, t=t,boundaries=boundaries, eos=eos)
               + artificial_viscosity(discr,t=t, r=state, eos=eos, boundaries=boundaries,
               alpha=alpha_sc, s0=s0_sc, kappa=kappa_sc))

    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)

        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,s0=s0_sc,kappa=kappa_sc)

    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())
Ejemplo n.º 21
0
def main(mesh_name="torus", visualize=False):
    import logging
    logging.basicConfig(level=logging.WARNING)  # INFO for more progress info

    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    if mesh_name == "torus":
        rout = 10
        rin = 1

        from meshmode.mesh.generation import generate_torus
        base_mesh = generate_torus(
                rout, rin, 40, 4,
                mesh_order)

        from meshmode.mesh.processing import affine_map, merge_disjoint_meshes
        # nx = 1
        # ny = 1
        nz = 1
        dz = 0
        meshes = [
                affine_map(
                    base_mesh,
                    A=np.diag([1, 1, 1]),
                    b=np.array([0, 0, iz*dz]))
                for iz in range(nz)]

        mesh = merge_disjoint_meshes(meshes, single_group=True)

        if visualize:
            from meshmode.mesh.visualization import draw_curve
            draw_curve(mesh)
            import matplotlib.pyplot as plt
            plt.show()
    else:
        raise ValueError(f"unknown mesh name: {mesh_name}")

    pre_density_discr = Discretization(
            actx, mesh,
            InterpolatoryQuadratureSimplexGroupFactory(bdry_quad_order))

    from pytential.qbx import (
            QBXLayerPotentialSource, QBXTargetAssociationFailedException)
    qbx = QBXLayerPotentialSource(
            pre_density_discr, fine_order=bdry_ovsmp_quad_order, qbx_order=qbx_order,
            fmm_order=fmm_order,
            )

    from sumpy.visualization import FieldPlotter
    fplot = FieldPlotter(np.zeros(3), extent=20, npoints=50)
    targets = actx.from_numpy(fplot.points)

    from pytential import GeometryCollection
    places = GeometryCollection({
        "qbx": qbx,
        "qbx_target_assoc": qbx.copy(target_association_tolerance=0.2),
        "targets": PointsTarget(targets)
        }, auto_where="qbx")
    density_discr = places.get_discretization("qbx")

    # {{{ describe bvp

    from sumpy.kernel import LaplaceKernel
    kernel = LaplaceKernel(3)

    sigma_sym = sym.var("sigma")
    #sqrt_w = sym.sqrt_jac_q_weight(3)
    sqrt_w = 1
    inv_sqrt_w_sigma = sym.cse(sigma_sym/sqrt_w)

    # -1 for interior Dirichlet
    # +1 for exterior Dirichlet
    loc_sign = +1

    bdry_op_sym = (loc_sign*0.5*sigma_sym
            + sqrt_w*(
                sym.S(kernel, inv_sqrt_w_sigma, qbx_forced_limit=+1)
                + sym.D(kernel, inv_sqrt_w_sigma, qbx_forced_limit="avg")
                ))

    # }}}

    bound_op = bind(places, bdry_op_sym)

    # {{{ fix rhs and solve

    from meshmode.dof_array import thaw, flatten, unflatten
    nodes = thaw(actx, density_discr.nodes())
    source = np.array([rout, 0, 0])

    def u_incoming_func(x):
        from pytools.obj_array import obj_array_vectorize
        x = obj_array_vectorize(actx.to_numpy, flatten(x))
        x = np.array(list(x))
        #        return 1/cl.clmath.sqrt( (x[0] - source[0])**2
        #                                +(x[1] - source[1])**2
        #                                +(x[2] - source[2])**2 )
        return 1.0/la.norm(x - source[:, None], axis=0)

    bc = unflatten(actx,
            density_discr,
            actx.from_numpy(u_incoming_func(nodes)))

    bvp_rhs = bind(places, sqrt_w*sym.var("bc"))(actx, bc=bc)

    from pytential.solve import gmres
    gmres_result = gmres(
            bound_op.scipy_op(actx, "sigma", dtype=np.float64),
            bvp_rhs, tol=1e-14, progress=True,
            stall_iterations=0,
            hard_failure=True)

    sigma = bind(places, sym.var("sigma")/sqrt_w)(
            actx, sigma=gmres_result.solution)

    # }}}

    from meshmode.discretization.visualization import make_visualizer
    bdry_vis = make_visualizer(actx, density_discr, 20)
    bdry_vis.write_vtk_file("laplace.vtu", [
        ("sigma", sigma),
        ])

    # {{{ postprocess/visualize

    repr_kwargs = dict(
            source="qbx_target_assoc",
            target="targets",
            qbx_forced_limit=None)
    representation_sym = (
            sym.S(kernel, inv_sqrt_w_sigma, **repr_kwargs)
            + sym.D(kernel, inv_sqrt_w_sigma, **repr_kwargs))

    try:
        fld_in_vol = actx.to_numpy(
                bind(places, representation_sym)(actx, sigma=sigma))
    except QBXTargetAssociationFailedException as e:
        fplot.write_vtk_file("laplace-dirichlet-3d-failed-targets.vts", [
            ("failed", e.failed_target_flags.get(queue)),
            ])
        raise

    #fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5)
    fplot.write_vtk_file("laplace-dirichlet-3d-potential.vts", [
        ("potential", fld_in_vol),
        ])
Ejemplo n.º 22
0
def _test_data_transfer(mpi_comm, actx, local_bdry_conns,
                        remote_to_local_bdry_conns, connected_parts):
    from mpi4py import MPI

    def f(x):
        return 10 * actx.np.sin(20. * x)

    """
    Here is a simplified example of what happens from
    the point of view of the local rank.

    Local rank:
        1. Transfer local points from local boundary to remote boundary
            to get remote points.
        2. Send remote points to remote rank.
    Remote rank:
        3. Receive remote points from local rank.
        4. Transfer remote points from remote boundary to local boundary
            to get local points.
        5. Send local points to local rank.
    Local rank:
        6. Receive local points from remote rank.
        7. Check if local points are the same as the original local points.
    """

    # 1.
    send_reqs = []
    for i_remote_part in connected_parts:
        conn = remote_to_local_bdry_conns[i_remote_part]
        bdry_discr = local_bdry_conns[i_remote_part].to_discr
        bdry_x = thaw(actx, bdry_discr.nodes()[0])

        true_local_f = f(bdry_x)
        remote_f = conn(true_local_f)

        # 2.
        send_reqs.append(
            mpi_comm.isend(actx.to_numpy(flatten(remote_f)),
                           dest=i_remote_part,
                           tag=TAG_SEND_REMOTE_NODES))

    # 3.
    buffers = {}
    for i_remote_part in connected_parts:
        status = MPI.Status()
        mpi_comm.probe(source=i_remote_part,
                       tag=TAG_SEND_REMOTE_NODES,
                       status=status)
        buffers[i_remote_part] = np.empty(status.count, dtype=bytes)

    recv_reqs = {}
    for i_remote_part, buf in buffers.items():
        recv_reqs[i_remote_part] = mpi_comm.irecv(buf=buf,
                                                  source=i_remote_part,
                                                  tag=TAG_SEND_REMOTE_NODES)
    remote_to_local_f_data = {}
    for i_remote_part, req in recv_reqs.items():
        remote_to_local_f_data[i_remote_part] = req.wait()
        buffers[i_remote_part] = None  # free buffer

    for req in send_reqs:
        req.wait()

    # 4.
    send_reqs = []
    for i_remote_part in connected_parts:
        conn = remote_to_local_bdry_conns[i_remote_part]
        local_f = unflatten(
            actx, conn.from_discr,
            actx.from_numpy(remote_to_local_f_data[i_remote_part]))
        remote_f = actx.to_numpy(flatten(conn(local_f)))

        # 5.
        send_reqs.append(
            mpi_comm.isend(remote_f,
                           dest=i_remote_part,
                           tag=TAG_SEND_LOCAL_NODES))

    # 6.
    buffers = {}
    for i_remote_part in connected_parts:
        status = MPI.Status()
        mpi_comm.probe(source=i_remote_part,
                       tag=TAG_SEND_LOCAL_NODES,
                       status=status)
        buffers[i_remote_part] = np.empty(status.count, dtype=bytes)

    recv_reqs = {}
    for i_remote_part, buf in buffers.items():
        recv_reqs[i_remote_part] = mpi_comm.irecv(buf=buf,
                                                  source=i_remote_part,
                                                  tag=TAG_SEND_LOCAL_NODES)
    local_f_data = {}
    for i_remote_part, req in recv_reqs.items():
        local_f_data[i_remote_part] = req.wait()
        buffers[i_remote_part] = None  # free buffer

    for req in send_reqs:
        req.wait()

    # 7.
    for i_remote_part in connected_parts:
        bdry_discr = local_bdry_conns[i_remote_part].to_discr
        bdry_x = thaw(actx, bdry_discr.nodes()[0])

        true_local_f = actx.to_numpy(flatten(f(bdry_x)))
        local_f = local_f_data[i_remote_part]

        from numpy.linalg import norm
        err = norm(true_local_f - local_f, np.inf)
        assert err < 1e-11, "Error = %f is too large" % err
Ejemplo n.º 23
0
def main(curve_fn=starfish, visualize=True):
    import logging
    logging.basicConfig(level=logging.WARNING)  # INFO for more progress info

    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)

    from meshmode.mesh.generation import make_curve_mesh
    mesh = make_curve_mesh(
            curve_fn,
            np.linspace(0, 1, nelements+1),
            target_order)

    from pytential.qbx import QBXLayerPotentialSource
    from meshmode.array_context import PyOpenCLArrayContext
    from meshmode.discretization import Discretization
    from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory

    actx = PyOpenCLArrayContext(queue)

    pre_density_discr = Discretization(
            actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order))

    qbx = QBXLayerPotentialSource(pre_density_discr, 4*target_order, qbx_order,
            fmm_order=qbx_order+3,
            target_association_tolerance=0.005)

    from pytential.target import PointsTarget
    fplot = FieldPlotter(np.zeros(2), extent=5, npoints=1000)
    targets_dev = cl.array.to_device(queue, fplot.points)

    from pytential import GeometryCollection
    places = GeometryCollection({
        "qbx": qbx,
        "targets": PointsTarget(targets_dev),
        }, auto_where="qbx")

    density_discr = places.get_discretization("qbx")

    from meshmode.dof_array import thaw
    nodes = thaw(actx, density_discr.nodes())
    angle = actx.np.arctan2(nodes[1], nodes[0])

    if k:
        kernel = HelmholtzKernel(2)
        kernel_kwargs = {"k": sym.var("k")}
    else:
        kernel = LaplaceKernel(2)
        kernel_kwargs = {}

    def op(**kwargs):
        kwargs.update(kernel_kwargs)

        #op = sym.d_dx(sym.S(kernel, sym.var("sigma"), **kwargs))
        return sym.D(kernel, sym.var("sigma"), **kwargs)
        #op = sym.S(kernel, sym.var("sigma"), qbx_forced_limit=None, **kwargs)

    sigma = actx.np.cos(mode_nr*angle)
    if 0:
        from meshmode.dof_array import flatten, unflatten
        sigma = flatten(0 * angle)
        from random import randrange
        for i in range(5):
            sigma[randrange(len(sigma))] = 1
        sigma = unflatten(actx, density_discr, sigma)

    if isinstance(kernel, HelmholtzKernel):
        for i, elem in np.ndenumerate(sigma):
            sigma[i] = elem.astype(np.complex128)

    bound_bdry_op = bind(places, op())
    if visualize:
        fld_in_vol = actx.to_numpy(
                bind(places, op(
                    source="qbx",
                    target="targets",
                    qbx_forced_limit=None))(actx, sigma=sigma, k=k))

        if enable_mayavi:
            fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5)
        else:
            fplot.write_vtk_file("layerpot-potential.vts", [
                ("potential", fld_in_vol)
                ])

    if 0:
        apply_op = bound_bdry_op.scipy_op(actx, "sigma", np.float64, k=k)
        from sumpy.tools import build_matrix
        mat = build_matrix(apply_op)

        import matplotlib.pyplot as pt
        pt.imshow(mat)
        pt.colorbar()
        pt.show()

    if enable_mayavi:
        # {{{ plot boundary field

        from pytential.utils import flatten_to_numpy

        fld_on_bdry = flatten_to_numpy(
                actx, bound_bdry_op(actx, sigma=sigma, k=k))
        nodes_host = flatten_to_numpy(actx, density_discr.nodes())

        mlab.points3d(nodes_host[0], nodes_host[1],
                fld_on_bdry.real, scale_factor=0.03)

        mlab.colorbar()
        mlab.show()
Ejemplo n.º 24
0
                pt.clf()
        elif ambient_dim == 3:
            from meshmode.discretization.visualization import make_visualizer
            marker = np.empty(density_discr.ndofs)

            for i in range(srcindices.nblocks):
                isrc = srcindices.block_indices(i)
                inbr = nbrindices.block_indices(i)

                marker.fill(0.0)
                marker[srcindices.indices] = 0.0
                marker[isrc] = -42.0
                marker[inbr] = +42.0

                from meshmode.dof_array import unflatten
                marker_dev = unflatten(actx, density_discr,
                                       actx.from_numpy(marker))

                vis = make_visualizer(actx, density_discr, 10)
                filename = "test_area_query_{}d_{:04}.vtu".format(
                    ambient_dim, i)
                vis.write_vtk_file(filename, [
                    ("marker", marker_dev),
                ])
    # }}}


if __name__ == "__main__":
    import sys
    if len(sys.argv) > 1:
        exec(sys.argv[1])
    else:
Ejemplo n.º 25
0
def test_dof_array_arithmetic_same_as_numpy(actx_factory):
    actx = actx_factory()

    from meshmode.mesh.generation import generate_regular_rect_mesh
    mesh = generate_regular_rect_mesh(
            a=(-0.5,)*2, b=(0.5,)*2, n=(3,)*2, order=1)

    discr = Discretization(actx, mesh, PolynomialWarpAndBlendGroupFactory(3))

    def get_real(ary):
        return ary.real

    def get_imag(ary):
        return ary.real

    import operator
    from pytools import generate_nonnegative_integer_tuples_below as gnitb
    from random import uniform, randrange
    for op_func, n_args, use_integers in [
            (operator.add, 2, False),
            (operator.sub, 2, False),
            (operator.mul, 2, False),
            (operator.truediv, 2, False),
            (operator.pow, 2, False),
            # FIXME pyopencl.Array doesn't do mod.
            #(operator.mod, 2, True),
            #(operator.mod, 2, False),
            #(operator.imod, 2, True),
            #(operator.imod, 2, False),
            # FIXME: Two outputs
            #(divmod, 2, False),

            (operator.iadd, 2, False),
            (operator.isub, 2, False),
            (operator.imul, 2, False),
            (operator.itruediv, 2, False),

            (operator.and_, 2, True),
            (operator.xor, 2, True),
            (operator.or_, 2, True),

            (operator.iand, 2, True),
            (operator.ixor, 2, True),
            (operator.ior, 2, True),

            (operator.ge, 2, False),
            (operator.lt, 2, False),
            (operator.gt, 2, False),
            (operator.eq, 2, True),
            (operator.ne, 2, True),

            (operator.pos, 1, False),
            (operator.neg, 1, False),
            (operator.abs, 1, False),

            (get_real, 1, False),
            (get_imag, 1, False),
            ]:
        for is_array_flags in gnitb(2, n_args):
            if sum(is_array_flags) == 0:
                # all scalars, no need to test
                continue

            if is_array_flags[0] == 0 and op_func in [
                    operator.iadd, operator.isub,
                    operator.imul, operator.itruediv,
                    operator.iand, operator.ixor, operator.ior,
                    ]:
                # can't do in place operations with a scalar lhs
                continue

            args = [
                    (0.5+np.random.rand(discr.ndofs)
                        if not use_integers else
                        np.random.randint(3, 200, discr.ndofs))

                    if is_array_flag else
                    (uniform(0.5, 2)
                        if not use_integers
                        else randrange(3, 200))
                    for is_array_flag in is_array_flags]

            # {{{ get reference numpy result

            # make a copy for the in place operators
            ref_args = [
                    arg.copy() if isinstance(arg, np.ndarray) else arg
                    for arg in args]
            ref_result = op_func(*ref_args)

            # }}}

            # {{{ test DOFArrays

            actx_args = [
                    unflatten(actx, discr, actx.from_numpy(arg))
                    if isinstance(arg, np.ndarray) else arg
                    for arg in args]

            actx_result = actx.to_numpy(flatten(op_func(*actx_args)))

            assert np.allclose(actx_result, ref_result)

            # }}}

            # {{{ test object arrays of DOFArrays

            # It would be very nice if comparisons on object arrays behaved
            # consistently with everything else. Alas, they do not. Instead:
            #
            # 0.5 < obj_array(DOFArray) -> obj_array([True])
            #
            # because hey, 0.5 < DOFArray returned something truthy.

            if op_func not in [
                    operator.eq, operator.ne,
                    operator.le, operator.lt,
                    operator.ge, operator.gt,

                    operator.iadd, operator.isub,
                    operator.imul, operator.itruediv,
                    operator.iand, operator.ixor, operator.ior,

                    # All Python objects are real-valued, right?
                    get_imag,
                    ]:
                obj_array_args = [
                        make_obj_array([arg]) if isinstance(arg, DOFArray) else arg
                        for arg in actx_args]

                obj_array_result = actx.to_numpy(
                        flatten(op_func(*obj_array_args)[0]))

                assert np.allclose(obj_array_result, ref_result)
Ejemplo n.º 26
0
    def exec_compute_potential_insn_direct(self, actx, insn, bound_expr,
                                           evaluate, return_timing_data):
        from pytential import bind, sym
        if return_timing_data:
            from pytential.source import UnableToCollectTimingData
            from warnings import warn
            warn("Timing data collection not supported.",
                 category=UnableToCollectTimingData)

        lpot_applier = self.get_lpot_applier(insn.target_kernels,
                                             insn.source_kernels)
        p2p = None
        lpot_applier_on_tgt_subset = None

        from pytential.utils import flatten_if_needed
        kernel_args = {}
        for arg_name, arg_expr in insn.kernel_arguments.items():
            kernel_args[arg_name] = flatten_if_needed(actx, evaluate(arg_expr))

        waa = bind(
            bound_expr.places,
            sym.weights_and_area_elements(self.ambient_dim,
                                          dofdesc=insn.source))(actx)
        strength_vecs = [waa * evaluate(density) for density in insn.densities]

        from meshmode.discretization import Discretization
        flat_strengths = [flatten(strength) for strength in strength_vecs]

        source_discr = bound_expr.places.get_discretization(
            insn.source.geometry, insn.source.discr_stage)

        # FIXME: Do this all at once
        results = []
        for o in insn.outputs:
            source_dd = insn.source.copy(discr_stage=o.target_name.discr_stage)
            target_discr = bound_expr.places.get_discretization(
                o.target_name.geometry, o.target_name.discr_stage)
            density_discr = bound_expr.places.get_discretization(
                source_dd.geometry, source_dd.discr_stage)

            is_self = density_discr is target_discr
            if is_self:
                # QBXPreprocessor is supposed to have taken care of this
                assert o.qbx_forced_limit is not None
                assert abs(o.qbx_forced_limit) > 0

                expansion_radii = bind(
                    bound_expr.places,
                    sym.expansion_radii(self.ambient_dim,
                                        dofdesc=o.target_name))(actx)
                centers = bind(
                    bound_expr.places,
                    sym.expansion_centers(self.ambient_dim,
                                          o.qbx_forced_limit,
                                          dofdesc=o.target_name))(actx)

                evt, output_for_each_kernel = lpot_applier(
                    actx.queue,
                    flatten(thaw(actx, target_discr.nodes())),
                    flatten(thaw(actx, source_discr.nodes())),
                    flatten(centers),
                    flat_strengths,
                    expansion_radii=flatten(expansion_radii),
                    **kernel_args)

                result = output_for_each_kernel[o.target_kernel_index]
                if isinstance(target_discr, Discretization):
                    result = unflatten(actx, target_discr, result)

                results.append((o.name, result))
            else:
                # no on-disk kernel caching
                if p2p is None:
                    p2p = self.get_p2p(actx, insn.target_kernels,
                                       insn.source_kernels)
                if lpot_applier_on_tgt_subset is None:
                    lpot_applier_on_tgt_subset = self.get_lpot_applier_on_tgt_subset(
                        insn.target_kernels, insn.source_kernels)

                queue = actx.queue

                flat_targets = flatten_if_needed(actx, target_discr.nodes())
                flat_sources = flatten(thaw(actx, source_discr.nodes()))

                evt, output_for_each_kernel = p2p(queue, flat_targets,
                                                  flat_sources, flat_strengths,
                                                  **kernel_args)

                qbx_forced_limit = o.qbx_forced_limit
                if qbx_forced_limit is None:
                    qbx_forced_limit = 0

                target_discrs_and_qbx_sides = ((target_discr,
                                                qbx_forced_limit), )
                geo_data = self.qbx_fmm_geometry_data(
                    bound_expr.places,
                    insn.source.geometry,
                    target_discrs_and_qbx_sides=target_discrs_and_qbx_sides)

                # center-related info is independent of targets

                # First ncenters targets are the centers
                tgt_to_qbx_center = (
                    geo_data.user_target_to_center()[geo_data.ncenters:].copy(
                        queue=queue).with_queue(queue))

                qbx_tgt_numberer = self.get_qbx_target_numberer(
                    tgt_to_qbx_center.dtype)
                qbx_tgt_count = cl.array.empty(queue, (), np.int32)
                qbx_tgt_numbers = cl.array.empty_like(tgt_to_qbx_center)

                qbx_tgt_numberer(tgt_to_qbx_center,
                                 qbx_tgt_numbers,
                                 qbx_tgt_count,
                                 queue=queue)

                qbx_tgt_count = int(qbx_tgt_count.get())

                if (o.qbx_forced_limit is not None
                        and abs(o.qbx_forced_limit) == 1
                        and qbx_tgt_count < target_discr.ndofs):
                    raise RuntimeError("Did not find a matching QBX center "
                                       "for some targets")

                qbx_tgt_numbers = qbx_tgt_numbers[:qbx_tgt_count]
                qbx_center_numbers = tgt_to_qbx_center[qbx_tgt_numbers]
                qbx_center_numbers.finish()

                tgt_subset_kwargs = kernel_args.copy()
                for i, res_i in enumerate(output_for_each_kernel):
                    tgt_subset_kwargs[f"result_{i}"] = res_i

                if qbx_tgt_count:
                    lpot_applier_on_tgt_subset(
                        queue,
                        targets=flat_targets,
                        sources=flat_sources,
                        centers=geo_data.flat_centers(),
                        expansion_radii=geo_data.flat_expansion_radii(),
                        strengths=flat_strengths,
                        qbx_tgt_numbers=qbx_tgt_numbers,
                        qbx_center_numbers=qbx_center_numbers,
                        **tgt_subset_kwargs)

                result = output_for_each_kernel[o.target_kernel_index]
                if isinstance(target_discr, Discretization):
                    result = unflatten(actx, target_discr, result)

                results.append((o.name, result))

        timing_data = {}
        return results, timing_data
Ejemplo n.º 27
0
def run_exterior_stokes_2d(
        ctx_factory,
        nelements,
        mesh_order=4,
        target_order=4,
        qbx_order=4,
        fmm_order=False,  # FIXME: FMM is slower than direct eval
        mu=1,
        circle_rad=1.5,
        visualize=False):

    # This program tests an exterior Stokes flow in 2D using the
    # compound representation given in Hsiao & Kress,
    # ``On an integral equation for the two-dimensional exterior Stokes problem,''
    # Applied Numerical Mathematics 1 (1985).

    logging.basicConfig(level=logging.INFO)

    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    ovsmp_target_order = 4 * target_order

    # {{{ geometries

    from meshmode.mesh.generation import (  # noqa
        make_curve_mesh, starfish, ellipse, drop)
    mesh = make_curve_mesh(lambda t: circle_rad * ellipse(1, t),
                           np.linspace(0, 1, nelements + 1), target_order)
    coarse_density_discr = Discretization(
        actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order))

    from pytential.qbx import QBXLayerPotentialSource
    target_association_tolerance = 0.05
    qbx = QBXLayerPotentialSource(
        coarse_density_discr,
        fine_order=ovsmp_target_order,
        qbx_order=qbx_order,
        fmm_order=fmm_order,
        target_association_tolerance=target_association_tolerance,
        _expansions_in_tree_have_extent=True,
    )

    def circle_mask(test_points, radius):
        return (test_points[0, :]**2 + test_points[1, :]**2 > radius**2)

    def outside_circle(test_points, radius):
        mask = circle_mask(test_points, radius)
        return np.array([row[mask] for row in test_points])

    from pytential.target import PointsTarget
    nsamp = 30
    eval_points_1d = np.linspace(-3., 3., nsamp)
    eval_points = np.zeros((2, len(eval_points_1d)**2))
    eval_points[0, :] = np.tile(eval_points_1d, len(eval_points_1d))
    eval_points[1, :] = np.repeat(eval_points_1d, len(eval_points_1d))
    eval_points = outside_circle(eval_points, radius=circle_rad)
    point_targets = PointsTarget(eval_points)

    fplot = FieldPlotter(np.zeros(2), extent=6, npoints=100)
    plot_targets = PointsTarget(outside_circle(fplot.points,
                                               radius=circle_rad))

    places = GeometryCollection({
        sym.DEFAULT_SOURCE: qbx,
        sym.DEFAULT_TARGET: qbx.density_discr,
        "point_target": point_targets,
        "plot_target": plot_targets,
    })

    density_discr = places.get_discretization(sym.DEFAULT_SOURCE)

    normal = bind(places, sym.normal(2).as_vector())(actx)
    path_length = bind(places, sym.integral(2, 1, 1))(actx)

    # }}}

    # {{{ describe bvp

    from pytential.symbolic.stokes import StressletWrapper, StokesletWrapper
    dim = 2
    cse = sym.cse

    sigma_sym = sym.make_sym_vector("sigma", dim)
    meanless_sigma_sym = cse(sigma_sym - sym.mean(2, 1, sigma_sym))
    int_sigma = sym.Ones() * sym.integral(2, 1, sigma_sym)

    nvec_sym = sym.make_sym_vector("normal", dim)
    mu_sym = sym.var("mu")

    # -1 for interior Dirichlet
    # +1 for exterior Dirichlet
    loc_sign = 1

    stresslet_obj = StressletWrapper(dim=2)
    stokeslet_obj = StokesletWrapper(dim=2)
    bdry_op_sym = (-loc_sign * 0.5 * sigma_sym - stresslet_obj.apply(
        sigma_sym, nvec_sym, mu_sym, qbx_forced_limit="avg") +
                   stokeslet_obj.apply(
                       meanless_sigma_sym, mu_sym, qbx_forced_limit="avg") -
                   (0.5 / np.pi) * int_sigma)

    # }}}

    bound_op = bind(places, bdry_op_sym)

    # {{{ fix rhs and solve

    def fund_soln(x, y, loc, strength):
        #with direction (1,0) for point source
        r = actx.np.sqrt((x - loc[0])**2 + (y - loc[1])**2)
        scaling = strength / (4 * np.pi * mu)
        xcomp = (-actx.np.log(r) + (x - loc[0])**2 / r**2) * scaling
        ycomp = ((x - loc[0]) * (y - loc[1]) / r**2) * scaling
        return [xcomp, ycomp]

    def rotlet_soln(x, y, loc):
        r = actx.np.sqrt((x - loc[0])**2 + (y - loc[1])**2)
        xcomp = -(y - loc[1]) / r**2
        ycomp = (x - loc[0]) / r**2
        return [xcomp, ycomp]

    def fund_and_rot_soln(x, y, loc, strength):
        #with direction (1,0) for point source
        r = actx.np.sqrt((x - loc[0])**2 + (y - loc[1])**2)
        scaling = strength / (4 * np.pi * mu)
        xcomp = ((-actx.np.log(r) + (x - loc[0])**2 / r**2) * scaling -
                 (y - loc[1]) * strength * 0.125 / r**2 + 3.3)
        ycomp = (((x - loc[0]) * (y - loc[1]) / r**2) * scaling +
                 (x - loc[0]) * strength * 0.125 / r**2 + 1.5)
        return make_obj_array([xcomp, ycomp])

    from meshmode.dof_array import unflatten, flatten, thaw
    nodes = flatten(thaw(actx, density_discr.nodes()))
    fund_soln_loc = np.array([0.5, -0.2])
    strength = 100.
    bc = unflatten(
        actx, density_discr,
        fund_and_rot_soln(nodes[0], nodes[1], fund_soln_loc, strength))

    omega_sym = sym.make_sym_vector("omega", dim)
    u_A_sym_bdry = stokeslet_obj.apply(  # noqa: N806
        omega_sym, mu_sym, qbx_forced_limit=1)

    from pytential.utils import unflatten_from_numpy
    omega = unflatten_from_numpy(
        actx, density_discr,
        make_obj_array([(strength / path_length) * np.ones(len(nodes[0])),
                        np.zeros(len(nodes[0]))]))

    bvp_rhs = bind(places,
                   sym.make_sym_vector("bc", dim) + u_A_sym_bdry)(actx,
                                                                  bc=bc,
                                                                  mu=mu,
                                                                  omega=omega)
    gmres_result = gmres(bound_op.scipy_op(actx,
                                           "sigma",
                                           np.float64,
                                           mu=mu,
                                           normal=normal),
                         bvp_rhs,
                         x0=bvp_rhs,
                         tol=1e-9,
                         progress=True,
                         stall_iterations=0,
                         hard_failure=True)

    # }}}

    # {{{ postprocess/visualize

    sigma = gmres_result.solution
    sigma_int_val_sym = sym.make_sym_vector("sigma_int_val", 2)
    int_val = bind(places, sym.integral(2, 1, sigma_sym))(actx, sigma=sigma)
    int_val = -int_val / (2 * np.pi)
    print("int_val = ", int_val)

    u_A_sym_vol = stokeslet_obj.apply(  # noqa: N806
        omega_sym, mu_sym, qbx_forced_limit=2)
    representation_sym = (
        -stresslet_obj.apply(sigma_sym, nvec_sym, mu_sym, qbx_forced_limit=2) +
        stokeslet_obj.apply(meanless_sigma_sym, mu_sym, qbx_forced_limit=2) -
        u_A_sym_vol + sigma_int_val_sym)

    where = (sym.DEFAULT_SOURCE, "point_target")
    vel = bind(places, representation_sym,
               auto_where=where)(actx,
                                 sigma=sigma,
                                 mu=mu,
                                 normal=normal,
                                 sigma_int_val=int_val,
                                 omega=omega)
    print("@@@@@@@@")

    plot_vel = bind(places,
                    representation_sym,
                    auto_where=(sym.DEFAULT_SOURCE,
                                "plot_target"))(actx,
                                                sigma=sigma,
                                                mu=mu,
                                                normal=normal,
                                                sigma_int_val=int_val,
                                                omega=omega)

    def get_obj_array(obj_array):
        return make_obj_array([ary.get() for ary in obj_array])

    exact_soln = fund_and_rot_soln(actx.from_numpy(eval_points[0]),
                                   actx.from_numpy(eval_points[1]),
                                   fund_soln_loc, strength)

    vel = get_obj_array(vel)
    err = vel - get_obj_array(exact_soln)

    # FIXME: Pointwise relative errors don't make sense!
    rel_err = err / (get_obj_array(exact_soln))

    if 0:
        print("@@@@@@@@")
        print("vel[0], err[0], rel_err[0] ***** vel[1], err[1], rel_err[1]: ")
        for i in range(len(vel[0])):
            print(
                "{:15.8e}, {:15.8e}, {:15.8e} ***** {:15.8e}, {:15.8e}, {:15.8e}"
                .format(vel[0][i], err[0][i], rel_err[0][i], vel[1][i],
                        err[1][i], rel_err[1][i]))

        print("@@@@@@@@")

    l2_err = np.sqrt((6. / (nsamp - 1))**2 * np.sum(err[0] * err[0]) +
                     (6. / (nsamp - 1))**2 * np.sum(err[1] * err[1]))
    l2_rel_err = np.sqrt((6. /
                          (nsamp - 1))**2 * np.sum(rel_err[0] * rel_err[0]) +
                         (6. /
                          (nsamp - 1))**2 * np.sum(rel_err[1] * rel_err[1]))

    print("L2 error estimate: ", l2_err)
    print("L2 rel error estimate: ", l2_rel_err)
    print("max error at sampled points: ", max(abs(err[0])), max(abs(err[1])))
    print("max rel error at sampled points: ", max(abs(rel_err[0])),
          max(abs(rel_err[1])))

    if visualize:
        import matplotlib.pyplot as plt

        full_pot = np.zeros_like(fplot.points) * float("nan")
        mask = circle_mask(fplot.points, radius=circle_rad)

        for i, vel in enumerate(plot_vel):
            full_pot[i, mask] = vel.get()

        plt.quiver(fplot.points[0],
                   fplot.points[1],
                   full_pot[0],
                   full_pot[1],
                   linewidth=0.1)
        plt.savefig("exterior-2d-field.pdf")

    # }}}

    h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx)
    return h_max, l2_err