73 const auto block_ids =
75 const std::set<SubdomainID> block_ids_set(block_ids.begin(), block_ids.end());
78 if (!
_input->preparation().has_cached_elem_data)
80 std::set<SubdomainID> mesh_blocks;
81 _input->subdomain_ids(mesh_blocks);
83 for (std::size_t i = 0; i < block_ids.size(); ++i)
87 getParam<std::vector<SubdomainName>>(
"block")[i],
88 "' was not found within the mesh");
91 for (
const auto & elem :
_input->active_subdomain_set_elements_ptr_range(block_ids_set))
93 if (elem->type() != QUAD4 && elem->type() != HEX8)
95 "The input mesh contains an unsupported element type '" +
97 std::to_string(elem->subdomain_id()));
100 std::unique_ptr<MeshBase>
mesh = std::move(
_input);
101 if (!
mesh->is_serial())
102 paramError(
"input",
"Input mesh must not be distributed");
109 paramError(
"starting_point",
"No element was found at that point");
111 for (
const auto i : index_range(block_ids))
112 if (block_ids[i] == start_elem->subdomain_id() &&
_coarsening[i] != max_c)
113 mooseError(
"The starting element must be in the block set to be coarsened the most.\n"
114 "Starting element is in block ",
115 start_elem->subdomain_id(),
116 " set to be coarsened ",
118 " times but the max coarsening required is ",
122 if (max_c > 0 && !
mesh->is_prepared())
124 mesh->prepare_for_use();
130 mesh_ptr->unset_is_prepared();
133 MeshTools::Modification::orient_elements(*mesh_ptr);
138 mesh_ptr->prepare_for_use();
139 unsigned int num_nonconformal_nodes = 0;
141 mesh_ptr,
_console, 10, TOLERANCE, num_nonconformal_nodes);
142 if (num_nonconformal_nodes)
143 mooseError(
"Coarsened mesh has non-conformal nodes. The coarsening process likely failed to "
144 "form a uniform paving of coarsened elements. Number of non-conformal nodes: " +
152 std::unique_ptr<MeshBase> & mesh,
153 const std::vector<unsigned int> & coarsening,
154 const unsigned int max,
155 unsigned int coarse_step)
157 if (coarse_step == max)
158 return dynamic_pointer_cast<MeshBase>(
mesh);
161 if (!
mesh->is_prepared())
162 mesh->prepare_for_use();
165 std::unique_ptr<MeshBase> mesh_return;
166 int max_num_coarsened = -1;
171 for (
const auto & start_node_index : base_start_elem->node_index_range())
174 _console <<
"Step " << coarse_step + 1 <<
" coarsening attempt #" << start_node_index
175 <<
"\nUsing node " << *base_start_elem->node_ptr(start_node_index)
176 <<
" as the interior node of the coarse element." << std::endl;
179 auto mesh_copy =
mesh->clone();
183 auto start_elem = mesh_copy->elem_ptr(base_start_elem->id());
184 mooseAssert(start_elem,
"Should have a real elem pointer");
185 mooseAssert(start_elem->active(),
"Starting element must be active");
187 auto start_node = start_elem->node_ptr(start_node_index);
188 mooseAssert(start_node,
"Starting node should exist");
191 auto cmp = [](std::pair<Elem *, Node *> a, std::pair<Elem *, Node *> b)
195 Point sorting_direction(1, 1, 1);
197 (a.first->vertex_average() - b.first->vertex_average()) * sorting_direction;
198 if (MooseUtils::absoluteFuzzyGreaterThan(sorting, 0))
200 else if (MooseUtils::absoluteFuzzyEqual(sorting, 0) &&
201 MooseUtils::absoluteFuzzyGreaterThan((*a.second - *b.second) * sorting_direction, 0))
205 return a.first->id() > b.first->id();
212 std::set<std::pair<Elem *, Node *>,
decltype(cmp)> candidate_pairs(cmp);
213 candidate_pairs.insert(std::make_pair(start_elem, start_node));
216 std::set<Elem *> coarse_elems;
218 while (candidate_pairs.size() > 0)
220 Elem * current_elem = candidate_pairs.begin()->first;
221 Node * interior_node = candidate_pairs.begin()->second;
222 mooseAssert(current_elem,
"Null candidate element pointer");
223 mooseAssert(interior_node,
"Null candidate node pointer");
224 const auto current_node_index = current_elem->get_node_index(interior_node);
226 const auto ref_node =
227 current_elem->node_ptr(current_node_index == 0 ? 1 : current_node_index - 1);
228 mooseAssert(ref_node,
"Should have a real node pointer");
229 candidate_pairs.erase(candidate_pairs.begin());
231 const auto elem_type = current_elem->type();
236 if (!current_elem->is_vertex(current_node_index))
240 if (current_elem->level() > 0)
241 mooseError(
"H-refined meshes cannot be coarsened with this mesh generator. Use the "
242 "[Adaptivity] block to coarsen them.");
245 std::vector<const Node *> tentative_coarse_nodes;
246 std::set<const Elem *> fine_elements_const;
248 *interior_node, *ref_node, *current_elem, tentative_coarse_nodes, fine_elements_const);
254 bool go_to_next_candidate =
false;
256 for (
auto elem : fine_elements_const)
257 if (elem && elem->type() != elem_type)
258 go_to_next_candidate =
true;
261 const auto common_subdomain_id = current_elem->subdomain_id();
262 for (
auto elem : fine_elements_const)
263 if (elem && elem->subdomain_id() != common_subdomain_id)
264 go_to_next_candidate =
true;
267 for (
const auto & check_node : tentative_coarse_nodes)
268 if (check_node ==
nullptr)
269 go_to_next_candidate =
true;
271 if (go_to_next_candidate)
275 auto cmp_elem = [](Elem * a, Elem * b) {
return a->id() - b->id(); };
276 std::set<Elem *,
decltype(cmp_elem)> fine_elements(cmp_elem);
277 for (
const auto elem_ptr : fine_elements_const)
278 fine_elements.insert(mesh_copy->elem_ptr(elem_ptr->id()));
281 std::unique_ptr<Elem> parent = Elem::build(Elem::first_order_equivalent_type(elem_type));
282 parent->subdomain_id() = common_subdomain_id;
283 auto parent_ptr = mesh_copy->add_elem(parent.release());
284 coarse_elems.insert(parent_ptr);
288 for (
auto i : index_range(tentative_coarse_nodes))
289 parent_ptr->set_node(i, mesh_copy->node_ptr(tentative_coarse_nodes[i]->id()));
293 for (
const auto side_index : make_range(parent_ptr->n_sides()))
298 const auto coarse_node = parent_ptr->side_ptr(side_index)->node_ptr(0);
299 mooseAssert(coarse_node,
300 "We should have a node on coarse side " + std::to_string(side_index));
304 Elem * fine_el =
nullptr;
305 for (
const auto & fine_elem : fine_elements)
308 for (
const auto & fine_elem_node : fine_elem->node_ref_range())
309 if (MooseUtils::absoluteFuzzyEqual((*coarse_node - fine_elem_node).norm_sq(), 0))
318 mooseAssert(fine_el,
"We should have found a fine element for the next candidate");
319 const Real fine_el_volume = fine_el->volume();
333 unsigned int fine_side_index = 0;
334 const auto coarse_side_center = parent_ptr->side_ptr(side_index)->vertex_average();
335 Real min_distance = std::numeric_limits<Real>::max();
337 for (
const auto side_index : make_range(fine_el->n_sides()))
343 (fine_el->side_ptr(side_index)->vertex_average() - coarse_side_center).norm_sq();
344 if (min_distance > dist)
347 fine_side_index = side_index;
350 mooseAssert(min_distance != std::numeric_limits<Real>::max(),
351 "We should have found a side");
357 fine_el->side_ptr(fine_side_index)->vertex_average() +
359 (fine_el->side_ptr(fine_side_index)->vertex_average() - fine_el->vertex_average());
360 auto pl = mesh_copy->sub_point_locator();
361 pl->enable_out_of_mesh_mode();
362 auto const_neighbor = (*pl)(offset_point);
363 pl->disable_out_of_mesh_mode();
370 auto neighbor_fine_elem = mesh_copy->elem_ptr(const_neighbor->id());
373 if (fine_elements.find(neighbor_fine_elem) != fine_elements.end())
378 const auto neighbor_coarse_node_index = neighbor_fine_elem->get_node_index(coarse_node);
384 " does not seem shared with any element other than the coarse element. "
385 "Is the mesh will stitched? Or are there non-conformalities?");
389 neighbor_fine_elem->type(), neighbor_coarse_node_index);
390 auto neighbor_interior_node = neighbor_fine_elem->node_ptr(opposite_node_index);
393 if (coarse_elems.find(neighbor_fine_elem) == coarse_elems.end())
397 for (
const auto i : index_range(block_ids))
398 if (block_ids[i] == neighbor_fine_elem->subdomain_id() &&
399 coarsening[i] > max - coarse_step - 1 &&
400 std::abs(neighbor_fine_elem->volume()) < std::abs(
_max_vol_ratio * fine_el_volume))
402 candidate_pairs.insert(std::make_pair(neighbor_fine_elem, neighbor_interior_node));
409 for (
auto & fine_elem : fine_elements)
416 mesh_copy->delete_elem(fine_elem);
419 for (
auto iter = candidate_pairs.begin(); iter != candidate_pairs.end();)
421 if (iter->first == fine_elem)
423 iter = candidate_pairs.erase(iter);
431 mesh_copy->contract();
433 mesh_copy->prepare_for_use();
442 _console <<
"Step " << coarse_step + 1 <<
" attempt #" << start_node_index <<
" created "
443 << coarse_elems.size() <<
" coarse elements." << std::endl;
444 if (
int(coarse_elems.size()) > max_num_coarsened)
446 mesh_return = std::move(mesh_copy);
447 max_num_coarsened = coarse_elems.size();
451 _console <<
"Step " << coarse_step + 1 <<
" created " << max_num_coarsened
452 <<
" coarsened elements in its most successful attempt." << std::endl;
454 return recursiveCoarsen(block_ids, mesh_return, coarsening, max, coarse_step);