diff --git a/docs/geos_mesh_docs/processing.rst b/docs/geos_mesh_docs/processing.rst
index 8a448e31..fec7f4b7 100644
--- a/docs/geos_mesh_docs/processing.rst
+++ b/docs/geos_mesh_docs/processing.rst
@@ -4,6 +4,15 @@ Processing filters
The `processing` module of `geos-mesh` package contains filters to process meshes.
+geos.mesh.processing.FillPartialArrays filter
+----------------------------------------------
+
+.. automodule:: geos.mesh.processing.FillPartialArrays
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
geos.mesh.processing.meshQualityMetricHelpers module
-----------------------------------------------------
@@ -12,6 +21,7 @@ geos.mesh.processing.meshQualityMetricHelpers module
:undoc-members:
:show-inheritance:
+
geos.mesh.processing.SplitMesh filter
--------------------------------------
diff --git a/docs/geos_pv_docs/processing.rst b/docs/geos_pv_docs/processing.rst
index 13a6d04d..43920078 100644
--- a/docs/geos_pv_docs/processing.rst
+++ b/docs/geos_pv_docs/processing.rst
@@ -1,6 +1,11 @@
Post-/Pre-processing
=========================
+PVFillPartialArrays
+--------------------
+.. automodule:: geos.pv.plugins.PVFillPartialArrays
+
+
PVSplitMesh
----------------------------------
diff --git a/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py b/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py
new file mode 100644
index 00000000..b89df611
--- /dev/null
+++ b/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py
@@ -0,0 +1,154 @@
+# SPDX-License-Identifier: Apache-2.0
+# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies.
+# SPDX-FileContributor: Romain Baville, Martin Lemay
+
+from typing_extensions import Self
+from typing import Union, Any
+
+from geos.utils.Logger import logging, Logger, getLogger
+from geos.mesh.utils.arrayModifiers import fillPartialAttributes
+from geos.mesh.utils.arrayHelpers import isAttributeInObject
+
+from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet
+
+__doc__ = """
+Fill partial attributes of the input mesh with constant values per component.
+
+Input mesh is vtkMultiBlockDataSet and attributes to fill must be partial.
+
+The list of filling values per attribute is given by a dictionary.
+Its keys are the attribute names and its items are the list of filling values for each component.
+
+If the list of filling value is None, attributes are filled with the same constant value for each component;
+0 for uint data, -1 for int data and nan for float data.
+
+To use a handler of yours for the logger, set the variable 'speHandler' to True and add it to the filter
+with the member function addLoggerHandler.
+
+To use it:
+
+.. code-block:: python
+
+ from geos.mesh.processing.FillPartialArrays import FillPartialArrays
+
+ # Filter inputs.
+ multiBlockDataSet: vtkMultiBlockDataSet
+ dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ]
+ # Optional inputs.
+ speHandler: bool
+
+ # Instantiate the filter.
+ filter: FillPartialArrays = FillPartialArrays( multiBlockDataSet, dictAttributesValues, speHandler )
+
+ # Set the handler of yours (only if speHandler is True).
+ yourHandler: logging.Handler
+ filter.addLoggerHandler( yourHandler )
+
+ # Do calculations.
+ filter.applyFilter()
+"""
+
+loggerTitle: str = "Fill Partial Attribute"
+
+
+class FillPartialArrays:
+
+ def __init__(
+ self: Self,
+ multiBlockDataSet: vtkMultiBlockDataSet,
+ dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ],
+ speHandler: bool = False,
+ ) -> None:
+ """Fill partial attributes with constant value per component.
+
+ If the list of filling values for an attribute is None, it will filled with the default value for each component:
+ 0 for uint data.
+ -1 for int data.
+ nan for float data.
+
+ Args:
+ multiBlockDataSet (vtkMultiBlockDataSet): The mesh where to fill the attribute.
+ dictAttributesValues (dict[str, Any]): The dictionary with the attribute to fill as keys and the list of filling values as items.
+ speHandler (bool, optional): True to use a specific handler, False to use the internal handler.
+ Defaults to False.
+ """
+ self.multiBlockDataSet: vtkMultiBlockDataSet = multiBlockDataSet
+ self.dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ] = dictAttributesValues
+
+ # Logger.
+ self.logger: Logger
+ if not speHandler:
+ self.logger = getLogger( loggerTitle, True )
+ else:
+ self.logger = logging.getLogger( loggerTitle )
+ self.logger.setLevel( logging.INFO )
+
+ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None:
+ """Set a specific handler for the filter logger.
+
+ In this filter 4 log levels are use, .info, .error, .warning and .critical, be sure to have at least the same 4 levels.
+
+ Args:
+ handler (logging.Handler): The handler to add.
+ """
+ if not self.logger.hasHandlers():
+ self.logger.addHandler( handler )
+ else:
+ self.logger.warning(
+ "The logger already has an handler, to use yours set the argument 'speHandler' to True during the filter initialization."
+ )
+
+ def applyFilter( self: Self ) -> bool:
+ """Create a constant attribute per region in the mesh.
+
+ Returns:
+ boolean (bool): True if calculation successfully ended, False otherwise.
+ """
+ self.logger.info( f"Apply filter { self.logger.name }." )
+
+ for attributeName in self.dictAttributesValues:
+ self._setPieceRegionAttribute( attributeName )
+ if self.onPoints is None:
+ self.logger.error( f"{ attributeName } is not in the mesh." )
+ self.logger.error( f"The attribute { attributeName } has not been filled." )
+ self.logger.error( f"The filter { self.logger.name } failed." )
+ return False
+
+ if self.onBoth:
+ self.logger.error(
+ f"Their is two attribute named { attributeName }, one on points and the other on cells. The attribute must be unique."
+ )
+ self.logger.error( f"The attribute { attributeName } has not been filled." )
+ self.logger.error( f"The filter { self.logger.name } failed." )
+ return False
+
+ if not fillPartialAttributes( self.multiBlockDataSet,
+ attributeName,
+ onPoints=self.onPoints,
+ listValues=self.dictAttributesValues[ attributeName ],
+ logger=self.logger ):
+ self.logger.error( f"The filter { self.logger.name } failed." )
+ return False
+
+ self.logger.info( f"The filter { self.logger.name } succeed." )
+
+ return True
+
+ def _setPieceRegionAttribute( self: Self, attributeName: str ) -> None:
+ """Set the attribute self.onPoints and self.onBoth.
+
+ self.onPoints is True if the region attribute is on points, False if it is on cells, None otherwise.
+
+ self.onBoth is True if a region attribute is on points and on cells, False otherwise.
+
+ Args:
+ attributeName (str): The name of the attribute to verify.
+ """
+ self.onPoints: Union[ bool, None ] = None
+ self.onBoth: bool = False
+ if isAttributeInObject( self.multiBlockDataSet, attributeName, False ):
+ self.onPoints = False
+ if isAttributeInObject( self.multiBlockDataSet, attributeName, True ):
+ if self.onPoints is False:
+ self.onBoth = True
+ self.onPoints = True
diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py
index 79a1b6b6..e5c8b0a6 100644
--- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py
+++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py
@@ -41,6 +41,7 @@
isAttributeGlobal,
getVtkArrayTypeInObject,
getVtkArrayTypeInMultiBlock,
+ getNumberOfComponentsMultiBlock,
)
from geos.mesh.utils.multiblockHelpers import (
getBlockElementIndexesFlatten,
@@ -61,18 +62,18 @@ def fillPartialAttributes(
multiBlockDataSet: Union[ vtkMultiBlockDataSet, vtkCompositeDataSet, vtkDataObject ],
attributeName: str,
onPoints: bool = False,
- value: Any = np.nan,
+ listValues: Union[ list[ Any ], None ] = None,
logger: Union[ Logger, None ] = None,
) -> bool:
- """Fill input partial attribute of multiBlockDataSet with the same value for all the components.
+ """Fill input partial attribute of multiBlockDataSet with a constant value per component.
Args:
multiBlockDataSet (vtkMultiBlockDataSet | vtkCompositeDataSet | vtkDataObject): MultiBlockDataSet where to fill the attribute.
attributeName (str): Attribute name.
onPoints (bool, optional): True if attributes are on points, False if they are on cells.
Defaults to False.
- value (Any, optional): Filling value. It is recommended to use numpy scalar type for the values.
- Defaults to:
+ listValues (list[Any], optional): List of filling value for each component.
+ Defaults to None, the filling value is for all components:
-1 for int VTK arrays.
0 for uint VTK arrays.
nan for float VTK arrays.
@@ -98,40 +99,53 @@ def fillPartialAttributes(
# Get information of the attribute to fill.
vtkDataType: int = getVtkArrayTypeInMultiBlock( multiBlockDataSet, attributeName, onPoints )
- infoAttributes: dict[ str, int ] = getAttributesWithNumberOfComponents( multiBlockDataSet, onPoints )
- nbComponents: int = infoAttributes[ attributeName ]
+ nbComponents: int = getNumberOfComponentsMultiBlock( multiBlockDataSet, attributeName, onPoints )
componentNames: tuple[ str, ...] = ()
if nbComponents > 1:
componentNames = getComponentNames( multiBlockDataSet, attributeName, onPoints )
+ typeMapping: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap()
+ valueType: type = typeMapping[ vtkDataType ]
# Set the default value depending of the type of the attribute to fill
- if np.isnan( value ):
- typeMapping: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap()
- valueType: type = typeMapping[ vtkDataType ]
+ if listValues is None:
+ defaultValue: Any
+ logger.warning( f"The attribute { attributeName } is filled with the default value for each component." )
# Default value for float types is nan.
if vtkDataType in ( VTK_FLOAT, VTK_DOUBLE ):
- value = valueType( value )
+ defaultValue = valueType( np.nan )
logger.warning(
- f"{ attributeName } vtk data type is { vtkDataType } corresponding to { value.dtype } numpy type, default value is automatically set to nan."
+ f"{ attributeName } vtk data type is { vtkDataType } corresponding to { defaultValue.dtype } numpy type, default value is automatically set to nan."
)
# Default value for int types is -1.
elif vtkDataType in ( VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE ):
- value = valueType( -1 )
+ defaultValue = valueType( -1 )
logger.warning(
- f"{ attributeName } vtk data type is { vtkDataType } corresponding to { value.dtype } numpy type, default value is automatically set to -1."
+ f"{ attributeName } vtk data type is { vtkDataType } corresponding to { defaultValue.dtype } numpy type, default value is automatically set to -1."
)
# Default value for uint types is 0.
elif vtkDataType in ( VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT,
VTK_UNSIGNED_LONG_LONG ):
- value = valueType( 0 )
+ defaultValue = valueType( 0 )
logger.warning(
- f"{ attributeName } vtk data type is { vtkDataType } corresponding to { value.dtype } numpy type, default value is automatically set to 0."
+ f"{ attributeName } vtk data type is { vtkDataType } corresponding to { defaultValue.dtype } numpy type, default value is automatically set to 0."
)
else:
logger.error( f"The type of the attribute { attributeName } is not compatible with the function." )
return False
- values: list[ Any ] = [ value for _ in range( nbComponents ) ]
+ listValues = [ defaultValue ] * nbComponents
+
+ else:
+ if len( listValues ) != nbComponents:
+ return False
+
+ for idValue in range( nbComponents ):
+ value: Any = listValues[ idValue ]
+ if type( value ) is not valueType:
+ listValues[ idValue ] = valueType( listValues[ idValue ] )
+ logger.warning(
+ f"The filling value { value } for the attribute { attributeName } has not the correct type, it is convert to the numpy scalar type { valueType().dtype }."
+ )
# Parse the multiBlockDataSet to create and fill the attribute on blocks where it is not.
iterator: vtkDataObjectTreeIterator = vtkDataObjectTreeIterator()
@@ -141,7 +155,7 @@ def fillPartialAttributes(
while iterator.GetCurrentDataObject() is not None:
dataSet: vtkDataSet = vtkDataSet.SafeDownCast( iterator.GetCurrentDataObject() )
if not isAttributeInObjectDataSet( dataSet, attributeName, onPoints ) and \
- not createConstantAttributeDataSet( dataSet, values, attributeName, componentNames, onPoints, vtkDataType, logger ):
+ not createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, onPoints, vtkDataType, logger ):
return False
iterator.GoToNextItem()
@@ -172,7 +186,7 @@ def fillAllPartialAttributes(
infoAttributes: dict[ str, int ] = getAttributesWithNumberOfComponents( multiBlockDataSet, onPoints )
for attributeName in infoAttributes:
if not isAttributeGlobal( multiBlockDataSet, attributeName, onPoints ) and \
- not fillPartialAttributes( multiBlockDataSet, attributeName, onPoints, logger=logger ):
+ not fillPartialAttributes( multiBlockDataSet, attributeName, onPoints=onPoints, logger=logger ):
return False
return True
@@ -384,7 +398,7 @@ def createConstantAttributeDataSet(
if valueType in ( int, float ):
npType: type = type( np.array( listValues )[ 0 ] )
logger.warning(
- f"During the creation of the constant attribute { attributeName }, values will be converted from { valueType } to { npType }."
+ f"During the creation of the constant attribute { attributeName }, values have been converted from { valueType } to { npType }."
)
logger.warning( "To avoid any issue with the conversion, please use directly numpy scalar type for the values" )
valueType = npType
diff --git a/geos-mesh/tests/test_FillPartialArrays.py b/geos-mesh/tests/test_FillPartialArrays.py
new file mode 100644
index 00000000..b1fd8f3a
--- /dev/null
+++ b/geos-mesh/tests/test_FillPartialArrays.py
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: Apache-2.0
+# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies.
+# SPDX-FileContributor: Romain Baville
+# SPDX-License-Identifier: Apache 2.0
+# ruff: noqa: E402 # disable Module level import not at top of file
+# mypy: disable-error-code="operator"
+import pytest
+
+from typing import Any
+from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet
+
+from geos.mesh.processing.FillPartialArrays import FillPartialArrays
+
+
+@pytest.mark.parametrize( "dictAttributesValues", [
+ ( {
+ "PORO": None
+ } ),
+ ( {
+ "PERM": None
+ } ),
+ ( {
+ "PORO": None,
+ "PERM": None
+ } ),
+ ( {
+ "PORO": [ 4 ]
+ } ),
+ ( {
+ "PERM": [ 4, 4, 4 ]
+ } ),
+ ( {
+ "PORO": [ 4 ],
+ "PERM": [ 4, 4, 4 ]
+ } ),
+ ( {
+ "PORO": None,
+ "PERM": [ 4, 4, 4 ]
+ } ),
+ ( {
+ "PORO": [ 4 ],
+ "PERM": None
+ } ),
+] )
+def test_FillPartialArrays(
+ dataSetTest: vtkMultiBlockDataSet,
+ dictAttributesValues: dict[ str, Any ],
+) -> None:
+ """Test FillPartialArrays vtk filter."""
+ multiBlockDataSet: vtkMultiBlockDataSet = dataSetTest( "multiblock" )
+
+ filter: FillPartialArrays = FillPartialArrays( multiBlockDataSet, dictAttributesValues )
+ assert filter.applyFilter()
diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py
index cf9b6311..91ecc423 100644
--- a/geos-mesh/tests/test_arrayModifiers.py
+++ b/geos-mesh/tests/test_arrayModifiers.py
@@ -5,7 +5,7 @@
# ruff: noqa: E402 # disable Module level import not at top of file
# mypy: disable-error-code="operator"
import pytest
-from typing import Union, Any, cast
+from typing import Union, Any
import numpy as np
import numpy.typing as npt
@@ -45,24 +45,25 @@
@pytest.mark.parametrize(
- "idBlock, attributeName, nbComponentsTest, componentNamesTest, onPoints, value, valueTest, vtkDataTypeTest",
+ "idBlock, attributeName, nbComponentsTest, componentNamesTest, onPoints, listValues, listValuesTest, vtkDataTypeTest",
[
# Test fill an attribute on point and on cell.
- ( 1, "CellAttribute", 3, ( "AX1", "AX2", "AX3" ), False, np.nan, np.nan, VTK_DOUBLE ),
- ( 1, "PointAttribute", 3, ( "AX1", "AX2", "AX3" ), True, np.nan, np.nan, VTK_DOUBLE ),
- # Test fill attributes with different number of component.
- ( 1, "PORO", 1, (), False, np.nan, np.float32( np.nan ), VTK_FLOAT ),
- ( 1, "PERM", 3, ( "AX1", "AX2", "AX3" ), False, np.nan, np.float32( np.nan ), VTK_FLOAT ),
- # Test fill an attribute with default value.
- ( 1, "FAULT", 1, (), False, np.nan, np.int32( -1 ), VTK_INT ),
- ( 0, "collocated_nodes", 2, ( None, None ), True, np.nan, np.int64( -1 ), VTK_ID_TYPE ),
- # Test fill an attribute with specified value.
- ( 1, "PORO", 1, (), False, np.float32( 4 ), np.float32( 4 ), VTK_FLOAT ),
- ( 1, "CellAttribute", 3, ( "AX1", "AX2", "AX3" ), False, 4., np.float64( 4 ), VTK_DOUBLE ),
- ( 1, "CellAttribute", 3, ( "AX1", "AX2", "AX3" ), False, np.float64( 4 ), np.float64( 4 ), VTK_DOUBLE ),
- ( 1, "FAULT", 1, (), False, np.int32( 4 ), np.int32( 4 ), VTK_INT ),
- ( 0, "collocated_nodes", 2, ( None, None ), True, 4, np.int64( 4 ), VTK_ID_TYPE ),
- ( 0, "collocated_nodes", 2, ( None, None ), True, np.int64( 4 ), np.int64( 4 ), VTK_ID_TYPE ),
+ ( 1, "PointAttribute", 3,
+ ( "AX1", "AX2", "AX3" ), True, None, [ np.float64(
+ np.nan ), np.float64( np.nan ), np.float64( np.nan ) ], VTK_DOUBLE ),
+ ( 1, "CellAttribute", 3,
+ ( "AX1", "AX2", "AX3" ), False, None, [ np.float64(
+ np.nan ), np.float64( np.nan ), np.float64( np.nan ) ], VTK_DOUBLE ),
+ # Test fill attributes with different number of component with or without component names.
+ ( 1, "PORO", 1, (), False, None, [ np.float32( np.nan ) ], VTK_FLOAT ),
+ ( 0, "collocated_nodes", 2, ( None, None ), True, None, [ np.int64( -1 ), np.int64( -1 ) ], VTK_ID_TYPE ),
+ # Test fill an attribute with different type of value.
+ ( 1, "FAULT", 1, (), False, None, [ np.int32( -1 ) ], VTK_INT ),
+ ( 1, "FAULT", 1, (), False, [ 4 ], [ np.int32( 4 ) ], VTK_INT ),
+ ( 1, "PORO", 1, (), False, [ 4 ], [ np.float32( 4 ) ], VTK_FLOAT ),
+ ( 0, "collocated_nodes", 2, ( None, None ), True, [ 4, 4 ], [ np.int64( 4 ), np.int64( 4 ) ], VTK_ID_TYPE ),
+ ( 1, "CellAttribute", 3, ( "AX1", "AX2", "AX3" ), False, [ 4, 4, 4 ],
+ [ np.float64( 4 ), np.float64( 4 ), np.float64( 4 ) ], VTK_DOUBLE ),
] )
def test_fillPartialAttributes(
dataSetTest: vtkMultiBlockDataSet,
@@ -71,18 +72,20 @@ def test_fillPartialAttributes(
nbComponentsTest: int,
componentNamesTest: tuple[ str, ...],
onPoints: bool,
- value: Any,
- valueTest: Any,
+ listValues: Union[ list[ Any ], None ],
+ listValuesTest: list[ Any ],
vtkDataTypeTest: int,
) -> None:
"""Test filling a partial attribute from a multiblock with values."""
multiBlockDataSetTest: vtkMultiBlockDataSet = dataSetTest( "multiblock" )
-
# Fill the attribute in the multiBlockDataSet.
- assert arrayModifiers.fillPartialAttributes( multiBlockDataSetTest, attributeName, onPoints, value )
+ assert arrayModifiers.fillPartialAttributes( multiBlockDataSetTest,
+ attributeName,
+ onPoints=onPoints,
+ listValues=listValues )
# Get the dataSet where the attribute has been filled.
- dataSet: vtkDataSet = cast( vtkDataSet, multiBlockDataSetTest.GetBlock( idBlock ) )
+ dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTest.GetBlock( idBlock ) )
# Get the filled attribute.
data: Union[ vtkPointData, vtkCellData ]
@@ -107,19 +110,19 @@ def test_fillPartialAttributes(
## Create the constant array test from the value.
npArrayTest: npt.NDArray[ Any ]
if nbComponentsTest > 1:
- npArrayTest = np.array( [ [ valueTest for _ in range( nbComponentsTest ) ] for _ in range( nbElements ) ] )
+ npArrayTest = np.array( [ listValuesTest for _ in range( nbElements ) ] )
else:
- npArrayTest = np.array( [ valueTest for _ in range( nbElements ) ] )
+ npArrayTest = np.array( [ listValuesTest[ 0 ] for _ in range( nbElements ) ] )
npArrayFilled: npt.NDArray[ Any ] = vnp.vtk_to_numpy( attributeFilled )
assert npArrayFilled.dtype == npArrayTest.dtype
- if np.isnan( value ) and vtkDataTypeTest in ( VTK_FLOAT, VTK_DOUBLE ):
+ if listValues is None and vtkDataTypeTest in ( VTK_FLOAT, VTK_DOUBLE ):
assert np.isnan( npArrayFilled ).all()
else:
assert ( npArrayFilled == npArrayTest ).all()
vtkDataTypeFilled: int = attributeFilled.GetDataType()
- assert vtkDataTypeTest == vtkDataTypeFilled
+ assert vtkDataTypeFilled == vtkDataTypeTest
@pytest.mark.parametrize( "multiBlockDataSetName", [ "multiblock" ] )
@@ -133,7 +136,7 @@ def test_FillAllPartialAttributes(
nbBlock: int = multiBlockDataSetTest.GetNumberOfBlocks()
for idBlock in range( nbBlock ):
- dataSet: vtkDataSet = cast( vtkDataSet, multiBlockDataSetTest.GetBlock( idBlock ) )
+ dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTest.GetBlock( idBlock ) )
attributeExist: int
for attributeNameOnPoint in [ "PointAttribute", "collocated_nodes" ]:
attributeExist = dataSet.GetPointData().HasArray( attributeNameOnPoint )
@@ -190,7 +193,7 @@ def test_createConstantAttributeMultiBlock(
nbBlock = multiBlockDataSetTest.GetNumberOfBlocks()
for idBlock in range( nbBlock ):
- dataSet: vtkDataSet = cast( vtkDataSet, multiBlockDataSetTest.GetBlock( idBlock ) )
+ dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTest.GetBlock( idBlock ) )
data: Union[ vtkPointData, vtkCellData ]
data = dataSet.GetPointData() if onPoints else dataSet.GetCellData()
@@ -409,8 +412,8 @@ def test_copyAttribute(
# Parse the two multiBlockDataSet and test if the attribute has been copied.
nbBlocks: int = multiBlockDataSetFrom.GetNumberOfBlocks()
for idBlock in range( nbBlocks ):
- dataSetFrom: vtkDataSet = cast( vtkDataSet, multiBlockDataSetFrom.GetBlock( idBlock ) )
- dataSetTo: vtkDataSet = cast( vtkDataSet, multiBlockDataSetTo.GetBlock( idBlock ) )
+ dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetFrom.GetBlock( idBlock ) )
+ dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTo.GetBlock( idBlock ) )
dataFrom: Union[ vtkPointData, vtkCellData ]
dataTo: Union[ vtkPointData, vtkCellData ]
if onPoints:
@@ -494,7 +497,7 @@ def test_renameAttributeMultiblock(
newAttributeName,
onPoints,
)
- block: vtkDataSet = cast( vtkDataSet, vtkMultiBlockDataSetTest.GetBlock( 0 ) )
+ block: vtkDataSet = vtkDataSet.SafeDownCast( vtkMultiBlockDataSetTest.GetBlock( 0 ) )
data: Union[ vtkPointData, vtkCellData ]
if onPoints:
data = block.GetPointData()
diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py
new file mode 100644
index 00000000..218d6bfc
--- /dev/null
+++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py
@@ -0,0 +1,170 @@
+# 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
+from pathlib import Path
+from typing import Union, Any
+from typing_extensions import Self
+
+from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found]
+ VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy,
+) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py
+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 vtkmodules.vtkCommonDataModel import (
+ vtkMultiBlockDataSet, )
+
+from vtkmodules.vtkCommonCore import (
+ vtkInformation,
+ vtkInformationVector,
+)
+
+# 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.processing.FillPartialArrays import FillPartialArrays
+
+__doc__ = """
+Fill partial arrays of input mesh.
+
+Input and output are vtkMultiBlockDataSet.
+
+To use it:
+
+* Load the module in Paraview: Tools>Manage Plugins...>Load new>PVFillPartialArrays.
+* Select the input mesh.
+* Select the partial arrays to fill.
+* Set the filling value (defaults to nan).
+* Apply.
+
+"""
+
+
+@smproxy.filter( name="PVFillPartialArrays", label="Fill Partial Arrays" )
+@smhint.xml( '' )
+@smproperty.input( name="Input", port_index=0 )
+@smdomain.datatype(
+ dataTypes=[ "vtkMultiBlockDataSet" ],
+ composite_data_supported=True,
+)
+class PVFillPartialArrays( VTKPythonAlgorithmBase ):
+
+ def __init__( self: Self, ) -> None:
+ """Fill a partial attribute with constant value per component."""
+ super().__init__( nInputPorts=1,
+ nOutputPorts=1,
+ inputType="vtkMultiBlockDataSet",
+ outputType="vtkMultiBlockDataSet" )
+
+ self.clearDictAttributesValues: bool = True
+ self.dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ] = {}
+
+
+ @smproperty.xml("""
+
+
+ Set the filling values for each partial attribute, use a coma between the value of each components:\n
+ attributeName | fillingValueComponent1 fillingValueComponent2 ...\n
+ To fill the attribute with the default value, live a blanc. The default value is:\n
+ 0 for uint type, -1 for int type and nan for float type.
+
+
+
+
+
+
+
+
+
+ """ )
+ def _setDictAttributesValues( self: Self, attributeName: str, values: str ) -> None:
+ """Set the dictionary with the region indexes and its corresponding list of value for each components.
+
+ Args:
+ attributeName (str): Name of the attribute to consider.
+ values (str): List of the filing values. If multiple components use a comma between the value of each component.
+ """
+ if self.clearDictAttributesValues:
+ self.dictAttributesValues = {}
+ self.clearDictAttributesValues = False
+
+ if attributeName is not None:
+ if values is not None :
+ self.dictAttributesValues[ attributeName ] = list( values.split( "," ) )
+ else:
+ self.dictAttributesValues[ attributeName ] = None
+
+ self.Modified()
+
+ 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 RequestData(
+ self: Self,
+ request: vtkInformation, # noqa: F841
+ 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.
+ """
+ inputMesh: vtkMultiBlockDataSet = self.GetInputData( inInfoVec, 0, 0 )
+ outputMesh: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 0 )
+ assert inputMesh is not None, "Input server mesh is null."
+ assert outputMesh is not None, "Output pipeline is null."
+
+ outputMesh.ShallowCopy( inputMesh )
+
+ filter: FillPartialArrays = FillPartialArrays( outputMesh,
+ self.dictAttributesValues,
+ True,
+ )
+
+ if not filter.logger.hasHandlers():
+ filter.setLoggerHandler( VTKHandler() )
+
+ filter.applyFilter()
+
+ self.clearDictAttributesValues = True
+
+ return 1