diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 0874c354..aae032d2 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 __doc__ = """ ArrayModifiers contains utilities to process VTK Arrays objects. @@ -64,7 +63,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 +81,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 +92,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 +129,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 +138,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 ] @@ -150,17 +152,17 @@ 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 + 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: @@ -173,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." ) @@ -188,11 +194,14 @@ 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,12 +216,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 ) @@ -234,7 +247,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: @@ -255,8 +268,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: @@ -264,13 +277,17 @@ 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 ): - return createConstantAttributeDataSet( mesh, listValues, attributeName, componentNames, onPoints, vtkDataType, - logger ) + createConstantAttributeDataSet( mesh, listValues, attributeName, componentNames, onPoints, vtkDataType, logger ) + + else: + raise TypeError( "Input mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet." ) + + return def createConstantAttributeMultiBlock( @@ -281,7 +298,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 +319,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: @@ -311,35 +329,20 @@ 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 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, onPoints, - vtkDataType, logger ): - return False + createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, onPoints, vtkDataType, + logger ) - return True + return def createConstantAttributeDataSet( @@ -350,7 +353,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 +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 +387,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 +403,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 +418,9 @@ 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 def createAttribute( @@ -432,8 +431,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 +452,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: @@ -462,31 +463,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 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: 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 +494,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 +530,7 @@ def createAttribute( data.AddArray( createdAttribute ) data.Modified() - return True + return def copyAttribute( @@ -550,7 +540,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: @@ -563,8 +553,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: @@ -572,57 +564,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( @@ -632,7 +602,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: @@ -645,8 +615,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: @@ -654,33 +625,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 ) - return createAttribute( dataSetTo, npArray, attributeNameTo, componentNames, onPoints, vtkArrayType, logger ) + createAttribute( dataSetTo, npArray, attributeNameTo, componentNames, onPoints, vtkArrayType, logger ) + + return def transferAttributeToDataSetWithElementMap( @@ -691,7 +648,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. @@ -719,23 +676,36 @@ 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 ) @@ -749,6 +719,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 ] @@ -777,13 +749,15 @@ 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 def transferAttributeWithElementMap( @@ -793,7 +767,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. @@ -821,37 +795,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, - 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( @@ -859,97 +830,207 @@ 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." ) -def createCellCenterAttribute( mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ], cellCenterAttributeName: str ) -> bool: - """Create elementCenter attribute if it does not exist. + 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, 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. + 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 - - -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 0b2528d8..1cd20ddb 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -81,10 +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. - 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 +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: @@ -148,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" ), @@ -170,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, onPoints", [ @@ -188,10 +227,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 +239,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, onPoints, vtkDataType, vtkDataTypeTest, attributeName", [ @@ -262,8 +312,8 @@ 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 +351,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", [ @@ -363,7 +437,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 +461,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, onPoints", [ @@ -408,8 +532,8 @@ 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 ) @@ -430,6 +554,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 ), @@ -445,7 +612,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 ] @@ -481,6 +648,87 @@ 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, 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 ), @@ -505,7 +753,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 ] @@ -534,6 +782,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 ), @@ -585,3 +857,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", 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/generic_processing_tools/AttributeMapping.py b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py index 598eedc0..9c287d5b 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -183,10 +183,8 @@ def applyFilter( self: Self ) -> None: 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 ValueError( f"Fail to transfer the attribute { attributeName }." ) + 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 aa3dd5e0..397c9b49 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -156,7 +156,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 }." ) @@ -204,14 +204,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, - onPoints=self.onPoints, - 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, + onPoints=self.onPoints, + logger=self.logger ) else: if len( invalidIndexes ) > 0: @@ -225,14 +223,12 @@ def applyFilter( self: Self ) -> None: 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 ValueError( - f"Something went wrong with the creation of the attribute { self.newAttributeName }." ) + createAttribute( dataSet, + newArray, + self.newAttributeName, + componentNames=self.componentNames, + onPoints=self.onPoints, + logger=self.logger ) else: validIndexes, invalidIndexes = checkValidValuesInDataSet( self.mesh, self.regionName, listIndexes, @@ -244,14 +240,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, - onPoints=self.onPoints, - 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, + onPoints=self.onPoints, + logger=self.logger ) else: if len( invalidIndexes ) > 0: @@ -260,14 +254,12 @@ def applyFilter( self: Self ) -> None: 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 ValueError( - f"Something went wrong with the creation of the attribute { self.newAttributeName }." ) + 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 2851d033..854db09b 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py @@ -133,12 +133,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, - onPoints=onPoints, - listValues=self.dictAttributesValues[ attributeName ], - logger=self.logger ): - raise ValueError( "Something went wrong with the filling of partial attributes" ) + fillPartialAttributes( self.multiBlockDataSet, + attributeName, + onPoints=onPoints, + 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 7caab5a8..6e8e4774 100644 --- a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py +++ b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py @@ -163,13 +163,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, - onPoints=False, - 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, + onPoints=False, + logger=self.logger ) # Rename attributes self.renameAttributes( volumeMesh ) @@ -207,9 +204,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 diff --git a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py index 34923702..e2016c68 100644 --- a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py +++ b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py @@ -291,16 +291,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, - onPoints=self.attributeOnPoints, - 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, + onPoints=self.attributeOnPoints, + logger=self.logger ) + self.logger.info( f"Attribute {attrNameXYZ} added to the output mesh." ) + self.newAttributeNames.add( attrNameXYZ ) return @@ -386,12 +384,13 @@ def computeShearCapacityUtilization( self: Self ) -> None: self.frictionAngle ) # Create attribute - if not createAttribute( - self.outputMesh, scuAttribute, SCUAttributeName, (), self.attributeOnPoints, logger=self.logger ): - 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.attributeOnPoints, + 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 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, ) 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 0193d5ce..4321fd17 100644 --- a/geos-pv/src/geos/pv/plugins/post_processing/PVGeosBlockExtractAndMerge.py +++ b/geos-pv/src/geos/pv/plugins/post_processing/PVGeosBlockExtractAndMerge.py @@ -306,7 +306,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 9af53ddb..c2866af0 100644 --- a/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py +++ b/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py @@ -184,13 +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 - 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