Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
06eca95
Modify isAttributeGlobal function to take into account recursive mult…
paloma-martinez Sep 2, 2025
8651018
Add bool output to mergeBlocks function for better logging and error …
paloma-martinez Sep 2, 2025
fbe6bf2
Adding a VTK filter for enhanced merge block
paloma-martinez Sep 2, 2025
b9855ce
Move and update plugin
paloma-martinez Sep 2, 2025
21d8b7d
Merge branch 'main' into pmartinez/refactor/createMergeBlockEnhancedV…
paloma-martinez Sep 12, 2025
54c70eb
Clean
paloma-martinez Sep 17, 2025
2de326a
Fix import format
paloma-martinez Sep 17, 2025
3d20b99
Typing
paloma-martinez Sep 17, 2025
22828f7
Adding tests and data for test for merge blocks
paloma-martinez Sep 17, 2025
29a038b
Documentation
paloma-martinez Sep 17, 2025
3d153cd
Yapf
paloma-martinez Sep 17, 2025
af3990e
Yapf again
paloma-martinez Sep 17, 2025
d9f289d
Yapf
paloma-martinez Sep 17, 2025
14bcb85
Fix docstring and typing
paloma-martinez Sep 17, 2025
05a1c17
Fix from Romain review
paloma-martinez Sep 18, 2025
df24923
Merge branch 'main' into pmartinez/refactor/createMergeBlockEnhancedV…
paloma-martinez Sep 18, 2025
f771dd6
Modification of mergeBlocks function following review's comment
paloma-martinez Sep 29, 2025
4d9c3d0
Merge branch 'main' into pmartinez/refactor/createMergeBlockEnhancedV…
paloma-martinez Sep 29, 2025
b3112f9
yapf
paloma-martinez Sep 29, 2025
b1b681b
bad merge fix
paloma-martinez Sep 29, 2025
682f50e
first error handling version
jafranc Oct 3, 2025
7da34e9
adding Fails test
jafranc Oct 6, 2025
43dd2d1
First completed attempt to capture errors from VTK
jafranc Oct 7, 2025
396ed11
last fixes
jafranc Oct 7, 2025
e0ab6fc
ruff + yapf
jafranc Oct 7, 2025
ba9e2b1
yapf tests
jafranc Oct 7, 2025
f8d2b1a
Update python-package.yml as order might matters
jafranc Oct 7, 2025
b8b1481
clean up
jafranc Oct 7, 2025
cd48656
Merge branch 'pmartinez/refactor/createMergeBlockEnhancedVTKFilter' o…
jafranc Oct 8, 2025
b839532
CI is testing vtk9.5.1 so skip the failing test for now
jafranc Oct 8, 2025
262932a
mypy and yapf
jafranc Oct 8, 2025
b98b49e
wrong version dispatch
jafranc Oct 8, 2025
073f4cd
Clean and formatting
paloma-martinez Oct 16, 2025
3d0c57c
Merge branch 'main' into pmartinez/refactor/createMergeBlockEnhancedV…
paloma-martinez Oct 16, 2025
3a7b061
bad merge
paloma-martinez Oct 16, 2025
907b2d6
Force int type to avoid errors
alexbenedicto Oct 16, 2025
24a8d27
Add comments to new Logger functionalities
alexbenedicto Oct 16, 2025
f9b5cf0
Correct invalid pattern
alexbenedicto Oct 16, 2025
df766cd
Fix error in doc
alexbenedicto Oct 17, 2025
d572700
Fix ruff error
alexbenedicto Oct 17, 2025
18b13f7
Add child logger to prevent to much verbosity in parent logger
paloma-martinez Oct 17, 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
11 changes: 10 additions & 1 deletion docs/geos_mesh_docs/processing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,13 @@ geos.mesh.processing.SplitMesh filter
.. automodule:: geos.mesh.processing.SplitMesh
:members:
:undoc-members:
:show-inheritance:
:show-inheritance:


geos.mesh.processing.MergeBlockEnhanced filter
------------------------------------------------

