Beispiel #1
0
def get_extension_bie_symbolic_operator(loc_sign=1):
    """
    loc_sign:
      -1 for interior Dirichlet
      +1 for exterior Dirichlet
    """
    logger = logging.getLogger("SETUP")
    logger.info(locals())

    dim = 2
    cse = sym.cse

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

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

    stresslet_obj = StressletWrapper(dim=dim)
    stokeslet_obj = StokesletWrapper(dim=dim)
    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(sigma_sym, mu_sym, qbx_forced_limit='avg') +
        int_sigma)

    return bdry_op_sym
Beispiel #2
0
    def _operator(self, sigma, normal, mu, qbx_forced_limit):
        slp_qbx_forced_limit = qbx_forced_limit
        if slp_qbx_forced_limit == "avg":
            slp_qbx_forced_limit = +1

        # NOTE: we set a dofdesc here to force the evaluation of this integral
        # on the source instead of the target when using automatic tagging
        # see :meth:`pytential.symbolic.mappers.LocationTagger._default_dofdesc`
        dd = sym.DOFDescriptor(None, discr_stage=sym.QBX_SOURCE_STAGE1)

        int_sigma = sym.integral(self.ambient_dim, self.dim, sigma, dofdesc=dd)
        meanless_sigma = sym.cse(sigma - sym.mean(self.ambient_dim, self.dim, sigma))

        op_k = self.stresslet.apply(sigma, normal, mu,
                qbx_forced_limit=qbx_forced_limit)
        op_s = (
                self.alpha / (2.0 * np.pi) * int_sigma
                - self.stokeslet.apply(meanless_sigma, mu,
                    qbx_forced_limit=slp_qbx_forced_limit)
                )

        return op_k + self.eta * op_s
Beispiel #3
0
 def _farfield(self, mu, qbx_forced_limit):
     length = sym.integral(self.ambient_dim, self.dim, 1)
     return self.stokeslet.apply(
             -self.omega / length,
             mu,
             qbx_forced_limit=qbx_forced_limit)
