diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 33fc9370..2f17b369 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, @@ -25,10 +26,7 @@ vtkCellCenters, vtkPointDataToCellData, ) -from vtkmodules.vtkCommonCore import ( - vtkDataArray, - vtkPoints, -) +from vtkmodules.vtkCommonCore import ( vtkDataArray, vtkPoints, vtkLogger ) from geos.mesh.utils.arrayHelpers import ( getComponentNames, getComponentNamesDataSet, @@ -44,6 +42,7 @@ getNumberOfComponentsMultiBlock, ) from geos.mesh.utils.multiblockHelpers import getBlockElementIndexesFlatten +from geos.utils.Errors import VTKError from geos.utils.pieceEnum import Piece @@ -66,7 +65,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: @@ -84,8 +83,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: @@ -93,13 +94,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, piece ): + raise AttributeError( f"The attribute { attributeName } is not in the mesh." ) # Check if the attribute is partial. if isAttributeGlobal( multiBlockDataSet, attributeName, piece ): - 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, piece ) @@ -128,8 +131,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 @@ -138,7 +140,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 ] @@ -152,17 +154,17 @@ def fillPartialAttributes( elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSet ) for blockIndex in elementaryBlockIndexes: dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) - if not isAttributeInObjectDataSet( dataSet, attributeName, piece ) and \ - not createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, piece, vtkDataType, logger ): - return False + if not isAttributeInObjectDataSet( dataSet, attributeName, piece ): + createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, piece, 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: @@ -175,13 +177,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." ) @@ -190,11 +196,10 @@ def fillAllPartialAttributes( for piece in [ Piece.POINTS, Piece.CELLS ]: infoAttributes: dict[ str, int ] = getAttributesWithNumberOfComponents( multiBlockDataSet, piece ) for attributeName in infoAttributes: - if not isAttributeGlobal( multiBlockDataSet, attributeName, piece ) and \ - not fillPartialAttributes( multiBlockDataSet, attributeName, piece=piece, logger=logger, fillAll=True ): - return False + if not isAttributeGlobal( multiBlockDataSet, attributeName, piece ): + fillPartialAttributes( multiBlockDataSet, attributeName, piece=piece, logger=logger, fillAll=True ) - return True + return def createEmptyAttribute( @@ -209,12 +214,16 @@ def createEmptyAttribute( componentNames (tuple[str,...]): Name of the components for vectorial attributes. vtkDataType (int): Data type. + Raises: + ValueError: Error with the vtkDataType. + 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 ) @@ -236,7 +245,7 @@ def createConstantAttribute( piece: Piece = Piece.CELLS, vtkDataType: Union[ int, None ] = None, logger: Union[ Logger, None ] = None, -) -> bool: +) -> None: """Create a new attribute with a constant value in the mesh. Args: @@ -257,8 +266,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: Error with the type of the mesh. """ # Check if an external logger is given. if logger is None: @@ -266,13 +275,16 @@ def createConstantAttribute( # Deals with multiBlocksDataSets. if isinstance( mesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): - return createConstantAttributeMultiBlock( mesh, listValues, attributeName, componentNames, piece, vtkDataType, - logger ) + createConstantAttributeMultiBlock( mesh, listValues, attributeName, componentNames, piece, vtkDataType, logger ) # Deals with dataSets. elif isinstance( mesh, vtkDataSet ): - return createConstantAttributeDataSet( mesh, listValues, attributeName, componentNames, piece, vtkDataType, - logger ) + createConstantAttributeDataSet( mesh, listValues, attributeName, componentNames, piece, vtkDataType, logger ) + + else: + raise TypeError( "Input mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet." ) + + return def createConstantAttributeMultiBlock( @@ -283,7 +295,7 @@ def createConstantAttributeMultiBlock( piece: Piece = Piece.CELLS, 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: @@ -304,8 +316,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: Error with the type of the mesh. + AttributeError: Error with the attribute attributeName. """ # Check if an external logger is given. if logger is None: @@ -313,9 +326,7 @@ 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 the piece. if piece != Piece.POINTS and piece != Piece.CELLS: @@ -323,19 +334,15 @@ def createConstantAttributeMultiBlock( # Check if the attribute already exist in the input mesh. if isAttributeInObjectMultiBlockDataSet( multiBlockDataSet, attributeName, piece ): - 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 + raise AttributeError( f"The attribute { attributeName } is already present in the mesh." ) # Parse the multiBlockDataSet to create the constant attribute on each blocks. elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSet ) for blockIndex in elementaryBlockIndexes: dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) - if not createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, piece, vtkDataType, - logger ): - return False + createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, piece, vtkDataType, logger ) - return True + return def createConstantAttributeDataSet( @@ -346,7 +353,7 @@ def createConstantAttributeDataSet( piece: Piece = Piece.CELLS, 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: @@ -367,8 +374,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: Error with the type of the npArray values. + ValueError: Error with the vtkDataType. """ # Check if an external logger is given. if logger is None: @@ -383,9 +391,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 +407,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 ) @@ -421,7 +422,9 @@ def createConstantAttributeDataSet( else: npArray = np.array( [ listValues[ 0 ] for _ in range( nbElements ) ], valueType ) - return createAttribute( dataSet, npArray, attributeName, componentNames, piece, vtkDataType, logger ) + createAttribute( dataSet, npArray, attributeName, componentNames, piece, vtkDataType, logger ) + + return def createAttribute( @@ -432,8 +435,8 @@ def createAttribute( piece: Piece = Piece.CELLS, 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 +456,10 @@ 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: 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: @@ -466,15 +471,11 @@ 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, piece ): - 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 AttributeError( f"The attribute { attributeName } is already present in the mesh." ) # Check if an attribute with the same name exist on the opposite piece (points or cells) on the input mesh. oppositePiece: Piece = Piece.CELLS if piece == Piece.POINTS else Piece.POINTS @@ -485,17 +486,12 @@ def createAttribute( 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 @@ -508,9 +504,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 ) }." ) # Convert the numpy array int a vtkDataArray. createdAttribute: vtkDataArray = vnp.numpy_to_vtk( npArray, deep=True, array_type=vtkDataType ) @@ -539,7 +533,7 @@ def createAttribute( data.AddArray( createdAttribute ) data.Modified() - return True + return def copyAttribute( @@ -549,7 +543,7 @@ def copyAttribute( attributeNameTo: str, piece: Piece = Piece.CELLS, logger: Union[ Logger, None ] = None, -) -> bool: +) -> None: """Copy an attribute from a multiBlockDataSet to a similar one on the same piece. Args: @@ -562,8 +556,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: @@ -571,57 +567,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, piece ): - 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, piece ): - 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, piece ) and \ - not copyAttributeDataSet( dataSetFrom, dataSetTo, attributeNameFrom, attributeNameTo, piece, logger ): - return False + if isAttributeInObjectDataSet( dataSetFrom, attributeNameFrom, piece ): + copyAttributeDataSet( dataSetFrom, dataSetTo, attributeNameFrom, attributeNameTo, piece, logger ) - return True + return def copyAttributeDataSet( @@ -631,7 +605,7 @@ def copyAttributeDataSet( attributeNameTo: str, piece: Piece = Piece.CELLS, logger: Union[ Logger, Any ] = None, -) -> bool: +) -> None: """Copy an attribute from a dataSet to a similar one on the same piece. Args: @@ -644,8 +618,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: @@ -653,33 +628,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, piece ): - 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, piece ): - 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, piece ) componentNames: tuple[ str, ...] = getComponentNamesDataSet( dataSetFrom, attributeNameFrom, piece ) vtkArrayType: int = getVtkArrayTypeInObject( dataSetFrom, attributeNameFrom, piece ) - return createAttribute( dataSetTo, npArray, attributeNameTo, componentNames, piece, vtkArrayType, logger ) + createAttribute( dataSetTo, npArray, attributeNameTo, componentNames, piece, vtkArrayType, logger ) + + return def transferAttributeToDataSetWithElementMap( @@ -690,7 +651,7 @@ def transferAttributeToDataSetWithElementMap( piece: Piece, 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. @@ -718,8 +679,10 @@ 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: @@ -729,16 +692,27 @@ def transferAttributeToDataSetWithElementMap( if piece not in [ Piece.POINTS, Piece.CELLS ]: raise ValueError( f"The attribute must be on { Piece.POINTS.value } or { Piece.CELLS.value }." ) + if not isinstance( dataSetTo, vtkDataSet ): + raise TypeError( "The 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 piece == Piece.POINTS 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, piece ): + raise AttributeError( f"The attribute { attributeName } is not in the mesh from." ) + + if isinstance( meshFrom, vtkMultiBlockDataSet ) and not isAttributeGlobal( meshFrom, attributeName, piece ): + raise AttributeError( f"The attribute { attributeName } must be global in the mesh from." ) componentNames: tuple[ str, ...] = getComponentNames( meshFrom, attributeName, piece ) nbComponents: int = len( componentNames ) @@ -752,6 +726,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 ] @@ -780,13 +756,15 @@ def transferAttributeToDataSetWithElementMap( arrayTo[ idElementTo ] = valueToTransfer - return createAttribute( dataSetTo, - arrayTo, - attributeName, - componentNames, - piece=piece, - vtkDataType=vtkDataType, - logger=logger ) + createAttribute( dataSetTo, + arrayTo, + attributeName, + componentNames, + piece=piece, + vtkDataType=vtkDataType, + logger=logger ) + + return def transferAttributeWithElementMap( @@ -796,7 +774,7 @@ def transferAttributeWithElementMap( attributeName: str, piece: Piece, 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. @@ -823,33 +801,34 @@ 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, - piece, - logger=logger ) + transferAttributeToDataSetWithElementMap( meshFrom, meshTo, elementMap, attributeName, piece, logger=logger ) elif isinstance( meshTo, vtkMultiBlockDataSet ): + if isAttributeInObjectMultiBlockDataSet( meshTo, attributeName, piece ): + 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, piece, 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, + piece, + flatIdDataSetTo=flatIdDataSetTo, + logger=logger ) + else: + raise TypeError( "The mesh to has to be inherited from vtkDataSet or vtkMultiBlockDataSet." ) + + return def renameAttribute( @@ -857,103 +836,214 @@ def renameAttribute( attributeName: str, newAttributeName: str, piece: Piece, -) -> 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. piece (Piece): The piece of the attribute. + logger (Union[Logger, None], optional): A logger to manage the output messages. + Defaults to None, an internal logger is used. - 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, piece ): + 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, piece ): + raise AttributeError( f"The attribute { attributeName } is not in the mesh." ) + + if isAttributeInObject( object, newAttributeName, piece ): + 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 if piece == Piece.POINTS: dim = 0 elif piece == Piece.CELLS: dim = 1 else: - raise ValueError( "The attribute to rename must be on points or on Cells." ) - filter = vtkArrayRename() - filter.SetInputData( object ) - filter.SetArrayName( dim, attributeName, newAttributeName ) - filter.Update() - object.ShallowCopy( filter.GetOutput() ) - else: - return False - return True + raise ValueError( + f"The attribute to rename must be on { Piece.POINTS.value } or on { Piece.CELLS.value }." ) + renameArrayFilter = vtkArrayRename() + renameArrayFilter.SetInputData( object ) + renameArrayFilter.SetArrayName( dim, attributeName, newAttributeName ) + renameArrayFilter.Update() + capturedLog.seek( 0 ) + captured = capturedLog.read().decode() -def createCellCenterAttribute( mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ], cellCenterAttributeName: str ) -> bool: - """Create elementCenter attribute if it does not exist. + 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, + 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, Piece.CELLS ): + 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. + 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, Piece.CELLS ): + raise AttributeError( f"The attribute { cellCenterAttributeName } in already in the mesh." ) + + vtkErrorLogger: Logger = logging.getLogger( f"{ logger.name } vtkError Logger" ) + vtkErrorLogger.setLevel( logging.INFO ) + vtkErrorLogger.addHandler( logger.handlers[ 0 ] ) + vtkErrorLogger.propagate = False + vtkLogger.SetStderrVerbosity( vtkLogger.VERBOSITY_ERROR ) + vtkErrorLogger.addFilter( RegexExceptionFilter() ) # will raise VTKError if captured VTK Error + with VTKCaptureLog() as capturedLog: # apply ElementCenter filter - 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 - - -def transferPointDataToCellData( mesh: vtkPointSet ) -> vtkPointSet: + cellCenterFilter: vtkCellCenters = vtkCellCenters() + cellCenterFilter.SetInputData( block ) + cellCenterFilter.Update() + + capturedLog.seek( 0 ) + captured = capturedLog.read().decode() + + if captured != "": + vtkErrorLogger.error( captured.strip() ) + + output: vtkPointSet = cellCenterFilter.GetOutputDataObject( 0 ) + if output is None: + raise VTKError( "Something went wrong with VTK cell center filter." ) + + # transfer output to output arrays + centers: vtkPoints = output.GetPoints() + if centers is None: + raise VTKError( "Something went wrong with VTK cell center filter." ) + + centerCoords: vtkDataArray = centers.GetData() + if centerCoords is None: + raise VTKError( "Something went wrong with VTK cell center filter." ) + + centerCoords.SetName( cellCenterAttributeName ) + block.GetCellData().AddArray( centerCoords ) + block.Modified() + + return + + +def transferPointDataToCellData( + mesh: vtkPointSet, + logger: Union[ Logger, Any ] = None, +) -> 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 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 7bd2c6f2..956a2815 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -84,10 +84,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, - piece=piece, - listValues=listValues ) + arrayModifiers.fillPartialAttributes( multiBlockDataSetTest, attributeName, piece=piece, listValues=listValues ) # Get the dataSet where the attribute has been filled. dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTest.GetDataSet( idBlock ) ) @@ -130,14 +127,40 @@ def test_fillPartialAttributes( assert vtkDataTypeFilled == vtkDataTypeTest -@pytest.mark.parametrize( "multiBlockDataSetName", [ "multiblock" ] ) -def test_FillAllPartialAttributes( +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, - multiBlockDataSetName: str, + 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, ) -> 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: @@ -151,6 +174,13 @@ 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" ), @@ -173,6 +203,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 ): + arrayModifiers.createEmptyAttribute( "newAttribute", (), 64 ) + + @pytest.mark.parametrize( "attributeName, piece", [ @@ -188,7 +224,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, piece=piece ) + arrayModifiers.createConstantAttributeMultiBlock( multiBlockDataSetTest, values, attributeName, piece=piece ) elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetTest ) for blockIndex in elementaryBlockIndexes: @@ -200,6 +236,20 @@ 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_createConstantAttributeMultiBlockRaiseAttributeError( dataSetTest: vtkMultiBlockDataSet, ) -> None: + """Test the raises AttributeError for the function createConstantAttributeMultiBlock with a wrong attributeName.""" + mesh: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) + with pytest.raises( AttributeError ): + arrayModifiers.createConstantAttributeMultiBlock( mesh, [ np.int32( 42 ) ], "PORO" ) + + @pytest.mark.parametrize( "listValues, componentNames, componentNamesTest, piece, vtkDataType, vtkDataTypeTest, attributeName", [ @@ -256,8 +306,8 @@ def test_createConstantAttributeDataSet( dataSet: vtkDataSet = dataSetTest( "dataset" ) # Create the new constant attribute in the dataSet. - assert arrayModifiers.createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, piece, - vtkDataType ) + arrayModifiers.createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, piece, + vtkDataType ) # Get the created attribute. data: Union[ vtkPointData, vtkCellData ] @@ -295,6 +345,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, piece, vtkDataType, vtkDataTypeTest, valueType, attributeName", [ @@ -357,7 +431,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, piece, vtkDataType ) + arrayModifiers.createAttribute( dataSet, npArrayTest, attributeName, componentNames, piece, vtkDataType ) # Get the created attribute. data: Union[ vtkPointData, vtkCellData ] @@ -381,6 +455,56 @@ 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( + "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 ], + 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, "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( "attributeNameFrom, attributeNameTo, piece", [ @@ -402,8 +526,8 @@ def test_copyAttribute( multiBlockDataSetTo: vtkMultiBlockDataSet = dataSetTest( "emptymultiblock" ) # Copy the attribute from the multiBlockDataSetFrom to the multiBlockDataSetTo. - assert arrayModifiers.copyAttribute( multiBlockDataSetFrom, multiBlockDataSetTo, attributeNameFrom, attributeNameTo, - piece ) + arrayModifiers.copyAttribute( multiBlockDataSetFrom, multiBlockDataSetTo, attributeNameFrom, attributeNameTo, + piece ) # Parse the two multiBlockDataSet and test if the attribute has been copied. elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetFrom ) @@ -424,6 +548,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, piece", [ ( "CellAttribute", "CellAttributeTo", Piece.CELLS ), ( "PointAttribute", "PointAttributeTo", Piece.POINTS ), @@ -439,7 +606,7 @@ def test_copyAttributeDataSet( dataSetTo: vtkDataSet = dataSetTest( "emptydataset" ) # Copy the attribute from the dataSetFrom to the dataSetTo. - assert arrayModifiers.copyAttributeDataSet( dataSetFrom, dataSetTo, attributeNameFrom, attributeNameTo, piece ) + arrayModifiers.copyAttributeDataSet( dataSetFrom, dataSetTo, attributeNameFrom, attributeNameTo, piece ) # Get the tested attribute and its copy. dataFrom: Union[ vtkPointData, vtkCellData ] @@ -475,6 +642,88 @@ 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( + "isMeshFrom, meshToName", + [ + ( True, "emptymultiblock" ), # The mesh to is not a vtkDataSet. + ( False, "emptyFracture" ), # The mesh from is not a mesh. + ] ) +def test_transferAttributeToDataSetWithElementMapTypeError( + dataSetTest: Any, + getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], + isMeshFrom: bool, + meshToName: str, +) -> None: + """Test the raises TypeError for the function transferAttributeToDataSetWithElementMap.""" + meshFrom: Union[ bool, vtkMultiBlockDataSet ] = dataSetTest( "multiblock" ) if isMeshFrom else False + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshToName ) + elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( "multiblock", meshToName, Piece.CELLS ) + with pytest.raises( TypeError ): + arrayModifiers.transferAttributeToDataSetWithElementMap( meshFrom, meshTo, elementMap, "FAULT", Piece.CELLS ) + + +@pytest.mark.parametrize( + "attributeName", + [ + ( "PORO" ), # The attribute is partial. + ( "newAttribute" ), # The attribute is not in the mesh from. + ] ) +def test_transferAttributeToDataSetWithElementMapAttributeError( + dataSetTest: vtkMultiBlockDataSet, + getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], + attributeName: str, +) -> None: + """Test the raises AttributeError for the function transferAttributeToDataSetWithElementMap.""" + meshFrom: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) + meshTo: vtkMultiBlockDataSet = dataSetTest( "emptyFracture" ) + elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( "multiblock", "emptyFracture", Piece.CELLS ) + with pytest.raises( AttributeError ): + arrayModifiers.transferAttributeToDataSetWithElementMap( meshFrom, meshTo, elementMap, attributeName, + Piece.CELLS ) + + +@pytest.mark.parametrize( + "meshToNameTransfer, meshToNameMap, flatIdDataSetTo", + [ + ( "emptyFracture", "emptymultiblock", 0 ), # The map is wrong. + ( "emptyFracture", "emptyFracture", 1 ), # The flatIdDataSetTo is wrong. + ] ) +def test_transferAttributeToDataSetWithElementMapValueError( + dataSetTest: vtkDataSet, + getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], + meshToNameTransfer: str, + meshToNameMap: str, + flatIdDataSetTo: int, +) -> None: + """Test the raises ValueError for the function transferAttributeToDataSetWithElementMap.""" + meshFrom: vtkDataSet = dataSetTest( "dataset" ) + meshTo: vtkDataSet = dataSetTest( meshToNameTransfer ) + elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( "dataset", meshToNameMap, False ) + with pytest.raises( ValueError ): + arrayModifiers.transferAttributeToDataSetWithElementMap( meshFrom, + meshTo, + elementMap, + "FAULT", + False, + flatIdDataSetTo=flatIdDataSetTo ) + + @pytest.mark.parametrize( "meshFromName, meshToName, attributeName, piece, defaultValueTest", [ ( "fracture", "emptyFracture", "collocated_nodes", Piece.POINTS, [ -1, -1 ] ), ( "multiblock", "emptyFracture", "FAULT", Piece.CELLS, -1 ), @@ -499,7 +748,7 @@ def test_transferAttributeWithElementMap( meshTo: Union[ vtkMultiBlockDataSet, vtkDataSet ] = dataSetTest( meshToName ) elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( meshFromName, meshToName, piece ) - assert arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, attributeName, piece ) + arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, attributeName, piece ) for flatIdDataSetTo in elementMap: dataTo: Union[ vtkPointData, vtkCellData ] @@ -528,6 +777,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", Piece.CELLS ) + with pytest.raises( TypeError ): + arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, "FAULT", Piece.CELLS ) + + +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", Piece.CELLS ) + with pytest.raises( AttributeError ): + arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, "FAULT", Piece.CELLS ) + + @pytest.mark.parametrize( "attributeName, piece", [ ( "CellAttribute", Piece.CELLS ), ( "PointAttribute", Piece.POINTS ), @@ -580,3 +853,26 @@ 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", Piece.CELLS ) + + +@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, Piece.CELLS ) 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 4fc389c8..1c8dbf40 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -181,10 +181,8 @@ def applyFilter( self: Self ) -> None: raise ValueError( f"The two meshes do not have any shared { self.piece.value }." ) for attributeName in self.attributeNames: - # TODO:: Modify arrayModifiers function to raise error. - if not transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, - self.piece, self.logger ): - raise ValueError( f"Fail to transfer the attribute { attributeName }." ) + transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, self.piece, + 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 4dca5cd8..572f1f09 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -155,7 +155,7 @@ def applyFilter( self: Self ) -> None: """Create a constant attribute per region in the mesh. Raises: - ValueError: Errors with the input value for the region index or errors during the creation of the new attribute. + ValueError: Errors with the input value for the region index. AttributeError: Errors with the attribute of the mesh. """ self.logger.info( f"Apply filter { self.logger.name }." ) @@ -203,14 +203,12 @@ def applyFilter( self: Self ) -> None: 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, - piece=self.piece, - logger=self.logger ): - raise ValueError( - f"Something went wrong with the creation of the attribute { self.newAttributeName }." ) + createConstantAttributeMultiBlock( self.mesh, + self.defaultValue, + self.newAttributeName, + componentNames=self.componentNames, + piece=self.piece, + logger=self.logger ) else: if len( invalidIndexes ) > 0: @@ -224,14 +222,12 @@ def applyFilter( self: Self ) -> None: regionArray = getArrayInObject( dataSet, self.regionName, self.piece ) newArray = self._createArrayFromRegionArrayWithValueMap( regionArray ) - if not createAttribute( dataSet, - newArray, - self.newAttributeName, - componentNames=self.componentNames, - piece=self.piece, - logger=self.logger ): - raise ValueError( - f"Something went wrong with the creation of the attribute { self.newAttributeName }." ) + createAttribute( dataSet, + newArray, + self.newAttributeName, + componentNames=self.componentNames, + piece=self.piece, + logger=self.logger ) else: validIndexes, invalidIndexes = checkValidValuesInDataSet( self.mesh, self.regionName, listIndexes, @@ -243,14 +239,12 @@ def applyFilter( self: Self ) -> None: 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, - piece=self.piece, - logger=self.logger ): - raise ValueError( - f"Something went wrong with the creation of the attribute { self.newAttributeName }." ) + createConstantAttributeDataSet( self.mesh, + self.defaultValue, + self.newAttributeName, + componentNames=self.componentNames, + piece=self.piece, + logger=self.logger ) else: if len( invalidIndexes ) > 0: @@ -259,14 +253,12 @@ def applyFilter( self: Self ) -> None: regionArray = getArrayInObject( self.mesh, self.regionName, self.piece ) newArray = self._createArrayFromRegionArrayWithValueMap( regionArray ) - if not createAttribute( self.mesh, - newArray, - self.newAttributeName, - componentNames=self.componentNames, - piece=self.piece, - logger=self.logger ): - raise ValueError( - f"Something went wrong with the creation of the attribute { self.newAttributeName }." ) + createAttribute( self.mesh, + newArray, + self.newAttributeName, + componentNames=self.componentNames, + piece=self.piece, + 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 5c592548..08b3356b 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py @@ -131,12 +131,11 @@ def applyFilter( self: Self ) -> None: 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, - piece=piece, - listValues=self.dictAttributesValues[ attributeName ], - logger=self.logger ): - raise ValueError( "Something went wrong with the filling of partial attributes" ) + fillPartialAttributes( self.multiBlockDataSet, + attributeName, + piece=piece, + listValues=self.dictAttributesValues[ attributeName ], + logger=self.logger ) self.logger.info( f"The filter { self.logger.name } succeed." ) diff --git a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py index f19e0d06..61e018bf 100644 --- a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py +++ b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py @@ -164,13 +164,10 @@ def applyFilter( self: Self ) -> None: logger=self.logger ) # Create index attribute keeping the index in initial mesh - if not createConstantAttribute( volumeMesh, [ blockIndex ], - PostProcessingOutputsEnum.BLOCK_INDEX.attributeName, - piece=Piece.CELLS, - logger=self.logger ): - raise ValueError( - f"Something went wrong during the creation of the attribute { PostProcessingOutputsEnum.BLOCK_INDEX.attributeName }." - ) + createConstantAttribute( volumeMesh, [ blockIndex ], + PostProcessingOutputsEnum.BLOCK_INDEX.attributeName, + piece=Piece.CELLS, + logger=self.logger ) # Rename attributes self.renameAttributes( volumeMesh ) @@ -208,9 +205,9 @@ def renameAttributes( if suffix == "_density": for phaseName in self.phaseNameDict[ PhaseTypeEnum.ROCK.type ]: if phaseName in attributeName: - renameAttribute( mesh, attributeName, newName, piece=Piece.CELLS ) + renameAttribute( mesh, attributeName, newName, Piece.CELLS, logger=self.logger ) else: - renameAttribute( mesh, attributeName, newName, piece=Piece.CELLS ) + renameAttribute( mesh, attributeName, newName, Piece.CELLS, logger=self.logger ) return diff --git a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py index c590e79f..377b9ba5 100644 --- a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py +++ b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py @@ -292,16 +292,14 @@ 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, - piece=self.attributePiece, - logger=self.logger ): - self.logger.info( f"Attribute {attrNameXYZ} added to the output mesh." ) - self.newAttributeNames.add( attrNameXYZ ) - else: - raise ValueError( f"Something went wrong during the creation of the attribute { attrNameXYZ }." ) + createAttribute( self.outputMesh, + arrayXYZ, + attrNameXYZ, + ComponentNameEnum.XYZ.value, + piece=self.attributePiece, + logger=self.logger ) + self.logger.info( f"Attribute {attrNameXYZ} added to the output mesh." ) + self.newAttributeNames.add( attrNameXYZ ) return @@ -387,13 +385,13 @@ def computeShearCapacityUtilization( self: Self ) -> None: self.frictionAngle ) # Create attribute - if not createAttribute( - self.outputMesh, scuAttribute, SCUAttributeName, (), self.attributePiece, logger=self.logger ): - self.logger.error( f"Failed to create attribute {SCUAttributeName}." ) - raise ValueError( f"Failed to create attribute {SCUAttributeName}." ) - else: - self.logger.info( "SCU computed and added to the output mesh." ) - self.newAttributeNames.add( SCUAttributeName ) + createAttribute( self.outputMesh, + scuAttribute, + SCUAttributeName, (), + self.attributePiece, + logger=self.logger ) + self.logger.info( "SCU computed and added to the output mesh." ) + self.newAttributeNames.add( SCUAttributeName ) return diff --git a/geos-processing/tests/test_CreateConstantAttributePerRegion.py b/geos-processing/tests/test_CreateConstantAttributePerRegion.py index d3a91088..fcdf9f2a 100644 --- a/geos-processing/tests/test_CreateConstantAttributePerRegion.py +++ b/geos-processing/tests/test_CreateConstantAttributePerRegion.py @@ -103,24 +103,26 @@ def test_CreateConstantAttributePerRegion( @pytest.mark.parametrize( - "meshType, regionName", + "meshType, newAttributeName, regionName", [ - ( "dataset", "PERM" ), # Region attribute has too many components - ( "multiblock", "FAULT" ), # 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, ) -> None: - """Test tes fails of CreateConstantAttributePerRegion with attributes issues.""" + """Test the fails of CreateConstantAttributePerRegion with attributes issues.""" mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ] = dataSetTest( meshType ) createConstantAttributePerRegionFilter: CreateConstantAttributePerRegion = CreateConstantAttributePerRegion( mesh, regionName, {}, - "newAttribute", + newAttributeName, ) with pytest.raises( AttributeError ): @@ -128,30 +130,28 @@ def test_CreateConstantAttributePerRegionRaisesAttributeError( @pytest.mark.parametrize( - "newAttributeName, dictRegionValues, componentNames", + "dictRegionValues, componentNames", [ - ( "newAttribute", { + ( { 0: [ 0 ], 100: [ 1, 1 ], }, () ), # Number of value inconsistent. - ( "newAttribute", { + ( { 0: [ 0, 0 ], 100: [ 1, 1 ], }, () ), # More values than components. - ( "newAttribute", { + ( { 0: [ 0 ], 100: [ 1 ], }, ( "X", "Y" ) ), # More components than value. - ( "PERM", {}, () ), # The attribute name already exist on the mesh on the same piece. ] ) def test_CreateConstantAttributePerRegionRaisesValueError( dataSetTest: vtkDataSet, - newAttributeName: str, dictRegionValues: dict[ Any, Any ], componentNames: tuple[ str, ...], ) -> None: """Test the fails of CreateConstantAttributePerRegion with inputs value issues.""" - mesh: vtkDataSet = dataSetTest( "dataset" ) + mesh: vtkDataSet = dataSetTest( 'dataset' ) nbComponents: int = len( componentNames ) if nbComponents == 0: # If the attribute has one component, the component has no name. nbComponents += 1 @@ -160,7 +160,7 @@ def test_CreateConstantAttributePerRegionRaisesValueError( mesh, "FAULT", dictRegionValues, - newAttributeName, + "newAttribute", nbComponents=nbComponents, componentNames=componentNames, ) 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 85e473dc..ee972080 100644 --- a/geos-pv/src/geos/pv/plugins/post_processing/PVGeosBlockExtractAndMerge.py +++ b/geos-pv/src/geos/pv/plugins/post_processing/PVGeosBlockExtractAndMerge.py @@ -307,7 +307,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() ) diff --git a/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py b/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py index 887bf1a0..13823096 100644 --- a/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py +++ b/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py @@ -185,13 +185,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 - success = createConstantAttributeDataSet( dataSet=mesh, - listValues=listValues, - attributeName=fieldInfo.name, - piece=piece, - logger=setupLogger ) - if not success: - setupLogger.warning( f"Failed to create field {fieldInfo.name}" ) + createConstantAttributeDataSet( dataSet=mesh, + listValues=listValues, + attributeName=fieldInfo.name, + piece=piece, + logger=setupLogger ) return mesh