15 #include "libmesh/elem.h" 16 #include "libmesh/mesh_modification.h" 26 params.
addClassDescription(
"Mesh generator which coarsens one or more blocks in an existing " 27 "mesh. The coarsening algorithm works best for regular meshes.");
28 params.
addRequiredParam<MeshGeneratorName>(
"input",
"Input mesh to coarsen");
30 "The list of blocks to be coarsened");
33 "Maximum amount of times to coarsen elements in each block. See 'block' for indexing");
35 "A point inside the element to start the coarsening from");
39 "maximum_volume_ratio",
41 "maximum_volume_ratio > 0",
42 "Maximum allowed volume ratio between two fine elements to propagate " 43 "the coarsening front through a side");
47 "Whether to make the mesh generator output details of its actions on the console");
48 params.
addParam<
bool>(
"check_for_non_conformal_output_mesh",
50 "Whether to check the entire mesh for non-conformal nodes indicating that " 51 "the coarsening operation has failed to produce a conformal mesh");
57 _input(getMesh(
"input")),
58 _block(getParam<
std::vector<SubdomainName>>(
"block")),
59 _coarsening(getParam<
std::vector<unsigned
int>>(
"coarsening")),
60 _starting_point(getParam<Point>(
"starting_point")),
61 _max_vol_ratio(getParam<
Real>(
"maximum_volume_ratio")),
62 _verbose(getParam<bool>(
"verbose")),
63 _check_output_mesh_for_nonconformality(getParam<bool>(
"check_for_non_conformal_output_mesh"))
66 paramError(
"coarsening",
"The blocks and coarsening parameter vectors should be the same size");
69 std::unique_ptr<MeshBase>
73 const auto block_ids =
75 const std::set<SubdomainID> block_ids_set(block_ids.begin(), block_ids.end());
78 std::set<SubdomainID> mesh_blocks;
79 _input->subdomain_ids(mesh_blocks);
81 for (std::size_t i = 0; i < block_ids.size(); ++i)
85 getParam<std::vector<SubdomainName>>(
"block")[i],
86 "' was not found within the mesh");
89 for (
const auto & elem :
_input->active_subdomain_set_elements_ptr_range(block_ids_set))
91 if (elem->type() !=
QUAD4 && elem->type() !=
HEX8)
93 "The input mesh contains an unsupported element type '" +
95 std::to_string(elem->subdomain_id()));
98 std::unique_ptr<MeshBase>
mesh = std::move(
_input);
99 if (!
mesh->is_serial())
100 paramError(
"input",
"Input mesh must not be distributed");
107 paramError(
"starting_point",
"No element was found at that point");
110 if (block_ids[i] == start_elem->subdomain_id() &&
_coarsening[i] != max_c)
111 mooseError(
"The starting element must be in the block set to be coarsened the most.\n" 112 "Starting element is in block ",
113 start_elem->subdomain_id(),
114 " set to be coarsened ",
116 " times but the max coarsening required is ",
120 if (max_c > 0 && !
mesh->is_prepared())
122 mesh->prepare_for_use();
128 mesh_ptr->set_isnt_prepared();
131 MeshTools::Modification::orient_elements(*mesh_ptr);
136 mesh_ptr->prepare_for_use();
137 unsigned int num_nonconformal_nodes = 0;
139 mesh_ptr,
_console, 10, TOLERANCE, num_nonconformal_nodes);
140 if (num_nonconformal_nodes)
141 mooseError(
"Coarsened mesh has non-conformal nodes. The coarsening process likely failed to " 142 "form a uniform paving of coarsened elements. Number of non-conformal nodes: " +
148 std::unique_ptr<MeshBase>
150 std::unique_ptr<MeshBase> & mesh,
151 const std::vector<unsigned int> & coarsening,
152 const unsigned int max,
153 unsigned int coarse_step)
155 if (coarse_step ==
max)
159 if (!
mesh->is_prepared())
160 mesh->prepare_for_use();
163 std::unique_ptr<MeshBase> mesh_return;
164 int max_num_coarsened = -1;
169 for (
const auto & start_node_index : base_start_elem->node_index_range())
172 _console <<
"Step " << coarse_step + 1 <<
" coarsening attempt #" << start_node_index
173 <<
"\nUsing node " << *base_start_elem->node_ptr(start_node_index)
174 <<
" as the interior node of the coarse element." << std::endl;
177 auto mesh_copy =
mesh->clone();
181 auto start_elem = mesh_copy->elem_ptr(base_start_elem->id());
182 mooseAssert(start_elem,
"Should have a real elem pointer");
183 mooseAssert(start_elem->active(),
"Starting element must be active");
185 auto start_node = start_elem->node_ptr(start_node_index);
186 mooseAssert(start_node,
"Starting node should exist");
189 auto cmp = [](std::pair<Elem *, Node *> a, std::pair<Elem *, Node *> b)
193 Point sorting_direction(1, 1, 1);
195 (a.first->vertex_average() - b.first->vertex_average()) * sorting_direction;
203 return a.first->id() > b.first->id();
210 std::set<std::pair<Elem *, Node *>, decltype(cmp)> candidate_pairs(cmp);
211 candidate_pairs.insert(std::make_pair(start_elem, start_node));
214 std::set<Elem *> coarse_elems;
216 while (candidate_pairs.size() > 0)
218 Elem * current_elem = candidate_pairs.begin()->first;
219 Node * interior_node = candidate_pairs.begin()->second;
220 mooseAssert(current_elem,
"Null candidate element pointer");
221 mooseAssert(interior_node,
"Null candidate node pointer");
222 const auto current_node_index = current_elem->get_node_index(interior_node);
224 const auto ref_node =
225 current_elem->node_ptr(current_node_index == 0 ? 1 : current_node_index - 1);
226 mooseAssert(ref_node,
"Should have a real node pointer");
227 candidate_pairs.erase(candidate_pairs.begin());
229 const auto elem_type = current_elem->type();
234 if (!current_elem->is_vertex(current_node_index))
238 if (current_elem->level() > 0)
239 mooseError(
"H-refined meshes cannot be coarsened with this mesh generator. Use the " 240 "[Adaptivity] block to coarsen them.");
243 std::vector<const Node *> tentative_coarse_nodes;
244 std::set<const Elem *> fine_elements_const;
246 *interior_node, *ref_node, *current_elem, tentative_coarse_nodes, fine_elements_const);
252 bool go_to_next_candidate =
false;
254 for (
auto elem : fine_elements_const)
255 if (elem && elem->type() != elem_type)
256 go_to_next_candidate =
true;
259 const auto common_subdomain_id = current_elem->subdomain_id();
260 for (
auto elem : fine_elements_const)
261 if (elem && elem->subdomain_id() != common_subdomain_id)
262 go_to_next_candidate =
true;
265 for (
const auto & check_node : tentative_coarse_nodes)
266 if (check_node ==
nullptr)
267 go_to_next_candidate =
true;
269 if (go_to_next_candidate)
273 auto cmp_elem = [](Elem * a, Elem * b) {
return a->id() - b->id(); };
274 std::set<Elem *, decltype(cmp_elem)> fine_elements(cmp_elem);
275 for (
const auto elem_ptr : fine_elements_const)
276 fine_elements.insert(mesh_copy->elem_ptr(elem_ptr->id()));
279 std::unique_ptr<Elem> parent = Elem::build(Elem::first_order_equivalent_type(elem_type));
280 parent->subdomain_id() = common_subdomain_id;
281 auto parent_ptr = mesh_copy->add_elem(parent.release());
282 coarse_elems.insert(parent_ptr);
287 parent_ptr->set_node(i, mesh_copy->node_ptr(tentative_coarse_nodes[i]->id()));
291 for (
const auto side_index :
make_range(parent_ptr->n_sides()))
296 const auto coarse_node = parent_ptr->side_ptr(side_index)->node_ptr(0);
297 mooseAssert(coarse_node,
298 "We should have a node on coarse side " + std::to_string(side_index));
302 Elem * fine_el =
nullptr;
303 for (
const auto & fine_elem : fine_elements)
306 for (
const auto & fine_elem_node : fine_elem->node_ref_range())
316 mooseAssert(fine_el,
"We should have found a fine element for the next candidate");
317 const Real fine_el_volume = fine_el->volume();
331 unsigned int fine_side_index = 0;
332 const auto coarse_side_center = parent_ptr->side_ptr(side_index)->vertex_average();
335 for (
const auto side_index :
make_range(fine_el->n_sides()))
341 (fine_el->side_ptr(side_index)->vertex_average() - coarse_side_center).
norm_sq();
342 if (min_distance > dist)
345 fine_side_index = side_index;
349 "We should have found a side");
355 fine_el->side_ptr(fine_side_index)->vertex_average() +
357 (fine_el->side_ptr(fine_side_index)->vertex_average() - fine_el->vertex_average());
358 auto pl = mesh_copy->sub_point_locator();
359 pl->enable_out_of_mesh_mode();
360 auto const_neighbor = (*pl)(offset_point);
361 pl->disable_out_of_mesh_mode();
368 auto neighbor_fine_elem = mesh_copy->elem_ptr(const_neighbor->id());
371 if (fine_elements.find(neighbor_fine_elem) != fine_elements.end())
376 const auto neighbor_coarse_node_index = neighbor_fine_elem->get_node_index(coarse_node);
382 " does not seem shared with any element other than the coarse element. " 383 "Is the mesh will stitched? Or are there non-conformalities?");
387 neighbor_fine_elem->type(), neighbor_coarse_node_index);
388 auto neighbor_interior_node = neighbor_fine_elem->node_ptr(opposite_node_index);
391 if (coarse_elems.find(neighbor_fine_elem) == coarse_elems.end())
396 if (block_ids[i] == neighbor_fine_elem->subdomain_id() &&
397 coarsening[i] >
max - coarse_step - 1 &&
400 candidate_pairs.insert(std::make_pair(neighbor_fine_elem, neighbor_interior_node));
407 for (
auto & fine_elem : fine_elements)
414 mesh_copy->delete_elem(fine_elem);
417 for (
auto iter = candidate_pairs.begin(); iter != candidate_pairs.end();)
419 if (iter->first == fine_elem)
421 iter = candidate_pairs.erase(iter);
429 mesh_copy->contract();
431 mesh_copy->prepare_for_use();
440 _console <<
"Step " << coarse_step + 1 <<
" attempt #" << start_node_index <<
" created " 441 << coarse_elems.size() <<
" coarse elements." << std::endl;
442 if (
int(coarse_elems.size()) > max_num_coarsened)
444 mesh_return = std::move(mesh_copy);
445 max_num_coarsened = coarse_elems.size();
449 _console <<
"Step " << coarse_step + 1 <<
" created " << max_num_coarsened
450 <<
" coarsened elements in its most successful attempt." << std::endl;
MetaPhysicL::DualNumber< V, D, asd > abs(const MetaPhysicL::DualNumber< V, D, asd > &a)
const bool _verbose
Whether the mesh generator should be verbose to the console.
const bool _check_output_mesh_for_nonconformality
Whether to check the output mesh for non-conformality.
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether two variables are equal within an absolute tolerance.
const unsigned int invalid_uint
const std::vector< unsigned int > _coarsening
The amount of times to coarsen each block, corresponding to their index in 'block'.
CoarsenBlockGenerator(const InputParameters ¶meters)
void mooseInfoRepeated(Args &&... args)
Emit an informational message with the given stringified, concatenated args.
std::unique_ptr< T_DEST, T_DELETER > dynamic_pointer_cast(std::unique_ptr< T_SRC, T_DELETER > &src)
These are reworked from https://stackoverflow.com/a/11003103.
std::vector< subdomain_id_type > getSubdomainIDs(const libMesh::MeshBase &mesh, const std::vector< SubdomainName > &subdomain_name)
Get the associated subdomainIDs for the subdomain names that are passed in.
bool getFineElementsFromInteriorNode(const libMesh::Node &interior_node, const libMesh::Node &reference_node, const libMesh::Elem &elem, std::vector< const libMesh::Node *> &tentative_coarse_nodes, std::set< const libMesh::Elem *> &fine_elements)
Utility routine to gather vertex nodes for, and elements contained in, for a coarse QUAD or HEX eleme...
virtual std::unique_ptr< MeshBase > recursiveCoarsen(const std::vector< subdomain_id_type > &block_ids, std::unique_ptr< MeshBase > &mesh, const std::vector< unsigned int > &coarsening, const unsigned int max, unsigned int coarse_step)
The actual function coarsening the blocks.
auto max(const L &left, const R &right)
const Point _starting_point
The location on the mesh to start the coarsening from.
bool hasSubdomainID(const MeshBase &input_mesh, const SubdomainID &id)
Whether a particular subdomain ID exists in the mesh.
MeshGenerator for coarsening one or more blocks.
const T & getParam(const std::string &name) const
Retrieve a parameter for the object.
void paramError(const std::string ¶m, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
static InputParameters validParams()
std::string stringify(const T &t)
conversion to string
const Real _max_vol_ratio
Maximum volume ratio between a neighbor and an element to consider the neighbor as a candidate for co...
registerMooseObject("MooseApp", CoarsenBlockGenerator)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::unique_ptr< MeshBase > & _input
Input mesh to coarsen.
IntRange< T > make_range(T beg, T end)
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type.
virtual std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
const ConsoleStream _console
An instance of helper class to write streams to the Console objects.
void checkNonConformalMesh(const std::unique_ptr< libMesh::MeshBase > &mesh, const ConsoleStream &console, const unsigned int num_outputs, const Real conformality_tol, unsigned int &num_nonconformal_nodes)
MeshGenerators are objects that can modify or add to an existing mesh.
void ErrorVector unsigned int
auto index_range(const T &sizable)
unsigned int getOppositeNodeIndex(libMesh::ElemType elem_type, unsigned int node_index)
Utility routine to get the index of the node opposite, in the element, to the node of interest...
bool absoluteFuzzyGreaterThan(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether a variable is greater than another variable within an absolute tolerance...
static InputParameters validParams()
const std::vector< SubdomainName > _block
List of block(s) to coarsen.