Esempio n. 1
0
def test_is_affine_group_check(mesh_name):
    order = 4
    nelements = 16

    if mesh_name.startswith("box"):
        dim = int(mesh_name[-2])
        is_affine = True
        mesh = mgen.generate_regular_rect_mesh(
            a=(-0.5, ) * dim,
            b=(0.5, ) * dim,
            nelements_per_axis=(nelements, ) * dim,
            order=order)
    elif mesh_name.startswith("warped_box"):
        dim = int(mesh_name[-2])
        is_affine = False
        mesh = mgen.generate_warped_rect_mesh(dim,
                                              order,
                                              nelements_side=nelements)
    elif mesh_name == "cross_warped_box":
        dim = 2
        is_affine = False
        mesh = _generate_cross_warped_rect_mesh(dim, order, nelements)
    elif mesh_name == "circle":
        is_affine = False
        mesh = mgen.make_curve_mesh(lambda t: mgen.ellipse(1.0, t),
                                    np.linspace(0.0, 1.0, nelements + 1),
                                    order=order)
    elif mesh_name == "ellipse":
        is_affine = False
        mesh = mgen.make_curve_mesh(lambda t: mgen.ellipse(2.0, t),
                                    np.linspace(0.0, 1.0, nelements + 1),
                                    order=order)
    elif mesh_name == "sphere":
        is_affine = False
        mesh = mgen.generate_icosphere(r=1.0, order=order)
    elif mesh_name == "torus":
        is_affine = False
        mesh = mgen.generate_torus(10.0, 2.0, order=order)
    else:
        raise ValueError(f"unknown mesh name: {mesh_name}")

    assert all(grp.is_affine for grp in mesh.groups) == is_affine
