-
Notifications
You must be signed in to change notification settings - Fork 0
refactor: Move PVMergeBlocksEnhanced
plugin and create the VTK filter
#129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 24 commits
06eca95
8651018
fbe6bf2
b9855ce
21d8b7d
54c70eb
2de326a
3d20b99
22828f7
29a038b
3d153cd
af3990e
d9f289d
14bcb85
05a1c17
df24923
f771dd6
4d9c3d0
b3112f9
b1b681b
682f50e
7da34e9
43dd2d1
396ed11
e0ab6fc
ba9e2b1
f8d2b1a
b8b1481
cd48656
b839532
262932a
b98b49e
073f4cd
3d0c57c
3a7b061
907b2d6
24a8d27
f9b5cf0
df766cd
d572700
18b13f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
# SPDX-License-Identifier: Apache-2.0 | ||
# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. | ||
# SPDX-FileContributor: Paloma Martinez | ||
# ruff: noqa: E402 # disable Module level import not at top of file | ||
import logging | ||
|
||
from typing_extensions import Self | ||
|
||
from geos.utils.Logger import Logger, getLogger | ||
from geos.utils.Errors import VTKError | ||
from geos.mesh.utils.multiblockModifiers import mergeBlocks | ||
|
||
from vtkmodules.vtkCommonDataModel import ( | ||
vtkMultiBlockDataSet, | ||
vtkUnstructuredGrid, | ||
) | ||
|
||
__doc__ = """ | ||
Merge Blocks Keeping Partial Attributes is a filter that allows to merge blocks from a multiblock dataset while keeping partial attributes. | ||
|
||
Input is a vtkMultiBlockDataSet and output is a vtkUnstructuredGrid. | ||
|
||
.. Note:: | ||
- You may encounter issues if two datasets of the input multiblock dataset have duplicated cell IDs. | ||
- Partial attributes are filled with default values depending on their types. | ||
- 0 for uint data. | ||
- -1 for int data. | ||
- nan for float data. | ||
|
||
|
||
To use it: | ||
|
||
.. code-block:: python | ||
|
||
from geos.mesh.processing.MergeBlockEnhanced import MergeBlockEnhanced | ||
import logging | ||
from geos.utils.Errors import VTKError | ||
|
||
# Define filter inputs | ||
multiblockdataset: vtkMultiblockDataSet | ||
speHandler: bool # optional | ||
|
||
# Instantiate the filter | ||
filter: MergeBlockEnhanced = MergeBlockEnhanced( multiblockdataset, speHandler ) | ||
|
||
# Use your own handler (if speHandler is True) | ||
yourHandler: logging.Handler | ||
filter.setLoggerHandler( yourHandler ) | ||
|
||
# Do calculations | ||
try: | ||
filter.applyFilter() | ||
except VTKError: | ||
logging.error("Something went wrong in VTK") | ||
|
||
# Get the merged mesh | ||
filter.getOutput() | ||
""" | ||
|
||
loggerTitle: str = "Merge Block Enhanced" | ||
|
||
|
||
class MergeBlockEnhanced: | ||
|
||
def __init__( | ||
self: Self, | ||
inputMesh: vtkMultiBlockDataSet, | ||
speHandler: bool = False, | ||
) -> None: | ||
"""Merge a multiblock dataset and keep the partial attributes in the output mesh. | ||
|
||
Partial attributes are filled with default values depending on the data type such that: | ||
- 0 for uint data. | ||
- -1 for int data. | ||
- nan for float data. | ||
|
||
Args: | ||
inputMesh (vtkMultiBlockDataSet): The input multiblock dataset to merge. | ||
speHandler (bool, optional) : True to use a specific handler, False to use the internal handler. | ||
Defaults to False. | ||
""" | ||
self.inputMesh: vtkMultiBlockDataSet = inputMesh | ||
self.outputMesh: vtkUnstructuredGrid = vtkUnstructuredGrid() | ||
|
||
# Logger | ||
self.logger: Logger | ||
if not speHandler: | ||
self.logger = getLogger( loggerTitle, True ) | ||
else: | ||
self.logger = logging.getLogger( loggerTitle ) | ||
self.logger.setLevel( logging.INFO ) | ||
|
||
def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: | ||
"""Set a specific handler for the filter logger. | ||
|
||
In this filter 4 log levels are use, .info, .error, .warning and .critical, be sure to have at least the same 4 levels. | ||
|
||
Args: | ||
handler (logging.Handler): The handler to add. | ||
""" | ||
if not self.logger.hasHandlers(): | ||
self.logger.addHandler( handler ) | ||
else: | ||
self.logger.warning( | ||
"The logger already has an handler, to use yours set the argument 'speHandler' to True during the filter initialization." | ||
) | ||
|
||
def applyFilter( self: Self ) -> None: | ||
"""Merge the blocks of a multiblock dataset mesh. | ||
|
||
Returns: | ||
bool: True if the blocks were successfully merged, False otherwise. | ||
|
||
Raises: | ||
VTKError (geos.utils.Errors) : error captured if any from the VTK log | ||
""" | ||
self.logger.info( f"Applying filter { self.logger.name }." ) | ||
|
||
outputMesh: vtkUnstructuredGrid | ||
outputMesh = mergeBlocks( self.inputMesh, keepPartialAttributes=True, logger=self.logger ) | ||
self.logger.info( f"The filter {self.logger.name} failed." ) | ||
self.outputMesh = outputMesh | ||
self.logger.info( f"The filter {self.logger.name} succeeded." ) | ||
|
||
|
||
def getOutput( self: Self ) -> vtkUnstructuredGrid: | ||
"""Get the merged mesh. | ||
|
||
Returns: | ||
vtkUnstructuredGrid: The merged mesh. | ||
""" | ||
return self.outputMesh |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,97 @@ | ||
# SPDX-License-Identifier: Apache-2.0 | ||
# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. | ||
# SPDX-FileContributor: Martin Lemay | ||
# SPDX-FileContributor: Martin Lemay, Paloma Martinez | ||
from typing import Union | ||
import logging | ||
# import re | ||
|
||
from vtkmodules.vtkCommonDataModel import ( vtkCompositeDataSet, vtkDataObjectTreeIterator, vtkMultiBlockDataSet, | ||
vtkDataSet ) | ||
from vtkmodules.vtkFiltersCore import vtkAppendDataSets | ||
vtkUnstructuredGrid, vtkDataSet ) | ||
from packaging.version import Version | ||
from vtkmodules.vtkCommonCore import vtkLogger | ||
|
||
# TODO: remove this condition when all codes are adapted for VTK newest version. | ||
import vtk | ||
if Version( vtk.__version__ ) >= Version( "9.5" ): | ||
from vtkmodules.vtkFiltersParallel import vtkMergeBlocks | ||
else: | ||
from vtkmodules.vtkFiltersCore import vtkAppendDataSets | ||
|
||
from geos.mesh.utils.arrayModifiers import fillAllPartialAttributes | ||
from geos.utils.Errors import VTKError | ||
from geos.utils.Logger import ( getLogger, Logger, VTKCaptureLog, RegexExceptionFilter) | ||
|
||
__doc__ = """Contains a method to merge blocks of a VTK multiblock dataset.""" | ||
|
||
|
||
# TODO : fix function for keepPartialAttributes = True | ||
def mergeBlocks( | ||
input: Union[ vtkMultiBlockDataSet, vtkCompositeDataSet ], | ||
inputMesh: Union[ vtkMultiBlockDataSet, vtkCompositeDataSet ], | ||
keepPartialAttributes: bool = False, | ||
) -> vtkDataSet: | ||
"""Merge all blocks of a multi block mesh. | ||
logger: Union[ Logger, None ] = None, | ||
) -> vtkUnstructuredGrid: | ||
"""Merge all blocks of a multiblock dataset mesh with the possibility of keeping all partial attributes present in the initial mesh. | ||
|
||
Args: | ||
input (vtkMultiBlockDataSet | vtkCompositeDataSet ): composite | ||
object to merge blocks | ||
keepPartialAttributes (bool): if True, keep partial attributes after merge. | ||
|
||
Defaults to False. | ||
inputMesh (vtkMultiBlockDataSet | vtkCompositeDataSet ): The input multiblock dataset to merge. | ||
keepPartialAttributes (bool): If False (default), only global attributes are kept during the merge. If True, partial attributes are filled with default values and kept in the output mesh. | ||
logger (Union[Logger, None], optional): A logger to manage the output messages. | ||
Defaults to None, an internal logger is used. | ||
|
||
Returns: | ||
vtkUnstructuredGrid: merged block object | ||
vtkUnstructuredGrid: Merged dataset or input mesh if it's already a single block | ||
|
||
Raises: | ||
geos.utilsVTKError (): | ||
|
||
.. Note:: | ||
Default filling values: | ||
- 0 for uint data. | ||
- -1 for int data. | ||
- nan for float data. | ||
|
||
.. Warning:: This function will not work properly if there are duplicated cell IDs in the different blocks of the input mesh. | ||
|
||
""" | ||
if keepPartialAttributes: | ||
fillAllPartialAttributes( input ) | ||
|
||
af = vtkAppendDataSets() | ||
af.MergePointsOn() | ||
iter: vtkDataObjectTreeIterator = vtkDataObjectTreeIterator() | ||
iter.SetDataSet( input ) | ||
iter.VisitOnlyLeavesOn() | ||
iter.GoToFirstItem() | ||
while iter.GetCurrentDataObject() is not None: | ||
block: vtkDataSet = vtkDataSet.SafeDownCast( iter.GetCurrentDataObject() ) | ||
af.AddInputData( block ) | ||
iter.GoToNextItem() | ||
af.Update() | ||
return af.GetOutputDataObject( 0 ) | ||
if logger is None: | ||
logger: Logger = getLogger( "mergeBlocks", True ) | ||
|
||
vtkLogger.SetStderrVerbosity(vtkLogger.VERBOSITY_TRACE) | ||
logger.addFilter(RegexExceptionFilter()) # will raise VTKError if captured VTK Error | ||
logger.setLevel(logging.DEBUG) | ||
|
||
# Fill the partial attributes with default values to keep them during the merge. | ||
if keepPartialAttributes and not fillAllPartialAttributes( inputMesh, logger ): | ||
logger.warning( "Failed to fill partial attributes. Merging without keeping partial attributes." ) | ||
|
||
if Version( vtk.__version__ ) >= Version( "9.5" ): | ||
filter: vtkMergeBlocks = vtkMergeBlocks() | ||
filter.SetInputData( inputMesh ) | ||
filter.Update() | ||
Comment on lines
+71
to
+73
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Genuinely asking, should we also call the VTKCaptureLog here because we are using the vtkMergeBlocks like done in the else block ?
And in a more general view, should we use the VTKCaptureLog anytime we use a vtk filter ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a short and a long answer, but IMO yes. |
||
|
||
outputMesh: vtkUnstructuredGrid = filter.GetOutputDataObject( 0 ) | ||
|
||
else: | ||
if inputMesh.IsA( "vtkDataSet" ): | ||
logger.warning( "Input mesh is already a single block." ) | ||
outputMesh = inputMesh | ||
else: | ||
with VTKCaptureLog() as captured_log: | ||
|
||
af: vtkAppendDataSets = vtkAppendDataSets() | ||
af.MergePointsOn() | ||
iterator: vtkDataObjectTreeIterator = vtkDataObjectTreeIterator() | ||
iterator.SetDataSet( inputMesh ) | ||
iterator.VisitOnlyLeavesOn() | ||
iterator.GoToFirstItem() | ||
while iterator.GetCurrentDataObject() is not None: | ||
block: vtkDataSet = vtkDataSet.SafeDownCast( iterator.GetCurrentDataObject() ) | ||
af.AddInputData( block ) | ||
iterator.GoToNextItem() | ||
|
||
af.Update() | ||
captured_log.seek(0) #be kind let's just rewind | ||
captured = captured_log.read().decode() | ||
|
||
logger.debug(captured.strip()) | ||
outputMesh: vtkUnstructuredGrid = af.GetOutputDataObject( 0 ) | ||
|
||
return outputMesh |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?xml version="1.0"?> | ||
<VTKFile type="vtkMultiBlockDataSet" version="1.0"> | ||
<vtkMultiBlockDataSet> | ||
<Block name="cartesianMesh"> | ||
<Block name="Level0"> | ||
<Block name="CellElementRegion"> | ||
<Block name="reservoir"> | ||
<DataSet name="rank_0" file="simpleReservoirViz_small_000478/cartesianMesh/Level0/reservoir/rank_0.vtu" /> | ||
<DataSet name="rank_1" file="simpleReservoirViz_small_000478/cartesianMesh/Level0/reservoir/rank_1.vtu" /> | ||
</Block> | ||
</Block> | ||
<Block name="WellElementRegion"> | ||
<Block name="wellRegion"> | ||
<DataSet name="rank_1" file="simpleReservoirViz_small_000478/cartesianMesh/Level0/wellRegion/rank_1.vtu" /> | ||
</Block> | ||
</Block> | ||
</Block> | ||
</Block> | ||
</vtkMultiBlockDataSet> | ||
</VTKFile> |
Uh oh!
There was an error while loading. Please reload this page.