.. automodule:: geos.mesh.processing.MergeBlockEnhanced
:members:
:undoc-members:
:show-inheritance:
5 changes: 0 additions & 5 deletions docs/geos_posp_docs/PVplugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,6 @@ PVGeomechanicsWorkflowVolumeWell plugin

.. automodule:: PVplugins.PVGeomechanicsWorkflowVolumeWell

PVplugins.PVMergeBlocksEnhanced module
--------------------------------------

.. automodule:: PVplugins.PVMergeBlocksEnhanced


PVMohrCirclePlot plugin
---------------------------------
Expand Down
6 changes: 6 additions & 0 deletions docs/geos_pv_docs/processing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@ PVSplitMesh
----------------------------------

.. automodule:: geos.pv.plugins.PVSplitMesh


PVMergeBlocksEnhanced module
--------------------------------------

.. automodule:: geos.pv.plugins.PVMergeBlocksEnhanced
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies.
# SPDX-FileContributor: Romain Baville
import numpy as np
import numpy.typing as npt
import logging

import numpy.typing as npt
from typing import Union, Any
from typing_extensions import Self

Expand All @@ -13,7 +14,7 @@
vtkDataSet,
)

from geos.utils.Logger import ( getLogger, Logger, logging, CountWarningHandler )
from geos.utils.Logger import getLogger, Logger, CountWarningHandler
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's keep parenthesis when doing multiple import other than standard

Suggested change
from geos.utils.Logger import getLogger, Logger, CountWarningHandler
from geos.utils.Logger import ( getLogger, Logger, CountWarningHandler )

from geos.mesh.utils.arrayHelpers import ( getArrayInObject, getComponentNames, getNumberOfComponents,
getVtkDataTypeInObject, isAttributeGlobal, getAttributePieceInfo,
checkValidValuesInDataSet, checkValidValuesInMultiBlock )
Expand Down
138 changes: 138 additions & 0 deletions geos-mesh/src/geos/mesh/processing/MergeBlockEnhanced.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies.
# SPDX-FileContributor: Paloma Martinez
# ruff: noqa: E402 # disable Module level import not at top of file
import logging

from typing import Union
from typing_extensions import Self

from geos.utils.Logger import Logger, getLogger
from geos.mesh.utils.multiblockModifiers import mergeBlocks

from vtkmodules.vtkCommonDataModel import (
vtkCompositeDataSet,
vtkMultiBlockDataSet,
vtkUnstructuredGrid,
)

__doc__ = """
Merge Blocks Keeping Partial Attributes is a filter that allows to merge blocks from a multiblock dataset while keeping partial attributes.
Input is a vtkMultiBlockDataSet and output is a vtkUnstructuredGrid.
.. Note::
- You may encounter issues if two datasets of the input multiblock dataset have duplicated cell IDs.
- Partial attributes are filled with default values depending on their types.
- 0 for uint data.
- -1 for int data.
- nan for float data.
To use it:
.. code-block:: python
from geos.mesh.processing.MergeBlockEnhanced import MergeBlockEnhanced
# Define filter inputs
multiblockdataset: vtkMultiblockDataSet
speHandler: bool # optional
# Instantiate the filter
filter: MergeBlockEnhanced = MergeBlockEnhanced( multiblockdataset, speHandler )
# Use your own handler (if speHandler is True)
yourHandler: logging.Handler
filter.addLoggerHandler( yourHandler )
# Do calculations
filter.applyFilter()
# Get the merged mesh
filter.getOutput()
"""

loggerTitle: str = "Merge Block Enhanced"


class MergeBlockEnhanced:

def __init__(
self: Self,
inputMesh: vtkMultiBlockDataSet,
speHandler: bool = False,
) -> None:
"""Merge a multiblock dataset and keep the partial attributes in the output mesh.
Partial attributes are filled with default values depending on the data type such that:
- 0 for uint data.
- -1 for int data.
- nan for float data.
Args:
inputMesh (vtkMultiBlockDataSet): The input multiblock dataset to merge.
speHandler (bool, optional) : True to use a specific handler, False to use the internal handler.
Defaults to False.
"""
self.inputMesh: vtkMultiBlockDataSet = inputMesh
self.outputMesh: vtkUnstructuredGrid = vtkUnstructuredGrid()

