Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e7447e4
Move GeosBlockExtractor to geos-mesh
RomainBaville Sep 26, 2025
89c8f0c
Remove getBlockFromName
RomainBaville Sep 26, 2025
f9b2e3f
Refactor using vtkExtractBlock instead of vtkPythonAlgorithmBase
RomainBaville Sep 26, 2025
bcf5d59
Update and clean the doc
RomainBaville Sep 26, 2025
889f59a
Update file using GeosExtractBlock
RomainBaville Sep 26, 2025
22abd05
fix the ci
RomainBaville Oct 6, 2025
a524d2d
Merge branch 'main' into RomainBaville/refactor/MoveGeosBlockExtractor
RomainBaville Oct 8, 2025
063ee86
Merge branch 'main' into RomainBaville/refactor/MoveGeosBlockExtractor
RomainBaville Oct 15, 2025
e577e07
Use dataclass for the extracted domain
RomainBaville Oct 15, 2025
445711f
Update the doc
RomainBaville Oct 15, 2025
7d79d28
Update de doc
RomainBaville Oct 15, 2025
e5835d0
fix AddGeosDomainIndex function
RomainBaville Oct 15, 2025
57c29e2
Merge branch 'main' into RomainBaville/refactor/MoveGeosBlockExtractor
RomainBaville Oct 15, 2025
c81193d
Add the test file and the test mesh
RomainBaville Oct 15, 2025
c22e876
Clarify the variable names and the doc
RomainBaville Oct 22, 2025
70b9358
Merge branch 'main' into RomainBaville/refactor/MoveGeosBlockExtractor
RomainBaville Oct 22, 2025
6a922d3
Test CI labels identification and dispatch
alexbenedicto Oct 23, 2025
14ea06c
Merge branch 'main' into RomainBaville/refactor/MoveGeosBlockExtractor
alexbenedicto Oct 23, 2025
757732f
Fix docs
alexbenedicto Oct 23, 2025
323b315
Revert Test CI labels identification and dispatch to create a dedicat…
alexbenedicto Oct 24, 2025
4fbe721
add a mesh with a well only
RomainBaville Oct 24, 2025
354dae9
Add a function to get the cell dimension of a mesh
RomainBaville Oct 24, 2025
e782d7e
Apply Palomas and Jacques suggestion
RomainBaville Oct 24, 2025
86b09f5
Merge branch 'main' into RomainBaville/refactor/MoveGeosBlockExtractor
RomainBaville Oct 28, 2025
28122b5
move GeosExtractBlock in geos-processing
RomainBaville Oct 28, 2025
57fa533
fix bad move
RomainBaville Oct 28, 2025
0161e2b
update the doc files
RomainBaville Oct 28, 2025
70fcbfb
fix doc
RomainBaville Oct 28, 2025
b17ead9
Apply Jacques suggestion
RomainBaville Oct 31, 2025
7aca9a8
Merge branch 'main' into RomainBaville/refactor/MoveGeosBlockExtractor
RomainBaville Oct 31, 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
8 changes: 8 additions & 0 deletions docs/geos_mesh_docs/processing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ geos.mesh.processing.FillPartialArrays filter
:undoc-members:
:show-inheritance:

geos.mesh.processing.GeosBlockExtractor module
-----------------------------------------------

.. automodule:: geos.mesh.processing.GeosBlockExtractor
:members:
:undoc-members:
:show-inheritance:


geos.mesh.processing.meshQualityMetricHelpers module
-----------------------------------------------------
Expand Down
8 changes: 0 additions & 8 deletions docs/geos_posp_docs/filters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,6 @@ geos_posp.filters.GeomechanicsCalculator module
:undoc-members:
:show-inheritance:

geos_posp.filters.GeosBlockExtractor module
-----------------------------------------------

.. automodule:: geos_posp.filters.GeosBlockExtractor
:members:
:undoc-members:
:show-inheritance:

geos_posp.filters.GeosBlockMerge module
-------------------------------------------

Expand Down
263 changes: 263 additions & 0 deletions geos-mesh/src/geos/mesh/processing/GeosBlockExtractor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies.
# SPDX-FileContributor: Martin Lemay, Romain Baville
import logging
from dataclasses import dataclass
from typing_extensions import Self

from geos.utils.Logger import ( Logger, getLogger )
from geos.utils.GeosOutputsConstants import ( GeosDomainNameEnum )
from geos.mesh.utils.multiblockHelpers import ( getBlockIndexFromName )

from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet
from vtkmodules.vtkFiltersExtraction import vtkExtractBlock

