Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
29 changes: 19 additions & 10 deletions geos-mesh/src/geos/mesh/utils/multiblockModifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
vtkUnstructuredGrid, vtkDataSet )
from packaging.version import Version
from vtkmodules.vtkCommonCore import vtkLogger
from geos.utils.Errors import VTKError

# TODO: remove this condition when all codes are adapted for VTK newest version.
import vtk
Expand Down Expand Up @@ -50,20 +51,21 @@ def mergeBlocks(
.. Warning:: This function will not work properly if there are duplicated cell IDs in the different blocks of the input mesh.

"""
vtkErrorLogger: Logger
if logger is None:
logger = getLogger( "mergeBlocks" )
# Creation of a child logger to deal with VTKErrors without polluting parent logger
mbLogger: Logger = getLogger( f"{logger.name}.vtkErrorLogger" )

mbLogger.propagate = False
vtkErrorLogger = getLogger( "Merge blocks", True )
else:
vtkErrorLogger = logging.getLogger( f"{ logger.name } vtkError Logger" )
vtkErrorLogger.setLevel( logging.INFO )
vtkErrorLogger.addHandler( logger.handlers[ 0 ] )
vtkErrorLogger.propagate = False

vtkLogger.SetStderrVerbosity( vtkLogger.VERBOSITY_ERROR )
mbLogger.addFilter( RegexExceptionFilter() ) # will raise VTKError if captured VTK Error
mbLogger.setLevel( logging.DEBUG )
vtkErrorLogger.addFilter( RegexExceptionFilter() ) # will raise VTKError if captured VTK Error

# Fill the partial attributes with default values to keep them during the merge.
if keepPartialAttributes and not fillAllPartialAttributes( inputMesh, logger ):
logger.warning( "Failed to fill partial attributes. Merging without keeping partial attributes." )
raise ValueError( "Failed to fill partial attributes. Merging without keeping partial attributes." )

outputMesh: vtkUnstructuredGrid

Expand All @@ -76,9 +78,10 @@ def mergeBlocks(

else:
if inputMesh.IsA( "vtkDataSet" ):
logger.warning( "Input mesh is already a single block." )
vtkErrorLogger.warning( "Input mesh is already a single block." )
outputMesh = vtkUnstructuredGrid.SafeDownCast( inputMesh )
else:

with VTKCaptureLog() as captured_log:

af: vtkAppendDataSets = vtkAppendDataSets()
Expand All @@ -95,8 +98,14 @@ def mergeBlocks(
captured_log.seek( 0 )
captured = captured_log.read().decode()

mbLogger.debug( captured.strip() )
if captured != "":
vtkErrorLogger.error( captured.strip() )
# raise VTKError( captured.strip() )
# pass

outputMesh = af.GetOutputDataObject( 0 )

if outputMesh is None:
raise VTKError( "Something went wrong in VTK" )

return outputMesh
17 changes: 17 additions & 0 deletions geos-mesh/tests/test_multiblockModifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet, vtkUnstructuredGrid
from geos.mesh.utils import multiblockModifiers

from unittest import TestCase
from geos.utils.Errors import VTKError

import vtk
from packaging.version import Version


@pytest.mark.parametrize( "keepPartialAttributes, nbPointAttributes, nbCellAttributes, nbFieldAttributes", [
( False, 0, 16, 1 ),
Expand All @@ -35,3 +41,14 @@ def test_mergeBlocks(

assert dataset.GetFieldData().GetNumberOfArrays(
) == nbFieldAttributes, f"Expected {nbFieldAttributes} field attributes after the merge, not {dataset.GetFieldData().GetNumberOfArrays()}."


class RaiseMergeBlocks( TestCase ):
"""Test failure on empty multiBlockDataSet."""

def test_TypeError( self ) -> None:
"""Test raise of TypeError."""
multiBlockDataset = vtkMultiBlockDataSet() # should fail on empty data
if Version( vtk.__version__ ) < Version( "9.5" ):
with pytest.raises( VTKError ):
multiblockModifiers.mergeBlocks( multiBlockDataset, True )
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def __init__(
else:
self.logger = logging.getLogger( loggerTitle )
self.logger.setLevel( logging.INFO )
self.logger.propagate = False

def setLoggerHandler( self: Self, handler: logging.Handler ) -> None:
"""Set a specific handler for the filter logger.
Expand All @@ -113,7 +114,7 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None:
Args:
handler (logging.Handler): The handler to add.
"""
if not self.logger.hasHandlers():
if len( self.logger.handlers ) == 0:
self.logger.addHandler( handler )
else:
self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler'"
Expand All @@ -139,63 +140,64 @@ def applyFilter( self: Self ) -> bool:
"""
self.logger.info( f"Apply filter { self.logger.name }." )

if len( self.attributeNames ) == 0:
self.logger.warning( f"Please enter at least one { self.piece } attribute to transfer." )
self.logger.warning( f"The filter { self.logger.name } has not been used." )
return False

attributesInMeshFrom: set[ str ] = getAttributeSet( self.meshFrom, self.onPoints )
wrongAttributeNames: set[ str ] = self.attributeNames.difference( attributesInMeshFrom )
if len( wrongAttributeNames ) > 0:
self.logger.error(
f"The { self.piece } attributes { wrongAttributeNames } are not present in the source mesh." )
self.logger.error( f"The filter { self.logger.name } failed." )
return False

attributesInMeshTo: set[ str ] = getAttributeSet( self.meshTo, self.onPoints )
attributesAlreadyInMeshTo: set[ str ] = self.attributeNames.intersection( attributesInMeshTo )
if len( attributesAlreadyInMeshTo ) > 0:
self.logger.error(
f"The { self.piece } attributes { attributesAlreadyInMeshTo } are already present in the final mesh." )
self.logger.error( f"The filter { self.logger.name } failed." )
return False
try:
if len( self.attributeNames ) == 0:
raise ValueError( f"Please enter at least one { self.piece } attribute to transfer." )

attributesInMeshFrom: set[ str ] = getAttributeSet( self.meshFrom, self.onPoints )
wrongAttributeNames: set[ str ] = self.attributeNames.difference( attributesInMeshFrom )
if len( wrongAttributeNames ) > 0:
raise AttributeError(
f"The { self.piece } attributes { wrongAttributeNames } are not present in the source mesh." )

attributesInMeshTo: set[ str ] = getAttributeSet( self.meshTo, self.onPoints )
attributesAlreadyInMeshTo: set[ str ] = self.attributeNames.intersection( attributesInMeshTo )
if len( attributesAlreadyInMeshTo ) > 0:
raise AttributeError(
f"The { self.piece } attributes { attributesAlreadyInMeshTo } are already present in the final mesh."
)

if isinstance( self.meshFrom, vtkMultiBlockDataSet ):
partialAttributes: list[ str ] = []
for attributeName in self.attributeNames:
if not isAttributeGlobal( self.meshFrom, attributeName, self.onPoints ):
partialAttributes.append( attributeName )

if len( partialAttributes ) > 0:
raise AttributeError(
f"All { self.piece } attributes to transfer must be global, { partialAttributes } are partials."
)

self.ElementMap = computeElementMapping( self.meshFrom, self.meshTo, self.onPoints )
sharedElement: bool = False
for key in self.ElementMap:
if np.any( self.ElementMap[ key ] > -1 ):
sharedElement = True

if not sharedElement:
raise ValueError( f"The two meshes do not have any shared { self.piece }." )

if isinstance( self.meshFrom, vtkMultiBlockDataSet ):
partialAttributes: list[ str ] = []
for attributeName in self.attributeNames:
if not isAttributeGlobal( self.meshFrom, attributeName, self.onPoints ):
partialAttributes.append( attributeName )

if len( partialAttributes ) > 0:
self.logger.error(
f"All { self.piece } attributes to transfer must be global, { partialAttributes } are partials." )
self.logger.error( f"The filter { self.logger.name } failed." )

self.ElementMap = computeElementMapping( self.meshFrom, self.meshTo, self.onPoints )
sharedElement: bool = False
for key in self.ElementMap:
if np.any( self.ElementMap[ key ] > -1 ):
sharedElement = True

if not sharedElement:
self.logger.warning( f"The two meshes do not have any shared { self.piece }." )
self.logger.warning( f"The filter { self.logger.name } has not been used." )
# TODO:: Modify arrayModifiers function to raise error.
if not transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName,
self.onPoints, self.logger ):
raise

