From a40f1492046c4b15ce2dbffad22025fa98de09db Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Dec 2025 11:40:12 +0100 Subject: [PATCH 01/17] refactor createAttribute to raise error --- .../src/geos/mesh/utils/arrayModifiers.py | 49 +++++++------------ 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 0874c354..8861966a 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -421,7 +421,8 @@ def createConstantAttributeDataSet( else: npArray = np.array( [ listValues[ 0 ] for _ in range( nbElements ) ], valueType ) - return createAttribute( dataSet, npArray, attributeName, componentNames, onPoints, vtkDataType, logger ) + createAttribute( dataSet, npArray, attributeName, componentNames, onPoints, vtkDataType, logger ) + return True def createAttribute( @@ -432,8 +433,8 @@ def createAttribute( onPoints: bool = False, vtkDataType: Union[ int, None ] = None, logger: Union[ Logger, None ] = None, -) -> bool: - """Create an attribute from the given numpy array. +) -> None: + """Create the attribute from the given numpy array on the dataSet. Args: dataSet (vtkDataSet): DataSet where to create the attribute. @@ -453,8 +454,9 @@ def createAttribute( logger (Union[Logger, None], optional): A logger to manage the output messages. Defaults to None, an internal logger is used. - Returns: - bool: True if the attribute was correctly created, False if it was not created. + Raises: + TypeError: Input data with wrong type. + ValueError: Input data with wrong value. """ # Check if an external logger is given. if logger is None: @@ -462,31 +464,22 @@ def createAttribute( # Check if the input mesh is inherited from vtkDataSet. if not isinstance( dataSet, vtkDataSet ): - logger.error( "Input mesh has to be inherited from vtkDataSet." ) # type: ignore[unreachable] - logger.error( f"The attribute { attributeName } has not been created into the mesh." ) - return False + raise TypeError( "Input datSet has to be inherited from vtkDataSet." ) # Check if the attribute already exist in the input mesh. if isAttributeInObjectDataSet( dataSet, attributeName, onPoints ): - logger.error( f"The attribute { attributeName } is already present in the dataSet." ) - logger.error( f"The attribute { attributeName } has not been created into the mesh." ) - return False + raise ValueError( f"The attribute { attributeName } is already present in the mesh." ) # Check the coherency between the given array type and the vtk array type if it exist. if vtkDataType is not None: vtkNumpyTypeMap: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap() if vtkDataType not in vtkNumpyTypeMap: - logger.error( f"The vtk data type { vtkDataType } is unknown." ) - logger.error( f"The attribute { attributeName } has not been created into the mesh." ) - return False + raise ValueError( f"The vtk data type { vtkDataType } is unknown." ) + npArrayTypeFromVtk: npt.DTypeLike = vtkNumpyTypeMap[ vtkDataType ]().dtype npArrayTypeFromInput: npt.DTypeLike = npArray.dtype if npArrayTypeFromVtk != npArrayTypeFromInput: - logger.error( - f"The numpy array type { npArrayTypeFromInput } is not coherent with the type of array created ({ npArrayTypeFromVtk }) from the given vtkDataType." - ) - logger.error( f"The attribute { attributeName } has not been created into the mesh." ) - return False + raise TypeError( f"Input npArray type must be { npArrayTypeFromVtk }, not { npArrayTypeFromInput }." ) data: Union[ vtkPointData, vtkCellData ] nbElements: int @@ -502,9 +495,7 @@ def createAttribute( # Check if the input array has the good size. if len( npArray ) != nbElements: - logger.error( f"The array has to have { nbElements } elements, but have only { len( npArray ) } elements" ) - logger.error( f"The attribute { attributeName } has not been created into the mesh." ) - return False + raise ValueError( f"The npArray must have { nbElements } elements, not { len( npArray ) }." ) # Check if an attribute with the same name exist on the opposite piece (points or cells). oppositePiece: bool = not onPoints @@ -540,7 +531,7 @@ def createAttribute( data.AddArray( createdAttribute ) data.Modified() - return True + return def copyAttribute( @@ -680,7 +671,8 @@ def copyAttributeDataSet( componentNames: tuple[ str, ...] = getComponentNamesDataSet( dataSetFrom, attributeNameFrom, onPoints ) vtkArrayType: int = getVtkArrayTypeInObject( dataSetFrom, attributeNameFrom, onPoints ) - return createAttribute( dataSetTo, npArray, attributeNameTo, componentNames, onPoints, vtkArrayType, logger ) + createAttribute( dataSetTo, npArray, attributeNameTo, componentNames, onPoints, vtkArrayType, logger ) + return True def transferAttributeToDataSetWithElementMap( @@ -777,13 +769,8 @@ def transferAttributeToDataSetWithElementMap( arrayTo[ idElementTo ] = valueToTransfer - return createAttribute( dataSetTo, - arrayTo, - attributeName, - componentNames, - onPoints=onPoints, - vtkDataType=vtkDataType, - logger=logger ) + createAttribute( dataSetTo, arrayTo, attributeName, componentNames, onPoints=onPoints, vtkDataType=vtkDataType, logger=logger ) + return True def transferAttributeWithElementMap( From 370ade1504529f0c46a827018d6272e20ae4e250 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Dec 2025 11:41:15 +0100 Subject: [PATCH 02/17] Update tests and use of createAttribute --- geos-mesh/tests/test_arrayModifiers.py | 39 ++++++++++++++++++- .../CreateConstantAttributePerRegion.py | 16 +------- .../post_processing/SurfaceGeomechanics.py | 23 +++-------- 3 files changed, 46 insertions(+), 32 deletions(-) diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index 0b2528d8..60e9d4b1 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -363,7 +363,7 @@ def test_createAttribute( npArrayTest: npt.NDArray[ Any ] = getArrayWithSpeTypeValue( nbComponentsTest, nbElements, valueType ) # Create the new attribute in the dataSet. - assert arrayModifiers.createAttribute( dataSet, npArrayTest, attributeName, componentNames, onPoints, vtkDataType ) + arrayModifiers.createAttribute( dataSet, npArrayTest, attributeName, componentNames, onPoints, vtkDataType ) # Get the created attribute. data: Union[ vtkPointData, vtkCellData ] @@ -387,6 +387,43 @@ def test_createAttribute( assert vtkDataTypeCreated == vtkDataTypeTest +@pytest.mark.parametrize( "meshName, arrayType", [ + ( "multiblock", "float64" ), # The input mesh has the wrong type + ( "dataset", "int32" ), # The input array has the wrong type (should be float64) +] ) +def test_createAttributeRaiseTypeError( + dataSetTest: Any, + getArrayWithSpeTypeValue: npt.NDArray[ Any ], + meshName: str, + arrayType: str, +) -> None: + """Test the raises TypeError for the function createAttribute.""" + mesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshName ) + npArray: npt.NDArray[ Any ] = getArrayWithSpeTypeValue( 1, 1, arrayType ) + attributeName: str = "NewAttribute" + with pytest.raises( TypeError ): + arrayModifiers.createAttribute( mesh, npArray, attributeName, vtkDataType=VTK_DOUBLE ) + + +@pytest.mark.parametrize( "attributeName, vtkDataType, nbElements", [ + ( "CellAttribute", VTK_DOUBLE, 1740 ), # The attribute name is already on the mesh + ( "newAttribute", 64, 1740 ), # The vtkDataType does not exist + ( "newAttribute", VTK_DOUBLE, 1741 ), # The number of element of the array is wrong +] ) +def test_createAttributeRaiseValueError( + dataSetTest: Any, + getArrayWithSpeTypeValue: npt.NDArray[ Any ], + attributeName: str, + vtkDataType: int, + nbElements: int, +) -> None: + """Test the raises ValueError for the function createAttribute.""" + mesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( "dataset" ) + npArray: npt.NDArray[ Any ] = getArrayWithSpeTypeValue( 1, nbElements, "float64" ) + with pytest.raises( ValueError ): + arrayModifiers.createAttribute( mesh, npArray, attributeName, vtkDataType=vtkDataType ) + + @pytest.mark.parametrize( "attributeNameFrom, attributeNameTo, onPoints", [ 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 c5fc2c38..cedf44d3 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -222,13 +222,7 @@ def applyFilter( self: Self ) -> bool: regionArray = getArrayInObject( dataSet, self.regionName, self.onPoints ) newArray = self._createArrayFromRegionArrayWithValueMap( regionArray ) - if not createAttribute( dataSet, - newArray, - self.newAttributeName, - componentNames=self.componentNames, - onPoints=self.onPoints, - logger=self.logger ): - raise + createAttribute( dataSet, newArray, self.newAttributeName, componentNames=self.componentNames, onPoints=self.onPoints, logger=self.logger ) else: validIndexes, invalidIndexes = checkValidValuesInDataSet( self.mesh, self.regionName, listIndexes, @@ -256,13 +250,7 @@ def applyFilter( self: Self ) -> bool: regionArray = getArrayInObject( self.mesh, self.regionName, self.onPoints ) newArray = self._createArrayFromRegionArrayWithValueMap( regionArray ) - if not createAttribute( self.mesh, - newArray, - self.newAttributeName, - componentNames=self.componentNames, - onPoints=self.onPoints, - logger=self.logger ): - raise + createAttribute( self.mesh, newArray, self.newAttributeName, componentNames=self.componentNames, onPoints=self.onPoints, logger=self.logger ) # Log the output message. self._logOutputMessage( validIndexes ) diff --git a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py index ed0a5a62..e94ea2ad 100644 --- a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py +++ b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py @@ -281,16 +281,9 @@ def convertAttributesFromLocalToXYZBasis( self: Self ) -> None: arrayXYZ: npt.NDArray[ np.float64 ] = self.__computeXYZCoordinates( localArray ) # Create converted attribute array in dataset - if createAttribute( self.outputMesh, - arrayXYZ, - attrNameXYZ, - ComponentNameEnum.XYZ.value, - onPoints=self.attributeOnPoints, - logger=self.logger ): - self.logger.info( f"Attribute {attrNameXYZ} added to the output mesh." ) - self.newAttributeNames.add( attrNameXYZ ) - else: - raise + createAttribute( self.outputMesh, arrayXYZ, attrNameXYZ, ComponentNameEnum.XYZ.value, onPoints=self.attributeOnPoints, logger=self.logger ) + self.logger.info( f"Attribute {attrNameXYZ} added to the output mesh." ) + self.newAttributeNames.add( attrNameXYZ ) return @@ -375,13 +368,9 @@ def computeShearCapacityUtilization( self: Self ) -> None: raise # Create attribute - if not createAttribute( - self.outputMesh, scuAttribute, SCUAttributeName, (), self.attributeOnPoints, logger=self.logger ): - self.logger.error( f"Failed to create attribute {SCUAttributeName}." ) - raise - else: - self.logger.info( "SCU computed and added to the output mesh." ) - self.newAttributeNames.add( SCUAttributeName ) + createAttribute( self.outputMesh, scuAttribute, SCUAttributeName, (), self.attributeOnPoints, logger=self.logger ) + self.logger.info( "SCU computed and added to the output mesh." ) + self.newAttributeNames.add( SCUAttributeName ) return From b210193333ea2f0eacda3669caa8e55fc749c4e1 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Dec 2025 12:04:11 +0100 Subject: [PATCH 03/17] Refactor CreateConstantAttributeDataSet --- .../src/geos/mesh/utils/arrayModifiers.py | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 8861966a..faeaf83f 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -150,9 +150,8 @@ def fillPartialAttributes( elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSet ) for blockIndex in elementaryBlockIndexes: dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) - if not isAttributeInObjectDataSet( dataSet, attributeName, onPoints ) and \ - not createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, onPoints, vtkDataType, logger ): - return False + if not isAttributeInObjectDataSet( dataSet, attributeName, onPoints ): + createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, onPoints, vtkDataType, logger ) return True @@ -269,8 +268,9 @@ def createConstantAttribute( # Deals with dataSets. elif isinstance( mesh, vtkDataSet ): - return createConstantAttributeDataSet( mesh, listValues, attributeName, componentNames, onPoints, vtkDataType, - logger ) + createConstantAttributeDataSet( mesh, listValues, attributeName, componentNames, onPoints, vtkDataType, logger ) + + return True def createConstantAttributeMultiBlock( @@ -335,9 +335,7 @@ def createConstantAttributeMultiBlock( elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSet ) for blockIndex in elementaryBlockIndexes: dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) - if not createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, onPoints, - vtkDataType, logger ): - return False + createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, onPoints, vtkDataType, logger ) return True @@ -350,7 +348,7 @@ def createConstantAttributeDataSet( onPoints: bool = False, vtkDataType: Union[ int, None ] = None, logger: Union[ Logger, None ] = None, -) -> bool: +) -> None: """Create an attribute with a constant value per component in the dataSet. Args: @@ -371,8 +369,9 @@ def createConstantAttributeDataSet( logger (Union[Logger, None], optional): A logger to manage the output messages. Defaults to None, an internal logger is used. - Returns: - bool: True if the attribute was correctly created, False if it was not created. + Raises: + TypeError: Input data with wrong type. + ValueError: Input data with wrong value. """ # Check if an external logger is given. if logger is None: @@ -383,9 +382,7 @@ def createConstantAttributeDataSet( for value in listValues: valueTypeTest: type = type( value ) if valueType != valueTypeTest: - logger.error( "All values in the list of values don't have the same type." ) - logger.error( f"The constant attribute { attributeName } has not been created into the mesh." ) - return False + 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 ): @@ -401,16 +398,11 @@ def createConstantAttributeDataSet( if vtkDataType is not None: vtkNumpyTypeMap: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap() if vtkDataType not in vtkNumpyTypeMap: - logger.error( f"The vtk data type { vtkDataType } is unknown." ) - logger.error( f"The constant attribute { attributeName } has not been created into the mesh." ) - return False + raise ValueError( f"The vtk data type { vtkDataType } is unknown." ) + npArrayTypeFromVtk: npt.DTypeLike = vtkNumpyTypeMap[ vtkDataType ]().dtype if npArrayTypeFromVtk != valueType: - logger.error( - f"Values type { valueType } is not coherent with the type of array created ({ npArrayTypeFromVtk }) from the given vtkDataType." - ) - logger.error( f"The constant attribute { attributeName } has not been created into the mesh." ) - return False + raise TypeError( f"Input values in listValues type must be { npArrayTypeFromVtk }, not { valueType }." ) # Create the numpy array constant per component. nbComponents: int = len( listValues ) @@ -422,8 +414,8 @@ def createConstantAttributeDataSet( npArray = np.array( [ listValues[ 0 ] for _ in range( nbElements ) ], valueType ) createAttribute( dataSet, npArray, attributeName, componentNames, onPoints, vtkDataType, logger ) - return True + return def createAttribute( dataSet: vtkDataSet, From 240e95c62da58d3d73ed3c78d70b4038e5514f98 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Dec 2025 13:47:01 +0100 Subject: [PATCH 04/17] Update test and use of createConstantAttributeDataSet --- geos-mesh/tests/test_arrayModifiers.py | 27 +++++++++++++++++-- .../CreateConstantAttributePerRegion.py | 8 +----- .../geos/mesh_doctor/actions/generateCube.py | 8 +----- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index 60e9d4b1..380905ff 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -262,8 +262,7 @@ def test_createConstantAttributeDataSet( dataSet: vtkDataSet = dataSetTest( "dataset" ) # Create the new constant attribute in the dataSet. - assert arrayModifiers.createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, onPoints, - vtkDataType ) + arrayModifiers.createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, onPoints, vtkDataType ) # Get the created attribute. data: Union[ vtkPointData, vtkCellData ] @@ -301,6 +300,30 @@ def test_createConstantAttributeDataSet( assert vtkDataTypeCreated == vtkDataTypeTest +@pytest.mark.parametrize( "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 +] ) +def test_createConstantAttributeDataSetRaiseTypeError( + dataSetTest: vtkDataSet, + listValues: list[ Any ], + vtkDataType: int, +) -> None: + """Test the raises TypeError for the function createConstantAttributeDataSet.""" + mesh: vtkDataSet = dataSetTest( "dataset" ) + with pytest.raises( TypeError ): + arrayModifiers.createConstantAttributeDataSet( mesh, listValues, "newAttribute", vtkDataType=vtkDataType ) + + +def test_createConstantAttributeDataSetRaiseValueError( + dataSetTest: vtkDataSet, +) -> None: + """Test the raises ValueError for the function createConstantAttributeDataSet with a wrong vtkDataType.""" + mesh: vtkDataSet = dataSetTest( "dataset" ) + with pytest.raises( ValueError ): + arrayModifiers.createConstantAttributeDataSet( mesh, [ np.int32( 42 ) ], "newAttribute", vtkDataType=64 ) + + @pytest.mark.parametrize( "componentNames, componentNamesTest, onPoints, vtkDataType, vtkDataTypeTest, valueType, attributeName", [ 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 cedf44d3..5ac0c08f 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -234,13 +234,7 @@ def applyFilter( self: Self ) -> bool: self.logger.warning( f"The region indexes entered are not in the region attribute { self.regionName }." ) - if not createConstantAttributeDataSet( self.mesh, - self.defaultValue, - self.newAttributeName, - componentNames=self.componentNames, - onPoints=self.onPoints, - logger=self.logger ): - raise + createConstantAttributeDataSet( self.mesh, self.defaultValue, self.newAttributeName, componentNames=self.componentNames, onPoints=self.onPoints, 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 9af53ddb..a37b3e9f 100644 --- a/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py +++ b/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py @@ -184,13 +184,7 @@ def addFields( mesh: vtkUnstructuredGrid, fields: Iterable[ FieldInfo ] ) -> vtk # Create list of values (all 1.0) for each component listValues = [ 1.0 ] * fieldInfo.dimension # Use the robust createConstantAttributeDataSet function - success = createConstantAttributeDataSet( dataSet=mesh, - listValues=listValues, - attributeName=fieldInfo.name, - onPoints=onPoints, - logger=setupLogger ) - if not success: - setupLogger.warning( f"Failed to create field {fieldInfo.name}" ) + createConstantAttributeDataSet( dataSet=mesh, listValues=listValues, attributeName=fieldInfo.name, onPoints=onPoints, logger=setupLogger ) return mesh From ba836373141b3169c95c35a6d67006145d20e69e Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Dec 2025 14:04:18 +0100 Subject: [PATCH 05/17] Refactor createConstantAttributeMultiBlock function --- .../src/geos/mesh/utils/arrayModifiers.py | 30 +++++-------------- geos-mesh/tests/test_arrayModifiers.py | 23 +++++++++++--- .../CreateConstantAttributePerRegion.py | 8 +---- 3 files changed, 28 insertions(+), 33 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index faeaf83f..b828344c 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -263,8 +263,7 @@ def createConstantAttribute( # Deals with multiBlocksDataSets. if isinstance( mesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): - return createConstantAttributeMultiBlock( mesh, listValues, attributeName, componentNames, onPoints, - vtkDataType, logger ) + createConstantAttributeMultiBlock( mesh, listValues, attributeName, componentNames, onPoints, vtkDataType, logger ) # Deals with dataSets. elif isinstance( mesh, vtkDataSet ): @@ -281,7 +280,7 @@ def createConstantAttributeMultiBlock( onPoints: bool = False, vtkDataType: Union[ int, None ] = None, logger: Union[ Logger, None ] = None, -) -> bool: +) -> None: """Create a new attribute with a constant value per component on every block of the multiBlockDataSet. Args: @@ -302,8 +301,9 @@ def createConstantAttributeMultiBlock( logger (Union[Logger, None], optional): A logger to manage the output messages. Defaults to None, an internal logger is used. - Returns: - bool: True if the attribute was correctly created, False if it was not created. + Raises: + TypeError: Input mesh with wrong type. + ValueError: Input attribute name already in the mesh. """ # Check if an external logger is given. if logger is None: @@ -311,25 +311,11 @@ def createConstantAttributeMultiBlock( # Check if the input mesh is inherited from vtkMultiBlockDataSet. if not isinstance( multiBlockDataSet, vtkMultiBlockDataSet ): - logger.error( "Input mesh has to be inherited from vtkMultiBlockDataSet." ) - logger.error( f"The constant attribute { attributeName } has not been created into the mesh." ) - return False + raise TypeError( "Input mesh has to be inherited from vtkMultiBlockDataSet." ) # Check if the attribute already exist in the input mesh. if isAttributeInObjectMultiBlockDataSet( multiBlockDataSet, attributeName, onPoints ): - logger.error( f"The attribute { attributeName } is already present in the multiBlockDataSet." ) - logger.error( f"The constant attribute { attributeName } has not been created into the mesh." ) - return False - - # Check if an attribute with the same name exist on the opposite piece (points or cells) on the input mesh. - oppositePiece: bool = not onPoints - oppositePieceName: str = "points" if oppositePiece else "cells" - if isAttributeInObjectMultiBlockDataSet( multiBlockDataSet, attributeName, oppositePiece ): - oppositePieceState: str = "global" if isAttributeGlobal( multiBlockDataSet, attributeName, - oppositePiece ) else "partial" - logger.warning( - f"A { oppositePieceState } attribute with the same name ({ attributeName }) is already present in the multiBlockDataSet but on { oppositePieceName }." - ) + raise ValueError( 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 ) @@ -337,7 +323,7 @@ def createConstantAttributeMultiBlock( dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, onPoints, vtkDataType, logger ) - return True + return def createConstantAttributeDataSet( diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index 380905ff..2984bc86 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -188,10 +188,7 @@ def test_createConstantAttributeMultiBlock( """Test creation of constant attribute in multiblock dataset.""" multiBlockDataSetTest: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) values: list[ float ] = [ np.nan ] - assert arrayModifiers.createConstantAttributeMultiBlock( multiBlockDataSetTest, - values, - attributeName, - onPoints=onPoints ) + arrayModifiers.createConstantAttributeMultiBlock( multiBlockDataSetTest, values, attributeName, onPoints=onPoints ) elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetTest ) for blockIndex in elementaryBlockIndexes: @@ -203,6 +200,24 @@ def test_createConstantAttributeMultiBlock( 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_createConstantAttributeMultiBlockRaiseValueError( + dataSetTest: vtkMultiBlockDataSet, +) -> None: + """Test the raises ValueError for the function createConstantAttributeMultiBlock with a wrong attributeName.""" + mesh: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) + with pytest.raises( ValueError ): + arrayModifiers.createConstantAttributeMultiBlock( mesh, [ np.int32( 42 ) ], "PORO" ) + + @pytest.mark.parametrize( "listValues, componentNames, componentNamesTest, onPoints, vtkDataType, vtkDataTypeTest, attributeName", [ 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 5ac0c08f..9a466383 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -201,13 +201,7 @@ def applyFilter( self: Self ) -> bool: self.logger.warning( f"The region indexes entered are not in the region attribute { self.regionName }." ) - if not createConstantAttributeMultiBlock( self.mesh, - self.defaultValue, - self.newAttributeName, - componentNames=self.componentNames, - onPoints=self.onPoints, - logger=self.logger ): - raise + createConstantAttributeMultiBlock( self.mesh, self.defaultValue, self.newAttributeName, componentNames=self.componentNames, onPoints=self.onPoints, logger=self.logger ) else: if len( invalidIndexes ) > 0: From eafaa7d197d25e49106afbc49f68f67d5e24b24c Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Dec 2025 14:12:43 +0100 Subject: [PATCH 06/17] Refactor createConstantAttribute function --- geos-mesh/src/geos/mesh/utils/arrayModifiers.py | 12 ++++++++---- .../processing/post_processing/GeosBlockMerge.py | 6 +----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index b828344c..1e5641c9 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -233,7 +233,7 @@ def createConstantAttribute( onPoints: bool = False, vtkDataType: Union[ int, None ] = None, logger: Union[ Logger, None ] = None, -) -> bool: +) -> None: """Create a new attribute with a constant value in the mesh. Args: @@ -254,8 +254,8 @@ def createConstantAttribute( logger (Union[Logger, None], optional): A logger to manage the output messages. Defaults to None, an internal logger is used. - Returns: - bool: True if the attribute was correctly created, False if it was not created. + Raises: + TypeError: Input mesh with wrong type. """ # Check if an external logger is given. if logger is None: @@ -269,7 +269,10 @@ def createConstantAttribute( elif isinstance( mesh, vtkDataSet ): createConstantAttributeDataSet( mesh, listValues, attributeName, componentNames, onPoints, vtkDataType, logger ) - return True + else: + raise TypeError( "Input mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet." ) + + return def createConstantAttributeMultiBlock( @@ -403,6 +406,7 @@ def createConstantAttributeDataSet( return + def createAttribute( dataSet: vtkDataSet, npArray: npt.NDArray[ Any ], diff --git a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py index f581ee20..15a35ede 100644 --- a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py +++ b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py @@ -157,11 +157,7 @@ def applyFilter( self: Self ) -> bool: logger=self.logger ) # Create index attribute keeping the index in initial mesh - if not createConstantAttribute( volumeMesh, [ blockIndex ], - PostProcessingOutputsEnum.BLOCK_INDEX.attributeName, - onPoints=False, - logger=self.logger ): - self.logger.warning( "BlockIndex attribute was not created." ) + createConstantAttribute( volumeMesh, [ blockIndex ], PostProcessingOutputsEnum.BLOCK_INDEX.attributeName, onPoints=False, logger=self.logger ) # Rename attributes self.renameAttributes( volumeMesh ) From 68d7efb1a3dd6063e98ab4f8ddf8f4ba1e41dcdb Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Dec 2025 14:22:04 +0100 Subject: [PATCH 07/17] Refactor createEmptyAttribute function --- geos-mesh/src/geos/mesh/utils/arrayModifiers.py | 6 +++++- geos-mesh/tests/test_arrayModifiers.py | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 1e5641c9..89dc2a8b 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -206,12 +206,16 @@ def createEmptyAttribute( componentNames (tuple[str,...]): Name of the components for vectorial attributes. vtkDataType (int): Data type. + Raises: + ValueError: Input vtkDataType is wrong. + Returns: vtkDataArray: The empty attribute. """ # Check if the vtk data type is correct. vtkNumpyTypeMap: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap() - assert vtkDataType in vtkNumpyTypeMap, f"Attribute type { vtkDataType } is unknown. The empty attribute { attributeName } has not been created into the mesh." + if vtkDataType not in vtkNumpyTypeMap: + raise ValueError( f"Attribute type { vtkDataType } is unknown." ) nbComponents: int = len( componentNames ) diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index 2984bc86..6a2dd334 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -170,6 +170,12 @@ def test_createEmptyAttribute( assert newAttr.IsA( str( expectedDatatypeArray ) ) +def test_createEmptyAttributeValueError() -> None: + """Test the raises ValueError for the function createEmptyAttribute with a wrong vtkDataType.""" + with pytest.raises( ValueError ): + newAttr: vtkDataArray = arrayModifiers.createEmptyAttribute( "newAttribute", (), 64 ) + + @pytest.mark.parametrize( "attributeName, onPoints", [ From efe5f8784898126297d8ea9452148f7be8c1ce86 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Dec 2025 15:35:11 +0100 Subject: [PATCH 08/17] Refactor FillPartialAttributes --- .../src/geos/mesh/utils/arrayModifiers.py | 63 ++++++++------- .../geos/mesh/utils/multiblockModifiers.py | 4 +- geos-mesh/tests/test_arrayModifiers.py | 79 +++++++++++++++---- .../FillPartialArrays.py | 7 +- 4 files changed, 100 insertions(+), 53 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 89dc2a8b..ffe0ded5 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -64,7 +64,7 @@ def fillPartialAttributes( listValues: Union[ list[ Any ], None ] = None, logger: Union[ Logger, None ] = None, fillAll: bool = False, -) -> bool: +) -> None: """Fill input partial attribute of multiBlockDataSet with a constant value per component. Args: @@ -82,8 +82,10 @@ def fillPartialAttributes( fillAll (bool, optional): True if fillPartialAttributes is used by fillAllPartialAttributes, else False. Defaults to False. - Returns: - bool: True if the attribute was correctly created and filled, False if not. + Raises: + TypeError: Error with the type of the mesh. + ValueError: Error with the values of the listValues. + AttributeError: Error with the attribute attributeName. """ # Check if an external logger is given. if logger is None: @@ -91,13 +93,15 @@ def fillPartialAttributes( # Check if the input mesh is inherited from vtkMultiBlockDataSet. if not isinstance( multiBlockDataSet, vtkMultiBlockDataSet ): - logger.error( "Input mesh has to be inherited from vtkMultiBlockDataSet." ) - return False + raise TypeError( "Input mesh has to be inherited from vtkMultiBlockDataSet." ) + + # Check if the attribute exist in the input mesh. + if not isAttributeInObjectMultiBlockDataSet( multiBlockDataSet, attributeName, onPoints ): + raise AttributeError( f"The attribute { attributeName } is not in the mesh." ) # Check if the attribute is partial. if isAttributeGlobal( multiBlockDataSet, attributeName, onPoints ): - logger.error( f"The attribute { attributeName } is already global." ) - return False + raise AttributeError( f"The attribute { attributeName } is already global." ) # Get information of the attribute to fill. vtkDataType: int = getVtkArrayTypeInMultiBlock( multiBlockDataSet, attributeName, onPoints ) @@ -126,8 +130,7 @@ def fillPartialAttributes( defaultValue = valueType( 0 ) mess = mess + f"{ attributeName } vtk data type is { vtkDataType } corresponding to { defaultValue.dtype } numpy type, default value is automatically set to 0." else: - logger.error( f"The type of the attribute { attributeName } is not compatible with the function." ) - return False + raise AttributeError( f"The attribute { attributeName } has an unknown type." ) listValues = [ defaultValue ] * nbComponents @@ -136,7 +139,7 @@ def fillPartialAttributes( else: if len( listValues ) != nbComponents: - return False + raise ValueError( f"The listValues must have { nbComponents } elements, not { len( listValues ) }." ) for idValue in range( nbComponents ): value: Any = listValues[ idValue ] @@ -153,13 +156,13 @@ def fillPartialAttributes( if not isAttributeInObjectDataSet( dataSet, attributeName, onPoints ): createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, onPoints, vtkDataType, logger ) - return True + return def fillAllPartialAttributes( multiBlockDataSet: Union[ vtkMultiBlockDataSet, vtkCompositeDataSet, vtkDataObject ], logger: Union[ Logger, None ] = None, -) -> bool: +) -> None: """Fill all partial attributes of a multiBlockDataSet with the default value. All components of each attributes are filled with the same value. Depending of the type of the attribute's data, the default value is different: @@ -172,13 +175,17 @@ def fillAllPartialAttributes( logger (Union[Logger, None], optional): A logger to manage the output messages. Defaults to None, an internal logger is used. - Returns: - bool: True if attributes were correctly created and filled, False if not. + Raises: + TypeError: Error with the type of the mesh. """ # Check if an external logger is given. if logger is None: logger = getLogger( "fillAllPartialAttributes", 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." ) + logger.warning( "The filling value for the attributes is depending of the type of attribute's data:\n0 for uint data,\n-1 for int data,\nnan for float data." ) @@ -187,11 +194,10 @@ def fillAllPartialAttributes( for onPoints in [ True, False ]: infoAttributes: dict[ str, int ] = getAttributesWithNumberOfComponents( multiBlockDataSet, onPoints ) for attributeName in infoAttributes: - if not isAttributeGlobal( multiBlockDataSet, attributeName, onPoints ) and \ - not fillPartialAttributes( multiBlockDataSet, attributeName, onPoints=onPoints, logger=logger, fillAll=True ): - return False + if not isAttributeGlobal( multiBlockDataSet, attributeName, onPoints ): + fillPartialAttributes( multiBlockDataSet, attributeName, onPoints=onPoints, logger=logger, fillAll=True ) - return True + return def createEmptyAttribute( @@ -207,7 +213,7 @@ def createEmptyAttribute( vtkDataType (int): Data type. Raises: - ValueError: Input vtkDataType is wrong. + ValueError: Error with the vtkDataType. Returns: vtkDataArray: The empty attribute. @@ -259,7 +265,7 @@ def createConstantAttribute( Defaults to None, an internal logger is used. Raises: - TypeError: Input mesh with wrong type. + TypeError: Error with the type of the mesh. """ # Check if an external logger is given. if logger is None: @@ -309,8 +315,8 @@ def createConstantAttributeMultiBlock( Defaults to None, an internal logger is used. Raises: - TypeError: Input mesh with wrong type. - ValueError: Input attribute name already in the mesh. + 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: @@ -322,7 +328,7 @@ def createConstantAttributeMultiBlock( # Check if the attribute already exist in the input mesh. if isAttributeInObjectMultiBlockDataSet( multiBlockDataSet, attributeName, onPoints ): - raise ValueError( f"The attribute { attributeName } is already present in the mesh." ) + 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 ) @@ -363,8 +369,8 @@ def createConstantAttributeDataSet( Defaults to None, an internal logger is used. Raises: - TypeError: Input data with wrong type. - ValueError: Input data with wrong value. + 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: @@ -441,8 +447,9 @@ def createAttribute( Defaults to None, an internal logger is used. Raises: - TypeError: Input data with wrong type. - ValueError: Input data with wrong value. + TypeError: Error with the type of the mesh or the npArray values. + ValueError: Error with the values of npArray or vtkDataType. + AttributeError: Error with the attribute attributeName. """ # Check if an external logger is given. if logger is None: @@ -454,7 +461,7 @@ def createAttribute( # Check if the attribute already exist in the input mesh. if isAttributeInObjectDataSet( dataSet, attributeName, onPoints ): - raise ValueError( f"The attribute { attributeName } is already present in the mesh." ) + raise AttributeError( f"The attribute { attributeName } is already present in the mesh." ) # Check the coherency between the given array type and the vtk array type if it exist. if vtkDataType is not None: diff --git a/geos-mesh/src/geos/mesh/utils/multiblockModifiers.py b/geos-mesh/src/geos/mesh/utils/multiblockModifiers.py index 352860bf..fac21e4c 100644 --- a/geos-mesh/src/geos/mesh/utils/multiblockModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/multiblockModifiers.py @@ -64,8 +64,8 @@ def mergeBlocks( vtkErrorLogger.addFilter( RegexExceptionFilter() ) # will raise VTKError if captured VTK Error # Fill the partial attributes with default values to keep them during the merge. - if keepPartialAttributes and not fillAllPartialAttributes( inputMesh, logger ): - raise ValueError( "Failed to fill partial attributes. Merging without keeping partial attributes." ) + if keepPartialAttributes: + fillAllPartialAttributes( inputMesh, logger ) outputMesh: vtkUnstructuredGrid diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index 6a2dd334..5266c178 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -81,10 +81,7 @@ def test_fillPartialAttributes( """Test filling a partial attribute from a multiblock with values.""" multiBlockDataSetTest: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) # Fill the attribute in the multiBlockDataSet. - assert arrayModifiers.fillPartialAttributes( multiBlockDataSetTest, - attributeName, - onPoints=onPoints, - listValues=listValues ) + arrayModifiers.fillPartialAttributes( multiBlockDataSetTest, attributeName, onPoints=onPoints, listValues=listValues ) # Get the dataSet where the attribute has been filled. dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTest.GetDataSet( idBlock ) ) @@ -127,14 +124,44 @@ def test_fillPartialAttributes( assert vtkDataTypeFilled == vtkDataTypeTest -@pytest.mark.parametrize( "multiBlockDataSetName", [ "multiblock" ] ) +def test_fillPartialAttributesTypeError( + dataSetTest: vtkDataSet, +) -> None: + """Test the raises TypeError for the function fillPartialAttributes with a wrong mesh type.""" + mesh: vtkDataSet = dataSetTest( "dataset" ) + with pytest.raises( TypeError ): + arrayModifiers.fillPartialAttributes( mesh, "PORO" ) + + +def test_fillPartialAttributesValueError( + dataSetTest: vtkMultiBlockDataSet, +) -> None: + """Test the raises ValueError for the function fillPartialAttributes with to many values for the attribute.""" + mesh: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) + with pytest.raises( ValueError ): + arrayModifiers.fillPartialAttributes( mesh, "PORO", listValues=[ 42, 42] ) + + +@pytest.mark.parametrize( "attributeName", [ + ( "newAttribute" ), # The attribute is not in the mesh + ( "GLOBAL_IDS_CELLS" ), # The attribute is already global +] ) +def test_fillPartialAttributesAttributeError( + dataSetTest: vtkMultiBlockDataSet, + attributeName: str, +) -> None: + """Test the raises AttributeError for the function fillPartialAttributes.""" + mesh: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) + with pytest.raises( AttributeError ): + arrayModifiers.fillPartialAttributes( mesh, attributeName ) + + def test_FillAllPartialAttributes( dataSetTest: vtkMultiBlockDataSet, - multiBlockDataSetName: str, ) -> None: """Test to fill all the partial attributes of a vtkMultiBlockDataSet with a value.""" - multiBlockDataSetTest: vtkMultiBlockDataSet = dataSetTest( multiBlockDataSetName ) - assert arrayModifiers.fillAllPartialAttributes( multiBlockDataSetTest ) + multiBlockDataSetTest: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) + arrayModifiers.fillAllPartialAttributes( multiBlockDataSetTest ) elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetTest ) for blockIndex in elementaryBlockIndexes: @@ -148,6 +175,15 @@ def test_FillAllPartialAttributes( assert attributeExist == 1 +def test_fillAllPartialAttributesTypeError( + dataSetTest: vtkDataSet, +) -> None: + """Test the raises TypeError for the function fillAllPartialAttributes with a wrong mesh type.""" + mesh: vtkDataSet = dataSetTest( "dataset" ) + with pytest.raises( TypeError ): + arrayModifiers.fillAllPartialAttributes( mesh ) + + @pytest.mark.parametrize( "attributeName, dataType, expectedDatatypeArray", [ ( "test_double", VTK_DOUBLE, "vtkDoubleArray" ), ( "test_float", VTK_FLOAT, "vtkFloatArray" ), @@ -215,12 +251,12 @@ def test_createConstantAttributeMultiBlockRaiseTypeError( arrayModifiers.createConstantAttributeMultiBlock( mesh, [ np.int32( 42 ) ], "newAttribute" ) -def test_createConstantAttributeMultiBlockRaiseValueError( +def test_createConstantAttributeMultiBlockRaiseAttributeError( dataSetTest: vtkMultiBlockDataSet, ) -> None: - """Test the raises ValueError for the function createConstantAttributeMultiBlock with a wrong attributeName.""" + """Test the raises AttributeError for the function createConstantAttributeMultiBlock with a wrong attributeName.""" mesh: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - with pytest.raises( ValueError ): + with pytest.raises( AttributeError ): arrayModifiers.createConstantAttributeMultiBlock( mesh, [ np.int32( 42 ) ], "PORO" ) @@ -449,15 +485,13 @@ def test_createAttributeRaiseTypeError( arrayModifiers.createAttribute( mesh, npArray, attributeName, vtkDataType=VTK_DOUBLE ) -@pytest.mark.parametrize( "attributeName, vtkDataType, nbElements", [ - ( "CellAttribute", VTK_DOUBLE, 1740 ), # The attribute name is already on the mesh - ( "newAttribute", 64, 1740 ), # The vtkDataType does not exist - ( "newAttribute", VTK_DOUBLE, 1741 ), # The number of element of the array is wrong +@pytest.mark.parametrize( "vtkDataType, nbElements", [ + ( 64, 1740 ), # The vtkDataType does not exist + ( VTK_DOUBLE, 1741 ), # The number of element of the array is wrong ] ) def test_createAttributeRaiseValueError( dataSetTest: Any, getArrayWithSpeTypeValue: npt.NDArray[ Any ], - attributeName: str, vtkDataType: int, nbElements: int, ) -> None: @@ -465,7 +499,18 @@ def test_createAttributeRaiseValueError( mesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( "dataset" ) npArray: npt.NDArray[ Any ] = getArrayWithSpeTypeValue( 1, nbElements, "float64" ) with pytest.raises( ValueError ): - arrayModifiers.createAttribute( mesh, npArray, attributeName, vtkDataType=vtkDataType ) + arrayModifiers.createAttribute( mesh, npArray, "newAttribute", vtkDataType=vtkDataType ) + + +def test_createAttributeRaiseAttributeError( + dataSetTest: Any, + getArrayWithSpeTypeValue: npt.NDArray[ Any ], +) -> None: + """Test the raises AttributeError for the function createAttribute with a wrong attribute name.""" + mesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( "dataset" ) + npArray: npt.NDArray[ Any ] = getArrayWithSpeTypeValue( 1, 1740, "float64" ) + with pytest.raises( AttributeError ): + arrayModifiers.createAttribute( mesh, npArray, "PORO" ) @pytest.mark.parametrize( diff --git a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py index 22d7275a..f0da89ae 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py @@ -126,12 +126,7 @@ def applyFilter( self: Self ) -> bool: f"There is two attribute named { attributeName }, one on points and the other on cells. The attribute name must be unique." ) - if not fillPartialAttributes( self.multiBlockDataSet, - attributeName, - onPoints=onPoints, - listValues=self.dictAttributesValues[ attributeName ], - logger=self.logger ): - raise + fillPartialAttributes( self.multiBlockDataSet, attributeName, onPoints=onPoints, listValues=self.dictAttributesValues[ attributeName ], logger=self.logger ) self.logger.info( f"The filter { self.logger.name } succeed." ) except ( ValueError, AttributeError ) as e: From 85b3cd497efd81130a59ba5234ef3e04593e03c0 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Dec 2025 16:24:32 +0100 Subject: [PATCH 09/17] Refactor copyAttribute --- .../src/geos/mesh/utils/arrayModifiers.py | 76 +++++-------------- geos-mesh/tests/test_arrayModifiers.py | 68 ++++++++++++++++- 2 files changed, 86 insertions(+), 58 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index ffe0ded5..2098aa0c 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -534,7 +534,7 @@ def copyAttribute( attributeNameTo: str, onPoints: bool = False, logger: Union[ Logger, None ] = None, -) -> bool: +) -> None: """Copy an attribute from a multiBlockDataSet to a similar one on the same piece. Args: @@ -547,8 +547,10 @@ def copyAttribute( logger (Union[Logger, None], optional): A logger to manage the output messages. Defaults to None, an internal logger is used. - Returns: - bool: True if copy successfully ended, False otherwise. + Raises: + TypeError: Error with the type of the mesh from or to. + ValueError: Error with the data of the meshes from and to. + AttributeError: Error with the attribute attributeNameFrom or attributeNameTo. """ # Check if an external logger is given. if logger is None: @@ -556,57 +558,35 @@ def copyAttribute( # Check if the multiBlockDataSetFrom is inherited from vtkMultiBlockDataSet. if not isinstance( multiBlockDataSetFrom, vtkMultiBlockDataSet ): - logger.error( # type: ignore[unreachable] - "multiBlockDataSetFrom has to be inherited from vtkMultiBlockDataSet." ) - logger.error( f"The attribute { attributeNameFrom } has not been copied." ) - return False + raise TypeError( "Input mesh from has to be inherited from vtkMultiBlockDataSet." ) # Check if the multiBlockDataSetTo is inherited from vtkMultiBlockDataSet. if not isinstance( multiBlockDataSetTo, vtkMultiBlockDataSet ): - logger.error( # type: ignore[unreachable] - "multiBlockDataSetTo has to be inherited from vtkMultiBlockDataSet." ) - logger.error( f"The attribute { attributeNameFrom } has not been copied." ) - return False + raise TypeError( "Input mesh to has to be inherited from vtkMultiBlockDataSet." ) # Check if the attribute exist in the multiBlockDataSetFrom. if not isAttributeInObjectMultiBlockDataSet( multiBlockDataSetFrom, attributeNameFrom, onPoints ): - logger.error( f"The attribute { attributeNameFrom } is not in the multiBlockDataSetFrom." ) - logger.error( f"The attribute { attributeNameFrom } has not been copied." ) - return False + raise AttributeError( f"The attribute { attributeNameFrom } is not present in the mesh from." ) # Check if the attribute already exist in the multiBlockDataSetTo. if isAttributeInObjectMultiBlockDataSet( multiBlockDataSetTo, attributeNameTo, onPoints ): - logger.error( f"The attribute { attributeNameTo } is already in the multiBlockDataSetTo." ) - logger.error( f"The attribute { attributeNameFrom } has not been copied." ) - return False + raise AttributeError( f"The attribute { attributeNameTo } is already present in the mesh to." ) # Check if the two multiBlockDataSets are similar. elementaryBlockIndexesTo: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetTo ) elementaryBlockIndexesFrom: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetFrom ) if elementaryBlockIndexesTo != elementaryBlockIndexesFrom: - logger.error( "multiBlockDataSetFrom and multiBlockDataSetTo do not have the same block indexes." ) - logger.error( f"The attribute { attributeNameFrom } has not been copied." ) - return False + 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 ) ) - if dataSetFrom is None: - logger.error( f"Block { idBlock } of multiBlockDataSetFrom is null." ) # type: ignore[unreachable] - logger.error( f"The attribute { attributeNameFrom } has not been copied." ) - return False - dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTo.GetDataSet( idBlock ) ) - if dataSetTo is None: - logger.error( f"Block { idBlock } of multiBlockDataSetTo is null." ) # type: ignore[unreachable] - logger.error( f"The attribute { attributeNameFrom } has not been copied." ) - return False - if isAttributeInObjectDataSet( dataSetFrom, attributeNameFrom, onPoints ) and \ - not copyAttributeDataSet( dataSetFrom, dataSetTo, attributeNameFrom, attributeNameTo, onPoints, logger ): - return False + if isAttributeInObjectDataSet( dataSetFrom, attributeNameFrom, onPoints ): + copyAttributeDataSet( dataSetFrom, dataSetTo, attributeNameFrom, attributeNameTo, onPoints, logger ) - return True + return def copyAttributeDataSet( @@ -616,7 +596,7 @@ def copyAttributeDataSet( attributeNameTo: str, onPoints: bool = False, logger: Union[ Logger, Any ] = None, -) -> bool: +) -> None: """Copy an attribute from a dataSet to a similar one on the same piece. Args: @@ -629,8 +609,9 @@ def copyAttributeDataSet( logger (Union[Logger, None], optional): A logger to manage the output messages. Defaults to None, an internal logger is used. - Returns: - bool: True if copy successfully ended, False otherwise. + Raises: + TypeError: Error with the type of the mesh from. + AttributeError: Error with the attribute attributeNameFrom. """ # Check if an external logger is given. if logger is None: @@ -638,34 +619,19 @@ def copyAttributeDataSet( # Check if the dataSetFrom is inherited from vtkDataSet. if not isinstance( dataSetFrom, vtkDataSet ): - logger.error( "dataSetFrom has to be inherited from vtkDataSet." ) # type: ignore[unreachable] - logger.error( f"The attribute { attributeNameFrom } has not been copied." ) - return False - - # Check if the dataSetTo is inherited from vtkDataSet. - if not isinstance( dataSetTo, vtkDataSet ): - logger.error( "dataSetTo has to be inherited from vtkDataSet." ) # type: ignore[unreachable] - logger.error( f"The attribute { attributeNameFrom } has not been copied." ) - return False + raise TypeError( "Input mesh from has to be inherited from vtkDataSet." ) # Check if the attribute exist in the dataSetFrom. if not isAttributeInObjectDataSet( dataSetFrom, attributeNameFrom, onPoints ): - logger.error( f"The attribute { attributeNameFrom } is not in the dataSetFrom." ) - logger.error( f"The attribute { attributeNameFrom } has not been copied." ) - return False - - # Check if the attribute already exist in the dataSetTo. - if isAttributeInObjectDataSet( dataSetTo, attributeNameTo, onPoints ): - logger.error( f"The attribute { attributeNameTo } is already in the dataSetTo." ) - logger.error( f"The attribute { attributeNameFrom } has not been copied." ) - return False + raise AttributeError( f"The attribute { attributeNameFrom } is not in the input mesh from." ) npArray: npt.NDArray[ Any ] = getArrayInObject( dataSetFrom, attributeNameFrom, onPoints ) componentNames: tuple[ str, ...] = getComponentNamesDataSet( dataSetFrom, attributeNameFrom, onPoints ) vtkArrayType: int = getVtkArrayTypeInObject( dataSetFrom, attributeNameFrom, onPoints ) createAttribute( dataSetTo, npArray, attributeNameTo, componentNames, onPoints, vtkArrayType, logger ) - return True + + return def transferAttributeToDataSetWithElementMap( diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index 5266c178..3b98ab02 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -534,8 +534,7 @@ def test_copyAttribute( multiBlockDataSetTo: vtkMultiBlockDataSet = dataSetTest( "emptymultiblock" ) # Copy the attribute from the multiBlockDataSetFrom to the multiBlockDataSetTo. - assert arrayModifiers.copyAttribute( multiBlockDataSetFrom, multiBlockDataSetTo, attributeNameFrom, attributeNameTo, - onPoints ) + arrayModifiers.copyAttribute( multiBlockDataSetFrom, multiBlockDataSetTo, attributeNameFrom, attributeNameTo, onPoints ) # Parse the two multiBlockDataSet and test if the attribute has been copied. elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetFrom ) @@ -556,6 +555,49 @@ def test_copyAttribute( assert attributeExistCopied == attributeExistTest +@pytest.mark.parametrize( "meshNameFrom, meshNameTo", [ + ( "dataset", "emptydataset" ), + ( "dataset", "emptymultiblock" ), + ( "multiblock", "emptydataset" ), +] ) +def test_copyAttributeTypeError( + dataSetTest: Any, + meshNameFrom: str, + meshNameTo: str, +) -> None: + """Test the raises TypeError for the function copyAttribute.""" + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshNameFrom ) + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] = 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" ) + with pytest.raises( ValueError ): + arrayModifiers.copyAttribute( meshFrom, meshTo, "PORO", "PORO" ) + + +@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 +] ) +def test_copyAttributeAttributeError( + dataSetTest: vtkMultiBlockDataSet, + attributeNameFrom: str, + attributeNameTo: str, +) -> None: + """Test the raises AttributeError for the function copyAttribute.""" + meshFrom: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) + meshTo: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) + with pytest.raises( AttributeError ): + arrayModifiers.copyAttribute( meshFrom, meshTo, attributeNameFrom, attributeNameTo ) + + @pytest.mark.parametrize( "attributeNameFrom, attributeNameTo, onPoints", [ ( "CellAttribute", "CellAttributeTo", False ), ( "PointAttribute", "PointAttributeTo", True ), @@ -571,7 +613,7 @@ def test_copyAttributeDataSet( dataSetTo: vtkDataSet = dataSetTest( "emptydataset" ) # Copy the attribute from the dataSetFrom to the dataSetTo. - assert arrayModifiers.copyAttributeDataSet( dataSetFrom, dataSetTo, attributeNameFrom, attributeNameTo, onPoints ) + arrayModifiers.copyAttributeDataSet( dataSetFrom, dataSetTo, attributeNameFrom, attributeNameTo, onPoints ) # Get the tested attribute and its copy. dataFrom: Union[ vtkPointData, vtkCellData ] @@ -607,6 +649,26 @@ def test_copyAttributeDataSet( 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( "meshFromName, meshToName, attributeName, onPoints, defaultValueTest", [ ( "fracture", "emptyFracture", "collocated_nodes", True, [ -1, -1 ] ), ( "multiblock", "emptyFracture", "FAULT", False, -1 ), From 8b5d28abc5a3862da355bf2a264a895f2b86753f Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Dec 2025 18:15:31 +0100 Subject: [PATCH 10/17] Refactor transferAttributeWithElementMap --- .../src/geos/mesh/utils/arrayModifiers.py | 68 ++++++++------- geos-mesh/tests/test_arrayModifiers.py | 84 ++++++++++++++++++- .../AttributeMapping.py | 5 +- 3 files changed, 120 insertions(+), 37 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 2098aa0c..576633b3 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -642,7 +642,7 @@ def transferAttributeToDataSetWithElementMap( onPoints: bool, flatIdDataSetTo: int = 0, logger: Union[ Logger, Any ] = None, -) -> bool: +) -> 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. @@ -670,23 +670,33 @@ def transferAttributeToDataSetWithElementMap( logger (Union[Logger, None], optional): A logger to manage the output messages. Defaults to None, an internal logger is used. - Returns: - bool: True if transfer successfully ended. + 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. """ # Check if an external logger is given. if logger is None: logger = getLogger( "transferAttributeToDataSetWithElementMap", True ) + if not isinstance( dataSetTo, vtkDataSet ): + raise TypeError( "The mesh to has to be inherited from vtkDataSet." ) + if flatIdDataSetTo not in elementMap: - logger.error( f"The map is incomplete, there is no data for the final mesh (flat index { flatIdDataSetTo })." ) - return False + raise ValueError( f"The map is incomplete, there is no data for the final mesh (flat index { flatIdDataSetTo })." ) nbElementsTo: int = dataSetTo.GetNumberOfPoints() if onPoints else dataSetTo.GetNumberOfCells() if len( elementMap[ flatIdDataSetTo ] ) != nbElementsTo: - logger.error( - f"The map is wrong, there is { nbElementsTo } elements in the final mesh (flat index { flatIdDataSetTo })\ - but { len( elementMap[ flatIdDataSetTo ] ) } elements in the map." ) - return False + 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 mesh from has to be inherited from vtkDataSet or vtkMultiBlockDataSet." ) + + if not isAttributeInObject( meshFrom, attributeName, onPoints ): + raise AttributeError( f"The attribute { attributeName } is not in the mesh from." ) + + if isinstance( meshFrom, vtkMultiBlockDataSet ) and not isAttributeGlobal( meshFrom, attributeName, onPoints ): + raise AttributeError( f"The attribute { attributeName } must be global in the mesh from." ) componentNames: tuple[ str, ...] = getComponentNames( meshFrom, attributeName, onPoints ) nbComponents: int = len( componentNames ) @@ -700,6 +710,8 @@ def transferAttributeToDataSetWithElementMap( 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 ] @@ -729,7 +741,8 @@ def transferAttributeToDataSetWithElementMap( arrayTo[ idElementTo ] = valueToTransfer createAttribute( dataSetTo, arrayTo, attributeName, componentNames, onPoints=onPoints, vtkDataType=vtkDataType, logger=logger ) - return True + + return def transferAttributeWithElementMap( @@ -739,7 +752,7 @@ def transferAttributeWithElementMap( attributeName: str, onPoints: bool, logger: Union[ Logger, Any ] = None, -) -> bool: +) -> 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. @@ -767,37 +780,28 @@ def transferAttributeWithElementMap( logger (Union[Logger, None], optional): A logger to manage the output messages. Defaults to None, an internal logger is used. - Returns: - bool: True if transfer successfully ended. + Raises: + TypeError: Error with the type of the mesh to. + AttributeError: Error with the attribute attributeName. """ # Check if an external logger is given. if logger is None: logger = getLogger( "transferAttributeWithElementMap", True ) if isinstance( meshTo, vtkDataSet ): - return transferAttributeToDataSetWithElementMap( meshFrom, - meshTo, - elementMap, - attributeName, - onPoints, - logger=logger ) + transferAttributeToDataSetWithElementMap( meshFrom, meshTo, elementMap, attributeName, onPoints, logger=logger ) elif isinstance( meshTo, vtkMultiBlockDataSet ): + if isAttributeInObjectMultiBlockDataSet( meshTo, attributeName, onPoints ): + raise AttributeError( f"The attribute { attributeName } is already in the mesh to." ) + listFlatIdDataSetTo: list[ int ] = getBlockElementIndexesFlatten( meshTo ) for flatIdDataSetTo in listFlatIdDataSetTo: dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( meshTo.GetDataSet( flatIdDataSetTo ) ) - if not transferAttributeToDataSetWithElementMap( meshFrom, - dataSetTo, - elementMap, - attributeName, - onPoints, - flatIdDataSetTo=flatIdDataSetTo, - logger=logger ): - logger.error( - f"The attribute transfer has failed for the dataset with the flat index { flatIdDataSetTo } of the final mesh." - ) - logger.warning( "The final mesh may has been modify for the other datasets." ) - return False - return True + transferAttributeToDataSetWithElementMap( meshFrom, dataSetTo, elementMap, attributeName, onPoints, flatIdDataSetTo=flatIdDataSetTo, logger=logger ) + else: + raise TypeError( "The mesh to has to be inherited from vtkDataSet or vtkMultiBlockDataSet." ) + + return def renameAttribute( diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index 3b98ab02..efe13a6d 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -669,6 +669,64 @@ def test_copyAttributeDataSetAttributeError( arrayModifiers.copyAttributeDataSet( meshFrom, meshTo, "newAttribute", "newAttribute" ) +@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 ] + if isMeshFrom: + meshFrom = dataSetTest( "multiblock" ) + else: + meshFrom = False + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshToName ) + elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( "multiblock", meshToName, False ) + with pytest.raises( TypeError ): + arrayModifiers.transferAttributeToDataSetWithElementMap( meshFrom, meshTo, elementMap, "FAULT", False ) + + +@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", False ) + with pytest.raises( AttributeError ): + arrayModifiers.transferAttributeToDataSetWithElementMap( meshFrom, meshTo, elementMap, attributeName, False ) + + +@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, onPoints, defaultValueTest", [ ( "fracture", "emptyFracture", "collocated_nodes", True, [ -1, -1 ] ), ( "multiblock", "emptyFracture", "FAULT", False, -1 ), @@ -693,7 +751,7 @@ def test_transferAttributeWithElementMap( meshTo: Union[ vtkMultiBlockDataSet, vtkDataSet ] = dataSetTest( meshToName ) elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( meshFromName, meshToName, onPoints ) - assert arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, attributeName, onPoints ) + arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, attributeName, onPoints ) for flatIdDataSetTo in elementMap: dataTo: Union[ vtkPointData, vtkCellData ] @@ -722,6 +780,30 @@ def test_transferAttributeWithElementMap( assert np.all( arrayTo[ idElementTo ] == arrayFrom[ idElementFrom ] ) +def test_transferAttributeWithElementMapTypeError( + dataSetTest: vtkMultiBlockDataSet, + getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], +) -> 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", False ) + with pytest.raises( TypeError ): + arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, "FAULT", False ) + + +def test_transferAttributeWithElementMapAttributeError( + dataSetTest: vtkMultiBlockDataSet, + getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], +) -> 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", False ) + with pytest.raises( AttributeError ): + arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, "FAULT", False ) + + @pytest.mark.parametrize( "attributeName, onPoints", [ ( "CellAttribute", False ), ( "PointAttribute", True ), 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 ba7e5852..7567ea9d 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -178,10 +178,7 @@ def applyFilter( self: Self ) -> bool: raise ValueError( f"The two meshes do not have any shared { self.piece }." ) for attributeName in self.attributeNames: - # TODO:: Modify arrayModifiers function to raise error. - if not transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, - self.onPoints, self.logger ): - raise + transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, self.onPoints, self.logger ) # Log the output message. self._logOutputMessage() From b94e1a5b5d9501f15a52cf8368493e2845298504 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Dec 2025 18:44:25 +0100 Subject: [PATCH 11/17] Refactor renameAttribute function --- .../src/geos/mesh/utils/arrayModifiers.py | 65 ++++++++++++++----- geos-mesh/tests/test_arrayModifiers.py | 21 ++++++ .../post_processing/GeosBlockMerge.py | 4 +- 3 files changed, 73 insertions(+), 17 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 576633b3..26ba8fa3 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -1,11 +1,12 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. # SPDX-FileContributor: Martin Lemay, Alexandre Benedicto, Paloma Martinez, Romain Baville +import logging import numpy as np import numpy.typing as npt import vtkmodules.util.numpy_support as vnp from typing import Union, Any -from geos.utils.Logger import getLogger, Logger +from geos.utils.Logger import ( getLogger, Logger, VTKCaptureLog, RegexExceptionFilter ) from vtk import ( # type: ignore[import-untyped] VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT, VTK_UNSIGNED_LONG_LONG, @@ -27,7 +28,7 @@ ) from vtkmodules.vtkCommonCore import ( vtkDataArray, - vtkPoints, + vtkPoints, vtkLogger ) from geos.mesh.utils.arrayHelpers import ( getComponentNames, @@ -44,6 +45,8 @@ getNumberOfComponentsMultiBlock, ) from geos.mesh.utils.multiblockHelpers import getBlockElementIndexesFlatten +from geos.utils.Errors import VTKError + __doc__ = """ ArrayModifiers contains utilities to process VTK Arrays objects. @@ -809,28 +812,60 @@ def renameAttribute( attributeName: str, newAttributeName: str, onPoints: bool, -) -> bool: - """Rename an attribute. + logger: Union[ Logger, Any ] = None, +) -> None: + """Rename an attribute with a unique name. Args: object (vtkMultiBlockDataSet): Object where the attribute is. attributeName (str): Name of the attribute. newAttributeName (str): New name of the attribute. onPoints (bool): True if attributes are on points, False if they are on cells. + logger (Union[Logger, None], optional): A logger to manage the output messages. + Defaults to None, an internal logger is used. - Returns: - bool: True if renaming operation successfully ended. + Raises: + TypeError: Error with the type of the mesh. + AttributeError: Error with the attribute attributeName or newAttributeName. + VTKError: Error with a VTK function. """ - if isAttributeInObject( object, attributeName, onPoints ): + if logger is None: + logger = getLogger( "renameAttribute", True ) + + if not isinstance( object, ( vtkDataSet, vtkMultiBlockDataSet ) ): + raise TypeError( "The mesh has to be inherited from vtkDataSet or vtkMultiBlockDataSet" ) + + if not isAttributeInObject( object, attributeName, onPoints ): + raise AttributeError( f"The attribute { attributeName } is not in the mesh." ) + + if isAttributeInObject( object, newAttributeName, onPoints ): + raise AttributeError( f"The attribute { newAttributeName } is already an attribute." ) + + 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: dim: int = 0 if onPoints else 1 - filter = vtkArrayRename() - filter.SetInputData( object ) - filter.SetArrayName( dim, attributeName, newAttributeName ) - filter.Update() - object.ShallowCopy( filter.GetOutput() ) - else: - return False - return True + renameArrayFilter = vtkArrayRename() + renameArrayFilter.SetInputData( object ) + renameArrayFilter.SetArrayName( dim, attributeName, newAttributeName ) + renameArrayFilter.Update() + + capturedLog.seek( 0 ) + captured = capturedLog.read().decode() + + if captured != "": + vtkErrorLogger.error( captured.strip() ) + + object.ShallowCopy( renameArrayFilter.GetOutput() ) + + if object is None: + raise VTKError( "Something went wrong with VTK renaming of the attribute." ) + + return def createCellCenterAttribute( mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ], cellCenterAttributeName: str ) -> bool: diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index efe13a6d..59a0791c 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -855,3 +855,24 @@ def test_renameAttributeDataSet( else: assert vtkDataSetTest.GetCellData().HasArray( attributeName ) == 0 assert vtkDataSetTest.GetCellData().HasArray( newAttributeName ) == 1 + + +def test_renameAttributeTypeError() -> None: + """Test the raises TypeError for the function renameAttribute with the mesh to with a wrong type.""" + with pytest.raises( TypeError ): + arrayModifiers.renameAttribute( False, "PORO", "newName", False ) + + +@pytest.mark.parametrize( "attributeName, newName", [ + ( "newName", "newName" ), # The attribute is not in the mesh. + ( "PORO", "PORO" ), # The new name is already an attribute in the mesh. +] ) +def test_renameAttributeAttributeError( + dataSetTest: vtkDataSet, + attributeName: str, + newName: str, +) -> None: + """Test the raises AttributeError for the function renameAttribute.""" + mesh: vtkDataSet = dataSetTest( "dataset" ) + with pytest.raises( AttributeError ): + arrayModifiers.renameAttribute( mesh, attributeName, newName, False ) diff --git a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py index 15a35ede..85997d67 100644 --- a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py +++ b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py @@ -202,9 +202,9 @@ def renameAttributes( if suffix == "_density": for phaseName in self.phaseNameDict[ PhaseTypeEnum.ROCK.type ]: if phaseName in attributeName: - renameAttribute( mesh, attributeName, newName, False ) + renameAttribute( mesh, attributeName, newName, False, logger=self.logger ) else: - renameAttribute( mesh, attributeName, newName, False ) + renameAttribute( mesh, attributeName, newName, False, logger=self.logger ) return From 3a80d4fdcbd1b89ea59d52733fcfed4b40ce8584 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Dec 2025 19:00:46 +0100 Subject: [PATCH 12/17] Refactor createCellCenterAttribute functions --- .../src/geos/mesh/utils/arrayModifiers.py | 107 ++++++++++++------ 1 file changed, 72 insertions(+), 35 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 26ba8fa3..a89dc6a6 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -861,66 +861,103 @@ def renameAttribute( vtkErrorLogger.error( captured.strip() ) object.ShallowCopy( renameArrayFilter.GetOutput() ) - if object is None: raise VTKError( "Something went wrong with VTK renaming of the attribute." ) return -def createCellCenterAttribute( mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ], cellCenterAttributeName: str ) -> bool: - """Create elementCenter attribute if it does not exist. +def createCellCenterAttribute( mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ], cellCenterAttributeName: str, logger: Union[ Logger, Any ] = None, ) -> None: + """Create cellElementCenter attribute if it does not exist. Args: - mesh (vtkMultiBlockDataSet | vtkDataSet): input mesh - cellCenterAttributeName (str): Name of the attribute + mesh (vtkMultiBlockDataSet | vtkDataSet): Input mesh. + 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: Raised if input mesh is not a vtkMultiBlockDataSet or a - vtkDataSet. - - Returns: - bool: True if calculation successfully ended, False otherwise. + TypeError: Error with the mesh type. + AttributeError: Error with the attribute cellCenterAttributeName. """ - ret: int = 1 + if logger is None: + logger = getLogger( "createCellCenterAttribute", True ) + if isinstance( mesh, vtkMultiBlockDataSet ): + if isAttributeInObjectMultiBlockDataSet( mesh, cellCenterAttributeName, False ): + raise AttributeError( f"The attribute { cellCenterAttributeName } in already in the mesh." ) + elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( mesh ) for blockIndex in elementaryBlockIndexes: dataSet: vtkDataSet = vtkDataSet.SafeDownCast( mesh.GetDataSet( blockIndex ) ) - ret *= int( doCreateCellCenterAttribute( dataSet, cellCenterAttributeName ) ) + createCellCenterAttributeDataSet( dataSet, cellCenterAttributeName, logger ) elif isinstance( mesh, vtkDataSet ): - ret = int( doCreateCellCenterAttribute( mesh, cellCenterAttributeName ) ) + createCellCenterAttributeDataSet( mesh, cellCenterAttributeName, logger ) else: raise TypeError( "Input object must be a vtkDataSet or vtkMultiBlockDataSet." ) - return bool( ret ) + + return -def doCreateCellCenterAttribute( block: vtkDataSet, cellCenterAttributeName: str ) -> bool: - """Create elementCenter attribute in a vtkDataSet if it does not exist. +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 + 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. - Returns: - bool: True if calculation successfully ended, False otherwise. + Raises: + TypeError: Error with the type of the mesh or the npArray values. + AttributeError: Error with the attribute cellCenterAttributeName. + VTKError: Error with a VTK function. """ - if not isAttributeInObject( block, cellCenterAttributeName, False ): + 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, False ): + 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 - filter: vtkCellCenters = vtkCellCenters() - filter.SetInputData( block ) - filter.Update() - output: vtkPointSet = filter.GetOutputDataObject( 0 ) - assert output is not None, "vtkCellCenters output is null." - # transfer output to output arrays - centers: vtkPoints = output.GetPoints() - assert centers is not None, "Center are undefined." - centerCoords: vtkDataArray = centers.GetData() - assert centers is not None, "Center coordinates are undefined." - centerCoords.SetName( cellCenterAttributeName ) - block.GetCellData().AddArray( centerCoords ) - block.Modified() - return True + 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 ) -> vtkPointSet: From 938d93170071f33efe800d8a5d7a95d6fe0b312f Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Dec 2025 19:03:54 +0100 Subject: [PATCH 13/17] Update the use of createCellCenterAttribute --- .../pv/plugins/post_processing/PVGeosBlockExtractAndMerge.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/geos-pv/src/geos/pv/plugins/post_processing/PVGeosBlockExtractAndMerge.py b/geos-pv/src/geos/pv/plugins/post_processing/PVGeosBlockExtractAndMerge.py index 123153fb..08636fd8 100644 --- a/geos-pv/src/geos/pv/plugins/post_processing/PVGeosBlockExtractAndMerge.py +++ b/geos-pv/src/geos/pv/plugins/post_processing/PVGeosBlockExtractAndMerge.py @@ -287,7 +287,8 @@ def RequestData( # Create elementCenter attribute in the volume mesh if needed cellCenterAttributeName: str = GeosMeshOutputsEnum.ELEMENT_CENTER.attributeName - createCellCenterAttribute( outputCells, cellCenterAttributeName ) + if cellCenterAttributeName not in meshAttributes: + createCellCenterAttribute( outputCells, cellCenterAttributeName, logger=self.logger ) # Stop the time step iteration request.Remove( executive.CONTINUE_EXECUTING() ) From f96dcd8b7bf1f8fc205e552bfd9c2c8a8716f67f Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Dec 2025 19:11:35 +0100 Subject: [PATCH 14/17] Refactor transferPointDataToCellData --- .../src/geos/mesh/utils/arrayModifiers.py | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index a89dc6a6..e4565e6b 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -909,7 +909,7 @@ def createCellCenterAttributeDataSet( block: vtkDataSet, cellCenterAttributeName Defaults to None, an internal logger is used. Raises: - TypeError: Error with the type of the mesh or the npArray values. + TypeError: Error with the type of the mesh. AttributeError: Error with the attribute cellCenterAttributeName. VTKError: Error with a VTK function. """ @@ -960,18 +960,48 @@ def createCellCenterAttributeDataSet( block: vtkDataSet, cellCenterAttributeName return -def transferPointDataToCellData( mesh: vtkPointSet ) -> vtkPointSet: +def transferPointDataToCellData( mesh: vtkPointSet, logger: Union[ Logger, Any ] = None, ) -> vtkPointSet: """Transfer point data to cell data. Args: mesh (vtkPointSet): Input mesh. + 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. + VTKError: Error with a VTK function. Returns: vtkPointSet: Output mesh where point data were transferred to cells. """ - filter = vtkPointDataToCellData() - filter.SetInputDataObject( mesh ) - filter.SetProcessAllArrays( True ) - filter.Update() - return filter.GetOutputDataObject( 0 ) + if logger is None: + logger = getLogger( "transferPointDataToCellData", True ) + + if not isinstance( mesh, vtkPointSet ): + raise TypeError( "Input mesh has to be inherited from vtkPointSet." ) + + 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: + pointToCellFilter = vtkPointDataToCellData() + pointToCellFilter.SetInputDataObject( mesh ) + pointToCellFilter.SetProcessAllArrays( True ) + pointToCellFilter.Update() + + capturedLog.seek( 0 ) + captured = capturedLog.read().decode() + + if captured != "": + vtkErrorLogger.error( captured.strip() ) + + output: vtkPointSet = pointToCellFilter.GetOutputDataObject( 0 ) + if output is None: + raise VTKError( "Something went wrong with VTK pointData to cellData filter." ) + + return output From 445dae3290145dcaf7448220df3256b92c759bb9 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Dec 2025 19:15:48 +0100 Subject: [PATCH 15/17] Clean ruff and yapf --- .../src/geos/mesh/utils/arrayModifiers.py | 65 +++++-- geos-mesh/tests/test_arrayModifiers.py | 158 +++++++++--------- 2 files changed, 128 insertions(+), 95 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index e4565e6b..aae032d2 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -26,10 +26,7 @@ vtkCellCenters, vtkPointDataToCellData, ) -from vtkmodules.vtkCommonCore import ( - vtkDataArray, - vtkPoints, vtkLogger -) +from vtkmodules.vtkCommonCore import ( vtkDataArray, vtkPoints, vtkLogger ) from geos.mesh.utils.arrayHelpers import ( getComponentNames, getComponentNamesDataSet, @@ -47,7 +44,6 @@ from geos.mesh.utils.multiblockHelpers import getBlockElementIndexesFlatten from geos.utils.Errors import VTKError - __doc__ = """ ArrayModifiers contains utilities to process VTK Arrays objects. @@ -157,7 +153,8 @@ def fillPartialAttributes( for blockIndex in elementaryBlockIndexes: dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) if not isAttributeInObjectDataSet( dataSet, attributeName, onPoints ): - createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, onPoints, vtkDataType, logger ) + createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, onPoints, vtkDataType, + logger ) return @@ -198,7 +195,11 @@ def fillAllPartialAttributes( infoAttributes: dict[ str, int ] = getAttributesWithNumberOfComponents( multiBlockDataSet, onPoints ) for attributeName in infoAttributes: if not isAttributeGlobal( multiBlockDataSet, attributeName, onPoints ): - fillPartialAttributes( multiBlockDataSet, attributeName, onPoints=onPoints, logger=logger, fillAll=True ) + fillPartialAttributes( multiBlockDataSet, + attributeName, + onPoints=onPoints, + logger=logger, + fillAll=True ) return @@ -276,7 +277,8 @@ def createConstantAttribute( # Deals with multiBlocksDataSets. if isinstance( mesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): - createConstantAttributeMultiBlock( mesh, listValues, attributeName, componentNames, onPoints, vtkDataType, logger ) + createConstantAttributeMultiBlock( mesh, listValues, attributeName, componentNames, onPoints, vtkDataType, + logger ) # Deals with dataSets. elif isinstance( mesh, vtkDataSet ): @@ -337,7 +339,8 @@ def createConstantAttributeMultiBlock( elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSet ) for blockIndex in elementaryBlockIndexes: dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) - createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, onPoints, vtkDataType, logger ) + createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, onPoints, vtkDataType, + logger ) return @@ -686,20 +689,23 @@ def transferAttributeToDataSetWithElementMap( raise TypeError( "The mesh to 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 })." ) + raise ValueError( + f"The map is incomplete, there is no data for the final mesh (flat index { flatIdDataSetTo })." ) nbElementsTo: int = dataSetTo.GetNumberOfPoints() if onPoints 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." ) + 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 mesh from has to be inherited from vtkDataSet or vtkMultiBlockDataSet." ) if not isAttributeInObject( meshFrom, attributeName, onPoints ): - raise AttributeError( f"The attribute { attributeName } is not in the mesh from." ) + raise AttributeError( f"The attribute { attributeName } is not in the mesh from." ) if isinstance( meshFrom, vtkMultiBlockDataSet ) and not isAttributeGlobal( meshFrom, attributeName, onPoints ): - raise AttributeError( f"The attribute { attributeName } must be global in the mesh from." ) + raise AttributeError( f"The attribute { attributeName } must be global in the mesh from." ) componentNames: tuple[ str, ...] = getComponentNames( meshFrom, attributeName, onPoints ) nbComponents: int = len( componentNames ) @@ -743,7 +749,13 @@ def transferAttributeToDataSetWithElementMap( arrayTo[ idElementTo ] = valueToTransfer - createAttribute( dataSetTo, arrayTo, attributeName, componentNames, onPoints=onPoints, vtkDataType=vtkDataType, logger=logger ) + createAttribute( dataSetTo, + arrayTo, + attributeName, + componentNames, + onPoints=onPoints, + vtkDataType=vtkDataType, + logger=logger ) return @@ -800,7 +812,13 @@ def transferAttributeWithElementMap( listFlatIdDataSetTo: list[ int ] = getBlockElementIndexesFlatten( meshTo ) for flatIdDataSetTo in listFlatIdDataSetTo: dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( meshTo.GetDataSet( flatIdDataSetTo ) ) - transferAttributeToDataSetWithElementMap( meshFrom, dataSetTo, elementMap, attributeName, onPoints, flatIdDataSetTo=flatIdDataSetTo, logger=logger ) + transferAttributeToDataSetWithElementMap( meshFrom, + dataSetTo, + elementMap, + attributeName, + onPoints, + flatIdDataSetTo=flatIdDataSetTo, + logger=logger ) else: raise TypeError( "The mesh to has to be inherited from vtkDataSet or vtkMultiBlockDataSet." ) @@ -867,7 +885,11 @@ def renameAttribute( return -def createCellCenterAttribute( mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ], cellCenterAttributeName: str, logger: Union[ Logger, Any ] = None, ) -> None: +def createCellCenterAttribute( + mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ], + cellCenterAttributeName: str, + logger: Union[ Logger, Any ] = None, +) -> None: """Create cellElementCenter attribute if it does not exist. Args: @@ -899,7 +921,11 @@ def createCellCenterAttribute( mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ], return -def createCellCenterAttributeDataSet( block: vtkDataSet, cellCenterAttributeName: str, logger: Union[ Logger, Any ] = None, ) -> None: +def createCellCenterAttributeDataSet( + block: vtkDataSet, + cellCenterAttributeName: str, + logger: Union[ Logger, Any ] = None, +) -> None: """Create cellElementCenter attribute in a vtkDataSet if it does not exist. Args: @@ -960,7 +986,10 @@ def createCellCenterAttributeDataSet( block: vtkDataSet, cellCenterAttributeName return -def transferPointDataToCellData( mesh: vtkPointSet, logger: Union[ Logger, Any ] = None, ) -> vtkPointSet: +def transferPointDataToCellData( + mesh: vtkPointSet, + logger: Union[ Logger, Any ] = None, +) -> vtkPointSet: """Transfer point data to cell data. Args: diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index 59a0791c..1cd20ddb 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -81,7 +81,10 @@ def test_fillPartialAttributes( """Test filling a partial attribute from a multiblock with values.""" multiBlockDataSetTest: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) # Fill the attribute in the multiBlockDataSet. - arrayModifiers.fillPartialAttributes( multiBlockDataSetTest, attributeName, onPoints=onPoints, listValues=listValues ) + arrayModifiers.fillPartialAttributes( multiBlockDataSetTest, + attributeName, + onPoints=onPoints, + listValues=listValues ) # Get the dataSet where the attribute has been filled. dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTest.GetDataSet( idBlock ) ) @@ -124,28 +127,26 @@ def test_fillPartialAttributes( assert vtkDataTypeFilled == vtkDataTypeTest -def test_fillPartialAttributesTypeError( - dataSetTest: vtkDataSet, -) -> None: +def test_fillPartialAttributesTypeError( dataSetTest: vtkDataSet, ) -> None: """Test the raises TypeError for the function fillPartialAttributes with a wrong mesh type.""" mesh: vtkDataSet = dataSetTest( "dataset" ) with pytest.raises( TypeError ): arrayModifiers.fillPartialAttributes( mesh, "PORO" ) -def test_fillPartialAttributesValueError( - dataSetTest: vtkMultiBlockDataSet, -) -> None: +def test_fillPartialAttributesValueError( dataSetTest: vtkMultiBlockDataSet, ) -> None: """Test the raises ValueError for the function fillPartialAttributes with to many values for the attribute.""" mesh: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) with pytest.raises( ValueError ): - arrayModifiers.fillPartialAttributes( mesh, "PORO", listValues=[ 42, 42] ) + arrayModifiers.fillPartialAttributes( mesh, "PORO", listValues=[ 42, 42 ] ) -@pytest.mark.parametrize( "attributeName", [ - ( "newAttribute" ), # The attribute is not in the mesh - ( "GLOBAL_IDS_CELLS" ), # The attribute is already global -] ) +@pytest.mark.parametrize( + "attributeName", + [ + ( "newAttribute" ), # The attribute is not in the mesh + ( "GLOBAL_IDS_CELLS" ), # The attribute is already global + ] ) def test_fillPartialAttributesAttributeError( dataSetTest: vtkMultiBlockDataSet, attributeName: str, @@ -156,9 +157,7 @@ def test_fillPartialAttributesAttributeError( arrayModifiers.fillPartialAttributes( mesh, attributeName ) -def test_FillAllPartialAttributes( - dataSetTest: vtkMultiBlockDataSet, -) -> None: +def test_FillAllPartialAttributes( dataSetTest: vtkMultiBlockDataSet, ) -> None: """Test to fill all the partial attributes of a vtkMultiBlockDataSet with a value.""" multiBlockDataSetTest: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) arrayModifiers.fillAllPartialAttributes( multiBlockDataSetTest ) @@ -175,9 +174,7 @@ def test_FillAllPartialAttributes( assert attributeExist == 1 -def test_fillAllPartialAttributesTypeError( - dataSetTest: vtkDataSet, -) -> None: +def test_fillAllPartialAttributesTypeError( dataSetTest: vtkDataSet, ) -> None: """Test the raises TypeError for the function fillAllPartialAttributes with a wrong mesh type.""" mesh: vtkDataSet = dataSetTest( "dataset" ) with pytest.raises( TypeError ): @@ -209,7 +206,7 @@ def test_createEmptyAttribute( def test_createEmptyAttributeValueError() -> None: """Test the raises ValueError for the function createEmptyAttribute with a wrong vtkDataType.""" with pytest.raises( ValueError ): - newAttr: vtkDataArray = arrayModifiers.createEmptyAttribute( "newAttribute", (), 64 ) + arrayModifiers.createEmptyAttribute( "newAttribute", (), 64 ) @pytest.mark.parametrize( @@ -242,18 +239,14 @@ def test_createConstantAttributeMultiBlock( assert attributeWellCreated == 1 -def test_createConstantAttributeMultiBlockRaiseTypeError( - dataSetTest: vtkDataSet, -) -> None: +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: +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 ): @@ -319,7 +312,8 @@ def test_createConstantAttributeDataSet( dataSet: vtkDataSet = dataSetTest( "dataset" ) # Create the new constant attribute in the dataSet. - arrayModifiers.createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, onPoints, vtkDataType ) + arrayModifiers.createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, onPoints, + vtkDataType ) # Get the created attribute. data: Union[ vtkPointData, vtkCellData ] @@ -357,10 +351,12 @@ def test_createConstantAttributeDataSet( assert vtkDataTypeCreated == vtkDataTypeTest -@pytest.mark.parametrize( "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 -] ) +@pytest.mark.parametrize( + "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 + ] ) def test_createConstantAttributeDataSetRaiseTypeError( dataSetTest: vtkDataSet, listValues: list[ Any ], @@ -372,9 +368,7 @@ def test_createConstantAttributeDataSetRaiseTypeError( arrayModifiers.createConstantAttributeDataSet( mesh, listValues, "newAttribute", vtkDataType=vtkDataType ) -def test_createConstantAttributeDataSetRaiseValueError( - dataSetTest: vtkDataSet, -) -> None: +def test_createConstantAttributeDataSetRaiseValueError( dataSetTest: vtkDataSet, ) -> None: """Test the raises ValueError for the function createConstantAttributeDataSet with a wrong vtkDataType.""" mesh: vtkDataSet = dataSetTest( "dataset" ) with pytest.raises( ValueError ): @@ -467,10 +461,12 @@ def test_createAttribute( assert vtkDataTypeCreated == vtkDataTypeTest -@pytest.mark.parametrize( "meshName, arrayType", [ - ( "multiblock", "float64" ), # The input mesh has the wrong type - ( "dataset", "int32" ), # The input array has the wrong type (should be float64) -] ) +@pytest.mark.parametrize( + "meshName, arrayType", + [ + ( "multiblock", "float64" ), # The input mesh has the wrong type + ( "dataset", "int32" ), # The input array has the wrong type (should be float64) + ] ) def test_createAttributeRaiseTypeError( dataSetTest: Any, getArrayWithSpeTypeValue: npt.NDArray[ Any ], @@ -485,10 +481,12 @@ def test_createAttributeRaiseTypeError( arrayModifiers.createAttribute( mesh, npArray, attributeName, vtkDataType=VTK_DOUBLE ) -@pytest.mark.parametrize( "vtkDataType, nbElements", [ - ( 64, 1740 ), # The vtkDataType does not exist - ( VTK_DOUBLE, 1741 ), # The number of element of the array is wrong -] ) +@pytest.mark.parametrize( + "vtkDataType, nbElements", + [ + ( 64, 1740 ), # The vtkDataType does not exist + ( VTK_DOUBLE, 1741 ), # The number of element of the array is wrong + ] ) def test_createAttributeRaiseValueError( dataSetTest: Any, getArrayWithSpeTypeValue: npt.NDArray[ Any ], @@ -534,7 +532,8 @@ def test_copyAttribute( multiBlockDataSetTo: vtkMultiBlockDataSet = dataSetTest( "emptymultiblock" ) # Copy the attribute from the multiBlockDataSetFrom to the multiBlockDataSetTo. - arrayModifiers.copyAttribute( multiBlockDataSetFrom, multiBlockDataSetTo, attributeNameFrom, attributeNameTo, onPoints ) + arrayModifiers.copyAttribute( multiBlockDataSetFrom, multiBlockDataSetTo, attributeNameFrom, attributeNameTo, + onPoints ) # Parse the two multiBlockDataSet and test if the attribute has been copied. elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetFrom ) @@ -572,9 +571,7 @@ def test_copyAttributeTypeError( arrayModifiers.copyAttribute( meshFrom, meshTo, "PORO", "PORO" ) -def test_copyAttributeValueError( - dataSetTest: vtkMultiBlockDataSet, -) -> None: +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" ) @@ -582,10 +579,12 @@ def test_copyAttributeValueError( arrayModifiers.copyAttribute( meshFrom, meshTo, "PORO", "PORO" ) -@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( + "attributeNameFrom, attributeNameTo", + [ + ( "PORO", "PORO" ), # An attribute PORO is already present in the mesh to + ( "newAttribute", "newAttribute" ), # newAttribute is not in the mesh from + ] ) def test_copyAttributeAttributeError( dataSetTest: vtkMultiBlockDataSet, attributeNameFrom: str, @@ -649,9 +648,7 @@ def test_copyAttributeDataSet( assert vtkDataTypeCopied == vtkDataTypeTest -def test_copyAttributeDataSetTypeError( - dataSetTest: Any, -) -> None: +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" ) @@ -659,9 +656,7 @@ def test_copyAttributeDataSetTypeError( arrayModifiers.copyAttributeDataSet( meshFrom, meshTo, "PORO", "PORO" ) -def test_copyAttributeDataSetAttributeError( - dataSetTest: vtkDataSet, -) -> None: +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" ) @@ -669,10 +664,12 @@ def test_copyAttributeDataSetAttributeError( arrayModifiers.copyAttributeDataSet( meshFrom, meshTo, "newAttribute", "newAttribute" ) -@pytest.mark.parametrize( "isMeshFrom, meshToName", [ - ( True, "emptymultiblock" ), # The mesh to is not a vtkDataSet. - ( False, "emptyFracture" ), # The mesh from is not a mesh. -] ) +@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 ] ], @@ -680,21 +677,19 @@ def test_transferAttributeToDataSetWithElementMapTypeError( meshToName: str, ) -> None: """Test the raises TypeError for the function transferAttributeToDataSetWithElementMap.""" - meshFrom: Union[ bool, vtkMultiBlockDataSet ] - if isMeshFrom: - meshFrom = dataSetTest( "multiblock" ) - else: - meshFrom = False + 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, False ) with pytest.raises( TypeError ): arrayModifiers.transferAttributeToDataSetWithElementMap( meshFrom, meshTo, elementMap, "FAULT", False ) -@pytest.mark.parametrize( "attributeName", [ - ( "PORO" ), # The attribute is partial. - ( "newAttribute" ), # The attribute is not in the mesh from. -] ) +@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 ] ], @@ -708,10 +703,12 @@ def test_transferAttributeToDataSetWithElementMapAttributeError( arrayModifiers.transferAttributeToDataSetWithElementMap( meshFrom, meshTo, elementMap, attributeName, False ) -@pytest.mark.parametrize( "meshToNameTransfer, meshToNameMap, flatIdDataSetTo", [ - ( "emptyFracture", "emptymultiblock", 0 ), # The map is wrong. - ( "emptyFracture", "emptyFracture", 1 ), # The flatIdDataSetTo is wrong. -] ) +@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 ] ], @@ -724,7 +721,12 @@ def test_transferAttributeToDataSetWithElementMapValueError( 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 ) + arrayModifiers.transferAttributeToDataSetWithElementMap( meshFrom, + meshTo, + elementMap, + "FAULT", + False, + flatIdDataSetTo=flatIdDataSetTo ) @pytest.mark.parametrize( "meshFromName, meshToName, attributeName, onPoints, defaultValueTest", [ @@ -863,10 +865,12 @@ def test_renameAttributeTypeError() -> None: arrayModifiers.renameAttribute( False, "PORO", "newName", False ) -@pytest.mark.parametrize( "attributeName, newName", [ - ( "newName", "newName" ), # The attribute is not in the mesh. - ( "PORO", "PORO" ), # The new name is already an attribute in the mesh. -] ) +@pytest.mark.parametrize( + "attributeName, newName", + [ + ( "newName", "newName" ), # The attribute is not in the mesh. + ( "PORO", "PORO" ), # The new name is already an attribute in the mesh. + ] ) def test_renameAttributeAttributeError( dataSetTest: vtkDataSet, attributeName: str, From deee0b5d7a5b0fba83b202c619e71acef4de7c0e Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Dec 2025 19:21:55 +0100 Subject: [PATCH 16/17] Clean ruff and Yapf --- .../AttributeMapping.py | 3 +- .../CreateConstantAttributePerRegion.py | 28 ++++++++++++++++--- .../FillPartialArrays.py | 6 +++- .../post_processing/GeosBlockMerge.py | 5 +++- .../post_processing/SurfaceGeomechanics.py | 13 +++++++-- .../geos/mesh_doctor/actions/generateCube.py | 6 +++- 6 files changed, 51 insertions(+), 10 deletions(-) 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 7567ea9d..b6a6cc04 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -178,7 +178,8 @@ def applyFilter( self: Self ) -> bool: raise ValueError( f"The two meshes do not have any shared { self.piece }." ) for attributeName in self.attributeNames: - transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, self.onPoints, self.logger ) + transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, + self.onPoints, self.logger ) # Log the output message. self._logOutputMessage() 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 9a466383..a8386f70 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -201,7 +201,12 @@ def applyFilter( self: Self ) -> bool: self.logger.warning( f"The region indexes entered are not in the region attribute { self.regionName }." ) - createConstantAttributeMultiBlock( self.mesh, self.defaultValue, self.newAttributeName, componentNames=self.componentNames, onPoints=self.onPoints, logger=self.logger ) + createConstantAttributeMultiBlock( self.mesh, + self.defaultValue, + self.newAttributeName, + componentNames=self.componentNames, + onPoints=self.onPoints, + logger=self.logger ) else: if len( invalidIndexes ) > 0: @@ -216,7 +221,12 @@ def applyFilter( self: Self ) -> bool: regionArray = getArrayInObject( dataSet, self.regionName, self.onPoints ) newArray = self._createArrayFromRegionArrayWithValueMap( regionArray ) - createAttribute( dataSet, newArray, self.newAttributeName, componentNames=self.componentNames, onPoints=self.onPoints, logger=self.logger ) + createAttribute( dataSet, + newArray, + self.newAttributeName, + componentNames=self.componentNames, + onPoints=self.onPoints, + logger=self.logger ) else: validIndexes, invalidIndexes = checkValidValuesInDataSet( self.mesh, self.regionName, listIndexes, @@ -228,7 +238,12 @@ def applyFilter( self: Self ) -> bool: self.logger.warning( f"The region indexes entered are not in the region attribute { self.regionName }." ) - createConstantAttributeDataSet( self.mesh, self.defaultValue, self.newAttributeName, componentNames=self.componentNames, onPoints=self.onPoints, logger=self.logger ) + createConstantAttributeDataSet( self.mesh, + self.defaultValue, + self.newAttributeName, + componentNames=self.componentNames, + onPoints=self.onPoints, + logger=self.logger ) else: if len( invalidIndexes ) > 0: @@ -238,7 +253,12 @@ def applyFilter( self: Self ) -> bool: regionArray = getArrayInObject( self.mesh, self.regionName, self.onPoints ) newArray = self._createArrayFromRegionArrayWithValueMap( regionArray ) - createAttribute( self.mesh, newArray, self.newAttributeName, componentNames=self.componentNames, onPoints=self.onPoints, logger=self.logger ) + createAttribute( self.mesh, + newArray, + self.newAttributeName, + componentNames=self.componentNames, + onPoints=self.onPoints, + logger=self.logger ) # Log the output message. self._logOutputMessage( validIndexes ) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py index f0da89ae..6db6be4c 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py @@ -126,7 +126,11 @@ def applyFilter( self: Self ) -> bool: f"There is two attribute named { attributeName }, one on points and the other on cells. The attribute name must be unique." ) - fillPartialAttributes( self.multiBlockDataSet, attributeName, onPoints=onPoints, listValues=self.dictAttributesValues[ attributeName ], logger=self.logger ) + fillPartialAttributes( self.multiBlockDataSet, + attributeName, + onPoints=onPoints, + listValues=self.dictAttributesValues[ attributeName ], + logger=self.logger ) self.logger.info( f"The filter { self.logger.name } succeed." ) except ( ValueError, AttributeError ) as e: diff --git a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py index 85997d67..0902c016 100644 --- a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py +++ b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py @@ -157,7 +157,10 @@ def applyFilter( self: Self ) -> bool: logger=self.logger ) # Create index attribute keeping the index in initial mesh - createConstantAttribute( volumeMesh, [ blockIndex ], PostProcessingOutputsEnum.BLOCK_INDEX.attributeName, onPoints=False, logger=self.logger ) + createConstantAttribute( volumeMesh, [ blockIndex ], + PostProcessingOutputsEnum.BLOCK_INDEX.attributeName, + onPoints=False, + logger=self.logger ) # Rename attributes self.renameAttributes( volumeMesh ) diff --git a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py index e94ea2ad..9198459f 100644 --- a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py +++ b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py @@ -281,7 +281,12 @@ def convertAttributesFromLocalToXYZBasis( self: Self ) -> None: arrayXYZ: npt.NDArray[ np.float64 ] = self.__computeXYZCoordinates( localArray ) # Create converted attribute array in dataset - createAttribute( self.outputMesh, arrayXYZ, attrNameXYZ, ComponentNameEnum.XYZ.value, onPoints=self.attributeOnPoints, logger=self.logger ) + createAttribute( self.outputMesh, + arrayXYZ, + attrNameXYZ, + ComponentNameEnum.XYZ.value, + onPoints=self.attributeOnPoints, + logger=self.logger ) self.logger.info( f"Attribute {attrNameXYZ} added to the output mesh." ) self.newAttributeNames.add( attrNameXYZ ) @@ -368,7 +373,11 @@ def computeShearCapacityUtilization( self: Self ) -> None: raise # Create attribute - createAttribute( self.outputMesh, scuAttribute, SCUAttributeName, (), self.attributeOnPoints, logger=self.logger ) + createAttribute( self.outputMesh, + scuAttribute, + SCUAttributeName, (), + self.attributeOnPoints, + logger=self.logger ) self.logger.info( "SCU computed and added to the output mesh." ) self.newAttributeNames.add( SCUAttributeName ) diff --git a/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py b/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py index a37b3e9f..c2866af0 100644 --- a/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py +++ b/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py @@ -184,7 +184,11 @@ def addFields( mesh: vtkUnstructuredGrid, fields: Iterable[ FieldInfo ] ) -> vtk # Create list of values (all 1.0) for each component listValues = [ 1.0 ] * fieldInfo.dimension # Use the robust createConstantAttributeDataSet function - createConstantAttributeDataSet( dataSet=mesh, listValues=listValues, attributeName=fieldInfo.name, onPoints=onPoints, logger=setupLogger ) + createConstantAttributeDataSet( dataSet=mesh, + listValues=listValues, + attributeName=fieldInfo.name, + onPoints=onPoints, + logger=setupLogger ) return mesh From 8cf903796ca3bcd5c5674a75861237a6ebb9f69b Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 5 Dec 2025 14:58:36 +0100 Subject: [PATCH 17/17] Fix the ci --- .../test_CreateConstantAttributePerRegion.py | 52 +++++++------------ 1 file changed, 18 insertions(+), 34 deletions(-) diff --git a/geos-processing/tests/test_CreateConstantAttributePerRegion.py b/geos-processing/tests/test_CreateConstantAttributePerRegion.py index 388fee0c..9068a924 100644 --- a/geos-processing/tests/test_CreateConstantAttributePerRegion.py +++ b/geos-processing/tests/test_CreateConstantAttributePerRegion.py @@ -103,35 +103,26 @@ def test_CreateConstantAttributePerRegion( @pytest.mark.parametrize( - "meshType, newAttributeName, regionName, dictRegionValues, componentNames, componentNamesTest, valueNpType", + "meshType, newAttributeName, regionName", [ - ( "dataset", "newAttribute", "PERM", {}, (), (), np.float32 ), # Region attribute has too many components - ( "multiblock", "newAttribute", "FAULT", {}, (), (), np.float32 ), # Region attribute is partial. + ( "dataset", "newAttribute", "PERM" ), # Region attribute has too many components + ( "multiblock", "newAttribute", "FAULT" ), # Region attribute is partial. + ( "dataset", "PERM", "FAULT" ), # The attribute name already exist in the mesh. ] ) def test_CreateConstantAttributePerRegionRaisesAttributeError( dataSetTest: Union[ vtkMultiBlockDataSet, vtkDataSet ], meshType: str, newAttributeName: str, regionName: str, - dictRegionValues: dict[ Any, Any ], - componentNames: tuple[ str, ...], - componentNamesTest: tuple[ str, ...], - valueNpType: int, ) -> None: - """Test tes fails of CreateConstantAttributePerRegion with attributes issues.""" + """Test the fails of CreateConstantAttributePerRegion with attributes issues.""" mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ] = dataSetTest( meshType ) - nbComponents: int = len( componentNamesTest ) - if nbComponents == 0: # If the attribute has one component, the component has no name. - nbComponents += 1 createConstantAttributePerRegionFilter: CreateConstantAttributePerRegion = CreateConstantAttributePerRegion( mesh, regionName, - dictRegionValues, + {}, newAttributeName, - valueNpType=valueNpType, - nbComponents=nbComponents, - componentNames=componentNames, ) with pytest.raises( AttributeError ): @@ -139,44 +130,37 @@ def test_CreateConstantAttributePerRegionRaisesAttributeError( @pytest.mark.parametrize( - "meshType, newAttributeName, regionName, dictRegionValues, componentNames, componentNamesTest, valueNpType", + "dictRegionValues, componentNames", [ - ( "dataset", "newAttribute", "FAULT", { + ( { 0: [ 0 ], 100: [ 1, 1 ], - }, (), (), np.float32 ), # Number of value inconsistent. - ( "dataset", "newAttribute", "FAULT", { + }, () ), # Number of value inconsistent. + ( { 0: [ 0, 0 ], 100: [ 1, 1 ], - }, (), (), np.float32 ), # More values than components. - ( "dataset", "newAttribute", "FAULT", { + }, () ), # More values than components. + ( { 0: [ 0 ], 100: [ 1 ], - }, ( "X", "Y" ), ( "X", "Y" ), np.float32 ), # More components than value. - ( "dataset", "PERM", "FAULT", {}, (), (), np.float32 ), # The attribute name already exist in the mesh. + }, ( "X", "Y" ) ), # More components than value. ] ) def test_CreateConstantAttributePerRegionRaisesValueError( - dataSetTest: Union[ vtkMultiBlockDataSet, vtkDataSet ], - meshType: str, - newAttributeName: str, - regionName: str, + dataSetTest: vtkDataSet, dictRegionValues: dict[ Any, Any ], componentNames: tuple[ str, ...], - componentNamesTest: tuple[ str, ...], - valueNpType: int, ) -> None: """Test the fails of CreateConstantAttributePerRegion with inputs value issues.""" - mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ] = dataSetTest( meshType ) - nbComponents: int = len( componentNamesTest ) + mesh: vtkDataSet = dataSetTest( 'dataset' ) + nbComponents: int = len( componentNames ) if nbComponents == 0: # If the attribute has one component, the component has no name. nbComponents += 1 createConstantAttributePerRegionFilter: CreateConstantAttributePerRegion = CreateConstantAttributePerRegion( mesh, - regionName, + "FAULT", dictRegionValues, - newAttributeName, - valueNpType=valueNpType, + "newAttribute", nbComponents=nbComponents, componentNames=componentNames, )