Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 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
254 changes: 254 additions & 0 deletions geos-mesh/src/geos/mesh/processing/GeosBlockExtractor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
# 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 from an output Geos multiBlockDataSet
all the blocks in a multiBlockDataSet of a Geos domain with its index.
There is tree domains:
0: all the blocks referring to volume,
1: all the blocks referring to faults,
2: all the blocks referring to wells,
.. Important::
The input mesh must be an output of a Geos simulation or have the same domain name.
.. Note::
The volume domain is automatically extracted, by defaults the fault and well domain are empty multiBlockDataSet.
To use the filter:
.. code-block:: python
from geos.mesh.processing.GeosBlockExtractor import GeosBlockExtractor
# Filter inputs.
geosMesh: vtkMultiBlockDataSet
# Optional inputs.
extractFaults: bool # Defaults to False
extractWells: bool # Defaults to False
speHandler: bool # Defaults to False
# Instantiate the filter
filter: GeosBlockExtractor = GeosBlockExtractor( geosMesh, extractFaults, extractWells, speHandler )
# Set the handler of yours (only if speHandler is True).
yourHandler: logging.Handler
filter.setLoggerHandler( yourHandler )
# Do calculations
filter.applyFilter()
# Get the mesh of the wanted extracted domain
domainId: int
domainExtracted: vtkMultiBlockDataSet = filter.getOutput( domainId )
"""

loggerTitle: str = "Geos Block Extractor Filter"


class GeosExtractDomain( vtkExtractBlock ):

def __init__( self: Self ) -> None:
"""Extract domain (volume, fault, well) from geos output."""
super().__init__()

self.geosDomainName: dict[ int, str ] = {
0: GeosDomainNameEnum.VOLUME_DOMAIN_NAME.value,
1: GeosDomainNameEnum.FAULT_DOMAIN_NAME.value,
2: GeosDomainNameEnum.WELL_DOMAIN_NAME.value,
}

def GetGeosDomainName( self: Self, domainId: int ) -> str:
"""Get the name from the Geos domain index.
Args:
domainId (int): The index of the Geos domain.
Returns:
str: The name of the Geos domain.
"""
return self.geosDomainName[ domainId ]

def AddGeosDomainIndex( self, domainId: int ) -> None:
"""Add the index of the wanted Geos domain block to extract.
The domain type to extract are:
- Volumes -> domain index = 0
- Faults -> domain index = 1
- Wells -> domain index = 2
Args:
domainId (int): Index of the Geos domain to extract.
"""
blockIndex: int = getBlockIndexFromName( self.GetInput(), self.geosDomainName[ domainId ] )
print( domainId )
return super().AddIndex( blockIndex )


class GeosBlockExtractor:

@dataclass
class ExtractDomain:
"""The dataclass with the three Geos domains (volume, fault, well)."""
_volume: vtkMultiBlockDataSet = vtkMultiBlockDataSet()
_fault: vtkMultiBlockDataSet = vtkMultiBlockDataSet()
_well: vtkMultiBlockDataSet = vtkMultiBlockDataSet()

@property
def volume( self: Self ) -> vtkMultiBlockDataSet:
"""Get the mesh corresponding to the Geos volume domain."""
return self._volume

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

@property
def fault( self: Self ) -> vtkMultiBlockDataSet:
"""Get the mesh corresponding to the Geos fault domain."""
return self._fault

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

@property
def well( self: Self ) -> vtkMultiBlockDataSet:
"""Get the mesh corresponding to the Geos well domain."""
return self._well

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

def getExtractDomain( self: Self, domainId: int ) -> vtkMultiBlockDataSet:
"""Get the mesh for the correct domain.
Args:
domainId (int): The index of the Geos domain to get.
Returns:
vtkMultiBlockDataSet: The mesh with the Geos domain.
"""
if domainId == 0:
return self.volume
elif domainId == 1:
return self.fault
elif domainId == 2:
return self.well
else:
raise IndexError

def setExtractDomain( self: Self, domainId: int, multiBlockDataSet: vtkMultiBlockDataSet ) -> None:
"""Set the mesh to the correct domain.
Args:
domainId (int): The index of the Geos domain.
multiBlockDataSet (vtkMultiBlockDataSet): The mesh to set.
"""
if domainId == 0:
self.volume = multiBlockDataSet
elif domainId == 1:
self.fault = multiBlockDataSet
elif domainId == 2:
self.well = multiBlockDataSet

extractDomain: ExtractDomain = ExtractDomain()

def __init__(
self: Self,
geosMesh: vtkMultiBlockDataSet,
extractFaults: bool = False,
extractWells: bool = False,
speHandler: bool = False,
) -> None:
"""Extract the volume, the surface or the well domain block of the mesh from Geos.
Args:
geosMesh (vtkMultiBlockDataSet): The mesh from Geos.
extractFaults (bool, Optional): True if the mesh contains Faults to extract, False otherwise.
Defaults to False.
extractWells (bool, Optional): True if the mesh contains wells to extract, 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.domainIdToExtract: list[ int ] = [ 0 ]
if extractFaults:
self.domainIdToExtract.append( 1 )
if extractWells:
self.domainIdToExtract.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.
"""
extractBlock: GeosExtractDomain = GeosExtractDomain()
extractBlock.SetInputData( self.geosMesh )

for domainId in self.domainIdToExtract:
extractBlock.RemoveAllIndices()
extractBlock.AddGeosDomainIndex( domainId )
extractBlock.Update()
self.extractDomain.setExtractDomain( domainId, extractBlock.GetOutput() )
if self.extractDomain.getExtractDomain( domainId ).GetNumberOfBlocks() == 0:
self.logger.error(
f"The input mesh does not have { extractBlock.GetGeosDomainName( domainId ) } to extract." )
return False

return True

def getOutput( self: Self, domainId: int ) -> vtkMultiBlockDataSet:
"""Get the Geos domain extracted.
The domain extracted are:
- Volumes -> domain index = 0
- Faults -> domain index = 1
- Wells -> domain index = 2
Args:
domainId (int): The index of the Geos domain to get.
Returns:
vtkMultiBlockDataSet: The Geos domain extracted.
"""
return self.extractDomain.getExtractDomain( domainId )
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 @@ -175,6 +175,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