diff --git a/docs/geos_pv_docs/processing.rst b/docs/geos_pv_docs/processing.rst index 726100fd..36800919 100644 --- a/docs/geos_pv_docs/processing.rst +++ b/docs/geos_pv_docs/processing.rst @@ -47,28 +47,10 @@ PVClipToMainFrame Geos output pre-processing +++++++++++++++++++++++++++++ -PVExtractMergeBlocksVolume plugin -------------------------------------------- - -.. automodule:: geos.pv.plugins.PVExtractMergeBlocksVolume - - -PVExtractMergeBlocksVolumeSurface plugin --------------------------------------------------- - -.. automodule:: geos.pv.plugins.PVExtractMergeBlocksVolumeSurface - - -PVExtractMergeBlocksVolumeSurfaceWell plugin ------------------------------------------------------- - -.. automodule:: geos.pv.plugins.PVExtractMergeBlocksVolumeSurfaceWell - - -PVExtractMergeBlocksVolumeWell plugin ------------------------------------------------ +PVGeosBlockExtractAndMerge plugin +---------------------------------- -.. automodule:: geos.pv.plugins.PVExtractMergeBlocksVolumeWell +.. automodule:: geos.pv.plugins.PVGeosBlockExtractAndMerge diff --git a/docs/geos_pv_docs/utils.rst b/docs/geos_pv_docs/utils.rst index 3a868de5..31443918 100644 --- a/docs/geos_pv_docs/utils.rst +++ b/docs/geos_pv_docs/utils.rst @@ -24,6 +24,14 @@ geos.pv.utils.paraviewTreatments module --------------------------------------------- .. automodule:: geos.pv.utils.paraviewTreatments + :members: + :undoc-members: + :show-inheritance: + +geos.pv.utils.workflowFunctions module +--------------------------------------- + +.. automodule:: geos.pv.utils.workflowFunctions :members: :undoc-members: :show-inheritance: \ No newline at end of file diff --git a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py index dcaf110e..09efe6a6 100644 --- a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py +++ b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py @@ -201,7 +201,7 @@ 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: + if PHASE_SEP in name and "dofIndex" not in name: phaseName: str suffixName: str phaseName, suffixName = name.split( PHASE_SEP ) diff --git a/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolume.py b/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolume.py deleted file mode 100644 index 382f8bc8..00000000 --- a/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolume.py +++ /dev/null @@ -1,315 +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 -import sys -from pathlib import Path -import numpy as np -import numpy.typing as npt -from typing_extensions import Self -from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector -from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet - -# update sys.path to load all GEOS Python Package dependencies -geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent -sys.path.insert( 0, str( geos_pv_path / "src" ) ) -from geos.pv.utils.config import update_paths - -update_paths() - -from geos.utils.GeosOutputsConstants import ( - GeosMeshOutputsEnum, - getAttributeToTransferFromInitialTime, -) -from geos.utils.Logger import ERROR, INFO, Logger, getLogger -from geos.processing.post_processing.GeosBlockExtractor import GeosBlockExtractor -from geos.processing.post_processing.GeosBlockMerge import GeosBlockMerge -from geos.mesh.utils.arrayModifiers import ( - copyAttribute, - createCellCenterAttribute, -) -from geos.pv.utils.paraviewTreatments import getTimeStepIndex -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 -of a Geos output objects containing only a volumic mesh. - -Input and output types are vtkMultiBlockDataSet. - -This filter results in a single output pipeline that contains the volume mesh. -If multiple regions were defined in the volume mesh, they are preserved as -distinct blocks. - -To use it: - -* Load the module in Paraview: Tools>Manage Plugins...>Load new>PVExtractMergeBlocksVolume. -* Select the Geos output .pvd file loaded in Paraview. -* Search and Apply PVExtractMergeBlocksVolume Filter. - -""" - - -@smproxy.filter( - name="PVExtractMergeBlocksVolume", - label="Geos Extract And Merge Blocks - Volume Only", -) -@smhint.xml( """ - - - """ ) -@smproperty.input( name="Input", port_index=0 ) -@smdomain.datatype( dataTypes=[ "vtkMultiBlockDataSet" ], composite_data_supported=True ) -class PVExtractMergeBlocksVolume( VTKPythonAlgorithmBase ): - - def __init__( self: Self ) -> None: - """Paraview plugin to extract and merge ranks from Geos output Mesh. - - To apply in the case of output ".pvd" file contains only a Volume. - """ - super().__init__( - nInputPorts=1, - nOutputPorts=1, - inputType="vtkMultiBlockDataSet", - outputType="vtkMultiBlockDataSet", - ) - - #: all time steps from input - self.m_timeSteps: npt.NDArray[ np.float64 ] = np.array( [] ) - #: displayed time step in the IHM - self.m_currentTime: float = 0.0 - #: time step index of displayed time step - self.m_currentTimeStepIndex: int = 0 - #: request data processing step - incremented each time RequestUpdateExtent is called - self.m_requestDataStep: int = -1 - - #: saved object at initial time step - self.m_outputT0: vtkMultiBlockDataSet = vtkMultiBlockDataSet() - - #: set logger - self.m_logger: Logger = getLogger( "Extract and merge block Filter" ) - - def SetLogger( self: Self, logger: Logger ) -> None: - """Set filter logger. - - Args: - logger (Logger): logger - """ - self.m_logger = logger - - 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 RequestDataObject( - self: Self, - request: vtkInformation, - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestDataObject. - - Args: - request (vtkInformation): request - inInfoVec (list[vtkInformationVector]): input objects - outInfoVec (vtkInformationVector): output objects - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. - """ - inData = self.GetInputData( inInfoVec, 0, 0 ) - outData = self.GetOutputData( outInfoVec, 0 ) - assert inData is not None - if outData is None or ( not outData.IsA( inData.GetClassName() ) ): - outData = inData.NewInstance() - outInfoVec.GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), outData ) - return super().RequestDataObject( request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] - - def RequestUpdateExtent( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestUpdateExtent. - - 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() - inInfo = inInfoVec[ 0 ] - # get displayed time step info before updating time - if self.m_requestDataStep == -1: - self.m_logger.info( f"Apply filter {__name__}" ) - self.m_timeSteps = inInfo.GetInformationObject( 0 ).Get( executive.TIME_STEPS() # type: ignore - ) - self.m_currentTime = inInfo.GetInformationObject( 0 ).Get( executive.UPDATE_TIME_STEP() # type: ignore - ) - self.m_currentTimeStepIndex = getTimeStepIndex( self.m_currentTime, self.m_timeSteps ) - # update requestDataStep - self.m_requestDataStep += 1 - - # update time according to requestDataStep iterator - inInfo.GetInformationObject( 0 ).Set( - executive.UPDATE_TIME_STEP(), - self.m_timeSteps[ self.m_requestDataStep ] # type: ignore - ) - outInfoVec.GetInformationObject( 0 ).Set( - executive.UPDATE_TIME_STEP(), - self.m_timeSteps[ self.m_requestDataStep ] # type: ignore - ) - - # update all objects according to new time info - self.Modified() - 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: - input: vtkMultiBlockDataSet = vtkMultiBlockDataSet.GetData( inInfoVec[ 0 ] ) - output: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 0 ) - - assert input is not None, "Input object is null." - assert output is not None, "Output object is null." - - # time controller - executive = self.GetExecutive() - if self.m_requestDataStep == 0: - # first time step - # do extraction and merge (do not display phase info) - self.m_logger.setLevel( ERROR ) - self.doExtractAndMerge( input, output ) - self.m_logger.setLevel( INFO ) - # save input mesh to copy later - self.m_outputT0.ShallowCopy( output ) - request.Set( executive.CONTINUE_EXECUTING(), 1 ) # type: ignore - if self.m_requestDataStep >= self.m_currentTimeStepIndex: - # displayed time step, no need to go further - request.Remove( executive.CONTINUE_EXECUTING() ) # type: ignore - # reinitialize requestDataStep if filter is recalled later - self.m_requestDataStep = -1 - # do extraction and merge - self.doExtractAndMerge( input, output ) - # copy attributes from initial time step - for ( - attributeName, - attributeNewName, - ) in getAttributeToTransferFromInitialTime().items(): - copyAttribute( self.m_outputT0, output, attributeName, attributeNewName ) - # create elementCenter attribute if needed - cellCenterAttributeName: str = ( GeosMeshOutputsEnum.ELEMENT_CENTER.attributeName ) - createCellCenterAttribute( output, cellCenterAttributeName ) - - # TODO add ForceStaticMesh filter https://gitlab.kitware.com/paraview/plugins/staticmeshplugin - - except AssertionError as e: - mess: str = "Block extraction and merge failed due to:" - self.m_logger.error( mess ) - self.m_logger.error( str( e ) ) - return 0 - except Exception as e: - mess1: str = "Block extraction and merge failed due to:" - self.m_logger.critical( mess1 ) - self.m_logger.critical( e, exc_info=True ) - return 0 - - return 1 - - def doExtractAndMerge( self: Self, input: vtkMultiBlockDataSet, output: vtkMultiBlockDataSet ) -> bool: - """Apply block extraction and merge. - - Args: - input (vtkMultiBlockDataSet): input multi block - output (vtkMultiBlockDataSet): output volume mesh - - Returns: - bool: True if extraction and merge successfully ended, False otherwise - """ - # extract blocks - blockExtractor: GeosBlockExtractor = GeosBlockExtractor( input ) - blockExtractor.applyFilter() - - # recover output objects from GeosBlockExtractor filter - volumeBlockExtracted: vtkMultiBlockDataSet = blockExtractor.extractedGeosDomain.volume - assert volumeBlockExtracted is not None, "Extracted Volume mesh is null." - - # merge internal blocks - output.ShallowCopy( self.mergeBlocksFilter( volumeBlockExtracted, False ) ) - output.Modified() - self.m_logger.info( "Ranks were successfully merged together." ) - return True - - def mergeBlocksFilter( self: Self, - input: vtkMultiBlockDataSet, - convertSurfaces: bool = False ) -> vtkMultiBlockDataSet: - """Apply vtk merge block filter on input multi block mesh. - - Args: - input (vtkMultiBlockDataSet): multiblock mesh to merge - convertSurfaces (bool, optional): True to convert surface from vtkUnstructuredGrid to - vtkPolyData. Defaults to False. - - Returns: - vtkMultiBlockDataSet: Multiblock mesh composed of internal merged blocks. - """ - 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 deleted file mode 100644 index a670df2b..00000000 --- a/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolumeSurface.py +++ /dev/null @@ -1,337 +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 -import sys -from pathlib import Path - -import numpy as np -import numpy.typing as npt -from typing_extensions import Self -from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector -from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet - -# update sys.path to load all GEOS Python Package dependencies -geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent -sys.path.insert( 0, str( geos_pv_path / "src" ) ) -from geos.pv.utils.config import update_paths - -update_paths() - -from geos.utils.GeosOutputsConstants import ( - GeosMeshOutputsEnum, - getAttributeToTransferFromInitialTime, -) -from geos.utils.Logger import ERROR, INFO, Logger, getLogger -from geos.processing.post_processing.GeosBlockExtractor import GeosBlockExtractor -from geos.processing.post_processing.GeosBlockMerge import GeosBlockMerge -from geos.mesh.utils.arrayModifiers import ( - copyAttribute, - createCellCenterAttribute, -) -from geos.pv.utils.paraviewTreatments import getTimeStepIndex -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 -ranks of a Geos output objects containing a volumic mesh and surfaces. - -Input and output types are vtkMultiBlockDataSet. - -This filter results in 2 output pipelines: - -* first pipeline contains the volume mesh. If multiple regions were defined in - the volume mesh, they are preserved as distinct blocks. -* second pipeline contains surfaces. If multiple surfaces were used, they are - preserved as distinct blocks. - -To use it: - -* Load the module in Paraview: Tools>Manage Plugins...>Load new>PVExtractMergeBlocksVolumeSurface. -* Select the Geos output .pvd file loaded in Paraview. -* Search and Apply PVExtractMergeBlocksVolumeSurface Filter. - -""" - - -@smproxy.filter( - name="PVExtractMergeBlocksVolumeSurface", - label="Geos Extract And Merge Blocks - Volume/Surface", -) -@smhint.xml( """ - - - - """ ) -@smproperty.input( name="Input", port_index=0 ) -@smdomain.datatype( dataTypes=[ "vtkMultiBlockDataSet" ], composite_data_supported=True ) -class PVExtractMergeBlocksVolumeSurface( VTKPythonAlgorithmBase ): - - def __init__( self: Self ) -> None: - """Paraview plugin to extract and merge ranks from Geos output Mesh. - - To apply in the case of output ".pvd" file contains Volume and Fault - elements. - """ - super().__init__( - nInputPorts=1, - nOutputPorts=2, - inputType="vtkMultiBlockDataSet", - outputType="vtkMultiBlockDataSet", - ) - - #: all time steps from input - self.m_timeSteps: npt.NDArray[ np.float64 ] = np.array( [] ) - #: displayed time step in the IHM - self.m_currentTime: float = 0.0 - #: time step index of displayed time step - self.m_currentTimeStepIndex: int = 0 - #: request data processing step - incremented each time RequestUpdateExtent is called - self.m_requestDataStep: int = -1 - - # saved object at initial time step - self.m_outputT0: vtkMultiBlockDataSet = vtkMultiBlockDataSet() - - # set logger - self.m_logger: Logger = getLogger( "Extract and merge block Filter" ) - - def SetLogger( self: Self, logger: Logger ) -> None: - """Set filter logger. - - Args: - logger (Logger): logger - """ - self.m_logger = logger - - 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 RequestDataObject( - self: Self, - request: vtkInformation, - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestDataObject. - - Args: - request (vtkInformation): request - inInfoVec (list[vtkInformationVector]): input objects - outInfoVec (vtkInformationVector): output objects - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. - """ - inData = self.GetInputData( inInfoVec, 0, 0 ) - outDataCells = self.GetOutputData( outInfoVec, 0 ) - outDataFaults = self.GetOutputData( outInfoVec, 1 ) - assert inData is not None - if outDataCells is None or ( not outDataCells.IsA( "vtkMultiBlockDataSet" ) ): - outDataCells = vtkMultiBlockDataSet() - outInfoVec.GetInformationObject( 0 ).Set( outDataCells.DATA_OBJECT(), outDataCells ) # type: ignore - if outDataFaults is None or ( not outDataFaults.IsA( "vtkMultiBlockDataSet" ) ): - outDataFaults = vtkMultiBlockDataSet() - outInfoVec.GetInformationObject( 1 ).Set( outDataFaults.DATA_OBJECT(), outDataFaults ) # type: ignore - - return super().RequestDataObject( request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] - - def RequestUpdateExtent( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestUpdateExtent. - - 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() - inInfo = inInfoVec[ 0 ] - # get displayed time step info before updating time - if self.m_requestDataStep == -1: - self.m_logger.info( f"Apply filter {__name__}" ) - self.m_timeSteps = inInfo.GetInformationObject( 0 ).Get( executive.TIME_STEPS() ) # type: ignore - self.m_currentTime = inInfo.GetInformationObject( 0 ).Get( executive.UPDATE_TIME_STEP() ) # type: ignore - self.m_currentTimeStepIndex = getTimeStepIndex( self.m_currentTime, self.m_timeSteps ) - # update requestDataStep - self.m_requestDataStep += 1 - - # update time according to requestDataStep iterator - inInfo.GetInformationObject( 0 ).Set( - executive.UPDATE_TIME_STEP(), # type: ignore - self.m_timeSteps[ self.m_requestDataStep ], - ) - outInfoVec.GetInformationObject( 0 ).Set( - executive.UPDATE_TIME_STEP(), # type: ignore - self.m_timeSteps[ self.m_requestDataStep ], - ) - - # update all objects according to new time info - self.Modified() - 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: - input: vtkMultiBlockDataSet = vtkMultiBlockDataSet.GetData( inInfoVec[ 0 ] ) - outputCells: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 0 ) - outputFaults: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 1 ) - - assert input is not None, "Input MultiBlockDataSet is null." - assert outputCells is not None, "Output volum mesh is null." - assert outputFaults is not None, "Output surface mesh is null." - - # time controller - executive = self.GetExecutive() - if self.m_requestDataStep == 0: - # first time step - # do extraction and merge (do not display phase info) - outputFaults0: vtkMultiBlockDataSet = vtkMultiBlockDataSet() - self.m_logger.setLevel( ERROR ) - self.doExtractAndMerge( input, self.m_outputT0, outputFaults0 ) - self.m_logger.setLevel( INFO ) - request.Set( executive.CONTINUE_EXECUTING(), 1 ) # type: ignore - - if self.m_requestDataStep >= self.m_currentTimeStepIndex: - # displayed time step, no need to go further - request.Remove( executive.CONTINUE_EXECUTING() ) # type: ignore - # reinitialize requestDataStep if filter is recalled later - self.m_requestDataStep = -1 - # do extraction and merge - self.doExtractAndMerge( input, outputCells, outputFaults ) - # copy attributes from initial time step - for ( - attributeName, - attributeNewName, - ) in getAttributeToTransferFromInitialTime().items(): - copyAttribute( self.m_outputT0, outputCells, attributeName, attributeNewName ) - # create elementCenter attribute if needed - cellCenterAttributeName: str = ( GeosMeshOutputsEnum.ELEMENT_CENTER.attributeName ) - createCellCenterAttribute( outputCells, cellCenterAttributeName ) - - except AssertionError as e: - mess: str = "Block extraction and merge failed due to:" - self.m_logger.error( mess ) - self.m_logger.error( str( e ) ) - return 0 - except Exception as e: - mess1: str = "Block extraction and merge failed due to:" - self.m_logger.critical( mess1 ) - self.m_logger.critical( e, exc_info=True ) - return 0 - - return 1 - - def doExtractAndMerge( - self: Self, - input: vtkMultiBlockDataSet, - outputCells: vtkMultiBlockDataSet, - outputFaults: vtkMultiBlockDataSet, - ) -> bool: - """Apply block extraction and merge. - - Args: - input (vtkMultiBlockDataSet): input multi block - outputCells (vtkMultiBlockDataSet): output volume mesh - outputFaults (vtkMultiBlockDataSet): output surface mesh - - Returns: - bool: True if extraction and merge successfully eneded, False otherwise - """ - # extract blocks - blockExtractor: GeosBlockExtractor = GeosBlockExtractor( input, extractFault=True ) - blockExtractor.applyFilter() - - # recover output objects from GeosBlockExtractor filter - volumeBlockExtracted: vtkMultiBlockDataSet = blockExtractor.extractedGeosDomain.volume - faultBlockExtracted: vtkMultiBlockDataSet = blockExtractor.extractedGeosDomain.fault - - # rename attributes and merge blocks - assert volumeBlockExtracted is not None, "Extracted Volume mesh is null." - outputCells.ShallowCopy( self.mergeBlocksFilter( volumeBlockExtracted, False ) ) - outputCells.Modified() - - assert faultBlockExtracted is not None, "Extracted Fault mesh is null." - outputFaults.ShallowCopy( self.mergeBlocksFilter( faultBlockExtracted, True ) ) - outputFaults.Modified() - - self.m_logger.info( "Volume blocks were successfully splitted from surfaces" + - " and ranks were merged together." ) - return True - - def mergeBlocksFilter( self: Self, - input: vtkMultiBlockDataSet, - convertSurfaces: bool = False ) -> vtkMultiBlockDataSet: - """Apply vtk merge block filter on input multi block mesh. - - Args: - input (vtkMultiBlockDataSet): multiblock mesh to merge - convertSurfaces (bool, optional): True to convert surface from vtkUnstructuredGrid to - vtkPolyData. Defaults to False. - - Returns: - vtkMultiBlockDataSet: Multiblock mesh composed of internal merged blocks. - """ - 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 deleted file mode 100644 index d3eff7af..00000000 --- a/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolumeSurfaceWell.py +++ /dev/null @@ -1,365 +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 -import sys -from pathlib import Path - -import numpy as np -import numpy.typing as npt -from typing_extensions import Self -from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector -from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet - -# update sys.path to load all GEOS Python Package dependencies -geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent -sys.path.insert( 0, str( geos_pv_path / "src" ) ) -from geos.pv.utils.config import update_paths - -update_paths() - -from geos.utils.GeosOutputsConstants import ( - GeosMeshOutputsEnum, - getAttributeToTransferFromInitialTime, -) -from geos.utils.Logger import ERROR, INFO, Logger, getLogger -from geos.processing.post_processing.GeosBlockExtractor import GeosBlockExtractor -from geos.processing.post_processing.GeosBlockMerge import GeosBlockMerge -from geos.mesh.utils.arrayModifiers import ( - copyAttribute, - createCellCenterAttribute, -) -from geos.pv.utils.paraviewTreatments import getTimeStepIndex -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 -ranks of a Geos output objects containing a volumic mesh, surfaces, and wells. - -Input and output types are vtkMultiBlockDataSet. - -This filter results in 3 output pipelines: - -* first pipeline contains the volume mesh. If multiple regions were defined in - the volume mesh, they are preserved as distinct blocks. -* second pipeline contains surfaces. If multiple surfaces were used, they are - preserved as distinct blocks. -* third pipeline contains wells. If multiple wells were used, they are preserved - as distinct blocks. - -To use it: - -* Load the module in Paraview: Tools>Manage Plugins...>Load new>PVExtractMergeBlocksVolumeSurfaceWell. -* Select the Geos output .pvd file loaded in Paraview. -* Search and Apply PVExtractMergeBlocksVolumeSurfaceWell Filter. - -""" - - -@smproxy.filter( - name="PVExtractMergeBlocksVolumeSurfaceWell", - label="Geos Extract And Merge Blocks - Volume/Surface/Well", -) -@smhint.xml( """ - - - - - """ ) -@smproperty.input( name="Input", port_index=0 ) -@smdomain.datatype( dataTypes=[ "vtkMultiBlockDataSet" ], composite_data_supported=True ) -class PVExtractMergeBlocksVolumeSurfaceWell( VTKPythonAlgorithmBase ): - - def __init__( self: Self ) -> None: - """Paraview plugin to extract and merge ranks from Geos output Mesh. - - To apply in the case of output ".pvd" file contains Volume, Fault and - Well elements. - """ - super().__init__( - nInputPorts=1, - nOutputPorts=3, - inputType="vtkMultiBlockDataSet", - outputType="vtkMultiBlockDataSet", - ) - - #: all time steps from input - self.m_timeSteps: npt.NDArray[ np.float64 ] = np.array( [] ) - #: displayed time step in the IHM - self.m_currentTime: float = 0.0 - #: time step index of displayed time step - self.m_currentTimeStepIndex: int = 0 - #: request data processing step - incremented each time RequestUpdateExtent is called - self.m_requestDataStep: int = -1 - - # saved object at initial time step - self.m_outputT0: vtkMultiBlockDataSet = vtkMultiBlockDataSet() - - # set logger - self.m_logger: Logger = getLogger( "Extract and merge block Filter" ) - - def SetLogger( self: Self, logger: Logger ) -> None: - """Set filter logger. - - Args: - logger (Logger): logger - """ - self.m_logger = logger - - 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 RequestDataObject( - self: Self, - request: vtkInformation, - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestDataObject. - - Args: - request (vtkInformation): request - inInfoVec (list[vtkInformationVector]): input objects - outInfoVec (vtkInformationVector): output objects - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. - """ - inData = self.GetInputData( inInfoVec, 0, 0 ) - outDataCells = self.GetOutputData( outInfoVec, 0 ) - outDataFaults = self.GetOutputData( outInfoVec, 1 ) - outDataWells = self.GetOutputData( outInfoVec, 2 ) - assert inData is not None - if outDataCells is None or ( not outDataCells.IsA( "vtkMultiBlockDataSet" ) ): - outDataCells = vtkMultiBlockDataSet() - outInfoVec.GetInformationObject( 0 ).Set( - outDataCells.DATA_OBJECT(), - outDataCells # type: ignore - ) - - if outDataFaults is None or ( not outDataFaults.IsA( "vtkMultiBlockDataSet" ) ): - outDataFaults = vtkMultiBlockDataSet() - outInfoVec.GetInformationObject( 1 ).Set( - outDataFaults.DATA_OBJECT(), - outDataFaults # type: ignore - ) - - if outDataWells is None or ( not outDataWells.IsA( "vtkMultiBlockDataSet" ) ): - outDataWells = vtkMultiBlockDataSet() - outInfoVec.GetInformationObject( 2 ).Set( - outDataWells.DATA_OBJECT(), - outDataWells # type: ignore - ) - - return super().RequestDataObject( request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] - - def RequestUpdateExtent( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestUpdateExtent. - - 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() - inInfo = inInfoVec[ 0 ] - # get displayed time step info before updating time - if self.m_requestDataStep == -1: - self.m_logger.info( f"Apply filter {__name__}" ) - self.m_timeSteps = inInfo.GetInformationObject( 0 ).Get( executive.TIME_STEPS() # type: ignore - ) - self.m_currentTime = inInfo.GetInformationObject( 0 ).Get( executive.UPDATE_TIME_STEP() # type: ignore - ) - self.m_currentTimeStepIndex = getTimeStepIndex( self.m_currentTime, self.m_timeSteps ) - # update requestDataStep - self.m_requestDataStep += 1 - - # update time according to requestDataStep iterator - inInfo.GetInformationObject( 0 ).Set( - executive.UPDATE_TIME_STEP(), - self.m_timeSteps[ self.m_requestDataStep ] # type: ignore - ) - outInfoVec.GetInformationObject( 0 ).Set( - executive.UPDATE_TIME_STEP(), - self.m_timeSteps[ self.m_requestDataStep ] # type: ignore - ) - - # update all objects according to new time info - self.Modified() - 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: - input: vtkMultiBlockDataSet = vtkMultiBlockDataSet.GetData( inInfoVec[ 0 ] ) - outputCells: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 0 ) - outputFaults: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 1 ) - outputWells: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 2 ) - - assert input is not None, "Input MultiBlockDataSet is null." - assert outputCells is not None, "Output volum mesh is null." - assert outputFaults is not None, "Output surface mesh is null." - assert outputWells is not None, "Output well mesh is null." - - # time controller - executive = self.GetExecutive() - if self.m_requestDataStep == 0: - # first time step - # do extraction and merge (do not display phase info) - self.m_logger.setLevel( ERROR ) - outputFaults0: vtkMultiBlockDataSet = vtkMultiBlockDataSet() - outputWells0: vtkMultiBlockDataSet = vtkMultiBlockDataSet() - self.doExtractAndMerge( input, outputCells, outputFaults0, outputWells0 ) - self.m_logger.setLevel( INFO ) - # save input mesh to copy later - self.m_outputT0.ShallowCopy( outputCells ) - request.Set( executive.CONTINUE_EXECUTING(), 1 ) # type: ignore - if self.m_requestDataStep >= self.m_currentTimeStepIndex: - # displayed time step, no need to go further - request.Remove( executive.CONTINUE_EXECUTING() ) # type: ignore - # reinitialize requestDataStep if filter is recalled later - self.m_requestDataStep = -1 - # do extraction and merge - self.doExtractAndMerge( input, outputCells, outputFaults, outputWells ) - # copy attributes from initial time step - for ( - attributeName, - attributeNewName, - ) in getAttributeToTransferFromInitialTime().items(): - copyAttribute( self.m_outputT0, outputCells, attributeName, attributeNewName ) - # create elementCenter attribute in the volume mesh if needed - cellCenterAttributeName: str = ( GeosMeshOutputsEnum.ELEMENT_CENTER.attributeName ) - createCellCenterAttribute( outputCells, cellCenterAttributeName ) - except AssertionError as e: - mess: str = "Block extraction and merge failed due to:" - self.m_logger.error( mess ) - self.m_logger.error( str( e ) ) - return 0 - except Exception as e: - mess1: str = "Block extraction and merge failed due to:" - self.m_logger.critical( mess1 ) - self.m_logger.critical( e, exc_info=True ) - return 0 - return 1 - - def doExtractAndMerge( - self: Self, - input: vtkMultiBlockDataSet, - outputCells: vtkMultiBlockDataSet, - outputFaults: vtkMultiBlockDataSet, - outputWells: vtkMultiBlockDataSet, - ) -> bool: - """Apply block extraction and merge. - - Args: - input (vtkMultiBlockDataSet): input multi block - outputCells (vtkMultiBlockDataSet): output volume mesh - outputFaults (vtkMultiBlockDataSet): output surface mesh - outputWells (vtkMultiBlockDataSet): output well mesh - - Returns: - bool: True if extraction and merge successfully eneded, False otherwise - """ - # extract blocks - blockExtractor: GeosBlockExtractor = GeosBlockExtractor( input, extractFaults=True, extractWells=True ) - blockExtractor.applyFilter() - - # recover output objects from GeosBlockExtractor filter and merge internal blocks - volumeBlockExtracted: vtkMultiBlockDataSet = blockExtractor.extractedGeosDomain.volume - assert volumeBlockExtracted is not None, "Extracted Volume mesh is null." - outputCells.ShallowCopy( self.mergeBlocksFilter( volumeBlockExtracted, False ) ) - - faultBlockExtracted: vtkMultiBlockDataSet = blockExtractor.extractedGeosDomain.fault - assert faultBlockExtracted is not None, "Extracted Fault mesh is null." - outputFaults.ShallowCopy( self.mergeBlocksFilter( faultBlockExtracted, True ) ) - - wellBlockExtracted: vtkMultiBlockDataSet = blockExtractor.extractedGeosDomain.well - assert wellBlockExtracted is not None, "Extracted Well mesh is null." - outputWells.ShallowCopy( self.mergeBlocksFilter( wellBlockExtracted, False ) ) - - outputCells.Modified() - outputFaults.Modified() - outputWells.Modified() - - self.m_logger.info( "Volume blocks were successfully splitted from surfaces" + - " and wells, and ranks were merged together." ) - return True - - def mergeBlocksFilter( self: Self, - input: vtkMultiBlockDataSet, - convertSurfaces: bool = False ) -> vtkMultiBlockDataSet: - """Apply vtk merge block filter on input multi block mesh. - - Args: - input (vtkMultiBlockDataSet): multiblock mesh to merge - convertSurfaces (bool, optional): True to convert surface from vtkUnstructuredGrid to - vtkPolyData. Defaults to False. - - Returns: - vtkMultiBlockDataSet: Multiblock mesh composed of internal merged blocks. - """ - 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 deleted file mode 100644 index d1da166b..00000000 --- a/geos-pv/src/geos/pv/plugins/PVExtractMergeBlocksVolumeWell.py +++ /dev/null @@ -1,346 +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 -import sys -from pathlib import Path - -import numpy as np -import numpy.typing as npt -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 - -# update sys.path to load all GEOS Python Package dependencies -geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent -sys.path.insert( 0, str( geos_pv_path / "src" ) ) -from geos.pv.utils.config import update_paths - -update_paths() - -from geos.utils.GeosOutputsConstants import ( - GeosMeshOutputsEnum, - getAttributeToTransferFromInitialTime, -) -from geos.utils.Logger import ERROR, INFO, Logger, getLogger -from geos.processing.post_processing.GeosBlockExtractor import GeosBlockExtractor -from geos.processing.post_processing.GeosBlockMerge import GeosBlockMerge -from geos.mesh.utils.arrayModifiers import ( - copyAttribute, - createCellCenterAttribute, -) -from geos.pv.utils.paraviewTreatments import getTimeStepIndex - -__doc__ = """ -PVExtractMergeBlocksVolumeWell is a Paraview plugin that allows to merge -ranks of a Geos output objects containing a volumic mesh and wells. - -Input and output types are vtkMultiBlockDataSet. - -This filter results in 2 output pipelines: - -* first pipeline contains the volume mesh. If multiple regions were defined in - the volume mesh, they are preserved as distinct blocks. -* second pipeline contains wells. If multiple wells were used, they are preserved - as distinct blocks. - -To use it: - -* Load the module in Paraview: Tools>Manage Plugins...>Load new>PVExtractMergeBlocksVolumeWell. -* Select the Geos output .pvd file loaded in Paraview. -* Search and Apply PVExtractMergeBlocksVolumeWell Filter. - -""" - - -@smproxy.filter( - name="PVExtractMergeBlocksVolumeWell", - label="Geos Extract And Merge Blocks - Volume/Well", -) -@smhint.xml( """ - - - - """ ) -@smproperty.input( name="Input", port_index=0 ) -@smdomain.datatype( dataTypes=[ "vtkMultiBlockDataSet" ], composite_data_supported=True ) -class PVExtractMergeBlocksVolumeWell( VTKPythonAlgorithmBase ): - - def __init__( self: Self ) -> None: - """Paraview plugin to extract and merge ranks from Geos output Mesh. - - To apply in the case of output ".pvd" file contains Volume and Well - elements. - """ - super().__init__( - nInputPorts=1, - nOutputPorts=2, - inputType="vtkMultiBlockDataSet", - outputType="vtkMultiBlockDataSet", - ) - - #: all time steps from input - self.m_timeSteps: npt.NDArray[ np.float64 ] = np.array( [] ) - #: displayed time step in the IHM - self.m_currentTime: float = 0.0 - #: time step index of displayed time step - self.m_currentTimeStepIndex: int = 0 - #: request data processing step - incremented each time RequestUpdateExtent is called - self.m_requestDataStep: int = -1 - - # saved object at initial time step - self.m_outputT0: vtkMultiBlockDataSet = vtkMultiBlockDataSet() - - # set logger - self.m_logger: Logger = getLogger( "Extract and merge block Filter" ) - - def SetLogger( self: Self, logger: Logger ) -> None: - """Set filter logger. - - Args: - logger (Logger): logger - """ - self.m_logger = logger - - 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 RequestDataObject( - self: Self, - request: vtkInformation, - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestDataObject. - - Args: - request (vtkInformation): request - inInfoVec (list[vtkInformationVector]): input objects - outInfoVec (vtkInformationVector): output objects - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. - """ - inData = self.GetInputData( inInfoVec, 0, 0 ) - outDataCells = self.GetOutputData( outInfoVec, 0 ) - outDataWells = self.GetOutputData( outInfoVec, 1 ) - assert inData is not None - if outDataCells is None or ( not outDataCells.IsA( "vtkMultiBlockDataSet" ) ): - outDataCells = vtkMultiBlockDataSet() - outInfoVec.GetInformationObject( 0 ).Set( - outDataCells.DATA_OBJECT(), - outDataCells # type: ignore - ) - - if outDataWells is None or ( not outDataWells.IsA( "vtkMultiBlockDataSet" ) ): - outDataWells = vtkMultiBlockDataSet() - outInfoVec.GetInformationObject( 1 ).Set( - outDataWells.DATA_OBJECT(), - outDataWells # type: ignore - ) - - return super().RequestDataObject( request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] - - def RequestUpdateExtent( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestUpdateExtent. - - 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() - inInfo = inInfoVec[ 0 ] - # get displayed time step info before updating time - if self.m_requestDataStep == -1: - self.m_logger.info( f"Apply filter {__name__}" ) - self.m_timeSteps = inInfo.GetInformationObject( 0 ).Get( executive.TIME_STEPS() # type: ignore - ) - self.m_currentTime = inInfo.GetInformationObject( 0 ).Get( executive.UPDATE_TIME_STEP() # type: ignore - ) - self.m_currentTimeStepIndex = getTimeStepIndex( self.m_currentTime, self.m_timeSteps ) - # update requestDataStep - self.m_requestDataStep += 1 - - # update time according to requestDataStep iterator - inInfo.GetInformationObject( 0 ).Set( - executive.UPDATE_TIME_STEP(), - self.m_timeSteps[ self.m_requestDataStep ] # type: ignore - ) - outInfoVec.GetInformationObject( 0 ).Set( - executive.UPDATE_TIME_STEP(), - self.m_timeSteps[ self.m_requestDataStep ] # type: ignore - ) - - # update all objects according to new time info - self.Modified() - 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: - input: vtkMultiBlockDataSet = vtkMultiBlockDataSet.GetData( inInfoVec[ 0 ] ) - outputCells: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 0 ) - outputWells: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 1 ) - - assert input is not None, "Input MultiBlockDataSet is null." - assert outputCells is not None, "Output volum mesh is null." - assert outputWells is not None, "Output well mesh is null." - - # time controller - executive = self.GetExecutive() - if self.m_requestDataStep == 0: - # first time step - # do extraction and merge (do not display phase info) - self.m_logger.setLevel( ERROR ) - outputWells0: vtkMultiBlockDataSet = vtkMultiBlockDataSet() - self.doExtractAndMerge( input, outputCells, outputWells0 ) - self.m_logger.setLevel( INFO ) - # save input mesh to copy later - self.m_outputT0.ShallowCopy( outputCells ) - request.Set( executive.CONTINUE_EXECUTING(), 1 ) # type: ignore - if self.m_requestDataStep >= self.m_currentTimeStepIndex: - # displayed time step, no need to go further - request.Remove( executive.CONTINUE_EXECUTING() ) # type: ignore - # reinitialize requestDataStep if filter is recalled later - self.m_requestDataStep = -1 - # do extraction and merge - self.doExtractAndMerge( input, outputCells, outputWells ) - # copy attributes from initial time step - for ( - attributeName, - attributeNewName, - ) in getAttributeToTransferFromInitialTime().items(): - copyAttribute( self.m_outputT0, outputCells, attributeName, attributeNewName ) - # create elementCenter attribute if needed - cellCenterAttributeName: str = ( GeosMeshOutputsEnum.ELEMENT_CENTER.attributeName ) - createCellCenterAttribute( outputCells, cellCenterAttributeName ) - - except AssertionError as e: - mess: str = "Block extraction and merge failed due to:" - self.m_logger.error( mess ) - self.m_logger.error( str( e ) ) - return 0 - except Exception as e: - mess1: str = "Block extraction and merge failed due to:" - self.m_logger.critical( mess1 ) - self.m_logger.critical( e, exc_info=True ) - return 0 - - return 1 - - def doExtractAndMerge( - self: Self, - input: vtkMultiBlockDataSet, - outputCells: vtkMultiBlockDataSet, - outputWells: vtkMultiBlockDataSet, - ) -> bool: - """Apply block extraction and merge. - - Args: - input (vtkMultiBlockDataSet): input multi block - outputCells (vtkMultiBlockDataSet): output volume mesh - outputWells (vtkMultiBlockDataSet): output well mesh - - Returns: - bool: True if extraction and merge successfully eneded, False otherwise - """ - # extract blocks - blockExtractor: GeosBlockExtractor = GeosBlockExtractor( input, extractWells=True ) - blockExtractor.applyFilter() - - # recover output objects from GeosBlockExtractor filter and merge internal blocks - volumeBlockExtracted: vtkMultiBlockDataSet = blockExtractor.extractedGeosDomain.volume - assert volumeBlockExtracted is not None, "Extracted Volume mesh is null." - outputCells.ShallowCopy( self.mergeBlocksFilter( volumeBlockExtracted, False ) ) - - wellBlockExtracted: vtkMultiBlockDataSet = blockExtractor.extractedGeosDomain.well - assert wellBlockExtracted is not None, "Extracted Well mesh is null." - outputWells.ShallowCopy( self.mergeBlocksFilter( wellBlockExtracted, False ) ) - - outputCells.Modified() - outputWells.Modified() - - self.m_logger.info( "Volume blocks were successfully splitted from " + - "wells, and ranks were merged together." ) - return True - - def mergeBlocksFilter( self: Self, - input: vtkMultiBlockDataSet, - convertSurfaces: bool = False ) -> vtkMultiBlockDataSet: - """Apply vtk merge block filter on input multi block mesh. - - Args: - input (vtkMultiBlockDataSet): multiblock mesh to merge - convertSurfaces (bool, optional): True to convert surface from vtkUnstructuredGrid to - vtkPolyData. Defaults to False. - - Returns: - vtkMultiBlockDataSet: Multiblock mesh composed of internal merged blocks. - """ - 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/PVGeomechanicsWorkflowVolume.py b/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflowVolume.py index 50a6a0f3..7c550c37 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflowVolume.py +++ b/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflowVolume.py @@ -30,7 +30,7 @@ VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, ) -from geos.pv.plugins.PVExtractMergeBlocksVolume import PVExtractMergeBlocksVolume +from geos.pv.plugins.PVGeosBlockExtractAndMerge import PVGeosBlockExtractAndMerge from geos.pv.plugins.PVGeomechanicsCalculator import PVGeomechanicsCalculator __doc__ = """ @@ -353,12 +353,12 @@ def doExtractAndMerge( self: Self ) -> bool: Returns: bool: True if extraction and merge successfully eneded, False otherwise """ - filter: PVExtractMergeBlocksVolume = PVExtractMergeBlocksVolume() + filter: PVGeosBlockExtractAndMerge = PVGeosBlockExtractAndMerge() filter.SetInputConnection( self.GetInputConnection( 0, 0 ) ) filter.SetLogger( self.m_logger ) filter.Update() - # recover output objects from PVExtractMergeBlocksVolume + # recover output objects from PVGeosBlockExtractAndMerge self.m_volumeMesh.ShallowCopy( filter.GetOutputDataObject( 0 ) ) self.m_volumeMesh.Modified() return True diff --git a/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflowVolumeSurface.py b/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflowVolumeSurface.py index c1370de6..d383c457 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflowVolumeSurface.py +++ b/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflowVolumeSurface.py @@ -30,8 +30,7 @@ DEFAULT_ROCK_COHESION, WATER_DENSITY, ) -from geos.pv.plugins.PVExtractMergeBlocksVolumeSurface import ( - PVExtractMergeBlocksVolumeSurface, ) +from geos.pv.plugins.PVGeosBlockExtractAndMerge import PVGeosBlockExtractAndMerge from geos.pv.plugins.PVGeomechanicsCalculator import PVGeomechanicsCalculator from geos.pv.plugins.PVSurfaceGeomechanics import PVSurfaceGeomechanics @@ -352,12 +351,12 @@ def doExtractAndMerge( self: Self ) -> bool: Returns: bool: True if extraction and merge successfully eneded, False otherwise """ - filter: PVExtractMergeBlocksVolumeSurface = PVExtractMergeBlocksVolumeSurface() + filter: PVGeosBlockExtractAndMerge = PVGeosBlockExtractAndMerge() filter.SetInputConnection( self.GetInputConnection( 0, 0 ) ) filter.SetLogger( self.m_logger ) filter.Update() - # recover output objects from PVExtractMergeBlocksVolumeSurface + # recover output objects from PVGeosBlockExtractAndMerge self.m_volumeMesh.ShallowCopy( filter.GetOutputDataObject( 0 ) ) self.m_surfaceMesh.ShallowCopy( filter.GetOutputDataObject( 1 ) ) self.m_volumeMesh.Modified() diff --git a/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflowVolumeSurfaceWell.py b/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflowVolumeSurfaceWell.py index 693c6055..3c8ac8fb 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflowVolumeSurfaceWell.py +++ b/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflowVolumeSurfaceWell.py @@ -30,8 +30,7 @@ DEFAULT_ROCK_COHESION, WATER_DENSITY, ) -from geos.pv.plugins.PVExtractMergeBlocksVolumeSurfaceWell import ( - PVExtractMergeBlocksVolumeSurfaceWell, ) +from geos.pv.plugins.PVGeosBlockExtractAndMerge import PVGeosBlockExtractAndMerge from geos.pv.plugins.PVGeomechanicsCalculator import PVGeomechanicsCalculator from geos.pv.plugins.PVSurfaceGeomechanics import PVSurfaceGeomechanics @@ -359,12 +358,12 @@ def doExtractAndMerge( self: Self ) -> bool: Returns: bool: True if extraction and merge successfully eneded, False otherwise """ - filter: PVExtractMergeBlocksVolumeSurfaceWell = ( PVExtractMergeBlocksVolumeSurfaceWell() ) + filter: PVGeosBlockExtractAndMerge = PVGeosBlockExtractAndMerge() filter.SetInputConnection( self.GetInputConnection( 0, 0 ) ) filter.SetLogger( self.m_logger ) filter.Update() - # recover output objects from PVExtractMergeBlocksVolumeSurfaceWell + # recover output objects from PVGeosBlockExtractAndMerge self.m_volumeMesh.ShallowCopy( filter.GetOutputDataObject( 0 ) ) self.m_surfaceMesh.ShallowCopy( filter.GetOutputDataObject( 1 ) ) self.m_wells.ShallowCopy( filter.GetOutputDataObject( 2 ) ) diff --git a/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflowVolumeWell.py b/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflowVolumeWell.py index 1e7c9980..44aeb977 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflowVolumeWell.py +++ b/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflowVolumeWell.py @@ -31,8 +31,7 @@ WATER_DENSITY, ) -from geos.pv.plugins.PVExtractMergeBlocksVolumeWell import ( - PVExtractMergeBlocksVolumeWell, ) +from geos.pv.plugins.PVGeosBlockExtractAndMerge import PVGeosBlockExtractAndMerge from geos.pv.plugins.PVGeomechanicsCalculator import PVGeomechanicsCalculator __doc__ = """ @@ -366,12 +365,12 @@ def doExtractAndMerge( self: Self ) -> bool: Returns: bool: True if extraction and merge successfully eneded, False otherwise """ - filter: PVExtractMergeBlocksVolumeWell = PVExtractMergeBlocksVolumeWell() + filter: PVGeosBlockExtractAndMerge = PVGeosBlockExtractAndMerge() filter.SetInputConnection( self.GetInputConnection( 0, 0 ) ) filter.SetLogger( self.m_logger ) filter.Update() - # recover output objects from PVExtractMergeBlocksVolumeWell + # recover output objects from PVGeosBlockExtractAndMerge self.m_volumeMesh.ShallowCopy( filter.GetOutputDataObject( 0 ) ) self.m_wells.ShallowCopy( filter.GetOutputDataObject( 1 ) ) self.m_volumeMesh.Modified() diff --git a/geos-pv/src/geos/pv/plugins/PVGeosBlockExtractAndMerge.py b/geos-pv/src/geos/pv/plugins/PVGeosBlockExtractAndMerge.py new file mode 100644 index 00000000..beea5400 --- /dev/null +++ b/geos-pv/src/geos/pv/plugins/PVGeosBlockExtractAndMerge.py @@ -0,0 +1,302 @@ +# 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 sys +import logging +import numpy as np +import numpy.typing as npt + +from pathlib import Path +from typing_extensions import Self + +# update sys.path to load all GEOS Python Package dependencies +geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent +sys.path.insert( 0, str( geos_pv_path / "src" ) ) +from geos.pv.utils.config import update_paths + +update_paths() + +from geos.mesh.utils.arrayHelpers import getAttributeSet +from geos.mesh.utils.arrayModifiers import ( copyAttribute, createCellCenterAttribute ) +from geos.mesh.utils.multiblockHelpers import getBlockNames + +from geos.utils.GeosOutputsConstants import ( GeosMeshOutputsEnum, GeosDomainNameEnum, + getAttributeToTransferFromInitialTime ) + +from geos.pv.utils.paraviewTreatments import getTimeStepIndex +from geos.pv.utils.workflowFunctions import doExtractAndMerge + +from vtkmodules.vtkCommonCore import ( vtkInformation, vtkInformationVector ) +from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet + +from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] + VTKPythonAlgorithmBase, smdomain, smproperty, smproxy ) +from paraview.detail.loghandler import ( # type: ignore[import-not-found] + VTKHandler ) + +__doc__ = """ +PVGeosBlockExtractAndMerge is a Paraview plugin processing the input mesh at the current time in two steps: + 1. Extraction of domains (volume, fault and well) from a GEOS output multiBlockDataSet mesh + 2. Actions on each region of a GEOS output domain (volume, fault, wells) to: + * Merge Ranks + * Identify "Fluids" and "Rock" phases + * Rename "Rock" attributes depending on the phase they refer to for more clarity + * Convert volume meshes to surface if needed + * Copy "geomechanics" attributes from the initial timestep to the current one if they exist + +This filter results in 3 output pipelines with the vtkMultiBlockDataSet: + - "Volume" contains the volume domain + - "Fault" contains the fault domain if it exist + - "Well" contains the well domain if it exist + +Input and output meshes are vtkMultiBlockDataSet. + +.. Important:: + - The input mesh must be an output of a GEOS simulation or contain at least three blocks labeled with the same domain names: + * "CellElementRegion" for volume domain + * "SurfaceElementRegion" for fault domain if the input contains fault + * "WellElementRegion" for well domain if the input contains well + * See more https://geosx-geosx.readthedocs-hosted.com/en/latest/docs/sphinx/datastructure/ElementRegions.html?_sm_au_=iVVT5rrr5fN00R8sQ0WpHK6H8sjL6#xml-element-elementregions + - The filter detected automatically the three domains. If one of them if not in the input mesh, the associated output pipeline will be empty + +To use it: + +* Load the module in Paraview: Tools>Manage Plugins...>Load new>PVGeosBlockExtractAndMerge. +* Select the Geos output .pvd file loaded in Paraview. +* Search and Apply PVGeosBlockExtractAndMerge Filter. +""" + +loggerTitle: str = "Extract & Merge GEOS Block" + + +@smproxy.filter( + name="PVGeosBlockExtractAndMerge", + label="Geos Extract and Merge Blocks", +) +@smproperty.xml( """ + + + + + + + + + + """ ) +@smproperty.input( name="Input", port_index=0 ) +@smdomain.datatype( dataTypes=[ "vtkMultiBlockDataSet" ], composite_data_supported=True ) +class PVGeosBlockExtractAndMerge( VTKPythonAlgorithmBase ): + + def __init__( self: Self ) -> None: + """Paraview plugin to extract and merge ranks from Geos output Mesh. + + To apply in the case of output ".pvd" file contains Volume, Fault or + Well elements. + """ + super().__init__( + nInputPorts=1, + nOutputPorts=3, + inputType="vtkMultiBlockDataSet", + outputType="vtkMultiBlockDataSet", + ) + + self.extractFault: bool = True + self.extractWell: bool = True + + # All time steps from input + self.timeSteps: npt.NDArray[ np.float64 ] = np.array( [] ) + # The time step of the input when the plugin is called or updated + self.currentTimeStepIndex: int = 0 + # The time step studies. It is incremental -1 during the initialization, from 0 to self.currentTimeStepIndex during the computation and -2 at the end of the computation + self.requestDataStep: int = -1 + + self.outputCellsT0: vtkMultiBlockDataSet = vtkMultiBlockDataSet() + + self.logger = logging.getLogger( loggerTitle ) + self.logger.setLevel( logging.INFO ) + self.logger.addHandler( VTKHandler() ) + + def RequestDataObject( + self: Self, + request: vtkInformation, + inInfoVec: list[ vtkInformationVector ], + outInfoVec: vtkInformationVector, + ) -> int: + """Inherited from VTKPythonAlgorithmBase::RequestDataObject. + + Args: + request (vtkInformation): request + inInfoVec (list[vtkInformationVector]): input objects + outInfoVec (vtkInformationVector): output objects + + Returns: + int: 1 if calculation successfully ended, 0 otherwise. + """ + inData = self.GetInputData( inInfoVec, 0, 0 ) + assert inData is not None + + outDataCells = self.GetOutputData( outInfoVec, 0 ) + if outDataCells is None or ( not outDataCells.IsA( "vtkMultiBlockDataSet" ) ): + outDataCells = vtkMultiBlockDataSet() + outInfoVec.GetInformationObject( 0 ).Set( outDataCells.DATA_OBJECT(), outDataCells ) # type: ignore + + outDataFaults = self.GetOutputData( outInfoVec, 1 ) + if outDataFaults is None or ( not outDataFaults.IsA( "vtkMultiBlockDataSet" ) ): + outDataFaults = vtkMultiBlockDataSet() + outInfoVec.GetInformationObject( 1 ).Set( outDataFaults.DATA_OBJECT(), outDataFaults ) # type: ignore + + outDataWells = self.GetOutputData( outInfoVec, 2 ) + if outDataWells is None or ( not outDataWells.IsA( "vtkMultiBlockDataSet" ) ): + outDataWells = vtkMultiBlockDataSet() + outInfoVec.GetInformationObject( 2 ).Set( outDataWells.DATA_OBJECT(), outDataWells ) # type: ignore + + return super().RequestDataObject( request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] + + def RequestInformation( + self: Self, + request: vtkInformation, + inInfoVec: list[ vtkInformationVector ], + 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. + """ + self.logger.info( f"Apply plugin { self.logger.name }." ) + + executive = self.GetExecutive() + inInfo = inInfoVec[ 0 ] + self.timeSteps = inInfo.GetInformationObject( 0 ).Get( executive.TIME_STEPS() ) + + # The time of the input mesh + currentTime = inInfo.GetInformationObject( 0 ).Get( executive.UPDATE_TIME_STEP() ) + + self.currentTimeStepIndex = getTimeStepIndex( currentTime, self.timeSteps ) + + inputMesh: vtkMultiBlockDataSet = vtkMultiBlockDataSet.GetData( inInfo ) + blockNames: list[ str ] = getBlockNames( inputMesh ) + if GeosDomainNameEnum.FAULT_DOMAIN_NAME.value not in blockNames: + self.extractFault = False + self.logger.warning( + f"The mesh to process does not contains the block named { GeosDomainNameEnum.FAULT_DOMAIN_NAME.value }. The output 'Fault' will be an empty mesh." + ) + + if GeosDomainNameEnum.WELL_DOMAIN_NAME.value not in blockNames: + self.extractWell = False + self.logger.warning( + f"The mesh to process does not contains the block named { GeosDomainNameEnum.WELL_DOMAIN_NAME.value }. The output 'Well' will be an empty mesh." + ) + + return 1 + + def RequestUpdateExtent( + self: Self, + request: vtkInformation, + inInfoVec: list[ vtkInformationVector ], + outInfoVec: vtkInformationVector, + ) -> int: + """Inherited from VTKPythonAlgorithmBase::RequestUpdateExtent. + + This function is call at each change of time: + on Paraview with the widget Time + if request.Set( self.GetExecutive.CONTINUE_EXECUTING(), 1 ) is set (time steps iterator) + + 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() + inInfo = inInfoVec[ 0 ] + + # Get update time step from Paraview + if self.requestDataStep == -2: + currentTime = inInfo.GetInformationObject( 0 ).Get( executive.UPDATE_TIME_STEP() ) + self.currentTimeStepIndex = getTimeStepIndex( currentTime, self.timeSteps ) + self.requestDataStep = self.currentTimeStepIndex + + # Update requestDataStep + else: + self.requestDataStep += 1 + + # Update time according to requestDataStep iterator + inInfo.GetInformationObject( 0 ).Set( executive.UPDATE_TIME_STEP(), self.timeSteps[ self.requestDataStep ] ) + + 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: + inputMesh: vtkMultiBlockDataSet = vtkMultiBlockDataSet.GetData( inInfoVec[ 0 ] ) + executive = self.GetExecutive() + + # First time step, compute the initial properties (useful for geomechanics analyses) + if self.requestDataStep == 0: + self.logger.info( "Apply the plugin for the first time step to get the initial properties." ) + doExtractAndMerge( inputMesh, self.outputCellsT0, vtkMultiBlockDataSet(), vtkMultiBlockDataSet(), + self.extractFault, self.extractWell ) + request.Set( executive.CONTINUE_EXECUTING(), 1 ) + + # Current time step, extract, merge, rename and transfer properties + if self.requestDataStep == self.currentTimeStepIndex: + self.logger.info( f"Apply the filter for the current time step: { self.currentTimeStepIndex }." ) + outputCells: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 0 ) + outputFaults: vtkMultiBlockDataSet = self.GetOutputData( + outInfoVec, 1 ) if self.extractFault else vtkMultiBlockDataSet() + outputWells: vtkMultiBlockDataSet = self.GetOutputData( + outInfoVec, 2 ) if self.extractWell else vtkMultiBlockDataSet() + doExtractAndMerge( inputMesh, outputCells, outputFaults, outputWells, self.extractFault, + self.extractWell ) + + # Copy attributes from the initial time step + meshAttributes: set[ str ] = getAttributeSet( self.outputCellsT0, False ) + for ( attributeName, attributeNewName ) in getAttributeToTransferFromInitialTime().items(): + if attributeName in meshAttributes: + copyAttribute( self.outputCellsT0, outputCells, attributeName, attributeNewName, False, + self.logger ) + + # Create elementCenter attribute in the volume mesh if needed + cellCenterAttributeName: str = GeosMeshOutputsEnum.ELEMENT_CENTER.attributeName + createCellCenterAttribute( outputCells, cellCenterAttributeName ) + + # Stop the time step iteration + request.Remove( executive.CONTINUE_EXECUTING() ) + + # Set to -2 in case time changes on Paraview + self.requestDataStep = -2 + + self.logger.info( f"The plugin { self.logger.name } succeeded." ) + except AssertionError as e: + self.logger.error( f"The plugin failed.\n{e}" ) + return 0 + except Exception as e: + mess1: str = "Block extraction and merge failed due to:" + self.logger.critical( mess1 ) + self.logger.critical( e, exc_info=True ) + return 0 + return 1 diff --git a/geos-pv/src/geos/pv/utils/workflowFunctions.py b/geos-pv/src/geos/pv/utils/workflowFunctions.py new file mode 100644 index 00000000..39ae35ee --- /dev/null +++ b/geos-pv/src/geos/pv/utils/workflowFunctions.py @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. +# SPDX-FileContributor: Romain Baville +# ruff: noqa: E402 # disable Module level import not at top of file + + +from geos.processing.post_processing.GeosBlockExtractor import GeosBlockExtractor +from geos.processing.post_processing.GeosBlockMerge import GeosBlockMerge + +from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet + +from paraview.detail.loghandler import ( VTKHandler ) # type: ignore[import-not-found] + + +def doExtractAndMerge( + mesh: vtkMultiBlockDataSet, + outputCells: vtkMultiBlockDataSet, + outputFaults: vtkMultiBlockDataSet, + outputWells: vtkMultiBlockDataSet, + extractFault: bool, + extractWell: bool, +) -> None: + """Apply block extraction and merge. + + Args: + mesh (vtkMultiBlockDataSet): Mesh to process. + outputCells (vtkMultiBlockDataSet): Output volume mesh. + outputFaults (vtkMultiBlockDataSet): Output surface mesh. + outputWells (vtkMultiBlockDataSet): Output well mesh. + """ + # Extract blocks + blockExtractor: GeosBlockExtractor = GeosBlockExtractor( mesh, extractFault=extractFault, extractWell=extractWell, speHandler=True ) + if not blockExtractor.logger.hasHandlers(): + blockExtractor.setLoggerHandler( VTKHandler() ) + blockExtractor.applyFilter() + + # recover output objects from GeosBlockExtractor filter and merge internal blocks + volumeBlockExtracted: vtkMultiBlockDataSet = blockExtractor.extractedGeosDomain.volume + outputCells.ShallowCopy( mergeBlocksFilter( volumeBlockExtracted, False ) ) + outputCells.Modified() + + if extractFault: + faultBlockExtracted: vtkMultiBlockDataSet = blockExtractor.extractedGeosDomain.fault + outputFaults.ShallowCopy( mergeBlocksFilter( faultBlockExtracted, True ) ) + outputFaults.Modified() + + if extractWell: + wellBlockExtracted: vtkMultiBlockDataSet = blockExtractor.extractedGeosDomain.well + outputWells.ShallowCopy( mergeBlocksFilter( wellBlockExtracted, False ) ) + outputWells.Modified() + + return + +def mergeBlocksFilter( mesh: vtkMultiBlockDataSet, + convertSurfaces: bool = False, ) -> vtkMultiBlockDataSet: + """Apply vtk merge block filter on input multi block mesh. + + Args: + mesh (vtkMultiBlockDataSet): Mesh to merge. + convertSurfaces (bool, optional): True to convert surface from vtkUnstructuredGrid to vtkPolyData. + Defaults to False. + + Returns: + vtkMultiBlockDataSet: Mesh composed of internal merged blocks. + """ + mergeBlockFilter: GeosBlockMerge = GeosBlockMerge( mesh, convertSurfaces, True ) + if not mergeBlockFilter.logger.hasHandlers(): + mergeBlockFilter.setLoggerHandler( VTKHandler() ) + mergeBlockFilter.applyFilter() + mergedBlocks: vtkMultiBlockDataSet = vtkMultiBlockDataSet() + mergedBlocks.ShallowCopy( mergeBlockFilter.getOutput() ) + return mergedBlocks diff --git a/geos-utils/src/geos/utils/GeosOutputsConstants.py b/geos-utils/src/geos/utils/GeosOutputsConstants.py index 52d50a3f..0dc0e691 100644 --- a/geos-utils/src/geos/utils/GeosOutputsConstants.py +++ b/geos-utils/src/geos/utils/GeosOutputsConstants.py @@ -12,7 +12,6 @@ .. WARNING:: Names may need to be updated when modifications occur in the GEOS code. - .. todo:: If possible, link GEOS names directly with GEOS code instead of redefining @@ -95,17 +94,33 @@ class GeosMeshSuffixEnum( Enum ): STRAIN_SUFFIX = "strain" PERMEABILITY_SUFFIX = "_permeability" POROSITY_SUFFIX = "_porosity" + POROSITY_INIT_SUFFIX = "_initialPorosity" POROSITY_REF_SUFFIX = "_referencePorosity" BULK_MODULUS_SUFFIX = "_bulkModulus" SHEAR_MODULUS_SUFFIX = "_shearModulus" GRAIN_BULK_MODULUS_SUFFIX = "_grainBulkModulus" BIOT_COEFFICIENT_SUFFIX = "_biotCoefficient" - # fluid attributes suffix - PHASE_DENSITY_SUFFIX = "_density" - PHASE_MASS_DENSITY_SUFFIX = "_phaseMassDensity" - PHASE_VISCOSITY_SUFFIX = "_viscosity" + # fluid attributes suffix multiPhases + TOTAL_FLUID_DENSITY_SUFFIX = "_totalDensity" + PHASE_DENSITY_SUFFIX = "_phaseDensity" PHASE_FRACTION_SUFFIX = "_phaseFraction" + PHASE_MASS_DENSITY_SUFFIX = "_phaseMassDensity" + PHASE_VISCOSITY_SUFFIX = "_phaseViscosity" + PHASE_INTERNAL_ENERGY_SUFFIX = "_phaseInternalEnergy" + PHASE_COMP_FRACTION_SUFFIX = "_phaseCompFraction" + PHASE_REAL_PERM_SUFFIX = "_phaseRelPerm" + PHASE_TRAP_VOL_FRACTION_SUFFIX = "_phaseTrappedVolFraction" + + # fluid attributes suffix singlePhase + FLUID_DENSITY_SUFFIX = "_density" + FLUID_VISCOSITY_SUFFIX = "_viscosity" + FLUID_DENSITY_DERIVATE_SUFFIX = "_dDensity" + FLUID_VISCOSITY_DERIVATE_SUFFIX = "_dViscosity" + FLUID_INTERNAL_ENERGY_SUFFIX = "_internalEnergy" + FLUID_INTERNAL_ENERGY_DERIVATE_SUFFIX = "_dInternalEnergy" + FLUID_ENTHALPY_SUFFIX = "_enthalpy" + FLUID_ENTHALPY_DERIVATE_SUFFIX = "_dEnthalpy" # surface attribute transfer suffix SURFACE_PLUS_SUFFIX = "_Plus" @@ -220,20 +235,36 @@ def __init__( self: Self, phaseType: str, attributes: tuple[ str, ...] ) -> None ( GeosMeshSuffixEnum.DENSITY_SUFFIX.value, GeosMeshSuffixEnum.STRESS_SUFFIX.value, - GeosMeshSuffixEnum.BULK_MODULUS_SUFFIX.value, - GeosMeshSuffixEnum.SHEAR_MODULUS_SUFFIX.value, + GeosMeshSuffixEnum.PERMEABILITY_SUFFIX.value, GeosMeshSuffixEnum.POROSITY_SUFFIX.value, + GeosMeshSuffixEnum.POROSITY_INIT_SUFFIX.value, GeosMeshSuffixEnum.POROSITY_REF_SUFFIX.value, - GeosMeshSuffixEnum.PERMEABILITY_SUFFIX.value, + GeosMeshSuffixEnum.BULK_MODULUS_SUFFIX.value, + GeosMeshSuffixEnum.SHEAR_MODULUS_SUFFIX.value, + GeosMeshSuffixEnum.GRAIN_BULK_MODULUS_SUFFIX.value, + GeosMeshSuffixEnum.BIOT_COEFFICIENT_SUFFIX.value, ), ) FLUID = ( "Fluid", ( + GeosMeshSuffixEnum.TOTAL_FLUID_DENSITY_SUFFIX.value, GeosMeshSuffixEnum.PHASE_DENSITY_SUFFIX.value, - GeosMeshSuffixEnum.PHASE_VISCOSITY_SUFFIX.value, GeosMeshSuffixEnum.PHASE_FRACTION_SUFFIX.value, GeosMeshSuffixEnum.PHASE_MASS_DENSITY_SUFFIX.value, + GeosMeshSuffixEnum.PHASE_VISCOSITY_SUFFIX.value, + GeosMeshSuffixEnum.PHASE_INTERNAL_ENERGY_SUFFIX.value, + GeosMeshSuffixEnum.PHASE_COMP_FRACTION_SUFFIX.value, + GeosMeshSuffixEnum.PHASE_REAL_PERM_SUFFIX.value, + GeosMeshSuffixEnum.PHASE_TRAP_VOL_FRACTION_SUFFIX.value, + GeosMeshSuffixEnum.FLUID_DENSITY_SUFFIX.value, + GeosMeshSuffixEnum.FLUID_VISCOSITY_SUFFIX.value, + GeosMeshSuffixEnum.FLUID_DENSITY_DERIVATE_SUFFIX.value, + GeosMeshSuffixEnum.FLUID_VISCOSITY_DERIVATE_SUFFIX.value, + GeosMeshSuffixEnum.FLUID_INTERNAL_ENERGY_SUFFIX.value, + GeosMeshSuffixEnum.FLUID_INTERNAL_ENERGY_DERIVATE_SUFFIX.value, + GeosMeshSuffixEnum.FLUID_ENTHALPY_SUFFIX.value, + GeosMeshSuffixEnum.FLUID_ENTHALPY_DERIVATE_SUFFIX.value, ), ) UNKNOWN = ( "Other", () )