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