def test_3d_orientation(ctx_factory, what, mesh_gen_func, visualize=False): pytest.importorskip("pytential") logging.basicConfig(level=logging.INFO) ctx = ctx_factory() queue = cl.CommandQueue(ctx) mesh = mesh_gen_func() logger.info("%d elements" % mesh.nelements) from meshmode.discretization import Discretization discr = Discretization(ctx, mesh, PolynomialWarpAndBlendGroupFactory(1)) from pytential import bind, sym # {{{ check normals point outward if what == "torus": nodes = sym.nodes(mesh.ambient_dim).as_vector() angle = sym.atan2(nodes[1], nodes[0]) center_nodes = sym.make_obj_array([ 5*sym.cos(angle), 5*sym.sin(angle), 0*angle]) normal_outward_expr = ( sym.normal(mesh.ambient_dim) | (nodes-center_nodes)) else: normal_outward_expr = ( sym.normal(mesh.ambient_dim) | sym.nodes(mesh.ambient_dim)) normal_outward_check = bind(discr, normal_outward_expr)(queue).as_scalar() > 0 assert normal_outward_check.get().all(), normal_outward_check.get() # }}} normals = bind(discr, sym.normal(mesh.ambient_dim).xproject(1))(queue) if visualize: from meshmode.discretization.visualization import make_visualizer vis = make_visualizer(queue, discr, 1) vis.write_vtk_file("normals.vtu", [ ("normals", normals), ])
def targets_from_sources(sign, dist, dim=2): nodes = dof_array_to_numpy( actx, bind(places, sym.nodes(dim, dofdesc=dd))(actx).as_vector(object)) normals = dof_array_to_numpy( actx, bind(places, sym.normal(dim, dofdesc=dd))(actx).as_vector(object)) return actx.from_numpy(nodes + normals * sign * dist)
def get_centers_on_side(lpot_src, sign): adim = lpot_src.density_discr.ambient_dim dim = lpot_src.density_discr.dim from pytential import sym, bind with cl.CommandQueue(lpot_src.cl_context) as queue: nodes = bind(lpot_src.density_discr, sym.nodes(adim))(queue) normals = bind(lpot_src.density_discr, sym.normal(adim, dim=dim))(queue) expansion_radii = lpot_src._expansion_radii("nsources").with_queue(queue) return (nodes + normals * sign * expansion_radii).as_vector(np.object)
def targets_from_sources(sign, dist, dim=2): nodes = actx.to_numpy( flatten( bind(places, sym.nodes(dim, dofdesc=dd))(actx).as_vector(), actx)).reshape(dim, -1) normals = actx.to_numpy( flatten( bind(places, sym.normal(dim, dofdesc=dd))(actx).as_vector(), actx)).reshape(dim, -1) return actx.from_numpy(nodes + normals * sign * dist)
def get_sym_maxwell_plane_wave(amplitude_vec, v, omega, epsilon=1, mu=1, dofdesc=None): r"""Return a symbolic expression that, when bound to a :class:`pytential.source.PointPotentialSource` will yield a field satisfying Maxwell's equations. :arg amplitude_vec: should be orthogonal to *v*. If it is not, it will be orthogonalized. :arg v: a three-vector representing the phase velocity of the wave (may be an object array of variables or a vector of concrete numbers) While *v* may mathematically be complex-valued, this function is for now only tested for real values. :arg omega: Accepts the "Helmholtz k" to be compatible with other parts of this module. Uses the sign convention :math:`\exp(-1 \omega t)` for the time dependency. This will return an object of six entries, the first three of which represent the electric, and the second three of which represent the magnetic field. This satisfies the time-domain Maxwell's equations as verified by :func:`sumpy.point_calculus.frequency_domain_maxwell`. """ # See section 7.1 of Jackson, third ed. for derivation. # NOTE: for complex, need to ensure real(n).dot(imag(n)) = 0 (7.15) x = sym.nodes(3, dofdesc=dofdesc).as_vector() v_mag_squared = sym.cse(np.dot(v, v), "v_mag_squared") n = v / sym.sqrt(v_mag_squared) amplitude_vec = amplitude_vec - np.dot(amplitude_vec, n) * n c_inv = np.sqrt(mu * epsilon) e = amplitude_vec * sym.exp(1j * np.dot(n * omega, x)) return sym.flat_obj_array(e, c_inv * sym.cross(n, e))
def get_sym_maxwell_plane_wave(amplitude_vec, v, omega, epsilon=1, mu=1, where=None): r"""Return a symbolic expression that, when bound to a :class:`pytential.source.PointPotentialSource` will yield a field satisfying Maxwell's equations. :arg amplitude_vec: should be orthogonal to *v*. If it is not, it will be orthogonalized. :arg v: a three-vector representing the phase velocity of the wave (may be an object array of variables or a vector of concrete numbers) While *v* may mathematically be complex-valued, this function is for now only tested for real values. :arg omega: Accepts the "Helmholtz k" to be compatible with other parts of this module. Uses the sign convention :math:`\exp(-1 \omega t)` for the time dependency. This will return an object of six entries, the first three of which represent the electric, and the second three of which represent the magnetic field. This satisfies the time-domain Maxwell's equations as verified by :func:`sumpy.point_calculus.frequency_domain_maxwell`. """ # See section 7.1 of Jackson, third ed. for derivation. # NOTE: for complex, need to ensure real(n).dot(imag(n)) = 0 (7.15) x = sym.nodes(3, where).as_vector() v_mag_squared = sym.cse(np.dot(v, v), "v_mag_squared") n = v/sym.sqrt(v_mag_squared) amplitude_vec = amplitude_vec - np.dot(amplitude_vec, n)*n c_inv = np.sqrt(mu*epsilon) e = amplitude_vec * sym.exp(1j*np.dot(n*omega, x)) return sym.join_fields(e, c_inv * sym.cross(n, e))
def test_sanity_balls(ctx_getter, src_file, dim, mesh_order, visualize=False): pytest.importorskip("pytential") logging.basicConfig(level=logging.INFO) ctx = ctx_getter() queue = cl.CommandQueue(ctx) from pytools.convergence import EOCRecorder vol_eoc_rec = EOCRecorder() surf_eoc_rec = EOCRecorder() # overkill quad_order = mesh_order from pytential import bind, sym for h in [0.2, 0.14, 0.1]: from meshmode.mesh.io import generate_gmsh, FileSource mesh = generate_gmsh(FileSource(src_file), dim, order=mesh_order, other_options=[ "-string", "Mesh.CharacteristicLengthMax = %g;" % h ], force_ambient_dim=dim) logger.info("%d elements" % mesh.nelements) # {{{ discretizations and connections from meshmode.discretization import Discretization vol_discr = Discretization( ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(quad_order)) from meshmode.discretization.connection import make_face_restriction bdry_connection = make_face_restriction( vol_discr, InterpolatoryQuadratureSimplexGroupFactory(quad_order), BTAG_ALL) bdry_discr = bdry_connection.to_discr # }}} # {{{ visualizers from meshmode.discretization.visualization import make_visualizer vol_vis = make_visualizer(queue, vol_discr, 20) bdry_vis = make_visualizer(queue, bdry_discr, 20) # }}} from math import gamma true_surf = 2 * np.pi**(dim / 2) / gamma(dim / 2) true_vol = true_surf / dim vol_x = vol_discr.nodes().with_queue(queue) vol_one = vol_x[0].copy() vol_one.fill(1) from pytential import norm, integral # noqa comp_vol = integral(vol_discr, queue, vol_one) rel_vol_err = abs(true_vol - comp_vol) / true_vol vol_eoc_rec.add_data_point(h, rel_vol_err) print("VOL", true_vol, comp_vol) bdry_x = bdry_discr.nodes().with_queue(queue) bdry_one_exact = bdry_x[0].copy() bdry_one_exact.fill(1) bdry_one = bdry_connection(queue, vol_one).with_queue(queue) intp_err = norm(bdry_discr, queue, bdry_one - bdry_one_exact) assert intp_err < 1e-14 comp_surf = integral(bdry_discr, queue, bdry_one) rel_surf_err = abs(true_surf - comp_surf) / true_surf surf_eoc_rec.add_data_point(h, rel_surf_err) print("SURF", true_surf, comp_surf) if visualize: vol_vis.write_vtk_file("volume-h=%g.vtu" % h, [ ("f", vol_one), ("area_el", bind(vol_discr, sym.area_element())(queue)), ]) bdry_vis.write_vtk_file("boundary-h=%g.vtu" % h, [("f", bdry_one)]) # {{{ check normals point outward normal_outward_check = bind( bdry_discr, sym.normal(mesh.ambient_dim) | sym.nodes(mesh.ambient_dim), )(queue).as_scalar() > 0 assert normal_outward_check.get().all(), normal_outward_check.get() # }}} print("---------------------------------") print("VOLUME") print("---------------------------------") print(vol_eoc_rec) assert vol_eoc_rec.order_estimate() >= mesh_order print("---------------------------------") print("SURFACE") print("---------------------------------") print(surf_eoc_rec) assert surf_eoc_rec.order_estimate() >= mesh_order
def test_sanity_single_element(ctx_getter, dim, order, visualize=False): pytest.importorskip("pytential") cl_ctx = ctx_getter() queue = cl.CommandQueue(cl_ctx) from modepy.tools import unit_vertices vertices = unit_vertices(dim).T.copy() center = np.empty(dim, np.float64) center.fill(-0.5) import modepy as mp from meshmode.mesh import SimplexElementGroup, Mesh, BTAG_ALL mg = SimplexElementGroup( order=order, vertex_indices=np.arange(dim + 1, dtype=np.int32).reshape(1, -1), nodes=mp.warp_and_blend_nodes(dim, order).reshape(dim, 1, -1), dim=dim) mesh = Mesh(vertices, [mg], nodal_adjacency=None, facial_adjacency_groups=None) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ PolynomialWarpAndBlendGroupFactory vol_discr = Discretization(cl_ctx, mesh, PolynomialWarpAndBlendGroupFactory(order + 3)) # {{{ volume calculation check vol_x = vol_discr.nodes().with_queue(queue) vol_one = vol_x[0].copy() vol_one.fill(1) from pytential import norm, integral # noqa from pytools import factorial true_vol = 1 / factorial(dim) * 2**dim comp_vol = integral(vol_discr, queue, vol_one) rel_vol_err = abs(true_vol - comp_vol) / true_vol assert rel_vol_err < 1e-12 # }}} # {{{ boundary discretization from meshmode.discretization.connection import make_face_restriction bdry_connection = make_face_restriction( vol_discr, PolynomialWarpAndBlendGroupFactory(order + 3), BTAG_ALL) bdry_discr = bdry_connection.to_discr # }}} # {{{ visualizers from meshmode.discretization.visualization import make_visualizer #vol_vis = make_visualizer(queue, vol_discr, 4) bdry_vis = make_visualizer(queue, bdry_discr, 4) # }}} from pytential import bind, sym bdry_normals = bind(bdry_discr, sym.normal(dim))(queue).as_vector(dtype=object) if visualize: bdry_vis.write_vtk_file("boundary.vtu", [("bdry_normals", bdry_normals)]) from pytential import bind, sym normal_outward_check = bind( bdry_discr, sym.normal(dim) | (sym.nodes(dim) + 0.5 * sym.ones_vec(dim)), )(queue).as_scalar() > 0 assert normal_outward_check.get().all(), normal_outward_check.get()
def test_sanity_balls(actx_factory, src_file, dim, mesh_order, visualize=False): pytest.importorskip("pytential") logging.basicConfig(level=logging.INFO) actx = actx_factory() from pytools.convergence import EOCRecorder vol_eoc_rec = EOCRecorder() surf_eoc_rec = EOCRecorder() # overkill quad_order = mesh_order from pytential import bind, sym for h in [0.2, 0.1, 0.05]: from meshmode.mesh.io import generate_gmsh, FileSource mesh = generate_gmsh(FileSource(src_file), dim, order=mesh_order, other_options=[ "-string", "Mesh.CharacteristicLengthMax = %g;" % h ], force_ambient_dim=dim, target_unit="MM") logger.info("%d elements", mesh.nelements) # {{{ discretizations and connections from meshmode.discretization import Discretization vol_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(quad_order)) from meshmode.discretization.connection import make_face_restriction bdry_connection = make_face_restriction( actx, vol_discr, InterpolatoryQuadratureSimplexGroupFactory(quad_order), BTAG_ALL) bdry_discr = bdry_connection.to_discr # }}} from math import gamma true_surf = 2 * np.pi**(dim / 2) / gamma(dim / 2) true_vol = true_surf / dim vol_x = thaw(vol_discr.nodes(), actx) vol_one = vol_x[0] * 0 + 1 from pytential import norm, integral # noqa comp_vol = integral(vol_discr, vol_one) rel_vol_err = abs(true_vol - comp_vol) / true_vol vol_eoc_rec.add_data_point(h, rel_vol_err) print("VOL", true_vol, comp_vol) bdry_x = thaw(bdry_discr.nodes(), actx) bdry_one_exact = bdry_x[0] * 0 + 1 bdry_one = bdry_connection(vol_one) intp_err = norm(bdry_discr, bdry_one - bdry_one_exact) assert intp_err < 1e-14 comp_surf = integral(bdry_discr, bdry_one) rel_surf_err = abs(true_surf - comp_surf) / true_surf surf_eoc_rec.add_data_point(h, rel_surf_err) print("SURF", true_surf, comp_surf) if visualize: from meshmode.discretization.visualization import make_visualizer vol_vis = make_visualizer(actx, vol_discr, 7) bdry_vis = make_visualizer(actx, bdry_discr, 7) name = src_file.split("-")[0] vol_vis.write_vtk_file(f"sanity_balls_volume_{name}_{h:g}.vtu", [ ("f", vol_one), ("area_el", bind(vol_discr, sym.area_element(mesh.ambient_dim, mesh.ambient_dim))(actx)), ]) bdry_vis.write_vtk_file(f"sanity_balls_boundary_{name}_{h:g}.vtu", [("f", bdry_one)]) # {{{ check normals point outward normal_outward_check = bind( bdry_discr, sym.normal(mesh.ambient_dim) | sym.nodes(mesh.ambient_dim), )(actx).as_scalar() normal_outward_check = flatten_to_numpy(actx, normal_outward_check > 0) assert normal_outward_check.all(), normal_outward_check # }}} print("---------------------------------") print("VOLUME") print("---------------------------------") print(vol_eoc_rec) assert vol_eoc_rec.order_estimate() >= mesh_order print("---------------------------------") print("SURFACE") print("---------------------------------") print(surf_eoc_rec) assert surf_eoc_rec.order_estimate() >= mesh_order
def test_sanity_single_element(actx_factory, dim, mesh_order, group_cls, visualize=False): pytest.importorskip("pytential") actx = actx_factory() if group_cls is SimplexElementGroup: group_factory = PolynomialWarpAndBlendGroupFactory(mesh_order + 3) elif group_cls is TensorProductElementGroup: group_factory = LegendreGaussLobattoTensorProductGroupFactory( mesh_order + 3) else: raise TypeError import modepy as mp shape = group_cls._modepy_shape_cls(dim) space = mp.space_for_shape(shape, mesh_order) vertices = mp.unit_vertices_for_shape(shape) nodes = mp.edge_clustered_nodes_for_space(space, shape).reshape(dim, 1, -1) vertex_indices = np.arange(shape.nvertices, dtype=np.int32).reshape(1, -1) center = np.empty(dim, np.float64) center.fill(-0.5) mg = group_cls(mesh_order, vertex_indices, nodes, dim=dim) mesh = Mesh(vertices, [mg], is_conforming=True) from meshmode.discretization import Discretization vol_discr = Discretization(actx, mesh, group_factory) # {{{ volume calculation check if isinstance(mg, SimplexElementGroup): from pytools import factorial true_vol = 1 / factorial(dim) * 2**dim elif isinstance(mg, TensorProductElementGroup): true_vol = 2**dim else: raise TypeError nodes = thaw(vol_discr.nodes(), actx) vol_one = 1 + 0 * nodes[0] from pytential import norm, integral # noqa comp_vol = integral(vol_discr, vol_one) rel_vol_err = abs(true_vol - comp_vol) / true_vol assert rel_vol_err < 1e-12 # }}} # {{{ boundary discretization from meshmode.discretization.connection import make_face_restriction bdry_connection = make_face_restriction(actx, vol_discr, group_factory, BTAG_ALL) bdry_discr = bdry_connection.to_discr # }}} from pytential import bind, sym bdry_normals = bind(bdry_discr, sym.normal(dim).as_vector())(actx) if visualize: from meshmode.discretization.visualization import make_visualizer bdry_vis = make_visualizer(actx, bdry_discr, 4) bdry_vis.write_vtk_file("sanity_single_element_boundary.vtu", [("normals", bdry_normals)]) normal_outward_check = bind( bdry_discr, sym.normal(dim) | (sym.nodes(dim) + 0.5 * sym.ones_vec(dim)), )(actx).as_scalar() normal_outward_check = flatten_to_numpy(actx, normal_outward_check > 0) assert normal_outward_check.all(), normal_outward_check
def test_geometry_collection_caching(ctx_factory): # NOTE: checks that the on-demand caching works properly in # the `GeometryCollection`. This is done by constructing a few separated # spheres, putting a few `QBXLayerPotentialSource`s on them and requesting # the `nodes` on each `discr_stage`. ctx = ctx_factory() queue = cl.CommandQueue(ctx) actx = PyOpenCLArrayContext(queue) ndim = 2 nelements = 1024 target_order = 7 qbx_order = 4 ngeometry = 3 # construct discretizations from meshmode.mesh.generation import ellipse, make_curve_mesh from meshmode.mesh.processing import affine_map from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory discrs = [] radius = 1.0 for k in range(ngeometry): if k == 0: mesh = make_curve_mesh(partial(ellipse, radius), np.linspace(0.0, 1.0, nelements + 1), target_order) else: mesh = affine_map(discrs[0].mesh, b=np.array([3 * k * radius, 0])) discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) discrs.append(discr) # construct qbx source from pytential.qbx import QBXLayerPotentialSource lpots = [] sources = [f"source_{k}" for k in range(ngeometry)] for k, density_discr in enumerate(discrs): qbx = QBXLayerPotentialSource(density_discr, fine_order=2 * target_order, qbx_order=qbx_order, fmm_order=False) lpots.append(qbx) # construct a geometry collection from pytential import GeometryCollection places = GeometryCollection(dict(zip(sources, lpots))) print(places.places) # check on-demand refinement from pytential import bind, sym discr_stages = [ sym.QBX_SOURCE_STAGE1, sym.QBX_SOURCE_STAGE2, sym.QBX_SOURCE_QUAD_STAGE2 ] for k in range(ngeometry): for discr_stage in discr_stages: with pytest.raises(KeyError): discr = places._get_discr_from_cache(sources[k], discr_stage) dofdesc = sym.DOFDescriptor(sources[k], discr_stage=discr_stage) bind(places, sym.nodes(ndim, dofdesc=dofdesc))(actx) discr = places._get_discr_from_cache(sources[k], discr_stage) assert discr is not None
def test_sanity_balls(ctx_factory, src_file, dim, mesh_order, visualize=False): pytest.importorskip("pytential") logging.basicConfig(level=logging.INFO) ctx = ctx_factory() queue = cl.CommandQueue(ctx) from pytools.convergence import EOCRecorder vol_eoc_rec = EOCRecorder() surf_eoc_rec = EOCRecorder() # overkill quad_order = mesh_order from pytential import bind, sym for h in [0.2, 0.1, 0.05]: from meshmode.mesh.io import generate_gmsh, FileSource mesh = generate_gmsh( FileSource(src_file), dim, order=mesh_order, other_options=["-string", "Mesh.CharacteristicLengthMax = %g;" % h], force_ambient_dim=dim) logger.info("%d elements" % mesh.nelements) # {{{ discretizations and connections from meshmode.discretization import Discretization vol_discr = Discretization(ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(quad_order)) from meshmode.discretization.connection import make_face_restriction bdry_connection = make_face_restriction( vol_discr, InterpolatoryQuadratureSimplexGroupFactory(quad_order), BTAG_ALL) bdry_discr = bdry_connection.to_discr # }}} # {{{ visualizers from meshmode.discretization.visualization import make_visualizer vol_vis = make_visualizer(queue, vol_discr, 20) bdry_vis = make_visualizer(queue, bdry_discr, 20) # }}} from math import gamma true_surf = 2*np.pi**(dim/2)/gamma(dim/2) true_vol = true_surf/dim vol_x = vol_discr.nodes().with_queue(queue) vol_one = vol_x[0].copy() vol_one.fill(1) from pytential import norm, integral # noqa comp_vol = integral(vol_discr, queue, vol_one) rel_vol_err = abs(true_vol - comp_vol) / true_vol vol_eoc_rec.add_data_point(h, rel_vol_err) print("VOL", true_vol, comp_vol) bdry_x = bdry_discr.nodes().with_queue(queue) bdry_one_exact = bdry_x[0].copy() bdry_one_exact.fill(1) bdry_one = bdry_connection(queue, vol_one).with_queue(queue) intp_err = norm(bdry_discr, queue, bdry_one-bdry_one_exact) assert intp_err < 1e-14 comp_surf = integral(bdry_discr, queue, bdry_one) rel_surf_err = abs(true_surf - comp_surf) / true_surf surf_eoc_rec.add_data_point(h, rel_surf_err) print("SURF", true_surf, comp_surf) if visualize: vol_vis.write_vtk_file("volume-h=%g.vtu" % h, [ ("f", vol_one), ("area_el", bind( vol_discr, sym.area_element(mesh.ambient_dim, mesh.ambient_dim)) (queue)), ]) bdry_vis.write_vtk_file("boundary-h=%g.vtu" % h, [("f", bdry_one)]) # {{{ check normals point outward normal_outward_check = bind(bdry_discr, sym.normal(mesh.ambient_dim) | sym.nodes(mesh.ambient_dim), )(queue).as_scalar() > 0 assert normal_outward_check.get().all(), normal_outward_check.get() # }}} print("---------------------------------") print("VOLUME") print("---------------------------------") print(vol_eoc_rec) assert vol_eoc_rec.order_estimate() >= mesh_order print("---------------------------------") print("SURFACE") print("---------------------------------") print(surf_eoc_rec) assert surf_eoc_rec.order_estimate() >= mesh_order
def test_sanity_single_element(ctx_factory, dim, order, visualize=False): pytest.importorskip("pytential") cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) from modepy.tools import unit_vertices vertices = unit_vertices(dim).T.copy() center = np.empty(dim, np.float64) center.fill(-0.5) import modepy as mp from meshmode.mesh import SimplexElementGroup, Mesh, BTAG_ALL mg = SimplexElementGroup( order=order, vertex_indices=np.arange(dim+1, dtype=np.int32).reshape(1, -1), nodes=mp.warp_and_blend_nodes(dim, order).reshape(dim, 1, -1), dim=dim) mesh = Mesh(vertices, [mg], is_conforming=True) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ PolynomialWarpAndBlendGroupFactory vol_discr = Discretization(cl_ctx, mesh, PolynomialWarpAndBlendGroupFactory(order+3)) # {{{ volume calculation check vol_x = vol_discr.nodes().with_queue(queue) vol_one = vol_x[0].copy() vol_one.fill(1) from pytential import norm, integral # noqa from pytools import factorial true_vol = 1/factorial(dim) * 2**dim comp_vol = integral(vol_discr, queue, vol_one) rel_vol_err = abs(true_vol - comp_vol) / true_vol assert rel_vol_err < 1e-12 # }}} # {{{ boundary discretization from meshmode.discretization.connection import make_face_restriction bdry_connection = make_face_restriction( vol_discr, PolynomialWarpAndBlendGroupFactory(order + 3), BTAG_ALL) bdry_discr = bdry_connection.to_discr # }}} # {{{ visualizers from meshmode.discretization.visualization import make_visualizer #vol_vis = make_visualizer(queue, vol_discr, 4) bdry_vis = make_visualizer(queue, bdry_discr, 4) # }}} from pytential import bind, sym bdry_normals = bind(bdry_discr, sym.normal(dim))(queue).as_vector(dtype=object) if visualize: bdry_vis.write_vtk_file("boundary.vtu", [ ("bdry_normals", bdry_normals) ]) normal_outward_check = bind(bdry_discr, sym.normal(dim) | (sym.nodes(dim) + 0.5*sym.ones_vec(dim)), )(queue).as_scalar() > 0 assert normal_outward_check.get().all(), normal_outward_check.get()
def targets_from_sources(sign, dist): from pytential import sym, bind dim = 2 nodes = bind(lpot_source.density_discr, sym.nodes(dim))(queue) normals = bind(lpot_source.density_discr, sym.normal(dim))(queue) return (nodes + normals * sign * dist).as_vector(np.object)
def targets_from_sources(sign, dist): from pytential import sym, bind dim = 2 nodes = bind(lpot_source.density_discr, sym.nodes(dim))(queue) normals = bind(lpot_source.density_discr, sym.normal(dim))(queue) return (nodes + normals * sign * dist).as_vector(np.object)