__doc__ = """
GeosBlockExtractor is a vtk filter that allows to extract blocks from the ElementRegions from a GEOS output multiBlockDataset mesh.
The three ElementRegions are:
0: CellElementRegion,
1: SurfaceElementRegion,
2: WellElementRegion,
.. Important::
The input mesh must be an output of a GEOS simulation or contain blocks labeled with the same names.
See more: https://geosx-geosx.readthedocs-hosted.com/en/latest/docs/sphinx/datastructure/ElementRegions.html?_sm_au_=iVVT5rrr5fN00R8sQ0WpHK6H8sjL6#xml-element-elementregions
.. Note::
CellElementRegion is automatically extracted, by defaults SurfaceElementRegion and SurfaceElementRegion are empty multiBlockDataSet.
To use the filter:
.. code-block:: python
from geos.mesh.processing.GeosBlockExtractor import GeosBlockExtractor
# Filter inputs.
geosMesh: vtkMultiBlockDataSet
# Optional inputs.
extractSurface: bool # Defaults to False
extractWell: bool # Defaults to False
speHandler: bool # Defaults to False
# Instantiate the filter
filter: GeosBlockExtractor = GeosBlockExtractor( geosMesh, extractSurface, extractWell, speHandler )
# Set the handler of yours (only if speHandler is True).
yourHandler: logging.Handler
filter.setLoggerHandler( yourHandler )
# Do calculations
filter.applyFilter()
# Get the multiBlockDataSet with blocks of the extracted ElementRegions
elementRegionId: int
elementRegionExtracted: vtkMultiBlockDataSet = filter.getOutput( elementRegionId )
"""

loggerTitle: str = "Geos Block Extractor Filter"


class GeosExtractElementRegionsBlock( vtkExtractBlock ):

def __init__( self: Self ) -> None:
"""Extract ElementRegions block from a GEOS output multiBlockDataset mesh."""
super().__init__()

self.geosElementRegionsName: dict[ int, str ] = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This I think is an enum and eventhough should be class var and not instance var :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is indeed an enum and it is already defined in GeosOutputsConstants. I don't need to rewrite it.

0: GeosDomainNameEnum.VOLUME_DOMAIN_NAME.value,
1: GeosDomainNameEnum.FAULT_DOMAIN_NAME.value,
2: GeosDomainNameEnum.WELL_DOMAIN_NAME.value,
}

def GetGeosElementRegionsName( self: Self, elementRegionId: int ) -> str:
"""Get the name of the GEOS ElementRegions from its index.
Args:
elementRegionId (int): The index of the GEOS ElementRegions.
Returns:
str: The name of the GEOS ElementRegions.
"""
return self.geosElementRegionsName[ elementRegionId ]

def AddGeosElementRegionsBlockIndex( self, elementRegionId: int ) -> None:
"""Add the index of the wanted GEOS ElementRegions to extract.
The GEOS ElementRegions indexes are:
0: CellElementRegion,
1: SurfaceElementRegion,
2: WellElementRegion,
Args:
elementRegionId (int): Index of the GEOS ElementRegions to extract.
"""
elementRegionsBlockIndex: int = getBlockIndexFromName( self.GetInput(),
self.geosElementRegionsName[ elementRegionId ] )
return super().AddIndex( elementRegionsBlockIndex )


class GeosBlockExtractor:

@dataclass
class ExtractedElementRegionsMesh:
"""The dataclass with the three GEOS ElementRegions mesh."""
_cell: vtkMultiBlockDataSet = vtkMultiBlockDataSet()
_surface: vtkMultiBlockDataSet = vtkMultiBlockDataSet()
_well: vtkMultiBlockDataSet = vtkMultiBlockDataSet()

@property
def cell( self: Self ) -> vtkMultiBlockDataSet:
"""Get the mesh with the blocks of the GEOS CellElementRegion."""
return self._cell

@cell.setter
def cell( self: Self, multiBlockDataSet: vtkMultiBlockDataSet ) -> None:
self._cell.DeepCopy( multiBlockDataSet )

@property
def surface( self: Self ) -> vtkMultiBlockDataSet:
"""Get the mesh with the blocks of the GEOS SurfaceElementRegion."""
return self._surface

@surface.setter
def surface( self: Self, multiBlockDataSet: vtkMultiBlockDataSet ) -> None:
self._surface.DeepCopy( multiBlockDataSet )

@property
def well( self: Self ) -> vtkMultiBlockDataSet:
"""Get the mesh with the blocks of the GEOS WellElementRegion."""
return self._well

@well.setter
def well( self: Self, multiBlockDataSet: vtkMultiBlockDataSet ) -> None:
self._well.DeepCopy( multiBlockDataSet )

