diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 6bc763b3..639a4c7c 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -20,13 +20,18 @@ vtkDataObject, vtkPointData, vtkCellData, + vtkCell, ) from vtkmodules.vtkFiltersCore import ( vtkArrayRename, vtkCellCenters, vtkPointDataToCellData, ) -from vtkmodules.vtkCommonCore import ( vtkDataArray, vtkPoints, vtkLogger ) +from vtkmodules.vtkCommonCore import ( + vtkDataArray, + vtkPoints, + vtkLogger, +) from geos.mesh.utils.arrayHelpers import ( getComponentNames, getComponentNamesDataSet, @@ -155,8 +160,7 @@ def fillPartialAttributes( for blockIndex in elementaryBlockIndexes: dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) if not isAttributeInObjectDataSet( dataSet, attributeName, piece ): - createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, piece, vtkDataType, - logger ) + createConstantAttribute( dataSet, listValues, attributeName, componentNames, piece, vtkDataType, logger ) return @@ -267,7 +271,9 @@ def createConstantAttribute( Defaults to None, an internal logger is used. Raises: - TypeError: Error with the type of the mesh. + TypeError: Error with the type of the input mesh or the type of the input values or the vtkDataType. + AttributeError: The attribute is already on the mesh. + ValueError: Error with the vtkDataType (unknown) or with the piece. """ # Check if an external logger is given. if logger is None: @@ -275,154 +281,63 @@ def createConstantAttribute( # Deals with multiBlocksDataSets. if isinstance( mesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): - createConstantAttributeMultiBlock( mesh, listValues, attributeName, componentNames, piece, vtkDataType, logger ) + # Check if the attribute already exist in the input mesh. + if isAttributeInObjectMultiBlockDataSet( mesh, attributeName, piece ): + raise AttributeError( f"The attribute { attributeName } is already on the mesh." ) + + # Parse the multiBlockDataSet to create the constant attribute on each blocks. + elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( mesh ) + for blockIndex in elementaryBlockIndexes: + dataSet: vtkDataSet = vtkDataSet.SafeDownCast( mesh.GetDataSet( blockIndex ) ) + createConstantAttribute( dataSet, listValues, attributeName, componentNames, piece, vtkDataType, logger ) # Deals with dataSets. elif isinstance( mesh, vtkDataSet ): - createConstantAttributeDataSet( mesh, listValues, attributeName, componentNames, piece, vtkDataType, logger ) - - else: - raise TypeError( "Input mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet." ) - - return - - -def createConstantAttributeMultiBlock( - multiBlockDataSet: Union[ vtkMultiBlockDataSet, vtkCompositeDataSet ], - listValues: list[ Any ], - attributeName: str, - componentNames: tuple[ str, ...] = (), # noqa: C408 - piece: Piece = Piece.CELLS, - vtkDataType: Union[ int, None ] = None, - logger: Union[ Logger, None ] = None, -) -> None: - """Create a new attribute with a constant value per component on every block of the multiBlockDataSet. - - Args: - multiBlockDataSet (Union[vtkMultiBlockDataSet, vtkCompositeDataSet]): MultiBlockDataSet where to create the attribute. - listValues (list[Any]): List of values of the attribute for each components. It is recommended to use numpy scalar type for the values. - attributeName (str): Name of the attribute. - componentNames (tuple[str,...], optional): Name of the components for vectorial attributes. If one component, gives an empty tuple. - Defaults to an empty tuple. - piece (Piece): The piece of the attribute. - Defaults to Piece.CELLS - vtkDataType (Union[int, None], optional): Vtk data type of the attribute to create. - Defaults to None, the vtk data type is given by the type of the values. - - Warning with int8, uint8 and int64 type of value, the corresponding vtk data type are multiples. By default: - - int8 -> VTK_SIGNED_CHAR - - uint8 -> VTK_UNSIGNED_CHAR - - int64 -> VTK_LONG_LONG - logger (Union[Logger, None], optional): A logger to manage the output messages. - Defaults to None, an internal logger is used. - - Raises: - TypeError: Error with the type of the mesh. - AttributeError: Error with the attribute attributeName. - """ - # Check if an external logger is given. - if logger is None: - logger = getLogger( "createConstantAttributeMultiBlock", True ) - - # Check if the input mesh is inherited from vtkMultiBlockDataSet. - if not isinstance( multiBlockDataSet, vtkMultiBlockDataSet ): - raise TypeError( "Input mesh has to be inherited from vtkMultiBlockDataSet." ) - - # Check the piece. - if piece != Piece.POINTS and piece != Piece.CELLS: - raise ValueError( f"The attribute must be created on { Piece.POINTS.value } or { Piece.CELLS.value }." ) - - # Check if the attribute already exist in the input mesh. - if isAttributeInObjectMultiBlockDataSet( multiBlockDataSet, attributeName, piece ): - raise AttributeError( f"The attribute { attributeName } is already present in the mesh." ) - - # Parse the multiBlockDataSet to create the constant attribute on each blocks. - elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSet ) - for blockIndex in elementaryBlockIndexes: - dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) - createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, piece, vtkDataType, logger ) - - return - - -def createConstantAttributeDataSet( - dataSet: vtkDataSet, - listValues: list[ Any ], - attributeName: str, - componentNames: tuple[ str, ...] = (), # noqa: C408 - piece: Piece = Piece.CELLS, - vtkDataType: Union[ int, None ] = None, - logger: Union[ Logger, None ] = None, -) -> None: - """Create an attribute with a constant value per component in the dataSet. - - Args: - dataSet (vtkDataSet): DataSet where to create the attribute. - listValues (list[Any]): List of values of the attribute for each components. It is recommended to use numpy scalar type for the values. - attributeName (str): Name of the attribute. - componentNames (tuple[str,...], optional): Name of the components for vectorial attributes. If one component, gives an empty tuple. - Defaults to an empty tuple. - piece (Piece): The piece of the attribute. - Defaults to Piece.CELLS - vtkDataType (Union[int, None], optional): Vtk data type of the attribute to create. - Defaults to None, the vtk data type is given by the type of the values. - - Warning with int8, uint8 and int64 type of value, the corresponding vtk data type are multiples. By default: - - int8 -> VTK_SIGNED_CHAR - - uint8 -> VTK_UNSIGNED_CHAR - - int64 -> VTK_LONG_LONG - logger (Union[Logger, None], optional): A logger to manage the output messages. - Defaults to None, an internal logger is used. - - Raises: - TypeError: Error with the type of the npArray values. - ValueError: Error with the vtkDataType. - """ - # Check if an external logger is given. - if logger is None: - logger = getLogger( "createConstantAttributeDataSet", True ) - - # Check the piece. - if piece not in [ Piece.POINTS, Piece.CELLS ]: - raise ValueError( f"The attribute must be created on { Piece.POINTS.value } or { Piece.CELLS.value }." ) - - # Check if all the values of listValues have the same type. - valueType: type = type( listValues[ 0 ] ) - for value in listValues: - valueTypeTest: type = type( value ) - if valueType != valueTypeTest: - raise TypeError( "All values in the list of values must have the same type." ) - - # Convert int and float type into numpy scalar type. - if valueType in ( int, float ): - npType: type = type( np.array( listValues )[ 0 ] ) - logger.warning( - f"During the creation of the constant attribute { attributeName }, values have been converted from { valueType } to { npType }." - ) - logger.warning( "To avoid any issue with the conversion, please use directly numpy scalar type for the values" ) - valueType = npType + # Check the piece. + if piece not in [ Piece.POINTS, Piece.CELLS ]: + raise ValueError( f"The attribute must be created on { Piece.POINTS.value } or { Piece.CELLS.value }." ) + + # Check if all the values of listValues have the same type. + valueType: type = type( listValues[ 0 ] ) + for value in listValues: + valueTypeTest: type = type( value ) + if valueType != valueTypeTest: + raise TypeError( "All values in the list of values must have the same type." ) + + # Convert int and float type into numpy scalar type. + if valueType in ( int, float ): + npType: type = type( np.array( listValues )[ 0 ] ) + logger.warning( + f"During the creation of the constant attribute { attributeName }, values have been converted from { valueType } to { npType }." + ) + logger.warning( + "To avoid any issue with the conversion, please use directly numpy scalar type for the values" ) + valueType = npType + + # Check the consistency between the given value type and the vtk array type if it exists. + valueType = valueType().dtype + if vtkDataType is not None: + vtkNumpyTypeMap: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap() + if vtkDataType not in vtkNumpyTypeMap: + raise ValueError( f"The vtk data type { vtkDataType } is unknown." ) + + npArrayTypeFromVtk: npt.DTypeLike = vtkNumpyTypeMap[ vtkDataType ]().dtype + if npArrayTypeFromVtk != valueType: + raise TypeError( f"Input values in listValues type must be { npArrayTypeFromVtk }, not { valueType }." ) + + # Create the numpy array constant per component. + nbComponents: int = len( listValues ) + nbElements: int = ( mesh.GetNumberOfPoints() if piece == Piece.POINTS else mesh.GetNumberOfCells() ) + npArray: npt.NDArray[ Any ] + if nbComponents > 1: + npArray = np.array( [ listValues for _ in range( nbElements ) ], valueType ) + else: + npArray = np.array( [ listValues[ 0 ] for _ in range( nbElements ) ], valueType ) - # Check the consistency between the given value type and the vtk array type if it exists. - valueType = valueType().dtype - if vtkDataType is not None: - vtkNumpyTypeMap: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap() - if vtkDataType not in vtkNumpyTypeMap: - raise ValueError( f"The vtk data type { vtkDataType } is unknown." ) + createAttribute( mesh, npArray, attributeName, componentNames, piece, vtkDataType, logger ) - npArrayTypeFromVtk: npt.DTypeLike = vtkNumpyTypeMap[ vtkDataType ]().dtype - if npArrayTypeFromVtk != valueType: - raise TypeError( f"Input values in listValues type must be { npArrayTypeFromVtk }, not { valueType }." ) - - # Create the numpy array constant per component. - nbComponents: int = len( listValues ) - nbElements: int = ( dataSet.GetNumberOfPoints() if piece == Piece.POINTS else dataSet.GetNumberOfCells() ) - npArray: npt.NDArray[ Any ] - if nbComponents > 1: - npArray = np.array( [ listValues for _ in range( nbElements ) ], valueType ) else: - npArray = np.array( [ listValues[ 0 ] for _ in range( nbElements ) ], valueType ) - - createAttribute( dataSet, npArray, attributeName, componentNames, piece, vtkDataType, logger ) + raise TypeError( "Input mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet." ) return @@ -537,20 +452,25 @@ def createAttribute( def copyAttribute( - multiBlockDataSetFrom: vtkMultiBlockDataSet, - multiBlockDataSetTo: vtkMultiBlockDataSet, + meshFrom: vtkMultiBlockDataSet | vtkDataSet, + meshTo: vtkMultiBlockDataSet | vtkDataSet, attributeNameFrom: str, attributeNameTo: str, piece: Piece = Piece.CELLS, logger: Union[ Logger, None ] = None, ) -> None: - """Copy an attribute from a multiBlockDataSet to a similar one on the same piece. + """Copy an attribute from a mesh to a similar one on the same piece. + + The similarity of two meshes means that the two mesh have the same number of elements (cells and points) located in the same coordinates and with the same indexation. Testing this similarity is time consuming therefore, only few metric are compared: + - the block indexation for multiblock dataset + - the number of the element where the attribute is located, for multiblock dataset it is done for each block + - the coordinates of the first element, for multiblock dataset it is done for each block Args: - multiBlockDataSetFrom (vtkMultiBlockDataSet): MultiBlockDataSet from which to copy the attribute. - multiBlockDataSetTo (vtkMultiBlockDataSet): MultiBlockDataSet where to copy the attribute. - attributeNameFrom (str): Attribute name in multiBlockDataSetFrom. - attributeNameTo (str): Attribute name in multiBlockDataSetTo. It will be a new attribute of multiBlockDataSetTo. + meshFrom (vtkMultiBlockDataSet | vtkDataSet): mesh from which to copy the attribute. + meshTo (vtkMultiBlockDataSet | vtkDataSet): mesh where to copy the attribute. + attributeNameFrom (str): Attribute name in meshFrom. + attributeNameTo (str): Attribute name to set in meshTo. piece (Piece): The piece of the attribute. Defaults to Piece.CELLS logger (Union[Logger, None], optional): A logger to manage the output messages. @@ -558,94 +478,75 @@ def copyAttribute( Raises: TypeError: Error with the type of the source or final mesh. - ValueError: Error with the data of the source or final mesh. + ValueError: Error with the data of the source or final mesh or the piece. AttributeError: Error with the attribute attributeNameFrom or attributeNameTo. """ # Check if an external logger is given. if logger is None: logger = getLogger( "copyAttribute", True ) - # Check if the multiBlockDataSetFrom is inherited from vtkMultiBlockDataSet. - if not isinstance( multiBlockDataSetFrom, vtkMultiBlockDataSet ): - raise TypeError( "Source mesh has to be inherited from vtkMultiBlockDataSet." ) - - # Check if the multiBlockDataSetTo is inherited from vtkMultiBlockDataSet. - if not isinstance( multiBlockDataSetTo, vtkMultiBlockDataSet ): - raise TypeError( "Final mesh has to be inherited from vtkMultiBlockDataSet." ) - - # Check if the attribute exist in the multiBlockDataSetFrom. - if not isAttributeInObjectMultiBlockDataSet( multiBlockDataSetFrom, attributeNameFrom, piece ): - raise AttributeError( f"The attribute { attributeNameFrom } is not present in the source mesh." ) - - # Check if the attribute already exist in the multiBlockDataSetTo. - if isAttributeInObjectMultiBlockDataSet( multiBlockDataSetTo, attributeNameTo, piece ): - raise AttributeError( f"The attribute { attributeNameTo } is already present in the final mesh." ) - - # Check if the two multiBlockDataSets are similar. - elementaryBlockIndexesTo: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetTo ) - elementaryBlockIndexesFrom: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetFrom ) - if elementaryBlockIndexesTo != elementaryBlockIndexesFrom: - raise ValueError( "The two meshes do not have the same block indexes." ) - - # Parse blocks of the two mesh to copy the attribute. - for idBlock in elementaryBlockIndexesTo: - dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetFrom.GetDataSet( idBlock ) ) - dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTo.GetDataSet( idBlock ) ) - - if isAttributeInObjectDataSet( dataSetFrom, attributeNameFrom, piece ): - copyAttributeDataSet( dataSetFrom, dataSetTo, attributeNameFrom, attributeNameTo, piece, logger ) - - return - - -def copyAttributeDataSet( - dataSetFrom: vtkDataSet, - dataSetTo: vtkDataSet, - attributeNameFrom: str, - attributeNameTo: str, - piece: Piece = Piece.CELLS, - logger: Union[ Logger, Any ] = None, -) -> None: - """Copy an attribute from a dataSet to a similar one on the same piece. - - Args: - dataSetFrom (vtkDataSet): DataSet from which to copy the attribute. - dataSetTo (vtkDataSet): DataSet where to copy the attribute. - attributeNameFrom (str): Attribute name in dataSetFrom. - attributeNameTo (str): Attribute name in dataSetTo. It will be a new attribute of dataSetTo. - piece (Piece): The piece of the attribute. - Defaults to Piece.CELLS - logger (Union[Logger, None], optional): A logger to manage the output messages. - Defaults to None, an internal logger is used. - - Raises: - TypeError: Error with the type of the source mesh. - AttributeError: Error with the attribute attributeNameFrom. - """ - # Check if an external logger is given. - if logger is None: - logger = getLogger( "copyAttributeDataSet", True ) - - # Check if the dataSetFrom is inherited from vtkDataSet. - if not isinstance( dataSetFrom, vtkDataSet ): - raise TypeError( "Source mesh has to be inherited from vtkDataSet." ) - - # Check if the attribute exist in the dataSetFrom. - if not isAttributeInObjectDataSet( dataSetFrom, attributeNameFrom, piece ): - raise AttributeError( f"The attribute { attributeNameFrom } is not in the source mesh." ) - - npArray: npt.NDArray[ Any ] = getArrayInObject( dataSetFrom, attributeNameFrom, piece ) - componentNames: tuple[ str, ...] = getComponentNamesDataSet( dataSetFrom, attributeNameFrom, piece ) - vtkArrayType: int = getVtkArrayTypeInObject( dataSetFrom, attributeNameFrom, piece ) - - createAttribute( dataSetTo, npArray, attributeNameTo, componentNames, piece, vtkArrayType, logger ) + if isinstance( meshTo, vtkDataSet ) and isinstance( meshFrom, vtkDataSet ): + # Small check to check if the two meshes are similar. + coordElementTo: set[ tuple[ float, ...] ] = set() + coordElementFrom: set[ tuple[ float, ...] ] = set() + if piece == Piece.POINTS: + coordElementTo.add( meshTo.GetPoint( 0 ) ) + coordElementFrom.add( meshFrom.GetPoint( 0 ) ) + elif piece == Piece.CELLS: + cellTo: vtkCell = meshTo.GetCell( 0 ) + cellFrom: vtkCell = meshFrom.GetCell( 0 ) + # Get the coordinates of each points of the cell. + nbPointsTo: int = cellTo.GetNumberOfPoints() + nbPointsFrom: int = cellTo.GetNumberOfPoints() + if nbPointsTo != nbPointsFrom: + raise ValueError( "The two meshes have not the same cells dimension." ) + + cellPointsTo: vtkPoints = cellTo.GetPoints() + cellPointsFrom: vtkPoints = cellFrom.GetPoints() + for idPoint in range( nbPointsTo ): + coordElementTo.add( cellPointsTo.GetPoint( idPoint ) ) + coordElementFrom.add( cellPointsFrom.GetPoint( idPoint ) ) + else: + raise ValueError( "The piece of the attribute to copy must be cells or points." ) + if coordElementTo != coordElementFrom: + raise ValueError( "The two meshes have not the same element indexation." ) + + npArray: npt.NDArray[ Any ] = getArrayInObject( meshFrom, attributeNameFrom, piece ) + componentNames: tuple[ str, ...] = getComponentNamesDataSet( meshFrom, attributeNameFrom, piece ) + vtkArrayType: int = getVtkArrayTypeInObject( meshFrom, attributeNameFrom, piece ) + + createAttribute( meshTo, npArray, attributeNameTo, componentNames, piece, vtkArrayType, logger ) + elif isinstance( meshTo, vtkMultiBlockDataSet ) and isinstance( meshFrom, vtkMultiBlockDataSet ): + # Check if the attribute exist in the meshFrom. + if not isAttributeInObject( meshFrom, attributeNameFrom, piece ): + raise AttributeError( f"The attribute { attributeNameFrom } is not present in the source mesh." ) + + # Check if the attribute already exist in the meshTo. + if isAttributeInObject( meshTo, attributeNameTo, piece ): + raise AttributeError( f"The attribute { attributeNameTo } is already present in the final mesh." ) + + # Check if the two multiBlockDataSets are similar. + elementaryBlockIndexesTo: list[ int ] = getBlockElementIndexesFlatten( meshTo ) + elementaryBlockIndexesFrom: list[ int ] = getBlockElementIndexesFlatten( meshFrom ) + if elementaryBlockIndexesTo != elementaryBlockIndexesFrom: + raise ValueError( "The two meshes do not have the same block indexes." ) + + # Parse blocks of the two meshes to copy the attribute. + for idBlock in elementaryBlockIndexesTo: + dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( meshFrom.GetDataSet( idBlock ) ) + dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( meshTo.GetDataSet( idBlock ) ) + + if isAttributeInObject( dataSetFrom, attributeNameFrom, piece ): + copyAttribute( dataSetFrom, dataSetTo, attributeNameFrom, attributeNameTo, piece, logger ) + else: + raise TypeError( "Input meshes must be both inherited from vtkMultiBlockDataSet or vtkDataSet." ) return -def transferAttributeToDataSetWithElementMap( +def transferAttributeWithElementMap( meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], - dataSetTo: vtkDataSet, + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], elementMap: dict[ int, npt.NDArray[ np.int64 ] ], attributeName: str, piece: Piece, @@ -655,22 +556,23 @@ def transferAttributeToDataSetWithElementMap( """Transfer attributes from the source mesh to the final mesh using a map of points/cells. If the source mesh is a vtkDataSet, its flat index (flatIdDataSetFrom) is set to 0. + If the final mesh is a vtkDataSet, its flat index (flatIdDataSetTo) is set to 0. The map of points/cells used to transfer the attribute is a dictionary where: - - The key is the flat index of the final mesh. - - The item is an array of size (nb elements in the final mesh, 2). + - Keys are the flat indexes of all the datasets of the final mesh. + - Items are arrays of size (nb elements in datasets of the final mesh, 2). - If an element (idElementTo) of the final mesh is mapped with no element of the source mesh: + If an element (idElementTo) of one dataset (flatIdDataSetTo) of the final mesh is mapped with no element of the source mesh: - elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. - The value of the attribute for this element depends of the type of the value of the attribute (0 for unit, -1 for int, nan for float). - If an element (idElementTo) of the final mesh is mapped with an element (idElementFrom) of one of the dataset (flatIdDataSetFrom) of the source mesh: + If an element (idElementTo) of one dataset (flatIdDataSetTo) of the final mesh is mapped with an element (idElementFrom) of one of the dataset (flatIdDataSetFrom) of the source mesh: - elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. - The value of the attribute for this element is the value of the element (idElementFrom) of the dataset (flatIdDataSetFrom) of the source mesh. Args: meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The source mesh with the attribute to transfer. - dataSetTo (vtkDataSet): The final mesh where to transfer the attribute. + meshTo (Union[vtkDataSet, vtkMultiBlockDataSet]): The final mesh where to transfer the attribute. elementMap (dict[int, npt.NDArray[np.int64]]): The map of points/cells. attributeName (str): The name of the attribute to transfer. piece (Piece): The piece of the attribute. @@ -682,149 +584,101 @@ def transferAttributeToDataSetWithElementMap( Raises: TypeError: Error with the type of the mesh to or the mesh from. ValueError: Error with the values of the map elementMap. - AttributeError: Error with the attribute AttributeName. + AttributeError: Error with the attribute attributeName. """ # Check if an external logger is given. if logger is None: - logger = getLogger( "transferAttributeToDataSetWithElementMap", True ) + logger = getLogger( "transferAttributeWithElementMap", True ) # Check the piece. if piece not in [ Piece.POINTS, Piece.CELLS ]: raise ValueError( f"The attribute must be on { Piece.POINTS.value } or { Piece.CELLS.value }." ) - if not isinstance( dataSetTo, vtkDataSet ): - raise TypeError( "The final mesh has to be inherited from vtkDataSet." ) - - if flatIdDataSetTo not in elementMap: - raise ValueError( - f"The map is incomplete, there is no data for the final mesh (flat index { flatIdDataSetTo })." ) - - nbElementsTo: int = dataSetTo.GetNumberOfPoints() if piece == Piece.POINTS else dataSetTo.GetNumberOfCells() - if len( elementMap[ flatIdDataSetTo ] ) != nbElementsTo: - raise ValueError( - f"The map is wrong, there is { nbElementsTo } elements in the final mesh (flat index { flatIdDataSetTo }) but { len( elementMap[ flatIdDataSetTo ] ) } elements in the map." - ) - - if not isinstance( meshFrom, ( vtkDataSet, vtkMultiBlockDataSet ) ): - raise TypeError( "The source mesh has to be inherited from vtkDataSet or vtkMultiBlockDataSet." ) - + # Check the attribute to transfer. + ## it is in the meshFrom if not isAttributeInObject( meshFrom, attributeName, piece ): raise AttributeError( f"The attribute { attributeName } is not in the source mesh." ) - + ## it is global if isinstance( meshFrom, vtkMultiBlockDataSet ) and not isAttributeGlobal( meshFrom, attributeName, piece ): raise AttributeError( f"The attribute { attributeName } must be global in the source mesh." ) - componentNames: tuple[ str, ...] = getComponentNames( meshFrom, attributeName, piece ) - nbComponents: int = len( componentNames ) - - vtkDataType: int = getVtkDataTypeInObject( meshFrom, attributeName, piece ) - defaultValue: Any - if vtkDataType in ( VTK_FLOAT, VTK_DOUBLE ): - defaultValue = np.nan - elif vtkDataType in ( VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE ): - defaultValue = -1 - elif vtkDataType in ( VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT, - VTK_UNSIGNED_LONG_LONG ): - defaultValue = 0 - else: - raise AttributeError( f"The attribute { attributeName } has an unknown type." ) - - typeMapping: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap() - valueType: type = typeMapping[ vtkDataType ] - - arrayTo: npt.NDArray[ Any ] - if nbComponents > 1: - defaultValue = [ defaultValue ] * nbComponents - arrayTo = np.full( ( nbElementsTo, nbComponents ), defaultValue, dtype=valueType ) - else: - arrayTo = np.array( [ defaultValue for _ in range( nbElementsTo ) ], dtype=valueType ) - - for idElementTo in range( nbElementsTo ): - valueToTransfer: Any = defaultValue - idElementFrom: int = int( elementMap[ flatIdDataSetTo ][ idElementTo ][ 1 ] ) - if idElementFrom != -1: - dataFrom: Union[ vtkPointData, vtkCellData ] - if isinstance( meshFrom, vtkDataSet ): - dataFrom = meshFrom.GetPointData() if piece == Piece.POINTS else meshFrom.GetCellData() - elif isinstance( meshFrom, vtkMultiBlockDataSet ): - flatIdDataSetFrom: int = int( elementMap[ flatIdDataSetTo ][ idElementTo ][ 0 ] ) - dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( meshFrom.GetDataSet( flatIdDataSetFrom ) ) - dataFrom = dataSetFrom.GetPointData() if piece == Piece.POINTS else dataSetFrom.GetCellData() - - arrayFrom: npt.NDArray[ Any ] = vnp.vtk_to_numpy( dataFrom.GetArray( attributeName ) ) - valueToTransfer = arrayFrom[ idElementFrom ] - - arrayTo[ idElementTo ] = valueToTransfer - - createAttribute( dataSetTo, - arrayTo, - attributeName, - componentNames, - piece=piece, - vtkDataType=vtkDataType, - logger=logger ) - - return - - -def transferAttributeWithElementMap( - meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], - meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], - elementMap: dict[ int, npt.NDArray[ np.int64 ] ], - attributeName: str, - piece: Piece, - logger: Union[ Logger, Any ] = None, -) -> None: - """Transfer attributes from the source mesh to the final mesh using a map of points/cells. - - If the source mesh is a vtkDataSet, its flat index (flatIdDataSetFrom) is set to 0. - If the final mesh is a vtkDataSet, its flat index (flatIdDataSetTo) is set to 0. - - The map of points/cells used to transfer the attribute is a dictionary where: - - Keys are the flat index of all the datasets of the final mesh. - - Items are arrays of size (nb elements in datasets of the final mesh, 2). + # Transfer the attribute + if isinstance( meshTo, vtkDataSet ): + if flatIdDataSetTo not in elementMap: + raise ValueError( + f"The map is incomplete, there is no data for the final mesh (flat index { flatIdDataSetTo })." ) - If an element (idElementTo) of one dataset (flatIdDataSetTo) of the final mesh is mapped with no element of the source mesh: - - elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. - - The value of the attribute for this element depends of the type of the value of the attribute (0 for unit, -1 for int, nan for float). + nbElementsTo: int = meshTo.GetNumberOfPoints() if piece == Piece.POINTS else meshTo.GetNumberOfCells() + if len( elementMap[ flatIdDataSetTo ] ) != nbElementsTo: + raise ValueError( + f"The map is wrong, there is { nbElementsTo } elements in the final mesh (flat index { flatIdDataSetTo }) but { len( elementMap[ flatIdDataSetTo ] ) } elements in the map." + ) - If an element (idElementTo) of one dataset (flatIdDataSetTo) of the final mesh is mapped with an element (idElementFrom) of one of the dataset (flatIdDataSetFrom) of the source mesh: - - elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. - - The value of the attribute for this element is the value of the element (idElementFrom) of the dataset (flatIdDataSetFrom) of the source mesh. + componentNames: tuple[ str, ...] = getComponentNames( meshFrom, attributeName, piece ) + nbComponents: int = len( componentNames ) - Args: - meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The source mesh with the attribute to transfer. - meshTo (Union[vtkDataSet, vtkMultiBlockDataSet]): The final mesh where to transfer the attribute. - elementMap (dict[int, npt.NDArray[np.int64]]): The map of points/cells. - attributeName (str): The name of the attribute to transfer. - piece (Piece): The piece of the attribute. - logger (Union[Logger, None], optional): A logger to manage the output messages. - Defaults to None, an internal logger is used. + vtkDataType: int = getVtkDataTypeInObject( meshFrom, attributeName, piece ) + defaultValue: Any + if vtkDataType in ( VTK_FLOAT, VTK_DOUBLE ): + defaultValue = np.nan + elif vtkDataType in ( VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE ): + defaultValue = -1 + elif vtkDataType in ( VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT, + VTK_UNSIGNED_LONG_LONG ): + defaultValue = 0 + else: + raise AttributeError( f"The attribute { attributeName } has an unknown type." ) - Raises: - TypeError: Error with the type of the final mesh. - AttributeError: Error with the attribute attributeName. - """ - # Check if an external logger is given. - if logger is None: - logger = getLogger( "transferAttributeWithElementMap", True ) + typeMapping: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap() + valueType: type = typeMapping[ vtkDataType ] - if isinstance( meshTo, vtkDataSet ): - transferAttributeToDataSetWithElementMap( meshFrom, meshTo, elementMap, attributeName, piece, logger=logger ) + arrayTo: npt.NDArray[ Any ] + if nbComponents > 1: + defaultValue = [ defaultValue ] * nbComponents + arrayTo = np.full( ( nbElementsTo, nbComponents ), defaultValue, dtype=valueType ) + else: + arrayTo = np.array( [ defaultValue for _ in range( nbElementsTo ) ], dtype=valueType ) + + for idElementTo in range( nbElementsTo ): + valueToTransfer: Any = defaultValue + idElementFrom: int = int( elementMap[ flatIdDataSetTo ][ idElementTo ][ 1 ] ) + if idElementFrom != -1: + dataFrom: Union[ vtkPointData, vtkCellData ] + if isinstance( meshFrom, vtkDataSet ): + dataFrom = meshFrom.GetPointData() if piece == Piece.POINTS else meshFrom.GetCellData() + elif isinstance( meshFrom, vtkMultiBlockDataSet ): + flatIdDataSetFrom: int = int( elementMap[ flatIdDataSetTo ][ idElementTo ][ 0 ] ) + dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( meshFrom.GetDataSet( flatIdDataSetFrom ) ) + dataFrom = dataSetFrom.GetPointData() if piece == Piece.POINTS else dataSetFrom.GetCellData() + else: + raise TypeError( "The source mesh has to be inherited from vtkDataSet or vtkMultiBlockDataSet." ) + + arrayFrom: npt.NDArray[ Any ] = vnp.vtk_to_numpy( dataFrom.GetArray( attributeName ) ) + valueToTransfer = arrayFrom[ idElementFrom ] + + arrayTo[ idElementTo ] = valueToTransfer + + createAttribute( meshTo, + arrayTo, + attributeName, + componentNames, + piece=piece, + vtkDataType=vtkDataType, + logger=logger ) elif isinstance( meshTo, vtkMultiBlockDataSet ): - if isAttributeInObjectMultiBlockDataSet( meshTo, attributeName, piece ): + if isAttributeInObject( meshTo, attributeName, piece ): raise AttributeError( f"The attribute { attributeName } is already in the final mesh." ) listFlatIdDataSetTo: list[ int ] = getBlockElementIndexesFlatten( meshTo ) for flatIdDataSetTo in listFlatIdDataSetTo: dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( meshTo.GetDataSet( flatIdDataSetTo ) ) - transferAttributeToDataSetWithElementMap( meshFrom, - dataSetTo, - elementMap, - attributeName, - piece, - flatIdDataSetTo=flatIdDataSetTo, - logger=logger ) + transferAttributeWithElementMap( meshFrom, + dataSetTo, + elementMap, + attributeName, + piece, + flatIdDataSetTo=flatIdDataSetTo, + logger=logger ) else: raise TypeError( "The final mesh has to be inherited from vtkDataSet or vtkMultiBlockDataSet." ) @@ -918,87 +772,55 @@ def createCellCenterAttribute( if logger is None: logger = getLogger( "createCellCenterAttribute", True ) - if isinstance( mesh, vtkMultiBlockDataSet ): - if isAttributeInObjectMultiBlockDataSet( mesh, cellCenterAttributeName, Piece.CELLS ): - raise AttributeError( f"The attribute { cellCenterAttributeName } in already in the mesh." ) + if isAttributeInObject( mesh, cellCenterAttributeName, Piece.CELLS ): + raise AttributeError( f"The attribute { cellCenterAttributeName } in already in the mesh." ) + if isinstance( mesh, vtkMultiBlockDataSet ): elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( mesh ) for blockIndex in elementaryBlockIndexes: dataSet: vtkDataSet = vtkDataSet.SafeDownCast( mesh.GetDataSet( blockIndex ) ) - createCellCenterAttributeDataSet( dataSet, cellCenterAttributeName, logger ) + createCellCenterAttribute( dataSet, cellCenterAttributeName, logger ) elif isinstance( mesh, vtkDataSet ): - createCellCenterAttributeDataSet( mesh, cellCenterAttributeName, logger ) + vtkErrorLogger: Logger = logging.getLogger( f"{ logger.name } vtkError Logger" ) + vtkErrorLogger.setLevel( logging.INFO ) + vtkErrorLogger.addHandler( logger.handlers[ 0 ] ) + vtkErrorLogger.propagate = False + vtkLogger.SetStderrVerbosity( vtkLogger.VERBOSITY_ERROR ) + vtkErrorLogger.addFilter( RegexExceptionFilter() ) # will raise VTKError if captured VTK Error + with VTKCaptureLog() as capturedLog: + # apply ElementCenter filter + cellCenterFilter: vtkCellCenters = vtkCellCenters() + cellCenterFilter.SetInputData( mesh ) + cellCenterFilter.Update() + + capturedLog.seek( 0 ) + captured = capturedLog.read().decode() + + if captured != "": + vtkErrorLogger.error( captured.strip() ) + + output: vtkPointSet = cellCenterFilter.GetOutputDataObject( 0 ) + if output is None: + raise VTKError( "Something went wrong with VTK cell center filter." ) + + # transfer output to output arrays + centers: vtkPoints = output.GetPoints() + if centers is None: + raise VTKError( "Something went wrong with VTK cell center filter." ) + + centerCoords: vtkDataArray = centers.GetData() + if centerCoords is None: + raise VTKError( "Something went wrong with VTK cell center filter." ) + + centerCoords.SetName( cellCenterAttributeName ) + mesh.GetCellData().AddArray( centerCoords ) + mesh.Modified() else: raise TypeError( "Input mesh must be a vtkDataSet or vtkMultiBlockDataSet." ) return -def createCellCenterAttributeDataSet( - block: vtkDataSet, - cellCenterAttributeName: str, - logger: Union[ Logger, Any ] = None, -) -> None: - """Create cellElementCenter attribute in a vtkDataSet if it does not exist. - - Args: - block (vtkDataSet): Input mesh that must be a vtkDataSet. - cellCenterAttributeName (str): Name of the attribute. - logger (Union[Logger, None], optional): A logger to manage the output messages. - Defaults to None, an internal logger is used. - - Raises: - TypeError: Error with the type of the mesh. - AttributeError: Error with the attribute cellCenterAttributeName. - VTKError: Error with a VTK function. - """ - if logger is None: - logger = getLogger( "createCellCenterAttributeDataSet", True ) - - if not isinstance( block, vtkDataSet ): - raise TypeError( "Input mesh has to be inherited from vtkDataSet." ) - - if isAttributeInObject( block, cellCenterAttributeName, Piece.CELLS ): - raise AttributeError( f"The attribute { cellCenterAttributeName } in already in the mesh." ) - - vtkErrorLogger: Logger = logging.getLogger( f"{ logger.name } vtkError Logger" ) - vtkErrorLogger.setLevel( logging.INFO ) - vtkErrorLogger.addHandler( logger.handlers[ 0 ] ) - vtkErrorLogger.propagate = False - vtkLogger.SetStderrVerbosity( vtkLogger.VERBOSITY_ERROR ) - vtkErrorLogger.addFilter( RegexExceptionFilter() ) # will raise VTKError if captured VTK Error - with VTKCaptureLog() as capturedLog: - # apply ElementCenter filter - cellCenterFilter: vtkCellCenters = vtkCellCenters() - cellCenterFilter.SetInputData( block ) - cellCenterFilter.Update() - - capturedLog.seek( 0 ) - captured = capturedLog.read().decode() - - if captured != "": - vtkErrorLogger.error( captured.strip() ) - - output: vtkPointSet = cellCenterFilter.GetOutputDataObject( 0 ) - if output is None: - raise VTKError( "Something went wrong with VTK cell center filter." ) - - # transfer output to output arrays - centers: vtkPoints = output.GetPoints() - if centers is None: - raise VTKError( "Something went wrong with VTK cell center filter." ) - - centerCoords: vtkDataArray = centers.GetData() - if centerCoords is None: - raise VTKError( "Something went wrong with VTK cell center filter." ) - - centerCoords.SetName( cellCenterAttributeName ) - block.GetCellData().AddArray( centerCoords ) - block.Modified() - - return - - def transferPointDataToCellData( mesh: vtkPointSet, logger: Union[ Logger, Any ] = None, diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index 4036d1f3..d154a7b9 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -15,6 +15,7 @@ from vtkmodules.vtkCommonDataModel import ( vtkDataSet, vtkMultiBlockDataSet, vtkPointData, vtkCellData ) from geos.mesh.utils.multiblockHelpers import getBlockElementIndexesFlatten +from geos.mesh.utils.arrayHelpers import isAttributeInObject from vtk import ( # type: ignore[import-untyped] VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_INT, VTK_UNSIGNED_LONG_LONG, VTK_CHAR, VTK_SIGNED_CHAR, @@ -210,90 +211,58 @@ def test_createEmptyAttributeValueError() -> None: @pytest.mark.parametrize( - "attributeName, piece", + "meshName, listValues, componentNames, componentNamesTest, piece, vtkDataType, vtkDataTypeTest, attributeName", [ - # Test to create a new attribute on points and on cells. - ( "newAttribute", Piece.CELLS ), - ( "newAttribute", Piece.POINTS ), - ] ) -def test_createConstantAttributeMultiBlock( - dataSetTest: vtkMultiBlockDataSet, - attributeName: str, - piece: Piece, -) -> None: - """Test creation of constant attribute in multiblock dataset.""" - multiBlockDataSetTest: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - values: list[ float ] = [ np.nan ] - arrayModifiers.createConstantAttributeMultiBlock( multiBlockDataSetTest, values, attributeName, piece=piece ) - - elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetTest ) - for blockIndex in elementaryBlockIndexes: - dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTest.GetDataSet( blockIndex ) ) - data: Union[ vtkPointData, vtkCellData ] - data = dataSet.GetPointData() if piece == Piece.POINTS else dataSet.GetCellData() - - attributeWellCreated: int = data.HasArray( attributeName ) - assert attributeWellCreated == 1 - - -def test_createConstantAttributeMultiBlockRaiseTypeError( dataSetTest: vtkDataSet, ) -> None: - """Test the raises TypeError for the function createConstantAttributeMultiBlock with a wrong mesh type.""" - mesh: vtkDataSet = dataSetTest( "dataset" ) - with pytest.raises( TypeError ): - arrayModifiers.createConstantAttributeMultiBlock( mesh, [ np.int32( 42 ) ], "newAttribute" ) - - -def test_createConstantAttributeMultiBlockRaiseAttributeError( dataSetTest: vtkMultiBlockDataSet, ) -> None: - """Test the raises AttributeError for the function createConstantAttributeMultiBlock with a wrong attributeName.""" - mesh: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - with pytest.raises( AttributeError ): - arrayModifiers.createConstantAttributeMultiBlock( mesh, [ np.int32( 42 ) ], "PORO" ) - - -@pytest.mark.parametrize( - "listValues, componentNames, componentNamesTest, piece, vtkDataType, vtkDataTypeTest, attributeName", - [ - # Test attribute names. - ## Test with a new attributeName on cells and on points. - ( [ np.float32( 42 ) ], (), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), - ( [ np.float32( 42 ) ], (), (), Piece.CELLS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), + # Test mesh types. + ( "dataset", [ np.float32( 42 ) ], (), (), Piece.CELLS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), + ( "dataset", [ np.float32( 42 ) ], (), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), + ( "multiblock", [ np.float32( 42 ) ], (), (), Piece.CELLS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), + ( "multiblock", [ np.float32( 42 ) ], (), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), + # Test with an attribute name that exist on the opposite piece. + ( "dataset", [ np.float32( 42 ) ], (), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "PORO" ), + ( "multiblock", [ np.float32( 42 ) ], (), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "PORO" ), # Partial + ( "multiblock", [ np.float32( 42 ) ], (), + (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "GLOBAL_IDS_CELLS" ), # Global # Test the number of components and their names. - ( [ np.float32( 42 ) ], ( "X" ), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), - ( [ np.float32( 42 ), np.float32( 42 ) ], ( "X", "Y" ), + ( "dataset", [ np.float32( 42 ) ], ( "X" ), (), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), + ( "dataset", [ np.float32( 42 ), np.float32( 42 ) ], ( "X", "Y" ), ( "X", "Y" ), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), - ( [ np.float32( 42 ), np.float32( 42 ) ], ( "X", "Y", "Z" ), + ( "dataset", [ np.float32( 42 ), np.float32( 42 ) ], ( "X", "Y", "Z" ), ( "X", "Y" ), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), - ( [ np.float32( 42 ), np.float32( 42 ) ], (), + ( "dataset", [ np.float32( 42 ), np.float32( 42 ) ], (), ( "Component0", "Component1" ), Piece.POINTS, VTK_FLOAT, VTK_FLOAT, "newAttribute" ), # Test the type of the values. ## With numpy scalar type. - ( [ np.int8( 42 ) ], (), (), Piece.POINTS, None, VTK_SIGNED_CHAR, "newAttribute" ), - ( [ np.int8( 42 ) ], (), (), Piece.POINTS, VTK_SIGNED_CHAR, VTK_SIGNED_CHAR, "newAttribute" ), - ( [ np.int16( 42 ) ], (), (), Piece.POINTS, None, VTK_SHORT, "newAttribute" ), - ( [ np.int16( 42 ) ], (), (), Piece.POINTS, VTK_SHORT, VTK_SHORT, "newAttribute" ), - ( [ np.int32( 42 ) ], (), (), Piece.POINTS, None, VTK_INT, "newAttribute" ), - ( [ np.int32( 42 ) ], (), (), Piece.POINTS, VTK_INT, VTK_INT, "newAttribute" ), - ( [ np.int64( 42 ) ], (), (), Piece.POINTS, None, VTK_LONG_LONG, "newAttribute" ), - ( [ np.int64( 42 ) ], (), (), Piece.POINTS, VTK_LONG_LONG, VTK_LONG_LONG, "newAttribute" ), - ( [ np.uint8( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_CHAR, "newAttribute" ), - ( [ np.uint8( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_CHAR, "newAttribute" ), - ( [ np.uint16( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_SHORT, "newAttribute" ), - ( [ np.uint16( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_SHORT, "newAttribute" ), - ( [ np.uint32( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_INT, "newAttribute" ), - ( [ np.uint32( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_INT, VTK_UNSIGNED_INT, "newAttribute" ), - ( [ np.uint64( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_LONG_LONG, "newAttribute" ), - ( [ np.uint64( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_LONG_LONG, VTK_UNSIGNED_LONG_LONG, "newAttribute" ), - ( [ np.float32( 42 ) ], (), (), Piece.POINTS, None, VTK_FLOAT, "newAttribute" ), - ( [ np.float64( 42 ) ], (), (), Piece.POINTS, None, VTK_DOUBLE, "newAttribute" ), - ( [ np.float64( 42 ) ], (), (), Piece.POINTS, VTK_DOUBLE, VTK_DOUBLE, "newAttribute" ), + ( "dataset", [ np.int8( 42 ) ], (), (), Piece.POINTS, None, VTK_SIGNED_CHAR, "newAttribute" ), + ( "dataset", [ np.int8( 42 ) ], (), (), Piece.POINTS, VTK_SIGNED_CHAR, VTK_SIGNED_CHAR, "newAttribute" ), + ( "dataset", [ np.int16( 42 ) ], (), (), Piece.POINTS, None, VTK_SHORT, "newAttribute" ), + ( "dataset", [ np.int16( 42 ) ], (), (), Piece.POINTS, VTK_SHORT, VTK_SHORT, "newAttribute" ), + ( "dataset", [ np.int32( 42 ) ], (), (), Piece.POINTS, None, VTK_INT, "newAttribute" ), + ( "dataset", [ np.int32( 42 ) ], (), (), Piece.POINTS, VTK_INT, VTK_INT, "newAttribute" ), + ( "dataset", [ np.int64( 42 ) ], (), (), Piece.POINTS, None, VTK_LONG_LONG, "newAttribute" ), + ( "dataset", [ np.int64( 42 ) ], (), (), Piece.POINTS, VTK_LONG_LONG, VTK_LONG_LONG, "newAttribute" ), + ( "dataset", [ np.uint8( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_CHAR, "newAttribute" ), + ( "dataset", [ np.uint8( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_CHAR, "newAttribute" ), + ( "dataset", [ np.uint16( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_SHORT, "newAttribute" ), + ( "dataset", [ np.uint16( 42 ) ], (), + (), Piece.POINTS, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_SHORT, "newAttribute" ), + ( "dataset", [ np.uint32( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_INT, "newAttribute" ), + ( "dataset", [ np.uint32( 42 ) ], (), (), Piece.POINTS, VTK_UNSIGNED_INT, VTK_UNSIGNED_INT, "newAttribute" ), + ( "dataset", [ np.uint64( 42 ) ], (), (), Piece.POINTS, None, VTK_UNSIGNED_LONG_LONG, "newAttribute" ), + ( "dataset", [ np.uint64( 42 ) ], (), + (), Piece.POINTS, VTK_UNSIGNED_LONG_LONG, VTK_UNSIGNED_LONG_LONG, "newAttribute" ), + ( "dataset", [ np.float32( 42 ) ], (), (), Piece.POINTS, None, VTK_FLOAT, "newAttribute" ), + ( "dataset", [ np.float64( 42 ) ], (), (), Piece.POINTS, None, VTK_DOUBLE, "newAttribute" ), + ( "dataset", [ np.float64( 42 ) ], (), (), Piece.POINTS, VTK_DOUBLE, VTK_DOUBLE, "newAttribute" ), ## With python scalar type. - ( [ 42 ], (), (), Piece.POINTS, None, VTK_LONG_LONG, "newAttribute" ), - ( [ 42 ], (), (), Piece.POINTS, VTK_LONG_LONG, VTK_LONG_LONG, "newAttribute" ), - ( [ 42. ], (), (), Piece.POINTS, None, VTK_DOUBLE, "newAttribute" ), - ( [ 42. ], (), (), Piece.POINTS, VTK_DOUBLE, VTK_DOUBLE, "newAttribute" ), + ( "dataset", [ 42 ], (), (), Piece.POINTS, None, VTK_LONG_LONG, "newAttribute" ), + ( "dataset", [ 42 ], (), (), Piece.POINTS, VTK_LONG_LONG, VTK_LONG_LONG, "newAttribute" ), + ( "dataset", [ 42. ], (), (), Piece.POINTS, None, VTK_DOUBLE, "newAttribute" ), + ( "dataset", [ 42. ], (), (), Piece.POINTS, VTK_DOUBLE, VTK_DOUBLE, "newAttribute" ), ] ) -def test_createConstantAttributeDataSet( - dataSetTest: vtkDataSet, +def test_createConstantAttribute( + dataSetTest: Any, + meshName: str, listValues: list[ Any ], componentNames: tuple[ str, ...], componentNamesTest: tuple[ str, ...], @@ -302,71 +271,107 @@ def test_createConstantAttributeDataSet( vtkDataTypeTest: int, attributeName: str, ) -> None: - """Test constant attribute creation in dataset.""" - dataSet: vtkDataSet = dataSetTest( "dataset" ) + """Test constant attribute creation.""" + mesh: vtkDataSet | vtkMultiBlockDataSet = dataSetTest( meshName ) - # Create the new constant attribute in the dataSet. - arrayModifiers.createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, piece, - vtkDataType ) + # Create the new constant attribute in the mesh. + arrayModifiers.createConstantAttribute( mesh, listValues, attributeName, componentNames, piece, vtkDataType ) - # Get the created attribute. - data: Union[ vtkPointData, vtkCellData ] - nbElements: int - if piece == Piece.POINTS: - data = dataSet.GetPointData() - nbElements = dataSet.GetNumberOfPoints() + listDataSet: list[ vtkDataSet ] = [] + if isinstance( mesh, vtkDataSet ): + listDataSet.append( mesh ) else: - data = dataSet.GetCellData() - nbElements = dataSet.GetNumberOfCells() - attributeCreated: vtkDataArray = data.GetArray( attributeName ) - - # Test the number of components and their names if multiple. - nbComponentsTest: int = len( listValues ) - nbComponentsCreated: int = attributeCreated.GetNumberOfComponents() - assert nbComponentsCreated == nbComponentsTest - if nbComponentsTest > 1: - componentNamesCreated: tuple[ str, ...] = tuple( - attributeCreated.GetComponentName( i ) for i in range( nbComponentsCreated ) ) - assert componentNamesCreated, componentNamesTest + elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( mesh ) + for blockIndex in elementaryBlockIndexes: + listDataSet.append( vtkDataSet.SafeDownCast( mesh.GetDataSet( blockIndex ) ) ) - # Test values and their types. - ## Create the constant array test from values in the list values. - npArrayTest: npt.NDArray[ Any ] - if len( listValues ) > 1: - npArrayTest = np.array( [ listValues for _ in range( nbElements ) ] ) - else: - npArrayTest = np.array( [ listValues[ 0 ] for _ in range( nbElements ) ] ) + for dataSet in listDataSet: + # Get the created attribute. + data: Union[ vtkPointData, vtkCellData ] + nbElements: int + if piece == Piece.POINTS: + data = dataSet.GetPointData() + nbElements = dataSet.GetNumberOfPoints() + else: + data = dataSet.GetCellData() + nbElements = dataSet.GetNumberOfCells() + attributeCreated: vtkDataArray = data.GetArray( attributeName ) + + # Test the number of components and their names if multiple. + nbComponentsTest: int = len( listValues ) + nbComponentsCreated: int = attributeCreated.GetNumberOfComponents() + assert nbComponentsCreated == nbComponentsTest + if nbComponentsTest > 1: + componentNamesCreated: tuple[ str, ...] = tuple( + attributeCreated.GetComponentName( i ) for i in range( nbComponentsCreated ) ) + assert componentNamesCreated, componentNamesTest + + # Test values and their types. + ## Create the constant array test from values in the list values. + npArrayTest: npt.NDArray[ Any ] + if len( listValues ) > 1: + npArrayTest = np.array( [ listValues for _ in range( nbElements ) ] ) + else: + npArrayTest = np.array( [ listValues[ 0 ] for _ in range( nbElements ) ] ) - npArrayCreated: npt.NDArray[ Any ] = vnp.vtk_to_numpy( attributeCreated ) - assert npArrayCreated.dtype == npArrayTest.dtype - assert ( npArrayCreated == npArrayTest ).all() + npArrayCreated: npt.NDArray[ Any ] = vnp.vtk_to_numpy( attributeCreated ) + assert npArrayCreated.dtype == npArrayTest.dtype + assert ( npArrayCreated == npArrayTest ).all() - vtkDataTypeCreated: int = attributeCreated.GetDataType() - assert vtkDataTypeCreated == vtkDataTypeTest + vtkDataTypeCreated: int = attributeCreated.GetDataType() + assert vtkDataTypeCreated == vtkDataTypeTest @pytest.mark.parametrize( - "listValues, vtkDataType", + "meshName, listValues, vtkDataType", [ - ( [ np.int32( 42 ), np.int64( 42 ) ], VTK_DOUBLE ), # All the values in the listValues are not the same - ( [ np.int32( 42 ) ], VTK_DOUBLE ), # The type of the value is not coherent with the vtkDataType + ( "dataset", [ np.int32( 42 ), np.int64( 42 ) + ], VTK_DOUBLE ), # All the values in the listValues are not the same + ( "dataset", [ np.int32( 42 ) ], VTK_DOUBLE ), # The type of the value is not coherent with the vtkDataType + ( "other", [ np.int64( 42 ) ], VTK_DOUBLE ), # The type of the mesh is wrong ] ) -def test_createConstantAttributeDataSetRaiseTypeError( +def test_createConstantAttributeRaiseTypeError( dataSetTest: vtkDataSet, + meshName: str, listValues: list[ Any ], vtkDataType: int, ) -> None: - """Test the raises TypeError for the function createConstantAttributeDataSet.""" - mesh: vtkDataSet = dataSetTest( "dataset" ) + """Test the raises TypeError for the function createConstantAttribute.""" + mesh: vtkCellData | vtkDataSet = vtkCellData() if meshName == "other" else dataSetTest( meshName ) with pytest.raises( TypeError ): - arrayModifiers.createConstantAttributeDataSet( mesh, listValues, "newAttribute", vtkDataType=vtkDataType ) + arrayModifiers.createConstantAttribute( mesh, listValues, "newAttribute", vtkDataType=vtkDataType ) -def test_createConstantAttributeDataSetRaiseValueError( dataSetTest: vtkDataSet, ) -> None: - """Test the raises ValueError for the function createConstantAttributeDataSet with a wrong vtkDataType.""" +def test_createConstantAttributeRaiseValueErrorVTKDataType( dataSetTest: vtkDataSet, ) -> None: + """Test the raises ValueError for the function createConstantAttribute with wrong values for the vtk data type.""" mesh: vtkDataSet = dataSetTest( "dataset" ) with pytest.raises( ValueError ): - arrayModifiers.createConstantAttributeDataSet( mesh, [ np.int32( 42 ) ], "newAttribute", vtkDataType=64 ) + arrayModifiers.createConstantAttribute( mesh, [ np.int32( 42 ) ], "newAttribute", vtkDataType=64 ) + + +def test_createConstantAttributeRaiseValueErrorPiece( dataSetTest: vtkDataSet, ) -> None: + """Test the raises ValueError for the function createConstantAttribute with wrong values for the piece.""" + mesh: vtkDataSet = dataSetTest( "dataset" ) + with pytest.raises( ValueError ): + arrayModifiers.createConstantAttribute( mesh, [ np.int32( 42 ) ], "newAttribute", piece=Piece.BOTH ) + + +@pytest.mark.parametrize( + "meshName, attributeName", + [ + ( "multiblock", "PORO" ), # Partial + ( "multiblock", "GLOBAL_IDS_CELLS" ), # Global + ( "dataset", "PORO" ), + ] ) +def test_createConstantAttributeRaiseAttributeError( + dataSetTest: Any, + meshName: str, + attributeName: str, +) -> None: + """Test the raises ValueError for the function createConstantAttribute with a wrong AttributeName.""" + mesh: vtkDataSet | vtkMultiBlockDataSet = dataSetTest( meshName ) + with pytest.raises( AttributeError ): + arrayModifiers.createConstantAttribute( mesh, [ np.int32( 42 ) ], attributeName ) @pytest.mark.parametrize( @@ -506,34 +511,50 @@ def test_createAttributeRaiseAttributeError( @pytest.mark.parametrize( - "attributeNameFrom, attributeNameTo, piece", + "meshFromName, meshToName, attributeNameFrom, attributeNameTo, piece", [ - # Test with global attributes. - ( "GLOBAL_IDS_POINTS", "GLOBAL_IDS_POINTS_To", Piece.POINTS ), - ( "GLOBAL_IDS_CELLS", 'GLOBAL_IDS_CELLS_To', Piece.CELLS ), - # Test with partial attributes. - ( "CellAttribute", "CellAttributeTo", Piece.CELLS ), - ( "PointAttribute", "PointAttributeTo", Piece.POINTS ), + # Test multiblock. + ## Test with global attributes. + ( "multiblock", "emptymultiblock", "GLOBAL_IDS_POINTS", "newAttribute", Piece.POINTS ), + ( "multiblock", "emptymultiblock", "GLOBAL_IDS_CELLS", 'newAttribute', Piece.CELLS ), + ## Test with partial attributes. + ( "multiblock", "emptymultiblock", "CellAttribute", "newAttribute", Piece.CELLS ), + ( "multiblock", "emptymultiblock", "PointAttribute", "newAttribute", Piece.POINTS ), + # Test dataset. + ( "dataset", "emptydataset", "CellAttribute", "newAttribute", Piece.CELLS ), + ( "dataset", "emptydataset", "PointAttribute", "newAttributes", Piece.POINTS ), + # Test attribute names. The copy attribute name is a name of an attribute on the other piece. + ( "multiblock", "multiblock", "GLOBAL_IDS_POINTS", "GLOBAL_IDS_CELLS", Piece.POINTS ), + ( "multiblock", "multiblock", "CellAttribute", "PointAttribute", Piece.CELLS ), + ( "dataset", "dataset", "CellAttribute", "PointAttribute", Piece.CELLS ), ] ) def test_copyAttribute( - dataSetTest: vtkMultiBlockDataSet, + dataSetTest: Any, + meshFromName: str, + meshToName: str, attributeNameFrom: str, attributeNameTo: str, piece: Piece, ) -> None: """Test copy of cell attribute from one multiblock to another.""" - multiBlockDataSetFrom: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - multiBlockDataSetTo: vtkMultiBlockDataSet = dataSetTest( "emptymultiblock" ) + meshFrom: Any = dataSetTest( meshFromName ) + meshTo: Any = dataSetTest( meshToName ) - # Copy the attribute from the multiBlockDataSetFrom to the multiBlockDataSetTo. - arrayModifiers.copyAttribute( multiBlockDataSetFrom, multiBlockDataSetTo, attributeNameFrom, attributeNameTo, - piece ) + # Copy the attribute from the meshFrom to the meshTo. + arrayModifiers.copyAttribute( meshFrom, meshTo, attributeNameFrom, attributeNameTo, piece ) - # Parse the two multiBlockDataSet and test if the attribute has been copied. - elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetFrom ) - for blockIndex in elementaryBlockIndexes: - dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetFrom.GetDataSet( blockIndex ) ) - dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTo.GetDataSet( blockIndex ) ) + listDataSets: list[ list[ vtkDataSet ] ] = [] + if isinstance( meshFrom, vtkDataSet ): + listDataSets.append( [ meshFrom, meshTo ] ) + else: + elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( meshFrom ) + for blockIndex in elementaryBlockIndexes: + dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( meshFrom.GetDataSet( blockIndex ) ) + if isAttributeInObject( dataSetFrom, attributeNameFrom, piece ): + listDataSets.append( [ dataSetFrom, vtkDataSet.SafeDownCast( meshTo.GetDataSet( blockIndex ) ) ] ) + + for dataSetFrom, dataSetTo in listDataSets: + # Get the tested attribute and its copy. dataFrom: Union[ vtkPointData, vtkCellData ] dataTo: Union[ vtkPointData, vtkCellData ] if piece == Piece.POINTS: @@ -542,14 +563,34 @@ def test_copyAttribute( else: dataFrom = dataSetFrom.GetCellData() dataTo = dataSetTo.GetCellData() - - attributeExistTest: int = dataFrom.HasArray( attributeNameFrom ) - attributeExistCopied: int = dataTo.HasArray( attributeNameTo ) - assert attributeExistCopied == attributeExistTest + attributeTest: vtkDataArray = dataFrom.GetArray( attributeNameFrom ) + attributeCopied: vtkDataArray = dataTo.GetArray( attributeNameTo ) + + # Test the number of components and their names if multiple. + nbComponentsTest: int = attributeTest.GetNumberOfComponents() + nbComponentsCopied: int = attributeCopied.GetNumberOfComponents() + assert nbComponentsCopied == nbComponentsTest + if nbComponentsTest > 1: + componentsNamesTest: tuple[ str, ...] = tuple( + attributeTest.GetComponentName( i ) for i in range( nbComponentsTest ) ) + componentsNamesCopied: tuple[ str, ...] = tuple( + attributeCopied.GetComponentName( i ) for i in range( nbComponentsCopied ) ) + assert componentsNamesCopied == componentsNamesTest + + # Test values and their types. + npArrayTest: npt.NDArray[ Any ] = vnp.vtk_to_numpy( attributeTest ) + npArrayCopied: npt.NDArray[ Any ] = vnp.vtk_to_numpy( attributeCopied ) + assert npArrayCopied.dtype == npArrayTest.dtype + assert ( npArrayCopied == npArrayTest ).all() + + vtkDataTypeTest: int = attributeTest.GetDataType() + vtkDataTypeCopied: int = attributeCopied.GetDataType() + assert vtkDataTypeCopied == vtkDataTypeTest @pytest.mark.parametrize( "meshNameFrom, meshNameTo", [ - ( "dataset", "emptydataset" ), + ( "dataset", "other" ), + ( "other", "emptydataset" ), ( "dataset", "emptymultiblock" ), ( "multiblock", "emptydataset" ), ] ) @@ -559,169 +600,59 @@ def test_copyAttributeTypeError( meshNameTo: str, ) -> None: """Test the raises TypeError for the function copyAttribute.""" - meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshNameFrom ) - meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshNameTo ) - with pytest.raises( TypeError ): - arrayModifiers.copyAttribute( meshFrom, meshTo, "PORO", "PORO" ) - + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCellData ] + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCellData ] + meshFrom = vtkCellData() if meshNameFrom == "other" else dataSetTest( meshNameFrom ) + meshTo = vtkCellData() if meshNameTo == "other" else dataSetTest( meshNameTo ) -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 ): + with pytest.raises( TypeError ): arrayModifiers.copyAttribute( meshFrom, meshTo, "PORO", "PORO" ) +# TODO: Create two meshes similar but with two different element indexation @pytest.mark.parametrize( - "attributeNameFrom, attributeNameTo", + "meshNameFrom, meshNameTo, piece", [ - ( "PORO", "PORO" ), # An attribute PORO is already present in the mesh to - ( "newAttribute", "newAttribute" ), # newAttribute is not in the mesh from + ( "dataset", "emptydataset", Piece.BOTH ), # The piece is wrong + ( "dataset", "well", Piece.CELLS ), # Two meshes with different cells dimension + ( "multiblock", "multiblockGeosOutput", Piece.CELLS ), # Two meshes with different blocks indexation ] ) -def test_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 ), -] ) -def test_copyAttributeDataSet( - dataSetTest: vtkDataSet, - attributeNameFrom: str, - attributeNameTo: str, +def test_copyAttributeValueError( + dataSetTest: Any, + meshNameFrom: str, + meshNameTo: str, piece: Piece, ) -> None: - """Test copy of an attribute from one dataset to another.""" - dataSetFrom: vtkDataSet = dataSetTest( "dataset" ) - dataSetTo: vtkDataSet = dataSetTest( "emptydataset" ) - - # Copy the attribute from the dataSetFrom to the dataSetTo. - arrayModifiers.copyAttributeDataSet( dataSetFrom, dataSetTo, attributeNameFrom, attributeNameTo, piece ) - - # Get the tested attribute and its copy. - dataFrom: Union[ vtkPointData, vtkCellData ] - dataTo: Union[ vtkPointData, vtkCellData ] - if piece == Piece.POINTS: - dataFrom = dataSetFrom.GetPointData() - dataTo = dataSetTo.GetPointData() - else: - dataFrom = dataSetFrom.GetCellData() - dataTo = dataSetTo.GetCellData() - attributeTest: vtkDataArray = dataFrom.GetArray( attributeNameFrom ) - attributeCopied: vtkDataArray = dataTo.GetArray( attributeNameTo ) - - # Test the number of components and their names if multiple. - nbComponentsTest: int = attributeTest.GetNumberOfComponents() - nbComponentsCopied: int = attributeCopied.GetNumberOfComponents() - assert nbComponentsCopied == nbComponentsTest - if nbComponentsTest > 1: - componentsNamesTest: tuple[ str, ...] = tuple( - attributeTest.GetComponentName( i ) for i in range( nbComponentsTest ) ) - componentsNamesCopied: tuple[ str, ...] = tuple( - attributeCopied.GetComponentName( i ) for i in range( nbComponentsCopied ) ) - assert componentsNamesCopied == componentsNamesTest - - # Test values and their types. - npArrayTest: npt.NDArray[ Any ] = vnp.vtk_to_numpy( attributeTest ) - npArrayCopied: npt.NDArray[ Any ] = vnp.vtk_to_numpy( attributeCopied ) - assert npArrayCopied.dtype == npArrayTest.dtype - assert ( npArrayCopied == npArrayTest ).all() - - vtkDataTypeTest: int = attributeTest.GetDataType() - vtkDataTypeCopied: int = attributeCopied.GetDataType() - assert vtkDataTypeCopied == vtkDataTypeTest - - -def test_copyAttributeDataSetTypeError( dataSetTest: Any, ) -> None: - """Test the raises TypeError for the function copyAttributeDataSet with a mesh from with a wrong type.""" - meshFrom: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - meshTo: vtkDataSet = dataSetTest( "emptydataset" ) - with pytest.raises( TypeError ): - arrayModifiers.copyAttributeDataSet( meshFrom, meshTo, "PORO", "PORO" ) - - -def test_copyAttributeDataSetAttributeError( dataSetTest: vtkDataSet, ) -> None: - """Test the raises AttributeError for the function copyAttributeDataSet with an attributeNameFrom not in the mesh From.""" - meshFrom: vtkDataSet = dataSetTest( "dataset" ) - meshTo: vtkDataSet = dataSetTest( "emptydataset" ) - with pytest.raises( AttributeError ): - arrayModifiers.copyAttributeDataSet( meshFrom, meshTo, "newAttribute", "newAttribute" ) + """Test the raises ValueError for the function copyAttribute.""" + meshFrom: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshNameFrom ) + meshTo: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshNameTo ) + with pytest.raises( ValueError ): + arrayModifiers.copyAttribute( meshFrom, meshTo, "GLOBAL_IDS_CELLS", "newAttribute", piece=piece ) @pytest.mark.parametrize( - "isMeshFrom, meshToName", + "meshNameFrom, meshNameTo, attributeNameFrom, attributeNameTo", [ - ( True, "emptymultiblock" ), # The mesh to is not a vtkDataSet. - ( False, "emptyFracture" ), # The mesh from is not a mesh. + # The copy attribute name is already an attribute on the mesh to + ( "dataset", "dataset", "PORO", "PORO" ), + ( "multiblock", "multiblock", "PORO", "PORO" ), + ( "multiblock", "multiblock", "PORO", "GLOBAL_IDS_CELLS" ), + # The attribute to copy is not in the mesh From + # ( "dataset", "emptydataset", "newAttribute", "newAttribute" ), TODO: activate when the PR 223 is merged + ( "multiblock", "emptymultiblock", "newAttribute", "newAttribute" ), ] ) -def test_transferAttributeToDataSetWithElementMapTypeError( +def test_copyAttributeAttributeError( 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, + meshNameFrom: str, + meshNameTo: str, + attributeNameFrom: str, + attributeNameTo: 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 ) + """Test the raises AttributeError for the function copyAttribute.""" + meshFrom: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshNameFrom ) + meshTo: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshNameTo ) 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 ) + arrayModifiers.copyAttribute( meshFrom, meshTo, attributeNameFrom, attributeNameTo ) @pytest.mark.parametrize( "meshFromName, meshToName, attributeName, piece, defaultValueTest", [ @@ -777,28 +708,75 @@ def test_transferAttributeWithElementMap( assert np.all( arrayTo[ idElementTo ] == arrayFrom[ idElementFrom ] ) +@pytest.mark.parametrize( "meshNameFrom, meshNameTo", [ + ( "dataset", "other" ), + ( "other", "emptydataset" ), + ( "other", "other" ), +] ) def test_transferAttributeWithElementMapTypeError( - dataSetTest: vtkMultiBlockDataSet, - getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], + dataSetTest: Any, + meshNameFrom: str, + meshNameTo: str, ) -> None: - """Test the raises TypeError for the function transferAttributeWithElementMap with the mesh to with a wrong type.""" - meshFrom: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - meshTo: bool = False - elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( "multiblock", "emptymultiblock", Piece.CELLS ) + """Test the raises TypeError for the function transferAttributeWithElementMap.""" + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCellData ] + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCellData ] + meshFrom = vtkCellData() if meshNameFrom == "other" else dataSetTest( meshNameFrom ) + meshTo = vtkCellData() if meshNameTo == "other" else dataSetTest( meshNameTo ) + with pytest.raises( TypeError ): - arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, "FAULT", Piece.CELLS ) + arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, {}, "GLOBAL_IDS_CELLS", Piece.CELLS ) +@pytest.mark.parametrize( + "meshNameFrom, meshNameTo, attributeName", + [ + ( "multiblock", "emptymultiblock", "PORO" ), # The attribute is partial in the mesh From + ( "dataset", "emptydataset", "newAttribute" ), # The attribute is not in the mesh From + ( "dataset", "emptydataset", "GLOBAL_IDS_CELLS" ), # The attribute is already in the mesh to + ( "multiblock", "emptymultiblock", "GLOBAL_IDS_CELLS" ), # The attribute is already in the mesh to + ] ) def test_transferAttributeWithElementMapAttributeError( dataSetTest: vtkMultiBlockDataSet, getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], + meshNameFrom: str, + meshNameTo: str, + attributeName: str, ) -> None: """Test the raises AttributeError for the function transferAttributeWithElementMap with an attribute already in the mesh to.""" - meshFrom: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - meshTo: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( "multiblock", "emptymultiblock", Piece.CELLS ) + meshFrom: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshNameFrom ) + meshTo: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshNameTo ) + elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( meshNameFrom, meshNameTo, Piece.CELLS ) with pytest.raises( AttributeError ): - arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, "FAULT", Piece.CELLS ) + arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, attributeName, Piece.CELLS ) + + +@pytest.mark.parametrize( + "meshNameTo, meshNameToMap, flatIdDataSetTo, piece", + [ + ( "emptyFracture", "emptyFracture", 0, Piece.BOTH ), # The piece is wrong. + ( "emptyFracture", "emptyFracture", 1, Piece.CELLS ), # The flatIdDataSetTo is wrong. + ( "emptyFracture", "emptymultiblock", 0, Piece.CELLS ), # The map is wrong. + ] ) +def test_transferAttributeWithElementMapValueError( + dataSetTest: vtkDataSet, + getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], + meshNameTo: str, + meshNameToMap: str, + flatIdDataSetTo: int, + piece: Piece, +) -> None: + """Test the raises ValueError for the function transferAttributeWithElementMap.""" + meshFrom: vtkDataSet = dataSetTest( "dataset" ) + meshTo: vtkDataSet = dataSetTest( meshNameTo ) + elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( "dataset", meshNameToMap, False ) + with pytest.raises( ValueError ): + arrayModifiers.transferAttributeWithElementMap( meshFrom, + meshTo, + elementMap, + "FAULT", + piece, + flatIdDataSetTo=flatIdDataSetTo ) @pytest.mark.parametrize( "attributeName, piece", [ diff --git a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py index 1c8dbf40..3c33ea0d 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -181,8 +181,12 @@ def applyFilter( self: Self ) -> None: raise ValueError( f"The two meshes do not have any shared { self.piece.value }." ) for attributeName in self.attributeNames: - transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, self.piece, - self.logger ) + transferAttributeWithElementMap( self.meshFrom, + self.meshTo, + self.ElementMap, + attributeName, + self.piece, + logger=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 572f1f09..9540cbea 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -16,8 +16,7 @@ from geos.mesh.utils.arrayHelpers import ( getArrayInObject, getComponentNames, getNumberOfComponents, getVtkDataTypeInObject, isAttributeGlobal, getAttributePieceInfo, checkValidValuesInDataSet, checkValidValuesInMultiBlock ) -from geos.mesh.utils.arrayModifiers import ( createAttribute, createConstantAttributeDataSet, - createConstantAttributeMultiBlock ) +from geos.mesh.utils.arrayModifiers import ( createAttribute, createConstantAttribute ) from geos.mesh.utils.multiblockHelpers import getBlockElementIndexesFlatten __doc__ = """ @@ -203,12 +202,12 @@ def applyFilter( self: Self ) -> None: self.logger.warning( f"The region indexes entered are not in the region attribute { self.regionName }." ) - createConstantAttributeMultiBlock( self.mesh, - self.defaultValue, - self.newAttributeName, - componentNames=self.componentNames, - piece=self.piece, - logger=self.logger ) + createConstantAttribute( self.mesh, + self.defaultValue, + self.newAttributeName, + componentNames=self.componentNames, + piece=self.piece, + logger=self.logger ) else: if len( invalidIndexes ) > 0: @@ -239,12 +238,12 @@ def applyFilter( self: Self ) -> None: self.logger.warning( f"The region indexes entered are not in the region attribute { self.regionName }." ) - createConstantAttributeDataSet( self.mesh, - self.defaultValue, - self.newAttributeName, - componentNames=self.componentNames, - piece=self.piece, - logger=self.logger ) + createConstantAttribute( self.mesh, + self.defaultValue, + self.newAttributeName, + componentNames=self.componentNames, + piece=self.piece, + logger=self.logger ) else: if len( invalidIndexes ) > 0: diff --git a/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py b/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py index 13823096..a3c549d6 100644 --- a/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py +++ b/mesh-doctor/src/geos/mesh_doctor/actions/generateCube.py @@ -10,7 +10,7 @@ from vtkmodules.vtkCommonDataModel import ( vtkCellArray, vtkHexahedron, vtkRectilinearGrid, vtkUnstructuredGrid, VTK_HEXAHEDRON ) from geos.mesh.io.vtkIO import VtkOutput, writeMesh -from geos.mesh.utils.arrayModifiers import createConstantAttributeDataSet +from geos.mesh.utils.arrayModifiers import createConstantAttribute from geos.mesh_doctor.actions.generateGlobalIds import buildGlobalIds from geos.mesh_doctor.parsing.cliParsing import setupLogger from geos.utils.pieceEnum import Piece @@ -184,12 +184,12 @@ def addFields( mesh: vtkUnstructuredGrid, fields: Iterable[ FieldInfo ] ) -> vtk piece: Piece = Piece.POINTS if fieldInfo.support == "POINTS" else Piece.CELLS # Create list of values (all 1.0) for each component listValues = [ 1.0 ] * fieldInfo.dimension - # Use the robust createConstantAttributeDataSet function - createConstantAttributeDataSet( dataSet=mesh, - listValues=listValues, - attributeName=fieldInfo.name, - piece=piece, - logger=setupLogger ) + # Use the robust createConstantAttribute function + createConstantAttribute( mesh=mesh, + listValues=listValues, + attributeName=fieldInfo.name, + piece=piece, + logger=setupLogger ) return mesh