From efe1c49cc072ed4d8fee928bff36dc790588e9b2 Mon Sep 17 00:00:00 2001 From: jfranc Date: Wed, 27 Mar 2024 15:28:37 +0100 Subject: [PATCH 1/5] transfert from GEOS closed PR #2790 --- docs/mesh_doctor.rst | 82 ++++++++++++++++++++++- geosx_mesh_doctor/mesh_doctor-pvplugin.py | 60 +++++++++++++++++ 2 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 geosx_mesh_doctor/mesh_doctor-pvplugin.py diff --git a/docs/mesh_doctor.rst b/docs/mesh_doctor.rst index 5d8540e7..55df3e59 100644 --- a/docs/mesh_doctor.rst +++ b/docs/mesh_doctor.rst @@ -121,4 +121,84 @@ The ``supported_elements`` check will validate that no unsupported element is in It will also verify that the ``VTK_POLYHEDRON`` cells can effectively get converted into a supported type of element. .. command-output:: python mesh_doctor.py supported_elements --help - :cwd: ../geosx_mesh_doctor + :cwd: ../../../coreComponents/python/modules/geosx_mesh_doctor + +``Using mesh_doctor in paraview`` +"""""""""""""""""""""""""""""""""" + +Using mesh_doctor as a programmable filter +____________________________________________ + +To use ``mesh_doctor`` in Paraview as a python programmable filter, a python package install is required first in Paraview python resolved +path. Paraview is storing its python ressources under its *lib/pythonX.X* depending on the paraview version, *e.g* Paraview 5.11 is working +with python 3.9. As a results the following command will install ``mesh_doctor`` package into Paraview resolved path. + +.. command-output:: python3 -m pip install --index-url https://test.pypi.org/simple/ --no-deps --upgrade mesh_doctor + +.. note:: + ``pip`` is installing the ``mesh_doctor`` package from the test.pypi repo, which is intended to test package deployment. + Once stabilized and ``mesh_doctor`` uploaded onto the main package repo, this should be dropped out. + +.. note:: + The packaged version of ``mesh_doctor`` has been amended so that `element_volumes.check` takes directly a vtk mesh as input instead of a file name as originally as the loading is taken care of by Paraview + +Once the installation done, the installation directory should contain ``mesh_doctor`` package content, *i.e.* ``checks`` and ``parsing``. +Then launching ``Paraview`` and loading our *mesh.vtu*, as an example, we will design a *Programmable python filter* relying on *element_volumes* from +``mesh_doctor``. Add such a filter pipelined after the mesh reader, in the script section paste the following, + +.. code-block:: python + :linenos: + + mesh = inputs[0].VTKObject + tol = 1.2e-6 + + from checks import element_volumes + import vtk + + res = element_volumes.__check(mesh, element_volumes.Options(tol)) + #print(res) + ids = vtk.vtkIdTypeArray() + ids.SetNumberOfComponents(1) + for cell_index, volume in res.element_volumes: + ids.InsertNextValue(cell_index) + + selectionNode = vtk.vtkSelectionNode() + selectionNode.SetFieldType(vtk.vtkSelectionNode.CELL) + selectionNode.SetContentType(vtk.vtkSelectionNode.INDICES) + selectionNode.SetSelectionList(ids) + selection = vtk.vtkSelection() + selection.AddNode(selectionNode) + extracted = vtk.vtkExtractSelection() + extracted.SetInputDataObject(0, mesh) + extracted.SetInputData(1, selection) + extracted.Update() + print("There are {} cells under {} m3 vol".format(extracted.GetOutput().GetNumberOfCells(), tol)) + output.ShallowCopy(extracted.GetOutput()) + +Here we rely on ``pyvtk`` interface more than on Paraview adaptation, for legacy and reusability reasons. This is the reason +for the full ``import vtk`` instead of ``from paraview import vtk``, the `vtkSelectionNode` being fully wrapped in paraview +and not accessible otherwise. + +On line 7, we leverage ``mesh_doctor`` package to provide us with pairs of `(index,volumes)` of cells with volumes lower +than tolerance `tol`. As input of *Programmable Python Filter* is wrapped in a `dataset_adapter.UnstructuredGrid`, we rely on +the copy of the inital VTKObject `inputs[0].VTKObject` to ensure consistency with our ``pyvtk`` workflow. + +What follows is ``pyvtk`` steps in oder to convert into input struct and extract from the original mesh this list of cells. +Eventually, the `extracted` selection is shallow-copied to the output and then accessible in ``Paraview``. An helper print +is left and should be reported in *Output Message* of ``Paraview`` (and in launching terminal if exist). + +Using mesh_doctor as a paraview plugins +____________________________________________ + +Another way of leveraging ``mesh_doctor`` in ``Paraview`` is to wrap it in a python plugin that would be loadable through the +``Paraview`` interface under **Tools | Manage Plugins/Extensions** and **Load New** looking for ``mesh_doctor-pvplugin.py``. +(see `Paraview How To `_ for more details). + +Starting by local installation to get ``Paraview`` to resolve ``mesh_doctor`` import. + +.. command-output:: python3 -m pip install --index-url https://test.pypi.org/simple/ --no-deps --upgrade mesh_doctor + + +The file ``mesh_doctor-pvplugin.py`` is located under the ``geosx_mesh_doctor`` module in GEOS. Once the plugin loaded and a mesh opened, +it should appear in filter list as *Mesh Doctor(GEOS)*. It displays a parameter value box allowing the user to enter the volume he wants as +threshold to select cells based on ``element_volumes`` capability. Once applied, it extracts selected set of cells as a new unstructured grid. diff --git a/geosx_mesh_doctor/mesh_doctor-pvplugin.py b/geosx_mesh_doctor/mesh_doctor-pvplugin.py new file mode 100644 index 00000000..29f4dc21 --- /dev/null +++ b/geosx_mesh_doctor/mesh_doctor-pvplugin.py @@ -0,0 +1,60 @@ +from paraview.util.vtkAlgorithm import * +from paraview.selection import * +from checks import element_volumes + + +@smproxy.filter(name="Mesh Doctor(GEOS)") +@smproperty.input(name="Input") +@smdomain.datatype(dataTypes=["vtkUnstructuredGrid"], composite_data_supported=False) +class ElementVolumesFilter(VTKPythonAlgorithmBase): + """ + Example portage meshDoctor in PV Python plugins + """ + def __init__(self): + super().__init__(outputType='vtkUnstructuredGrid') + self.opt = element_volumes.Options(0) + + def RequestData(self, request, inInfo, outInfo): + inData = self.GetInputData(inInfo, 0, 0) + outData = self.GetOutputData(outInfo, 0) + assert inData is not None + if outData is None or (not outData.IsA(inData.GetClassName())): + outData = inData.NewInstance() + extracted = self._Process(inData) + outData.DeepCopy( extracted.GetOutput() ) + outInfo.GetInformationObject(0).Set(outData.DATA_OBJECT(), extracted.GetOutput()) + + print("1> There are {} cells under {} m3 vol".format(outData.GetNumberOfCells(), self.opt)) + return 1 + + def _Process(self,mesh): + from paraview.vtk import vtkIdTypeArray, vtkSelectionNode, vtkSelection, vtkCollection + from vtk import vtkExtractSelection + res = element_volumes.check(mesh, self.opt) + ids = vtkIdTypeArray() + ids.SetNumberOfComponents(1) + for val in res.element_volumes: + ids.InsertNextValue(val[0]) + + + selectionNode = vtkSelectionNode() + selectionNode.SetFieldType(vtkSelectionNode.CELL) + selectionNode.SetContentType(vtkSelectionNode.INDICES) + selectionNode.SetSelectionList(ids) + selection = vtkSelection() + selection.AddNode(selectionNode) + + extracted = vtkExtractSelection() + extracted.SetInputDataObject(0, mesh) + extracted.SetInputData(1, selection) + extracted.Update() + print("There are {} cells under {} m3 vol".format(extracted.GetOutput().GetNumberOfCells(), self.opt)) + print("There are {} arrays of cell data".format(extracted.GetOutput().GetCellData().GetNumberOfArrays(), self.opt)) + + return extracted + + @smproperty.doublevector(name="Vol Threshold", default_values=["0.0"]) + def SetValue(self, val): + self.opt = element_volumes.Options(val) + print("Settings value:", self.opt) + self.Modified() From 1b5d6e3154c01f27ca3bdc1983b6548632894c6e Mon Sep 17 00:00:00 2001 From: jfranc Date: Fri, 29 Mar 2024 13:53:59 +0100 Subject: [PATCH 2/5] decorated --- geosx_mesh_doctor/mesh_doctor-pvplugin.py | 118 +++++++++++++++------- 1 file changed, 82 insertions(+), 36 deletions(-) diff --git a/geosx_mesh_doctor/mesh_doctor-pvplugin.py b/geosx_mesh_doctor/mesh_doctor-pvplugin.py index 29f4dc21..a1e1381d 100644 --- a/geosx_mesh_doctor/mesh_doctor-pvplugin.py +++ b/geosx_mesh_doctor/mesh_doctor-pvplugin.py @@ -1,60 +1,106 @@ +import vtk from paraview.util.vtkAlgorithm import * from paraview.selection import * -from checks import element_volumes +import functools +from checks import element_volumes, non_conformal -@smproxy.filter(name="Mesh Doctor(GEOS)") -@smproperty.input(name="Input") -@smdomain.datatype(dataTypes=["vtkUnstructuredGrid"], composite_data_supported=False) -class ElementVolumesFilter(VTKPythonAlgorithmBase): +# #decorator +def extract_mesh(attr_key): + def mesh_decorator(func): + """Make a selected set from a list of points/face/cells""" + + @functools.wraps(func) + def wrapper_extract_mesh(self, **kwargs): + from paraview.vtk import vtkIdTypeArray, vtkSelectionNode, vtkSelection, vtkCollection + from vtk import vtkExtractSelection + res = func(self, **kwargs) + ids = vtkIdTypeArray() + ids.SetNumberOfComponents(1) + print(attr_key) + for val in getattr(res, attr_key): + ids.InsertNextValue(val[0]) + + selectionNode = vtkSelectionNode() + selectionNode.SetFieldType(vtkSelectionNode.CELL) + selectionNode.SetContentType(vtkSelectionNode.INDICES) + selectionNode.SetSelectionList(ids) + selection = vtkSelection() + selection.AddNode(selectionNode) + + extracted = vtkExtractSelection() + extracted.SetInputDataObject(0, self.GetInputData(kwargs['inInfo'], 0, 0)) + extracted.SetInputData(1, selection) + extracted.Update() + print("There are {} cells under {} m3 vol".format(extracted.GetOutput().GetNumberOfCells(), self.opt)) + print("There are {} arrays of cell data".format(extracted.GetOutput().GetCellData().GetNumberOfArrays(), + self.opt)) + + outData = self.GetOutputData(kwargs['outInfo'], 0) + kwargs['outInfo'].GetInformationObject(0).Set(outData.DATA_OBJECT(), extracted.GetOutput()) + + return res + + return wrapper_extract_mesh + + return mesh_decorator + +class BaseFilter(VTKPythonAlgorithmBase): """ - Example portage meshDoctor in PV Python plugins + Base Class refactoring filter construction """ + def __init__(self): super().__init__(outputType='vtkUnstructuredGrid') - self.opt = element_volumes.Options(0) + self.opt = {'elementVolumes': element_volumes.Options(0), 'nonConformal': non_conformal.Options(0, 0, 0)} - def RequestData(self, request, inInfo, outInfo): + def RequestData(self, request: vtk.vtkInformation, inInfo: vtk.vtkInformation, outInfo: vtk.vtkInformation): inData = self.GetInputData(inInfo, 0, 0) outData = self.GetOutputData(outInfo, 0) assert inData is not None if outData is None or (not outData.IsA(inData.GetClassName())): outData = inData.NewInstance() - extracted = self._Process(inData) - outData.DeepCopy( extracted.GetOutput() ) - outInfo.GetInformationObject(0).Set(outData.DATA_OBJECT(), extracted.GetOutput()) + self._Process(inInfo=inInfo, outInfo=outInfo) - print("1> There are {} cells under {} m3 vol".format(outData.GetNumberOfCells(), self.opt)) + print("1> There are {} cells under {} vol".format(outData.GetNumberOfCells(), self.opt)) return 1 - def _Process(self,mesh): - from paraview.vtk import vtkIdTypeArray, vtkSelectionNode, vtkSelection, vtkCollection - from vtk import vtkExtractSelection - res = element_volumes.check(mesh, self.opt) - ids = vtkIdTypeArray() - ids.SetNumberOfComponents(1) - for val in res.element_volumes: - ids.InsertNextValue(val[0]) - - selectionNode = vtkSelectionNode() - selectionNode.SetFieldType(vtkSelectionNode.CELL) - selectionNode.SetContentType(vtkSelectionNode.INDICES) - selectionNode.SetSelectionList(ids) - selection = vtkSelection() - selection.AddNode(selectionNode) +@smproxy.filter(name="Mesh Doctor(GEOS) - Element Volume Filter") +@smproperty.input(name="Input") +@smdomain.datatype(dataTypes=["vtkUnstructuredGrid"], composite_data_supported=False) +class ElementVolumeFilter(BaseFilter): - extracted = vtkExtractSelection() - extracted.SetInputDataObject(0, mesh) - extracted.SetInputData(1, selection) - extracted.Update() - print("There are {} cells under {} m3 vol".format(extracted.GetOutput().GetNumberOfCells(), self.opt)) - print("There are {} arrays of cell data".format(extracted.GetOutput().GetCellData().GetNumberOfArrays(), self.opt)) + def __init__(self): + super().__init__() + self.opt = element_volumes.Options(0) - return extracted + @extract_mesh(attr_key='element_volumes') + def _Process(self, inInfo: vtk.vtkInformation, outInfo: vtk.vtkInformation): + return element_volumes.check(self.GetInputData(inInfo, 0, 0), self.opt) @smproperty.doublevector(name="Vol Threshold", default_values=["0.0"]) - def SetValue(self, val): - self.opt = element_volumes.Options(val) + def SetValue(self, val: float): + self.opt = element_volumes.Options(min_volume=val) + print("Settings value:", self.opt) + self.Modified() + + +@smproxy.filter(name="Mesh Doctor(GEOS) - NonConformal") +@smproperty.input(name="Input") +@smdomain.datatype(dataTypes=["vtkUnstructuredGrid"], composite_data_supported=False) +class NonConformalFilter(BaseFilter): + def __init__(self): + super().__init__() + self.opt = non_conformal.Options(0,0,0) + + @extract_mesh(attr_key='non_conformal_cells') + def _Process(self, inInfo: vtk.vtkInformation, outInfo: vtk.vtkInformation): + return non_conformal.check(self.GetInputData(inInfo, 0, 0), self.opt) + + @smproperty.doublevector(name="angle/point/face tol", default_values=["0.0", "0.0", "0.0"]) + def SetValue(self, angle: float, point: float, face: float): + self.opt = non_conformal.Options(angle_tolerance=angle, point_tolerance=point, + face_tolerance=face) print("Settings value:", self.opt) self.Modified() From 2775123af313576f1b012869e6f8c2d5401ad8bb Mon Sep 17 00:00:00 2001 From: jfranc Date: Fri, 29 Mar 2024 14:05:26 +0100 Subject: [PATCH 3/5] sanitize deps --- geosx_mesh_doctor/mesh_doctor-pvplugin.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/geosx_mesh_doctor/mesh_doctor-pvplugin.py b/geosx_mesh_doctor/mesh_doctor-pvplugin.py index a1e1381d..06cb7bf2 100644 --- a/geosx_mesh_doctor/mesh_doctor-pvplugin.py +++ b/geosx_mesh_doctor/mesh_doctor-pvplugin.py @@ -1,6 +1,7 @@ -import vtk -from paraview.util.vtkAlgorithm import * -from paraview.selection import * +from vtk import vtkExtractSelection, vtkInformation +from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase,smproxy,smproperty,smdomain +from paraview.vtk import vtkIdTypeArray, vtkSelectionNode, vtkSelection, vtkCollection +# from paraview.selection import * import functools from checks import element_volumes, non_conformal @@ -12,8 +13,6 @@ def mesh_decorator(func): @functools.wraps(func) def wrapper_extract_mesh(self, **kwargs): - from paraview.vtk import vtkIdTypeArray, vtkSelectionNode, vtkSelection, vtkCollection - from vtk import vtkExtractSelection res = func(self, **kwargs) ids = vtkIdTypeArray() ids.SetNumberOfComponents(1) @@ -54,7 +53,7 @@ def __init__(self): super().__init__(outputType='vtkUnstructuredGrid') self.opt = {'elementVolumes': element_volumes.Options(0), 'nonConformal': non_conformal.Options(0, 0, 0)} - def RequestData(self, request: vtk.vtkInformation, inInfo: vtk.vtkInformation, outInfo: vtk.vtkInformation): + def RequestData(self, request: vtkInformation, inInfo: vtkInformation, outInfo: vtkInformation): inData = self.GetInputData(inInfo, 0, 0) outData = self.GetOutputData(outInfo, 0) assert inData is not None @@ -76,7 +75,7 @@ def __init__(self): self.opt = element_volumes.Options(0) @extract_mesh(attr_key='element_volumes') - def _Process(self, inInfo: vtk.vtkInformation, outInfo: vtk.vtkInformation): + def _Process(self, inInfo: vtkInformation, outInfo: vtkInformation): return element_volumes.check(self.GetInputData(inInfo, 0, 0), self.opt) @smproperty.doublevector(name="Vol Threshold", default_values=["0.0"]) @@ -95,7 +94,7 @@ def __init__(self): self.opt = non_conformal.Options(0,0,0) @extract_mesh(attr_key='non_conformal_cells') - def _Process(self, inInfo: vtk.vtkInformation, outInfo: vtk.vtkInformation): + def _Process(self, inInfo: vtkInformation, outInfo: vtkInformation): return non_conformal.check(self.GetInputData(inInfo, 0, 0), self.opt) @smproperty.doublevector(name="angle/point/face tol", default_values=["0.0", "0.0", "0.0"]) From e45322d812ad10320900126645a5ca091465c497 Mon Sep 17 00:00:00 2001 From: jfranc Date: Fri, 29 Mar 2024 14:29:30 +0100 Subject: [PATCH 4/5] yapfing --- geosx_mesh_doctor/mesh_doctor-pvplugin.py | 118 +++++++++++----------- 1 file changed, 60 insertions(+), 58 deletions(-) diff --git a/geosx_mesh_doctor/mesh_doctor-pvplugin.py b/geosx_mesh_doctor/mesh_doctor-pvplugin.py index 06cb7bf2..e124b3ac 100644 --- a/geosx_mesh_doctor/mesh_doctor-pvplugin.py +++ b/geosx_mesh_doctor/mesh_doctor-pvplugin.py @@ -1,5 +1,5 @@ from vtk import vtkExtractSelection, vtkInformation -from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase,smproxy,smproperty,smdomain +from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase, smproxy, smproperty, smdomain from paraview.vtk import vtkIdTypeArray, vtkSelectionNode, vtkSelection, vtkCollection # from paraview.selection import * import functools @@ -7,36 +7,37 @@ # #decorator -def extract_mesh(attr_key): - def mesh_decorator(func): +def extract_mesh( attr_key ): + + def mesh_decorator( func ): """Make a selected set from a list of points/face/cells""" - @functools.wraps(func) - def wrapper_extract_mesh(self, **kwargs): - res = func(self, **kwargs) + @functools.wraps( func ) + def wrapper_extract_mesh( self, **kwargs ): + res = func( self, **kwargs ) ids = vtkIdTypeArray() - ids.SetNumberOfComponents(1) - print(attr_key) - for val in getattr(res, attr_key): - ids.InsertNextValue(val[0]) + ids.SetNumberOfComponents( 1 ) + print( attr_key ) + for val in getattr( res, attr_key ): + ids.InsertNextValue( val[ 0 ] ) selectionNode = vtkSelectionNode() - selectionNode.SetFieldType(vtkSelectionNode.CELL) - selectionNode.SetContentType(vtkSelectionNode.INDICES) - selectionNode.SetSelectionList(ids) + selectionNode.SetFieldType( vtkSelectionNode.CELL ) + selectionNode.SetContentType( vtkSelectionNode.INDICES ) + selectionNode.SetSelectionList( ids ) selection = vtkSelection() - selection.AddNode(selectionNode) + selection.AddNode( selectionNode ) extracted = vtkExtractSelection() - extracted.SetInputDataObject(0, self.GetInputData(kwargs['inInfo'], 0, 0)) - extracted.SetInputData(1, selection) + extracted.SetInputDataObject( 0, self.GetInputData( kwargs[ 'inInfo' ], 0, 0 ) ) + extracted.SetInputData( 1, selection ) extracted.Update() - print("There are {} cells under {} m3 vol".format(extracted.GetOutput().GetNumberOfCells(), self.opt)) - print("There are {} arrays of cell data".format(extracted.GetOutput().GetCellData().GetNumberOfArrays(), - self.opt)) + print( "There are {} cells under {} m3 vol".format( extracted.GetOutput().GetNumberOfCells(), self.opt ) ) + print( "There are {} arrays of cell data".format( extracted.GetOutput().GetCellData().GetNumberOfArrays(), + self.opt ) ) - outData = self.GetOutputData(kwargs['outInfo'], 0) - kwargs['outInfo'].GetInformationObject(0).Set(outData.DATA_OBJECT(), extracted.GetOutput()) + outData = self.GetOutputData( kwargs[ 'outInfo' ], 0 ) + kwargs[ 'outInfo' ].GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), extracted.GetOutput() ) return res @@ -44,62 +45,63 @@ def wrapper_extract_mesh(self, **kwargs): return mesh_decorator -class BaseFilter(VTKPythonAlgorithmBase): + +class BaseFilter( VTKPythonAlgorithmBase ): """ Base Class refactoring filter construction """ - def __init__(self): - super().__init__(outputType='vtkUnstructuredGrid') - self.opt = {'elementVolumes': element_volumes.Options(0), 'nonConformal': non_conformal.Options(0, 0, 0)} + def __init__( self ): + super().__init__( outputType='vtkUnstructuredGrid' ) + self.opt = { 'elementVolumes': element_volumes.Options( 0 ), 'nonConformal': non_conformal.Options( 0, 0, 0 ) } - def RequestData(self, request: vtkInformation, inInfo: vtkInformation, outInfo: vtkInformation): - inData = self.GetInputData(inInfo, 0, 0) - outData = self.GetOutputData(outInfo, 0) + def RequestData( self, request: vtkInformation, inInfo: vtkInformation, outInfo: vtkInformation ): + inData = self.GetInputData( inInfo, 0, 0 ) + outData = self.GetOutputData( outInfo, 0 ) assert inData is not None - if outData is None or (not outData.IsA(inData.GetClassName())): + if outData is None or ( not outData.IsA( inData.GetClassName() ) ): outData = inData.NewInstance() - self._Process(inInfo=inInfo, outInfo=outInfo) + self._Process( inInfo=inInfo, outInfo=outInfo ) - print("1> There are {} cells under {} vol".format(outData.GetNumberOfCells(), self.opt)) + print( "1> There are {} cells under {} vol".format( outData.GetNumberOfCells(), self.opt ) ) return 1 -@smproxy.filter(name="Mesh Doctor(GEOS) - Element Volume Filter") -@smproperty.input(name="Input") -@smdomain.datatype(dataTypes=["vtkUnstructuredGrid"], composite_data_supported=False) -class ElementVolumeFilter(BaseFilter): +@smproxy.filter( name="Mesh Doctor(GEOS) - Element Volume Filter" ) +@smproperty.input( name="Input" ) +@smdomain.datatype( dataTypes=[ "vtkUnstructuredGrid" ], composite_data_supported=False ) +class ElementVolumeFilter( BaseFilter ): - def __init__(self): + def __init__( self ): super().__init__() - self.opt = element_volumes.Options(0) + self.opt = element_volumes.Options( 0 ) - @extract_mesh(attr_key='element_volumes') - def _Process(self, inInfo: vtkInformation, outInfo: vtkInformation): - return element_volumes.check(self.GetInputData(inInfo, 0, 0), self.opt) + @extract_mesh( attr_key='element_volumes' ) + def _Process( self, inInfo: vtkInformation, outInfo: vtkInformation ): + return element_volumes.check( self.GetInputData( inInfo, 0, 0 ), self.opt ) - @smproperty.doublevector(name="Vol Threshold", default_values=["0.0"]) - def SetValue(self, val: float): - self.opt = element_volumes.Options(min_volume=val) - print("Settings value:", self.opt) + @smproperty.doublevector( name="Vol Threshold", default_values=[ "0.0" ] ) + def SetValue( self, val: float ): + self.opt = element_volumes.Options( min_volume=val ) + print( "Settings value:", self.opt ) self.Modified() -@smproxy.filter(name="Mesh Doctor(GEOS) - NonConformal") -@smproperty.input(name="Input") -@smdomain.datatype(dataTypes=["vtkUnstructuredGrid"], composite_data_supported=False) -class NonConformalFilter(BaseFilter): - def __init__(self): +@smproxy.filter( name="Mesh Doctor(GEOS) - NonConformal" ) +@smproperty.input( name="Input" ) +@smdomain.datatype( dataTypes=[ "vtkUnstructuredGrid" ], composite_data_supported=False ) +class NonConformalFilter( BaseFilter ): + + def __init__( self ): super().__init__() - self.opt = non_conformal.Options(0,0,0) + self.opt = non_conformal.Options( 0, 0, 0 ) - @extract_mesh(attr_key='non_conformal_cells') - def _Process(self, inInfo: vtkInformation, outInfo: vtkInformation): - return non_conformal.check(self.GetInputData(inInfo, 0, 0), self.opt) + @extract_mesh( attr_key='non_conformal_cells' ) + def _Process( self, inInfo: vtkInformation, outInfo: vtkInformation ): + return non_conformal.check( self.GetInputData( inInfo, 0, 0 ), self.opt ) - @smproperty.doublevector(name="angle/point/face tol", default_values=["0.0", "0.0", "0.0"]) - def SetValue(self, angle: float, point: float, face: float): - self.opt = non_conformal.Options(angle_tolerance=angle, point_tolerance=point, - face_tolerance=face) - print("Settings value:", self.opt) + @smproperty.doublevector( name="angle/point/face tol", default_values=[ "0.0", "0.0", "0.0" ] ) + def SetValue( self, angle: float, point: float, face: float ): + self.opt = non_conformal.Options( angle_tolerance=angle, point_tolerance=point, face_tolerance=face ) + print( "Settings value:", self.opt ) self.Modified() From 1aa9f8d5debd81b84937ccdffc6cb9bc8dd90d8e Mon Sep 17 00:00:00 2001 From: jfranc Date: Tue, 2 Apr 2024 11:23:23 +0200 Subject: [PATCH 5/5] changing extraction for flagging a la PV preserve topology --- geosx_mesh_doctor/mesh_doctor-pvplugin.py | 46 ++++++++++------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/geosx_mesh_doctor/mesh_doctor-pvplugin.py b/geosx_mesh_doctor/mesh_doctor-pvplugin.py index e124b3ac..c5a98010 100644 --- a/geosx_mesh_doctor/mesh_doctor-pvplugin.py +++ b/geosx_mesh_doctor/mesh_doctor-pvplugin.py @@ -1,8 +1,11 @@ -from vtk import vtkExtractSelection, vtkInformation -from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase, smproxy, smproperty, smdomain -from paraview.vtk import vtkIdTypeArray, vtkSelectionNode, vtkSelection, vtkCollection -# from paraview.selection import * +import numpy as np import functools + +from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase, smproxy, smproperty, smdomain +from paraview.vtk import vtkIdTypeArray, vtkSelectionNode, vtkSelection, vtkCollection, vtkInformation, vtkDataObject +from paraview.vtk.util import numpy_support +from vtkmodules.util import vtkConstants + from checks import element_volumes, non_conformal @@ -15,29 +18,19 @@ def mesh_decorator( func ): @functools.wraps( func ) def wrapper_extract_mesh( self, **kwargs ): res = func( self, **kwargs ) - ids = vtkIdTypeArray() - ids.SetNumberOfComponents( 1 ) - print( attr_key ) - for val in getattr( res, attr_key ): - ids.InsertNextValue( val[ 0 ] ) - - selectionNode = vtkSelectionNode() - selectionNode.SetFieldType( vtkSelectionNode.CELL ) - selectionNode.SetContentType( vtkSelectionNode.INDICES ) - selectionNode.SetSelectionList( ids ) - selection = vtkSelection() - selection.AddNode( selectionNode ) - - extracted = vtkExtractSelection() - extracted.SetInputDataObject( 0, self.GetInputData( kwargs[ 'inInfo' ], 0, 0 ) ) - extracted.SetInputData( 1, selection ) - extracted.Update() - print( "There are {} cells under {} m3 vol".format( extracted.GetOutput().GetNumberOfCells(), self.opt ) ) - print( "There are {} arrays of cell data".format( extracted.GetOutput().GetCellData().GetNumberOfArrays(), - self.opt ) ) - + inData = self.GetInputData( kwargs[ 'inInfo' ], 0, 0 ) + mesh = inData.NewInstance() + mesh.DeepCopy( inData ) + maskArray = np.full( ( mesh.GetNumberOfCells(), ), 0 ) + for ix, _ in getattr( res, attr_key ): + maskArray[ ix ] = 1 + + print( f'There are {np.sum(maskArray)} cells under {self.opt} m3 vol' ) + insidedness = numpy_support.numpy_to_vtk( maskArray, deep=1, array_type=vtkConstants.VTK_SIGNED_CHAR ) + insidedness.SetName( attr_key ) + mesh.GetAttributes( vtkDataObject.CELL ).AddArray( insidedness ) outData = self.GetOutputData( kwargs[ 'outInfo' ], 0 ) - kwargs[ 'outInfo' ].GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), extracted.GetOutput() ) + kwargs[ 'outInfo' ].GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), mesh ) return res @@ -53,7 +46,6 @@ class BaseFilter( VTKPythonAlgorithmBase ): def __init__( self ): super().__init__( outputType='vtkUnstructuredGrid' ) - self.opt = { 'elementVolumes': element_volumes.Options( 0 ), 'nonConformal': non_conformal.Options( 0, 0, 0 ) } def RequestData( self, request: vtkInformation, inInfo: vtkInformation, outInfo: vtkInformation ): inData = self.GetInputData( inInfo, 0, 0 )