diff --git a/docs/geos_posp_docs/filters.rst b/docs/geos_posp_docs/filters.rst deleted file mode 100644 index 7bd6a88d..00000000 --- a/docs/geos_posp_docs/filters.rst +++ /dev/null @@ -1,12 +0,0 @@ -vtk Filters -=========== - -This package defines vtk filters that allows to process Geos outputs. - -geos_posp.filters.GeosBlockMerge module -------------------------------------------- - -.. automodule:: geos_posp.filters.GeosBlockMerge - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/geos_posp_docs/modules.rst b/docs/geos_posp_docs/modules.rst index cf77644b..a87e24eb 100644 --- a/docs/geos_posp_docs/modules.rst +++ b/docs/geos_posp_docs/modules.rst @@ -4,6 +4,4 @@ Processing .. toctree:: :maxdepth: 5 - filters - pyvistaTools diff --git a/docs/geos_processing_docs/post_processing.rst b/docs/geos_processing_docs/post_processing.rst index 58224d9f..19b2906a 100644 --- a/docs/geos_processing_docs/post_processing.rst +++ b/docs/geos_processing_docs/post_processing.rst @@ -44,3 +44,12 @@ geos.processing.post_processing.GeosBlockExtractor module :members: :undoc-members: :show-inheritance: + + +geos.processing.post_processing.GeosBlockMerge module +----------------------------------------------------- + +.. automodule:: geos.processing.post_processing.GeosBlockMerge + :members: + :undoc-members: + :show-inheritance: diff --git a/geos-posp/src/geos_posp/filters/GeosBlockMerge.py b/geos-posp/src/geos_posp/filters/GeosBlockMerge.py deleted file mode 100644 index 4b76d9a2..00000000 --- a/geos-posp/src/geos_posp/filters/GeosBlockMerge.py +++ /dev/null @@ -1,417 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. -# SPDX-FileContributor: Martin Lemay -# ruff: noqa: E402 # disable Module level import not at top of file - -from geos.utils.GeosOutputsConstants import ( - PHASE_SEP, - FluidPrefixEnum, - PhaseTypeEnum, - PostProcessingOutputsEnum, - getRockSuffixRenaming, -) -from geos.utils.Logger import Logger, getLogger -from typing_extensions import Self -from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase -from vtkmodules.vtkCommonCore import ( - vtkInformation, - vtkInformationVector, -) -from vtkmodules.vtkCommonDataModel import ( - vtkCompositeDataSet, - vtkDataObjectTreeIterator, - vtkMultiBlockDataSet, - vtkPolyData, - vtkUnstructuredGrid, -) -from vtkmodules.vtkFiltersCore import vtkArrayRename -from vtkmodules.vtkFiltersGeometry import vtkDataSetSurfaceFilter - -from geos.mesh.utils.multiblockHelpers import getElementaryCompositeBlockIndexes -from geos.mesh.utils.arrayHelpers import getAttributeSet -from geos.mesh.utils.arrayModifiers import createConstantAttribute, fillAllPartialAttributes -from geos.mesh.utils.multiblockHelpers import extractBlock -from geos.mesh.utils.multiblockModifiers import mergeBlocks -from geos.mesh.utils.genericHelpers import ( - computeNormals, - computeTangents, -) - -__doc__ = """ -GeosBlockMerge module is a vtk filter that allows to merge Geos ranks, rename -stress and porosity attributes, and identify fluids and rock phases. - -Filter input and output types are vtkMultiBlockDataSet. - -To use the filter: - -.. code-block:: python - - from filters.GeosBlockMerge import GeosBlockMerge - - # filter inputs - logger :Logger - input :vtkMultiBlockDataSet - - # instanciate the filter - mergeBlockFilter :GeosBlockMerge = GeosBlockMerge() - # set the logger - mergeBlockFilter.SetLogger(logger) - # set input data object - mergeBlockFilter.SetInputDataObject(input) - # ConvertSurfaceMeshOff or ConvertSurfaceMeshOn to (de)activate the conversion - # of vtkUnstructuredGrid to surface withNormals and Tangents calculation. - mergeBlockFilter.ConvertSurfaceMeshOff() - # do calculations - mergeBlockFilter.Update() - # get output object - output :vtkMultiBlockDataSet = mergeBlockFilter.GetOutputDataObject(0) -""" - - -class GeosBlockMerge( VTKPythonAlgorithmBase ): - - def __init__( self: Self ) -> None: - """VTK Filter that perform GEOS rank merge. - - The filter returns a multiblock mesh composed of elementary blocks. - - """ - super().__init__( nInputPorts=1, nOutputPorts=1, - outputType="vtkMultiBlockDataSet" ) # type: ignore[no-untyped-call] - - self.m_input: vtkMultiBlockDataSet - self.m_output: vtkMultiBlockDataSet - - self.m_convertFaultToSurface: bool = True - - # set logger - self.m_logger: Logger = getLogger( "Geos Block Merge Filter" ) - - def FillInputPortInformation( self: Self, port: int, info: vtkInformation ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestInformation. - - Args: - port (int): input port - info (vtkInformationVector): info - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. - """ - if port == 0: - info.Set( self.INPUT_REQUIRED_DATA_TYPE(), "vtkMultiBlockDataSet" ) - return 1 - - def RequestInformation( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], # noqa: F841 - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestInformation. - - Args: - request (vtkInformation): request - inInfoVec (list[vtkInformationVector]): input objects - outInfoVec (vtkInformationVector): output objects - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. - """ - executive = self.GetExecutive() # noqa: F841 - outInfo = outInfoVec.GetInformationObject( 0 ) # noqa: F841 - return 1 - - def RequestData( - self: Self, - request: vtkInformation, - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestData. - - Args: - request (vtkInformation): request - inInfoVec (list[vtkInformationVector]): input objects - outInfoVec (vtkInformationVector): output objects - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. - """ - try: - self.m_input = vtkMultiBlockDataSet.GetData( inInfoVec[ 0 ] ) - - # initialize output objects -- TODO: separate it as soon as we are sure not to alter behavior - self.m_output = self.GetOutputData( outInfoVec, 0 ) # type: ignore[no-untyped-call] - - self.doMerge() - except ( ValueError, TypeError, RuntimeError ) as e: - self.m_logger.critical( "Geos block merge failed due to:" ) - self.m_logger.critical( e, exc_info=True ) - return 0 - else: - return 1 - - def SetLogger( self: Self, logger: Logger ) -> None: - """Set the logger. - - Args: - logger (Logger): logger - """ - self.m_logger = logger - - def ConvertSurfaceMeshOn( self: Self ) -> None: - """Activate surface conversion from vtkUnstructredGrid to vtkPolyData.""" - self.m_convertFaultToSurface = True - - def ConvertSurfaceMeshOff( self: Self ) -> None: - """Deactivate surface conversion from vtkUnstructredGrid to vtkPolyData.""" - self.m_convertFaultToSurface = False - - def doMerge( self: Self ) -> int: - """Apply block merge. - - Returns: - bool: True if block merge successfully ended, False otherwise. - """ - self.mergeRankBlocks() - if self.m_convertFaultToSurface: - self.convertFaultsToSurfaces() - - self.m_output.ShallowCopy( self.m_outputMesh ) - return 1 - - def mergeRankBlocks( self: Self ) -> bool: - """Merge all elementary node that belong to a same parent node. - - Returns: - bool: True if calculation successfully ended, False otherwise - """ - # display phase names - try: - phaseClassification: dict[ str, PhaseTypeEnum ] = self.getPhases( True ) - if phaseClassification is not None: - for phaseTypeRef in list( PhaseTypeEnum ): - phases = [ - phaseName for phaseName, phaseType in phaseClassification.items() if phaseType is phaseTypeRef - ] - if len( phases ) > 0: - self.m_logger.info( f"Identified {phaseTypeRef.type} phase(s) are: {phases}" ) - except AssertionError as e: - self.m_logger.warning( "Phases were not identified due to:" ) - self.m_logger.warning( e ) - - compositeBlockIndexesToMerge: dict[ str, int ] = ( getElementaryCompositeBlockIndexes( self.m_input ) ) - nbBlocks: int = len( compositeBlockIndexesToMerge ) - self.m_outputMesh = vtkMultiBlockDataSet() - self.m_outputMesh.SetNumberOfBlocks( nbBlocks ) - for newIndex, ( blockName, blockIndex ) in enumerate( compositeBlockIndexesToMerge.items() ): - # extract composite block - blockToMerge1: vtkMultiBlockDataSet = extractBlock( self.m_input, blockIndex ) - assert blockToMerge1 is not None, "Extracted block to merge is null." - - # rename attributes - blockToMerge2: vtkMultiBlockDataSet = self.renameAttributes( blockToMerge1, phaseClassification ) - assert blockToMerge2 is not None, "Attribute renaming failed." - - # merge all its children - mergedBlock: vtkUnstructuredGrid = self.mergeChildBlocks( blockToMerge2 ) - - # create index attribute keeping the index in intial mesh - if not createConstantAttribute( - mergedBlock, - [ - blockIndex, - ], - PostProcessingOutputsEnum.BLOCK_INDEX.attributeName, - (), - False, - ): - self.m_logger.warning( "BlockIndex attribute was not created." ) - - # set this composite block into the output - self.m_outputMesh.SetBlock( newIndex, mergedBlock ) - self.m_outputMesh.GetMetaData( newIndex ).Set( vtkCompositeDataSet.NAME(), blockName ) - - assert ( self.m_outputMesh.GetNumberOfBlocks() == nbBlocks ), "Final number of merged blocks is wrong." - return True - - def renameAttributes( - self: Self, - mesh: vtkMultiBlockDataSet, - phaseClassification: dict[ str, PhaseTypeEnum ], - ) -> vtkMultiBlockDataSet: - """Rename attributes to harmonize throughout the mesh. - - Args: - mesh (vtkMultiBlockDataSet): input mesh - phaseClassification (dict[str, PhaseTypeEnum]): phase classification - detected from attributes - - Returns: - vtkMultiBlockDataSet: output mesh with renamed attributes - """ - assert phaseClassification is not None, "Phases were not correctly identified." - - renameFilter: vtkArrayRename = vtkArrayRename() - renameFilter.SetInputData( mesh ) - rockPhase: list[ str ] = [ - phaseName for phaseName, phaseType in phaseClassification.items() if phaseType is PhaseTypeEnum.ROCK - ] - for attributeName in getAttributeSet( mesh, False ): - for phaseName in rockPhase: - if phaseName in attributeName: - for suffix, newName in getRockSuffixRenaming().items(): - if suffix in attributeName: - renameFilter.SetCellArrayName( attributeName, newName ) - - renameFilter.Update() - output: vtkMultiBlockDataSet = renameFilter.GetOutput() - return output - - def getPhaseNames( self: Self, attributeSet: set[ str ] ) -> set[ str ]: - """Get the names of the phases in the mesh from Point/Cell attributes. - - Args: - attributeSet (set[str]): list of attributes where to find phases - - Returns: - set[str]: the list of phase names that appear at least twice. - """ - phaseNameDict: dict[ str, int ] = {} - for name in attributeSet: - if PHASE_SEP in name: - # use the last occurence of PHASE_SEP to split phase name from - # property name - index = name.rindex( PHASE_SEP ) - phaseName: str = name[ :index ] - if phaseName in phaseNameDict: - phaseNameDict[ phaseName ] += 1 - else: - phaseNameDict[ phaseName ] = 1 - - # remove names that appear only once - return set( phaseNameDict.keys() ) - - def getPhases( self: Self, onCells: bool = True ) -> dict[ str, PhaseTypeEnum ]: - """Get the dictionnary of phases classified according to PhaseTypeEnum. - - Args: - onCells (bool, optional): Attributes are on Cells (Default) or on - Points. - - Defaults to True. - - Returns: - dict[str, PhaseTypeEnum]: a dictionnary with phase names as keys and - phase type as value. - """ - attributeSet: set[ str ] = getAttributeSet( self.m_input, not onCells ) - assert len( attributeSet ) > 0, "Input object does not have any attribute." - - phaseClassification: dict[ str, PhaseTypeEnum ] = {} - phaseNames: set[ str ] = self.getPhaseNames( attributeSet ) - for phaseName in phaseNames: - # check for fluid phase names (most often the same names: fluid, water, or gas) - if any( fluidPrefix.value.lower() in phaseName.lower() for fluidPrefix in list( FluidPrefixEnum ) ): - phaseClassification[ phaseName ] = PhaseTypeEnum.FLUID - continue - - for attributeName in attributeSet: - if phaseName in attributeName: - index = attributeName.index( phaseName ) + len( phaseName ) - suffix = attributeName[ index: ] - for phaseType in list( PhaseTypeEnum ): - if suffix in phaseType.attributes: - if ( phaseName in phaseClassification ) and ( phaseType - is not phaseClassification[ phaseName ] ): - self.m_logger.warning( f"The phase {phaseName} may be misclassified " + - "since the same name is used for both " + - "{phaseType.type} and " + - "{phaseClassification[phaseName].type} types" ) - phaseClassification[ phaseName ] = phaseType - - return phaseClassification - - def mergeChildBlocks( self: Self, compositeBlock: vtkMultiBlockDataSet ) -> vtkUnstructuredGrid: - """Merge all children of the input composite block. - - Args: - compositeBlock (vtkMultiBlockDataSet): composite block - - Returns: - vtkUnstructuredGrid: merged block - """ - # fill partial attributes in all children blocks - if not fillAllPartialAttributes( compositeBlock ): - self.m_logger.warning( "Some partial attributes may not have been propagated to the whole mesh." ) - - # merge blocks - mergedBlocks = mergeBlocks( compositeBlock ) - return mergedBlocks - - def convertFaultsToSurfaces( self: Self ) -> bool: - """Convert blocks corresponding to faults to surface. - - Returns: - bool: True if calculation succesfully ended, False otherwise - """ - assert self.m_outputMesh is not None, "Output surface is null." - - transientMesh: vtkMultiBlockDataSet = vtkMultiBlockDataSet() - nbSurfaces: int = self.m_outputMesh.GetNumberOfBlocks() - transientMesh.SetNumberOfBlocks( nbSurfaces ) - - # initialize data object tree iterator - iter: vtkDataObjectTreeIterator = vtkDataObjectTreeIterator() - iter.SetDataSet( self.m_outputMesh ) - iter.VisitOnlyLeavesOn() - iter.GoToFirstItem() - surfaceIndex: int = 0 - while iter.GetCurrentDataObject() is not None: - surfaceName: str = iter.GetCurrentMetaData().Get( vtkCompositeDataSet.NAME() ) - # convert block to surface - surface0: vtkUnstructuredGrid = vtkUnstructuredGrid.SafeDownCast( iter.GetCurrentDataObject() ) - surface1: vtkPolyData = self.convertBlockToSurface( surface0 ) - assert surface1 is not None, "Surface extraction from block failed." - # compute normals - surface2: vtkPolyData = computeNormals( surface1 ) - assert surface2 is not None, "Normal calculation failed." - # compute tangents - surface3: vtkPolyData = computeTangents( surface2 ) - assert surface3 is not None, "Tangent calculation failed." - - # set surface to output multiBlockDataSet - transientMesh.SetBlock( surfaceIndex, surface3 ) - transientMesh.GetMetaData( surfaceIndex ).Set( vtkCompositeDataSet.NAME(), surfaceName ) - - iter.GoToNextItem() - surfaceIndex += 1 - - self.m_outputMesh.ShallowCopy( transientMesh ) - return True - - def convertBlockToSurface( self: Self, block: vtkUnstructuredGrid ) -> vtkPolyData: - """Convert vtkUnstructuredGrid to a surface vtkPolyData. - - .. WARNING:: work only with triangulated surfaces - - .. TODO:: need to convert quadrangular to triangulated surfaces first - - - Args: - block (vtkUnstructuredGrid): block from which to extract the surface - - Returns: - vtkPolyData: extracted surface - """ - extractSurfaceFilter: vtkDataSetSurfaceFilter = vtkDataSetSurfaceFilter() - extractSurfaceFilter.SetInputData( block ) - # fast mode should be used for rendering only - extractSurfaceFilter.FastModeOff() - # Delegation activated allow to accelerate the processing with unstructured mesh - # see https://vtk.org/doc/nightly/html/classvtkDataSetSurfaceFilter.html - extractSurfaceFilter.DelegationOn() - extractSurfaceFilter.Update() - output: vtkPolyData = extractSurfaceFilter.GetOutput() - return output diff --git a/geos-posp/src/geos_posp/filters/__init__.py b/geos-posp/src/geos_posp/filters/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py new file mode 100644 index 00000000..b1188ddf --- /dev/null +++ b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py @@ -0,0 +1,238 @@ +# SPDX-License-Identifier: Apache-2.0 +# # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. +# SPDX-FileContributor: Martin Lemay, Romain Baville +# ruff: noqa: E402 # disable Module level import not at top of file +import logging +from typing_extensions import Self + +from vtkmodules.vtkCommonDataModel import ( vtkCompositeDataSet, vtkMultiBlockDataSet, vtkPolyData, + vtkUnstructuredGrid ) +from vtkmodules.vtkFiltersGeometry import vtkDataSetSurfaceFilter + +from geos.utils.Logger import ( Logger, getLogger ) +from geos.utils.GeosOutputsConstants import ( PHASE_SEP, PhaseTypeEnum, FluidPrefixEnum, PostProcessingOutputsEnum, + getRockSuffixRenaming ) +from geos.mesh.utils.arrayHelpers import getAttributeSet +from geos.mesh.utils.arrayModifiers import ( createConstantAttribute, renameAttribute ) +from geos.mesh.utils.multiblockModifiers import mergeBlocks +from geos.mesh.utils.multiblockHelpers import ( getElementaryCompositeBlockIndexes, extractBlock ) +from geos.mesh.utils.genericHelpers import ( computeNormals, computeTangents ) + +__doc__ = """ +GeosBlockMerge is a vtk filter that allows to merge for a GEOS domain the ranks per region, identify "Fluids" and "Rock" phases and rename "Rock" attributes. + +Filter input and output types are vtkMultiBlockDataSet. + +.. Important:: + This filter deals with the domain mesh of GEOS. This domain needs to be extracted before. + See geos-processing/src/geos/processing/post_processing/GeosBlockExtractor.py to see the type of input requires by this filter. + +To use the filter: + +.. code-block:: python + + from geos.processing.post_processing.GeosBlockMerge import GeosBlockMerge + + # Filter inputs. + inputMesh: vtkMultiBlockDataSet + # Optional inputs. + convertFaultToSurface: bool # Defaults to False + speHandler: bool # Defaults to False + + # Instantiate the filter + mergeBlockFilter: GeosBlockMerge = GeosBlockMerge( inputMesh, convertFaultToSurface, speHandler ) + + # Set the handler of yours (only if speHandler is True). + yourHandler: logging.Handler + mergeBlockFilter.setLoggerHandler( yourHandler ) + + # Do calculations + mergeBlockFilter.applyFilter() + + # Get the multiBlockDataSet with one dataSet per region + outputMesh: vtkMultiBlockDataSet = mergeBlockFilter.getOutput() +""" + +loggerTitle: str = "GEOS Block Merge" + + +class GeosBlockMerge(): + + def __init__( + self: Self, + inputMesh: vtkMultiBlockDataSet, + convertFaultToSurface: bool = False, + speHandler: bool = False, + ) -> None: + """VTK Filter that merge ranks of GEOS output mesh. + + for all the composite blocks of the input mesh: + - Ranks are merged + - "Rock" attributes are renamed + - Volume mesh are convert to surface if needed + + Args: + inputMesh (vtkMultiBlockDataSet): The mesh with the blocks to merge. + convertFaultToSurface (bool, optional): True if the merged block need to be convert to vtp, False otherwise. + Defaults to False. + speHandler (bool, optional): True to use a specific handler, False to use the internal handler. + Defaults to False. + + """ + self.inputMesh: vtkMultiBlockDataSet = inputMesh + self.convertFaultToSurface: bool = convertFaultToSurface + + self.outputMesh: vtkMultiBlockDataSet = vtkMultiBlockDataSet() + self.phaseNameDict: dict[ str, set[ str ] ] = { + PhaseTypeEnum.ROCK.type: set(), + PhaseTypeEnum.FLUID.type: set(), + } + + # 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 getOutput( self: Self ) -> vtkMultiBlockDataSet: + """Get the mesh with the composite blocks merged.""" + return self.outputMesh + + def applyFilter( self: Self ) -> None: + """Apply the filter on the mesh.""" + self.logger.info( f"Apply filter { self.logger.name }." ) + + try: + # Display phase names + self.computePhaseNames() + for phase, phaseNames in self.phaseNameDict.items(): + if len( phaseNames ) > 0: + self.logger.info( f"Identified { phase } phase(s) are: { phaseNames }." ) + else: + self.logger.info( f"No { phase } phase has been identified." ) + + # Parse all the composite blocks + compositeBlockIndexesToMerge: dict[ str, int ] = getElementaryCompositeBlockIndexes( self.inputMesh ) + nbBlocks: int = len( compositeBlockIndexesToMerge ) + self.outputMesh.SetNumberOfBlocks( nbBlocks ) + for newIndex, ( blockName, blockIndex ) in enumerate( compositeBlockIndexesToMerge.items() ): + # Set the name of the composite block + self.outputMesh.GetMetaData( newIndex ).Set( vtkCompositeDataSet.NAME(), blockName ) + + # Merge blocks + blockToMerge: vtkMultiBlockDataSet = extractBlock( self.inputMesh, blockIndex ) + volumeMesh: vtkUnstructuredGrid = mergeBlocks( blockToMerge, + keepPartialAttributes=True, + logger=self.logger ) + + # Create index attribute keeping the index in initial mesh + if not createConstantAttribute( volumeMesh, [ blockIndex ], + PostProcessingOutputsEnum.BLOCK_INDEX.attributeName, + onPoints=False, + logger=self.logger ): + self.logger.warning( "BlockIndex attribute was not created." ) + + # Rename attributes + self.renameAttributes( volumeMesh ) + + # Convert the volume mesh to a surface mesh + if self.convertFaultToSurface: + surfaceMesh: vtkPolyData = self.convertBlockToSurface( volumeMesh ) + assert surfaceMesh is not None, "Surface extraction from block failed." + surfaceMesh.ShallowCopy( computeNormals( surfaceMesh, logger=self.logger ) ) + assert surfaceMesh is not None, "Normal calculation failed." + surfaceMesh.ShallowCopy( computeTangents( surfaceMesh, logger=self.logger ) ) + assert surfaceMesh is not None, "Tangent calculation failed." + # Add the merged block to the output mesh + self.outputMesh.SetBlock( newIndex, surfaceMesh ) + else: + self.outputMesh.SetBlock( newIndex, volumeMesh ) + + self.logger.info( "The filter succeeded." ) + except ( ValueError, TypeError, RuntimeError, AssertionError ) as e: + self.logger.critical( f"The filter failed.\n{ e }" ) + + return + + def renameAttributes( + self: Self, + mesh: vtkUnstructuredGrid, + ) -> None: + """Rename attributes to harmonize GEOS output, see more geos.utils.OutputsConstants.py. + + Args: + mesh (vtkUnstructuredGrid): The mesh with the attribute to rename. + """ + # All the attributes to rename are on cells + for attributeName in getAttributeSet( mesh, False ): + for suffix, newName in getRockSuffixRenaming().items(): + if suffix in attributeName: + # Fluid and Rock density attribute have the same suffix, only the rock density need to be renamed + if suffix == "_density": + for phaseName in self.phaseNameDict[ PhaseTypeEnum.ROCK.type ]: + if phaseName in attributeName: + renameAttribute( mesh, attributeName, newName, False ) + else: + renameAttribute( mesh, attributeName, newName, False ) + + def computePhaseNames( self: Self ) -> None: + """Get the names of the phases in the mesh from Cell attributes.""" + # All the phase attributes are on cells + for name in getAttributeSet( self.inputMesh, False ): + if PHASE_SEP in name: + phaseName: str + suffixName: str + phaseName, suffixName = name.split( PHASE_SEP ) + # Fluid and Rock density attribute have the same suffix, common fluid name are used to separated the two phases + if f"{ PHASE_SEP }{ suffixName }" == "_density": + if any( phaseName in fluidPrefix.value for fluidPrefix in list( FluidPrefixEnum ) ): + self.phaseNameDict[ PhaseTypeEnum.FLUID.type ].add( phaseName ) + else: + self.phaseNameDict[ PhaseTypeEnum.ROCK.type ].add( phaseName ) + elif f"{ PHASE_SEP }{ suffixName }" in PhaseTypeEnum.ROCK.attributes: + self.phaseNameDict[ PhaseTypeEnum.ROCK.type ].add( phaseName ) + elif f"{ PHASE_SEP }{ suffixName }" in PhaseTypeEnum.FLUID.attributes: + self.phaseNameDict[ PhaseTypeEnum.FLUID.type ].add( phaseName ) + + return + + def convertBlockToSurface( self: Self, block: vtkUnstructuredGrid ) -> vtkPolyData: + """Convert vtkUnstructuredGrid to a surface vtkPolyData. + + .. WARNING:: work only with triangulated surfaces + + .. TODO:: need to convert quadrangular to triangulated surfaces first + + Args: + block (vtkUnstructuredGrid): block from which to extract the surface + + Returns: + vtkPolyData: extracted surface + """ + extractSurfaceFilter: vtkDataSetSurfaceFilter = vtkDataSetSurfaceFilter() + extractSurfaceFilter.SetInputData( block ) + # fast mode should be used for rendering only + extractSurfaceFilter.FastModeOff() + # Delegation activated allow to accelerate the processing with unstructured mesh + # see https://vtk.org/doc/nightly/html/classvtkDataSetSurfaceFilter.html + extractSurfaceFilter.DelegationOn() + extractSurfaceFilter.Update() + output: vtkPolyData = extractSurfaceFilter.GetOutput() + return output diff --git a/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolume.py b/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolume.py index ef97c659..382f8bc8 100644 --- a/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolume.py +++ b/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolume.py @@ -23,7 +23,7 @@ ) from geos.utils.Logger import ERROR, INFO, Logger, getLogger from geos.processing.post_processing.GeosBlockExtractor import GeosBlockExtractor -from geos_posp.filters.GeosBlockMerge import GeosBlockMerge +from geos.processing.post_processing.GeosBlockMerge import GeosBlockMerge from geos.mesh.utils.arrayModifiers import ( copyAttribute, createCellCenterAttribute, @@ -32,6 +32,9 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, ) +from paraview.detail.loghandler import ( # type: ignore[import-not-found] + VTKHandler, +) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py __doc__ = """ PVExtractMergeBlocksVolume is a Paraview plugin that allows to merge ranks @@ -274,7 +277,7 @@ def doExtractAndMerge( self: Self, input: vtkMultiBlockDataSet, output: vtkMulti output (vtkMultiBlockDataSet): output volume mesh Returns: - bool: True if extraction and merge successfully eneded, False otherwise + bool: True if extraction and merge successfully ended, False otherwise """ # extract blocks blockExtractor: GeosBlockExtractor = GeosBlockExtractor( input ) @@ -303,14 +306,10 @@ def mergeBlocksFilter( self: Self, Returns: vtkMultiBlockDataSet: Multiblock mesh composed of internal merged blocks. """ - mergeBlockFilter: GeosBlockMerge = GeosBlockMerge() - mergeBlockFilter.SetLogger( self.m_logger ) - mergeBlockFilter.SetInputDataObject( input ) - if convertSurfaces: - mergeBlockFilter.ConvertSurfaceMeshOn() - else: - mergeBlockFilter.ConvertSurfaceMeshOff() - mergeBlockFilter.Update() - mergedBlocks: vtkMultiBlockDataSet = mergeBlockFilter.GetOutputDataObject( 0 ) + mergeBlockFilter: GeosBlockMerge = GeosBlockMerge( input, convertSurfaces, True ) + if not mergeBlockFilter.logger.hasHandlers(): + mergeBlockFilter.setLoggerHandler( VTKHandler() ) + mergeBlockFilter.applyFilter() + mergedBlocks: vtkMultiBlockDataSet = mergeBlockFilter.getOutput() assert mergedBlocks is not None, "Final merged MultiBlockDataSet is null." return mergedBlocks diff --git a/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolumeSurface.py b/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolumeSurface.py index c9a27e53..a670df2b 100644 --- a/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolumeSurface.py +++ b/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolumeSurface.py @@ -24,7 +24,7 @@ ) from geos.utils.Logger import ERROR, INFO, Logger, getLogger from geos.processing.post_processing.GeosBlockExtractor import GeosBlockExtractor -from geos_posp.filters.GeosBlockMerge import GeosBlockMerge +from geos.processing.post_processing.GeosBlockMerge import GeosBlockMerge from geos.mesh.utils.arrayModifiers import ( copyAttribute, createCellCenterAttribute, @@ -33,6 +33,9 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, ) +from paraview.detail.loghandler import ( # type: ignore[import-not-found] + VTKHandler, +) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py __doc__ = """ PVExtractMergeBlocksVolumeSurface is a Paraview plugin that allows to merge @@ -325,14 +328,10 @@ def mergeBlocksFilter( self: Self, Returns: vtkMultiBlockDataSet: Multiblock mesh composed of internal merged blocks. """ - mergeBlockFilter = GeosBlockMerge() - mergeBlockFilter.SetLogger( self.m_logger ) - mergeBlockFilter.SetInputDataObject( input ) - if convertSurfaces: - mergeBlockFilter.ConvertSurfaceMeshOn() - else: - mergeBlockFilter.ConvertSurfaceMeshOff() - mergeBlockFilter.Update() - mergedBlocks: vtkMultiBlockDataSet = mergeBlockFilter.GetOutputDataObject( 0 ) + mergeBlockFilter: GeosBlockMerge = GeosBlockMerge( input, convertSurfaces, True ) + if not mergeBlockFilter.logger.hasHandlers(): + mergeBlockFilter.setLoggerHandler( VTKHandler() ) + mergeBlockFilter.applyFilter() + mergedBlocks: vtkMultiBlockDataSet = mergeBlockFilter.getOutput() assert mergedBlocks is not None, "Final merged MultiBlockDataSet is null." return mergedBlocks diff --git a/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolumeSurfaceWell.py b/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolumeSurfaceWell.py index 74710a34..d3eff7af 100644 --- a/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolumeSurfaceWell.py +++ b/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolumeSurfaceWell.py @@ -24,7 +24,7 @@ ) from geos.utils.Logger import ERROR, INFO, Logger, getLogger from geos.processing.post_processing.GeosBlockExtractor import GeosBlockExtractor -from geos_posp.filters.GeosBlockMerge import GeosBlockMerge +from geos.processing.post_processing.GeosBlockMerge import GeosBlockMerge from geos.mesh.utils.arrayModifiers import ( copyAttribute, createCellCenterAttribute, @@ -33,6 +33,9 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, ) +from paraview.detail.loghandler import ( # type: ignore[import-not-found] + VTKHandler, +) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py __doc__ = """ PVExtractMergeBlocksVolumeSurfaceWell is a Paraview plugin that allows to merge @@ -353,14 +356,10 @@ def mergeBlocksFilter( self: Self, Returns: vtkMultiBlockDataSet: Multiblock mesh composed of internal merged blocks. """ - mergeBlockFilter = GeosBlockMerge() - mergeBlockFilter.SetLogger( self.m_logger ) - mergeBlockFilter.SetInputDataObject( input ) - if convertSurfaces: - mergeBlockFilter.ConvertSurfaceMeshOn() - else: - mergeBlockFilter.ConvertSurfaceMeshOff() - mergeBlockFilter.Update() - mergedBlocks: vtkMultiBlockDataSet = mergeBlockFilter.GetOutputDataObject( 0 ) + mergeBlockFilter: GeosBlockMerge = GeosBlockMerge( input, convertSurfaces, True ) + if not mergeBlockFilter.logger.hasHandlers(): + mergeBlockFilter.setLoggerHandler( VTKHandler() ) + mergeBlockFilter.applyFilter() + mergedBlocks: vtkMultiBlockDataSet = mergeBlockFilter.getOutput() assert mergedBlocks is not None, "Final merged MultiBlockDataSet is null." return mergedBlocks diff --git a/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolumeWell.py b/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolumeWell.py index 4b7e4389..d1da166b 100644 --- a/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolumeWell.py +++ b/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolumeWell.py @@ -10,6 +10,9 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, ) +from paraview.detail.loghandler import ( # type: ignore[import-not-found] + VTKHandler, +) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from typing_extensions import Self from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet @@ -27,7 +30,7 @@ ) from geos.utils.Logger import ERROR, INFO, Logger, getLogger from geos.processing.post_processing.GeosBlockExtractor import GeosBlockExtractor -from geos_posp.filters.GeosBlockMerge import GeosBlockMerge +from geos.processing.post_processing.GeosBlockMerge import GeosBlockMerge from geos.mesh.utils.arrayModifiers import ( copyAttribute, createCellCenterAttribute, @@ -334,14 +337,10 @@ def mergeBlocksFilter( self: Self, Returns: vtkMultiBlockDataSet: Multiblock mesh composed of internal merged blocks. """ - mergeBlockFilter = GeosBlockMerge() - mergeBlockFilter.SetLogger( self.m_logger ) - mergeBlockFilter.SetInputDataObject( input ) - if convertSurfaces: - mergeBlockFilter.ConvertSurfaceMeshOn() - else: - mergeBlockFilter.ConvertSurfaceMeshOff() - mergeBlockFilter.Update() - mergedBlocks: vtkMultiBlockDataSet = mergeBlockFilter.GetOutputDataObject( 0 ) + mergeBlockFilter: GeosBlockMerge = GeosBlockMerge( input, convertSurfaces, True ) + if not mergeBlockFilter.logger.hasHandlers(): + mergeBlockFilter.setLoggerHandler( VTKHandler() ) + mergeBlockFilter.applyFilter() + mergedBlocks: vtkMultiBlockDataSet = mergeBlockFilter.getOutput() assert mergedBlocks is not None, "Final merged MultiBlockDataSet is null." return mergedBlocks diff --git a/geos-utils/src/geos/utils/GeosOutputsConstants.py b/geos-utils/src/geos/utils/GeosOutputsConstants.py index a2fc72cb..52d50a3f 100644 --- a/geos-utils/src/geos/utils/GeosOutputsConstants.py +++ b/geos-utils/src/geos/utils/GeosOutputsConstants.py @@ -102,9 +102,9 @@ class GeosMeshSuffixEnum( Enum ): BIOT_COEFFICIENT_SUFFIX = "_biotCoefficient" # fluid attributes suffix - PHASE_DENSITY_SUFFIX = "_phaseDensity" + PHASE_DENSITY_SUFFIX = "_density" PHASE_MASS_DENSITY_SUFFIX = "_phaseMassDensity" - PHASE_VISCOSITY_SUFFIX = "_phaseViscosity" + PHASE_VISCOSITY_SUFFIX = "_viscosity" PHASE_FRACTION_SUFFIX = "_phaseFraction" # surface attribute transfer suffix