From 61de2de2c4382275759b945bf36410d17528cc0c Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 10 Feb 2026 16:26:33 +0100 Subject: [PATCH 1/5] refactor createConstantAttribute* functions --- .../src/geos/mesh/utils/arrayModifiers.py | 198 ++++---------- geos-mesh/tests/test_arrayModifiers.py | 241 +++++++++--------- .../CreateConstantAttributePerRegion.py | 7 +- .../geos/mesh_doctor/actions/generateCube.py | 6 +- 4 files changed, 181 insertions(+), 271 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 6bc763b3..8711d083 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -155,7 +155,7 @@ def fillPartialAttributes( for blockIndex in elementaryBlockIndexes: dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) if not isAttributeInObjectDataSet( dataSet, attributeName, piece ): - createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, piece, vtkDataType, + createConstantAttribute( dataSet, listValues, attributeName, componentNames, piece, vtkDataType, logger ) return @@ -267,7 +267,9 @@ def createConstantAttribute( Defaults to None, an internal logger is used. Raises: - TypeError: Error with the type of the mesh. + TypeError: Error with the type of the input mesh or the type of the input values or the vtkDataType. + AttributeError: The attribute is already on the mesh. + ValueError: Error with the vtkDataType (unknown) or with the piece. """ # Check if an external logger is given. if logger is None: @@ -275,154 +277,62 @@ def createConstantAttribute( # Deals with multiBlocksDataSets. if isinstance( mesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): - createConstantAttributeMultiBlock( mesh, listValues, attributeName, componentNames, piece, vtkDataType, logger ) + # Check if the attribute already exist in the input mesh. + if isAttributeInObjectMultiBlockDataSet( mesh, attributeName, piece ): + raise AttributeError( f"The attribute { attributeName } is already on the mesh." ) + + # Parse the multiBlockDataSet to create the constant attribute on each blocks. + elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( mesh ) + for blockIndex in elementaryBlockIndexes: + dataSet: vtkDataSet = vtkDataSet.SafeDownCast( mesh.GetDataSet( blockIndex ) ) + createConstantAttribute( dataSet, listValues, attributeName, componentNames, piece, vtkDataType, logger ) # Deals with dataSets. elif isinstance( mesh, vtkDataSet ): - createConstantAttributeDataSet( mesh, listValues, attributeName, componentNames, piece, vtkDataType, logger ) - - else: - raise TypeError( "Input mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet." ) - - return - - -def createConstantAttributeMultiBlock( - multiBlockDataSet: Union[ vtkMultiBlockDataSet, vtkCompositeDataSet ], - listValues: list[ Any ], - attributeName: str, - componentNames: tuple[ str, ...] = (), # noqa: C408 - piece: Piece = Piece.CELLS, - vtkDataType: Union[ int, None ] = None, - logger: Union[ Logger, None ] = None, -) -> None: - """Create a new attribute with a constant value per component on every block of the multiBlockDataSet. - - Args: - multiBlockDataSet (Union[vtkMultiBlockDataSet, vtkCompositeDataSet]): MultiBlockDataSet where to create the attribute. - listValues (list[Any]): List of values of the attribute for each components. It is recommended to use numpy scalar type for the values. - attributeName (str): Name of the attribute. - componentNames (tuple[str,...], optional): Name of the components for vectorial attributes. If one component, gives an empty tuple. - Defaults to an empty tuple. - piece (Piece): The piece of the attribute. - Defaults to Piece.CELLS - vtkDataType (Union[int, None], optional): Vtk data type of the attribute to create. - Defaults to None, the vtk data type is given by the type of the values. - - Warning with int8, uint8 and int64 type of value, the corresponding vtk data type are multiples. By default: - - int8 -> VTK_SIGNED_CHAR - - uint8 -> VTK_UNSIGNED_CHAR - - int64 -> VTK_LONG_LONG - logger (Union[Logger, None], optional): A logger to manage the output messages. - Defaults to None, an internal logger is used. - - Raises: - TypeError: Error with the type of the mesh. - AttributeError: Error with the attribute attributeName. - """ - # Check if an external logger is given. - if logger is None: - logger = getLogger( "createConstantAttributeMultiBlock", True ) - - # Check if the input mesh is inherited from vtkMultiBlockDataSet. - if not isinstance( multiBlockDataSet, vtkMultiBlockDataSet ): - raise TypeError( "Input mesh has to be inherited from vtkMultiBlockDataSet." ) - - # Check the piece. - if piece != Piece.POINTS and piece != Piece.CELLS: - raise ValueError( f"The attribute must be created on { Piece.POINTS.value } or { Piece.CELLS.value }." ) - - # Check if the attribute already exist in the input mesh. - if isAttributeInObjectMultiBlockDataSet( multiBlockDataSet, attributeName, piece ): - raise AttributeError( f"The attribute { attributeName } is already present in the mesh." ) - - # Parse the multiBlockDataSet to create the constant attribute on each blocks. - elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSet ) - for blockIndex in elementaryBlockIndexes: - dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) - createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, piece, vtkDataType, logger ) - - return - - -def createConstantAttributeDataSet( - dataSet: vtkDataSet, - listValues: list[ Any ], - attributeName: str, - componentNames: tuple[ str, ...] = (), # noqa: C408 - piece: Piece = Piece.CELLS, - vtkDataType: Union[ int, None ] = None, - logger: Union[ Logger, None ] = None, -) -> None: - """Create an attribute with a constant value per component in the dataSet. - - Args: - dataSet (vtkDataSet): DataSet where to create the attribute. - listValues (list[Any]): List of values of the attribute for each components. It is recommended to use numpy scalar type for the values. - attributeName (str): Name of the attribute. - componentNames (tuple[str,...], optional): Name of the components for vectorial attributes. If one component, gives an empty tuple. - Defaults to an empty tuple. - piece (Piece): The piece of the attribute. - Defaults to Piece.CELLS - vtkDataType (Union[int, None], optional): Vtk data type of the attribute to create. - Defaults to None, the vtk data type is given by the type of the values. - - Warning with int8, uint8 and int64 type of value, the corresponding vtk data type are multiples. By default: - - int8 -> VTK_SIGNED_CHAR - - uint8 -> VTK_UNSIGNED_CHAR - - int64 -> VTK_LONG_LONG - logger (Union[Logger, None], optional): A logger to manage the output messages. - Defaults to None, an internal logger is used. - - Raises: - TypeError: Error with the type of the npArray values. - ValueError: Error with the vtkDataType. - """ - # Check if an external logger is given. - if logger is None: - logger = getLogger( "createConstantAttributeDataSet", True ) - - # Check the piece. - if piece not in [ Piece.POINTS, Piece.CELLS ]: - raise ValueError( f"The attribute must be created on { Piece.POINTS.value } or { Piece.CELLS.value }." ) - - # Check if all the values of listValues have the same type. - valueType: type = type( listValues[ 0 ] ) - for value in listValues: - valueTypeTest: type = type( value ) - if valueType != valueTypeTest: - raise TypeError( "All values in the list of values must have the same type." ) - - # Convert int and float type into numpy scalar type. - if valueType in ( int, float ): - npType: type = type( np.array( listValues )[ 0 ] ) - logger.warning( - 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 - - # Check the consistency between the given value type and the vtk array type if it exists. - valueType = valueType().dtype - if vtkDataType is not None: - vtkNumpyTypeMap: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap() - if vtkDataType not in vtkNumpyTypeMap: - raise ValueError( f"The vtk data type { vtkDataType } is unknown." ) + # Check the piece. + if piece not in [ Piece.POINTS, Piece.CELLS ]: + raise ValueError( f"The attribute must be created on { Piece.POINTS.value } or { Piece.CELLS.value }." ) + + # Check if all the values of listValues have the same type. + valueType: type = type( listValues[ 0 ] ) + for value in listValues: + valueTypeTest: type = type( value ) + if valueType != valueTypeTest: + raise TypeError( "All values in the list of values must have the same type." ) + + # Convert int and float type into numpy scalar type. + if valueType in ( int, float ): + npType: type = type( np.array( listValues )[ 0 ] ) + logger.warning( + 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 + + # Check the consistency between the given value type and the vtk array type if it exists. + valueType = valueType().dtype + if vtkDataType is not None: + vtkNumpyTypeMap: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap() + if vtkDataType not in vtkNumpyTypeMap: + raise ValueError( f"The vtk data type { vtkDataType } is unknown." ) + + npArrayTypeFromVtk: npt.DTypeLike = vtkNumpyTypeMap[ vtkDataType ]().dtype + if npArrayTypeFromVtk != valueType: + raise TypeError( f"Input values in listValues type must be { npArrayTypeFromVtk }, not { valueType }." ) + + # Create the numpy array constant per component. + nbComponents: int = len( listValues ) + nbElements: int = ( mesh.GetNumberOfPoints() if piece == Piece.POINTS else mesh.GetNumberOfCells() ) + npArray: npt.NDArray[ Any ] + if nbComponents > 1: + npArray = np.array( [ listValues for _ in range( nbElements ) ], valueType ) + else: + npArray = np.array( [ listValues[ 0 ] for _ in range( nbElements ) ], valueType ) - npArrayTypeFromVtk: npt.DTypeLike = vtkNumpyTypeMap[ vtkDataType ]().dtype - if npArrayTypeFromVtk != valueType: - raise TypeError( f"Input values in listValues type must be { npArrayTypeFromVtk }, not { valueType }." ) + createAttribute( mesh, npArray, attributeName, componentNames, piece, vtkDataType, logger ) - # Create the numpy array constant per component. - nbComponents: int = len( listValues ) - nbElements: int = ( dataSet.GetNumberOfPoints() if piece == Piece.POINTS else dataSet.GetNumberOfCells() ) - npArray: npt.NDArray[ Any ] - if nbComponents > 1: - npArray = np.array( [ listValues for _ in range( nbElements ) ], valueType ) else: - npArray = np.array( [ listValues[ 0 ] for _ in range( nbElements ) ], valueType ) - - createAttribute( dataSet, npArray, attributeName, componentNames, piece, vtkDataType, logger ) + raise TypeError( "Input mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet." ) return diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index 4036d1f3..7015ce93 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -210,90 +210,55 @@ def test_createEmptyAttributeValueError() -> None: @pytest.mark.parametrize( - "attributeName, piece", + "meshName, listValues, componentNames, componentNamesTest, piece, vtkDataType, vtkDataTypeTest, attributeName", [ - # Test to create a new attribute on points and on cells. - ( "newAttribute", Piece.CELLS ), - ( "newAttribute", Piece.POINTS ), - ] ) -def test_createConstantAttributeMultiBlock( - dataSetTest: vtkMultiBlockDataSet, - attributeName: str, - piece: Piece, -) -> None: - """Test creation of constant attribute in multiblock dataset.""" - multiBlockDataSetTest: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - values: list[ float ] = [ np.nan ] - arrayModifiers.createConstantAttributeMultiBlock( multiBlockDataSetTest, values, attributeName, piece=piece ) - - elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetTest ) - for blockIndex in elementaryBlockIndexes: - dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTest.GetDataSet( blockIndex ) ) - data: Union[ vtkPointData, vtkCellData ] - data = dataSet.GetPointData() if piece == Piece.POINTS else dataSet.GetCellData() - - attributeWellCreated: int = data.HasArray( attributeName ) - assert attributeWellCreated == 1 - - -def test_createConstantAttributeMultiBlockRaiseTypeError( dataSetTest: vtkDataSet, ) -> None: - """Test the raises TypeError for the function createConstantAttributeMultiBlock with a wrong mesh type.""" - mesh: vtkDataSet = dataSetTest( "dataset" ) - with pytest.raises( TypeError ): - arrayModifiers.createConstantAttributeMultiBlock( mesh, [ np.int32( 42 ) ], "newAttribute" ) - - -def test_createConstantAttributeMultiBlockRaiseAttributeError( dataSetTest: vtkMultiBlockDataSet, ) -> None: - """Test the raises AttributeError for the function createConstantAttributeMultiBlock with a wrong attributeName.""" - mesh: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - with pytest.raises( AttributeError ): - arrayModifiers.createConstantAttributeMultiBlock( mesh, [ np.int32( 42 ) ], "PORO" ) - - -@pytest.mark.parametrize( - "listValues, componentNames, componentNamesTest, piece, vtkDataType, vtkDataTypeTest, attributeName", - [ - # Test attribute names. - ## Test with a new attributeName on cells and on points. - ( [ np.float32( 42 ) ], (), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), - ( [ np.float32( 42 ) ], (), (), Piece.CELLS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), + # Test mesh types. + ( "dataset", [ np.float32( 42 ) ], (), (), Piece.CELLS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), + ( "dataset", [ np.float32( 42 ) ], (), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), + ( "multiblock", [ np.float32( 42 ) ], (), (), Piece.CELLS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), + ( "multiblock", [ np.float32( 42 ) ], (), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), + # Test with an attribute name that exist on the opposite piece. + ( "dataset", [ np.float32( 42 ) ], (), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "PORO" ), + ( "multiblock", [ np.float32( 42 ) ], (), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "PORO" ), # Partial + ( "multiblock", [ np.float32( 42 ) ], (), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "GLOBAL_IDS_CELLS" ), # Global # Test the number of components and their names. - ( [ np.float32( 42 ) ], ( "X" ), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), - ( [ np.float32( 42 ), np.float32( 42 ) ], ( "X", "Y" ), + ( "dataset", [ np.float32( 42 ) ], ( "X" ), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), + ( "dataset", [ np.float32( 42 ), np.float32( 42 ) ], ( "X", "Y" ), ( "X", "Y" ), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), - ( [ np.float32( 42 ), np.float32( 42 ) ], ( "X", "Y", "Z" ), + ( "dataset", [ np.float32( 42 ), np.float32( 42 ) ], ( "X", "Y", "Z" ), ( "X", "Y" ), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), - ( [ np.float32( 42 ), np.float32( 42 ) ], (), + ( "dataset", [ np.float32( 42 ), np.float32( 42 ) ], (), ( "Component0", "Component1" ), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), # Test the type of the values. ## With numpy scalar type. - ( [ np.int8( 42 ) ], (), (), Piece.POINTS, None, VTK_SIGNED_CHAR, "newAttribute" ), - ( [ np.int8( 42 ) ], (), (), Piece.POINTS, VTK_SIGNED_CHAR, VTK_SIGNED_CHAR, "newAttribute" ), - ( [ np.int16( 42 ) ], (), (), Piece.POINTS, None, VTK_SHORT, "newAttribute" ), - ( [ np.int16( 42 ) ], (), (), Piece.POINTS, VTK_SHORT, VTK_SHORT, "newAttribute" ), - ( [ np.int32( 42 ) ], (), (), Piece.POINTS, None, VTK_INT, "newAttribute" ), - ( [ np.int32( 42 ) ], (), (), Piece.POINTS, VTK_INT, VTK_INT, "newAttribute" ), - ( [ np.int64( 42 ) ], (), (), Piece.POINTS, None, VTK_LONG_LONG, "newAttribute" ), - ( [ np.int64( 42 ) ], (), (), Piece.POINTS, VTK_LONG_LONG, VTK_LONG_LONG, "newAttribute" ), - ( [ np.uint8( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_CHAR, "newAttribute" ), - ( [ np.uint8( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_CHAR, "newAttribute" ), - ( [ np.uint16( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_SHORT, "newAttribute" ), - ( [ np.uint16( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_SHORT, "newAttribute" ), - ( [ np.uint32( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_INT, "newAttribute" ), - ( [ np.uint32( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_INT, VTK_UNSIGNED_INT, "newAttribute" ), - ( [ np.uint64( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_LONG_LONG, "newAttribute" ), - ( [ np.uint64( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_LONG_LONG, VTK_UNSIGNED_LONG_LONG, "newAttribute" ), - ( [ np.float32( 42 ) ], (), (), Piece.POINTS, None, VTK_FLOAT, "newAttribute" ), - ( [ np.float64( 42 ) ], (), (), Piece.POINTS, None, VTK_DOUBLE, "newAttribute" ), - ( [ np.float64( 42 ) ], (), (), Piece.POINTS, VTK_DOUBLE, VTK_DOUBLE, "newAttribute" ), + ( "dataset", [ np.int8( 42 ) ], (), (), Piece.POINTS, None, VTK_SIGNED_CHAR, "newAttribute" ), + ( "dataset", [ np.int8( 42 ) ], (), (), Piece.POINTS, VTK_SIGNED_CHAR, VTK_SIGNED_CHAR, "newAttribute" ), + ( "dataset", [ np.int16( 42 ) ], (), (), Piece.POINTS, None, VTK_SHORT, "newAttribute" ), + ( "dataset", [ np.int16( 42 ) ], (), (), Piece.POINTS, VTK_SHORT, VTK_SHORT, "newAttribute" ), + ( "dataset", [ np.int32( 42 ) ], (), (), Piece.POINTS, None, VTK_INT, "newAttribute" ), + ( "dataset", [ np.int32( 42 ) ], (), (), Piece.POINTS, VTK_INT, VTK_INT, "newAttribute" ), + ( "dataset", [ np.int64( 42 ) ], (), (), Piece.POINTS, None, VTK_LONG_LONG, "newAttribute" ), + ( "dataset", [ np.int64( 42 ) ], (), (), Piece.POINTS, VTK_LONG_LONG, VTK_LONG_LONG, "newAttribute" ), + ( "dataset", [ np.uint8( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_CHAR, "newAttribute" ), + ( "dataset", [ np.uint8( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_CHAR, "newAttribute" ), + ( "dataset", [ np.uint16( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_SHORT, "newAttribute" ), + ( "dataset", [ np.uint16( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_SHORT, "newAttribute" ), + ( "dataset", [ np.uint32( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_INT, "newAttribute" ), + ( "dataset", [ np.uint32( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_INT, VTK_UNSIGNED_INT, "newAttribute" ), + ( "dataset", [ np.uint64( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_LONG_LONG, "newAttribute" ), + ( "dataset", [ np.uint64( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_LONG_LONG, VTK_UNSIGNED_LONG_LONG, "newAttribute" ), + ( "dataset", [ np.float32( 42 ) ], (), (), Piece.POINTS, None, VTK_FLOAT, "newAttribute" ), + ( "dataset", [ np.float64( 42 ) ], (), (), Piece.POINTS, None, VTK_DOUBLE, "newAttribute" ), + ( "dataset", [ np.float64( 42 ) ], (), (), Piece.POINTS, VTK_DOUBLE, VTK_DOUBLE, "newAttribute" ), ## With python scalar type. - ( [ 42 ], (), (), Piece.POINTS, None, VTK_LONG_LONG, "newAttribute" ), - ( [ 42 ], (), (), Piece.POINTS, VTK_LONG_LONG, VTK_LONG_LONG, "newAttribute" ), - ( [ 42. ], (), (), Piece.POINTS, None, VTK_DOUBLE, "newAttribute" ), - ( [ 42. ], (), (), Piece.POINTS, VTK_DOUBLE, VTK_DOUBLE, "newAttribute" ), + ( "dataset", [ 42 ], (), (), Piece.POINTS, None, VTK_LONG_LONG, "newAttribute" ), + ( "dataset", [ 42 ], (), (), Piece.POINTS, VTK_LONG_LONG, VTK_LONG_LONG, "newAttribute" ), + ( "dataset", [ 42. ], (), (), Piece.POINTS, None, VTK_DOUBLE, "newAttribute" ), + ( "dataset", [ 42. ], (), (), Piece.POINTS, VTK_DOUBLE, VTK_DOUBLE, "newAttribute" ), ] ) -def test_createConstantAttributeDataSet( - dataSetTest: vtkDataSet, +def test_createConstantAttribute( + dataSetTest: Any, + meshName: str, listValues: list[ Any ], componentNames: tuple[ str, ...], componentNamesTest: tuple[ str, ...], @@ -302,71 +267,107 @@ def test_createConstantAttributeDataSet( vtkDataTypeTest: int, attributeName: str, ) -> None: - """Test constant attribute creation in dataset.""" - dataSet: vtkDataSet = dataSetTest( "dataset" ) + """Test constant attribute creation.""" + mesh: vtkDataSet | vtkMultiBlockDataSet = dataSetTest( meshName ) - # Create the new constant attribute in the dataSet. - arrayModifiers.createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, piece, - vtkDataType ) + # Create the new constant attribute in the mesh. + arrayModifiers.createConstantAttribute( mesh, listValues, attributeName, componentNames, piece, vtkDataType ) - # Get the created attribute. - data: Union[ vtkPointData, vtkCellData ] - nbElements: int - if piece == Piece.POINTS: - data = dataSet.GetPointData() - nbElements = dataSet.GetNumberOfPoints() + listDataSet: list[ vtkDataSet ] = [] + if isinstance( mesh, vtkDataSet ): + listDataSet.append( mesh ) else: - data = dataSet.GetCellData() - nbElements = dataSet.GetNumberOfCells() - attributeCreated: vtkDataArray = data.GetArray( attributeName ) + elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( mesh ) + for blockIndex in elementaryBlockIndexes: + listDataSet.append( vtkDataSet.SafeDownCast( mesh.GetDataSet( blockIndex ) ) ) - # Test the number of components and their names if multiple. - nbComponentsTest: int = len( listValues ) - nbComponentsCreated: int = attributeCreated.GetNumberOfComponents() - assert nbComponentsCreated == nbComponentsTest - if nbComponentsTest > 1: - componentNamesCreated: tuple[ str, ...] = tuple( - attributeCreated.GetComponentName( i ) for i in range( nbComponentsCreated ) ) - assert componentNamesCreated, componentNamesTest + for dataSet in listDataSet: + # Get the created attribute. + data: Union[ vtkPointData, vtkCellData ] + nbElements: int + if piece == Piece.POINTS: + data = dataSet.GetPointData() + nbElements = dataSet.GetNumberOfPoints() + else: + data = dataSet.GetCellData() + nbElements = dataSet.GetNumberOfCells() + attributeCreated: vtkDataArray = data.GetArray( attributeName ) + + # Test the number of components and their names if multiple. + nbComponentsTest: int = len( listValues ) + nbComponentsCreated: int = attributeCreated.GetNumberOfComponents() + assert nbComponentsCreated == nbComponentsTest + if nbComponentsTest > 1: + componentNamesCreated: tuple[ str, ...] = tuple( + attributeCreated.GetComponentName( i ) for i in range( nbComponentsCreated ) ) + assert componentNamesCreated, componentNamesTest + + # Test values and their types. + ## Create the constant array test from values in the list values. + npArrayTest: npt.NDArray[ Any ] + if len( listValues ) > 1: + npArrayTest = np.array( [ listValues for _ in range( nbElements ) ] ) + else: + npArrayTest = np.array( [ listValues[ 0 ] for _ in range( nbElements ) ] ) - # Test values and their types. - ## Create the constant array test from values in the list values. - npArrayTest: npt.NDArray[ Any ] - if len( listValues ) > 1: - npArrayTest = np.array( [ listValues for _ in range( nbElements ) ] ) - else: - npArrayTest = np.array( [ listValues[ 0 ] for _ in range( nbElements ) ] ) + npArrayCreated: npt.NDArray[ Any ] = vnp.vtk_to_numpy( attributeCreated ) + assert npArrayCreated.dtype == npArrayTest.dtype + assert ( npArrayCreated == npArrayTest ).all() - npArrayCreated: npt.NDArray[ Any ] = vnp.vtk_to_numpy( attributeCreated ) - assert npArrayCreated.dtype == npArrayTest.dtype - assert ( npArrayCreated == npArrayTest ).all() - - vtkDataTypeCreated: int = attributeCreated.GetDataType() - assert vtkDataTypeCreated == vtkDataTypeTest + vtkDataTypeCreated: int = attributeCreated.GetDataType() + assert vtkDataTypeCreated == vtkDataTypeTest @pytest.mark.parametrize( - "listValues, vtkDataType", + "meshName, listValues, vtkDataType", [ - ( [ np.int32( 42 ), np.int64( 42 ) ], VTK_DOUBLE ), # All the values in the listValues are not the same - ( [ np.int32( 42 ) ], VTK_DOUBLE ), # The type of the value is not coherent with the vtkDataType + ( "dataset", [ np.int32( 42 ), np.int64( 42 ) ], VTK_DOUBLE ), # All the values in the listValues are not the same + ( "dataset", [ np.int32( 42 ) ], VTK_DOUBLE ), # The type of the value is not coherent with the vtkDataType + ( "other", [ np.int64( 42 ) ], VTK_DOUBLE ), # The type of the mesh is wrong ] ) -def test_createConstantAttributeDataSetRaiseTypeError( +def test_createConstantAttributeRaiseTypeError( dataSetTest: vtkDataSet, + meshName: str, listValues: list[ Any ], vtkDataType: int, ) -> None: - """Test the raises TypeError for the function createConstantAttributeDataSet.""" - mesh: vtkDataSet = dataSetTest( "dataset" ) + """Test the raises TypeError for the function createConstantAttribute.""" + mesh: vtkCellData | vtkDataSet + if meshName == "other": + mesh = vtkCellData() + else: + mesh = dataSetTest( meshName ) with pytest.raises( TypeError ): - arrayModifiers.createConstantAttributeDataSet( mesh, listValues, "newAttribute", vtkDataType=vtkDataType ) + arrayModifiers.createConstantAttribute( mesh, listValues, "newAttribute", vtkDataType=vtkDataType ) -def test_createConstantAttributeDataSetRaiseValueError( dataSetTest: vtkDataSet, ) -> None: - """Test the raises ValueError for the function createConstantAttributeDataSet with a wrong vtkDataType.""" +def test_createConstantAttributeRaiseValueError( dataSetTest: vtkDataSet, ) -> None: + """Test the raises ValueError for the function createConstantAttribute with wrong values.""" mesh: vtkDataSet = dataSetTest( "dataset" ) + + # Wrong vtk type + with pytest.raises( ValueError ): + arrayModifiers.createConstantAttribute( mesh, [ np.int32( 42 ) ], "newAttribute", vtkDataType=64 ) + + # Wrong piece with pytest.raises( ValueError ): - arrayModifiers.createConstantAttributeDataSet( mesh, [ np.int32( 42 ) ], "newAttribute", vtkDataType=64 ) + arrayModifiers.createConstantAttribute( mesh, [ np.int32( 42 ) ], "newAttribute", piece=Piece.BOTH ) + +@pytest.mark.parametrize("meshName, attributeName", + [ + ( "multiblock", "PORO" ), # Partial + ( "multiblock", "GLOBAL_IDS_CELLS" ), # Global + ( "dataset", "PORO" ), +] ) +def test_createConstantAttributeRaiseAttributeError( + dataSetTest: Any, + meshName: str, + attributeName: str, +) -> None: + """Test the raises ValueError for the function createConstantAttribute with a wrong AttributeName.""" + mesh: vtkDataSet | vtkMultiBlockDataSet = dataSetTest( meshName ) + with pytest.raises( AttributeError ): + arrayModifiers.createConstantAttribute( mesh, [ np.int32( 42 ) ], attributeName ) @pytest.mark.parametrize( diff --git a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py index 572f1f09..eb338236 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -16,8 +16,7 @@ from geos.mesh.utils.arrayHelpers import ( getArrayInObject, getComponentNames, getNumberOfComponents, getVtkDataTypeInObject, isAttributeGlobal, getAttributePieceInfo, checkValidValuesInDataSet, checkValidValuesInMultiBlock ) -from geos.mesh.utils.arrayModifiers import ( createAttribute, createConstantAttributeDataSet, - createConstantAttributeMultiBlock ) +from geos.mesh.utils.arrayModifiers import ( createAttribute, createConstantAttribute ) from geos.mesh.utils.multiblockHelpers import getBlockElementIndexesFlatten __doc__ = """ @@ -203,7 +202,7 @@ def applyFilter( self: Self ) -> None: self.logger.warning( f"The region indexes entered are not in the region attribute { self.regionName }." ) - createConstantAttributeMultiBlock( self.mesh, + createConstantAttribute( self.mesh, self.defaultValue, self.newAttributeName, componentNames=self.componentNames, @@ -239,7 +238,7 @@ def applyFilter( self: Self ) -> None: self.logger.warning( f"The region indexes entered are not in the region attribute { self.regionName }." ) - createConstantAttributeDataSet( self.mesh, + createConstantAttribute( self.mesh, self.defaultValue, self.newAttributeName, componentNames=self.componentNames, diff --git a/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py b/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py index 13823096..86313b77 100644 --- a/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py +++ b/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py @@ -10,7 +10,7 @@ from vtkmodules.vtkCommonDataModel import ( vtkCellArray, vtkHexahedron, vtkRectilinearGrid, vtkUnstructuredGrid, VTK_HEXAHEDRON ) from geos.mesh.io.vtkIO import VtkOutput, writeMesh -from geos.mesh.utils.arrayModifiers import createConstantAttributeDataSet +from geos.mesh.utils.arrayModifiers import createConstantAttribute from geos.mesh_doctor.actions.generateGlobalIds import buildGlobalIds from geos.mesh_doctor.parsing.cliParsing import setupLogger from geos.utils.pieceEnum import Piece @@ -184,8 +184,8 @@ def addFields( mesh: vtkUnstructuredGrid, fields: Iterable[ FieldInfo ] ) -> vtk piece: Piece = Piece.POINTS if fieldInfo.support == "POINTS" else Piece.CELLS # Create list of values (all 1.0) for each component listValues = [ 1.0 ] * fieldInfo.dimension - # Use the robust createConstantAttributeDataSet function - createConstantAttributeDataSet( dataSet=mesh, + # Use the robust createConstantAttribute function + createConstantAttribute( dataSet=mesh, listValues=listValues, attributeName=fieldInfo.name, piece=piece, From fcff21b9b707c1bbab3140d04c23a3df64fc3590 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 11 Feb 2026 11:10:02 +0100 Subject: [PATCH 2/5] refactor copyAttribute* function --- .../src/geos/mesh/utils/arrayModifiers.py | 154 ++++++------- geos-mesh/tests/test_arrayModifiers.py | 207 +++++++++--------- 2 files changed, 173 insertions(+), 188 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 8711d083..28b42604 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -20,13 +20,14 @@ vtkDataObject, vtkPointData, vtkCellData, + vtkCell, ) from vtkmodules.vtkFiltersCore import ( vtkArrayRename, vtkCellCenters, vtkPointDataToCellData, ) -from vtkmodules.vtkCommonCore import ( vtkDataArray, vtkPoints, vtkLogger ) +from vtkmodules.vtkCommonCore import ( vtkDataArray, vtkPoints, vtkLogger, ) from geos.mesh.utils.arrayHelpers import ( getComponentNames, getComponentNamesDataSet, @@ -447,20 +448,25 @@ def createAttribute( def copyAttribute( - multiBlockDataSetFrom: vtkMultiBlockDataSet, - multiBlockDataSetTo: vtkMultiBlockDataSet, + meshFrom: vtkMultiBlockDataSet | vtkDataSet, + meshTo: vtkMultiBlockDataSet | vtkDataSet, attributeNameFrom: str, attributeNameTo: str, piece: Piece = Piece.CELLS, logger: Union[ Logger, None ] = None, ) -> None: - """Copy an attribute from a multiBlockDataSet to a similar one on the same piece. + """Copy an attribute from a mesh to a similar one on the same piece. + + The similarity of two meshes means that the two mesh have the same number of elements (cells and points) located in the same coordinates and with the same indexation. Testing this similarity is time consuming therefore, only few metric are compared: + - the block indexation for multiblock dataset + - the number of the element where the attribute is located, for multiblock dataset it is done for each block + - the coordinates of the first element, for multiblock dataset it is done for each block Args: - multiBlockDataSetFrom (vtkMultiBlockDataSet): MultiBlockDataSet from which to copy the attribute. - multiBlockDataSetTo (vtkMultiBlockDataSet): MultiBlockDataSet where to copy the attribute. - attributeNameFrom (str): Attribute name in multiBlockDataSetFrom. - attributeNameTo (str): Attribute name in multiBlockDataSetTo. It will be a new attribute of multiBlockDataSetTo. + meshFrom (vtkMultiBlockDataSet | vtkDataSet): mesh from which to copy the attribute. + meshTo (vtkMultiBlockDataSet | vtkDataSet): mesh where to copy the attribute. + attributeNameFrom (str): Attribute name in meshFrom. + attributeNameTo (str): Attribute name to set in meshTo. piece (Piece): The piece of the attribute. Defaults to Piece.CELLS logger (Union[Logger, None], optional): A logger to manage the output messages. @@ -468,87 +474,69 @@ def copyAttribute( Raises: TypeError: Error with the type of the source or final mesh. - ValueError: Error with the data of the source or final mesh. + ValueError: Error with the data of the source or final mesh or the piece. AttributeError: Error with the attribute attributeNameFrom or attributeNameTo. """ # Check if an external logger is given. if logger is None: logger = getLogger( "copyAttribute", True ) - # Check if the multiBlockDataSetFrom is inherited from vtkMultiBlockDataSet. - if not isinstance( multiBlockDataSetFrom, vtkMultiBlockDataSet ): - raise TypeError( "Source mesh has to be inherited from vtkMultiBlockDataSet." ) - - # Check if the multiBlockDataSetTo is inherited from vtkMultiBlockDataSet. - if not isinstance( multiBlockDataSetTo, vtkMultiBlockDataSet ): - raise TypeError( "Final mesh has to be inherited from vtkMultiBlockDataSet." ) - - # Check if the attribute exist in the multiBlockDataSetFrom. - if not isAttributeInObjectMultiBlockDataSet( multiBlockDataSetFrom, attributeNameFrom, piece ): - raise AttributeError( f"The attribute { attributeNameFrom } is not present in the source mesh." ) - - # Check if the attribute already exist in the multiBlockDataSetTo. - if isAttributeInObjectMultiBlockDataSet( multiBlockDataSetTo, attributeNameTo, piece ): - raise AttributeError( f"The attribute { attributeNameTo } is already present in the final mesh." ) - - # Check if the two multiBlockDataSets are similar. - elementaryBlockIndexesTo: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetTo ) - elementaryBlockIndexesFrom: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetFrom ) - if elementaryBlockIndexesTo != elementaryBlockIndexesFrom: - raise ValueError( "The two meshes do not have the same block indexes." ) - - # Parse blocks of the two mesh to copy the attribute. - for idBlock in elementaryBlockIndexesTo: - dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetFrom.GetDataSet( idBlock ) ) - dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTo.GetDataSet( idBlock ) ) - - if isAttributeInObjectDataSet( dataSetFrom, attributeNameFrom, piece ): - copyAttributeDataSet( dataSetFrom, dataSetTo, attributeNameFrom, attributeNameTo, piece, logger ) - - return - - -def copyAttributeDataSet( - dataSetFrom: vtkDataSet, - dataSetTo: vtkDataSet, - attributeNameFrom: str, - attributeNameTo: str, - piece: Piece = Piece.CELLS, - logger: Union[ Logger, Any ] = None, -) -> None: - """Copy an attribute from a dataSet to a similar one on the same piece. - - Args: - dataSetFrom (vtkDataSet): DataSet from which to copy the attribute. - dataSetTo (vtkDataSet): DataSet where to copy the attribute. - attributeNameFrom (str): Attribute name in dataSetFrom. - attributeNameTo (str): Attribute name in dataSetTo. It will be a new attribute of dataSetTo. - piece (Piece): The piece of the attribute. - Defaults to Piece.CELLS - logger (Union[Logger, None], optional): A logger to manage the output messages. - Defaults to None, an internal logger is used. - - Raises: - TypeError: Error with the type of the source mesh. - AttributeError: Error with the attribute attributeNameFrom. - """ - # Check if an external logger is given. - if logger is None: - logger = getLogger( "copyAttributeDataSet", True ) - - # Check if the dataSetFrom is inherited from vtkDataSet. - if not isinstance( dataSetFrom, vtkDataSet ): - raise TypeError( "Source mesh has to be inherited from vtkDataSet." ) - - # Check if the attribute exist in the dataSetFrom. - if not isAttributeInObjectDataSet( dataSetFrom, attributeNameFrom, piece ): - raise AttributeError( f"The attribute { attributeNameFrom } is not in the source mesh." ) - - npArray: npt.NDArray[ Any ] = getArrayInObject( dataSetFrom, attributeNameFrom, piece ) - componentNames: tuple[ str, ...] = getComponentNamesDataSet( dataSetFrom, attributeNameFrom, piece ) - vtkArrayType: int = getVtkArrayTypeInObject( dataSetFrom, attributeNameFrom, piece ) - - createAttribute( dataSetTo, npArray, attributeNameTo, componentNames, piece, vtkArrayType, logger ) + if isinstance( meshTo, vtkDataSet ) and isinstance( meshFrom, vtkDataSet ): + # Small check to check if the two meshes are similar. + coordElementTo: set[ tuple[ int, ...] ] = set() + coordElementFrom: set[ tuple[ int, ...] ] = set() + if piece == Piece.POINTS: + coordElementTo.add( meshTo.GetPoint( 0 ) ) + coordElementFrom.add( meshFrom.GetPoint( 0 ) ) + elif piece == Piece.CELLS: + cellTo: vtkCell = meshTo.GetCell( 0 ) + cellFrom: vtkCell = meshFrom.GetCell( 0 ) + # Get the coordinates of each points of the cell. + nbPointsTo: int = cellTo.GetNumberOfPoints() + nbPointsFrom: int = cellTo.GetNumberOfPoints() + if nbPointsTo != nbPointsFrom: + raise ValueError( "The two meshes have not the same cells dimension.") + + cellPointsTo: vtkPoints = cellTo.GetPoints() + cellPointsFrom: vtkPoints = cellFrom.GetPoints() + for idPoint in range( nbPointsTo ): + coordElementTo.add( cellPointsTo.GetPoint( idPoint ) ) + coordElementFrom.add( cellPointsFrom.GetPoint( idPoint ) ) + else: + raise ValueError( "The piece of the attribute to copy must be cells or points.") + print(coordElementTo, coordElementFrom) + if coordElementTo != coordElementFrom: + raise ValueError( "The two meshes have not the same element indexation.") + + npArray: npt.NDArray[ Any ] = getArrayInObject( meshFrom, attributeNameFrom, piece ) + componentNames: tuple[ str, ...] = getComponentNamesDataSet( meshFrom, attributeNameFrom, piece ) + vtkArrayType: int = getVtkArrayTypeInObject( meshFrom, attributeNameFrom, piece ) + + createAttribute( meshTo, npArray, attributeNameTo, componentNames, piece, vtkArrayType, logger ) + elif isinstance( meshTo, vtkMultiBlockDataSet ) and isinstance( meshFrom, vtkMultiBlockDataSet ): + # Check if the attribute exist in the meshFrom. + if not isAttributeInObject( meshFrom, attributeNameFrom, piece ): + raise AttributeError( f"The attribute { attributeNameFrom } is not present in the source mesh." ) + + # Check if the attribute already exist in the meshTo. + if isAttributeInObject( meshTo, attributeNameTo, piece ): + raise AttributeError( f"The attribute { attributeNameTo } is already present in the final mesh." ) + + # Check if the two multiBlockDataSets are similar. + elementaryBlockIndexesTo: list[ int ] = getBlockElementIndexesFlatten( meshTo ) + elementaryBlockIndexesFrom: list[ int ] = getBlockElementIndexesFlatten( meshFrom ) + if elementaryBlockIndexesTo != elementaryBlockIndexesFrom: + raise ValueError( "The two meshes do not have the same block indexes." ) + + # Parse blocks of the two mesh to copy the attribute. + for idBlock in elementaryBlockIndexesTo: + dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( meshFrom.GetDataSet( idBlock ) ) + dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( meshTo.GetDataSet( idBlock ) ) + + if isAttributeInObject( dataSetFrom, attributeNameFrom, piece ): + copyAttribute( dataSetFrom, dataSetTo, attributeNameFrom, attributeNameTo, piece, logger ) + else: + raise TypeError( "Input meshes must be both inherited from vtkMultiBlockDataSet or vtkDataSet." ) return diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index 7015ce93..fac7b41a 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -15,6 +15,7 @@ from vtkmodules.vtkCommonDataModel import ( vtkDataSet, vtkMultiBlockDataSet, vtkPointData, vtkCellData ) from geos.mesh.utils.multiblockHelpers import getBlockElementIndexesFlatten +from geos.mesh.utils.arrayHelpers import isAttributeInObject from vtk import ( # type: ignore[import-untyped] VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_INT, VTK_UNSIGNED_LONG_LONG, VTK_CHAR, VTK_SIGNED_CHAR, @@ -353,6 +354,7 @@ def test_createConstantAttributeRaiseValueError( dataSetTest: vtkDataSet, ) -> N with pytest.raises( ValueError ): arrayModifiers.createConstantAttribute( mesh, [ np.int32( 42 ) ], "newAttribute", piece=Piece.BOTH ) + @pytest.mark.parametrize("meshName, attributeName", [ ( "multiblock", "PORO" ), # Partial @@ -507,34 +509,50 @@ def test_createAttributeRaiseAttributeError( @pytest.mark.parametrize( - "attributeNameFrom, attributeNameTo, piece", + "meshFromName, meshToName, attributeNameFrom, attributeNameTo, piece", [ - # Test with global attributes. - ( "GLOBAL_IDS_POINTS", "GLOBAL_IDS_POINTS_To", Piece.POINTS ), - ( "GLOBAL_IDS_CELLS", 'GLOBAL_IDS_CELLS_To', Piece.CELLS ), - # Test with partial attributes. - ( "CellAttribute", "CellAttributeTo", Piece.CELLS ), - ( "PointAttribute", "PointAttributeTo", Piece.POINTS ), + # Test multiblock. + ## Test with global attributes. + ( "multiblock", "emptymultiblock", "GLOBAL_IDS_POINTS", "newAttribute", Piece.POINTS ), + ( "multiblock", "emptymultiblock","GLOBAL_IDS_CELLS", 'newAttribute', Piece.CELLS ), + ## Test with partial attributes. + ( "multiblock", "emptymultiblock","CellAttribute", "newAttribute", Piece.CELLS ), + ( "multiblock", "emptymultiblock","PointAttribute", "newAttribute", Piece.POINTS ), + # Test dataset. + ( "dataset", "emptydataset","CellAttribute", "newAttribute", Piece.CELLS ), + ( "dataset", "emptydataset","PointAttribute", "newAttributes", Piece.POINTS ), + # Test attribute names. The copy attribute name is a name of an attribute on the other piece. + ( "multiblock", "multiblock", "GLOBAL_IDS_POINTS", "GLOBAL_IDS_CELLS", Piece.POINTS ), + ( "multiblock", "multiblock","CellAttribute", "PointAttribute", Piece.CELLS ), + ( "dataset", "dataset","CellAttribute", "PointAttribute", Piece.CELLS ), ] ) def test_copyAttribute( - dataSetTest: vtkMultiBlockDataSet, + dataSetTest: Any, + meshFromName: str, + meshToName: str, attributeNameFrom: str, attributeNameTo: str, piece: Piece, ) -> None: """Test copy of cell attribute from one multiblock to another.""" - multiBlockDataSetFrom: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - multiBlockDataSetTo: vtkMultiBlockDataSet = dataSetTest( "emptymultiblock" ) + meshFrom: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshFromName ) + meshTo: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshToName ) - # Copy the attribute from the multiBlockDataSetFrom to the multiBlockDataSetTo. - arrayModifiers.copyAttribute( multiBlockDataSetFrom, multiBlockDataSetTo, attributeNameFrom, attributeNameTo, - piece ) + # Copy the attribute from the meshFrom to the meshTo. + arrayModifiers.copyAttribute( meshFrom, meshTo, attributeNameFrom, attributeNameTo, piece ) - # Parse the two multiBlockDataSet and test if the attribute has been copied. - elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetFrom ) - for blockIndex in elementaryBlockIndexes: - dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetFrom.GetDataSet( blockIndex ) ) - dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTo.GetDataSet( blockIndex ) ) + listDataSets: list[ list[ vtkDataSet ] ] = [] + if isinstance( meshFrom, vtkDataSet ): + listDataSets.append( [ meshFrom, meshTo ] ) + else: + elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( meshFrom ) + for blockIndex in elementaryBlockIndexes: + dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( meshFrom.GetDataSet( blockIndex ) ) + if isAttributeInObject( dataSetFrom, attributeNameFrom, piece ): + listDataSets.append( [ dataSetFrom, vtkDataSet.SafeDownCast( meshTo.GetDataSet( blockIndex ) ) ] ) + + for dataSetFrom, dataSetTo in listDataSets: + # Get the tested attribute and its copy. dataFrom: Union[ vtkPointData, vtkCellData ] dataTo: Union[ vtkPointData, vtkCellData ] if piece == Piece.POINTS: @@ -543,14 +561,34 @@ def test_copyAttribute( else: dataFrom = dataSetFrom.GetCellData() dataTo = dataSetTo.GetCellData() + attributeTest: vtkDataArray = dataFrom.GetArray( attributeNameFrom ) + attributeCopied: vtkDataArray = dataTo.GetArray( attributeNameTo ) + + # Test the number of components and their names if multiple. + nbComponentsTest: int = attributeTest.GetNumberOfComponents() + nbComponentsCopied: int = attributeCopied.GetNumberOfComponents() + assert nbComponentsCopied == nbComponentsTest + if nbComponentsTest > 1: + componentsNamesTest: tuple[ str, ...] = tuple( + attributeTest.GetComponentName( i ) for i in range( nbComponentsTest ) ) + componentsNamesCopied: tuple[ str, ...] = tuple( + attributeCopied.GetComponentName( i ) for i in range( nbComponentsCopied ) ) + assert componentsNamesCopied == componentsNamesTest - attributeExistTest: int = dataFrom.HasArray( attributeNameFrom ) - attributeExistCopied: int = dataTo.HasArray( attributeNameTo ) - assert attributeExistCopied == attributeExistTest + # Test values and their types. + npArrayTest: npt.NDArray[ Any ] = vnp.vtk_to_numpy( attributeTest ) + npArrayCopied: npt.NDArray[ Any ] = vnp.vtk_to_numpy( attributeCopied ) + assert npArrayCopied.dtype == npArrayTest.dtype + assert ( npArrayCopied == npArrayTest ).all() + + vtkDataTypeTest: int = attributeTest.GetDataType() + vtkDataTypeCopied: int = attributeCopied.GetDataType() + assert vtkDataTypeCopied == vtkDataTypeTest @pytest.mark.parametrize( "meshNameFrom, meshNameTo", [ - ( "dataset", "emptydataset" ), + ( "dataset", "other" ), + ( "other", "emptydataset" ), ( "dataset", "emptymultiblock" ), ( "multiblock", "emptydataset" ), ] ) @@ -560,105 +598,64 @@ def test_copyAttributeTypeError( meshNameTo: str, ) -> None: """Test the raises TypeError for the function copyAttribute.""" - meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshNameFrom ) - meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshNameTo ) + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCellData ] + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCellData ] + if meshNameFrom == "other": + meshFrom = vtkCellData() + else: + meshFrom= dataSetTest( meshNameFrom ) + + if meshNameTo == "other": + meshTo = vtkCellData() + else: + meshTo = dataSetTest( meshNameTo ) + with pytest.raises( TypeError ): arrayModifiers.copyAttribute( meshFrom, meshTo, "PORO", "PORO" ) -def test_copyAttributeValueError( dataSetTest: vtkMultiBlockDataSet, ) -> None: - """Test the raises ValueError for the function copyAttribute with two meshes with different block architecture.""" - meshFrom: vtkMultiBlockDataSet = dataSetTest( "meshGeosExtractBlockTmp" ) - meshTo: vtkMultiBlockDataSet = dataSetTest( "emptymultiblock" ) +# TODO: Create two meshes similar but with two different element indexation +@pytest.mark.parametrize( "meshNameFrom, meshNameTo, piece", [ + ( "dataset", "emptydataset", Piece.BOTH ), # The piece is wrong + ( "dataset", "well", Piece.CELLS ), # Two meshes with different cells dimension + ( "multiblock", "multiblockGeosOutput", Piece.CELLS ), # Two meshes with different blocks indexation +] ) +def test_copyAttributeValueError( + dataSetTest: Any, + meshNameFrom: str, + meshNameTo: str, + piece: Piece, +) -> None: + """Test the raises ValueError for the function copyAttribute.""" + meshFrom: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshNameFrom ) + meshTo: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshNameTo ) with pytest.raises( ValueError ): - arrayModifiers.copyAttribute( meshFrom, meshTo, "PORO", "PORO" ) + arrayModifiers.copyAttribute( meshFrom, meshTo, "GLOBAL_IDS_CELLS", "newAttribute", piece=piece ) -@pytest.mark.parametrize( - "attributeNameFrom, attributeNameTo", - [ - ( "PORO", "PORO" ), # An attribute PORO is already present in the mesh to - ( "newAttribute", "newAttribute" ), # newAttribute is not in the mesh from - ] ) +@pytest.mark.parametrize( "meshNameFrom, meshNameTo, attributeNameFrom, attributeNameTo", [ + # The copy attribute name is already an attribute on the mesh to + ( "dataset", "dataset", "PORO", "PORO" ), + ( "multiblock", "multiblock", "PORO", "PORO" ), + ( "multiblock", "multiblock", "PORO", "GLOBAL_IDS_CELLS" ), + # The attribute to copy is not in the mesh From + # ( "dataset", "emptydataset", "newAttribute", "newAttribute" ), TODO: activate when the PR 223 is merged + ( "multiblock", "emptymultiblock", "newAttribute", "newAttribute" ), +] ) def test_copyAttributeAttributeError( - dataSetTest: vtkMultiBlockDataSet, + dataSetTest: Any, + meshNameFrom: str, + meshNameTo: str, attributeNameFrom: str, attributeNameTo: str, ) -> None: """Test the raises AttributeError for the function copyAttribute.""" - meshFrom: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - meshTo: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) + meshFrom: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshNameFrom ) + meshTo: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshNameTo ) with pytest.raises( AttributeError ): arrayModifiers.copyAttribute( meshFrom, meshTo, attributeNameFrom, attributeNameTo ) -@pytest.mark.parametrize( "attributeNameFrom, attributeNameTo, piece", [ - ( "CellAttribute", "CellAttributeTo", Piece.CELLS ), - ( "PointAttribute", "PointAttributeTo", Piece.POINTS ), -] ) -def test_copyAttributeDataSet( - dataSetTest: vtkDataSet, - attributeNameFrom: str, - attributeNameTo: str, - piece: Piece, -) -> None: - """Test copy of an attribute from one dataset to another.""" - dataSetFrom: vtkDataSet = dataSetTest( "dataset" ) - dataSetTo: vtkDataSet = dataSetTest( "emptydataset" ) - - # Copy the attribute from the dataSetFrom to the dataSetTo. - arrayModifiers.copyAttributeDataSet( dataSetFrom, dataSetTo, attributeNameFrom, attributeNameTo, piece ) - - # Get the tested attribute and its copy. - dataFrom: Union[ vtkPointData, vtkCellData ] - dataTo: Union[ vtkPointData, vtkCellData ] - if piece == Piece.POINTS: - dataFrom = dataSetFrom.GetPointData() - dataTo = dataSetTo.GetPointData() - else: - dataFrom = dataSetFrom.GetCellData() - dataTo = dataSetTo.GetCellData() - attributeTest: vtkDataArray = dataFrom.GetArray( attributeNameFrom ) - attributeCopied: vtkDataArray = dataTo.GetArray( attributeNameTo ) - - # Test the number of components and their names if multiple. - nbComponentsTest: int = attributeTest.GetNumberOfComponents() - nbComponentsCopied: int = attributeCopied.GetNumberOfComponents() - assert nbComponentsCopied == nbComponentsTest - if nbComponentsTest > 1: - componentsNamesTest: tuple[ str, ...] = tuple( - attributeTest.GetComponentName( i ) for i in range( nbComponentsTest ) ) - componentsNamesCopied: tuple[ str, ...] = tuple( - attributeCopied.GetComponentName( i ) for i in range( nbComponentsCopied ) ) - assert componentsNamesCopied == componentsNamesTest - - # Test values and their types. - npArrayTest: npt.NDArray[ Any ] = vnp.vtk_to_numpy( attributeTest ) - npArrayCopied: npt.NDArray[ Any ] = vnp.vtk_to_numpy( attributeCopied ) - assert npArrayCopied.dtype == npArrayTest.dtype - assert ( npArrayCopied == npArrayTest ).all() - - vtkDataTypeTest: int = attributeTest.GetDataType() - vtkDataTypeCopied: int = attributeCopied.GetDataType() - assert vtkDataTypeCopied == vtkDataTypeTest - - -def test_copyAttributeDataSetTypeError( dataSetTest: Any, ) -> None: - """Test the raises TypeError for the function copyAttributeDataSet with a mesh from with a wrong type.""" - meshFrom: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - meshTo: vtkDataSet = dataSetTest( "emptydataset" ) - with pytest.raises( TypeError ): - arrayModifiers.copyAttributeDataSet( meshFrom, meshTo, "PORO", "PORO" ) - - -def test_copyAttributeDataSetAttributeError( dataSetTest: vtkDataSet, ) -> None: - """Test the raises AttributeError for the function copyAttributeDataSet with an attributeNameFrom not in the mesh From.""" - meshFrom: vtkDataSet = dataSetTest( "dataset" ) - meshTo: vtkDataSet = dataSetTest( "emptydataset" ) - with pytest.raises( AttributeError ): - arrayModifiers.copyAttributeDataSet( meshFrom, meshTo, "newAttribute", "newAttribute" ) - - @pytest.mark.parametrize( "isMeshFrom, meshToName", [ From 40fe8408fa69ef85134948775c1dfc0f0f49b894 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 11 Feb 2026 11:19:10 +0100 Subject: [PATCH 3/5] refactor createCellCenterAttribute --- .../src/geos/mesh/utils/arrayModifiers.py | 108 ++++++------------ 1 file changed, 38 insertions(+), 70 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 28b42604..73289b78 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -816,87 +816,55 @@ def createCellCenterAttribute( if logger is None: logger = getLogger( "createCellCenterAttribute", True ) - if isinstance( mesh, vtkMultiBlockDataSet ): - if isAttributeInObjectMultiBlockDataSet( mesh, cellCenterAttributeName, Piece.CELLS ): - raise AttributeError( f"The attribute { cellCenterAttributeName } in already in the mesh." ) + if isAttributeInObject( mesh, cellCenterAttributeName, Piece.CELLS ): + raise AttributeError( f"The attribute { cellCenterAttributeName } in already in the mesh." ) + if isinstance( mesh, vtkMultiBlockDataSet ): elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( mesh ) for blockIndex in elementaryBlockIndexes: dataSet: vtkDataSet = vtkDataSet.SafeDownCast( mesh.GetDataSet( blockIndex ) ) - createCellCenterAttributeDataSet( dataSet, cellCenterAttributeName, logger ) + createCellCenterAttribute( dataSet, cellCenterAttributeName, logger ) elif isinstance( mesh, vtkDataSet ): - createCellCenterAttributeDataSet( mesh, cellCenterAttributeName, logger ) + vtkErrorLogger: Logger = logging.getLogger( f"{ logger.name } vtkError Logger" ) + vtkErrorLogger.setLevel( logging.INFO ) + vtkErrorLogger.addHandler( logger.handlers[ 0 ] ) + vtkErrorLogger.propagate = False + vtkLogger.SetStderrVerbosity( vtkLogger.VERBOSITY_ERROR ) + vtkErrorLogger.addFilter( RegexExceptionFilter() ) # will raise VTKError if captured VTK Error + with VTKCaptureLog() as capturedLog: + # apply ElementCenter filter + cellCenterFilter: vtkCellCenters = vtkCellCenters() + cellCenterFilter.SetInputData( mesh ) + cellCenterFilter.Update() + + capturedLog.seek( 0 ) + captured = capturedLog.read().decode() + + if captured != "": + vtkErrorLogger.error( captured.strip() ) + + output: vtkPointSet = cellCenterFilter.GetOutputDataObject( 0 ) + if output is None: + raise VTKError( "Something went wrong with VTK cell center filter." ) + + # transfer output to output arrays + centers: vtkPoints = output.GetPoints() + if centers is None: + raise VTKError( "Something went wrong with VTK cell center filter." ) + + centerCoords: vtkDataArray = centers.GetData() + if centerCoords is None: + raise VTKError( "Something went wrong with VTK cell center filter." ) + + centerCoords.SetName( cellCenterAttributeName ) + mesh.GetCellData().AddArray( centerCoords ) + mesh.Modified() else: raise TypeError( "Input mesh must be a vtkDataSet or vtkMultiBlockDataSet." ) return -def createCellCenterAttributeDataSet( - block: vtkDataSet, - cellCenterAttributeName: str, - logger: Union[ Logger, Any ] = None, -) -> None: - """Create cellElementCenter attribute in a vtkDataSet if it does not exist. - - Args: - block (vtkDataSet): Input mesh that must be a vtkDataSet. - cellCenterAttributeName (str): Name of the attribute. - logger (Union[Logger, None], optional): A logger to manage the output messages. - Defaults to None, an internal logger is used. - - Raises: - TypeError: Error with the type of the mesh. - AttributeError: Error with the attribute cellCenterAttributeName. - VTKError: Error with a VTK function. - """ - if logger is None: - logger = getLogger( "createCellCenterAttributeDataSet", True ) - - if not isinstance( block, vtkDataSet ): - raise TypeError( "Input mesh has to be inherited from vtkDataSet." ) - - if isAttributeInObject( block, cellCenterAttributeName, Piece.CELLS ): - raise AttributeError( f"The attribute { cellCenterAttributeName } in already in the mesh." ) - - vtkErrorLogger: Logger = logging.getLogger( f"{ logger.name } vtkError Logger" ) - vtkErrorLogger.setLevel( logging.INFO ) - vtkErrorLogger.addHandler( logger.handlers[ 0 ] ) - vtkErrorLogger.propagate = False - vtkLogger.SetStderrVerbosity( vtkLogger.VERBOSITY_ERROR ) - vtkErrorLogger.addFilter( RegexExceptionFilter() ) # will raise VTKError if captured VTK Error - with VTKCaptureLog() as capturedLog: - # apply ElementCenter filter - cellCenterFilter: vtkCellCenters = vtkCellCenters() - cellCenterFilter.SetInputData( block ) - cellCenterFilter.Update() - - capturedLog.seek( 0 ) - captured = capturedLog.read().decode() - - if captured != "": - vtkErrorLogger.error( captured.strip() ) - - output: vtkPointSet = cellCenterFilter.GetOutputDataObject( 0 ) - if output is None: - raise VTKError( "Something went wrong with VTK cell center filter." ) - - # transfer output to output arrays - centers: vtkPoints = output.GetPoints() - if centers is None: - raise VTKError( "Something went wrong with VTK cell center filter." ) - - centerCoords: vtkDataArray = centers.GetData() - if centerCoords is None: - raise VTKError( "Something went wrong with VTK cell center filter." ) - - centerCoords.SetName( cellCenterAttributeName ) - block.GetCellData().AddArray( centerCoords ) - block.Modified() - - return - - def transferPointDataToCellData( mesh: vtkPointSet, logger: Union[ Logger, Any ] = None, From fe5293c19b99155c5787fff48d017bb1ef4c936e Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 11 Feb 2026 14:23:53 +0100 Subject: [PATCH 4/5] refactor transferAttributeWithElementMap function --- .../src/geos/mesh/utils/arrayModifiers.py | 193 +++++++----------- geos-mesh/tests/test_arrayModifiers.py | 138 ++++++------- .../AttributeMapping.py | 2 +- 3 files changed, 135 insertions(+), 198 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 73289b78..4436c6b1 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -541,9 +541,9 @@ def copyAttribute( return -def transferAttributeToDataSetWithElementMap( +def transferAttributeWithElementMap( meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], - dataSetTo: vtkDataSet, + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], elementMap: dict[ int, npt.NDArray[ np.int64 ] ], attributeName: str, piece: Piece, @@ -553,22 +553,23 @@ def transferAttributeToDataSetWithElementMap( """Transfer attributes from the source mesh to the final mesh using a map of points/cells. If the source mesh is a vtkDataSet, its flat index (flatIdDataSetFrom) is set to 0. + If the final mesh is a vtkDataSet, its flat index (flatIdDataSetTo) is set to 0. The map of points/cells used to transfer the attribute is a dictionary where: - - The key is the flat index of the final mesh. - - The item is an array of size (nb elements in the final mesh, 2). + - Keys are the flat index of all the datasets of the final mesh. + - Items are arrays of size (nb elements in datasets of the final mesh, 2). - If an element (idElementTo) of the final mesh is mapped with no element of the source mesh: + If an element (idElementTo) of one dataset (flatIdDataSetTo) of the final mesh is mapped with no element of the source mesh: - elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. - The value of the attribute for this element depends of the type of the value of the attribute (0 for unit, -1 for int, nan for float). - If an element (idElementTo) of the final mesh is mapped with an element (idElementFrom) of one of the dataset (flatIdDataSetFrom) of the source mesh: + If an element (idElementTo) of one dataset (flatIdDataSetTo) of the final mesh is mapped with an element (idElementFrom) of one of the dataset (flatIdDataSetFrom) of the source mesh: - elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. - The value of the attribute for this element is the value of the element (idElementFrom) of the dataset (flatIdDataSetFrom) of the source mesh. Args: meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The source mesh with the attribute to transfer. - dataSetTo (vtkDataSet): The final mesh where to transfer the attribute. + meshTo (Union[vtkDataSet, vtkMultiBlockDataSet]): The final mesh where to transfer the attribute. elementMap (dict[int, npt.NDArray[np.int64]]): The map of points/cells. attributeName (str): The name of the attribute to transfer. piece (Piece): The piece of the attribute. @@ -580,143 +581,95 @@ def transferAttributeToDataSetWithElementMap( Raises: TypeError: Error with the type of the mesh to or the mesh from. ValueError: Error with the values of the map elementMap. - AttributeError: Error with the attribute AttributeName. + AttributeError: Error with the attribute attributeName. """ # Check if an external logger is given. if logger is None: - logger = getLogger( "transferAttributeToDataSetWithElementMap", True ) + logger = getLogger( "transferAttributeWithElementMap", True ) # Check the piece. if piece not in [ Piece.POINTS, Piece.CELLS ]: raise ValueError( f"The attribute must be on { Piece.POINTS.value } or { Piece.CELLS.value }." ) - if not isinstance( dataSetTo, vtkDataSet ): - raise TypeError( "The final mesh has to be inherited from vtkDataSet." ) - - if flatIdDataSetTo not in elementMap: - raise ValueError( - f"The map is incomplete, there is no data for the final mesh (flat index { flatIdDataSetTo })." ) - - nbElementsTo: int = dataSetTo.GetNumberOfPoints() if piece == Piece.POINTS else dataSetTo.GetNumberOfCells() - if len( elementMap[ flatIdDataSetTo ] ) != nbElementsTo: - raise ValueError( - f"The map is wrong, there is { nbElementsTo } elements in the final mesh (flat index { flatIdDataSetTo }) but { len( elementMap[ flatIdDataSetTo ] ) } elements in the map." - ) - - if not isinstance( meshFrom, ( vtkDataSet, vtkMultiBlockDataSet ) ): - raise TypeError( "The source mesh has to be inherited from vtkDataSet or vtkMultiBlockDataSet." ) - + # Check the attribute to transfer. + ## it is in the meshFrom if not isAttributeInObject( meshFrom, attributeName, piece ): raise AttributeError( f"The attribute { attributeName } is not in the source mesh." ) - + ## it is global if isinstance( meshFrom, vtkMultiBlockDataSet ) and not isAttributeGlobal( meshFrom, attributeName, piece ): raise AttributeError( f"The attribute { attributeName } must be global in the source mesh." ) - componentNames: tuple[ str, ...] = getComponentNames( meshFrom, attributeName, piece ) - nbComponents: int = len( componentNames ) - - vtkDataType: int = getVtkDataTypeInObject( meshFrom, attributeName, piece ) - defaultValue: Any - if vtkDataType in ( VTK_FLOAT, VTK_DOUBLE ): - defaultValue = np.nan - elif vtkDataType in ( VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE ): - defaultValue = -1 - elif vtkDataType in ( VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT, - VTK_UNSIGNED_LONG_LONG ): - defaultValue = 0 - else: - raise AttributeError( f"The attribute { attributeName } has an unknown type." ) - - typeMapping: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap() - valueType: type = typeMapping[ vtkDataType ] - - arrayTo: npt.NDArray[ Any ] - if nbComponents > 1: - defaultValue = [ defaultValue ] * nbComponents - arrayTo = np.full( ( nbElementsTo, nbComponents ), defaultValue, dtype=valueType ) - else: - arrayTo = np.array( [ defaultValue for _ in range( nbElementsTo ) ], dtype=valueType ) - - for idElementTo in range( nbElementsTo ): - valueToTransfer: Any = defaultValue - idElementFrom: int = int( elementMap[ flatIdDataSetTo ][ idElementTo ][ 1 ] ) - if idElementFrom != -1: - dataFrom: Union[ vtkPointData, vtkCellData ] - if isinstance( meshFrom, vtkDataSet ): - dataFrom = meshFrom.GetPointData() if piece == Piece.POINTS else meshFrom.GetCellData() - elif isinstance( meshFrom, vtkMultiBlockDataSet ): - flatIdDataSetFrom: int = int( elementMap[ flatIdDataSetTo ][ idElementTo ][ 0 ] ) - dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( meshFrom.GetDataSet( flatIdDataSetFrom ) ) - dataFrom = dataSetFrom.GetPointData() if piece == Piece.POINTS else dataSetFrom.GetCellData() - - arrayFrom: npt.NDArray[ Any ] = vnp.vtk_to_numpy( dataFrom.GetArray( attributeName ) ) - valueToTransfer = arrayFrom[ idElementFrom ] - - arrayTo[ idElementTo ] = valueToTransfer - - createAttribute( dataSetTo, - arrayTo, - attributeName, - componentNames, - piece=piece, - vtkDataType=vtkDataType, - logger=logger ) - - return - - -def transferAttributeWithElementMap( - meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], - meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], - elementMap: dict[ int, npt.NDArray[ np.int64 ] ], - attributeName: str, - piece: Piece, - logger: Union[ Logger, Any ] = None, -) -> None: - """Transfer attributes from the source mesh to the final mesh using a map of points/cells. - - If the source mesh is a vtkDataSet, its flat index (flatIdDataSetFrom) is set to 0. - If the final mesh is a vtkDataSet, its flat index (flatIdDataSetTo) is set to 0. - - The map of points/cells used to transfer the attribute is a dictionary where: - - Keys are the flat index of all the datasets of the final mesh. - - Items are arrays of size (nb elements in datasets of the final mesh, 2). + # Transfer the attribute + if isinstance( meshTo, vtkDataSet ): + if flatIdDataSetTo not in elementMap: + raise ValueError( + f"The map is incomplete, there is no data for the final mesh (flat index { flatIdDataSetTo })." ) - If an element (idElementTo) of one dataset (flatIdDataSetTo) of the final mesh is mapped with no element of the source mesh: - - elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. - - The value of the attribute for this element depends of the type of the value of the attribute (0 for unit, -1 for int, nan for float). + nbElementsTo: int = meshTo.GetNumberOfPoints() if piece == Piece.POINTS else meshTo.GetNumberOfCells() + if len( elementMap[ flatIdDataSetTo ] ) != nbElementsTo: + raise ValueError( + f"The map is wrong, there is { nbElementsTo } elements in the final mesh (flat index { flatIdDataSetTo }) but { len( elementMap[ flatIdDataSetTo ] ) } elements in the map." + ) - If an element (idElementTo) of one dataset (flatIdDataSetTo) of the final mesh is mapped with an element (idElementFrom) of one of the dataset (flatIdDataSetFrom) of the source mesh: - - elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. - - The value of the attribute for this element is the value of the element (idElementFrom) of the dataset (flatIdDataSetFrom) of the source mesh. + componentNames: tuple[ str, ...] = getComponentNames( meshFrom, attributeName, piece ) + nbComponents: int = len( componentNames ) - Args: - meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The source mesh with the attribute to transfer. - meshTo (Union[vtkDataSet, vtkMultiBlockDataSet]): The final mesh where to transfer the attribute. - elementMap (dict[int, npt.NDArray[np.int64]]): The map of points/cells. - attributeName (str): The name of the attribute to transfer. - piece (Piece): The piece of the attribute. - logger (Union[Logger, None], optional): A logger to manage the output messages. - Defaults to None, an internal logger is used. + vtkDataType: int = getVtkDataTypeInObject( meshFrom, attributeName, piece ) + defaultValue: Any + if vtkDataType in ( VTK_FLOAT, VTK_DOUBLE ): + defaultValue = np.nan + elif vtkDataType in ( VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE ): + defaultValue = -1 + elif vtkDataType in ( VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT, + VTK_UNSIGNED_LONG_LONG ): + defaultValue = 0 + else: + raise AttributeError( f"The attribute { attributeName } has an unknown type." ) - Raises: - TypeError: Error with the type of the final mesh. - AttributeError: Error with the attribute attributeName. - """ - # Check if an external logger is given. - if logger is None: - logger = getLogger( "transferAttributeWithElementMap", True ) + typeMapping: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap() + valueType: type = typeMapping[ vtkDataType ] - if isinstance( meshTo, vtkDataSet ): - transferAttributeToDataSetWithElementMap( meshFrom, meshTo, elementMap, attributeName, piece, logger=logger ) + arrayTo: npt.NDArray[ Any ] + if nbComponents > 1: + defaultValue = [ defaultValue ] * nbComponents + arrayTo = np.full( ( nbElementsTo, nbComponents ), defaultValue, dtype=valueType ) + else: + arrayTo = np.array( [ defaultValue for _ in range( nbElementsTo ) ], dtype=valueType ) + + for idElementTo in range( nbElementsTo ): + valueToTransfer: Any = defaultValue + idElementFrom: int = int( elementMap[ flatIdDataSetTo ][ idElementTo ][ 1 ] ) + if idElementFrom != -1: + dataFrom: Union[ vtkPointData, vtkCellData ] + if isinstance( meshFrom, vtkDataSet ): + dataFrom = meshFrom.GetPointData() if piece == Piece.POINTS else meshFrom.GetCellData() + elif isinstance( meshFrom, vtkMultiBlockDataSet ): + flatIdDataSetFrom: int = int( elementMap[ flatIdDataSetTo ][ idElementTo ][ 0 ] ) + dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( meshFrom.GetDataSet( flatIdDataSetFrom ) ) + dataFrom = dataSetFrom.GetPointData() if piece == Piece.POINTS else dataSetFrom.GetCellData() + else: + raise TypeError( "The source mesh has to be inherited from vtkDataSet or vtkMultiBlockDataSet." ) + + arrayFrom: npt.NDArray[ Any ] = vnp.vtk_to_numpy( dataFrom.GetArray( attributeName ) ) + valueToTransfer = arrayFrom[ idElementFrom ] + + arrayTo[ idElementTo ] = valueToTransfer + + createAttribute( meshTo, + arrayTo, + attributeName, + componentNames, + piece=piece, + vtkDataType=vtkDataType, + logger=logger ) elif isinstance( meshTo, vtkMultiBlockDataSet ): - if isAttributeInObjectMultiBlockDataSet( meshTo, attributeName, piece ): + if isAttributeInObject( meshTo, attributeName, piece ): raise AttributeError( f"The attribute { attributeName } is already in the final mesh." ) listFlatIdDataSetTo: list[ int ] = getBlockElementIndexesFlatten( meshTo ) for flatIdDataSetTo in listFlatIdDataSetTo: dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( meshTo.GetDataSet( flatIdDataSetTo ) ) - transferAttributeToDataSetWithElementMap( meshFrom, + transferAttributeWithElementMap( meshFrom, dataSetTo, elementMap, attributeName, diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index fac7b41a..7d9a2f61 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -656,72 +656,6 @@ def test_copyAttributeAttributeError( arrayModifiers.copyAttribute( meshFrom, meshTo, attributeNameFrom, attributeNameTo ) -@pytest.mark.parametrize( - "isMeshFrom, meshToName", - [ - ( True, "emptymultiblock" ), # The mesh to is not a vtkDataSet. - ( False, "emptyFracture" ), # The mesh from is not a mesh. - ] ) -def test_transferAttributeToDataSetWithElementMapTypeError( - dataSetTest: Any, - getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], - isMeshFrom: bool, - meshToName: str, -) -> None: - """Test the raises TypeError for the function transferAttributeToDataSetWithElementMap.""" - meshFrom: Union[ bool, vtkMultiBlockDataSet ] = dataSetTest( "multiblock" ) if isMeshFrom else False - meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshToName ) - elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( "multiblock", meshToName, Piece.CELLS ) - with pytest.raises( TypeError ): - arrayModifiers.transferAttributeToDataSetWithElementMap( meshFrom, meshTo, elementMap, "FAULT", Piece.CELLS ) - - -@pytest.mark.parametrize( - "attributeName", - [ - ( "PORO" ), # The attribute is partial. - ( "newAttribute" ), # The attribute is not in the mesh from. - ] ) -def test_transferAttributeToDataSetWithElementMapAttributeError( - dataSetTest: vtkMultiBlockDataSet, - getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], - attributeName: str, -) -> None: - """Test the raises AttributeError for the function transferAttributeToDataSetWithElementMap.""" - meshFrom: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - meshTo: vtkMultiBlockDataSet = dataSetTest( "emptyFracture" ) - elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( "multiblock", "emptyFracture", Piece.CELLS ) - with pytest.raises( AttributeError ): - arrayModifiers.transferAttributeToDataSetWithElementMap( meshFrom, meshTo, elementMap, attributeName, - Piece.CELLS ) - - -@pytest.mark.parametrize( - "meshToNameTransfer, meshToNameMap, flatIdDataSetTo", - [ - ( "emptyFracture", "emptymultiblock", 0 ), # The map is wrong. - ( "emptyFracture", "emptyFracture", 1 ), # The flatIdDataSetTo is wrong. - ] ) -def test_transferAttributeToDataSetWithElementMapValueError( - dataSetTest: vtkDataSet, - getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], - meshToNameTransfer: str, - meshToNameMap: str, - flatIdDataSetTo: int, -) -> None: - """Test the raises ValueError for the function transferAttributeToDataSetWithElementMap.""" - meshFrom: vtkDataSet = dataSetTest( "dataset" ) - meshTo: vtkDataSet = dataSetTest( meshToNameTransfer ) - elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( "dataset", meshToNameMap, False ) - with pytest.raises( ValueError ): - arrayModifiers.transferAttributeToDataSetWithElementMap( meshFrom, - meshTo, - elementMap, - "FAULT", - False, - flatIdDataSetTo=flatIdDataSetTo ) - - @pytest.mark.parametrize( "meshFromName, meshToName, attributeName, piece, defaultValueTest", [ ( "fracture", "emptyFracture", "collocated_nodes", Piece.POINTS, [ -1, -1 ] ), ( "multiblock", "emptyFracture", "FAULT", Piece.CELLS, -1 ), @@ -775,28 +709,78 @@ def test_transferAttributeWithElementMap( assert np.all( arrayTo[ idElementTo ] == arrayFrom[ idElementFrom ] ) +@pytest.mark.parametrize( "meshNameFrom, meshNameTo", [ + ( "dataset", "other" ), + ( "other", "emptydataset" ), + ( "other", "other" ), +] ) def test_transferAttributeWithElementMapTypeError( - dataSetTest: vtkMultiBlockDataSet, - getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], + dataSetTest: Any, + meshNameFrom: str, + meshNameTo: str, ) -> None: - """Test the raises TypeError for the function transferAttributeWithElementMap with the mesh to with a wrong type.""" - meshFrom: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - meshTo: bool = False - elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( "multiblock", "emptymultiblock", Piece.CELLS ) + """Test the raises TypeError for the function transferAttributeWithElementMap.""" + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCellData ] + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCellData ] + if meshNameFrom == "other": + meshFrom = vtkCellData() + else: + meshFrom= dataSetTest( meshNameFrom ) + + if meshNameTo == "other": + meshTo = vtkCellData() + else: + meshTo = dataSetTest( meshNameTo ) + with pytest.raises( TypeError ): - arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, "FAULT", Piece.CELLS ) + arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, {}, "GLOBAL_IDS_CELLS", Piece.CELLS ) +@pytest.mark.parametrize( "meshNameFrom, meshNameTo, attributeName", [ + ( "multiblock", "emptymultiblock", "PORO" ), # The attribute is partial in the mesh From + ( "dataset", "emptydataset", "newAttribute" ), # The attribute is not in the mesh From + ( "dataset", "emptydataset", "GLOBAL_IDS_CELLS" ), # The attribute is already in the mesh to + ( "multiblock", "emptymultiblock", "GLOBAL_IDS_CELLS" ), # The attribute is already in the mesh to +] ) def test_transferAttributeWithElementMapAttributeError( dataSetTest: vtkMultiBlockDataSet, getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], + meshNameFrom: str, + meshNameTo: str, + attributeName: str, ) -> None: """Test the raises AttributeError for the function transferAttributeWithElementMap with an attribute already in the mesh to.""" - meshFrom: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - meshTo: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( "multiblock", "emptymultiblock", Piece.CELLS ) + meshFrom: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshNameFrom ) + meshTo: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshNameTo ) + elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( meshNameFrom, meshNameTo, Piece.CELLS ) with pytest.raises( AttributeError ): - arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, "FAULT", Piece.CELLS ) + arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, attributeName, Piece.CELLS ) + + +@pytest.mark.parametrize( "meshNameTo, meshNameToMap, flatIdDataSetTo, piece", [ + ( "emptyFracture", "emptyFracture", 0, Piece.BOTH ), # The piece is wrong. + ( "emptyFracture", "emptyFracture", 1, Piece.CELLS ), # The flatIdDataSetTo is wrong. + ( "emptyFracture", "emptymultiblock", 0, Piece.CELLS ), # The map is wrong. +] ) +def test_transferAttributeWithElementMapValueError( + dataSetTest: vtkDataSet, + getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], + meshNameTo: str, + meshNameToMap: str, + flatIdDataSetTo: int, + piece: Piece, +) -> None: + """Test the raises ValueError for the function transferAttributeWithElementMap.""" + meshFrom: vtkDataSet = dataSetTest( "dataset" ) + meshTo: vtkDataSet = dataSetTest( meshNameTo ) + elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( "dataset", meshNameToMap, False ) + with pytest.raises( ValueError ): + arrayModifiers.transferAttributeWithElementMap( meshFrom, + meshTo, + elementMap, + "FAULT", + piece, + flatIdDataSetTo=flatIdDataSetTo ) @pytest.mark.parametrize( "attributeName, piece", [ diff --git a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py index 1c8dbf40..ba2e743f 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -182,7 +182,7 @@ def applyFilter( self: Self ) -> None: for attributeName in self.attributeNames: transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, self.piece, - self.logger ) + logger=self.logger ) # Log the output message. self._logOutputMessage() From a38934233dac631b56fa87cdcecffc4640ee4f27 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 11 Feb 2026 14:50:30 +0100 Subject: [PATCH 5/5] Clean for ci --- .../src/geos/mesh/utils/arrayModifiers.py | 37 ++--- geos-mesh/tests/test_arrayModifiers.py | 131 +++++++++--------- .../AttributeMapping.py | 6 +- .../CreateConstantAttributePerRegion.py | 20 +-- .../geos/mesh_doctor/actions/generateCube.py | 8 +- 5 files changed, 102 insertions(+), 100 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 4436c6b1..b7ad3543 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -27,7 +27,11 @@ vtkCellCenters, vtkPointDataToCellData, ) -from vtkmodules.vtkCommonCore import ( vtkDataArray, vtkPoints, vtkLogger, ) +from vtkmodules.vtkCommonCore import ( + vtkDataArray, + vtkPoints, + vtkLogger, +) from geos.mesh.utils.arrayHelpers import ( getComponentNames, getComponentNamesDataSet, @@ -156,8 +160,7 @@ def fillPartialAttributes( for blockIndex in elementaryBlockIndexes: dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) if not isAttributeInObjectDataSet( dataSet, attributeName, piece ): - createConstantAttribute( dataSet, listValues, attributeName, componentNames, piece, vtkDataType, - logger ) + createConstantAttribute( dataSet, listValues, attributeName, componentNames, piece, vtkDataType, logger ) return @@ -307,7 +310,8 @@ def createConstantAttribute( logger.warning( 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" ) + logger.warning( + "To avoid any issue with the conversion, please use directly numpy scalar type for the values" ) valueType = npType # Check the consistency between the given value type and the vtk array type if it exists. @@ -483,8 +487,8 @@ def copyAttribute( if isinstance( meshTo, vtkDataSet ) and isinstance( meshFrom, vtkDataSet ): # Small check to check if the two meshes are similar. - coordElementTo: set[ tuple[ int, ...] ] = set() - coordElementFrom: set[ tuple[ int, ...] ] = set() + coordElementTo: set[ tuple[ float, ...] ] = set() + coordElementFrom: set[ tuple[ float, ...] ] = set() if piece == Piece.POINTS: coordElementTo.add( meshTo.GetPoint( 0 ) ) coordElementFrom.add( meshFrom.GetPoint( 0 ) ) @@ -495,7 +499,7 @@ def copyAttribute( nbPointsTo: int = cellTo.GetNumberOfPoints() nbPointsFrom: int = cellTo.GetNumberOfPoints() if nbPointsTo != nbPointsFrom: - raise ValueError( "The two meshes have not the same cells dimension.") + raise ValueError( "The two meshes have not the same cells dimension." ) cellPointsTo: vtkPoints = cellTo.GetPoints() cellPointsFrom: vtkPoints = cellFrom.GetPoints() @@ -503,10 +507,9 @@ def copyAttribute( coordElementTo.add( cellPointsTo.GetPoint( idPoint ) ) coordElementFrom.add( cellPointsFrom.GetPoint( idPoint ) ) else: - raise ValueError( "The piece of the attribute to copy must be cells or points.") - print(coordElementTo, coordElementFrom) + raise ValueError( "The piece of the attribute to copy must be cells or points." ) if coordElementTo != coordElementFrom: - raise ValueError( "The two meshes have not the same element indexation.") + raise ValueError( "The two meshes have not the same element indexation." ) npArray: npt.NDArray[ Any ] = getArrayInObject( meshFrom, attributeNameFrom, piece ) componentNames: tuple[ str, ...] = getComponentNamesDataSet( meshFrom, attributeNameFrom, piece ) @@ -621,7 +624,7 @@ def transferAttributeWithElementMap( elif vtkDataType in ( VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE ): defaultValue = -1 elif vtkDataType in ( VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT, - VTK_UNSIGNED_LONG_LONG ): + VTK_UNSIGNED_LONG_LONG ): defaultValue = 0 else: raise AttributeError( f"The attribute { attributeName } has an unknown type." ) @@ -670,12 +673,12 @@ def transferAttributeWithElementMap( for flatIdDataSetTo in listFlatIdDataSetTo: dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( meshTo.GetDataSet( flatIdDataSetTo ) ) transferAttributeWithElementMap( meshFrom, - dataSetTo, - elementMap, - attributeName, - piece, - flatIdDataSetTo=flatIdDataSetTo, - logger=logger ) + dataSetTo, + elementMap, + attributeName, + piece, + flatIdDataSetTo=flatIdDataSetTo, + logger=logger ) else: raise TypeError( "The final mesh has to be inherited from vtkDataSet or vtkMultiBlockDataSet." ) diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index 7d9a2f61..458d1bb1 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -221,7 +221,8 @@ def test_createEmptyAttributeValueError() -> None: # Test with an attribute name that exist on the opposite piece. ( "dataset", [ np.float32( 42 ) ], (), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "PORO" ), ( "multiblock", [ np.float32( 42 ) ], (), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "PORO" ), # Partial - ( "multiblock", [ np.float32( 42 ) ], (), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "GLOBAL_IDS_CELLS" ), # Global + ( "multiblock", [ np.float32( 42 ) ], (), + (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "GLOBAL_IDS_CELLS" ), # Global # Test the number of components and their names. ( "dataset", [ np.float32( 42 ) ], ( "X" ), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), ( "dataset", [ np.float32( 42 ), np.float32( 42 ) ], ( "X", "Y" ), @@ -243,11 +244,13 @@ def test_createEmptyAttributeValueError() -> None: ( "dataset", [ np.uint8( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_CHAR, "newAttribute" ), ( "dataset", [ np.uint8( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_CHAR, "newAttribute" ), ( "dataset", [ np.uint16( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_SHORT, "newAttribute" ), - ( "dataset", [ np.uint16( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_SHORT, "newAttribute" ), + ( "dataset", [ np.uint16( 42 ) ], (), + (), Piece.POINTS, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_SHORT, "newAttribute" ), ( "dataset", [ np.uint32( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_INT, "newAttribute" ), ( "dataset", [ np.uint32( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_INT, VTK_UNSIGNED_INT, "newAttribute" ), ( "dataset", [ np.uint64( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_LONG_LONG, "newAttribute" ), - ( "dataset", [ np.uint64( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_LONG_LONG, VTK_UNSIGNED_LONG_LONG, "newAttribute" ), + ( "dataset", [ np.uint64( 42 ) ], (), + (), Piece.POINTS, VTK_UNSIGNED_LONG_LONG, VTK_UNSIGNED_LONG_LONG, "newAttribute" ), ( "dataset", [ np.float32( 42 ) ], (), (), Piece.POINTS, None, VTK_FLOAT, "newAttribute" ), ( "dataset", [ np.float64( 42 ) ], (), (), Piece.POINTS, None, VTK_DOUBLE, "newAttribute" ), ( "dataset", [ np.float64( 42 ) ], (), (), Piece.POINTS, VTK_DOUBLE, VTK_DOUBLE, "newAttribute" ), @@ -322,7 +325,8 @@ def test_createConstantAttribute( @pytest.mark.parametrize( "meshName, listValues, vtkDataType", [ - ( "dataset", [ np.int32( 42 ), np.int64( 42 ) ], VTK_DOUBLE ), # All the values in the listValues are not the same + ( "dataset", [ np.int32( 42 ), np.int64( 42 ) + ], VTK_DOUBLE ), # All the values in the listValues are not the same ( "dataset", [ np.int32( 42 ) ], VTK_DOUBLE ), # The type of the value is not coherent with the vtkDataType ( "other", [ np.int64( 42 ) ], VTK_DOUBLE ), # The type of the mesh is wrong ] ) @@ -333,11 +337,7 @@ def test_createConstantAttributeRaiseTypeError( vtkDataType: int, ) -> None: """Test the raises TypeError for the function createConstantAttribute.""" - mesh: vtkCellData | vtkDataSet - if meshName == "other": - mesh = vtkCellData() - else: - mesh = dataSetTest( meshName ) + mesh: vtkCellData | vtkDataSet = vtkCellData() if meshName == "other" else dataSetTest( meshName ) with pytest.raises( TypeError ): arrayModifiers.createConstantAttribute( mesh, listValues, "newAttribute", vtkDataType=vtkDataType ) @@ -355,12 +355,13 @@ def test_createConstantAttributeRaiseValueError( dataSetTest: vtkDataSet, ) -> N arrayModifiers.createConstantAttribute( mesh, [ np.int32( 42 ) ], "newAttribute", piece=Piece.BOTH ) -@pytest.mark.parametrize("meshName, attributeName", +@pytest.mark.parametrize( + "meshName, attributeName", [ ( "multiblock", "PORO" ), # Partial ( "multiblock", "GLOBAL_IDS_CELLS" ), # Global ( "dataset", "PORO" ), -] ) + ] ) def test_createConstantAttributeRaiseAttributeError( dataSetTest: Any, meshName: str, @@ -514,17 +515,17 @@ def test_createAttributeRaiseAttributeError( # Test multiblock. ## Test with global attributes. ( "multiblock", "emptymultiblock", "GLOBAL_IDS_POINTS", "newAttribute", Piece.POINTS ), - ( "multiblock", "emptymultiblock","GLOBAL_IDS_CELLS", 'newAttribute', Piece.CELLS ), + ( "multiblock", "emptymultiblock", "GLOBAL_IDS_CELLS", 'newAttribute', Piece.CELLS ), ## Test with partial attributes. - ( "multiblock", "emptymultiblock","CellAttribute", "newAttribute", Piece.CELLS ), - ( "multiblock", "emptymultiblock","PointAttribute", "newAttribute", Piece.POINTS ), + ( "multiblock", "emptymultiblock", "CellAttribute", "newAttribute", Piece.CELLS ), + ( "multiblock", "emptymultiblock", "PointAttribute", "newAttribute", Piece.POINTS ), # Test dataset. - ( "dataset", "emptydataset","CellAttribute", "newAttribute", Piece.CELLS ), - ( "dataset", "emptydataset","PointAttribute", "newAttributes", Piece.POINTS ), + ( "dataset", "emptydataset", "CellAttribute", "newAttribute", Piece.CELLS ), + ( "dataset", "emptydataset", "PointAttribute", "newAttributes", Piece.POINTS ), # Test attribute names. The copy attribute name is a name of an attribute on the other piece. ( "multiblock", "multiblock", "GLOBAL_IDS_POINTS", "GLOBAL_IDS_CELLS", Piece.POINTS ), - ( "multiblock", "multiblock","CellAttribute", "PointAttribute", Piece.CELLS ), - ( "dataset", "dataset","CellAttribute", "PointAttribute", Piece.CELLS ), + ( "multiblock", "multiblock", "CellAttribute", "PointAttribute", Piece.CELLS ), + ( "dataset", "dataset", "CellAttribute", "PointAttribute", Piece.CELLS ), ] ) def test_copyAttribute( dataSetTest: Any, @@ -535,8 +536,8 @@ def test_copyAttribute( piece: Piece, ) -> None: """Test copy of cell attribute from one multiblock to another.""" - meshFrom: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshFromName ) - meshTo: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshToName ) + meshFrom: Any = dataSetTest( meshFromName ) + meshTo: Any = dataSetTest( meshToName ) # Copy the attribute from the meshFrom to the meshTo. arrayModifiers.copyAttribute( meshFrom, meshTo, attributeNameFrom, attributeNameTo, piece ) @@ -600,26 +601,21 @@ def test_copyAttributeTypeError( """Test the raises TypeError for the function copyAttribute.""" meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCellData ] meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCellData ] - if meshNameFrom == "other": - meshFrom = vtkCellData() - else: - meshFrom= dataSetTest( meshNameFrom ) - - if meshNameTo == "other": - meshTo = vtkCellData() - else: - meshTo = dataSetTest( meshNameTo ) + meshFrom = vtkCellData() if meshNameFrom == "other" else dataSetTest( meshNameFrom ) + meshTo = vtkCellData() if meshNameTo == "other" else dataSetTest( meshNameTo ) with pytest.raises( TypeError ): arrayModifiers.copyAttribute( meshFrom, meshTo, "PORO", "PORO" ) # TODO: Create two meshes similar but with two different element indexation -@pytest.mark.parametrize( "meshNameFrom, meshNameTo, piece", [ - ( "dataset", "emptydataset", Piece.BOTH ), # The piece is wrong - ( "dataset", "well", Piece.CELLS ), # Two meshes with different cells dimension - ( "multiblock", "multiblockGeosOutput", Piece.CELLS ), # Two meshes with different blocks indexation -] ) +@pytest.mark.parametrize( + "meshNameFrom, meshNameTo, piece", + [ + ( "dataset", "emptydataset", Piece.BOTH ), # The piece is wrong + ( "dataset", "well", Piece.CELLS ), # Two meshes with different cells dimension + ( "multiblock", "multiblockGeosOutput", Piece.CELLS ), # Two meshes with different blocks indexation + ] ) def test_copyAttributeValueError( dataSetTest: Any, meshNameFrom: str, @@ -633,15 +629,17 @@ def test_copyAttributeValueError( arrayModifiers.copyAttribute( meshFrom, meshTo, "GLOBAL_IDS_CELLS", "newAttribute", piece=piece ) -@pytest.mark.parametrize( "meshNameFrom, meshNameTo, attributeNameFrom, attributeNameTo", [ - # The copy attribute name is already an attribute on the mesh to - ( "dataset", "dataset", "PORO", "PORO" ), - ( "multiblock", "multiblock", "PORO", "PORO" ), - ( "multiblock", "multiblock", "PORO", "GLOBAL_IDS_CELLS" ), - # The attribute to copy is not in the mesh From - # ( "dataset", "emptydataset", "newAttribute", "newAttribute" ), TODO: activate when the PR 223 is merged - ( "multiblock", "emptymultiblock", "newAttribute", "newAttribute" ), -] ) +@pytest.mark.parametrize( + "meshNameFrom, meshNameTo, attributeNameFrom, attributeNameTo", + [ + # The copy attribute name is already an attribute on the mesh to + ( "dataset", "dataset", "PORO", "PORO" ), + ( "multiblock", "multiblock", "PORO", "PORO" ), + ( "multiblock", "multiblock", "PORO", "GLOBAL_IDS_CELLS" ), + # The attribute to copy is not in the mesh From + # ( "dataset", "emptydataset", "newAttribute", "newAttribute" ), TODO: activate when the PR 223 is merged + ( "multiblock", "emptymultiblock", "newAttribute", "newAttribute" ), + ] ) def test_copyAttributeAttributeError( dataSetTest: Any, meshNameFrom: str, @@ -722,26 +720,21 @@ def test_transferAttributeWithElementMapTypeError( """Test the raises TypeError for the function transferAttributeWithElementMap.""" meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCellData ] meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCellData ] - if meshNameFrom == "other": - meshFrom = vtkCellData() - else: - meshFrom= dataSetTest( meshNameFrom ) - - if meshNameTo == "other": - meshTo = vtkCellData() - else: - meshTo = dataSetTest( meshNameTo ) + meshFrom = vtkCellData() if meshNameFrom == "other" else dataSetTest( meshNameFrom ) + meshTo = vtkCellData() if meshNameTo == "other" else dataSetTest( meshNameTo ) with pytest.raises( TypeError ): arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, {}, "GLOBAL_IDS_CELLS", Piece.CELLS ) -@pytest.mark.parametrize( "meshNameFrom, meshNameTo, attributeName", [ - ( "multiblock", "emptymultiblock", "PORO" ), # The attribute is partial in the mesh From - ( "dataset", "emptydataset", "newAttribute" ), # The attribute is not in the mesh From - ( "dataset", "emptydataset", "GLOBAL_IDS_CELLS" ), # The attribute is already in the mesh to - ( "multiblock", "emptymultiblock", "GLOBAL_IDS_CELLS" ), # The attribute is already in the mesh to -] ) +@pytest.mark.parametrize( + "meshNameFrom, meshNameTo, attributeName", + [ + ( "multiblock", "emptymultiblock", "PORO" ), # The attribute is partial in the mesh From + ( "dataset", "emptydataset", "newAttribute" ), # The attribute is not in the mesh From + ( "dataset", "emptydataset", "GLOBAL_IDS_CELLS" ), # The attribute is already in the mesh to + ( "multiblock", "emptymultiblock", "GLOBAL_IDS_CELLS" ), # The attribute is already in the mesh to + ] ) def test_transferAttributeWithElementMapAttributeError( dataSetTest: vtkMultiBlockDataSet, getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], @@ -757,11 +750,13 @@ def test_transferAttributeWithElementMapAttributeError( arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, attributeName, Piece.CELLS ) -@pytest.mark.parametrize( "meshNameTo, meshNameToMap, flatIdDataSetTo, piece", [ - ( "emptyFracture", "emptyFracture", 0, Piece.BOTH ), # The piece is wrong. - ( "emptyFracture", "emptyFracture", 1, Piece.CELLS ), # The flatIdDataSetTo is wrong. - ( "emptyFracture", "emptymultiblock", 0, Piece.CELLS ), # The map is wrong. -] ) +@pytest.mark.parametrize( + "meshNameTo, meshNameToMap, flatIdDataSetTo, piece", + [ + ( "emptyFracture", "emptyFracture", 0, Piece.BOTH ), # The piece is wrong. + ( "emptyFracture", "emptyFracture", 1, Piece.CELLS ), # The flatIdDataSetTo is wrong. + ( "emptyFracture", "emptymultiblock", 0, Piece.CELLS ), # The map is wrong. + ] ) def test_transferAttributeWithElementMapValueError( dataSetTest: vtkDataSet, getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], @@ -776,11 +771,11 @@ def test_transferAttributeWithElementMapValueError( elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( "dataset", meshNameToMap, False ) with pytest.raises( ValueError ): arrayModifiers.transferAttributeWithElementMap( meshFrom, - meshTo, - elementMap, - "FAULT", - piece, - flatIdDataSetTo=flatIdDataSetTo ) + meshTo, + elementMap, + "FAULT", + piece, + flatIdDataSetTo=flatIdDataSetTo ) @pytest.mark.parametrize( "attributeName, piece", [ diff --git a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py index ba2e743f..3c33ea0d 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -181,7 +181,11 @@ def applyFilter( self: Self ) -> None: raise ValueError( f"The two meshes do not have any shared { self.piece.value }." ) for attributeName in self.attributeNames: - transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, self.piece, + transferAttributeWithElementMap( self.meshFrom, + self.meshTo, + self.ElementMap, + attributeName, + self.piece, logger=self.logger ) # Log the output message. diff --git a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py index eb338236..9540cbea 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -203,11 +203,11 @@ def applyFilter( self: Self ) -> None: f"The region indexes entered are not in the region attribute { self.regionName }." ) createConstantAttribute( self.mesh, - self.defaultValue, - self.newAttributeName, - componentNames=self.componentNames, - piece=self.piece, - logger=self.logger ) + self.defaultValue, + self.newAttributeName, + componentNames=self.componentNames, + piece=self.piece, + logger=self.logger ) else: if len( invalidIndexes ) > 0: @@ -239,11 +239,11 @@ def applyFilter( self: Self ) -> None: f"The region indexes entered are not in the region attribute { self.regionName }." ) createConstantAttribute( self.mesh, - self.defaultValue, - self.newAttributeName, - componentNames=self.componentNames, - piece=self.piece, - logger=self.logger ) + self.defaultValue, + self.newAttributeName, + componentNames=self.componentNames, + piece=self.piece, + logger=self.logger ) else: if len( invalidIndexes ) > 0: diff --git a/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py b/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py index 86313b77..428933c3 100644 --- a/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py +++ b/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py @@ -186,10 +186,10 @@ def addFields( mesh: vtkUnstructuredGrid, fields: Iterable[ FieldInfo ] ) -> vtk listValues = [ 1.0 ] * fieldInfo.dimension # Use the robust createConstantAttribute function createConstantAttribute( dataSet=mesh, - listValues=listValues, - attributeName=fieldInfo.name, - piece=piece, - logger=setupLogger ) + listValues=listValues, + attributeName=fieldInfo.name, + piece=piece, + logger=setupLogger ) return mesh