From 216e4d8cad7bcde4897bfcbc5e89d2a9c848102a Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 9 Feb 2026 16:24:46 +0100 Subject: [PATCH 01/10] Factorize getComponentNames* in one function --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 58 +++++-------------- .../src/geos/mesh/utils/arrayModifiers.py | 3 +- geos-mesh/tests/test_arrayHelpers.py | 46 ++++++++------- 3 files changed, 41 insertions(+), 66 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index ad88ba02..c18f6a35 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -615,7 +615,9 @@ def getVtkArrayInObject( dataSet: vtkDataSet, attributeName: str, piece: Piece ) Returns: vtkDataArray: The vtk array corresponding to input attribute name. """ - assert isAttributeInObject( dataSet, attributeName, piece ), f"{attributeName} is not in input mesh." + if not isAttributeInObject( dataSet, attributeName, piece ): + raise AttributeError( f"{ attributeName } is not in input mesh." ) + dataArray: vtkDataArray if piece == Piece.POINTS: dataArray = dataSet.GetPointData().GetArray( attributeName ) @@ -708,55 +710,25 @@ def getComponentNames( tuple[str,...]: Names of the components. """ if isinstance( mesh, vtkDataSet ): - return getComponentNamesDataSet( mesh, attributeName, piece ) + array: vtkDataArray = getVtkArrayInObject( mesh, attributeName, piece ) + componentNames: list[ str ] = [] + if array.GetNumberOfComponents() > 1: + componentNames += [ array.GetComponentName( i ) for i in range( array.GetNumberOfComponents() ) ] elif isinstance( mesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): - return getComponentNamesMultiBlock( mesh, attributeName, piece ) + elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( mesh ) + for blockIndex in elementaryBlockIndexes: + dataSet: vtkDataSet = vtkDataSet.SafeDownCast( mesh.GetDataSet( blockIndex ) ) + try: + return getComponentNames( dataSet, attributeName, piece ) + except: + continue + raise AttributeError( f"{ attributeName } is not in input mesh.") else: raise TypeError( "Mesh type is not managed." ) - -def getComponentNamesDataSet( dataSet: vtkDataSet, attributeName: str, piece: Piece ) -> tuple[ str, ...]: - """Get the name of the components of attribute attributeName in dataSet. - - Args: - dataSet (vtkDataSet): DataSet where the attribute is. - attributeName (str): Name of the attribute. - piece (Piece): The piece of the attribute. - - Returns: - tuple[str,...]: Names of the components. - """ - array: vtkDataArray = getVtkArrayInObject( dataSet, attributeName, piece ) - componentNames: list[ str ] = [] - - if array.GetNumberOfComponents() > 1: - componentNames += [ array.GetComponentName( i ) for i in range( array.GetNumberOfComponents() ) ] return tuple( componentNames ) -def getComponentNamesMultiBlock( - multiBlockDataSet: Union[ vtkMultiBlockDataSet, vtkCompositeDataSet ], - attributeName: str, - piece: Piece, -) -> tuple[ str, ...]: - """Get the name of the components of attribute in MultiBlockDataSet. - - Args: - multiBlockDataSet (vtkMultiBlockDataSet | vtkCompositeDataSet): DataSet where the attribute is. - attributeName (str): Name of the attribute. - piece (Piece): The piece of the attribute. - - Returns: - tuple[str,...]: Names of the components. - """ - elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSet ) - for blockIndex in elementaryBlockIndexes: - dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) - if isAttributeInObject( dataSet, attributeName, piece ): - return getComponentNamesDataSet( dataSet, attributeName, piece ) - return () - - def getAttributeValuesAsDF( surface: vtkPolyData, attributeNames: tuple[ str, ...], piece: Piece = Piece.CELLS ) -> pd.DataFrame: diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 6bc763b3..11478895 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -29,7 +29,6 @@ from vtkmodules.vtkCommonCore import ( vtkDataArray, vtkPoints, vtkLogger ) from geos.mesh.utils.arrayHelpers import ( getComponentNames, - getComponentNamesDataSet, getAttributesWithNumberOfComponents, getArrayInObject, isAttributeInObject, @@ -635,7 +634,7 @@ def copyAttributeDataSet( 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 ) + componentNames: tuple[ str, ...] = getComponentNames( dataSetFrom, attributeNameFrom, piece ) vtkArrayType: int = getVtkArrayTypeInObject( dataSetFrom, attributeNameFrom, piece ) createAttribute( dataSetTo, npArray, attributeNameTo, componentNames, piece, vtkArrayType, logger ) diff --git a/geos-mesh/tests/test_arrayHelpers.py b/geos-mesh/tests/test_arrayHelpers.py index 4c8a31c6..18dba6d5 100644 --- a/geos-mesh/tests/test_arrayHelpers.py +++ b/geos-mesh/tests/test_arrayHelpers.py @@ -497,33 +497,37 @@ def test_getNumberOfComponentsTypeError() -> None: arrayHelpers.getNumberOfComponents( mesh, "attribute", Piece.CELLS ) -@pytest.mark.parametrize( "attributeName, piece, expected", [ - ( "PERM", Piece.CELLS, ( "AX1", "AX2", "AX3" ) ), - ( "PORO", Piece.CELLS, () ), +@pytest.mark.parametrize( "meshName, attributeName, piece, expected", [ + ( "dataset", "PERM", Piece.CELLS, ( "AX1", "AX2", "AX3" ) ), + ( "dataset", "PORO", Piece.CELLS, () ), + ( "multiblock", "PERM", Piece.CELLS, ( "AX1", "AX2", "AX3" ) ), + ( "multiblock", "PORO", Piece.CELLS, () ), ] ) -def test_getComponentNamesDataSet( dataSetTest: vtkDataSet, attributeName: str, piece: Piece, - expected: tuple[ str, ...] ) -> None: - """Test getting the component names of an attribute from a dataset.""" - vtkDataSetTest: vtkDataSet = dataSetTest( "dataset" ) - obtained: tuple[ str, ...] = arrayHelpers.getComponentNamesDataSet( vtkDataSetTest, attributeName, piece ) +def test_getComponentNames( + dataSetTest: Any, + meshName: str, + attributeName: str, + piece: Piece, + expected: tuple[ str, ...] ) -> None: + """Test getting the component names of an attribute from a mesh.""" + vtkDataSetTest: Any = dataSetTest( meshName ) + obtained: tuple[ str, ...] = arrayHelpers.getComponentNames( vtkDataSetTest, attributeName, piece ) assert obtained == expected -@pytest.mark.parametrize( "attributeName, piece, expected", [ - ( "PERM", Piece.CELLS, ( "AX1", "AX2", "AX3" ) ), - ( "PORO", Piece.CELLS, () ), -] ) -def test_getComponentNamesMultiBlock( - dataSetTest: vtkMultiBlockDataSet, - attributeName: str, - piece: Piece, - expected: tuple[ str, ...], +def test_getComponentNamesRaises( + dataSetTest: Any, ) -> None: """Test getting the component names of an attribute from a multiblock.""" - vtkMultiBlockDataSetTest: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - obtained: tuple[ str, ...] = arrayHelpers.getComponentNamesMultiBlock( vtkMultiBlockDataSetTest, attributeName, - piece ) - assert obtained == expected + # TypeError + meshWrongType: vtkFieldData = vtkFieldData() + with pytest.raises( TypeError ): + arrayHelpers.getComponentNames( meshWrongType, "PORO", Piece.CELLS ) + + # AttributeError + mesh: vtkDataSet = dataSetTest( "dataset" ) + with pytest.raises( AttributeError ): + arrayHelpers.getComponentNames( mesh, "attributeName", Piece.POINTS ) @pytest.mark.parametrize( "attributeNames, piece, expected_columns", [ From 47e7eed4efc12e7abe7e89b8d83b1a4684f96a09 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 9 Feb 2026 16:28:26 +0100 Subject: [PATCH 02/10] Update the doc --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index c18f6a35..b64242e3 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -26,7 +26,7 @@ The getter functions: - get the array of an attribute (one for dataset, one for multiblockDataset, one for the both and one for fieldData) - - get the component names of an attribute (one for dataset, one for multiblockDataset and one for the both) + - get the component names of an attribute - get the number of components of an attribute (one for dataset, one for multiblockDataset and one for the both) - get the piece of an attribute (for any meshes) - get the values of an attribute as data frame (for polyData only) @@ -699,7 +699,7 @@ def getComponentNames( attributeName: str, piece: Piece, ) -> tuple[ str, ...]: - """Get the name of the components of attribute attributeName in dataSet. + """Get the name of the components of the attribute 'attributeName' in a mesh. Args: mesh (vtkDataSet | vtkMultiBlockDataSet | vtkCompositeDataSet | vtkDataObject): Mesh where the attribute is. @@ -708,6 +708,10 @@ def getComponentNames( Returns: tuple[str,...]: Names of the components. + + Raises: + AttributeError: The attribute 'attributeName' is not in the mesh for the given piece. + TypeError: The mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet. """ if isinstance( mesh, vtkDataSet ): array: vtkDataArray = getVtkArrayInObject( mesh, attributeName, piece ) @@ -722,9 +726,9 @@ def getComponentNames( return getComponentNames( dataSet, attributeName, piece ) except: continue - raise AttributeError( f"{ attributeName } is not in input mesh.") + raise AttributeError( f"The attribute '{ attributeName }' is not in the mesh for the given piece { piece }.") else: - raise TypeError( "Mesh type is not managed." ) + raise TypeError( "The mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet." ) return tuple( componentNames ) From fbd5c3b01f62088c2a9b4ab8de45a0cd2562cbb5 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 9 Feb 2026 16:51:28 +0100 Subject: [PATCH 03/10] Factorize the function getNumberOfComponent* in one --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 70 +++++++------------ .../src/geos/mesh/utils/arrayModifiers.py | 4 +- geos-mesh/tests/test_arrayHelpers.py | 67 +++++++----------- 3 files changed, 55 insertions(+), 86 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index b64242e3..eed81e1a 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -27,7 +27,7 @@ The getter functions: - get the array of an attribute (one for dataset, one for multiblockDataset, one for the both and one for fieldData) - get the component names of an attribute - - get the number of components of an attribute (one for dataset, one for multiblockDataset and one for the both) + - get the number of components of an attribute - get the piece of an attribute (for any meshes) - get the values of an attribute as data frame (for polyData only) - get the vtk type of an attribute (one for dataset, one for multiblockDataset and one for the both) @@ -637,7 +637,7 @@ def getNumberOfComponents( attributeName: str, piece: Piece, ) -> int: - """Get the number of components of attribute attributeName in dataSet. + """Get the number of components of the attribute 'attributeName' in the mesh for a given piece. Args: mesh (vtkMultiBlockDataSet | vtkCompositeDataSet | vtkDataSet): Mesh where the attribute is. @@ -646,52 +646,31 @@ def getNumberOfComponents( Returns: int: Number of components. + + Raises: + AttributeError: The attribute 'attributeName' is not in the mesh for the given piece. + TypeError: The mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet. + ValueError: The attribute's piece must be 'cells', 'points' or 'field'. """ + nbComponents: int = 0 if isinstance( mesh, vtkDataSet ): - return getNumberOfComponentsDataSet( mesh, attributeName, piece ) + array: vtkDataArray = getVtkArrayInObject( mesh, attributeName, piece ) + nbComponents = array.GetNumberOfComponents() elif isinstance( mesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): - return getNumberOfComponentsMultiBlock( mesh, attributeName, piece ) + elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( mesh ) + for blockIndex in elementaryBlockIndexes: + dataSet: vtkDataSet = vtkDataSet.SafeDownCast( mesh.GetDataSet( blockIndex ) ) + try: + return getNumberOfComponents( dataSet, attributeName, piece ) + except ValueError as e: + raise e + except AttributeError: + continue + raise AttributeError( f"The attribute '{ attributeName }' is not in the mesh for the given piece { piece }.") else: raise TypeError( "The mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet." ) - -def getNumberOfComponentsDataSet( dataSet: vtkDataSet, attributeName: str, piece: Piece ) -> int: - """Get the number of components of attribute attributeName in dataSet. - - Args: - dataSet (vtkDataSet): DataSet where the attribute is. - attributeName (str): Name of the attribute. - piece (Piece): The piece of the attribute. - - Returns: - int: Number of components. - """ - array: vtkDataArray = getVtkArrayInObject( dataSet, attributeName, piece ) - return array.GetNumberOfComponents() - - -def getNumberOfComponentsMultiBlock( - multiBlockDataSet: Union[ vtkMultiBlockDataSet, vtkCompositeDataSet ], - attributeName: str, - piece: Piece, -) -> int: - """Get the number of components of attribute attributeName in dataSet. - - Args: - multiBlockDataSet (vtkMultiBlockDataSet | vtkCompositeDataSet): multi block data Set where the attribute is. - attributeName (str): Name of the attribute. - piece (Piece): The piece of the attribute. - - Returns: - int: Number of components. - """ - elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSet ) - for blockIndex in elementaryBlockIndexes: - dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) - if isAttributeInObject( dataSet, attributeName, piece ): - array: vtkDataArray = getVtkArrayInObject( dataSet, attributeName, piece ) - return array.GetNumberOfComponents() - return 0 + return nbComponents def getComponentNames( @@ -712,10 +691,11 @@ def getComponentNames( Raises: AttributeError: The attribute 'attributeName' is not in the mesh for the given piece. TypeError: The mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet. + ValueError: The attribute's piece must be 'cells', 'points' or 'field'. """ + componentNames: list[ str ] = [] if isinstance( mesh, vtkDataSet ): array: vtkDataArray = getVtkArrayInObject( mesh, attributeName, piece ) - componentNames: list[ str ] = [] if array.GetNumberOfComponents() > 1: componentNames += [ array.GetComponentName( i ) for i in range( array.GetNumberOfComponents() ) ] elif isinstance( mesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): @@ -724,7 +704,9 @@ def getComponentNames( dataSet: vtkDataSet = vtkDataSet.SafeDownCast( mesh.GetDataSet( blockIndex ) ) try: return getComponentNames( dataSet, attributeName, piece ) - except: + except ValueError as e: + raise e + except AttributeError: continue raise AttributeError( f"The attribute '{ attributeName }' is not in the mesh for the given piece { piece }.") else: diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 11478895..870502a5 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -38,7 +38,7 @@ getVtkArrayTypeInObject, getVtkArrayTypeInMultiBlock, getVtkDataTypeInObject, - getNumberOfComponentsMultiBlock, + getNumberOfComponents, ) from geos.mesh.utils.multiblockHelpers import getBlockElementIndexesFlatten from geos.utils.Errors import VTKError @@ -105,7 +105,7 @@ def fillPartialAttributes( # Get information of the attribute to fill. vtkDataType: int = getVtkArrayTypeInMultiBlock( multiBlockDataSet, attributeName, piece ) - nbComponents: int = getNumberOfComponentsMultiBlock( multiBlockDataSet, attributeName, piece ) + nbComponents: int = getNumberOfComponents( multiBlockDataSet, attributeName, piece ) componentNames: tuple[ str, ...] = () if nbComponents > 1: componentNames = getComponentNames( multiBlockDataSet, attributeName, piece ) diff --git a/geos-mesh/tests/test_arrayHelpers.py b/geos-mesh/tests/test_arrayHelpers.py index 18dba6d5..5add692f 100644 --- a/geos-mesh/tests/test_arrayHelpers.py +++ b/geos-mesh/tests/test_arrayHelpers.py @@ -438,44 +438,12 @@ def test_getVtkArrayInObject( request: pytest.FixtureRequest, arrayExpected: npt assert ( obtained_as_np == arrayExpected ).all() -@pytest.mark.parametrize( "attributeName, piece, expected", [ - ( "PORO", Piece.CELLS, 1 ), - ( "PERM", Piece.CELLS, 3 ), - ( "PointAttribute", Piece.POINTS, 3 ), -] ) -def test_getNumberOfComponentsDataSet( - dataSetTest: vtkDataSet, - attributeName: str, - piece: Piece, - expected: int, -) -> None: - """Test getting the number of components of an attribute from a dataset.""" - vtkDataSetTest: vtkDataSet = dataSetTest( "dataset" ) - obtained: int = arrayHelpers.getNumberOfComponentsDataSet( vtkDataSetTest, attributeName, piece ) - assert obtained == expected - - -@pytest.mark.parametrize( "attributeName, piece, expected", [ - ( "PORO", Piece.CELLS, 1 ), - ( "PERM", Piece.CELLS, 3 ), - ( "PointAttribute", Piece.POINTS, 3 ), -] ) -def test_getNumberOfComponentsMultiBlock( - dataSetTest: vtkMultiBlockDataSet, - attributeName: str, - piece: Piece, - expected: int, -) -> None: - """Test getting the number of components of an attribute from a multiblock.""" - vtkMultiBlockDataSetTest: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - obtained: int = arrayHelpers.getNumberOfComponentsMultiBlock( vtkMultiBlockDataSetTest, attributeName, piece ) - - assert obtained == expected - - @pytest.mark.parametrize( "meshName, attributeName, piece, expected", [ ( "dataset", "PORO", Piece.CELLS, 1 ), ( "dataset", "PERM", Piece.CELLS, 3 ), + ( "dataset", "PointAttribute", Piece.POINTS, 3 ), + ( "multiblock", "PORO", Piece.CELLS, 1 ), + ( "multiblock", "PERM", Piece.CELLS, 3 ), ( "multiblock", "PointAttribute", Piece.POINTS, 3 ), ] ) def test_getNumberOfComponents( @@ -490,11 +458,25 @@ def test_getNumberOfComponents( assert arrayHelpers.getNumberOfComponents( mesh, attributeName, piece ) == expected -def test_getNumberOfComponentsTypeError() -> None: - """Test getNumberOfComponents TypeError raises.""" - mesh: vtkCellData = vtkCellData() +def test_getNumberOfComponentsRaises( + dataSetTest: Any, +) -> None: + """Test getNumberOfComponents fails.""" + # TypeError + meshWrongType: vtkCellData = vtkCellData() with pytest.raises( TypeError ): - arrayHelpers.getNumberOfComponents( mesh, "attribute", Piece.CELLS ) + arrayHelpers.getNumberOfComponents( meshWrongType, "PORO", Piece.CELLS ) + + # AttributeError + mesh: vtkDataSet = dataSetTest( "dataset" ) + with pytest.raises( AttributeError ): + arrayHelpers.getNumberOfComponents( mesh, "attributeName", Piece.POINTS ) + + # TODO + # # ValueError + # with pytest.raises( ValueError ): + # arrayHelpers.getNumberOfComponents( mesh, "mass", Piece.BOTH ) + @pytest.mark.parametrize( "meshName, attributeName, piece, expected", [ @@ -518,7 +500,7 @@ def test_getComponentNames( def test_getComponentNamesRaises( dataSetTest: Any, ) -> None: - """Test getting the component names of an attribute from a multiblock.""" + """Test getting the component names fails.""" # TypeError meshWrongType: vtkFieldData = vtkFieldData() with pytest.raises( TypeError ): @@ -529,6 +511,11 @@ def test_getComponentNamesRaises( with pytest.raises( AttributeError ): arrayHelpers.getComponentNames( mesh, "attributeName", Piece.POINTS ) + # TODO + # # ValueError + # with pytest.raises( ValueError ): + # arrayHelpers.getComponentNames( mesh, "mass", Piece.BOTH ) + @pytest.mark.parametrize( "attributeNames, piece, expected_columns", [ ( ( "collocated_nodes", ), Piece.POINTS, ( "collocated_nodes_0", "collocated_nodes_1" ) ), From 5b6b0e1d7a37df154d8c5c0fdaa8342519fe1dd2 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 9 Feb 2026 17:10:02 +0100 Subject: [PATCH 04/10] factorize isAttributeInObject* in one --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 74 ++++++------------- .../src/geos/mesh/utils/arrayModifiers.py | 24 +++--- geos-mesh/tests/test_arrayHelpers.py | 46 ++---------- 3 files changed, 42 insertions(+), 102 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index eed81e1a..a51ef053 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -450,7 +450,7 @@ def getAttributesFromDataSet( dataSet: vtkDataSet, piece: Piece ) -> dict[ str, def isAttributeInObject( mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ], attributeName: str, piece: Piece ) -> bool: - """Check if an attribute is in the input object. + """Check if an attribute is in the input mesh for the given piece. Args: mesh (vtkMultiBlockDataSet | vtkDataSet): Input mesh. @@ -458,62 +458,34 @@ def isAttributeInObject( mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ], attrib piece (Piece): The piece of the attribute. Returns: - bool: True if the attribute is in the table, False otherwise. + bool: True if the attribute is on the mesh, False otherwise. + + Raises: + TypeError: The mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet. """ - if isinstance( mesh, vtkMultiBlockDataSet ): - return isAttributeInObjectMultiBlockDataSet( mesh, attributeName, piece ) - elif isinstance( mesh, vtkDataSet ): - return isAttributeInObjectDataSet( mesh, attributeName, piece ) + if isinstance( mesh, vtkDataSet ): + if piece == Piece.FIELD: + return bool( mesh.GetFieldData().HasArray( attributeName ) ) + elif piece == Piece.POINTS: + return bool( mesh.GetPointData().HasArray( attributeName ) ) + elif piece == Piece.CELLS: + return bool( mesh.GetCellData().HasArray( attributeName ) ) + elif piece == Piece.BOTH: + onPoints: int = mesh.GetPointData().HasArray( attributeName ) + onCells: int = mesh.GetCellData().HasArray( attributeName ) + return onCells == onPoints == 1 + elif isinstance( mesh, vtkMultiBlockDataSet ): + elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( mesh ) + for blockIndex in elementaryBlockIndexes: + dataSet: vtkDataSet = vtkDataSet.SafeDownCast( mesh.GetDataSet( blockIndex ) ) + if isAttributeInObject( dataSet, attributeName, piece ): + return True else: raise TypeError( "Input object must be a vtkDataSet or vtkMultiBlockDataSet." ) - -def isAttributeInObjectMultiBlockDataSet( multiBlockDataSet: vtkMultiBlockDataSet, attributeName: str, - piece: Piece ) -> bool: - """Check if an attribute is in the input object. - - Args: - multiBlockDataSet (vtkMultiBlockDataSet): Input multiBlockDataSet. - attributeName (str): Name of the attribute. - piece (Piece): The piece of the attribute. - - Returns: - bool: True if the attribute is in the table, False otherwise. - """ - elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSet ) - for blockIndex in elementaryBlockIndexes: - dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) - if isAttributeInObjectDataSet( dataSet, attributeName, piece ): - return True - return False -def isAttributeInObjectDataSet( dataSet: vtkDataSet, attributeName: str, piece: Piece ) -> bool: - """Check if an attribute is in the input object for the input piece. - - Args: - dataSet (vtkDataSet): Input dataSet. - attributeName (str): Name of the attribute. - piece (Piece): The piece of the attribute. - - Returns: - bool: True if the attribute is in the table, False otherwise. - """ - if piece == Piece.FIELD: - return bool( dataSet.GetFieldData().HasArray( attributeName ) ) - elif piece == Piece.POINTS: - return bool( dataSet.GetPointData().HasArray( attributeName ) ) - elif piece == Piece.CELLS: - return bool( dataSet.GetCellData().HasArray( attributeName ) ) - elif piece == Piece.BOTH: - onPoints: int = dataSet.GetPointData().HasArray( attributeName ) - onCells: int = dataSet.GetCellData().HasArray( attributeName ) - return onCells == onPoints == 1 - else: - return False - - def isAttributeGlobal( multiBlockDataSet: vtkMultiBlockDataSet, attributeName: str, piece: Piece ) -> bool: """Check if an attribute is global in the input multiBlockDataSet. @@ -528,7 +500,7 @@ def isAttributeGlobal( multiBlockDataSet: vtkMultiBlockDataSet, attributeName: s elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSet ) for blockIndex in elementaryBlockIndexes: dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) - if not isAttributeInObjectDataSet( dataSet, attributeName, piece ): + if not isAttributeInObject( dataSet, attributeName, piece ): return False return True diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 870502a5..390a1537 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -32,8 +32,6 @@ getAttributesWithNumberOfComponents, getArrayInObject, isAttributeInObject, - isAttributeInObjectDataSet, - isAttributeInObjectMultiBlockDataSet, isAttributeGlobal, getVtkArrayTypeInObject, getVtkArrayTypeInMultiBlock, @@ -96,7 +94,7 @@ def fillPartialAttributes( raise TypeError( "Input mesh has to be inherited from vtkMultiBlockDataSet." ) # Check if the attribute exist in the input mesh. - if not isAttributeInObjectMultiBlockDataSet( multiBlockDataSet, attributeName, piece ): + if not isAttributeInObject( multiBlockDataSet, attributeName, piece ): raise AttributeError( f"The attribute { attributeName } is not in the mesh." ) # Check if the attribute is partial. @@ -153,7 +151,7 @@ def fillPartialAttributes( elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSet ) for blockIndex in elementaryBlockIndexes: dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) - if not isAttributeInObjectDataSet( dataSet, attributeName, piece ): + if not isAttributeInObject( dataSet, attributeName, piece ): createConstantAttributeDataSet( dataSet, listValues, attributeName, componentNames, piece, vtkDataType, logger ) @@ -332,7 +330,7 @@ def createConstantAttributeMultiBlock( 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 ): + if isAttributeInObject( 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. @@ -473,12 +471,12 @@ def createAttribute( raise TypeError( "Input dataSet has to be inherited from vtkDataSet." ) # Check if the attribute already exist in the input mesh. - if isAttributeInObjectDataSet( dataSet, attributeName, piece ): + if isAttributeInObject( dataSet, attributeName, piece ): raise AttributeError( f"The attribute { attributeName } is already present in the mesh." ) # Check if an attribute with the same name exist on the opposite piece (points or cells) on the input mesh. oppositePiece: Piece = Piece.CELLS if piece == Piece.POINTS else Piece.POINTS - if isAttributeInObjectDataSet( dataSet, attributeName, oppositePiece ): + if isAttributeInObject( dataSet, attributeName, oppositePiece ): logger.warning( f"The attribute { attributeName } exist on the opposite piece { oppositePiece.value }." ) # Check the coherency between the given array type and the vtk array type if it exist. @@ -573,11 +571,11 @@ def copyAttribute( raise TypeError( "Final mesh has to be inherited from vtkMultiBlockDataSet." ) # Check if the attribute exist in the multiBlockDataSetFrom. - if not isAttributeInObjectMultiBlockDataSet( multiBlockDataSetFrom, attributeNameFrom, piece ): + if not isAttributeInObject( 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 ): + if isAttributeInObject( multiBlockDataSetTo, attributeNameTo, piece ): raise AttributeError( f"The attribute { attributeNameTo } is already present in the final mesh." ) # Check if the two multiBlockDataSets are similar. @@ -591,7 +589,7 @@ def copyAttribute( dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetFrom.GetDataSet( idBlock ) ) dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTo.GetDataSet( idBlock ) ) - if isAttributeInObjectDataSet( dataSetFrom, attributeNameFrom, piece ): + if isAttributeInObject( dataSetFrom, attributeNameFrom, piece ): copyAttributeDataSet( dataSetFrom, dataSetTo, attributeNameFrom, attributeNameTo, piece, logger ) return @@ -630,7 +628,7 @@ def copyAttributeDataSet( raise TypeError( "Source mesh has to be inherited from vtkDataSet." ) # Check if the attribute exist in the dataSetFrom. - if not isAttributeInObjectDataSet( dataSetFrom, attributeNameFrom, piece ): + if not isAttributeInObject( dataSetFrom, attributeNameFrom, piece ): raise AttributeError( f"The attribute { attributeNameFrom } is not in the source mesh." ) npArray: npt.NDArray[ Any ] = getArrayInObject( dataSetFrom, attributeNameFrom, piece ) @@ -811,7 +809,7 @@ def transferAttributeWithElementMap( if isinstance( meshTo, vtkDataSet ): transferAttributeToDataSetWithElementMap( meshFrom, meshTo, elementMap, attributeName, piece, 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 ) @@ -918,7 +916,7 @@ def createCellCenterAttribute( logger = getLogger( "createCellCenterAttribute", True ) if isinstance( mesh, vtkMultiBlockDataSet ): - if isAttributeInObjectMultiBlockDataSet( mesh, cellCenterAttributeName, Piece.CELLS ): + if isAttributeInObject( mesh, cellCenterAttributeName, Piece.CELLS ): raise AttributeError( f"The attribute { cellCenterAttributeName } in already in the mesh." ) elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( mesh ) diff --git a/geos-mesh/tests/test_arrayHelpers.py b/geos-mesh/tests/test_arrayHelpers.py index 5add692f..d275f15b 100644 --- a/geos-mesh/tests/test_arrayHelpers.py +++ b/geos-mesh/tests/test_arrayHelpers.py @@ -310,33 +310,6 @@ def test_isAttributeInObjectTypeError() -> None: arrayHelpers.isAttributeInObject( mesh, "Attribute", Piece.CELLS ) -@pytest.mark.parametrize( "attributeName, piece", [ - ( "rockPorosity_referencePorosity", Piece.CELLS ), - ( "ghostRank", Piece.POINTS ), - ( "TIME", Piece.FIELD ), - ( "ghostRank", Piece.BOTH ), -] ) -def test_isAttributeInObjectMultiBlockDataSet( dataSetTest: vtkMultiBlockDataSet, attributeName: str, - piece: Piece ) -> None: - """Test presence of attribute in a multiblock.""" - multiBlockDataset: vtkMultiBlockDataSet = dataSetTest( "multiblockGeosOutput" ) - obtained: bool = arrayHelpers.isAttributeInObjectMultiBlockDataSet( multiBlockDataset, attributeName, piece ) - assert obtained - - -@pytest.mark.parametrize( "attributeName, piece, expected", [ - ( "PointAttribute", Piece.POINTS, True ), - ( "PORO", Piece.CELLS, True ), - ( "PORO", Piece.POINTS, False ), -] ) -def test_isAttributeInObjectDataSet( dataSetTest: vtkDataSet, attributeName: str, piece: Piece, - expected: bool ) -> None: - """Test presence of attribute in a dataset.""" - vtkDataset: vtkDataSet = dataSetTest( "dataset" ) - obtained: bool = arrayHelpers.isAttributeInObjectDataSet( vtkDataset, attributeName, piece ) - assert obtained == expected - - @pytest.mark.parametrize( "attributeName, piece, expected", [ ( "PORO", Piece.CELLS, False ), ( "GLOBAL_IDS_POINTS", Piece.POINTS, True ), @@ -468,15 +441,13 @@ def test_getNumberOfComponentsRaises( arrayHelpers.getNumberOfComponents( meshWrongType, "PORO", Piece.CELLS ) # AttributeError - mesh: vtkDataSet = dataSetTest( "dataset" ) + mesh: vtkDataSet = dataSetTest( "multiblockGeosOutput" ) with pytest.raises( AttributeError ): arrayHelpers.getNumberOfComponents( mesh, "attributeName", Piece.POINTS ) - # TODO - # # ValueError - # with pytest.raises( ValueError ): - # arrayHelpers.getNumberOfComponents( mesh, "mass", Piece.BOTH ) - + # ValueError + with pytest.raises( ValueError ): + arrayHelpers.getNumberOfComponents( mesh, "ghostRank", Piece.BOTH ) @pytest.mark.parametrize( "meshName, attributeName, piece, expected", [ @@ -507,14 +478,13 @@ def test_getComponentNamesRaises( arrayHelpers.getComponentNames( meshWrongType, "PORO", Piece.CELLS ) # AttributeError - mesh: vtkDataSet = dataSetTest( "dataset" ) + mesh: vtkDataSet = dataSetTest( "multiblockGeosOutput" ) with pytest.raises( AttributeError ): arrayHelpers.getComponentNames( mesh, "attributeName", Piece.POINTS ) - # TODO - # # ValueError - # with pytest.raises( ValueError ): - # arrayHelpers.getComponentNames( mesh, "mass", Piece.BOTH ) + # ValueError + with pytest.raises( ValueError ): + arrayHelpers.getComponentNames( mesh, "ghostRank", Piece.BOTH ) @pytest.mark.parametrize( "attributeNames, piece, expected_columns", [ From 2f6f724b427896c80335819ef97ba55a93e185e5 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 10 Feb 2026 11:15:30 +0100 Subject: [PATCH 05/10] refactor getAttributesWithNumberOfComponents --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 98 +++++++------------ geos-mesh/tests/test_arrayHelpers.py | 67 ++++++------- .../pre_processing/MeshQualityEnhanced.py | 4 +- 3 files changed, 70 insertions(+), 99 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index a51ef053..7f7a606c 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -28,13 +28,13 @@ - get the array of an attribute (one for dataset, one for multiblockDataset, one for the both and one for fieldData) - get the component names of an attribute - get the number of components of an attribute - - get the piece of an attribute (for any meshes) + - get the piece of an attribute - get the values of an attribute as data frame (for polyData only) - get the vtk type of an attribute (one for dataset, one for multiblockDataset and one for the both) - - get the set of attributes on one piece of a mesh (for any mesh) - - get the attribute and they number of component on one piece of a mesh (one for dataset, one for multiblockDataset and one for the both) - - get all the cells dimension of a mesh (for any meshes) - - get the GlobalIds array on one piece of a mesh (for any meshes) + - get the set of attributes on one piece of a mesh + - get the attribute and they number of component on one piece of a mesh + - get all the cells dimension of a mesh + - get the GlobalIds array on one piece of a mesh - get the cell center coordinates of a mesh - get the mapping between cells or points shared by two meshes @@ -379,73 +379,47 @@ def getAttributesWithNumberOfComponents( """Get the dictionary of all attributes from object on points or cells. Args: - mesh (Any): Mesh where to find the attributes. - piece (Piece): The piece of the attribute. + mesh (vtkMultiBlockDataSet | vtkDataSet): Mesh where to find the attributes. + piece (Piece): The piece of the attributes to get. Returns: dict[str, int]: Dictionary where keys are the names of the attributes and values the number of components. - """ - attributes: dict[ str, int ] - if isinstance( mesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): - attributes = getAttributesFromMultiBlockDataSet( mesh, piece ) - elif isinstance( mesh, vtkDataSet ): - attributes = getAttributesFromDataSet( mesh, piece ) - else: - raise TypeError( "Input mesh must be a vtkDataSet or vtkMultiBlockDataSet." ) - return attributes - -def getAttributesFromMultiBlockDataSet( multiBlockDataSet: Union[ vtkMultiBlockDataSet, vtkCompositeDataSet ], - piece: Piece ) -> dict[ str, int ]: - """Get the dictionary of all attributes of object on points or on cells. - - Args: - multiBlockDataSet (vtkMultiBlockDataSet | vtkCompositeDataSet): multiBlockDataSet where to find the attributes. - piece (Piece): The piece of the attribute. - - Returns: - dict[str, int]: Dictionary of the names of the attributes as keys, and number of components as values. + Raises: + ValueError: The piece must be cells or points only + AttributeError: One attribute one the mesh is null. + TypeError: Input mesh must be a vtkDataSet or vtkMultiBlockDataSet. """ attributes: dict[ str, int ] = {} - elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSet ) - for blockIndex in elementaryBlockIndexes: - dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) - blockAttributes: dict[ str, int ] = getAttributesFromDataSet( dataSet, piece ) - for attributeName, nbComponents in blockAttributes.items(): - if attributeName not in attributes: - attributes[ attributeName ] = nbComponents - - return attributes - + if isinstance( mesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): + elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( mesh ) + for blockIndex in elementaryBlockIndexes: + dataSet: vtkDataSet = vtkDataSet.SafeDownCast( mesh.GetDataSet( blockIndex ) ) + blockAttributes: dict[ str, int ] = getAttributesWithNumberOfComponents( dataSet, piece ) + for attributeName, nbComponents in blockAttributes.items(): + if attributeName not in attributes: + attributes[ attributeName ] = nbComponents + elif isinstance( mesh, vtkDataSet ): + data: Union[ vtkPointData, vtkCellData ] + if piece == Piece.POINTS: + data = mesh.GetPointData() + elif piece == Piece.CELLS: + data = mesh.GetCellData() + else: + raise ValueError( f"The attribute piece must be { Piece.POINTS.value } or { Piece.CELLS.value }." ) -def getAttributesFromDataSet( dataSet: vtkDataSet, piece: Piece ) -> dict[ str, int ]: - """Get the dictionary of all attributes of a vtkDataSet on points or cells. + nbAttributes: int = data.GetNumberOfArrays() + for i in range( nbAttributes ): + attributeName: str = data.GetArrayName( i ) + attribute: vtkDataArray = data.GetArray( attributeName ) + if attribute is None: + raise AttributeError( f"The attribute { attributeName } is null" ) - Args: - dataSet (vtkDataSet): DataSet where to find the attributes. - piece (Piece): The piece of the attribute. - - Returns: - dict[str, int]: List of the names of the attributes. - """ - attributes: dict[ str, int ] = {} - data: Union[ vtkPointData, vtkCellData ] - if piece == Piece.POINTS: - data = dataSet.GetPointData() - elif piece == Piece.CELLS: - data = dataSet.GetCellData() + nbComponents: int = attribute.GetNumberOfComponents() + attributes[ attributeName ] = nbComponents else: - raise ValueError( f"The attribute piece must be { Piece.POINTS.value } or { Piece.CELLS.value }." ) - - assert data is not None, f"Data on { piece.value } was not recovered." + raise TypeError( "Input mesh must be a vtkDataSet or vtkMultiBlockDataSet." ) - nbAttributes: int = data.GetNumberOfArrays() - for i in range( nbAttributes ): - attributeName: str = data.GetArrayName( i ) - attribute: vtkDataArray = data.GetArray( attributeName ) - assert attribute is not None, f"Attribute { attributeName } is null" - nbComponents: int = attribute.GetNumberOfComponents() - attributes[ attributeName ] = nbComponents return attributes diff --git a/geos-mesh/tests/test_arrayHelpers.py b/geos-mesh/tests/test_arrayHelpers.py index d275f15b..e57881ae 100644 --- a/geos-mesh/tests/test_arrayHelpers.py +++ b/geos-mesh/tests/test_arrayHelpers.py @@ -103,27 +103,42 @@ def test_computeElementMappingValueError() -> None: arrayHelpers.computeElementMapping( vtkMultiBlockDataSet(), vtkMultiBlockDataSet(), pieceWrongValue ) -@pytest.mark.parametrize( "piece, expected", [ ( Piece.POINTS, { - 'GLOBAL_IDS_POINTS': 1, - 'collocated_nodes': 2, - 'PointAttribute': 3, -} ), ( Piece.CELLS, { - 'CELL_MARKERS': 1, - 'PERM': 3, - 'PORO': 1, - 'FAULT': 1, - 'GLOBAL_IDS_CELLS': 1, - 'CellAttribute': 3, -} ) ] ) -def test_getAttributeFromMultiBlockDataSet( dataSetTest: vtkMultiBlockDataSet, piece: Piece, - expected: dict[ str, int ] ) -> None: - """Test getting attribute list as dict from multiblock.""" - multiBlockTest: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - attributes: dict[ str, int ] = arrayHelpers.getAttributesFromMultiBlockDataSet( multiBlockTest, piece ) +@pytest.mark.parametrize( "meshName, piece, expected", [ + ( "multiblock", Piece.POINTS, { 'GLOBAL_IDS_POINTS': 1, 'collocated_nodes': 2, 'PointAttribute': 3 } ), + ( "multiblock", Piece.CELLS, { 'CELL_MARKERS': 1, 'PERM': 3, 'PORO': 1, 'FAULT': 1, 'GLOBAL_IDS_CELLS': 1, 'CellAttribute': 3 } ), + ( "dataset", Piece.POINTS, { 'GLOBAL_IDS_POINTS': 1, 'PointAttribute': 3 } ), + ( "dataset", Piece.CELLS, { 'CELL_MARKERS': 1, 'PERM': 3, 'PORO': 1, 'FAULT': 1, 'GLOBAL_IDS_CELLS': 1, 'CellAttribute': 3 } ), +] ) +def test_getAttributesWithNumberOfComponents( + dataSetTest: Any, + meshName: str, + piece: Piece, + expected: dict[ str, int ] +) -> None: + """Test getting attribute list as dict from a mesh.""" + mesh: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshName ) + attributes: dict[ str, int ] = arrayHelpers.getAttributesWithNumberOfComponents( mesh, piece ) assert attributes == expected +def test_getAttributesWithNumberOfComponentsRaises() -> None: + """Test the fails of the function getAttributesWithNumberOfComponents.""" + # ValueError + with pytest.raises( ValueError ): + arrayHelpers.getAttributesWithNumberOfComponents( vtkPolyData(), Piece.BOTH ) + + # AttributeError + mesh: vtkPolyData = vtkPolyData() + mesh.GetCellData().AddArray( vtkDoubleArray() ) + with pytest.raises( AttributeError ): + arrayHelpers.getAttributesWithNumberOfComponents( mesh, Piece.CELLS ) + + # TypeError + with pytest.raises( TypeError ): + arrayHelpers.getAttributesWithNumberOfComponents( vtkCellData(), Piece.CELLS ) + + @pytest.mark.parametrize( "meshName, attributeName, pieceTest", [ ( "dataset", "CellAttribute", Piece.CELLS ), ( "dataset", "PointAttribute", Piece.POINTS ), @@ -265,24 +280,6 @@ def test_checkValidValuesInDataSet( assert invalidValues == invalidValuesTest -@pytest.mark.parametrize( "piece, expected", [ ( Piece.POINTS, { - 'GLOBAL_IDS_POINTS': 1, - 'PointAttribute': 3, -} ), ( Piece.CELLS, { - 'CELL_MARKERS': 1, - 'PERM': 3, - 'PORO': 1, - 'FAULT': 1, - 'GLOBAL_IDS_CELLS': 1, - 'CellAttribute': 3, -} ) ] ) -def test_getAttributesFromDataSet( dataSetTest: vtkDataSet, piece: Piece, expected: dict[ str, int ] ) -> None: - """Test getting attribute list as dict from dataset.""" - vtkDataSetTest: vtkDataSet = dataSetTest( "dataset" ) - attributes: dict[ str, int ] = arrayHelpers.getAttributesFromDataSet( vtkDataSetTest, piece ) - assert attributes == expected - - @pytest.mark.parametrize( "meshName, attributeName, piece, expected", [ ( "dataset", "Attribute", Piece.CELLS, False ), ( "dataset", "GLOBAL_IDS_CELLS", Piece.CELLS, True ), diff --git a/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py b/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py index 930c64c4..929c7f18 100644 --- a/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py +++ b/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py @@ -19,7 +19,7 @@ from geos.processing.pre_processing.CellTypeCounterEnhanced import CellTypeCounterEnhanced from geos.mesh.model.CellTypeCounts import CellTypeCounts from geos.mesh.model.QualityMetricSummary import ( QualityMetricSummary, StatTypes ) -from geos.mesh.utils.arrayHelpers import getAttributesFromDataSet +from geos.mesh.utils.arrayHelpers import getAttributesWithNumberOfComponents from geos.mesh.stats.meshQualityMetricHelpers import ( getQualityMeasureNameFromIndex, getQualityMetricFromIndex, VtkCellQualityMetricEnum, CellQualityMetricAdditionalEnum, QualityMetricOtherEnum, MeshQualityMetricEnum, @@ -355,7 +355,7 @@ def _evaluateCellQuality( self: Self, metricIndex: int ) -> None: metricIndex (int): Quality metric index """ arrayName: str = getQualityMetricArrayName( metricIndex ) - if arrayName in getAttributesFromDataSet( self._outputMesh, piece=Piece.CELLS ): + if arrayName in getAttributesWithNumberOfComponents( self._outputMesh, piece=Piece.CELLS ): # Metric is already computed (by default computed for all cell types if applicable ) return From f30f13ee6d1d84477baa98d7612ee950f41bf053 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 10 Feb 2026 11:32:42 +0100 Subject: [PATCH 06/10] refactor getAttributeSet --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 16 +++------------- geos-mesh/tests/test_arrayHelpers.py | 7 ------- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index 7f7a606c..869f8699 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -351,23 +351,13 @@ def getAttributeSet( mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ], piece: Pie Args: mesh (Union[vtkMultiBlockDataSet, vtkDataSet]): Mesh where to find the attributes. - piece (Piece): The piece of the attribute. + piece (Piece): The piece of the attributes to get. Returns: set[str]: Set of attribute names present in input mesh. """ - attributeSet: set[ str ] - if isinstance( mesh, vtkMultiBlockDataSet ): - listDataSetIds: list[ int ] = getBlockElementIndexesFlatten( mesh ) - attributeSet = set() - for dataSetId in listDataSetIds: - dataset: vtkDataSet = vtkDataSet.SafeDownCast( mesh.GetDataSet( dataSetId ) ) - attributeSet.update( getAttributeSet( dataset, piece ) ) - elif isinstance( mesh, vtkDataSet ): - fieldData: vtkPointData | vtkCellData = mesh.GetPointData() if piece == Piece.POINTS else mesh.GetCellData() - attributeSet = { fieldData.GetArrayName( i ) for i in range( fieldData.GetNumberOfArrays() ) } - else: - raise TypeError( "Input mesh must be a vtkDataSet or vtkMultiBlockDataSet." ) + dictAttributes: dict[ str, int ] = getAttributesWithNumberOfComponents( mesh, piece ) + attributeSet: set[ str ] = set( dictAttributes.keys() ) return attributeSet diff --git a/geos-mesh/tests/test_arrayHelpers.py b/geos-mesh/tests/test_arrayHelpers.py index e57881ae..39278019 100644 --- a/geos-mesh/tests/test_arrayHelpers.py +++ b/geos-mesh/tests/test_arrayHelpers.py @@ -201,13 +201,6 @@ def test_getAttributeSet( assert obtainedAttributeSet == expectedAttributeSet -def test_getAttributeSetTypeError() -> None: - """Test getAttributeSet TypeError raises.""" - mesh: vtkFieldData = vtkFieldData() - with pytest.raises( TypeError ): - arrayHelpers.getAttributeSet( mesh, Piece.CELLS ) - - @pytest.mark.parametrize( "arrayName, sorted, piece, expectedNpArray", [ ( "PORO", True, Piece.CELLS, np.array( [ 0.20000000298 for _ in range( 1740 ) ], dtype=np.float32 ) ), ( "PORO", False, Piece.CELLS, np.array( [ 0.20000000298 for _ in range( 1740 ) ], dtype=np.float32 ) ), From b2bbec419b2c54ca8e4a6babcb70f000f21dcc58 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 10 Feb 2026 12:04:37 +0100 Subject: [PATCH 07/10] refactor getVtkArrayTypeInObject --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 68 +++++++------------ .../src/geos/mesh/utils/arrayModifiers.py | 3 +- geos-mesh/tests/test_arrayHelpers.py | 44 ++++-------- .../CreateConstantAttributePerRegion.py | 4 +- 4 files changed, 40 insertions(+), 79 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index 869f8699..b7840166 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -30,7 +30,7 @@ - get the number of components of an attribute - get the piece of an attribute - get the values of an attribute as data frame (for polyData only) - - get the vtk type of an attribute (one for dataset, one for multiblockDataset and one for the both) + - get the vtk type of an attribute - get the set of attributes on one piece of a mesh - get the attribute and they number of component on one piece of a mesh - get all the cells dimension of a mesh @@ -485,59 +485,36 @@ def getArrayInObject( dataSet: vtkDataSet, attributeName: str, piece: Piece ) -> return npArray -def getVtkDataTypeInObject( mesh: Union[ vtkDataSet, vtkMultiBlockDataSet ], attributeName: str, piece: Piece ) -> int: +def getVtkArrayTypeInObject( mesh: Union[ vtkDataSet, vtkMultiBlockDataSet ], attributeName: str, piece: Piece ) -> int: """Return VTK type of requested array from input mesh. Args: - mesh (Union[vtkDataSet, vtkMultiBlockDataSet]): Input multiBlockDataSet. + mesh (Union[vtkDataSet, vtkMultiBlockDataSet]): Input mesh. attributeName (str): Name of the attribute. piece (Piece): The piece of the attribute. Returns: int: The type of the vtk array corresponding to input attribute name. + + Raises: + AttributeError: The attribute 'attributeName' is not in the mesh. + TypeError: The mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet. """ if isinstance( mesh, vtkDataSet ): - return getVtkArrayTypeInObject( mesh, attributeName, piece ) + array: vtkDataArray = getVtkArrayInObject( mesh, attributeName, piece ) + vtkArrayType: int = array.GetDataType() + return vtkArrayType + elif isinstance( mesh, vtkMultiBlockDataSet ): + elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( mesh ) + for blockIndex in elementaryBlockIndexes: + dataSet: vtkDataSet = vtkDataSet.SafeDownCast( mesh.GetDataSet( blockIndex ) ) + try: + return getVtkArrayTypeInObject( dataSet, attributeName, piece ) + except: + continue + raise AttributeError( f"The attribute { attributeName } is not in input mesh." ) else: - return getVtkArrayTypeInMultiBlock( mesh, attributeName, piece ) - - -def getVtkArrayTypeInObject( dataSet: vtkDataSet, attributeName: str, piece: Piece ) -> int: - """Return VTK type of requested array from dataset input. - - Args: - dataSet (vtkDataSet): Input dataSet. - attributeName (str): Name of the attribute. - piece (Piece): The piece of the attribute. - - Returns: - int: The type of the vtk array corresponding to input attribute name. - """ - array: vtkDataArray = getVtkArrayInObject( dataSet, attributeName, piece ) - vtkArrayType: int = array.GetDataType() - - return vtkArrayType - - -def getVtkArrayTypeInMultiBlock( multiBlockDataSet: vtkMultiBlockDataSet, attributeName: str, piece: Piece ) -> int: - """Return VTK type of requested array from multiblock dataset input, if existing. - - Args: - multiBlockDataSet (vtkMultiBlockDataSet): Input multiBlockDataSet. - attributeName (str): Name of the attribute. - piece (Piece): The piece of the attribute. - - Returns: - int: Type of the requested vtk array if existing in input multiblock dataset. - """ - elementaryBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSet ) - for blockIndex in elementaryBlockIndexes: - dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) - listAttributes: set[ str ] = getAttributeSet( dataSet, piece ) - if attributeName in listAttributes: - return getVtkArrayTypeInObject( dataSet, attributeName, piece ) - - raise AssertionError( "The vtkMultiBlockDataSet has no attribute with the name " + attributeName + "." ) + raise TypeError( "Input object must be a vtkDataSet or vtkMultiBlockDataSet." ) def getVtkArrayInObject( dataSet: vtkDataSet, attributeName: str, piece: Piece ) -> vtkDataArray: @@ -550,9 +527,12 @@ def getVtkArrayInObject( dataSet: vtkDataSet, attributeName: str, piece: Piece ) Returns: vtkDataArray: The vtk array corresponding to input attribute name. + + Raises: + AttributeError: The attribute 'attributeName' is not in the mesh. """ if not isAttributeInObject( dataSet, attributeName, piece ): - raise AttributeError( f"{ attributeName } is not in input mesh." ) + raise AttributeError( f"The attribute { attributeName } is not in input mesh." ) dataArray: vtkDataArray if piece == Piece.POINTS: diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 390a1537..5fd4f98f 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -34,7 +34,6 @@ isAttributeInObject, isAttributeGlobal, getVtkArrayTypeInObject, - getVtkArrayTypeInMultiBlock, getVtkDataTypeInObject, getNumberOfComponents, ) @@ -102,7 +101,7 @@ def fillPartialAttributes( raise AttributeError( f"The attribute { attributeName } is already global." ) # Get information of the attribute to fill. - vtkDataType: int = getVtkArrayTypeInMultiBlock( multiBlockDataSet, attributeName, piece ) + vtkDataType: int = getVtkArrayTypeInObject( multiBlockDataSet, attributeName, piece ) nbComponents: int = getNumberOfComponents( multiBlockDataSet, attributeName, piece ) componentNames: tuple[ str, ...] = () if nbComponents > 1: diff --git a/geos-mesh/tests/test_arrayHelpers.py b/geos-mesh/tests/test_arrayHelpers.py index 39278019..930c5196 100644 --- a/geos-mesh/tests/test_arrayHelpers.py +++ b/geos-mesh/tests/test_arrayHelpers.py @@ -278,8 +278,7 @@ def test_checkValidValuesInDataSet( ( "dataset", "GLOBAL_IDS_CELLS", Piece.CELLS, True ), ( "dataset", "GLOBAL_IDS_POINTS", Piece.POINTS, True ), ( "multiblockGeosOutput", "TIME", Piece.FIELD, True ), - ( "multiblockGeosOutput", "ghostRank", Piece.CELLS, True ), - ( "multiblockGeosOutput", "ghostRank", Piece.POINTS, True ), + ( "multiblockGeosOutput", "ghostRank", Piece.BOTH, True ), ] ) def test_isAttributeInObject( dataSetTest: Any, @@ -340,47 +339,30 @@ def test_getArrayInObject( request: pytest.FixtureRequest, arrayExpected: npt.ND ( "dataset", "PointAttribute", Piece.POINTS, 11 ), ( "multiblock", "collocated_nodes", Piece.POINTS, 12 ), ] ) -def test_getVtkDataTypeInObject( +def test_getVtkArrayTypeInObject( dataSetTest: Any, meshName: str, attributeName: str, piece: Piece, expectedVtkType: int, ) -> None: - """Test the function getVtkDataTypeInObject.""" + """Test the function getVtkArrayTypeInObject.""" mesh: vtkDataSet | vtkMultiBlockDataSet = dataSetTest( meshName ) - obtainedVtkType: int = arrayHelpers.getVtkDataTypeInObject( mesh, attributeName, piece ) + obtainedVtkType: int = arrayHelpers.getVtkArrayTypeInObject( mesh, attributeName, piece ) assert obtainedVtkType == expectedVtkType -@pytest.mark.parametrize( "attributeName, vtkDataType, piece", [ - ( "CellAttribute", 11, Piece.CELLS ), - ( "PointAttribute", 11, Piece.POINTS ), - ( "collocated_nodes", 12, Piece.POINTS ), -] ) -def test_getVtkArrayTypeInMultiBlock( dataSetTest: vtkMultiBlockDataSet, attributeName: str, vtkDataType: int, - piece: Piece ) -> None: - """Test getting the type of the vtk array of an attribute from multiBlockDataSet.""" - multiBlockDataSet: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) - - vtkDataTypeTest: int = arrayHelpers.getVtkArrayTypeInMultiBlock( multiBlockDataSet, attributeName, piece ) - - assert ( vtkDataTypeTest == vtkDataType ) - - -@pytest.mark.parametrize( "attributeName, piece", [ - ( "CellAttribute", Piece.CELLS ), - ( "PointAttribute", Piece.POINTS ), -] ) -def test_getVtkArrayTypeInObject( dataSetTest: vtkDataSet, attributeName: str, piece: Piece ) -> None: - """Test getting the type of the vtk array of an attribute from dataset.""" - vtkDataSetTest: vtkDataSet = dataSetTest( "dataset" ) - - obtained: int = arrayHelpers.getVtkArrayTypeInObject( vtkDataSetTest, attributeName, piece ) - expected: int = 11 +def test_getVtkArrayTypeInObjectRaises( dataSetTest: Any ) -> None: + """Test the fails of the function getVtkArrayTypeInObject.""" + # AttributeError + mesh: vtkDataSet = dataSetTest( "dataset" ) + with pytest.raises( AttributeError ): + arrayHelpers.getVtkArrayTypeInObject( mesh, "attributeName", Piece.CELLS ) - assert ( obtained == expected ) + # TypeError + with pytest.raises( TypeError ): + arrayHelpers.getVtkArrayTypeInObject( vtkCellData(), "PORO", Piece.CELLS ) @pytest.mark.parametrize( "arrayExpected, piece", [ 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..3d2ee61c 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -14,7 +14,7 @@ from geos.utils.pieceEnum import Piece from geos.utils.Logger import ( getLogger, Logger, CountWarningHandler ) from geos.mesh.utils.arrayHelpers import ( getArrayInObject, getComponentNames, getNumberOfComponents, - getVtkDataTypeInObject, isAttributeGlobal, getAttributePieceInfo, + getVtkArrayTypeInObject, isAttributeGlobal, getAttributePieceInfo, checkValidValuesInDataSet, checkValidValuesInMultiBlock ) from geos.mesh.utils.arrayModifiers import ( createAttribute, createConstantAttributeDataSet, createConstantAttributeMultiBlock ) @@ -273,7 +273,7 @@ def _setInfoRegion( self: Self ) -> None: """ # Get the numpy type from the vtk typecode. dictType: dict[ int, Any ] = vnp.get_vtk_to_numpy_typemap() - regionVtkType: int = getVtkDataTypeInObject( self.mesh, self.regionName, self.piece ) + regionVtkType: int = getVtkArrayTypeInObject( self.mesh, self.regionName, self.piece ) regionNpType: type = dictType[ regionVtkType ] # Set the correct type of values and region index. From 297f838441463db7559abae73912b4c9846d71c1 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 10 Feb 2026 13:59:19 +0100 Subject: [PATCH 08/10] refactor checkValidValuesInObject functions --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 98 +++++++++---------- geos-mesh/tests/test_arrayHelpers.py | 50 +++++----- .../CreateConstantAttributePerRegion.py | 15 +-- 3 files changed, 70 insertions(+), 93 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index b7840166..c652e632 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -229,69 +229,57 @@ def getAttributePieceInfo( return Piece.NONE -def checkValidValuesInMultiBlock( - multiBlockDataSet: vtkMultiBlockDataSet, +def checkValidValuesInObject( + mesh: vtkMultiBlockDataSet | vtkDataSet, attributeName: str, listValues: list[ Any ], piece: Piece, ) -> tuple[ list[ Any ], list[ Any ] ]: - """Check if each value is valid , ie if that value is a data of the attribute in at least one dataset of the multiblock. + """Check if each value is valid, ie if that value is a data of the mesh's attribute. Args: - multiBlockDataSet (vtkMultiBlockDataSet): The multiblock dataset mesh to check. + mesh (vtkMultiBlockDataSet | vtkDataSet): The mesh to check. attributeName (str): The name of the attribute with the data. listValues (list[Any]): The list of values to check. piece (Piece): The piece of the attribute. Returns: tuple[list[Any], list[Any]]: Tuple containing the list of valid values and the list of the invalid ones. - """ - validValues: list[ Any ] = [] - invalidValues: list[ Any ] = [] - listFlatIdDataSet: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSet ) - for flatIdDataSet in listFlatIdDataSet: - dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( flatIdDataSet ) ) - # Get the valid values of the dataset. - validValuesDataSet: list[ Any ] = checkValidValuesInDataSet( dataSet, attributeName, listValues, piece )[ 0 ] - - # Keep the new true values. - for value in validValuesDataSet: - if value not in validValues: - validValues.append( value ) - - # Get the false indexes. - for value in listValues: - if value not in validValues: - invalidValues.append( value ) - - return ( validValues, invalidValues ) - - -def checkValidValuesInDataSet( - dataSet: vtkDataSet, - attributeName: str, - listValues: list[ Any ], - piece: Piece, -) -> tuple[ list[ Any ], list[ Any ] ]: - """Check if each value is valid , ie if that value is a data of the attribute in the dataset. - - Args: - dataSet (vtkDataSet): The dataset mesh to check. - attributeName (str): The name of the attribute with the data. - listValues (list[Any]): The list of values to check. - piece (Piece): The piece of the attribute. - Returns: - tuple[list[Any], list[Any]]: Tuple containing the list of valid values and the list of the invalid ones. + Raises: + TypeError: The mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet. + AttributeError: The attribute must be global for multiblock dataset. """ - attributeNpArray = getArrayInObject( dataSet, attributeName, piece ) validValues: list[ Any ] = [] invalidValues: list[ Any ] = [] - for value in listValues: - if value in attributeNpArray: - validValues.append( value ) - else: - invalidValues.append( value ) + if isinstance( mesh, vtkDataSet ): + attributeNpArray = getArrayInObject( mesh, attributeName, piece ) + for value in listValues: + if value in attributeNpArray: + validValues.append( value ) + else: + invalidValues.append( value ) + elif isinstance( mesh, vtkMultiBlockDataSet ): + if not isAttributeGlobal( mesh, attributeName, piece ): + raise AttributeError( f"The attribute { attributeName } must be global." ) + + listFlatIdDataSet: list[ int ] = getBlockElementIndexesFlatten( mesh ) + for flatIdDataSet in listFlatIdDataSet: + dataSet: vtkDataSet = vtkDataSet.SafeDownCast( mesh.GetDataSet( flatIdDataSet ) ) + # Get the valid values of the dataset. + validValuesDataSet: list[ Any ] = checkValidValuesInObject( dataSet, attributeName, listValues, piece )[ 0 ] + + # Keep the new true values. + for value in validValuesDataSet: + if value not in validValues: + validValues.append( value ) + + # Get the false indexes. + for value in listValues: + if value not in validValues: + invalidValues.append( value ) + else: + raise TypeError( "Input mesh must be a vtkDataSet or vtkMultiBlockDataSet." ) return ( validValues, invalidValues ) @@ -445,7 +433,7 @@ def isAttributeInObject( mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ], attrib if isAttributeInObject( dataSet, attributeName, piece ): return True else: - raise TypeError( "Input object must be a vtkDataSet or vtkMultiBlockDataSet." ) + raise TypeError( "Input mesh must be a vtkDataSet or vtkMultiBlockDataSet." ) return False @@ -470,7 +458,7 @@ def isAttributeGlobal( multiBlockDataSet: vtkMultiBlockDataSet, attributeName: s def getArrayInObject( dataSet: vtkDataSet, attributeName: str, piece: Piece ) -> npt.NDArray[ Any ]: - """Return the numpy array corresponding to input attribute name in table. + """Return the numpy array corresponding to input attribute name in the mesh. Args: dataSet (vtkDataSet): Input dataSet. @@ -478,10 +466,11 @@ def getArrayInObject( dataSet: vtkDataSet, attributeName: str, piece: Piece ) -> piece (Piece): The piece of the attribute. Returns: - ArrayLike[Any]: The numpy array corresponding to input attribute name. + NDArray[Any]: The numpy array corresponding to input attribute name. """ vtkArray: vtkDataArray = getVtkArrayInObject( dataSet, attributeName, piece ) npArray: npt.NDArray[ Any ] = vnp.vtk_to_numpy( vtkArray ) # type: ignore[no-untyped-call] + return npArray @@ -518,17 +507,18 @@ def getVtkArrayTypeInObject( mesh: Union[ vtkDataSet, vtkMultiBlockDataSet ], at def getVtkArrayInObject( dataSet: vtkDataSet, attributeName: str, piece: Piece ) -> vtkDataArray: - """Return the array corresponding to input attribute name in table. + """Return the array corresponding to input attribute name in the mesh. Args: - dataSet (vtkDataSet): Input dataSet. - attributeName (str): Name of the attribute. - piece (Piece): The piece of the attribute. + dataSet (vtkDataSet): Input mesh with the attribute to get. + attributeName (str): Name of the attribute to get. + piece (Piece): The piece of the attribute to get. Returns: vtkDataArray: The vtk array corresponding to input attribute name. Raises: + ValueError: The piece must be cells, points or fields. AttributeError: The attribute 'attributeName' is not in the mesh. """ if not isAttributeInObject( dataSet, attributeName, piece ): diff --git a/geos-mesh/tests/test_arrayHelpers.py b/geos-mesh/tests/test_arrayHelpers.py index 930c5196..f1f7e4ef 100644 --- a/geos-mesh/tests/test_arrayHelpers.py +++ b/geos-mesh/tests/test_arrayHelpers.py @@ -229,48 +229,42 @@ def test_getNumpyArrayByNameAttributeError( dataSetTest: vtkDataSet ) -> None: arrayHelpers.getNumpyArrayByName( fieldData, "Attribute" ) -@pytest.mark.parametrize( "attributeName, listValues, piece, validValuesTest, invalidValuesTest", [ - ( "GLOBAL_IDS_POINTS", [ 0, 1, 11, -9 ], Piece.POINTS, [ 0, 1, 11 ], [ -9 ] ), - ( "GLOBAL_IDS_CELLS", [ 0, 1, 11, -9 ], Piece.CELLS, [ 0, 1, 11 ], [ -9 ] ), +@pytest.mark.parametrize( "meshName, attributeName, listValues, piece, validValuesTest, invalidValuesTest", [ + ( "multiblock", "GLOBAL_IDS_POINTS", [ 0, 1, 11, -9 ], Piece.POINTS, [ 0, 1, 11 ], [ -9 ] ), + ( "multiblock", "GLOBAL_IDS_CELLS", [ 0, 1, 11, -9 ], Piece.CELLS, [ 0, 1, 11 ], [ -9 ] ), + ( "dataset", "PointAttribute", [ [ 12.4, 9.7, 10.5 ], [ 0, 0, 0 ] ], Piece.POINTS, [ [ 12.4, 9.7, 10.5 ] ], [ [ 0, 0, 0 ] ] ), + ( "dataset", "CellAttribute", [ [ 24.8, 19.4, 21 ], [ 0, 0, 0 ] ], Piece.CELLS, [ [ 24.8, 19.4, 21 ] ], [ [ 0, 0, 0 ] ] ), + ( "dataset", "FAULT", [ 0, 100, 101, 2 ], Piece.CELLS, [ 0, 100, 101 ], [ 2 ] ), ] ) -def test_checkValidValuesInMultiBlock( - dataSetTest: vtkMultiBlockDataSet, +def test_checkValidValuesInObject( + dataSetTest: Any, + meshName: str, attributeName: str, listValues: list[ Any ], piece: Piece, validValuesTest: list[ Any ], invalidValuesTest: list[ Any ], ) -> None: - """Test the function checkValidValuesInDataSet.""" - multiBlockDataSet: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) + """Test the function checkValidValuesInObject.""" + mesh: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshName ) validValues: list[ Any ] invalidValues: list[ Any ] - validValues, invalidValues = arrayHelpers.checkValidValuesInMultiBlock( multiBlockDataSet, attributeName, + validValues, invalidValues = arrayHelpers.checkValidValuesInObject( mesh, attributeName, listValues, piece ) assert validValues == validValuesTest assert invalidValues == invalidValuesTest -@pytest.mark.parametrize( "attributeName, listValues, piece, validValuesTest, invalidValuesTest", [ - ( "PointAttribute", [ [ 12.4, 9.7, 10.5 ], [ 0, 0, 0 ] ], Piece.POINTS, [ [ 12.4, 9.7, 10.5 ] ], [ [ 0, 0, 0 ] ] ), - ( "CellAttribute", [ [ 24.8, 19.4, 21 ], [ 0, 0, 0 ] ], Piece.CELLS, [ [ 24.8, 19.4, 21 ] ], [ [ 0, 0, 0 ] ] ), - ( "FAULT", [ 0, 100, 101, 2 ], Piece.CELLS, [ 0, 100, 101 ], [ 2 ] ), -] ) -def test_checkValidValuesInDataSet( - dataSetTest: vtkDataSet, - attributeName: str, - listValues: list[ Any ], - piece: Piece, - validValuesTest: list[ Any ], - invalidValuesTest: list[ Any ], -) -> None: - """Test the function checkValidValuesInDataSet.""" - dataSet: vtkDataSet = dataSetTest( "dataset" ) - validValues: list[ Any ] - invalidValues: list[ Any ] - validValues, invalidValues = arrayHelpers.checkValidValuesInDataSet( dataSet, attributeName, listValues, piece ) - assert validValues == validValuesTest - assert invalidValues == invalidValuesTest +def test_checkValidValuesInObjectRaises( dataSetTest: Any ) -> None: + """Test the fails of checkValidValuesInObject.""" + # AttributeError + mesh: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) + with pytest.raises( AttributeError ): + arrayHelpers.checkValidValuesInObject( mesh, "PORO", [], Piece.CELLS ) + + # TypeError + with pytest.raises( TypeError ): + arrayHelpers.checkValidValuesInObject( vtkCellData(), "AttributeName", [], Piece.CELLS ) @pytest.mark.parametrize( "meshName, attributeName, piece, expected", [ 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 3d2ee61c..bb6884d0 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -15,7 +15,7 @@ from geos.utils.Logger import ( getLogger, Logger, CountWarningHandler ) from geos.mesh.utils.arrayHelpers import ( getArrayInObject, getComponentNames, getNumberOfComponents, getVtkArrayTypeInObject, isAttributeGlobal, getAttributePieceInfo, - checkValidValuesInDataSet, checkValidValuesInMultiBlock ) + checkValidValuesInObject ) from geos.mesh.utils.arrayModifiers import ( createAttribute, createConstantAttributeDataSet, createConstantAttributeMultiBlock ) from geos.mesh.utils.multiblockHelpers import getBlockElementIndexesFlatten @@ -185,17 +185,12 @@ def applyFilter( self: Self ) -> None: ) listIndexes: list[ Any ] = list( self.dictRegionValues.keys() ) - validIndexes: list[ Any ] = [] - invalidIndexes: list[ Any ] = [] + validIndexes: list[ Any ] + invalidIndexes: list[ Any ] + validIndexes, invalidIndexes = checkValidValuesInObject( self.mesh, self.regionName, listIndexes, self.piece ) regionArray: npt.NDArray[ Any ] newArray: npt.NDArray[ Any ] if isinstance( self.mesh, vtkMultiBlockDataSet ): - # Check if the attribute region is global. - if not isAttributeGlobal( self.mesh, self.regionName, self.piece ): - raise AttributeError( f"The region attribute { self.regionName } has to be global." ) - - validIndexes, invalidIndexes = checkValidValuesInMultiBlock( self.mesh, self.regionName, listIndexes, - self.piece ) if len( validIndexes ) == 0: if len( self.dictRegionValues ) == 0: self.logger.warning( "No region index entered." ) @@ -230,8 +225,6 @@ def applyFilter( self: Self ) -> None: logger=self.logger ) else: - validIndexes, invalidIndexes = checkValidValuesInDataSet( self.mesh, self.regionName, listIndexes, - self.piece ) if len( validIndexes ) == 0: if len( self.dictRegionValues ) == 0: self.logger.warning( "No region index entered." ) From 3fdbdfb113247bfeaa49c9f2a69d0848b27aa545 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 10 Feb 2026 14:42:48 +0100 Subject: [PATCH 09/10] clean arrayHelpers functions --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 24 +++++++++---------- .../src/geos/mesh/utils/arrayModifiers.py | 4 ++-- .../tests/test_SurfaceGeomechanics.py | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index c652e632..1f2ba52a 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -25,11 +25,11 @@ - Checks The getter functions: - - get the array of an attribute (one for dataset, one for multiblockDataset, one for the both and one for fieldData) + - get the vtk or array data of an attribute (dataset and vtkFieldData only) - get the component names of an attribute - get the number of components of an attribute - get the piece of an attribute - - get the values of an attribute as data frame (for polyData only) + - get the values of an attribute as data frame (polyData only) - get the vtk type of an attribute - get the set of attributes on one piece of a mesh - get the attribute and they number of component on one piece of a mesh @@ -39,9 +39,10 @@ - get the mapping between cells or points shared by two meshes The check functions: - - check if an attribute is on a mesh (one for dataset, one for multiblockDataset, one for the both and one for a list of attributes) - - check if an attribute is global (for multiblockDataset meshes) - - check if a value is a value of an attribute (one for dataset and one for multiblockDataset) + - check if an attribute is on a mesh + - check if at least one attribute from a list of attributes is on a mesh (dataset only) + - check if an attribute is global (multiblock dataset only) + - check if a value is a value of an attribute """ @@ -192,15 +193,12 @@ def hasArray( mesh: vtkUnstructuredGrid, arrayNames: list[ str ] ) -> bool: Returns: bool: True if at least one array is found, else False. """ - # Check the cell data fields - data: Union[ vtkFieldData, None ] - for data in ( mesh.GetCellData(), mesh.GetFieldData(), mesh.GetPointData() ): - if data is None: - continue # type: ignore[unreachable] + for piece in [ Piece.CELLS, Piece.POINTS, Piece.FIELD ]: for arrayName in arrayNames: - if data.HasArray( arrayName ): - logging.error( f"The mesh contains the array named '{arrayName}'." ) + if isAttributeInObject( mesh, arrayName, piece ): + logging.error( f"The mesh contains the array named '{ arrayName }'." ) return True + return False @@ -303,6 +301,7 @@ def getNumpyGlobalIdsArray( data: Union[ vtkCellData, vtkPointData ] ) -> npt.ND global_ids: Optional[ vtkDataArray ] = data.GetGlobalIds() if global_ids is None: raise AttributeError( "There is no GlobalIds in the given fieldData." ) + return vtk_to_numpy( global_ids ) @@ -454,6 +453,7 @@ def isAttributeGlobal( multiBlockDataSet: vtkMultiBlockDataSet, attributeName: s dataSet: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSet.GetDataSet( blockIndex ) ) if not isAttributeInObject( dataSet, attributeName, piece ): return False + return True diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 5fd4f98f..30b6512b 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -34,7 +34,7 @@ isAttributeInObject, isAttributeGlobal, getVtkArrayTypeInObject, - getVtkDataTypeInObject, + getVtkArrayInObject, getNumberOfComponents, ) from geos.mesh.utils.multiblockHelpers import getBlockElementIndexesFlatten @@ -713,7 +713,7 @@ def transferAttributeToDataSetWithElementMap( componentNames: tuple[ str, ...] = getComponentNames( meshFrom, attributeName, piece ) nbComponents: int = len( componentNames ) - vtkDataType: int = getVtkDataTypeInObject( meshFrom, attributeName, piece ) + vtkDataType: int = getVtkArrayTypeInObject( meshFrom, attributeName, piece ) defaultValue: Any if vtkDataType in ( VTK_FLOAT, VTK_DOUBLE ): defaultValue = np.nan diff --git a/geos-processing/tests/test_SurfaceGeomechanics.py b/geos-processing/tests/test_SurfaceGeomechanics.py index e42f3110..92f53a80 100644 --- a/geos-processing/tests/test_SurfaceGeomechanics.py +++ b/geos-processing/tests/test_SurfaceGeomechanics.py @@ -80,5 +80,5 @@ def test_failingSurfaceGeomechanics() -> None: """Test failing of SurfaceGeomechanics due to absence of attributes in the mesh.""" failingCase: TriangulatedSurfaceTestCase = TriangulatedSurfaceTestCase( pointsCoords, triangles, None ) sgFilter: SurfaceGeomechanics = SurfaceGeomechanics( failingCase.mesh ) - with pytest.raises( AssertionError ): + with pytest.raises( AttributeError ): sgFilter.applyFilter() From 3716d9d9ed854e1fa72dd55f1ce1a00147118916 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 10 Feb 2026 14:54:14 +0100 Subject: [PATCH 10/10] clean for ci --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 10 +-- .../src/geos/mesh/utils/arrayModifiers.py | 1 - geos-mesh/tests/test_arrayHelpers.py | 62 +++++++++++-------- .../CreateConstantAttributePerRegion.py | 3 +- 4 files changed, 42 insertions(+), 34 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index 1f2ba52a..dbfb62b7 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -387,12 +387,12 @@ def getAttributesWithNumberOfComponents( nbAttributes: int = data.GetNumberOfArrays() for i in range( nbAttributes ): - attributeName: str = data.GetArrayName( i ) + attributeName = data.GetArrayName( i ) attribute: vtkDataArray = data.GetArray( attributeName ) if attribute is None: raise AttributeError( f"The attribute { attributeName } is null" ) - nbComponents: int = attribute.GetNumberOfComponents() + nbComponents = attribute.GetNumberOfComponents() attributes[ attributeName ] = nbComponents else: raise TypeError( "Input mesh must be a vtkDataSet or vtkMultiBlockDataSet." ) @@ -499,7 +499,7 @@ def getVtkArrayTypeInObject( mesh: Union[ vtkDataSet, vtkMultiBlockDataSet ], at dataSet: vtkDataSet = vtkDataSet.SafeDownCast( mesh.GetDataSet( blockIndex ) ) try: return getVtkArrayTypeInObject( dataSet, attributeName, piece ) - except: + except Exception: continue raise AttributeError( f"The attribute { attributeName } is not in input mesh." ) else: @@ -572,7 +572,7 @@ def getNumberOfComponents( raise e except AttributeError: continue - raise AttributeError( f"The attribute '{ attributeName }' is not in the mesh for the given piece { piece }.") + raise AttributeError( f"The attribute '{ attributeName }' is not in the mesh for the given piece { piece }." ) else: raise TypeError( "The mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet." ) @@ -614,7 +614,7 @@ def getComponentNames( raise e except AttributeError: continue - raise AttributeError( f"The attribute '{ attributeName }' is not in the mesh for the given piece { piece }.") + raise AttributeError( f"The attribute '{ attributeName }' is not in the mesh for the given piece { piece }." ) else: raise TypeError( "The mesh has to be inherited from vtkMultiBlockDataSet or vtkDataSet." ) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 30b6512b..835dd127 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -34,7 +34,6 @@ isAttributeInObject, isAttributeGlobal, getVtkArrayTypeInObject, - getVtkArrayInObject, getNumberOfComponents, ) from geos.mesh.utils.multiblockHelpers import getBlockElementIndexesFlatten diff --git a/geos-mesh/tests/test_arrayHelpers.py b/geos-mesh/tests/test_arrayHelpers.py index f1f7e4ef..0b18c47c 100644 --- a/geos-mesh/tests/test_arrayHelpers.py +++ b/geos-mesh/tests/test_arrayHelpers.py @@ -104,17 +104,34 @@ def test_computeElementMappingValueError() -> None: @pytest.mark.parametrize( "meshName, piece, expected", [ - ( "multiblock", Piece.POINTS, { 'GLOBAL_IDS_POINTS': 1, 'collocated_nodes': 2, 'PointAttribute': 3 } ), - ( "multiblock", Piece.CELLS, { 'CELL_MARKERS': 1, 'PERM': 3, 'PORO': 1, 'FAULT': 1, 'GLOBAL_IDS_CELLS': 1, 'CellAttribute': 3 } ), - ( "dataset", Piece.POINTS, { 'GLOBAL_IDS_POINTS': 1, 'PointAttribute': 3 } ), - ( "dataset", Piece.CELLS, { 'CELL_MARKERS': 1, 'PERM': 3, 'PORO': 1, 'FAULT': 1, 'GLOBAL_IDS_CELLS': 1, 'CellAttribute': 3 } ), + ( "multiblock", Piece.POINTS, { + 'GLOBAL_IDS_POINTS': 1, + 'collocated_nodes': 2, + 'PointAttribute': 3 + } ), + ( "multiblock", Piece.CELLS, { + 'CELL_MARKERS': 1, + 'PERM': 3, + 'PORO': 1, + 'FAULT': 1, + 'GLOBAL_IDS_CELLS': 1, + 'CellAttribute': 3 + } ), + ( "dataset", Piece.POINTS, { + 'GLOBAL_IDS_POINTS': 1, + 'PointAttribute': 3 + } ), + ( "dataset", Piece.CELLS, { + 'CELL_MARKERS': 1, + 'PERM': 3, + 'PORO': 1, + 'FAULT': 1, + 'GLOBAL_IDS_CELLS': 1, + 'CellAttribute': 3 + } ), ] ) -def test_getAttributesWithNumberOfComponents( - dataSetTest: Any, - meshName: str, - piece: Piece, - expected: dict[ str, int ] -) -> None: +def test_getAttributesWithNumberOfComponents( dataSetTest: Any, meshName: str, piece: Piece, + expected: dict[ str, int ] ) -> None: """Test getting attribute list as dict from a mesh.""" mesh: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshName ) attributes: dict[ str, int ] = arrayHelpers.getAttributesWithNumberOfComponents( mesh, piece ) @@ -232,8 +249,10 @@ def test_getNumpyArrayByNameAttributeError( dataSetTest: vtkDataSet ) -> None: @pytest.mark.parametrize( "meshName, attributeName, listValues, piece, validValuesTest, invalidValuesTest", [ ( "multiblock", "GLOBAL_IDS_POINTS", [ 0, 1, 11, -9 ], Piece.POINTS, [ 0, 1, 11 ], [ -9 ] ), ( "multiblock", "GLOBAL_IDS_CELLS", [ 0, 1, 11, -9 ], Piece.CELLS, [ 0, 1, 11 ], [ -9 ] ), - ( "dataset", "PointAttribute", [ [ 12.4, 9.7, 10.5 ], [ 0, 0, 0 ] ], Piece.POINTS, [ [ 12.4, 9.7, 10.5 ] ], [ [ 0, 0, 0 ] ] ), - ( "dataset", "CellAttribute", [ [ 24.8, 19.4, 21 ], [ 0, 0, 0 ] ], Piece.CELLS, [ [ 24.8, 19.4, 21 ] ], [ [ 0, 0, 0 ] ] ), + ( "dataset", "PointAttribute", [ [ 12.4, 9.7, 10.5 ], [ 0, 0, 0 ] ], Piece.POINTS, [ [ 12.4, 9.7, 10.5 ] + ], [ [ 0, 0, 0 ] ] ), + ( "dataset", "CellAttribute", [ [ 24.8, 19.4, 21 ], [ 0, 0, 0 ] ], Piece.CELLS, [ [ 24.8, 19.4, 21 ] + ], [ [ 0, 0, 0 ] ] ), ( "dataset", "FAULT", [ 0, 100, 101, 2 ], Piece.CELLS, [ 0, 100, 101 ], [ 2 ] ), ] ) def test_checkValidValuesInObject( @@ -249,8 +268,7 @@ def test_checkValidValuesInObject( mesh: vtkMultiBlockDataSet | vtkDataSet = dataSetTest( meshName ) validValues: list[ Any ] invalidValues: list[ Any ] - validValues, invalidValues = arrayHelpers.checkValidValuesInObject( mesh, attributeName, - listValues, piece ) + validValues, invalidValues = arrayHelpers.checkValidValuesInObject( mesh, attributeName, listValues, piece ) assert validValues == validValuesTest assert invalidValues == invalidValuesTest @@ -397,9 +415,7 @@ def test_getNumberOfComponents( assert arrayHelpers.getNumberOfComponents( mesh, attributeName, piece ) == expected -def test_getNumberOfComponentsRaises( - dataSetTest: Any, -) -> None: +def test_getNumberOfComponentsRaises( dataSetTest: Any, ) -> None: """Test getNumberOfComponents fails.""" # TypeError meshWrongType: vtkCellData = vtkCellData() @@ -422,21 +438,15 @@ def test_getNumberOfComponentsRaises( ( "multiblock", "PERM", Piece.CELLS, ( "AX1", "AX2", "AX3" ) ), ( "multiblock", "PORO", Piece.CELLS, () ), ] ) -def test_getComponentNames( - dataSetTest: Any, - meshName: str, - attributeName: str, - piece: Piece, - expected: tuple[ str, ...] ) -> None: +def test_getComponentNames( dataSetTest: Any, meshName: str, attributeName: str, piece: Piece, + expected: tuple[ str, ...] ) -> None: """Test getting the component names of an attribute from a mesh.""" vtkDataSetTest: Any = dataSetTest( meshName ) obtained: tuple[ str, ...] = arrayHelpers.getComponentNames( vtkDataSetTest, attributeName, piece ) assert obtained == expected -def test_getComponentNamesRaises( - dataSetTest: Any, -) -> None: +def test_getComponentNamesRaises( dataSetTest: Any, ) -> None: """Test getting the component names fails.""" # TypeError meshWrongType: vtkFieldData = vtkFieldData() 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 bb6884d0..3cfed92e 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -14,8 +14,7 @@ from geos.utils.pieceEnum import Piece from geos.utils.Logger import ( getLogger, Logger, CountWarningHandler ) from geos.mesh.utils.arrayHelpers import ( getArrayInObject, getComponentNames, getNumberOfComponents, - getVtkArrayTypeInObject, isAttributeGlobal, getAttributePieceInfo, - checkValidValuesInObject ) + getVtkArrayTypeInObject, getAttributePieceInfo, checkValidValuesInObject ) from geos.mesh.utils.arrayModifiers import ( createAttribute, createConstantAttributeDataSet, createConstantAttributeMultiBlock ) from geos.mesh.utils.multiblockHelpers import getBlockElementIndexesFlatten