Skip to content

feat: Perform all mesh-doctor checks at once #100

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Jun 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5c87d4c
Change folder named "checks" for "actions" to be less ambiguous about…
alexbenedicto May 29, 2025
3f240fc
First version without results
alexbenedicto Jun 2, 2025
892f9f1
Improve logging functionality across main mesh-doctor scripts
alexbenedicto Jun 5, 2025
4e4a9bb
Improve logging across other scripts + better argparse variable name …
alexbenedicto Jun 5, 2025
54c3cdf
First version of all_checks without "supported_elements" check
alexbenedicto Jun 5, 2025
3d0552e
supported_elements action corrected and now included in all_checks
alexbenedicto Jun 5, 2025
964a094
Update tests
alexbenedicto Jun 5, 2025
1ebccdf
Add test for all_checks + bug fix
alexbenedicto Jun 5, 2025
cb63ad8
Update documentation
alexbenedicto Jun 5, 2025
a4fe9d6
yapf format
alexbenedicto Jun 5, 2025
9f62b7f
Merge remote-tracking branch 'origin/main' into benedicto/feature/mes…
alexbenedicto Jun 6, 2025
44d0ba7
Remove automatic documentation + yapf
alexbenedicto Jun 6, 2025
a8e4a7c
ruff checking
alexbenedicto Jun 6, 2025
a502e23
Add RESULTS log level to Logger
alexbenedicto Jun 10, 2025
c05762f
Replace logger by setup_logger + use of new "results" attribute
alexbenedicto Jun 10, 2025
9c9efb9
typing and restore constant name Logger
alexbenedicto Jun 11, 2025
2dba83a
Change default checks + update documentation
alexbenedicto Jun 12, 2025
67deb6b
Update test and log output
alexbenedicto Jun 12, 2025
0bd9334
Add main_checks feature by refactoring all_checks feature + doc updat…
alexbenedicto Jun 17, 2025
8a28075
Improve message output from certain checks + add description of param…
alexbenedicto Jun 21, 2025
f45246a
Update test file
alexbenedicto Jun 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions docs/geos-mesh.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
GEOS Mesh tools
====================

**geos-mesh** is a Python package that contains several tools and utilities to handle processing and quality checks of meshes.

.. toctree::
:maxdepth: 5
:maxdepth: 1
:caption: Contents:

./geos_mesh_docs/home.rst
./geos_mesh_docs/doctor

./geos_mesh_docs/converter

./geos_mesh_docs/io

./geos_mesh_docs/model

./geos_mesh_docs/processing

./geos_mesh_docs/stats

./geos_mesh_docs/modules.rst
./geos_mesh_docs/utils
83 changes: 58 additions & 25 deletions docs/geos_mesh_docs/doctor.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,29 @@ Mesh Doctor
``mesh-doctor`` is organized as a collection of modules with their dedicated sets of options.
The current page will introduce those modules, but the details and all the arguments can be retrieved by using the ``--help`` option for each module.

Prerequisites
^^^^^^^^^^^^^

To use mesh-doctor, you first need to have installed the ``geos-mesh`` package using the following command:

.. code-block:: bash

python -m pip install --upgrade ./geos-mesh

Once done, you can call ``mesh-doctor`` in your command line as presented in the rest of this documentation.

Modules
^^^^^^^

To list all the modules available through ``mesh-doctor``, you can simply use the ``--help`` option, which will list all available modules as well as a quick summary.

.. code-block::

$ python src/geos/mesh/doctor/mesh_doctor.py --help
$ mesh-doctor --help
usage: mesh_doctor.py [-h] [-v] [-q] -i VTK_MESH_FILE
{collocated_nodes,element_volumes,fix_elements_orderings,generate_cube,generate_fractures,generate_global_ids,non_conformal,self_intersecting_elements,supported_elements}
...

Inspects meshes for GEOSX.

positional arguments:
{collocated_nodes,element_volumes,fix_elements_orderings,generate_cube,generate_fractures,generate_global_ids,non_conformal,self_intersecting_elements,supported_elements}
Modules
Expand All @@ -40,14 +49,12 @@ To list all the modules available through ``mesh-doctor``, you can simply use th
Checks if the faces of the elements are self intersecting.
supported_elements
Check that all the elements of the mesh are supported by GEOSX.

