From 1a573227268ebe02326f277a37c1c0790d8660eb Mon Sep 17 00:00:00 2001 From: jacques franc Date: Tue, 16 Sep 2025 15:02:40 +0200 Subject: [PATCH 01/26] wip --- .../geos/pv/plugins/PVFillPartialArrays.py | 37 ++++----- geos-pv/src/geos/pv/utils/details.py | 79 +++++++++++++++++++ 2 files changed, 98 insertions(+), 18 deletions(-) create mode 100644 geos-pv/src/geos/pv/utils/details.py diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index 218d6bfc..c98c82a7 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -30,7 +30,7 @@ update_paths() from geos.mesh.processing.FillPartialArrays import FillPartialArrays - +import geos.pv.utils.details __doc__ = """ Fill partial arrays of input mesh. @@ -47,21 +47,22 @@ """ -@smproxy.filter( name="PVFillPartialArrays", label="Fill Partial Arrays" ) -@smhint.xml( '' ) -@smproperty.input( name="Input", port_index=0 ) -@smdomain.datatype( - dataTypes=[ "vtkMultiBlockDataSet" ], - composite_data_supported=True, -) -class PVFillPartialArrays( VTKPythonAlgorithmBase ): +# @smproxy.filter( name="PVFillPartialArrays", label="Fill Partial Arrays" ) +# @smhint.xml( '' ) +# @smproperty.input( name="Input", port_index=0 ) +# @smdomain.datatype( +# dataTypes=[ "vtkMultiBlockDataSet" ], +# composite_data_supported=True, +# ) +@geos.pv.utils.details.SISOFilter(decorated_name="PVFillPartialArrays", decorated_label="Fill Partial Arrays",decorated_type="vtkMultiBlockDataSet") +class PVFillPartialArrays: def __init__( self: Self, ) -> None: """Fill a partial attribute with constant value per component.""" - super().__init__( nInputPorts=1, - nOutputPorts=1, - inputType="vtkMultiBlockDataSet", - outputType="vtkMultiBlockDataSet" ) + # super().__init__( nInputPorts=1, + # nOutputPorts=1, + # inputType="vtkMultiBlockDataSet", + # outputType="vtkMultiBlockDataSet" ) self.clearDictAttributesValues: bool = True self.dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ] = {} @@ -99,13 +100,13 @@ def _setDictAttributesValues( self: Self, attributeName: str, values: str ) -> N if self.clearDictAttributesValues: self.dictAttributesValues = {} self.clearDictAttributesValues = False - + if attributeName is not None: if values is not None : self.dictAttributesValues[ attributeName ] = list( values.split( "," ) ) else: self.dictAttributesValues[ attributeName ] = None - + self.Modified() def RequestDataObject( @@ -156,13 +157,13 @@ def RequestData( outputMesh.ShallowCopy( inputMesh ) filter: FillPartialArrays = FillPartialArrays( outputMesh, - self.dictAttributesValues, + self.dictAttributesValues, True, ) - + if not filter.logger.hasHandlers(): filter.setLoggerHandler( VTKHandler() ) - + filter.applyFilter() self.clearDictAttributesValues = True diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py new file mode 100644 index 00000000..97fd45f4 --- /dev/null +++ b/geos-pv/src/geos/pv/utils/details.py @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. +# SPDX-FileContributor: Martin Lemay, Romain Baville +# ruff: noqa: E402 # disable Module level import not at top of file +import sys +from pathlib import Path +from typing import Union, Any +from typing_extensions import Self + +# from functools import wraps +# from dataclasses import dataclass + +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 ( # 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 vtkmodules.vtkCommonCore import ( + vtkInformation, + vtkInformationVector, +) + +# 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.pv.utils.config import update_paths + +update_paths() + +__doc__ = """ +Set of decorators that allow: + - quicker generation of MultiBlockDataSet to MultiBlockDataSet filters + - more stable logger strategy + +Usage is: + + @SISO(name='MyFilter',label='This is my filter',dtype='vtkMultiBlockDataSet') + class PVMyFilter: + ... + def __hidden_layer(self): + + +""" + +def SISOFilter(decorated_name, decorated_label, decorated_type): + """ + Decorate single input single output filter + """ + def decorated_class(cls): + @smproxy.filter( name=decorated_name, label=decorated_label ) + @smhint.xml( '' ) + @smproperty.input( name="Input", port_index=0 ) + @smdomain.datatype( + dataTypes=[ decorated_type ], + composite_data_supported=True, + ) + # @dataclass + # class WrappingClass(cls): + class WrappingClass(cls,VTKPythonAlgorithmBase): + + def __init__(self,*ar,**kw): + VTKPythonAlgorithmBase.__init__(self, nInputPorts=1, + nOutputPorts=1, + inputType=decorated_type, + outputType=decorated_type ) + cls.___init__(self,*ar,**kw) + WrappingClass.__name__ = cls.__name__ + WrappingClass.__module__ = cls.__module__ + + return WrappingClass + return decorated_class + + + From 84c0ba865030318340d16150fd61fb433c1d456b Mon Sep 17 00:00:00 2001 From: jacques franc Date: Tue, 30 Sep 2025 18:21:51 +0200 Subject: [PATCH 02/26] PoC --- .../geos/mesh/processing/FillPartialArrays.py | 27 +-------- .../geos/pv/plugins/PVFillPartialArrays.py | 30 +++++----- geos-utils/src/geos/utils/details.py | 55 +++++++++++++++++++ 3 files changed, 73 insertions(+), 39 deletions(-) create mode 100644 geos-utils/src/geos/utils/details.py diff --git a/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py b/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py index 1810bc73..492a77db 100644 --- a/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py +++ b/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py @@ -9,6 +9,7 @@ from geos.mesh.utils.arrayModifiers import fillPartialAttributes from geos.mesh.utils.arrayHelpers import getAttributePieceInfo +from geos.utils.details import addLogSupport from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet __doc__ = """ @@ -51,13 +52,13 @@ loggerTitle: str = "Fill Partial Attribute" +@addLogSupport(loggerTitle= loggerTitle) class FillPartialArrays: def __init__( self: Self, multiBlockDataSet: vtkMultiBlockDataSet, dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ], - speHandler: bool = False, ) -> None: """Fill partial attributes with constant value per component. @@ -75,30 +76,8 @@ def __init__( self.multiBlockDataSet: vtkMultiBlockDataSet = multiBlockDataSet self.dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ] = dictAttributesValues - # Logger. - self.logger: Logger - if not speHandler: - self.logger = getLogger( loggerTitle, True ) - else: - self.logger = logging.getLogger( loggerTitle ) - self.logger.setLevel( logging.INFO ) - 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. - """ - 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 ) -> bool: + def applyFilter( self : Self ) -> bool: """Create a constant attribute per region in the mesh. Returns: diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index c98c82a7..a8773bbd 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -30,7 +30,7 @@ update_paths() from geos.mesh.processing.FillPartialArrays import FillPartialArrays -import geos.pv.utils.details +# import geos.pv.utils.details __doc__ = """ Fill partial arrays of input mesh. @@ -47,22 +47,22 @@ """ -# @smproxy.filter( name="PVFillPartialArrays", label="Fill Partial Arrays" ) -# @smhint.xml( '' ) -# @smproperty.input( name="Input", port_index=0 ) -# @smdomain.datatype( -# dataTypes=[ "vtkMultiBlockDataSet" ], -# composite_data_supported=True, -# ) -@geos.pv.utils.details.SISOFilter(decorated_name="PVFillPartialArrays", decorated_label="Fill Partial Arrays",decorated_type="vtkMultiBlockDataSet") -class PVFillPartialArrays: +@smproxy.filter( name="PVFillPartialArrays", label="Fill Partial Arrays" ) +@smhint.xml( '' ) +@smproperty.input( name="Input", port_index=0 ) +@smdomain.datatype( + dataTypes=[ "vtkMultiBlockDataSet" ], + composite_data_supported=True, +) +# @geos.pv.utils.details.SISOFilter(decorated_name="PVFillPartialArrays", decorated_label="Fill Partial Arrays",decorated_type="vtkMultiBlockDataSet") +class PVFillPartialArrays(VTKPythonAlgorithmBase): def __init__( self: Self, ) -> None: """Fill a partial attribute with constant value per component.""" - # super().__init__( nInputPorts=1, - # nOutputPorts=1, - # inputType="vtkMultiBlockDataSet", - # outputType="vtkMultiBlockDataSet" ) + super().__init__( nInputPorts=1, + nOutputPorts=1, + inputType="vtkMultiBlockDataSet", + outputType="vtkMultiBlockDataSet" ) self.clearDictAttributesValues: bool = True self.dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ] = {} @@ -158,7 +158,7 @@ def RequestData( filter: FillPartialArrays = FillPartialArrays( outputMesh, self.dictAttributesValues, - True, + speHandler=True, ) if not filter.logger.hasHandlers(): diff --git a/geos-utils/src/geos/utils/details.py b/geos-utils/src/geos/utils/details.py new file mode 100644 index 00000000..cf6378e8 --- /dev/null +++ b/geos-utils/src/geos/utils/details.py @@ -0,0 +1,55 @@ +import logging +from geos.utils.Logger import Logger, getLogger +from functools import wraps +from typing import Type, TypeVar + + +__doc__ = """ + Decorators + +""" + +def addLogSupport(loggerTitle : str): + T = TypeVar('T') + def decorator(cls:Type[T]) -> Type[T]: + original_init = cls.__init__ + + @wraps(cls) + def new_init(self : T, *args, **kwargs): + + self.logger : Logger + + if kwargs.get('speHandler'): + kwargs.pop('speHandler') + self.logger = logging.getLogger( loggerTitle ) + self.logger.setLevel( logging.INFO ) + else: + self.logger = getLogger( loggerTitle, True) + + original_init(self,*args,*kwargs) + + @property + def logger(self : T)->Logger: + return self.logger + + def setLoggerHandler(self, handler : logging.Handler): + """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. + """ + 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." + ) + + cls.__init__ = new_init + cls.setLoggerHandler = setLoggerHandler + + return cls + + return decorator \ No newline at end of file From f32fe2ec799b18138f61017bcf5a785fb4bdac4b Mon Sep 17 00:00:00 2001 From: jacques franc Date: Thu, 2 Oct 2025 10:24:07 +0200 Subject: [PATCH 03/26] decorator and precommit recipe --- .hooks/pre-commit.example | 20 ++++++++ geos-utils/src/geos/utils/details.py | 71 ++++++++++++++++------------ 2 files changed, 61 insertions(+), 30 deletions(-) create mode 100755 .hooks/pre-commit.example diff --git a/.hooks/pre-commit.example b/.hooks/pre-commit.example new file mode 100755 index 00000000..7c7dc461 --- /dev/null +++ b/.hooks/pre-commit.example @@ -0,0 +1,20 @@ +#!/bin/bash -e + +function run_precommit_checks +{ + python -m mypy --config-file ./.mypy.ini --check-untyped-defs $1 + python -m ruff check --unsafe-fixes --config .ruff.toml $1 + python -m yapf -r -i --style .style.yapf $1 + + return 0 +} + + +source ${ENV_PYTHON_PATH}/bin/activate + +sphinx-build -b html docs/ docs/_build -W +for file in $(git diff --name-only --cached | grep py) +do + run_precommit_checks $file +done + diff --git a/geos-utils/src/geos/utils/details.py b/geos-utils/src/geos/utils/details.py index cf6378e8..88bf7da0 100644 --- a/geos-utils/src/geos/utils/details.py +++ b/geos-utils/src/geos/utils/details.py @@ -1,55 +1,66 @@ import logging from geos.utils.Logger import Logger, getLogger from functools import wraps -from typing import Type, TypeVar - +from typing import Type, TypeVar, Callable, Protocol, Any __doc__ = """ - Decorators + Group of decorators and Protocols that will be used in filters to wrap behaviors without code replication """ -def addLogSupport(loggerTitle : str): - T = TypeVar('T') - def decorator(cls:Type[T]) -> Type[T]: - original_init = cls.__init__ - @wraps(cls) - def new_init(self : T, *args, **kwargs): +class HasLogger( Protocol ): + """Protocol for classes that have logging support.""" + + logger: Logger + + def setLoggerHandler( 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. + """ + pass + - self.logger : Logger +T = TypeVar( 'T', bound='HasLogger' ) - if kwargs.get('speHandler'): - kwargs.pop('speHandler') + +def addLogSupport( loggerTitle: str ) -> Callable[ [ Type[ T ] ], Type[ T ] ]: + """Decorator to add logger support in the class following existing architecture. + + Args: + loggerTitle (str): Title to display in the logger + """ + + def decorator( cls: Type[ T ] ) -> Type[ T ]: + original_init = cls.__init__ + + @wraps( original_init ) + def new_init( self: T, *args: Any, **kwargs: Any ) -> None: + spe_handler = kwargs.pop( 'speHandler', False ) + if spe_handler: self.logger = logging.getLogger( loggerTitle ) self.logger.setLevel( logging.INFO ) else: - self.logger = getLogger( loggerTitle, True) - - original_init(self,*args,*kwargs) - - @property - def logger(self : T)->Logger: - return self.logger - - def setLoggerHandler(self, handler : logging.Handler): - """Set a specific handler for the filter logger. + self.logger = getLogger( loggerTitle, True ) - In this filter 4 log levels are use, .info, .error, .warning and .critical, be sure to have at least the same 4 levels. + original_init( self, *args, **kwargs ) - Args: - handler (logging.Handler): The handler to add. - """ - if not self.logger.hasHandlers(): + def setLoggerHandler( self: T, handler: logging.Handler ) -> None: + # No docstring needed - Protocol defines the contract + if not self.logger.handlers: 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." ) - cls.__init__ = new_init - cls.setLoggerHandler = setLoggerHandler + cls.__init__ = new_init # type: ignore[assignment] + cls.setLoggerHandler = setLoggerHandler # type: ignore[assignment] return cls - return decorator \ No newline at end of file + return decorator From 6f6e65269824771b054dd529a2daf7815ac74144 Mon Sep 17 00:00:00 2001 From: jacques franc Date: Thu, 2 Oct 2025 10:55:15 +0200 Subject: [PATCH 04/26] clean up --- .hooks/pre-commit.example | 9 ++- .../geos/pv/plugins/PVFillPartialArrays.py | 25 +++--- geos-pv/src/geos/pv/utils/details.py | 79 ------------------- 3 files changed, 18 insertions(+), 95 deletions(-) delete mode 100644 geos-pv/src/geos/pv/utils/details.py diff --git a/.hooks/pre-commit.example b/.hooks/pre-commit.example index 7c7dc461..f4f188d7 100755 --- a/.hooks/pre-commit.example +++ b/.hooks/pre-commit.example @@ -2,18 +2,21 @@ function run_precommit_checks { + echo "running mypy" python -m mypy --config-file ./.mypy.ini --check-untyped-defs $1 + echo "running ruff" python -m ruff check --unsafe-fixes --config .ruff.toml $1 + echo "running yapf" python -m yapf -r -i --style .style.yapf $1 return 0 } -source ${ENV_PYTHON_PATH}/bin/activate +source ${ENV_PYTHON}/bin/activate -sphinx-build -b html docs/ docs/_build -W -for file in $(git diff --name-only --cached | grep py) +#sphinx-build -b html docs/ docs/_build -W +for file in $(git diff --name-only --cached | grep -e "modified\|added" | grep py) do run_precommit_checks $file done diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index a8773bbd..b6b89272 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -9,10 +9,10 @@ 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 +) # 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 +) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from vtkmodules.vtkCommonDataModel import ( vtkMultiBlockDataSet, ) @@ -30,7 +30,7 @@ update_paths() from geos.mesh.processing.FillPartialArrays import FillPartialArrays -# import geos.pv.utils.details + __doc__ = """ Fill partial arrays of input mesh. @@ -54,8 +54,7 @@ dataTypes=[ "vtkMultiBlockDataSet" ], composite_data_supported=True, ) -# @geos.pv.utils.details.SISOFilter(decorated_name="PVFillPartialArrays", decorated_label="Fill Partial Arrays",decorated_type="vtkMultiBlockDataSet") -class PVFillPartialArrays(VTKPythonAlgorithmBase): +class PVFillPartialArrays( VTKPythonAlgorithmBase ): def __init__( self: Self, ) -> None: """Fill a partial attribute with constant value per component.""" @@ -67,8 +66,7 @@ def __init__( self: Self, ) -> None: self.clearDictAttributesValues: bool = True self.dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ] = {} - - @smproperty.xml(""" + @smproperty.xml( """ None: attributeName | fillingValueComponent1 fillingValueComponent2 ...\n To fill the attribute with the default value, live a blanc. The default value is:\n 0 for uint type, -1 for int type and nan for float type. - + @@ -102,10 +100,10 @@ def _setDictAttributesValues( self: Self, attributeName: str, values: str ) -> N self.clearDictAttributesValues = False if attributeName is not None: - if values is not None : + if values is not None: self.dictAttributesValues[ attributeName ] = list( values.split( "," ) ) else: - self.dictAttributesValues[ attributeName ] = None + self.dictAttributesValues[ attributeName ] = None # type: ignore[unreachable] self.Modified() @@ -156,9 +154,10 @@ def RequestData( outputMesh.ShallowCopy( inputMesh ) - filter: FillPartialArrays = FillPartialArrays( outputMesh, - self.dictAttributesValues, - speHandler=True, + filter: FillPartialArrays = FillPartialArrays( + outputMesh, + self.dictAttributesValues, + speHandler=True, ) if not filter.logger.hasHandlers(): diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py deleted file mode 100644 index 97fd45f4..00000000 --- a/geos-pv/src/geos/pv/utils/details.py +++ /dev/null @@ -1,79 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. -# SPDX-FileContributor: Martin Lemay, Romain Baville -# ruff: noqa: E402 # disable Module level import not at top of file -import sys -from pathlib import Path -from typing import Union, Any -from typing_extensions import Self - -# from functools import wraps -# from dataclasses import dataclass - -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 ( # 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 vtkmodules.vtkCommonCore import ( - vtkInformation, - vtkInformationVector, -) - -# 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.pv.utils.config import update_paths - -update_paths() - -__doc__ = """ -Set of decorators that allow: - - quicker generation of MultiBlockDataSet to MultiBlockDataSet filters - - more stable logger strategy - -Usage is: - - @SISO(name='MyFilter',label='This is my filter',dtype='vtkMultiBlockDataSet') - class PVMyFilter: - ... - def __hidden_layer(self): - - -""" - -def SISOFilter(decorated_name, decorated_label, decorated_type): - """ - Decorate single input single output filter - """ - def decorated_class(cls): - @smproxy.filter( name=decorated_name, label=decorated_label ) - @smhint.xml( '' ) - @smproperty.input( name="Input", port_index=0 ) - @smdomain.datatype( - dataTypes=[ decorated_type ], - composite_data_supported=True, - ) - # @dataclass - # class WrappingClass(cls): - class WrappingClass(cls,VTKPythonAlgorithmBase): - - def __init__(self,*ar,**kw): - VTKPythonAlgorithmBase.__init__(self, nInputPorts=1, - nOutputPorts=1, - inputType=decorated_type, - outputType=decorated_type ) - cls.___init__(self,*ar,**kw) - WrappingClass.__name__ = cls.__name__ - WrappingClass.__module__ = cls.__module__ - - return WrappingClass - return decorated_class - - - From 5437cc04940fcd39d6c4122e4b154934e44c3aa7 Mon Sep 17 00:00:00 2001 From: jacques franc Date: Thu, 2 Oct 2025 11:09:36 +0200 Subject: [PATCH 05/26] yapf uncommited --- geos-mesh/src/geos/mesh/processing/FillPartialArrays.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py b/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py index 492a77db..0f048c86 100644 --- a/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py +++ b/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py @@ -52,7 +52,7 @@ loggerTitle: str = "Fill Partial Attribute" -@addLogSupport(loggerTitle= loggerTitle) +@addLogSupport( loggerTitle=loggerTitle ) class FillPartialArrays: def __init__( @@ -76,8 +76,7 @@ def __init__( self.multiBlockDataSet: vtkMultiBlockDataSet = multiBlockDataSet self.dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ] = dictAttributesValues - - def applyFilter( self : Self ) -> bool: + def applyFilter( self: Self ) -> bool: """Create a constant attribute per region in the mesh. Returns: From 1f65fe7190b2d7311fb008eee8985553e38fd6b5 Mon Sep 17 00:00:00 2001 From: jacques franc Date: Thu, 2 Oct 2025 13:27:37 +0200 Subject: [PATCH 06/26] n+ attempts --- .../geos/pv/plugins/PVFillPartialArrays.py | 37 +++----- geos-pv/src/geos/pv/utils/details.py | 93 +++++++++++++++++++ 2 files changed, 106 insertions(+), 24 deletions(-) create mode 100644 geos-pv/src/geos/pv/utils/details.py diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index b6b89272..4cf6d67a 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -9,10 +9,10 @@ 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 +) # 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 +) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from vtkmodules.vtkCommonDataModel import ( vtkMultiBlockDataSet, ) @@ -29,8 +29,8 @@ update_paths() +from geos.pv.utils.details import SISOFilter from geos.mesh.processing.FillPartialArrays import FillPartialArrays - __doc__ = """ Fill partial arrays of input mesh. @@ -46,27 +46,17 @@ """ - -@smproxy.filter( name="PVFillPartialArrays", label="Fill Partial Arrays" ) -@smhint.xml( '' ) -@smproperty.input( name="Input", port_index=0 ) -@smdomain.datatype( - dataTypes=[ "vtkMultiBlockDataSet" ], - composite_data_supported=True, -) -class PVFillPartialArrays( VTKPythonAlgorithmBase ): +@SISOFilter(decorated_name="PVFillPartialArrays", decorated_label="Fill Partial Arrays",decorated_type="vtkMultiBlockDataSet") +class PVFillPartialArrays: def __init__( self: Self, ) -> None: """Fill a partial attribute with constant value per component.""" - super().__init__( nInputPorts=1, - nOutputPorts=1, - inputType="vtkMultiBlockDataSet", - outputType="vtkMultiBlockDataSet" ) self.clearDictAttributesValues: bool = True self.dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ] = {} - @smproperty.xml( """ + + @smproperty.xml(""" None: attributeName | fillingValueComponent1 fillingValueComponent2 ...\n To fill the attribute with the default value, live a blanc. The default value is:\n 0 for uint type, -1 for int type and nan for float type. - + @@ -100,10 +90,10 @@ def _setDictAttributesValues( self: Self, attributeName: str, values: str ) -> N self.clearDictAttributesValues = False if attributeName is not None: - if values is not None: + if values is not None : self.dictAttributesValues[ attributeName ] = list( values.split( "," ) ) else: - self.dictAttributesValues[ attributeName ] = None # type: ignore[unreachable] + self.dictAttributesValues[ attributeName ] = None self.Modified() @@ -154,10 +144,9 @@ def RequestData( outputMesh.ShallowCopy( inputMesh ) - filter: FillPartialArrays = FillPartialArrays( - outputMesh, - self.dictAttributesValues, - speHandler=True, + filter: FillPartialArrays = FillPartialArrays( outputMesh, + self.dictAttributesValues, + True, ) if not filter.logger.hasHandlers(): diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py new file mode 100644 index 00000000..9534e0fa --- /dev/null +++ b/geos-pv/src/geos/pv/utils/details.py @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. +# SPDX-FileContributor: Martin Lemay, Romain Baville +# ruff: noqa: E402 # disable Module level import not at top of file +import sys +from pathlib import Path +from functools import update_wrapper + +# from functools import wraps +# from dataclasses import dataclass + +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 ( # 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 vtkmodules.vtkCommonCore import ( + vtkInformation, + vtkInformationVector, +) + +# 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.pv.utils.config import update_paths + +update_paths() + +__doc__ = """ +Set of decorators that allow: + - quicker generation of MultiBlockDataSet to MultiBlockDataSet filters + - more stable logger strategy + +Usage is: + + @SISO(name='MyFilter',label='This is my filter',dtype='vtkMultiBlockDataSet') + class PVMyFilter: + ... + def __hidden_layer(self): + + +""" + +def SISOFilter(decorated_name, decorated_label, decorated_type): + """ + Decorate single input single output filter + """ + def decorated_class(cls): + @smproxy.filter( name=decorated_name, label=decorated_label ) + @smhint.xml( '' ) + @smproperty.input( name="Input", port_index=0 ) + @smdomain.datatype( + dataTypes=[ decorated_type ], + composite_data_supported=True, + ) + # @dataclass + # class WrappingClass(cls): + class WrappingClass(cls,VTKPythonAlgorithmBase): + + def __init__(self,*ar,**kw): + VTKPythonAlgorithmBase.__init__(self, nInputPorts=1, + nOutputPorts=1, + inputType=decorated_type, + outputType=decorated_type ) + cls.__init__(self,*ar,**kw) + + # IMPORTANT: Set the wrapper's name to match the original class + WrappingClass.__name__ = cls.__name__ + WrappingClass.__qualname__ = cls.__qualname__ + WrappingClass.__module__ = cls.__module__ + update_wrapper(WrappingClass, cls, updated=[]) + # # Copy metada + # import sys + # original_module = sys.modules.get(cls.__module__) + # print(f"Registering {cls.__name__} in module {cls.__module__}") + # print(f"Module found: {original_module is not None}") + + # if original_module is not None: + # setattr(original_module, cls.__name__, WrappingClass) + # # Verify registration + # print(f"Successfully registered: {hasattr(original_module, cls.__name__)}") + + return WrappingClass + + return decorated_class + + + From adaa26b7736df853c00d0bc12d7048a2eee59269 Mon Sep 17 00:00:00 2001 From: jacques franc Date: Fri, 3 Oct 2025 12:15:00 +0200 Subject: [PATCH 07/26] wip - need Protocol --- .../geos/pv/plugins/PVFillPartialArrays.py | 3 +- geos-pv/src/geos/pv/utils/details.py | 126 ++++++++++++------ 2 files changed, 90 insertions(+), 39 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index 4cf6d67a..f2d74e72 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -46,6 +46,7 @@ """ +print(f"Is using PVFillPartialArrays") @SISOFilter(decorated_name="PVFillPartialArrays", decorated_label="Fill Partial Arrays",decorated_type="vtkMultiBlockDataSet") class PVFillPartialArrays: @@ -146,7 +147,7 @@ def RequestData( filter: FillPartialArrays = FillPartialArrays( outputMesh, self.dictAttributesValues, - True, + speHandler=True, ) if not filter.logger.hasHandlers(): diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py index 9534e0fa..3096f051 100644 --- a/geos-pv/src/geos/pv/utils/details.py +++ b/geos-pv/src/geos/pv/utils/details.py @@ -5,7 +5,7 @@ import sys from pathlib import Path from functools import update_wrapper - +from typing import Protocol # from functools import wraps # from dataclasses import dataclass @@ -46,48 +46,98 @@ def __hidden_layer(self): """ + +# def SISOFilter(decorated_name, decorated_label, decorated_type): +# """ +# Decorate single input single output filter +# """ +# def decorated_class(cls): + +# # @dataclass +# # class WrappingClass(cls): +# @smproxy.filter( name=decorated_name, label=decorated_label ) +# @smhint.xml( '' ) +# @smproperty.input( name="Input", port_index=0 ) +# @smdomain.datatype( +# dataTypes=[ decorated_type ], +# composite_data_supported=True, +# ) +# class WrappingClass(cls,VTKPythonAlgorithmBase): + +# def __init__(self,*ar,**kw): +# VTKPythonAlgorithmBase.__init__(self, nInputPorts=1, +# nOutputPorts=1, +# inputType=decorated_type, +# outputType=decorated_type ) +# cls.__init__(self,*ar,**kw) + +# # IMPORTANT: Set the wrapper's name to match the original class +# WrappingClass.__name__ = cls.__name__ +# WrappingClass.__qualname__ = cls.__qualname__ +# WrappingClass.__module__ = cls.__module__ +# update_wrapper(WrappingClass, cls, updated=[]) +# # # Copy metada +# # import sys +# # original_module = sys.modules.get(cls.__module__) +# # print(f"Registering {cls.__name__} in module {cls.__module__}") +# # print(f"Module found: {original_module is not None}") + +# # if original_module is not None: +# # setattr(original_module, cls.__name__, WrappingClass) +# # # Verify registration +# # print(f"Successfully registered: {hasattr(original_module, cls.__name__)}") + +# return WrappingClass + +# return decorated_class + + + def SISOFilter(decorated_name, decorated_label, decorated_type): """ - Decorate single input single output filter + Decorate single input single output filter """ + print(f"Is using decorator") + def decorated_class(cls): - @smproxy.filter( name=decorated_name, label=decorated_label ) - @smhint.xml( '' ) - @smproperty.input( name="Input", port_index=0 ) - @smdomain.datatype( - dataTypes=[ decorated_type ], - composite_data_supported=True, + # Créer une fonction __init__ personnalisée + def new_init(self, *ar, **kw): + print(f"Is decorating init") + VTKPythonAlgorithmBase.__init__( + self, + nInputPorts=1, + nOutputPorts=1, + inputType=decorated_type, + outputType=decorated_type ) - # @dataclass - # class WrappingClass(cls): - class WrappingClass(cls,VTKPythonAlgorithmBase): - - def __init__(self,*ar,**kw): - VTKPythonAlgorithmBase.__init__(self, nInputPorts=1, - nOutputPorts=1, - inputType=decorated_type, - outputType=decorated_type ) - cls.__init__(self,*ar,**kw) - - # IMPORTANT: Set the wrapper's name to match the original class - WrappingClass.__name__ = cls.__name__ - WrappingClass.__qualname__ = cls.__qualname__ - WrappingClass.__module__ = cls.__module__ + cls.__init__(self, *ar, **kw) + + print(f"Is creating Wrapping class") + # Créer dynamiquement la nouvelle classe + WrappingClass = type( + cls.__name__, # Nom de la classe + (VTKPythonAlgorithmBase,), # Bases + { + '__init__': new_init, + '__module__': cls.__module__, + '__qualname__': cls.__qualname__, + 'RequestData' : cls.RequestData, + 'RequestDataObject':cls.RequestDataObject, + } + ) + + # Copier les métadonnées update_wrapper(WrappingClass, cls, updated=[]) - # # Copy metada - # import sys - # original_module = sys.modules.get(cls.__module__) - # print(f"Registering {cls.__name__} in module {cls.__module__}") - # print(f"Module found: {original_module is not None}") - - # if original_module is not None: - # setattr(original_module, cls.__name__, WrappingClass) - # # Verify registration - # print(f"Successfully registered: {hasattr(original_module, cls.__name__)}") + #decorate it old fashion way + smhint.xml( '')(WrappingClass) + smproxy.filter( name="PVFillPartialArrays", label="Fill Partial Arrays" )(WrappingClass) + smproperty.input( name="Input", port_index=0 )(WrappingClass) + smdomain.datatype(dataTypes=[ "vtkMultiBlockDataSet" ], composite_data_supported=True, )(WrappingClass) + print(f"returned class ids {cls.__name__}") + # dbg = getattr(WrappingClass) + print(f"returned class ids {dir(WrappingClass)}") + print(f"returned class WrappingClass {WrappingClass.__name__}") + return WrappingClass - - return decorated_class - - - + return decorated_class \ No newline at end of file From 2ec2a527f7f399a95e3c746deb048119a26d5d4f Mon Sep 17 00:00:00 2001 From: jacques franc Date: Fri, 3 Oct 2025 15:31:25 +0200 Subject: [PATCH 08/26] Crash but closer --- .../geos/pv/plugins/PVFillPartialArrays.py | 49 ++++++++++--------- geos-pv/src/geos/pv/utils/details.py | 47 +++++++++++++++--- 2 files changed, 65 insertions(+), 31 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index f2d74e72..8206756b 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -47,7 +47,8 @@ """ print(f"Is using PVFillPartialArrays") -@SISOFilter(decorated_name="PVFillPartialArrays", decorated_label="Fill Partial Arrays",decorated_type="vtkMultiBlockDataSet") +@smproperty.input(name="Input", port_index=0) +@SISOFilter( decorated_label="Fill Partial Arrays",decorated_type="vtkMultiBlockDataSet") class PVFillPartialArrays: def __init__( self: Self, ) -> None: @@ -98,29 +99,29 @@ def _setDictAttributesValues( self: Self, attributeName: str, values: str ) -> N self.Modified() - def RequestDataObject( - self: Self, - request: vtkInformation, - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestDataObject. - - 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 ) # type: ignore[no-any-return] + # def RequestDataObject( + # self: Self, + # request: vtkInformation, + # inInfoVec: list[ vtkInformationVector ], + # outInfoVec: vtkInformationVector, + # ) -> int: + # """Inherited from VTKPythonAlgorithmBase::RequestDataObject. + + # 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 ) # type: ignore[no-any-return] def RequestData( self: Self, diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py index 3096f051..6de6958f 100644 --- a/geos-pv/src/geos/pv/utils/details.py +++ b/geos-pv/src/geos/pv/utils/details.py @@ -6,6 +6,7 @@ from pathlib import Path from functools import update_wrapper from typing import Protocol +from abc import abstractmethod # from functools import wraps # from dataclasses import dataclass @@ -91,9 +92,19 @@ def __hidden_layer(self): # return decorated_class +# def IsSISOFilter(Protocol): +# @abstractmethod +# def RequestData( +# self, +# request: vtkInformation, # noqa: F841 +# inInfoVec: list[ vtkInformationVector ], +# outInfoVec: vtkInformationVector, +# ) -> int: +# raise NotImplementedError -def SISOFilter(decorated_name, decorated_label, decorated_type): + +def SISOFilter(decorated_label, decorated_type): """ Decorate single input single output filter """ @@ -112,17 +123,39 @@ def new_init(self, *ar, **kw): ) cls.__init__(self, *ar, **kw) + def RequestDataObject( + self, + request: vtkInformation, + inInfoVec: list[ vtkInformationVector ], + outInfoVec: vtkInformationVector, ) -> int: + """Inherited from VTKPythonAlgorithmBase::RequestDataObject. + + 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 ) # type: ignore[no-any-return] + print(f"Is creating Wrapping class") # Créer dynamiquement la nouvelle classe WrappingClass = type( cls.__name__, # Nom de la classe - (VTKPythonAlgorithmBase,), # Bases + (VTKPythonAlgorithmBase,cls), # Bases { '__init__': new_init, '__module__': cls.__module__, '__qualname__': cls.__qualname__, - 'RequestData' : cls.RequestData, - 'RequestDataObject':cls.RequestDataObject, + 'RequestDataObject' : RequestDataObject, } ) @@ -131,9 +164,9 @@ def new_init(self, *ar, **kw): #decorate it old fashion way smhint.xml( '')(WrappingClass) - smproxy.filter( name="PVFillPartialArrays", label="Fill Partial Arrays" )(WrappingClass) - smproperty.input( name="Input", port_index=0 )(WrappingClass) - smdomain.datatype(dataTypes=[ "vtkMultiBlockDataSet" ], composite_data_supported=True, )(WrappingClass) + smproxy.filter( name=cls.__name__, label=decorated_label)(WrappingClass) + # smproperty.input( name="Input", port_index=0 )(WrappingClass) + smdomain.datatype(dataTypes=[ decorated_type ], composite_data_supported=True, )(WrappingClass) print(f"returned class ids {cls.__name__}") # dbg = getattr(WrappingClass) print(f"returned class ids {dir(WrappingClass)}") From 99929178d87e30071e49424ea5dcef09d7a00c7a Mon Sep 17 00:00:00 2001 From: jacques franc Date: Fri, 10 Oct 2025 10:40:43 +0200 Subject: [PATCH 09/26] safe path --- geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py | 5 ++++- geos-pv/src/geos/pv/utils/details.py | 10 +++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index 8206756b..ba362871 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -47,7 +47,10 @@ """ print(f"Is using PVFillPartialArrays") -@smproperty.input(name="Input", port_index=0) +@smhint.xml( '') +@smproxy.filter( name="PVFillPartialArray", label="Fill Partial Arrays") +@smproperty.input( name="Input", port_index=0 ) +@smdomain.datatype(dataTypes=[ "vtkMultiBlockDataSet" ], composite_data_supported=True, ) @SISOFilter( decorated_label="Fill Partial Arrays",decorated_type="vtkMultiBlockDataSet") class PVFillPartialArrays: diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py index 6de6958f..0d7c170a 100644 --- a/geos-pv/src/geos/pv/utils/details.py +++ b/geos-pv/src/geos/pv/utils/details.py @@ -144,7 +144,7 @@ def RequestDataObject( 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] + return VTKPythonAlgorithmBase.RequestDataObject(self, request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] print(f"Is creating Wrapping class") # Créer dynamiquement la nouvelle classe @@ -156,6 +156,7 @@ def RequestDataObject( '__module__': cls.__module__, '__qualname__': cls.__qualname__, 'RequestDataObject' : RequestDataObject, + 'RequestData': cls.RequestData, } ) @@ -163,12 +164,11 @@ def RequestDataObject( update_wrapper(WrappingClass, cls, updated=[]) #decorate it old fashion way - smhint.xml( '')(WrappingClass) - smproxy.filter( name=cls.__name__, label=decorated_label)(WrappingClass) + # smhint.xml( '')(WrappingClass) + # smproxy.filter( name=cls.__name__, label=decorated_label)(WrappingClass) # smproperty.input( name="Input", port_index=0 )(WrappingClass) - smdomain.datatype(dataTypes=[ decorated_type ], composite_data_supported=True, )(WrappingClass) + # smdomain.datatype(dataTypes=[ decorated_type ], composite_data_supported=True, )(WrappingClass) print(f"returned class ids {cls.__name__}") - # dbg = getattr(WrappingClass) print(f"returned class ids {dir(WrappingClass)}") print(f"returned class WrappingClass {WrappingClass.__name__}") From f08e059049a6a1ce2e139b45d83b4397ce25299d Mon Sep 17 00:00:00 2001 From: jacques franc Date: Fri, 10 Oct 2025 12:37:45 +0200 Subject: [PATCH 10/26] ordering filter and others to avoid segfault --- .../geos/pv/plugins/PVFillPartialArrays.py | 4 --- geos-pv/src/geos/pv/utils/details.py | 31 ++++++++++++------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index ba362871..a5694d7a 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -47,10 +47,6 @@ """ print(f"Is using PVFillPartialArrays") -@smhint.xml( '') -@smproxy.filter( name="PVFillPartialArray", label="Fill Partial Arrays") -@smproperty.input( name="Input", port_index=0 ) -@smdomain.datatype(dataTypes=[ "vtkMultiBlockDataSet" ], composite_data_supported=True, ) @SISOFilter( decorated_label="Fill Partial Arrays",decorated_type="vtkMultiBlockDataSet") class PVFillPartialArrays: diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py index 0d7c170a..5cee0059 100644 --- a/geos-pv/src/geos/pv/utils/details.py +++ b/geos-pv/src/geos/pv/utils/details.py @@ -111,6 +111,7 @@ def SISOFilter(decorated_label, decorated_type): print(f"Is using decorator") def decorated_class(cls): + original_init = cls.__init__ # Créer une fonction __init__ personnalisée def new_init(self, *ar, **kw): print(f"Is decorating init") @@ -121,7 +122,9 @@ def new_init(self, *ar, **kw): inputType=decorated_type, outputType=decorated_type ) - cls.__init__(self, *ar, **kw) + + if original_init is not object.__init__: + original_init(self, *ar, **kw) def RequestDataObject( self, @@ -145,29 +148,33 @@ def RequestDataObject( outData = inData.NewInstance() outInfoVec.GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), outData ) return VTKPythonAlgorithmBase.RequestDataObject(self, request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] - - print(f"Is creating Wrapping class") - # Créer dynamiquement la nouvelle classe - WrappingClass = type( - cls.__name__, # Nom de la classe - (VTKPythonAlgorithmBase,cls), # Bases - { + + class_dict = { '__init__': new_init, '__module__': cls.__module__, '__qualname__': cls.__qualname__, 'RequestDataObject' : RequestDataObject, 'RequestData': cls.RequestData, } + print(f"Is creating Wrapping class") + # Créer dynamiquement la nouvelle classe + WrappingClass = type( + cls.__name__, # Nom de la classe + (VTKPythonAlgorithmBase,cls), # Bases + class_dict ) # Copier les métadonnées update_wrapper(WrappingClass, cls, updated=[]) #decorate it old fashion way - # smhint.xml( '')(WrappingClass) - # smproxy.filter( name=cls.__name__, label=decorated_label)(WrappingClass) - # smproperty.input( name="Input", port_index=0 )(WrappingClass) - # smdomain.datatype(dataTypes=[ decorated_type ], composite_data_supported=True, )(WrappingClass) + WrappingClass = smdomain. datatype(dataTypes=[ decorated_type ], + composite_data_supported=True, + )(WrappingClass) + WrappingClass = smproperty.input( name="Input", port_index=0 )(WrappingClass) + WrappingClass = smhint.xml( '')(WrappingClass) + WrappingClass = smproxy.filter( name=cls.__name__, + label=decorated_label)(WrappingClass) print(f"returned class ids {cls.__name__}") print(f"returned class ids {dir(WrappingClass)}") print(f"returned class WrappingClass {WrappingClass.__name__}") From 9ed4ec421f23ec6e439c30078f7424b7c56c80c7 Mon Sep 17 00:00:00 2001 From: jacques franc Date: Fri, 10 Oct 2025 13:50:43 +0200 Subject: [PATCH 11/26] some inheritance --- .../geos/pv/plugins/PVFillPartialArrays.py | 28 ++----------------- geos-pv/src/geos/pv/utils/details.py | 22 +++++++-------- 2 files changed, 14 insertions(+), 36 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index a5694d7a..2746b5a7 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -47,7 +47,9 @@ """ print(f"Is using PVFillPartialArrays") -@SISOFilter( decorated_label="Fill Partial Arrays",decorated_type="vtkMultiBlockDataSet") +@SISOFilter( category = '', + decorated_label="Fill Partial Arrays", + decorated_type="vtkMultiBlockDataSet") class PVFillPartialArrays: def __init__( self: Self, ) -> None: @@ -98,30 +100,6 @@ def _setDictAttributesValues( self: Self, attributeName: str, values: str ) -> N self.Modified() - # def RequestDataObject( - # self: Self, - # request: vtkInformation, - # inInfoVec: list[ vtkInformationVector ], - # outInfoVec: vtkInformationVector, - # ) -> int: - # """Inherited from VTKPythonAlgorithmBase::RequestDataObject. - - # 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 ) # type: ignore[no-any-return] - def RequestData( self: Self, request: vtkInformation, # noqa: F841 diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py index 5cee0059..04cefbd8 100644 --- a/geos-pv/src/geos/pv/utils/details.py +++ b/geos-pv/src/geos/pv/utils/details.py @@ -104,7 +104,7 @@ def __hidden_layer(self): # raise NotImplementedError -def SISOFilter(decorated_label, decorated_type): +def SISOFilter(category, decorated_label, decorated_type): """ Decorate single input single output filter """ @@ -114,7 +114,6 @@ def decorated_class(cls): original_init = cls.__init__ # Créer une fonction __init__ personnalisée def new_init(self, *ar, **kw): - print(f"Is decorating init") VTKPythonAlgorithmBase.__init__( self, nInputPorts=1, @@ -149,22 +148,26 @@ def RequestDataObject( outInfoVec.GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), outData ) return VTKPythonAlgorithmBase.RequestDataObject(self, request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] + # to be inserted in the class class_dict = { '__init__': new_init, '__module__': cls.__module__, '__qualname__': cls.__qualname__, 'RequestDataObject' : RequestDataObject, - 'RequestData': cls.RequestData, + # 'RequestData': cls.RequestData, } - print(f"Is creating Wrapping class") - # Créer dynamiquement la nouvelle classe + + # if hasattr(cls,'RequestData'): + # class_dict['RequestData'] = cls.RequestData + + # dynamically creates a class WrappingClass = type( cls.__name__, # Nom de la classe - (VTKPythonAlgorithmBase,cls), # Bases + (cls,VTKPythonAlgorithmBase), # Bases class_dict ) - # Copier les métadonnées + # Copy metadata update_wrapper(WrappingClass, cls, updated=[]) #decorate it old fashion way @@ -172,12 +175,9 @@ def RequestDataObject( composite_data_supported=True, )(WrappingClass) WrappingClass = smproperty.input( name="Input", port_index=0 )(WrappingClass) - WrappingClass = smhint.xml( '')(WrappingClass) + WrappingClass = smhint.xml( category )(WrappingClass) WrappingClass = smproxy.filter( name=cls.__name__, label=decorated_label)(WrappingClass) - print(f"returned class ids {cls.__name__}") - print(f"returned class ids {dir(WrappingClass)}") - print(f"returned class WrappingClass {WrappingClass.__name__}") return WrappingClass return decorated_class \ No newline at end of file From 4e4b7042b68876cddf465f8dc8a38e26fe5766ee Mon Sep 17 00:00:00 2001 From: jacques franc Date: Fri, 10 Oct 2025 14:01:33 +0200 Subject: [PATCH 12/26] copied inherited --- geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py | 4 ++-- geos-pv/src/geos/pv/utils/details.py | 14 ++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index 2746b5a7..6609cbf2 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -63,7 +63,7 @@ def __init__( self: Self, ) -> None: @@ -81,7 +81,7 @@ def __init__( self: Self, ) -> None: """ ) - def _setDictAttributesValues( self: Self, attributeName: str, values: str ) -> None: + def setDictAttributesValues( self: Self, attributeName: str, values: str ) -> None: """Set the dictionary with the region indexes and its corresponding list of value for each components. Args: diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py index 04cefbd8..41a55d06 100644 --- a/geos-pv/src/geos/pv/utils/details.py +++ b/geos-pv/src/geos/pv/utils/details.py @@ -153,12 +153,18 @@ def RequestDataObject( '__init__': new_init, '__module__': cls.__module__, '__qualname__': cls.__qualname__, + '__doc__' : cls.__doc__, 'RequestDataObject' : RequestDataObject, - # 'RequestData': cls.RequestData, } - - # if hasattr(cls,'RequestData'): - # class_dict['RequestData'] = cls.RequestData + # Copy all methods and attributes from cls, including decorator metadata + for attr_name in dir(cls): + if attr_name.startswith('_'): + continue # Skip private/magic methods (already handled or inherited) + + attr = getattr(cls, attr_name) + # Copy methods with their decorators + if callable(attr) and attr_name not in class_dict: + class_dict[attr_name] = attr # dynamically creates a class WrappingClass = type( From 4919f5dad7fc86777485cbba599f7d0a5d17f231 Mon Sep 17 00:00:00 2001 From: jacques franc Date: Fri, 10 Oct 2025 14:28:36 +0200 Subject: [PATCH 13/26] Even more refactored --- .../geos/pv/plugins/PVFillPartialArrays.py | 28 ++----------------- geos-pv/src/geos/pv/utils/details.py | 27 ++++++++++++++++++ 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index 6609cbf2..46686414 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -100,29 +100,7 @@ def setDictAttributesValues( self: Self, attributeName: str, values: str ) -> No self.Modified() - def RequestData( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], - 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. - """ - inputMesh: vtkMultiBlockDataSet = self.GetInputData( inInfoVec, 0, 0 ) - outputMesh: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 0 ) - assert inputMesh is not None, "Input server mesh is null." - assert outputMesh is not None, "Output pipeline is null." - - outputMesh.ShallowCopy( inputMesh ) - + def Filter(self,inputMesh, outputMesh): filter: FillPartialArrays = FillPartialArrays( outputMesh, self.dictAttributesValues, speHandler=True, @@ -134,5 +112,5 @@ def RequestData( filter.applyFilter() self.clearDictAttributesValues = True - - return 1 + + return \ No newline at end of file diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py index 41a55d06..2c19327e 100644 --- a/geos-pv/src/geos/pv/utils/details.py +++ b/geos-pv/src/geos/pv/utils/details.py @@ -148,6 +148,32 @@ def RequestDataObject( outInfoVec.GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), outData ) return VTKPythonAlgorithmBase.RequestDataObject(self, request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] + + def RequestData( self , + request: vtkInformation, # noqa: F841 + inInfoVec: list[ vtkInformationVector ], + 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. + """ + inputMesh: vtkMultiBlockDataSet = self.GetInputData( inInfoVec, 0, 0 ) + outputMesh: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 0 ) + assert inputMesh is not None, "Input server mesh is null." + assert outputMesh is not None, "Output pipeline is null." + + outputMesh.ShallowCopy( inputMesh ) + + cls.Filter(self, inputMesh,outputMesh) #TODO make it abstract in a Protocol + return 1 + # to be inserted in the class class_dict = { '__init__': new_init, @@ -155,6 +181,7 @@ def RequestDataObject( '__qualname__': cls.__qualname__, '__doc__' : cls.__doc__, 'RequestDataObject' : RequestDataObject, + 'RequestData' : RequestData } # Copy all methods and attributes from cls, including decorator metadata for attr_name in dir(cls): From ee309ffc434b489b95ed123521dfa33f7c850ac4 Mon Sep 17 00:00:00 2001 From: jacques franc Date: Tue, 14 Oct 2025 15:42:49 +0200 Subject: [PATCH 14/26] make it a class !! --- .../geos/pv/plugins/PVFillPartialArrays.py | 12 +- geos-pv/src/geos/pv/utils/details.py | 268 +++++++----------- 2 files changed, 110 insertions(+), 170 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index 46686414..93ead490 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -17,11 +17,6 @@ from vtkmodules.vtkCommonDataModel import ( vtkMultiBlockDataSet, ) -from vtkmodules.vtkCommonCore import ( - vtkInformation, - vtkInformationVector, -) - # 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" ) ) @@ -29,7 +24,7 @@ update_paths() -from geos.pv.utils.details import SISOFilter +from geos.pv.utils.details import SISOFilter, FilterCategory from geos.mesh.processing.FillPartialArrays import FillPartialArrays __doc__ = """ Fill partial arrays of input mesh. @@ -46,8 +41,7 @@ """ -print(f"Is using PVFillPartialArrays") -@SISOFilter( category = '', +@SISOFilter( category = FilterCategory.GEOS_UTILS, decorated_label="Fill Partial Arrays", decorated_type="vtkMultiBlockDataSet") class PVFillPartialArrays: @@ -100,7 +94,7 @@ def setDictAttributesValues( self: Self, attributeName: str, values: str ) -> No self.Modified() - def Filter(self,inputMesh, outputMesh): + def Filter(self,inputMesh : vtkMultiBlockDataSet, outputMesh : vtkMultiBlockDataSet): filter: FillPartialArrays = FillPartialArrays( outputMesh, self.dictAttributesValues, speHandler=True, diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py index 2c19327e..b0d8ba5b 100644 --- a/geos-pv/src/geos/pv/utils/details.py +++ b/geos-pv/src/geos/pv/utils/details.py @@ -4,9 +4,20 @@ # ruff: noqa: E402 # disable Module level import not at top of file import sys from pathlib import Path +# Add Enum for filter categories from functools import update_wrapper -from typing import Protocol +from typing import Protocol ,Any from abc import abstractmethod +from enum import Enum + +# Enum for filter categories +class FilterCategory(str,Enum): + GEOS_UTILS = '4- Geos Utils' + GEOS_MESH = '1- Geos Mesh' + GEOS_GEOMECHANICS = '2- Geos Geomechanics' + GEOS_PV = '3- Geos PV' + # Add more as needed + # from functools import wraps # from dataclasses import dataclass @@ -33,174 +44,110 @@ update_paths() __doc__ = """ -Set of decorators that allow: - - quicker generation of MultiBlockDataSet to MultiBlockDataSet filters - - more stable logger strategy +Set of decorators that allows quicker generation of MultiBlockDataSet to MultiBlockDataSet filters Usage is: @SISO(name='MyFilter',label='This is my filter',dtype='vtkMultiBlockDataSet') class PVMyFilter: - ... - def __hidden_layer(self): - + ... """ - - -# def SISOFilter(decorated_name, decorated_label, decorated_type): -# """ -# Decorate single input single output filter -# """ -# def decorated_class(cls): - -# # @dataclass -# # class WrappingClass(cls): -# @smproxy.filter( name=decorated_name, label=decorated_label ) -# @smhint.xml( '' ) -# @smproperty.input( name="Input", port_index=0 ) -# @smdomain.datatype( -# dataTypes=[ decorated_type ], -# composite_data_supported=True, -# ) -# class WrappingClass(cls,VTKPythonAlgorithmBase): - -# def __init__(self,*ar,**kw): -# VTKPythonAlgorithmBase.__init__(self, nInputPorts=1, -# nOutputPorts=1, -# inputType=decorated_type, -# outputType=decorated_type ) -# cls.__init__(self,*ar,**kw) - -# # IMPORTANT: Set the wrapper's name to match the original class -# WrappingClass.__name__ = cls.__name__ -# WrappingClass.__qualname__ = cls.__qualname__ -# WrappingClass.__module__ = cls.__module__ -# update_wrapper(WrappingClass, cls, updated=[]) -# # # Copy metada -# # import sys -# # original_module = sys.modules.get(cls.__module__) -# # print(f"Registering {cls.__name__} in module {cls.__module__}") -# # print(f"Module found: {original_module is not None}") - -# # if original_module is not None: -# # setattr(original_module, cls.__name__, WrappingClass) -# # # Verify registration -# # print(f"Successfully registered: {hasattr(original_module, cls.__name__)}") - -# return WrappingClass - -# return decorated_class - -# def IsSISOFilter(Protocol): - -# @abstractmethod -# def RequestData( -# self, -# request: vtkInformation, # noqa: F841 -# inInfoVec: list[ vtkInformationVector ], -# outInfoVec: vtkInformationVector, -# ) -> int: -# raise NotImplementedError - - -def SISOFilter(category, decorated_label, decorated_type): - """ - Decorate single input single output filter - """ - print(f"Is using decorator") - - def decorated_class(cls): +def IsSISOFilter(Protocol): + """Protocol to ensure that the wrapped filter defines the correct Filter core function.""" + + @abstractmethod + def Filter( + self, + inputMesh: vtkMultiBlockDataSet, + outputMesh: vtkMultiBlockDataSet, + ) -> None: + raise NotImplementedError + + +def SISOFilter(category: FilterCategory, decorated_label: str, decorated_type: str): + """Decorate single input single output filter.""" + def decorated_class(cls : IsSISOFilter): + """Outer wrapper function. All is in the WrappingClass below.""" original_init = cls.__init__ + + class WrappingClass(cls, VTKPythonAlgorithmBase): # Créer une fonction __init__ personnalisée - def new_init(self, *ar, **kw): - VTKPythonAlgorithmBase.__init__( - self, - nInputPorts=1, - nOutputPorts=1, - inputType=decorated_type, - outputType=decorated_type - ) - - if original_init is not object.__init__: - original_init(self, *ar, **kw) - - def RequestDataObject( - self, - request: vtkInformation, - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestDataObject. - - 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 VTKPythonAlgorithmBase.RequestDataObject(self, request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] - + def __init__(self, *ar:Any, **kw : Any)->None: + """Pre-init the filter with the Base algo and I/O single type (usually vtkMultiBlockDataSet) + + Args: + ar : fowarded arguments + kw : forwarded keywords args + """ + VTKPythonAlgorithmBase.__init__( + self, + nInputPorts=1, + nOutputPorts=1, + inputType=decorated_type, + outputType=decorated_type + ) + + #If wrapped class has more to init there it is applied + #avoid the overwritten init by decorator taking place of the cls + if original_init is not object.__init__: + original_init(self, *ar, **kw) + + def RequestDataObject( + self, + request: vtkInformation, + inInfoVec: list[ vtkInformationVector ], + outInfoVec: vtkInformationVector, ) -> int: + """Inherited from VTKPythonAlgorithmBase::RequestDataObject. + + 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 VTKPythonAlgorithmBase.RequestDataObject(self, request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] + + def RequestData( self , + request: vtkInformation, # noqa: F841 + inInfoVec: list[ vtkInformationVector ], + 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. + """ + inputMesh: vtkMultiBlockDataSet = self.GetInputData( inInfoVec, 0, 0 ) + outputMesh: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 0 ) + assert inputMesh is not None, "Input server mesh is null." + assert outputMesh is not None, "Output pipeline is null." + + outputMesh.ShallowCopy( inputMesh ) + + cls.Filter(self, inputMesh,outputMesh) #TODO make it abstract in a Protocol + return 1 - def RequestData( self , - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], - 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. - """ - inputMesh: vtkMultiBlockDataSet = self.GetInputData( inInfoVec, 0, 0 ) - outputMesh: vtkMultiBlockDataSet = self.GetOutputData( outInfoVec, 0 ) - assert inputMesh is not None, "Input server mesh is null." - assert outputMesh is not None, "Output pipeline is null." - - outputMesh.ShallowCopy( inputMesh ) - - cls.Filter(self, inputMesh,outputMesh) #TODO make it abstract in a Protocol - return 1 - - # to be inserted in the class - class_dict = { - '__init__': new_init, - '__module__': cls.__module__, - '__qualname__': cls.__qualname__, - '__doc__' : cls.__doc__, - 'RequestDataObject' : RequestDataObject, - 'RequestData' : RequestData - } - # Copy all methods and attributes from cls, including decorator metadata - for attr_name in dir(cls): - if attr_name.startswith('_'): - continue # Skip private/magic methods (already handled or inherited) - - attr = getattr(cls, attr_name) - # Copy methods with their decorators - if callable(attr) and attr_name not in class_dict: - class_dict[attr_name] = attr - - # dynamically creates a class - WrappingClass = type( - cls.__name__, # Nom de la classe - (cls,VTKPythonAlgorithmBase), # Bases - class_dict - ) + # Copy metadata + WrappingClass.__name__ = cls.__name__ + WrappingClass.__qualname__ = cls.__qualname__ + WrappingClass.__module__ = cls.__module__ + WrappingClass.__doc__ = cls.__doc__ update_wrapper(WrappingClass, cls, updated=[]) #decorate it old fashion way @@ -208,9 +155,8 @@ def RequestData( self , composite_data_supported=True, )(WrappingClass) WrappingClass = smproperty.input( name="Input", port_index=0 )(WrappingClass) - WrappingClass = smhint.xml( category )(WrappingClass) - WrappingClass = smproxy.filter( name=cls.__name__, - label=decorated_label)(WrappingClass) - + # Use enum value for category + WrappingClass = smhint.xml(f'')(WrappingClass) + WrappingClass = smproxy.filter(name=getattr(cls, '__name__', str(cls)), label=decorated_label)(WrappingClass) return WrappingClass return decorated_class \ No newline at end of file From 827fa23a06adde2fb21ae2083317a033af66b7f1 Mon Sep 17 00:00:00 2001 From: jacques franc Date: Tue, 14 Oct 2025 17:06:49 +0200 Subject: [PATCH 15/26] mypy/ruff/yapf --- .../geos/pv/plugins/PVFillPartialArrays.py | 44 ++++---- geos-pv/src/geos/pv/utils/details.py | 100 ++++++++++-------- 2 files changed, 84 insertions(+), 60 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index 93ead490..6ddd69d4 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -8,11 +8,11 @@ from typing_extensions import Self 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 + 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 +) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from vtkmodules.vtkCommonDataModel import ( vtkMultiBlockDataSet, ) @@ -26,6 +26,7 @@ from geos.pv.utils.details import SISOFilter, FilterCategory from geos.mesh.processing.FillPartialArrays import FillPartialArrays + __doc__ = """ Fill partial arrays of input mesh. @@ -41,19 +42,18 @@ """ -@SISOFilter( category = FilterCategory.GEOS_UTILS, - decorated_label="Fill Partial Arrays", - decorated_type="vtkMultiBlockDataSet") + +@SISOFilter( category=FilterCategory.GEOS_UTILS, + decorated_label="Fill Partial Arrays", + decorated_type="vtkMultiBlockDataSet" ) class PVFillPartialArrays: def __init__( self: Self, ) -> None: """Fill a partial attribute with constant value per component.""" - self.clearDictAttributesValues: bool = True self.dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ] = {} - - @smproperty.xml(""" + @smproperty.xml( """ None: attributeName | fillingValueComponent1 fillingValueComponent2 ...\n To fill the attribute with the default value, live a blanc. The default value is:\n 0 for uint type, -1 for int type and nan for float type. - + @@ -87,17 +87,25 @@ def setDictAttributesValues( self: Self, attributeName: str, values: str ) -> No self.clearDictAttributesValues = False if attributeName is not None: - if values is not None : + if values is not None: self.dictAttributesValues[ attributeName ] = list( values.split( "," ) ) else: - self.dictAttributesValues[ attributeName ] = None + self.dictAttributesValues[ attributeName ] = None # ignore : type[unreachable] self.Modified() - def Filter(self,inputMesh : vtkMultiBlockDataSet, outputMesh : vtkMultiBlockDataSet): - filter: FillPartialArrays = FillPartialArrays( outputMesh, - self.dictAttributesValues, - speHandler=True, + def Filter( self, inputMesh: vtkMultiBlockDataSet, outputMesh: vtkMultiBlockDataSet ) -> None: + """Is applying FillPartialArrays to the mesh and return with the class's dictionnary for attributes values. + + Args: + inputMesh : a mesh to transform + outputMesh : a mesh transformed + + """ + filter: FillPartialArrays = FillPartialArrays( + outputMesh, + self.dictAttributesValues, + speHandler=True, ) if not filter.logger.hasHandlers(): @@ -106,5 +114,5 @@ def Filter(self,inputMesh : vtkMultiBlockDataSet, outputMesh : vtkMultiBlockData filter.applyFilter() self.clearDictAttributesValues = True - - return \ No newline at end of file + + return diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py index b0d8ba5b..eadf5dea 100644 --- a/geos-pv/src/geos/pv/utils/details.py +++ b/geos-pv/src/geos/pv/utils/details.py @@ -2,31 +2,31 @@ # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. # SPDX-FileContributor: Martin Lemay, Romain Baville # ruff: noqa: E402 # disable Module level import not at top of file +# mypy: disable-error-code="misc" import sys from pathlib import Path # Add Enum for filter categories from functools import update_wrapper -from typing import Protocol ,Any +from typing import Protocol, Any, Type, TypeVar, Callable from abc import abstractmethod from enum import Enum + # Enum for filter categories -class FilterCategory(str,Enum): +class FilterCategory( str, Enum ): GEOS_UTILS = '4- Geos Utils' GEOS_MESH = '1- Geos Mesh' GEOS_GEOMECHANICS = '2- Geos Geomechanics' GEOS_PV = '3- Geos PV' # Add more as needed + # from functools import wraps # from dataclasses import dataclass 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 ( # type: ignore[import-not-found] - VTKHandler, -) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py +) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py from vtkmodules.vtkCommonDataModel import ( vtkMultiBlockDataSet, ) @@ -50,54 +50,67 @@ class FilterCategory(str,Enum): @SISO(name='MyFilter',label='This is my filter',dtype='vtkMultiBlockDataSet') class PVMyFilter: - ... + ... """ -def IsSISOFilter(Protocol): + + +class IsSISOFilter( Protocol ): """Protocol to ensure that the wrapped filter defines the correct Filter core function.""" @abstractmethod def Filter( self, - inputMesh: vtkMultiBlockDataSet, + inputMesh: vtkMultiBlockDataSet, #maybe too reducing scope outputMesh: vtkMultiBlockDataSet, ) -> None: + """Define filter here. + + Args: + inputMesh : a mesh to transform + outputMesh : a mesh transformed + + """ raise NotImplementedError -def SISOFilter(category: FilterCategory, decorated_label: str, decorated_type: str): +T = TypeVar( 'T', bound='IsSISOFilter' ) + + +def SISOFilter( category: FilterCategory, decorated_label: str, + decorated_type: str ) -> Callable[ [ Type[ T ] ], Type[ T ] ]: """Decorate single input single output filter.""" - def decorated_class(cls : IsSISOFilter): + + def decorated_class( cls: Type[ T ] ) -> Type[ T ]: """Outer wrapper function. All is in the WrappingClass below.""" original_init = cls.__init__ - class WrappingClass(cls, VTKPythonAlgorithmBase): - # Créer une fonction __init__ personnalisée - def __init__(self, *ar:Any, **kw : Any)->None: - """Pre-init the filter with the Base algo and I/O single type (usually vtkMultiBlockDataSet) - + class WrappingClass( cls, VTKPythonAlgorithmBase ): # type: ignore[valid-type] + + def __init__( self, *ar: Any, **kw: Any ) -> None: + """Pre-init the filter with the Base algo and I/O single type (usually vtkMultiBlockDataSet). + Args: ar : fowarded arguments kw : forwarded keywords args """ - VTKPythonAlgorithmBase.__init__( - self, - nInputPorts=1, - nOutputPorts=1, - inputType=decorated_type, - outputType=decorated_type - ) + VTKPythonAlgorithmBase.__init__( self, + nInputPorts=1, + nOutputPorts=1, + inputType=decorated_type, + outputType=decorated_type ) #If wrapped class has more to init there it is applied #avoid the overwritten init by decorator taking place of the cls if original_init is not object.__init__: - original_init(self, *ar, **kw) + original_init( self, *ar, **kw ) def RequestDataObject( self, request: vtkInformation, inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, ) -> int: + outInfoVec: vtkInformationVector, + ) -> int: """Inherited from VTKPythonAlgorithmBase::RequestDataObject. Args: @@ -114,13 +127,15 @@ def RequestDataObject( if outData is None or ( not outData.IsA( inData.GetClassName() ) ): outData = inData.NewInstance() outInfoVec.GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), outData ) - return VTKPythonAlgorithmBase.RequestDataObject(self, request, inInfoVec, outInfoVec ) # type: ignore[no-any-return] + return VTKPythonAlgorithmBase.RequestDataObject( self, request, inInfoVec, + outInfoVec ) # type: ignore[no-any-return] - def RequestData( self , - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: + def RequestData( + self, + request: vtkInformation, # noqa: F841 + inInfoVec: list[ vtkInformationVector ], + outInfoVec: vtkInformationVector, + ) -> int: """Inherited from VTKPythonAlgorithmBase::RequestData. Args: @@ -138,25 +153,26 @@ def RequestData( self , outputMesh.ShallowCopy( inputMesh ) - cls.Filter(self, inputMesh,outputMesh) #TODO make it abstract in a Protocol + cls.Filter( self, inputMesh, outputMesh ) return 1 - - # Copy metadata WrappingClass.__name__ = cls.__name__ WrappingClass.__qualname__ = cls.__qualname__ WrappingClass.__module__ = cls.__module__ WrappingClass.__doc__ = cls.__doc__ - update_wrapper(WrappingClass, cls, updated=[]) + update_wrapper( WrappingClass, cls, updated=[] ) #decorate it old fashion way - WrappingClass = smdomain. datatype(dataTypes=[ decorated_type ], - composite_data_supported=True, - )(WrappingClass) - WrappingClass = smproperty.input( name="Input", port_index=0 )(WrappingClass) + WrappingClass = smdomain.datatype( + dataTypes=[ decorated_type ], + composite_data_supported=True, + )( WrappingClass ) + WrappingClass = smproperty.input( name="Input", port_index=0 )( WrappingClass ) # Use enum value for category - WrappingClass = smhint.xml(f'')(WrappingClass) - WrappingClass = smproxy.filter(name=getattr(cls, '__name__', str(cls)), label=decorated_label)(WrappingClass) + WrappingClass = smhint.xml( f'' )( WrappingClass ) + WrappingClass = smproxy.filter( name=getattr( cls, '__name__', str( cls ) ), + label=decorated_label )( WrappingClass ) return WrappingClass - return decorated_class \ No newline at end of file + + return decorated_class From 4b468e8658fc878a62acffaf2d7fd9abb9178ddf Mon Sep 17 00:00:00 2001 From: jacques franc Date: Tue, 14 Oct 2025 17:14:51 +0200 Subject: [PATCH 16/26] stay generic in Protocol --- geos-pv/src/geos/pv/utils/details.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py index eadf5dea..1a668c4d 100644 --- a/geos-pv/src/geos/pv/utils/details.py +++ b/geos-pv/src/geos/pv/utils/details.py @@ -7,7 +7,7 @@ from pathlib import Path # Add Enum for filter categories from functools import update_wrapper -from typing import Protocol, Any, Type, TypeVar, Callable +from typing import Protocol, Any, Type, TypeVar, Callable, runtime_checkable from abc import abstractmethod from enum import Enum @@ -54,15 +54,16 @@ class PVMyFilter: """ - -class IsSISOFilter( Protocol ): +U = TypeVar('U') +@runtime_checkable +class IsSISOFilter( Protocol[U] ): """Protocol to ensure that the wrapped filter defines the correct Filter core function.""" @abstractmethod def Filter( self, - inputMesh: vtkMultiBlockDataSet, #maybe too reducing scope - outputMesh: vtkMultiBlockDataSet, + inputMesh: U, + outputMesh: U, ) -> None: """Define filter here. From 39c2b2fe9ef18f2eaab59ef112be6a986dcacc83 Mon Sep 17 00:00:00 2001 From: jacques franc Date: Tue, 14 Oct 2025 17:19:02 +0200 Subject: [PATCH 17/26] fix --- geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py | 4 ++-- geos-pv/src/geos/pv/utils/details.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index 6ddd69d4..af73b970 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -57,7 +57,7 @@ def __init__( self: Self, ) -> None: @@ -75,7 +75,7 @@ def __init__( self: Self, ) -> None: """ ) - def setDictAttributesValues( self: Self, attributeName: str, values: str ) -> None: + def _setDictAttributesValues( self: Self, attributeName: str, values: str ) -> None: """Set the dictionary with the region indexes and its corresponding list of value for each components. Args: diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py index 1a668c4d..e1d10392 100644 --- a/geos-pv/src/geos/pv/utils/details.py +++ b/geos-pv/src/geos/pv/utils/details.py @@ -18,6 +18,7 @@ class FilterCategory( str, Enum ): GEOS_MESH = '1- Geos Mesh' GEOS_GEOMECHANICS = '2- Geos Geomechanics' GEOS_PV = '3- Geos PV' + GEOS_QC = '5- Geos QC' # Add more as needed From 82e89969be5a1a257dd570ad67823d3281c18fc3 Mon Sep 17 00:00:00 2001 From: jacques franc Date: Tue, 14 Oct 2025 18:05:54 +0200 Subject: [PATCH 18/26] expanding --- .../PVCreateConstantAttributePerRegion.py | 83 +++------------- .../geos/pv/plugins/PVMeshQualityEnhanced.py | 69 +++---------- .../pv/plugins/PVPythonViewConfigurator.py | 62 +++--------- geos-pv/src/geos/pv/plugins/PVSplitMesh.py | 37 +++---- .../pv/utils/AbstractPVPluginVtkWrapper.py | 97 ------------------- geos-pv/src/geos/pv/utils/details.py | 5 +- 6 files changed, 57 insertions(+), 296 deletions(-) delete mode 100644 geos-pv/src/geos/pv/utils/AbstractPVPluginVtkWrapper.py diff --git a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py index fe6a61b4..f2afca01 100644 --- a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py +++ b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py @@ -5,23 +5,18 @@ import sys from pathlib import Path -from typing import Union, Any +from typing import Any from typing_extensions import Self from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - smdomain, smhint, smproperty, smproxy, + 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 vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase -from vtkmodules.vtkCommonCore import ( - vtkInformation, - vtkInformationVector, -) + from vtkmodules.vtkCommonDataModel import ( - vtkMultiBlockDataSet, vtkDataSet, ) @@ -32,6 +27,7 @@ update_paths() +from geos.pv.utils.details import SISOFilter, FilterCategory from geos.mesh.processing.CreateConstantAttributePerRegion import CreateConstantAttributePerRegion, vnp, np __doc__ = """ @@ -42,7 +38,7 @@ Input mesh is either vtkMultiBlockDataSet or vtkDataSet and the region attribute must have one component. The relation index/values is given by a dictionary. Its keys are the indexes and its items are the list of values for each component. -.. Warning:: +.. Warning:: The input mesh should contain an attribute corresponding to the regions. To use it: @@ -54,24 +50,13 @@ * Apply. """ - - -@smproxy.filter( - name="PVCreateConstantAttributePerRegion", - label="Create Constant Attribute Per Region", -) -@smhint.xml( """""" ) -@smproperty.input( name="Input", port_index=0 ) -@smdomain.datatype( - dataTypes=[ "vtkMultiBlockDataSet", "vtkDataSet" ], - composite_data_supported=True, -) -class PVCreateConstantAttributePerRegion( VTKPythonAlgorithmBase ): +@SISOFilter( category=FilterCategory.GEOS_PROP, + decorated_label="Create Constant Attribute Per Region", + decorated_type=[ "vtkMultiBlockDataSet", "vtkDataSet" ]) +class PVCreateConstantAttributePerRegion: def __init__( self: Self ) -> None: """Create an attribute with constant value per region.""" - super().__init__( nInputPorts=1, nOutputPorts=1, inputType="vtkDataObject", outputType="vtkDataObject" ) - self.clearDictRegionValues: bool = True # Region attribute settings. @@ -287,53 +272,13 @@ def _groupNewAttributeSettingsWidgets( self: Self ) -> None: """Group the widgets to set the settings of the new attribute.""" self.Modified() - def RequestDataObject( - self: Self, - request: vtkInformation, - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestDataObject. + def Filter(self, inputMesh: vtkDataSet , outputMesh: vtkDataSet) -> None: + """Is applying CreateConstantAttributePerRegion filter. Args: - request (vtkInformation): request - inInfoVec (list[vtkInformationVector]): input objects - outInfoVec (vtkInformationVector): output objects - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. + inputMesh : a mesh to transform + outputMesh : a mesh transformed """ - 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] - - def RequestData( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], # noqa: F841 - outInfoVec: vtkInformationVector, # noqa: F841 - ) -> 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. - """ - inputMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] = self.GetInputData( inInfoVec, 0, 0 ) - outputMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] = self.GetOutputData( outInfoVec, 0 ) - - assert inputMesh is not None, "Input mesh is null." - assert outputMesh is not None, "Output pipeline is null." - - outputMesh.ShallowCopy( inputMesh ) filter: CreateConstantAttributePerRegion = CreateConstantAttributePerRegion( outputMesh, self.regionName, @@ -352,4 +297,4 @@ def RequestData( self.clearDictRegion = True - return 1 + return diff --git a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py index a82b0a49..a98b875f 100644 --- a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py @@ -7,11 +7,9 @@ from typing_extensions import Self, Optional from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, + VTKPythonAlgorithmBase, smdomain, smproperty, ) from vtkmodules.vtkCommonCore import ( - vtkInformation, - vtkInformationVector, vtkDataArraySelection, ) from vtkmodules.vtkCommonDataModel import ( @@ -44,6 +42,8 @@ createModifiedCallback, ) from geos.pv.utils.paraviewTreatments import getArrayChoices +from geos.pv.utils.details import SISOFilter, FilterCategory + __doc__ = """ The ``Mesh Quality Enhanced`` filter computes requested mesh quality metrics on meshes. Both surfaces and volumic metrics can be computed with this plugin. @@ -65,21 +65,13 @@ .. IMPORTANT:: Please refer to the `Verdict Manual `_ for metrics and range definitions. """ - - -@smproxy.filter( name="PVMeshQualityEnhanced", label="Mesh Quality Enhanced" ) -@smhint.xml( '' ) -@smproperty.input( name="Input", port_index=0 ) -@smdomain.datatype( - dataTypes=[ "vtkUnstructuredGrid" ], - composite_data_supported=True, -) -class PVMeshQualityEnhanced( VTKPythonAlgorithmBase ): +@SISOFilter( category=FilterCategory.GEOS_QC, + decorated_label="Mesh Quality Enhanced", + decorated_type="vtkUnstructuredGrid") +class PVMeshQualityEnhanced: def __init__( self: Self ) -> None: """Merge collocated points.""" - super().__init__( nInputPorts=1, nOutputPorts=1, outputType="vtkUnstructuredGrid" ) - self._filename: Optional[ str ] = None self._saveToFile: bool = True self._blockIndex: int = 0 @@ -212,31 +204,7 @@ def b03GroupAdvancedOutputParameters( self: Self ) -> None: def Modified( self: Self ) -> None: """Overload Modified method to reset _blockIndex.""" self._blockIndex = 0 - super().Modified() - - def RequestDataObject( - self: Self, - request: vtkInformation, - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestDataObject. - - 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 ) + VTKPythonAlgorithmBase.Modified() def _getQualityMetricsToUse( self: Self, selection: vtkDataArraySelection ) -> set[ int ]: """Get mesh quality metric indexes from user selection. @@ -247,27 +215,14 @@ def _getQualityMetricsToUse( self: Self, selection: vtkDataArraySelection ) -> s metricsNames: set[ str ] = getArrayChoices( selection ) return { getQualityMeasureIndexFromName( name ) for name in metricsNames } - def RequestData( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestData. + def Filter(self, inputMesh: vtkUnstructuredGrid, outputMesh:vtkUnstructuredGrid) -> int: + """Is applying MeshQualityEnhanced to the input Mesh. Args: - request (vtkInformation): Request - inInfoVec (list[vtkInformationVector]): Input objects - outInfoVec (vtkInformationVector): Output objects + inputMesh : a mesh to transform + outputMesh : a mesh transformed - Returns: - int: 1 if calculation successfully ended, 0 otherwise. """ - inputMesh: vtkUnstructuredGrid = self.GetInputData( inInfoVec, 0, 0 ) - outputMesh: vtkUnstructuredGrid = vtkUnstructuredGrid.GetData( outInfoVec, 0 ) - assert inputMesh is not None, "Input server mesh is null." - assert outputMesh is not None, "Output pipeline is null." - triangleMetrics: set[ int ] = self._getQualityMetricsToUse( self._commonCellSurfaceQualityMetric ).union( self._getQualityMetricsToUse( self._triangleQualityMetric ) ) quadMetrics: set[ int ] = self._getQualityMetricsToUse( self._commonCellSurfaceQualityMetric ).union( diff --git a/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py b/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py index 5ba12bd1..35cd772e 100755 --- a/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py +++ b/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py @@ -34,14 +34,16 @@ GetActiveSource, GetActiveView, Render, Show, servermanager, ) from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, + smdomain, smproperty, ) from vtkmodules.vtkCommonCore import ( vtkDataArraySelection, vtkInformation, - vtkInformationVector, ) +from vtkmodules.vtkCommonDataModel import vtkDataObject +from geos.pv.utils.details import SISOFilter, FilterCategory + __doc__ = """ PVPythonViewConfigurator is a Paraview plugin that allows to create cross-plots from input data using the PythonView. @@ -57,20 +59,17 @@ * Search and Apply PVPythonViewConfigurator Filter. """ - - -@smproxy.filter( name="PVPythonViewConfigurator", label="Python View Configurator" ) -@smhint.xml( '' ) -@smproperty.input( name="Input" ) -@smdomain.datatype( dataTypes=[ "vtkDataObject" ], composite_data_supported=True ) -class PVPythonViewConfigurator( VTKPythonAlgorithmBase ): +@SISOFilter( category=FilterCategory.GEOS_UTILS, + decorated_label="Python View Configurator", + decorated_type="vtkDataObject") +class PVPythonViewConfigurator: def __init__( self: Self ) -> None: """Paraview plugin to create cross-plots in a Python View. Input is a vtkDataObject. """ - super().__init__( nInputPorts=1, nOutputPorts=1 ) + # super().__init__( nInputPorts=1, nOutputPorts=1 ) # Python view layout and object. self.m_layoutName: str = "" self.m_pythonView: Any @@ -806,47 +805,14 @@ def FillInputPortInformation( self: Self, port: int, info: vtkInformation ) -> i info.Set( self.INPUT_REQUIRED_DATA_TYPE(), "vtkDataObject" ) return 1 - def RequestDataObject( - self: Self, - request: vtkInformation, - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestDataObject. + def Filter(self, inputMesh : vtkDataObject, outputMesh : vtkDataObject) -> None: + """Dummy interface for plugin to fit decorator reqs. Args: - request (vtkInformation): Request. - inInfoVec (list[vtkInformationVector]): Input objects. - outInfoVec (vtkInformationVector): Output objects. + inputMesh : a dummy mesh to transform + outputMesh : a dummy mesh transformed - 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 ) # type: ignore[no-any-return] - - def RequestData( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], # noqa: F841 - outInfoVec: vtkInformationVector, # noqa: F841 - ) -> 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. - """ - # pythonViewGeneration assert self.m_pythonView is not None, "No Python View was found." viewSize = GetActiveView().ViewSize self.m_userChoices[ "ratio" ] = viewSize[ 0 ] / viewSize[ 1 ] @@ -855,4 +821,4 @@ def RequestData( self.defineCurvesAspect() self.m_pythonView.Script = self.buildPythonViewScript() Render() - return 1 + return diff --git a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py index e6e3d55e..018b45fd 100644 --- a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py +++ b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py @@ -6,9 +6,6 @@ from pathlib import Path from typing_extensions import Self -from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - smdomain, smhint, smproperty, smproxy, -) from vtkmodules.vtkCommonDataModel import ( vtkPointSet, ) @@ -21,8 +18,7 @@ update_paths() from geos.mesh.processing.SplitMesh import SplitMesh -from geos.pv.utils.AbstractPVPluginVtkWrapper import AbstractPVPluginVtkWrapper - +from geos.pv.utils.details import SISOFilter, FilterCategory __doc__ = """ Split each cell of input mesh to smaller cells. @@ -37,32 +33,27 @@ """ -@smproxy.filter( name="PVSplitMesh", label="Split Mesh" ) -@smhint.xml( '' ) -@smproperty.input( name="Input", port_index=0 ) -@smdomain.datatype( - dataTypes=[ "vtkPointSet" ], - composite_data_supported=True, -) -class PVSplitMesh( AbstractPVPluginVtkWrapper ): +@SISOFilter( category=FilterCategory.GEOS_UTILS, + decorated_label="Split Mesh", + decorated_type="vtkPointSet") +class PVSplitMesh: def __init__( self: Self ) -> None: """Split mesh cells.""" - super().__init__() + pass - def applyVtkFilter( + def Filter( self: Self, - input: vtkPointSet, - ) -> vtkPointSet: + inputMesh: vtkPointSet, + outputMesh:vtkPointSet + ) -> None: """Apply vtk filter. Args: - input (vtkPointSet): input mesh - - Returns: - vtkPointSet: output mesh + inputMesh(vtkPointSet): input mesh + outputMesh: output mesh """ filter: SplitMesh = SplitMesh() - filter.SetInputDataObject( input ) + filter.SetInputDataObject( inputMesh ) filter.Update() - return filter.GetOutputDataObject( 0 ) + outputMesh.ShallowCopy( filter.GetOutputDataObject( 0 ) ) diff --git a/geos-pv/src/geos/pv/utils/AbstractPVPluginVtkWrapper.py b/geos-pv/src/geos/pv/utils/AbstractPVPluginVtkWrapper.py deleted file mode 100644 index 8ae8c27f..00000000 --- a/geos-pv/src/geos/pv/utils/AbstractPVPluginVtkWrapper.py +++ /dev/null @@ -1,97 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. -# SPDX-FileContributor: Martin Lemay -# ruff: noqa: E402 # disable Module level import not at top of file -from typing import Any -from typing_extensions import Self - -from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase, ) - -from vtkmodules.vtkCommonCore import ( - vtkInformation, - vtkInformationVector, -) - -__doc__ = """ -AbstractPVPluginVtkWrapper module defines the parent Paraview plugin from which inheritates PV plugins that directly wrap a vtk filter. - -To use it, make children PV plugins inherited from AbstractPVPluginVtkWrapper. Output mesh is of same type as input mesh. -If output type needs to be specified, this must be done in the child class. -""" - - -class AbstractPVPluginVtkWrapper( VTKPythonAlgorithmBase ): - - def __init__( self: Self ) -> None: - """Abstract Paraview Plugin class.""" - super().__init__( nInputPorts=1, nOutputPorts=1, outputType="vtkPointSet" ) - - def RequestDataObject( - self: Self, - request: vtkInformation, - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestDataObject. - - 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 ], - 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. - """ - try: - inputMesh: Any = self.GetInputData( inInfoVec, 0, 0 ) - outputMesh: Any = self.GetOutputData( outInfoVec, 0 ) - assert inputMesh is not None, "Input server mesh is null." - assert outputMesh is not None, "Output pipeline is null." - - tmpMesh = self.applyVtkFilter( inputMesh ) - assert tmpMesh is not None, "Output mesh is null." - outputMesh.ShallowCopy( tmpMesh ) - print( "Filter was successfully applied." ) - except ( AssertionError, Exception ) as e: - print( f"Filter failed due to: {e}" ) - return 0 - return 1 - - def applyVtkFilter( - self: Self, - input: Any, - ) -> Any: - """Apply vtk filter. - - Args: - input (Any): input object - - Returns: - Any: output mesh - """ - pass diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py index e1d10392..431c326a 100644 --- a/geos-pv/src/geos/pv/utils/details.py +++ b/geos-pv/src/geos/pv/utils/details.py @@ -16,6 +16,7 @@ class FilterCategory( str, Enum ): GEOS_UTILS = '4- Geos Utils' GEOS_MESH = '1- Geos Mesh' + GEOS_PROP = '0- Geos Pre-processing' GEOS_GEOMECHANICS = '2- Geos Geomechanics' GEOS_PV = '3- Geos PV' GEOS_QC = '5- Geos QC' @@ -99,8 +100,8 @@ def __init__( self, *ar: Any, **kw: Any ) -> None: VTKPythonAlgorithmBase.__init__( self, nInputPorts=1, nOutputPorts=1, - inputType=decorated_type, - outputType=decorated_type ) + inputType=decorated_type if not isinstance(list,decorated_type) else "vtkDataObject", + outputType=decorated_type if not isinstance(list,decorated_type) else "vtkDataObject") #If wrapped class has more to init there it is applied #avoid the overwritten init by decorator taking place of the cls From 840dccaa2775f044450f22d023fd2eb13944bc4a Mon Sep 17 00:00:00 2001 From: jacques franc Date: Wed, 15 Oct 2025 11:13:40 +0200 Subject: [PATCH 19/26] some adj --- .../PVCreateConstantAttributePerRegion.py | 22 +++++++++---------- .../geos/pv/plugins/PVFillPartialArrays.py | 8 +++---- .../geos/pv/plugins/PVMeshQualityEnhanced.py | 10 ++++----- geos-pv/src/geos/pv/plugins/PVSplitMesh.py | 5 ++++- geos-pv/src/geos/pv/utils/details.py | 22 ++++++++++++++----- 5 files changed, 40 insertions(+), 27 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py index f2afca01..df586270 100644 --- a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py +++ b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py @@ -9,7 +9,7 @@ from typing_extensions import Self from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - smdomain, smproperty, + 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, @@ -53,7 +53,7 @@ @SISOFilter( category=FilterCategory.GEOS_PROP, decorated_label="Create Constant Attribute Per Region", decorated_type=[ "vtkMultiBlockDataSet", "vtkDataSet" ]) -class PVCreateConstantAttributePerRegion: +class PVCreateConstantAttributePerRegion(VTKPythonAlgorithmBase): def __init__( self: Self ) -> None: """Create an attribute with constant value per region.""" @@ -96,7 +96,7 @@ def __init__( self: Self ) -> None: """ ) - def _setRegionAttributeName( self: Self, regionName: str ) -> None: + def setRegionAttributeName( self: Self, regionName: str ) -> None: """Set region attribute name. Args: @@ -109,7 +109,7 @@ def _setRegionAttributeName( self: Self, regionName: str ) -> None: @@ -127,7 +127,7 @@ def _setRegionAttributeName( self: Self, regionName: str ) -> None: """ ) - def _setDictRegionValues( self: Self, regionIndex: str, value: str ) -> None: + def setDictRegionValues( self: Self, regionIndex: str, value: str ) -> None: """Set the dictionary with the region indexes and its corresponding list of values for each components. Args: @@ -151,7 +151,7 @@ def _setDictRegionValues( self: Self, regionIndex: str, value: str ) -> None: """ ) - def _groupRegionAttributeSettingsWidgets( self: Self ) -> None: + def groupRegionAttributeSettingsWidgets( self: Self ) -> None: """Group the widgets to set the settings of the region attribute.""" self.Modified() @@ -168,7 +168,7 @@ def _groupRegionAttributeSettingsWidgets( self: Self ) -> None: """ ) - def _setAttributeName( self: Self, newAttributeName: str ) -> None: + def setAttributeName( self: Self, newAttributeName: str ) -> None: """Set attribute name. Args: @@ -201,7 +201,7 @@ def _setAttributeName( self: Self, newAttributeName: str ) -> None: The requested numpy scalar type for values of the new attribute. """ ) - def _setValueType( self: Self, valueType: int ) -> None: + def setValueType( self: Self, valueType: int ) -> None: """Set the type for the value used to create the new attribute. Args: @@ -223,7 +223,7 @@ def _setValueType( self: Self, valueType: int ) -> None: The number of components for the new attribute to create. """ ) - def _setNbComponent( self: Self, nbComponents: int ) -> None: + def setNbComponent( self: Self, nbComponents: int ) -> None: """Set the number of components of the attribute to create. Args: @@ -246,7 +246,7 @@ def _setNbComponent( self: Self, nbComponents: int ) -> None: Names of components: X, Y, Z """ ) - def _setComponentNames( self: Self, componentNames: str ) -> None: + def setComponentNames( self: Self, componentNames: str ) -> None: """Set the names of the components of the attribute to create. Args: @@ -268,7 +268,7 @@ def _setComponentNames( self: Self, componentNames: str ) -> None: """ ) - def _groupNewAttributeSettingsWidgets( self: Self ) -> None: + def groupNewAttributeSettingsWidgets( self: Self ) -> None: """Group the widgets to set the settings of the new attribute.""" self.Modified() diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index af73b970..37142a5d 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -8,7 +8,7 @@ from typing_extensions import Self from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - smproperty, + 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, @@ -46,7 +46,7 @@ @SISOFilter( category=FilterCategory.GEOS_UTILS, decorated_label="Fill Partial Arrays", decorated_type="vtkMultiBlockDataSet" ) -class PVFillPartialArrays: +class PVFillPartialArrays(VTKPythonAlgorithmBase): def __init__( self: Self, ) -> None: """Fill a partial attribute with constant value per component.""" @@ -57,7 +57,7 @@ def __init__( self: Self, ) -> None: @@ -75,7 +75,7 @@ def __init__( self: Self, ) -> None: """ ) - def _setDictAttributesValues( self: Self, attributeName: str, values: str ) -> None: + def setDictAttributesValues( self: Self, attributeName: str, values: str ) -> None: """Set the dictionary with the region indexes and its corresponding list of value for each components. Args: diff --git a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py index a98b875f..6e5199e5 100644 --- a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py @@ -68,7 +68,7 @@ @SISOFilter( category=FilterCategory.GEOS_QC, decorated_label="Mesh Quality Enhanced", decorated_type="vtkUnstructuredGrid") -class PVMeshQualityEnhanced: +class PVMeshQualityEnhanced(VTKPythonAlgorithmBase): def __init__( self: Self ) -> None: """Merge collocated points.""" @@ -164,7 +164,7 @@ def b01SetSaveToFile( self: Self, saveToFile: bool ) -> None: """ if self._saveToFile != saveToFile: self._saveToFile = saveToFile - self.Modified() + PVMeshQualityEnhanced.Modified(self) @smproperty.stringvector( name="FilePath", label="File Path" ) @smdomain.xml( """ @@ -183,7 +183,7 @@ def b02SetFileName( self: Self, fname: str ) -> None: """ if self._filename != fname: self._filename = fname - self.Modified() + PVMeshQualityEnhanced.Modified(self) @smproperty.xml( """ None: """ ) def b03GroupAdvancedOutputParameters( self: Self ) -> None: """Organize groups.""" - self.Modified() + PVMeshQualityEnhanced.Modified(self) def Modified( self: Self ) -> None: """Overload Modified method to reset _blockIndex.""" self._blockIndex = 0 - VTKPythonAlgorithmBase.Modified() + VTKPythonAlgorithmBase.Modified(self) def _getQualityMetricsToUse( self: Self, selection: vtkDataArraySelection ) -> set[ int ]: """Get mesh quality metric indexes from user selection. diff --git a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py index 018b45fd..42e96c72 100644 --- a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py +++ b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py @@ -6,6 +6,9 @@ from pathlib import Path from typing_extensions import Self +from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] + VTKPythonAlgorithmBase +) from vtkmodules.vtkCommonDataModel import ( vtkPointSet, ) @@ -36,7 +39,7 @@ @SISOFilter( category=FilterCategory.GEOS_UTILS, decorated_label="Split Mesh", decorated_type="vtkPointSet") -class PVSplitMesh: +class PVSplitMesh(VTKPythonAlgorithmBase): def __init__( self: Self ) -> None: """Split mesh cells.""" diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py index 431c326a..a88a9078 100644 --- a/geos-pv/src/geos/pv/utils/details.py +++ b/geos-pv/src/geos/pv/utils/details.py @@ -7,7 +7,7 @@ from pathlib import Path # Add Enum for filter categories from functools import update_wrapper -from typing import Protocol, Any, Type, TypeVar, Callable, runtime_checkable +from typing import Protocol, Any, Type, TypeVar, Callable, runtime_checkable, Union from abc import abstractmethod from enum import Enum @@ -81,14 +81,14 @@ def Filter( def SISOFilter( category: FilterCategory, decorated_label: str, - decorated_type: str ) -> Callable[ [ Type[ T ] ], Type[ T ] ]: + decorated_type: Union[str,list] ) -> Callable[ [ Type[ T ] ], Type[ T ] ]: """Decorate single input single output filter.""" def decorated_class( cls: Type[ T ] ) -> Type[ T ]: """Outer wrapper function. All is in the WrappingClass below.""" original_init = cls.__init__ - class WrappingClass( cls, VTKPythonAlgorithmBase ): # type: ignore[valid-type] + class WrappingClass( cls ): # type: ignore[valid-type] def __init__( self, *ar: Any, **kw: Any ) -> None: """Pre-init the filter with the Base algo and I/O single type (usually vtkMultiBlockDataSet). @@ -100,8 +100,8 @@ def __init__( self, *ar: Any, **kw: Any ) -> None: VTKPythonAlgorithmBase.__init__( self, nInputPorts=1, nOutputPorts=1, - inputType=decorated_type if not isinstance(list,decorated_type) else "vtkDataObject", - outputType=decorated_type if not isinstance(list,decorated_type) else "vtkDataObject") + inputType=decorated_type if isinstance(decorated_type,str) else "vtkDataObject", + outputType=decorated_type if isinstance(decorated_type,str) else "vtkDataObject") #If wrapped class has more to init there it is applied #avoid the overwritten init by decorator taking place of the cls @@ -158,6 +158,16 @@ def RequestData( cls.Filter( self, inputMesh, outputMesh ) return 1 + + # Copy all methods and attributes from cls, including decorator metadata + for attr_name in dir(cls): + if attr_name.startswith('_'): + continue # Skip private/magic methods (already handled or inherited) + + attr = getattr(cls, attr_name) + # Copy methods with their decorators + if callable(attr) and attr_name not in WrappingClass.__dict__: + setattr(WrappingClass,attr_name,attr) # Copy metadata WrappingClass.__name__ = cls.__name__ @@ -168,7 +178,7 @@ def RequestData( #decorate it old fashion way WrappingClass = smdomain.datatype( - dataTypes=[ decorated_type ], + dataTypes=[ decorated_type ] if isinstance(decorated_type,str) else decorated_type, composite_data_supported=True, )( WrappingClass ) WrappingClass = smproperty.input( name="Input", port_index=0 )( WrappingClass ) From 8ca7bf6b01a9f4b9c1b3cfed58658bb578dc6798 Mon Sep 17 00:00:00 2001 From: jacques franc Date: Wed, 15 Oct 2025 11:19:39 +0200 Subject: [PATCH 20/26] revert changes from logger PR --- geos-utils/src/geos/utils/details.py | 66 ---------------------------- 1 file changed, 66 deletions(-) delete mode 100644 geos-utils/src/geos/utils/details.py diff --git a/geos-utils/src/geos/utils/details.py b/geos-utils/src/geos/utils/details.py deleted file mode 100644 index 88bf7da0..00000000 --- a/geos-utils/src/geos/utils/details.py +++ /dev/null @@ -1,66 +0,0 @@ -import logging -from geos.utils.Logger import Logger, getLogger -from functools import wraps -from typing import Type, TypeVar, Callable, Protocol, Any - -__doc__ = """ - Group of decorators and Protocols that will be used in filters to wrap behaviors without code replication - -""" - - -class HasLogger( Protocol ): - """Protocol for classes that have logging support.""" - - logger: Logger - - def setLoggerHandler( 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. - """ - pass - - -T = TypeVar( 'T', bound='HasLogger' ) - - -def addLogSupport( loggerTitle: str ) -> Callable[ [ Type[ T ] ], Type[ T ] ]: - """Decorator to add logger support in the class following existing architecture. - - Args: - loggerTitle (str): Title to display in the logger - """ - - def decorator( cls: Type[ T ] ) -> Type[ T ]: - original_init = cls.__init__ - - @wraps( original_init ) - def new_init( self: T, *args: Any, **kwargs: Any ) -> None: - spe_handler = kwargs.pop( 'speHandler', False ) - if spe_handler: - self.logger = logging.getLogger( loggerTitle ) - self.logger.setLevel( logging.INFO ) - else: - self.logger = getLogger( loggerTitle, True ) - - original_init( self, *args, **kwargs ) - - def setLoggerHandler( self: T, handler: logging.Handler ) -> None: - # No docstring needed - Protocol defines the contract - if not self.logger.handlers: - 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." - ) - - cls.__init__ = new_init # type: ignore[assignment] - cls.setLoggerHandler = setLoggerHandler # type: ignore[assignment] - - return cls - - return decorator From ecd13f75cdb0e5655067a61885706f0ce484751d Mon Sep 17 00:00:00 2001 From: jacques franc Date: Wed, 15 Oct 2025 11:50:12 +0200 Subject: [PATCH 21/26] SISOFilter for all --- .../src/geos/pv/plugins/PVClipToMainFrame.py | 68 +++---------------- 1 file changed, 10 insertions(+), 58 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVClipToMainFrame.py b/geos-pv/src/geos/pv/plugins/PVClipToMainFrame.py index 8c0cfdb2..0fc51f4a 100644 --- a/geos-pv/src/geos/pv/plugins/PVClipToMainFrame.py +++ b/geos-pv/src/geos/pv/plugins/PVClipToMainFrame.py @@ -4,24 +4,16 @@ # ruff: noqa: E402 # disable Module level import not at top of file import sys from pathlib import Path -from typing import Union from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, + 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, - vtkUnstructuredGrid, -) - -from vtkmodules.vtkCommonCore import ( - vtkInformation, - vtkInformationVector, -) + vtkMultiBlockDataSet, ) # update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent @@ -30,6 +22,7 @@ update_paths() +from geos.pv.utils.details import SISOFilter, FilterCategory from geos.mesh.processing.ClipToMainFrame import ClipToMainFrame __doc__ = """ @@ -43,67 +36,26 @@ """ -@smproxy.filter( name="PVClipToMainFrame", label="Clip to the main frame" ) -@smhint.xml( '' ) -@smproperty.input( name="Input", port_index=0 ) -@smdomain.datatype( - dataTypes=[ "vtkMultiBlockDataSet", "vtkUnstructuredGrid" ], - composite_data_supported=True, -) +@SISOFilter( category=FilterCategory.GEOS_UTILS, + decorated_label="Clip to the main frame", + decorated_type=[ "vtkMultiBlockDataSet", "vtkDataSet" ] ) class PVClipToMainFrame( VTKPythonAlgorithmBase ): def __init__( self ) -> None: """Init motherclass, filter and logger.""" - VTKPythonAlgorithmBase.__init__( self, - nInputPorts=1, - nOutputPorts=1, - inputType="vtkDataObject", - outputType="vtkDataObject" ) - self._realFilter = ClipToMainFrame( speHandler=True ) if not self._realFilter.logger.hasHandlers(): self._realFilter.SetLoggerHandler( VTKHandler() ) - #ensure I/O consistency - def RequestDataObject( self, request: vtkInformation, inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestDataObject. - - 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 ) # type: ignore[no-any-return] - - def RequestData( self, request: vtkInformation, inInfo: list[ vtkInformationVector ], - outInfo: vtkInformationVector ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestData. Apply ClipToMainFrame filter. + def Filter( self, inputMesh: vtkMultiBlockDataSet, outputMesh: vtkMultiBlockDataSet ) -> None: + """Is applying CreateConstantAttributePerRegion filter. Args: - request (vtkInformation): Request - inInfo (list[vtkInformationVector]): Input objects - outInfo (vtkInformationVector): Output objects - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. + inputMesh : a mesh to transform + outputMesh : a mesh transformed """ - inputMesh: Union[ vtkMultiBlockDataSet, vtkUnstructuredGrid ] = self.GetInputData( inInfo, 0, 0 ) - outputMesh: Union[ vtkMultiBlockDataSet, vtkUnstructuredGrid ] = self.GetOutputData( outInfo, 0 ) - # struct self._realFilter.SetInputData( inputMesh ) self._realFilter.ComputeTransform() self._realFilter.Update() outputMesh.ShallowCopy( self._realFilter.GetOutputDataObject( 0 ) ) - - return 1 From a1492141e3b4a73227e48963f0836327907c85a0 Mon Sep 17 00:00:00 2001 From: jacques franc Date: Wed, 15 Oct 2025 13:07:43 +0200 Subject: [PATCH 22/26] revert logger's branch change --- .../geos/mesh/processing/FillPartialArrays.py | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py b/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py index 0f048c86..1810bc73 100644 --- a/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py +++ b/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py @@ -9,7 +9,6 @@ from geos.mesh.utils.arrayModifiers import fillPartialAttributes from geos.mesh.utils.arrayHelpers import getAttributePieceInfo -from geos.utils.details import addLogSupport from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet __doc__ = """ @@ -52,13 +51,13 @@ loggerTitle: str = "Fill Partial Attribute" -@addLogSupport( loggerTitle=loggerTitle ) class FillPartialArrays: def __init__( self: Self, multiBlockDataSet: vtkMultiBlockDataSet, dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ], + speHandler: bool = False, ) -> None: """Fill partial attributes with constant value per component. @@ -76,6 +75,29 @@ def __init__( self.multiBlockDataSet: vtkMultiBlockDataSet = multiBlockDataSet self.dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ] = dictAttributesValues + # Logger. + self.logger: Logger + if not speHandler: + self.logger = getLogger( loggerTitle, True ) + else: + self.logger = logging.getLogger( loggerTitle ) + self.logger.setLevel( logging.INFO ) + + 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. + """ + 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 ) -> bool: """Create a constant attribute per region in the mesh. From c68f4b1e4e0b26e45b720d97aded534882e901f8 Mon Sep 17 00:00:00 2001 From: jacques franc Date: Tue, 21 Oct 2025 15:58:03 +0200 Subject: [PATCH 23/26] addressing all comments --- .../src/geos/pv/plugins/PVClipToMainFrame.py | 8 +- .../PVCreateConstantAttributePerRegion.py | 15 ++-- .../geos/pv/plugins/PVFillPartialArrays.py | 8 +- .../geos/pv/plugins/PVMeshQualityEnhanced.py | 10 +-- .../pv/plugins/PVPythonViewConfigurator.py | 8 +- geos-pv/src/geos/pv/plugins/PVSplitMesh.py | 8 +- geos-pv/src/geos/pv/utils/details.py | 75 +++++++++---------- 7 files changed, 66 insertions(+), 66 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVClipToMainFrame.py b/geos-pv/src/geos/pv/plugins/PVClipToMainFrame.py index 0fc51f4a..f2703956 100644 --- a/geos-pv/src/geos/pv/plugins/PVClipToMainFrame.py +++ b/geos-pv/src/geos/pv/plugins/PVClipToMainFrame.py @@ -37,8 +37,8 @@ @SISOFilter( category=FilterCategory.GEOS_UTILS, - decorated_label="Clip to the main frame", - decorated_type=[ "vtkMultiBlockDataSet", "vtkDataSet" ] ) + decoratedLabel="Clip to the main frame", + decoratedType=[ "vtkMultiBlockDataSet", "vtkDataSet" ] ) class PVClipToMainFrame( VTKPythonAlgorithmBase ): def __init__( self ) -> None: @@ -51,8 +51,8 @@ def Filter( self, inputMesh: vtkMultiBlockDataSet, outputMesh: vtkMultiBlockData """Is applying CreateConstantAttributePerRegion filter. Args: - inputMesh : a mesh to transform - outputMesh : a mesh transformed + inputMesh : A mesh to transform. + outputMesh : A mesh transformed. """ # struct self._realFilter.SetInputData( inputMesh ) diff --git a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py index df586270..fb71932a 100644 --- a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py +++ b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py @@ -3,6 +3,7 @@ # SPDX-FileContributor: Martin Lemay, Romain Baville # ruff: noqa: E402 # disable Module level import not at top of file import sys +import numpy as np from pathlib import Path from typing import Any @@ -15,6 +16,8 @@ VTKHandler, ) # 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, @@ -25,7 +28,7 @@ sys.path.insert( 0, str( geos_pv_path / "src" ) ) from geos.pv.utils.config import update_paths -update_paths() +from geos.mesh.processing.CreateConstantAttributePerRegion import ( CreateConstantAttributePerRegion ) from geos.pv.utils.details import SISOFilter, FilterCategory from geos.mesh.processing.CreateConstantAttributePerRegion import CreateConstantAttributePerRegion, vnp, np @@ -51,8 +54,8 @@ """ @SISOFilter( category=FilterCategory.GEOS_PROP, - decorated_label="Create Constant Attribute Per Region", - decorated_type=[ "vtkMultiBlockDataSet", "vtkDataSet" ]) + decoratedLabel="Create Constant Attribute Per Region", + decoratedType=[ "vtkMultiBlockDataSet", "vtkDataSet" ]) class PVCreateConstantAttributePerRegion(VTKPythonAlgorithmBase): def __init__( self: Self ) -> None: @@ -274,10 +277,10 @@ def groupNewAttributeSettingsWidgets( self: Self ) -> None: def Filter(self, inputMesh: vtkDataSet , outputMesh: vtkDataSet) -> None: """Is applying CreateConstantAttributePerRegion filter. - + outputMesh : A mesh transformed. Args: - inputMesh : a mesh to transform - outputMesh : a mesh transformed + inputMesh : A mesh to transform + outputMesh : A mesh transformed """ filter: CreateConstantAttributePerRegion = CreateConstantAttributePerRegion( outputMesh, diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index 37142a5d..b9adfbc1 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -44,8 +44,8 @@ @SISOFilter( category=FilterCategory.GEOS_UTILS, - decorated_label="Fill Partial Arrays", - decorated_type="vtkMultiBlockDataSet" ) + decoratedLabel="Fill Partial Arrays", + decoratedType="vtkMultiBlockDataSet" ) class PVFillPartialArrays(VTKPythonAlgorithmBase): def __init__( self: Self, ) -> None: @@ -98,8 +98,8 @@ def Filter( self, inputMesh: vtkMultiBlockDataSet, outputMesh: vtkMultiBlockData """Is applying FillPartialArrays to the mesh and return with the class's dictionnary for attributes values. Args: - inputMesh : a mesh to transform - outputMesh : a mesh transformed + inputMesh : A mesh to transform. + outputMesh : A mesh transformed. """ filter: FillPartialArrays = FillPartialArrays( diff --git a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py index 6e5199e5..3088f9e2 100644 --- a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py @@ -66,8 +66,8 @@ Please refer to the `Verdict Manual `_ for metrics and range definitions. """ @SISOFilter( category=FilterCategory.GEOS_QC, - decorated_label="Mesh Quality Enhanced", - decorated_type="vtkUnstructuredGrid") + decoratedLabel="Mesh Quality Enhanced", + decoratedType="vtkUnstructuredGrid") class PVMeshQualityEnhanced(VTKPythonAlgorithmBase): def __init__( self: Self ) -> None: @@ -215,12 +215,12 @@ def _getQualityMetricsToUse( self: Self, selection: vtkDataArraySelection ) -> s metricsNames: set[ str ] = getArrayChoices( selection ) return { getQualityMeasureIndexFromName( name ) for name in metricsNames } - def Filter(self, inputMesh: vtkUnstructuredGrid, outputMesh:vtkUnstructuredGrid) -> int: + def Filter(self, inputMesh: vtkUnstructuredGrid, outputMesh:vtkUnstructuredGrid) -> None: """Is applying MeshQualityEnhanced to the input Mesh. Args: - inputMesh : a mesh to transform - outputMesh : a mesh transformed + inputMesh : A mesh to transform. + outputMesh : A mesh transformed. """ triangleMetrics: set[ int ] = self._getQualityMetricsToUse( self._commonCellSurfaceQualityMetric ).union( diff --git a/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py b/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py index 35cd772e..f536e80e 100755 --- a/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py +++ b/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py @@ -60,8 +60,8 @@ """ @SISOFilter( category=FilterCategory.GEOS_UTILS, - decorated_label="Python View Configurator", - decorated_type="vtkDataObject") + decoratedLabel="Python View Configurator", + decoratedType="vtkDataObject") class PVPythonViewConfigurator: def __init__( self: Self ) -> None: @@ -809,8 +809,8 @@ def Filter(self, inputMesh : vtkDataObject, outputMesh : vtkDataObject) -> None: """Dummy interface for plugin to fit decorator reqs. Args: - inputMesh : a dummy mesh to transform - outputMesh : a dummy mesh transformed + inputMesh : A dummy mesh to transform + outputMesh : A dummy mesh transformed """ assert self.m_pythonView is not None, "No Python View was found." diff --git a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py index 42e96c72..2c0f68ea 100644 --- a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py +++ b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py @@ -37,8 +37,8 @@ @SISOFilter( category=FilterCategory.GEOS_UTILS, - decorated_label="Split Mesh", - decorated_type="vtkPointSet") + decoratedLabel="Split Mesh", + decoratedType="vtkPointSet") class PVSplitMesh(VTKPythonAlgorithmBase): def __init__( self: Self ) -> None: @@ -53,8 +53,8 @@ def Filter( """Apply vtk filter. Args: - inputMesh(vtkPointSet): input mesh - outputMesh: output mesh + inputMesh(vtkPointSet): Input mesh. + outputMesh: Output mesh. """ filter: SplitMesh = SplitMesh() filter.SetInputDataObject( inputMesh ) diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py index a88a9078..fc0ad001 100644 --- a/geos-pv/src/geos/pv/utils/details.py +++ b/geos-pv/src/geos/pv/utils/details.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. -# SPDX-FileContributor: Martin Lemay, Romain Baville +# SPDX-FileContributor: Martin Lemay, Romain Baville, Jacques Franc # ruff: noqa: E402 # disable Module level import not at top of file # mypy: disable-error-code="misc" import sys @@ -12,20 +12,6 @@ from enum import Enum -# Enum for filter categories -class FilterCategory( str, Enum ): - GEOS_UTILS = '4- Geos Utils' - GEOS_MESH = '1- Geos Mesh' - GEOS_PROP = '0- Geos Pre-processing' - GEOS_GEOMECHANICS = '2- Geos Geomechanics' - GEOS_PV = '3- Geos PV' - GEOS_QC = '5- Geos QC' - # Add more as needed - - -# from functools import wraps -# from dataclasses import dataclass - 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 @@ -50,11 +36,24 @@ class FilterCategory( str, Enum ): Usage is: - @SISO(name='MyFilter',label='This is my filter',dtype='vtkMultiBlockDataSet') + from geos.pv.utils.details import SISOFilter, FilterCategory + + @SISO(category=FilterCategory.GEOS_UTILS,decoratedLabel='Awesome Filter',decoratedType='vtkMultiBlockDataSet') class PVMyFilter: ... """ +# Enum for filter categories +class FilterCategory( str, Enum ): + """String Enum to sort into category in PV task bar under Plugins.""" + GEOS_PROP = '0- Geos Pre-processing' + GEOS_MESH = '1- Geos Mesh' + GEOS_GEOMECHANICS = '2- Geos Geomechanics' + GEOS_PV = '3- Geos PV' + GEOS_UTILS = '4- Geos Utils' + GEOS_QC = '5- Geos QC' + # Add more as needed + U = TypeVar('U') @runtime_checkable @@ -70,23 +69,21 @@ def Filter( """Define filter here. Args: - inputMesh : a mesh to transform - outputMesh : a mesh transformed + inputMesh : A mesh to transform + outputMesh : A mesh transformed """ raise NotImplementedError - T = TypeVar( 'T', bound='IsSISOFilter' ) -def SISOFilter( category: FilterCategory, decorated_label: str, - decorated_type: Union[str,list] ) -> Callable[ [ Type[ T ] ], Type[ T ] ]: - """Decorate single input single output filter.""" - - def decorated_class( cls: Type[ T ] ) -> Type[ T ]: +def SISOFilter( category: FilterCategory, decoratedLabel: str, + decoratedType: Union[str,list] ) -> Callable[ [ Type[ T ] ], Type[ T ] ]: + """Decorate Single Input Single Output (SISO) filter.""" + def decoratedClass( cls: Type[ T ] ) -> Type[ T ]: """Outer wrapper function. All is in the WrappingClass below.""" - original_init = cls.__init__ + originalInit = cls.__init__ class WrappingClass( cls ): # type: ignore[valid-type] @@ -94,19 +91,19 @@ def __init__( self, *ar: Any, **kw: Any ) -> None: """Pre-init the filter with the Base algo and I/O single type (usually vtkMultiBlockDataSet). Args: - ar : fowarded arguments - kw : forwarded keywords args + ar : Fowarded arguments + kw : Forwarded keywords args """ VTKPythonAlgorithmBase.__init__( self, nInputPorts=1, nOutputPorts=1, - inputType=decorated_type if isinstance(decorated_type,str) else "vtkDataObject", - outputType=decorated_type if isinstance(decorated_type,str) else "vtkDataObject") + inputType=decoratedType if isinstance(decoratedType,str) else "vtkDataObject", + outputType=decoratedType if isinstance(decoratedType,str) else "vtkDataObject") #If wrapped class has more to init there it is applied #avoid the overwritten init by decorator taking place of the cls - if original_init is not object.__init__: - original_init( self, *ar, **kw ) + if originalInit is not object.__init__: + originalInit( self, *ar, **kw ) def RequestDataObject( self, @@ -160,14 +157,14 @@ def RequestData( return 1 # Copy all methods and attributes from cls, including decorator metadata - for attr_name in dir(cls): - if attr_name.startswith('_'): + for attrName in dir(cls): + if attrName.startswith('_'): continue # Skip private/magic methods (already handled or inherited) - attr = getattr(cls, attr_name) + attr = getattr(cls, attrName) # Copy methods with their decorators - if callable(attr) and attr_name not in WrappingClass.__dict__: - setattr(WrappingClass,attr_name,attr) + if callable(attr) and attrName not in WrappingClass.__dict__: + setattr(WrappingClass,attrName,attr) # Copy metadata WrappingClass.__name__ = cls.__name__ @@ -178,14 +175,14 @@ def RequestData( #decorate it old fashion way WrappingClass = smdomain.datatype( - dataTypes=[ decorated_type ] if isinstance(decorated_type,str) else decorated_type, + dataTypes=[ decoratedType ] if isinstance(decoratedType,str) else decoratedType, composite_data_supported=True, )( WrappingClass ) WrappingClass = smproperty.input( name="Input", port_index=0 )( WrappingClass ) # Use enum value for category WrappingClass = smhint.xml( f'' )( WrappingClass ) WrappingClass = smproxy.filter( name=getattr( cls, '__name__', str( cls ) ), - label=decorated_label )( WrappingClass ) + label=decoratedLabel )( WrappingClass ) return WrappingClass - return decorated_class + return decoratedClass From e943d4c29d0a37d56e77a2311e69ef5abdacf1cf Mon Sep 17 00:00:00 2001 From: jacques franc Date: Tue, 21 Oct 2025 16:13:38 +0200 Subject: [PATCH 24/26] mypy --- .../src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py | 2 +- geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py index fb71932a..94ed5adb 100644 --- a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py +++ b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py @@ -31,7 +31,7 @@ from geos.mesh.processing.CreateConstantAttributePerRegion import ( CreateConstantAttributePerRegion ) from geos.pv.utils.details import SISOFilter, FilterCategory -from geos.mesh.processing.CreateConstantAttributePerRegion import CreateConstantAttributePerRegion, vnp, np +from geos.mesh.processing.CreateConstantAttributePerRegion import CreateConstantAttributePerRegion __doc__ = """ PVCreateConstantAttributePerRegion is a Paraview plugin that allows to create an attribute diff --git a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py index 3088f9e2..b721b7ab 100644 --- a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py @@ -257,7 +257,7 @@ def Filter(self, inputMesh: vtkUnstructuredGrid, outputMesh:vtkUnstructuredGrid) stats: QualityMetricSummary = filter.GetQualityMetricSummary() self.saveFile( stats ) self._blockIndex += 1 - return 1 + return def saveFile( self: Self, stats: QualityMetricSummary ) -> None: """Export mesh quality metric summary file.""" From 358a45f8dadb27b27983b2e6fc61c95617041daa Mon Sep 17 00:00:00 2001 From: jacques franc Date: Tue, 21 Oct 2025 16:36:05 +0200 Subject: [PATCH 25/26] some more constraints --- .../PVCreateConstantAttributePerRegion.py | 18 ++++---- .../geos/pv/plugins/PVFillPartialArrays.py | 4 +- .../geos/pv/plugins/PVMeshQualityEnhanced.py | 21 +++++---- .../pv/plugins/PVPythonViewConfigurator.py | 11 +++-- geos-pv/src/geos/pv/plugins/PVSplitMesh.py | 16 ++----- geos-pv/src/geos/pv/utils/details.py | 46 +++++++++++-------- 6 files changed, 61 insertions(+), 55 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py index 94ed5adb..aabb2e56 100644 --- a/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py +++ b/geos-pv/src/geos/pv/plugins/PVCreateConstantAttributePerRegion.py @@ -18,20 +18,16 @@ import vtkmodules.util.numpy_support as vnp - from vtkmodules.vtkCommonDataModel import ( - vtkDataSet, -) + 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.pv.utils.config import update_paths from geos.mesh.processing.CreateConstantAttributePerRegion import ( CreateConstantAttributePerRegion ) from geos.pv.utils.details import SISOFilter, FilterCategory -from geos.mesh.processing.CreateConstantAttributePerRegion import CreateConstantAttributePerRegion __doc__ = """ PVCreateConstantAttributePerRegion is a Paraview plugin that allows to create an attribute @@ -53,10 +49,12 @@ * Apply. """ + + @SISOFilter( category=FilterCategory.GEOS_PROP, decoratedLabel="Create Constant Attribute Per Region", - decoratedType=[ "vtkMultiBlockDataSet", "vtkDataSet" ]) -class PVCreateConstantAttributePerRegion(VTKPythonAlgorithmBase): + decoratedType=[ "vtkMultiBlockDataSet", "vtkDataSet" ] ) +class PVCreateConstantAttributePerRegion( VTKPythonAlgorithmBase ): def __init__( self: Self ) -> None: """Create an attribute with constant value per region.""" @@ -275,12 +273,12 @@ def groupNewAttributeSettingsWidgets( self: Self ) -> None: """Group the widgets to set the settings of the new attribute.""" self.Modified() - def Filter(self, inputMesh: vtkDataSet , outputMesh: vtkDataSet) -> None: + def Filter( self, inputMesh: vtkDataSet, outputMesh: vtkDataSet ) -> None: """Is applying CreateConstantAttributePerRegion filter. - outputMesh : A mesh transformed. + Args: inputMesh : A mesh to transform - outputMesh : A mesh transformed + outputMesh : A mesh transformed. """ filter: CreateConstantAttributePerRegion = CreateConstantAttributePerRegion( outputMesh, diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index b9adfbc1..b5c60dc8 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -46,7 +46,7 @@ @SISOFilter( category=FilterCategory.GEOS_UTILS, decoratedLabel="Fill Partial Arrays", decoratedType="vtkMultiBlockDataSet" ) -class PVFillPartialArrays(VTKPythonAlgorithmBase): +class PVFillPartialArrays( VTKPythonAlgorithmBase ): def __init__( self: Self, ) -> None: """Fill a partial attribute with constant value per component.""" @@ -90,7 +90,7 @@ def setDictAttributesValues( self: Self, attributeName: str, values: str ) -> No if values is not None: self.dictAttributesValues[ attributeName ] = list( values.split( "," ) ) else: - self.dictAttributesValues[ attributeName ] = None # ignore : type[unreachable] + self.dictAttributesValues[ attributeName ] = None #ignore : type[unreachable] self.Modified() diff --git a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py index b721b7ab..2145fc1d 100644 --- a/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py +++ b/geos-pv/src/geos/pv/plugins/PVMeshQualityEnhanced.py @@ -7,11 +7,10 @@ from typing_extensions import Self, Optional from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase, smdomain, smproperty, + VTKPythonAlgorithmBase, smdomain, smproperty, ) from vtkmodules.vtkCommonCore import ( - vtkDataArraySelection, -) + vtkDataArraySelection, ) from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, ) @@ -65,10 +64,12 @@ .. IMPORTANT:: Please refer to the `Verdict Manual `_ for metrics and range definitions. """ + + @SISOFilter( category=FilterCategory.GEOS_QC, decoratedLabel="Mesh Quality Enhanced", - decoratedType="vtkUnstructuredGrid") -class PVMeshQualityEnhanced(VTKPythonAlgorithmBase): + decoratedType="vtkUnstructuredGrid" ) +class PVMeshQualityEnhanced( VTKPythonAlgorithmBase ): def __init__( self: Self ) -> None: """Merge collocated points.""" @@ -164,7 +165,7 @@ def b01SetSaveToFile( self: Self, saveToFile: bool ) -> None: """ if self._saveToFile != saveToFile: self._saveToFile = saveToFile - PVMeshQualityEnhanced.Modified(self) + PVMeshQualityEnhanced.Modified( self ) @smproperty.stringvector( name="FilePath", label="File Path" ) @smdomain.xml( """ @@ -183,7 +184,7 @@ def b02SetFileName( self: Self, fname: str ) -> None: """ if self._filename != fname: self._filename = fname - PVMeshQualityEnhanced.Modified(self) + PVMeshQualityEnhanced.Modified( self ) @smproperty.xml( """ None: """ ) def b03GroupAdvancedOutputParameters( self: Self ) -> None: """Organize groups.""" - PVMeshQualityEnhanced.Modified(self) + PVMeshQualityEnhanced.Modified( self ) def Modified( self: Self ) -> None: """Overload Modified method to reset _blockIndex.""" self._blockIndex = 0 - VTKPythonAlgorithmBase.Modified(self) + VTKPythonAlgorithmBase.Modified( self ) def _getQualityMetricsToUse( self: Self, selection: vtkDataArraySelection ) -> set[ int ]: """Get mesh quality metric indexes from user selection. @@ -215,7 +216,7 @@ def _getQualityMetricsToUse( self: Self, selection: vtkDataArraySelection ) -> s metricsNames: set[ str ] = getArrayChoices( selection ) return { getQualityMeasureIndexFromName( name ) for name in metricsNames } - def Filter(self, inputMesh: vtkUnstructuredGrid, outputMesh:vtkUnstructuredGrid) -> None: + def Filter( self, inputMesh: vtkUnstructuredGrid, outputMesh: vtkUnstructuredGrid ) -> None: """Is applying MeshQualityEnhanced to the input Mesh. Args: diff --git a/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py b/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py index f536e80e..56f8e7e7 100755 --- a/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py +++ b/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py @@ -41,6 +41,9 @@ vtkInformation, ) +from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] + VTKPythonAlgorithmBase ) + from vtkmodules.vtkCommonDataModel import vtkDataObject from geos.pv.utils.details import SISOFilter, FilterCategory @@ -59,10 +62,12 @@ * Search and Apply PVPythonViewConfigurator Filter. """ + + @SISOFilter( category=FilterCategory.GEOS_UTILS, decoratedLabel="Python View Configurator", - decoratedType="vtkDataObject") -class PVPythonViewConfigurator: + decoratedType="vtkDataObject" ) +class PVPythonViewConfigurator( VTKPythonAlgorithmBase ): def __init__( self: Self ) -> None: """Paraview plugin to create cross-plots in a Python View. @@ -805,7 +810,7 @@ def FillInputPortInformation( self: Self, port: int, info: vtkInformation ) -> i info.Set( self.INPUT_REQUIRED_DATA_TYPE(), "vtkDataObject" ) return 1 - def Filter(self, inputMesh : vtkDataObject, outputMesh : vtkDataObject) -> None: + def Filter( self, inputMesh: vtkDataObject, outputMesh: vtkDataObject ) -> None: """Dummy interface for plugin to fit decorator reqs. Args: diff --git a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py index 2c0f68ea..4f2ba58e 100644 --- a/geos-pv/src/geos/pv/plugins/PVSplitMesh.py +++ b/geos-pv/src/geos/pv/plugins/PVSplitMesh.py @@ -7,8 +7,7 @@ from typing_extensions import Self from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] - VTKPythonAlgorithmBase -) + VTKPythonAlgorithmBase ) from vtkmodules.vtkCommonDataModel import ( vtkPointSet, ) @@ -22,6 +21,7 @@ from geos.mesh.processing.SplitMesh import SplitMesh from geos.pv.utils.details import SISOFilter, FilterCategory + __doc__ = """ Split each cell of input mesh to smaller cells. @@ -36,20 +36,14 @@ """ -@SISOFilter( category=FilterCategory.GEOS_UTILS, - decoratedLabel="Split Mesh", - decoratedType="vtkPointSet") -class PVSplitMesh(VTKPythonAlgorithmBase): +@SISOFilter( category=FilterCategory.GEOS_UTILS, decoratedLabel="Split Mesh", decoratedType="vtkPointSet" ) +class PVSplitMesh( VTKPythonAlgorithmBase ): def __init__( self: Self ) -> None: """Split mesh cells.""" pass - def Filter( - self: Self, - inputMesh: vtkPointSet, - outputMesh:vtkPointSet - ) -> None: + def Filter( self: Self, inputMesh: vtkPointSet, outputMesh: vtkPointSet ) -> None: """Apply vtk filter. Args: diff --git a/geos-pv/src/geos/pv/utils/details.py b/geos-pv/src/geos/pv/utils/details.py index fc0ad001..fc233c1a 100644 --- a/geos-pv/src/geos/pv/utils/details.py +++ b/geos-pv/src/geos/pv/utils/details.py @@ -11,13 +11,14 @@ from abc import abstractmethod from enum import Enum - 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 vtkmodules.vtkCommonDataModel import ( - vtkMultiBlockDataSet, ) + vtkMultiBlockDataSet, + vtkDataObject, +) from vtkmodules.vtkCommonCore import ( vtkInformation, @@ -43,6 +44,8 @@ class PVMyFilter: ... """ + + # Enum for filter categories class FilterCategory( str, Enum ): """String Enum to sort into category in PV task bar under Plugins.""" @@ -55,15 +58,17 @@ class FilterCategory( str, Enum ): # Add more as needed -U = TypeVar('U') +U = TypeVar( 'U', bound='vtkDataObject' ) + + @runtime_checkable -class IsSISOFilter( Protocol[U] ): +class IsSISOFilter( Protocol[ U ] ): """Protocol to ensure that the wrapped filter defines the correct Filter core function.""" @abstractmethod def Filter( self, - inputMesh: U, + inputMesh: U, outputMesh: U, ) -> None: """Define filter here. @@ -75,12 +80,14 @@ def Filter( """ raise NotImplementedError + T = TypeVar( 'T', bound='IsSISOFilter' ) def SISOFilter( category: FilterCategory, decoratedLabel: str, - decoratedType: Union[str,list] ) -> Callable[ [ Type[ T ] ], Type[ T ] ]: + decoratedType: Union[ str, list ] ) -> Callable[ [ Type[ T ] ], Type[ T ] ]: """Decorate Single Input Single Output (SISO) filter.""" + def decoratedClass( cls: Type[ T ] ) -> Type[ T ]: """Outer wrapper function. All is in the WrappingClass below.""" originalInit = cls.__init__ @@ -94,11 +101,12 @@ def __init__( self, *ar: Any, **kw: Any ) -> None: ar : Fowarded arguments kw : Forwarded keywords args """ - VTKPythonAlgorithmBase.__init__( self, - nInputPorts=1, - nOutputPorts=1, - inputType=decoratedType if isinstance(decoratedType,str) else "vtkDataObject", - outputType=decoratedType if isinstance(decoratedType,str) else "vtkDataObject") + VTKPythonAlgorithmBase.__init__( + self, + nInputPorts=1, + nOutputPorts=1, + inputType=decoratedType if isinstance( decoratedType, str ) else "vtkDataObject", + outputType=decoratedType if isinstance( decoratedType, str ) else "vtkDataObject" ) #If wrapped class has more to init there it is applied #avoid the overwritten init by decorator taking place of the cls @@ -155,16 +163,16 @@ def RequestData( cls.Filter( self, inputMesh, outputMesh ) return 1 - + # Copy all methods and attributes from cls, including decorator metadata - for attrName in dir(cls): - if attrName.startswith('_'): + for attrName in dir( cls ): + if attrName.startswith( '_' ): continue # Skip private/magic methods (already handled or inherited) - - attr = getattr(cls, attrName) + + attr = getattr( cls, attrName ) # Copy methods with their decorators - if callable(attr) and attrName not in WrappingClass.__dict__: - setattr(WrappingClass,attrName,attr) + if callable( attr ) and attrName not in WrappingClass.__dict__: + setattr( WrappingClass, attrName, attr ) # Copy metadata WrappingClass.__name__ = cls.__name__ @@ -175,7 +183,7 @@ def RequestData( #decorate it old fashion way WrappingClass = smdomain.datatype( - dataTypes=[ decoratedType ] if isinstance(decoratedType,str) else decoratedType, + dataTypes=[ decoratedType ] if isinstance( decoratedType, str ) else decoratedType, composite_data_supported=True, )( WrappingClass ) WrappingClass = smproperty.input( name="Input", port_index=0 )( WrappingClass ) From 08bd8e229e8a234cf9f507a87f30ce999e55c584 Mon Sep 17 00:00:00 2001 From: alexbenedicto Date: Wed, 22 Oct 2025 17:24:08 -0700 Subject: [PATCH 26/26] Fix for PVPythonViewConfigurator to handle new MultiblockDataSet input. Can still crash in some cases and will need separate PR to deal with it. --- .../src/geos/pv/plugins/PVPythonViewConfigurator.py | 9 ++++++++- geos-pv/src/geos/pv/utils/paraviewTreatments.py | 13 ++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py b/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py index 56f8e7e7..5361c8f4 100755 --- a/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py +++ b/geos-pv/src/geos/pv/plugins/PVPythonViewConfigurator.py @@ -16,6 +16,7 @@ update_paths() +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, ) @@ -44,7 +45,10 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase ) -from vtkmodules.vtkCommonDataModel import vtkDataObject +from vtkmodules.vtkCommonDataModel import ( + vtkDataObject, + vtkMultiBlockDataSet, +) from geos.pv.utils.details import SISOFilter, FilterCategory __doc__ = """ @@ -84,6 +88,9 @@ def __init__( self: Self ) -> None: # Input source and curve names. inputSource = GetActiveSource() dataset = servermanager.Fetch( inputSource ) + # Handle vtkMultiBlockDataSet by merging blocks first + if isinstance( dataset, vtkMultiBlockDataSet ): + dataset = mergeBlocks( dataset, keepPartialAttributes=True ) dataframe: pd.DataFrame = pvt.vtkToDataframe( dataset ) self.m_pathPythonViewScript: Path = geos_pv_path / "src/geos/pv/pythonViewUtils/mainPythonView.py" diff --git a/geos-pv/src/geos/pv/utils/paraviewTreatments.py b/geos-pv/src/geos/pv/utils/paraviewTreatments.py index 41894e4f..2856aee7 100644 --- a/geos-pv/src/geos/pv/utils/paraviewTreatments.py +++ b/geos-pv/src/geos/pv/utils/paraviewTreatments.py @@ -37,6 +37,7 @@ vtkTable, vtkUnstructuredGrid, ) +from vtkmodules.vtkFiltersParallelDIY2 import vtkGenerateGlobalIds from geos.utils.GeosOutputsConstants import ( ComponentNameEnum, @@ -122,8 +123,18 @@ def vtkUnstructuredGridCellsToDataframe( grid: vtkUnstructuredGrid ) -> pd.DataF Returns: pd.DataFrame: Pandas dataframe. """ - cellIdAttributeName = GeosMeshOutputsEnum.VTK_ORIGINAL_CELL_ID.attributeName + cellIdAttributeName: str = GeosMeshOutputsEnum.VTK_ORIGINAL_CELL_ID.attributeName cellData = grid.GetCellData() + if not cellData.HasArray( GeosMeshOutputsEnum.VTK_ORIGINAL_CELL_ID.attributeName ): + print( "We have to create global ids." ) + idFilter = vtkGenerateGlobalIds() + idFilter.SetInputData( grid ) + idFilter.Update() + grid = idFilter.GetOutput() + cellData = grid.GetCellData() # Update cellData to point to the new grid's cell data + cellIdAttributeName = "GlobalCellIds" + assert cellData.HasArray(cellIdAttributeName), "Invalid global ids array name selected." + numberCells: int = grid.GetNumberOfCells() data: dict[ str, Any ] = {} for i in range( cellData.GetNumberOfArrays() ):