Beispiel #4
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
Beispiel #5
0
 def Wr(operand: sym.Expression) -> sym.Expression:  # noqa: N802
     return sym.Ones() * sym.integral(self.ambient_dim, self.dim,
                                      operand)
    def build_kernel_exterior_normalizer_table(self,
                                               cl_ctx,
                                               queue,
                                               pool=None,
                                               ncpus=None,
                                               mesh_order=5,
                                               quad_order=10,
                                               mesh_size=0.03,
                                               remove_tmp_files=True,
                                               **kwargs):
        r"""Build the kernel exterior normalizer table for fractional Laplacians.

        An exterior normalizer for kernel :math:`G(r)` and target
        :math:`x` is defined as

        .. math::

            \int_{B^c} G(\lVert x - y \rVert) dy

        where :math:`B` is the source box :math:`[0, source_box_extent]^dim`.
        """
        logger.warn("this method is currently under construction.")

        if not self.inverse_droste:
            raise ValueError()

        if ncpus is None:
            import multiprocessing
            ncpus = multiprocessing.cpu_count()

        if pool is None:
            from multiprocessing import Pool
            pool = Pool(ncpus)

        def fl_scaling(k, s):
            # scaling constant
            from scipy.special import gamma
            return (2**(2 * s) * s * gamma(s + k / 2)) / (np.pi**(k / 2) *
                                                          gamma(1 - s))

        # Directly compute and return in 1D
        if self.dim == 1:
            s = self.integral_knl.s

            targets = np.array(self.q_points).reshape(-1)
            r1 = targets
            r2 = self.source_box_extent - targets
            self.kernel_exterior_normalizers = 1 / (2 * s) * (
                1 / r1**(2 * s) + 1 / r2**(2 * s)) * fl_scaling(k=self.dim,
                                                                s=s)
            return

        from meshmode.array_context import PyOpenCLArrayContext
        from meshmode.dof_array import thaw, flatten
        from meshmode.mesh.io import read_gmsh
        from meshmode.discretization import Discretization
        from meshmode.discretization.poly_element import \
            PolynomialWarpAndBlendGroupFactory

        # {{{ gmsh processing

        import gmsh

        gmsh.initialize()
        gmsh.option.setNumber("General.Terminal", 1)

        # meshmode does not support other versions
        gmsh.option.setNumber("Mesh.MshFileVersion", 2)
        gmsh.option.setNumber("Mesh.CharacteristicLengthMax", mesh_size)

        gmsh.option.setNumber("Mesh.ElementOrder", mesh_order)
        if mesh_order > 1:
            gmsh.option.setNumber("Mesh.CharacteristicLengthFromCurvature", 1)

        # radius of source box
        hs = self.source_box_extent / 2
        # radius of bouding sphere
        r = hs * np.sqrt(self.dim)
        logger.debug("r_inner = %f, r_outer = %f" % (hs, r))

        if self.dim == 2:
            tag_box = gmsh.model.occ.addRectangle(x=0,
                                                  y=0,
                                                  z=0,
                                                  dx=2 * hs,
                                                  dy=2 * hs,
                                                  tag=-1)
        elif self.dim == 3:
            tag_box = gmsh.model.occ.addBox(x=0,
                                            y=0,
                                            z=0,
                                            dx=2 * hs,
                                            dy=2 * hs,
                                            dz=2 * hs,
                                            tag=-1)
        else:
            raise NotImplementedError()

        if self.dim == 2:
            tag_ball = gmsh.model.occ.addDisk(xc=hs,
                                              yc=hs,
                                              zc=0,
                                              rx=r,
                                              ry=r,
                                              tag=-1)
        elif self.dim == 3:
            tag_sphere = gmsh.model.occ.addSphere(xc=hs,
                                                  yc=hs,
                                                  zc=hs,
                                                  radius=r,
                                                  tag=-1)
            tag_ball = gmsh.model.occ.addVolume([tag_sphere], tag=-1)
        else:
            raise NotImplementedError()

        dimtags_ints, dimtags_map_ints = gmsh.model.occ.cut(
            objectDimTags=[(self.dim, tag_ball)],
            toolDimTags=[(self.dim, tag_box)],
            tag=-1,
            removeObject=True,
            removeTool=True)
        gmsh.model.occ.synchronize()
        gmsh.model.mesh.generate(self.dim)

        from tempfile import mkdtemp
        from os.path import join
        temp_dir = mkdtemp(prefix="tmp_volumential_nft")
        msh_filename = join(temp_dir, 'chinese_lucky_coin.msh')
        gmsh.write(msh_filename)
        gmsh.finalize()

        mesh = read_gmsh(msh_filename)
        if remove_tmp_files:
            import shutil
            shutil.rmtree(temp_dir)

        # }}} End gmsh processing

        arr_ctx = PyOpenCLArrayContext(queue)
        discr = Discretization(
            arr_ctx, mesh,
            PolynomialWarpAndBlendGroupFactory(order=quad_order))

        from pytential import bind, sym

        # {{{ optional checks

        if 1:
            if self.dim == 2:
                arerr = np.abs((np.pi * r**2 - (2 * hs)**2) -
                               bind(discr, sym.integral(self.dim, self.dim, 1))
                               (queue)) / (np.pi * r**2 - (2 * hs)**2)
                if arerr > 1e-12:
                    log_to = logger.warn
                else:
                    log_to = logger.debug
                log_to("the numerical error when computing the measure of a "
                       "unit ball is %e" % arerr)

            elif self.dim == 3:
                arerr = np.abs((4 / 3 * np.pi * r**3 - (2 * hs)**3) -
                               bind(discr, sym.integral(self.dim, self.dim, 1))
                               (queue)) / (4 / 3 * np.pi * r**3 - (2 * hs)**3)
                if arerr > 1e-12:
                    log_to = logger.warn
                else:
                    log_to = logger.debug
                logger.warn(
                    "The numerical error when computing the measure of a "
                    "unit ball is %e" % arerr)

        # }}} End optional checks

        # {{{ kernel evaluation

        # TODO: take advantage of symmetry if this is too slow

        from volumential.droste import InverseDrosteReduced

        # only for getting kernel evaluation related stuff
        drf = InverseDrosteReduced(self.integral_knl,
                                   self.quad_order,
                                   self.interaction_case_vecs,
                                   n_brick_quad_points=0,
                                   knl_symmetry_tags=[],
                                   auto_windowing=False)

        # uses "dist[dim]", assigned to "knl_val"
        knl_insns = drf.get_sumpy_kernel_insns()

        eval_kernel_insns = [
            insn.copy(within_inames=insn.within_inames | frozenset(["iqpt"]))
            for insn in knl_insns
        ]

        from sumpy.symbolic import SympyToPymbolicMapper
        sympy_conv = SympyToPymbolicMapper()

        scaling_assignment = lp.Assignment(
            id=None,
            assignee="knl_scaling",
            expression=sympy_conv(
                self.integral_knl.get_global_scaling_const()),
            temp_var_type=lp.Optional(),
        )

        extra_kernel_kwarg_types = ()
        if "extra_kernel_kwarg_types" in kwargs:
            extra_kernel_kwarg_types = kwargs["extra_kernel_kwarg_types"]

        lpknl = lp.make_kernel(  # NOQA
            "{ [iqpt, iaxis]: 0<=iqpt<n_q_points and 0<=iaxis<dim }",
            [
                """
                for iqpt
                    for iaxis
                        <> dist[iaxis] = (quad_points[iaxis, iqpt]
                            - target_point[iaxis])
                    end
                end
                """
            ] + eval_kernel_insns + [scaling_assignment] + [
                """
                for iqpt
                    result[iqpt] = knl_val * knl_scaling
                end
                """
            ],
            [
                lp.ValueArg("dim, n_q_points", np.int32),
                lp.GlobalArg("quad_points", np.float64, "dim, n_q_points"),
                lp.GlobalArg("target_point", np.float64, "dim")
            ] + list(extra_kernel_kwarg_types) + [
                "...",
            ],
            name="eval_kernel_lucky_coin",
            lang_version=(2018, 2),
        )

        lpknl = lp.fix_parameters(lpknl, dim=self.dim)
        lpknl = lp.set_options(lpknl, write_cl=False)
        lpknl = lp.set_options(lpknl, return_dict=True)

        # }}} End kernel evaluation

        node_coords = flatten(thaw(arr_ctx, discr.nodes()))
        nodes = cl.array.to_device(
            queue, np.vstack([crd.get() for crd in node_coords]))

        int_vals = []

        for target in self.q_points:
            evt, res = lpknl(queue, quad_points=nodes, target_point=target)
            knl_vals = res['result']

            integ = bind(
                discr, sym.integral(self.dim, self.dim,
                                    sym.var("integrand")))(queue,
                                                           integrand=knl_vals)
            queue.finish()
            int_vals.append(integ)

        int_vals_coins = np.array(int_vals)

        int_vals_inf = np.zeros(self.n_q_points)

        # {{{ integrate over the exterior of the ball

        if self.dim == 2:

            def rho_0(theta, target, radius):
                rho_x = np.linalg.norm(target, ord=2)
                return (-1 * rho_x * np.cos(theta) +
                        np.sqrt(radius**2 - rho_x**2 * (np.sin(theta)**2)))

            def ext_inf_integrand(theta, s, target, radius):
                _rho_0 = rho_0(theta, target=target, radius=radius)
                return _rho_0**(-2 * s)

            def compute_ext_inf_integral(target, s, radius):
                # target: target point
                # s: fractional order
                # radius: radius of the circle
                import scipy.integrate as sint
                val, _ = sint.quadrature(partial(ext_inf_integrand,
                                                 s=s,
                                                 target=target,
                                                 radius=radius),
                                         a=0,
                                         b=2 * np.pi)
                return val * (1 / (2 * s)) * fl_scaling(k=self.dim, s=s)

            if 1:
                # optional test
                target = [0, 0]
                s = 0.5
                radius = 1
                scaling = fl_scaling(k=self.dim, s=s)
                val = compute_ext_inf_integral(target, s, radius)
                test_err = np.abs(val - radius**(-2 * s) * 2 * np.pi *
                                  (1 /
                                   (2 * s)) * scaling) / (radius**(-2 * s) *
                                                          2 * np.pi *
                                                          (1 /
                                                           (2 * s)) * scaling)
                if test_err > 1e-12:
                    logger.warn("Error evaluating at origin = %f" % test_err)

            for tid, target in enumerate(self.q_points):
                # The formula assumes that the source box is centered at origin
                int_vals_inf[tid] = compute_ext_inf_integral(
                    target=target - hs, s=self.integral_knl.s, radius=r)

        elif self.dim == 3:
            # FIXME
            raise NotImplementedError("3D not yet implemented.")

        else:
            raise NotImplementedError("Unsupported dimension")

        # }}} End integrate over the exterior of the ball

        self.kernel_exterior_normalizers = int_vals_coins + int_vals_inf
        return