# Log the output message.
self._logOutputMessage()
except ( TypeError, ValueError, AttributeError ) as e:
self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" )
return False
except Exception as e:
mess: str = f"The filter { self.logger.name } failed.\n{ e }"
self.logger.critical( mess, exc_info=True )
return False

for attributeName in self.attributeNames:
if not transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName,
self.onPoints, self.logger ):
self.logger.error( f"The attribute { attributeName } has not been mapped." )
self.logger.error( f"The filter { self.logger.name } failed." )
return False

# Log the output message.
self._logOutputMessage()

return True

def _logOutputMessage( self: Self ) -> None:
"""Create and log result messages of the filter."""
self.logger.info( f"The filter { self.logger.name } succeeded." )
self.logger.info( f"The { self.piece } attributes { self.attributeNames } have been transferred from the source"
" mesh to the final mesh with the { self.piece } mapping." )
self.logger.info(
f"The { self.piece } attributes { self.attributeNames } have been transferred from the source mesh to the final mesh with the { self.piece } mapping."
)
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ def __init__( self, speHandler: bool = False, **properties: str ) -> None:
else:
self.logger = logging.getLogger( loggerTitle )
self.logger.setLevel( logging.INFO )
self.logger.propagate = False

def ComputeTransform( self ) -> None:
"""Update the transformation."""
Expand Down Expand Up @@ -264,7 +265,7 @@ def SetLoggerHandler( self, handler: logging.Handler ) -> None:
Args:
handler (logging.Handler): The handler to add.
"""
if not self.logger.hasHandlers():
if len( self.logger.handlers ) == 0:
self.logger.addHandler( handler )
else:
self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler' to True"
Expand Down
Loading
Loading