From 02ff32b80060e2006083fa214cf14e75e6619118 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 10 Dec 2025 10:48:24 +0100 Subject: [PATCH 1/5] Fix plugin's categorie --- .../src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py b/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py index 067d36f83..3203323fc 100644 --- a/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py +++ b/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py @@ -73,7 +73,7 @@ * Extract a few number of cells with the `ExtractSelection` ParaView Filter, then use the `MergeBlocks` ParaView Filter * Select the resulting mesh in the pipeline -* Select the filter: Filters > { FilterCategory.GENERIC_PROCESSING.value } > Plot Mohr's Circle +* Select the filter: Filters > { FilterCategory.GEOS_POST_PROCESSING.value } > Plot Mohr's Circle * Select the cell Ids and time steps you want * (Optional) Set rock cohesion and/or friction angle * Apply @@ -89,7 +89,7 @@ @smproxy.filter( name="PVMohrCirclePlot", label="Plot Mohr's Circles" ) @smhint.xml( f""" - + """ ) @smproperty.input( name="Input", port_index=0 ) From afcf70fa73a73b35995584f3710727b373215349 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 10 Dec 2025 13:30:54 +0100 Subject: [PATCH 2/5] sort the morh circle by cell indexes --- .../plugins/post_processing/PVMohrCirclePlot.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py b/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py index 3203323fc..a845a8d4a 100644 --- a/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py +++ b/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py @@ -766,6 +766,7 @@ def RequestData( assert self.pythonView is not None, "No Python View was found." self._defineCurvesAspect() mohrCircles: list[ MohrCircle ] = self._filterMohrCircles() + print( mohrCircles ) self.pythonView.Script = mcf.buildPythonViewScript( geos_pv_path, @@ -812,12 +813,21 @@ def _createMohrCirclesAtTimeStep( def _filterMohrCircles( self: Self ) -> list[ MohrCircle ]: """Filter the list of all MohrCircle to get those to plot. + Mohr circles are sort by cell indexes first then by timesteps. + Returns: list[MohrCircle]: list of MohrCircle to plot. """ # Circle ids to plot circleIds: list[ str ] = self._getCircleIds() - return [ mohrCircle for mohrCircle in self.mohrCircles if mohrCircle.getCircleId() in circleIds ] + mohrCircleToPlot: list[ MohrCircle ] = [ MohrCircle( "-1" ) for i in range( len( circleIds ) ) ] + for mohrCircle in self.mohrCircles: + try: + mohrCircleToPlot[ circleIds.index( str( mohrCircle.getCircleId() ) ) ] = mohrCircle + except ValueError: + continue + + return mohrCircleToPlot def _updateRequestedTimeSteps( self: Self ) -> None: """Update the requestedTimeStepsIndexes attribute from user choice.""" @@ -842,13 +852,15 @@ def _getUserChoices( self: Self ) -> dict[ str, Any ]: def _getCircleIds( self: Self ) -> list[ str ]: """Get circle ids to plot. + This list of circle indexes is sort by cell indexes first then by timesteps + Returns: list[str]: list of circle ids to plot. """ cellIds: list[ str ] = pvt.getArrayChoices( self.a01GetCellIdsDAS() ) timeSteps: list[ str ] = pvt.getArrayChoices( self.a02GetTimestepsToPlot() ) - return [ mcf.getMohrCircleId( cellId, timeStep ) for timeStep in timeSteps for cellId in cellIds ] + return [ mcf.getMohrCircleId( cellId, timeStep ) for cellId in cellIds for timeStep in timeSteps ] def _defineCurvesAspect( self: Self ) -> None: """Add curve aspect parameters according to user choices.""" From 81edf23c6060a4504414a309be19dc63cdded6c0 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 10 Dec 2025 19:24:17 +0100 Subject: [PATCH 3/5] add the cell id anotation on paraview --- .../post_processing/PVMohrCirclePlot.py | 75 +++++++++++++++++-- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py b/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py index a845a8d4a..4e2992762 100644 --- a/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py +++ b/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py @@ -17,11 +17,11 @@ # 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 vtk 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, vtkMultiBlockDataSet # Update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent.parent @@ -104,7 +104,10 @@ def __init__( self: Self ) -> None: Mohr's circles are plotted using a Python View. """ - super().__init__( nInputPorts=1, nOutputPorts=1, outputType="vtkDataObject" ) + super().__init__( nInputPorts=1, + nOutputPorts=1, + inputType="vtkUnstructuredGrid", + outputType="vtkMultiBlockDataSet" ) # Create a new PythonView self.pythonView: Any = buildNewLayoutWithPythonView() @@ -719,9 +722,9 @@ def RequestDataObject( 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 ) + if outData is None or ( not outData.IsA( "vtkMultiBlockDataSet" ) ): + outData = vtk.vtkMultiBlockDataSet() + outInfoVec.GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), outData ) # type: ignore return super().RequestDataObject( request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] def RequestData( @@ -766,7 +769,6 @@ def RequestData( assert self.pythonView is not None, "No Python View was found." self._defineCurvesAspect() mohrCircles: list[ MohrCircle ] = self._filterMohrCircles() - print( mohrCircles ) self.pythonView.Script = mcf.buildPythonViewScript( geos_pv_path, @@ -775,8 +777,67 @@ def RequestData( self.frictionAngle, self._getUserChoices(), ) + Render() + # Cell indexes annotation + nbCells = inputMesh.GetNumberOfCells() + inputData = inputMesh.NewInstance() + inputData.ShallowCopy( inputMesh ) + outputMesh: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 0 ) + + cellId = vtk.vtkStringArray() + cellId.SetName( "cellId" ) + cellId.SetNumberOfValues( nbCells ) + + cellMask = vtk.vtkIntArray() + cellMask.SetName( "CellMask" ) + cellMask.SetNumberOfValues( nbCells ) + + selected_local = set() + originalCellIds = inputMesh.GetCellData().GetArray( "vtkOriginalCellIds" ) + for localCellId in range( nbCells ): + if str( originalCellIds.GetValue( localCellId ) ) in self.requestedCellIds: + selected_local.add( localCellId ) + cellId.SetValue( localCellId, f"{ originalCellIds.GetValue( localCellId ) }" ) + cellMask.SetValue( localCellId, 1 ) + else: + cellMask.SetValue( localCellId, 0 ) + + inputData.GetCellData().AddArray( cellId ) + inputData.GetCellData().AddArray( cellMask ) + + idBlock = 0 + for localCellId in selected_local: + globalCellId = f"{ originalCellIds.GetValue( localCellId ) }" + text = vtk.vtkVectorText() + text.SetText( globalCellId ) + text.Update() + + cellBounds = inputMesh.GetCell( localCellId ).GetBounds() + transformFilter = vtk.vtkTransform() + transformFilter.Translate( cellBounds[ 1 ], cellBounds[ 3 ], cellBounds[ 5 ] ) + + scaleX = ( cellBounds[ 1 ] - cellBounds[ 0 ] ) / 4 + scaleY = ( cellBounds[ 3 ] - cellBounds[ 2 ] ) / 4 + scaleZ = ( cellBounds[ 5 ] - cellBounds[ 4 ] ) / 4 + transformFilter.Scale( scaleX, scaleY, scaleZ ) + + transformFromPolyDataFilter = vtk.vtkTransformPolyDataFilter() + transformFromPolyDataFilter.SetTransform( transformFilter ) + transformFromPolyDataFilter.SetInputData( text.GetOutput() ) + transformFromPolyDataFilter.Update() + + meshText = vtk.vtkPolyData() + meshText.ShallowCopy( transformFromPolyDataFilter.GetOutput() ) + + outputMesh.SetBlock( idBlock, meshText ) + outputMesh.GetMetaData( idBlock ).Set( vtk.vtkCompositeDataSet.NAME(), f"Cell_{ globalCellId }" ) + idBlock += 1 + + outputMesh.SetBlock( idBlock, inputData ) + outputMesh.GetMetaData( idBlock ).Set( vtk.vtkCompositeDataSet.NAME(), "Input Data" ) + except Exception as e: self.logger.error( "Mohr circles cannot be plotted due to:" ) self.logger.error( e ) From 1e3661cf3cac3f9e0a4f5993785cbdca3fe4fc42 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 11 Dec 2025 17:04:43 +0100 Subject: [PATCH 4/5] Update vtk import --- .../post_processing/PVMohrCirclePlot.py | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py b/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py index 4e2992762..3c6f38aa6 100644 --- a/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py +++ b/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py @@ -7,6 +7,7 @@ from pathlib import Path from enum import Enum from typing import Any, Union, cast +from typing_extensions import Self import numpy as np import numpy.typing as npt @@ -17,11 +18,13 @@ # 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 vtk -from typing_extensions import Self + from vtkmodules.vtkCommonCore import vtkDataArraySelection as vtkDAS -from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector -from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid, vtkMultiBlockDataSet +from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector, vtkStringArray, vtkIntArray +from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid, vtkMultiBlockDataSet, vtkPolyData, vtkCompositeDataSet +from vtkmodules.vtkCommonTransforms import vtkTransform +from vtkmodules.vtkRenderingFreeType import vtkVectorText +from vtkmodules.vtkFiltersGeneral import vtkTransformPolyDataFilter # Update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent.parent @@ -723,7 +726,7 @@ def RequestDataObject( assert inData is not None if outData is None or ( not outData.IsA( "vtkMultiBlockDataSet" ) ): - outData = vtk.vtkMultiBlockDataSet() + outData = vtkMultiBlockDataSet() outInfoVec.GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), outData ) # type: ignore return super().RequestDataObject( request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] @@ -786,11 +789,11 @@ def RequestData( inputData.ShallowCopy( inputMesh ) outputMesh: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 0 ) - cellId = vtk.vtkStringArray() + cellId = vtkStringArray() cellId.SetName( "cellId" ) cellId.SetNumberOfValues( nbCells ) - cellMask = vtk.vtkIntArray() + cellMask = vtkIntArray() cellMask.SetName( "CellMask" ) cellMask.SetNumberOfValues( nbCells ) @@ -810,12 +813,12 @@ def RequestData( idBlock = 0 for localCellId in selected_local: globalCellId = f"{ originalCellIds.GetValue( localCellId ) }" - text = vtk.vtkVectorText() + text = vtkVectorText() text.SetText( globalCellId ) text.Update() cellBounds = inputMesh.GetCell( localCellId ).GetBounds() - transformFilter = vtk.vtkTransform() + transformFilter = vtkTransform() transformFilter.Translate( cellBounds[ 1 ], cellBounds[ 3 ], cellBounds[ 5 ] ) scaleX = ( cellBounds[ 1 ] - cellBounds[ 0 ] ) / 4 @@ -823,20 +826,20 @@ def RequestData( scaleZ = ( cellBounds[ 5 ] - cellBounds[ 4 ] ) / 4 transformFilter.Scale( scaleX, scaleY, scaleZ ) - transformFromPolyDataFilter = vtk.vtkTransformPolyDataFilter() + transformFromPolyDataFilter = vtkTransformPolyDataFilter() transformFromPolyDataFilter.SetTransform( transformFilter ) transformFromPolyDataFilter.SetInputData( text.GetOutput() ) transformFromPolyDataFilter.Update() - meshText = vtk.vtkPolyData() + meshText = vtkPolyData() meshText.ShallowCopy( transformFromPolyDataFilter.GetOutput() ) outputMesh.SetBlock( idBlock, meshText ) - outputMesh.GetMetaData( idBlock ).Set( vtk.vtkCompositeDataSet.NAME(), f"Cell_{ globalCellId }" ) + outputMesh.GetMetaData( idBlock ).Set( vtkCompositeDataSet.NAME(), f"Cell_{ globalCellId }" ) idBlock += 1 outputMesh.SetBlock( idBlock, inputData ) - outputMesh.GetMetaData( idBlock ).Set( vtk.vtkCompositeDataSet.NAME(), "Input Data" ) + outputMesh.GetMetaData( idBlock ).Set( vtkCompositeDataSet.NAME(), "Input Data" ) except Exception as e: self.logger.error( "Mohr circles cannot be plotted due to:" ) From 9f72944d75ba8f0379cb22083c0c857a30090ea8 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 23 Jan 2026 10:30:48 +0100 Subject: [PATCH 5/5] keep only the mask strategie to anotate the mesh --- .../post_processing/PVMohrCirclePlot.py | 70 +++++-------------- 1 file changed, 18 insertions(+), 52 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py b/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py index 3c6f38aa6..ff801ebb2 100644 --- a/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py +++ b/geos-pv/src/geos/pv/plugins/post_processing/PVMohrCirclePlot.py @@ -21,10 +21,7 @@ from vtkmodules.vtkCommonCore import vtkDataArraySelection as vtkDAS from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector, vtkStringArray, vtkIntArray -from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid, vtkMultiBlockDataSet, vtkPolyData, vtkCompositeDataSet -from vtkmodules.vtkCommonTransforms import vtkTransform -from vtkmodules.vtkRenderingFreeType import vtkVectorText -from vtkmodules.vtkFiltersGeneral import vtkTransformPolyDataFilter +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.parent @@ -87,6 +84,9 @@ After a first application, select again cells and time steps to display, then * Apply again * Click on `Refresh Data` (you may have to click twice to refresh the Python view correctly). + To visualize the index of the cell used to calculate Mohr circle, use the ParaView tool 'Find Data': + * The attribute 'ActiveCellMask' allows to select only the right cells (equal to 1). + * The attribute 'CellId' has to be used for the 'Selection Labels'. """ @@ -110,7 +110,7 @@ def __init__( self: Self ) -> None: super().__init__( nInputPorts=1, nOutputPorts=1, inputType="vtkUnstructuredGrid", - outputType="vtkMultiBlockDataSet" ) + outputType="vtkUnstructuredGrid" ) # Create a new PythonView self.pythonView: Any = buildNewLayoutWithPythonView() @@ -725,8 +725,8 @@ def RequestDataObject( outData = self.GetOutputData( outInfoVec, 0 ) assert inData is not None - if outData is None or ( not outData.IsA( "vtkMultiBlockDataSet" ) ): - outData = vtkMultiBlockDataSet() + if outData is None or ( not outData.IsA( "vtkUnstructuredGrid" ) ): + outData = vtkUnstructuredGrid() outInfoVec.GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), outData ) # type: ignore return super().RequestDataObject( request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] @@ -785,62 +785,28 @@ def RequestData( # Cell indexes annotation nbCells = inputMesh.GetNumberOfCells() - inputData = inputMesh.NewInstance() - inputData.ShallowCopy( inputMesh ) - outputMesh: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 0 ) + outputMesh: vtkUnstructuredGrid = self.GetOutputData( outInfoVec, 0 ) + outputMesh.ShallowCopy( inputMesh ) cellId = vtkStringArray() - cellId.SetName( "cellId" ) + cellId.SetName( "CellId" ) cellId.SetNumberOfValues( nbCells ) - cellMask = vtkIntArray() - cellMask.SetName( "CellMask" ) - cellMask.SetNumberOfValues( nbCells ) + activeCellMask = vtkIntArray() + activeCellMask.SetName( "ActiveCellMask" ) + activeCellMask.SetNumberOfValues( nbCells ) - selected_local = set() originalCellIds = inputMesh.GetCellData().GetArray( "vtkOriginalCellIds" ) for localCellId in range( nbCells ): if str( originalCellIds.GetValue( localCellId ) ) in self.requestedCellIds: - selected_local.add( localCellId ) cellId.SetValue( localCellId, f"{ originalCellIds.GetValue( localCellId ) }" ) - cellMask.SetValue( localCellId, 1 ) + activeCellMask.SetValue( localCellId, 1 ) else: - cellMask.SetValue( localCellId, 0 ) - - inputData.GetCellData().AddArray( cellId ) - inputData.GetCellData().AddArray( cellMask ) - - idBlock = 0 - for localCellId in selected_local: - globalCellId = f"{ originalCellIds.GetValue( localCellId ) }" - text = vtkVectorText() - text.SetText( globalCellId ) - text.Update() - - cellBounds = inputMesh.GetCell( localCellId ).GetBounds() - transformFilter = vtkTransform() - transformFilter.Translate( cellBounds[ 1 ], cellBounds[ 3 ], cellBounds[ 5 ] ) - - scaleX = ( cellBounds[ 1 ] - cellBounds[ 0 ] ) / 4 - scaleY = ( cellBounds[ 3 ] - cellBounds[ 2 ] ) / 4 - scaleZ = ( cellBounds[ 5 ] - cellBounds[ 4 ] ) / 4 - transformFilter.Scale( scaleX, scaleY, scaleZ ) - - transformFromPolyDataFilter = vtkTransformPolyDataFilter() - transformFromPolyDataFilter.SetTransform( transformFilter ) - transformFromPolyDataFilter.SetInputData( text.GetOutput() ) - transformFromPolyDataFilter.Update() - - meshText = vtkPolyData() - meshText.ShallowCopy( transformFromPolyDataFilter.GetOutput() ) - - outputMesh.SetBlock( idBlock, meshText ) - outputMesh.GetMetaData( idBlock ).Set( vtkCompositeDataSet.NAME(), f"Cell_{ globalCellId }" ) - idBlock += 1 - - outputMesh.SetBlock( idBlock, inputData ) - outputMesh.GetMetaData( idBlock ).Set( vtkCompositeDataSet.NAME(), "Input Data" ) + activeCellMask.SetValue( localCellId, 0 ) + outputMesh.GetCellData().AddArray( cellId ) + outputMesh.GetCellData().AddArray( activeCellMask ) + outputMesh.Modified() except Exception as e: self.logger.error( "Mohr circles cannot be plotted due to:" ) self.logger.error( e )