Beispiel #7
0
def get_path_length(queue, density_discr):
    return bind(density_discr, sym.integral(2, 1, 1))(queue)
Beispiel #8
0
def compute_biharmonic_extension(queue,
                                 target_discr,
                                 qbx,
                                 density_discr,
                                 f,
                                 fx,
                                 fy,
                                 target_association_tolerance=0.05):
    """Biharmoc extension. Currently only support
    interior domains in 2D (i.e., extension is on the exterior).
    """
    # pylint: disable=invalid-unary-operand-type
    dim = 2
    queue = setup_command_queue(queue=queue)
    qbx_forced_limit = 1

    normal = get_normal_vectors(queue, density_discr, loc_sign=1)

    bdry_op_sym = get_extension_bie_symbolic_operator(loc_sign=1)
    bound_op = bind(qbx, bdry_op_sym)

    bc = [fy, -fx]
    bvp_rhs = bind(qbx, sym.make_sym_vector("bc", dim))(queue, bc=bc)
    gmres_result = gmres(bound_op.scipy_op(queue,
                                           "sigma",
                                           np.float64,
                                           mu=1.,
                                           normal=normal),
                         bvp_rhs,
                         tol=1e-9,
                         progress=True,
                         stall_iterations=0,
                         hard_failure=True)
    mu = gmres_result.solution

    arclength_parametrization_derivatives_sym = sym.make_sym_vector(
        "arclength_parametrization_derivatives", dim)
    density_mu_sym = sym.make_sym_vector("mu", dim)
    dxids_sym = arclength_parametrization_derivatives_sym[0] + \
            1j * arclength_parametrization_derivatives_sym[1]
    dxids_conj_sym = arclength_parametrization_derivatives_sym[0] - \
            1j * arclength_parametrization_derivatives_sym[1]
    density_rho_sym = density_mu_sym[1] - 1j * density_mu_sym[0]
    density_conj_rho_sym = density_mu_sym[1] + 1j * density_mu_sym[0]

    # convolutions
    GS1 = sym.IntG(  # noqa: N806
        ComplexLinearLogKernel(dim),
        density_rho_sym,
        qbx_forced_limit=None)
    GS2 = sym.IntG(  # noqa: N806
        ComplexLinearKernel(dim),
        density_conj_rho_sym,
        qbx_forced_limit=None)
    GD1 = sym.IntG(  # noqa: N806
        ComplexFractionalKernel(dim),
        density_rho_sym * dxids_sym,
        qbx_forced_limit=None)
    GD2 = [
        sym.IntG(  # noqa: N806
            AxisTargetDerivative(iaxis, ComplexLogKernel(dim)),
            density_conj_rho_sym * dxids_sym +
            density_rho_sym * dxids_conj_sym,
            qbx_forced_limit=qbx_forced_limit) for iaxis in range(dim)
    ]

    GS1_bdry = sym.IntG(  # noqa: N806
        ComplexLinearLogKernel(dim),
        density_rho_sym,
        qbx_forced_limit=qbx_forced_limit)
    GS2_bdry = sym.IntG(  # noqa: N806
        ComplexLinearKernel(dim),
        density_conj_rho_sym,
        qbx_forced_limit=qbx_forced_limit)
    GD1_bdry = sym.IntG(  # noqa: N806
        ComplexFractionalKernel(dim),
        density_rho_sym * dxids_sym,
        qbx_forced_limit=qbx_forced_limit)

    xp, yp = get_arclength_parametrization_derivative(queue, density_discr)
    xp = -xp
    yp = -yp
    tangent = get_tangent_vectors(queue,
                                  density_discr,
                                  loc_sign=qbx_forced_limit)

    # check and fix the direction of parametrization
    # logger.info("Fix all negative signs in:" +
    #        str(xp * tangent[0] + yp * tangent[1]))

    grad_v2 = [
        bind(qbx,
             GD2[iaxis])(queue,
                         mu=mu,
                         arclength_parametrization_derivatives=make_obj_array(
                             [xp, yp])).real for iaxis in range(dim)
    ]
    v2_tangent_der = sum(tangent[iaxis] * grad_v2[iaxis]
                         for iaxis in range(dim))

    from pytential.symbolic.pde.scalar import NeumannOperator
    from sumpy.kernel import LaplaceKernel
    operator_v1 = NeumannOperator(LaplaceKernel(dim),
                                  loc_sign=qbx_forced_limit)
    bound_op_v1 = bind(qbx, operator_v1.operator(var("sigma")))
    # FIXME: the positive sign works here
    rhs_v1 = operator_v1.prepare_rhs(1 * v2_tangent_der)
    gmres_result = gmres(bound_op_v1.scipy_op(queue, "sigma",
                                              dtype=np.float64),
                         rhs_v1,
                         tol=1e-9,
                         progress=True,
                         stall_iterations=0,
                         hard_failure=True)
    sigma = gmres_result.solution
    qbx_stick_out = qbx.copy(
        target_association_tolerance=target_association_tolerance)
    v1 = bind((qbx_stick_out, target_discr),
              operator_v1.representation(var("sigma"),
                                         qbx_forced_limit=None))(queue,
                                                                 sigma=sigma)
    grad_v1 = bind(
        (qbx_stick_out, target_discr),
        operator_v1.representation(
            var("sigma"),
            qbx_forced_limit=None,
            map_potentials=lambda pot: sym.grad(dim, pot)))(queue, sigma=sigma)
    v1_bdry = bind(
        qbx,
        operator_v1.representation(var("sigma"),
                                   qbx_forced_limit=qbx_forced_limit))(
                                       queue, sigma=sigma)

    z_conj = target_discr.nodes()[0] - 1j * target_discr.nodes()[1]
    z_conj_bdry = density_discr.nodes().with_queue(queue)[0] \
            - 1j * density_discr.nodes().with_queue(queue)[1]
    int_rho = 1 / (8 * np.pi) * bind(
        qbx, sym.integral(dim, dim - 1, density_rho_sym))(queue, mu=mu)

    omega_S1 = bind(  # noqa: N806
        (qbx_stick_out, target_discr), GS1)(queue, mu=mu).real
    omega_S2 = -1 * bind(  # noqa: N806
        (qbx_stick_out, target_discr), GS2)(queue, mu=mu).real
    omega_S3 = (z_conj * int_rho).real  # noqa: N806
    omega_S = -(omega_S1 + omega_S2 + omega_S3)  # noqa: N806

    grad_omega_S1 = bind(  # noqa: N806
        (qbx_stick_out, target_discr), sym.grad(dim, GS1))(queue, mu=mu).real
    grad_omega_S2 = -1 * bind(  # noqa: N806
        (qbx_stick_out, target_discr), sym.grad(dim, GS2))(queue, mu=mu).real
    grad_omega_S3 = (int_rho * make_obj_array([1., -1.])).real  # noqa: N806
    grad_omega_S = -(grad_omega_S1 + grad_omega_S2 + grad_omega_S3
                     )  # noqa: N806

    omega_S1_bdry = bind(qbx, GS1_bdry)(queue, mu=mu).real  # noqa: N806
    omega_S2_bdry = -1 * bind(qbx, GS2_bdry)(queue, mu=mu).real  # noqa: N806
    omega_S3_bdry = (z_conj_bdry * int_rho).real  # noqa: N806
    omega_S_bdry = -(omega_S1_bdry + omega_S2_bdry + omega_S3_bdry
                     )  # noqa: N806

    omega_D1 = bind(  # noqa: N806
        (qbx_stick_out, target_discr),
        GD1)(queue,
             mu=mu,
             arclength_parametrization_derivatives=make_obj_array([xp,
                                                                   yp])).real
    omega_D = (omega_D1 + v1)  # noqa: N806

    grad_omega_D1 = bind(  # noqa: N806
        (qbx_stick_out, target_discr), sym.grad(dim, GD1))(
            queue,
            mu=mu,
            arclength_parametrization_derivatives=make_obj_array([xp,
                                                                  yp])).real
    grad_omega_D = grad_omega_D1 + grad_v1  # noqa: N806

    omega_D1_bdry = bind(  # noqa: N806
        qbx, GD1_bdry)(queue,
                       mu=mu,
                       arclength_parametrization_derivatives=make_obj_array(
                           [xp, yp])).real
    omega_D_bdry = (omega_D1_bdry + v1_bdry)  # noqa: N806

    int_bdry_mu = bind(
        qbx, sym.integral(dim, dim - 1, sym.make_sym_vector("mu", dim)))(queue,
                                                                         mu=mu)
    omega_W = (  # noqa: N806
        int_bdry_mu[0] * target_discr.nodes()[1] -
        int_bdry_mu[1] * target_discr.nodes()[0])
    grad_omega_W = make_obj_array(  # noqa: N806
        [-int_bdry_mu[1], int_bdry_mu[0]])
    omega_W_bdry = (  # noqa: N806
        int_bdry_mu[0] * density_discr.nodes().with_queue(queue)[1] -
        int_bdry_mu[1] * density_discr.nodes().with_queue(queue)[0])

    int_bdry = bind(qbx, sym.integral(dim, dim - 1, var("integrand")))(
        queue, integrand=omega_S_bdry + omega_D_bdry + omega_W_bdry)

    debugging_info = {}
    debugging_info['omega_S'] = omega_S
    debugging_info['omega_D'] = omega_D
    debugging_info['omega_W'] = omega_W
    debugging_info['omega_v1'] = v1
    debugging_info['omega_D1'] = omega_D1

    int_interior_func_bdry = bind(qbx,
                                  sym.integral(2, 1,
                                               var("integrand")))(queue,
                                                                  integrand=f)

    path_length = get_path_length(queue, density_discr)
    ext_f = omega_S + omega_D + omega_W + (int_interior_func_bdry -
                                           int_bdry) / path_length
    grad_f = grad_omega_S + grad_omega_D + grad_omega_W

    return ext_f, grad_f[0], grad_f[1], debugging_info