Esempio n. 2
0
def main(ctx_factory, dim=2, order=4, product_tag=None, visualize=False):
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    # {{{ parameters

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

    # cfl
    dt_factor = 2.0
    # final time
    final_time = np.pi

    # velocity field
    sym_x = sym.nodes(dim)
    c = make_obj_array([-sym_x[1], sym_x[0], 0.0])[:dim]
    # flux
    flux_type = "lf"

    # }}}

    # {{{ discretization

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

    discr_tag_to_group_factory = {}
    if product_tag == "none":
        product_tag = None
    else:
        product_tag = dof_desc.DISCR_TAG_QUAD

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

    discr_tag_to_group_factory[dof_desc.DISCR_TAG_BASE] = \
        PolynomialWarpAndBlendGroupFactory(order)

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

    from grudge import DiscretizationCollection
    discr = DiscretizationCollection(
        actx, mesh, discr_tag_to_group_factory=discr_tag_to_group_factory)

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

    # }}}

    # {{{ symbolic operators

    def f_initial_condition(x):
        return x[0]

    from grudge.models.advection import SurfaceAdvectionOperator
    op = SurfaceAdvectionOperator(c, flux_type=flux_type, quad_tag=product_tag)

    bound_op = bind(discr, op.sym_operator())
    u0 = bind(discr, f_initial_condition(sym_x))(actx, t=0)

    def rhs(t, u):
        return bound_op(actx, t=t, u=u)

    # check velocity is tangential
    sym_normal = sym.surface_normal(dim, dim=dim - 1,
                                    dd=dof_desc.DD_VOLUME).as_vector()
    error = bind(discr, sym.norm(2, c.dot(sym_normal)))(actx)
    logger.info("u_dot_n:   %.5e", error)

    # }}}

    # {{{ time stepping

    # compute time step
    h_min = bind(discr, sym.h_max_from_volume(discr.ambient_dim,
                                              dim=discr.dim))(actx)
    dt = dt_factor * h_min / order**2
    nsteps = int(final_time // dt) + 1
    dt = final_time / nsteps + 1.0e-15

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

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

    norm = bind(discr, sym.norm(2, sym.var("u")))
    norm_u = norm(actx, u=u0)

    step = 0

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

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

        df = dof_desc.DOFDesc(FACE_RESTR_INTERIOR)
        face_discr = discr.connection_from_dds(dof_desc.DD_VOLUME, df).to_discr

        face_normal = bind(
            discr, sym.normal(df, face_discr.ambient_dim,
                              dim=face_discr.dim))(actx)

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

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

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

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

    plot(event, "fld-surface-%04d" % step)
Esempio n. 3
0
 def curve_func(self, x):
     return ellipse(3, x)
Esempio n. 4
0
 def curve_func(self, x):
     return ellipse(3, x)
Esempio n. 5
0
 def curve_fn(self):
     from meshmode.mesh.generation import ellipse
     return lambda t: self.radius * ellipse(self.aspect_ratio, t)
Esempio n. 6
0
def run_exterior_stokes_2d(ctx_factory,
                           nelements,
                           mesh_order=4,
                           target_order=4,
                           qbx_order=4,
                           fmm_order=10,
                           mu=1,
                           circle_rad=1.5,
                           do_plot=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)

    ovsmp_target_order = 4 * target_order

    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(
        cl_ctx, 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,
    ).with_refinement()

    density_discr = qbx.density_discr
    normal = bind(density_discr, sym.normal(2).as_vector())(queue)
    path_length = bind(density_discr, sym.integral(2, 1, 1))(queue)

    # {{{ 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(qbx, bdry_op_sym)

    # {{{ fix rhs and solve

    def fund_soln(x, y, loc, strength):
        #with direction (1,0) for point source
        r = cl.clmath.sqrt((x - loc[0])**2 + (y - loc[1])**2)
        scaling = strength / (4 * np.pi * mu)
        xcomp = (-cl.clmath.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 = cl.clmath.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 = cl.clmath.sqrt((x - loc[0])**2 + (y - loc[1])**2)
        scaling = strength / (4 * np.pi * mu)
        xcomp = ((-cl.clmath.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 [xcomp, ycomp]

    nodes = density_discr.nodes().with_queue(queue)
    fund_soln_loc = np.array([0.5, -0.2])
    strength = 100.
    bc = 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)

    omega = [
        cl.array.to_device(queue,
                           (strength / path_length) * np.ones(len(nodes[0]))),
        cl.array.to_device(queue, np.zeros(len(nodes[0])))
    ]
    bvp_rhs = bind(qbx,
                   sym.make_sym_vector("bc", dim) + u_A_sym_bdry)(queue,
                                                                  bc=bc,
                                                                  mu=mu,
                                                                  omega=omega)
    gmres_result = gmres(bound_op.scipy_op(queue,
                                           "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(qbx, sym.integral(2, 1, sigma_sym))(queue, 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)

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

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

    eval_points = outside_circle(eval_points, radius=circle_rad)
    from pytential.target import PointsTarget
    vel = bind((qbx, PointsTarget(eval_points)),
               representation_sym)(queue,
                                   sigma=sigma,
                                   mu=mu,
                                   normal=normal,
                                   sigma_int_val=int_val,
                                   omega=omega)
    print("@@@@@@@@")

    fplot = FieldPlotter(np.zeros(2), extent=6, npoints=100)
    plot_pts = outside_circle(fplot.points, radius=circle_rad)
    plot_vel = bind((qbx, PointsTarget(plot_pts)),
                    representation_sym)(queue,
                                        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(cl.array.to_device(queue, eval_points[0]),
                                   cl.array.to_device(queue, 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\n" %
                  (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 do_plot:
        import matplotlib
        matplotlib.use("Agg")
        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(qbx, sym.h_max(qbx.ambient_dim))(queue)
    return h_max, l2_err
Esempio n. 7
0
def run_exterior_stokes(
        ctx_factory,
        *,
        ambient_dim,
        target_order,
        qbx_order,
        resolution,
        fmm_order=False,  # FIXME: FMM is slower than direct evaluation
        source_ovsmp=None,
        radius=1.5,
        mu=1.0,
        visualize=False,
        _target_association_tolerance=0.05,
        _expansions_in_tree_have_extent=True):
    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    # {{{ geometry

    if source_ovsmp is None:
        source_ovsmp = 4 if ambient_dim == 2 else 8

    places = {}

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

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

    from pytential.qbx import QBXLayerPotentialSource
    qbx = QBXLayerPotentialSource(
        pre_density_discr,
        fine_order=source_ovsmp * target_order,
        qbx_order=qbx_order,
        fmm_order=fmm_order,
        target_association_tolerance=_target_association_tolerance,
        _expansions_in_tree_have_extent=_expansions_in_tree_have_extent)
    places["source"] = qbx

    from extra_int_eq_data import make_source_and_target_points
    point_source, point_target = make_source_and_target_points(
        side=+1,
        inner_radius=0.5 * radius,
        outer_radius=2.0 * radius,
        ambient_dim=ambient_dim,
    )
    places["point_source"] = point_source
    places["point_target"] = point_target

    if visualize:
        from sumpy.visualization import make_field_plotter_from_bbox
        from meshmode.mesh.processing import find_bounding_box
        fplot = make_field_plotter_from_bbox(find_bounding_box(mesh),
                                             h=0.1,
                                             extend_factor=1.0)
        mask = np.linalg.norm(fplot.points, ord=2, axis=0) > (radius + 0.25)

        from pytential.target import PointsTarget
        plot_target = PointsTarget(fplot.points[:, mask].copy())
        places["plot_target"] = plot_target

        del mask

    places = GeometryCollection(places, auto_where="source")

    density_discr = places.get_discretization("source")
    logger.info("ndofs:     %d", density_discr.ndofs)
    logger.info("nelements: %d", density_discr.mesh.nelements)

    # }}}

    # {{{ symbolic

    sym_normal = sym.make_sym_vector("normal", ambient_dim)
    sym_mu = sym.var("mu")

    if ambient_dim == 2:
        from pytential.symbolic.stokes import HsiaoKressExteriorStokesOperator
        sym_omega = sym.make_sym_vector("omega", ambient_dim)
        op = HsiaoKressExteriorStokesOperator(omega=sym_omega)
    elif ambient_dim == 3:
        from pytential.symbolic.stokes import HebekerExteriorStokesOperator
        op = HebekerExteriorStokesOperator()
    else:
        assert False

    sym_sigma = op.get_density_var("sigma")
    sym_bc = op.get_density_var("bc")

    sym_op = op.operator(sym_sigma, normal=sym_normal, mu=sym_mu)
    sym_rhs = op.prepare_rhs(sym_bc, mu=mu)

    sym_velocity = op.velocity(sym_sigma, normal=sym_normal, mu=sym_mu)

    sym_source_pot = op.stokeslet.apply(sym_sigma,
                                        sym_mu,
                                        qbx_forced_limit=None)

    # }}}

    # {{{ boundary conditions

    normal = bind(places, sym.normal(ambient_dim).as_vector())(actx)

    np.random.seed(42)
    charges = make_obj_array([
        actx.from_numpy(np.random.randn(point_source.ndofs))
        for _ in range(ambient_dim)
    ])

    if ambient_dim == 2:
        total_charge = make_obj_array([actx.np.sum(c) for c in charges])
        omega = bind(places, total_charge * sym.Ones())(actx)

    if ambient_dim == 2:
        bc_context = {"mu": mu, "omega": omega}
        op_context = {"mu": mu, "omega": omega, "normal": normal}
    else:
        bc_context = {}
        op_context = {"mu": mu, "normal": normal}

    bc = bind(places, sym_source_pot,
              auto_where=("point_source", "source"))(actx,
                                                     sigma=charges,
                                                     mu=mu)

    rhs = bind(places, sym_rhs)(actx, bc=bc, **bc_context)
    bound_op = bind(places, sym_op)

    # }}}

    # {{{ solve

    from pytential.solve import gmres
    gmres_tol = 1.0e-9
    result = gmres(bound_op.scipy_op(actx, "sigma", np.float64, **op_context),
                   rhs,
                   x0=rhs,
                   tol=gmres_tol,
                   progress=visualize,
                   stall_iterations=0,
                   hard_failure=True)

    sigma = result.solution

    # }}}

    # {{{ check velocity at "point_target"

    def rnorm2(x, y):
        y_norm = actx.np.linalg.norm(y.dot(y), ord=2)
        if y_norm < 1.0e-14:
            y_norm = 1.0

        d = x - y
        return actx.np.linalg.norm(d.dot(d), ord=2) / y_norm

    ps_velocity = bind(places,
                       sym_velocity,
                       auto_where=("source", "point_target"))(actx,
                                                              sigma=sigma,
                                                              **op_context)
    ex_velocity = bind(places,
                       sym_source_pot,
                       auto_where=("point_source",
                                   "point_target"))(actx, sigma=charges, mu=mu)

    v_error = rnorm2(ps_velocity, ex_velocity)
    h_max = bind(places, sym.h_max(ambient_dim))(actx)

    logger.info("resolution %4d h_max %.5e error %.5e", resolution, h_max,
                v_error)

    # }}}}

    # {{{ visualize

    if not visualize:
        return h_max, v_error

    from meshmode.discretization.visualization import make_visualizer
    vis = make_visualizer(actx, density_discr, target_order)

    filename = "stokes_solution_{}d_{}_ovsmp_{}.vtu".format(
        ambient_dim, resolution, source_ovsmp)

    vis.write_vtk_file(filename, [
        ("density", sigma),
        ("bc", bc),
        ("rhs", rhs),
    ],
                       overwrite=True)

    # }}}

    return h_max, v_error
Esempio n. 8
0
 def _curve_fn(self, t):
     from meshmode.mesh.generation import ellipse
     return self.radius * ellipse(self.aspect_ratio, t)
Esempio n. 9
0
def test_build_matrix_conditioning(actx_factory,
                                   side,
                                   op_type,
                                   visualize=False):
    """Checks that :math:`I + K`, where :math:`K` is compact gives a
    well-conditioned operator when it should. For example, the exterior Laplace
    problem has a nullspace, so we check that and remove it.
    """

    actx = actx_factory()

    # prevent cache explosion
    from sympy.core.cache import clear_cache
    clear_cache()

    case = extra.CurveTestCase(
        name="ellipse",
        curve_fn=lambda t: ellipse(3.0, t),
        target_order=16,
        source_ovsmp=1,
        qbx_order=4,
        resolutions=[64],
        op_type=op_type,
        side=side,
    )
    logger.info("\n%s", case)

    # {{{ geometry

    qbx = case.get_layer_potential(actx, case.resolutions[-1],
                                   case.target_order)

    from pytential.qbx.refinement import refine_geometry_collection
    places = GeometryCollection(qbx, auto_where=case.name)
    places = refine_geometry_collection(
        places, refine_discr_stage=sym.QBX_SOURCE_QUAD_STAGE2)

    dd = places.auto_source.to_stage1()
    density_discr = places.get_discretization(dd.geometry)

    logger.info("nelements:     %d", density_discr.mesh.nelements)
    logger.info("ndofs:         %d", density_discr.ndofs)

    # }}}

    # {{{ check matrix

    from pytential.symbolic.execution import build_matrix
    sym_u, sym_op = case.get_operator(places.ambient_dim,
                                      qbx_forced_limit="avg")

    mat = actx.to_numpy(
        build_matrix(actx,
                     places,
                     sym_op,
                     sym_u,
                     context=case.knl_concrete_kwargs))

    kappa = la.cond(mat)
    _, sigma, _ = la.svd(mat)

    logger.info("cond: %.5e sigma_max %.5e", kappa, sigma[0])

    # NOTE: exterior Laplace has a nullspace
    if side == +1 and op_type == "double":
        assert kappa > 1.0e+9
        assert sigma[-1] < 1.0e-9
    else:
        assert kappa < 1.0e+1
        assert sigma[-1] > 1.0e-2

    # remove the nullspace and check that it worked
    if side == +1 and op_type == "double":
        # NOTE: this adds the "mean" to remove the nullspace for the operator
        # See `pytential.symbolic.pde.scalar` for the equivalent formulation
        w = actx.to_numpy(
            flatten(
                bind(places,
                     sym.sqrt_jac_q_weight(places.ambient_dim)**2)(actx),
                actx))

        w = np.tile(w.reshape(-1, 1), w.size).T
        kappa = la.cond(mat + w)

        assert kappa < 1.0e+2

    # }}}

    # {{{ plot

    if not visualize:
        return

    side = "int" if side == -1 else "ext"

    import matplotlib.pyplot as plt
    plt.imshow(mat)
    plt.colorbar()
    plt.title(fr"$\kappa(A) = {kappa:.5e}$")
    plt.savefig(f"test_cond_{op_type}_{side}_mat")
    plt.clf()

    plt.plot(sigma)
    plt.ylabel(r"$\sigma$")
    plt.grid()
    plt.savefig(f"test_cond_{op_type}_{side}_svd")
    plt.clf()
Esempio n. 10
0
def main(ctx_factory, dim=2, order=4, use_quad=False, visualize=False):
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(
        queue,
        allocator=cl_tools.MemoryPool(cl_tools.ImmediateAllocator(queue)),
        force_device_scalars=True,
    )

    # {{{ parameters

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

    # final time
    final_time = np.pi

    # flux
    flux_type = "lf"

    # }}}

    # {{{ discretization

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

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

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

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

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

    from grudge import DiscretizationCollection

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

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

    # }}}

    # {{{ Surface advection operator

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

    def f_initial_condition(x):
        return x[0]

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

    u0 = f_initial_condition(x)

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

    # check velocity is tangential
    from grudge.geometry import normal

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

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

    # }}}

    # {{{ time stepping

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

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

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

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

    step = 0

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

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

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

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

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

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

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

        # NOTE: These are here to ensure the solution is bounded for the
        # time interval specified
        assert norm_u < 3
Esempio n. 11
0
def run(actx, *,
        ambient_dim: int = 3,
        resolution: int = None,
        target_order: int = 4,
        tmax: float = 1.0,
        timestep: float = 1.0e-2,
        group_factory_name: str = "warp_and_blend",
        visualize: bool = True):
    if ambient_dim not in (2, 3):
        raise ValueError(f"unsupported dimension: {ambient_dim}")

    mesh_order = target_order
    radius = 1.0

    # {{{ geometry

    # {{{ element groups

    import modepy as mp
    import meshmode.discretization.poly_element as poly

    # NOTE: picking the same unit nodes for the mesh and the discr saves
    # a bit of work when reconstructing after a time step

    if group_factory_name == "warp_and_blend":
        group_factory_cls = poly.PolynomialWarpAndBlendGroupFactory

        unit_nodes = mp.warp_and_blend_nodes(ambient_dim - 1, mesh_order)
    elif group_factory_name == "quadrature":
        group_factory_cls = poly.InterpolatoryQuadratureSimplexGroupFactory

        if ambient_dim == 2:
            unit_nodes = mp.LegendreGaussQuadrature(
                    mesh_order, force_dim_axis=True).nodes
        else:
            unit_nodes = mp.VioreanuRokhlinSimplexQuadrature(mesh_order, 2).nodes
    else:
        raise ValueError(f"unknown group factory: '{group_factory_name}'")

    # }}}

    # {{{ discretization

    import meshmode.mesh.generation as gen
    if ambient_dim == 2:
        nelements = 8192 if resolution is None else resolution
        mesh = gen.make_curve_mesh(
                lambda t: radius * gen.ellipse(1.0, t),
                np.linspace(0.0, 1.0, nelements + 1),
                order=mesh_order,
                unit_nodes=unit_nodes)
    else:
        nrounds = 4 if resolution is None else resolution
        mesh = gen.generate_icosphere(radius,
                uniform_refinement_rounds=nrounds,
                order=mesh_order,
                unit_nodes=unit_nodes)

    from meshmode.discretization import Discretization
    discr0 = Discretization(actx, mesh, group_factory_cls(target_order))

    logger.info("ndofs:     %d", discr0.ndofs)
    logger.info("nelements: %d", discr0.mesh.nelements)

    # }}}

    if visualize:
        from meshmode.discretization.visualization import make_visualizer
        vis = make_visualizer(actx, discr0,
                vis_order=target_order,
                # NOTE: setting this to True will add some unnecessary
                # resampling in Discretization.nodes for the vis_discr underneath
                force_equidistant=False)

    # }}}

    # {{{ ode

    def velocity_field(nodes, alpha=1.0):
        return make_obj_array([
            alpha * nodes[0], -alpha * nodes[1], 0.0 * nodes[0]
            ][:ambient_dim])

    def source(t, x):
        discr = reconstruct_discr_from_nodes(actx, discr0, x)
        u = velocity_field(thaw(discr.nodes(), actx))

        # {{{

        # NOTE: these are just here because this was at some point used to
        # profile some more operators (turned out well!)

        from meshmode.discretization import num_reference_derivative
        x = thaw(discr.nodes()[0], actx)
        gradx = sum(
                num_reference_derivative(discr, (i,), x)
                for i in range(discr.dim))
        intx = sum(actx.np.sum(xi * wi) for xi, wi in zip(x, discr.quad_weights()))

        assert gradx is not None
        assert intx is not None

        # }}}

        return u

    # }}}

    # {{{ evolve

    maxiter = int(tmax // timestep) + 1
    dt = tmax / maxiter + 1.0e-15

    x = thaw(discr0.nodes(), actx)
    t = 0.0

    if visualize:
        filename = f"moving-geometry-{0:09d}.vtu"
        plot_solution(actx, vis, filename, discr0, t, x)

    for n in range(1, maxiter + 1):
        x = advance(actx, dt, t, x, source)
        t += dt

        if visualize:
            discr = reconstruct_discr_from_nodes(actx, discr0, x)
            vis = make_visualizer(actx, discr, vis_order=target_order)
            # vis = vis.copy_with_same_connectivity(actx, discr)

            filename = f"moving-geometry-{n:09d}.vtu"
            plot_solution(actx, vis, filename, discr, t, x)

        logger.info("[%05d/%05d] t = %.5e/%.5e dt = %.5e",
                n, maxiter, t, tmax, dt)
Esempio n. 12
0
 def curve_fn(self):
     return lambda t: self.radius * mgen.ellipse(self.aspect_ratio, t)