def getExtractedElementRegions( self: Self, elementRegionId: int ) -> vtkMultiBlockDataSet:
"""Get the GEOS ElementRegions mesh extracted from its index.
The GEOS ElementRegions indexes are:
0: CellElementRegion,
1: SurfaceElementRegion,
2: WellElementRegion,
Args:
elementRegionId (int): Index of the GEOS ElementRegions to get.
Returns:
vtkMultiBlockDataSet: The mesh with the GEOS ElementRegions blocks.
"""
if elementRegionId == 0:
return self.cell
elif elementRegionId == 1:
return self.surface
elif elementRegionId == 2:
return self.well
else:
raise IndexError

def setExtractedElementRegions( self: Self, elementRegionId: int,
multiBlockDataSet: vtkMultiBlockDataSet ) -> None:
"""Set the mesh to the correct ElementRegions.
Args:
elementRegionId (int): Index of the GEOS ElementRegions.
multiBlockDataSet (vtkMultiBlockDataSet): The mesh to set.
"""
if elementRegionId == 0:
self.cell = multiBlockDataSet
elif elementRegionId == 1:
self.surface = multiBlockDataSet
elif elementRegionId == 2:
self.well = multiBlockDataSet

extractedElementRegions: ExtractedElementRegionsMesh
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should it be accessible outside of the class ? If not then decorate it with @property

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It needs to be accessible outside of the class to get the result of the extraction


def __init__(
self: Self,
geosMesh: vtkMultiBlockDataSet,
extractSurface: bool = False,
extractWell: bool = False,
speHandler: bool = False,
) -> None:
"""Blocks from the ElementRegions from a GEOS output multiBlockDataset mesh.
Args:
geosMesh (vtkMultiBlockDataSet): The mesh from Geos.
extractSurface (bool, Optional): True if SurfaceElementRegion needs to be extracted, False otherwise.
Defaults to False.
extractWell (bool, Optional): True if WellElementRegion needs to be extracted, False otherwise.
Defaults to False.
speHandler (bool, optional): True to use a specific handler, False to use the internal handler.
Defaults to False.
"""
self.geosMesh: vtkMultiBlockDataSet = geosMesh
self.extractedElementRegions = self.ExtractedElementRegionsMesh()

self.elementRegionsIdToExtract: list[ int ] = [ 0 ]
if extractSurface:
self.elementRegionsIdToExtract.append( 1 )
if extractWell:
self.elementRegionsIdToExtract.append( 2 )

# Logger.
self.logger: Logger
if not speHandler:
self.logger = getLogger( loggerTitle, True )
else:
self.logger = logging.getLogger( loggerTitle )
self.logger.setLevel( logging.INFO )

def setLoggerHandler( self: Self, handler: logging.Handler ) -> None:
"""Set a specific handler for the filter logger.
In this filter 4 log levels are use, .info, .error, .warning and .critical, be sure to have at least the same 4 levels.
Args:
handler (logging.Handler): The handler to add.
"""
if not self.logger.hasHandlers():
self.logger.addHandler( handler )
else:
self.logger.warning(
"The logger already has an handler, to use yours set the argument 'speHandler' to True during the filter initialization."
)

def applyFilter( self: Self ) -> bool:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If returned value is not used then maybe drop it, else maybe raise an error with diagnostic

"""Extract the volume, the surface or the well domain of the mesh from Geos.
Returns:
bool: True if calculation successfully ended, False otherwise.
"""
extractElementRegions: GeosExtractElementRegionsBlock = GeosExtractElementRegionsBlock()
extractElementRegions.SetInputData( self.geosMesh )

for elementRegionId in self.elementRegionsIdToExtract:
extractElementRegions.RemoveAllIndices()
extractElementRegions.AddGeosElementRegionsBlockIndex( elementRegionId )
extractElementRegions.Update()
self.extractedElementRegions.setExtractedElementRegions( elementRegionId,
extractElementRegions.GetOutput() )
if self.extractedElementRegions.getExtractedElementRegions( elementRegionId ).GetNumberOfBlocks() == 0:
self.logger.error(
f"The input mesh does not have { extractElementRegions.GetGeosElementRegionsName( elementRegionId ) } to extract."
)
return False

return True