# 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:
"""Merge the blocks of a multiblock dataset mesh.
Returns:
bool: True if the blocks were successfully merged, False otherwise.
"""
self.logger.info( f"Applying filter { self.logger.name }." )

if not isinstance( self.inputMesh, vtkCompositeDataSet ) or not isinstance( self.inputMesh,
vtkMultiBlockDataSet ):
self.logger.error(
f"Expected a vtkMultiblockdataset or vtkCompositeDataSet, Got a {type(self.inputMesh)} \n The filter { self.logger.name } failed."
)
return False

success: bool
outputMesh: Union[ vtkUnstructuredGrid, vtkMultiBlockDataSet, vtkCompositeDataSet ]
success, outputMesh = mergeBlocks( self.inputMesh, True, self.logger )
Copy link
Collaborator

Choose a reason for hiding this comment

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

Following the comments made on mergeBlocks function, a try/except block can be used here to evaluate the success of the mergeBlocks function


if not success:
self.logger.error( f"The filter {self.logger.name} failed." )
return False

else:
self.logger.info( f"The filter { self.logger.name } succeeded." )
self.outputMesh = outputMesh
return True

def getOutput( self: Self ) -> vtkUnstructuredGrid:
"""Get the merged mesh.
Returns:
vtkUnstructuredGrid: The merged mesh.
"""
return self.outputMesh
1 change: 0 additions & 1 deletion geos-mesh/src/geos/mesh/utils/arrayHelpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,6 @@ def isAttributeGlobal( multiBlockDataSet: vtkMultiBlockDataSet, attributeName: s
dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) )
if not isAttributeInObjectDataSet( dataSet, attributeName, onPoints ):
return False

return True


Expand Down
59 changes: 44 additions & 15 deletions geos-mesh/src/geos/mesh/utils/multiblockModifiers.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,74 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies.
# SPDX-FileContributor: Martin Lemay
# SPDX-FileContributor: Martin Lemay, Paloma Martinez
from typing import Union
from vtkmodules.vtkCommonDataModel import ( vtkCompositeDataSet, vtkDataObjectTreeIterator, vtkMultiBlockDataSet,
vtkUnstructuredGrid )
from vtkmodules.vtkFiltersCore import vtkAppendDataSets
from geos.mesh.utils.arrayModifiers import fillAllPartialAttributes
from geos.utils.Logger import getLogger, Logger
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
from geos.utils.Logger import getLogger, Logger
from geos.utils.Logger import ( getLogger, Logger )


__doc__ = """Contains a method to merge blocks of a VTK multiblock dataset."""