options:
-h, --help
show this help message and exit
-v Use -v 'INFO', -vv for 'DEBUG'. Defaults to 'WARNING'.
-q Use -q to reduce the verbosity of the output.
-i VTK_MESH_FILE, --vtk-input-file VTK_MESH_FILE

Note that checks are dynamically loaded.
An option may be missing because of an unloaded module.
Increase verbosity (-v, -vv) to get full information.
Expand All @@ -57,9 +64,8 @@ For example

.. code-block::

$ python src/geos/mesh/doctor/mesh_doctor.py collocated_nodes --help
$ mesh-doctor collocated_nodes --help
usage: mesh_doctor.py collocated_nodes [-h] --tolerance TOLERANCE

options:
-h, --help show this help message and exit
--tolerance TOLERANCE [float]: The absolute distance between two nodes for them to be considered collocated.
Expand All @@ -78,6 +84,42 @@ You can solve this issue by installing the dependencies of ``mesh-doctor`` defin

Here is a list and brief description of all the modules available.

``all_checks`` and ``main_checks``
""""""""""""""""""""""""""""""""""

``mesh-doctor`` modules are called ``actions`` and they can be split into 2 different categories:
``check actions`` that will give you a feedback on a .vtu mesh that you would like to use in GEOS.
``operate actions`` that will either create a new mesh or modify an existing mesh.

``all_checks`` aims at applying every single ``check`` action in one single command. The available list is of check is:
``collocated_nodes``, ``element_volumes``, ``non_conformal``, ``self_intersecting_elements``, ``supported_elements``.

``main_checks`` does only the fastest checks ``collocated_nodes``, ``element_volumes`` and ``self_intersecting_elements``
that can quickly highlight some issues to deal with before investigating the other checks.

Both ``all_checks`` and ``main_checks`` have the same keywords and can be operated in the same way. The example below shows
the case of ``all_checks``, but it can be swapped for ``main_checks``.

.. code-block::

$ mesh-doctor all_checks --help
usage: mesh-doctor all_checks [-h] [--checks_to_perform CHECKS_TO_PERFORM] [--set_parameters SET_PARAMETERS]

options:
-h, --help show this help message and exit
--checks_to_perform CHECKS_TO_PERFORM
Comma-separated list of mesh-doctor checks to perform.
If no input was given, all of the following checks will be executed by default: ['collocated_nodes', 'element_volumes', 'self_intersecting_elements'].
The available choices for checks are ['collocated_nodes', 'element_volumes', 'non_conformal', 'self_intersecting_elements', 'supported_elements'].
If you want to choose only certain of them, you can name them individually.
Example: --checks_to_perform collocated_nodes,element_volumes (default: )
--set_parameters SET_PARAMETERS
Comma-separated list of parameters to set for the checks (e.g., 'param_name:value'). These parameters override the defaults.
Default parameters are: For collocated_nodes: tolerance:0.0. For element_volumes: min_volume:0.0.
For non_conformal: angle_tolerance:10.0, point_tolerance:0.0, face_tolerance:0.0.
For self_intersecting_elements: min_distance:2.220446049250313e-16. For supported_elements: chunk_size:1, nproc:8.
Example: --set_parameters parameter_name:10.5,other_param:25 (default: )

``collocated_nodes``
""""""""""""""""""""

Expand All @@ -86,9 +128,8 @@ It is not uncommon to define multiple nodes for the exact same position, which w

.. code-block::

$ python src/geos/mesh/doctor/mesh_doctor.py collocated_nodes --help
$ mesh-doctor collocated_nodes --help
usage: mesh_doctor.py collocated_nodes [-h] --tolerance TOLERANCE

options:
-h, --help show this help message and exit
--tolerance TOLERANCE [float]: The absolute distance between two nodes for them to be considered collocated.
Expand All @@ -101,9 +142,8 @@ Cells with negative volumes will typically be an issue for ``geos`` and should b

.. code-block::

$ python src/geos/mesh/doctor/mesh_doctor.py element_volumes --help
$ mesh-doctor element_volumes --help
usage: mesh_doctor.py element_volumes [-h] --min 0.0

options:
-h, --help show this help message and exit
--min 0.0 [float]: The minimum acceptable volume. Defaults to 0.0.
Expand All @@ -117,12 +157,11 @@ This can be convenient if you cannot regenerate the mesh.

.. code-block::

$ python src/geos/mesh/doctor/mesh_doctor.py fix_elements_orderings --help
$ mesh-doctor fix_elements_orderings --help
usage: mesh_doctor.py fix_elements_orderings [-h] [--Hexahedron 1,6,5,4,7,0,2,3] [--Prism5 8,2,0,7,6,9,5,1,4,3]
[--Prism6 11,2,8,10,5,0,9,7,6,1,4,3] [--Pyramid 3,4,0,2,1]
[--Tetrahedron 2,0,3,1] [--Voxel 1,6,5,4,7,0,2,3]
[--Wedge 3,5,4,0,2,1] --output OUTPUT [--data-mode binary, ascii]

options:
-h, --help show this help message and exit
--Hexahedron 1,6,5,4,7,0,2,3
Expand All @@ -148,11 +187,10 @@ This tool can also be useful to generate a trial mesh that will later be refined

.. code-block::

$ python src/geos/mesh/doctor/mesh_doctor.py generate_cube --help
$ mesh-doctor generate_cube --help
usage: mesh_doctor.py generate_cube [-h] [--x 0:1.5:3] [--y 0:5:10] [--z 0:1] [--nx 2:2] [--ny 1:1] [--nz 4]
[--fields name:support:dim [name:support:dim ...]] [--cells] [--no-cells]
[--points] [--no-points] --output OUTPUT [--data-mode binary, ascii]

options:
-h, --help show this help message and exit
--x 0:1.5:3 [list of floats]: X coordinates of the points.
Expand All @@ -179,10 +217,9 @@ The ``generate_fractures`` module will split the mesh and generate the multi-blo

.. code-block::

$ python src/geos/mesh/doctor/mesh_doctor.py generate_fractures --help
$ mesh-doctor generate_fractures --help
usage: mesh_doctor.py generate_fractures [-h] --policy field, internal_surfaces [--name NAME] [--values VALUES] --output OUTPUT
[--data-mode binary, ascii] [--fractures_output_dir FRACTURES_OUTPUT_DIR]

options:
-h, --help show this help message and exit
--policy field, internal_surfaces
Expand Down Expand Up @@ -210,10 +247,9 @@ The ``generate_global_ids`` can generate `global ids` for the imported ``vtk`` m

.. code-block::

$ python src/geos/mesh/doctor/mesh_doctor.py generate_global_ids --help
$ mesh-doctor generate_global_ids --help
usage: mesh_doctor.py generate_global_ids [-h] [--cells] [--no-cells] [--points] [--no-points] --output OUTPUT
[--data-mode binary, ascii]

options:
-h, --help show this help message and exit
--cells [bool]: Generate global ids for cells. Defaults to true.
Expand All @@ -234,10 +270,9 @@ This module can be a bit time consuming.

.. code-block::

$ python src/geos/mesh/doctor/mesh_doctor.py non_conformal --help
$ mesh-doctor non_conformal --help
usage: mesh_doctor.py non_conformal [-h] [--angle_tolerance 10.0] [--point_tolerance POINT_TOLERANCE]
[--face_tolerance FACE_TOLERANCE]

options:
-h, --help show this help message and exit
--angle_tolerance 10.0 [float]: angle tolerance in degrees. Defaults to 10.0
Expand All @@ -254,9 +289,8 @@ This module will display the elements that have faces intersecting.

.. code-block::

$ python src/geos/mesh/doctor/mesh_doctor.py self_intersecting_elements --help
$ mesh-doctor self_intersecting_elements --help
usage: mesh_doctor.py self_intersecting_elements [-h] [--min 2.220446049250313e-16]

options:
-h, --help show this help message and exit
--min 2.220446049250313e-16
Expand All @@ -275,9 +309,8 @@ It will also verify that the ``VTK_POLYHEDRON`` cells can effectively get conver

.. code-block::

$ python src/geos/mesh/doctor/mesh_doctor.py supported_elements --help
$ mesh-doctor supported_elements --help
usage: mesh_doctor.py supported_elements [-h] [--chunck_size 1] [--nproc 8]

options:
-h, --help show this help message and exit
--chunck_size 1 [int]: Defaults chunk size for parallel processing to 1
Expand Down
4 changes: 0 additions & 4 deletions docs/geos_mesh_docs/home.rst

This file was deleted.

20 changes: 0 additions & 20 deletions docs/geos_mesh_docs/modules.rst

This file was deleted.

26 changes: 26 additions & 0 deletions geos-mesh/src/geos/mesh/doctor/actions/all_checks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from dataclasses import dataclass
from geos.mesh.doctor.register import __load_module_action
from geos.mesh.doctor.parsing.cli_parsing import setup_logger


@dataclass( frozen=True )
class Options:
checks_to_perform: list[ str ]
checks_options: dict[ str, any ]
check_displays: dict[ str, any ]


@dataclass( frozen=True )
class Result:
check_results: dict[ str, any ]


def action( vtk_input_file: str, options: Options ) -> list[ Result ]:
check_results: dict[ str, any ] = dict()
for check_name in options.checks_to_perform:
check_action = __load_module_action( check_name )
setup_logger.info( f"Performing check '{check_name}'." )
option = options.checks_options[ check_name ]
check_result = check_action( vtk_input_file, option )
check_results[ check_name ] = check_result
return Result( check_results=check_results )
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import logging
import numpy
from dataclasses import dataclass
from tqdm import tqdm
Expand All @@ -7,7 +6,8 @@
from vtkmodules.vtkCommonCore import vtkPoints
from vtkmodules.vtkIOXML import vtkXMLMultiBlockDataReader
from vtkmodules.util.numpy_support import vtk_to_numpy
from geos.mesh.doctor.checks.generate_fractures import Coordinates3D
from geos.mesh.doctor.actions.generate_fractures import Coordinates3D
from geos.mesh.doctor.parsing.cli_parsing import setup_logger
from geos.mesh.utils.genericHelpers import vtk_iter


Expand Down Expand Up @@ -113,11 +113,11 @@ def __check_neighbors( matrix: vtkUnstructuredGrid, fracture: vtkUnstructuredGri
if f in fracture_faces:
found += 1
if found != 2:
logging.warning( f"Something went wrong since we should have found 2 fractures faces (we found {found})" +
f" for collocated nodes {cns}." )
setup_logger.warning( "Something went wrong since we should have found 2 fractures faces (we found" +
f" {found}) for collocated nodes {cns}." )


def __check( vtk_input_file: str, options: Options ) -> Result:
def __action( vtk_input_file: str, options: Options ) -> Result:
matrix, fracture = __read_multiblock( vtk_input_file, options.matrix_name, options.fracture_name )
matrix_points: vtkPoints = matrix.GetPoints()
fracture_points: vtkPoints = fracture.GetPoints()
Expand Down Expand Up @@ -148,9 +148,9 @@ def __check( vtk_input_file: str, options: Options ) -> Result:
return Result( errors=errors )


def check( vtk_input_file: str, options: Options ) -> Result:
def action( vtk_input_file: str, options: Options ) -> Result:
try:
return __check( vtk_input_file, options )
return __action( vtk_input_file, options )
except BaseException as e:
logging.error( e )
setup_logger.error( e )
return Result( errors=() )
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from collections import defaultdict
from dataclasses import dataclass
import logging
import numpy
from typing import Collection, Iterable
from vtkmodules.vtkCommonCore import reference, vtkPoints
from vtkmodules.vtkCommonDataModel import vtkIncrementalOctreePointLocator
from geos.mesh.doctor.parsing.cli_parsing import setup_logger
from geos.mesh.io.vtkIO import read_mesh


Expand All @@ -19,7 +19,7 @@ class Result:
wrong_support_elements: Collection[ int ] # Element indices with support node indices appearing more than once.


def __check( mesh, options: Options ) -> Result:
def __action( mesh, options: Options ) -> Result:
points = mesh.GetPoints()

locator = vtkIncrementalOctreePointLocator()
Expand All @@ -38,7 +38,7 @@ def __check( mesh, options: Options ) -> Result:
# If it's not inserted, `point_id` contains the node that was already at that location.
# But in that case, `point_id` is the new numbering in the destination points array.
# It's more useful for the user to get the old index in the original mesh, so he can look for it in his data.
logging.debug(
setup_logger.debug(
f"Point {i} at {points.GetPoint(i)} has been rejected, point {filtered_to_original[point_id.get()]} is already inserted."
)
rejected_points[ point_id.get() ].append( i )
Expand All @@ -63,6 +63,6 @@ def __check( mesh, options: Options ) -> Result:
return Result( nodes_buckets=tmp, wrong_support_elements=wrong_support_elements )


def check( vtk_input_file: str, options: Options ) -> Result:
def action( vtk_input_file: str, options: Options ) -> Result:
mesh = read_mesh( vtk_input_file )
return __check( mesh, options )
return __action( mesh, options )
Loading