def test_chained_full_resample_matrix(actx_factory, ndim, visualize=False): from meshmode.discretization.connection.chained import \ make_full_resample_matrix actx = actx_factory() discr = create_discretization(actx, ndim, order=2, nelements=12) connections = [] conn = create_refined_connection(actx, discr) connections.append(conn) conn = create_refined_connection(actx, conn.to_discr) connections.append(conn) from meshmode.discretization.connection import \ ChainedDiscretizationConnection chained = ChainedDiscretizationConnection(connections) def f(x): from functools import reduce return 0.1 * reduce(lambda x, y: x * actx.np.sin(5 * y), x) resample_mat = actx.to_numpy(make_full_resample_matrix(actx, chained)) x = thaw(actx, connections[0].from_discr.nodes()) fx = f(x) f1 = resample_mat @ actx.to_numpy(flatten(fx)) f2 = actx.to_numpy(flatten(chained(fx))) f3 = actx.to_numpy(flatten(connections[1](connections[0](fx)))) assert np.allclose(f1, f2) assert np.allclose(f2, f3)
def test_chained_connection(actx_factory, ndim, visualize=False): actx = actx_factory() discr = create_discretization(actx, ndim, nelements=10) connections = [] conn = create_refined_connection(actx, discr, threshold=np.inf) connections.append(conn) conn = create_refined_connection(actx, conn.to_discr, threshold=np.inf) connections.append(conn) from meshmode.discretization.connection import \ ChainedDiscretizationConnection chained = ChainedDiscretizationConnection(connections) def f(x): from functools import reduce return 0.1 * reduce(lambda x, y: x * actx.np.sin(5 * y), x) x = thaw(actx, connections[0].from_discr.nodes()) fx = f(x) f1 = chained(fx) f2 = connections[1](connections[0](fx)) assert actx.np.linalg.norm(f1 - f2, np.inf) / actx.np.linalg.norm(f2) < 1e-11
def test_chained_connection(ctx_factory, ndim, visualize=False): ctx = ctx_factory() queue = cl.CommandQueue(ctx) discr = create_discretization(queue, ndim, nelements=10) connections = [] conn = create_refined_connection(queue, discr, threshold=np.inf) connections.append(conn) conn = create_refined_connection(queue, conn.to_discr, threshold=np.inf) connections.append(conn) from meshmode.discretization.connection import \ ChainedDiscretizationConnection chained = ChainedDiscretizationConnection(connections) def f(x): from six.moves import reduce return 0.1 * reduce(lambda x, y: x * cl.clmath.sin(5 * y), x) x = connections[0].from_discr.nodes().with_queue(queue) fx = f(x) f1 = chained(queue, fx).get(queue) f2 = connections[1](queue, connections[0](queue, fx)).get(queue) assert np.allclose(f1, f2)
def test_chained_full_resample_matrix(ctx_factory, ndim, visualize=False): from meshmode.discretization.connection.chained import \ make_full_resample_matrix ctx = ctx_factory() queue = cl.CommandQueue(ctx) discr = create_discretization(queue, ndim) connections = [] conn = create_refined_connection(queue, discr) connections.append(conn) conn = create_refined_connection(queue, conn.to_discr) connections.append(conn) from meshmode.discretization.connection import \ ChainedDiscretizationConnection chained = ChainedDiscretizationConnection(connections) def f(x): from six.moves import reduce return 0.1 * reduce(lambda x, y: x * cl.clmath.sin(5 * y), x) resample_mat = make_full_resample_matrix(queue, chained).get(queue) x = connections[0].from_discr.nodes().with_queue(queue) fx = f(x) f1 = np.dot(resample_mat, fx.get(queue)) f2 = chained(queue, fx).get(queue) f3 = connections[1](queue, connections[0](queue, fx)).get(queue) assert np.allclose(f1, f2) assert np.allclose(f2, f3)
def test_chained_batch_table(ctx_factory, ndim, visualize=False): from meshmode.discretization.connection.chained import \ _build_element_lookup_table ctx = ctx_factory() queue = cl.CommandQueue(ctx) discr = create_discretization(queue, ndim) connections = [] conn = create_refined_connection(queue, discr) connections.append(conn) conn = create_refined_connection(queue, conn.to_discr) connections.append(conn) from meshmode.discretization.connection import ChainedDiscretizationConnection chained = ChainedDiscretizationConnection(connections) conn = chained.connections[0] el_table = _build_element_lookup_table(queue, conn) for igrp, grp in enumerate(conn.groups): for ibatch, batch in enumerate(grp.batches): ifrom = batch.from_element_indices.get(queue) jfrom = el_table[igrp][batch.to_element_indices.get(queue)] assert np.all(ifrom == jfrom) assert np.min(el_table[igrp]) >= 0
def test_chained_full_resample_matrix(ctx_factory, ndim, visualize=False): from meshmode.discretization.connection.chained import \ make_full_resample_matrix ctx = ctx_factory() queue = cl.CommandQueue(ctx) actx = PyOpenCLArrayContext(queue) discr = create_discretization(actx, ndim) connections = [] conn = create_refined_connection(actx, discr) connections.append(conn) conn = create_refined_connection(actx, conn.to_discr) connections.append(conn) from meshmode.discretization.connection import \ ChainedDiscretizationConnection chained = ChainedDiscretizationConnection(connections) def f(x): from six.moves import reduce return 0.1 * reduce(lambda x, y: x * actx.np.sin(5 * y), x) resample_mat = actx.to_numpy(make_full_resample_matrix(actx, chained)) x = thaw(actx, connections[0].from_discr.nodes()) fx = f(x) f1 = resample_mat @ actx.to_numpy(flatten(fx)) f2 = actx.to_numpy(flatten(chained(fx))) f3 = actx.to_numpy(flatten(connections[1](connections[0](fx)))) assert np.allclose(f1, f2) assert np.allclose(f2, f3)
def resampler(self): from meshmode.discretization.connection import \ ChainedDiscretizationConnection conn = self.refined_interp_to_ovsmp_quad_connection if self._to_refined_connection is not None: return ChainedDiscretizationConnection( [self._to_refined_connection, conn]) return conn
def run(nelements, order): discr = create_discretization(actx, ndim, nelements=nelements, order=order, mesh_name=mesh_name) threshold = 1.0 connections = [] conn = create_refined_connection(actx, discr, threshold=threshold) connections.append(conn) if ndim == 2: # NOTE: additional refinement makes the 3D meshes explode in size conn = create_refined_connection(actx, conn.to_discr, threshold=threshold) connections.append(conn) conn = create_refined_connection(actx, conn.to_discr, threshold=threshold) connections.append(conn) from meshmode.discretization.connection import \ ChainedDiscretizationConnection chained = ChainedDiscretizationConnection(connections) from meshmode.discretization.connection import \ L2ProjectionInverseDiscretizationConnection reverse = L2ProjectionInverseDiscretizationConnection(chained) # create test vector from_nodes = thaw(actx, chained.from_discr.nodes()) to_nodes = thaw(actx, chained.to_discr.nodes()) from_x = 0 to_x = 0 for d in range(ndim): from_x += actx.np.cos(from_nodes[d])**(d + 1) to_x += actx.np.cos(to_nodes[d])**(d + 1) from_interp = reverse(to_x) return (1.0 / nelements, actx.np.linalg.norm(from_interp - from_x, np.inf) / actx.np.linalg.norm(from_x, np.inf))
def run(nelements, order): discr = create_discretization(queue, ndim, nelements=nelements, order=order, mesh_name=mesh_name) threshold = 1.0 connections = [] conn = create_refined_connection(queue, discr, threshold=threshold) connections.append(conn) if ndim == 2: # NOTE: additional refinement makes the 3D meshes explode in size conn = create_refined_connection(queue, conn.to_discr, threshold=threshold) connections.append(conn) conn = create_refined_connection(queue, conn.to_discr, threshold=threshold) connections.append(conn) from meshmode.discretization.connection import \ ChainedDiscretizationConnection chained = ChainedDiscretizationConnection(connections) from meshmode.discretization.connection import \ L2ProjectionInverseDiscretizationConnection reverse = L2ProjectionInverseDiscretizationConnection(chained) # create test vector from_nodes = chained.from_discr.nodes().with_queue(queue) to_nodes = chained.to_discr.nodes().with_queue(queue) from_x = 0 to_x = 0 for d in range(ndim): from_x += cl.clmath.cos(from_nodes[d]) ** (d + 1) to_x += cl.clmath.cos(to_nodes[d]) ** (d + 1) from_interp = reverse(queue, to_x) from_interp = from_interp.get(queue) from_x = from_x.get(queue) return 1.0 / nelements, la.norm(from_interp - from_x) / la.norm(from_x)
def get_qbx(self, **kwargs): """ Return a :class:`QBXLayerPotentialSource` to bind to an operator """ qbx = QBXLayerPotentialSource(self._discr, **kwargs) # {{{ If refining, refine and store connection (possibly composed with bdy # connection if self._refine: self._refine = False from meshmode.discretization.connection import \ ChainedDiscretizationConnection qbx, refinement_connection = qbx.with_refinement() if self._connection: connections = [self._connection, refinement_connection] self._connection = ChainedDiscretizationConnection(connections) else: self._connection = refinement_connection # }}} return qbx
def _refine_qbx_stage2(lpot_source, stage1_density_discr, wrangler, group_factory, expansion_disturbance_tolerance=None, force_stage2_uniform_refinement_rounds=None, maxiter=None, debug=None, visualize=False): from meshmode.discretization.connection import ChainedDiscretizationConnection if lpot_source._disable_refinement: return stage1_density_discr, ChainedDiscretizationConnection( [], from_discr=stage1_density_discr) from meshmode.mesh.refinement import RefinerWithoutAdjacency refiner = RefinerWithoutAdjacency(stage1_density_discr.mesh) # TODO: Stop doing redundant checks by avoiding panels which no longer need # refinement. connections = [] violated_criteria = [] iter_violated_criteria = ["start"] niter = 0 stage2_density_discr = stage1_density_discr while iter_violated_criteria: iter_violated_criteria = [] niter += 1 if niter > maxiter: _warn_max_iterations(violated_criteria, expansion_disturbance_tolerance) break places = _make_temporary_collection( lpot_source, stage1_density_discr=stage1_density_discr, stage2_density_discr=stage2_density_discr) # Build tree and auxiliary data. # FIXME: The tree should not have to be rebuilt at each iteration. tree = wrangler.build_tree(places, sources_list=[places.auto_source.geometry], use_stage2_discr=True) peer_lists = wrangler.find_peer_lists(tree) refine_flags = make_empty_refine_flags(wrangler.queue, stage2_density_discr) has_insufficient_quad_resolution = \ wrangler.check_sufficient_source_quadrature_resolution( stage2_density_discr, tree, peer_lists, refine_flags, debug) if has_insufficient_quad_resolution: iter_violated_criteria.append("insufficient quadrature resolution") _visualize_refinement(wrangler.queue, stage2_density_discr, niter, 2, "quad-resolution", refine_flags, visualize=visualize) if iter_violated_criteria: violated_criteria.append(" and ".join(iter_violated_criteria)) conn = wrangler.refine(stage2_density_discr, refiner, refine_flags, group_factory, debug) stage2_density_discr = conn.to_discr connections.append(conn) del tree del refine_flags del peer_lists for _ in range(force_stage2_uniform_refinement_rounds): conn = wrangler.refine( stage2_density_discr, refiner, np.ones(stage2_density_discr.mesh.nelements, dtype=np.bool), group_factory, debug) stage2_density_discr = conn.to_discr connections.append(conn) conn = ChainedDiscretizationConnection(connections, from_discr=stage1_density_discr) return stage2_density_discr, conn
def test_chained_to_direct(ctx_factory, ndim, chain_type, nelements=128, visualize=False): import time from meshmode.discretization.connection.chained import \ flatten_chained_connection ctx = ctx_factory() queue = cl.CommandQueue(ctx) discr = create_discretization(queue, ndim, nelements=nelements) connections = [] if chain_type == 1: conn = create_refined_connection(queue, discr) connections.append(conn) conn = create_refined_connection(queue, conn.to_discr) connections.append(conn) elif chain_type == 2: conn = create_refined_connection(queue, discr) connections.append(conn) conn = create_refined_connection(queue, conn.to_discr) connections.append(conn) conn = create_refined_connection(queue, conn.to_discr) connections.append(conn) elif chain_type == 3 and ndim == 3: conn = create_refined_connection(queue, discr, threshold=np.inf) connections.append(conn) conn = create_face_connection(queue, conn.to_discr) connections.append(conn) else: raise ValueError('unknown test case') from meshmode.discretization.connection import \ ChainedDiscretizationConnection chained = ChainedDiscretizationConnection(connections) t_start = time.time() direct = flatten_chained_connection(queue, chained) t_end = time.time() if visualize: print('[TIME] Flatten: {:.5e}'.format(t_end - t_start)) if chain_type < 3: to_element_indices = np.full(direct.to_discr.mesh.nelements, 0, dtype=np.int) for grp in direct.groups: for batch in grp.batches: for i in batch.to_element_indices.get(queue): to_element_indices[i] += 1 assert np.min(to_element_indices) > 0 def f(x): from six.moves import reduce return 0.1 * reduce(lambda x, y: x * cl.clmath.sin(5 * y), x) x = connections[0].from_discr.nodes().with_queue(queue) fx = f(x) t_start = time.time() f1 = direct(queue, fx).get(queue) t_end = time.time() if visualize: print('[TIME] Direct: {:.5e}'.format(t_end - t_start)) t_start = time.time() f2 = chained(queue, fx).get(queue) t_end = time.time() if visualize: print('[TIME] Chained: {:.5e}'.format(t_end - t_start)) if visualize and ndim == 2: import matplotlib.pyplot as pt pt.figure(figsize=(10, 8), dpi=300) pt.plot(f1, label='Direct') pt.plot(f2, label='Chained') pt.ylim([np.min(f2) - 0.1, np.max(f2) + 0.1]) pt.legend() pt.savefig('test_chained_to_direct.png') pt.clf() assert np.allclose(f1, f2)
def connection_from_dds(places, from_dd, to_dd): """ :arg places: a :class:`~pytential.symbolic.execution.GeometryCollection` or an argument taken by its constructor. :arg from_dd: a descriptor for the incoming degrees of freedom. This can be a :class:`~pytential.symbolic.primitives.DOFDescriptor` or an identifier that can be transformed into one by :func:`~pytential.symbolic.primitives.as_dofdesc`. :arg to_dd: a descriptor for the outgoing degrees of freedom. :return: a :class:`DOFConnection` transporting between the two kinds of DOF vectors. """ from pytential import sym from_dd = sym.as_dofdesc(from_dd) to_dd = sym.as_dofdesc(to_dd) from pytential import GeometryCollection if not isinstance(places, GeometryCollection): places = GeometryCollection(places) lpot = places.get_geometry(from_dd.geometry) from_discr = places.get_discretization(from_dd.geometry, from_dd.discr_stage) to_discr = places.get_discretization(to_dd.geometry, to_dd.discr_stage) if from_dd.geometry != to_dd.geometry: raise ValueError("cannot interpolate between different geometries") if from_dd.granularity is not sym.GRANULARITY_NODE: raise ValueError("can only interpolate from `GRANULARITY_NODE`") connections = [] if from_dd.discr_stage is not to_dd.discr_stage: from pytential.qbx import QBXLayerPotentialSource if not isinstance(lpot, QBXLayerPotentialSource): raise ValueError("can only interpolate on a " "`QBXLayerPotentialSource`") if to_dd.discr_stage is not sym.QBX_SOURCE_QUAD_STAGE2: # TODO: can probably extend this to project from a QUAD_STAGE2 # using L2ProjectionInverseDiscretizationConnection raise ValueError("can only interpolate to " "`QBX_SOURCE_QUAD_STAGE2`") # FIXME: would be nice if these were ordered by themselves stage_name_to_index_map = { None: 0, sym.QBX_SOURCE_STAGE1: 1, sym.QBX_SOURCE_STAGE2: 2, sym.QBX_SOURCE_QUAD_STAGE2: 3 } stage_index_to_name_map = { i: name for name, i in stage_name_to_index_map.items() } from_stage = stage_name_to_index_map[from_dd.discr_stage] to_stage = stage_name_to_index_map[to_dd.discr_stage] for istage in range(from_stage, to_stage): conn = places._get_conn_from_cache( from_dd.geometry, stage_index_to_name_map[istage], stage_index_to_name_map[istage + 1]) connections.append(conn) if from_dd.granularity is not to_dd.granularity: if to_dd.granularity is sym.GRANULARITY_NODE: pass elif to_dd.granularity is sym.GRANULARITY_CENTER: connections.append(CenterGranularityConnection(to_discr)) elif to_dd.granularity is sym.GRANULARITY_ELEMENT: raise ValueError("Creating a connection to element granularity " "is not allowed. Use Elementwise{Max,Min,Sum}.") else: raise ValueError(f"invalid to_dd granularity: {to_dd.granularity}") if from_dd.granularity is not to_dd.granularity: conn = DOFConnection(connections, from_dd=from_dd, to_dd=to_dd) else: from meshmode.discretization.connection import \ ChainedDiscretizationConnection conn = ChainedDiscretizationConnection(connections, from_discr=from_discr) return conn
def connection_from_dds(self, from_dd, to_dd): from_dd = sym.as_dofdesc(from_dd) to_dd = sym.as_dofdesc(to_dd) to_qtag = to_dd.quadrature_tag if (not from_dd.is_volume() and from_dd.quadrature_tag == to_dd.quadrature_tag and to_dd.domain_tag is sym.FACE_RESTR_ALL): faces_conn = self.connection_from_dds( sym.DOFDesc("vol"), sym.DOFDesc(from_dd.domain_tag)) from meshmode.discretization.connection import \ make_face_to_all_faces_embedding return make_face_to_all_faces_embedding( faces_conn, self.discr_from_dd(to_dd), self.discr_from_dd(from_dd)) # {{{ simplify domain + qtag change into chained if (from_dd.domain_tag != to_dd.domain_tag and from_dd.quadrature_tag is sym.QTAG_NONE and to_dd.quadrature_tag is not sym.QTAG_NONE): from meshmode.discretization.connection import \ ChainedDiscretizationConnection intermediate_dd = sym.DOFDesc(to_dd.domain_tag) return ChainedDiscretizationConnection([ # first change domain self.connection_from_dds(from_dd, intermediate_dd), # then go to quad grid self.connection_from_dds(intermediate_dd, to_dd) ]) # }}} # {{{ generic to-quad if (from_dd.domain_tag == to_dd.domain_tag and from_dd.quadrature_tag is sym.QTAG_NONE and to_dd.quadrature_tag is not sym.QTAG_NONE): from meshmode.discretization.connection.same_mesh import \ make_same_mesh_connection return make_same_mesh_connection(self.discr_from_dd(to_dd), self.discr_from_dd(from_dd)) # }}} if from_dd.quadrature_tag is not sym.QTAG_NONE: raise ValueError("cannot interpolate *from* a " "(non-interpolatory) quadrature grid") assert to_qtag is sym.QTAG_NONE if from_dd.is_volume(): if to_dd.domain_tag is sym.FACE_RESTR_ALL: return self._all_faces_volume_connection() if to_dd.domain_tag is sym.FACE_RESTR_INTERIOR: return self._interior_faces_connection() elif to_dd.is_boundary(): assert from_dd.quadrature_tag is sym.QTAG_NONE return self._boundary_connection(to_dd.domain_tag) elif to_dd.is_volume(): from meshmode.discretization.connection import \ make_same_mesh_connection to_discr = self._quad_volume_discr(to_dd.quadrature_tag) from_discr = self._volume_discr return make_same_mesh_connection(to_discr, from_discr) else: raise ValueError("cannot interpolate from volume to: " + str(to_dd)) else: raise ValueError("cannot interpolate from: " + str(from_dd))
def test_chained_to_direct(actx_factory, ndim, chain_type, nelements=128, visualize=False): import time from meshmode.discretization.connection.chained import \ flatten_chained_connection actx = actx_factory() discr = create_discretization(actx, ndim, nelements=nelements) connections = [] if chain_type == 1: conn = create_refined_connection(actx, discr) connections.append(conn) conn = create_refined_connection(actx, conn.to_discr) connections.append(conn) elif chain_type == 2: conn = create_refined_connection(actx, discr) connections.append(conn) conn = create_refined_connection(actx, conn.to_discr) connections.append(conn) conn = create_refined_connection(actx, conn.to_discr) connections.append(conn) elif chain_type == 3 and ndim == 3: conn = create_refined_connection(actx, discr, threshold=np.inf) connections.append(conn) conn = create_face_connection(actx, conn.to_discr) connections.append(conn) else: raise ValueError("unknown test case") from meshmode.discretization.connection import \ ChainedDiscretizationConnection chained = ChainedDiscretizationConnection(connections) t_start = time.time() direct = flatten_chained_connection(actx, chained) t_end = time.time() if visualize: print("[TIME] Flatten: {:.5e}".format(t_end - t_start)) if chain_type < 3: to_element_indices = np.full(direct.to_discr.mesh.nelements, 0, dtype=np.int64) for grp in direct.groups: for batch in grp.batches: for i in batch.to_element_indices.get(actx.queue): to_element_indices[i] += 1 assert np.min(to_element_indices) > 0 def f(x): from functools import reduce return 0.1 * reduce(lambda x, y: x * actx.np.sin(5 * y), x) x = thaw(actx, connections[0].from_discr.nodes()) fx = f(x) t_start = time.time() f1 = actx.to_numpy(flatten(direct(fx))) t_end = time.time() if visualize: print("[TIME] Direct: {:.5e}".format(t_end - t_start)) t_start = time.time() f2 = actx.to_numpy(flatten(chained(fx))) t_end = time.time() if visualize: print("[TIME] Chained: {:.5e}".format(t_end - t_start)) if visualize and ndim == 2: import matplotlib.pyplot as pt pt.figure(figsize=(10, 8), dpi=300) pt.plot(f1, label="Direct") pt.plot(f2, label="Chained") pt.ylim([np.min(f2) - 0.1, np.max(f2) + 0.1]) pt.legend() pt.savefig("test_chained_to_direct.png") pt.clf() assert np.allclose(f1, f2)
def connection_from_dds(self, from_dd, to_dd): """Provides a mapping (connection) from one discretization to another, e.g. from the volume to the boundary, or from the base to the an overintegrated quadrature discretization, or from a nodal representation to a modal representation. :arg from_dd: a :class:`~grudge.dof_desc.DOFDesc`, or a value convertible to one. :arg to_dd: a :class:`~grudge.dof_desc.DOFDesc`, or a value convertible to one. """ from_dd = as_dofdesc(from_dd) to_dd = as_dofdesc(to_dd) to_discr_tag = to_dd.discretization_tag from_discr_tag = from_dd.discretization_tag # {{{ mapping between modal and nodal representations if (from_discr_tag is DISCR_TAG_MODAL and to_discr_tag is not DISCR_TAG_MODAL): return self._modal_to_nodal_connection(to_dd) if (to_discr_tag is DISCR_TAG_MODAL and from_discr_tag is not DISCR_TAG_MODAL): return self._nodal_to_modal_connection(from_dd) # }}} assert (to_discr_tag is not DISCR_TAG_MODAL and from_discr_tag is not DISCR_TAG_MODAL) if (not from_dd.is_volume() and from_discr_tag == to_discr_tag and to_dd.domain_tag is FACE_RESTR_ALL): faces_conn = self.connection_from_dds(DOFDesc("vol"), DOFDesc(from_dd.domain_tag)) from meshmode.discretization.connection import \ make_face_to_all_faces_embedding return make_face_to_all_faces_embedding( self._setup_actx, faces_conn, self.discr_from_dd(to_dd), self.discr_from_dd(from_dd)) # {{{ simplify domain + discr_tag change into chained if (from_dd.domain_tag != to_dd.domain_tag and from_discr_tag is DISCR_TAG_BASE and to_discr_tag is not DISCR_TAG_BASE): from meshmode.discretization.connection import \ ChainedDiscretizationConnection intermediate_dd = DOFDesc(to_dd.domain_tag) return ChainedDiscretizationConnection([ # first change domain self.connection_from_dds(from_dd, intermediate_dd), # then go to quad grid self.connection_from_dds(intermediate_dd, to_dd) ]) # }}} # {{{ generic to-quad # DISCR_TAG_MODAL is handled above if (from_dd.domain_tag == to_dd.domain_tag and from_discr_tag is DISCR_TAG_BASE and to_discr_tag is not DISCR_TAG_BASE): from meshmode.discretization.connection.same_mesh import \ make_same_mesh_connection return make_same_mesh_connection(self._setup_actx, self.discr_from_dd(to_dd), self.discr_from_dd(from_dd)) # }}} if from_discr_tag is not DISCR_TAG_BASE: raise ValueError("cannot interpolate *from* a " "(non-interpolatory) quadrature grid") assert to_discr_tag is DISCR_TAG_BASE if from_dd.is_volume(): if to_dd.domain_tag is FACE_RESTR_ALL: return self._all_faces_volume_connection() if to_dd.domain_tag is FACE_RESTR_INTERIOR: return self._interior_faces_connection() elif to_dd.is_boundary_or_partition_interface(): assert from_discr_tag is DISCR_TAG_BASE return self._boundary_connection(to_dd.domain_tag.tag) elif to_dd.is_volume(): from meshmode.discretization.connection import \ make_same_mesh_connection to_discr = self._discr_tag_volume_discr(to_discr_tag) from_discr = self._volume_discr return make_same_mesh_connection(self._setup_actx, to_discr, from_discr) else: raise ValueError("cannot interpolate from volume to: " + str(to_dd)) else: raise ValueError("cannot interpolate from: " + str(from_dd))
def refine_for_global_qbx(lpot_source, wrangler, group_factory, kernel_length_scale=None, force_stage2_uniform_refinement_rounds=None, scaled_max_curvature_threshold=None, debug=None, maxiter=None, visualize=None, expansion_disturbance_tolerance=None, refiner=None): """ Entry point for calling the refiner. :arg lpot_source: An instance of :class:`QBXLayerPotentialSource`. :arg wrangler: An instance of :class:`RefinerWrangler`. :arg group_factory: An instance of :class:`meshmode.mesh.discretization.ElementGroupFactory`. Used for discretizing the coarse refined mesh. :arg kernel_length_scale: The kernel length scale, or *None* if not applicable. All panels are refined to below this size. :arg maxiter: The maximum number of refiner iterations. :returns: A tuple ``(lpot_source, *conn*)`` where ``lpot_source`` is the refined layer potential source, and ``conn`` is a :class:`meshmode.discretization.connection.DiscretizationConnection` going from the original mesh to the refined mesh. """ if maxiter is None: maxiter = 10 if debug is None: # FIXME: Set debug=False by default once everything works. debug = True if expansion_disturbance_tolerance is None: expansion_disturbance_tolerance = 0.025 if force_stage2_uniform_refinement_rounds is None: force_stage2_uniform_refinement_rounds = 0 # TODO: Stop doing redundant checks by avoiding panels which no longer need # refinement. from meshmode.mesh.refinement import RefinerWithoutAdjacency from meshmode.discretization.connection import ( ChainedDiscretizationConnection, make_same_mesh_connection) if refiner is not None: assert refiner.get_current_mesh() == lpot_source.density_discr.mesh else: # We may be handed a mesh that's already non-conforming, we don't rely # on adjacency, and the no-adjacency refiner is faster. refiner = RefinerWithoutAdjacency(lpot_source.density_discr.mesh) connections = [] # {{{ first stage refinement def visualize_refinement(niter, stage_nr, stage_name, flags): if not visualize: return if stage_nr == 1: discr = lpot_source.density_discr elif stage_nr == 2: discr = lpot_source.stage2_density_discr else: raise ValueError("unexpected stage number") flags = flags.get() logger.info("for stage %s: splitting %d/%d stage-%d elements", stage_name, np.sum(flags), discr.mesh.nelements, stage_nr) from meshmode.discretization.visualization import make_visualizer vis = make_visualizer(wrangler.queue, discr, 3) assert len(flags) == discr.mesh.nelements flags = flags.astype(np.bool) nodes_flags = np.zeros(discr.nnodes) for grp in discr.groups: meg = grp.mesh_el_group grp.view(nodes_flags)[flags[meg.element_nr_base:meg.nelements + meg.element_nr_base]] = 1 nodes_flags = cl.array.to_device(wrangler.queue, nodes_flags) vis_data = [ ("refine_flags", nodes_flags), ] if 0: from pytential import sym, bind bdry_normals = bind(discr, sym.normal(discr.ambient_dim))( wrangler.queue).as_vector(dtype=object) vis_data.append(("bdry_normals", bdry_normals), ) vis.write_vtk_file("refinement-%s-%03d.vtu" % (stage_name, niter), vis_data) def warn_max_iterations(): from warnings import warn warn( "QBX layer potential source refiner did not terminate " "after %d iterations (the maximum). " "You may pass 'visualize=True' to with_refinement() " "to see what area of the geometry is causing trouble. " "If the issue is disturbance of expansion disks, you may " "pass a slightly increased value (currently: %g) for " "_expansion_disturbance_tolerance in with_refinement(). " "As a last resort, " "you may use Python's warning filtering mechanism to " "not treat this warning as an error. " "The criteria triggering refinement in each iteration " "were: %s. " % (len(violated_criteria), expansion_disturbance_tolerance, ", ".join("%d: %s" % (i + 1, vc_text) for i, vc_text in enumerate(violated_criteria))), RefinerNotConvergedWarning) violated_criteria = [] iter_violated_criteria = ["start"] niter = 0 while iter_violated_criteria: iter_violated_criteria = [] niter += 1 if niter > maxiter: warn_max_iterations() break refine_flags = make_empty_refine_flags(wrangler.queue, lpot_source) if kernel_length_scale is not None: with ProcessLogger( logger, "checking kernel length scale to panel size ratio"): from pytential import bind, sym quad_resolution = bind( lpot_source, sym._quad_resolution(lpot_source.ambient_dim, dofdesc=sym.GRANULARITY_ELEMENT))( wrangler.queue) violates_kernel_length_scale = \ wrangler.check_element_prop_threshold( element_property=quad_resolution, threshold=kernel_length_scale, refine_flags=refine_flags, debug=debug) if violates_kernel_length_scale: iter_violated_criteria.append("kernel length scale") visualize_refinement(niter, 1, "kernel-length-scale", refine_flags) if scaled_max_curvature_threshold is not None: with ProcessLogger(logger, "checking scaled max curvature threshold"): from pytential import sym, bind scaled_max_curv = bind( lpot_source, sym.ElementwiseMax( sym._scaled_max_curvature(lpot_source.ambient_dim), dofdesc=sym.GRANULARITY_ELEMENT))(wrangler.queue) violates_scaled_max_curv = \ wrangler.check_element_prop_threshold( element_property=scaled_max_curv, threshold=scaled_max_curvature_threshold, refine_flags=refine_flags, debug=debug) if violates_scaled_max_curv: iter_violated_criteria.append("curvature") visualize_refinement(niter, 1, "curvature", refine_flags) if not iter_violated_criteria: # Only start building trees once the simple length-based criteria # are happy. # Build tree and auxiliary data. # FIXME: The tree should not have to be rebuilt at each iteration. tree = wrangler.build_tree(lpot_source) peer_lists = wrangler.find_peer_lists(tree) has_disturbed_expansions = \ wrangler.check_expansion_disks_undisturbed_by_sources( lpot_source, tree, peer_lists, expansion_disturbance_tolerance, refine_flags, debug) if has_disturbed_expansions: iter_violated_criteria.append("disturbed expansions") visualize_refinement(niter, 1, "disturbed-expansions", refine_flags) del tree del peer_lists if iter_violated_criteria: violated_criteria.append(" and ".join(iter_violated_criteria)) conn = wrangler.refine(lpot_source.density_discr, refiner, refine_flags, group_factory, debug) connections.append(conn) lpot_source = lpot_source.copy(density_discr=conn.to_discr) del refine_flags # }}} # {{{ second stage refinement iter_violated_criteria = ["start"] niter = 0 fine_connections = [] stage2_density_discr = lpot_source.density_discr while iter_violated_criteria: iter_violated_criteria = [] niter += 1 if niter > maxiter: warn_max_iterations() break # Build tree and auxiliary data. # FIXME: The tree should not have to be rebuilt at each iteration. tree = wrangler.build_tree(lpot_source, use_stage2_discr=True) peer_lists = wrangler.find_peer_lists(tree) refine_flags = make_empty_refine_flags(wrangler.queue, lpot_source, use_stage2_discr=True) has_insufficient_quad_res = \ wrangler.check_sufficient_source_quadrature_resolution( lpot_source, tree, peer_lists, refine_flags, debug) if has_insufficient_quad_res: iter_violated_criteria.append("insufficient quadrature resolution") visualize_refinement(niter, 2, "quad-resolution", refine_flags) if iter_violated_criteria: violated_criteria.append(" and ".join(iter_violated_criteria)) conn = wrangler.refine(stage2_density_discr, refiner, refine_flags, group_factory, debug) stage2_density_discr = conn.to_discr fine_connections.append(conn) lpot_source = lpot_source.copy( to_refined_connection=ChainedDiscretizationConnection( fine_connections)) del tree del refine_flags del peer_lists for round in range(force_stage2_uniform_refinement_rounds): conn = wrangler.refine( stage2_density_discr, refiner, np.ones(stage2_density_discr.mesh.nelements, dtype=np.bool), group_factory, debug) stage2_density_discr = conn.to_discr fine_connections.append(conn) lpot_source = lpot_source.copy( to_refined_connection=ChainedDiscretizationConnection( fine_connections)) # }}} lpot_source = lpot_source.copy(debug=debug, _refined_for_global_qbx=True) if len(connections) == 0: # FIXME: This is inefficient connection = make_same_mesh_connection(lpot_source.density_discr, lpot_source.density_discr) else: connection = ChainedDiscretizationConnection(connections) return lpot_source, connection
def _refine_qbx_stage1(lpot_source, density_discr, wrangler, group_factory, kernel_length_scale=None, scaled_max_curvature_threshold=None, expansion_disturbance_tolerance=None, maxiter=None, debug=None, visualize=False): from pytential import bind, sym from meshmode.discretization.connection import ChainedDiscretizationConnection if lpot_source._disable_refinement: return density_discr, ChainedDiscretizationConnection( [], from_discr=density_discr) from meshmode.mesh.refinement import RefinerWithoutAdjacency refiner = RefinerWithoutAdjacency(density_discr.mesh) # TODO: Stop doing redundant checks by avoiding panels which no longer need # refinement. connections = [] violated_criteria = [] iter_violated_criteria = ["start"] niter = 0 actx = wrangler.array_context stage1_density_discr = density_discr while iter_violated_criteria: iter_violated_criteria = [] niter += 1 if niter > maxiter: _warn_max_iterations(violated_criteria, expansion_disturbance_tolerance) break refine_flags = make_empty_refine_flags(wrangler.queue, stage1_density_discr) if kernel_length_scale is not None: with ProcessLogger( logger, "checking kernel length scale to panel size ratio"): quad_resolution = bind( stage1_density_discr, sym._quad_resolution( stage1_density_discr.ambient_dim, dofdesc=sym.GRANULARITY_ELEMENT))(actx) violates_kernel_length_scale = \ wrangler.check_element_prop_threshold( element_property=quad_resolution, threshold=kernel_length_scale, refine_flags=refine_flags, debug=debug) if violates_kernel_length_scale: iter_violated_criteria.append("kernel length scale") _visualize_refinement(actx, stage1_density_discr, niter, 1, "kernel-length-scale", refine_flags, visualize=visualize) if scaled_max_curvature_threshold is not None: with ProcessLogger(logger, "checking scaled max curvature threshold"): scaled_max_curv = bind( stage1_density_discr, sym.ElementwiseMax(sym._scaled_max_curvature( stage1_density_discr.ambient_dim), dofdesc=sym.GRANULARITY_ELEMENT))(actx) violates_scaled_max_curv = \ wrangler.check_element_prop_threshold( element_property=scaled_max_curv, threshold=scaled_max_curvature_threshold, refine_flags=refine_flags, debug=debug) if violates_scaled_max_curv: iter_violated_criteria.append("curvature") _visualize_refinement(wrangler.queue, stage1_density_discr, niter, 1, "curvature", refine_flags, visualize=visualize) if not iter_violated_criteria: # Only start building trees once the simple length-based criteria # are happy. places = _make_temporary_collection( lpot_source, stage1_density_discr=stage1_density_discr) # Build tree and auxiliary data. # FIXME: The tree should not have to be rebuilt at each iteration. tree = wrangler.build_tree( places, sources_list=[places.auto_source.geometry]) peer_lists = wrangler.find_peer_lists(tree) has_disturbed_expansions = \ wrangler.check_expansion_disks_undisturbed_by_sources( stage1_density_discr, tree, peer_lists, expansion_disturbance_tolerance, refine_flags, debug) if has_disturbed_expansions: iter_violated_criteria.append("disturbed expansions") _visualize_refinement(wrangler.queue, stage1_density_discr, niter, 1, "disturbed-expansions", refine_flags, visualize=visualize) del tree del peer_lists if iter_violated_criteria: violated_criteria.append(" and ".join(iter_violated_criteria)) conn = wrangler.refine(stage1_density_discr, refiner, refine_flags, group_factory, debug) stage1_density_discr = conn.to_discr connections.append(conn) del refine_flags conn = ChainedDiscretizationConnection(connections, from_discr=density_discr) return stage1_density_discr, conn