def getOutput( self: Self, elementRegionId: int ) -> vtkMultiBlockDataSet:
"""Get the GEOS ElementRegions extracted from its index.
The GEOS ElementRegions indexes are:
0: CellElementRegion,
1: SurfaceElementRegion,
2: WellElementRegion,
Args:
elementRegionId (int): Index of the GEOS ElementRegions to get.
Returns:
vtkMultiBlockDataSet: The GEOS ElementRegions mesh extracted.
"""
return self.extractedElementRegions.getExtractedElementRegions( elementRegionId )
23 changes: 0 additions & 23 deletions geos-mesh/src/geos/mesh/utils/multiblockHelpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,29 +190,6 @@ def getBlockFromFlatIndex( multiBlockDataSet: Union[ vtkMultiBlockDataSet, vtkCo
return None


def getBlockFromName( multiBlockDataSet: Union[ vtkMultiBlockDataSet, vtkCompositeDataSet ],
blockName: str ) -> Union[ None, vtkDataObject ]:
"""Get the block named blockName from the vtkMultiBlockDataSet.

Args:
multiBlockDataSet (vtkMultiBlockDataSet | vtkCompositeDataSet): MultiBlockDataSet with the block to get.
blockName (str): The name of the block to get.

Returns:
Union[None, vtkDataObject]: The block name blockName if it exists, None otherwise
"""
# initialize data object tree iterator
iterator: vtkDataObjectTreeIterator = vtkDataObjectTreeIterator()
iterator.SetDataSet( multiBlockDataSet )
iterator.VisitOnlyLeavesOff()
iterator.GoToFirstItem()
while iterator.GetCurrentDataObject() is not None:
if iterator.GetCurrentMetaData().Get( vtkMultiBlockDataSet.NAME() ) == blockName:
return iterator.GetCurrentDataObject()
iterator.GoToNextItem()
return None


def extractBlock( multiBlockDataSet: vtkMultiBlockDataSet, blockIndex: int ) -> vtkMultiBlockDataSet:
"""Extract the block with index blockIndex from multiBlockDataSet.

Expand Down
2 changes: 2 additions & 0 deletions geos-mesh/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ def _get_dataset( datasetType: str ) -> Union[ vtkMultiBlockDataSet, vtkPolyData
vtkFilename = "data/fracture_res5_id.vtp"
elif datasetType == "emptypolydata":
vtkFilename = "data/fracture_res5_id_empty.vtp"
elif datasetType == "meshGeosExtractBlockTmp":
vtkFilename = "data/meshGeosExtractBlockTmp.vtm"
datapath: str = os.path.join( os.path.dirname( os.path.realpath( __file__ ) ), vtkFilename )
reader.SetFileName( datapath )
reader.Update()
Expand Down
16 changes: 16 additions & 0 deletions geos-mesh/tests/data/meshGeosExtractBlockTmp.vtm
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0"?>
<VTKFile type="vtkMultiBlockDataSet" version="1.0">
<vtkMultiBlockDataSet>
<Block name="CellElementRegion">
<DataSet name="domain" file="domain_res5_id.vtu"/>
<DataSet name="emptyDomain" file="domain_res5_id_empty.vtu"/>
</Block>
<Block name="SurfaceElementRegion">
<DataSet name="fracture" file="fracture_res5_id.vtu"/>
<DataSet name="emptyFracture" file="fracture_res5_id_empty.vtu"/>
</Block>
<Block name="WellElementRegion">
<DataSet name="triangulatedSurface" file="triangulatedSurface.vtu"/>
</Block>
</vtkMultiBlockDataSet>
</VTKFile>
40 changes: 40 additions & 0 deletions geos-mesh/tests/test_GeosExtractBlock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies.
# SPDX-FileContributor: Romain Baville
# SPDX-License-Identifier: Apache 2.0
# ruff: noqa: E402 # disable Module level import not at top of file
# mypy: disable-error-code="operator"
import pytest

from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet
from geos.mesh.processing.GeosBlockExtractor import GeosBlockExtractor


@pytest.mark.parametrize( "extractFaults, extractWells", [
( False, False ),
( True, False ),
( False, True ),
( True, True ),
] )
def test_GeosExtractBlock(
dataSetTest: vtkMultiBlockDataSet,
extractFaults: bool,
extractWells: bool,
) -> None:
"""Test GeosExtractBlock vtk filter."""
multiBlockDataSet: vtkMultiBlockDataSet = dataSetTest( "meshGeosExtractBlockTmp" )

filter: GeosBlockExtractor = GeosBlockExtractor( multiBlockDataSet, extractFaults, extractWells )
assert filter.applyFilter()

extractVolume: vtkMultiBlockDataSet = filter.getOutput( 0 )
extractFault: vtkMultiBlockDataSet = filter.getOutput( 1 )
extractWell: vtkMultiBlockDataSet = filter.getOutput( 2 )

assert extractVolume.GetNumberOfBlocks() == 2

if extractFaults:
assert extractFault.GetNumberOfBlocks() == 2

if extractWells:
assert extractWell.GetNumberOfBlocks() == 1
Loading
Loading