# TODO : fix function for keepPartialAttributes = True
def mergeBlocks(
input: Union[ vtkMultiBlockDataSet, vtkCompositeDataSet ],
inputMesh: Union[ vtkMultiBlockDataSet, vtkCompositeDataSet ],
keepPartialAttributes: bool = False,
logger: Union[ Logger, None ] = None,
) -> vtkUnstructuredGrid:
"""Merge all blocks of a multi block mesh.
"""Merge all blocks of a multiblock dataset mesh with the possibility of keeping all partial attributes present in the initial mesh.
Args:
input (vtkMultiBlockDataSet | vtkCompositeDataSet ): composite
object to merge blocks
keepPartialAttributes (bool): if True, keep partial attributes after merge.
Defaults to False.
inputMesh (vtkMultiBlockDataSet | vtkCompositeDataSet ): The input multiblock dataset to merge.
keepPartialAttributes (bool): If False (default), only global attributes are kept during the merge. If True, partial attributes are filled with default values and kept in the output mesh.
logger (Union[Logger, None], optional): A logger to manage the output messages.
Defaults to None, an internal logger is used.
Returns:
vtkUnstructuredGrid: merged block object
bool: True if the mesh was correctly merged. False otherwise
vtkUnstructuredGrid: Merged dataset if success, empty dataset otherwise.
.. Note::
Default filling values:
- 0 for uint data.
- -1 for int data.
- nan for float data.
.. Warning:: This function will not work properly if there are duplicated cell IDs in the different blocks of the input mesh.
"""
if keepPartialAttributes:
fillAllPartialAttributes( input )
if logger is None:
logger = getLogger( "mergeBlocks", True )

outputMesh = vtkUnstructuredGrid()

if not inputMesh.IsA( "vtkMultiBlockDataSet" ) and not inputMesh.IsA( "vtkCompositeDataSet" ):
logger.error(
"The input mesh should be either a vtkMultiBlockDataSet or a vtkCompositeDataSet. Cannot proceed with the block merge."
)
return False, outputMesh

af = vtkAppendDataSets()
if inputMesh.IsA( "vtkDataSet" ):
logger.error( "The input mesh is already a single block. Cannot proceed with the block merge." )
return False, outputMesh

# Fill the partial attributes with default values to keep them during the merge.
if keepPartialAttributes and not fillAllPartialAttributes( inputMesh, logger ):
logger.error( "Failed to fill partial attributes. Cannot proceed with the block merge." )
return False, outputMesh

af: vtkAppendDataSets = vtkAppendDataSets()
af.MergePointsOn()
iter: vtkDataObjectTreeIterator = vtkDataObjectTreeIterator()
iter.SetDataSet( input )
iter.SetDataSet( inputMesh )
iter.VisitOnlyLeavesOn()
iter.GoToFirstItem()
while iter.GetCurrentDataObject() is not None:
block: vtkUnstructuredGrid = vtkUnstructuredGrid.SafeDownCast( iter.GetCurrentDataObject() )
af.AddInputData( block )
iter.GoToNextItem()
af.Update()
return af.GetOutputDataObject( 0 )

outputMesh.ShallowCopy( af.GetOutputDataObject( 0 ) )

return True, outputMesh
4 changes: 4 additions & 0 deletions geos-mesh/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ def _get_dataset( datasetType: str ) -> Union[ vtkMultiBlockDataSet, vtkPolyData
elif datasetType == "emptymultiblock":
reader: vtkXMLMultiBlockDataReader = vtkXMLMultiBlockDataReader()
vtkFilename = "data/displacedFaultempty.vtm"
elif datasetType == "multiblockGeosOutput":
# adapted from example GEOS/inputFiles/compositionalMultiphaseWell/simpleCo2InjTutorial_smoke.xml
reader: vtkXMLMultiBlockDataReader = vtkXMLMultiBlockDataReader()
vtkFilename = "data/simpleReservoirViz_small_000478.vtm"
elif datasetType == "dataset":
reader: vtkXMLUnstructuredGridReader = vtkXMLUnstructuredGridReader()
vtkFilename = "data/domain_res5_id.vtu"
Expand Down
20 changes: 20 additions & 0 deletions geos-mesh/tests/data/simpleReservoirViz_small_000478.vtm
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<VTKFile type="vtkMultiBlockDataSet" version="1.0">
<vtkMultiBlockDataSet>
<Block name="cartesianMesh">
<Block name="Level0">
<Block name="CellElementRegion">
<Block name="reservoir">
<DataSet name="rank_0" file="simpleReservoirViz_small_000478/cartesianMesh/Level0/reservoir/rank_0.vtu" />
<DataSet name="rank_1" file="simpleReservoirViz_small_000478/cartesianMesh/Level0/reservoir/rank_1.vtu" />
</Block>
</Block>
<Block name="WellElementRegion">
<Block name="wellRegion">
<DataSet name="rank_1" file="simpleReservoirViz_small_000478/cartesianMesh/Level0/wellRegion/rank_1.vtu" />
</Block>
</Block>
</Block>
</Block>
</vtkMultiBlockDataSet>
</VTKFile>
Loading