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 067d36f8..ff801ebb 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 @@ -18,9 +19,8 @@ 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 -from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector +from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector, vtkStringArray, vtkIntArray from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid # Update sys.path to load all GEOS Python Package dependencies @@ -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 @@ -84,12 +84,15 @@ 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'. """ @smproxy.filter( name="PVMohrCirclePlot", label="Plot Mohr's Circles" ) @smhint.xml( f""" - + """ ) @smproperty.input( name="Input", port_index=0 ) @@ -104,7 +107,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="vtkUnstructuredGrid" ) # Create a new PythonView self.pythonView: Any = buildNewLayoutWithPythonView() @@ -719,9 +725,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( "vtkUnstructuredGrid" ) ): + outData = vtkUnstructuredGrid() + outInfoVec.GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), outData ) # type: ignore return super().RequestDataObject( request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] def RequestData( @@ -774,8 +780,33 @@ def RequestData( self.frictionAngle, self._getUserChoices(), ) + Render() + # Cell indexes annotation + nbCells = inputMesh.GetNumberOfCells() + outputMesh: vtkUnstructuredGrid = self.GetOutputData( outInfoVec, 0 ) + outputMesh.ShallowCopy( inputMesh ) + + cellId = vtkStringArray() + cellId.SetName( "CellId" ) + cellId.SetNumberOfValues( nbCells ) + + activeCellMask = vtkIntArray() + activeCellMask.SetName( "ActiveCellMask" ) + activeCellMask.SetNumberOfValues( nbCells ) + + originalCellIds = inputMesh.GetCellData().GetArray( "vtkOriginalCellIds" ) + for localCellId in range( nbCells ): + if str( originalCellIds.GetValue( localCellId ) ) in self.requestedCellIds: + cellId.SetValue( localCellId, f"{ originalCellIds.GetValue( localCellId ) }" ) + activeCellMask.SetValue( localCellId, 1 ) + else: + 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 ) @@ -812,12 +843,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 +882,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."""