Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
DynamicMeshChecker.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* DynamicMeshChecker.cc (C) 2000-2025 */
9/* */
10/* Class providing methods for mesh checking. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/ScopedPtr.h"
15#include "arcane/utils/ValueChecker.h"
16
17#include "arcane/mesh/DynamicMesh.h"
18#include "arcane/mesh/ItemGroupsSynchronize.h"
19#include "arcane/mesh/FaceReorienter.h"
20#include "arcane/mesh/DynamicMeshChecker.h"
21
23#include "arcane/core/Properties.h"
24#include "arcane/core/ItemPrinter.h"
25#include "arcane/core/TemporaryVariableBuildInfo.h"
26#include "arcane/core/IXmlDocumentHolder.h"
27#include "arcane/core/XmlNodeList.h"
28#include "arcane/core/IIOMng.h"
29#include "arcane/core/IParallelMng.h"
30#include "arcane/core/IParallelReplication.h"
31#include "arcane/core/VariableCollection.h"
32#include "arcane/core/NodesOfItemReorderer.h"
33
34#include <set>
35
36/*---------------------------------------------------------------------------*/
37/*---------------------------------------------------------------------------*/
38
39namespace Arcane::mesh
40{
41
42/*---------------------------------------------------------------------------*/
43/*---------------------------------------------------------------------------*/
44
45DynamicMeshChecker::
46DynamicMeshChecker(IMesh* mesh)
47: TraceAccessor(mesh->traceMng())
48, m_mesh(mesh)
49{
50 if (arcaneIsCheck())
51 m_check_level = 1;
52}
53
54/*---------------------------------------------------------------------------*/
55/*---------------------------------------------------------------------------*/
56
57DynamicMeshChecker::
58~DynamicMeshChecker()
59{
60 delete m_var_cells_faces;
61 delete m_var_cells_nodes;
62}
63
64/*---------------------------------------------------------------------------*/
65/*---------------------------------------------------------------------------*/
73void DynamicMeshChecker::
74checkValidMesh()
75{
76 info(4) << "Check mesh coherence.";
78
79 for (IItemFamilyCollection::Enumerator i(m_mesh->itemFamilies()); ++i;) {
80 IItemFamily* family = *i;
81 //GG: check why this test exists.
82 if (family->itemKind() >= NB_ITEM_KIND)
83 continue;
84 family->checkValid();
85 }
86 mesh_utils::checkMeshProperties(m_mesh, false, false, true);
87
88 // Store in a cell variable the list of faces and
89 // nodes that compose it.
90 // This is only useful for checks
91 // NOTE: currently set to false because it crashes if
92 // initial partitioning is used in Arcane
93 const bool global_check = false;
94 if (m_check_level >= 1 && global_check) {
95 if (!m_var_cells_faces)
96 m_var_cells_faces = new VariableCellArrayInt64(VariableBuildInfo(m_mesh, "MeshTestCellFaces"));
97
98 if (!m_var_cells_nodes)
99 m_var_cells_nodes = new VariableCellArrayInt64(VariableBuildInfo(m_mesh, "MeshTestCellNodes"));
100
101 CellGroup all_cells(m_mesh->allCells());
102 Integer max_nb_face = 0;
103 Integer max_nb_node = 0;
104 ENUMERATE_CELL (i, all_cells) {
105 Cell cell = *i;
106 Integer nb_face = cell.nbFace();
107 Integer nb_node = cell.nbNode();
108 if (nb_face > max_nb_face)
109 max_nb_face = nb_face;
110 if (nb_node > max_nb_node)
111 max_nb_node = nb_node;
112 }
113 m_var_cells_faces->resize(max_nb_face);
114 m_var_cells_nodes->resize(max_nb_node);
115 ENUMERATE_CELL (i, all_cells) {
116 Cell cell = *i;
117 Integer nb_face = cell.nbFace();
118 for (Integer z = 0; z < nb_face; ++z)
119 (*m_var_cells_faces)[cell][z] = cell.face(z).uniqueId().asInt64();
120 Integer nb_node = cell.nbNode();
121 for (Integer z = 0; z < nb_node; ++z)
122 (*m_var_cells_nodes)[cell][z] = cell.node(z).uniqueId().asInt64();
123 }
124 }
126}
127
128/*---------------------------------------------------------------------------*/
129/*---------------------------------------------------------------------------*/
130
131void DynamicMeshChecker::
132checkValidMeshFull()
133{
134 String func_name("DynamicMesh::checkValidMeshFull");
135 info() << func_name << " on " << m_mesh->name();
136 Integer nb_error = 0;
137 VariableFaceInt64 var_faces(VariableBuildInfo(m_mesh, "MeshCheckFaces"));
138 debug() << " VAR_FACE GROUP=" << var_faces.variable()->itemFamily()
139 << " NAME=" << var_faces.itemGroup().name()
140 << " FAMILY_NAME=" << var_faces.variable()->itemFamilyName()
141 << " GROUP_NAME=" << var_faces.variable()->itemGroupName();
142 ENUMERATE_FACE (iface, m_mesh->allFaces()) {
143 Face face = *iface;
144 Cell c = face.backCell();
145 Int64 uid = c.null() ? NULL_ITEM_UNIQUE_ID : c.uniqueId();
146 var_faces[face] = uid;
147 }
148 var_faces.synchronize();
149 ENUMERATE_FACE (iface, m_mesh->allFaces()) {
150 Face face = *iface;
151 Cell c = face.backCell();
152 Int64 uid = c.null() ? NULL_ITEM_UNIQUE_ID : c.uniqueId();
153 if (uid != var_faces[face] && uid != NULL_ITEM_ID && var_faces[face] != NULL_ITEM_ID) {
154 error() << func_name << " bad back cell in face uid=" << face.uniqueId();
155 ++nb_error;
156 }
157 }
158
159 ENUMERATE_FACE (iface, m_mesh->allFaces()) {
160 Face face = *iface;
161 Cell c = face.frontCell();
162 Int64 uid = c.null() ? NULL_ITEM_UNIQUE_ID : c.uniqueId();
163 var_faces[face] = uid;
164 }
165 var_faces.synchronize();
166 ENUMERATE_FACE (iface, m_mesh->allFaces()) {
167 Face face = *iface;
168 Cell c = face.frontCell();
169 Int64 uid = c.null() ? NULL_ITEM_UNIQUE_ID : c.uniqueId();
170 if (uid != var_faces[face] && uid != NULL_ITEM_ID && var_faces[face] != NULL_ITEM_ID) {
171 error() << func_name << " bad front cell in face uid=" << face.uniqueId();
172 ++nb_error;
173 }
174 }
175
176 ENUMERATE_CELL (icell, m_mesh->allCells()) {
177 Cell cell = *icell;
178 Integer cell_type = cell.type();
179 if (cell_type == IT_Line2) {
180 error() << func_name << " bad cell type in face uid=" << cell.uniqueId();
181 ++nb_error;
182 }
183 }
184
185 ENUMERATE_FACE (iface, m_mesh->allFaces()) {
186 Face face = *iface;
187 Integer face_type = face.type();
188 if (face_type == IT_Vertex) {
189 error() << func_name << " bad face type in face uid=" << face.uniqueId();
190 ++nb_error;
191 }
192 }
193
194 if (nb_error != 0)
195 ARCANE_FATAL("Invalid mesh");
196}
197
198/*---------------------------------------------------------------------------*/
199/*---------------------------------------------------------------------------*/
200/*
201 * \brief Checks that connectivity is valid.
202 *
203 * The checks cover the following points:
204 * - no mesh entities having a null index.
205 * - for faces, checks that at least one frontCell or backCell exists.
206 * Furthermore, if it has two cells, the backCell must always be the first.
207 * - nodes and faces must have the same owner as one of the adjacent cells.
208 * - checks that faces are correctly ordered and oriented
209 */
210void DynamicMeshChecker::
211checkValidConnectivity()
212{
213 String func_name = "MeshChecker::checkValidConnectivity";
214 debug() << func_name << " check";
215
216 // Calls the checkValidConnectivity method for each family.
217 IItemFamilyCollection item_families = m_mesh->itemFamilies();
218 for (auto x = item_families.enumerator(); ++x;) {
219 IItemFamily* f = *x;
221 }
222
223 if (!m_mesh->parentMesh()) {
224 Integer index = 0;
225 ENUMERATE_ (Face, i, m_mesh->allFaces()) {
226 Face elem = *i;
227 Cell front_cell = elem.frontCell();
228 Cell back_cell = elem.backCell();
229 if (front_cell.null() && back_cell.null())
230 ARCANE_FATAL("Local face '{0}' has two null cell face=", index, ItemPrinter(elem));
231 index++;
232 }
233 }
234
235 // Checks that the cell<->node connectivity
236 // is reciprocal
237 ENUMERATE_NODE (inode, m_mesh->allNodes()) {
238 Node node = *inode;
239 for (Cell cell : node.cells()) {
240 bool has_found = false;
241 for (Node node2 : cell.nodes()) {
242 if (node2 == node) {
243 has_found = true;
244 break;
245 }
246 }
247 if (!has_found) {
248 ARCANE_FATAL("Node uid={0} is connected to the cell uid={1} but the cell"
249 " is not connected to this node.",
250 ItemPrinter(node), ItemPrinter(cell));
251 }
252 }
253 }
254
255 // ATT: the following checks are not compatible with AMR
256 // TODO : extend checks for AMR
258 if (!m_mesh->isAmrActivated()) {
259 // Checks that the cell<->edge connectivity
260 // is reciprocal
261 ENUMERATE_EDGE (iedge, m_mesh->allEdges()) {
262 Edge edge = *iedge;
263 for (Cell cell : edge.cells()) {
264 bool has_found = false;
265 for (Edge edge2 : cell.edges()) {
266 if (edge2 == edge) {
267 has_found = true;
268 break;
269 }
270 }
271 if (!has_found) {
272 ARCANE_FATAL("Edge uid={0} is connected to the cell uid={1} but the cell"
273 " is not connected to this edge.",
274 ItemPrinter(edge), ItemPrinter(cell));
275 }
276 }
277 }
278 }
279 // Checks that the cell<->face connectivity
280 // is reciprocal
281 ENUMERATE_FACE (iface, m_mesh->allFaces()) {
282 Face face = *iface;
283 if (!m_mesh->isAmrActivated()) {
284 for (Cell cell : face.cells()) {
285 bool has_found = false;
286 for (Face face2 : cell.faces()) {
287 if (face2 == face) {
288 has_found = true;
289 break;
290 }
291 }
292 if (!has_found) {
293 ARCANE_FATAL("Face uid={0} is connected to the cell uid={1} but the cell"
294 " is not connected to this face.",
295 ItemPrinter(face), ItemPrinter(cell));
296 }
297 }
298 }
299 for (Cell cell : face.cells()) {
300 bool has_found = false;
301 for (Face face2 : cell.faces()) {
302 if (face2 == face) {
303 has_found = true;
304 break;
305 }
306 }
307 if (!has_found && (face.backCell().level() == face.frontCell().level())) {
308 warning() << func_name << ". The face " << FullItemPrinter(face)
309 << " is connected to the cell " << FullItemPrinter(cell)
310 << " but the cell (level " << cell.level() << ")"
311 << " is not connected to the face.";
312 }
313 }
314
315 Integer nb_cell = face.nbCell();
316 Cell back_cell = face.backCell();
317 Cell front_cell = face.frontCell();
318 if ((back_cell.null() || front_cell.null()) && nb_cell == 2)
319 ARCANE_FATAL("Face uid='{0}' bad number of cells face", ItemPrinter(face));
320 // If we have two connected cells, then the back cell must be the first
321 if (nb_cell == 2 && back_cell != face.cell(0))
322 ARCANE_FATAL("Bad face face.backCell()!=face.cell(0) face={0} back_cell={1} from_cell={2} cell0={3}",
323 ItemPrinter(face), ItemPrinter(back_cell), ItemPrinter(front_cell), ItemPrinter(face.cell(0)));
324 }
325
328
329 if (m_mesh->parentMesh()) {
330 Integer nerror = 0;
331 eItemKind kinds[] = { IK_Node, IK_Edge, IK_Face, IK_Cell };
332 Integer nb_kind = sizeof(kinds) / sizeof(eItemKind);
333
334 for (Integer i_kind = 0; i_kind < nb_kind; ++i_kind) {
335 const eItemKind kind = kinds[i_kind];
336 IItemFamily* family = m_mesh->itemFamily(kind);
337 if (!family->parentFamily()) {
338 error() << "Mesh " << m_mesh->name() << " : Family " << kind << " does not exist in mesh";
339 ++nerror;
340 }
341 else {
342 ENUMERATE_ITEM (iitem, family->allItems()) {
343 const Item& item = *iitem;
344 const Item& parent_item = item.parent();
345 if (parent_item.itemBase().isSuppressed()) {
346 error() << "Mesh " << m_mesh->name() << " : Inconsistent suppresssed parent item uid : "
347 << ItemPrinter(item) << " / " << ItemPrinter(parent_item);
348 ++nerror;
349 }
350 if (parent_item.uniqueId() != item.uniqueId()) {
351 error() << "Mesh " << m_mesh->name() << " : Inconsistent item/parent item uid : " << ItemPrinter(item);
352 ++nerror;
353 }
354 }
355 }
356 }
357 if (nerror > 0)
358 ARCANE_FATAL("Mesh name={0} has {1} (see above)", m_mesh->name(), String::plural(nerror, "error"));
359
360 // Checks the parallel consistency of the parent group
361 nerror = 0;
362 {
363 ItemGroup parent_group = m_mesh->parentGroup();
364 String var_name = String::format("SubMesh_{0}_GroupConsistencyChecker", parent_group.name());
365 TemporaryVariableBuildInfo tvbi(m_mesh->parentMesh(), var_name, parent_group.itemFamily()->name());
366 ItemVariableScalarRefT<Integer> var(tvbi, m_mesh->parentGroup().itemKind());
367 var.fill(-1);
368 ENUMERATE_ITEM (iitem, m_mesh->parentGroup()) {
369 var[iitem] = iitem->owner();
370 }
371 nerror += var.checkIfSync(10);
372 }
373 if (nerror > 0)
374 ARCANE_FATAL("Mesh name={0} has parent group consistency {1}\n"
375 "This usually means that parent group was not symmetrically built",
376 m_mesh->name(), String::plural(nerror, "error", false));
377 }
378
379 if (m_is_check_items_owner) {
380 _checkValidItemOwner(m_mesh->nodeFamily());
381 _checkValidItemOwner(m_mesh->edgeFamily());
382 _checkValidItemOwner(m_mesh->faceFamily());
383 _checkValidItemOwner(m_mesh->cellFamily());
384 }
385}
386
387/*---------------------------------------------------------------------------*/
388/*---------------------------------------------------------------------------*/
389
390void DynamicMeshChecker::
391updateAMRFaceOrientation()
392{
393 String func_name = "MeshChecker::updateAMRFaceOrientation";
394 FaceReorienter fr(m_mesh);
395 ENUMERATE_FACE (iface, m_mesh->allFaces()) {
396 Face face = *iface;
397 Integer nb_cell = face.nbCell();
398 Cell back_cell = face.backCell();
399 Cell front_cell = face.frontCell();
400 if ((back_cell.null() || front_cell.null()) && nb_cell == 2)
401 ARCANE_FATAL("Bad number of cells for face={0}", ItemPrinter(face));
402 // If we have two connected cells, then the back cell must be the first
403 if (nb_cell == 2 && back_cell != face.cell(0))
404 ARCANE_FATAL("Bad face face.backCell()!=face.cell(0) face={0} back_cell={1} from_cell={2} cell0={3}",
405 ItemPrinter(face), ItemPrinter(back_cell), ItemPrinter(front_cell), ItemPrinter(face.cell(0)));
406
407 // This could undoubtedly be optimized
408 fr.checkAndChangeOrientationAMR(face);
409 }
410}
411
412/*---------------------------------------------------------------------------*/
413/*---------------------------------------------------------------------------*/
414
415void DynamicMeshChecker::
416updateAMRFaceOrientation(ArrayView<Int64> ghost_cell_to_refine)
417{
418 ItemInternalList cells = m_mesh->itemsInternal(IK_Cell);
419 UniqueArray<Integer> lids(ghost_cell_to_refine.size());
420 m_mesh->cellFamily()->itemsUniqueIdToLocalId(lids, ghost_cell_to_refine, true);
421 FaceReorienter fr(m_mesh);
422 std::set<Int64> set;
423 typedef std::pair<std::set<Int64>::iterator, bool> return_type;
424 for (Integer i = 0, n = lids.size(); i < n; ++i) {
425 Cell icell = cells[lids[i]];
426 for (Integer ic = 0, nchild = icell.nbHChildren(); ic < nchild; ++ic) {
427 Cell child = icell.hChild(ic);
428 for (Face face : child.faces()) {
429 return_type value = set.insert(face.uniqueId());
430 if (value.second) {
431 fr.checkAndChangeOrientationAMR(face);
432 }
433 }
434 }
435 }
436}
437
438/*---------------------------------------------------------------------------*/
439/*---------------------------------------------------------------------------*/
443void DynamicMeshChecker::
444_checkFacesOrientation()
445{
446 bool is_1d = (m_mesh->dimension() == 1);
447 //Temporary: for now, does not test orientation in 1D.
448 if (is_1d)
449 return;
450
451 String func_name = "MeshChecker::_checkFacesOrientation";
452
453 Int64UniqueArray work_face_sorted_nodes;
454 IntegerUniqueArray work_face_nodes_index;
455 Int64UniqueArray work_face_orig_nodes_uid;
456 ItemTypeMng* item_type_mng = m_mesh->itemTypeMng();
457 NodesOfItemReorderer face_reorderer(item_type_mng);
458 ENUMERATE_ (Cell, icell, m_mesh->allCells()) {
459 Cell cell = *icell;
460 const ItemTypeInfo* type = cell.typeInfo();
461 Int32 cell_nb_face = type->nbLocalFace();
462 for (Integer i_face = 0; i_face < cell_nb_face; ++i_face) {
463 const ItemTypeInfo::LocalFace& lf = type->localFace(i_face);
464 Integer face_nb_node = lf.nbNode();
465
466 work_face_orig_nodes_uid.resize(face_nb_node);
467 for (Integer z = 0; z < face_nb_node; ++z)
468 work_face_orig_nodes_uid[z] = cell.node(lf.node(z)).uniqueId();
469
470 bool is_reorder = false;
471 if (is_1d) {
472 is_reorder = face_reorderer.reorder1D(i_face, work_face_orig_nodes_uid[0]);
473 }
474 else
475 is_reorder = face_reorderer.reorder(ItemTypeId::fromInteger(lf.typeId()), work_face_orig_nodes_uid);
476 ConstArrayView<Int64> face_sorted_nodes(face_reorderer.sortedNodes());
477
478 Face cell_face = cell.face(i_face);
479 if (cell_face.nbNode() != face_nb_node)
480 ARCANE_FATAL("Incoherent number of node for 'face' and 'localFace'"
481 " cell={0} face={1} nb_local_node={2} nb_face_node={3}",
482 ItemPrinter(cell), ItemPrinter(cell_face), face_nb_node, cell_face.nbNode());
483
484 for (Integer z = 0; z < face_nb_node; ++z) {
485 if (cell_face.node(z).uniqueId() != face_sorted_nodes[z])
486 ARCANE_FATAL("Bad node unique id for face: cell={0} face={1} cell_node_uid={2} face_node_uid={3} z={4}",
487 ItemPrinter(cell), ItemPrinter(cell_face), face_sorted_nodes[z],
488 cell_face.node(z).uniqueId());
489 }
490 if (is_reorder) {
491 Cell front_cell = cell_face.frontCell();
492 if (front_cell != cell) {
493 if (!front_cell.null())
494 ARCANE_FATAL("Bad orientation for face. Should be front cell: cell={0} face={1} front_cell={2}",
495 ItemPrinter(cell), ItemPrinter(cell_face), ItemPrinter(front_cell));
496 else
497 ARCANE_FATAL("Bad orientation for face. Should be front cell (no front cell) cell={0} face={1}",
498 ItemPrinter(cell), ItemPrinter(cell_face));
499 }
500 }
501 else {
502 Cell back_cell = cell_face.backCell();
503 if (back_cell != cell) {
504 if (!back_cell.null())
505 ARCANE_FATAL("Bad orientation for face. Should be back cell: cell={0} face={1} front_cell={2}",
506 ItemPrinter(cell), ItemPrinter(cell_face), ItemPrinter(back_cell));
507 else
508 ARCANE_FATAL("Bad orientation for face. Should be back cell (no back cell) cell={0} face={1}",
509 ItemPrinter(cell), ItemPrinter(cell_face));
510 }
511 }
512 }
513 }
514}
515
516/*---------------------------------------------------------------------------*/
517/*---------------------------------------------------------------------------*/
521void DynamicMeshChecker::
522_checkEdgesOrientation()
523{
524 // For edges, checks that the uniqueId() of the first node is
525 // less than that of the second.
526 Int32 nb_error = 0;
527 ENUMERATE_ (Edge, iedge, m_mesh->allEdges()) {
528 Edge edge = *iedge;
529 Int32 nb_node = edge.nbNode();
530 if (nb_node >= 2) {
531 Node node0 = edge.node(0);
532 Node node1 = edge.node(1);
533 if (node0.uniqueId() > node1.uniqueId()) {
534 ++nb_error;
535 info() << "Error: bad orientation for edge '" << ItemPrinter(edge)
536 << " n0=" << node0.uniqueId() << " n1=" << node1.uniqueId();
537 }
538 }
539 }
540 if (nb_error != 0)
541 ARCANE_FATAL("Bad connectivity for '{0}' edges", nb_error);
542}
543
544/*---------------------------------------------------------------------------*/
545/*---------------------------------------------------------------------------*/
546
547void DynamicMeshChecker::
548_checkValidItemOwner(IItemFamily* family)
549{
550 // For non-sub-meshes, every sub-item
551 // (Node, Edge or Face) must have an adjacent cell with the same owner,
552 // unless orphan entities are allowed and the entity is not
553 // connected to any cell.
554 // For sub-meshes, furthermore, every item must have the same owner
555 // as its parent.
556 bool allow_orphan_items = m_mesh->meshKind().isNonManifold();
557
558 Integer nerror = 0;
559 if (!m_mesh->parentMesh()) {
560
561 if (family->itemKind() == IK_Cell)
562 return; // implicitly valid for cells
563
564 ItemGroup own_items = family->allItems().own();
565 ENUMERATE_ITEM (iitem, own_items) {
566 Item item = *iitem;
567 Int32 owner = item.owner();
568 bool is_ok = false;
569 ItemVectorView cells = item.itemBase().cellList();
570 if (cells.size() == 0 && allow_orphan_items)
571 continue;
572 for (Item cell : cells) {
573 if (cell.owner() == owner) {
574 is_ok = true;
575 break;
576 }
577 }
578 if (!is_ok) {
579 OStringStream ostr;
580 Integer index = 0;
581 ostr() << " nb_cell=" << cells.size();
582 for (Item cell : cells) {
583 ostr() << " SubCell i=" << index << " cell=" << ItemPrinter(cell);
584 ++index;
585 }
586 error() << "Mesh " << m_mesh->name() << " family=" << family->name()
587 << " Item" << ItemPrinter(item) << " has no cell with same owner:"
588 << ostr.str();
589 ++nerror;
590 }
591 }
592 }
593 else {
594 ENUMERATE_ITEM (iitem, family->allItems()) {
595 Item item = *iitem;
596 Item parent_item = item.parent();
597 if (parent_item.owner() != item.owner()) {
598 error() << "Mesh " << m_mesh->name() << " : Inconsistent item/parent item owner : "
599 << ItemPrinter(item) << " / " << ItemPrinter(parent_item);
600 ++nerror;
601 }
602 }
603 }
604
605 if (nerror > 0)
606 ARCANE_FATAL("mesh {0} family={1} has {2}", m_mesh->name(), family->name(),
607 String::plural(nerror, "owner error"));
608}
609
610/*---------------------------------------------------------------------------*/
611/*---------------------------------------------------------------------------*/
612
613void DynamicMeshChecker::
614checkVariablesSynchronization()
615{
616 Int64 nb_diff = 0;
617 VariableCollection used_vars(m_mesh->variableMng()->usedVariables());
618 for (VariableCollection::Enumerator i_var(used_vars); ++i_var;) {
619 IVariable* var = *i_var;
620 switch (var->itemKind()) {
621 case IK_Node:
622 case IK_Edge:
623 case IK_Face:
624 case IK_Cell:
625 case IK_DoF:
626 nb_diff += var->checkIfSync(10);
627 break;
628 case IK_Particle:
629 case IK_Unknown:
630 break;
631 }
632 }
633 if (nb_diff != 0)
634 ARCANE_FATAL("Error in checkVariablesSynchronization() nb_diff=", nb_diff);
635}
636
637/*---------------------------------------------------------------------------*/
638/*---------------------------------------------------------------------------*/
639
640void DynamicMeshChecker::
641checkItemGroupsSynchronization()
642{
643 Int64 nb_diff = 0;
644 // TODO: iterate over all families (except particles)
645 ItemGroupsSynchronize node_sync(m_mesh->nodeFamily());
646 nb_diff += node_sync.checkSynchronize();
647 ItemGroupsSynchronize edge_sync(m_mesh->edgeFamily());
648 nb_diff += edge_sync.checkSynchronize();
649 ItemGroupsSynchronize face_sync(m_mesh->faceFamily());
650 nb_diff += face_sync.checkSynchronize();
651 ItemGroupsSynchronize cell_sync(m_mesh->cellFamily());
652 nb_diff += cell_sync.checkSynchronize();
653 if (nb_diff != 0)
654 ARCANE_FATAL("some groups are not synchronized nb_diff={0}", nb_diff);
655}
656
657/*---------------------------------------------------------------------------*/
658/*---------------------------------------------------------------------------*/
667void DynamicMeshChecker::
668checkGhostCells()
669{
670 pwarning() << "CHECK GHOST CELLS";
671 Integer sid = m_mesh->meshPartInfo().partRank();
672 ENUMERATE_CELL (icell, m_mesh->cellFamily()->allItems()) {
673 Cell cell = *icell;
674 if (cell.isOwn())
675 continue;
676 bool is_ok = false;
677 for (Node node : cell.nodes()) {
678 for (Cell cell2 : node.cells()) {
679 if (cell2.owner() == sid) {
680 is_ok = true;
681 }
682 }
683 }
684 if (!is_ok)
685 info() << "WARNING: Cell " << ItemPrinter(cell) << " should not be a ghost cell";
686 }
687}
688
689/*---------------------------------------------------------------------------*/
690/*---------------------------------------------------------------------------*/
691
692void DynamicMeshChecker::
693checkMeshFromReferenceFile()
694{
695 if (!m_compare_reference_file)
696 return;
697
698 IParallelMng* pm = m_mesh->parallelMng();
699
700 if (!pm->isParallel())
701 return; // only in parallel
702
703 debug() << "Testing the mesh against the initial mesh";
704 String base_file_name("meshconnectivity");
705 // In parallel, compare the current mesh with the file
706 // containing the full connectivity (sequential case)
707 IIOMng* io_mng = pm->ioMng();
708 ScopedPtrT<IXmlDocumentHolder> xml_doc(io_mng->parseXmlFile(base_file_name));
709 if (xml_doc.get()) {
710 XmlNode doc_node = xml_doc->documentNode();
711 if (!doc_node.null())
712 mesh_utils::checkMeshConnectivity(m_mesh, doc_node, true);
713 }
714 else {
715 warning() << "Can't test the subdomain coherence "
716 << "against the initial mesh";
717 }
718}
719
720/*---------------------------------------------------------------------------*/
721/*---------------------------------------------------------------------------*/
722
723void DynamicMeshChecker::
724checkValidReplication()
725{
726 info() << "Checking valid replication";
727 IParallelMng* mesh_pm = m_mesh->parallelMng();
728 IParallelReplication* pr = mesh_pm->replication();
729 if (!pr->hasReplication())
730 return;
731
733
734 // Checks that all families (except particles) are the same
735 UniqueArray<IItemFamily*> wanted_same_family;
736
737 for (IItemFamilyCollection::Enumerator i(m_mesh->itemFamilies()); ++i;) {
738 IItemFamily* family = *i;
739 if (family->itemKind() != IK_Particle)
740 wanted_same_family.add(family);
741 }
742 ValueChecker vc(A_FUNCINFO);
743
744 // Checks that everyone has the same number of families.
745 Integer nb_family = wanted_same_family.size();
746 Integer max_nb_family = pm->reduce(Parallel::ReduceMax, nb_family);
747 vc.areEqual(nb_family, max_nb_family, "Bad number of family");
748
749 // Checks that all families have the same number of elements.
750 //TODO: it should also check that the family names match.
751 {
752 UniqueArray<Int32> families_size(nb_family);
753 for (Integer i = 0; i < nb_family; ++i)
754 families_size[i] = wanted_same_family[i]->nbItem();
755 UniqueArray<Int32> global_families_size(families_size);
756 pm->reduce(Parallel::ReduceMax, global_families_size.view());
757 vc.areEqualArray(global_families_size, families_size, "Bad family");
758 }
759
760 // Checks that all families have the same entities (same uniqueId())
761 {
762 UniqueArray<Int64> unique_ids;
763 UniqueArray<Int64> global_unique_ids;
764 for (Integer i = 0; i < nb_family; ++i) {
765 ItemGroup group = wanted_same_family[i]->allItems();
766 unique_ids.resize(group.size());
767 Integer index = 0;
768 ENUMERATE_ITEM (iitem, group) {
769 unique_ids[index] = iitem->uniqueId();
770 ++index;
771 }
772 global_unique_ids.resize(group.size());
773 global_unique_ids.copy(unique_ids);
774 pm->reduce(Parallel::ReduceMax, global_unique_ids.view());
775 String message = String::format("Bad unique ids for family '{0}'",
776 wanted_same_family[i]->name());
777 vc.areEqualArray(global_unique_ids, unique_ids, message);
778 }
779 }
780}
781
782/*---------------------------------------------------------------------------*/
783/*---------------------------------------------------------------------------*/
784
785void DynamicMeshChecker::
786_checkReplicationFamily(IItemFamily*)
787{
788}
789
790/*---------------------------------------------------------------------------*/
791/*---------------------------------------------------------------------------*/
792
793} // End namespace Arcane::mesh
794
795/*---------------------------------------------------------------------------*/
796/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
#define ENUMERATE_FACE(name, group)
Generic enumerator for a face group.
#define ENUMERATE_(type, name, group)
Generic enumerator for an entity group.
#define ENUMERATE_CELL(name, group)
Generic enumerator for a cell group.
#define ENUMERATE_EDGE(name, group)
Generic enumerator for an edge group.
#define ENUMERATE_ITEM(name, group)
Generic enumerator for a node group.
#define ENUMERATE_NODE(name, group)
Generic enumerator for a node group.
Utility functions for the mesh.
void checkMeshProperties(IMesh *mesh, bool is_sorted, bool has_no_hole, bool check_faces)
Checks if the mesh has certain properties.
Integer size() const
Number of elements in the vector.
void resize(Int64 s)
Changes the number of elements in the array to s.
void copy(Span< const T > rhs)
Copies the values from rhs into the instance.
void add(ConstReferenceType val)
Adds element val to the end of the array.
ArrayView< T > view() const
Mutable view of this array.
Cell of a mesh.
Definition Item.h:1300
Face face(Int32 i) const
i-th face of the cell
Definition Item.h:1400
Int32 nbFace() const
Number of faces of the cell.
Definition Item.h:1397
Int32 level() const
Definition Item.h:1473
Constant view of an array of type T.
Edge of a cell.
Definition Item.h:875
CellConnectedListViewType cells() const
List of edge cells.
Definition Item.h:984
Int32 nbNode() const
Number of vertices of the edge.
Definition Item.h:972
This function/class reorients faces.
Face of a cell.
Definition Item.h:1032
Cell frontCell() const
Cell in front of the face (null cell if none).
Definition Item.h:1780
Cell cell(Int32 i) const
i-th cell of the face
Definition Item.h:1793
Int32 nbCell() const
Number of cells of the face (1 or 2).
Definition Item.h:1129
CellConnectedListViewType cells() const
List of cells of the face.
Definition Item.h:1135
Cell backCell() const
Cell behind the face (null cell if none).
Definition Item.h:1774
Interface of the input/output manager.
Definition IIOMng.h:37
virtual IXmlDocumentHolder * parseXmlFile(const String &filename, const String &schemaname=String{})=0
Reads and parses the XML file filename.
Interface of an entity family.
Definition IItemFamily.h:83
virtual ItemGroup allItems() const =0
Group of all entities.
virtual void checkValid()=0
Check the validity of internal structures (internal).
virtual String name() const =0
Family name.
virtual IItemFamily * parentFamily() const =0
IItemFamily parent.
virtual eItemKind itemKind() const =0
Entity kind.
virtual void checkValidConnectivity()=0
Verification of the validity of internal structures concerning connectivity.
Interface of the parallelism manager for a subdomain.
virtual IParallelReplication * replication() const =0
Replication information.
virtual IIOMng * ioMng() const =0
I/O manager.
virtual bool isParallel() const =0
Returns true if the execution is parallel.
virtual char reduce(eReduceType rt, char v)=0
Performs a reduction of type rt on the real v and returns the value.
Brief information on parallel subdomain replication.
virtual bool hasReplication() const =0
Indicates if replication is active.
virtual IParallelMng * replicaParallelMng() const =0
Communicator associated with all replicas representing the same subdomain.
Interface of a variable.
Definition IVariable.h:40
virtual String itemFamilyName() const =0
Name of the associated family (null if none).
virtual eItemKind itemKind() const =0
Kind of mesh entities on which the variable is based.
virtual Int32 checkIfSync(Integer max_print=0)=0
Checks if the variable is properly synchronized.
virtual IItemFamily * itemFamily() const =0
Associated entity family.
virtual String itemGroupName() const =0
Name of the associated entity group.
bool isSuppressed() const
True if the entity is suppressed.
Mesh entity group.
Definition ItemGroup.h:51
const String & name() const
Group name.
Definition ItemGroup.h:81
Integer size() const
Number of elements in the group.
Definition ItemGroup.h:93
IItemFamily * itemFamily() const
Entity family to which this group belongs (0 for the null group).
Definition ItemGroup.h:128
ItemGroup own() const
Group equivalent to this one but containing only the local elements of the subdomain.
Definition ItemGroup.cc:182
Utility class for printing information about an entity.
Definition ItemPrinter.h:35
static ItemTypeId fromInteger(Int64 v)
Creates an instance from an integer.
Local information about a cell face.
Integer node(Integer i) const
Local index in the cell of the i-th node of the face.
Integer typeId() const
Type of the face entity.
Integer nbNode() const
Number of nodes of the face.
Info on a mesh entity type.
Mesh entity type manager.
Definition ItemTypeMng.h:66
Scalar variable on a mesh entity type.
View on a vector of entities.
Int32 size() const
Number of elements in the vector.
Node node(Int32 i) const
i-th node of the entity
Definition Item.h:840
NodeConnectedListViewType nodes() const
List of nodes of the entity.
Definition Item.h:843
Int32 nbNode() const
Number of nodes of the entity.
Definition Item.h:837
Base class for a mesh element.
Definition Item.h:84
const ItemTypeInfo * typeInfo() const
Information about the entity type.
Definition Item.h:406
Int32 owner() const
Owner subdomain number of the entity.
Definition Item.h:252
ItemUniqueId uniqueId() const
Unique identifier across all domains.
Definition Item.h:239
constexpr bool null() const
true if the entity is null (i.e. not connected to the mesh)
Definition Item.h:230
constexpr bool isOwn() const
true if the entity belongs to the subdomain
Definition Item.h:267
Item parent(Int32 i) const
i-th parent for submeshes
Definition Item.h:300
impl::ItemBase itemBase() const
Internal part of the entity.
Definition Item.h:383
Int16 type() const
Entity type.
Definition Item.h:255
GroupType itemGroup() const
Group associated with the quantity.
Node of a mesh.
Definition Item.h:598
CellConnectedListViewType cells() const
List of cells of the node.
Definition Item.h:728
Utility class to reorder the nodes of an entity.
Encapsulation of an automatically destructing pointer.
Definition ScopedPtr.h:44
static String plural(const Integer n, const String &str, const bool with_number=true)
Standard plural form by adding an 's'.
Definition String.cc:1052
Parameters required to build a temporary variable.
TraceMessageDbg debug(Trace::eDebugLevel=Trace::Medium) const
Flow for a debug message.
TraceMessage info() const
Flow for an information message.
TraceMessage error() const
Flow for an error message.
TraceMessage warning() const
Flow for a warning message.
TraceMessage pwarning() const
1D data vector with value semantics (STL style).
Checking the validity of certain values.
void areEqual(const T1 &value, const T2 &expected_value, const String &message)
void areEqualArray(const T1 &x_values, const T2 &x_expected_values, const String &message)
Checks that the two arrays values and expected_values have the same values.
Parameters necessary for building a variable.
virtual Integer checkIfSync(int max_print=0)
Checks if the variable is synchronized.
IVariable * variable() const
Associated variable.
Node of a DOM tree.
Definition XmlNode.h:51
bool null() const
True if the node is null.
Definition XmlNode.h:303
void checkValidMeshFull() override
Verification of mesh validity.
void _checkEdgesOrientation()
Checks that edges are correctly numbered.
void _checkFacesOrientation()
Checks that faces are correctly oriented and connected.
Information to synchronize groups between sub-domains.
Integer checkSynchronize()
Checks if the groups are synchronized.
ItemGroupT< Cell > CellGroup
Group of cells.
Definition ItemTypes.h:184
MeshVariableArrayRefT< Cell, Int64 > VariableCellArrayInt64
Quantity at cell centers of integer array type.
MeshVariableScalarRefT< Face, Int64 > VariableFaceInt64
Quantity at the face of 64-bit integer type.
@ ReduceMax
Maximum of values.
bool arcaneIsCheck()
True if running in check mode.
Definition Misc.cc:66
UniqueArray< Int64 > Int64UniqueArray
Dynamic 1D array of 64-bit integers.
Definition UtilsTypes.h:339
std::int64_t Int64
Signed integer type of 64 bits.
Int32 Integer
Type representing an integer.
Collection< IItemFamily * > IItemFamilyCollection
Collection of item families.
eItemKind
Mesh entity type.
@ IK_Particle
Particle mesh entity.
@ IK_Node
Node mesh entity.
@ IK_Cell
Cell mesh entity.
@ IK_Unknown
Unknown or uninitialized mesh entity.
@ IK_Face
Face mesh entity.
@ IK_DoF
Degree of Freedom mesh entity.
@ IK_Edge
Edge mesh entity.
UniqueArray< Integer > IntegerUniqueArray
Dynamic 1D array of integers.
Definition UtilsTypes.h:347
std::int32_t Int32
Signed integer type of 32 bits.