From f4f9bc6b908ef14f63b02c8d526ded5fbf051249 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 17 Nov 2025 15:30:08 +0100 Subject: [PATCH 01/39] update logger generic processing tool --- .../generic_processing_tools/AttributeMapping.py | 3 ++- .../generic_processing_tools/ClipToMainFrame.py | 1 + .../CreateConstantAttributePerRegion.py | 6 ++---- .../generic_processing_tools/FillPartialArrays.py | 5 +++-- .../generic_processing_tools/MergeBlockEnhanced.py | 10 +++++----- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py index 9eb7aa6f..4a68d359 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -6,7 +6,7 @@ import numpy.typing as npt import logging from typing_extensions import Self, Union -from vtkmodules.vtkCommonDataModel import vtkDataSet, vtkMultiBlockDataSet +from vtkmodules.vtkCommonDataModel import ( vtkDataSet, vtkMultiBlockDataSet ) from geos.mesh.utils.arrayModifiers import transferAttributeWithElementMap from geos.mesh.utils.arrayHelpers import ( computeElementMapping, getAttributeSet, isAttributeGlobal ) from geos.utils.Logger import ( Logger, getLogger ) @@ -103,6 +103,7 @@ def __init__( else: self.logger = logging.getLogger( loggerTitle ) self.logger.setLevel( logging.INFO ) + self.logger.propagate = False def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: """Set a specific handler for the filter logger. diff --git a/geos-processing/src/geos/processing/generic_processing_tools/ClipToMainFrame.py b/geos-processing/src/geos/processing/generic_processing_tools/ClipToMainFrame.py index 6b95fbba..eba72f08 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/ClipToMainFrame.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/ClipToMainFrame.py @@ -234,6 +234,7 @@ def __init__( self, speHandler: bool = False, **properties: str ) -> None: else: self.logger = logging.getLogger( loggerTitle ) self.logger.setLevel( logging.INFO ) + self.logger.propagate = False def ComputeTransform( self ) -> None: """Update the transformation.""" 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 85e1fc74..8e79f998 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -9,10 +9,7 @@ from typing_extensions import Self import vtkmodules.util.numpy_support as vnp -from vtkmodules.vtkCommonDataModel import ( - vtkMultiBlockDataSet, - vtkDataSet, -) +from vtkmodules.vtkCommonDataModel import ( vtkMultiBlockDataSet, vtkDataSet ) from geos.utils.Logger import ( getLogger, Logger, CountWarningHandler ) from geos.mesh.utils.arrayHelpers import ( getArrayInObject, getComponentNames, getNumberOfComponents, @@ -134,6 +131,7 @@ def __init__( else: self.logger = logging.getLogger( loggerTitle ) self.logger.setLevel( logging.INFO ) + self.logger.propagate = False def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: """Set a specific handler for the filter logger. diff --git a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py index feb56198..730809e7 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py @@ -1,11 +1,11 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. # SPDX-FileContributor: Romain Baville, Martin Lemay - +import logging from typing_extensions import Self from typing import Union, Any -from geos.utils.Logger import logging, Logger, getLogger +from geos.utils.Logger import ( Logger, getLogger ) from geos.mesh.utils.arrayModifiers import fillPartialAttributes from geos.mesh.utils.arrayHelpers import getAttributePieceInfo @@ -89,6 +89,7 @@ def __init__( else: self.logger = logging.getLogger( loggerTitle ) self.logger.setLevel( logging.INFO ) + self.logger.propagate = False def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: """Set a specific handler for the filter logger. diff --git a/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py b/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py index 9bcfa573..e7507fbb 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py @@ -6,13 +6,10 @@ from typing_extensions import Self -from geos.utils.Logger import Logger, getLogger +from geos.utils.Logger import ( Logger, getLogger ) from geos.mesh.utils.multiblockModifiers import mergeBlocks -from vtkmodules.vtkCommonDataModel import ( - vtkMultiBlockDataSet, - vtkUnstructuredGrid, -) +from vtkmodules.vtkCommonDataModel import ( vtkMultiBlockDataSet, vtkUnstructuredGrid ) __doc__ = """ Merge Blocks Keeping Partial Attributes is a filter that allows to merge blocks from a multiblock dataset @@ -89,6 +86,7 @@ def __init__( else: self.logger = logging.getLogger( loggerTitle ) self.logger.setLevel( logging.INFO ) + self.logger.propagate = False def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: """Set a specific handler for the filter logger. @@ -119,6 +117,8 @@ def applyFilter( self: Self ) -> None: outputMesh = mergeBlocks( self.inputMesh, keepPartialAttributes=True, logger=self.logger ) self.outputMesh = outputMesh + self.logger.info( f"The filter { self.logger.name } succeeded." ) + def getOutput( self: Self ) -> vtkUnstructuredGrid: """Get the merged mesh. From d0b2bfb4925e5bef9ca54eaf4d87fb2f7cbdbfa4 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 17 Nov 2025 15:34:28 +0100 Subject: [PATCH 02/39] Refactor SplitMesh without PVPythonAlgorytmBase --- .../generic_processing_tools/SplitMesh.py | 245 ++++++++---------- geos-processing/tests/test_SplitMesh.py | 7 +- geos-pv/src/geos/pv/plugins/PVSplitMesh.py | 11 +- 3 files changed, 113 insertions(+), 150 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py index f4a7a6bd..4bb356ed 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py @@ -1,35 +1,17 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. # SPDX-FileContributor: Antoine Mazuyer, Martin Lemay +import logging import numpy as np import numpy.typing as npt from typing_extensions import Self -from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase -from vtkmodules.vtkCommonCore import ( - vtkPoints, - vtkIdTypeArray, - vtkDataArray, - vtkInformation, - vtkInformationVector, -) -from vtkmodules.vtkCommonDataModel import ( - vtkUnstructuredGrid, - vtkCellArray, - vtkCellData, - vtkCell, - vtkCellTypes, - VTK_TRIANGLE, - VTK_QUAD, - VTK_TETRA, - VTK_HEXAHEDRON, - VTK_PYRAMID, - VTK_WEDGE, - VTK_POLYHEDRON, - VTK_POLYGON, -) +from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase +from vtkmodules.vtkCommonCore import ( vtkPoints, vtkIdTypeArray, vtkDataArray ) +from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkCellArray, vtkCellData, vtkCell, vtkCellTypes, VTK_TRIANGLE, VTK_QUAD, VTK_TETRA, VTK_HEXAHEDRON, VTK_PYRAMID, VTK_WEDGE, VTK_POLYHEDRON, VTK_POLYGON ) from vtkmodules.util.numpy_support import ( numpy_to_vtk, vtk_to_numpy ) +from geos.utils.Logger import ( Logger, getLogger ) from geos.processing.pre_processing.CellTypeCounterEnhanced import CellTypeCounterEnhanced from geos.mesh.model.CellTypeCounts import CellTypeCounts @@ -45,145 +27,124 @@ from geos.processing.generic_processing_tools.SplitMesh import SplitMesh # Filter inputs - input: vtkUnstructuredGrid + inputMesh: vtkUnstructuredGrid + speHandler: bool # optional # Instantiate the filter - splitMeshFilter: SplitMesh = SplitMesh() + splitMeshFilter: SplitMesh = SplitMesh( inputMesh, speHandler ) - # Set input data object - splitMeshFilter.SetInputDataObject( input ) + # Use your own handler (if speHandler is True) + yourHandler: logging.Handler + splitMeshFilter.setLoggerHandler( yourHandler ) # Do calculations - splitMeshFilter.Update() + splitMeshFilter.applyFilter() - # Get output object - output :vtkUnstructuredGrid = splitMeshFilter.GetOutputDataObject( 0 ) + # Get splitted mesh + outputMesh: vtkUnstructuredGrid = splitMeshFilter.getOutput() """ +loggerTitle: str = "Split Mesh" + class SplitMesh( VTKPythonAlgorithmBase ): - def __init__( self ) -> None: + def __init__( self, inputMesh: vtkUnstructuredGrid, speHandler: bool = False ) -> None: """SplitMesh filter splits each cell using edge centers.""" - super().__init__( nInputPorts=1, nOutputPorts=1, outputType="vtkUnstructuredGrid" ) - self.inData: vtkUnstructuredGrid + self.inputMesh: vtkUnstructuredGrid = inputMesh + self.outputMesh: vtkUnstructuredGrid = inputMesh.NewInstance() self.cells: vtkCellArray self.points: vtkPoints self.originalId: vtkIdTypeArray self.cellTypes: list[ int ] - def FillInputPortInformation( self: Self, port: int, info: vtkInformation ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestInformation. + # Logger + self.logger: Logger + if not speHandler: + self.logger = getLogger( loggerTitle, True ) + else: + self.logger = logging.getLogger( loggerTitle ) + self.logger.setLevel( logging.INFO ) + self.logger.propagate = False - Args: - port (int): input port - info (vtkInformationVector): info + def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: + """Set a specific handler for the filter logger. - Returns: - int: 1 if calculation successfully ended, 0 otherwise. - """ - if port == 0: - info.Set( self.INPUT_REQUIRED_DATA_TYPE(), "vtkUnstructuredGrid" ) - return 1 - - def RequestDataObject( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], # noqa: F841 - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestDataObject. + In this filter 4 log levels are use, .info, .error, .warning and .critical, be sure to have at least the same 4 levels. Args: - request (vtkInformation): request - inInfoVec (list[vtkInformationVector]): input objects - outInfoVec (vtkInformationVector): output objects - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. - """ - inData = self.GetInputData( inInfoVec, 0, 0 ) - outData = self.GetOutputData( outInfoVec, 0 ) - assert inData is not None - if outData is None or ( not outData.IsA( inData.GetClassName() ) ): - outData = inData.NewInstance() - outInfoVec.GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), outData ) - return super().RequestDataObject( request, inInfoVec, outInfoVec ) - - def RequestData( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], # noqa: F841 - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestData. - - Args: - request (vtkInformation): request - inInfoVec (list[vtkInformationVector]): input objects - outInfoVec (vtkInformationVector): output objects - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. + handler (logging.Handler): The handler to add. """ - self.inData = self.GetInputData( inInfoVec, 0, 0 ) - output: vtkUnstructuredGrid = self.GetOutputData( outInfoVec, 0 ) - - assert self.inData is not None, "Input mesh is undefined." - assert output is not None, "Output mesh is undefined." - - nbCells: int = self.inData.GetNumberOfCells() - counts: CellTypeCounts = self._getCellCounts() - nbTet: int = counts.getTypeCount( VTK_TETRA ) - nbPyr: int = counts.getTypeCount( VTK_PYRAMID ) - nbHex: int = counts.getTypeCount( VTK_HEXAHEDRON ) - nbTriangles: int = counts.getTypeCount( VTK_TRIANGLE ) - nbQuad: int = counts.getTypeCount( VTK_QUAD ) - nbPolygon: int = counts.getTypeCount( VTK_POLYGON ) - nbPolyhedra: int = counts.getTypeCount( VTK_POLYHEDRON ) - assert counts.getTypeCount( VTK_WEDGE ) == 0, "Input mesh contains wedges that are not currently supported." - assert nbPolyhedra * nbPolygon == 0, ( "Input mesh is composed of both polygons and polyhedra," - " but it must contains only one of the two." ) - nbNewPoints: int = 0 - nbNewPoints = nbHex * 19 + nbTet * 6 + nbPyr * 9 if nbPolyhedra > 0 else nbTriangles * 3 + nbQuad * 5 - nbNewCells: int = nbHex * 8 + nbTet * 8 + nbPyr * 10 * nbTriangles * 4 + nbQuad * 4 - - self.points = vtkPoints() - self.points.DeepCopy( self.inData.GetPoints() ) - self.points.Resize( self.inData.GetNumberOfPoints() + nbNewPoints ) - - self.cells = vtkCellArray() - self.cells.AllocateExact( nbNewCells, 8 ) - self.originalId = vtkIdTypeArray() - self.originalId.SetName( "OriginalID" ) - self.originalId.Allocate( nbNewCells ) - self.cellTypes = [] - for c in range( nbCells ): - cell: vtkCell = self.inData.GetCell( c ) - cellType: int = cell.GetCellType() - if cellType == VTK_HEXAHEDRON: - self._splitHexahedron( cell, c ) - elif cellType == VTK_TETRA: - self._splitTetrahedron( cell, c ) - elif cellType == VTK_PYRAMID: - self._splitPyramid( cell, c ) - elif cellType == VTK_TRIANGLE: - self._splitTriangle( cell, c ) - elif cellType == VTK_QUAD: - self._splitQuad( cell, c ) - else: - raise TypeError( f"Cell type {vtkCellTypes.GetClassNameFromTypeId(cellType)} is not supported." ) - # add points and cells - output.SetPoints( self.points ) - output.SetCells( self.cellTypes, self.cells ) - # add attribute saving original cell ids - cellArrays: vtkCellData = output.GetCellData() - assert cellArrays is not None, "Cell data is undefined." - cellArrays.AddArray( self.originalId ) - # transfer all cell arrays - self._transferCellArrays( output ) - return 1 + if not self.logger.hasHandlers(): + self.logger.addHandler( handler ) + else: + self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler' to True" + " during the filter initialization." ) + + def applyFilter( self: Self ) -> None: + """Apply the filter SplitMesh.""" + self.logger.info( f"Applying filter { self.logger.name }." ) + try: + nbCells: int = self.inputMesh.GetNumberOfCells() + counts: CellTypeCounts = self._getCellCounts() + nbTet: int = counts.getTypeCount( VTK_TETRA ) + nbPyr: int = counts.getTypeCount( VTK_PYRAMID ) + nbHex: int = counts.getTypeCount( VTK_HEXAHEDRON ) + nbTriangles: int = counts.getTypeCount( VTK_TRIANGLE ) + nbQuad: int = counts.getTypeCount( VTK_QUAD ) + nbPolygon: int = counts.getTypeCount( VTK_POLYGON ) + nbPolyhedra: int = counts.getTypeCount( VTK_POLYHEDRON ) + assert counts.getTypeCount( VTK_WEDGE ) == 0, "Input mesh contains wedges that are not currently supported." + assert nbPolyhedra * nbPolygon == 0, ( "Input mesh is composed of both polygons and polyhedra," + " but it must contains only one of the two." ) + nbNewPoints: int = 0 + nbNewPoints = nbHex * 19 + nbTet * 6 + nbPyr * 9 if nbPolyhedra > 0 else nbTriangles * 3 + nbQuad * 5 + nbNewCells: int = nbHex * 8 + nbTet * 8 + nbPyr * 10 * nbTriangles * 4 + nbQuad * 4 + + self.points = vtkPoints() + self.points.DeepCopy( self.inputMesh.GetPoints() ) + self.points.Resize( self.inputMesh.GetNumberOfPoints() + nbNewPoints ) + + self.cells = vtkCellArray() + self.cells.AllocateExact( nbNewCells, 8 ) + self.originalId = vtkIdTypeArray() + self.originalId.SetName( "OriginalID" ) + self.originalId.Allocate( nbNewCells ) + self.cellTypes = [] + for c in range( nbCells ): + cell: vtkCell = self.inputMesh.GetCell( c ) + cellType: int = cell.GetCellType() + if cellType == VTK_HEXAHEDRON: + self._splitHexahedron( cell, c ) + elif cellType == VTK_TETRA: + self._splitTetrahedron( cell, c ) + elif cellType == VTK_PYRAMID: + self._splitPyramid( cell, c ) + elif cellType == VTK_TRIANGLE: + self._splitTriangle( cell, c ) + elif cellType == VTK_QUAD: + self._splitQuad( cell, c ) + else: + raise TypeError( f"Cell type {vtkCellTypes.GetClassNameFromTypeId(cellType)} is not supported." ) + # add points and cells + self.outputMesh.SetPoints( self.points ) + self.outputMesh.SetCells( self.cellTypes, self.cells ) + # add attribute saving original cell ids + cellArrays: vtkCellData = self.outputMesh.GetCellData() + assert cellArrays is not None, "Cell data is undefined." + cellArrays.AddArray( self.originalId ) + # transfer all cell arrays + self._transferCellArrays( self.outputMesh ) + self.logger.info( f"The filter { self.logger.name } succeeded." ) + except ( TypeError, AssertionError ) as e: + self.logger.error( f"The filter {self.logger.name } failed.\n{ e }") + + return + + def getOutput( self: Self ) -> vtkUnstructuredGrid: + """Get the splitted mesh computed.""" + return self.outputMesh def _getCellCounts( self: Self ) -> CellTypeCounts: """Get the number of cells of each type. @@ -192,7 +153,7 @@ def _getCellCounts( self: Self ) -> CellTypeCounts: CellTypeCounts: cell type counts """ cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced() - cellTypeCounterEnhancedFilter.SetInputDataObject( self.inData ) + cellTypeCounterEnhancedFilter.SetInputDataObject( self.inputMesh ) cellTypeCounterEnhancedFilter.Update() return cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() @@ -450,11 +411,11 @@ def _transferCellArrays( self: Self, splittedMesh: vtkUnstructuredGrid ) -> bool splittedMesh (vtkUnstructuredGrid): splitted mesh Returns: - bool: True if arrays were successfully transfered. + bool: True if arrays were successfully transferred. """ cellDataSplitted: vtkCellData = splittedMesh.GetCellData() assert cellDataSplitted is not None, "Cell data of splitted mesh should be defined." - cellData: vtkCellData = self.inData.GetCellData() + cellData: vtkCellData = self.inputMesh.GetCellData() assert cellData is not None, "Cell data of input mesh should be defined." # for each array of input mesh for i in range( cellData.GetNumberOfArrays() ): diff --git a/geos-processing/tests/test_SplitMesh.py b/geos-processing/tests/test_SplitMesh.py index 0612b729..27700ece 100644 --- a/geos-processing/tests/test_SplitMesh.py +++ b/geos-processing/tests/test_SplitMesh.py @@ -149,10 +149,9 @@ def test_single_cell_split( test_case: TestCase ) -> None: test_case (TestCase): test case """ cellTypeName: str = vtkCellTypes.GetClassNameFromTypeId( test_case.cellType ) - splitMeshFilter: SplitMesh = SplitMesh() - splitMeshFilter.SetInputDataObject( test_case.mesh ) - splitMeshFilter.Update() - output: vtkUnstructuredGrid = splitMeshFilter.GetOutputDataObject( 0 ) + splitMeshFilter: SplitMesh = SplitMesh( test_case.mesh ) + splitMeshFilter.applyFilter() + output: vtkUnstructuredGrid = splitMeshFilter.getOutput() assert output is not None, "Output mesh is undefined." pointsOut: vtkPoints = output.GetPoints() assert pointsOut is not None, "Points from output mesh are undefined." diff --git a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py index f4381a83..02db9460 100644 --- a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py +++ b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py @@ -8,6 +8,8 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase ) +from paraview.detail.loghandler import ( # type: ignore[import-not-found] + VTKHandler ) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from vtkmodules.vtkCommonDataModel import ( vtkPointSet, ) @@ -52,9 +54,10 @@ def Filter( self: Self, inputMesh: vtkPointSet, outputMesh: vtkPointSet ) -> Non inputMesh(vtkPointSet): Input mesh. outputMesh: Output mesh. """ - splitMeshFilter: SplitMesh = SplitMesh() - splitMeshFilter.SetInputDataObject( inputMesh ) - splitMeshFilter.Update() - outputMesh.ShallowCopy( splitMeshFilter.GetOutputDataObject( 0 ) ) + splitMeshFilter: SplitMesh = SplitMesh( inputMesh, True ) + if len( splitMeshFilter.logger.handlers ) == 0: + splitMeshFilter.setLoggerHandler( VTKHandler() ) + splitMeshFilter.applyFilter() + outputMesh.ShallowCopy( splitMeshFilter.getOutput() ) return From be96ffd1302e99fb0e13ed1651130be36690ff47 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 17 Nov 2025 15:51:04 +0100 Subject: [PATCH 03/39] Uniform logger of posp-processing filters --- .../geos/processing/post_processing/GeomechanicsCalculator.py | 3 ++- .../src/geos/processing/post_processing/GeosBlockExtractor.py | 3 ++- .../src/geos/processing/post_processing/GeosBlockMerge.py | 2 +- .../src/geos/processing/post_processing/SurfaceGeomechanics.py | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py b/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py index 05340469..1c48b8cb 100644 --- a/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py +++ b/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py @@ -717,6 +717,7 @@ def __init__( else: self.logger = logging.getLogger( loggerName ) self.logger.setLevel( logging.INFO ) + self.logger.propagate = False def applyFilter( self: Self ) -> None: """Compute the geomechanics properties and create attributes on the mesh.""" @@ -774,7 +775,7 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: Args: handler (logging.Handler): The handler to add. """ - if not self.logger.hasHandlers(): + if len( self.logger.handlers ) == 0: self.logger.addHandler( handler ) else: self.logger.warning( diff --git a/geos-processing/src/geos/processing/post_processing/GeosBlockExtractor.py b/geos-processing/src/geos/processing/post_processing/GeosBlockExtractor.py index 985959aa..65050684 100644 --- a/geos-processing/src/geos/processing/post_processing/GeosBlockExtractor.py +++ b/geos-processing/src/geos/processing/post_processing/GeosBlockExtractor.py @@ -179,6 +179,7 @@ def __init__( else: self.logger = logging.getLogger( loggerName ) self.logger.setLevel( logging.INFO ) + self.logger.propagate = False def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: """Set a specific handler for the filter logger. @@ -188,7 +189,7 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: Args: handler (logging.Handler): The handler to add. """ - if not self.logger.hasHandlers(): + if len( self.logger.handlers ) == 0: self.logger.addHandler( handler ) else: self.logger.warning( diff --git a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py index fac8c108..b45904fa 100644 --- a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py +++ b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py @@ -114,7 +114,7 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: Args: handler (logging.Handler): The handler to add. """ - if not self.logger.hasHandlers(): + if len( self.logger.handlers ) == 0: self.handler = handler self.logger.addHandler( handler ) else: diff --git a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py index 62fcd013..3144a7be 100644 --- a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py +++ b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py @@ -117,6 +117,7 @@ def __init__( self: Self, surfacicMesh: vtkPolyData, speHandler: bool = False ) else: self.logger = logging.getLogger( loggerTitle ) self.logger.setLevel( logging.INFO ) + self.logger.propagate = False # Input surfacic mesh if not surfacicMesh.IsA( "vtkPolyData" ): @@ -149,7 +150,7 @@ def SetLoggerHandler( self: Self, handler: Logger ) -> None: Args: handler (logging.Handler): The handler to add. """ - if not self.logger.hasHandlers(): + if len( self.logger.handlers ) == 0: self.logger.addHandler( handler ) else: self.logger.warning( From cbba20b86b73909edba0183d0202a87399c064d7 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 17 Nov 2025 15:52:41 +0100 Subject: [PATCH 04/39] Uniform logger of generic-processing-tools filters --- .../processing/generic_processing_tools/AttributeMapping.py | 2 +- .../geos/processing/generic_processing_tools/ClipToMainFrame.py | 2 +- .../CreateConstantAttributePerRegion.py | 2 +- .../processing/generic_processing_tools/FillPartialArrays.py | 2 +- .../processing/generic_processing_tools/MergeBlockEnhanced.py | 2 +- .../src/geos/processing/generic_processing_tools/SplitMesh.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py index 4a68d359..fa211561 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -114,7 +114,7 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: Args: handler (logging.Handler): The handler to add. """ - if not self.logger.hasHandlers(): + if len( self.logger.handlers ) == 0: self.logger.addHandler( handler ) else: self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler'" diff --git a/geos-processing/src/geos/processing/generic_processing_tools/ClipToMainFrame.py b/geos-processing/src/geos/processing/generic_processing_tools/ClipToMainFrame.py index eba72f08..90a53411 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/ClipToMainFrame.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/ClipToMainFrame.py @@ -265,7 +265,7 @@ def SetLoggerHandler( self, handler: logging.Handler ) -> None: Args: handler (logging.Handler): The handler to add. """ - if not self.logger.hasHandlers(): + if len( self.logger.handlers ) == 0: self.logger.addHandler( handler ) else: self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler' to True" 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 8e79f998..9d7abf31 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -142,7 +142,7 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: Args: handler (logging.Handler): The handler to add. """ - if not self.logger.hasHandlers(): + if len( self.logger.handlers ) == 0: self.logger.addHandler( handler ) else: # This warning does not count for the number of warning created during the application of the filter. diff --git a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py index 730809e7..689169d1 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py @@ -100,7 +100,7 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: Args: handler (logging.Handler): The handler to add. """ - if not self.logger.hasHandlers(): + if len( self.logger.handlers ) == 0: self.logger.addHandler( handler ) else: self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler' to True" diff --git a/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py b/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py index e7507fbb..3ad563f5 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py @@ -96,7 +96,7 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: Args: handler (logging.Handler): The handler to add. """ - if not self.logger.hasHandlers(): + if len( self.logger.handlers ) == 0: self.logger.addHandler( handler ) else: self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler' to True" diff --git a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py index 4bb356ed..02e8d482 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py @@ -76,7 +76,7 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: Args: handler (logging.Handler): The handler to add. """ - if not self.logger.hasHandlers(): + if len( self.logger.handlers ) == 0: self.logger.addHandler( handler ) else: self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler' to True" From 313469d8b06c28e1053a3444d4f4d8add6fe9627 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 17 Nov 2025 16:25:41 +0100 Subject: [PATCH 05/39] Refactor the filter without VTKPythonAlgorythmBase --- .../pre_processing/CellTypeCounterEnhanced.py | 147 ++++++++++-------- .../tests/test_CellTypeCounterEnhanced.py | 10 +- .../pv/plugins/PVCellTypeCounterEnhanced.py | 14 +- 3 files changed, 91 insertions(+), 80 deletions(-) diff --git a/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py b/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py index c8f02e8b..424ee518 100644 --- a/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py +++ b/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py @@ -1,17 +1,15 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. # SPDX-FileContributor: Antoine Mazuyer, Martin Lemay +import logging + from typing_extensions import Self -from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase -from vtkmodules.vtkCommonCore import ( - vtkInformation, - vtkInformationVector, - vtkIntArray, -) +from vtkmodules.vtkCommonCore import vtkIntArray from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkCell, vtkTable, vtkCellTypes, VTK_VERTEX ) from geos.mesh.model.CellTypeCounts import CellTypeCounts from geos.mesh.stats.meshQualityMetricHelpers import getAllCellTypes +from geos.utils.Logger import ( Logger, getLogger ) __doc__ = """ CellTypeCounterEnhanced module is a vtk filter that computes cell type counts. @@ -25,86 +23,95 @@ from geos.processing.pre_processing.CellTypeCounterEnhanced import CellTypeCounterEnhanced # Filter inputs - input: vtkUnstructuredGrid + inputMesh: vtkUnstructuredGrid + speHandler: bool # defaults to False # Instantiate the filter - cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced() + cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( inputMesh, speHandler ) - # Set input data object - cellTypeCounterEnhancedFilter.SetInputDataObject(input) + # Set the handler of yours (only if speHandler is True). + yourHandler: logging.Handler + cellTypeCounterEnhancedFilter.setLoggerHandler( yourHandler ) # Do calculations - cellTypeCounterEnhancedFilter.Update() + cellTypeCounterEnhancedFilter.applyFilter() - # Get counts + # Get result counts: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() + outputTable: vtkTable = cellTypeCounterEnhancedFilter.getOutput() """ +loggerTitle: str = "Cell Type Counter Enhanced" -class CellTypeCounterEnhanced( VTKPythonAlgorithmBase ): - def __init__( self ) -> None: - """CellTypeCounterEnhanced filter computes mesh stats.""" - super().__init__( nInputPorts=1, nOutputPorts=1, inputType="vtkUnstructuredGrid", outputType="vtkTable" ) - self._counts: CellTypeCounts = CellTypeCounts() +class CellTypeCounterEnhanced(): - def FillInputPortInformation( self: Self, port: int, info: vtkInformation ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestInformation. + def __init__( self: Self, inputMesh: vtkUnstructuredGrid, speHandler: bool = False, ) -> None: + """CellTypeCounterEnhanced filter computes mesh stats. Args: - port (int): Input port - info (vtkInformationVector): Info - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. + inputMesh (vtkUnstructuredGrid): The input mesh. + speHandler (bool, optional): True to use a specific handler, False to use the internal handler. + Defaults to False. """ - if port == 0: - info.Set( self.INPUT_REQUIRED_DATA_TYPE(), "vtkUnstructuredGrid" ) - return 1 - - def RequestData( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], # noqa: F841 - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestData. + self.inputMesh: vtkUnstructuredGrid = inputMesh + self.outTable: vtkTable = vtkTable() + self._counts: CellTypeCounts = CellTypeCounts() - Args: - request (vtkInformation): Request - inInfoVec (list[vtkInformationVector]): Input objects - outInfoVec (vtkInformationVector): Output objects + # Logger. + self.logger: Logger + if not speHandler: + self.logger = getLogger( loggerTitle, True ) + else: + self.logger = logging.getLogger( loggerTitle ) + self.logger.setLevel( logging.INFO ) + self.logger.propagate = False - Returns: - int: 1 if calculation successfully ended, 0 otherwise. + def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: + """Set a specific handler for the filter logger. + + In this filter 4 log levels are use, .info, .error, .warning and .critical, + be sure to have at least the same 4 levels. + + Args: + handler (logging.Handler): The handler to add. """ - inData: vtkUnstructuredGrid = self.GetInputData( inInfoVec, 0, 0 ) - outTable: vtkTable = vtkTable.GetData( outInfoVec, 0 ) - assert inData is not None, "Input mesh is undefined." - assert outTable is not None, "Output table is undefined." - - # compute cell type counts - self._counts.reset() - self._counts.setTypeCount( VTK_VERTEX, inData.GetNumberOfPoints() ) - for i in range( inData.GetNumberOfCells() ): - cell: vtkCell = inData.GetCell( i ) - self._counts.addType( cell.GetCellType() ) - - # create output table - # first reset output table - outTable.RemoveAllRows() - outTable.RemoveAllColumns() - outTable.SetNumberOfRows( 1 ) - - # create columns per types - for cellType in getAllCellTypes(): - array: vtkIntArray = vtkIntArray() - array.SetName( vtkCellTypes.GetClassNameFromTypeId( cellType ) ) - array.SetNumberOfComponents( 1 ) - array.SetNumberOfValues( 1 ) - array.SetValue( 0, self._counts.getTypeCount( cellType ) ) - outTable.AddColumn( array ) - return 1 + if len( self.logger.handlers ) == 0: + self.logger.addHandler( handler ) + else: + self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler'" + " to True during the filter initialization." ) + + def applyFilter( self: Self ) -> None: + """Apply CellTypeCounterEnhanced filter.""" + self.logger.info( f"Apply filter { self.logger.name }." ) + try: + # compute cell type counts + self._counts.reset() + self._counts.setTypeCount( VTK_VERTEX, self.inputMesh.GetNumberOfPoints() ) + for i in range( self.inputMesh.GetNumberOfCells() ): + cell: vtkCell = self.inputMesh.GetCell( i ) + self._counts.addType( cell.GetCellType() ) + + # create output table + # first reset output table + self.outTable.RemoveAllRows() + self.outTable.RemoveAllColumns() + self.outTable.SetNumberOfRows( 1 ) + + # create columns per types + for cellType in getAllCellTypes(): + array: vtkIntArray = vtkIntArray() + array.SetName( vtkCellTypes.GetClassNameFromTypeId( cellType ) ) + array.SetNumberOfComponents( 1 ) + array.SetNumberOfValues( 1 ) + array.SetValue( 0, self._counts.getTypeCount( cellType ) ) + self.outTable.AddColumn( array ) + self.logger.info( f"The filter { self.logger.name } succeeded." ) + except AssertionError as e: + self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) + + return def GetCellTypeCountsObject( self: Self ) -> CellTypeCounts: """Get CellTypeCounts object. @@ -113,3 +120,7 @@ def GetCellTypeCountsObject( self: Self ) -> CellTypeCounts: CellTypeCounts: CellTypeCounts object. """ return self._counts + + def getOutput( self: Self ) -> vtkTable: + """Get the computed vtkTable.""" + return self.outTable diff --git a/geos-processing/tests/test_CellTypeCounterEnhanced.py b/geos-processing/tests/test_CellTypeCounterEnhanced.py index 03097775..4aaf9d4d 100644 --- a/geos-processing/tests/test_CellTypeCounterEnhanced.py +++ b/geos-processing/tests/test_CellTypeCounterEnhanced.py @@ -71,9 +71,8 @@ def test_CellTypeCounterEnhanced_single( test_case: TestCase ) -> None: Args: test_case (TestCase): Test case """ - cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced() - cellTypeCounterEnhancedFilter.SetInputDataObject( test_case.mesh ) - cellTypeCounterEnhancedFilter.Update() + cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( test_case.mesh ) + cellTypeCounterEnhancedFilter.applyFilter() countsObs: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() assert countsObs is not None, "CellTypeCounts is undefined" @@ -130,9 +129,8 @@ def test_CellTypeCounterEnhanced_multi( test_case: TestCase ) -> None: Args: test_case (TestCase): Test case """ - cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced() - cellTypeCounterEnhancedFilter.SetInputDataObject( test_case.mesh ) - cellTypeCounterEnhancedFilter.Update() + cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( test_case.mesh ) + cellTypeCounterEnhancedFilter.applyFilter() countsObs: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() assert countsObs is not None, "CellTypeCounts is undefined" diff --git a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py index c5d3f046..4ce9f291 100644 --- a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py @@ -9,6 +9,8 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy ) +from paraview.detail.loghandler import ( # type: ignore[import-not-found] + VTKHandler ) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from vtkmodules.vtkCommonCore import ( vtkInformation, @@ -137,10 +139,11 @@ def RequestData( assert inputMesh is not None, "Input server mesh is null." assert outputTable is not None, "Output pipeline is null." - cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced() - cellTypeCounterEnhancedFilter.SetInputDataObject( inputMesh ) - cellTypeCounterEnhancedFilter.Update() - outputTable.ShallowCopy( cellTypeCounterEnhancedFilter.GetOutputDataObject( 0 ) ) + cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( inputMesh, True ) + if len( cellTypeCounterEnhancedFilter.logger.handlers ) == 0: + cellTypeCounterEnhancedFilter.setLoggerHandler( VTKHandler() ) + cellTypeCounterEnhancedFilter.applyFilter() + outputTable.ShallowCopy( cellTypeCounterEnhancedFilter.getOutput() ) # print counts in Output Messages view counts: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() @@ -153,6 +156,5 @@ def RequestData( fout.write( self._countsAll.print() ) print( f"File {self._filename} was successfully written." ) except Exception as e: - print( "Error while exporting the file due to:" ) - print( str( e ) ) + print( f"Error while exporting the file due to:\n{ e }" ) return 1 From 97010df7d771ff86ff791cebe47535df5904dd71 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 17 Nov 2025 16:38:28 +0100 Subject: [PATCH 06/39] Update with CellTypeCounterEnhanced changes --- .../generic_processing_tools/SplitMesh.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py index 02e8d482..cd565021 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py @@ -50,14 +50,21 @@ class SplitMesh( VTKPythonAlgorithmBase ): def __init__( self, inputMesh: vtkUnstructuredGrid, speHandler: bool = False ) -> None: - """SplitMesh filter splits each cell using edge centers.""" + """SplitMesh filter splits each cell using edge centers. + Args: + inputMesh (vtkUnstructuredGrid): The input mesh. + speHandler (bool, optional): True to use a specific handler, False to use the internal handler. + Defaults to False. + """ self.inputMesh: vtkUnstructuredGrid = inputMesh self.outputMesh: vtkUnstructuredGrid = inputMesh.NewInstance() self.cells: vtkCellArray self.points: vtkPoints self.originalId: vtkIdTypeArray self.cellTypes: list[ int ] + self.speHandler: bool = speHandler + self.handler: None | logging.Handler = None # Logger self.logger: Logger @@ -76,6 +83,7 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: Args: handler (logging.Handler): The handler to add. """ + self.handler = handler if len( self.logger.handlers ) == 0: self.logger.addHandler( handler ) else: @@ -137,7 +145,7 @@ def applyFilter( self: Self ) -> None: # transfer all cell arrays self._transferCellArrays( self.outputMesh ) self.logger.info( f"The filter { self.logger.name } succeeded." ) - except ( TypeError, AssertionError ) as e: + except Exception as e: self.logger.error( f"The filter {self.logger.name } failed.\n{ e }") return @@ -152,9 +160,10 @@ def _getCellCounts( self: Self ) -> CellTypeCounts: Returns: CellTypeCounts: cell type counts """ - cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced() - cellTypeCounterEnhancedFilter.SetInputDataObject( self.inputMesh ) - cellTypeCounterEnhancedFilter.Update() + cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( self.inputMesh, self.speHandler ) + if self.speHandler and len( cellTypeCounterEnhancedFilter.logger.handlers ) == 0: + cellTypeCounterEnhancedFilter.setLoggerHandler( self.handler ) + cellTypeCounterEnhancedFilter.applyFilter() return cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() def _addMidPoint( self: Self, ptA: int, ptB: int ) -> int: From 5bdc8e0d48f323f1809f0177b91e0b11efe16923 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 17 Nov 2025 17:18:47 +0100 Subject: [PATCH 07/39] Refactor the filter without VTKPythonAlgorythmBase --- .../pre_processing/MeshQualityEnhanced.py | 153 +++++++++--------- .../tests/test_MeshQualityEnhanced.py | 9 +- .../geos/pv/plugins/PVMeshQualityEnhanced.py | 12 +- 3 files changed, 83 insertions(+), 91 deletions(-) diff --git a/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py b/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py index fc8c662b..099bdd32 100644 --- a/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py +++ b/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py @@ -1,16 +1,14 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. # SPDX-FileContributor: Antoine Mazuyer, Martin Lemay, Paloma Martinez +import logging import numpy as np import numpy.typing as npt from typing import Optional, cast from typing_extensions import Self from vtkmodules.vtkFiltersCore import vtkExtractEdges from vtkmodules.vtkFiltersVerdict import vtkMeshQuality -from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase from vtkmodules.vtkCommonCore import ( - vtkInformation, - vtkInformationVector, vtkIdList, vtkPoints, vtkDataArray, @@ -23,11 +21,11 @@ vtkCell, vtkCell3D, vtkTetra, vtkCellTypes, vtkPolygon, VTK_TRIANGLE, VTK_QUAD, VTK_TETRA, VTK_PYRAMID, VTK_HEXAHEDRON, VTK_WEDGE, VTK_POLYGON, VTK_POLYHEDRON ) -from vtkmodules.util.numpy_support import vtk_to_numpy, numpy_to_vtk +from vtkmodules.util.numpy_support import ( vtk_to_numpy, numpy_to_vtk ) 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.model.QualityMetricSummary import ( QualityMetricSummary, StatTypes ) from geos.mesh.utils.arrayHelpers import getAttributesFromDataSet from geos.mesh.stats.meshQualityMetricHelpers import ( getQualityMeasureNameFromIndex, @@ -45,6 +43,7 @@ ) import geos.utils.geometryFunctions as geom +from geos.utils.Logger import ( Logger, getLogger ) __doc__ = """ MeshQualityEnhanced module is a vtk filter that computes mesh quality stats. @@ -60,13 +59,15 @@ from geos.processing.pre_processing.MeshQualityEnhanced import MeshQualityEnhanced # Filter inputs - input: vtkUnstructuredGrid + inputMesh: vtkUnstructuredGrid + speHandler: bool # optional # Instantiate the filter - meshQualityEnhancedFilter: MeshQualityEnhanced = MeshQualityEnhanced() + meshQualityEnhancedFilter: MeshQualityEnhanced = MeshQualityEnhanced( inputMesh, speHandler ) - # Set input data object - meshQualityEnhancedFilter.SetInputDataObject(input) + # Use your own handler (if speHandler is True) + yourHandler: logging.Handler + meshQualityEnhancedFilter.setLoggerHandler( yourHandler ) # Set metrics to use meshQualityEnhancedFilter.SetTriangleMetrics(triangleQualityMetrics) @@ -78,10 +79,10 @@ meshQualityEnhancedFilter.SetOtherMeshQualityMetrics(otherQualityMetrics) # Do calculations - meshQualityEnhancedFilter.Update() + meshQualityEnhancedFilter.applyFilter() # Get output mesh quality report - outputMesh: vtkUnstructuredGrid = meshQualityEnhancedFilter.GetOutputDataObject(0) + outputMesh: vtkUnstructuredGrid = meshQualityEnhancedFilter.getOutput() outputStats: QualityMetricSummary = meshQualityEnhancedFilter.GetQualityMetricSummary() """ @@ -100,13 +101,21 @@ def getQualityMetricArrayName( metric: int ) -> str: """ return QUALITY_ARRAY_NAME + "_" + "".join( getQualityMeasureNameFromIndex( metric ).split( " " ) ) +loggerTitle: str = "Mesh Quality Enhanced" -class MeshQualityEnhanced( VTKPythonAlgorithmBase ): - def __init__( self: Self ) -> None: - """Enhanced vtkMeshQuality filter.""" - super().__init__( nInputPorts=1, nOutputPorts=1, outputType="vtkUnstructuredGrid" ) - self._outputMesh: vtkUnstructuredGrid +class MeshQualityEnhanced(): + + def __init__( self: Self, inputMesh: vtkUnstructuredGrid, speHandler: bool = False, ) -> None: + """Enhanced vtkMeshQuality filter. + + Args: + inputMesh (vtkUnstructuredGrid): Input mesh + speHandler (bool, optional): True to use a specific handler, False to use the internal handler. + Defaults to False. + """ + self.inputMesh: vtkUnstructuredGrid = inputMesh + self._outputMesh: vtkUnstructuredGrid = vtkUnstructuredGrid() self._cellCounts: CellTypeCounts self._qualityMetricSummary: QualityMetricSummary = QualityMetricSummary() @@ -126,43 +135,32 @@ def __init__( self: Self ) -> None: self._allCellTypesExtended: tuple[ int, ...] = getAllCellTypesExtended() self._allCellTypes: tuple[ int, ...] = getAllCellTypes() - def FillInputPortInformation( self: Self, port: int, info: vtkInformation ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestInformation. - - Args: - port (int): Input port - info (vtkInformationVector): Info + # Logger. + self.speHandler: bool = speHandler + self.handler: None | logging.Handler = None + self.logger: Logger + if not speHandler: + self.logger = getLogger( loggerTitle, True ) + else: + self.logger = logging.getLogger( loggerTitle ) + self.logger.setLevel( logging.INFO ) + self.logger.propagate = False - Returns: - int: 1 if calculation successfully ended, 0 otherwise. - """ - if port == 0: - info.Set( self.INPUT_REQUIRED_DATA_TYPE(), "vtkUnstructuredGrid" ) - return 1 + def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: + """Set a specific handler for the filter logger. - def RequestDataObject( - self: Self, - request: vtkInformation, - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestDataObject. + In this filter 4 log levels are use, .info, .error, .warning and .critical, + be sure to have at least the same 4 levels. Args: - request (vtkInformation): Request - inInfoVec (list[vtkInformationVector]): Input objects - outInfoVec (vtkInformationVector): Output objects - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. + handler (logging.Handler): The handler to add. """ - inData = self.GetInputData( inInfoVec, 0, 0 ) - outData = self.GetOutputData( outInfoVec, 0 ) - assert inData is not None - if outData is None or ( not outData.IsA( inData.GetClassName() ) ): - outData = inData.NewInstance() - outInfoVec.GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), outData ) - return super().RequestDataObject( request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] + self.handler = handler + if len( self.logger.handlers ) == 0: + self.logger.addHandler( handler ) + else: + self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler'" + " to True during the filter initialization." ) def GetQualityMetricSummary( self: Self ) -> QualityMetricSummary: """Get QualityMetricSummary object. @@ -294,48 +292,41 @@ def getComputedMetricsFromCellType( self: Self, cellType: int ) -> Optional[ set metrics = metrics.intersection( computedMetrics ) return metrics if commonComputedMetricsExists else None - def RequestData( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], # noqa: F841 - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestData. + def applyFilter( self: Self ) -> None: + """Apply MeshQualityEnhanced filter.""" + self.logger.info( f"Apply filter { self.logger.name }." ) + try: + self._outputMesh.ShallowCopy( self.inputMesh ) + # Compute cell type counts + self._computeCellTypeCounts() - Args: - request (vtkInformation): Request - inInfoVec (list[vtkInformationVector]): Input objects - outInfoVec (vtkInformationVector): Output objects + # Compute metrics and associated attributes + self._evaluateMeshQualityAll() - Returns: - int: 1 if calculation successfully ended, 0 otherwise. - """ - inData: vtkUnstructuredGrid = self.GetInputData( inInfoVec, 0, 0 ) - self._outputMesh = self.GetOutputData( outInfoVec, 0 ) - assert inData is not None, "Input mesh is undefined." - assert self._outputMesh is not None, "Output pipeline is undefined." - self._outputMesh.ShallowCopy( inData ) + # Compute stats summary + self._updateStatsSummary() - # Compute cell type counts - self._computeCellTypeCounts() + # Create field data + self._createFieldDataStatsSummary() - # Compute metrics and associated attributes - self._evaluateMeshQualityAll() + self._outputMesh.Modified() - # Compute stats summary - self._updateStatsSummary() + self.logger.info( f"The filter { self.logger.name } succeeded." ) + except Exception as e: + self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) - # Create field data - self._createFieldDataStatsSummary() + return - self._outputMesh.Modified() - return 1 + def getOutput( self: Self ) -> vtkUnstructuredGrid: + """Get the mesh computed with the stats.""" + return self._outputMesh def _computeCellTypeCounts( self: Self ) -> None: """Compute cell type counts.""" - cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced() - cellTypeCounterEnhancedFilter.SetInputDataObject( self._outputMesh ) - cellTypeCounterEnhancedFilter.Update() + cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( self._outputMesh, self.speHandler ) + if self.speHandler and len( cellTypeCounterEnhancedFilter.logger.handlers ) == 0: + cellTypeCounterEnhancedFilter.setLoggerHandler( self.handler ) + cellTypeCounterEnhancedFilter.applyFilter() counts: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() assert counts is not None, "CellTypeCounts is undefined" self._qualityMetricSummary.setCellTypeCounts( counts ) @@ -426,7 +417,7 @@ def _applyMeshQualityFilter( self: Self, metric: int, cellTypes: list[ int ] ) - elif cellType == VTK_HEXAHEDRON: meshQualityFilter.SetHexQualityMeasure( metric ) else: - print( "Cell type is not supported." ) + raise TypeError( "Cell type is not supported." ) meshQualityFilter.Update() return meshQualityFilter.GetOutputDataObject( 0 ) diff --git a/geos-processing/tests/test_MeshQualityEnhanced.py b/geos-processing/tests/test_MeshQualityEnhanced.py index a86e2c97..574bfdc2 100644 --- a/geos-processing/tests/test_MeshQualityEnhanced.py +++ b/geos-processing/tests/test_MeshQualityEnhanced.py @@ -126,14 +126,13 @@ def __generate_test_data() -> Iterator[ TestCase ]: @pytest.mark.parametrize( "test_case", __generate_test_data(), ids=ids ) def test_MeshQualityEnhanced( test_case: TestCase ) -> None: - """Test of CellTypeCounterEnhanced filter. + """Test of MeshQualityEnhanced filter. Args: test_case (TestCase): Test case """ mesh = test_case.mesh - meshQualityEnhancedFilter: MeshQualityEnhanced = MeshQualityEnhanced() - meshQualityEnhancedFilter.SetInputDataObject( mesh ) + meshQualityEnhancedFilter: MeshQualityEnhanced = MeshQualityEnhanced( mesh ) if test_case.cellType == VTK_TRIANGLE: meshQualityEnhancedFilter.SetTriangleMetrics( test_case.qualityMetrics ) elif test_case.cellType == VTK_QUAD: @@ -146,7 +145,7 @@ def test_MeshQualityEnhanced( test_case: TestCase ) -> None: meshQualityEnhancedFilter.SetWedgeMetrics( test_case.qualityMetrics ) elif test_case.cellType == VTK_HEXAHEDRON: meshQualityEnhancedFilter.SetHexaMetrics( test_case.qualityMetrics ) - meshQualityEnhancedFilter.Update() + meshQualityEnhancedFilter.applyFilter() # test method getComputedMetricsFromCellType for i, cellType in enumerate( getAllCellTypesExtended() ): @@ -156,7 +155,7 @@ def test_MeshQualityEnhanced( test_case: TestCase ) -> None: assert metrics is not None, f"Metrics from {vtkCellTypes.GetClassNameFromTypeId(cellType)} cells is undefined." # test attributes - outputMesh: vtkUnstructuredGrid = meshQualityEnhancedFilter.GetOutputDataObject( 0 ) + outputMesh: vtkUnstructuredGrid = meshQualityEnhancedFilter.getOutput() cellData: vtkCellData = outputMesh.GetCellData() assert cellData is not None, "Cell data is undefined." diff --git a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py index ebccf9db..be5d52c7 100644 --- a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py @@ -9,6 +9,8 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smproperty, ) +from paraview.detail.loghandler import ( # type: ignore[import-not-found] + VTKHandler ) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from vtkmodules.vtkCommonCore import ( vtkDataArraySelection, ) from vtkmodules.vtkCommonDataModel import ( @@ -239,9 +241,9 @@ def Filter( self, inputMesh: vtkUnstructuredGrid, outputMesh: vtkUnstructuredGri self._getQualityMetricsToUse( self._HexQualityMetric ) ) otherMetrics: set[ int ] = self._getQualityMetricsToUse( self._commonMeshQualityMetric ) - meshQualityEnhancedFilter: MeshQualityEnhanced = MeshQualityEnhanced() - - meshQualityEnhancedFilter.SetInputDataObject( inputMesh ) + meshQualityEnhancedFilter: MeshQualityEnhanced = MeshQualityEnhanced( inputMesh, True ) + if len( meshQualityEnhancedFilter.logger.handlers ) == 0: + meshQualityEnhancedFilter.setLoggerHandler( VTKHandler() ) meshQualityEnhancedFilter.SetCellQualityMetrics( triangleMetrics=triangleMetrics, quadMetrics=quadMetrics, tetraMetrics=tetraMetrics, @@ -249,9 +251,9 @@ def Filter( self, inputMesh: vtkUnstructuredGrid, outputMesh: vtkUnstructuredGri wedgeMetrics=wedgeMetrics, hexaMetrics=hexaMetrics ) meshQualityEnhancedFilter.SetOtherMeshQualityMetrics( otherMetrics ) - meshQualityEnhancedFilter.Update() + meshQualityEnhancedFilter.applyFilter() - outputMesh.ShallowCopy( meshQualityEnhancedFilter.GetOutputDataObject( 0 ) ) + outputMesh.ShallowCopy( meshQualityEnhancedFilter.getOutput() ) # save to file if asked if self._saveToFile: From 479813a99050cada856c1fbc45d6a1dead52da25 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 17 Nov 2025 17:48:54 +0100 Subject: [PATCH 08/39] Uniform the logger in the paraview plugin --- .../src/geos/pv/plugins/PVAttributeMapping.py | 2 +- .../pv/plugins/PVCellTypeCounterEnhanced.py | 4 +-- .../src/geos/pv/plugins/PVClipToMainFrame.py | 2 +- .../PVCreateConstantAttributePerRegion.py | 2 +- .../geos/pv/plugins/PVFillPartialArrays.py | 2 +- .../pv/plugins/PVGeomechanicsCalculator.py | 6 ++-- .../src/geos/pv/plugins/PVGeosLogReader.py | 15 ++++++++-- .../geos/pv/plugins/PVMergeBlocksEnhanced.py | 10 +++---- .../geos/pv/plugins/PVMeshQualityEnhanced.py | 29 +++++++++---------- .../geos/pv/plugins/PVSurfaceGeomechanics.py | 2 +- 10 files changed, 41 insertions(+), 33 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index a8dfc28c..208dea76 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -191,7 +191,7 @@ def RequestData( attributeMappingFilter: AttributeMapping = AttributeMapping( meshFrom, outData, set( self.attributeNames ), self.onPoints, True ) - if not attributeMappingFilter.logger.hasHandlers(): + if len( attributeMappingFilter.logger.handlers ) == 0: attributeMappingFilter.setLoggerHandler( VTKHandler() ) attributeMappingFilter.applyFilter() diff --git a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py index 4ce9f291..d6120d2f 100644 --- a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py @@ -154,7 +154,7 @@ def RequestData( try: with open( self._filename, 'w' ) as fout: fout.write( self._countsAll.print() ) - print( f"File {self._filename} was successfully written." ) + cellTypeCounterEnhancedFilter.logger.info( f"File {self._filename} was successfully written." ) except Exception as e: - print( f"Error while exporting the file due to:\n{ e }" ) + cellTypeCounterEnhancedFilter.logger.info( f"Error while exporting the file due to:\n{ e }" ) return 1 diff --git a/geos-pv/src/geos/pv/plugins/PVClipToMainFrame.py b/geos-pv/src/geos/pv/plugins/PVClipToMainFrame.py index ac5bad1b..3a315102 100644 --- a/geos-pv/src/geos/pv/plugins/PVClipToMainFrame.py +++ b/geos-pv/src/geos/pv/plugins/PVClipToMainFrame.py @@ -44,7 +44,7 @@ class PVClipToMainFrame( VTKPythonAlgorithmBase ): def __init__( self ) -> None: """Init motherclass, filter and logger.""" self._realFilter = ClipToMainFrame( speHandler=True ) - if not self._realFilter.logger.hasHandlers(): + if len( self._realFilter.logger.handlers ) == 0: self._realFilter.SetLoggerHandler( VTKHandler() ) def Filter( self, inputMesh: vtkMultiBlockDataSet, outputMesh: vtkMultiBlockDataSet ) -> None: diff --git a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py index 8865739d..82ffec96 100644 --- a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py +++ b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py @@ -291,7 +291,7 @@ def Filter( self, inputMesh: vtkDataSet, outputMesh: vtkDataSet ) -> None: self.speHandler, ) - if not createConstantAttributePerRegionFilter.logger.hasHandlers(): + if len( createConstantAttributePerRegionFilter.logger.handlers ) == 0: createConstantAttributePerRegionFilter.setLoggerHandler( VTKHandler() ) createConstantAttributePerRegionFilter.applyFilter() diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index 5bf66234..c7ded546 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -108,7 +108,7 @@ def Filter( self, inputMesh: vtkMultiBlockDataSet, outputMesh: vtkMultiBlockData speHandler=True, ) - if not fillPartialArraysFilter.logger.hasHandlers(): + if len( fillPartialArraysFilter.logger.handlers ) == 0: fillPartialArraysFilter.setLoggerHandler( VTKHandler() ) fillPartialArraysFilter.applyFilter() diff --git a/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py b/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py index 5d870d75..694795f7 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py +++ b/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py @@ -199,7 +199,7 @@ def setRockCohesion( self: Self, rockCohesion: float ) -> None: @smdomain.xml( """ Reference friction angle to compute critical pore pressure. - The unit is °. Default is no friction case (i.e., 0.°). + The unit is °. Default is no friction case (i.e., 10.°). """ ) def setFrictionAngle( self: Self, frictionAngle: float ) -> None: @@ -247,7 +247,7 @@ def Filter( speHandler=True, ) - if not geomechanicsCalculatorFilter.logger.hasHandlers(): + if len( geomechanicsCalculatorFilter.logger.handlers ) == 0: geomechanicsCalculatorFilter.setLoggerHandler( VTKHandler() ) geomechanicsCalculatorFilter.physicalConstants.grainBulkModulus = self.grainBulkModulus @@ -272,7 +272,7 @@ def Filter( True, ) - if not geomechanicsCalculatorFilter.logger.hasHandlers(): + if len( geomechanicsCalculatorFilter.logger.handlers ) == 0: geomechanicsCalculatorFilter.setLoggerHandler( VTKHandler() ) geomechanicsCalculatorFilter.physicalConstants.grainBulkModulus = self.grainBulkModulus diff --git a/geos-pv/src/geos/pv/plugins/PVGeosLogReader.py b/geos-pv/src/geos/pv/plugins/PVGeosLogReader.py index 743d6dc2..d8141f2e 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeosLogReader.py +++ b/geos-pv/src/geos/pv/plugins/PVGeosLogReader.py @@ -4,6 +4,7 @@ # ruff: noqa: E402 # disable Module level import not at top of file import os import sys +import logging from pathlib import Path from enum import Enum from typing import Union, cast @@ -24,6 +25,9 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, ) +from paraview.detail.loghandler import ( # type: ignore[import-not-found] + VTKHandler, +) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from vtk import VTK_DOUBLE # type: ignore[import-untyped] from vtkmodules.vtkCommonCore import vtkDataArraySelection as vtkDAS from vtkmodules.vtkCommonCore import ( @@ -164,6 +168,13 @@ def __init__( self: Self ) -> None: for prop in propsSolvers: self.m_convergence.AddArray( prop ) + self.logger: logging.Logger = logging.getLogger( "Geos Log Reader" ) + self.logger.setLevel( logging.INFO ) + if len( self.logger.handlers ) == 0: + self.logger.addHandler( VTKHandler() ) + self.logger.propagate = False + self.logger.info( f"Apply plugin { self.logger.name }." ) + @smproperty.stringvector( name="DataFilepath", default_values="Enter a filepath to your data" ) @smdomain.filelist() @smhint.filechooser( extensions=[ "txt", "out" ], file_description="Data files" ) @@ -587,8 +598,8 @@ def RequestData( array_type=VTK_DOUBLE ) # type: ignore[no-untyped-call] newAttr.SetName( column ) output.AddColumn( newAttr ) + self.logger.info( f"The plugin { self.logger.name } succeeded.") except Exception as e: - print( "Error while reading Geos log file:" ) - print( str( e ) ) + self.logger.error( f"The plugin { self.logger.name } failed.\n{ e }" ) return 0 return 1 diff --git a/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py index 3ebcd7eb..e8047afb 100644 --- a/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py @@ -119,15 +119,15 @@ def RequestData( mergeBlockEnhancedFilter: MergeBlockEnhanced = MergeBlockEnhanced( inputMesh, True ) - if not mergeBlockEnhancedFilter.logger.hasHandlers(): + if len( mergeBlockEnhancedFilter.logger.handlers ) == 0: mergeBlockEnhancedFilter.setLoggerHandler( VTKHandler() ) try: mergeBlockEnhancedFilter.applyFilter() + outputMesh.ShallowCopy( mergeBlockEnhancedFilter.getOutput() ) + outputMesh.Modified() except ( ValueError, TypeError, RuntimeError ) as e: mergeBlockEnhancedFilter.logger.error( f"MergeBlock failed due to {e}", exc_info=True ) return 0 - else: - outputMesh.ShallowCopy( mergeBlockEnhancedFilter.getOutput() ) - outputMesh.Modified() - return 1 + + return 1 diff --git a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py index be5d52c7..62894f73 100644 --- a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py @@ -230,7 +230,6 @@ def Filter( self, inputMesh: vtkUnstructuredGrid, outputMesh: vtkUnstructuredGri self._getQualityMetricsToUse( self._triangleQualityMetric ) ) quadMetrics: set[ int ] = self._getQualityMetricsToUse( self._commonCellSurfaceQualityMetric ).union( self._getQualityMetricsToUse( self._quadsQualityMetric ) ) - tetraMetrics: set[ int ] = self._getQualityMetricsToUse( self._commonCellVolumeQualityMetric ).union( self._getQualityMetricsToUse( self._tetQualityMetric ) ) pyrMetrics: set[ int ] = self._getQualityMetricsToUse( self._commonCellVolumeQualityMetric ).union( @@ -241,42 +240,40 @@ def Filter( self, inputMesh: vtkUnstructuredGrid, outputMesh: vtkUnstructuredGri self._getQualityMetricsToUse( self._HexQualityMetric ) ) otherMetrics: set[ int ] = self._getQualityMetricsToUse( self._commonMeshQualityMetric ) - meshQualityEnhancedFilter: MeshQualityEnhanced = MeshQualityEnhanced( inputMesh, True ) - if len( meshQualityEnhancedFilter.logger.handlers ) == 0: - meshQualityEnhancedFilter.setLoggerHandler( VTKHandler() ) - meshQualityEnhancedFilter.SetCellQualityMetrics( triangleMetrics=triangleMetrics, + self.meshQualityEnhancedFilter: MeshQualityEnhanced = MeshQualityEnhanced( inputMesh, True ) + if len( self.meshQualityEnhancedFilter.logger.handlers ) == 0: + self.meshQualityEnhancedFilter.setLoggerHandler( VTKHandler() ) + self.meshQualityEnhancedFilter.SetCellQualityMetrics( triangleMetrics=triangleMetrics, quadMetrics=quadMetrics, tetraMetrics=tetraMetrics, pyramidMetrics=pyrMetrics, wedgeMetrics=wedgeMetrics, hexaMetrics=hexaMetrics ) - meshQualityEnhancedFilter.SetOtherMeshQualityMetrics( otherMetrics ) - meshQualityEnhancedFilter.applyFilter() + self.meshQualityEnhancedFilter.SetOtherMeshQualityMetrics( otherMetrics ) + self.meshQualityEnhancedFilter.applyFilter() - outputMesh.ShallowCopy( meshQualityEnhancedFilter.getOutput() ) + outputMesh.ShallowCopy( self.meshQualityEnhancedFilter.getOutput() ) # save to file if asked if self._saveToFile: - stats: QualityMetricSummary = meshQualityEnhancedFilter.GetQualityMetricSummary() + stats: QualityMetricSummary = self.meshQualityEnhancedFilter.GetQualityMetricSummary() self.saveFile( stats ) self._blockIndex += 1 return - def saveFile( self: Self, stats: QualityMetricSummary ) -> None: + def saveFile( self: Self, stats: QualityMetricSummary, ) -> None: """Export mesh quality metric summary file.""" try: - if self._filename is None: - print( "Mesh quality summary report file path is undefined." ) - return + assert self._filename is not None, "Mesh quality summary report file path is undefined." + # add index for multiblock meshes index: int = self._filename.rfind( '.' ) filename: str = self._filename[ :index ] + f"_{self._blockIndex}" + self._filename[ index: ] fig = stats.plotSummaryFigure() fig.savefig( filename, dpi=150 ) - print( f"File {filename} was successfully written." ) + self.meshQualityEnhancedFilter.logger.info( f"File {filename} was successfully written." ) except Exception as e: - print( "Error while exporting the file due to:" ) - print( str( e ) ) + self.meshQualityEnhancedFilter.logger.error( f"Error while exporting the file due to:\n{ e }" ) def __initVolumeQualityMetricSelection( self: Self ) -> None: """Initialize the volumic metrics selection.""" diff --git a/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py b/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py index 2858766e..84c68d6f 100644 --- a/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py +++ b/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py @@ -131,7 +131,7 @@ def Filter( self: Self, inputMesh: vtkMultiBlockDataSet, outputMesh: vtkMultiBlo sgFilter: SurfaceGeomechanics = SurfaceGeomechanics( surfaceBlock, True ) sgFilter.SetSurfaceName( f"blockIndex {blockIndex}" ) - if not sgFilter.logger.hasHandlers(): + if len( sgFilter.logger.handlers ) == 0: sgFilter.SetLoggerHandler( VTKHandler() ) sgFilter.SetRockCohesion( self._getRockCohesion() ) From 33e8249d352293642088ff98c4748d478543c823 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 18 Nov 2025 11:07:31 +0100 Subject: [PATCH 09/39] clean ruff and yapf --- .../AttributeMapping.py | 2 +- .../generic_processing_tools/SplitMesh.py | 11 +++++++---- .../pre_processing/CellTypeCounterEnhanced.py | 6 +++++- .../pre_processing/MeshQualityEnhanced.py | 10 ++++++++-- .../pv/plugins/PVCellTypeCounterEnhanced.py | 3 ++- geos-pv/src/geos/pv/plugins/PVGeosLogReader.py | 2 +- .../geos/pv/plugins/PVMeshQualityEnhanced.py | 18 +++++++++++------- geos-pv/src/geos/pv/plugins/PVSplitMesh.py | 3 ++- 8 files changed, 37 insertions(+), 18 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py index fa211561..eb344523 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -5,7 +5,7 @@ import numpy as np import numpy.typing as npt import logging -from typing_extensions import Self, Union +from typing_extensions import ( Self, Union ) from vtkmodules.vtkCommonDataModel import ( vtkDataSet, vtkMultiBlockDataSet ) from geos.mesh.utils.arrayModifiers import transferAttributeWithElementMap from geos.mesh.utils.arrayHelpers import ( computeElementMapping, getAttributeSet, isAttributeGlobal ) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py index cd565021..f04ca9b3 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py @@ -8,7 +8,9 @@ from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase from vtkmodules.vtkCommonCore import ( vtkPoints, vtkIdTypeArray, vtkDataArray ) -from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkCellArray, vtkCellData, vtkCell, vtkCellTypes, VTK_TRIANGLE, VTK_QUAD, VTK_TETRA, VTK_HEXAHEDRON, VTK_PYRAMID, VTK_WEDGE, VTK_POLYHEDRON, VTK_POLYGON ) +from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkCellArray, vtkCellData, vtkCell, vtkCellTypes, + VTK_TRIANGLE, VTK_QUAD, VTK_TETRA, VTK_HEXAHEDRON, VTK_PYRAMID, VTK_WEDGE, + VTK_POLYHEDRON, VTK_POLYGON ) from vtkmodules.util.numpy_support import ( numpy_to_vtk, vtk_to_numpy ) from geos.utils.Logger import ( Logger, getLogger ) @@ -105,7 +107,7 @@ def applyFilter( self: Self ) -> None: nbPolyhedra: int = counts.getTypeCount( VTK_POLYHEDRON ) assert counts.getTypeCount( VTK_WEDGE ) == 0, "Input mesh contains wedges that are not currently supported." assert nbPolyhedra * nbPolygon == 0, ( "Input mesh is composed of both polygons and polyhedra," - " but it must contains only one of the two." ) + " but it must contains only one of the two." ) nbNewPoints: int = 0 nbNewPoints = nbHex * 19 + nbTet * 6 + nbPyr * 9 if nbPolyhedra > 0 else nbTriangles * 3 + nbQuad * 5 nbNewCells: int = nbHex * 8 + nbTet * 8 + nbPyr * 10 * nbTriangles * 4 + nbQuad * 4 @@ -146,7 +148,7 @@ def applyFilter( self: Self ) -> None: self._transferCellArrays( self.outputMesh ) self.logger.info( f"The filter { self.logger.name } succeeded." ) except Exception as e: - self.logger.error( f"The filter {self.logger.name } failed.\n{ e }") + self.logger.error( f"The filter {self.logger.name } failed.\n{ e }" ) return @@ -160,7 +162,8 @@ def _getCellCounts( self: Self ) -> CellTypeCounts: Returns: CellTypeCounts: cell type counts """ - cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( self.inputMesh, self.speHandler ) + cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( + self.inputMesh, self.speHandler ) if self.speHandler and len( cellTypeCounterEnhancedFilter.logger.handlers ) == 0: cellTypeCounterEnhancedFilter.setLoggerHandler( self.handler ) cellTypeCounterEnhancedFilter.applyFilter() diff --git a/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py b/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py index 424ee518..072222cc 100644 --- a/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py +++ b/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py @@ -46,7 +46,11 @@ class CellTypeCounterEnhanced(): - def __init__( self: Self, inputMesh: vtkUnstructuredGrid, speHandler: bool = False, ) -> None: + def __init__( + self: Self, + inputMesh: vtkUnstructuredGrid, + speHandler: bool = False, + ) -> None: """CellTypeCounterEnhanced filter computes mesh stats. Args: diff --git a/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py b/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py index 099bdd32..29afa3b6 100644 --- a/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py +++ b/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py @@ -101,12 +101,17 @@ def getQualityMetricArrayName( metric: int ) -> str: """ return QUALITY_ARRAY_NAME + "_" + "".join( getQualityMeasureNameFromIndex( metric ).split( " " ) ) + loggerTitle: str = "Mesh Quality Enhanced" class MeshQualityEnhanced(): - def __init__( self: Self, inputMesh: vtkUnstructuredGrid, speHandler: bool = False, ) -> None: + def __init__( + self: Self, + inputMesh: vtkUnstructuredGrid, + speHandler: bool = False, + ) -> None: """Enhanced vtkMeshQuality filter. Args: @@ -323,7 +328,8 @@ def getOutput( self: Self ) -> vtkUnstructuredGrid: def _computeCellTypeCounts( self: Self ) -> None: """Compute cell type counts.""" - cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( self._outputMesh, self.speHandler ) + cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( + self._outputMesh, self.speHandler ) if self.speHandler and len( cellTypeCounterEnhancedFilter.logger.handlers ) == 0: cellTypeCounterEnhancedFilter.setLoggerHandler( self.handler ) cellTypeCounterEnhancedFilter.applyFilter() diff --git a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py index d6120d2f..387d6213 100644 --- a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py @@ -10,7 +10,8 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy ) from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler ) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py + VTKHandler +) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from vtkmodules.vtkCommonCore import ( vtkInformation, diff --git a/geos-pv/src/geos/pv/plugins/PVGeosLogReader.py b/geos-pv/src/geos/pv/plugins/PVGeosLogReader.py index d8141f2e..0062a5e9 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeosLogReader.py +++ b/geos-pv/src/geos/pv/plugins/PVGeosLogReader.py @@ -598,7 +598,7 @@ def RequestData( array_type=VTK_DOUBLE ) # type: ignore[no-untyped-call] newAttr.SetName( column ) output.AddColumn( newAttr ) - self.logger.info( f"The plugin { self.logger.name } succeeded.") + self.logger.info( f"The plugin { self.logger.name } succeeded." ) except Exception as e: self.logger.error( f"The plugin { self.logger.name } failed.\n{ e }" ) return 0 diff --git a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py index 1ee16074..f84864b8 100644 --- a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py @@ -10,7 +10,8 @@ VTKPythonAlgorithmBase, smdomain, smproperty, ) from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler ) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py + VTKHandler +) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from vtkmodules.vtkCommonCore import ( vtkDataArraySelection, ) from vtkmodules.vtkCommonDataModel import ( @@ -244,11 +245,11 @@ def ApplyFilter( self, inputMesh: vtkUnstructuredGrid, outputMesh: vtkUnstructur if len( self.meshQualityEnhancedFilter.logger.handlers ) == 0: self.meshQualityEnhancedFilter.setLoggerHandler( VTKHandler() ) self.meshQualityEnhancedFilter.SetCellQualityMetrics( triangleMetrics=triangleMetrics, - quadMetrics=quadMetrics, - tetraMetrics=tetraMetrics, - pyramidMetrics=pyrMetrics, - wedgeMetrics=wedgeMetrics, - hexaMetrics=hexaMetrics ) + quadMetrics=quadMetrics, + tetraMetrics=tetraMetrics, + pyramidMetrics=pyrMetrics, + wedgeMetrics=wedgeMetrics, + hexaMetrics=hexaMetrics ) self.meshQualityEnhancedFilter.SetOtherMeshQualityMetrics( otherMetrics ) self.meshQualityEnhancedFilter.applyFilter() @@ -261,7 +262,10 @@ def ApplyFilter( self, inputMesh: vtkUnstructuredGrid, outputMesh: vtkUnstructur self._blockIndex += 1 return - def saveFile( self: Self, stats: QualityMetricSummary, ) -> None: + def saveFile( + self: Self, + stats: QualityMetricSummary, + ) -> None: """Export mesh quality metric summary file.""" try: assert self._filename is not None, "Mesh quality summary report file path is undefined." diff --git a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py index aaedac77..be55b8e8 100644 --- a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py +++ b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py @@ -9,7 +9,8 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase ) from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler ) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py + VTKHandler +) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from vtkmodules.vtkCommonDataModel import ( vtkPointSet, ) From 94537cb9fb6848d3f16927e02dbeaaaa7d296587 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 18 Nov 2025 11:16:31 +0100 Subject: [PATCH 10/39] remove PVPythonAlgorythmBase --- .../src/geos/processing/generic_processing_tools/SplitMesh.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py index f04ca9b3..24cd85fc 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py @@ -6,7 +6,6 @@ import numpy.typing as npt from typing_extensions import Self -from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase from vtkmodules.vtkCommonCore import ( vtkPoints, vtkIdTypeArray, vtkDataArray ) from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkCellArray, vtkCellData, vtkCell, vtkCellTypes, VTK_TRIANGLE, VTK_QUAD, VTK_TETRA, VTK_HEXAHEDRON, VTK_PYRAMID, VTK_WEDGE, @@ -49,7 +48,7 @@ loggerTitle: str = "Split Mesh" -class SplitMesh( VTKPythonAlgorithmBase ): +class SplitMesh(): def __init__( self, inputMesh: vtkUnstructuredGrid, speHandler: bool = False ) -> None: """SplitMesh filter splits each cell using edge centers. From 533fce1a90e8f43d28fccdf5552f8d72a7f6ce06 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 19 Nov 2025 13:59:58 +0100 Subject: [PATCH 11/39] update logger --- .../geos/pv/plugins/PVMeshQualityEnhanced.py | 65 ++++++++----------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py index f84864b8..67fa7710 100644 --- a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py @@ -2,20 +2,17 @@ # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. # SPDX-FileContributor: Martin Lemay, Paloma Martinez # ruff: noqa: E402 # disable Module level import not at top of file +import logging import sys from pathlib import Path from typing_extensions import Self, Optional -from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase, smdomain, smproperty, -) -from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler -) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py -from vtkmodules.vtkCommonCore import ( - vtkDataArraySelection, ) -from vtkmodules.vtkCommonDataModel import ( - vtkUnstructuredGrid, ) +from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase, smdomain, smproperty # type: ignore[import-not-found] +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py + +from vtkmodules.vtkCommonCore import vtkDataArraySelection +from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid # update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent @@ -26,24 +23,14 @@ from geos.mesh.model.QualityMetricSummary import QualityMetricSummary from geos.processing.pre_processing.MeshQualityEnhanced import MeshQualityEnhanced - -from geos.mesh.stats.meshQualityMetricHelpers import ( - getQualityMetricsOther, - getQualityMeasureNameFromIndex, - getQualityMeasureIndexFromName, - getQuadQualityMeasure, - getTriangleQualityMeasure, - getCommonPolygonQualityMeasure, - getTetQualityMeasure, - getPyramidQualityMeasure, - getWedgeQualityMeasure, - getHexQualityMeasure, - getCommonPolyhedraQualityMeasure, -) -from geos.pv.utils.checkboxFunction import ( # type: ignore[attr-defined] - createModifiedCallback, ) +from geos.mesh.stats.meshQualityMetricHelpers import ( getQualityMetricsOther, getQualityMeasureNameFromIndex, + getQualityMeasureIndexFromName, getQuadQualityMeasure, + getTriangleQualityMeasure, getCommonPolygonQualityMeasure, + getTetQualityMeasure, getPyramidQualityMeasure, + getWedgeQualityMeasure, getHexQualityMeasure, + getCommonPolyhedraQualityMeasure ) +from geos.pv.utils.checkboxFunction import createModifiedCallback # type: ignore[attr-defined] from geos.pv.utils.paraviewTreatments import getArrayChoices - from geos.pv.utils.details import ( SISOFilter, FilterCategory ) __doc__ = """ @@ -241,30 +228,32 @@ def ApplyFilter( self, inputMesh: vtkUnstructuredGrid, outputMesh: vtkUnstructur self._getQualityMetricsToUse( self._HexQualityMetric ) ) otherMetrics: set[ int ] = self._getQualityMetricsToUse( self._commonMeshQualityMetric ) - self.meshQualityEnhancedFilter: MeshQualityEnhanced = MeshQualityEnhanced( inputMesh, True ) - if len( self.meshQualityEnhancedFilter.logger.handlers ) == 0: - self.meshQualityEnhancedFilter.setLoggerHandler( VTKHandler() ) - self.meshQualityEnhancedFilter.SetCellQualityMetrics( triangleMetrics=triangleMetrics, + meshQualityEnhancedFilter: MeshQualityEnhanced = MeshQualityEnhanced( inputMesh, True ) + if len( meshQualityEnhancedFilter.logger.handlers ) == 0: + meshQualityEnhancedFilter.setLoggerHandler( VTKHandler() ) + meshQualityEnhancedFilter.SetCellQualityMetrics( triangleMetrics=triangleMetrics, quadMetrics=quadMetrics, tetraMetrics=tetraMetrics, pyramidMetrics=pyrMetrics, wedgeMetrics=wedgeMetrics, hexaMetrics=hexaMetrics ) - self.meshQualityEnhancedFilter.SetOtherMeshQualityMetrics( otherMetrics ) - self.meshQualityEnhancedFilter.applyFilter() + meshQualityEnhancedFilter.SetOtherMeshQualityMetrics( otherMetrics ) + meshQualityEnhancedFilter.applyFilter() - outputMesh.ShallowCopy( self.meshQualityEnhancedFilter.getOutput() ) + outputMesh.ShallowCopy( meshQualityEnhancedFilter.getOutput() ) # save to file if asked if self._saveToFile: - stats: QualityMetricSummary = self.meshQualityEnhancedFilter.GetQualityMetricSummary() - self.saveFile( stats ) + stats: QualityMetricSummary = meshQualityEnhancedFilter.GetQualityMetricSummary() + logger: logging.Logger = meshQualityEnhancedFilter.logger + self.saveFile( stats, logger ) self._blockIndex += 1 return def saveFile( self: Self, stats: QualityMetricSummary, + logger: logging.Logger, ) -> None: """Export mesh quality metric summary file.""" try: @@ -275,9 +264,9 @@ def saveFile( filename: str = self._filename[ :index ] + f"_{self._blockIndex}" + self._filename[ index: ] fig = stats.plotSummaryFigure() fig.savefig( filename, dpi=150 ) - self.meshQualityEnhancedFilter.logger.info( f"File {filename} was successfully written." ) + logger.info( f"File {filename} was successfully written." ) except Exception as e: - self.meshQualityEnhancedFilter.logger.error( f"Error while exporting the file due to:\n{ e }" ) + logger.error( f"Error while exporting the file due to:\n{ e }" ) def __initVolumeQualityMetricSelection( self: Self ) -> None: """Initialize the volumic metrics selection.""" From 1e935edadb90110af3b203cce124c4b60ca87aff Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 19 Nov 2025 14:35:10 +0100 Subject: [PATCH 12/39] Uniform the import --- .../AttributeMapping.py | 4 +- .../CreateConstantAttributePerRegion.py | 2 +- .../MergeBlockEnhanced.py | 2 +- .../generic_processing_tools/SplitMesh.py | 4 +- .../post_processing/GeomechanicsCalculator.py | 28 +++--------- .../post_processing/GeosBlockMerge.py | 3 +- .../post_processing/SurfaceGeomechanics.py | 22 ++-------- .../pre_processing/CellTypeCounterEnhanced.py | 2 +- .../pre_processing/MeshQualityEnhanced.py | 33 ++++---------- .../tests/test_CellTypeCounterEnhanced.py | 22 +++------- .../tests/test_MeshQualityEnhanced.py | 9 ++-- geos-processing/tests/test_SplitMesh.py | 9 +--- .../src/geos/pv/plugins/PVAttributeMapping.py | 18 ++------ .../pv/plugins/PVCellTypeCounterEnhanced.py | 14 ++---- .../src/geos/pv/plugins/PVClipToMainFrame.py | 15 +++---- .../PVCreateConstantAttributePerRegion.py | 17 +++---- .../geos/pv/plugins/PVFillPartialArrays.py | 15 +++---- .../pv/plugins/PVGeomechanicsCalculator.py | 20 +++------ .../geos/pv/plugins/PVGeomechanicsWorkflow.py | 13 ++---- .../pv/plugins/PVGeosBlockExtractAndMerge.py | 5 +-- .../src/geos/pv/plugins/PVGeosLogReader.py | 40 ++++------------- .../geos/pv/plugins/PVMergeBlocksEnhanced.py | 18 +++----- .../geos/pv/plugins/PVMeshQualityEnhanced.py | 10 ++--- .../src/geos/pv/plugins/PVMohrCirclePlot.py | 44 ++++++------------- .../pv/plugins/PVPythonViewConfigurator.py | 42 +++++------------- geos-pv/src/geos/pv/plugins/PVSplitMesh.py | 13 +++--- .../geos/pv/plugins/PVSurfaceGeomechanics.py | 26 +++-------- 27 files changed, 131 insertions(+), 319 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py index eb344523..0ab6f3d3 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -5,8 +5,8 @@ import numpy as np import numpy.typing as npt import logging -from typing_extensions import ( Self, Union ) -from vtkmodules.vtkCommonDataModel import ( vtkDataSet, vtkMultiBlockDataSet ) +from typing_extensions import Self, Union +from vtkmodules.vtkCommonDataModel import vtkDataSet, vtkMultiBlockDataSet from geos.mesh.utils.arrayModifiers import transferAttributeWithElementMap from geos.mesh.utils.arrayHelpers import ( computeElementMapping, getAttributeSet, isAttributeGlobal ) from geos.utils.Logger import ( Logger, getLogger ) 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 9d7abf31..7844c118 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -9,7 +9,7 @@ from typing_extensions import Self import vtkmodules.util.numpy_support as vnp -from vtkmodules.vtkCommonDataModel import ( vtkMultiBlockDataSet, vtkDataSet ) +from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet, vtkDataSet from geos.utils.Logger import ( getLogger, Logger, CountWarningHandler ) from geos.mesh.utils.arrayHelpers import ( getArrayInObject, getComponentNames, getNumberOfComponents, diff --git a/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py b/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py index 3ad563f5..1c6f6b18 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py @@ -9,7 +9,7 @@ from geos.utils.Logger import ( Logger, getLogger ) from geos.mesh.utils.multiblockModifiers import mergeBlocks -from vtkmodules.vtkCommonDataModel import ( vtkMultiBlockDataSet, vtkUnstructuredGrid ) +from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet, vtkUnstructuredGrid __doc__ = """ Merge Blocks Keeping Partial Attributes is a filter that allows to merge blocks from a multiblock dataset diff --git a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py index 24cd85fc..9978c5a0 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py @@ -6,11 +6,11 @@ import numpy.typing as npt from typing_extensions import Self -from vtkmodules.vtkCommonCore import ( vtkPoints, vtkIdTypeArray, vtkDataArray ) +from vtkmodules.vtkCommonCore import vtkPoints, vtkIdTypeArray, vtkDataArray from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkCellArray, vtkCellData, vtkCell, vtkCellTypes, VTK_TRIANGLE, VTK_QUAD, VTK_TETRA, VTK_HEXAHEDRON, VTK_PYRAMID, VTK_WEDGE, VTK_POLYHEDRON, VTK_POLYGON ) -from vtkmodules.util.numpy_support import ( numpy_to_vtk, vtk_to_numpy ) +from vtkmodules.util.numpy_support import numpy_to_vtk, vtk_to_numpy from geos.utils.Logger import ( Logger, getLogger ) from geos.processing.pre_processing.CellTypeCounterEnhanced import CellTypeCounterEnhanced diff --git a/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py b/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py index 1c48b8cb..0d5b89ef 100644 --- a/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py +++ b/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py @@ -13,27 +13,13 @@ import geos.geomechanics.processing.geomechanicsCalculatorFunctions as fcts from geos.mesh.utils.arrayModifiers import createAttribute -from geos.mesh.utils.arrayHelpers import ( - getArrayInObject, - isAttributeInObject, -) - -from geos.utils.Logger import ( - Logger, - getLogger, -) -from geos.utils.GeosOutputsConstants import ( - AttributeEnum, - ComponentNameEnum, - GeosMeshOutputsEnum, - PostProcessingOutputsEnum, -) -from geos.utils.PhysicalConstants import ( - DEFAULT_FRICTION_ANGLE_RAD, - DEFAULT_GRAIN_BULK_MODULUS, - DEFAULT_ROCK_COHESION, - WATER_DENSITY, -) +from geos.mesh.utils.arrayHelpers import ( getArrayInObject, isAttributeInObject ) + +from geos.utils.Logger import ( Logger, getLogger ) +from geos.utils.GeosOutputsConstants import ( AttributeEnum, ComponentNameEnum, GeosMeshOutputsEnum, + PostProcessingOutputsEnum ) +from geos.utils.PhysicalConstants import ( DEFAULT_FRICTION_ANGLE_RAD, DEFAULT_GRAIN_BULK_MODULUS, + DEFAULT_ROCK_COHESION, WATER_DENSITY ) from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid diff --git a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py index b45904fa..f604d049 100644 --- a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py +++ b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py @@ -5,8 +5,7 @@ import logging from typing_extensions import Self -from vtkmodules.vtkCommonDataModel import ( vtkCompositeDataSet, vtkMultiBlockDataSet, vtkPolyData, - vtkUnstructuredGrid ) +from vtkmodules.vtkCommonDataModel import vtkCompositeDataSet, vtkMultiBlockDataSet, vtkPolyData, vtkUnstructuredGrid from geos.utils.Errors import VTKError from geos.utils.Logger import ( Logger, getLogger ) diff --git a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py index 3144a7be..1ef8e591 100644 --- a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py +++ b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py @@ -12,26 +12,12 @@ from vtkmodules.vtkCommonDataModel import vtkPolyData from geos.mesh.utils.arrayModifiers import createAttribute -from geos.mesh.utils.arrayHelpers import ( - getArrayInObject, - getAttributeSet, - isAttributeInObject, -) -from geos.mesh.utils.genericHelpers import ( - getLocalBasisVectors, - convertAttributeFromLocalToXYZForOneCell, -) +from geos.mesh.utils.arrayHelpers import ( getArrayInObject, getAttributeSet, isAttributeInObject ) +from geos.mesh.utils.genericHelpers import ( getLocalBasisVectors, convertAttributeFromLocalToXYZForOneCell ) import geos.geomechanics.processing.geomechanicsCalculatorFunctions as fcts from geos.utils.Logger import ( Logger, getLogger ) -from geos.utils.PhysicalConstants import ( - DEFAULT_FRICTION_ANGLE_RAD, - DEFAULT_ROCK_COHESION, -) -from geos.utils.GeosOutputsConstants import ( - ComponentNameEnum, - GeosMeshOutputsEnum, - PostProcessingOutputsEnum, -) +from geos.utils.PhysicalConstants import ( DEFAULT_FRICTION_ANGLE_RAD, DEFAULT_ROCK_COHESION ) +from geos.utils.GeosOutputsConstants import ( ComponentNameEnum, GeosMeshOutputsEnum, PostProcessingOutputsEnum ) __doc__ = """ SurfaceGeomechanics is a VTK filter that allows: diff --git a/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py b/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py index 072222cc..568146dd 100644 --- a/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py +++ b/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py @@ -5,7 +5,7 @@ from typing_extensions import Self from vtkmodules.vtkCommonCore import vtkIntArray -from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkCell, vtkTable, vtkCellTypes, VTK_VERTEX ) +from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid, vtkCell, vtkTable, vtkCellTypes, VTK_VERTEX from geos.mesh.model.CellTypeCounts import CellTypeCounts from geos.mesh.stats.meshQualityMetricHelpers import getAllCellTypes diff --git a/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py b/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py index 29afa3b6..a5e94b6d 100644 --- a/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py +++ b/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py @@ -8,39 +8,24 @@ from typing_extensions import Self from vtkmodules.vtkFiltersCore import vtkExtractEdges from vtkmodules.vtkFiltersVerdict import vtkMeshQuality -from vtkmodules.vtkCommonCore import ( - vtkIdList, - vtkPoints, - vtkDataArray, - vtkIntArray, - vtkDoubleArray, - vtkIdTypeArray, - vtkMath, -) +from vtkmodules.vtkCommonCore import ( vtkIdList, vtkPoints, vtkDataArray, vtkIntArray, vtkDoubleArray, vtkIdTypeArray, + vtkMath ) from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkPolyData, vtkCellData, vtkPointData, vtkFieldData, vtkCell, vtkCell3D, vtkTetra, vtkCellTypes, vtkPolygon, VTK_TRIANGLE, VTK_QUAD, VTK_TETRA, VTK_PYRAMID, VTK_HEXAHEDRON, VTK_WEDGE, VTK_POLYGON, VTK_POLYHEDRON ) -from vtkmodules.util.numpy_support import ( vtk_to_numpy, numpy_to_vtk ) +from vtkmodules.util.numpy_support import vtk_to_numpy, numpy_to_vtk 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.stats.meshQualityMetricHelpers import ( - getQualityMeasureNameFromIndex, - getQualityMetricFromIndex, - VtkCellQualityMetricEnum, - CellQualityMetricAdditionalEnum, - QualityMetricOtherEnum, - MeshQualityMetricEnum, - getAllCellTypesExtended, - getAllCellTypes, - getPolygonCellTypes, - getPolyhedronCellTypes, - getCellQualityMeasureFromCellType, - getChildrenCellTypes, -) +from geos.mesh.stats.meshQualityMetricHelpers import ( getQualityMeasureNameFromIndex, getQualityMetricFromIndex, + VtkCellQualityMetricEnum, CellQualityMetricAdditionalEnum, + QualityMetricOtherEnum, MeshQualityMetricEnum, + getAllCellTypesExtended, getAllCellTypes, getPolygonCellTypes, + getPolyhedronCellTypes, getCellQualityMeasureFromCellType, + getChildrenCellTypes ) import geos.utils.geometryFunctions as geom from geos.utils.Logger import ( Logger, getLogger ) diff --git a/geos-processing/tests/test_CellTypeCounterEnhanced.py b/geos-processing/tests/test_CellTypeCounterEnhanced.py index 4aaf9d4d..7883592c 100644 --- a/geos-processing/tests/test_CellTypeCounterEnhanced.py +++ b/geos-processing/tests/test_CellTypeCounterEnhanced.py @@ -6,27 +6,15 @@ import numpy as np import numpy.typing as npt import pytest -from typing import ( - Iterator, ) +from typing import Iterator -from geos.mesh.utils.genericHelpers import createSingleCellMesh, createMultiCellMesh +from geos.mesh.utils.genericHelpers import ( createSingleCellMesh, createMultiCellMesh ) from geos.processing.pre_processing.CellTypeCounterEnhanced import CellTypeCounterEnhanced from geos.mesh.model.CellTypeCounts import CellTypeCounts -from vtkmodules.vtkCommonDataModel import ( - vtkUnstructuredGrid, - vtkCellTypes, - vtkCell, - VTK_TRIANGLE, - VTK_QUAD, - VTK_TETRA, - VTK_VERTEX, - VTK_POLYHEDRON, - VTK_POLYGON, - VTK_PYRAMID, - VTK_HEXAHEDRON, - VTK_WEDGE, -) +from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkCellTypes, vtkCell, VTK_TRIANGLE, VTK_QUAD, + VTK_TETRA, VTK_VERTEX, VTK_POLYHEDRON, VTK_POLYGON, VTK_PYRAMID, + VTK_HEXAHEDRON, VTK_WEDGE ) data_root: str = os.path.join( os.path.dirname( os.path.abspath( __file__ ) ), "data" ) diff --git a/geos-processing/tests/test_MeshQualityEnhanced.py b/geos-processing/tests/test_MeshQualityEnhanced.py index 574bfdc2..730ec173 100644 --- a/geos-processing/tests/test_MeshQualityEnhanced.py +++ b/geos-processing/tests/test_MeshQualityEnhanced.py @@ -8,13 +8,10 @@ import numpy.typing as npt import pandas as pd import pytest -from typing import ( - Iterator, - Optional, -) +from typing import Iterator, Optional + from geos.mesh.utils.genericHelpers import createMultiCellMesh -from geos.mesh.stats.meshQualityMetricHelpers import ( - getAllCellTypesExtended, ) +from geos.mesh.stats.meshQualityMetricHelpers import getAllCellTypesExtended from geos.processing.pre_processing.MeshQualityEnhanced import MeshQualityEnhanced from geos.mesh.model.QualityMetricSummary import QualityMetricSummary diff --git a/geos-processing/tests/test_SplitMesh.py b/geos-processing/tests/test_SplitMesh.py index 27700ece..e5256b93 100644 --- a/geos-processing/tests/test_SplitMesh.py +++ b/geos-processing/tests/test_SplitMesh.py @@ -6,8 +6,7 @@ import numpy as np import numpy.typing as npt import pytest -from typing import ( - Iterator, ) +from typing import Iterator from geos.mesh.utils.genericHelpers import createSingleCellMesh from geos.processing.generic_processing_tools.SplitMesh import SplitMesh @@ -17,11 +16,7 @@ from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkCellArray, vtkCellData, vtkCellTypes, VTK_TRIANGLE, VTK_QUAD, VTK_TETRA, VTK_HEXAHEDRON, VTK_PYRAMID ) -from vtkmodules.vtkCommonCore import ( - vtkPoints, - vtkIdList, - vtkDataArray, -) +from vtkmodules.vtkCommonCore import vtkPoints, vtkIdList, vtkDataArray data_root: str = os.path.join( os.path.dirname( os.path.abspath( __file__ ) ), "data" ) diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index 208dea76..804f1c13 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -16,21 +16,11 @@ from geos.processing.generic_processing_tools.AttributeMapping import AttributeMapping from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, -) -from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler, -) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py + VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy ) +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] -from vtkmodules.vtkCommonCore import ( - vtkInformation, - vtkInformationVector, -) -from vtkmodules.vtkCommonDataModel import ( - vtkCompositeDataSet, - vtkDataSet, - vtkMultiBlockDataSet, -) +from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector +from vtkmodules.vtkCommonDataModel import vtkCompositeDataSet, vtkDataSet, vtkMultiBlockDataSet __doc__ = """ AttributeMapping is a paraview plugin that transfers global attributes from a source mesh to a final mesh with same point/cell coordinates. diff --git a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py index 387d6213..d87bc656 100644 --- a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py @@ -9,18 +9,10 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy ) -from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler -) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] -from vtkmodules.vtkCommonCore import ( - vtkInformation, - vtkInformationVector, -) -from vtkmodules.vtkCommonDataModel import ( - vtkPointSet, - vtkTable, -) +from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector +from vtkmodules.vtkCommonDataModel import vtkPointSet, vtkTable # update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent diff --git a/geos-pv/src/geos/pv/plugins/PVClipToMainFrame.py b/geos-pv/src/geos/pv/plugins/PVClipToMainFrame.py index 71fc1693..dff25ed5 100644 --- a/geos-pv/src/geos/pv/plugins/PVClipToMainFrame.py +++ b/geos-pv/src/geos/pv/plugins/PVClipToMainFrame.py @@ -5,15 +5,12 @@ import sys from pathlib import Path -from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase, -) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py -from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler, -) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py - -from vtkmodules.vtkCommonDataModel import ( - vtkMultiBlockDataSet, ) +from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py + +from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet # update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent diff --git a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py index bac209fe..95a7af0c 100644 --- a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py +++ b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py @@ -9,25 +9,20 @@ from typing import Any from typing_extensions import Self -from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase, smdomain, smproperty, -) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py -from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler, -) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py +from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase, smdomain, smproperty # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py import vtkmodules.util.numpy_support as vnp - -from vtkmodules.vtkCommonDataModel import ( - vtkDataSet, ) +from vtkmodules.vtkCommonDataModel import vtkDataSet # update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent sys.path.insert( 0, str( geos_pv_path / "src" ) ) from geos.processing.generic_processing_tools.CreateConstantAttributePerRegion import CreateConstantAttributePerRegion - -from geos.pv.utils.details import SISOFilter, FilterCategory +from geos.pv.utils.details import ( SISOFilter, FilterCategory ) __doc__ = """ PVCreateConstantAttributePerRegion is a Paraview plugin that allows to create an attribute diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index bc91a1d3..ee7fa1b1 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -7,15 +7,12 @@ from typing import Any, Optional, Union from typing_extensions import Self -from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase, smproperty, -) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py -from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler, -) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py +from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase, smproperty # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py -from vtkmodules.vtkCommonDataModel import ( - vtkMultiBlockDataSet, ) +from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet # update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent @@ -24,7 +21,7 @@ update_paths() -from geos.pv.utils.details import SISOFilter, FilterCategory +from geos.pv.utils.details import ( SISOFilter, FilterCategory ) from geos.processing.generic_processing_tools.FillPartialArrays import FillPartialArrays __doc__ = """ diff --git a/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py b/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py index d46c3887..deea93cc 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py +++ b/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py @@ -8,14 +8,12 @@ from pathlib import Path from typing_extensions import Self -from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase, smdomain, smproperty -) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py -from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler -) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py +from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase, smdomain, smproperty # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py -from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkMultiBlockDataSet ) +from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid, vtkMultiBlockDataSet # update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent @@ -24,12 +22,8 @@ update_paths() -from geos.utils.PhysicalConstants import ( - DEFAULT_FRICTION_ANGLE_DEG, - DEFAULT_GRAIN_BULK_MODULUS, - DEFAULT_ROCK_COHESION, - WATER_DENSITY, -) +from geos.utils.PhysicalConstants import ( DEFAULT_FRICTION_ANGLE_DEG, DEFAULT_GRAIN_BULK_MODULUS, + DEFAULT_ROCK_COHESION, WATER_DENSITY ) from geos.mesh.utils.multiblockHelpers import ( getBlockElementIndexesFlatten, getBlockNameFromIndex ) from geos.processing.post_processing.GeomechanicsCalculator import GeomechanicsCalculator from geos.pv.utils.details import ( SISOFilter, FilterCategory ) diff --git a/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflow.py b/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflow.py index 992a7302..44828829 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflow.py +++ b/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflow.py @@ -15,24 +15,19 @@ update_paths() -from geos.utils.PhysicalConstants import ( - DEFAULT_FRICTION_ANGLE_DEG, - DEFAULT_GRAIN_BULK_MODULUS, - DEFAULT_ROCK_COHESION, - WATER_DENSITY, -) +from geos.utils.PhysicalConstants import ( DEFAULT_FRICTION_ANGLE_DEG, DEFAULT_GRAIN_BULK_MODULUS, + DEFAULT_ROCK_COHESION, WATER_DENSITY ) from geos.pv.plugins.PVGeosBlockExtractAndMerge import PVGeosBlockExtractAndMerge from geos.pv.plugins.PVGeomechanicsCalculator import PVGeomechanicsCalculator from geos.pv.plugins.PVSurfaceGeomechanics import PVSurfaceGeomechanics -from vtkmodules.vtkCommonCore import ( vtkInformation, vtkInformationVector ) +from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smproperty, smproxy ) -from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler ) +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] __doc__ = """ PVGeomechanicsWorkflow is a Paraview plugin that executes multiple plugins: diff --git a/geos-pv/src/geos/pv/plugins/PVGeosBlockExtractAndMerge.py b/geos-pv/src/geos/pv/plugins/PVGeosBlockExtractAndMerge.py index c779c5eb..bd3bb3b8 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeosBlockExtractAndMerge.py +++ b/geos-pv/src/geos/pv/plugins/PVGeosBlockExtractAndMerge.py @@ -27,13 +27,12 @@ from geos.pv.utils.paraviewTreatments import getTimeStepIndex from geos.pv.utils.workflowFunctions import doExtractAndMerge -from vtkmodules.vtkCommonCore import ( vtkInformation, vtkInformationVector ) +from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smproperty, smproxy ) -from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler ) +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] __doc__ = """ PVGeosBlockExtractAndMerge is a Paraview plugin processing the input mesh at the current time in two steps: diff --git a/geos-pv/src/geos/pv/plugins/PVGeosLogReader.py b/geos-pv/src/geos/pv/plugins/PVGeosLogReader.py index 0062a5e9..ba91a014 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeosLogReader.py +++ b/geos-pv/src/geos/pv/plugins/PVGeosLogReader.py @@ -23,45 +23,23 @@ import vtkmodules.util.numpy_support as vnp from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, -) -from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler, -) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py -from vtk import VTK_DOUBLE # type: ignore[import-untyped] + VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy ) +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py + from vtkmodules.vtkCommonCore import vtkDataArraySelection as vtkDAS -from vtkmodules.vtkCommonCore import ( - vtkDoubleArray, - vtkInformation, - vtkInformationVector, -) +from vtkmodules.vtkCommonCore import vtkDoubleArray, vtkInformation, vtkInformationVector, VTK_DOUBLE from vtkmodules.vtkCommonDataModel import vtkTable -from geos.pv.geosLogReaderUtils.geosLogReaderFunctions import ( - identifyProperties, - transformUserChoiceToListPhases, -) - +from geos.pv.geosLogReaderUtils.geosLogReaderFunctions import ( identifyProperties, transformUserChoiceToListPhases ) from geos.pv.geosLogReaderUtils.GeosLogReaderAquifers import GeosLogReaderAquifers from geos.pv.geosLogReaderUtils.GeosLogReaderConvergence import GeosLogReaderConvergence from geos.pv.geosLogReaderUtils.GeosLogReaderFlow import GeosLogReaderFlow from geos.pv.geosLogReaderUtils.GeosLogReaderWells import GeosLogReaderWells -from geos.utils.enumUnits import ( - Mass, - MassRate, - Pressure, - Time, - Unit, - Volume, - VolumetricRate, - enumerationDomainUnit, -) - +from geos.utils.enumUnits import ( Mass, MassRate, Pressure, Time, Unit, Volume, VolumetricRate, enumerationDomainUnit ) from geos.utils.UnitRepository import UnitRepository -from geos.pv.utils.checkboxFunction import ( # type: ignore[attr-defined] - createModifiedCallback, ) -from geos.pv.utils.paraviewTreatments import ( - strListToEnumerationDomainXml, ) +from geos.pv.utils.checkboxFunction import createModifiedCallback # type: ignore[attr-defined] +from geos.pv.utils.paraviewTreatments import strListToEnumerationDomainXml __doc__ = """ ``PVGeosLogReader`` is a Paraview plugin that allows to read Geos output log. diff --git a/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py index e8047afb..c7ecd276 100644 --- a/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py @@ -8,19 +8,11 @@ from typing_extensions import Self from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, -) -from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler, ) -from vtkmodules.vtkCommonCore import ( - vtkInformation, - vtkInformationVector, -) -from vtkmodules.vtkCommonDataModel import ( - vtkCompositeDataSet, - vtkMultiBlockDataSet, - vtkUnstructuredGrid, -) + VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy ) +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] + +from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector +from vtkmodules.vtkCommonDataModel import vtkCompositeDataSet, vtkMultiBlockDataSet, vtkUnstructuredGrid # Update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent diff --git a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py index 67fa7710..a7ef8c8b 100644 --- a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py @@ -232,11 +232,11 @@ def ApplyFilter( self, inputMesh: vtkUnstructuredGrid, outputMesh: vtkUnstructur if len( meshQualityEnhancedFilter.logger.handlers ) == 0: meshQualityEnhancedFilter.setLoggerHandler( VTKHandler() ) meshQualityEnhancedFilter.SetCellQualityMetrics( triangleMetrics=triangleMetrics, - quadMetrics=quadMetrics, - tetraMetrics=tetraMetrics, - pyramidMetrics=pyrMetrics, - wedgeMetrics=wedgeMetrics, - hexaMetrics=hexaMetrics ) + quadMetrics=quadMetrics, + tetraMetrics=tetraMetrics, + pyramidMetrics=pyrMetrics, + wedgeMetrics=wedgeMetrics, + hexaMetrics=hexaMetrics ) meshQualityEnhancedFilter.SetOtherMeshQualityMetrics( otherMetrics ) meshQualityEnhancedFilter.applyFilter() diff --git a/geos-pv/src/geos/pv/plugins/PVMohrCirclePlot.py b/geos-pv/src/geos/pv/plugins/PVMohrCirclePlot.py index d6efdfd5..412355a3 100644 --- a/geos-pv/src/geos/pv/plugins/PVMohrCirclePlot.py +++ b/geos-pv/src/geos/pv/plugins/PVMohrCirclePlot.py @@ -10,19 +10,16 @@ import numpy as np import numpy.typing as npt -from paraview.simple import ( # type: ignore[import-not-found] - Render, ) + +from paraview.simple import Render # type: ignore[import-not-found] from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, -) -from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler, ) + VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy ) +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] from typing_extensions import Self from vtkmodules.vtkCommonCore import vtkDataArraySelection as vtkDAS from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector -from vtkmodules.vtkCommonDataModel import ( - vtkUnstructuredGrid, ) +from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid # Update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent @@ -33,33 +30,18 @@ from geos.geomechanics.model.MohrCircle import MohrCircle from geos.utils.enumUnits import Pressure, enumerationDomainUnit -from geos.utils.GeosOutputsConstants import ( - FAILURE_ENVELOPE, - GeosMeshOutputsEnum, -) +from geos.utils.GeosOutputsConstants import ( FAILURE_ENVELOPE, GeosMeshOutputsEnum ) from geos.utils.Logger import CustomLoggerFormatter -from geos.utils.PhysicalConstants import ( - DEFAULT_FRICTION_ANGLE_DEG, - DEFAULT_FRICTION_ANGLE_RAD, - DEFAULT_ROCK_COHESION, -) +from geos.utils.PhysicalConstants import ( DEFAULT_FRICTION_ANGLE_DEG, DEFAULT_FRICTION_ANGLE_RAD, + DEFAULT_ROCK_COHESION ) from geos.mesh.utils.arrayHelpers import getArrayInObject import geos.pv.utils.mohrCircles.functionsMohrCircle as mcf import geos.pv.utils.paraviewTreatments as pvt -from geos.pv.utils.checkboxFunction import ( # type: ignore[attr-defined] - createModifiedCallback, ) -from geos.pv.utils.DisplayOrganizationParaview import ( - buildNewLayoutWithPythonView, ) -from geos.pv.pyplotUtils.matplotlibOptions import ( - FontStyleEnum, - FontWeightEnum, - LegendLocationEnum, - LineStyleEnum, - MarkerStyleEnum, - OptionSelectionEnum, - optionEnumToXml, -) +from geos.pv.utils.checkboxFunction import createModifiedCallback # type: ignore[attr-defined] +from geos.pv.utils.DisplayOrganizationParaview import buildNewLayoutWithPythonView +from geos.pv.pyplotUtils.matplotlibOptions import ( FontStyleEnum, FontWeightEnum, LegendLocationEnum, LineStyleEnum, + MarkerStyleEnum, OptionSelectionEnum, optionEnumToXml ) from geos.pv.utils.mohrCircles.functionsMohrCircle import StressConventionEnum __doc__ = """ @@ -660,7 +642,7 @@ def RequestInformation( if self.requestDataStep < 0: # Get cell ids inData = self.GetInputData( inInfoVec, 0, 0 ) - self.cellIds = pvt.getVtkOriginalCellIds( inData, self.logger) + self.cellIds = pvt.getVtkOriginalCellIds( inData, self.logger ) # Update vtkDAS for circleId in self.cellIds: diff --git a/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py b/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py index b03f001b..e994e385 100755 --- a/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py +++ b/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py @@ -18,38 +18,18 @@ from geos.mesh.utils.multiblockModifiers import mergeBlocks import geos.pv.utils.paraviewTreatments as pvt -from geos.pv.utils.checkboxFunction import ( # type: ignore[attr-defined] - createModifiedCallback, ) -from geos.pv.utils.DisplayOrganizationParaview import ( - DisplayOrganizationParaview, ) -from geos.pv.pyplotUtils.matplotlibOptions import ( - FontStyleEnum, - FontWeightEnum, - LegendLocationEnum, - LineStyleEnum, - MarkerStyleEnum, - OptionSelectionEnum, - optionEnumToXml, -) +from geos.pv.utils.checkboxFunction import createModifiedCallback # type: ignore[attr-defined] +from geos.pv.utils.DisplayOrganizationParaview import DisplayOrganizationParaview +from geos.pv.pyplotUtils.matplotlibOptions import ( FontStyleEnum, FontWeightEnum, LegendLocationEnum, LineStyleEnum, + MarkerStyleEnum, OptionSelectionEnum, optionEnumToXml ) +from geos.pv.utils.details import ( SISOFilter, FilterCategory ) + from paraview.simple import ( # type: ignore[import-not-found] - GetActiveSource, GetActiveView, Render, Show, servermanager, -) -from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - smdomain, smproperty, -) -from vtkmodules.vtkCommonCore import ( - vtkDataArraySelection, - vtkInformation, -) - -from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase ) - -from vtkmodules.vtkCommonDataModel import ( - vtkDataObject, - vtkMultiBlockDataSet, -) -from geos.pv.utils.details import SISOFilter, FilterCategory + GetActiveSource, GetActiveView, Render, Show, servermanager ) +from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase, smdomain, smproperty # type: ignore[import-not-found] + +from vtkmodules.vtkCommonCore import vtkDataArraySelection, vtkInformation +from vtkmodules.vtkCommonDataModel import vtkDataObject, vtkMultiBlockDataSet __doc__ = """ PVPythonViewConfigurator is a Paraview plugin that allows to create cross-plots diff --git a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py index be55b8e8..e5461b2c 100644 --- a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py +++ b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py @@ -6,14 +6,11 @@ from pathlib import Path from typing_extensions import Self -from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase ) -from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler -) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py +from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase # type: ignore[import-not-found] +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py -from vtkmodules.vtkCommonDataModel import ( - vtkPointSet, ) +from vtkmodules.vtkCommonDataModel import vtkPointSet # update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent @@ -23,7 +20,7 @@ update_paths() from geos.processing.generic_processing_tools.SplitMesh import SplitMesh -from geos.pv.utils.details import SISOFilter, FilterCategory +from geos.pv.utils.details import ( SISOFilter, FilterCategory ) __doc__ = """ Split each cell of input mesh to smaller cells. diff --git a/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py b/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py index 6adf0c00..5c585ba7 100644 --- a/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py +++ b/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py @@ -7,11 +7,8 @@ import numpy as np from typing_extensions import Self -from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase, smdomain, smproperty, -) -from paraview.detail.loghandler import ( # type: ignore[import-not-found] - VTKHandler, ) +from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase, smdomain, smproperty # type: ignore[import-not-found] +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] # update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent @@ -21,21 +18,12 @@ update_paths() -from geos.utils.PhysicalConstants import ( - DEFAULT_FRICTION_ANGLE_DEG, - DEFAULT_ROCK_COHESION, -) +from geos.utils.PhysicalConstants import ( DEFAULT_FRICTION_ANGLE_DEG, DEFAULT_ROCK_COHESION ) from geos.processing.post_processing.SurfaceGeomechanics import SurfaceGeomechanics -from geos.mesh.utils.multiblockHelpers import ( - getBlockElementIndexesFlatten, - getBlockFromFlatIndex, -) -from vtkmodules.vtkCommonCore import ( - vtkDataArray, ) -from vtkmodules.vtkCommonDataModel import ( - vtkMultiBlockDataSet, - vtkPolyData, -) +from geos.mesh.utils.multiblockHelpers import ( getBlockElementIndexesFlatten, getBlockFromFlatIndex ) + +from vtkmodules.vtkCommonCore import vtkDataArray +from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet, vtkPolyData __doc__ = """ PVSurfaceGeomechanics is a Paraview plugin that allows to compute From cf9a43770b023bf8496c85c253b64049adf06764 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 20 Nov 2025 09:23:38 +0100 Subject: [PATCH 13/39] Refactor SplitMesh --- .../generic_processing_tools/SplitMesh.py | 270 ++++++++---------- geos-processing/tests/test_SplitMesh.py | 18 +- geos-pv/src/geos/pv/plugins/PVSplitMesh.py | 21 +- 3 files changed, 138 insertions(+), 171 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py index f4a7a6bd..8a59ce4e 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py @@ -1,35 +1,18 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. # SPDX-FileContributor: Antoine Mazuyer, Martin Lemay +import logging import numpy as np import numpy.typing as npt from typing_extensions import Self -from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase -from vtkmodules.vtkCommonCore import ( - vtkPoints, - vtkIdTypeArray, - vtkDataArray, - vtkInformation, - vtkInformationVector, -) -from vtkmodules.vtkCommonDataModel import ( - vtkUnstructuredGrid, - vtkCellArray, - vtkCellData, - vtkCell, - vtkCellTypes, - VTK_TRIANGLE, - VTK_QUAD, - VTK_TETRA, - VTK_HEXAHEDRON, - VTK_PYRAMID, - VTK_WEDGE, - VTK_POLYHEDRON, - VTK_POLYGON, -) - -from vtkmodules.util.numpy_support import ( numpy_to_vtk, vtk_to_numpy ) +from vtkmodules.vtkCommonCore import vtkPoints, vtkIdTypeArray, vtkDataArray +from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkCellArray, vtkCellData, vtkCell, vtkCellTypes, + VTK_TRIANGLE, VTK_QUAD, VTK_TETRA, VTK_HEXAHEDRON, VTK_PYRAMID, VTK_WEDGE, + VTK_POLYHEDRON, VTK_POLYGON ) +from vtkmodules.util.numpy_support import numpy_to_vtk, vtk_to_numpy + +from geos.utils.Logger import ( Logger, getLogger ) from geos.processing.pre_processing.CellTypeCounterEnhanced import CellTypeCounterEnhanced from geos.mesh.model.CellTypeCounts import CellTypeCounts @@ -45,145 +28,132 @@ from geos.processing.generic_processing_tools.SplitMesh import SplitMesh # Filter inputs - input: vtkUnstructuredGrid + inputMesh: vtkUnstructuredGrid + speHandler: bool # optional # Instantiate the filter - splitMeshFilter: SplitMesh = SplitMesh() + splitMeshFilter: SplitMesh = SplitMesh( inputMesh, speHandler ) - # Set input data object - splitMeshFilter.SetInputDataObject( input ) + # Use your own handler (if speHandler is True) + yourHandler: logging.Handler + splitMeshFilter.setLoggerHandler( yourHandler ) # Do calculations - splitMeshFilter.Update() + splitMeshFilter.applyFilter() - # Get output object - output :vtkUnstructuredGrid = splitMeshFilter.GetOutputDataObject( 0 ) + # Get splitted mesh + outputMesh: vtkUnstructuredGrid = splitMeshFilter.getOutput() """ +loggerTitle: str = "Split Mesh" + -class SplitMesh( VTKPythonAlgorithmBase ): +class SplitMesh(): - def __init__( self ) -> None: - """SplitMesh filter splits each cell using edge centers.""" - super().__init__( nInputPorts=1, nOutputPorts=1, outputType="vtkUnstructuredGrid" ) + def __init__( self, inputMesh: vtkUnstructuredGrid, speHandler: bool = False ) -> None: + """SplitMesh filter splits each cell using edge centers. - self.inData: vtkUnstructuredGrid + Args: + inputMesh (vtkUnstructuredGrid): The input mesh. + speHandler (bool, optional): True to use a specific handler, False to use the internal handler. + Defaults to False. + """ + self.inputMesh: vtkUnstructuredGrid = inputMesh + self.outputMesh: vtkUnstructuredGrid = inputMesh.NewInstance() self.cells: vtkCellArray self.points: vtkPoints self.originalId: vtkIdTypeArray self.cellTypes: list[ int ] + self.speHandler: bool = speHandler + self.handler: None | logging.Handler = None - def FillInputPortInformation( self: Self, port: int, info: vtkInformation ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestInformation. - - Args: - port (int): input port - info (vtkInformationVector): info - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. - """ - if port == 0: - info.Set( self.INPUT_REQUIRED_DATA_TYPE(), "vtkUnstructuredGrid" ) - return 1 - - def RequestDataObject( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], # noqa: F841 - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestDataObject. + # Logger + self.logger: Logger + if not speHandler: + self.logger = getLogger( loggerTitle, True ) + else: + self.logger = logging.getLogger( loggerTitle ) + self.logger.setLevel( logging.INFO ) + self.logger.propagate = False - Args: - request (vtkInformation): request - inInfoVec (list[vtkInformationVector]): input objects - outInfoVec (vtkInformationVector): output objects + def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: + """Set a specific handler for the filter logger. - Returns: - int: 1 if calculation successfully ended, 0 otherwise. - """ - inData = self.GetInputData( inInfoVec, 0, 0 ) - outData = self.GetOutputData( outInfoVec, 0 ) - assert inData is not None - if outData is None or ( not outData.IsA( inData.GetClassName() ) ): - outData = inData.NewInstance() - outInfoVec.GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), outData ) - return super().RequestDataObject( request, inInfoVec, outInfoVec ) - - def RequestData( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], # noqa: F841 - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestData. + In this filter 4 log levels are use, .info, .error, .warning and .critical, be sure to have at least the same 4 levels. Args: - request (vtkInformation): request - inInfoVec (list[vtkInformationVector]): input objects - outInfoVec (vtkInformationVector): output objects - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. + handler (logging.Handler): The handler to add. """ - self.inData = self.GetInputData( inInfoVec, 0, 0 ) - output: vtkUnstructuredGrid = self.GetOutputData( outInfoVec, 0 ) - - assert self.inData is not None, "Input mesh is undefined." - assert output is not None, "Output mesh is undefined." - - nbCells: int = self.inData.GetNumberOfCells() - counts: CellTypeCounts = self._getCellCounts() - nbTet: int = counts.getTypeCount( VTK_TETRA ) - nbPyr: int = counts.getTypeCount( VTK_PYRAMID ) - nbHex: int = counts.getTypeCount( VTK_HEXAHEDRON ) - nbTriangles: int = counts.getTypeCount( VTK_TRIANGLE ) - nbQuad: int = counts.getTypeCount( VTK_QUAD ) - nbPolygon: int = counts.getTypeCount( VTK_POLYGON ) - nbPolyhedra: int = counts.getTypeCount( VTK_POLYHEDRON ) - assert counts.getTypeCount( VTK_WEDGE ) == 0, "Input mesh contains wedges that are not currently supported." - assert nbPolyhedra * nbPolygon == 0, ( "Input mesh is composed of both polygons and polyhedra," - " but it must contains only one of the two." ) - nbNewPoints: int = 0 - nbNewPoints = nbHex * 19 + nbTet * 6 + nbPyr * 9 if nbPolyhedra > 0 else nbTriangles * 3 + nbQuad * 5 - nbNewCells: int = nbHex * 8 + nbTet * 8 + nbPyr * 10 * nbTriangles * 4 + nbQuad * 4 - - self.points = vtkPoints() - self.points.DeepCopy( self.inData.GetPoints() ) - self.points.Resize( self.inData.GetNumberOfPoints() + nbNewPoints ) - - self.cells = vtkCellArray() - self.cells.AllocateExact( nbNewCells, 8 ) - self.originalId = vtkIdTypeArray() - self.originalId.SetName( "OriginalID" ) - self.originalId.Allocate( nbNewCells ) - self.cellTypes = [] - for c in range( nbCells ): - cell: vtkCell = self.inData.GetCell( c ) - cellType: int = cell.GetCellType() - if cellType == VTK_HEXAHEDRON: - self._splitHexahedron( cell, c ) - elif cellType == VTK_TETRA: - self._splitTetrahedron( cell, c ) - elif cellType == VTK_PYRAMID: - self._splitPyramid( cell, c ) - elif cellType == VTK_TRIANGLE: - self._splitTriangle( cell, c ) - elif cellType == VTK_QUAD: - self._splitQuad( cell, c ) - else: - raise TypeError( f"Cell type {vtkCellTypes.GetClassNameFromTypeId(cellType)} is not supported." ) - # add points and cells - output.SetPoints( self.points ) - output.SetCells( self.cellTypes, self.cells ) - # add attribute saving original cell ids - cellArrays: vtkCellData = output.GetCellData() - assert cellArrays is not None, "Cell data is undefined." - cellArrays.AddArray( self.originalId ) - # transfer all cell arrays - self._transferCellArrays( output ) - return 1 + self.handler = handler + if len( self.logger.handlers ) == 0: + self.logger.addHandler( handler ) + else: + self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler' to True" + " during the filter initialization." ) + + def applyFilter( self: Self ) -> None: + """Apply the filter SplitMesh.""" + self.logger.info( f"Applying filter { self.logger.name }." ) + try: + nbCells: int = self.inputMesh.GetNumberOfCells() + counts: CellTypeCounts = self._getCellCounts() + nbTet: int = counts.getTypeCount( VTK_TETRA ) + nbPyr: int = counts.getTypeCount( VTK_PYRAMID ) + nbHex: int = counts.getTypeCount( VTK_HEXAHEDRON ) + nbTriangles: int = counts.getTypeCount( VTK_TRIANGLE ) + nbQuad: int = counts.getTypeCount( VTK_QUAD ) + nbPolygon: int = counts.getTypeCount( VTK_POLYGON ) + nbPolyhedra: int = counts.getTypeCount( VTK_POLYHEDRON ) + assert counts.getTypeCount( VTK_WEDGE ) == 0, "Input mesh contains wedges that are not currently supported." + assert nbPolyhedra * nbPolygon == 0, ( "Input mesh is composed of both polygons and polyhedra," + " but it must contains only one of the two." ) + nbNewPoints: int = 0 + nbNewPoints = nbHex * 19 + nbTet * 6 + nbPyr * 9 if nbPolyhedra > 0 else nbTriangles * 3 + nbQuad * 5 + nbNewCells: int = nbHex * 8 + nbTet * 8 + nbPyr * 10 * nbTriangles * 4 + nbQuad * 4 + + self.points = vtkPoints() + self.points.DeepCopy( self.inputMesh.GetPoints() ) + self.points.Resize( self.inputMesh.GetNumberOfPoints() + nbNewPoints ) + + self.cells = vtkCellArray() + self.cells.AllocateExact( nbNewCells, 8 ) + self.originalId = vtkIdTypeArray() + self.originalId.SetName( "OriginalID" ) + self.originalId.Allocate( nbNewCells ) + self.cellTypes = [] + for c in range( nbCells ): + cell: vtkCell = self.inputMesh.GetCell( c ) + cellType: int = cell.GetCellType() + if cellType == VTK_HEXAHEDRON: + self._splitHexahedron( cell, c ) + elif cellType == VTK_TETRA: + self._splitTetrahedron( cell, c ) + elif cellType == VTK_PYRAMID: + self._splitPyramid( cell, c ) + elif cellType == VTK_TRIANGLE: + self._splitTriangle( cell, c ) + elif cellType == VTK_QUAD: + self._splitQuad( cell, c ) + else: + raise TypeError( f"Cell type {vtkCellTypes.GetClassNameFromTypeId(cellType)} is not supported." ) + # add points and cells + self.outputMesh.SetPoints( self.points ) + self.outputMesh.SetCells( self.cellTypes, self.cells ) + # add attribute saving original cell ids + cellArrays: vtkCellData = self.outputMesh.GetCellData() + assert cellArrays is not None, "Cell data is undefined." + cellArrays.AddArray( self.originalId ) + # transfer all cell arrays + self._transferCellArrays( self.outputMesh ) + self.logger.info( f"The filter { self.logger.name } succeeded." ) + except Exception as e: + self.logger.error( f"The filter {self.logger.name } failed.\n{ e }" ) + + return + + def getOutput( self: Self ) -> vtkUnstructuredGrid: + """Get the splitted mesh computed.""" + return self.outputMesh def _getCellCounts( self: Self ) -> CellTypeCounts: """Get the number of cells of each type. @@ -191,9 +161,11 @@ def _getCellCounts( self: Self ) -> CellTypeCounts: Returns: CellTypeCounts: cell type counts """ - cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced() - cellTypeCounterEnhancedFilter.SetInputDataObject( self.inData ) - cellTypeCounterEnhancedFilter.Update() + cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( + self.inputMesh, self.speHandler ) + if self.speHandler and len( cellTypeCounterEnhancedFilter.logger.handlers ) == 0: + cellTypeCounterEnhancedFilter.setLoggerHandler( self.handler ) + cellTypeCounterEnhancedFilter.applyFilter() return cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() def _addMidPoint( self: Self, ptA: int, ptB: int ) -> int: @@ -450,11 +422,11 @@ def _transferCellArrays( self: Self, splittedMesh: vtkUnstructuredGrid ) -> bool splittedMesh (vtkUnstructuredGrid): splitted mesh Returns: - bool: True if arrays were successfully transfered. + bool: True if arrays were successfully transferred. """ cellDataSplitted: vtkCellData = splittedMesh.GetCellData() assert cellDataSplitted is not None, "Cell data of splitted mesh should be defined." - cellData: vtkCellData = self.inData.GetCellData() + cellData: vtkCellData = self.inputMesh.GetCellData() assert cellData is not None, "Cell data of input mesh should be defined." # for each array of input mesh for i in range( cellData.GetNumberOfArrays() ): @@ -476,4 +448,4 @@ def _transferCellArrays( self: Self, splittedMesh: vtkUnstructuredGrid ) -> bool cellDataSplitted.AddArray( newArray ) cellDataSplitted.Modified() splittedMesh.Modified() - return True + return True \ No newline at end of file diff --git a/geos-processing/tests/test_SplitMesh.py b/geos-processing/tests/test_SplitMesh.py index 0612b729..4fd1c3c4 100644 --- a/geos-processing/tests/test_SplitMesh.py +++ b/geos-processing/tests/test_SplitMesh.py @@ -6,8 +6,7 @@ import numpy as np import numpy.typing as npt import pytest -from typing import ( - Iterator, ) +from typing import Iterator from geos.mesh.utils.genericHelpers import createSingleCellMesh from geos.processing.generic_processing_tools.SplitMesh import SplitMesh @@ -17,11 +16,7 @@ from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkCellArray, vtkCellData, vtkCellTypes, VTK_TRIANGLE, VTK_QUAD, VTK_TETRA, VTK_HEXAHEDRON, VTK_PYRAMID ) -from vtkmodules.vtkCommonCore import ( - vtkPoints, - vtkIdList, - vtkDataArray, -) +from vtkmodules.vtkCommonCore import vtkPoints, vtkIdList, vtkDataArray data_root: str = os.path.join( os.path.dirname( os.path.abspath( __file__ ) ), "data" ) @@ -149,10 +144,9 @@ def test_single_cell_split( test_case: TestCase ) -> None: test_case (TestCase): test case """ cellTypeName: str = vtkCellTypes.GetClassNameFromTypeId( test_case.cellType ) - splitMeshFilter: SplitMesh = SplitMesh() - splitMeshFilter.SetInputDataObject( test_case.mesh ) - splitMeshFilter.Update() - output: vtkUnstructuredGrid = splitMeshFilter.GetOutputDataObject( 0 ) + splitMeshFilter: SplitMesh = SplitMesh( test_case.mesh ) + splitMeshFilter.applyFilter() + output: vtkUnstructuredGrid = splitMeshFilter.getOutput() assert output is not None, "Output mesh is undefined." pointsOut: vtkPoints = output.GetPoints() assert pointsOut is not None, "Points from output mesh are undefined." @@ -202,4 +196,4 @@ def test_single_cell_split( test_case: TestCase ) -> None: nbArraySplited: int = cellData.GetNumberOfArrays() assert nbArraySplited == nbArrayInput + 1, f"Number of arrays should be {nbArrayInput + 1}" - #assert False + #assert False \ No newline at end of file diff --git a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py index 728b398f..04952083 100644 --- a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py +++ b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py @@ -6,11 +6,11 @@ from pathlib import Path from typing_extensions import Self -from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase ) +from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase # type: ignore[import-not-found] +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py -from vtkmodules.vtkCommonDataModel import ( - vtkPointSet, ) +from vtkmodules.vtkCommonDataModel import vtkPointSet # update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent @@ -20,7 +20,7 @@ update_paths() from geos.processing.generic_processing_tools.SplitMesh import SplitMesh -from geos.pv.utils.details import SISOFilter, FilterCategory +from geos.pv.utils.details import ( SISOFilter, FilterCategory ) __doc__ = """ Split each cell of input mesh to smaller cells. @@ -50,9 +50,10 @@ def ApplyFilter( self: Self, inputMesh: vtkPointSet, outputMesh: vtkPointSet ) - inputMesh(vtkPointSet): Input mesh. outputMesh: Output mesh. """ - splitMeshFilter: SplitMesh = SplitMesh() - splitMeshFilter.SetInputDataObject( inputMesh ) - splitMeshFilter.Update() - outputMesh.ShallowCopy( splitMeshFilter.GetOutputDataObject( 0 ) ) + splitMeshFilter: SplitMesh = SplitMesh( inputMesh, True ) + if len( splitMeshFilter.logger.handlers ) == 0: + splitMeshFilter.setLoggerHandler( VTKHandler() ) + splitMeshFilter.applyFilter() + outputMesh.ShallowCopy( splitMeshFilter.getOutput() ) - return + return \ No newline at end of file From fe04cb2c82ff3918478670af59b5d39a5595ba73 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 20 Nov 2025 09:27:58 +0100 Subject: [PATCH 14/39] Refactor CellTypeCounterEnhanced --- .../pre_processing/CellTypeCounterEnhanced.py | 151 ++++++++++-------- .../tests/test_CellTypeCounterEnhanced.py | 34 ++-- .../pv/plugins/PVCellTypeCounterEnhanced.py | 27 ++-- 3 files changed, 104 insertions(+), 108 deletions(-) diff --git a/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py b/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py index c8f02e8b..e7fc39fd 100644 --- a/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py +++ b/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py @@ -1,17 +1,15 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. # SPDX-FileContributor: Antoine Mazuyer, Martin Lemay +import logging + from typing_extensions import Self -from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase -from vtkmodules.vtkCommonCore import ( - vtkInformation, - vtkInformationVector, - vtkIntArray, -) -from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkCell, vtkTable, vtkCellTypes, VTK_VERTEX ) +from vtkmodules.vtkCommonCore import vtkIntArray +from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid, vtkCell, vtkTable, vtkCellTypes, VTK_VERTEX from geos.mesh.model.CellTypeCounts import CellTypeCounts from geos.mesh.stats.meshQualityMetricHelpers import getAllCellTypes +from geos.utils.Logger import ( Logger, getLogger ) __doc__ = """ CellTypeCounterEnhanced module is a vtk filter that computes cell type counts. @@ -25,86 +23,99 @@ from geos.processing.pre_processing.CellTypeCounterEnhanced import CellTypeCounterEnhanced # Filter inputs - input: vtkUnstructuredGrid + inputMesh: vtkUnstructuredGrid + speHandler: bool # defaults to False # Instantiate the filter - cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced() + cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( inputMesh, speHandler ) - # Set input data object - cellTypeCounterEnhancedFilter.SetInputDataObject(input) + # Set the handler of yours (only if speHandler is True). + yourHandler: logging.Handler + cellTypeCounterEnhancedFilter.setLoggerHandler( yourHandler ) # Do calculations - cellTypeCounterEnhancedFilter.Update() + cellTypeCounterEnhancedFilter.applyFilter() - # Get counts + # Get result counts: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() + outputTable: vtkTable = cellTypeCounterEnhancedFilter.getOutput() """ +loggerTitle: str = "Cell Type Counter Enhanced" -class CellTypeCounterEnhanced( VTKPythonAlgorithmBase ): - def __init__( self ) -> None: - """CellTypeCounterEnhanced filter computes mesh stats.""" - super().__init__( nInputPorts=1, nOutputPorts=1, inputType="vtkUnstructuredGrid", outputType="vtkTable" ) - self._counts: CellTypeCounts = CellTypeCounts() +class CellTypeCounterEnhanced(): - def FillInputPortInformation( self: Self, port: int, info: vtkInformation ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestInformation. + def __init__( + self: Self, + inputMesh: vtkUnstructuredGrid, + speHandler: bool = False, + ) -> None: + """CellTypeCounterEnhanced filter computes mesh stats. Args: - port (int): Input port - info (vtkInformationVector): Info - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. + inputMesh (vtkUnstructuredGrid): The input mesh. + speHandler (bool, optional): True to use a specific handler, False to use the internal handler. + Defaults to False. """ - if port == 0: - info.Set( self.INPUT_REQUIRED_DATA_TYPE(), "vtkUnstructuredGrid" ) - return 1 + self.inputMesh: vtkUnstructuredGrid = inputMesh + self.outTable: vtkTable = vtkTable() + self._counts: CellTypeCounts = CellTypeCounts() - def RequestData( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], # noqa: F841 - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestData. + # Logger. + self.logger: Logger + if not speHandler: + self.logger = getLogger( loggerTitle, True ) + else: + self.logger = logging.getLogger( loggerTitle ) + self.logger.setLevel( logging.INFO ) + self.logger.propagate = False - Args: - request (vtkInformation): Request - inInfoVec (list[vtkInformationVector]): Input objects - outInfoVec (vtkInformationVector): Output objects + def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: + """Set a specific handler for the filter logger. - Returns: - int: 1 if calculation successfully ended, 0 otherwise. + In this filter 4 log levels are use, .info, .error, .warning and .critical, + be sure to have at least the same 4 levels. + + Args: + handler (logging.Handler): The handler to add. """ - inData: vtkUnstructuredGrid = self.GetInputData( inInfoVec, 0, 0 ) - outTable: vtkTable = vtkTable.GetData( outInfoVec, 0 ) - assert inData is not None, "Input mesh is undefined." - assert outTable is not None, "Output table is undefined." - - # compute cell type counts - self._counts.reset() - self._counts.setTypeCount( VTK_VERTEX, inData.GetNumberOfPoints() ) - for i in range( inData.GetNumberOfCells() ): - cell: vtkCell = inData.GetCell( i ) - self._counts.addType( cell.GetCellType() ) - - # create output table - # first reset output table - outTable.RemoveAllRows() - outTable.RemoveAllColumns() - outTable.SetNumberOfRows( 1 ) - - # create columns per types - for cellType in getAllCellTypes(): - array: vtkIntArray = vtkIntArray() - array.SetName( vtkCellTypes.GetClassNameFromTypeId( cellType ) ) - array.SetNumberOfComponents( 1 ) - array.SetNumberOfValues( 1 ) - array.SetValue( 0, self._counts.getTypeCount( cellType ) ) - outTable.AddColumn( array ) - return 1 + if len( self.logger.handlers ) == 0: + self.logger.addHandler( handler ) + else: + self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler'" + " to True during the filter initialization." ) + + def applyFilter( self: Self ) -> None: + """Apply CellTypeCounterEnhanced filter.""" + self.logger.info( f"Apply filter { self.logger.name }." ) + try: + # compute cell type counts + self._counts.reset() + self._counts.setTypeCount( VTK_VERTEX, self.inputMesh.GetNumberOfPoints() ) + for i in range( self.inputMesh.GetNumberOfCells() ): + cell: vtkCell = self.inputMesh.GetCell( i ) + self._counts.addType( cell.GetCellType() ) + + # create output table + # first reset output table + self.outTable.RemoveAllRows() + self.outTable.RemoveAllColumns() + self.outTable.SetNumberOfRows( 1 ) + + # create columns per types + for cellType in getAllCellTypes(): + array: vtkIntArray = vtkIntArray() + array.SetName( vtkCellTypes.GetClassNameFromTypeId( cellType ) ) + array.SetNumberOfComponents( 1 ) + array.SetNumberOfValues( 1 ) + array.SetValue( 0, self._counts.getTypeCount( cellType ) ) + self.outTable.AddColumn( array ) + self.logger.info( f"The filter { self.logger.name } succeeded." ) + except AssertionError as e: + self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) + + return def GetCellTypeCountsObject( self: Self ) -> CellTypeCounts: """Get CellTypeCounts object. @@ -113,3 +124,7 @@ def GetCellTypeCountsObject( self: Self ) -> CellTypeCounts: CellTypeCounts: CellTypeCounts object. """ return self._counts + + def getOutput( self: Self ) -> vtkTable: + """Get the computed vtkTable.""" + return self.outTable \ No newline at end of file diff --git a/geos-processing/tests/test_CellTypeCounterEnhanced.py b/geos-processing/tests/test_CellTypeCounterEnhanced.py index 03097775..6350bbf1 100644 --- a/geos-processing/tests/test_CellTypeCounterEnhanced.py +++ b/geos-processing/tests/test_CellTypeCounterEnhanced.py @@ -6,27 +6,15 @@ import numpy as np import numpy.typing as npt import pytest -from typing import ( - Iterator, ) +from typing import Iterator -from geos.mesh.utils.genericHelpers import createSingleCellMesh, createMultiCellMesh +from geos.mesh.utils.genericHelpers import ( createSingleCellMesh, createMultiCellMesh ) from geos.processing.pre_processing.CellTypeCounterEnhanced import CellTypeCounterEnhanced from geos.mesh.model.CellTypeCounts import CellTypeCounts -from vtkmodules.vtkCommonDataModel import ( - vtkUnstructuredGrid, - vtkCellTypes, - vtkCell, - VTK_TRIANGLE, - VTK_QUAD, - VTK_TETRA, - VTK_VERTEX, - VTK_POLYHEDRON, - VTK_POLYGON, - VTK_PYRAMID, - VTK_HEXAHEDRON, - VTK_WEDGE, -) +from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkCellTypes, vtkCell, VTK_TRIANGLE, VTK_QUAD, + VTK_TETRA, VTK_VERTEX, VTK_POLYHEDRON, VTK_POLYGON, VTK_PYRAMID, + VTK_HEXAHEDRON, VTK_WEDGE ) data_root: str = os.path.join( os.path.dirname( os.path.abspath( __file__ ) ), "data" ) @@ -71,9 +59,8 @@ def test_CellTypeCounterEnhanced_single( test_case: TestCase ) -> None: Args: test_case (TestCase): Test case """ - cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced() - cellTypeCounterEnhancedFilter.SetInputDataObject( test_case.mesh ) - cellTypeCounterEnhancedFilter.Update() + cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( test_case.mesh ) + cellTypeCounterEnhancedFilter.applyFilter() countsObs: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() assert countsObs is not None, "CellTypeCounts is undefined" @@ -130,9 +117,8 @@ def test_CellTypeCounterEnhanced_multi( test_case: TestCase ) -> None: Args: test_case (TestCase): Test case """ - cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced() - cellTypeCounterEnhancedFilter.SetInputDataObject( test_case.mesh ) - cellTypeCounterEnhancedFilter.Update() + cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( test_case.mesh ) + cellTypeCounterEnhancedFilter.applyFilter() countsObs: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() assert countsObs is not None, "CellTypeCounts is undefined" @@ -156,4 +142,4 @@ def test_CellTypeCounterEnhanced_multi( test_case: TestCase ) -> None: nbPolyhedra: int = np.sum( counts[ 2: ], dtype=int ) assert int( countsObs.getTypeCount( VTK_POLYGON ) ) == nbPolygon, f"The number of faces should be {nbPolygon}." assert int( - countsObs.getTypeCount( VTK_POLYHEDRON ) ) == nbPolyhedra, f"The number of polyhedra should be {nbPolyhedra}." + countsObs.getTypeCount( VTK_POLYHEDRON ) ) == nbPolyhedra, f"The number of polyhedra should be {nbPolyhedra}." \ No newline at end of file diff --git a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py index c5d3f046..4a0b8e99 100644 --- a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py @@ -9,15 +9,10 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy ) +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] -from vtkmodules.vtkCommonCore import ( - vtkInformation, - vtkInformationVector, -) -from vtkmodules.vtkCommonDataModel import ( - vtkPointSet, - vtkTable, -) +from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector +from vtkmodules.vtkCommonDataModel import vtkPointSet, vtkTable # update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent @@ -137,10 +132,11 @@ def RequestData( assert inputMesh is not None, "Input server mesh is null." assert outputTable is not None, "Output pipeline is null." - cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced() - cellTypeCounterEnhancedFilter.SetInputDataObject( inputMesh ) - cellTypeCounterEnhancedFilter.Update() - outputTable.ShallowCopy( cellTypeCounterEnhancedFilter.GetOutputDataObject( 0 ) ) + cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( inputMesh, True ) + if len( cellTypeCounterEnhancedFilter.logger.handlers ) == 0: + cellTypeCounterEnhancedFilter.setLoggerHandler( VTKHandler() ) + cellTypeCounterEnhancedFilter.applyFilter() + outputTable.ShallowCopy( cellTypeCounterEnhancedFilter.getOutput() ) # print counts in Output Messages view counts: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() @@ -151,8 +147,7 @@ def RequestData( try: with open( self._filename, 'w' ) as fout: fout.write( self._countsAll.print() ) - print( f"File {self._filename} was successfully written." ) + cellTypeCounterEnhancedFilter.logger.info( f"File {self._filename} was successfully written." ) except Exception as e: - print( "Error while exporting the file due to:" ) - print( str( e ) ) - return 1 + cellTypeCounterEnhancedFilter.logger.info( f"Error while exporting the file due to:\n{ e }" ) + return 1 \ No newline at end of file From 380a69316a8bd495b11650b6ed24ccb49c3a5f50 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 20 Nov 2025 09:31:13 +0100 Subject: [PATCH 15/39] Refactor MeshQualityEnhanced --- .../pre_processing/MeshQualityEnhanced.py | 190 ++++++++---------- .../tests/test_MeshQualityEnhanced.py | 20 +- .../geos/pv/plugins/PVMeshQualityEnhanced.py | 70 +++---- 3 files changed, 125 insertions(+), 155 deletions(-) diff --git a/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py b/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py index fc8c662b..3f6ac283 100644 --- a/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py +++ b/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py @@ -1,24 +1,15 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. # SPDX-FileContributor: Antoine Mazuyer, Martin Lemay, Paloma Martinez +import logging import numpy as np import numpy.typing as npt from typing import Optional, cast from typing_extensions import Self from vtkmodules.vtkFiltersCore import vtkExtractEdges from vtkmodules.vtkFiltersVerdict import vtkMeshQuality -from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase -from vtkmodules.vtkCommonCore import ( - vtkInformation, - vtkInformationVector, - vtkIdList, - vtkPoints, - vtkDataArray, - vtkIntArray, - vtkDoubleArray, - vtkIdTypeArray, - vtkMath, -) +from vtkmodules.vtkCommonCore import ( vtkIdList, vtkPoints, vtkDataArray, vtkIntArray, vtkDoubleArray, vtkIdTypeArray, + vtkMath ) from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkPolyData, vtkCellData, vtkPointData, vtkFieldData, vtkCell, vtkCell3D, vtkTetra, vtkCellTypes, vtkPolygon, VTK_TRIANGLE, VTK_QUAD, VTK_TETRA, VTK_PYRAMID, VTK_HEXAHEDRON, VTK_WEDGE, VTK_POLYGON, @@ -27,24 +18,17 @@ 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.model.QualityMetricSummary import ( QualityMetricSummary, StatTypes ) from geos.mesh.utils.arrayHelpers import getAttributesFromDataSet -from geos.mesh.stats.meshQualityMetricHelpers import ( - getQualityMeasureNameFromIndex, - getQualityMetricFromIndex, - VtkCellQualityMetricEnum, - CellQualityMetricAdditionalEnum, - QualityMetricOtherEnum, - MeshQualityMetricEnum, - getAllCellTypesExtended, - getAllCellTypes, - getPolygonCellTypes, - getPolyhedronCellTypes, - getCellQualityMeasureFromCellType, - getChildrenCellTypes, -) +from geos.mesh.stats.meshQualityMetricHelpers import ( getQualityMeasureNameFromIndex, getQualityMetricFromIndex, + VtkCellQualityMetricEnum, CellQualityMetricAdditionalEnum, + QualityMetricOtherEnum, MeshQualityMetricEnum, + getAllCellTypesExtended, getAllCellTypes, getPolygonCellTypes, + getPolyhedronCellTypes, getCellQualityMeasureFromCellType, + getChildrenCellTypes ) import geos.utils.geometryFunctions as geom +from geos.utils.Logger import ( Logger, getLogger ) __doc__ = """ MeshQualityEnhanced module is a vtk filter that computes mesh quality stats. @@ -60,13 +44,15 @@ from geos.processing.pre_processing.MeshQualityEnhanced import MeshQualityEnhanced # Filter inputs - input: vtkUnstructuredGrid + inputMesh: vtkUnstructuredGrid + speHandler: bool # optional # Instantiate the filter - meshQualityEnhancedFilter: MeshQualityEnhanced = MeshQualityEnhanced() + meshQualityEnhancedFilter: MeshQualityEnhanced = MeshQualityEnhanced( inputMesh, speHandler ) - # Set input data object - meshQualityEnhancedFilter.SetInputDataObject(input) + # Use your own handler (if speHandler is True) + yourHandler: logging.Handler + meshQualityEnhancedFilter.setLoggerHandler( yourHandler ) # Set metrics to use meshQualityEnhancedFilter.SetTriangleMetrics(triangleQualityMetrics) @@ -78,10 +64,10 @@ meshQualityEnhancedFilter.SetOtherMeshQualityMetrics(otherQualityMetrics) # Do calculations - meshQualityEnhancedFilter.Update() + meshQualityEnhancedFilter.applyFilter() # Get output mesh quality report - outputMesh: vtkUnstructuredGrid = meshQualityEnhancedFilter.GetOutputDataObject(0) + outputMesh: vtkUnstructuredGrid = meshQualityEnhancedFilter.getOutput() outputStats: QualityMetricSummary = meshQualityEnhancedFilter.GetQualityMetricSummary() """ @@ -101,12 +87,25 @@ def getQualityMetricArrayName( metric: int ) -> str: return QUALITY_ARRAY_NAME + "_" + "".join( getQualityMeasureNameFromIndex( metric ).split( " " ) ) -class MeshQualityEnhanced( VTKPythonAlgorithmBase ): +loggerTitle: str = "Mesh Quality Enhanced" - def __init__( self: Self ) -> None: - """Enhanced vtkMeshQuality filter.""" - super().__init__( nInputPorts=1, nOutputPorts=1, outputType="vtkUnstructuredGrid" ) - self._outputMesh: vtkUnstructuredGrid + +class MeshQualityEnhanced(): + + def __init__( + self: Self, + inputMesh: vtkUnstructuredGrid, + speHandler: bool = False, + ) -> None: + """Enhanced vtkMeshQuality filter. + + Args: + inputMesh (vtkUnstructuredGrid): Input mesh + speHandler (bool, optional): True to use a specific handler, False to use the internal handler. + Defaults to False. + """ + self.inputMesh: vtkUnstructuredGrid = inputMesh + self._outputMesh: vtkUnstructuredGrid = vtkUnstructuredGrid() self._cellCounts: CellTypeCounts self._qualityMetricSummary: QualityMetricSummary = QualityMetricSummary() @@ -126,43 +125,32 @@ def __init__( self: Self ) -> None: self._allCellTypesExtended: tuple[ int, ...] = getAllCellTypesExtended() self._allCellTypes: tuple[ int, ...] = getAllCellTypes() - def FillInputPortInformation( self: Self, port: int, info: vtkInformation ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestInformation. + # Logger. + self.speHandler: bool = speHandler + self.handler: None | logging.Handler = None + self.logger: Logger + if not speHandler: + self.logger = getLogger( loggerTitle, True ) + else: + self.logger = logging.getLogger( loggerTitle ) + self.logger.setLevel( logging.INFO ) + self.logger.propagate = False - Args: - port (int): Input port - info (vtkInformationVector): Info + def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: + """Set a specific handler for the filter logger. - Returns: - int: 1 if calculation successfully ended, 0 otherwise. - """ - if port == 0: - info.Set( self.INPUT_REQUIRED_DATA_TYPE(), "vtkUnstructuredGrid" ) - return 1 - - def RequestDataObject( - self: Self, - request: vtkInformation, - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestDataObject. + In this filter 4 log levels are use, .info, .error, .warning and .critical, + be sure to have at least the same 4 levels. Args: - request (vtkInformation): Request - inInfoVec (list[vtkInformationVector]): Input objects - outInfoVec (vtkInformationVector): Output objects - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. + handler (logging.Handler): The handler to add. """ - inData = self.GetInputData( inInfoVec, 0, 0 ) - outData = self.GetOutputData( outInfoVec, 0 ) - assert inData is not None - if outData is None or ( not outData.IsA( inData.GetClassName() ) ): - outData = inData.NewInstance() - outInfoVec.GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), outData ) - return super().RequestDataObject( request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] + self.handler = handler + if len( self.logger.handlers ) == 0: + self.logger.addHandler( handler ) + else: + self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler'" + " to True during the filter initialization." ) def GetQualityMetricSummary( self: Self ) -> QualityMetricSummary: """Get QualityMetricSummary object. @@ -294,48 +282,42 @@ def getComputedMetricsFromCellType( self: Self, cellType: int ) -> Optional[ set metrics = metrics.intersection( computedMetrics ) return metrics if commonComputedMetricsExists else None - def RequestData( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], # noqa: F841 - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestData. + def applyFilter( self: Self ) -> None: + """Apply MeshQualityEnhanced filter.""" + self.logger.info( f"Apply filter { self.logger.name }." ) + try: + self._outputMesh.ShallowCopy( self.inputMesh ) + # Compute cell type counts + self._computeCellTypeCounts() - Args: - request (vtkInformation): Request - inInfoVec (list[vtkInformationVector]): Input objects - outInfoVec (vtkInformationVector): Output objects + # Compute metrics and associated attributes + self._evaluateMeshQualityAll() - Returns: - int: 1 if calculation successfully ended, 0 otherwise. - """ - inData: vtkUnstructuredGrid = self.GetInputData( inInfoVec, 0, 0 ) - self._outputMesh = self.GetOutputData( outInfoVec, 0 ) - assert inData is not None, "Input mesh is undefined." - assert self._outputMesh is not None, "Output pipeline is undefined." - self._outputMesh.ShallowCopy( inData ) + # Compute stats summary + self._updateStatsSummary() - # Compute cell type counts - self._computeCellTypeCounts() + # Create field data + self._createFieldDataStatsSummary() - # Compute metrics and associated attributes - self._evaluateMeshQualityAll() + self._outputMesh.Modified() - # Compute stats summary - self._updateStatsSummary() + self.logger.info( f"The filter { self.logger.name } succeeded." ) + except Exception as e: + self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) - # Create field data - self._createFieldDataStatsSummary() + return - self._outputMesh.Modified() - return 1 + def getOutput( self: Self ) -> vtkUnstructuredGrid: + """Get the mesh computed with the stats.""" + return self._outputMesh def _computeCellTypeCounts( self: Self ) -> None: """Compute cell type counts.""" - cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced() - cellTypeCounterEnhancedFilter.SetInputDataObject( self._outputMesh ) - cellTypeCounterEnhancedFilter.Update() + cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( + self._outputMesh, self.speHandler ) + if self.speHandler and len( cellTypeCounterEnhancedFilter.logger.handlers ) == 0: + cellTypeCounterEnhancedFilter.setLoggerHandler( self.handler ) + cellTypeCounterEnhancedFilter.applyFilter() counts: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() assert counts is not None, "CellTypeCounts is undefined" self._qualityMetricSummary.setCellTypeCounts( counts ) @@ -426,7 +408,7 @@ def _applyMeshQualityFilter( self: Self, metric: int, cellTypes: list[ int ] ) - elif cellType == VTK_HEXAHEDRON: meshQualityFilter.SetHexQualityMeasure( metric ) else: - print( "Cell type is not supported." ) + raise TypeError( "Cell type is not supported." ) meshQualityFilter.Update() return meshQualityFilter.GetOutputDataObject( 0 ) @@ -826,4 +808,4 @@ def _getNormalVector( self: Self, points: vtkPoints, face: vtkCell ) -> npt.NDAr ptsCoords: npt.NDArray[ np.float64 ] = np.zeros( ( 3, 3 ), dtype=float ) for i in range( 3 ): points.GetPoint( facePtsIds.GetId( i ), ptsCoords[ i ] ) - return geom.computeNormalFromPoints( ptsCoords[ 0 ], ptsCoords[ 1 ], ptsCoords[ 2 ] ) + return geom.computeNormalFromPoints( ptsCoords[ 0 ], ptsCoords[ 1 ], ptsCoords[ 2 ] ) \ No newline at end of file diff --git a/geos-processing/tests/test_MeshQualityEnhanced.py b/geos-processing/tests/test_MeshQualityEnhanced.py index a86e2c97..8857175b 100644 --- a/geos-processing/tests/test_MeshQualityEnhanced.py +++ b/geos-processing/tests/test_MeshQualityEnhanced.py @@ -8,13 +8,10 @@ import numpy.typing as npt import pandas as pd import pytest -from typing import ( - Iterator, - Optional, -) +from typing import Iterator, Optional + from geos.mesh.utils.genericHelpers import createMultiCellMesh -from geos.mesh.stats.meshQualityMetricHelpers import ( - getAllCellTypesExtended, ) +from geos.mesh.stats.meshQualityMetricHelpers import getAllCellTypesExtended from geos.processing.pre_processing.MeshQualityEnhanced import MeshQualityEnhanced from geos.mesh.model.QualityMetricSummary import QualityMetricSummary @@ -126,14 +123,13 @@ def __generate_test_data() -> Iterator[ TestCase ]: @pytest.mark.parametrize( "test_case", __generate_test_data(), ids=ids ) def test_MeshQualityEnhanced( test_case: TestCase ) -> None: - """Test of CellTypeCounterEnhanced filter. + """Test of MeshQualityEnhanced filter. Args: test_case (TestCase): Test case """ mesh = test_case.mesh - meshQualityEnhancedFilter: MeshQualityEnhanced = MeshQualityEnhanced() - meshQualityEnhancedFilter.SetInputDataObject( mesh ) + meshQualityEnhancedFilter: MeshQualityEnhanced = MeshQualityEnhanced( mesh ) if test_case.cellType == VTK_TRIANGLE: meshQualityEnhancedFilter.SetTriangleMetrics( test_case.qualityMetrics ) elif test_case.cellType == VTK_QUAD: @@ -146,7 +142,7 @@ def test_MeshQualityEnhanced( test_case: TestCase ) -> None: meshQualityEnhancedFilter.SetWedgeMetrics( test_case.qualityMetrics ) elif test_case.cellType == VTK_HEXAHEDRON: meshQualityEnhancedFilter.SetHexaMetrics( test_case.qualityMetrics ) - meshQualityEnhancedFilter.Update() + meshQualityEnhancedFilter.applyFilter() # test method getComputedMetricsFromCellType for i, cellType in enumerate( getAllCellTypesExtended() ): @@ -156,7 +152,7 @@ def test_MeshQualityEnhanced( test_case: TestCase ) -> None: assert metrics is not None, f"Metrics from {vtkCellTypes.GetClassNameFromTypeId(cellType)} cells is undefined." # test attributes - outputMesh: vtkUnstructuredGrid = meshQualityEnhancedFilter.GetOutputDataObject( 0 ) + outputMesh: vtkUnstructuredGrid = meshQualityEnhancedFilter.getOutput() cellData: vtkCellData = outputMesh.GetCellData() assert cellData is not None, "Cell data is undefined." @@ -192,4 +188,4 @@ def test_MeshQualityEnhanced( test_case: TestCase ) -> None: test_case.metricsSummary[ j ] ), f"Stats at metric index {j} are wrong." fig: Figure = stats.plotSummaryFigure() - assert len( fig.get_axes() ) == 6, "Number of Axes is expected to be 6." + assert len( fig.get_axes() ) == 6, "Number of Axes is expected to be 6." \ No newline at end of file diff --git a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py index cc3a978d..e6f7160d 100644 --- a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py @@ -2,17 +2,17 @@ # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. # SPDX-FileContributor: Martin Lemay, Paloma Martinez # ruff: noqa: E402 # disable Module level import not at top of file +import logging import sys from pathlib import Path from typing_extensions import Self, Optional -from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase, smdomain, smproperty, -) -from vtkmodules.vtkCommonCore import ( - vtkDataArraySelection, ) -from vtkmodules.vtkCommonDataModel import ( - vtkUnstructuredGrid, ) +from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase, smdomain, smproperty # type: ignore[import-not-found] +from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py + +from vtkmodules.vtkCommonCore import vtkDataArraySelection +from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid # update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent @@ -23,24 +23,14 @@ from geos.mesh.model.QualityMetricSummary import QualityMetricSummary from geos.processing.pre_processing.MeshQualityEnhanced import MeshQualityEnhanced - -from geos.mesh.stats.meshQualityMetricHelpers import ( - getQualityMetricsOther, - getQualityMeasureNameFromIndex, - getQualityMeasureIndexFromName, - getQuadQualityMeasure, - getTriangleQualityMeasure, - getCommonPolygonQualityMeasure, - getTetQualityMeasure, - getPyramidQualityMeasure, - getWedgeQualityMeasure, - getHexQualityMeasure, - getCommonPolyhedraQualityMeasure, -) -from geos.pv.utils.checkboxFunction import ( # type: ignore[attr-defined] - createModifiedCallback, ) +from geos.mesh.stats.meshQualityMetricHelpers import ( getQualityMetricsOther, getQualityMeasureNameFromIndex, + getQualityMeasureIndexFromName, getQuadQualityMeasure, + getTriangleQualityMeasure, getCommonPolygonQualityMeasure, + getTetQualityMeasure, getPyramidQualityMeasure, + getWedgeQualityMeasure, getHexQualityMeasure, + getCommonPolyhedraQualityMeasure ) +from geos.pv.utils.checkboxFunction import createModifiedCallback # type: ignore[attr-defined] from geos.pv.utils.paraviewTreatments import getArrayChoices - from geos.pv.utils.details import ( SISOFilter, FilterCategory ) __doc__ = """ @@ -228,7 +218,6 @@ def ApplyFilter( self, inputMesh: vtkUnstructuredGrid, outputMesh: vtkUnstructur self._getQualityMetricsToUse( self._triangleQualityMetric ) ) quadMetrics: set[ int ] = self._getQualityMetricsToUse( self._commonCellSurfaceQualityMetric ).union( self._getQualityMetricsToUse( self._quadsQualityMetric ) ) - tetraMetrics: set[ int ] = self._getQualityMetricsToUse( self._commonCellVolumeQualityMetric ).union( self._getQualityMetricsToUse( self._tetQualityMetric ) ) pyrMetrics: set[ int ] = self._getQualityMetricsToUse( self._commonCellVolumeQualityMetric ).union( @@ -239,9 +228,9 @@ def ApplyFilter( self, inputMesh: vtkUnstructuredGrid, outputMesh: vtkUnstructur self._getQualityMetricsToUse( self._HexQualityMetric ) ) otherMetrics: set[ int ] = self._getQualityMetricsToUse( self._commonMeshQualityMetric ) - meshQualityEnhancedFilter: MeshQualityEnhanced = MeshQualityEnhanced() - - meshQualityEnhancedFilter.SetInputDataObject( inputMesh ) + meshQualityEnhancedFilter: MeshQualityEnhanced = MeshQualityEnhanced( inputMesh, True ) + if len( meshQualityEnhancedFilter.logger.handlers ) == 0: + meshQualityEnhancedFilter.setLoggerHandler( VTKHandler() ) meshQualityEnhancedFilter.SetCellQualityMetrics( triangleMetrics=triangleMetrics, quadMetrics=quadMetrics, tetraMetrics=tetraMetrics, @@ -249,32 +238,35 @@ def ApplyFilter( self, inputMesh: vtkUnstructuredGrid, outputMesh: vtkUnstructur wedgeMetrics=wedgeMetrics, hexaMetrics=hexaMetrics ) meshQualityEnhancedFilter.SetOtherMeshQualityMetrics( otherMetrics ) - meshQualityEnhancedFilter.Update() + meshQualityEnhancedFilter.applyFilter() - outputMesh.ShallowCopy( meshQualityEnhancedFilter.GetOutputDataObject( 0 ) ) + outputMesh.ShallowCopy( meshQualityEnhancedFilter.getOutput() ) # save to file if asked if self._saveToFile: stats: QualityMetricSummary = meshQualityEnhancedFilter.GetQualityMetricSummary() - self.saveFile( stats ) + logger: logging.Logger = meshQualityEnhancedFilter.logger + self.saveFile( stats, logger ) self._blockIndex += 1 return - def saveFile( self: Self, stats: QualityMetricSummary ) -> None: + def saveFile( + self: Self, + stats: QualityMetricSummary, + logger: logging.Logger, + ) -> None: """Export mesh quality metric summary file.""" try: - if self._filename is None: - print( "Mesh quality summary report file path is undefined." ) - return + assert self._filename is not None, "Mesh quality summary report file path is undefined." + # add index for multiblock meshes index: int = self._filename.rfind( '.' ) filename: str = self._filename[ :index ] + f"_{self._blockIndex}" + self._filename[ index: ] fig = stats.plotSummaryFigure() fig.savefig( filename, dpi=150 ) - print( f"File {filename} was successfully written." ) + logger.info( f"File {filename} was successfully written." ) except Exception as e: - print( "Error while exporting the file due to:" ) - print( str( e ) ) + logger.error( f"Error while exporting the file due to:\n{ e }" ) def __initVolumeQualityMetricSelection( self: Self ) -> None: """Initialize the volumic metrics selection.""" @@ -339,4 +331,4 @@ def __initSurfaceQualityMetricSelection( self: Self ) -> None: otherMetrics: set[ int ] = getQualityMetricsOther() for measure in otherMetrics: # TODO: fix issue with incident vertex count metrics - self._commonMeshQualityMetric.AddArray( getQualityMeasureNameFromIndex( measure ), False ) + self._commonMeshQualityMetric.AddArray( getQualityMeasureNameFromIndex( measure ), False ) \ No newline at end of file From 5b7a2af95e4acf4cae82978d8f3385ee7545bd35 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 20 Nov 2025 11:10:45 +0100 Subject: [PATCH 16/39] clean test --- geos-processing/tests/test_SplitMesh.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/geos-processing/tests/test_SplitMesh.py b/geos-processing/tests/test_SplitMesh.py index 69a0ae5d..7fc8e607 100644 --- a/geos-processing/tests/test_SplitMesh.py +++ b/geos-processing/tests/test_SplitMesh.py @@ -151,12 +151,9 @@ def test_single_cell_split( test_case: TestCase ) -> None: assert pointsOut.GetNumberOfPoints( ) == test_case.pointsExp.shape[ 0 ], f"Number of points is expected to be {test_case.pointsExp.shape[0]}." pointCoords: npt.NDArray[ np.float64 ] = vtk_to_numpy( pointsOut.GetData() ) - print( "Points coords: ", cellTypeName, pointCoords.tolist() ) assert np.array_equal( pointCoords.ravel(), test_case.pointsExp.ravel() ), "Points coordinates mesh are wrong." cellsOut: vtkCellArray = output.GetCells() - typesArray0: npt.NDArray[ np.int64 ] = vtk_to_numpy( output.GetDistinctCellTypesArray() ) - print( "typesArray0", cellTypeName, typesArray0 ) assert cellsOut is not None, "Cells from output mesh are undefined." assert cellsOut.GetNumberOfCells() == len( @@ -167,7 +164,6 @@ def test_single_cell_split( test_case: TestCase ) -> None: assert types is not None, "Cell types must be defined" typesArray: npt.NDArray[ np.int64 ] = vtk_to_numpy( types.GetCellTypesArray() ) - print( "typesArray", cellTypeName, typesArray ) # Pyramid splitting produces both pyramids (first 6 cells) and tetrahedra (last 4 cells) if test_case.cellType == VTK_PYRAMID: assert typesArray.size == 2, "Pyramid splitting should produce 2 distinct cell types" @@ -183,8 +179,6 @@ def test_single_cell_split( test_case: TestCase ) -> None: cellsOutObs: list[ int ] = [ ptIds.GetId( j ) for j in range( ptIds.GetNumberOfIds() ) ] nbPtsExp: int = len( test_case.cellsExp[ i ] ) actualCellType: int = output.GetCellType( i ) - print( "cell type", cellTypeName, i, vtkCellTypes.GetClassNameFromTypeId( actualCellType ) ) - print( "cellsOutObs: ", cellTypeName, i, cellsOutObs ) assert ptIds is not None, "Point ids must be defined" assert ptIds.GetNumberOfIds() == nbPtsExp, f"Cells must be defined by {nbPtsExp} points." assert cellsOutObs == test_case.cellsExp[ i ], "Cell point ids are wrong." @@ -234,7 +228,6 @@ def test_multi_cells_mesh_split() -> None: elif cellType == VTK_PYRAMID: nbPyr += 1 - print( f"Input mesh contains: {nbHex} hexahedra, {nbTet} tetrahedra, {nbPyr} pyramids" ) assert nbHex == 3, "Expected 3 hexahedra in input mesh" assert nbTet == 36, "Expected 36 tetrahedra in input mesh" assert nbPyr == 18, "Expected 18 pyramids in input mesh" @@ -248,8 +241,6 @@ def test_multi_cells_mesh_split() -> None: # Calculate expected number of cells using the formula # 1 hex -> 8 hexes, 1 tet -> 8 tets, 1 pyramid -> 6 pyramids + 4 tets = 10 cells expectedNbCells = nbHex * 8 + nbTet * 8 + nbPyr * 10 - print( f"Expected number of cells: {expectedNbCells} (3*8 + 36*8 + 18*10)" ) - print( f"Actual number of cells: {output.GetNumberOfCells()}" ) assert output.GetNumberOfCells() == expectedNbCells, \ f"Expected {expectedNbCells} cells, got {output.GetNumberOfCells()}" @@ -266,7 +257,6 @@ def test_multi_cells_mesh_split() -> None: elif cellType == VTK_PYRAMID: nbPyrOut += 1 - print( f"Output mesh contains: {nbHexOut} hexahedra, {nbTetOut} tetrahedra, {nbPyrOut} pyramids" ) # Expected output: 3*8=24 hexes, 36*8 + 18*4=360 tets, 18*6=108 pyramids assert nbHexOut == 3 * 8, f"Expected {3*8} hexahedra in output, got {nbHexOut}" assert nbTetOut == 36 * 8 + 18 * 4, f"Expected {36*8 + 18*4} tetrahedra in output, got {nbTetOut}" @@ -301,7 +291,6 @@ def test_multi_polygon_mesh_split() -> None: elif cellType == VTK_TRIANGLE: nbTriangle += 1 - print( f"Input mesh contains: {nbQuad} quads, {nbTriangle} triangles" ) assert nbQuad == 2, "Expected 2 quads in input mesh" assert nbTriangle == 4, "Expected 4 triangles in input mesh" @@ -314,8 +303,6 @@ def test_multi_polygon_mesh_split() -> None: # Calculate expected number of cells using the formula # 1 quad -> 4 quads, 1 triangle -> 4 triangles expectedNbCells = nbQuad * 4 + nbTriangle * 4 - print( f"Expected number of cells: {expectedNbCells} (2*4 + 4*4)" ) - print( f"Actual number of cells: {output.GetNumberOfCells()}" ) assert output.GetNumberOfCells() == expectedNbCells, \ f"Expected {expectedNbCells} cells, got {output.GetNumberOfCells()}" @@ -329,7 +316,6 @@ def test_multi_polygon_mesh_split() -> None: elif cellType == VTK_TRIANGLE: nbTriangleOut += 1 - print( f"Output mesh contains: {nbQuadOut} quads, {nbTriangleOut} triangles" ) # Expected output: 2*4=8 quads, 4*4=16 triangles assert nbQuadOut == 2 * 4, f"Expected {2*4} quads in output, got {nbQuadOut}" assert nbTriangleOut == 4 * 4, f"Expected {4*4} triangles in output, got {nbTriangleOut}" From e14a0655d03bfeaaf0b044ad6590e7492eb264c2 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 20 Nov 2025 11:25:43 +0100 Subject: [PATCH 17/39] fix ruff --- .../geos/processing/generic_processing_tools/SplitMesh.py | 5 +++-- .../processing/pre_processing/CellTypeCounterEnhanced.py | 2 +- .../geos/processing/pre_processing/MeshQualityEnhanced.py | 2 +- geos-processing/tests/test_CellTypeCounterEnhanced.py | 2 +- geos-processing/tests/test_MeshQualityEnhanced.py | 2 +- geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py | 2 +- geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py | 2 +- geos-pv/src/geos/pv/plugins/PVSplitMesh.py | 2 +- 8 files changed, 10 insertions(+), 9 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py index 1fb85022..99f7dbec 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py @@ -140,7 +140,8 @@ def applyFilter( self: Self ) -> None: if splitMethod is not None: splitMethod( cell, c ) else: - raise TypeError( f"Cell type { vtkCellTypes.GetClassNameFromTypeId( cellType ) } is not supported." ) + raise TypeError( + f"Cell type { vtkCellTypes.GetClassNameFromTypeId( cellType ) } is not supported." ) # add points and cells self.outputMesh.SetPoints( self.points ) self.outputMesh.SetCells( self.cellTypes, self.cells ) @@ -454,4 +455,4 @@ def _transferCellArrays( self: Self, splittedMesh: vtkUnstructuredGrid ) -> bool cellDataSplitted.AddArray( newArray ) cellDataSplitted.Modified() splittedMesh.Modified() - return True \ No newline at end of file + return True diff --git a/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py b/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py index e7fc39fd..568146dd 100644 --- a/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py +++ b/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py @@ -127,4 +127,4 @@ def GetCellTypeCountsObject( self: Self ) -> CellTypeCounts: def getOutput( self: Self ) -> vtkTable: """Get the computed vtkTable.""" - return self.outTable \ No newline at end of file + return self.outTable diff --git a/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py b/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py index 3f6ac283..a5e94b6d 100644 --- a/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py +++ b/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py @@ -808,4 +808,4 @@ def _getNormalVector( self: Self, points: vtkPoints, face: vtkCell ) -> npt.NDAr ptsCoords: npt.NDArray[ np.float64 ] = np.zeros( ( 3, 3 ), dtype=float ) for i in range( 3 ): points.GetPoint( facePtsIds.GetId( i ), ptsCoords[ i ] ) - return geom.computeNormalFromPoints( ptsCoords[ 0 ], ptsCoords[ 1 ], ptsCoords[ 2 ] ) \ No newline at end of file + return geom.computeNormalFromPoints( ptsCoords[ 0 ], ptsCoords[ 1 ], ptsCoords[ 2 ] ) diff --git a/geos-processing/tests/test_CellTypeCounterEnhanced.py b/geos-processing/tests/test_CellTypeCounterEnhanced.py index 6350bbf1..7883592c 100644 --- a/geos-processing/tests/test_CellTypeCounterEnhanced.py +++ b/geos-processing/tests/test_CellTypeCounterEnhanced.py @@ -142,4 +142,4 @@ def test_CellTypeCounterEnhanced_multi( test_case: TestCase ) -> None: nbPolyhedra: int = np.sum( counts[ 2: ], dtype=int ) assert int( countsObs.getTypeCount( VTK_POLYGON ) ) == nbPolygon, f"The number of faces should be {nbPolygon}." assert int( - countsObs.getTypeCount( VTK_POLYHEDRON ) ) == nbPolyhedra, f"The number of polyhedra should be {nbPolyhedra}." \ No newline at end of file + countsObs.getTypeCount( VTK_POLYHEDRON ) ) == nbPolyhedra, f"The number of polyhedra should be {nbPolyhedra}." diff --git a/geos-processing/tests/test_MeshQualityEnhanced.py b/geos-processing/tests/test_MeshQualityEnhanced.py index 8857175b..730ec173 100644 --- a/geos-processing/tests/test_MeshQualityEnhanced.py +++ b/geos-processing/tests/test_MeshQualityEnhanced.py @@ -188,4 +188,4 @@ def test_MeshQualityEnhanced( test_case: TestCase ) -> None: test_case.metricsSummary[ j ] ), f"Stats at metric index {j} are wrong." fig: Figure = stats.plotSummaryFigure() - assert len( fig.get_axes() ) == 6, "Number of Axes is expected to be 6." \ No newline at end of file + assert len( fig.get_axes() ) == 6, "Number of Axes is expected to be 6." diff --git a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py index 4a0b8e99..d87bc656 100644 --- a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py @@ -150,4 +150,4 @@ def RequestData( cellTypeCounterEnhancedFilter.logger.info( f"File {self._filename} was successfully written." ) except Exception as e: cellTypeCounterEnhancedFilter.logger.info( f"Error while exporting the file due to:\n{ e }" ) - return 1 \ No newline at end of file + return 1 diff --git a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py index e6f7160d..a7ef8c8b 100644 --- a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py @@ -331,4 +331,4 @@ def __initSurfaceQualityMetricSelection( self: Self ) -> None: otherMetrics: set[ int ] = getQualityMetricsOther() for measure in otherMetrics: # TODO: fix issue with incident vertex count metrics - self._commonMeshQualityMetric.AddArray( getQualityMeasureNameFromIndex( measure ), False ) \ No newline at end of file + self._commonMeshQualityMetric.AddArray( getQualityMeasureNameFromIndex( measure ), False ) diff --git a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py index 04952083..e5461b2c 100644 --- a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py +++ b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py @@ -56,4 +56,4 @@ def ApplyFilter( self: Self, inputMesh: vtkPointSet, outputMesh: vtkPointSet ) - splitMeshFilter.applyFilter() outputMesh.ShallowCopy( splitMeshFilter.getOutput() ) - return \ No newline at end of file + return From 1966e367cafbdb1e1f0acc560eb92db3dde7e05a Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 20 Nov 2025 11:32:22 +0100 Subject: [PATCH 18/39] add source for the paraview handler --- geos-pv/src/geos/pv/plugins/PVAttributeMapping.py | 2 ++ geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py | 2 ++ geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflow.py | 2 ++ geos-pv/src/geos/pv/plugins/PVGeosBlockExtractAndMerge.py | 2 ++ geos-pv/src/geos/pv/plugins/PVGeosLogReader.py | 1 + geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py | 2 ++ geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py | 1 + geos-pv/src/geos/pv/plugins/PVMohrCirclePlot.py | 2 ++ geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py | 1 + geos-pv/src/geos/pv/plugins/PVSplitMesh.py | 1 + geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py | 2 ++ 11 files changed, 18 insertions(+) diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index 804f1c13..cb5af13f 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -17,7 +17,9 @@ from geos.processing.generic_processing_tools.AttributeMapping import AttributeMapping from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy ) +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector from vtkmodules.vtkCommonDataModel import vtkCompositeDataSet, vtkDataSet, vtkMultiBlockDataSet diff --git a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py index d87bc656..59dbf6f4 100644 --- a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py @@ -9,7 +9,9 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy ) +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector from vtkmodules.vtkCommonDataModel import vtkPointSet, vtkTable diff --git a/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflow.py b/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflow.py index 44828829..3f674fa6 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflow.py +++ b/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflow.py @@ -27,7 +27,9 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smproperty, smproxy ) +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py __doc__ = """ PVGeomechanicsWorkflow is a Paraview plugin that executes multiple plugins: diff --git a/geos-pv/src/geos/pv/plugins/PVGeosBlockExtractAndMerge.py b/geos-pv/src/geos/pv/plugins/PVGeosBlockExtractAndMerge.py index bd3bb3b8..a2de08ea 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeosBlockExtractAndMerge.py +++ b/geos-pv/src/geos/pv/plugins/PVGeosBlockExtractAndMerge.py @@ -32,7 +32,9 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smproperty, smproxy ) +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py __doc__ = """ PVGeosBlockExtractAndMerge is a Paraview plugin processing the input mesh at the current time in two steps: diff --git a/geos-pv/src/geos/pv/plugins/PVGeosLogReader.py b/geos-pv/src/geos/pv/plugins/PVGeosLogReader.py index ba91a014..b3a1222e 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeosLogReader.py +++ b/geos-pv/src/geos/pv/plugins/PVGeosLogReader.py @@ -24,6 +24,7 @@ import vtkmodules.util.numpy_support as vnp from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy ) +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py diff --git a/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py index c7ecd276..c5dcd111 100644 --- a/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py @@ -9,7 +9,9 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy ) +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector from vtkmodules.vtkCommonDataModel import vtkCompositeDataSet, vtkMultiBlockDataSet, vtkUnstructuredGrid diff --git a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py index a7ef8c8b..f215e736 100644 --- a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py @@ -8,6 +8,7 @@ from typing_extensions import Self, Optional from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase, smdomain, smproperty # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py diff --git a/geos-pv/src/geos/pv/plugins/PVMohrCirclePlot.py b/geos-pv/src/geos/pv/plugins/PVMohrCirclePlot.py index 412355a3..8f2ba62c 100644 --- a/geos-pv/src/geos/pv/plugins/PVMohrCirclePlot.py +++ b/geos-pv/src/geos/pv/plugins/PVMohrCirclePlot.py @@ -14,7 +14,9 @@ from paraview.simple import Render # type: ignore[import-not-found] from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy ) +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from typing_extensions import Self from vtkmodules.vtkCommonCore import vtkDataArraySelection as vtkDAS diff --git a/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py b/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py index e994e385..aa7574fe 100755 --- a/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py +++ b/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py @@ -27,6 +27,7 @@ from paraview.simple import ( # type: ignore[import-not-found] GetActiveSource, GetActiveView, Render, Show, servermanager ) from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase, smdomain, smproperty # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py from vtkmodules.vtkCommonCore import vtkDataArraySelection, vtkInformation from vtkmodules.vtkCommonDataModel import vtkDataObject, vtkMultiBlockDataSet diff --git a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py index e5461b2c..2b2e7651 100644 --- a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py +++ b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py @@ -7,6 +7,7 @@ from typing_extensions import Self from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py diff --git a/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py b/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py index 5c585ba7..dcdacf52 100644 --- a/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py +++ b/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py @@ -8,7 +8,9 @@ from typing_extensions import Self from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase, smdomain, smproperty # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py from paraview.detail.loghandler import VTKHandler # type: ignore[import-not-found] +# source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py # update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent From 6ac33eced8ac457faf2a33f963e4763f90466cd1 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 21 Nov 2025 15:18:38 +0100 Subject: [PATCH 19/39] improve try - except strategy --- .../generic_processing_tools/SplitMesh.py | 70 ++++++++++------ .../pre_processing/CellTypeCounterEnhanced.py | 17 +++- .../pre_processing/MeshQualityEnhanced.py | 79 +++++++++++++------ .../pv/plugins/PVCellTypeCounterEnhanced.py | 30 +++---- .../geos/pv/plugins/PVMeshQualityEnhanced.py | 25 +++--- geos-pv/src/geos/pv/plugins/PVSplitMesh.py | 4 +- 6 files changed, 142 insertions(+), 83 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py index 99f7dbec..8cbe9d02 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py @@ -93,24 +93,33 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler' to True" " during the filter initialization." ) - def applyFilter( self: Self ) -> None: - """Apply the filter SplitMesh.""" + def applyFilter( self: Self ) -> bool: + """Apply the filter SplitMesh. + + Returns: + bool: True if the filter succeeded, False otherwise. + """ self.logger.info( f"Applying filter { self.logger.name }." ) try: # Count the number of cells before splitting. Then we will be able to know how many new cells and points # to allocate because each cell type is splitted in a known number of new cells and points. nbCells: int = self.inputMesh.GetNumberOfCells() counts: CellTypeCounts = self._getCellCounts() + if counts.getTypeCount( VTK_WEDGE ) != 0: + raise TypeError( "Input mesh contains wedges that are not currently supported." ) + + nbPolygon: int = counts.getTypeCount( VTK_POLYGON ) + nbPolyhedra: int = counts.getTypeCount( VTK_POLYHEDRON ) + # Current implementation only supports meshes composed of either polygons or polyhedra + if nbPolyhedra * nbPolygon != 0: + raise TypeError( + "Input mesh is composed of both polygons and polyhedra, but it must contains only one of the two." ) + nbTet: int = counts.getTypeCount( VTK_TETRA ) # will divide into 8 tets nbPyr: int = counts.getTypeCount( VTK_PYRAMID ) # will divide into 6 pyramids and 4 tets so 10 new cells nbHex: int = counts.getTypeCount( VTK_HEXAHEDRON ) # will divide into 8 hexes nbTriangles: int = counts.getTypeCount( VTK_TRIANGLE ) # will divide into 4 triangles nbQuad: int = counts.getTypeCount( VTK_QUAD ) # will divide into 4 quads - nbPolygon: int = counts.getTypeCount( VTK_POLYGON ) - nbPolyhedra: int = counts.getTypeCount( VTK_POLYHEDRON ) - assert counts.getTypeCount( VTK_WEDGE ) == 0, "Input mesh contains wedges that are not currently supported." - # Current implementation only supports meshes composed of either polygons or polyhedra - assert nbPolyhedra * nbPolygon == 0, "Input mesh is composed of both polygons and polyhedra, but it must contains only one of the two." nbNewPoints: int = 0 nbNewPoints = nbHex * 19 + nbTet * 6 + nbPyr * 9 if nbPolyhedra > 0 else nbTriangles * 3 + nbQuad * 5 nbNewCells: int = nbHex * 8 + nbTet * 8 + nbPyr * 10 + nbTriangles * 4 + nbQuad * 4 @@ -125,6 +134,7 @@ def applyFilter( self: Self ) -> None: self.originalId.SetName( "OriginalID" ) self.originalId.Allocate( nbNewCells ) self.cellTypes = [] + # Define cell type to splitting method mapping splitMethods = { VTK_HEXAHEDRON: self._splitHexahedron, @@ -142,20 +152,29 @@ def applyFilter( self: Self ) -> None: else: raise TypeError( f"Cell type { vtkCellTypes.GetClassNameFromTypeId( cellType ) } is not supported." ) - # add points and cells + + # Add points and cells self.outputMesh.SetPoints( self.points ) self.outputMesh.SetCells( self.cellTypes, self.cells ) - # add attribute saving original cell ids + + # Add attribute saving original cell ids cellArrays: vtkCellData = self.outputMesh.GetCellData() - assert cellArrays is not None, "Cell data is undefined." + if cellArrays is None: + raise AttributeError( "Cell data is undefined." ) cellArrays.AddArray( self.originalId ) - # transfer all cell arrays + + # Transfer all cell arrays self._transferCellArrays( self.outputMesh ) self.logger.info( f"The filter { self.logger.name } succeeded." ) - except Exception as e: + except ( TypeError, AttributeError ) as e: self.logger.error( f"The filter {self.logger.name } failed.\n{ e }" ) + return False + except Exception as e: + mess: str = f"The filter { self.logger.name } failed.\n{ e }" + self.logger.critical( mess, exc_info=True ) + return False - return + return True def getOutput( self: Self ) -> vtkUnstructuredGrid: """Get the splitted mesh computed.""" @@ -171,7 +190,8 @@ def _getCellCounts( self: Self ) -> CellTypeCounts: self.inputMesh, self.speHandler ) if self.speHandler and len( cellTypeCounterEnhancedFilter.logger.handlers ) == 0: cellTypeCounterEnhancedFilter.setLoggerHandler( self.handler ) - cellTypeCounterEnhancedFilter.applyFilter() + if not cellTypeCounterEnhancedFilter.applyFilter(): + raise return cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() def _addMidPoint( self: Self, ptA: int, ptB: int ) -> int: @@ -422,23 +442,25 @@ def _splitQuad( self: Self, cell: vtkCell, index: int ) -> None: self.originalId.InsertNextValue( index ) self.cellTypes.extend( [ VTK_QUAD ] * 4 ) - def _transferCellArrays( self: Self, splittedMesh: vtkUnstructuredGrid ) -> bool: + def _transferCellArrays( self: Self, splittedMesh: vtkUnstructuredGrid ) -> None: """Transfer arrays from input mesh to splitted mesh. Args: - splittedMesh (vtkUnstructuredGrid): splitted mesh - - Returns: - bool: True if arrays were successfully transferred. + splittedMesh (vtkUnstructuredGrid): Splitted mesh. """ - cellDataSplitted: vtkCellData = splittedMesh.GetCellData() - assert cellDataSplitted is not None, "Cell data of splitted mesh should be defined." cellData: vtkCellData = self.inputMesh.GetCellData() - assert cellData is not None, "Cell data of input mesh should be defined." + if cellData is None: + raise AttributeError( "Cell data of input mesh should be defined." ) + + cellDataSplitted: vtkCellData = splittedMesh.GetCellData() + if cellDataSplitted is None: + raise AttributeError( "Cell data of splitted mesh should be defined." ) + # for each array of input mesh for i in range( cellData.GetNumberOfArrays() ): array: vtkDataArray = cellData.GetArray( i ) - assert array is not None, "Array should be defined." + if array is None: + raise AttributeError( "Array should be defined." ) npArray: npt.NDArray[ np.float64 ] = vtk_to_numpy( array ) # get number of components dims: tuple[ int, ...] = npArray.shape @@ -455,4 +477,4 @@ def _transferCellArrays( self: Self, splittedMesh: vtkUnstructuredGrid ) -> bool cellDataSplitted.AddArray( newArray ) cellDataSplitted.Modified() splittedMesh.Modified() - return True + return diff --git a/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py b/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py index 568146dd..3aa8953a 100644 --- a/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py +++ b/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py @@ -86,8 +86,12 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler'" " to True during the filter initialization." ) - def applyFilter( self: Self ) -> None: - """Apply CellTypeCounterEnhanced filter.""" + def applyFilter( self: Self ) -> bool: + """Apply CellTypeCounterEnhanced filter. + + Returns: + bool: True if the filter succeeded, False otherwise. + """ self.logger.info( f"Apply filter { self.logger.name }." ) try: # compute cell type counts @@ -112,10 +116,15 @@ def applyFilter( self: Self ) -> None: array.SetValue( 0, self._counts.getTypeCount( cellType ) ) self.outTable.AddColumn( array ) self.logger.info( f"The filter { self.logger.name } succeeded." ) - except AssertionError as e: + except TypeError as e: self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) + return False + except Exception as e: + mess: str = f"The filter { self.logger.name } failed.\n{ e }" + self.logger.critical( mess, exc_info=True ) + return False - return + return True def GetCellTypeCountsObject( self: Self ) -> CellTypeCounts: """Get CellTypeCounts object. diff --git a/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py b/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py index a5e94b6d..6c454c69 100644 --- a/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py +++ b/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py @@ -282,8 +282,12 @@ def getComputedMetricsFromCellType( self: Self, cellType: int ) -> Optional[ set metrics = metrics.intersection( computedMetrics ) return metrics if commonComputedMetricsExists else None - def applyFilter( self: Self ) -> None: - """Apply MeshQualityEnhanced filter.""" + def applyFilter( self: Self ) -> bool: + """Apply MeshQualityEnhanced filter. + + Returns: + bool: True if the filter succeeded, False otherwise. + """ self.logger.info( f"Apply filter { self.logger.name }." ) try: self._outputMesh.ShallowCopy( self.inputMesh ) @@ -302,10 +306,15 @@ def applyFilter( self: Self ) -> None: self._outputMesh.Modified() self.logger.info( f"The filter { self.logger.name } succeeded." ) - except Exception as e: + except ( ValueError, IndexError, TypeError, AttributeError ) as e: self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) + return False + except Exception as e: + mess: str = f"The filter { self.logger.name } failed.\n{ e }" + self.logger.critical( mess, exc_info=True ) + return False - return + return True def getOutput( self: Self ) -> vtkUnstructuredGrid: """Get the mesh computed with the stats.""" @@ -317,10 +326,15 @@ def _computeCellTypeCounts( self: Self ) -> None: self._outputMesh, self.speHandler ) if self.speHandler and len( cellTypeCounterEnhancedFilter.logger.handlers ) == 0: cellTypeCounterEnhancedFilter.setLoggerHandler( self.handler ) - cellTypeCounterEnhancedFilter.applyFilter() + if not cellTypeCounterEnhancedFilter.applyFilter(): + raise + counts: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() - assert counts is not None, "CellTypeCounts is undefined" + if counts is None: + raise AttributeError( "CellTypeCounts is undefined" ) + self._qualityMetricSummary.setCellTypeCounts( counts ) + return def _evaluateMeshQualityAll( self: Self ) -> None: """Compute all mesh quality metrics.""" @@ -335,7 +349,9 @@ def _evaluateMeshQualityAll( self: Self ) -> None: self._countVertexIncidentEdges() else: # TODO: add other metrics - print( "" ) + pass + + return def _evaluateCellQuality( self: Self, metricIndex: int ) -> None: """Compute mesh input quality metric. By default, the metric is computed for all cell types. @@ -375,7 +391,8 @@ def _evaluateCellQuality( self: Self, metricIndex: int ) -> None: else: output = self._applyMeshQualityFilter( metricIndex, cellToApplyTo ) - assert output is not None, "Output mesh from mesh quality calculation is undefined." + if output is None: + raise AttributeError( "Output mesh from mesh quality calculation is undefined." ) # Transfer output cell array to input mesh # TODO: to test if Shallow copy of vtkMeshQualityFilter result # and rename "Quality" array is more efficient than what is done here @@ -422,7 +439,8 @@ def _computeAdditionalMetrics( self: Self, metricIndex: int ) -> None: metricIndex (int): Metric index """ metric = getQualityMetricFromIndex( metricIndex ) - assert metric is not None, f"Additional cell quality metric index {metricIndex} is undefined." + if metric is None: + raise AttributeError( f"Additional cell quality metric index {metricIndex} is undefined." ) # Output array name: str = getQualityMetricArrayName( metric.getMetricIndex() ) newArray: vtkDoubleArray = vtkDoubleArray() @@ -436,7 +454,8 @@ def _computeAdditionalMetrics( self: Self, metricIndex: int ) -> None: newArray.SetValue( i, val ) # Add array cellArrays: vtkCellData = self._outputMesh.GetCellData() - assert cellArrays is not None, "Cell data from output mesh is undefined." + if cellArrays is None: + raise AttributeError( "Cell data from output mesh is undefined." ) cellArrays.AddArray( newArray ) cellArrays.Modified() self._outputMesh.Modified() @@ -448,7 +467,7 @@ def _transferCellAttribute( attributeFromName: str, attributeToName: str, qualityMetric: int, - ) -> bool: + ) -> None: """Transfer quality attribute to the client mesh. The attribute is renamed with quality metric name. Because a default quality @@ -460,14 +479,13 @@ def _transferCellAttribute( attributeFromName (str): The name of the quality attribute in initial mesh attributeToName (str): Name of the attribute in the final mesh qualityMetric (QualityMetricOtherEnum):The quality metric. - - Returns: - bool: True if the attribute was successfully transferred, False otherwise """ cellArrays: vtkCellData = inputMesh.GetCellData() - assert cellArrays is not None, "Cell data from vtkMeshQuality output mesh is undefined." + if cellArrays is None: + raise AttributeError( "Cell data from vtkMeshQuality output mesh is undefined." ) array: vtkDataArray = cellArrays.GetArray( attributeFromName ) - assert array is not None, f"{attributeFromName} attribute is undefined." + if array is None: + raise AttributeError( f"{ attributeFromName } attribute is undefined." ) # Rename array array.SetName( attributeToName ) @@ -478,11 +496,12 @@ def _transferCellAttribute( # Add array to input mesh inputCellArrays: vtkCellData = self._outputMesh.GetCellData() - assert inputCellArrays is not None, "Cell data from input mesh is undefined." + if inputCellArrays is None: + raise AttributeError( "Cell data from input mesh is undefined." ) inputCellArrays.AddArray( array ) inputCellArrays.Modified() - return True + return def _replaceIrrelevantValues( self: Self, array: vtkDataArray, mesh: vtkUnstructuredGrid, metric: MeshQualityMetricEnum ) -> None: @@ -543,7 +562,8 @@ def _updateStatsSummaryByCellType( self: Self, metricIndex: int, cellType: int ) cellType (int): Cell type index """ cellArrays: vtkCellData = self._outputMesh.GetCellData() - assert cellArrays is not None, "Cell data from input mesh is undefined." + if cellArrays is None: + raise AttributeError( "Cell data from input mesh is undefined." ) arrayName: str = getQualityMetricArrayName( metricIndex ) array: vtkDataArray | None = cellArrays.GetArray( arrayName ) @@ -584,7 +604,8 @@ def _initCellTypeMasks( self: Self ) -> None: def _createFieldDataStatsSummary( self: Self ) -> None: """Create field data arrays with quality statistics.""" fieldData: vtkFieldData = self._outputMesh.GetFieldData() - assert fieldData is not None, "Field data is undefined." + if fieldData is None: + raise AttributeError( "Field data is undefined." ) for cellType in self._allCellTypesExtended: count: int = self._qualityMetricSummary.getCellTypeCountsOfCellType( cellType ) metrics: Optional[ set[ int ] ] = self.getComputedMetricsFromCellType( cellType ) @@ -687,7 +708,8 @@ def _countVertexIncidentEdges( self: Self ) -> None: # Create point attribute pointData: vtkPointData = self._outputMesh.GetPointData() - assert pointData is not None, "Point data is undefined." + if pointData is None: + raise AttributeError( "Point data is undefined." ) countArray: vtkIntArray = numpy_to_vtk( incidentCounts, deep=1 ) metricName: str = metric.getMetricName().replace( " ", "" ) name: str = QUALITY_ARRAY_NAME + "_" + metricName @@ -696,7 +718,8 @@ def _countVertexIncidentEdges( self: Self ) -> None: pointData.Modified() fieldData: vtkFieldData = self._outputMesh.GetFieldData() - assert fieldData is not None, "Field data is undefined." + if fieldData is None: + raise AttributeError( "Field data is undefined." ) for statType in list( StatTypes ): name = metricName + "_" + statType.getString() val: float | int = statType.compute( incidentCounts ) @@ -756,7 +779,8 @@ def _computeSquishIndex( self: Self ) -> None: # Add array cellArrays: vtkCellData = self._outputMesh.GetCellData() - assert cellArrays is not None, "Cell data from output mesh is undefined." + if cellArrays is None: + raise AttributeError( "Cell data from output mesh is undefined." ) cellArrays.AddArray( newArray ) cellArrays.Modified() self._outputMesh.Modified() @@ -778,8 +802,10 @@ def _getCellCenter( self: Self, cellCenter: npt.NDArray[ np.float64 ] = np.zeros( 3 ) if cell.GetCellDimension() == 2: # Polygonal cell - assert ptsIds is not None, "Point ids are required for computing polygonal cell center." - assert points is not None, "Points are required for computing polygonal cell center." + if ptsIds is None: + raise ValueError( "Point ids are required for computing polygonal cell center." ) + if points is None: + raise ValueError( "Points are required for computing polygonal cell center." ) cell.GetPointIds() vtkPolygon.ComputeCentroid( ptsIds, points, cellCenter ) # type: ignore[call-overload] elif cell.GetCellDimension() == 3: @@ -802,7 +828,8 @@ def _getNormalVector( self: Self, points: vtkPoints, face: vtkCell ) -> npt.NDAr Returns: npt.NDArray[np.float64]: Coordinates of the normal vector """ - assert face.GetCellDimension() == 2, "Cell must be a planar polygon." + if face.GetCellDimension() != 2: + raise TypeError( "Cell must be a planar polygon." ) facePtsIds: vtkIdList = face.GetPointIds() # Need only 3 points among all to get the normal of the face since we suppose face is a plane ptsCoords: npt.NDArray[ np.float64 ] = np.zeros( ( 3, 3 ), dtype=float ) diff --git a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py index d87bc656..ad237bd8 100644 --- a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py @@ -135,19 +135,19 @@ def RequestData( cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( inputMesh, True ) if len( cellTypeCounterEnhancedFilter.logger.handlers ) == 0: cellTypeCounterEnhancedFilter.setLoggerHandler( VTKHandler() ) - cellTypeCounterEnhancedFilter.applyFilter() - outputTable.ShallowCopy( cellTypeCounterEnhancedFilter.getOutput() ) - - # print counts in Output Messages view - counts: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() - - self._countsAll += counts - # save to file if asked - if self._saveToFile and self._filename is not None: - try: - with open( self._filename, 'w' ) as fout: - fout.write( self._countsAll.print() ) - cellTypeCounterEnhancedFilter.logger.info( f"File {self._filename} was successfully written." ) - except Exception as e: - cellTypeCounterEnhancedFilter.logger.info( f"Error while exporting the file due to:\n{ e }" ) + if cellTypeCounterEnhancedFilter.applyFilter(): + outputTable.ShallowCopy( cellTypeCounterEnhancedFilter.getOutput() ) + + # print counts in Output Messages view + counts: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() + + self._countsAll += counts + # save to file if asked + if self._saveToFile and self._filename is not None: + try: + with open( self._filename, 'w' ) as fout: + fout.write( self._countsAll.print() ) + cellTypeCounterEnhancedFilter.logger.info( f"File {self._filename} was successfully written." ) + except Exception as e: + cellTypeCounterEnhancedFilter.logger.info( f"Error while exporting the file due to:\n{ e }" ) return 1 diff --git a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py index a7ef8c8b..9384f92f 100644 --- a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py @@ -127,7 +127,7 @@ def a07sSetWedgeMetrics( self: Self ) -> vtkDataArraySelection: @smproperty.dataarrayselection( name="HexahedronSpecificQualityMetric" ) def a08sSetHexMetrics( self: Self ) -> vtkDataArraySelection: - """Set Hexahdron quality metrics selection.""" + """Set Hexahedron quality metrics selection.""" return self._HexQualityMetric @smproperty.dataarrayselection( name="OtherMeshQualityMetric" ) @@ -238,16 +238,16 @@ def ApplyFilter( self, inputMesh: vtkUnstructuredGrid, outputMesh: vtkUnstructur wedgeMetrics=wedgeMetrics, hexaMetrics=hexaMetrics ) meshQualityEnhancedFilter.SetOtherMeshQualityMetrics( otherMetrics ) - meshQualityEnhancedFilter.applyFilter() + if meshQualityEnhancedFilter.applyFilter(): - outputMesh.ShallowCopy( meshQualityEnhancedFilter.getOutput() ) + outputMesh.ShallowCopy( meshQualityEnhancedFilter.getOutput() ) - # save to file if asked - if self._saveToFile: - stats: QualityMetricSummary = meshQualityEnhancedFilter.GetQualityMetricSummary() - logger: logging.Logger = meshQualityEnhancedFilter.logger - self.saveFile( stats, logger ) - self._blockIndex += 1 + # save to file if asked + if self._saveToFile: + stats: QualityMetricSummary = meshQualityEnhancedFilter.GetQualityMetricSummary() + logger: logging.Logger = meshQualityEnhancedFilter.logger + self.saveFile( stats, logger ) + self._blockIndex += 1 return def saveFile( @@ -257,14 +257,15 @@ def saveFile( ) -> None: """Export mesh quality metric summary file.""" try: - assert self._filename is not None, "Mesh quality summary report file path is undefined." + if self._filename is None: + raise AttributeError( "Mesh quality summary report file path is undefined." ) # add index for multiblock meshes index: int = self._filename.rfind( '.' ) - filename: str = self._filename[ :index ] + f"_{self._blockIndex}" + self._filename[ index: ] + filename: str = self._filename[ :index ] + f"{ self._blockIndex }" + self._filename[ index: ] fig = stats.plotSummaryFigure() fig.savefig( filename, dpi=150 ) - logger.info( f"File {filename} was successfully written." ) + logger.info( f"File { filename } was successfully written." ) except Exception as e: logger.error( f"Error while exporting the file due to:\n{ e }" ) diff --git a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py index e5461b2c..697cc285 100644 --- a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py +++ b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py @@ -53,7 +53,7 @@ def ApplyFilter( self: Self, inputMesh: vtkPointSet, outputMesh: vtkPointSet ) - splitMeshFilter: SplitMesh = SplitMesh( inputMesh, True ) if len( splitMeshFilter.logger.handlers ) == 0: splitMeshFilter.setLoggerHandler( VTKHandler() ) - splitMeshFilter.applyFilter() - outputMesh.ShallowCopy( splitMeshFilter.getOutput() ) + if splitMeshFilter.applyFilter(): + outputMesh.ShallowCopy( splitMeshFilter.getOutput() ) return From d7f69d85b4c363bcf053d32ce3e95bde92a755fe Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 21 Nov 2025 19:02:33 +0100 Subject: [PATCH 20/39] Uniform applyFilter to log messages --- .../geos/mesh/utils/multiblockModifiers.py | 29 ++- geos-mesh/tests/test_multiblockModifiers.py | 16 ++ .../AttributeMapping.py | 96 ++++----- .../CreateConstantAttributePerRegion.py | 199 +++++++++--------- .../FillPartialArrays.py | 53 +++-- .../MergeBlockEnhanced.py | 32 +-- .../post_processing/GeomechanicsCalculator.py | 15 +- .../post_processing/GeosBlockExtractor.py | 15 +- .../post_processing/GeosBlockMerge.py | 15 +- .../post_processing/SurfaceGeomechanics.py | 61 +++--- .../tests/test_CellTypeCounterEnhanced.py | 4 +- .../tests/test_GeosExtractBlock.py | 2 +- .../tests/test_MergeBlocksEnhanced.py | 19 +- .../tests/test_MeshQualityEnhanced.py | 3 +- geos-processing/tests/test_SplitMesh.py | 6 +- .../pv/plugins/PVGeomechanicsCalculator.py | 10 +- .../geos/pv/plugins/PVMergeBlocksEnhanced.py | 6 +- .../geos/pv/plugins/PVSurfaceGeomechanics.py | 16 +- .../src/geos/pv/utils/workflowFunctions.py | 7 +- 19 files changed, 310 insertions(+), 294 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/multiblockModifiers.py b/geos-mesh/src/geos/mesh/utils/multiblockModifiers.py index fad5958e..352860bf 100644 --- a/geos-mesh/src/geos/mesh/utils/multiblockModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/multiblockModifiers.py @@ -8,6 +8,7 @@ vtkUnstructuredGrid, vtkDataSet ) from packaging.version import Version from vtkmodules.vtkCommonCore import vtkLogger +from geos.utils.Errors import VTKError # TODO: remove this condition when all codes are adapted for VTK newest version. import vtk @@ -50,20 +51,21 @@ def mergeBlocks( .. Warning:: This function will not work properly if there are duplicated cell IDs in the different blocks of the input mesh. """ + vtkErrorLogger: Logger if logger is None: - logger = getLogger( "mergeBlocks" ) - # Creation of a child logger to deal with VTKErrors without polluting parent logger - mbLogger: Logger = getLogger( f"{logger.name}.vtkErrorLogger" ) - - mbLogger.propagate = False + vtkErrorLogger = getLogger( "Merge blocks", True ) + else: + vtkErrorLogger = logging.getLogger( f"{ logger.name } vtkError Logger" ) + vtkErrorLogger.setLevel( logging.INFO ) + vtkErrorLogger.addHandler( logger.handlers[ 0 ] ) + vtkErrorLogger.propagate = False vtkLogger.SetStderrVerbosity( vtkLogger.VERBOSITY_ERROR ) - mbLogger.addFilter( RegexExceptionFilter() ) # will raise VTKError if captured VTK Error - mbLogger.setLevel( logging.DEBUG ) + vtkErrorLogger.addFilter( RegexExceptionFilter() ) # will raise VTKError if captured VTK Error # Fill the partial attributes with default values to keep them during the merge. if keepPartialAttributes and not fillAllPartialAttributes( inputMesh, logger ): - logger.warning( "Failed to fill partial attributes. Merging without keeping partial attributes." ) + raise ValueError( "Failed to fill partial attributes. Merging without keeping partial attributes." ) outputMesh: vtkUnstructuredGrid @@ -76,9 +78,10 @@ def mergeBlocks( else: if inputMesh.IsA( "vtkDataSet" ): - logger.warning( "Input mesh is already a single block." ) + vtkErrorLogger.warning( "Input mesh is already a single block." ) outputMesh = vtkUnstructuredGrid.SafeDownCast( inputMesh ) else: + with VTKCaptureLog() as captured_log: af: vtkAppendDataSets = vtkAppendDataSets() @@ -95,8 +98,14 @@ def mergeBlocks( captured_log.seek( 0 ) captured = captured_log.read().decode() - mbLogger.debug( captured.strip() ) + if captured != "": + vtkErrorLogger.error( captured.strip() ) + # raise VTKError( captured.strip() ) + # pass outputMesh = af.GetOutputDataObject( 0 ) + if outputMesh is None: + raise VTKError( "Something went wrong in VTK" ) + return outputMesh diff --git a/geos-mesh/tests/test_multiblockModifiers.py b/geos-mesh/tests/test_multiblockModifiers.py index d240736e..34b6d700 100644 --- a/geos-mesh/tests/test_multiblockModifiers.py +++ b/geos-mesh/tests/test_multiblockModifiers.py @@ -9,6 +9,12 @@ from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet, vtkUnstructuredGrid from geos.mesh.utils import multiblockModifiers +from unittest import TestCase +from geos.utils.Errors import VTKError + +import vtk +from packaging.version import Version + @pytest.mark.parametrize( "keepPartialAttributes, nbPointAttributes, nbCellAttributes, nbFieldAttributes", [ ( False, 0, 16, 1 ), @@ -35,3 +41,13 @@ def test_mergeBlocks( assert dataset.GetFieldData().GetNumberOfArrays( ) == nbFieldAttributes, f"Expected {nbFieldAttributes} field attributes after the merge, not {dataset.GetFieldData().GetNumberOfArrays()}." + +class RaiseMergeBlocks( TestCase ): + """Test failure on empty multiBlockDataSet.""" + + def test_TypeError( self ) -> None: + """Test raise of TypeError.""" + multiBlockDataset = vtkMultiBlockDataSet() # should fail on empty data + if Version( vtk.__version__ ) < Version( "9.5" ): + with pytest.raises( VTKError ): + multiblockModifiers.mergeBlocks( multiBlockDataset, True ) \ No newline at end of file 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 0ab6f3d3..9b3ce073 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -140,63 +140,57 @@ def applyFilter( self: Self ) -> bool: """ self.logger.info( f"Apply filter { self.logger.name }." ) - if len( self.attributeNames ) == 0: - self.logger.warning( f"Please enter at least one { self.piece } attribute to transfer." ) - self.logger.warning( f"The filter { self.logger.name } has not been used." ) - return False - - attributesInMeshFrom: set[ str ] = getAttributeSet( self.meshFrom, self.onPoints ) - wrongAttributeNames: set[ str ] = self.attributeNames.difference( attributesInMeshFrom ) - if len( wrongAttributeNames ) > 0: - self.logger.error( - f"The { self.piece } attributes { wrongAttributeNames } are not present in the source mesh." ) - self.logger.error( f"The filter { self.logger.name } failed." ) - return False - - attributesInMeshTo: set[ str ] = getAttributeSet( self.meshTo, self.onPoints ) - attributesAlreadyInMeshTo: set[ str ] = self.attributeNames.intersection( attributesInMeshTo ) - if len( attributesAlreadyInMeshTo ) > 0: - self.logger.error( - f"The { self.piece } attributes { attributesAlreadyInMeshTo } are already present in the final mesh." ) - self.logger.error( f"The filter { self.logger.name } failed." ) - return False + try: + if len( self.attributeNames ) == 0: + raise ValueError( f"Please enter at least one { self.piece } attribute to transfer." ) + + attributesInMeshFrom: set[ str ] = getAttributeSet( self.meshFrom, self.onPoints ) + wrongAttributeNames: set[ str ] = self.attributeNames.difference( attributesInMeshFrom ) + if len( wrongAttributeNames ) > 0: + raise AttributeError( f"The { self.piece } attributes { wrongAttributeNames } are not present in the source mesh." ) + + attributesInMeshTo: set[ str ] = getAttributeSet( self.meshTo, self.onPoints ) + attributesAlreadyInMeshTo: set[ str ] = self.attributeNames.intersection( attributesInMeshTo ) + if len( attributesAlreadyInMeshTo ) > 0: + raise AttributeError( f"The { self.piece } attributes { attributesAlreadyInMeshTo } are already present in the final mesh." ) + + if isinstance( self.meshFrom, vtkMultiBlockDataSet ): + partialAttributes: list[ str ] = [] + for attributeName in self.attributeNames: + if not isAttributeGlobal( self.meshFrom, attributeName, self.onPoints ): + partialAttributes.append( attributeName ) + + if len( partialAttributes ) > 0: + raise AttributeError( f"All { self.piece } attributes to transfer must be global, { partialAttributes } are partials." ) + + self.ElementMap = computeElementMapping( self.meshFrom, self.meshTo, self.onPoints ) + sharedElement: bool = False + for key in self.ElementMap: + if np.any( self.ElementMap[ key ] > -1 ): + sharedElement = True + + if not sharedElement: + raise ValueError( f"The two meshes do not have any shared { self.piece }." ) - if isinstance( self.meshFrom, vtkMultiBlockDataSet ): - partialAttributes: list[ str ] = [] for attributeName in self.attributeNames: - if not isAttributeGlobal( self.meshFrom, attributeName, self.onPoints ): - partialAttributes.append( attributeName ) - - if len( partialAttributes ) > 0: - self.logger.error( - f"All { self.piece } attributes to transfer must be global, { partialAttributes } are partials." ) - self.logger.error( f"The filter { self.logger.name } failed." ) - - self.ElementMap = computeElementMapping( self.meshFrom, self.meshTo, self.onPoints ) - sharedElement: bool = False - for key in self.ElementMap: - if np.any( self.ElementMap[ key ] > -1 ): - sharedElement = True - - if not sharedElement: - self.logger.warning( f"The two meshes do not have any shared { self.piece }." ) - self.logger.warning( f"The filter { self.logger.name } has not been used." ) + # TODO:: Modify arrayModifiers function to raise error. + if not transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, + self.onPoints, self.logger ): + raise + + # Log the output message. + self._logOutputMessage() + except ( TypeError, ValueError, AttributeError ) as e: + self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) + return False + except Exception as e: + mess: str = f"The filter { self.logger.name } failed.\n{ e }" + self.logger.critical( mess, exc_info=True ) return False - - for attributeName in self.attributeNames: - if not transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, - self.onPoints, self.logger ): - self.logger.error( f"The attribute { attributeName } has not been mapped." ) - self.logger.error( f"The filter { self.logger.name } failed." ) - return False - - # Log the output message. - self._logOutputMessage() return True def _logOutputMessage( self: Self ) -> None: """Create and log result messages of the filter.""" self.logger.info( f"The filter { self.logger.name } succeeded." ) - self.logger.info( f"The { self.piece } attributes { self.attributeNames } have been transferred from the source" - " mesh to the final mesh with the { self.piece } mapping." ) + self.logger.info( f"The { self.piece } attributes { self.attributeNames } have been transferred from the source mesh to the final mesh with the { self.piece } mapping." ) 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 7844c118..7e2c2960 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -160,124 +160,113 @@ def applyFilter( self: Self ) -> bool: # Add the handler to count warnings messages. self.logger.addHandler( self.counter ) - # Check the validity of the attribute region. - if self.onPoints is None: - self.logger.error( f"{ self.regionName } is not in the mesh." ) - self.logger.error( f"The new attribute { self.newAttributeName } has not been added." ) - self.logger.error( f"The filter { self.logger.name } failed." ) - return False - - if self.onBoth: - self.logger.error( f"There are two attributes named { self.regionName }, one on points" - "and the other on cells. The region attribute must be unique." ) - self.logger.error( f"The new attribute { self.newAttributeName } has not been added." ) - self.logger.error( f"The filter { self.logger.name } failed." ) - return False + try: + # Check the validity of the attribute region. + if self.onPoints is None: + raise AttributeError( f"{ self.regionName } is not in the mesh." ) + + if self.onBoth: + raise ValueError( f"There are two attributes named { self.regionName }, one on points and the other on cells. The region attribute must be unique." ) + + nbComponentsRegion: int = getNumberOfComponents( self.mesh, self.regionName, self.onPoints ) + if nbComponentsRegion != 1: + raise ValueError( f"The region attribute { self.regionName } has to many components, one is requires." ) + + self._setInfoRegion() + # Check if the number of components and number of values for the region indexes are coherent. + for index in self.dictRegionValues: + if len( self.dictRegionValues[ index ] ) != self.nbComponents: + raise ValueError( f"The number of value given for the region index { index } is not correct. You must set a value for each component, in this case { self.nbComponents }." ) + + listIndexes: list[ Any ] = list( self.dictRegionValues.keys() ) + validIndexes: list[ Any ] = [] + invalidIndexes: list[ Any ] = [] + 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.onPoints ): + raise AttributeError( f"The region attribute { self.regionName } has to be global." ) + + validIndexes, invalidIndexes = checkValidValuesInMultiBlock( self.mesh, self.regionName, listIndexes, + self.onPoints ) + if len( validIndexes ) == 0: + if len( self.dictRegionValues ) == 0: + self.logger.warning( "No region indexes entered." ) + else: + self.logger.warning( + f"The region indexes entered are not in the region attribute { self.regionName }." ) + + if not createConstantAttributeMultiBlock( self.mesh, + self.defaultValue, + self.newAttributeName, + componentNames=self.componentNames, + onPoints=self.onPoints, + logger=self.logger ): + raise - nbComponentsRegion: int = getNumberOfComponents( self.mesh, self.regionName, self.onPoints ) - if nbComponentsRegion != 1: - self.logger.error( f"The region attribute { self.regionName } has to many components, one is requires." ) - self.logger.error( f"The new attribute { self.newAttributeName } has not been added." ) - self.logger.error( f"The filter { self.logger.name } failed." ) - return False - - self._setInfoRegion() - # Check if the number of components and number of values for the region indexes are coherent. - for index in self.dictRegionValues: - if len( self.dictRegionValues[ index ] ) != self.nbComponents: - self.logger.error( f"The number of value given for the region index { index } is not correct." - f" You must set a value for each component, in this case { self.nbComponents }." ) - return False - - listIndexes: list[ Any ] = list( self.dictRegionValues.keys() ) - validIndexes: list[ Any ] = [] - invalidIndexes: list[ Any ] = [] - 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.onPoints ): - self.logger.error( f"The region attribute { self.regionName } has to be global." ) - self.logger.error( f"The new attribute { self.newAttributeName } has not been added." ) - self.logger.error( f"The filter { self.logger.name } failed." ) - return False - - validIndexes, invalidIndexes = checkValidValuesInMultiBlock( self.mesh, self.regionName, listIndexes, - self.onPoints ) - if len( validIndexes ) == 0: - if len( self.dictRegionValues ) == 0: - self.logger.warning( "No region indexes entered." ) else: - self.logger.warning( - f"The region indexes entered are not in the region attribute { self.regionName }." ) - - if not createConstantAttributeMultiBlock( self.mesh, - self.defaultValue, - self.newAttributeName, - componentNames=self.componentNames, - onPoints=self.onPoints, - logger=self.logger ): - self.logger.error( f"The filter { self.logger.name } failed." ) - return False + if len( invalidIndexes ) > 0: + self.logger.warning( + f"The region indexes { invalidIndexes } are not in the region attribute { self.regionName }." ) + + # Parse the mesh to add the attribute on each dataset. + listFlatIdDataSet: list[ int ] = getBlockElementIndexesFlatten( self.mesh ) + for flatIdDataSet in listFlatIdDataSet: + dataSet: vtkDataSet = vtkDataSet.SafeDownCast( self.mesh.GetDataSet( flatIdDataSet ) ) + + regionArray = getArrayInObject( dataSet, self.regionName, self.onPoints ) + newArray = self._createArrayFromRegionArrayWithValueMap( regionArray ) + if not createAttribute( dataSet, + newArray, + self.newAttributeName, + componentNames=self.componentNames, + onPoints=self.onPoints, + logger=self.logger ): + raise else: - if len( invalidIndexes ) > 0: - self.logger.warning( - f"The region indexes { invalidIndexes } are not in the region attribute { self.regionName }." ) + validIndexes, invalidIndexes = checkValidValuesInDataSet( self.mesh, self.regionName, listIndexes, + self.onPoints ) + if len( validIndexes ) == 0: + if len( self.dictRegionValues ) == 0: + self.logger.warning( "No region indexes entered." ) + else: + self.logger.warning( + f"The region indexes entered are not in the region attribute { self.regionName }." ) + + if not createConstantAttributeDataSet( self.mesh, + self.defaultValue, + self.newAttributeName, + componentNames=self.componentNames, + onPoints=self.onPoints, + logger=self.logger ): + raise - # Parse the mesh to add the attribute on each dataset. - listFlatIdDataSet: list[ int ] = getBlockElementIndexesFlatten( self.mesh ) - for flatIdDataSet in listFlatIdDataSet: - dataSet: vtkDataSet = vtkDataSet.SafeDownCast( self.mesh.GetDataSet( flatIdDataSet ) ) + else: + if len( invalidIndexes ) > 0: + self.logger.warning( + f"The region indexes { invalidIndexes } are not in the region attribute { self.regionName }." ) - regionArray = getArrayInObject( dataSet, self.regionName, self.onPoints ) + regionArray = getArrayInObject( self.mesh, self.regionName, self.onPoints ) newArray = self._createArrayFromRegionArrayWithValueMap( regionArray ) - if not createAttribute( dataSet, + if not createAttribute( self.mesh, newArray, self.newAttributeName, componentNames=self.componentNames, onPoints=self.onPoints, logger=self.logger ): - self.logger.error( f"The filter { self.logger.name } failed." ) - return False + raise - else: - validIndexes, invalidIndexes = checkValidValuesInDataSet( self.mesh, self.regionName, listIndexes, - self.onPoints ) - if len( validIndexes ) == 0: - if len( self.dictRegionValues ) == 0: - self.logger.warning( "No region indexes entered." ) - else: - self.logger.warning( - f"The region indexes entered are not in the region attribute { self.regionName }." ) - - if not createConstantAttributeDataSet( self.mesh, - self.defaultValue, - self.newAttributeName, - componentNames=self.componentNames, - onPoints=self.onPoints, - logger=self.logger ): - self.logger.error( f"The filter { self.logger.name } failed." ) - return False - - else: - if len( invalidIndexes ) > 0: - self.logger.warning( - f"The region indexes { invalidIndexes } are not in the region attribute { self.regionName }." ) - - regionArray = getArrayInObject( self.mesh, self.regionName, self.onPoints ) - newArray = self._createArrayFromRegionArrayWithValueMap( regionArray ) - if not createAttribute( self.mesh, - newArray, - self.newAttributeName, - componentNames=self.componentNames, - onPoints=self.onPoints, - logger=self.logger ): - self.logger.error( f"The filter { self.logger.name } failed." ) - return False - - # Log the output message. - self._logOutputMessage( validIndexes ) + # Log the output message. + self._logOutputMessage( validIndexes ) + except ( ValueError, AttributeError ) as e: + self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) + return False + except Exception as e: + mess: str = f"The filter { self.logger.name } failed.\n{ e }" + self.logger.critical( mess, exc_info=True ) + return False return True diff --git a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py index 689169d1..afbf0d0b 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py @@ -113,32 +113,31 @@ def applyFilter( self: Self ) -> bool: boolean (bool): True if calculation successfully ended, False otherwise. """ self.logger.info( f"Apply filter { self.logger.name }." ) - - onPoints: Union[ None, bool ] - onBoth: bool - for attributeName in self.dictAttributesValues: - onPoints, onBoth = getAttributePieceInfo( self.multiBlockDataSet, attributeName ) - if onPoints is None: - self.logger.error( f"{ attributeName } is not in the mesh." ) - self.logger.error( f"The attribute { attributeName } has not been filled." ) - self.logger.error( f"The filter { self.logger.name } failed." ) - return False - - if onBoth: - self.logger.error( f"There is two attribute named { attributeName }," - " one on points and the other on cells. The attribute must be unique." ) - self.logger.error( f"The attribute { attributeName } has not been filled." ) - self.logger.error( f"The filter { self.logger.name } failed." ) - return False - - if not fillPartialAttributes( self.multiBlockDataSet, - attributeName, - onPoints=onPoints, - listValues=self.dictAttributesValues[ attributeName ], - logger=self.logger ): - self.logger.error( f"The filter { self.logger.name } failed." ) - return False - - self.logger.info( f"The filter { self.logger.name } succeed." ) + try: + onPoints: Union[ None, bool ] + onBoth: bool + for attributeName in self.dictAttributesValues: + onPoints, onBoth = getAttributePieceInfo( self.multiBlockDataSet, attributeName ) + if onPoints is None: + raise ValueError( f"{ attributeName } is not in the mesh." ) + + if onBoth: + raise ValueError( f"There is two attribute named { attributeName }, one on points and the other on cells. The attribute name must be unique." ) + + if not fillPartialAttributes( self.multiBlockDataSet, + attributeName, + onPoints=onPoints, + listValues=self.dictAttributesValues[ attributeName ], + logger=self.logger ): + raise + + self.logger.info( f"The filter { self.logger.name } succeed." ) + except ( ValueError, AttributeError ) as e: + self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) + return False + except Exception as e: + mess: str = f"The filter { self.logger.name } failed.\n{ e }" + self.logger.critical( mess, exc_info=True ) + return False return True diff --git a/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py b/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py index 1c6f6b18..4630d642 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py @@ -8,6 +8,7 @@ from geos.utils.Logger import ( Logger, getLogger ) from geos.mesh.utils.multiblockModifiers import mergeBlocks +from geos.utils.Errors import VTKError from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet, vtkUnstructuredGrid @@ -31,7 +32,6 @@ from geos.processing.generic_processing_tools.MergeBlockEnhanced import MergeBlockEnhanced import logging - from geos.utils.Errors import VTKError # Define filter inputs multiblockdataset: vtkMultiblockDataSet @@ -45,10 +45,7 @@ mergeBlockEnhancedFilter.setLoggerHandler( yourHandler ) # Do calculations - try: - mergeBlockEnhancedFilter.applyFilter() - except VTKError: - logging.error("Something went wrong in VTK") + mergeBlockEnhancedFilter.applyFilter() # Get the merged mesh mergeBlockEnhancedFilter.getOutput() @@ -102,22 +99,29 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler' to True" " during the filter initialization." ) - def applyFilter( self: Self ) -> None: + def applyFilter( self: Self ) -> bool: """Merge the blocks of a multiblock dataset mesh. Returns: bool: True if the blocks were successfully merged, False otherwise. - - Raises: - VTKError (geos.utils.Errors) : error captured if any from the VTK log """ self.logger.info( f"Applying filter { self.logger.name }." ) - outputMesh: vtkUnstructuredGrid - outputMesh = mergeBlocks( self.inputMesh, keepPartialAttributes=True, logger=self.logger ) - self.outputMesh = outputMesh - - self.logger.info( f"The filter { self.logger.name } succeeded." ) + try: + outputMesh: vtkUnstructuredGrid + outputMesh = mergeBlocks( self.inputMesh, keepPartialAttributes=True, logger=self.logger ) + self.outputMesh = outputMesh + + self.logger.info( f"The filter { self.logger.name } succeeded." ) + except VTKError as e: + self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) + return False + except Exception as e: + mess: str = f"The filter { self.logger.name } failed.\n{ e }" + self.logger.critical( mess, exc_info=True ) + return False + + return True def getOutput( self: Self ) -> vtkUnstructuredGrid: """Get the merged mesh. diff --git a/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py b/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py index 0d5b89ef..2e7677a9 100644 --- a/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py +++ b/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py @@ -705,8 +705,12 @@ def __init__( self.logger.setLevel( logging.INFO ) self.logger.propagate = False - def applyFilter( self: Self ) -> None: - """Compute the geomechanics properties and create attributes on the mesh.""" + def applyFilter( self: Self ) -> bool: + """Compute the geomechanics properties and create attributes on the mesh. + + Returns: + bool: True if the filter succeeded, False otherwise. + """ self.logger.info( f"Apply filter { self.logger.name }." ) try: @@ -742,8 +746,13 @@ def applyFilter( self: Self ) -> None: self.logger.info( f"The filter { self.logger.name } succeeded." ) except ( ValueError, TypeError, NameError ) as e: self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) + return False + except Exception as e: + mess: str = f"The filter { self.logger.name } failed.\n{ e }" + self.logger.critical( mess, exc_info=True ) + return False - return + return True def getOutput( self: Self ) -> vtkUnstructuredGrid: """Get the mesh with the geomechanics properties computed as attributes. diff --git a/geos-processing/src/geos/processing/post_processing/GeosBlockExtractor.py b/geos-processing/src/geos/processing/post_processing/GeosBlockExtractor.py index 65050684..13d451e4 100644 --- a/geos-processing/src/geos/processing/post_processing/GeosBlockExtractor.py +++ b/geos-processing/src/geos/processing/post_processing/GeosBlockExtractor.py @@ -196,8 +196,12 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: "The logger already has an handler, to use yours set the argument 'speHandler' to True during the filter initialization." ) - def applyFilter( self: Self ) -> None: - """Extract the volume, the fault or the well domain of the mesh from GEOS.""" + def applyFilter( self: Self ) -> bool: + """Extract the volume, the fault or the well domain of the mesh from GEOS. + + Returns: + bool: True if the filter succeeded, False otherwise. + """ self.logger.info( f"Apply filter { self.logger.name }." ) try: @@ -217,5 +221,10 @@ def applyFilter( self: Self ) -> None: except ( ValueError, TypeError ) as e: self.logger.error( f"The filter { self.logger.name } failed.\n{ e }." ) + return False + except Exception as e: + mess: str = f"The filter { self.logger.name } failed.\n{ e }" + self.logger.critical( mess, exc_info=True ) + return False - return + return True diff --git a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py index f604d049..f581ee20 100644 --- a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py +++ b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py @@ -125,8 +125,12 @@ def getOutput( self: Self ) -> vtkMultiBlockDataSet: """Get the mesh with the composite blocks merged.""" return self.outputMesh - def applyFilter( self: Self ) -> None: - """Apply the filter on the mesh.""" + def applyFilter( self: Self ) -> bool: + """Apply the filter on the mesh. + + Returns: + bool: True if the filter succeeded, False otherwise. + """ self.logger.info( f"Apply filter { self.logger.name }." ) try: @@ -177,8 +181,13 @@ def applyFilter( self: Self ) -> None: self.logger.info( f"The filter { self.logger.name } succeeded." ) except ( ValueError, TypeError, RuntimeError, AssertionError, VTKError ) as e: self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) + return False + except Exception as e: + mess: str = f"The filter { self.logger.name } failed.\n{ e }" + self.logger.critical( mess, exc_info=True ) + return False - return + return True def renameAttributes( self: Self, diff --git a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py index 1ef8e591..e85826d5 100644 --- a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py +++ b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py @@ -235,36 +235,34 @@ def applyFilter( self: Self ) -> bool: self.logger.info( msg ) - self.outputMesh = vtkPolyData() - self.outputMesh.ShallowCopy( self.inputMesh ) - - # Conversion of attributes from Normal/Tangent basis to xyz basis - if self.convertAttributesOn: - self.logger.info( "Conversion of attributes from local to XYZ basis." ) - if not self.convertAttributesFromLocalToXYZBasis(): - self.logger.error( "Error while converting attributes from local to XYZ basis." ) - return False - - # Compute shear capacity utilization - if not self.computeShearCapacityUtilization(): - self.logger.error( "Error while computing SCU." ) + try: + self.outputMesh = vtkPolyData() + self.outputMesh.ShallowCopy( self.inputMesh ) + + # Conversion of attributes from Normal/Tangent basis to xyz basis + if self.convertAttributesOn: + self.logger.info( "Conversion of attributes from local to XYZ basis." ) + self.convertAttributesFromLocalToXYZBasis() + + # Compute shear capacity utilization + self.computeShearCapacityUtilization() + + self.logger.info( f"Filter {self.logger.name} successfully applied on surface {self.name}." ) + except Exception as e: + mess: str = f"The filter { self.logger.name } failed.\n{ e }" + self.logger.critical( mess, exc_info=True ) return False - self.logger.info( f"Filter {self.logger.name} successfully applied on surface {self.name}." ) return True - def convertAttributesFromLocalToXYZBasis( self: Self ) -> bool: - """Convert attributes from local to XYZ basis. - - Returns: - bool: True if calculation successfully ended or no attributes, False otherwise. - """ + def convertAttributesFromLocalToXYZBasis( self: Self ) -> None: + """Convert attributes from local to XYZ basis.""" # Get the list of attributes to convert and filter attributesToConvert: set[ str ] = self.__filterAttributesToConvert() if len( attributesToConvert ) == 0: self.logger.warning( "No attribute to convert from local to XYZ basis were found." ) - return True + return # Do conversion from local to XYZ for each attribute for attrNameLocal in attributesToConvert: @@ -275,8 +273,7 @@ def convertAttributesFromLocalToXYZBasis( self: Self ) -> bool: continue if self.attributeOnPoints: - self.logger.error( - "This filter can only convert cell attributes from local to XYZ basis, not point attributes." ) + raise ValueError( "This filter can only convert cell attributes from local to XYZ basis, not point attributes." ) localArray: npt.NDArray[ np.float64 ] = getArrayInObject( self.outputMesh, attrNameLocal, self.attributeOnPoints ) @@ -291,8 +288,10 @@ def convertAttributesFromLocalToXYZBasis( self: Self ) -> bool: logger=self.logger ): self.logger.info( f"Attribute {attrNameXYZ} added to the output mesh." ) self.newAttributeNames.add( attrNameXYZ ) + else: + raise - return True + return def __filterAttributesToConvert( self: Self ) -> set[ str ]: """Filter the set of attribute names if they are vectorial and present. @@ -355,12 +354,8 @@ def __computeXYZCoordinates( return attrXYZ - def computeShearCapacityUtilization( self: Self ) -> bool: - """Compute the shear capacity utilization (SCU) on surface. - - Returns: - bool: True if calculation successfully ended, False otherwise. - """ + def computeShearCapacityUtilization( self: Self ) -> None: + """Compute the shear capacity utilization (SCU) on surface.""" SCUAttributeName: str = PostProcessingOutputsEnum.SCU.attributeName if not isAttributeInObject( self.outputMesh, SCUAttributeName, self.attributeOnPoints ): @@ -376,18 +371,18 @@ def computeShearCapacityUtilization( self: Self ) -> bool: traction, self.rockCohesion, self.frictionAngle ) except AssertionError: self.logger.error( f"Failed to compute {SCUAttributeName}." ) - return False + raise # Create attribute if not createAttribute( self.outputMesh, scuAttribute, SCUAttributeName, (), self.attributeOnPoints, logger=self.logger ): self.logger.error( f"Failed to create attribute {SCUAttributeName}." ) - return False + raise else: self.logger.info( "SCU computed and added to the output mesh." ) self.newAttributeNames.add( SCUAttributeName ) - return True + return def GetOutputMesh( self: Self ) -> vtkPolyData: """Get the output mesh with computed attributes. diff --git a/geos-processing/tests/test_CellTypeCounterEnhanced.py b/geos-processing/tests/test_CellTypeCounterEnhanced.py index 7883592c..7783ce70 100644 --- a/geos-processing/tests/test_CellTypeCounterEnhanced.py +++ b/geos-processing/tests/test_CellTypeCounterEnhanced.py @@ -60,7 +60,7 @@ def test_CellTypeCounterEnhanced_single( test_case: TestCase ) -> None: test_case (TestCase): Test case """ cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( test_case.mesh ) - cellTypeCounterEnhancedFilter.applyFilter() + assert cellTypeCounterEnhancedFilter.applyFilter() countsObs: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() assert countsObs is not None, "CellTypeCounts is undefined" @@ -118,7 +118,7 @@ def test_CellTypeCounterEnhanced_multi( test_case: TestCase ) -> None: test_case (TestCase): Test case """ cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( test_case.mesh ) - cellTypeCounterEnhancedFilter.applyFilter() + assert cellTypeCounterEnhancedFilter.applyFilter() countsObs: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() assert countsObs is not None, "CellTypeCounts is undefined" diff --git a/geos-processing/tests/test_GeosExtractBlock.py b/geos-processing/tests/test_GeosExtractBlock.py index 2f47b1b2..bfdecf63 100644 --- a/geos-processing/tests/test_GeosExtractBlock.py +++ b/geos-processing/tests/test_GeosExtractBlock.py @@ -26,7 +26,7 @@ def test_GeosExtractBlock( multiBlockDataSet: vtkMultiBlockDataSet = dataSetTest( "meshGeosExtractBlockTmp" ) geosBlockExtractor: GeosBlockExtractor = GeosBlockExtractor( multiBlockDataSet, extractFault, extractWell ) - geosBlockExtractor.applyFilter() + assert geosBlockExtractor.applyFilter() extractedVolume: vtkMultiBlockDataSet = geosBlockExtractor.extractedGeosDomain.volume extractedFault: vtkMultiBlockDataSet = geosBlockExtractor.extractedGeosDomain.fault diff --git a/geos-processing/tests/test_MergeBlocksEnhanced.py b/geos-processing/tests/test_MergeBlocksEnhanced.py index 677a99d9..fee349f0 100644 --- a/geos-processing/tests/test_MergeBlocksEnhanced.py +++ b/geos-processing/tests/test_MergeBlocksEnhanced.py @@ -7,26 +7,13 @@ from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet from geos.processing.generic_processing_tools.MergeBlockEnhanced import MergeBlockEnhanced -from unittest import TestCase -from geos.utils.Errors import VTKError - -import vtk -from packaging.version import Version def test_MergeBlocksEnhancedFilter( dataSetTest: vtkMultiBlockDataSet, ) -> None: """Test MergeBlockEnhanced vtk filter.""" multiBlockDataset: vtkMultiBlockDataSet = dataSetTest( "multiblockGeosOutput" ) mergeBlockEnhancedFilter: MergeBlockEnhanced = MergeBlockEnhanced( multiBlockDataset ) - mergeBlockEnhancedFilter.applyFilter() - - -class RaiseMergeBlocksEnhanced( TestCase ): - """Test failure on empty multiBlockDataSet.""" + assert mergeBlockEnhancedFilter.applyFilter() - def test_TypeError( self ) -> None: - """Test raise of TypeError.""" - multiBlockDataset = vtkMultiBlockDataSet() # should fail on empty data - mergeBlockEnhancedFilter: MergeBlockEnhanced = MergeBlockEnhanced( multiBlockDataset ) - if Version( vtk.__version__ ) < Version( "9.5" ): - self.assertRaises( VTKError, mergeBlockEnhancedFilter.applyFilter ) + failedMergeBlockEnhancedFilter: MergeBlockEnhanced = MergeBlockEnhanced( vtkMultiBlockDataSet() ) + assert not failedMergeBlockEnhancedFilter.applyFilter() diff --git a/geos-processing/tests/test_MeshQualityEnhanced.py b/geos-processing/tests/test_MeshQualityEnhanced.py index 730ec173..2938c736 100644 --- a/geos-processing/tests/test_MeshQualityEnhanced.py +++ b/geos-processing/tests/test_MeshQualityEnhanced.py @@ -142,11 +142,10 @@ def test_MeshQualityEnhanced( test_case: TestCase ) -> None: meshQualityEnhancedFilter.SetWedgeMetrics( test_case.qualityMetrics ) elif test_case.cellType == VTK_HEXAHEDRON: meshQualityEnhancedFilter.SetHexaMetrics( test_case.qualityMetrics ) - meshQualityEnhancedFilter.applyFilter() + assert meshQualityEnhancedFilter.applyFilter() # test method getComputedMetricsFromCellType for i, cellType in enumerate( getAllCellTypesExtended() ): - print( cellType ) metrics: Optional[ set[ int ] ] = meshQualityEnhancedFilter.getComputedMetricsFromCellType( cellType ) if test_case.cellTypeCounts[ i ] > 0: assert metrics is not None, f"Metrics from {vtkCellTypes.GetClassNameFromTypeId(cellType)} cells is undefined." diff --git a/geos-processing/tests/test_SplitMesh.py b/geos-processing/tests/test_SplitMesh.py index 7fc8e607..99f06572 100644 --- a/geos-processing/tests/test_SplitMesh.py +++ b/geos-processing/tests/test_SplitMesh.py @@ -143,7 +143,7 @@ def test_single_cell_split( test_case: TestCase ) -> None: """ cellTypeName: str = vtkCellTypes.GetClassNameFromTypeId( test_case.cellType ) splitMeshFilter: SplitMesh = SplitMesh( test_case.mesh ) - splitMeshFilter.applyFilter() + assert splitMeshFilter.applyFilter() output: vtkUnstructuredGrid = splitMeshFilter.getOutput() assert output is not None, "Output mesh is undefined." pointsOut: vtkPoints = output.GetPoints() @@ -234,7 +234,7 @@ def test_multi_cells_mesh_split() -> None: # Apply the split filter splitMeshFilter = SplitMesh( input_mesh ) - splitMeshFilter.applyFilter() + assert splitMeshFilter.applyFilter() output: vtkUnstructuredGrid = splitMeshFilter.getOutput() assert output is not None, "Output mesh should be defined" @@ -296,7 +296,7 @@ def test_multi_polygon_mesh_split() -> None: # Apply the split filter splitMeshFilter = SplitMesh( input_mesh ) - splitMeshFilter.applyFilter() + assert splitMeshFilter.applyFilter() output: vtkUnstructuredGrid = splitMeshFilter.getOutput() assert output is not None, "Output mesh should be defined" diff --git a/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py b/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py index deea93cc..d6b4fa4d 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py +++ b/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py @@ -249,8 +249,8 @@ def ApplyFilter( geomechanicsCalculatorFilter.physicalConstants.rockCohesion = self.rockCohesion geomechanicsCalculatorFilter.physicalConstants.frictionAngle = self.frictionAngle - geomechanicsCalculatorFilter.applyFilter() - outputMesh.ShallowCopy( geomechanicsCalculatorFilter.getOutput() ) + if geomechanicsCalculatorFilter.applyFilter(): + outputMesh.ShallowCopy( geomechanicsCalculatorFilter.getOutput() ) elif isinstance( outputMesh, vtkMultiBlockDataSet ): volumeBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( outputMesh ) for blockIndex in volumeBlockIndexes: @@ -274,9 +274,9 @@ def ApplyFilter( geomechanicsCalculatorFilter.physicalConstants.rockCohesion = self.rockCohesion geomechanicsCalculatorFilter.physicalConstants.frictionAngle = self.frictionAngle - geomechanicsCalculatorFilter.applyFilter() - volumeBlock.ShallowCopy( geomechanicsCalculatorFilter.getOutput() ) - volumeBlock.Modified() + if geomechanicsCalculatorFilter.applyFilter(): + volumeBlock.ShallowCopy( geomechanicsCalculatorFilter.getOutput() ) + volumeBlock.Modified() outputMesh.Modified() diff --git a/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py index c5dcd111..ea96de82 100644 --- a/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py @@ -116,12 +116,8 @@ def RequestData( if len( mergeBlockEnhancedFilter.logger.handlers ) == 0: mergeBlockEnhancedFilter.setLoggerHandler( VTKHandler() ) - try: - mergeBlockEnhancedFilter.applyFilter() + if mergeBlockEnhancedFilter.applyFilter(): outputMesh.ShallowCopy( mergeBlockEnhancedFilter.getOutput() ) outputMesh.Modified() - except ( ValueError, TypeError, RuntimeError ) as e: - mergeBlockEnhancedFilter.logger.error( f"MergeBlock failed due to {e}", exc_info=True ) - return 0 return 1 diff --git a/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py b/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py index dcdacf52..792cbde0 100644 --- a/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py +++ b/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py @@ -126,16 +126,16 @@ def ApplyFilter( self: Self, inputMesh: vtkMultiBlockDataSet, outputMesh: vtkMul sgFilter.SetRockCohesion( self._getRockCohesion() ) sgFilter.SetFrictionAngle( self._getFrictionAngle() ) - sgFilter.applyFilter() + if sgFilter.applyFilter(): - outputSurface: vtkPolyData = sgFilter.GetOutputMesh() + outputSurface: vtkPolyData = sgFilter.GetOutputMesh() - # add attributes to output surface mesh - for attributeName in sgFilter.GetNewAttributeNames(): - attr: vtkDataArray = outputSurface.GetCellData().GetArray( attributeName ) - surfaceBlock.GetCellData().AddArray( attr ) - surfaceBlock.GetCellData().Modified() - surfaceBlock.Modified() + # add attributes to output surface mesh + for attributeName in sgFilter.GetNewAttributeNames(): + attr: vtkDataArray = outputSurface.GetCellData().GetArray( attributeName ) + surfaceBlock.GetCellData().AddArray( attr ) + surfaceBlock.GetCellData().Modified() + surfaceBlock.Modified() outputMesh.Modified() return diff --git a/geos-pv/src/geos/pv/utils/workflowFunctions.py b/geos-pv/src/geos/pv/utils/workflowFunctions.py index 28602fce..b4fd87aa 100644 --- a/geos-pv/src/geos/pv/utils/workflowFunctions.py +++ b/geos-pv/src/geos/pv/utils/workflowFunctions.py @@ -36,8 +36,8 @@ def doExtractAndMerge( speHandler=True ) if not blockExtractor.logger.hasHandlers(): blockExtractor.setLoggerHandler( VTKHandler() ) - blockExtractor.applyFilter() - + if not blockExtractor.applyFilter(): + raise # recover output objects from GeosBlockExtractor filter and merge internal blocks volumeBlockExtracted: vtkMultiBlockDataSet = blockExtractor.extractedGeosDomain.volume outputCells.ShallowCopy( mergeBlocksFilter( volumeBlockExtracted, False, "Volume" ) ) @@ -77,7 +77,8 @@ def mergeBlocksFilter( mergeBlockFilter: GeosBlockMerge = GeosBlockMerge( mesh, convertSurfaces, True, loggerName ) if not mergeBlockFilter.logger.hasHandlers(): mergeBlockFilter.setLoggerHandler( VTKHandler() ) - mergeBlockFilter.applyFilter() + if not mergeBlockFilter.applyFilter(): + raise mergedBlocks: vtkMultiBlockDataSet = vtkMultiBlockDataSet() mergedBlocks.ShallowCopy( mergeBlockFilter.getOutput() ) return mergedBlocks From c6c1c9d8c2c447f634f1769b93baa6dc1678993b Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 21 Nov 2025 19:13:00 +0100 Subject: [PATCH 21/39] fix ruff and yapf --- geos-mesh/tests/test_multiblockModifiers.py | 3 +- .../AttributeMapping.py | 15 ++++++-- .../CreateConstantAttributePerRegion.py | 38 +++++++++++-------- .../FillPartialArrays.py | 12 +++--- .../post_processing/SurfaceGeomechanics.py | 5 ++- geos-processing/tests/test_SplitMesh.py | 4 +- .../src/geos/pv/plugins/PVMohrCirclePlot.py | 2 +- .../geos/pv/plugins/PVSurfaceGeomechanics.py | 2 +- 8 files changed, 49 insertions(+), 32 deletions(-) diff --git a/geos-mesh/tests/test_multiblockModifiers.py b/geos-mesh/tests/test_multiblockModifiers.py index 34b6d700..4ae3a0e5 100644 --- a/geos-mesh/tests/test_multiblockModifiers.py +++ b/geos-mesh/tests/test_multiblockModifiers.py @@ -42,6 +42,7 @@ def test_mergeBlocks( assert dataset.GetFieldData().GetNumberOfArrays( ) == nbFieldAttributes, f"Expected {nbFieldAttributes} field attributes after the merge, not {dataset.GetFieldData().GetNumberOfArrays()}." + class RaiseMergeBlocks( TestCase ): """Test failure on empty multiBlockDataSet.""" @@ -50,4 +51,4 @@ def test_TypeError( self ) -> None: multiBlockDataset = vtkMultiBlockDataSet() # should fail on empty data if Version( vtk.__version__ ) < Version( "9.5" ): with pytest.raises( VTKError ): - multiblockModifiers.mergeBlocks( multiBlockDataset, True ) \ No newline at end of file + multiblockModifiers.mergeBlocks( multiBlockDataset, True ) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py index 9b3ce073..ba7e5852 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -147,12 +147,15 @@ def applyFilter( self: Self ) -> bool: attributesInMeshFrom: set[ str ] = getAttributeSet( self.meshFrom, self.onPoints ) wrongAttributeNames: set[ str ] = self.attributeNames.difference( attributesInMeshFrom ) if len( wrongAttributeNames ) > 0: - raise AttributeError( f"The { self.piece } attributes { wrongAttributeNames } are not present in the source mesh." ) + raise AttributeError( + f"The { self.piece } attributes { wrongAttributeNames } are not present in the source mesh." ) attributesInMeshTo: set[ str ] = getAttributeSet( self.meshTo, self.onPoints ) attributesAlreadyInMeshTo: set[ str ] = self.attributeNames.intersection( attributesInMeshTo ) if len( attributesAlreadyInMeshTo ) > 0: - raise AttributeError( f"The { self.piece } attributes { attributesAlreadyInMeshTo } are already present in the final mesh." ) + raise AttributeError( + f"The { self.piece } attributes { attributesAlreadyInMeshTo } are already present in the final mesh." + ) if isinstance( self.meshFrom, vtkMultiBlockDataSet ): partialAttributes: list[ str ] = [] @@ -161,7 +164,9 @@ def applyFilter( self: Self ) -> bool: partialAttributes.append( attributeName ) if len( partialAttributes ) > 0: - raise AttributeError( f"All { self.piece } attributes to transfer must be global, { partialAttributes } are partials." ) + raise AttributeError( + f"All { self.piece } attributes to transfer must be global, { partialAttributes } are partials." + ) self.ElementMap = computeElementMapping( self.meshFrom, self.meshTo, self.onPoints ) sharedElement: bool = False @@ -193,4 +198,6 @@ def applyFilter( self: Self ) -> bool: def _logOutputMessage( self: Self ) -> None: """Create and log result messages of the filter.""" self.logger.info( f"The filter { self.logger.name } succeeded." ) - self.logger.info( f"The { self.piece } attributes { self.attributeNames } have been transferred from the source mesh to the final mesh with the { self.piece } mapping." ) + self.logger.info( + f"The { self.piece } attributes { self.attributeNames } have been transferred from the source mesh to the final mesh with the { self.piece } mapping." + ) 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 7e2c2960..c5fc2c38 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -166,7 +166,9 @@ def applyFilter( self: Self ) -> bool: raise AttributeError( f"{ self.regionName } is not in the mesh." ) if self.onBoth: - raise ValueError( f"There are two attributes named { self.regionName }, one on points and the other on cells. The region attribute must be unique." ) + raise ValueError( + f"There are two attributes named { self.regionName }, one on points and the other on cells. The region attribute must be unique." + ) nbComponentsRegion: int = getNumberOfComponents( self.mesh, self.regionName, self.onPoints ) if nbComponentsRegion != 1: @@ -176,7 +178,9 @@ def applyFilter( self: Self ) -> bool: # Check if the number of components and number of values for the region indexes are coherent. for index in self.dictRegionValues: if len( self.dictRegionValues[ index ] ) != self.nbComponents: - raise ValueError( f"The number of value given for the region index { index } is not correct. You must set a value for each component, in this case { self.nbComponents }." ) + raise ValueError( + f"The number of value given for the region index { index } is not correct. You must set a value for each component, in this case { self.nbComponents }." + ) listIndexes: list[ Any ] = list( self.dictRegionValues.keys() ) validIndexes: list[ Any ] = [] @@ -189,7 +193,7 @@ def applyFilter( self: Self ) -> bool: raise AttributeError( f"The region attribute { self.regionName } has to be global." ) validIndexes, invalidIndexes = checkValidValuesInMultiBlock( self.mesh, self.regionName, listIndexes, - self.onPoints ) + self.onPoints ) if len( validIndexes ) == 0: if len( self.dictRegionValues ) == 0: self.logger.warning( "No region indexes entered." ) @@ -198,17 +202,18 @@ def applyFilter( self: Self ) -> bool: f"The region indexes entered are not in the region attribute { self.regionName }." ) if not createConstantAttributeMultiBlock( self.mesh, - self.defaultValue, - self.newAttributeName, - componentNames=self.componentNames, - onPoints=self.onPoints, - logger=self.logger ): + self.defaultValue, + self.newAttributeName, + componentNames=self.componentNames, + onPoints=self.onPoints, + logger=self.logger ): raise else: if len( invalidIndexes ) > 0: self.logger.warning( - f"The region indexes { invalidIndexes } are not in the region attribute { self.regionName }." ) + f"The region indexes { invalidIndexes } are not in the region attribute { self.regionName }." + ) # Parse the mesh to add the attribute on each dataset. listFlatIdDataSet: list[ int ] = getBlockElementIndexesFlatten( self.mesh ) @@ -227,7 +232,7 @@ def applyFilter( self: Self ) -> bool: else: validIndexes, invalidIndexes = checkValidValuesInDataSet( self.mesh, self.regionName, listIndexes, - self.onPoints ) + self.onPoints ) if len( validIndexes ) == 0: if len( self.dictRegionValues ) == 0: self.logger.warning( "No region indexes entered." ) @@ -236,17 +241,18 @@ def applyFilter( self: Self ) -> bool: f"The region indexes entered are not in the region attribute { self.regionName }." ) if not createConstantAttributeDataSet( self.mesh, - self.defaultValue, - self.newAttributeName, - componentNames=self.componentNames, - onPoints=self.onPoints, - logger=self.logger ): + self.defaultValue, + self.newAttributeName, + componentNames=self.componentNames, + onPoints=self.onPoints, + logger=self.logger ): raise else: if len( invalidIndexes ) > 0: self.logger.warning( - f"The region indexes { invalidIndexes } are not in the region attribute { self.regionName }." ) + f"The region indexes { invalidIndexes } are not in the region attribute { self.regionName }." + ) regionArray = getArrayInObject( self.mesh, self.regionName, self.onPoints ) newArray = self._createArrayFromRegionArrayWithValueMap( regionArray ) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py index afbf0d0b..22d7275a 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py @@ -122,13 +122,15 @@ def applyFilter( self: Self ) -> bool: raise ValueError( f"{ attributeName } is not in the mesh." ) if onBoth: - raise ValueError( f"There is two attribute named { attributeName }, one on points and the other on cells. The attribute name must be unique." ) + raise ValueError( + f"There is two attribute named { attributeName }, one on points and the other on cells. The attribute name must be unique." + ) if not fillPartialAttributes( self.multiBlockDataSet, - attributeName, - onPoints=onPoints, - listValues=self.dictAttributesValues[ attributeName ], - logger=self.logger ): + attributeName, + onPoints=onPoints, + listValues=self.dictAttributesValues[ attributeName ], + logger=self.logger ): raise self.logger.info( f"The filter { self.logger.name } succeed." ) diff --git a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py index e85826d5..ed0a5a62 100644 --- a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py +++ b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py @@ -172,7 +172,7 @@ def ConvertAttributesOn( self: Self ) -> None: self.convertAttributesOn = True def ConvertAttributesOff( self: Self ) -> None: - """Deactivate the conversion of attributes from local to XYZ bais.""" + """Deactivate the conversion of attributes from local to XYZ basis.""" self.convertAttributesOn = False def GetConvertAttributes( self: Self ) -> bool: @@ -273,7 +273,8 @@ def convertAttributesFromLocalToXYZBasis( self: Self ) -> None: continue if self.attributeOnPoints: - raise ValueError( "This filter can only convert cell attributes from local to XYZ basis, not point attributes." ) + raise ValueError( + "This filter can only convert cell attributes from local to XYZ basis, not point attributes." ) localArray: npt.NDArray[ np.float64 ] = getArrayInObject( self.outputMesh, attrNameLocal, self.attributeOnPoints ) diff --git a/geos-processing/tests/test_SplitMesh.py b/geos-processing/tests/test_SplitMesh.py index 99f06572..fb175b30 100644 --- a/geos-processing/tests/test_SplitMesh.py +++ b/geos-processing/tests/test_SplitMesh.py @@ -201,8 +201,8 @@ def test_single_cell_split( test_case: TestCase ) -> None: cellDataInput: vtkCellData = test_case.mesh.GetCellData() assert cellDataInput is not None, "Cell data from input mesh should be defined." nbArrayInput: int = cellDataInput.GetNumberOfArrays() - nbArraySplited: int = cellData.GetNumberOfArrays() - assert nbArraySplited == nbArrayInput + 1, f"Number of arrays should be {nbArrayInput + 1}" + nbArraySplitted: int = cellData.GetNumberOfArrays() + assert nbArraySplitted == nbArrayInput + 1, f"Number of arrays should be {nbArrayInput + 1}" def test_multi_cells_mesh_split() -> None: diff --git a/geos-pv/src/geos/pv/plugins/PVMohrCirclePlot.py b/geos-pv/src/geos/pv/plugins/PVMohrCirclePlot.py index 8f2ba62c..54871bbb 100644 --- a/geos-pv/src/geos/pv/plugins/PVMohrCirclePlot.py +++ b/geos-pv/src/geos/pv/plugins/PVMohrCirclePlot.py @@ -287,7 +287,7 @@ def b05SetStressCompressionConvention( self: Self, useGeosConvention: bool ) -> @smproperty.intvector( name="AnnotateCircles", label="Annotate Circles", default_values=1 ) @smdomain.xml( """""" ) def b06SetAnnotateCircles( self: Self, boolean: bool ) -> None: - """Set option to add annotatations to circles. + """Set option to add annotations to circles. Args: boolean (bool): True to annotate circles, False otherwise. diff --git a/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py b/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py index 792cbde0..0fc204a7 100644 --- a/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py +++ b/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py @@ -40,7 +40,7 @@ To use it: * Load the module in Paraview: Tools>Manage Plugins...>Load new>PVSurfaceGeomechanics. -* Select any pipeline child of the second ouput from +* Select any pipeline child of the second output from GeosExtractMergeBlocksVolumeSurface* filter. * Select Filters > 3- Geos Geomechanics > Geos Surface Geomechanics. * (Optional) Set rock cohesion and/or friction angle. From 64c5c219420e3929691721f4e6d391b7d7c2bf83 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 24 Nov 2025 11:40:49 +0100 Subject: [PATCH 22/39] Fix test --- geos-processing/tests/test_MergeBlocksEnhanced.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/geos-processing/tests/test_MergeBlocksEnhanced.py b/geos-processing/tests/test_MergeBlocksEnhanced.py index fee349f0..8662d0ab 100644 --- a/geos-processing/tests/test_MergeBlocksEnhanced.py +++ b/geos-processing/tests/test_MergeBlocksEnhanced.py @@ -15,5 +15,6 @@ def test_MergeBlocksEnhancedFilter( dataSetTest: vtkMultiBlockDataSet, ) -> None mergeBlockEnhancedFilter: MergeBlockEnhanced = MergeBlockEnhanced( multiBlockDataset ) assert mergeBlockEnhancedFilter.applyFilter() - failedMergeBlockEnhancedFilter: MergeBlockEnhanced = MergeBlockEnhanced( vtkMultiBlockDataSet() ) + vtkMultiBlockDataSetFail: vtkMultiBlockDataSet = vtkMultiBlockDataSet() + failedMergeBlockEnhancedFilter: MergeBlockEnhanced = MergeBlockEnhanced( vtkMultiBlockDataSetFail ) assert not failedMergeBlockEnhancedFilter.applyFilter() From 4eaef30a027c102440cdcfdfe043613584c3ddda Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 24 Nov 2025 11:48:33 +0100 Subject: [PATCH 23/39] fix test --- geos-processing/tests/test_MergeBlocksEnhanced.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/geos-processing/tests/test_MergeBlocksEnhanced.py b/geos-processing/tests/test_MergeBlocksEnhanced.py index 8662d0ab..ce56b5c3 100644 --- a/geos-processing/tests/test_MergeBlocksEnhanced.py +++ b/geos-processing/tests/test_MergeBlocksEnhanced.py @@ -7,6 +7,8 @@ from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet from geos.processing.generic_processing_tools.MergeBlockEnhanced import MergeBlockEnhanced +import vtk +from packaging.version import Version def test_MergeBlocksEnhancedFilter( dataSetTest: vtkMultiBlockDataSet, ) -> None: @@ -15,6 +17,6 @@ def test_MergeBlocksEnhancedFilter( dataSetTest: vtkMultiBlockDataSet, ) -> None mergeBlockEnhancedFilter: MergeBlockEnhanced = MergeBlockEnhanced( multiBlockDataset ) assert mergeBlockEnhancedFilter.applyFilter() - vtkMultiBlockDataSetFail: vtkMultiBlockDataSet = vtkMultiBlockDataSet() - failedMergeBlockEnhancedFilter: MergeBlockEnhanced = MergeBlockEnhanced( vtkMultiBlockDataSetFail ) - assert not failedMergeBlockEnhancedFilter.applyFilter() + if Version( vtk.__version__ ) < Version( "9.5" ): + failedMergeBlockEnhancedFilter: MergeBlockEnhanced = MergeBlockEnhanced( vtkMultiBlockDataSet() ) + assert not failedMergeBlockEnhancedFilter.applyFilter() From 7edc6d43e4dae371f4669b44eb9dda894957a8fc Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 24 Nov 2025 14:21:55 +0100 Subject: [PATCH 24/39] first commit refactor attributeMapping --- .../AttributeMapping.py | 96 +++++++++---------- .../tests/test_AttributeMapping.py | 30 +++++- .../src/geos/pv/plugins/PVAttributeMapping.py | 8 +- 3 files changed, 80 insertions(+), 54 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py index ba7e5852..9e3c64c1 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -32,7 +32,7 @@ To use the filter: .. code-block:: python - + import logging from geos.processing.generic_processing_tools.AttributeMapping import AttributeMapping # Filter inputs. @@ -57,7 +57,10 @@ attributeMappingFilter.setLoggerHandler( yourHandler ) # Do calculations. - attributeMappingFilter.applyFilter() + try: + attributeMappingFilter.applyFilter() + except( ValueError, AttributeError ) as e: + attributeMappingFilter.logger.error( f"The filter failed du to:\n{ e }" ) """ loggerTitle: str = "Attribute Mapping" @@ -130,70 +133,61 @@ def getElementMap( self: Self ) -> dict[ int, npt.NDArray[ np.int64 ] ]: """ return self.ElementMap - def applyFilter( self: Self ) -> bool: + def applyFilter( self: Self ) -> None: """Transfer global attributes from a source mesh to a final mesh. Mapping the piece of the attributes to transfer. - Returns: - boolean (bool): True if calculation successfully ended, False otherwise. + Raise: + ValueError: Errors with the input attributeNames or the input mesh. + AttributeError: Errors with the attribute of the mesh. """ self.logger.info( f"Apply filter { self.logger.name }." ) - try: - if len( self.attributeNames ) == 0: - raise ValueError( f"Please enter at least one { self.piece } attribute to transfer." ) + if len( self.attributeNames ) == 0: + raise ValueError( f"Please enter at least one { self.piece } attribute to transfer." ) - attributesInMeshFrom: set[ str ] = getAttributeSet( self.meshFrom, self.onPoints ) - wrongAttributeNames: set[ str ] = self.attributeNames.difference( attributesInMeshFrom ) - if len( wrongAttributeNames ) > 0: - raise AttributeError( - f"The { self.piece } attributes { wrongAttributeNames } are not present in the source mesh." ) + attributesInMeshFrom: set[ str ] = getAttributeSet( self.meshFrom, self.onPoints ) + wrongAttributeNames: set[ str ] = self.attributeNames.difference( attributesInMeshFrom ) + if len( wrongAttributeNames ) > 0: + raise AttributeError( + f"The { self.piece } attributes { wrongAttributeNames } are not present in the source mesh." ) + + attributesInMeshTo: set[ str ] = getAttributeSet( self.meshTo, self.onPoints ) + attributesAlreadyInMeshTo: set[ str ] = self.attributeNames.intersection( attributesInMeshTo ) + if len( attributesAlreadyInMeshTo ) > 0: + raise AttributeError( + f"The { self.piece } attributes { attributesAlreadyInMeshTo } are already present in the final mesh." ) - attributesInMeshTo: set[ str ] = getAttributeSet( self.meshTo, self.onPoints ) - attributesAlreadyInMeshTo: set[ str ] = self.attributeNames.intersection( attributesInMeshTo ) - if len( attributesAlreadyInMeshTo ) > 0: + if isinstance( self.meshFrom, vtkMultiBlockDataSet ): + partialAttributes: list[ str ] = [] + for attributeName in self.attributeNames: + if not isAttributeGlobal( self.meshFrom, attributeName, self.onPoints ): + partialAttributes.append( attributeName ) + + if len( partialAttributes ) > 0: raise AttributeError( - f"The { self.piece } attributes { attributesAlreadyInMeshTo } are already present in the final mesh." - ) + f"All { self.piece } attributes to transfer must be global, { partialAttributes } are partials." ) - if isinstance( self.meshFrom, vtkMultiBlockDataSet ): - partialAttributes: list[ str ] = [] - for attributeName in self.attributeNames: - if not isAttributeGlobal( self.meshFrom, attributeName, self.onPoints ): - partialAttributes.append( attributeName ) + self.ElementMap = computeElementMapping( self.meshFrom, self.meshTo, self.onPoints ) + sharedElement: bool = False + for key in self.ElementMap: + if np.any( self.ElementMap[ key ] > -1 ): + sharedElement = True - if len( partialAttributes ) > 0: - raise AttributeError( - f"All { self.piece } attributes to transfer must be global, { partialAttributes } are partials." - ) + if not sharedElement: + raise ValueError( f"The two meshes do not have any shared { self.piece }." ) - self.ElementMap = computeElementMapping( self.meshFrom, self.meshTo, self.onPoints ) - sharedElement: bool = False - for key in self.ElementMap: - if np.any( self.ElementMap[ key ] > -1 ): - sharedElement = True + for attributeName in self.attributeNames: + # TODO:: Modify arrayModifiers function to raise error. + if not transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, + self.onPoints, self.logger ): + raise ValueError( f"Fail to transfer the attribute { attributeName }." ) - if not sharedElement: - raise ValueError( f"The two meshes do not have any shared { self.piece }." ) + # Log the output message. + self._logOutputMessage() - for attributeName in self.attributeNames: - # TODO:: Modify arrayModifiers function to raise error. - if not transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, - self.onPoints, self.logger ): - raise - - # Log the output message. - self._logOutputMessage() - except ( TypeError, ValueError, AttributeError ) as e: - self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) - return False - except Exception as e: - mess: str = f"The filter { self.logger.name } failed.\n{ e }" - self.logger.critical( mess, exc_info=True ) - return False - - return True + return def _logOutputMessage( self: Self ) -> None: """Create and log result messages of the filter.""" diff --git a/geos-processing/tests/test_AttributeMapping.py b/geos-processing/tests/test_AttributeMapping.py index 5a421a27..00d0c8a7 100644 --- a/geos-processing/tests/test_AttributeMapping.py +++ b/geos-processing/tests/test_AttributeMapping.py @@ -30,4 +30,32 @@ def test_AttributeMapping( fillAllPartialAttributes( meshFrom ) attributeMappingFilter: AttributeMapping = AttributeMapping( meshFrom, meshTo, attributeNames, onPoints ) - assert attributeMappingFilter.applyFilter() + attributeMappingFilter.applyFilter() + + +@pytest.mark.parametrize( "meshFromName, meshToName, attributeNames, onPoints, error", [ + ( "dataset", "emptydataset", {}, False, "ValueError" ), + ( "dataset", "emptydataset", { "Fault" }, False, "AttributeError" ), + ( "dataset", "dataset", { "GLOBAL_IDS_CELLS" }, False, "AttributeError" ), + ( "multiblock", "emptymultiblock", { "FAULT" }, False, "AttributeError" ), + ( "dataset", "emptyFracture", { "FAULT" }, False, "ValueError" ), +] ) +def test_AttributeMappingRaises( + dataSetTest: Any, + meshFromName: str, + meshToName: str, + attributeNames: set[ str ], + onPoints: bool, + error: str, +) -> None: + """Test the fails of the filter.""" + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshFromName ) + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshToName ) + attributeMappingFilter: AttributeMapping = AttributeMapping( meshFrom, meshTo, attributeNames, onPoints ) + + if error == "AttributeError": + with pytest.raises( AttributeError ): + attributeMappingFilter.applyFilter() + elif error == "ValueError": + with pytest.raises( ValueError ): + attributeMappingFilter.applyFilter() diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index cb5af13f..084219c3 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -186,7 +186,11 @@ def RequestData( if len( attributeMappingFilter.logger.handlers ) == 0: attributeMappingFilter.setLoggerHandler( VTKHandler() ) - attributeMappingFilter.applyFilter() - self.clearAttributeNames = True + try: + attributeMappingFilter.applyFilter() + self.clearAttributeNames = True + except ( ValueError, AttributeError ) as e: + attributeMappingFilter.logger.error( + f"The filter { attributeMappingFilter.logger.name } failed du to:\n{ e }" ) return 1 From f72c2134a4cfb898a3f0145d9f588962875e0ea2 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 24 Nov 2025 15:34:17 +0100 Subject: [PATCH 25/39] Refactor CreateConstantAttributePerRegion --- .../AttributeMapping.py | 5 +- .../CreateConstantAttributePerRegion.py | 209 +++++++++--------- .../test_CreateConstantAttributePerRegion.py | 78 ++++--- .../PVCreateConstantAttributePerRegion.py | 9 +- 4 files changed, 160 insertions(+), 141 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py index 9e3c64c1..5b010e51 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -60,7 +60,10 @@ try: attributeMappingFilter.applyFilter() except( ValueError, AttributeError ) as e: - attributeMappingFilter.logger.error( f"The filter failed du to:\n{ e }" ) + attributeMappingFilter.logger.error( f"The filter { attributeMappingFilter.logger.name } failed du to: { e }" ) + except Exception as e: + mess: str = f"The filter { attributeMappingFilter.logger.name } failed du to: { e }" + attributeMappingFilter.logger.critical( mess, exc_info=True ) """ loggerTitle: str = "Attribute Mapping" diff --git a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py index c5fc2c38..1dc663f5 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -66,7 +66,13 @@ createConstantAttributePerRegionFilter.addLoggerHandler( yourHandler ) # Do calculations. - createConstantAttributePerRegionFilter.applyFilter() + try: + createConstantAttributePerRegionFilter.applyFilter() + except ( ValueError, AttributeError ) as e: + createConstantAttributePerRegionFilter.logger.error( f"The filter { createConstantAttributePerRegionFilter.logger.name } failed du to { e }" ) + except Exception as e: + mess: str = f"The filter { createConstantAttributePerRegionFilter.logger.name } failed du to: { e }" + createConstantAttributePerRegionFilter.logger.critical( mess, exc_info=True ) """ loggerTitle: str = "Create Constant Attribute Per Region" @@ -149,132 +155,127 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler' to True" " during the filter initialization." ) - def applyFilter( self: Self ) -> bool: + def applyFilter( self: Self ) -> None: """Create a constant attribute per region in the mesh. - Returns: - boolean (bool): True if calculation successfully ended, False otherwise. + Raise: + ValueError: Errors with the input value for the region index or errors during the creation of the new attribute. + AttributeError: Errors with the attribute of the mesh. """ self.logger.info( f"Apply filter { self.logger.name }." ) # Add the handler to count warnings messages. self.logger.addHandler( self.counter ) - try: - # Check the validity of the attribute region. - if self.onPoints is None: - raise AttributeError( f"{ self.regionName } is not in the mesh." ) + # Check the validity of the attribute region. + if self.onPoints is None: + raise AttributeError( f"The attribute { self.regionName } is not in the mesh." ) - if self.onBoth: - raise ValueError( - f"There are two attributes named { self.regionName }, one on points and the other on cells. The region attribute must be unique." - ) + if self.onBoth: + raise AttributeError( + f"There are two attributes named { self.regionName }, one on points and the other on cells. The region attribute must be unique." + ) - nbComponentsRegion: int = getNumberOfComponents( self.mesh, self.regionName, self.onPoints ) - if nbComponentsRegion != 1: - raise ValueError( f"The region attribute { self.regionName } has to many components, one is requires." ) + nbComponentsRegion: int = getNumberOfComponents( self.mesh, self.regionName, self.onPoints ) + if nbComponentsRegion != 1: + raise AttributeError( f"The region attribute { self.regionName } has to many components, one is requires." ) - self._setInfoRegion() - # Check if the number of components and number of values for the region indexes are coherent. - for index in self.dictRegionValues: - if len( self.dictRegionValues[ index ] ) != self.nbComponents: - raise ValueError( - f"The number of value given for the region index { index } is not correct. You must set a value for each component, in this case { self.nbComponents }." - ) - - listIndexes: list[ Any ] = list( self.dictRegionValues.keys() ) - validIndexes: list[ Any ] = [] - invalidIndexes: list[ Any ] = [] - 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.onPoints ): - raise AttributeError( f"The region attribute { self.regionName } has to be global." ) - - validIndexes, invalidIndexes = checkValidValuesInMultiBlock( self.mesh, self.regionName, listIndexes, - self.onPoints ) - if len( validIndexes ) == 0: - if len( self.dictRegionValues ) == 0: - self.logger.warning( "No region indexes entered." ) - else: - self.logger.warning( - f"The region indexes entered are not in the region attribute { self.regionName }." ) - - if not createConstantAttributeMultiBlock( self.mesh, - self.defaultValue, - self.newAttributeName, - componentNames=self.componentNames, - onPoints=self.onPoints, - logger=self.logger ): - raise + self._setInfoRegion() + # Check if the number of components and number of values for the region indexes are coherent. + for index in self.dictRegionValues: + if len( self.dictRegionValues[ index ] ) != self.nbComponents: + raise ValueError( + f"The number of value given for the region index { index } is not correct. You must set a value for each component, in this case { self.nbComponents }." + ) + listIndexes: list[ Any ] = list( self.dictRegionValues.keys() ) + validIndexes: list[ Any ] = [] + invalidIndexes: list[ Any ] = [] + 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.onPoints ): + raise AttributeError( f"The region attribute { self.regionName } has to be global." ) + + validIndexes, invalidIndexes = checkValidValuesInMultiBlock( self.mesh, self.regionName, listIndexes, + self.onPoints ) + if len( validIndexes ) == 0: + if len( self.dictRegionValues ) == 0: + self.logger.warning( "No region indexes entered." ) else: - if len( invalidIndexes ) > 0: - self.logger.warning( - f"The region indexes { invalidIndexes } are not in the region attribute { self.regionName }." - ) - - # Parse the mesh to add the attribute on each dataset. - listFlatIdDataSet: list[ int ] = getBlockElementIndexesFlatten( self.mesh ) - for flatIdDataSet in listFlatIdDataSet: - dataSet: vtkDataSet = vtkDataSet.SafeDownCast( self.mesh.GetDataSet( flatIdDataSet ) ) - - regionArray = getArrayInObject( dataSet, self.regionName, self.onPoints ) - newArray = self._createArrayFromRegionArrayWithValueMap( regionArray ) - if not createAttribute( dataSet, - newArray, - self.newAttributeName, - componentNames=self.componentNames, - onPoints=self.onPoints, - logger=self.logger ): - raise + self.logger.warning( + f"The region indexes entered are not in the region attribute { self.regionName }." ) + + if not createConstantAttributeMultiBlock( self.mesh, + self.defaultValue, + self.newAttributeName, + componentNames=self.componentNames, + onPoints=self.onPoints, + logger=self.logger ): + raise ValueError( + f"Something got wrong with the creation of the attribute { self.newAttributeName }." ) else: - validIndexes, invalidIndexes = checkValidValuesInDataSet( self.mesh, self.regionName, listIndexes, - self.onPoints ) - if len( validIndexes ) == 0: - if len( self.dictRegionValues ) == 0: - self.logger.warning( "No region indexes entered." ) - else: - self.logger.warning( - f"The region indexes entered are not in the region attribute { self.regionName }." ) - - if not createConstantAttributeDataSet( self.mesh, - self.defaultValue, - self.newAttributeName, - componentNames=self.componentNames, - onPoints=self.onPoints, - logger=self.logger ): - raise + if len( invalidIndexes ) > 0: + self.logger.warning( + f"The region indexes { invalidIndexes } are not in the region attribute { self.regionName }." ) - else: - if len( invalidIndexes ) > 0: - self.logger.warning( - f"The region indexes { invalidIndexes } are not in the region attribute { self.regionName }." - ) + # Parse the mesh to add the attribute on each dataset. + listFlatIdDataSet: list[ int ] = getBlockElementIndexesFlatten( self.mesh ) + for flatIdDataSet in listFlatIdDataSet: + dataSet: vtkDataSet = vtkDataSet.SafeDownCast( self.mesh.GetDataSet( flatIdDataSet ) ) - regionArray = getArrayInObject( self.mesh, self.regionName, self.onPoints ) + regionArray = getArrayInObject( dataSet, self.regionName, self.onPoints ) newArray = self._createArrayFromRegionArrayWithValueMap( regionArray ) - if not createAttribute( self.mesh, + if not createAttribute( dataSet, newArray, self.newAttributeName, componentNames=self.componentNames, onPoints=self.onPoints, logger=self.logger ): - raise - - # Log the output message. - self._logOutputMessage( validIndexes ) - except ( ValueError, AttributeError ) as e: - self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) - return False - except Exception as e: - mess: str = f"The filter { self.logger.name } failed.\n{ e }" - self.logger.critical( mess, exc_info=True ) - return False - - return True + raise ValueError( + f"Something got wrong with the creation of the attribute { self.newAttributeName }." ) + + else: + validIndexes, invalidIndexes = checkValidValuesInDataSet( self.mesh, self.regionName, listIndexes, + self.onPoints ) + if len( validIndexes ) == 0: + if len( self.dictRegionValues ) == 0: + self.logger.warning( "No region indexes entered." ) + else: + self.logger.warning( + f"The region indexes entered are not in the region attribute { self.regionName }." ) + + if not createConstantAttributeDataSet( self.mesh, + self.defaultValue, + self.newAttributeName, + componentNames=self.componentNames, + onPoints=self.onPoints, + logger=self.logger ): + raise ValueError( + f"Something got wrong with the creation of the attribute { self.newAttributeName }." ) + + else: + if len( invalidIndexes ) > 0: + self.logger.warning( + f"The region indexes { invalidIndexes } are not in the region attribute { self.regionName }." ) + + regionArray = getArrayInObject( self.mesh, self.regionName, self.onPoints ) + newArray = self._createArrayFromRegionArrayWithValueMap( regionArray ) + if not createAttribute( self.mesh, + newArray, + self.newAttributeName, + componentNames=self.componentNames, + onPoints=self.onPoints, + logger=self.logger ): + raise ValueError( + f"Something got wrong with the creation of the attribute { self.newAttributeName }." ) + + # Log the output message. + self._logOutputMessage( validIndexes ) + + return def _setInfoRegion( self: Self ) -> None: """Update self.dictRegionValues and set self.defaultValue. diff --git a/geos-processing/tests/test_CreateConstantAttributePerRegion.py b/geos-processing/tests/test_CreateConstantAttributePerRegion.py index 98896763..47e08f5e 100644 --- a/geos-processing/tests/test_CreateConstantAttributePerRegion.py +++ b/geos-processing/tests/test_CreateConstantAttributePerRegion.py @@ -13,85 +13,86 @@ @pytest.mark.parametrize( - "meshType, newAttributeName, regionName, dictRegionValues, componentNames, componentNamesTest, valueNpType, succeed", + "meshType, newAttributeName, regionName, dictRegionValues, componentNames, componentNamesTest, valueNpType, error", [ # Test the name of the new attribute (new on the mesh, one present on the other piece). ## For vtkDataSet. - ( "dataset", "newAttribute", "GLOBAL_IDS_POINTS", {}, (), (), np.float32, True ), - ( "dataset", "CellAttribute", "GLOBAL_IDS_POINTS", {}, (), (), np.float32, True ), + ( "dataset", "newAttribute", "GLOBAL_IDS_POINTS", {}, (), (), np.float32, "None" ), + ( "dataset", "CellAttribute", "GLOBAL_IDS_POINTS", {}, (), (), np.float32, "None" ), ## For vtkMultiBlockDataSet. - ( "multiblock", "newAttribute", "GLOBAL_IDS_POINTS", {}, (), (), np.float32, True ), - ( "multiblock", "CellAttribute", "GLOBAL_IDS_POINTS", {}, (), (), np.float32, True ), - ( "multiblock", "GLOBAL_IDS_CELLS", "GLOBAL_IDS_POINTS", {}, (), (), np.float32, True ), + ( "multiblock", "newAttribute", "GLOBAL_IDS_POINTS", {}, (), (), np.float32, "None" ), + ( "multiblock", "CellAttribute", "GLOBAL_IDS_POINTS", {}, (), (), np.float32, "None" ), + ( "multiblock", "GLOBAL_IDS_CELLS", "GLOBAL_IDS_POINTS", {}, (), (), np.float32, "None" ), # Test if the region attribute is on cells or on points. - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.float32, True ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.float32, "None" ), # Test the component name. - ( "dataset", "newAttribute", "FAULT", {}, ( "X" ), (), np.float32, True ), - ( "dataset", "newAttribute", "FAULT", {}, (), ( "Component0", "Component1" ), np.float32, True ), - ( "dataset", "newAttribute", "FAULT", {}, ( "X" ), ( "Component0", "Component1" ), np.float32, True ), - ( "dataset", "newAttribute", "FAULT", {}, ( "X", "Y" ), ( "X", "Y" ), np.float32, True ), - ( "dataset", "newAttribute", "FAULT", {}, ( "X", "Y", "Z" ), ( "X", "Y" ), np.float32, True ), + ( "dataset", "newAttribute", "FAULT", {}, ( "X" ), (), np.float32, "None" ), + ( "dataset", "newAttribute", "FAULT", {}, (), ( "Component0", "Component1" ), np.float32, "None" ), + ( "dataset", "newAttribute", "FAULT", {}, ( "X" ), ( "Component0", "Component1" ), np.float32, "None" ), + ( "dataset", "newAttribute", "FAULT", {}, ( "X", "Y" ), ( "X", "Y" ), np.float32, "None" ), + ( "dataset", "newAttribute", "FAULT", {}, ( "X", "Y", "Z" ), ( "X", "Y" ), np.float32, "None" ), # Test the type of value. - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.int8, True ), - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.int16, True ), - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.int32, True ), - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.int64, True ), - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.uint8, True ), - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.uint16, True ), - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.uint32, True ), - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.uint64, True ), - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.float64, True ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.int8, "None" ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.int16, "None" ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.int32, "None" ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.int64, "None" ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.uint8, "None" ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.uint16, "None" ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.uint32, "None" ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.uint64, "None" ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.float64, "None" ), # Test index/value. ( "dataset", "newAttribute", "FAULT", { 0: [ 0 ], 100: [ 1 ] - }, (), (), np.float32, True ), + }, (), (), np.float32, "None" ), ( "dataset", "newAttribute", "FAULT", { 0: [ 0 ], 100: [ 1 ], 101: [ 2 ] - }, (), (), np.float32, True ), + }, (), (), np.float32, "None" ), ( "dataset", "newAttribute", "FAULT", { 0: [ 0 ], 100: [ 1 ], 101: [ 2 ], 2: [ 3 ] - }, (), (), np.float32, True ), + }, (), (), np.float32, "None" ), ( "dataset", "newAttribute", "FAULT", { 0: [ 0, 0 ], 100: [ 1, 1 ] - }, (), ( "Component0", "Component1" ), np.float32, True ), + }, (), ( "Component0", "Component1" ), np.float32, "None" ), ( "dataset", "newAttribute", "FAULT", { 0: [ 0, 0 ], 100: [ 1, 1 ], 101: [ 2, 2 ] - }, (), ( "Component0", "Component1" ), np.float32, True ), + }, (), ( "Component0", "Component1" ), np.float32, "None" ), ( "dataset", "newAttribute", "FAULT", { 0: [ 0, 0 ], 100: [ 1, 1 ], 101: [ 2, 2 ], 2: [ 3, 3 ] - }, (), ( "Component0", "Component1" ), np.float32, True ), + }, (), ( "Component0", "Component1" ), np.float32, "None" ), # Test common error. ## Number of components. ( "dataset", "newAttribute", "FAULT", { 0: [ 0 ], 100: [ 1, 1 ] - }, (), (), np.float32, False ), # Number of value inconsistent. + }, (), (), np.float32, "ValueError" ), # Number of value inconsistent. ( "dataset", "newAttribute", "FAULT", { 0: [ 0, 0 ], 100: [ 1, 1 ] - }, (), (), np.float32, False ), # More values than components. + }, (), (), np.float32, "ValueError" ), # More values than components. ( "dataset", "newAttribute", "FAULT", { 0: [ 0 ], 100: [ 1 ] - }, ( "X", "Y" ), ( "X", "Y" ), np.float32, False ), # More components than value. + }, ( "X", "Y" ), ( "X", "Y" ), np.float32, "ValueError" ), # More components than value. ## Attribute name. - ( "dataset", "PERM", "FAULT", {}, (), (), np.float32, False ), # The attribute name already exist. + ( "dataset", "PERM", "FAULT", {}, (), (), np.float32, "ValueError" ), # The attribute name already exist. ## Region attribute. ( "dataset", "newAttribute", "PERM", {}, (), - (), np.float32, False ), # Region attribute has too many components. - ( "multiblock", "newAttribute", "FAULT", {}, (), (), np.float32, False ), # Region attribute is partial. + (), np.float32, "AttributeError" ), # Region attribute has too many components. + ( "multiblock", "newAttribute", "FAULT", {}, (), + (), np.float32, "AttributeError" ), # Region attribute is partial. ] ) def test_CreateConstantAttributePerRegion( dataSetTest: Union[ vtkMultiBlockDataSet, vtkDataSet ], @@ -102,7 +103,7 @@ def test_CreateConstantAttributePerRegion( componentNames: tuple[ str, ...], componentNamesTest: tuple[ str, ...], valueNpType: int, - succeed: bool, + error: str, ) -> None: """Test CreateConstantAttributePerRegion.""" mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ] = dataSetTest( meshType ) @@ -120,4 +121,11 @@ def test_CreateConstantAttributePerRegion( componentNames=componentNames, ) - assert createConstantAttributePerRegionFilter.applyFilter() == succeed + if error == "None": + createConstantAttributePerRegionFilter.applyFilter() + elif error == "AttributeError": + with pytest.raises( AttributeError ): + createConstantAttributePerRegionFilter.applyFilter() + elif error == "ValueError": + with pytest.raises( ValueError ): + createConstantAttributePerRegionFilter.applyFilter() diff --git a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py index 95a7af0c..2612170e 100644 --- a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py +++ b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py @@ -289,7 +289,14 @@ def ApplyFilter( self, inputMesh: vtkDataSet, outputMesh: vtkDataSet ) -> None: if len( createConstantAttributePerRegionFilter.logger.handlers ) == 0: createConstantAttributePerRegionFilter.setLoggerHandler( VTKHandler() ) - createConstantAttributePerRegionFilter.applyFilter() + try: + createConstantAttributePerRegionFilter.applyFilter() + except ( ValueError, AttributeError ) as e: + createConstantAttributePerRegionFilter.logger.error( + f"The filter { createConstantAttributePerRegionFilter.logger.name } failed du to:\n{ e }" ) + except Exception as e: + mess: str = f"The filter { createConstantAttributePerRegionFilter.logger.name } failed du to:\n{ e }" + createConstantAttributePerRegionFilter.logger.critical( mess, exc_info=True ) self.clearDictRegion = True From 205bd9caf936c7af0f9577b5fb1d27180764e358 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 24 Nov 2025 16:43:08 +0100 Subject: [PATCH 26/39] Refactor FillPartialArray --- .../FillPartialArrays.py | 68 +++++++++---------- .../tests/test_FillPartialArrays.py | 16 ++++- .../geos/pv/plugins/PVFillPartialArrays.py | 9 ++- 3 files changed, 57 insertions(+), 36 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py index 22d7275a..cd91e06c 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py @@ -49,7 +49,13 @@ fillPartialArraysFilter.setLoggerHandler( yourHandler ) # Do calculations. - fillPartialArraysFilter.applyFilter() + try: + fillPartialArraysFilter.applyFilter() + except ( ValueError, AttributeError ) as e: + fillPartialArraysFilter.logger.error( f"The filter { fillPartialArraysFilter.logger.name } failed du to: { e }" ) + except Exception as e: + mess: str = f"The filter { fillPartialArraysFilter.logger.name } failed du to: { e }" + fillPartialArraysFilter.logger.critical( mess, exc_info=True ) """ loggerTitle: str = "Fill Partial Attribute" @@ -106,40 +112,34 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler' to True" " during the filter initialization." ) - def applyFilter( self: Self ) -> bool: + def applyFilter( self: Self ) -> None: """Create a constant attribute per region in the mesh. - Returns: - boolean (bool): True if calculation successfully ended, False otherwise. + Raise: + AttributeError: Error with attributes to fill. + ValueError: Error during the filling of the attribute. """ self.logger.info( f"Apply filter { self.logger.name }." ) - try: - onPoints: Union[ None, bool ] - onBoth: bool - for attributeName in self.dictAttributesValues: - onPoints, onBoth = getAttributePieceInfo( self.multiBlockDataSet, attributeName ) - if onPoints is None: - raise ValueError( f"{ attributeName } is not in the mesh." ) - - if onBoth: - raise ValueError( - f"There is two attribute named { attributeName }, one on points and the other on cells. The attribute name must be unique." - ) - - if not fillPartialAttributes( self.multiBlockDataSet, - attributeName, - onPoints=onPoints, - listValues=self.dictAttributesValues[ attributeName ], - logger=self.logger ): - raise - - self.logger.info( f"The filter { self.logger.name } succeed." ) - except ( ValueError, AttributeError ) as e: - self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) - return False - except Exception as e: - mess: str = f"The filter { self.logger.name } failed.\n{ e }" - self.logger.critical( mess, exc_info=True ) - return False - - return True + + onPoints: Union[ None, bool ] + onBoth: bool + for attributeName in self.dictAttributesValues: + onPoints, onBoth = getAttributePieceInfo( self.multiBlockDataSet, attributeName ) + if onPoints is None: + raise AttributeError( f"The attribute { attributeName } is not in the mesh." ) + + if onBoth: + raise AttributeError( + f"There is two attribute named { attributeName }, one on points and the other on cells. The attribute name must be unique." + ) + + if not fillPartialAttributes( self.multiBlockDataSet, + attributeName, + onPoints=onPoints, + listValues=self.dictAttributesValues[ attributeName ], + logger=self.logger ): + raise ValueError( "Something got wrong with the filling of partial attributes" ) + + self.logger.info( f"The filter { self.logger.name } succeed." ) + + return diff --git a/geos-processing/tests/test_FillPartialArrays.py b/geos-processing/tests/test_FillPartialArrays.py index e5720d0f..0c0f1f9a 100644 --- a/geos-processing/tests/test_FillPartialArrays.py +++ b/geos-processing/tests/test_FillPartialArrays.py @@ -50,4 +50,18 @@ def test_FillPartialArrays( multiBlockDataSet: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) fillPartialArraysFilter: FillPartialArrays = FillPartialArrays( multiBlockDataSet, dictAttributesValues ) - assert fillPartialArraysFilter.applyFilter() + fillPartialArraysFilter.applyFilter() + + +def test_FillPartialArraysRaises( dataSetTest: vtkMultiBlockDataSet, ) -> None: + """Test the raise of FillPartialArray.""" + multiBlockDataSet: vtkMultiBlockDataSet = dataSetTest( "multiblock" ) + fillPartialArraysFilter: FillPartialArrays + + with pytest.raises( AttributeError ): + fillPartialArraysFilter = FillPartialArrays( multiBlockDataSet, { "poro": None } ) + fillPartialArraysFilter.applyFilter() + + with pytest.raises( ValueError ): + fillPartialArraysFilter = FillPartialArrays( multiBlockDataSet, { "PORO": [ 4, 4, 4 ] } ) + fillPartialArraysFilter.applyFilter() diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index ee7fa1b1..c8b2ab73 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -108,7 +108,14 @@ def ApplyFilter( self, inputMesh: vtkMultiBlockDataSet, outputMesh: vtkMultiBloc if len( fillPartialArraysFilter.logger.handlers ) == 0: fillPartialArraysFilter.setLoggerHandler( VTKHandler() ) - fillPartialArraysFilter.applyFilter() + try: + fillPartialArraysFilter.applyFilter() + except ( ValueError, AttributeError ) as e: + fillPartialArraysFilter.logger.error( + f"The filter { fillPartialArraysFilter.logger.name } failed du to:\n{ e }" ) + except Exception as e: + mess: str = f"The filter { fillPartialArraysFilter.logger.name } failed du to:\n{ e }" + fillPartialArraysFilter.logger.critical( mess, exc_info=True ) self.clearDictAttributesValues = True From 660ac2798ccd180da58d6309af02943bb8cf89a7 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 24 Nov 2025 17:11:35 +0100 Subject: [PATCH 27/39] Refator MergeBlockEnhanced --- .../MergeBlockEnhanced.py | 38 +++++++++---------- .../tests/test_MergeBlocksEnhanced.py | 18 +++++++-- .../geos/pv/plugins/PVMergeBlocksEnhanced.py | 10 ++++- 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py b/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py index 4630d642..56034b5c 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py @@ -8,7 +8,6 @@ from geos.utils.Logger import ( Logger, getLogger ) from geos.mesh.utils.multiblockModifiers import mergeBlocks -from geos.utils.Errors import VTKError from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet, vtkUnstructuredGrid @@ -32,6 +31,7 @@ from geos.processing.generic_processing_tools.MergeBlockEnhanced import MergeBlockEnhanced import logging + from geos.utils.Errors import VTKError # Define filter inputs multiblockdataset: vtkMultiblockDataSet @@ -45,7 +45,13 @@ mergeBlockEnhancedFilter.setLoggerHandler( yourHandler ) # Do calculations - mergeBlockEnhancedFilter.applyFilter() + try: + mergeBlockEnhancedFilter.applyFilter() + except VTKError as e: + mergeBlockEnhancedFilter.logger.error( f"The filter { mergeBlockEnhancedFilter.logger.name } failed du to: { e }" ) + except Exception as e: + mess: str = f"The filter { mergeBlockEnhancedFilter.logger.name } failed du to: { e }" + mergeBlockEnhancedFilter.logger.critical( mess, exc_info=True ) # Get the merged mesh mergeBlockEnhancedFilter.getOutput() @@ -99,29 +105,21 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler' to True" " during the filter initialization." ) - def applyFilter( self: Self ) -> bool: + def applyFilter( self: Self ) -> None: """Merge the blocks of a multiblock dataset mesh. - Returns: - bool: True if the blocks were successfully merged, False otherwise. + Raise: + VTKError (geos.utils.Errors): Errors captured if any from the VTK log. """ self.logger.info( f"Applying filter { self.logger.name }." ) - try: - outputMesh: vtkUnstructuredGrid - outputMesh = mergeBlocks( self.inputMesh, keepPartialAttributes=True, logger=self.logger ) - self.outputMesh = outputMesh - - self.logger.info( f"The filter { self.logger.name } succeeded." ) - except VTKError as e: - self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) - return False - except Exception as e: - mess: str = f"The filter { self.logger.name } failed.\n{ e }" - self.logger.critical( mess, exc_info=True ) - return False - - return True + outputMesh: vtkUnstructuredGrid + outputMesh = mergeBlocks( self.inputMesh, keepPartialAttributes=True, logger=self.logger ) + self.outputMesh = outputMesh + + self.logger.info( f"The filter { self.logger.name } succeeded." ) + + return def getOutput( self: Self ) -> vtkUnstructuredGrid: """Get the merged mesh. diff --git a/geos-processing/tests/test_MergeBlocksEnhanced.py b/geos-processing/tests/test_MergeBlocksEnhanced.py index ce56b5c3..677a99d9 100644 --- a/geos-processing/tests/test_MergeBlocksEnhanced.py +++ b/geos-processing/tests/test_MergeBlocksEnhanced.py @@ -7,6 +7,9 @@ from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet from geos.processing.generic_processing_tools.MergeBlockEnhanced import MergeBlockEnhanced +from unittest import TestCase +from geos.utils.Errors import VTKError + import vtk from packaging.version import Version @@ -15,8 +18,15 @@ def test_MergeBlocksEnhancedFilter( dataSetTest: vtkMultiBlockDataSet, ) -> None """Test MergeBlockEnhanced vtk filter.""" multiBlockDataset: vtkMultiBlockDataSet = dataSetTest( "multiblockGeosOutput" ) mergeBlockEnhancedFilter: MergeBlockEnhanced = MergeBlockEnhanced( multiBlockDataset ) - assert mergeBlockEnhancedFilter.applyFilter() + mergeBlockEnhancedFilter.applyFilter() + + +class RaiseMergeBlocksEnhanced( TestCase ): + """Test failure on empty multiBlockDataSet.""" - if Version( vtk.__version__ ) < Version( "9.5" ): - failedMergeBlockEnhancedFilter: MergeBlockEnhanced = MergeBlockEnhanced( vtkMultiBlockDataSet() ) - assert not failedMergeBlockEnhancedFilter.applyFilter() + def test_TypeError( self ) -> None: + """Test raise of TypeError.""" + multiBlockDataset = vtkMultiBlockDataSet() # should fail on empty data + mergeBlockEnhancedFilter: MergeBlockEnhanced = MergeBlockEnhanced( multiBlockDataset ) + if Version( vtk.__version__ ) < Version( "9.5" ): + self.assertRaises( VTKError, mergeBlockEnhancedFilter.applyFilter ) diff --git a/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py index ea96de82..1e6223b3 100644 --- a/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py @@ -24,6 +24,7 @@ update_paths() from geos.processing.generic_processing_tools.MergeBlockEnhanced import MergeBlockEnhanced +from geos.utils.Errors import VTKError __doc__ = """ Merge Blocks Keeping Partial Attributes is a Paraview plugin filter that allows to merge blocks from a multiblock dataset while keeping partial attributes. @@ -116,8 +117,15 @@ def RequestData( if len( mergeBlockEnhancedFilter.logger.handlers ) == 0: mergeBlockEnhancedFilter.setLoggerHandler( VTKHandler() ) - if mergeBlockEnhancedFilter.applyFilter(): + try: + mergeBlockEnhancedFilter.applyFilter() outputMesh.ShallowCopy( mergeBlockEnhancedFilter.getOutput() ) outputMesh.Modified() + except VTKError as e: + mergeBlockEnhancedFilter.logger.error( + f"The filter { mergeBlockEnhancedFilter.logger.name } failed du to:\n{ e }" ) + except Exception as e: + mess: str = f"The filter { mergeBlockEnhancedFilter.logger.name } failed du to:\n{ e }" + mergeBlockEnhancedFilter.logger.critical( mess, exc_info=True ) return 1 From 4ef951a1cb4b732d0aa24c3b47d16d0e6805a58a Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 24 Nov 2025 17:18:02 +0100 Subject: [PATCH 28/39] Update doc --- .../processing/generic_processing_tools/AttributeMapping.py | 4 ++-- .../CreateConstantAttributePerRegion.py | 4 ++-- .../processing/generic_processing_tools/FillPartialArrays.py | 4 ++-- .../generic_processing_tools/MergeBlockEnhanced.py | 4 ++-- geos-pv/src/geos/pv/plugins/PVAttributeMapping.py | 5 ++++- .../geos/pv/plugins/PVCreateConstantAttributePerRegion.py | 4 ++-- geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py | 4 ++-- geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py | 4 ++-- 8 files changed, 18 insertions(+), 15 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py index 5b010e51..f0637b4e 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -60,9 +60,9 @@ try: attributeMappingFilter.applyFilter() except( ValueError, AttributeError ) as e: - attributeMappingFilter.logger.error( f"The filter { attributeMappingFilter.logger.name } failed du to: { e }" ) + attributeMappingFilter.logger.error( f"The filter { attributeMappingFilter.logger.name } failed due to: { e }" ) except Exception as e: - mess: str = f"The filter { attributeMappingFilter.logger.name } failed du to: { e }" + mess: str = f"The filter { attributeMappingFilter.logger.name } failed due to: { e }" attributeMappingFilter.logger.critical( mess, exc_info=True ) """ 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 1dc663f5..7c58b437 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -69,9 +69,9 @@ try: createConstantAttributePerRegionFilter.applyFilter() except ( ValueError, AttributeError ) as e: - createConstantAttributePerRegionFilter.logger.error( f"The filter { createConstantAttributePerRegionFilter.logger.name } failed du to { e }" ) + createConstantAttributePerRegionFilter.logger.error( f"The filter { createConstantAttributePerRegionFilter.logger.name } failed due to: { e }" ) except Exception as e: - mess: str = f"The filter { createConstantAttributePerRegionFilter.logger.name } failed du to: { e }" + mess: str = f"The filter { createConstantAttributePerRegionFilter.logger.name } failed due to: { e }" createConstantAttributePerRegionFilter.logger.critical( mess, exc_info=True ) """ diff --git a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py index cd91e06c..cf2d30c4 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py @@ -52,9 +52,9 @@ try: fillPartialArraysFilter.applyFilter() except ( ValueError, AttributeError ) as e: - fillPartialArraysFilter.logger.error( f"The filter { fillPartialArraysFilter.logger.name } failed du to: { e }" ) + fillPartialArraysFilter.logger.error( f"The filter { fillPartialArraysFilter.logger.name } failed due to: { e }" ) except Exception as e: - mess: str = f"The filter { fillPartialArraysFilter.logger.name } failed du to: { e }" + mess: str = f"The filter { fillPartialArraysFilter.logger.name } failed due to: { e }" fillPartialArraysFilter.logger.critical( mess, exc_info=True ) """ diff --git a/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py b/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py index 56034b5c..87bc1bbb 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/MergeBlockEnhanced.py @@ -48,9 +48,9 @@ try: mergeBlockEnhancedFilter.applyFilter() except VTKError as e: - mergeBlockEnhancedFilter.logger.error( f"The filter { mergeBlockEnhancedFilter.logger.name } failed du to: { e }" ) + mergeBlockEnhancedFilter.logger.error( f"The filter { mergeBlockEnhancedFilter.logger.name } failed due to: { e }" ) except Exception as e: - mess: str = f"The filter { mergeBlockEnhancedFilter.logger.name } failed du to: { e }" + mess: str = f"The filter { mergeBlockEnhancedFilter.logger.name } failed due to: { e }" mergeBlockEnhancedFilter.logger.critical( mess, exc_info=True ) # Get the merged mesh diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index 084219c3..b6d239d8 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -191,6 +191,9 @@ def RequestData( self.clearAttributeNames = True except ( ValueError, AttributeError ) as e: attributeMappingFilter.logger.error( - f"The filter { attributeMappingFilter.logger.name } failed du to:\n{ e }" ) + f"The filter { attributeMappingFilter.logger.name } failed due to:\n{ e }" ) + except Exception as e: + mess: str = f"The filter { attributeMappingFilter.logger.name } failed due to:\n{ e }" + attributeMappingFilter.logger.critical( mess, exc_info=True ) return 1 diff --git a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py index 2612170e..ea44eaf3 100644 --- a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py +++ b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py @@ -293,9 +293,9 @@ def ApplyFilter( self, inputMesh: vtkDataSet, outputMesh: vtkDataSet ) -> None: createConstantAttributePerRegionFilter.applyFilter() except ( ValueError, AttributeError ) as e: createConstantAttributePerRegionFilter.logger.error( - f"The filter { createConstantAttributePerRegionFilter.logger.name } failed du to:\n{ e }" ) + f"The filter { createConstantAttributePerRegionFilter.logger.name } failed due to:\n{ e }" ) except Exception as e: - mess: str = f"The filter { createConstantAttributePerRegionFilter.logger.name } failed du to:\n{ e }" + mess: str = f"The filter { createConstantAttributePerRegionFilter.logger.name } failed due to:\n{ e }" createConstantAttributePerRegionFilter.logger.critical( mess, exc_info=True ) self.clearDictRegion = True diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index c8b2ab73..cec56556 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -112,9 +112,9 @@ def ApplyFilter( self, inputMesh: vtkMultiBlockDataSet, outputMesh: vtkMultiBloc fillPartialArraysFilter.applyFilter() except ( ValueError, AttributeError ) as e: fillPartialArraysFilter.logger.error( - f"The filter { fillPartialArraysFilter.logger.name } failed du to:\n{ e }" ) + f"The filter { fillPartialArraysFilter.logger.name } failed due to:\n{ e }" ) except Exception as e: - mess: str = f"The filter { fillPartialArraysFilter.logger.name } failed du to:\n{ e }" + mess: str = f"The filter { fillPartialArraysFilter.logger.name } failed due to:\n{ e }" fillPartialArraysFilter.logger.critical( mess, exc_info=True ) self.clearDictAttributesValues = True diff --git a/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py index 1e6223b3..b4faec3d 100644 --- a/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMergeBlocksEnhanced.py @@ -123,9 +123,9 @@ def RequestData( outputMesh.Modified() except VTKError as e: mergeBlockEnhancedFilter.logger.error( - f"The filter { mergeBlockEnhancedFilter.logger.name } failed du to:\n{ e }" ) + f"The filter { mergeBlockEnhancedFilter.logger.name } failed due to:\n{ e }" ) except Exception as e: - mess: str = f"The filter { mergeBlockEnhancedFilter.logger.name } failed du to:\n{ e }" + mess: str = f"The filter { mergeBlockEnhancedFilter.logger.name } failed due to:\n{ e }" mergeBlockEnhancedFilter.logger.critical( mess, exc_info=True ) return 1 From 0a9c2407e2b6d251c27115bac25ac246f01fcdf6 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 24 Nov 2025 17:33:04 +0100 Subject: [PATCH 29/39] Refactor SplitMesh --- .../generic_processing_tools/SplitMesh.py | 162 +++++++++--------- geos-processing/tests/test_SplitMesh.py | 6 +- geos-pv/src/geos/pv/plugins/PVSplitMesh.py | 9 +- 3 files changed, 92 insertions(+), 85 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py index 8cbe9d02..0f2a4674 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py @@ -41,7 +41,13 @@ splitMeshFilter.setLoggerHandler( yourHandler ) # Do calculations - splitMeshFilter.applyFilter() + try: + splitMeshFilter.applyFilter() + except ( TypeError, AttributeError ) as e: + splitMeshFilter.logger.error( f"The filter {splitMeshFilter.logger.name } failed due to: { e }" ) + except Exception as e: + mess: str = f"The filter { splitMeshFilter.logger.name } failed due to: { e }" + splitMeshFilter.logger.critical( mess, exc_info=True ) # Get splitted mesh outputMesh: vtkUnstructuredGrid = splitMeshFilter.getOutput() @@ -93,88 +99,80 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler' to True" " during the filter initialization." ) - def applyFilter( self: Self ) -> bool: + def applyFilter( self: Self ) -> None: """Apply the filter SplitMesh. - Returns: - bool: True if the filter succeeded, False otherwise. + Raises: + TypeError: Errors due to objects with the wrong type. + AttributeError: Errors with cell data. """ self.logger.info( f"Applying filter { self.logger.name }." ) - try: - # Count the number of cells before splitting. Then we will be able to know how many new cells and points - # to allocate because each cell type is splitted in a known number of new cells and points. - nbCells: int = self.inputMesh.GetNumberOfCells() - counts: CellTypeCounts = self._getCellCounts() - if counts.getTypeCount( VTK_WEDGE ) != 0: - raise TypeError( "Input mesh contains wedges that are not currently supported." ) - - nbPolygon: int = counts.getTypeCount( VTK_POLYGON ) - nbPolyhedra: int = counts.getTypeCount( VTK_POLYHEDRON ) - # Current implementation only supports meshes composed of either polygons or polyhedra - if nbPolyhedra * nbPolygon != 0: - raise TypeError( - "Input mesh is composed of both polygons and polyhedra, but it must contains only one of the two." ) - - nbTet: int = counts.getTypeCount( VTK_TETRA ) # will divide into 8 tets - nbPyr: int = counts.getTypeCount( VTK_PYRAMID ) # will divide into 6 pyramids and 4 tets so 10 new cells - nbHex: int = counts.getTypeCount( VTK_HEXAHEDRON ) # will divide into 8 hexes - nbTriangles: int = counts.getTypeCount( VTK_TRIANGLE ) # will divide into 4 triangles - nbQuad: int = counts.getTypeCount( VTK_QUAD ) # will divide into 4 quads - nbNewPoints: int = 0 - nbNewPoints = nbHex * 19 + nbTet * 6 + nbPyr * 9 if nbPolyhedra > 0 else nbTriangles * 3 + nbQuad * 5 - nbNewCells: int = nbHex * 8 + nbTet * 8 + nbPyr * 10 + nbTriangles * 4 + nbQuad * 4 - - self.points = vtkPoints() - self.points.DeepCopy( self.inputMesh.GetPoints() ) - self.points.Resize( self.inputMesh.GetNumberOfPoints() + nbNewPoints ) - - self.cells = vtkCellArray() - self.cells.AllocateExact( nbNewCells, 8 ) - self.originalId = vtkIdTypeArray() - self.originalId.SetName( "OriginalID" ) - self.originalId.Allocate( nbNewCells ) - self.cellTypes = [] - - # Define cell type to splitting method mapping - splitMethods = { - VTK_HEXAHEDRON: self._splitHexahedron, - VTK_TETRA: self._splitTetrahedron, - VTK_PYRAMID: self._splitPyramid, - VTK_TRIANGLE: self._splitTriangle, - VTK_QUAD: self._splitQuad, - } - for c in range( nbCells ): - cell: vtkCell = self.inputMesh.GetCell( c ) - cellType: int = cell.GetCellType() - splitMethod = splitMethods.get( cellType ) - if splitMethod is not None: - splitMethod( cell, c ) - else: - raise TypeError( - f"Cell type { vtkCellTypes.GetClassNameFromTypeId( cellType ) } is not supported." ) - - # Add points and cells - self.outputMesh.SetPoints( self.points ) - self.outputMesh.SetCells( self.cellTypes, self.cells ) - - # Add attribute saving original cell ids - cellArrays: vtkCellData = self.outputMesh.GetCellData() - if cellArrays is None: - raise AttributeError( "Cell data is undefined." ) - cellArrays.AddArray( self.originalId ) - - # Transfer all cell arrays - self._transferCellArrays( self.outputMesh ) - self.logger.info( f"The filter { self.logger.name } succeeded." ) - except ( TypeError, AttributeError ) as e: - self.logger.error( f"The filter {self.logger.name } failed.\n{ e }" ) - return False - except Exception as e: - mess: str = f"The filter { self.logger.name } failed.\n{ e }" - self.logger.critical( mess, exc_info=True ) - return False - - return True + # Count the number of cells before splitting. Then we will be able to know how many new cells and points + # to allocate because each cell type is splitted in a known number of new cells and points. + nbCells: int = self.inputMesh.GetNumberOfCells() + counts: CellTypeCounts = self._getCellCounts() + if counts.getTypeCount( VTK_WEDGE ) != 0: + raise TypeError( "Input mesh contains wedges that are not currently supported." ) + + nbPolygon: int = counts.getTypeCount( VTK_POLYGON ) + nbPolyhedra: int = counts.getTypeCount( VTK_POLYHEDRON ) + # Current implementation only supports meshes composed of either polygons or polyhedra + if nbPolyhedra * nbPolygon != 0: + raise TypeError( + "Input mesh is composed of both polygons and polyhedra, but it must contains only one of the two." ) + + nbTet: int = counts.getTypeCount( VTK_TETRA ) # will divide into 8 tets + nbPyr: int = counts.getTypeCount( VTK_PYRAMID ) # will divide into 6 pyramids and 4 tets so 10 new cells + nbHex: int = counts.getTypeCount( VTK_HEXAHEDRON ) # will divide into 8 hexes + nbTriangles: int = counts.getTypeCount( VTK_TRIANGLE ) # will divide into 4 triangles + nbQuad: int = counts.getTypeCount( VTK_QUAD ) # will divide into 4 quads + nbNewPoints: int = 0 + nbNewPoints = nbHex * 19 + nbTet * 6 + nbPyr * 9 if nbPolyhedra > 0 else nbTriangles * 3 + nbQuad * 5 + nbNewCells: int = nbHex * 8 + nbTet * 8 + nbPyr * 10 + nbTriangles * 4 + nbQuad * 4 + + self.points = vtkPoints() + self.points.DeepCopy( self.inputMesh.GetPoints() ) + self.points.Resize( self.inputMesh.GetNumberOfPoints() + nbNewPoints ) + + self.cells = vtkCellArray() + self.cells.AllocateExact( nbNewCells, 8 ) + self.originalId = vtkIdTypeArray() + self.originalId.SetName( "OriginalID" ) + self.originalId.Allocate( nbNewCells ) + self.cellTypes = [] + + # Define cell type to splitting method mapping + splitMethods = { + VTK_HEXAHEDRON: self._splitHexahedron, + VTK_TETRA: self._splitTetrahedron, + VTK_PYRAMID: self._splitPyramid, + VTK_TRIANGLE: self._splitTriangle, + VTK_QUAD: self._splitQuad, + } + for c in range( nbCells ): + cell: vtkCell = self.inputMesh.GetCell( c ) + cellType: int = cell.GetCellType() + splitMethod = splitMethods.get( cellType ) + if splitMethod is not None: + splitMethod( cell, c ) + else: + raise TypeError( f"Cell type { vtkCellTypes.GetClassNameFromTypeId( cellType ) } is not supported." ) + + # Add points and cells + self.outputMesh.SetPoints( self.points ) + self.outputMesh.SetCells( self.cellTypes, self.cells ) + + # Add attribute saving original cell ids + cellArrays: vtkCellData = self.outputMesh.GetCellData() + if cellArrays is None: + raise AttributeError( "Cell data is undefined." ) + cellArrays.AddArray( self.originalId ) + + # Transfer all cell arrays + self._transferCellArrays( self.outputMesh ) + self.logger.info( f"The filter { self.logger.name } succeeded." ) + + return def getOutput( self: Self ) -> vtkUnstructuredGrid: """Get the splitted mesh computed.""" @@ -190,8 +188,7 @@ def _getCellCounts( self: Self ) -> CellTypeCounts: self.inputMesh, self.speHandler ) if self.speHandler and len( cellTypeCounterEnhancedFilter.logger.handlers ) == 0: cellTypeCounterEnhancedFilter.setLoggerHandler( self.handler ) - if not cellTypeCounterEnhancedFilter.applyFilter(): - raise + cellTypeCounterEnhancedFilter.applyFilter() return cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() def _addMidPoint( self: Self, ptA: int, ptB: int ) -> int: @@ -447,6 +444,9 @@ def _transferCellArrays( self: Self, splittedMesh: vtkUnstructuredGrid ) -> None Args: splittedMesh (vtkUnstructuredGrid): Splitted mesh. + + Raises: + AttributeError: Errors with cell data. """ cellData: vtkCellData = self.inputMesh.GetCellData() if cellData is None: diff --git a/geos-processing/tests/test_SplitMesh.py b/geos-processing/tests/test_SplitMesh.py index fb175b30..c97f24be 100644 --- a/geos-processing/tests/test_SplitMesh.py +++ b/geos-processing/tests/test_SplitMesh.py @@ -143,7 +143,7 @@ def test_single_cell_split( test_case: TestCase ) -> None: """ cellTypeName: str = vtkCellTypes.GetClassNameFromTypeId( test_case.cellType ) splitMeshFilter: SplitMesh = SplitMesh( test_case.mesh ) - assert splitMeshFilter.applyFilter() + splitMeshFilter.applyFilter() output: vtkUnstructuredGrid = splitMeshFilter.getOutput() assert output is not None, "Output mesh is undefined." pointsOut: vtkPoints = output.GetPoints() @@ -234,7 +234,7 @@ def test_multi_cells_mesh_split() -> None: # Apply the split filter splitMeshFilter = SplitMesh( input_mesh ) - assert splitMeshFilter.applyFilter() + splitMeshFilter.applyFilter() output: vtkUnstructuredGrid = splitMeshFilter.getOutput() assert output is not None, "Output mesh should be defined" @@ -296,7 +296,7 @@ def test_multi_polygon_mesh_split() -> None: # Apply the split filter splitMeshFilter = SplitMesh( input_mesh ) - assert splitMeshFilter.applyFilter() + splitMeshFilter.applyFilter() output: vtkUnstructuredGrid = splitMeshFilter.getOutput() assert output is not None, "Output mesh should be defined" diff --git a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py index 6b149538..b677bf6d 100644 --- a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py +++ b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py @@ -54,7 +54,14 @@ def ApplyFilter( self: Self, inputMesh: vtkPointSet, outputMesh: vtkPointSet ) - splitMeshFilter: SplitMesh = SplitMesh( inputMesh, True ) if len( splitMeshFilter.logger.handlers ) == 0: splitMeshFilter.setLoggerHandler( VTKHandler() ) - if splitMeshFilter.applyFilter(): + + try: + splitMeshFilter.applyFilter() outputMesh.ShallowCopy( splitMeshFilter.getOutput() ) + except ( TypeError, AttributeError ) as e: + splitMeshFilter.logger.error( f"The filter {splitMeshFilter.logger.name } failed due to:\n{ e }" ) + except Exception as e: + mess: str = f"The filter { splitMeshFilter.logger.name } failed due to:\n{ e }" + splitMeshFilter.logger.critical( mess, exc_info=True ) return From e57b5ae31592f8bc7059ce8eee7b1962ffcfadc9 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 24 Nov 2025 17:48:18 +0100 Subject: [PATCH 30/39] refactor CellTypeCounterEnhanced --- .../pre_processing/CellTypeCounterEnhanced.py | 71 +++++++++---------- .../tests/test_CellTypeCounterEnhanced.py | 4 +- .../pv/plugins/PVCellTypeCounterEnhanced.py | 12 +++- 3 files changed, 48 insertions(+), 39 deletions(-) diff --git a/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py b/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py index 3aa8953a..3938a597 100644 --- a/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py +++ b/geos-processing/src/geos/processing/pre_processing/CellTypeCounterEnhanced.py @@ -34,7 +34,13 @@ cellTypeCounterEnhancedFilter.setLoggerHandler( yourHandler ) # Do calculations - cellTypeCounterEnhancedFilter.applyFilter() + try: + cellTypeCounterEnhancedFilter.applyFilter() + except TypeError as e: + cellTypeCounterEnhancedFilter.logger.error( f"The filter { cellTypeCounterEnhancedFilter.logger.name } failed due to: { e }" ) + except Exception as e: + mess: str = f"The filter { cellTypeCounterEnhancedFilter.logger.name } failed due to: { e }" + cellTypeCounterEnhancedFilter.logger.critical( mess, exc_info=True ) # Get result counts: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() @@ -86,45 +92,38 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: self.logger.warning( "The logger already has an handler, to use yours set the argument 'speHandler'" " to True during the filter initialization." ) - def applyFilter( self: Self ) -> bool: + def applyFilter( self: Self ) -> None: """Apply CellTypeCounterEnhanced filter. - Returns: - bool: True if the filter succeeded, False otherwise. + Raises: + TypeError: Errors with the type of the cells. """ self.logger.info( f"Apply filter { self.logger.name }." ) - try: - # compute cell type counts - self._counts.reset() - self._counts.setTypeCount( VTK_VERTEX, self.inputMesh.GetNumberOfPoints() ) - for i in range( self.inputMesh.GetNumberOfCells() ): - cell: vtkCell = self.inputMesh.GetCell( i ) - self._counts.addType( cell.GetCellType() ) - - # create output table - # first reset output table - self.outTable.RemoveAllRows() - self.outTable.RemoveAllColumns() - self.outTable.SetNumberOfRows( 1 ) - - # create columns per types - for cellType in getAllCellTypes(): - array: vtkIntArray = vtkIntArray() - array.SetName( vtkCellTypes.GetClassNameFromTypeId( cellType ) ) - array.SetNumberOfComponents( 1 ) - array.SetNumberOfValues( 1 ) - array.SetValue( 0, self._counts.getTypeCount( cellType ) ) - self.outTable.AddColumn( array ) - self.logger.info( f"The filter { self.logger.name } succeeded." ) - except TypeError as e: - self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) - return False - except Exception as e: - mess: str = f"The filter { self.logger.name } failed.\n{ e }" - self.logger.critical( mess, exc_info=True ) - return False - - return True + + # Compute cell type counts + self._counts.reset() + self._counts.setTypeCount( VTK_VERTEX, self.inputMesh.GetNumberOfPoints() ) + for i in range( self.inputMesh.GetNumberOfCells() ): + cell: vtkCell = self.inputMesh.GetCell( i ) + self._counts.addType( cell.GetCellType() ) + + # Create output table + # First reset output table + self.outTable.RemoveAllRows() + self.outTable.RemoveAllColumns() + self.outTable.SetNumberOfRows( 1 ) + + # Create columns per types + for cellType in getAllCellTypes(): + array: vtkIntArray = vtkIntArray() + array.SetName( vtkCellTypes.GetClassNameFromTypeId( cellType ) ) + array.SetNumberOfComponents( 1 ) + array.SetNumberOfValues( 1 ) + array.SetValue( 0, self._counts.getTypeCount( cellType ) ) + self.outTable.AddColumn( array ) + self.logger.info( f"The filter { self.logger.name } succeeded." ) + + return def GetCellTypeCountsObject( self: Self ) -> CellTypeCounts: """Get CellTypeCounts object. diff --git a/geos-processing/tests/test_CellTypeCounterEnhanced.py b/geos-processing/tests/test_CellTypeCounterEnhanced.py index 7783ce70..7883592c 100644 --- a/geos-processing/tests/test_CellTypeCounterEnhanced.py +++ b/geos-processing/tests/test_CellTypeCounterEnhanced.py @@ -60,7 +60,7 @@ def test_CellTypeCounterEnhanced_single( test_case: TestCase ) -> None: test_case (TestCase): Test case """ cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( test_case.mesh ) - assert cellTypeCounterEnhancedFilter.applyFilter() + cellTypeCounterEnhancedFilter.applyFilter() countsObs: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() assert countsObs is not None, "CellTypeCounts is undefined" @@ -118,7 +118,7 @@ def test_CellTypeCounterEnhanced_multi( test_case: TestCase ) -> None: test_case (TestCase): Test case """ cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( test_case.mesh ) - assert cellTypeCounterEnhancedFilter.applyFilter() + cellTypeCounterEnhancedFilter.applyFilter() countsObs: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() assert countsObs is not None, "CellTypeCounts is undefined" diff --git a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py index bbf6178c..1724f778 100644 --- a/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVCellTypeCounterEnhanced.py @@ -137,7 +137,9 @@ def RequestData( cellTypeCounterEnhancedFilter: CellTypeCounterEnhanced = CellTypeCounterEnhanced( inputMesh, True ) if len( cellTypeCounterEnhancedFilter.logger.handlers ) == 0: cellTypeCounterEnhancedFilter.setLoggerHandler( VTKHandler() ) - if cellTypeCounterEnhancedFilter.applyFilter(): + + try: + cellTypeCounterEnhancedFilter.applyFilter() outputTable.ShallowCopy( cellTypeCounterEnhancedFilter.getOutput() ) # print counts in Output Messages view @@ -152,4 +154,12 @@ def RequestData( cellTypeCounterEnhancedFilter.logger.info( f"File {self._filename} was successfully written." ) except Exception as e: cellTypeCounterEnhancedFilter.logger.info( f"Error while exporting the file due to:\n{ e }" ) + + except TypeError as e: + cellTypeCounterEnhancedFilter.logger.error( + f"The filter { cellTypeCounterEnhancedFilter.logger.name } failed due to:\n{ e }" ) + except Exception as e: + mess: str = f"The filter { cellTypeCounterEnhancedFilter.logger.name } failed due to:\n{ e }" + cellTypeCounterEnhancedFilter.logger.critical( mess, exc_info=True ) + return 1 From b2f7a8827043bebc217d495fdc03c453fa1ec533 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 24 Nov 2025 17:56:03 +0100 Subject: [PATCH 31/39] Refactor MeshQualityEnhanced --- .../pre_processing/MeshQualityEnhanced.py | 52 ++++++++----------- .../tests/test_MeshQualityEnhanced.py | 2 +- .../geos/pv/plugins/PVMeshQualityEnhanced.py | 11 +++- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py b/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py index 6c454c69..de5fb1d8 100644 --- a/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py +++ b/geos-processing/src/geos/processing/pre_processing/MeshQualityEnhanced.py @@ -64,7 +64,13 @@ meshQualityEnhancedFilter.SetOtherMeshQualityMetrics(otherQualityMetrics) # Do calculations - meshQualityEnhancedFilter.applyFilter() + try: + meshQualityEnhancedFilter.applyFilter() + except ( ValueError, IndexError, TypeError, AttributeError ) as e: + meshQualityEnhancedFilter.logger.error( f"The filter { meshQualityEnhancedFilter.logger.name } failed due to: { e }" ) + except Exception as e: + mess: str = f"The filter { meshQualityEnhancedFilter.logger.name } failed due to: { e }" + meshQualityEnhancedFilter.logger.critical( mess, exc_info=True ) # Get output mesh quality report outputMesh: vtkUnstructuredGrid = meshQualityEnhancedFilter.getOutput() @@ -282,39 +288,28 @@ def getComputedMetricsFromCellType( self: Self, cellType: int ) -> Optional[ set metrics = metrics.intersection( computedMetrics ) return metrics if commonComputedMetricsExists else None - def applyFilter( self: Self ) -> bool: - """Apply MeshQualityEnhanced filter. - - Returns: - bool: True if the filter succeeded, False otherwise. - """ + def applyFilter( self: Self ) -> None: + """Apply MeshQualityEnhanced filter.""" self.logger.info( f"Apply filter { self.logger.name }." ) - try: - self._outputMesh.ShallowCopy( self.inputMesh ) - # Compute cell type counts - self._computeCellTypeCounts() - # Compute metrics and associated attributes - self._evaluateMeshQualityAll() + self._outputMesh.ShallowCopy( self.inputMesh ) + # Compute cell type counts + self._computeCellTypeCounts() - # Compute stats summary - self._updateStatsSummary() + # Compute metrics and associated attributes + self._evaluateMeshQualityAll() - # Create field data - self._createFieldDataStatsSummary() + # Compute stats summary + self._updateStatsSummary() - self._outputMesh.Modified() + # Create field data + self._createFieldDataStatsSummary() - self.logger.info( f"The filter { self.logger.name } succeeded." ) - except ( ValueError, IndexError, TypeError, AttributeError ) as e: - self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) - return False - except Exception as e: - mess: str = f"The filter { self.logger.name } failed.\n{ e }" - self.logger.critical( mess, exc_info=True ) - return False + self._outputMesh.Modified() + + self.logger.info( f"The filter { self.logger.name } succeeded." ) - return True + return def getOutput( self: Self ) -> vtkUnstructuredGrid: """Get the mesh computed with the stats.""" @@ -326,8 +321,7 @@ def _computeCellTypeCounts( self: Self ) -> None: self._outputMesh, self.speHandler ) if self.speHandler and len( cellTypeCounterEnhancedFilter.logger.handlers ) == 0: cellTypeCounterEnhancedFilter.setLoggerHandler( self.handler ) - if not cellTypeCounterEnhancedFilter.applyFilter(): - raise + cellTypeCounterEnhancedFilter.applyFilter() counts: CellTypeCounts = cellTypeCounterEnhancedFilter.GetCellTypeCountsObject() if counts is None: diff --git a/geos-processing/tests/test_MeshQualityEnhanced.py b/geos-processing/tests/test_MeshQualityEnhanced.py index 2938c736..6b3cb678 100644 --- a/geos-processing/tests/test_MeshQualityEnhanced.py +++ b/geos-processing/tests/test_MeshQualityEnhanced.py @@ -142,7 +142,7 @@ def test_MeshQualityEnhanced( test_case: TestCase ) -> None: meshQualityEnhancedFilter.SetWedgeMetrics( test_case.qualityMetrics ) elif test_case.cellType == VTK_HEXAHEDRON: meshQualityEnhancedFilter.SetHexaMetrics( test_case.qualityMetrics ) - assert meshQualityEnhancedFilter.applyFilter() + meshQualityEnhancedFilter.applyFilter() # test method getComputedMetricsFromCellType for i, cellType in enumerate( getAllCellTypesExtended() ): diff --git a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py index 4debf0a4..06149776 100644 --- a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py @@ -239,8 +239,8 @@ def ApplyFilter( self, inputMesh: vtkUnstructuredGrid, outputMesh: vtkUnstructur wedgeMetrics=wedgeMetrics, hexaMetrics=hexaMetrics ) meshQualityEnhancedFilter.SetOtherMeshQualityMetrics( otherMetrics ) - if meshQualityEnhancedFilter.applyFilter(): - + try: + meshQualityEnhancedFilter.applyFilter() outputMesh.ShallowCopy( meshQualityEnhancedFilter.getOutput() ) # save to file if asked @@ -249,6 +249,13 @@ def ApplyFilter( self, inputMesh: vtkUnstructuredGrid, outputMesh: vtkUnstructur logger: logging.Logger = meshQualityEnhancedFilter.logger self.saveFile( stats, logger ) self._blockIndex += 1 + except ( ValueError, IndexError, TypeError, AttributeError ) as e: + meshQualityEnhancedFilter.logger.error( + f"The filter { meshQualityEnhancedFilter.logger.name } failed due to:\n{ e }" ) + except Exception as e: + mess: str = f"The filter { meshQualityEnhancedFilter.logger.name } failed due to:\n{ e }" + meshQualityEnhancedFilter.logger.critical( mess, exc_info=True ) + return def saveFile( From 4a1ac35b9d2ba0b9f72724b58ab043e30653a57b Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 25 Nov 2025 10:19:24 +0100 Subject: [PATCH 32/39] fix the doc --- .../geos/processing/generic_processing_tools/AttributeMapping.py | 1 + 1 file changed, 1 insertion(+) 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 f0637b4e..2bb02f6d 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -32,6 +32,7 @@ To use the filter: .. code-block:: python + import logging from geos.processing.generic_processing_tools.AttributeMapping import AttributeMapping From fd9886b6d78a7c2378c63d79fe51b88f7c039915 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 25 Nov 2025 10:38:16 +0100 Subject: [PATCH 33/39] Refactor GeomechanicsCalculator --- .../post_processing/GeomechanicsCalculator.py | 132 ++++++++++-------- .../pv/plugins/PVGeomechanicsCalculator.py | 20 ++- 2 files changed, 93 insertions(+), 59 deletions(-) diff --git a/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py b/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py index 2e7677a9..11f5fc56 100644 --- a/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py +++ b/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py @@ -94,7 +94,13 @@ geomechanicsCalculatorFilter.physicalConstants.frictionAngle = frictionAngle # Do calculations - geomechanicsCalculatorFilter.applyFilter() + try: + geomechanicsCalculatorFilter.applyFilter() + except ( ValueError, TypeError, NameError ) as e: + geomechanicsCalculatorFilter.logger.error( f"The filter { geomechanicsCalculatorFilter.logger.name } failed due to: { e }" ) + except Exception as e: + mess: str = f"The filter { geomechanicsCalculatorFilter.logger.name } failed due to: { e }" + geomechanicsCalculatorFilter.logger.critical( mess, exc_info=True ) # Get the mesh with the geomechanics properties computed as attribute output: vtkUnstructuredGrid @@ -705,54 +711,48 @@ def __init__( self.logger.setLevel( logging.INFO ) self.logger.propagate = False - def applyFilter( self: Self ) -> bool: + def applyFilter( self: Self ) -> None: """Compute the geomechanics properties and create attributes on the mesh. - Returns: - bool: True if the filter succeeded, False otherwise. + Raises: + AttributeError: A mandatory attribute is missing. + ValueError: Something got wrong during the creation of an attribute. """ self.logger.info( f"Apply filter { self.logger.name }." ) - try: - self._checkMandatoryProperties() - self._computeBasicProperties() - - if self.doComputeAdvancedProperties: - self._computeAdvancedProperties() - - # Create an attribute on the mesh for each geomechanics properties computed: - for attribute in self._attributesToCreate: - attributeName: str = attribute.attributeName - onPoints: bool = attribute.isOnPoints - array: npt.NDArray[ np.float64 ] | None - if attribute in ELASTIC_MODULI: - array = self._elasticModuli.getElasticModulusValue( attributeName ) - elif attribute in BASIC_PROPERTIES: - array = self._basicProperties.getBasicPropertyValue( attributeName ) - elif attribute in ADVANCED_PROPERTIES: - array = self._advancedProperties.getAdvancedPropertyValue( attributeName ) - componentNames: tuple[ str, ...] = () - if attribute.nbComponent == 6: - componentNames = ComponentNameEnum.XYZ.value - - createAttribute( self.output, - array, - attributeName, - componentNames=componentNames, - onPoints=onPoints, - logger=self.logger ) - - self.logger.info( "All the geomechanics properties have been added to the mesh." ) - self.logger.info( f"The filter { self.logger.name } succeeded." ) - except ( ValueError, TypeError, NameError ) as e: - self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) - return False - except Exception as e: - mess: str = f"The filter { self.logger.name } failed.\n{ e }" - self.logger.critical( mess, exc_info=True ) - return False - - return True + self._checkMandatoryProperties() + self._computeBasicProperties() + + if self.doComputeAdvancedProperties: + self._computeAdvancedProperties() + + # Create an attribute on the mesh for each geomechanics properties computed: + for attribute in self._attributesToCreate: + attributeName: str = attribute.attributeName + onPoints: bool = attribute.isOnPoints + array: npt.NDArray[ np.float64 ] | None + if attribute in ELASTIC_MODULI: + array = self._elasticModuli.getElasticModulusValue( attributeName ) + elif attribute in BASIC_PROPERTIES: + array = self._basicProperties.getBasicPropertyValue( attributeName ) + elif attribute in ADVANCED_PROPERTIES: + array = self._advancedProperties.getAdvancedPropertyValue( attributeName ) + componentNames: tuple[ str, ...] = () + if attribute.nbComponent == 6: + componentNames = ComponentNameEnum.XYZ.value + + if not createAttribute( self.output, + array, + attributeName, + componentNames=componentNames, + onPoints=onPoints, + logger=self.logger ): + raise ValueError( f"Something got wrong during the creation of the attribute { attributeName }." ) + + self.logger.info( "All the geomechanics properties have been added to the mesh." ) + self.logger.info( f"The filter { self.logger.name } succeeded." ) + + return def getOutput( self: Self ) -> vtkUnstructuredGrid: """Get the mesh with the geomechanics properties computed as attributes. @@ -798,6 +798,9 @@ def _checkMandatoryProperties( self: Self ) -> None: - The density named "density" - The effective stress named "stressEffective" - The initial effective stress named "stressEffectiveInitial" + + Raises: + AttributeError: A mandatory attribute is missing. """ mess: str for elasticModulus in ELASTIC_MODULI: @@ -821,7 +824,7 @@ def _checkMandatoryProperties( self: Self ) -> None: self.computeYoungPoisson = True else: mess = f"{ BULK_MODULUS.attributeName } or { SHEAR_MODULUS.attributeName } are missing to compute geomechanics properties." - raise ValueError( mess ) + raise AttributeError( mess ) elif self._elasticModuli.bulkModulus is None and self._elasticModuli.shearModulus is None: if self._elasticModuli.youngModulus is not None and self._elasticModuli.poissonRatio is not None: self._elasticModuli.bulkModulus = fcts.bulkModulus( self._elasticModuli.youngModulus, @@ -833,10 +836,10 @@ def _checkMandatoryProperties( self: Self ) -> None: self.computeYoungPoisson = False else: mess = f"{ YOUNG_MODULUS.attributeName } or { POISSON_RATIO.attributeName } are missing to compute geomechanics properties." - raise ValueError( mess ) + raise AttributeError( mess ) else: mess = f"{ BULK_MODULUS.attributeName } and { SHEAR_MODULUS.attributeName } or { YOUNG_MODULUS.attributeName } and { POISSON_RATIO.attributeName } are mandatory to compute geomechanics properties." - raise ValueError( mess ) + raise AttributeError( mess ) # Check the presence of the elastic moduli at the initial time. if self._elasticModuli.bulkModulusT0 is None: @@ -846,7 +849,7 @@ def _checkMandatoryProperties( self: Self ) -> None: self._attributesToCreate.append( BULK_MODULUS_T0 ) else: mess = f"{ BULK_MODULUS_T0.attributeName } or { YOUNG_MODULUS_T0.attributeName } and { POISSON_RATIO_T0.attributeName } are mandatory to compute geomechanics properties." - raise ValueError( mess ) + raise AttributeError( mess ) # Check the presence of the other mandatory properties for mandatoryAttribute in MANDATORY_PROPERTIES: @@ -854,7 +857,7 @@ def _checkMandatoryProperties( self: Self ) -> None: mandatoryAttributeOnPoints: bool = mandatoryAttribute.isOnPoints if not isAttributeInObject( self.output, mandatoryAttributeName, mandatoryAttributeOnPoints ): mess = f"The mandatory property { mandatoryAttributeName } is missing to compute geomechanical properties." - raise ValueError( mess ) + raise AttributeError( mess ) else: self._mandatoryProperties.setMandatoryPropertyValue( mandatoryAttributeName, @@ -1059,6 +1062,9 @@ def _computeTotalStresses( self: Self ) -> None: Total stress is computed at the initial and current time steps. Total stress ratio is computed at current time step only. + + Raises: + AttributeError: A mandatory attribute is missing. """ # Compute the total stress at the initial time step. self._doComputeTotalStressInitial() @@ -1072,7 +1078,7 @@ def _computeTotalStresses( self: Self ) -> None: STRESS_TOTAL ) else: mess = f"{ STRESS_TOTAL.attributeName } has not been computed, geomechanics property { STRESS_EFFECTIVE.attributeName } or { BIOT_COEFFICIENT.attributeName } are missing." - raise ValueError( mess ) + raise AttributeError( mess ) # Compute the total stress ratio. if self._basicProperties.totalStress is not None: @@ -1186,7 +1192,11 @@ def _computeElasticStrain( self: Self ) -> None: return def _computeReservoirStressPathReal( self: Self ) -> None: - """Compute reservoir stress paths.""" + """Compute reservoir stress paths. + + Raises: + AttributeError: A mandatory attribute is missing. + """ # create delta stress attribute for QC if not isAttributeInObject( self.output, STRESS_TOTAL_DELTA.attributeName, STRESS_TOTAL_DELTA.isOnPoints ): if self._basicProperties.totalStress is not None and self._basicProperties.totalStressT0 is not None: @@ -1194,7 +1204,7 @@ def _computeReservoirStressPathReal( self: Self ) -> None: self._attributesToCreate.append( STRESS_TOTAL_DELTA ) else: mess: str = f"{ STRESS_TOTAL_DELTA.attributeName } has not been computed, geomechanics properties { STRESS_TOTAL.attributeName } or { STRESS_TOTAL_T0.attributeName } are missing." - raise ValueError( mess ) + raise AttributeError( mess ) else: self._basicProperties.deltaTotalStress = getArrayInObject( self.output, STRESS_TOTAL_DELTA.attributeName, STRESS_TOTAL_DELTA.isOnPoints ) @@ -1244,7 +1254,11 @@ def _computeEffectiveStressRatioOed( self: Self ) -> None: return def _computeCriticalTotalStressRatio( self: Self ) -> None: - """Compute fracture index and fracture threshold.""" + """Compute fracture index and fracture threshold. + + Raises: + AttributeError: A mandatory attribute is missing. + """ mess: str if not isAttributeInObject( self.output, CRITICAL_TOTAL_STRESS_RATIO.attributeName, CRITICAL_TOTAL_STRESS_RATIO.isOnPoints ): @@ -1255,7 +1269,7 @@ def _computeCriticalTotalStressRatio( self: Self ) -> None: self._attributesToCreate.append( CRITICAL_TOTAL_STRESS_RATIO ) else: mess = f"{ CRITICAL_TOTAL_STRESS_RATIO.attributeName } has not been computed, geomechanics property { STRESS_TOTAL.attributeName } is missing." - raise ValueError( mess ) + raise AttributeError( mess ) else: self._advancedProperties.criticalTotalStressRatio = getArrayInObject( self.output, CRITICAL_TOTAL_STRESS_RATIO.attributeName, CRITICAL_TOTAL_STRESS_RATIO.isOnPoints ) @@ -1275,7 +1289,7 @@ def _computeCriticalTotalStressRatio( self: Self ) -> None: self._attributesToCreate.append( TOTAL_STRESS_RATIO_THRESHOLD ) else: mess = f"{ TOTAL_STRESS_RATIO_THRESHOLD.attributeName } has not been computed, geomechanics property { STRESS_TOTAL.attributeName } is missing." - raise ValueError( mess ) + raise AttributeError( mess ) else: self._advancedProperties.stressRatioThreshold = getArrayInObject( self.output, TOTAL_STRESS_RATIO_THRESHOLD.attributeName, TOTAL_STRESS_RATIO_THRESHOLD.isOnPoints ) @@ -1286,7 +1300,11 @@ def _computeCriticalTotalStressRatio( self: Self ) -> None: return def _computeCriticalPorePressure( self: Self ) -> None: - """Compute the critical pore pressure and the pressure index.""" + """Compute the critical pore pressure and the pressure index. + + Raises: + AttributeError: A mandatory attribute is missing. + """ if not isAttributeInObject( self.output, CRITICAL_PORE_PRESSURE.attributeName, CRITICAL_PORE_PRESSURE.isOnPoints ): if self._basicProperties.totalStress is not None: @@ -1297,7 +1315,7 @@ def _computeCriticalPorePressure( self: Self ) -> None: else: mess: str mess = f"{ CRITICAL_PORE_PRESSURE.attributeName } has not been computed, geomechanics property { STRESS_TOTAL.attributeName } is missing." - raise ValueError( mess ) + raise AttributeError( mess ) else: self._advancedProperties.criticalPorePressure = getArrayInObject( self.output, CRITICAL_PORE_PRESSURE.attributeName, diff --git a/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py b/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py index d6b4fa4d..8dfec767 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py +++ b/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py @@ -234,6 +234,7 @@ def ApplyFilter( """ geomechanicsCalculatorFilter: GeomechanicsCalculator outputMesh.ShallowCopy( inputMesh ) + mess: str if isinstance( outputMesh, vtkUnstructuredGrid ): geomechanicsCalculatorFilter = GeomechanicsCalculator( outputMesh, @@ -249,8 +250,16 @@ def ApplyFilter( geomechanicsCalculatorFilter.physicalConstants.rockCohesion = self.rockCohesion geomechanicsCalculatorFilter.physicalConstants.frictionAngle = self.frictionAngle - if geomechanicsCalculatorFilter.applyFilter(): + try: + geomechanicsCalculatorFilter.applyFilter() outputMesh.ShallowCopy( geomechanicsCalculatorFilter.getOutput() ) + except ( ValueError, TypeError, NameError ) as e: + geomechanicsCalculatorFilter.logger.error( + f"The filter { geomechanicsCalculatorFilter.logger.name } failed due to:\n{ e }" ) + except Exception as e: + mess = f"The filter { geomechanicsCalculatorFilter.logger.name } failed due to:\n{ e }" + geomechanicsCalculatorFilter.logger.critical( mess, exc_info=True ) + elif isinstance( outputMesh, vtkMultiBlockDataSet ): volumeBlockIndexes: list[ int ] = getBlockElementIndexesFlatten( outputMesh ) for blockIndex in volumeBlockIndexes: @@ -274,9 +283,16 @@ def ApplyFilter( geomechanicsCalculatorFilter.physicalConstants.rockCohesion = self.rockCohesion geomechanicsCalculatorFilter.physicalConstants.frictionAngle = self.frictionAngle - if geomechanicsCalculatorFilter.applyFilter(): + try: + geomechanicsCalculatorFilter.applyFilter() volumeBlock.ShallowCopy( geomechanicsCalculatorFilter.getOutput() ) volumeBlock.Modified() + except ( ValueError, TypeError, NameError ) as e: + geomechanicsCalculatorFilter.logger.error( + f"The filter { geomechanicsCalculatorFilter.logger.name } failed due to:\n{ e }" ) + except Exception as e: + mess = f"The filter { geomechanicsCalculatorFilter.logger.name } failed due to:\n{ e }" + geomechanicsCalculatorFilter.logger.critical( mess, exc_info=True ) outputMesh.Modified() From 18fc3249ea4e07ddde0b43c1b681f91e0984e5da Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 25 Nov 2025 10:39:46 +0100 Subject: [PATCH 34/39] fix except --- .../geos/processing/post_processing/GeomechanicsCalculator.py | 2 +- geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py b/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py index 11f5fc56..666f58ae 100644 --- a/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py +++ b/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py @@ -96,7 +96,7 @@ # Do calculations try: geomechanicsCalculatorFilter.applyFilter() - except ( ValueError, TypeError, NameError ) as e: + except ( ValueError, AttributeError ) as e: geomechanicsCalculatorFilter.logger.error( f"The filter { geomechanicsCalculatorFilter.logger.name } failed due to: { e }" ) except Exception as e: mess: str = f"The filter { geomechanicsCalculatorFilter.logger.name } failed due to: { e }" diff --git a/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py b/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py index 8dfec767..72995dd7 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py +++ b/geos-pv/src/geos/pv/plugins/PVGeomechanicsCalculator.py @@ -253,7 +253,7 @@ def ApplyFilter( try: geomechanicsCalculatorFilter.applyFilter() outputMesh.ShallowCopy( geomechanicsCalculatorFilter.getOutput() ) - except ( ValueError, TypeError, NameError ) as e: + except ( ValueError, AttributeError ) as e: geomechanicsCalculatorFilter.logger.error( f"The filter { geomechanicsCalculatorFilter.logger.name } failed due to:\n{ e }" ) except Exception as e: @@ -287,7 +287,7 @@ def ApplyFilter( geomechanicsCalculatorFilter.applyFilter() volumeBlock.ShallowCopy( geomechanicsCalculatorFilter.getOutput() ) volumeBlock.Modified() - except ( ValueError, TypeError, NameError ) as e: + except ( ValueError, AttributeError ) as e: geomechanicsCalculatorFilter.logger.error( f"The filter { geomechanicsCalculatorFilter.logger.name } failed due to:\n{ e }" ) except Exception as e: From 6ad6eaad584089a47b05b5bba85f2b0d597ab654 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 25 Nov 2025 11:12:41 +0100 Subject: [PATCH 35/39] Refactor GeosBlockMergeAndExtract --- .../post_processing/GeosBlockExtractor.py | 57 +++++---- .../post_processing/GeosBlockMerge.py | 121 +++++++++--------- .../tests/test_GeosExtractBlock.py | 2 +- .../pv/plugins/PVGeosBlockExtractAndMerge.py | 55 ++++---- .../src/geos/pv/utils/workflowFunctions.py | 8 +- 5 files changed, 127 insertions(+), 116 deletions(-) diff --git a/geos-processing/src/geos/processing/post_processing/GeosBlockExtractor.py b/geos-processing/src/geos/processing/post_processing/GeosBlockExtractor.py index 13d451e4..7f0792e1 100644 --- a/geos-processing/src/geos/processing/post_processing/GeosBlockExtractor.py +++ b/geos-processing/src/geos/processing/post_processing/GeosBlockExtractor.py @@ -48,7 +48,13 @@ geosBlockExtractor.setLoggerHandler( yourHandler ) # Do calculations - geosBlockExtractor.applyFilter() + try: + geosBlockExtractor.applyFilter() + except ( ValueError, TypeError ) as e: + geosBlockExtractor.logger.error( f"The filter { geosBlockExtractor.logger.name } failed due to: { e }." ) + except Exception as e: + mess: str = f"The filter { geosBlockExtractor.logger.name } failed du to: { e }" + geosBlockExtractor.logger.critical( mess, exc_info=True ) # Get the multiBlockDataSet with blocks of the extracted domain. geosDomainExtracted: vtkMultiBlockDataSet @@ -128,6 +134,9 @@ def setExtractedDomain( self: Self, geosDomainName: GeosDomainNameEnum, Args: geosDomainName (GeosDomainNameEnum): Name of the GEOS domain. multiBlockDataSet (vtkMultiBlockDataSet): The mesh to set. + + Raises: + ValueError: The mesh is not a GEOS domain. """ if geosDomainName.value == "CellElementRegion": self.volume = multiBlockDataSet @@ -196,35 +205,27 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: "The logger already has an handler, to use yours set the argument 'speHandler' to True during the filter initialization." ) - def applyFilter( self: Self ) -> bool: + def applyFilter( self: Self ) -> None: """Extract the volume, the fault or the well domain of the mesh from GEOS. - Returns: - bool: True if the filter succeeded, False otherwise. + Raises: + ValueError: The mesh extracted is not a GEOS domain. + TypeError: The mesh extracted has the wrong dimension. """ self.logger.info( f"Apply filter { self.logger.name }." ) - try: - extractGeosDomain: GeosExtractDomainBlock = GeosExtractDomainBlock() - extractGeosDomain.SetInputData( self.geosMesh ) - - domainNames: list = [] - for domain in self.domainToExtract: - extractGeosDomain.RemoveAllIndices() - extractGeosDomain.AddGeosDomainName( domain ) - extractGeosDomain.Update() - self.extractedGeosDomain.setExtractedDomain( domain, extractGeosDomain.GetOutput() ) - domainNames.append( domain.value ) - - self.logger.info( f"The GEOS domain { domainNames } have been extracted." ) - self.logger.info( f"The filter { self.logger.name } succeeded." ) - - except ( ValueError, TypeError ) as e: - self.logger.error( f"The filter { self.logger.name } failed.\n{ e }." ) - return False - except Exception as e: - mess: str = f"The filter { self.logger.name } failed.\n{ e }" - self.logger.critical( mess, exc_info=True ) - return False - - return True + extractGeosDomain: GeosExtractDomainBlock = GeosExtractDomainBlock() + extractGeosDomain.SetInputData( self.geosMesh ) + + domainNames: list = [] + for domain in self.domainToExtract: + extractGeosDomain.RemoveAllIndices() + extractGeosDomain.AddGeosDomainName( domain ) + extractGeosDomain.Update() + self.extractedGeosDomain.setExtractedDomain( domain, extractGeosDomain.GetOutput() ) + domainNames.append( domain.value ) + + self.logger.info( f"The GEOS domain { domainNames } have been extracted." ) + self.logger.info( f"The filter { self.logger.name } succeeded." ) + + return diff --git a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py index f581ee20..07754f70 100644 --- a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py +++ b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py @@ -7,7 +7,6 @@ from vtkmodules.vtkCommonDataModel import vtkCompositeDataSet, vtkMultiBlockDataSet, vtkPolyData, vtkUnstructuredGrid -from geos.utils.Errors import VTKError from geos.utils.Logger import ( Logger, getLogger ) from geos.utils.GeosOutputsConstants import ( PHASE_SEP, PhaseTypeEnum, FluidPrefixEnum, PostProcessingOutputsEnum, getRockSuffixRenaming ) @@ -37,6 +36,7 @@ .. code-block:: python from geos.processing.post_processing.GeosBlockMerge import GeosBlockMerge + from geos.utils.Errors import VTKError # Filter inputs. inputMesh: vtkMultiBlockDataSet @@ -53,7 +53,13 @@ mergeBlockFilter.setLoggerHandler( yourHandler ) # Do calculations - mergeBlockFilter.applyFilter() + try: + mergeBlockFilter.applyFilter() + except ( ValueError, VTKError ) as e: + mergeBlockFilter.logger.error( f"The filter { mergeBlockFilter.logger.name } failed due to: { e }" ) + except Exception as e: + mess: str = f"The filter { mergeBlockFilter.logger.name } failed due to: { e }" + mergeBlockFilter.logger.critical( mess, exc_info=True ) # Get the multiBlockDataSet with one dataSet per region outputMesh: vtkMultiBlockDataSet = mergeBlockFilter.getOutput() @@ -125,69 +131,64 @@ def getOutput( self: Self ) -> vtkMultiBlockDataSet: """Get the mesh with the composite blocks merged.""" return self.outputMesh - def applyFilter( self: Self ) -> bool: + def applyFilter( self: Self ) -> None: """Apply the filter on the mesh. - Returns: - bool: True if the filter succeeded, False otherwise. + Raises: + ValueError: Something got wrong during the creation of an attribute. + VTKError: Error raises during the call of VTK function. """ self.logger.info( f"Apply filter { self.logger.name }." ) - try: - # Display phase names - self.computePhaseNames() - for phase, phaseNames in self.phaseNameDict.items(): - if len( phaseNames ) > 0: - self.logger.info( f"Identified { phase } phase(s) are: { phaseNames }." ) - else: - self.logger.info( f"No { phase } phase has been identified." ) - - # Parse all the composite blocks - compositeBlockIndexesToMerge: dict[ str, int ] = getElementaryCompositeBlockIndexes( self.inputMesh ) - nbBlocks: int = len( compositeBlockIndexesToMerge ) - self.outputMesh.SetNumberOfBlocks( nbBlocks ) - for newIndex, ( blockName, blockIndex ) in enumerate( compositeBlockIndexesToMerge.items() ): - # Set the name of the composite block - self.outputMesh.GetMetaData( newIndex ).Set( vtkCompositeDataSet.NAME(), blockName ) - - # Merge blocks - blockToMerge: vtkMultiBlockDataSet = extractBlock( self.inputMesh, blockIndex ) - volumeMesh: vtkUnstructuredGrid = mergeBlocks( blockToMerge, - keepPartialAttributes=True, - logger=self.logger ) - - # Create index attribute keeping the index in initial mesh - if not createConstantAttribute( volumeMesh, [ blockIndex ], - PostProcessingOutputsEnum.BLOCK_INDEX.attributeName, - onPoints=False, - logger=self.logger ): - self.logger.warning( "BlockIndex attribute was not created." ) - - # Rename attributes - self.renameAttributes( volumeMesh ) - - # Convert the volume mesh to a surface mesh - if self.convertFaultToSurface: - if not isTriangulate( volumeMesh ): - volumeMesh.ShallowCopy( triangulateMesh( volumeMesh, self.logger ) ) - surfaceMesh: vtkPolyData = convertUnstructuredGridToPolyData( volumeMesh, self.logger ) - surfaceMesh.ShallowCopy( computeNormals( surfaceMesh, logger=self.logger ) ) - surfaceMesh.ShallowCopy( computeTangents( surfaceMesh, logger=self.logger ) ) - # Add the merged block to the output mesh - self.outputMesh.SetBlock( newIndex, surfaceMesh ) - else: - self.outputMesh.SetBlock( newIndex, volumeMesh ) - - self.logger.info( f"The filter { self.logger.name } succeeded." ) - except ( ValueError, TypeError, RuntimeError, AssertionError, VTKError ) as e: - self.logger.error( f"The filter { self.logger.name } failed.\n{ e }" ) - return False - except Exception as e: - mess: str = f"The filter { self.logger.name } failed.\n{ e }" - self.logger.critical( mess, exc_info=True ) - return False - - return True + # Display phase names + self.computePhaseNames() + for phase, phaseNames in self.phaseNameDict.items(): + if len( phaseNames ) > 0: + self.logger.info( f"Identified { phase } phase(s) are: { phaseNames }." ) + else: + self.logger.info( f"No { phase } phase has been identified." ) + + # Parse all the composite blocks + compositeBlockIndexesToMerge: dict[ str, int ] = getElementaryCompositeBlockIndexes( self.inputMesh ) + nbBlocks: int = len( compositeBlockIndexesToMerge ) + self.outputMesh.SetNumberOfBlocks( nbBlocks ) + for newIndex, ( blockName, blockIndex ) in enumerate( compositeBlockIndexesToMerge.items() ): + # Set the name of the composite block + self.outputMesh.GetMetaData( newIndex ).Set( vtkCompositeDataSet.NAME(), blockName ) + + # Merge blocks + blockToMerge: vtkMultiBlockDataSet = extractBlock( self.inputMesh, blockIndex ) + volumeMesh: vtkUnstructuredGrid = mergeBlocks( blockToMerge, + keepPartialAttributes=True, + logger=self.logger ) + + # Create index attribute keeping the index in initial mesh + if not createConstantAttribute( volumeMesh, [ blockIndex ], + PostProcessingOutputsEnum.BLOCK_INDEX.attributeName, + onPoints=False, + logger=self.logger ): + raise ValueError( + f"Something got wrong during the creation of the attribute { PostProcessingOutputsEnum.BLOCK_INDEX.attributeName }." + ) + + # Rename attributes + self.renameAttributes( volumeMesh ) + + # Convert the volume mesh to a surface mesh + if self.convertFaultToSurface: + if not isTriangulate( volumeMesh ): + volumeMesh.ShallowCopy( triangulateMesh( volumeMesh, self.logger ) ) + surfaceMesh: vtkPolyData = convertUnstructuredGridToPolyData( volumeMesh, self.logger ) + surfaceMesh.ShallowCopy( computeNormals( surfaceMesh, logger=self.logger ) ) + surfaceMesh.ShallowCopy( computeTangents( surfaceMesh, logger=self.logger ) ) + # Add the merged block to the output mesh + self.outputMesh.SetBlock( newIndex, surfaceMesh ) + else: + self.outputMesh.SetBlock( newIndex, volumeMesh ) + + self.logger.info( f"The filter { self.logger.name } succeeded." ) + + return def renameAttributes( self: Self, diff --git a/geos-processing/tests/test_GeosExtractBlock.py b/geos-processing/tests/test_GeosExtractBlock.py index bfdecf63..2f47b1b2 100644 --- a/geos-processing/tests/test_GeosExtractBlock.py +++ b/geos-processing/tests/test_GeosExtractBlock.py @@ -26,7 +26,7 @@ def test_GeosExtractBlock( multiBlockDataSet: vtkMultiBlockDataSet = dataSetTest( "meshGeosExtractBlockTmp" ) geosBlockExtractor: GeosBlockExtractor = GeosBlockExtractor( multiBlockDataSet, extractFault, extractWell ) - assert geosBlockExtractor.applyFilter() + geosBlockExtractor.applyFilter() extractedVolume: vtkMultiBlockDataSet = geosBlockExtractor.extractedGeosDomain.volume extractedFault: vtkMultiBlockDataSet = geosBlockExtractor.extractedGeosDomain.fault diff --git a/geos-pv/src/geos/pv/plugins/PVGeosBlockExtractAndMerge.py b/geos-pv/src/geos/pv/plugins/PVGeosBlockExtractAndMerge.py index a2de08ea..1587f9e2 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeosBlockExtractAndMerge.py +++ b/geos-pv/src/geos/pv/plugins/PVGeosBlockExtractAndMerge.py @@ -21,6 +21,7 @@ from geos.mesh.utils.arrayModifiers import ( copyAttribute, createCellCenterAttribute ) from geos.mesh.utils.multiblockHelpers import getBlockNames +from geos.utils.Errors import VTKError from geos.utils.GeosOutputsConstants import ( GeosMeshOutputsEnum, GeosDomainNameEnum, getAttributeToTransferFromInitialTime ) @@ -253,25 +254,35 @@ def RequestData( Returns: int: 1 if calculation successfully ended, 0 otherwise. """ - try: - inputMesh: vtkMultiBlockDataSet = vtkMultiBlockDataSet.GetData( inInfoVec[ 0 ] ) - executive = self.GetExecutive() + inputMesh: vtkMultiBlockDataSet = vtkMultiBlockDataSet.GetData( inInfoVec[ 0 ] ) + executive = self.GetExecutive() + mess: str - # First time step, compute the initial properties (useful for geomechanics analyses) - if self.requestDataStep == 0: - self.logger.info( "Apply the plugin for the first time step to get the initial properties." ) + # First time step, compute the initial properties (useful for geomechanics analyses) + if self.requestDataStep == 0: + self.logger.info( + f"Apply the plugin { self.logger.name } for the first time step to get the initial properties." ) + try: doExtractAndMerge( inputMesh, self.outputCellsT0, vtkMultiBlockDataSet(), vtkMultiBlockDataSet(), self.extractFault, self.extractWell ) request.Set( executive.CONTINUE_EXECUTING(), 1 ) - - # Current time step, extract, merge, rename and transfer properties - if self.requestDataStep == self.currentTimeStepIndex: - self.logger.info( f"Apply the filter for the current time step: { self.currentTimeStepIndex }." ) - outputCells: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 0 ) - outputFaults: vtkMultiBlockDataSet = self.GetOutputData( - outInfoVec, 1 ) if self.extractFault else vtkMultiBlockDataSet() - outputWells: vtkMultiBlockDataSet = self.GetOutputData( - outInfoVec, 2 ) if self.extractWell else vtkMultiBlockDataSet() + except ( ValueError, VTKError ) as e: + self.logger.error( f"The plugin { self.logger.name } failed due to:\n{ e }" ) + except Exception as e: + mess = f"The plugin { self.logger.name } failed due to:\n{ e }" + self.logger.critical( mess, exc_info=True ) + + # Current time step, extract, merge, rename and transfer properties + if self.requestDataStep == self.currentTimeStepIndex: + self.logger.info( + f"Apply the plugin { self.logger.name } for the current time step: { self.currentTimeStepIndex }." ) + outputCells: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 0 ) + outputFaults: vtkMultiBlockDataSet = self.GetOutputData( + outInfoVec, 1 ) if self.extractFault else vtkMultiBlockDataSet() + outputWells: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, + 2 ) if self.extractWell else vtkMultiBlockDataSet() + + try: doExtractAndMerge( inputMesh, outputCells, outputFaults, outputWells, self.extractFault, self.extractWell ) @@ -293,12 +304,10 @@ def RequestData( self.requestDataStep = -2 self.logger.info( f"The plugin { self.logger.name } succeeded." ) - except AssertionError as e: - self.logger.error( f"The plugin failed.\n{e}" ) - return 0 - except Exception as e: - mess1: str = "Block extraction and merge failed due to:" - self.logger.critical( mess1 ) - self.logger.critical( e, exc_info=True ) - return 0 + except ( ValueError, VTKError ) as e: + self.logger.error( f"The plugin { self.logger.name } failed due to:\n{ e }" ) + except Exception as e: + mess = f"The plugin { self.logger.name } failed due to:\n{ e }" + self.logger.critical( mess, exc_info=True ) + return 1 diff --git a/geos-pv/src/geos/pv/utils/workflowFunctions.py b/geos-pv/src/geos/pv/utils/workflowFunctions.py index b4fd87aa..1e2a07aa 100644 --- a/geos-pv/src/geos/pv/utils/workflowFunctions.py +++ b/geos-pv/src/geos/pv/utils/workflowFunctions.py @@ -36,8 +36,9 @@ def doExtractAndMerge( speHandler=True ) if not blockExtractor.logger.hasHandlers(): blockExtractor.setLoggerHandler( VTKHandler() ) - if not blockExtractor.applyFilter(): - raise + + blockExtractor.applyFilter() + # recover output objects from GeosBlockExtractor filter and merge internal blocks volumeBlockExtracted: vtkMultiBlockDataSet = blockExtractor.extractedGeosDomain.volume outputCells.ShallowCopy( mergeBlocksFilter( volumeBlockExtracted, False, "Volume" ) ) @@ -77,8 +78,7 @@ def mergeBlocksFilter( mergeBlockFilter: GeosBlockMerge = GeosBlockMerge( mesh, convertSurfaces, True, loggerName ) if not mergeBlockFilter.logger.hasHandlers(): mergeBlockFilter.setLoggerHandler( VTKHandler() ) - if not mergeBlockFilter.applyFilter(): - raise + mergeBlockFilter.applyFilter() mergedBlocks: vtkMultiBlockDataSet = vtkMultiBlockDataSet() mergedBlocks.ShallowCopy( mergeBlockFilter.getOutput() ) return mergedBlocks From 08692f7e235034e0539186cee52f1baf251798f3 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 25 Nov 2025 11:42:08 +0100 Subject: [PATCH 36/39] Refactor SurfaceGeomechanic --- .../post_processing/SurfaceGeomechanics.py | 72 +++++++++++-------- .../tests/test_SurfaceGeomechanics.py | 4 +- .../geos/pv/plugins/PVSurfaceGeomechanics.py | 9 ++- 3 files changed, 51 insertions(+), 34 deletions(-) diff --git a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py index ed0a5a62..bb9098e0 100644 --- a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py +++ b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py @@ -40,6 +40,7 @@ .. code-block:: python from geos.processing.post_processing.SurfaceGeomechanics import SurfaceGeomechanics + from geos.utils.Errors import VTKError # filter inputs inputMesh: vtkPolyData @@ -61,7 +62,13 @@ sg.SetFrictionAngle( frictionAngle ) # Do calculations - sg.applyFilter() + try: + sg.applyFilter() + except ( ValueError, VTKError, AttributeError, AssertionError ) as e: + sg.logger.error( f"The filter { sg.logger.name } failed due to: { e }" ) + except Exception as e: + mess: str = f"The filter { sg.logger.name } failed due to: { e }" + sg.logger.critical( mess, exc_info=True ) # Get output object output: vtkPolyData = sg.GetOutputMesh() @@ -221,11 +228,14 @@ def GetNewAttributeNames( self: Self ) -> set[ str ]: """ return self.newAttributeNames - def applyFilter( self: Self ) -> bool: + def applyFilter( self: Self ) -> None: """Compute Geomechanical properties on input surface. - Returns: - int: 1 if calculation successfully ended, 0 otherwise. + Raises: + ValueError: Errors during the creation of an attribute. + VTKError: Error raises during the call of VTK function. + AttributeError: Attributes must be on cell. + AssertionError: Something got wrong during the shearCapacityUtilization computation. """ msg = f"Applying filter {self.logger.name}" if self.name is not None: @@ -235,28 +245,28 @@ def applyFilter( self: Self ) -> bool: self.logger.info( msg ) - try: - self.outputMesh = vtkPolyData() - self.outputMesh.ShallowCopy( self.inputMesh ) + self.outputMesh = vtkPolyData() + self.outputMesh.ShallowCopy( self.inputMesh ) - # Conversion of attributes from Normal/Tangent basis to xyz basis - if self.convertAttributesOn: - self.logger.info( "Conversion of attributes from local to XYZ basis." ) - self.convertAttributesFromLocalToXYZBasis() + # Conversion of attributes from Normal/Tangent basis to xyz basis + if self.convertAttributesOn: + self.logger.info( "Conversion of attributes from local to XYZ basis." ) + self.convertAttributesFromLocalToXYZBasis() - # Compute shear capacity utilization - self.computeShearCapacityUtilization() + # Compute shear capacity utilization + self.computeShearCapacityUtilization() - self.logger.info( f"Filter {self.logger.name} successfully applied on surface {self.name}." ) - except Exception as e: - mess: str = f"The filter { self.logger.name } failed.\n{ e }" - self.logger.critical( mess, exc_info=True ) - return False + self.logger.info( f"Filter {self.logger.name} successfully applied on surface {self.name}." ) - return True + return def convertAttributesFromLocalToXYZBasis( self: Self ) -> None: - """Convert attributes from local to XYZ basis.""" + """Convert attributes from local to XYZ basis. + + Raises: + ValueError: Something got wrong during the creation of an attribute. + AttributeError: Attributes must be on cell. + """ # Get the list of attributes to convert and filter attributesToConvert: set[ str ] = self.__filterAttributesToConvert() @@ -273,7 +283,7 @@ def convertAttributesFromLocalToXYZBasis( self: Self ) -> None: continue if self.attributeOnPoints: - raise ValueError( + raise AttributeError( "This filter can only convert cell attributes from local to XYZ basis, not point attributes." ) localArray: npt.NDArray[ np.float64 ] = getArrayInObject( self.outputMesh, attrNameLocal, self.attributeOnPoints ) @@ -290,7 +300,7 @@ def convertAttributesFromLocalToXYZBasis( self: Self ) -> None: self.logger.info( f"Attribute {attrNameXYZ} added to the output mesh." ) self.newAttributeNames.add( attrNameXYZ ) else: - raise + raise ValueError( f"Something got wrong during the creation of the attribute { attrNameXYZ }." ) return @@ -356,7 +366,12 @@ def __computeXYZCoordinates( return attrXYZ def computeShearCapacityUtilization( self: Self ) -> None: - """Compute the shear capacity utilization (SCU) on surface.""" + """Compute the shear capacity utilization (SCU) on surface. + + Raises: + ValueError: Something got wrong during the creation of an attribute. + AssertionError: Something got wrong during the shearCapacityUtilization computation. + """ SCUAttributeName: str = PostProcessingOutputsEnum.SCU.attributeName if not isAttributeInObject( self.outputMesh, SCUAttributeName, self.attributeOnPoints ): @@ -367,18 +382,13 @@ def computeShearCapacityUtilization( self: Self ) -> None: # Computation of the shear capacity utilization (SCU) # TODO: better handling of errors in shearCapacityUtilization - try: - scuAttribute: npt.NDArray[ np.float64 ] = fcts.shearCapacityUtilization( - traction, self.rockCohesion, self.frictionAngle ) - except AssertionError: - self.logger.error( f"Failed to compute {SCUAttributeName}." ) - raise + scuAttribute: npt.NDArray[ np.float64 ] = fcts.shearCapacityUtilization( traction, self.rockCohesion, + self.frictionAngle ) # Create attribute if not createAttribute( self.outputMesh, scuAttribute, SCUAttributeName, (), self.attributeOnPoints, logger=self.logger ): - self.logger.error( f"Failed to create attribute {SCUAttributeName}." ) - raise + raise ValueError( f"Failed to create attribute {SCUAttributeName}." ) else: self.logger.info( "SCU computed and added to the output mesh." ) self.newAttributeNames.add( SCUAttributeName ) diff --git a/geos-processing/tests/test_SurfaceGeomechanics.py b/geos-processing/tests/test_SurfaceGeomechanics.py index 7623e802..e42f3110 100644 --- a/geos-processing/tests/test_SurfaceGeomechanics.py +++ b/geos-processing/tests/test_SurfaceGeomechanics.py @@ -69,7 +69,7 @@ def test_SurfaceGeomechanics() -> None: sgFilter: SurfaceGeomechanics = SurfaceGeomechanics( testCase.mesh ) - assert sgFilter.applyFilter() + sgFilter.applyFilter() mesh: vtkPolyData = sgFilter.GetOutputMesh() assert mesh.GetCellData().HasArray( "SCU" ) @@ -81,4 +81,4 @@ def test_failingSurfaceGeomechanics() -> None: failingCase: TriangulatedSurfaceTestCase = TriangulatedSurfaceTestCase( pointsCoords, triangles, None ) sgFilter: SurfaceGeomechanics = SurfaceGeomechanics( failingCase.mesh ) with pytest.raises( AssertionError ): - assert sgFilter.applyFilter() + sgFilter.applyFilter() diff --git a/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py b/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py index 0fc204a7..99d2570c 100644 --- a/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py +++ b/geos-pv/src/geos/pv/plugins/PVSurfaceGeomechanics.py @@ -20,6 +20,7 @@ update_paths() +from geos.utils.Errors import VTKError from geos.utils.PhysicalConstants import ( DEFAULT_FRICTION_ANGLE_DEG, DEFAULT_ROCK_COHESION ) from geos.processing.post_processing.SurfaceGeomechanics import SurfaceGeomechanics from geos.mesh.utils.multiblockHelpers import ( getBlockElementIndexesFlatten, getBlockFromFlatIndex ) @@ -126,8 +127,9 @@ def ApplyFilter( self: Self, inputMesh: vtkMultiBlockDataSet, outputMesh: vtkMul sgFilter.SetRockCohesion( self._getRockCohesion() ) sgFilter.SetFrictionAngle( self._getFrictionAngle() ) - if sgFilter.applyFilter(): + try: + sgFilter.applyFilter() outputSurface: vtkPolyData = sgFilter.GetOutputMesh() # add attributes to output surface mesh @@ -136,6 +138,11 @@ def ApplyFilter( self: Self, inputMesh: vtkMultiBlockDataSet, outputMesh: vtkMul surfaceBlock.GetCellData().AddArray( attr ) surfaceBlock.GetCellData().Modified() surfaceBlock.Modified() + except ( ValueError, VTKError, AttributeError, AssertionError ) as e: + sgFilter.logger.error( f"The filter { sgFilter.logger.name } failed due to:\n{ e }" ) + except Exception as e: + mess: str = f"The filter { sgFilter.logger.name } failed due to:\n{ e }" + sgFilter.logger.critical( mess, exc_info=True ) outputMesh.Modified() return From e62002058302cc7a5be94c3d01f8828d9295f1df Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 25 Nov 2025 11:47:30 +0100 Subject: [PATCH 37/39] Update PVGeomechanicsWorkflow --- .../src/geos/pv/plugins/PVGeomechanicsWorkflow.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflow.py b/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflow.py index 3f674fa6..77029c3b 100644 --- a/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflow.py +++ b/geos-pv/src/geos/pv/plugins/PVGeomechanicsWorkflow.py @@ -15,6 +15,7 @@ update_paths() +from geos.utils.Errors import VTKError from geos.utils.PhysicalConstants import ( DEFAULT_FRICTION_ANGLE_DEG, DEFAULT_GRAIN_BULK_MODULUS, DEFAULT_ROCK_COHESION, WATER_DENSITY ) @@ -334,16 +335,12 @@ def RequestData( self.logger.info( f"The plugin { self.logger.name } succeeded." ) - except AssertionError as e: - mess: str = "Geomechanics workflow failed due to:" - self.logger.error( mess ) - self.logger.error( str( e ) ) - return 0 + except ( ValueError, VTKError, AttributeError, AssertionError ) as e: + self.logger.error( f"The plugin { self.logger.name } failed due to:\n{ e }" ) except Exception as e: - mess1: str = "Geomechanics workflow failed due to:" - self.logger.critical( mess1 ) - self.logger.critical( e, exc_info=True ) - return 0 + mess: str = f"The filter { self.logger.name } failed due to:\n{ e }" + self.logger.critical( mess, exc_info=True ) + return 1 def applyPVGeosBlockExtractAndMerge( self: Self ) -> None: From c6adb4def776d838a10d406c48ce2679fa03f361 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 27 Nov 2025 14:21:28 +0100 Subject: [PATCH 38/39] Apply Paloma's suggestions --- .../AttributeMapping.py | 2 +- .../CreateConstantAttributePerRegion.py | 14 +- .../FillPartialArrays.py | 2 +- .../generic_processing_tools/SplitMesh.py | 2 +- .../post_processing/GeomechanicsCalculator.py | 4 +- .../post_processing/GeosBlockMerge.py | 4 +- .../post_processing/SurfaceGeomechanics.py | 12 +- .../tests/test_AttributeMapping.py | 43 +++-- .../test_CreateConstantAttributePerRegion.py | 170 ++++++++++++------ 9 files changed, 160 insertions(+), 93 deletions(-) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py index 2bb02f6d..598eedc0 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/AttributeMapping.py @@ -142,7 +142,7 @@ def applyFilter( self: Self ) -> None: Mapping the piece of the attributes to transfer. - Raise: + Raises: ValueError: Errors with the input attributeNames or the input mesh. AttributeError: Errors with the attribute of the mesh. """ 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 7c58b437..df737474 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/CreateConstantAttributePerRegion.py @@ -158,7 +158,7 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: def applyFilter( self: Self ) -> None: """Create a constant attribute per region in the mesh. - Raise: + Raises: ValueError: Errors with the input value for the region index or errors during the creation of the new attribute. AttributeError: Errors with the attribute of the mesh. """ @@ -202,7 +202,7 @@ def applyFilter( self: Self ) -> None: self.onPoints ) if len( validIndexes ) == 0: if len( self.dictRegionValues ) == 0: - self.logger.warning( "No region indexes entered." ) + self.logger.warning( "No region index entered." ) else: self.logger.warning( f"The region indexes entered are not in the region attribute { self.regionName }." ) @@ -214,7 +214,7 @@ def applyFilter( self: Self ) -> None: onPoints=self.onPoints, logger=self.logger ): raise ValueError( - f"Something got wrong with the creation of the attribute { self.newAttributeName }." ) + f"Something went wrong with the creation of the attribute { self.newAttributeName }." ) else: if len( invalidIndexes ) > 0: @@ -235,14 +235,14 @@ def applyFilter( self: Self ) -> None: onPoints=self.onPoints, logger=self.logger ): raise ValueError( - f"Something got wrong with the creation of the attribute { self.newAttributeName }." ) + f"Something went wrong with the creation of the attribute { self.newAttributeName }." ) else: validIndexes, invalidIndexes = checkValidValuesInDataSet( self.mesh, self.regionName, listIndexes, self.onPoints ) if len( validIndexes ) == 0: if len( self.dictRegionValues ) == 0: - self.logger.warning( "No region indexes entered." ) + self.logger.warning( "No region index entered." ) else: self.logger.warning( f"The region indexes entered are not in the region attribute { self.regionName }." ) @@ -254,7 +254,7 @@ def applyFilter( self: Self ) -> None: onPoints=self.onPoints, logger=self.logger ): raise ValueError( - f"Something got wrong with the creation of the attribute { self.newAttributeName }." ) + f"Something went wrong with the creation of the attribute { self.newAttributeName }." ) else: if len( invalidIndexes ) > 0: @@ -270,7 +270,7 @@ def applyFilter( self: Self ) -> None: onPoints=self.onPoints, logger=self.logger ): raise ValueError( - f"Something got wrong with the creation of the attribute { self.newAttributeName }." ) + f"Something went wrong with the creation of the attribute { self.newAttributeName }." ) # Log the output message. self._logOutputMessage( validIndexes ) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py index cf2d30c4..2851d033 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/FillPartialArrays.py @@ -138,7 +138,7 @@ def applyFilter( self: Self ) -> None: onPoints=onPoints, listValues=self.dictAttributesValues[ attributeName ], logger=self.logger ): - raise ValueError( "Something got wrong with the filling of partial attributes" ) + raise ValueError( "Something went wrong with the filling of partial attributes" ) self.logger.info( f"The filter { self.logger.name } succeed." ) diff --git a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py index 0f2a4674..faa030d1 100644 --- a/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py +++ b/geos-processing/src/geos/processing/generic_processing_tools/SplitMesh.py @@ -446,7 +446,7 @@ def _transferCellArrays( self: Self, splittedMesh: vtkUnstructuredGrid ) -> None splittedMesh (vtkUnstructuredGrid): Splitted mesh. Raises: - AttributeError: Errors with cell data. + AttributeError: Cell attribute splitted are not defined. """ cellData: vtkCellData = self.inputMesh.GetCellData() if cellData is None: diff --git a/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py b/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py index 666f58ae..ff9a0d88 100644 --- a/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py +++ b/geos-processing/src/geos/processing/post_processing/GeomechanicsCalculator.py @@ -716,7 +716,7 @@ def applyFilter( self: Self ) -> None: Raises: AttributeError: A mandatory attribute is missing. - ValueError: Something got wrong during the creation of an attribute. + ValueError: Something went wrong during the creation of an attribute. """ self.logger.info( f"Apply filter { self.logger.name }." ) @@ -747,7 +747,7 @@ def applyFilter( self: Self ) -> None: componentNames=componentNames, onPoints=onPoints, logger=self.logger ): - raise ValueError( f"Something got wrong during the creation of the attribute { attributeName }." ) + raise ValueError( f"Something went wrong during the creation of the attribute { attributeName }." ) self.logger.info( "All the geomechanics properties have been added to the mesh." ) self.logger.info( f"The filter { self.logger.name } succeeded." ) diff --git a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py index 07754f70..7caab5a8 100644 --- a/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py +++ b/geos-processing/src/geos/processing/post_processing/GeosBlockMerge.py @@ -135,7 +135,7 @@ def applyFilter( self: Self ) -> None: """Apply the filter on the mesh. Raises: - ValueError: Something got wrong during the creation of an attribute. + ValueError: Something went wrong during the creation of an attribute. VTKError: Error raises during the call of VTK function. """ self.logger.info( f"Apply filter { self.logger.name }." ) @@ -168,7 +168,7 @@ def applyFilter( self: Self ) -> None: onPoints=False, logger=self.logger ): raise ValueError( - f"Something got wrong during the creation of the attribute { PostProcessingOutputsEnum.BLOCK_INDEX.attributeName }." + f"Something went wrong during the creation of the attribute { PostProcessingOutputsEnum.BLOCK_INDEX.attributeName }." ) # Rename attributes diff --git a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py index bb9098e0..087dd426 100644 --- a/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py +++ b/geos-processing/src/geos/processing/post_processing/SurfaceGeomechanics.py @@ -235,7 +235,7 @@ def applyFilter( self: Self ) -> None: ValueError: Errors during the creation of an attribute. VTKError: Error raises during the call of VTK function. AttributeError: Attributes must be on cell. - AssertionError: Something got wrong during the shearCapacityUtilization computation. + AssertionError: Something went wrong during the shearCapacityUtilization computation. """ msg = f"Applying filter {self.logger.name}" if self.name is not None: @@ -264,7 +264,7 @@ def convertAttributesFromLocalToXYZBasis( self: Self ) -> None: """Convert attributes from local to XYZ basis. Raises: - ValueError: Something got wrong during the creation of an attribute. + ValueError: Something went wrong during the creation of an attribute. AttributeError: Attributes must be on cell. """ # Get the list of attributes to convert and filter @@ -300,7 +300,7 @@ def convertAttributesFromLocalToXYZBasis( self: Self ) -> None: self.logger.info( f"Attribute {attrNameXYZ} added to the output mesh." ) self.newAttributeNames.add( attrNameXYZ ) else: - raise ValueError( f"Something got wrong during the creation of the attribute { attrNameXYZ }." ) + raise ValueError( f"Something went wrong during the creation of the attribute { attrNameXYZ }." ) return @@ -353,7 +353,7 @@ def __computeXYZCoordinates( for i, cellAttribute in enumerate( attrArray ): if len( cellAttribute ) not in ( 3, 6, 9 ): raise ValueError( - f"Inconsistent number of components for attribute. Expected 3, 6 or 9 but got { len( cellAttribute.shape ) }." + f"Inconsistent number of components for attribute. Expected 3, 6 or 9 but went { len( cellAttribute.shape ) }." ) # Compute attribute XYZ components @@ -369,8 +369,8 @@ def computeShearCapacityUtilization( self: Self ) -> None: """Compute the shear capacity utilization (SCU) on surface. Raises: - ValueError: Something got wrong during the creation of an attribute. - AssertionError: Something got wrong during the shearCapacityUtilization computation. + ValueError: Something went wrong during the creation of an attribute. + AssertionError: Something went wrong during the shearCapacityUtilization computation. """ SCUAttributeName: str = PostProcessingOutputsEnum.SCU.attributeName diff --git a/geos-processing/tests/test_AttributeMapping.py b/geos-processing/tests/test_AttributeMapping.py index 00d0c8a7..39a7fe7d 100644 --- a/geos-processing/tests/test_AttributeMapping.py +++ b/geos-processing/tests/test_AttributeMapping.py @@ -33,29 +33,42 @@ def test_AttributeMapping( attributeMappingFilter.applyFilter() -@pytest.mark.parametrize( "meshFromName, meshToName, attributeNames, onPoints, error", [ - ( "dataset", "emptydataset", {}, False, "ValueError" ), - ( "dataset", "emptydataset", { "Fault" }, False, "AttributeError" ), - ( "dataset", "dataset", { "GLOBAL_IDS_CELLS" }, False, "AttributeError" ), - ( "multiblock", "emptymultiblock", { "FAULT" }, False, "AttributeError" ), - ( "dataset", "emptyFracture", { "FAULT" }, False, "ValueError" ), +@pytest.mark.parametrize( "meshFromName, meshToName, attributeNames, onPoints", [ + ( "dataset", "emptydataset", { "Fault" }, False ), # Attribute not in the mesh from + ( "dataset", "dataset", { "GLOBAL_IDS_CELLS" }, False ), # Attribute on both meshes + ( "multiblock", "emptymultiblock", { "FAULT" }, False ), # Partial attribute in the mesh from +] ) +def test_AttributeMappingRaisesAttributeError( + dataSetTest: Any, + meshFromName: str, + meshToName: str, + attributeNames: set[ str ], + onPoints: bool, +) -> None: + """Test the fails of the filter with attributes issues.""" + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshFromName ) + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshToName ) + attributeMappingFilter: AttributeMapping = AttributeMapping( meshFrom, meshTo, attributeNames, onPoints ) + + with pytest.raises( AttributeError ): + attributeMappingFilter.applyFilter() + + +@pytest.mark.parametrize( "meshFromName, meshToName, attributeNames, onPoints", [ + ( "dataset", "emptydataset", {}, False ), # no attribute to map + ( "dataset", "emptyFracture", { "FAULT" }, False ), # meshes with same type but different cells dimension ] ) -def test_AttributeMappingRaises( +def test_AttributeMappingRaisesValueError( dataSetTest: Any, meshFromName: str, meshToName: str, attributeNames: set[ str ], onPoints: bool, - error: str, ) -> None: - """Test the fails of the filter.""" + """Test the fails of the filter with input value issue.""" meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshFromName ) meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshToName ) attributeMappingFilter: AttributeMapping = AttributeMapping( meshFrom, meshTo, attributeNames, onPoints ) - if error == "AttributeError": - with pytest.raises( AttributeError ): - attributeMappingFilter.applyFilter() - elif error == "ValueError": - with pytest.raises( ValueError ): - attributeMappingFilter.applyFilter() + with pytest.raises( ValueError ): + attributeMappingFilter.applyFilter() \ No newline at end of file diff --git a/geos-processing/tests/test_CreateConstantAttributePerRegion.py b/geos-processing/tests/test_CreateConstantAttributePerRegion.py index 47e08f5e..388fee0c 100644 --- a/geos-processing/tests/test_CreateConstantAttributePerRegion.py +++ b/geos-processing/tests/test_CreateConstantAttributePerRegion.py @@ -13,88 +13,149 @@ @pytest.mark.parametrize( - "meshType, newAttributeName, regionName, dictRegionValues, componentNames, componentNamesTest, valueNpType, error", + "meshType, newAttributeName, regionName, dictRegionValues, componentNames, componentNamesTest, valueNpType", [ # Test the name of the new attribute (new on the mesh, one present on the other piece). ## For vtkDataSet. - ( "dataset", "newAttribute", "GLOBAL_IDS_POINTS", {}, (), (), np.float32, "None" ), - ( "dataset", "CellAttribute", "GLOBAL_IDS_POINTS", {}, (), (), np.float32, "None" ), + ( "dataset", "newAttribute", "GLOBAL_IDS_POINTS", {}, (), (), np.float32 ), + ( "dataset", "CellAttribute", "GLOBAL_IDS_POINTS", {}, (), (), np.float32 ), ## For vtkMultiBlockDataSet. - ( "multiblock", "newAttribute", "GLOBAL_IDS_POINTS", {}, (), (), np.float32, "None" ), - ( "multiblock", "CellAttribute", "GLOBAL_IDS_POINTS", {}, (), (), np.float32, "None" ), - ( "multiblock", "GLOBAL_IDS_CELLS", "GLOBAL_IDS_POINTS", {}, (), (), np.float32, "None" ), + ( "multiblock", "newAttribute", "GLOBAL_IDS_POINTS", {}, (), (), np.float32 ), + ( "multiblock", "CellAttribute", "GLOBAL_IDS_POINTS", {}, (), (), np.float32 ), + ( "multiblock", "GLOBAL_IDS_CELLS", "GLOBAL_IDS_POINTS", {}, (), (), np.float32 ), # Test if the region attribute is on cells or on points. - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.float32, "None" ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.float32 ), # Test the component name. - ( "dataset", "newAttribute", "FAULT", {}, ( "X" ), (), np.float32, "None" ), - ( "dataset", "newAttribute", "FAULT", {}, (), ( "Component0", "Component1" ), np.float32, "None" ), - ( "dataset", "newAttribute", "FAULT", {}, ( "X" ), ( "Component0", "Component1" ), np.float32, "None" ), - ( "dataset", "newAttribute", "FAULT", {}, ( "X", "Y" ), ( "X", "Y" ), np.float32, "None" ), - ( "dataset", "newAttribute", "FAULT", {}, ( "X", "Y", "Z" ), ( "X", "Y" ), np.float32, "None" ), + ( "dataset", "newAttribute", "FAULT", {}, ( "X" ), (), np.float32 ), + ( "dataset", "newAttribute", "FAULT", {}, (), ( "Component0", "Component1" ), np.float32 ), + ( "dataset", "newAttribute", "FAULT", {}, ( "X" ), ( "Component0", "Component1" ), np.float32 ), + ( "dataset", "newAttribute", "FAULT", {}, ( "X", "Y" ), ( "X", "Y" ), np.float32 ), + ( "dataset", "newAttribute", "FAULT", {}, ( "X", "Y", "Z" ), ( "X", "Y" ), np.float32 ), # Test the type of value. - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.int8, "None" ), - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.int16, "None" ), - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.int32, "None" ), - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.int64, "None" ), - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.uint8, "None" ), - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.uint16, "None" ), - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.uint32, "None" ), - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.uint64, "None" ), - ( "dataset", "newAttribute", "FAULT", {}, (), (), np.float64, "None" ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.int8 ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.int16 ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.int32 ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.int64 ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.uint8 ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.uint16 ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.uint32 ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.uint64 ), + ( "dataset", "newAttribute", "FAULT", {}, (), (), np.float64 ), # Test index/value. ( "dataset", "newAttribute", "FAULT", { 0: [ 0 ], - 100: [ 1 ] - }, (), (), np.float32, "None" ), + 100: [ 1 ], + }, (), (), np.float32 ), ( "dataset", "newAttribute", "FAULT", { 0: [ 0 ], 100: [ 1 ], - 101: [ 2 ] - }, (), (), np.float32, "None" ), + 101: [ 2 ], + }, (), (), np.float32 ), ( "dataset", "newAttribute", "FAULT", { 0: [ 0 ], 100: [ 1 ], 101: [ 2 ], - 2: [ 3 ] - }, (), (), np.float32, "None" ), + 2: [ 3 ], + }, (), (), np.float32 ), ( "dataset", "newAttribute", "FAULT", { 0: [ 0, 0 ], - 100: [ 1, 1 ] - }, (), ( "Component0", "Component1" ), np.float32, "None" ), + 100: [ 1, 1 ], + }, (), ( "Component0", "Component1" ), np.float32 ), ( "dataset", "newAttribute", "FAULT", { 0: [ 0, 0 ], 100: [ 1, 1 ], - 101: [ 2, 2 ] - }, (), ( "Component0", "Component1" ), np.float32, "None" ), + 101: [ 2, 2 ], + }, (), ( "Component0", "Component1" ), np.float32 ), ( "dataset", "newAttribute", "FAULT", { 0: [ 0, 0 ], 100: [ 1, 1 ], 101: [ 2, 2 ], - 2: [ 3, 3 ] - }, (), ( "Component0", "Component1" ), np.float32, "None" ), - # Test common error. - ## Number of components. + 2: [ 3, 3 ], + }, (), ( "Component0", "Component1" ), np.float32 ), + ] ) +def test_CreateConstantAttributePerRegion( + dataSetTest: Union[ vtkMultiBlockDataSet, vtkDataSet ], + meshType: str, + newAttributeName: str, + regionName: str, + dictRegionValues: dict[ Any, Any ], + componentNames: tuple[ str, ...], + componentNamesTest: tuple[ str, ...], + valueNpType: int, +) -> None: + """Test CreateConstantAttributePerRegion.""" + mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ] = dataSetTest( meshType ) + nbComponents: int = len( componentNamesTest ) + if nbComponents == 0: # If the attribute has one component, the component has no name. + nbComponents += 1 + + createConstantAttributePerRegionFilter: CreateConstantAttributePerRegion = CreateConstantAttributePerRegion( + mesh, + regionName, + dictRegionValues, + newAttributeName, + valueNpType=valueNpType, + nbComponents=nbComponents, + componentNames=componentNames, + ) + + createConstantAttributePerRegionFilter.applyFilter() + + +@pytest.mark.parametrize( + "meshType, newAttributeName, regionName, dictRegionValues, componentNames, componentNamesTest, valueNpType", + [ + ( "dataset", "newAttribute", "PERM", {}, (), (), np.float32 ), # Region attribute has too many components + ( "multiblock", "newAttribute", "FAULT", {}, (), (), np.float32 ), # Region attribute is partial. + ] ) +def test_CreateConstantAttributePerRegionRaisesAttributeError( + dataSetTest: Union[ vtkMultiBlockDataSet, vtkDataSet ], + meshType: str, + newAttributeName: str, + regionName: str, + dictRegionValues: dict[ Any, Any ], + componentNames: tuple[ str, ...], + componentNamesTest: tuple[ str, ...], + valueNpType: int, +) -> None: + """Test tes fails of CreateConstantAttributePerRegion with attributes issues.""" + mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ] = dataSetTest( meshType ) + nbComponents: int = len( componentNamesTest ) + if nbComponents == 0: # If the attribute has one component, the component has no name. + nbComponents += 1 + + createConstantAttributePerRegionFilter: CreateConstantAttributePerRegion = CreateConstantAttributePerRegion( + mesh, + regionName, + dictRegionValues, + newAttributeName, + valueNpType=valueNpType, + nbComponents=nbComponents, + componentNames=componentNames, + ) + + with pytest.raises( AttributeError ): + createConstantAttributePerRegionFilter.applyFilter() + + +@pytest.mark.parametrize( + "meshType, newAttributeName, regionName, dictRegionValues, componentNames, componentNamesTest, valueNpType", + [ ( "dataset", "newAttribute", "FAULT", { 0: [ 0 ], - 100: [ 1, 1 ] - }, (), (), np.float32, "ValueError" ), # Number of value inconsistent. + 100: [ 1, 1 ], + }, (), (), np.float32 ), # Number of value inconsistent. ( "dataset", "newAttribute", "FAULT", { 0: [ 0, 0 ], - 100: [ 1, 1 ] - }, (), (), np.float32, "ValueError" ), # More values than components. + 100: [ 1, 1 ], + }, (), (), np.float32 ), # More values than components. ( "dataset", "newAttribute", "FAULT", { 0: [ 0 ], - 100: [ 1 ] - }, ( "X", "Y" ), ( "X", "Y" ), np.float32, "ValueError" ), # More components than value. - ## Attribute name. - ( "dataset", "PERM", "FAULT", {}, (), (), np.float32, "ValueError" ), # The attribute name already exist. - ## Region attribute. - ( "dataset", "newAttribute", "PERM", {}, (), - (), np.float32, "AttributeError" ), # Region attribute has too many components. - ( "multiblock", "newAttribute", "FAULT", {}, (), - (), np.float32, "AttributeError" ), # Region attribute is partial. + 100: [ 1 ], + }, ( "X", "Y" ), ( "X", "Y" ), np.float32 ), # More components than value. + ( "dataset", "PERM", "FAULT", {}, (), (), np.float32 ), # The attribute name already exist in the mesh. ] ) -def test_CreateConstantAttributePerRegion( +def test_CreateConstantAttributePerRegionRaisesValueError( dataSetTest: Union[ vtkMultiBlockDataSet, vtkDataSet ], meshType: str, newAttributeName: str, @@ -103,9 +164,8 @@ def test_CreateConstantAttributePerRegion( componentNames: tuple[ str, ...], componentNamesTest: tuple[ str, ...], valueNpType: int, - error: str, ) -> None: - """Test CreateConstantAttributePerRegion.""" + """Test the fails of CreateConstantAttributePerRegion with inputs value issues.""" mesh: Union[ vtkMultiBlockDataSet, vtkDataSet ] = dataSetTest( meshType ) nbComponents: int = len( componentNamesTest ) if nbComponents == 0: # If the attribute has one component, the component has no name. @@ -121,11 +181,5 @@ def test_CreateConstantAttributePerRegion( componentNames=componentNames, ) - if error == "None": + with pytest.raises( ValueError ): createConstantAttributePerRegionFilter.applyFilter() - elif error == "AttributeError": - with pytest.raises( AttributeError ): - createConstantAttributePerRegionFilter.applyFilter() - elif error == "ValueError": - with pytest.raises( ValueError ): - createConstantAttributePerRegionFilter.applyFilter() From 134293d154406efaaebda6c2217617577e7a2ca9 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 27 Nov 2025 14:26:29 +0100 Subject: [PATCH 39/39] Fix ci --- .../tests/test_AttributeMapping.py | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/geos-processing/tests/test_AttributeMapping.py b/geos-processing/tests/test_AttributeMapping.py index 39a7fe7d..9494a2cc 100644 --- a/geos-processing/tests/test_AttributeMapping.py +++ b/geos-processing/tests/test_AttributeMapping.py @@ -33,11 +33,13 @@ def test_AttributeMapping( attributeMappingFilter.applyFilter() -@pytest.mark.parametrize( "meshFromName, meshToName, attributeNames, onPoints", [ - ( "dataset", "emptydataset", { "Fault" }, False ), # Attribute not in the mesh from - ( "dataset", "dataset", { "GLOBAL_IDS_CELLS" }, False ), # Attribute on both meshes - ( "multiblock", "emptymultiblock", { "FAULT" }, False ), # Partial attribute in the mesh from -] ) +@pytest.mark.parametrize( + "meshFromName, meshToName, attributeNames, onPoints", + [ + ( "dataset", "emptydataset", { "Fault" }, False ), # Attribute not in the mesh from + ( "dataset", "dataset", { "GLOBAL_IDS_CELLS" }, False ), # Attribute on both meshes + ( "multiblock", "emptymultiblock", { "FAULT" }, False ), # Partial attribute in the mesh from + ] ) def test_AttributeMappingRaisesAttributeError( dataSetTest: Any, meshFromName: str, @@ -54,10 +56,12 @@ def test_AttributeMappingRaisesAttributeError( attributeMappingFilter.applyFilter() -@pytest.mark.parametrize( "meshFromName, meshToName, attributeNames, onPoints", [ - ( "dataset", "emptydataset", {}, False ), # no attribute to map - ( "dataset", "emptyFracture", { "FAULT" }, False ), # meshes with same type but different cells dimension -] ) +@pytest.mark.parametrize( + "meshFromName, meshToName, attributeNames, onPoints", + [ + ( "dataset", "emptydataset", {}, False ), # no attribute to map + ( "dataset", "emptyFracture", { "FAULT" }, False ), # meshes with same type but different cells dimension + ] ) def test_AttributeMappingRaisesValueError( dataSetTest: Any, meshFromName: str, @@ -71,4 +75,4 @@ def test_AttributeMappingRaisesValueError( attributeMappingFilter: AttributeMapping = AttributeMapping( meshFrom, meshTo, attributeNames, onPoints ) with pytest.raises( ValueError ): - attributeMappingFilter.applyFilter() \ No newline at end of file + attributeMappingFilter.applyFilter()