Beispiel #9
0
def compute_harmonic_extension(queue,
                               target_discr,
                               qbx,
                               density_discr,
                               f,
                               loc_sign=1,
                               target_association_tolerance=0.05,
                               gmres_tolerance=1e-14):
    """Harmonic extension.

    :param: loc_sign indicates the domain for extension,
            which equals to the negation of the loc_sign
            for the original problem.
    """
    dim = qbx.ambient_dim
    queue = setup_command_queue(queue=queue)

    # {{{ describe bvp

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

    cse = sym.cse

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

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

    # }}}

    bound_op = bind(qbx, bdry_op_sym)

    # {{{ fix rhs and solve

    bvp_rhs = bind(qbx, sqrt_w * sym.var("bc"))(queue, bc=f)

    from pytential.solve import gmres
    gmres_result = gmres(bound_op.scipy_op(queue, "sigma", dtype=np.float64),
                         bvp_rhs,
                         tol=gmres_tolerance,
                         progress=True,
                         stall_iterations=0,
                         hard_failure=True)

    sigma = bind(qbx,
                 sym.var("sigma") / sqrt_w)(queue, sigma=gmres_result.solution)

    # }}}

    debugging_info = {}
    debugging_info['gmres_result'] = gmres_result

    # {{{ postprocess

    repr_kwargs = dict(qbx_forced_limit=None)
    representation_sym = (sym.S(kernel, inv_sqrt_w_sigma, **repr_kwargs) +
                          sym.D(kernel, inv_sqrt_w_sigma, **repr_kwargs))

    qbx_stick_out = qbx.copy(
        target_association_tolerance=target_association_tolerance)

    debugging_info['qbx'] = qbx_stick_out
    debugging_info['representation'] = representation_sym
    debugging_info['density'] = sigma

    ext_f = bind((qbx_stick_out, target_discr),
                 representation_sym)(queue, sigma=sigma).real

    # }}}

    # NOTE: matching is needed if using
    # pytential.symbolic.pde.scalar.DirichletOperator
    # but here we are using a representation that does not have null
    # space for exterior Dirichlet problem
    if loc_sign == 1 and False:
        bdry_measure = bind(density_discr, sym.integral(dim, dim - 1,
                                                        1))(queue)

        int_func_bdry = bind(qbx, sym.integral(dim, dim - 1,
                                               var("integrand")))(queue,
                                                                  integrand=f)

        solu_bdry = bind((qbx, density_discr),
                         representation_sym)(queue, sigma=sigma).real
        int_solu_bdry = bind(qbx, sym.integral(
            dim, dim - 1, var("integrand")))(queue, integrand=solu_bdry)

        matching_const = (int_func_bdry - int_solu_bdry) / bdry_measure

    else:
        matching_const = 0.

    ext_f = ext_f + matching_const

    def eval_ext_f(target_discr):
        return bind((qbx_stick_out, target_discr), representation_sym)(
            queue, sigma=sigma).real + matching_const

    debugging_info['eval_ext_f'] = eval_ext_f

    return ext_f, debugging_info