From e4aa661c5cd2bcd0bbab84ecd6acd2f5c7f488bb Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 22 Aug 2025 15:34:58 +0200 Subject: [PATCH 01/45] First commit, move the plugin and the filter --- .../AttributeMappingFromCellCoords.py | 0 .../geos/pv/plugins}/PVAttributeMapping.py | 27 +++++++++---------- 2 files changed, 12 insertions(+), 15 deletions(-) rename {geos-posp/src/geos_posp/filters => geos-mesh/src/geos/mesh/processing}/AttributeMappingFromCellCoords.py (100%) rename {geos-posp/src/PVplugins => geos-pv/src/geos/pv/plugins}/PVAttributeMapping.py (94%) diff --git a/geos-posp/src/geos_posp/filters/AttributeMappingFromCellCoords.py b/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py similarity index 100% rename from geos-posp/src/geos_posp/filters/AttributeMappingFromCellCoords.py rename to geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py diff --git a/geos-posp/src/PVplugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py similarity index 94% rename from geos-posp/src/PVplugins/PVAttributeMapping.py rename to geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index 39b17b51f..5ba4aaa77 100644 --- a/geos-posp/src/PVplugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -1,30 +1,27 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. -# SPDX-FileContributor: Raphaël Vinour, Martin Lemay +# SPDX-FileContributor: Raphaël Vinour, Martin Lemay, Romain Baville # ruff: noqa: E402 # disable Module level import not at top of file -import os import sys +from pathlib import Path from typing import Union from typing_extensions import Self -dir_path = os.path.dirname( os.path.realpath( __file__ ) ) -parent_dir_path = os.path.dirname( dir_path ) -if parent_dir_path not in sys.path: - sys.path.append( parent_dir_path ) - -import PVplugins # noqa: F401 +# 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() from geos.utils.Logger import Logger, getLogger -from geos_posp.filters.AttributeMappingFromCellCoords import ( - AttributeMappingFromCellCoords, ) +from geos.mesh.processing.AttributeMappingFromCellCoords import AttributeMappingFromCellCoords from geos.mesh.utils.arrayModifiers import fillPartialAttributes from geos.mesh.utils.multiblockModifiers import mergeBlocks -from geos.mesh.utils.arrayHelpers import ( - getAttributeSet, ) -from geos_posp.visu.PVUtils.checkboxFunction import ( # type: ignore[attr-defined] - createModifiedCallback, ) -from geos_posp.visu.PVUtils.paraviewTreatments import getArrayChoices +from geos.mesh.utils.arrayHelpers import getAttributeSet + +from geos.pv.utils.checkboxFunction import createModifiedCallback +from geos.pv.utils.paraviewTreatments import getArrayChoices from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, ) From 702a3a4661312c2a3ec9a2e54bfd439dcf08f8d7 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 22 Aug 2025 16:06:31 +0200 Subject: [PATCH 02/45] fix issue with multiblock input --- .../mesh/processing/AttributeMappingFromCellCoords.py | 3 +-- geos-pv/src/geos/pv/plugins/PVAttributeMapping.py | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py b/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py index 4d9500b11..04b66dab0 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py @@ -218,8 +218,7 @@ def transferAttributes( self: Self ) -> bool: if nbComponents > 1: for i in range( nbComponents ): componentNames.append( array.GetComponentName( i ) ) - newArray: vtkDataArray = createEmptyAttribute( self.m_clientMesh, attributeName, tuple( componentNames ), - dataType ) + newArray: vtkDataArray = createEmptyAttribute( attributeName, tuple( componentNames ), dataType ) nanValues: list[ float ] = [ np.nan for _ in range( nbComponents ) ] for indexClient in range( self.m_clientMesh.GetNumberOfCells() ): indexServer: int = self.m_cellMap[ indexClient ] diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index 5ba4aaa77..966d03f85 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -63,8 +63,8 @@ ) @smproperty.input( name="Server", port_index=0, label="Server mesh" ) @smdomain.datatype( - dataTypes=[ "vtkUnstructuredGrid" ], - composite_data_supported=False, + dataTypes=[ "vtkUnstructuredGrid", "vtkMultiBlockDataSet" ], + composite_data_supported=True, ) class PVAttributeMapping( VTKPythonAlgorithmBase ): @@ -186,13 +186,13 @@ def RequestData( outData.ShallowCopy( clientMesh ) attributeNames: set[ str ] = set( getArrayChoices( self.a02GetAttributeToTransfer() ) ) - for attributeName in attributeNames: - fillPartialAttributes( serverMesh, attributeName, False ) mergedServerMesh: vtkUnstructuredGrid if isinstance( serverMesh, vtkUnstructuredGrid ): mergedServerMesh = serverMesh elif isinstance( serverMesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): + for attributeName in attributeNames: + fillPartialAttributes( serverMesh, attributeName, False ) mergedServerMesh = mergeBlocks( serverMesh ) else: raise ValueError( "Server mesh data type is not supported. " + From 79a1db4567cf6ca079e1a056731bdfa94374c27f Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 25 Aug 2025 18:39:02 +0200 Subject: [PATCH 03/45] Update logger and refactor - plugin with the VTKPythonAlgorithmBase - filtre deals with the mesh directly --- .../AttributeMappingFromCellCoords.py | 299 ++++++++++-------- .../src/geos/pv/plugins/PVAttributeMapping.py | 162 +++------- 2 files changed, 201 insertions(+), 260 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py b/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py index 04b66dab0..501692b40 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py @@ -7,22 +7,27 @@ import numpy as np import numpy.typing as npt -from geos.utils.Logger import Logger, getLogger -from typing_extensions import Self -from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase -from vtkmodules.vtkCommonCore import ( - vtkDataArray, - vtkDoubleArray, - vtkInformation, - vtkInformationVector, +from geos.utils.Logger import logging, Logger, getLogger +from typing_extensions import Self, Union, Any + +from vtk import ( # type: ignore[import-untyped] + VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT, VTK_UNSIGNED_LONG_LONG, + VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE, VTK_FLOAT, VTK_DOUBLE, ) +from vtkmodules.vtkCommonCore import vtkDataArray from vtkmodules.vtkCommonDataModel import ( vtkCellData, vtkCellLocator, + vtkDataSet, + vtkMultiBlockDataSet, vtkUnstructuredGrid, + vtkCompositeDataSet, ) + +from geos.mesh.utils.arrayModifiers import fillPartialAttributes +from geos.mesh.utils.multiblockModifiers import mergeBlocks from geos.mesh.utils.arrayModifiers import createEmptyAttribute -from geos.mesh.utils.arrayHelpers import ( getVtkArrayInObject, computeCellCenterCoordinates ) +from geos.mesh.utils.arrayHelpers import ( getVtkArrayInObject, computeCellCenterCoordinates, isAttributeGlobal ) __doc__ = """ AttributeMappingFromCellCoords module is a vtk filter that map two identical mesh (or a mesh is @@ -41,7 +46,7 @@ input :vtkUnstructuredGrid TransferAttributeName : str - # instanciate the filter + # instantiate the filter filter :AttributeMappingFromCellCoords = AttributeMappingFromCellCoords() # set the logger filter.SetLogger(logger) @@ -59,24 +64,51 @@ newAttributeNames :set[str] = filter.GetNewAttributeNames() """ +loggerTitle: str = "Attribute Mapping" + -class AttributeMappingFromCellCoords( VTKPythonAlgorithmBase ): +class AttributeMappingFromCellCoords: - def __init__( self: Self ) -> None: - """Map the properties of a source mesh to a receiver mesh.""" - super().__init__( nInputPorts=2, nOutputPorts=1, outputType="vtkUnstructuredGrid" ) + def __init__( + self: Self, + sourceMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ], + workingMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ], + speHandler: bool = False, + ) -> None: + """Map the properties of the source mesh to the working mesh.""" + + self.sourceMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] = sourceMesh + self.workingMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] = workingMesh - # source mesh - self.m_clientMesh: vtkUnstructuredGrid - # input mesh - self.m_serverMesh: vtkUnstructuredGrid # cell map self.m_cellMap: npt.NDArray[ np.int64 ] = np.empty( 0 ).astype( int ) # Transfer Attribute name - self.m_transferedAttributeNames: set[ str ] = set() - # logger - self.m_logger: Logger = getLogger( "Attribute Mapping From Cell Coords Filter" ) + self.m_transferredAttributeNames: set[ str ] = set() + + # 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 SetTransferAttributeNames( self: Self, transferredAttributeNames: set[ str ] ) -> None: """Setter for transferredAttributeName. @@ -86,149 +118,148 @@ def SetTransferAttributeNames( self: Self, transferredAttributeNames: set[ str ] attributes to transfer. """ - self.m_transferedAttributeNames.clear() + self.m_transferredAttributeNames.clear() for name in transferredAttributeNames: - self.m_transferedAttributeNames.add( name ) - - def SetLogger( self: Self, logger: Logger ) -> None: - """Set filter logger. + self.m_transferredAttributeNames.add( name ) - Args: - logger (Logger): logger - """ - self.m_logger = logger - self.Modified() def GetCellMap( self: Self ) -> npt.NDArray[ np.int64 ]: """Getter of cell map.""" return self.m_cellMap - 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 - inInfoVec: list[ vtkInformationVector ], - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestData. - Args: - request (vtkInformation): request - inInfoVec (list[vtkInformationVector]): input objects - outInfoVec (vtkInformationVector): output objects + def applyFilter( self: Self ) -> bool: + """Map the properties from the source mesh to the working mesh for the common cells. Returns: - int: 1 if calculation successfully ended, 0 otherwise. + boolean (bool): True if calculation successfully ended, False otherwise. """ - try: - self.m_serverMesh = vtkUnstructuredGrid.GetData( inInfoVec[ 0 ] ) - clientMesh = vtkUnstructuredGrid.GetData( inInfoVec[ 1 ] ) - self.m_clientMesh = self.GetOutputData( outInfoVec, 0 ) - - assert self.m_serverMesh is not None, "Server mesh is null." - assert clientMesh is not None, "Client mesh is null." - assert self.m_clientMesh is not None, "Output pipeline is null." - - self.m_clientMesh.ShallowCopy( clientMesh ) - - # create cell map - self.computeCellMapping() + self.logger.info( f"Apply filter { self.logger.name }." ) + + if isinstance( self.sourceMesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): + for attributeName in self.m_transferredAttributeNames: + if not isAttributeGlobal( self.sourceMesh, attributeName, False ): + fillPartialAttributes( self.sourceMesh, attributeName, logger=self.logger ) + self.sourceMesh = mergeBlocks( self.sourceMesh ) + elif not isinstance( self.sourceMesh, vtkDataSet ): + self.logger.error( "The source mesh data type is not vtkDataSet nor vtkMultiBlockDataSet." ) + self.logger.error( f"The filter { self.logger.name } failed." ) + return False + + if isinstance( self.workingMesh, vtkDataSet ): + if not self._transferAttributes( self.workingMesh ): + self.logger.warning( "Source mesh and working mesh do not have any corresponding cells." ) + self.logger.warning( f"The filter { self.logger.name } has not been needed." ) + return False + elif isinstance( self.workingMesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): + if not self._transferAttributesMultiBlock(): + return False + else: + self.logger.error( "The working mesh data type is not vtkDataSet nor vtkMultiBlockDataSet." ) + self.logger.error( f"The filter { self.logger.name } failed." ) + return False + + # Log the output message. + self._logOutputMessage() - # transfer attributes if at least one corresponding cell - if np.any( self.m_cellMap > -1 ): - self.transferAttributes() - self.m_clientMesh.Modified() - else: - self.m_logger.warning( "Input and output meshes do not have any corresponding cells" ) - - except AssertionError as e: - mess1: str = "Mapping Transfer Coord failed due to:" - self.m_logger.error( mess1 ) - self.m_logger.error( e, exc_info=True ) - return 0 - except Exception as e: - mess0: str = "Mapping Transfer Coord failed due to:" - self.m_logger.critical( mess0 ) - self.m_logger.critical( e, exc_info=True ) - return 0 - mess2: str = "Mapping Transfer Coord were successfully computed." - self.m_logger.info( mess2 ) - - return 1 + return True - def computeCellMapping( self: Self ) -> bool: - """Create the cell map from client to server mesh cell indexes. + def _computeCellMapping( self: Self, workingMesh ) -> bool: + """Create the cell map from the source mesh to the working mesh cell indexes. - For each cell index of the client mesh, stores the index of the cell - in the server mesh. + For each cell index of the working mesh, stores the index of the cell + in the source mesh. Returns: bool: True if the map was computed. """ - self.m_cellMap = np.full( self.m_clientMesh.GetNumberOfCells(), -1 ).astype( int ) + self.m_cellMap = np.full( workingMesh.GetNumberOfCells(), -1 ).astype( int ) cellLocator: vtkCellLocator = vtkCellLocator() - cellLocator.SetDataSet( self.m_serverMesh ) + cellLocator.SetDataSet( self.sourceMesh ) cellLocator.BuildLocator() - cellCenters: vtkDataArray = computeCellCenterCoordinates( self.m_clientMesh ) - for i in range( self.m_clientMesh.GetNumberOfCells() ): + cellCenters: vtkDataArray = computeCellCenterCoordinates( self.sourceMesh ) + for i in range( workingMesh.GetNumberOfCells() ): cellCoords: MutableSequence[ float ] = [ 0.0, 0.0, 0.0 ] cellCenters.GetTuple( i, cellCoords ) cellIndex: int = cellLocator.FindCell( cellCoords ) self.m_cellMap[ i ] = cellIndex return True - def transferAttributes( self: Self ) -> bool: - """Transfer attributes from server to client meshes using cell mapping. + def _transferAttributes( self: Self, workingMesh: vtkUnstructuredGrid ) -> bool: + """Transfer attributes from the source mesh to the working meshes using cell mapping. Returns: bool: True if transfer successfully ended. """ - for attributeName in self.m_transferedAttributeNames: - array: vtkDoubleArray = getVtkArrayInObject( self.m_serverMesh, attributeName, False ) - - dataType = array.GetDataType() - nbComponents: int = array.GetNumberOfComponents() - componentNames: list[ str ] = [] - if nbComponents > 1: - for i in range( nbComponents ): - componentNames.append( array.GetComponentName( i ) ) - newArray: vtkDataArray = createEmptyAttribute( attributeName, tuple( componentNames ), dataType ) - nanValues: list[ float ] = [ np.nan for _ in range( nbComponents ) ] - for indexClient in range( self.m_clientMesh.GetNumberOfCells() ): - indexServer: int = self.m_cellMap[ indexClient ] - data: MutableSequence[ float ] = nanValues - if indexServer > -1: - array.GetTuple( indexServer, data ) - newArray.InsertNextTuple( data ) - - cellData: vtkCellData = self.m_clientMesh.GetCellData() - assert cellData is not None, "CellData is undefined." - cellData.AddArray( newArray ) - cellData.Modified() - return True + # create cell map + self._computeCellMapping( workingMesh ) + + # transfer attributes if at least one corresponding cell + if np.any( self.m_cellMap > -1 ): + for attributeName in self.m_transferredAttributeNames: + array: vtkDataArray = getVtkArrayInObject( self.sourceMesh, attributeName, False ) + + dataType = array.GetDataType() + nbComponents: int = array.GetNumberOfComponents() + componentNames: list[ str ] = [] + + defaultValue: Any + if dataType in ( VTK_FLOAT, VTK_DOUBLE ): + defaultValue =[ np.nan for _ in range( nbComponents ) ] + elif dataType in ( VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE ): + defaultValue = [ -1 for _ in range( nbComponents ) ] + elif dataType in ( VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT, + VTK_UNSIGNED_LONG_LONG ): + defaultValue = [ 0 for _ in range( nbComponents ) ] + + if nbComponents > 1: + for i in range( nbComponents ): + componentNames.append( array.GetComponentName( i ) ) + newArray: vtkDataArray = createEmptyAttribute( attributeName, tuple( componentNames ), dataType ) + + for indexWorking in range( workingMesh.GetNumberOfCells() ): + indexSource: int = self.m_cellMap[ indexWorking ] + data: MutableSequence[ float ] = defaultValue + if indexSource > -1: + array.GetTuple( indexSource, data ) + newArray.InsertNextTuple( data ) + + cellData: vtkCellData = workingMesh.GetCellData() + assert cellData is not None, "CellData is undefined." + cellData.AddArray( newArray ) + cellData.Modified() + + return True + + return False + + + def _transferAttributesMultiBlock( self: Self ) -> bool: + """Transfer attributes from a vtkUnstructuredGrid to a multiblock. + + Returns: + bool: True if attributes were successfully transferred. + + """ + usedCheck: bool = False + nbBlocks: int = self.workingMesh.GetNumberOfBlocks() + for idBlock in range( nbBlocks ): + workingBlock: vtkUnstructuredGrid = vtkUnstructuredGrid.SafeDownCast( self.workingMesh.GetBlock( idBlock ) ) + if not self._transferAttributes( workingBlock ): + self.logger.warning( f"Source mesh and the working mesh block number { idBlock } do not have any corresponding cells." ) + else: + usedCheck = True + + if usedCheck: + return True + + self.logger.warning( f"The filter { self.logger.name } has not been needed." ) + return False + + + def _logOutputMessage( self: Self ) -> None: + self.logger.info( f"The filter { self.logger.name } succeed." ) + diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index 966d03f85..6018feeaf 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -14,10 +14,7 @@ from geos.pv.utils.config import update_paths update_paths() -from geos.utils.Logger import Logger, getLogger from geos.mesh.processing.AttributeMappingFromCellCoords import AttributeMappingFromCellCoords -from geos.mesh.utils.arrayModifiers import fillPartialAttributes -from geos.mesh.utils.multiblockModifiers import mergeBlocks from geos.mesh.utils.arrayHelpers import getAttributeSet from geos.pv.utils.checkboxFunction import createModifiedCallback @@ -25,6 +22,10 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, ) +from paraview.detail.loghandler import ( # type: ignore[import-not-found] + VTKHandler, +) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py + from vtkmodules.vtkCommonCore import ( vtkDataArraySelection, vtkInformation, @@ -32,45 +33,44 @@ ) from vtkmodules.vtkCommonDataModel import ( vtkCompositeDataSet, - vtkDataObjectTreeIterator, vtkDataSet, vtkMultiBlockDataSet, - vtkUnstructuredGrid, ) __doc__ = """ -Map the attributes from a source mesh to a client mesh. +Map the attributes from a source mesh to a working mesh. Input and output are vtkUnstructuredGrid. To use it: * Load the module in Paraview: Tools>Manage Plugins...>Load new>PVAttributeMapping. -* Select the server mesh. +* Select the working mesh. * Search and Select Attribute Mapping Filter. -* Select the client mesh. -* Select the attributes to transfer and Apply. +* Select the source mesh. +* Select the attributes to transfer from the source mesh to the working mesh. +* Apply. """ @smproxy.filter( name="PVAttributeMapping", label="Attribute Mapping" ) @smhint.xml( '' ) -@smproperty.input( name="Client", port_index=1, label="Client mesh" ) +@smproperty.input( name="Source", port_index=1, label="Source mesh" ) @smdomain.datatype( - dataTypes=[ "vtkUnstructuredGrid", "vtkMultiBlockDataSet" ], + dataTypes=[ "vtkDataSet", "vtkMultiBlockDataSet" ], composite_data_supported=True, ) -@smproperty.input( name="Server", port_index=0, label="Server mesh" ) +@smproperty.input( name="Working", port_index=0, label="Working mesh" ) @smdomain.datatype( - dataTypes=[ "vtkUnstructuredGrid", "vtkMultiBlockDataSet" ], + dataTypes=[ "vtkDataSet", "vtkMultiBlockDataSet" ], composite_data_supported=True, ) class PVAttributeMapping( VTKPythonAlgorithmBase ): def __init__( self: Self ) -> None: - """Map the properties of a server mesh to a client mesh.""" - super().__init__( nInputPorts=2, nOutputPorts=1, outputType="vtkUnstructuredGrid" ) + """Map the properties of the source mesh to the working mesh.""" + super().__init__( nInputPorts=2, nOutputPorts=1, inputType="vtkObject", outputType="vtkObject" ) # boolean to check if first use of the filter for attribute list initialization self.m_firstUse = True @@ -79,18 +79,6 @@ def __init__( self: Self ) -> None: self.m_attributes: vtkDataArraySelection = vtkDataArraySelection() self.m_attributes.AddObserver( 0, createModifiedCallback( self ) ) - # logger - self.m_logger: Logger = getLogger( "Attribute Mapping" ) - - def SetLogger( self: Self, logger: Logger ) -> None: - """Set filter logger. - - Args: - logger (Logger): logger - """ - self.m_logger = logger - self.Modified() - @smproperty.dataarrayselection( name="AttributesToTransfer" ) def a02GetAttributeToTransfer( self: Self ) -> vtkDataArraySelection: """Get selected attribute names to transfer. @@ -119,14 +107,14 @@ def RequestInformation( # only at initialization step, no change later if self.m_firstUse: # get cell ids - inData = self.GetInputData( inInfoVec, 0, 0 ) - assert isinstance( inData, ( vtkDataSet, vtkMultiBlockDataSet ) ), "Input object type is not supported." + inData = self.GetInputData( inInfoVec, 1, 0 ) + assert isinstance( inData, ( vtkDataSet, vtkMultiBlockDataSet ) ), "Working mesh type is not supported." # update vtkDAS attributeNames: set[ str ] = getAttributeSet( inData, False ) for attributeName in attributeNames: if not self.m_attributes.ArrayExists( attributeName ): - self.m_attributes.AddArray( attributeName ) + self.m_attributes.AddArray( attributeName, False ) self.m_firstUse = False return 1 @@ -147,7 +135,7 @@ def RequestDataObject( Returns: int: 1 if calculation successfully ended, 0 otherwise. """ - inData = self.GetInputData( inInfoVec, 1, 0 ) + 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() ) ): @@ -171,106 +159,28 @@ def RequestData( Returns: int: 1 if calculation successfully ended, 0 otherwise. """ - self.m_logger.info( f"Apply filter {__name__}" ) - try: - serverMesh: Union[ vtkUnstructuredGrid, vtkMultiBlockDataSet, - vtkCompositeDataSet ] = self.GetInputData( inInfoVec, 0, 0 ) - clientMesh: Union[ vtkUnstructuredGrid, vtkMultiBlockDataSet, - vtkCompositeDataSet ] = self.GetInputData( inInfoVec, 1, 0 ) - outData: Union[ vtkUnstructuredGrid, vtkMultiBlockDataSet, - vtkCompositeDataSet ] = self.GetOutputData( outInfoVec, 0 ) - - assert serverMesh is not None, "Input server mesh is null." - assert clientMesh is not None, "Input client mesh is null." - assert outData is not None, "Output pipeline is null." - - outData.ShallowCopy( clientMesh ) - attributeNames: set[ str ] = set( getArrayChoices( self.a02GetAttributeToTransfer() ) ) - - mergedServerMesh: vtkUnstructuredGrid - if isinstance( serverMesh, vtkUnstructuredGrid ): - mergedServerMesh = serverMesh - elif isinstance( serverMesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): - for attributeName in attributeNames: - fillPartialAttributes( serverMesh, attributeName, False ) - mergedServerMesh = mergeBlocks( serverMesh ) - else: - raise ValueError( "Server mesh data type is not supported. " + - "Use either vtkUnstructuredGrid or vtkMultiBlockDataSet" ) - - if isinstance( outData, vtkUnstructuredGrid ): - self.doTransferAttributes( mergedServerMesh, outData, attributeNames ) - elif isinstance( outData, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): - self.transferAttributesMultiBlockDataSet( mergedServerMesh, outData, attributeNames ) - else: - raise ValueError( "Client mesh data type is not supported. " + - "Use either vtkUnstructuredGrid or vtkMultiBlockDataSet" ) - - outData.Modified() - - mess: str = "Attributes were successfully transferred ." - self.m_logger.info( mess ) - except AssertionError as e: - mess1: str = "Attribute transfer failed due to:" - self.m_logger.error( mess1 ) - self.m_logger.error( e, exc_info=True ) - return 0 - except Exception as e: - mess0: str = "Attribute transfer failed due to:" - self.m_logger.critical( mess0 ) - self.m_logger.critical( e, exc_info=True ) - return 0 - return 1 + workingMesh: Union[ vtkDataSet, vtkMultiBlockDataSet, + vtkCompositeDataSet ] = self.GetInputData( inInfoVec, 0, 0 ) + sourceMesh: Union[ vtkDataSet, vtkMultiBlockDataSet, + vtkCompositeDataSet ] = self.GetInputData( inInfoVec, 1, 0 ) + outData: Union[ vtkDataSet, vtkMultiBlockDataSet, + vtkCompositeDataSet ] = self.GetOutputData( outInfoVec, 0 ) - def doTransferAttributes( - self: Self, - serverMesh: vtkUnstructuredGrid, - clientMesh: vtkUnstructuredGrid, - attributeNames: set[ str ], - ) -> bool: - """Transfer attributes between two vtkUnstructuredGrids. + assert workingMesh is not None, "Input working mesh is null." + assert sourceMesh is not None, "Input source mesh is null." + assert outData is not None, "Output pipeline is null." - Args: - serverMesh (vtkUnstructuredGrid): server mesh - clientMesh (vtkUnstructuredGrid): client mesh - attributeNames (set[str]): set of attribut names to transfer + outData.ShallowCopy( workingMesh ) - Returns: - bool: True if attributes were successfully transferred. + attributeNames: set[ str ] = set( getArrayChoices( self.a02GetAttributeToTransfer() ) ) - """ - filter: AttributeMappingFromCellCoords = AttributeMappingFromCellCoords() - filter.AddInputDataObject( 0, serverMesh ) - filter.AddInputDataObject( 1, clientMesh ) + filter: AttributeMappingFromCellCoords = AttributeMappingFromCellCoords( sourceMesh, outData, True ) + if not filter.logger.hasHandlers(): + filter.setLoggerHandler( VTKHandler() ) filter.SetTransferAttributeNames( attributeNames ) - filter.Update() - clientMesh.ShallowCopy( filter.GetOutputDataObject( 0 ) ) - return True - def transferAttributesMultiBlockDataSet( - self: Self, - serverBlock: vtkUnstructuredGrid, - clientMesh: Union[ vtkMultiBlockDataSet, vtkCompositeDataSet ], - attributeNames: set[ str ], - ) -> bool: - """Transfer attributes from a vtkUnstructuredGrid to a multiblock. - - Args: - serverBlock (vtkUnstructuredGrid): server mesh - clientMesh (vtkMultiBlockDataSet | vtkCompositeDataSet): client mesh - attributeNames (set[str]): set of attribut names to transfer + filter.applyFilter() + self.m_firstUse = True - Returns: - bool: True if attributes were successfully transferred. + return 1 - """ - iter: vtkDataObjectTreeIterator = vtkDataObjectTreeIterator() - iter.SetDataSet( clientMesh ) - iter.VisitOnlyLeavesOn() - iter.GoToFirstItem() - while iter.GetCurrentDataObject() is not None: - clientBlock: vtkUnstructuredGrid = vtkUnstructuredGrid.SafeDownCast( iter.GetCurrentDataObject() ) - self.doTransferAttributes( serverBlock, clientBlock, attributeNames ) - clientBlock.Modified() - iter.GoToNextItem() - return True From 9b86c83552e59c5bca11c66561977fd4b276a857 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 1 Sep 2025 14:56:52 +0200 Subject: [PATCH 04/45] fix apply feature on paraview --- .../AttributeMappingFromCellCoords.py | 82 +++++++++++-------- .../src/geos/pv/plugins/PVAttributeMapping.py | 7 +- 2 files changed, 50 insertions(+), 39 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py b/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py index 501692b40..fec1229dd 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. -# SPDX-FileContributor: Raphaël Vinour, Martin Lemay +# SPDX-FileContributor: Raphaël Vinour, Martin Lemay, Romain Baville # ruff: noqa: E402 # disable Module level import not at top of file from collections.abc import MutableSequence @@ -73,6 +73,7 @@ def __init__( self: Self, sourceMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ], workingMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ], + transferredAttributeNames: set[ str ], speHandler: bool = False, ) -> None: """Map the properties of the source mesh to the working mesh.""" @@ -84,7 +85,7 @@ def __init__( self.m_cellMap: npt.NDArray[ np.int64 ] = np.empty( 0 ).astype( int ) # Transfer Attribute name - self.m_transferredAttributeNames: set[ str ] = set() + self.transferredAttributeNames: set[ str ] = transferredAttributeNames # Logger. self.logger: Logger @@ -94,6 +95,7 @@ def __init__( 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. @@ -110,19 +112,6 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: ) - def SetTransferAttributeNames( self: Self, transferredAttributeNames: set[ str ] ) -> None: - """Setter for transferredAttributeName. - - Args: - transferredAttributeNames (set[str]): set of names of the - attributes to transfer. - - """ - self.m_transferredAttributeNames.clear() - for name in transferredAttributeNames: - self.m_transferredAttributeNames.add( name ) - - def GetCellMap( self: Self ) -> npt.NDArray[ np.int64 ]: """Getter of cell map.""" return self.m_cellMap @@ -136,23 +125,28 @@ def applyFilter( self: Self ) -> bool: """ self.logger.info( f"Apply filter { self.logger.name }." ) - if isinstance( self.sourceMesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): - for attributeName in self.m_transferredAttributeNames: + sourceDataSet: vtkUnstructuredGrid = vtkUnstructuredGrid() + if isinstance( self.sourceMesh, vtkDataSet ): + sourceDataSet.ShallowCopy( self.sourceMesh ) + elif isinstance( self.sourceMesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): + sourceMultiBlockDataSet: vtkMultiBlockDataSet = vtkMultiBlockDataSet() + sourceMultiBlockDataSet.ShallowCopy( self.sourceMesh ) + for attributeName in self.transferredAttributeNames: if not isAttributeGlobal( self.sourceMesh, attributeName, False ): - fillPartialAttributes( self.sourceMesh, attributeName, logger=self.logger ) - self.sourceMesh = mergeBlocks( self.sourceMesh ) - elif not isinstance( self.sourceMesh, vtkDataSet ): + fillPartialAttributes( sourceMultiBlockDataSet, attributeName, logger=self.logger ) + sourceDataSet.ShallowCopy( mergeBlocks( sourceMultiBlockDataSet ) ) + else: self.logger.error( "The source mesh data type is not vtkDataSet nor vtkMultiBlockDataSet." ) self.logger.error( f"The filter { self.logger.name } failed." ) return False if isinstance( self.workingMesh, vtkDataSet ): - if not self._transferAttributes( self.workingMesh ): + if not self._transferAttributes( sourceDataSet, self.workingMesh ): self.logger.warning( "Source mesh and working mesh do not have any corresponding cells." ) self.logger.warning( f"The filter { self.logger.name } has not been needed." ) return False elif isinstance( self.workingMesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): - if not self._transferAttributesMultiBlock(): + if not self._transferAttributesMultiBlock( sourceDataSet ): return False else: self.logger.error( "The working mesh data type is not vtkDataSet nor vtkMultiBlockDataSet." ) @@ -164,22 +158,30 @@ def applyFilter( self: Self ) -> bool: return True - def _computeCellMapping( self: Self, workingMesh ) -> bool: + def _computeCellMapping( + self: Self, + sourceMesh: vtkUnstructuredGrid, + workingMesh: vtkUnstructuredGrid, + ) -> bool: """Create the cell map from the source mesh to the working mesh cell indexes. For each cell index of the working mesh, stores the index of the cell in the source mesh. + Args: + sourceMesh (vtkUnstructuredGrid): The mesh with the properties to transfer. + workingMesh (vtkUnstructuredGrid): The mesh where to copy the properties. + Returns: bool: True if the map was computed. """ self.m_cellMap = np.full( workingMesh.GetNumberOfCells(), -1 ).astype( int ) cellLocator: vtkCellLocator = vtkCellLocator() - cellLocator.SetDataSet( self.sourceMesh ) + cellLocator.SetDataSet( sourceMesh ) cellLocator.BuildLocator() - cellCenters: vtkDataArray = computeCellCenterCoordinates( self.sourceMesh ) + cellCenters: vtkDataArray = computeCellCenterCoordinates( sourceMesh ) for i in range( workingMesh.GetNumberOfCells() ): cellCoords: MutableSequence[ float ] = [ 0.0, 0.0, 0.0 ] cellCenters.GetTuple( i, cellCoords ) @@ -187,20 +189,27 @@ def _computeCellMapping( self: Self, workingMesh ) -> bool: self.m_cellMap[ i ] = cellIndex return True - def _transferAttributes( self: Self, workingMesh: vtkUnstructuredGrid ) -> bool: + def _transferAttributes( + self: Self, + sourceMesh: vtkUnstructuredGrid, + workingMesh: vtkUnstructuredGrid, + ) -> bool: """Transfer attributes from the source mesh to the working meshes using cell mapping. + Args: + sourceMesh (vtkUnstructuredGrid): The mesh with the properties to transfer. + workingMesh (vtkUnstructuredGrid): The mesh where to copy the properties. + Returns: bool: True if transfer successfully ended. - """ # create cell map - self._computeCellMapping( workingMesh ) + self._computeCellMapping( sourceMesh, workingMesh ) # transfer attributes if at least one corresponding cell if np.any( self.m_cellMap > -1 ): - for attributeName in self.m_transferredAttributeNames: - array: vtkDataArray = getVtkArrayInObject( self.sourceMesh, attributeName, False ) + for attributeName in self.transferredAttributeNames: + array: vtkDataArray = getVtkArrayInObject( sourceMesh, attributeName, False ) dataType = array.GetDataType() nbComponents: int = array.GetNumberOfComponents() @@ -237,18 +246,20 @@ def _transferAttributes( self: Self, workingMesh: vtkUnstructuredGrid ) -> bool: return False - def _transferAttributesMultiBlock( self: Self ) -> bool: - """Transfer attributes from a vtkUnstructuredGrid to a multiblock. + def _transferAttributesMultiBlock( self: Self, sourceMesh: vtkUnstructuredGrid ) -> bool: + """Transfer attributes from a source vtkUnstructuredGrid to a working multiblock. - Returns: - bool: True if attributes were successfully transferred. + Args: + sourceMesh (vtkUnstructuredGrid): The source mesh with the properties to transfer. + Returns: + boolean (bool): True if attributes were successfully transferred. """ usedCheck: bool = False nbBlocks: int = self.workingMesh.GetNumberOfBlocks() for idBlock in range( nbBlocks ): workingBlock: vtkUnstructuredGrid = vtkUnstructuredGrid.SafeDownCast( self.workingMesh.GetBlock( idBlock ) ) - if not self._transferAttributes( workingBlock ): + if not self._transferAttributes( sourceMesh, workingBlock ): self.logger.warning( f"Source mesh and the working mesh block number { idBlock } do not have any corresponding cells." ) else: usedCheck = True @@ -261,5 +272,6 @@ def _transferAttributesMultiBlock( self: Self ) -> bool: def _logOutputMessage( self: Self ) -> None: + """Create and log result messages of the filter.""" self.logger.info( f"The filter { self.logger.name } succeed." ) diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index 6018feeaf..0beaeb413 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -86,6 +86,7 @@ def a02GetAttributeToTransfer( self: Self ) -> vtkDataArraySelection: Returns: vtkDataArraySelection: selected attribute names. """ + self.Modified() return self.m_attributes def RequestInformation( @@ -173,14 +174,12 @@ def RequestData( outData.ShallowCopy( workingMesh ) attributeNames: set[ str ] = set( getArrayChoices( self.a02GetAttributeToTransfer() ) ) - - filter: AttributeMappingFromCellCoords = AttributeMappingFromCellCoords( sourceMesh, outData, True ) + + filter: AttributeMappingFromCellCoords = AttributeMappingFromCellCoords( sourceMesh, outData, attributeNames, True ) if not filter.logger.hasHandlers(): filter.setLoggerHandler( VTKHandler() ) - filter.SetTransferAttributeNames( attributeNames ) filter.applyFilter() - self.m_firstUse = True return 1 From 8a92269206ce8ceae6e255c4ca567cfe7bc65b12 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 1 Sep 2025 15:49:31 +0200 Subject: [PATCH 05/45] Update the doc and add input checks --- .../AttributeMappingFromCellCoords.py | 98 ++++++++++++------- .../src/geos/pv/plugins/PVAttributeMapping.py | 2 +- 2 files changed, 61 insertions(+), 39 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py b/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py index fec1229dd..d4b1c7681 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py @@ -27,13 +27,16 @@ from geos.mesh.utils.arrayModifiers import fillPartialAttributes from geos.mesh.utils.multiblockModifiers import mergeBlocks from geos.mesh.utils.arrayModifiers import createEmptyAttribute -from geos.mesh.utils.arrayHelpers import ( getVtkArrayInObject, computeCellCenterCoordinates, isAttributeGlobal ) +from geos.mesh.utils.arrayHelpers import ( getAttributeSet, getVtkArrayInObject, computeCellCenterCoordinates, isAttributeGlobal ) __doc__ = """ -AttributeMappingFromCellCoords module is a vtk filter that map two identical mesh (or a mesh is -an extract from the other one) and create an attribute containing shared cell ids. +AttributeMappingFromCellCoords is a vtk filter that transfer attributes from a source mesh to the working mesh for each +cell of the two meshes with the same coordinates. +The filter update the working mesh directly, no copy is created. -Filter input and output types are vtkUnstructuredGrid. +Input and output meshes can be vtkDataSet or vtkMultiBlockDataSet. +The names of the attributes to transfer are give with a set of string. +To use a handler of yours, set the variable 'speHandler' to True and add it using the member function addLoggerHandler. To use the filter: @@ -41,27 +44,26 @@ from filters.AttributeMappingFromCellCoords import AttributeMappingFromCellCoords - # filter inputs - logger :Logger - input :vtkUnstructuredGrid - TransferAttributeName : str + # filter inputs. + sourceMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] + workingMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] + transferredAttributeNames: set[ str ] + # Optional inputs. + speHandler: bool # instantiate the filter - filter :AttributeMappingFromCellCoords = AttributeMappingFromCellCoords() - # set the logger - filter.SetLogger(logger) - # set input data object - filter.SetInputDataObject(input) - # set Attribute to transfer - filter.SetTransferAttributeNames(AttributeName) - # set Attribute to compare - filter.SetIDAttributeName(AttributeName) - # do calculations - filter.Update() - # get output object - output :vtkPolyData = filter.GetOutputDataObject(0) - # get created attribute names - newAttributeNames :set[str] = filter.GetNewAttributeNames() + filter :AttributeMappingFromCellCoords = AttributeMappingFromCellCoords( soucreMesh, + workingMesh, + transferredAttributeNames, + speHandler, + ) + + # Set the handler of yours (only if speHandler is True). + yourHandler: logging.Handler + filter.setLoggerHandler( yourHandler ) + + # Do calculations. + filter.applyFilter() """ loggerTitle: str = "Attribute Mapping" @@ -76,17 +78,22 @@ def __init__( transferredAttributeNames: set[ str ], speHandler: bool = False, ) -> None: - """Map the properties of the source mesh to the working mesh.""" - + """Map the properties of the source mesh to the working mesh. + + Args: + sourceMesh (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The mesh with attributes to transfer. + workingMesh (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The mesh where to copy attributes. + transferredAttributeNames (set[str]): Names of the attributes to transfer. + speHandler (bool, optional): True to use a specific handler, False to use the internal handler. + Defaults to False. + """ self.sourceMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] = sourceMesh self.workingMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] = workingMesh + self.transferredAttributeNames: set[ str ] = transferredAttributeNames # cell map self.m_cellMap: npt.NDArray[ np.int64 ] = np.empty( 0 ).astype( int ) - # Transfer Attribute name - self.transferredAttributeNames: set[ str ] = transferredAttributeNames - # Logger. self.logger: Logger if not speHandler: @@ -118,13 +125,28 @@ def GetCellMap( self: Self ) -> npt.NDArray[ np.int64 ]: def applyFilter( self: Self ) -> bool: - """Map the properties from the source mesh to the working mesh for the common cells. + """Map attributes from the source mesh to the working mesh for the shared cells. Returns: boolean (bool): True if calculation successfully ended, False otherwise. """ self.logger.info( f"Apply filter { self.logger.name }." ) + if len( self.transferredAttributeNames ) == 0: + self.logger.warning( "Please enter at least one attribute to transfer." ) + self.logger.warning( f"The filter { self.logger.name } has not been used." ) + return False + + falseAttributeNames: list[ str ] = [] + attributeNameSource: set[ str ] = getAttributeSet( self.sourceMesh, False ) + for attribute in self.transferredAttributeNames: + if attribute not in attributeNameSource: + falseAttributeNames.append( attribute ) + if len( falseAttributeNames ) > 0: + self.logger.error( f"The attributes { falseAttributeNames } are not in the mesh." ) + self.logger.error( f"The filter { self.logger.name } failed." ) + return False + sourceDataSet: vtkUnstructuredGrid = vtkUnstructuredGrid() if isinstance( self.sourceMesh, vtkDataSet ): sourceDataSet.ShallowCopy( self.sourceMesh ) @@ -142,8 +164,8 @@ def applyFilter( self: Self ) -> bool: if isinstance( self.workingMesh, vtkDataSet ): if not self._transferAttributes( sourceDataSet, self.workingMesh ): - self.logger.warning( "Source mesh and working mesh do not have any corresponding cells." ) - self.logger.warning( f"The filter { self.logger.name } has not been needed." ) + self.logger.warning( "Source mesh and working mesh do not have any shared cell." ) + self.logger.warning( f"The filter { self.logger.name } has not been used." ) return False elif isinstance( self.workingMesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): if not self._transferAttributesMultiBlock( sourceDataSet ): @@ -169,8 +191,8 @@ def _computeCellMapping( in the source mesh. Args: - sourceMesh (vtkUnstructuredGrid): The mesh with the properties to transfer. - workingMesh (vtkUnstructuredGrid): The mesh where to copy the properties. + sourceMesh (vtkUnstructuredGrid): The mesh with attributes to transfer. + workingMesh (vtkUnstructuredGrid): The mesh where to copy attributes. Returns: bool: True if the map was computed. @@ -197,8 +219,8 @@ def _transferAttributes( """Transfer attributes from the source mesh to the working meshes using cell mapping. Args: - sourceMesh (vtkUnstructuredGrid): The mesh with the properties to transfer. - workingMesh (vtkUnstructuredGrid): The mesh where to copy the properties. + sourceMesh (vtkUnstructuredGrid): The mesh with attributes to transfer. + workingMesh (vtkUnstructuredGrid): The mesh where to copy attributes. Returns: bool: True if transfer successfully ended. @@ -250,7 +272,7 @@ def _transferAttributesMultiBlock( self: Self, sourceMesh: vtkUnstructuredGrid ) """Transfer attributes from a source vtkUnstructuredGrid to a working multiblock. Args: - sourceMesh (vtkUnstructuredGrid): The source mesh with the properties to transfer. + sourceMesh (vtkUnstructuredGrid): The source mesh with attributes to transfer. Returns: boolean (bool): True if attributes were successfully transferred. @@ -260,14 +282,14 @@ def _transferAttributesMultiBlock( self: Self, sourceMesh: vtkUnstructuredGrid ) for idBlock in range( nbBlocks ): workingBlock: vtkUnstructuredGrid = vtkUnstructuredGrid.SafeDownCast( self.workingMesh.GetBlock( idBlock ) ) if not self._transferAttributes( sourceMesh, workingBlock ): - self.logger.warning( f"Source mesh and the working mesh block number { idBlock } do not have any corresponding cells." ) + self.logger.warning( f"Source mesh and the working mesh block number { idBlock } do not have any shared cell." ) else: usedCheck = True if usedCheck: return True - self.logger.warning( f"The filter { self.logger.name } has not been needed." ) + self.logger.warning( f"The filter { self.logger.name } has not been used." ) return False diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index 0beaeb413..580268dd7 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -48,7 +48,7 @@ * Select the working mesh. * Search and Select Attribute Mapping Filter. * Select the source mesh. -* Select the attributes to transfer from the source mesh to the working mesh. +* Select attributes to transfer from the source mesh to the working mesh. * Apply. """ From 2d778d8e5d042ed11a6a04a8cb93949f643d98aa Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 1 Sep 2025 15:51:12 +0200 Subject: [PATCH 06/45] update doc --- .../geos/mesh/processing/AttributeMappingFromCellCoords.py | 2 +- geos-pv/src/geos/pv/plugins/PVAttributeMapping.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py b/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py index d4b1c7681..c90e59731 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py @@ -52,7 +52,7 @@ speHandler: bool # instantiate the filter - filter :AttributeMappingFromCellCoords = AttributeMappingFromCellCoords( soucreMesh, + filter :AttributeMappingFromCellCoords = AttributeMappingFromCellCoords( sourceMesh, workingMesh, transferredAttributeNames, speHandler, diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index 580268dd7..6f66ec058 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -38,9 +38,9 @@ ) __doc__ = """ -Map the attributes from a source mesh to a working mesh. - -Input and output are vtkUnstructuredGrid. +AttributeMapping is a paraview plugin that transfer attributes from a source mesh to the working mesh for each +cell of the two meshes with the same coordinates. +Input and output meshes can be vtkDataSet or vtkMultiBlockDataSet. To use it: From 40fae76fcad6953770f767fb4eb13ab1ed5a89d2 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 2 Sep 2025 08:50:28 +0200 Subject: [PATCH 07/45] Update the name of the filter --- ...pingFromCellCoords.py => AttributeMapping.py} | 16 ++++++++-------- .../src/geos/pv/plugins/PVAttributeMapping.py | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) rename geos-mesh/src/geos/mesh/processing/{AttributeMappingFromCellCoords.py => AttributeMapping.py} (94%) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py similarity index 94% rename from geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py rename to geos-mesh/src/geos/mesh/processing/AttributeMapping.py index c90e59731..0921cc344 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMappingFromCellCoords.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py @@ -30,7 +30,7 @@ from geos.mesh.utils.arrayHelpers import ( getAttributeSet, getVtkArrayInObject, computeCellCenterCoordinates, isAttributeGlobal ) __doc__ = """ -AttributeMappingFromCellCoords is a vtk filter that transfer attributes from a source mesh to the working mesh for each +AttributeMapping is a vtk filter that transfer attributes from a source mesh to the working mesh for each cell of the two meshes with the same coordinates. The filter update the working mesh directly, no copy is created. @@ -42,7 +42,7 @@ .. code-block:: python - from filters.AttributeMappingFromCellCoords import AttributeMappingFromCellCoords + from filters.AttributeMapping import AttributeMapping # filter inputs. sourceMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] @@ -52,10 +52,10 @@ speHandler: bool # instantiate the filter - filter :AttributeMappingFromCellCoords = AttributeMappingFromCellCoords( sourceMesh, - workingMesh, - transferredAttributeNames, - speHandler, + filter :AttributeMapping = AttributeMapping( sourceMesh, + workingMesh, + transferredAttributeNames, + speHandler, ) # Set the handler of yours (only if speHandler is True). @@ -69,7 +69,7 @@ loggerTitle: str = "Attribute Mapping" -class AttributeMappingFromCellCoords: +class AttributeMapping: def __init__( self: Self, @@ -78,7 +78,7 @@ def __init__( transferredAttributeNames: set[ str ], speHandler: bool = False, ) -> None: - """Map the properties of the source mesh to the working mesh. + """Map attributes of the source mesh to the working mesh. Args: sourceMesh (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The mesh with attributes to transfer. diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index 6f66ec058..5c06d3d36 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -14,7 +14,7 @@ from geos.pv.utils.config import update_paths update_paths() -from geos.mesh.processing.AttributeMappingFromCellCoords import AttributeMappingFromCellCoords +from geos.mesh.processing.AttributeMapping import AttributeMapping from geos.mesh.utils.arrayHelpers import getAttributeSet from geos.pv.utils.checkboxFunction import createModifiedCallback @@ -69,7 +69,7 @@ class PVAttributeMapping( VTKPythonAlgorithmBase ): def __init__( self: Self ) -> None: - """Map the properties of the source mesh to the working mesh.""" + """Map attributes of the source mesh to the working mesh.""" super().__init__( nInputPorts=2, nOutputPorts=1, inputType="vtkObject", outputType="vtkObject" ) # boolean to check if first use of the filter for attribute list initialization @@ -175,7 +175,7 @@ def RequestData( attributeNames: set[ str ] = set( getArrayChoices( self.a02GetAttributeToTransfer() ) ) - filter: AttributeMappingFromCellCoords = AttributeMappingFromCellCoords( sourceMesh, outData, attributeNames, True ) + filter: AttributeMapping = AttributeMapping( sourceMesh, outData, attributeNames, True ) if not filter.logger.hasHandlers(): filter.setLoggerHandler( VTKHandler() ) From 8ba8e893c7e04ede9879b15a428911afe495bf1c Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 2 Sep 2025 13:10:30 +0200 Subject: [PATCH 08/45] fix the function computeCellMapping, bounds are used instead of center --- .../geos/mesh/processing/AttributeMapping.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py index 0921cc344..e790b358d 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py @@ -16,6 +16,7 @@ ) from vtkmodules.vtkCommonCore import vtkDataArray from vtkmodules.vtkCommonDataModel import ( + vtkCell, vtkCellData, vtkCellLocator, vtkDataSet, @@ -92,7 +93,7 @@ def __init__( self.transferredAttributeNames: set[ str ] = transferredAttributeNames # cell map - self.m_cellMap: npt.NDArray[ np.int64 ] = np.empty( 0 ).astype( int ) + self.m_cellMap = np.full( workingMesh.GetNumberOfCells(), -1 ).astype( int ) # Logger. self.logger: Logger @@ -199,16 +200,18 @@ def _computeCellMapping( """ self.m_cellMap = np.full( workingMesh.GetNumberOfCells(), -1 ).astype( int ) - cellLocator: vtkCellLocator = vtkCellLocator() - cellLocator.SetDataSet( sourceMesh ) - cellLocator.BuildLocator() - - cellCenters: vtkDataArray = computeCellCenterCoordinates( sourceMesh ) - for i in range( workingMesh.GetNumberOfCells() ): - cellCoords: MutableSequence[ float ] = [ 0.0, 0.0, 0.0 ] - cellCenters.GetTuple( i, cellCoords ) - cellIndex: int = cellLocator.FindCell( cellCoords ) - self.m_cellMap[ i ] = cellIndex + for idCellWorking in range( workingMesh.GetNumberOfCells() ): + workingCell: vtkCell = workingMesh.GetCell( idCellWorking ) + boundsWorkingCell: list[ float ] = workingCell.GetBounds() + idCellSource: int = 0 + cellFund: bool = False + while idCellSource < sourceMesh.GetNumberOfCells() and not cellFund: + sourceCell: vtkCell = sourceMesh.GetCell( idCellSource ) + boundsSourceCell: list[ float ] = sourceCell.GetBounds() + if boundsSourceCell == boundsWorkingCell: + self.m_cellMap[ idCellWorking ] = idCellSource + cellFund = True + idCellSource += 1 return True def _transferAttributes( From 7f6a8a20eca755b0200a880bb1b73e095d86ee18 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 2 Sep 2025 19:05:36 +0200 Subject: [PATCH 09/45] adapte the plugin to deals with global attributes only --- .../src/geos/pv/plugins/PVAttributeMapping.py | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index 5c06d3d36..09dad9058 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -15,7 +15,7 @@ update_paths() from geos.mesh.processing.AttributeMapping import AttributeMapping -from geos.mesh.utils.arrayHelpers import getAttributeSet +from geos.mesh.utils.arrayHelpers import ( getAttributeSet, isAttributeGlobal ) from geos.pv.utils.checkboxFunction import createModifiedCallback from geos.pv.utils.paraviewTreatments import getArrayChoices @@ -38,17 +38,17 @@ ) __doc__ = """ -AttributeMapping is a paraview plugin that transfer attributes from a source mesh to the working mesh for each -cell of the two meshes with the same coordinates. +AttributeMapping is a paraview plugin that transfer global attributes from a source mesh to the other mesh for each +cell of the two meshes with the same bounds coordinates. Input and output meshes can be vtkDataSet or vtkMultiBlockDataSet. To use it: * Load the module in Paraview: Tools>Manage Plugins...>Load new>PVAttributeMapping. -* Select the working mesh. +* Select the mesh to transfer the global attributes (meshTo). * Search and Select Attribute Mapping Filter. -* Select the source mesh. -* Select attributes to transfer from the source mesh to the working mesh. +* Select the source mesh with global attributes to transfer (meshFrom). +* Select global attributes to transfer from the source mesh (meshFrom) to the other mesh (meshTo). * Apply. """ @@ -56,12 +56,12 @@ @smproxy.filter( name="PVAttributeMapping", label="Attribute Mapping" ) @smhint.xml( '' ) -@smproperty.input( name="Source", port_index=1, label="Source mesh" ) +@smproperty.input( name="meshFrom", port_index=1, label="Mesh From" ) @smdomain.datatype( dataTypes=[ "vtkDataSet", "vtkMultiBlockDataSet" ], composite_data_supported=True, ) -@smproperty.input( name="Working", port_index=0, label="Working mesh" ) +@smproperty.input( name="meshTo", port_index=0, label="Mesh To" ) @smdomain.datatype( dataTypes=[ "vtkDataSet", "vtkMultiBlockDataSet" ], composite_data_supported=True, @@ -69,7 +69,7 @@ class PVAttributeMapping( VTKPythonAlgorithmBase ): def __init__( self: Self ) -> None: - """Map attributes of the source mesh to the working mesh.""" + """Map attributes of the source mesh (meshFrom) to the other mesh (meshTo).""" super().__init__( nInputPorts=2, nOutputPorts=1, inputType="vtkObject", outputType="vtkObject" ) # boolean to check if first use of the filter for attribute list initialization @@ -113,9 +113,11 @@ def RequestInformation( # update vtkDAS attributeNames: set[ str ] = getAttributeSet( inData, False ) + for attributeName in attributeNames: - if not self.m_attributes.ArrayExists( attributeName ): - self.m_attributes.AddArray( attributeName, False ) + if isinstance( inData, vtkMultiBlockDataSet ) and isAttributeGlobal( inData, attributeName, False ) or isinstance( inData, vtkDataSet ): + if not self.m_attributes.ArrayExists( attributeName ): + self.m_attributes.AddArray( attributeName, False ) self.m_firstUse = False return 1 @@ -160,22 +162,22 @@ def RequestData( Returns: int: 1 if calculation successfully ended, 0 otherwise. """ - workingMesh: Union[ vtkDataSet, vtkMultiBlockDataSet, + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCompositeDataSet ] = self.GetInputData( inInfoVec, 0, 0 ) - sourceMesh: Union[ vtkDataSet, vtkMultiBlockDataSet, + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCompositeDataSet ] = self.GetInputData( inInfoVec, 1, 0 ) outData: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCompositeDataSet ] = self.GetOutputData( outInfoVec, 0 ) - assert workingMesh is not None, "Input working mesh is null." - assert sourceMesh is not None, "Input source mesh is null." + assert meshTo is not None, "Input mesh (meshTo) to transfer attributed is null." + assert meshFrom is not None, "Input mesh (meshFrom) with attributes to transfer is null." assert outData is not None, "Output pipeline is null." - outData.ShallowCopy( workingMesh ) + outData.ShallowCopy( meshTo ) attributeNames: set[ str ] = set( getArrayChoices( self.a02GetAttributeToTransfer() ) ) - filter: AttributeMapping = AttributeMapping( sourceMesh, outData, attributeNames, True ) + filter: AttributeMapping = AttributeMapping( meshFrom, outData, attributeNames, True ) if not filter.logger.hasHandlers(): filter.setLoggerHandler( VTKHandler() ) From 975e86c33b3b2c9a2aca85f2c1ad515ad0cf66d0 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 2 Sep 2025 19:08:21 +0200 Subject: [PATCH 10/45] Add the function to get the vtk type of an attribute on a mesh --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index 98388fd59..bef1e3d68 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -368,6 +368,24 @@ def getArrayInObject( object: vtkDataSet, attributeName: str, onPoints: bool ) - return npArray +def getVtkDataTypeInObject( object: Union[ vtkDataSet, vtkMultiBlockDataSet ], attributeName: str, + onPoints: bool ) -> int: + """Return VTK type of requested array from input mesh. + + Args: + object (Union[vtkDataSet, vtkMultiBlockDataSet]): Input object. + attributeName (str): Name of the attribute. + onPoints (bool): True if attributes are on points, False if they are on cells. + + Returns: + int: The type of the vtk array corresponding to input attribute name. + """ + if isinstance( object, vtkDataSet ): + return getVtkArrayTypeInObject( object, attributeName, onPoints ) + else: + return getVtkArrayTypeInMultiBlock( object, attributeName, onPoints ) + + def getVtkArrayTypeInObject( object: vtkDataSet, attributeName: str, onPoints: bool ) -> int: """Return VTK type of requested array from dataset input. From 1d38c90c2d3fb32637450f402ed02dd507155565 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 2 Sep 2025 19:09:51 +0200 Subject: [PATCH 11/45] Change the method, meshes are parsed instead of merged --- .../geos/mesh/processing/AttributeMapping.py | 347 ++++++++++-------- 1 file changed, 192 insertions(+), 155 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py index e790b358d..63f21c523 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py @@ -2,38 +2,30 @@ # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. # SPDX-FileContributor: Raphaël Vinour, Martin Lemay, Romain Baville # ruff: noqa: E402 # disable Module level import not at top of file - -from collections.abc import MutableSequence - import numpy as np import numpy.typing as npt from geos.utils.Logger import logging, Logger, getLogger from typing_extensions import Self, Union, Any +import vtkmodules.util.numpy_support as vnp from vtk import ( # type: ignore[import-untyped] VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT, VTK_UNSIGNED_LONG_LONG, VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE, VTK_FLOAT, VTK_DOUBLE, ) -from vtkmodules.vtkCommonCore import vtkDataArray from vtkmodules.vtkCommonDataModel import ( vtkCell, - vtkCellData, - vtkCellLocator, vtkDataSet, vtkMultiBlockDataSet, - vtkUnstructuredGrid, vtkCompositeDataSet, ) -from geos.mesh.utils.arrayModifiers import fillPartialAttributes -from geos.mesh.utils.multiblockModifiers import mergeBlocks -from geos.mesh.utils.arrayModifiers import createEmptyAttribute -from geos.mesh.utils.arrayHelpers import ( getAttributeSet, getVtkArrayInObject, computeCellCenterCoordinates, isAttributeGlobal ) +from geos.mesh.utils.arrayModifiers import createAttribute +from geos.mesh.utils.arrayHelpers import ( getAttributeSet, getComponentNames, getVtkDataTypeInObject, isAttributeGlobal ) __doc__ = """ -AttributeMapping is a vtk filter that transfer attributes from a source mesh to the working mesh for each -cell of the two meshes with the same coordinates. -The filter update the working mesh directly, no copy is created. +AttributeMapping is a vtk filter that transfer global attributes from a source mesh (meshFrom) to another (meshTo) for each +cell of the two meshes with the same bounds coordinates. +The filter update the mesh where attributes are transferred directly, no copy is created. Input and output meshes can be vtkDataSet or vtkMultiBlockDataSet. The names of the attributes to transfer are give with a set of string. @@ -46,16 +38,16 @@ from filters.AttributeMapping import AttributeMapping # filter inputs. - sourceMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] - workingMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] - transferredAttributeNames: set[ str ] + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] + attributeNames: set[ str ] # Optional inputs. speHandler: bool # instantiate the filter - filter :AttributeMapping = AttributeMapping( sourceMesh, - workingMesh, - transferredAttributeNames, + filter :AttributeMapping = AttributeMapping( meshFrom, + meshTo, + attributeNames, speHandler, ) @@ -74,26 +66,27 @@ class AttributeMapping: def __init__( self: Self, - sourceMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ], - workingMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ], - transferredAttributeNames: set[ str ], + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], + attributeNames: set[ str ], speHandler: bool = False, ) -> None: - """Map attributes of the source mesh to the working mesh. + """Map attributes of the source mesh (meshFrom) to the other mesh (meshTo). Args: - sourceMesh (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The mesh with attributes to transfer. - workingMesh (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The mesh where to copy attributes. - transferredAttributeNames (set[str]): Names of the attributes to transfer. + meshFrom (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The mesh with attributes to transfer. + meshTo (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The mesh where to copy attributes. + attributeNames (set[str]): Names of the attributes to transfer. speHandler (bool, optional): True to use a specific handler, False to use the internal handler. Defaults to False. """ - self.sourceMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] = sourceMesh - self.workingMesh: Union[ vtkDataSet, vtkMultiBlockDataSet ] = workingMesh - self.transferredAttributeNames: set[ str ] = transferredAttributeNames + self.meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] = meshFrom + self.meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] = meshTo + self.attributeNames: set[ str ] = attributeNames # cell map - self.m_cellMap = np.full( workingMesh.GetNumberOfCells(), -1 ).astype( int ) + self.dictCellMap: dict[ int, list[ int ] ] = {} + self.sharedCell: bool = False # Logger. self.logger: Logger @@ -122,70 +115,146 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: def GetCellMap( self: Self ) -> npt.NDArray[ np.int64 ]: """Getter of cell map.""" - return self.m_cellMap + return self.dictCellMap def applyFilter( self: Self ) -> bool: - """Map attributes from the source mesh to the working mesh for the shared cells. + """Map attributes from the source mesh (meshFrom) to the other (meshTo) for the shared cells. Returns: boolean (bool): True if calculation successfully ended, False otherwise. """ self.logger.info( f"Apply filter { self.logger.name }." ) - if len( self.transferredAttributeNames ) == 0: + if len( self.attributeNames ) == 0: self.logger.warning( "Please enter at least one attribute to transfer." ) self.logger.warning( f"The filter { self.logger.name } has not been used." ) return False - falseAttributeNames: list[ str ] = [] - attributeNameSource: set[ str ] = getAttributeSet( self.sourceMesh, False ) - for attribute in self.transferredAttributeNames: - if attribute not in attributeNameSource: - falseAttributeNames.append( attribute ) - if len( falseAttributeNames ) > 0: - self.logger.error( f"The attributes { falseAttributeNames } are not in the mesh." ) - self.logger.error( f"The filter { self.logger.name } failed." ) - return False - - sourceDataSet: vtkUnstructuredGrid = vtkUnstructuredGrid() - if isinstance( self.sourceMesh, vtkDataSet ): - sourceDataSet.ShallowCopy( self.sourceMesh ) - elif isinstance( self.sourceMesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): - sourceMultiBlockDataSet: vtkMultiBlockDataSet = vtkMultiBlockDataSet() - sourceMultiBlockDataSet.ShallowCopy( self.sourceMesh ) - for attributeName in self.transferredAttributeNames: - if not isAttributeGlobal( self.sourceMesh, attributeName, False ): - fillPartialAttributes( sourceMultiBlockDataSet, attributeName, logger=self.logger ) - sourceDataSet.ShallowCopy( mergeBlocks( sourceMultiBlockDataSet ) ) - else: - self.logger.error( "The source mesh data type is not vtkDataSet nor vtkMultiBlockDataSet." ) + wrongAttributeNames: list[ str ] = [] + attributesAlreadyInMeshTo: list = [] + attributesInMeshFrom: set[ str ] = getAttributeSet( self.meshFrom, False ) + attributesInMeshTo: set[ str ] = getAttributeSet( self.meshTo, False ) + for attributeName in self.attributeNames: + if attributeName not in attributesInMeshFrom: + wrongAttributeNames.append( attributeName ) + + if attributeName in attributesInMeshTo: + attributesAlreadyInMeshTo.append( attributeName ) + + if len( wrongAttributeNames ) > 0: + self.logger.error( f"The attributes { wrongAttributeNames } are not in the mesh from where to transfer attributes." ) self.logger.error( f"The filter { self.logger.name } failed." ) return False - if isinstance( self.workingMesh, vtkDataSet ): - if not self._transferAttributes( sourceDataSet, self.workingMesh ): - self.logger.warning( "Source mesh and working mesh do not have any shared cell." ) - self.logger.warning( f"The filter { self.logger.name } has not been used." ) - return False - elif isinstance( self.workingMesh, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): - if not self._transferAttributesMultiBlock( sourceDataSet ): - return False - else: - self.logger.error( "The working mesh data type is not vtkDataSet nor vtkMultiBlockDataSet." ) + if len( attributesAlreadyInMeshTo ) > 0: + self.logger.error( f"The attributes { attributesAlreadyInMeshTo } are already in the mesh where attributes must be transferred." ) self.logger.error( f"The filter { self.logger.name } failed." ) return False + + if isinstance( self.meshFrom, vtkMultiBlockDataSet ): + partialAttributes: list[ str ] = [] + for attributeName in self.attributeNames: + if not isAttributeGlobal( self.meshFrom, attributeName, False ): + partialAttributes.append( attributeName ) + + if len( partialAttributes ) > 0: + self.logger.error( f"All attributes to transfer must be global, { partialAttributes } are partials." ) + self.logger.error( f"The filter { self.logger.name } failed." ) + + self._cellMapping( self.meshFrom, self.meshTo ) + if not self.sharedCell: + self.logger.warning( "The two meshes do not have any shared cell." ) + self.logger.warning( f"The filter { self.logger.name } has not been used." ) + return False + + for attributeName in self.attributeNames: + componentNames: tuple[ str, ... ] = getComponentNames( self.meshFrom, attributeName, False ) + + vtkDataType: int = getVtkDataTypeInObject( self.meshFrom, attributeName, False ) + defaultValue: Any + if vtkDataType in ( VTK_FLOAT, VTK_DOUBLE ): + defaultValue = np.nan + elif vtkDataType in ( VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE ): + defaultValue = -1 + elif vtkDataType in ( VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT, + VTK_UNSIGNED_LONG_LONG ): + defaultValue = 0 + + if isinstance( self.meshTo, vtkDataSet ): + if not self._transferAttribute( self.meshFrom, self.meshTo, attributeName, componentNames, vtkDataType, defaultValue ): + return False + elif isinstance( self.meshTo, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): + nbBlocksTo: int = self.meshTo.GetNumberOfBlocks() + for idBlockTo in range( nbBlocksTo ): + blockTo: vtkDataSet = vtkDataSet.SafeDownCast( self.meshTo.GetBlock( idBlockTo ) ) + if not self._transferAttribute( self.meshFrom, blockTo, attributeName, componentNames, vtkDataType, defaultValue, idBlockTo ): + return False + else: + self.logger.error( "The working mesh data type is not vtkDataSet nor vtkMultiBlockDataSet." ) + self.logger.error( f"The filter { self.logger.name } failed." ) + return False # Log the output message. self._logOutputMessage() return True + + def _cellMapping( + self: Self, + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], + ) -> None: + if isinstance( meshTo, vtkDataSet ): + self.dictCellMap[ 0 ] = np.full( ( meshTo.GetNumberOfCells(), 2 ), -1, int ) + self._cellMappingToDataSet( meshFrom, meshTo ) + elif isinstance( meshTo, vtkMultiBlockDataSet ): + self._cellMappingToMultiBlockDataSet( meshFrom, meshTo ) + + + def _cellMappingToMultiBlockDataSet( + self: Self, + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], + multiBlockDataSetTo: vtkMultiBlockDataSet, + ) -> None: + nbBlocksTo: int = multiBlockDataSetTo.GetNumberOfBlocks() + for idBlockTo in range( nbBlocksTo ): + blockTo: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTo.GetBlock( idBlockTo ) ) + self.dictCellMap[ idBlockTo ] = np.full( ( blockTo.GetNumberOfCells(), 2 ), -1, int ) + self._cellMappingToDataSet( meshFrom, blockTo, idBlockTo=idBlockTo ) + + + def _cellMappingToDataSet( + self: Self, + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], + dataSetTo: vtkDataSet, + idBlockTo: int = 0, + ) -> None: + if isinstance( meshFrom, vtkDataSet ): + self._cellMappingFromDataSetToDataSet( meshFrom, dataSetTo, idBlockTo=idBlockTo ) + elif isinstance( meshFrom, vtkMultiBlockDataSet ): + self._cellMappingFromMultiBlockDataSetToDataSet( meshFrom, dataSetTo, idBlockTo=idBlockTo ) + + + def _cellMappingFromMultiBlockDataSetToDataSet( + self: Self, + multiBlockDataSetFrom: vtkMultiBlockDataSet, + dataSetTo: vtkDataSet, + idBlockTo: int = 0, + ) -> None: + nbBlocksFrom: int = multiBlockDataSetFrom.GetNumberOfBlocks() + for idBlockFrom in range( nbBlocksFrom ): + blockFrom: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetFrom.GetBlock( idBlockFrom ) ) + self._cellMappingFromDataSetToDataSet( blockFrom, dataSetTo, idBlockFrom=idBlockFrom, idBlockTo=idBlockTo ) + - def _computeCellMapping( + def _cellMappingFromDataSetToDataSet( self: Self, - sourceMesh: vtkUnstructuredGrid, - workingMesh: vtkUnstructuredGrid, - ) -> bool: + dataSetFrom: vtkDataSet, + dataSetTo: vtkDataSet, + idBlockFrom: int = 0, + idBlockTo: int = 0, + ) -> None: """Create the cell map from the source mesh to the working mesh cell indexes. For each cell index of the working mesh, stores the index of the cell @@ -194,31 +263,39 @@ def _computeCellMapping( Args: sourceMesh (vtkUnstructuredGrid): The mesh with attributes to transfer. workingMesh (vtkUnstructuredGrid): The mesh where to copy attributes. - - Returns: - bool: True if the map was computed. - """ - self.m_cellMap = np.full( workingMesh.GetNumberOfCells(), -1 ).astype( int ) - for idCellWorking in range( workingMesh.GetNumberOfCells() ): - workingCell: vtkCell = workingMesh.GetCell( idCellWorking ) - boundsWorkingCell: list[ float ] = workingCell.GetBounds() - idCellSource: int = 0 + idCellFromFund: list[ int ] = [] + for idCellTo in range( dataSetTo.GetNumberOfCells() ): + cellTo: vtkCell = dataSetTo.GetCell( idCellTo ) + boundsCellTo: list[ float ] = cellTo.GetBounds() + + idCellFrom: int = 0 cellFund: bool = False - while idCellSource < sourceMesh.GetNumberOfCells() and not cellFund: - sourceCell: vtkCell = sourceMesh.GetCell( idCellSource ) - boundsSourceCell: list[ float ] = sourceCell.GetBounds() - if boundsSourceCell == boundsWorkingCell: - self.m_cellMap[ idCellWorking ] = idCellSource - cellFund = True - idCellSource += 1 - return True + while idCellFrom < dataSetFrom.GetNumberOfCells() and not cellFund: + if idCellFrom not in idCellFromFund: + cellFrom: vtkCell = dataSetFrom.GetCell( idCellFrom ) + boundsCellFrom: list[ float ] = cellFrom.GetBounds() + if boundsCellFrom == boundsCellTo: + self.dictCellMap[ idBlockTo ][ idCellTo ] = [ idBlockFrom, idCellFrom ] + cellFund = True + idCellFromFund.append( idCellFrom ) + + idCellFrom += 1 + + if len( idCellFromFund ) > 0: + self.sharedCell = True + - def _transferAttributes( + def _transferAttribute( self: Self, - sourceMesh: vtkUnstructuredGrid, - workingMesh: vtkUnstructuredGrid, - ) -> bool: + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], + dataSetTo: vtkDataSet, + attributeName: str, + componentNames: tuple[ str, ... ], + vtkDataType: int, + defaultValue: Any, + idBlockTo: int = 0, + ) -> None: """Transfer attributes from the source mesh to the working meshes using cell mapping. Args: @@ -228,72 +305,32 @@ def _transferAttributes( Returns: bool: True if transfer successfully ended. """ - # create cell map - self._computeCellMapping( sourceMesh, workingMesh ) - - # transfer attributes if at least one corresponding cell - if np.any( self.m_cellMap > -1 ): - for attributeName in self.transferredAttributeNames: - array: vtkDataArray = getVtkArrayInObject( sourceMesh, attributeName, False ) - - dataType = array.GetDataType() - nbComponents: int = array.GetNumberOfComponents() - componentNames: list[ str ] = [] - - defaultValue: Any - if dataType in ( VTK_FLOAT, VTK_DOUBLE ): - defaultValue =[ np.nan for _ in range( nbComponents ) ] - elif dataType in ( VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE ): - defaultValue = [ -1 for _ in range( nbComponents ) ] - elif dataType in ( VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT, - VTK_UNSIGNED_LONG_LONG ): - defaultValue = [ 0 for _ in range( nbComponents ) ] - - if nbComponents > 1: - for i in range( nbComponents ): - componentNames.append( array.GetComponentName( i ) ) - newArray: vtkDataArray = createEmptyAttribute( attributeName, tuple( componentNames ), dataType ) - - for indexWorking in range( workingMesh.GetNumberOfCells() ): - indexSource: int = self.m_cellMap[ indexWorking ] - data: MutableSequence[ float ] = defaultValue - if indexSource > -1: - array.GetTuple( indexSource, data ) - newArray.InsertNextTuple( data ) - - cellData: vtkCellData = workingMesh.GetCellData() - assert cellData is not None, "CellData is undefined." - cellData.AddArray( newArray ) - cellData.Modified() - - return True + nbCellTo: int = dataSetTo.GetNumberOfCells() + nbComponents: int = len( componentNames ) + typeMapping: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap() + valueType: type = typeMapping[ vtkDataType ] + arrayTo: npt.NDArray[ Any ] + if nbComponents > 1: + defaultValue = [ defaultValue ] * nbComponents + arrayTo = np.full( ( nbCellTo, nbComponents ), defaultValue, dtype=valueType ) + else: + arrayTo = np.array( [ defaultValue for _ in range( nbCellTo ) ], dtype=valueType ) - return False - - - def _transferAttributesMultiBlock( self: Self, sourceMesh: vtkUnstructuredGrid ) -> bool: - """Transfer attributes from a source vtkUnstructuredGrid to a working multiblock. - - Args: - sourceMesh (vtkUnstructuredGrid): The source mesh with attributes to transfer. - - Returns: - boolean (bool): True if attributes were successfully transferred. - """ - usedCheck: bool = False - nbBlocks: int = self.workingMesh.GetNumberOfBlocks() - for idBlock in range( nbBlocks ): - workingBlock: vtkUnstructuredGrid = vtkUnstructuredGrid.SafeDownCast( self.workingMesh.GetBlock( idBlock ) ) - if not self._transferAttributes( sourceMesh, workingBlock ): - self.logger.warning( f"Source mesh and the working mesh block number { idBlock } do not have any shared cell." ) - else: - usedCheck = True - - if usedCheck: - return True + for idCellTo in range( nbCellTo ): + value: Any = defaultValue + idCellFrom: int = self.dictCellMap[ idBlockTo ][ idCellTo ][ 1 ] + if idCellFrom != -1: + idBlockFrom = self.dictCellMap[ idBlockTo ][ idCellTo ][ 0 ] + arrayFrom: npt.NDArray[ Any ] + if isinstance( meshFrom, vtkDataSet ): + arrayFrom = vnp.vtk_to_numpy( meshFrom.GetCellData().GetArray( attributeName ) ) + elif isinstance( meshFrom, vtkMultiBlockDataSet ): + blockFrom: vtkDataSet = vtkDataSet.SafeDownCast( meshFrom.GetBlock( idBlockFrom ) ) + arrayFrom = vnp.vtk_to_numpy( blockFrom.GetCellData().GetArray( attributeName ) ) + value = arrayFrom[ idCellFrom ] + arrayTo[ idCellTo ] = value - self.logger.warning( f"The filter { self.logger.name } has not been used." ) - return False + return createAttribute( dataSetTo, arrayTo, attributeName, componentNames, onPoints=False, vtkDataType=vtkDataType, logger=self.logger ) def _logOutputMessage( self: Self ) -> None: From 25b1fc327559aefe856cdb3375b1136fe866494a Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Sep 2025 15:37:30 +0200 Subject: [PATCH 12/45] add functions to compute the cellMapping from the coordinate between two meshes --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 181 +++++++++++++++++- 1 file changed, 180 insertions(+), 1 deletion(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index bef1e3d68..439c7f4d0 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -12,7 +12,7 @@ from vtkmodules.vtkCommonCore import vtkDataArray, vtkPoints from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkFieldData, vtkMultiBlockDataSet, vtkDataSet, vtkCompositeDataSet, vtkDataObject, vtkPointData, vtkCellData, - vtkDataObjectTreeIterator, vtkPolyData ) + vtkDataObjectTreeIterator, vtkPolyData, vtkCell ) from vtkmodules.vtkFiltersCore import vtkCellCenters from geos.mesh.utils.multiblockHelpers import ( getBlockElementIndexesFlatten, getBlockFromFlatIndex ) @@ -20,11 +20,190 @@ ArrayHelpers module contains several utilities methods to get information on arrays in VTK datasets. These methods include: + - array getters, with conversion into numpy array or pandas dataframe - boolean functions to check whether an array is present in the dataset - bounds getter for vtu and multiblock datasets """ +def computeCellMapping( + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], +) -> dict[ int, npt.NDArray ]: + """Compute the cell index mapping dictionary from the mesh meshFrom to the mesh meshTo. + + If meshes are vtkDataSet, 0 is use as block index. + + For each cell (idCellTo) of each block (idBlockTo) of meshTo: + - if a cell (idCellFrom) of a block (idBlockFrom) of meshFrom has the same bounds coordinates, + dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. + - else, dictCellMap[idBlockTo][idCellTo] = [-1, -1]. + + Args: + meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the cell indexes to map. + meshTo (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the reference cell indexes. + + Returns: + dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary where: + - Keys are the block index of the multiBlockDataSetTo. + - Items are arrays of size (nb cell in block, 2) filled with: + + [-1, -1] for cells not shared by the two mesh. + + [idBlockFrom, idCellFrom] for cells shared by the two meshes. + """ + dictCellMap: dict[ int, npt.NDArray ] = {} + if isinstance( meshTo, vtkDataSet ): + UpdateCellMappingToDataSet( meshFrom, meshTo, dictCellMap ) + elif isinstance( meshTo, vtkMultiBlockDataSet ): + UpdateCellMappingToMultiBlockDataSet( meshFrom, meshTo, dictCellMap ) + + return dictCellMap + + +def UpdateCellMappingToMultiBlockDataSet( + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], + multiBlockDataSetTo: vtkMultiBlockDataSet, + dictCellMap: dict[ int, npt.NDArray ], +) -> None: + """Update the cell index mapping dictionary from the mesh meshFrom to the mesh multiBlockDataSetTo. + + If meshFrom is a vtkDataSet, 0 is use as block index. + + Add the mapping for each block (idBlockTo) of the multiBlockDataSetTo: + - Keys are the block index of the multiBlockDataSetTo. + - Items are arrays of size (nb cell in block, 2). + + For each cell (idCellTo) of each block (idBlockTo) of multiBlockDataSetTo: + - if a cell (idCellFrom) of a block (idBlockFrom) of meshFrom has the same bounds coordinates, + dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. + - else, dictCellMap[idBlockTo][idCellTo] = [-1, -1]. + + Args: + meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the cell indexes to map. + multiBlockDataSetTo (vtkMultiBlockDataSet): The mesh with the reference cell indexes. + dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary to update with the mapping of all blocks of the multiBlockDataSetTo. + """ + nbBlocksTo: int = multiBlockDataSetTo.GetNumberOfBlocks() + for idBlockTo in range( nbBlocksTo ): + blockTo: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTo.GetBlock( idBlockTo ) ) + UpdateCellMappingToDataSet( meshFrom, blockTo, dictCellMap, idBlockTo=idBlockTo ) + + +def UpdateCellMappingToDataSet( + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], + dataSetTo: vtkDataSet, + dictCellMap: dict[ int, npt.NDArray ], + idBlockTo: int = 0, +) -> None: + """Update the cell index mapping dictionary from the mesh meshFrom to the mesh dataSetTo. + + If meshFrom is a vtkDataSet, 0 is use as block index. + dataSetTo is considered as block of a vtkMultiblockDataSet, if not, 0 is use as block index. + + Add the mapping for the dataSetTo: + - The keys is the block index of the dataSetTo (idBlockTo). + - The item is an array of size (nb cell in dataSetTo, 2). + + For each cell (idCellTo) of the mesh dataSetTo: + - if a cell (idCellFrom) of the meshFrom or one of its block (idBlockFrom) has the same bounds coordinates, + dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. + - else, dictCellMap[idBlockTo][idCellTo] = [-1, -1]. + + Args: + meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the cell indexes to map. + dataSetTo (vtkDataSet): The mesh with the reference cell indexes. + dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary to update with the mapping for the dataSetTo. + idBlockTo (int, Optional): The block index of the dataSetTo. + Defaults to 0. + """ + dictCellMap[ idBlockTo ] = np.full( ( dataSetTo.GetNumberOfCells(), 2 ), -1, int ) + if isinstance( meshFrom, vtkDataSet ): + UpdateCellMappingFromDataSetToDataSet( meshFrom, dataSetTo, dictCellMap, idBlockTo=idBlockTo ) + elif isinstance( meshFrom, vtkMultiBlockDataSet ): + UpdateCellMappingFromMultiBlockDataSetToDataSet( meshFrom, dataSetTo, dictCellMap, idBlockTo=idBlockTo ) + + +def UpdateCellMappingFromMultiBlockDataSetToDataSet( + multiBlockDataSetFrom: vtkMultiBlockDataSet, + dataSetTo: vtkDataSet, + dictCellMap: dict[ int, npt.NDArray ], + idBlockTo: int = 0, +) -> None: + """Update the cell index mapping dictionary from the mesh multiBlockDataSetFrom to the mesh dataSetTo. + + dataSetTo is considered as block of a vtkMultiblockDataSet, if not, 0 is use as block index. + + For each cell (idCellTo) of the mesh dataSetTo not yet mapped (dictCellMap[idBlockTo][idCellTo] = [-1, -1]), + if a cell (idCellFrom) of a block (idBlockFrom) of multiBlockDataSetFrom has the same bounds coordinates, + the dictCellMap is update to dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. + + Args: + multiBlockDataSetFrom (vtkMultiBlockDataSet): The mesh with the cell indexes to map. + dataSetTo (vtkDataSet): The mesh with the reference cell indexes. + dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary to update with: + - The block index of the dataSetTo as keys. + - An array of size (nb cell in dataSetTo, 2) as items filled with: + + [-1, -1] for cell not yet mapped. + + [idBlockFrom, idCellFrom] for cell already mapped. + idBlockTo (int, Optional): The block index of the dataSetTo. + Defaults to 0. + """ + nbBlocksFrom: int = multiBlockDataSetFrom.GetNumberOfBlocks() + for idBlockFrom in range( nbBlocksFrom ): + blockFrom: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetFrom.GetBlock( idBlockFrom ) ) + UpdateCellMappingFromDataSetToDataSet( blockFrom, dataSetTo, dictCellMap, idBlockFrom=idBlockFrom, idBlockTo=idBlockTo ) + + +def UpdateCellMappingFromDataSetToDataSet( + dataSetFrom: vtkDataSet, + dataSetTo: vtkDataSet, + dictCellMap: dict[ int, npt.NDArray[ np.int64 ] ], + idBlockFrom: int = 0, + idBlockTo: int = 0, +) -> None: + """Update the cell index mapping dictionary from the mesh dataSetFrom to the mesh dataSetTo. + + Meshes are considered as block of vtkMultiblockDataSet, if not, 0 is use as block index. + + For each cell (idCellTo) of the mesh dataSetTo not yet mapped (dictCellMap[idBlockTo][idCellTo] = [-1, -1]), + if a cell (idCellFrom) of the mesh dataSetFrom has the same bounds coordinates, + the dictCellMap is update to dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. + + Args: + dataSetFrom (vtkDataSet): The mesh with the cell indexes to map. + dataSetTo (vtkDataSet): The mesh with the reference cell indexes. + dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary to update with: + - The block index of the dataSetTo as keys. + - An array of size (nb cell in dataSetTo, 2) as items filled with: + + [-1, -1] for cell not yet mapped. + + [idBlockFrom, idCellFrom] for cell already mapped. + idBlockFrom (int, Optional): The block index of the dataSetFrom. + Defaults to 0. + idBlockTo (int, Optional): The block index of the dataSetTo. + Defaults to 0. + """ + idCellFromFund: list[ int ] = [] + nbCellsTo: int = dataSetTo.GetNumberOfCells() + nbCellsFrom: int = dataSetFrom.GetNumberOfCells() + for idCellTo in range( nbCellsTo ): + # Test if the cell is already mapped. + if -1 in dictCellMap[ idBlockTo ][ idCellTo ]: + cellTo: vtkCell = dataSetTo.GetCell( idCellTo ) + boundsCellTo: list[ float ] = cellTo.GetBounds() + + idCellFrom: int = 0 + cellFund: bool = False + while idCellFrom < nbCellsFrom and not cellFund: + # Test if the cell is already mapped. + if idCellFrom not in idCellFromFund: + cellFrom: vtkCell = dataSetFrom.GetCell( idCellFrom ) + boundsCellFrom: list[ float ] = cellFrom.GetBounds() + if boundsCellFrom == boundsCellTo: + dictCellMap[ idBlockTo ][ idCellTo ] = [ idBlockFrom, idCellFrom ] + cellFund = True + idCellFromFund.append( idCellFrom ) + + idCellFrom += 1 + def has_array( mesh: vtkUnstructuredGrid, array_names: list[ str ] ) -> bool: """Checks if input mesh contains at least one of input data arrays. From bebfdcd6b5d778b00b557ee1f5a2efcd1f02ed66 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Sep 2025 16:12:50 +0200 Subject: [PATCH 13/45] clean for ci --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 92 +++++++++---------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index 439c7f4d0..c877e3e61 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -26,36 +26,36 @@ - bounds getter for vtu and multiblock datasets """ + def computeCellMapping( meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], -) -> dict[ int, npt.NDArray ]: - """Compute the cell index mapping dictionary from the mesh meshFrom to the mesh meshTo. +) -> dict[ int, npt.NDArray ]: + """Compute the cell index mapping dictionary from the mesh meshFrom to the mesh meshTo. - If meshes are vtkDataSet, 0 is use as block index. + The cell mapping dictionary keep the mapping cell index where: + - Keys are the block index of the meshTo. + - Items are arrays of size (nb cell in the block, 2). For each cell (idCellTo) of each block (idBlockTo) of meshTo: - - if a cell (idCellFrom) of a block (idBlockFrom) of meshFrom has the same bounds coordinates, - dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. + - if a cell (idCellFrom) of a block (idBlockFrom) of meshFrom has the same bounds coordinates, dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. - else, dictCellMap[idBlockTo][idCellTo] = [-1, -1]. + If meshes are vtkDataSet, 0 is use as block index. + Args: meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the cell indexes to map. meshTo (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the reference cell indexes. Returns: - dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary where: - - Keys are the block index of the multiBlockDataSetTo. - - Items are arrays of size (nb cell in block, 2) filled with: - + [-1, -1] for cells not shared by the two mesh. - + [idBlockFrom, idCellFrom] for cells shared by the two meshes. + dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary. """ dictCellMap: dict[ int, npt.NDArray ] = {} if isinstance( meshTo, vtkDataSet ): UpdateCellMappingToDataSet( meshFrom, meshTo, dictCellMap ) elif isinstance( meshTo, vtkMultiBlockDataSet ): UpdateCellMappingToMultiBlockDataSet( meshFrom, meshTo, dictCellMap ) - + return dictCellMap @@ -64,23 +64,22 @@ def UpdateCellMappingToMultiBlockDataSet( multiBlockDataSetTo: vtkMultiBlockDataSet, dictCellMap: dict[ int, npt.NDArray ], ) -> None: - """Update the cell index mapping dictionary from the mesh meshFrom to the mesh multiBlockDataSetTo. - - If meshFrom is a vtkDataSet, 0 is use as block index. + """Update the cell index mapping dictionary from the mesh meshFrom to the mesh multiBlockDataSetTo. Add the mapping for each block (idBlockTo) of the multiBlockDataSetTo: - Keys are the block index of the multiBlockDataSetTo. - - Items are arrays of size (nb cell in block, 2). + - Items are arrays of size (nb cell in block, 2). For each cell (idCellTo) of each block (idBlockTo) of multiBlockDataSetTo: - - if a cell (idCellFrom) of a block (idBlockFrom) of meshFrom has the same bounds coordinates, - dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. + - if a cell (idCellFrom) of a block (idBlockFrom) of meshFrom has the same bounds coordinates, dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. - else, dictCellMap[idBlockTo][idCellTo] = [-1, -1]. + If meshFrom is a vtkDataSet, 0 is use as block index. + Args: meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the cell indexes to map. multiBlockDataSetTo (vtkMultiBlockDataSet): The mesh with the reference cell indexes. - dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary to update with the mapping of all blocks of the multiBlockDataSetTo. + dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary to update. """ nbBlocksTo: int = multiBlockDataSetTo.GetNumberOfBlocks() for idBlockTo in range( nbBlocksTo ): @@ -94,24 +93,23 @@ def UpdateCellMappingToDataSet( dictCellMap: dict[ int, npt.NDArray ], idBlockTo: int = 0, ) -> None: - """Update the cell index mapping dictionary from the mesh meshFrom to the mesh dataSetTo. - - If meshFrom is a vtkDataSet, 0 is use as block index. - dataSetTo is considered as block of a vtkMultiblockDataSet, if not, 0 is use as block index. + """Update the cell index mapping dictionary from the mesh meshFrom to the mesh dataSetTo. Add the mapping for the dataSetTo: - The keys is the block index of the dataSetTo (idBlockTo). - - The item is an array of size (nb cell in dataSetTo, 2). + - The item is an array of size (nb cell in dataSetTo, 2). For each cell (idCellTo) of the mesh dataSetTo: - - if a cell (idCellFrom) of the meshFrom or one of its block (idBlockFrom) has the same bounds coordinates, - dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. + - if a cell (idCellFrom) of the meshFrom or one of its block (idBlockFrom) has the same bounds coordinates, dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. - else, dictCellMap[idBlockTo][idCellTo] = [-1, -1]. + If meshFrom is a vtkDataSet, 0 is use as block index. + dataSetTo is considered as block of a vtkMultiblockDataSet, if not, 0 is use as block index. + Args: meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the cell indexes to map. dataSetTo (vtkDataSet): The mesh with the reference cell indexes. - dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary to update with the mapping for the dataSetTo. + dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary to update. idBlockTo (int, Optional): The block index of the dataSetTo. Defaults to 0. """ @@ -128,29 +126,31 @@ def UpdateCellMappingFromMultiBlockDataSetToDataSet( dictCellMap: dict[ int, npt.NDArray ], idBlockTo: int = 0, ) -> None: - """Update the cell index mapping dictionary from the mesh multiBlockDataSetFrom to the mesh dataSetTo. - - dataSetTo is considered as block of a vtkMultiblockDataSet, if not, 0 is use as block index. + """Update the cell index mapping dictionary from the mesh multiBlockDataSetFrom to the mesh dataSetTo. For each cell (idCellTo) of the mesh dataSetTo not yet mapped (dictCellMap[idBlockTo][idCellTo] = [-1, -1]), if a cell (idCellFrom) of a block (idBlockFrom) of multiBlockDataSetFrom has the same bounds coordinates, the dictCellMap is update to dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. + dataSetTo is considered as block of a vtkMultiblockDataSet, if not, 0 is use as block index. + Args: multiBlockDataSetFrom (vtkMultiBlockDataSet): The mesh with the cell indexes to map. dataSetTo (vtkDataSet): The mesh with the reference cell indexes. - dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary to update with: - - The block index of the dataSetTo as keys. - - An array of size (nb cell in dataSetTo, 2) as items filled with: - + [-1, -1] for cell not yet mapped. - + [idBlockFrom, idCellFrom] for cell already mapped. + dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary to update with; + The block index of the dataSetTo as keys. + An array of size (nb cell in dataSetTo, 2) as item. idBlockTo (int, Optional): The block index of the dataSetTo. Defaults to 0. """ nbBlocksFrom: int = multiBlockDataSetFrom.GetNumberOfBlocks() for idBlockFrom in range( nbBlocksFrom ): blockFrom: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetFrom.GetBlock( idBlockFrom ) ) - UpdateCellMappingFromDataSetToDataSet( blockFrom, dataSetTo, dictCellMap, idBlockFrom=idBlockFrom, idBlockTo=idBlockTo ) + UpdateCellMappingFromDataSetToDataSet( blockFrom, + dataSetTo, + dictCellMap, + idBlockFrom=idBlockFrom, + idBlockTo=idBlockTo ) def UpdateCellMappingFromDataSetToDataSet( @@ -160,22 +160,20 @@ def UpdateCellMappingFromDataSetToDataSet( idBlockFrom: int = 0, idBlockTo: int = 0, ) -> None: - """Update the cell index mapping dictionary from the mesh dataSetFrom to the mesh dataSetTo. - - Meshes are considered as block of vtkMultiblockDataSet, if not, 0 is use as block index. + """Update the cell index mapping dictionary from the mesh dataSetFrom to the mesh dataSetTo. For each cell (idCellTo) of the mesh dataSetTo not yet mapped (dictCellMap[idBlockTo][idCellTo] = [-1, -1]), if a cell (idCellFrom) of the mesh dataSetFrom has the same bounds coordinates, the dictCellMap is update to dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. + Meshes are considered as block of vtkMultiblockDataSet, if not, 0 is use as block index. + Args: dataSetFrom (vtkDataSet): The mesh with the cell indexes to map. dataSetTo (vtkDataSet): The mesh with the reference cell indexes. - dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary to update with: - - The block index of the dataSetTo as keys. - - An array of size (nb cell in dataSetTo, 2) as items filled with: - + [-1, -1] for cell not yet mapped. - + [idBlockFrom, idCellFrom] for cell already mapped. + dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary to update with; + The block index of the dataSetTo as keys. + An array of size (nb cell in dataSetTo, 2) as item. idBlockFrom (int, Optional): The block index of the dataSetFrom. Defaults to 0. idBlockTo (int, Optional): The block index of the dataSetTo. @@ -188,20 +186,20 @@ def UpdateCellMappingFromDataSetToDataSet( # Test if the cell is already mapped. if -1 in dictCellMap[ idBlockTo ][ idCellTo ]: cellTo: vtkCell = dataSetTo.GetCell( idCellTo ) - boundsCellTo: list[ float ] = cellTo.GetBounds() + boundsCellTo: tuple[ float, ...] = cellTo.GetBounds() idCellFrom: int = 0 cellFund: bool = False while idCellFrom < nbCellsFrom and not cellFund: # Test if the cell is already mapped. - if idCellFrom not in idCellFromFund: + if idCellFrom not in idCellFromFund: cellFrom: vtkCell = dataSetFrom.GetCell( idCellFrom ) - boundsCellFrom: list[ float ] = cellFrom.GetBounds() + boundsCellFrom: tuple[ float, ...] = cellFrom.GetBounds() if boundsCellFrom == boundsCellTo: dictCellMap[ idBlockTo ][ idCellTo ] = [ idBlockFrom, idCellFrom ] cellFund = True idCellFromFund.append( idCellFrom ) - + idCellFrom += 1 From 5bcc52d1308533ca6393b85c8f0dac1d9a3e8348 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Sep 2025 17:11:11 +0200 Subject: [PATCH 14/45] move function to compute the cellMap in arrayHelpers --- .../geos/mesh/processing/AttributeMapping.py | 98 ++----------------- 1 file changed, 8 insertions(+), 90 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py index 63f21c523..88cce58bd 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py @@ -13,14 +13,13 @@ VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE, VTK_FLOAT, VTK_DOUBLE, ) from vtkmodules.vtkCommonDataModel import ( - vtkCell, vtkDataSet, vtkMultiBlockDataSet, vtkCompositeDataSet, ) from geos.mesh.utils.arrayModifiers import createAttribute -from geos.mesh.utils.arrayHelpers import ( getAttributeSet, getComponentNames, getVtkDataTypeInObject, isAttributeGlobal ) +from geos.mesh.utils.arrayHelpers import ( computeCellMapping, getAttributeSet, getComponentNames, getVtkDataTypeInObject, isAttributeGlobal ) __doc__ = """ AttributeMapping is a vtk filter that transfer global attributes from a source mesh (meshFrom) to another (meshTo) for each @@ -86,7 +85,6 @@ def __init__( # cell map self.dictCellMap: dict[ int, list[ int ] ] = {} - self.sharedCell: bool = False # Logger. self.logger: Logger @@ -162,8 +160,13 @@ def applyFilter( self: Self ) -> bool: self.logger.error( f"All attributes to transfer must be global, { partialAttributes } are partials." ) self.logger.error( f"The filter { self.logger.name } failed." ) - self._cellMapping( self.meshFrom, self.meshTo ) - if not self.sharedCell: + self.dictCellMap = computeCellMapping( self.meshFrom, self.meshTo ) + sharedCell: bool = False + for key in self.dictCellMap.keys(): + if np.any( self.dictCellMap[ key ] > -1 ): + sharedCell = True + + if not sharedCell: self.logger.warning( "The two meshes do not have any shared cell." ) self.logger.warning( f"The filter { self.logger.name } has not been used." ) return False @@ -199,91 +202,6 @@ def applyFilter( self: Self ) -> bool: self._logOutputMessage() return True - - def _cellMapping( - self: Self, - meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], - meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], - ) -> None: - if isinstance( meshTo, vtkDataSet ): - self.dictCellMap[ 0 ] = np.full( ( meshTo.GetNumberOfCells(), 2 ), -1, int ) - self._cellMappingToDataSet( meshFrom, meshTo ) - elif isinstance( meshTo, vtkMultiBlockDataSet ): - self._cellMappingToMultiBlockDataSet( meshFrom, meshTo ) - - - def _cellMappingToMultiBlockDataSet( - self: Self, - meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], - multiBlockDataSetTo: vtkMultiBlockDataSet, - ) -> None: - nbBlocksTo: int = multiBlockDataSetTo.GetNumberOfBlocks() - for idBlockTo in range( nbBlocksTo ): - blockTo: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTo.GetBlock( idBlockTo ) ) - self.dictCellMap[ idBlockTo ] = np.full( ( blockTo.GetNumberOfCells(), 2 ), -1, int ) - self._cellMappingToDataSet( meshFrom, blockTo, idBlockTo=idBlockTo ) - - - def _cellMappingToDataSet( - self: Self, - meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], - dataSetTo: vtkDataSet, - idBlockTo: int = 0, - ) -> None: - if isinstance( meshFrom, vtkDataSet ): - self._cellMappingFromDataSetToDataSet( meshFrom, dataSetTo, idBlockTo=idBlockTo ) - elif isinstance( meshFrom, vtkMultiBlockDataSet ): - self._cellMappingFromMultiBlockDataSetToDataSet( meshFrom, dataSetTo, idBlockTo=idBlockTo ) - - - def _cellMappingFromMultiBlockDataSetToDataSet( - self: Self, - multiBlockDataSetFrom: vtkMultiBlockDataSet, - dataSetTo: vtkDataSet, - idBlockTo: int = 0, - ) -> None: - nbBlocksFrom: int = multiBlockDataSetFrom.GetNumberOfBlocks() - for idBlockFrom in range( nbBlocksFrom ): - blockFrom: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetFrom.GetBlock( idBlockFrom ) ) - self._cellMappingFromDataSetToDataSet( blockFrom, dataSetTo, idBlockFrom=idBlockFrom, idBlockTo=idBlockTo ) - - - def _cellMappingFromDataSetToDataSet( - self: Self, - dataSetFrom: vtkDataSet, - dataSetTo: vtkDataSet, - idBlockFrom: int = 0, - idBlockTo: int = 0, - ) -> None: - """Create the cell map from the source mesh to the working mesh cell indexes. - - For each cell index of the working mesh, stores the index of the cell - in the source mesh. - - Args: - sourceMesh (vtkUnstructuredGrid): The mesh with attributes to transfer. - workingMesh (vtkUnstructuredGrid): The mesh where to copy attributes. - """ - idCellFromFund: list[ int ] = [] - for idCellTo in range( dataSetTo.GetNumberOfCells() ): - cellTo: vtkCell = dataSetTo.GetCell( idCellTo ) - boundsCellTo: list[ float ] = cellTo.GetBounds() - - idCellFrom: int = 0 - cellFund: bool = False - while idCellFrom < dataSetFrom.GetNumberOfCells() and not cellFund: - if idCellFrom not in idCellFromFund: - cellFrom: vtkCell = dataSetFrom.GetCell( idCellFrom ) - boundsCellFrom: list[ float ] = cellFrom.GetBounds() - if boundsCellFrom == boundsCellTo: - self.dictCellMap[ idBlockTo ][ idCellTo ] = [ idBlockFrom, idCellFrom ] - cellFund = True - idCellFromFund.append( idCellFrom ) - - idCellFrom += 1 - - if len( idCellFromFund ) > 0: - self.sharedCell = True def _transferAttribute( From 498079763cc476cf2f68529de4921249d6df0226 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Sep 2025 17:31:35 +0200 Subject: [PATCH 15/45] clean for ci --- .../geos/mesh/processing/AttributeMapping.py | 119 ++++++++++-------- 1 file changed, 66 insertions(+), 53 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py index 88cce58bd..352e73ebc 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py @@ -15,11 +15,11 @@ from vtkmodules.vtkCommonDataModel import ( vtkDataSet, vtkMultiBlockDataSet, - vtkCompositeDataSet, ) from geos.mesh.utils.arrayModifiers import createAttribute -from geos.mesh.utils.arrayHelpers import ( computeCellMapping, getAttributeSet, getComponentNames, getVtkDataTypeInObject, isAttributeGlobal ) +from geos.mesh.utils.arrayHelpers import ( computeCellMapping, getAttributeSet, getComponentNames, + getVtkDataTypeInObject, isAttributeGlobal ) __doc__ = """ AttributeMapping is a vtk filter that transfer global attributes from a source mesh (meshFrom) to another (meshTo) for each @@ -64,14 +64,14 @@ class AttributeMapping: def __init__( - self: Self, - meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], - meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], - attributeNames: set[ str ], - speHandler: bool = False, - ) -> None: + self: Self, + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], + attributeNames: set[ str ], + speHandler: bool = False, + ) -> None: """Map attributes of the source mesh (meshFrom) to the other mesh (meshTo). - + Args: meshFrom (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The mesh with attributes to transfer. meshTo (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The mesh where to copy attributes. @@ -84,7 +84,7 @@ def __init__( self.attributeNames: set[ str ] = attributeNames # cell map - self.dictCellMap: dict[ int, list[ int ] ] = {} + self.dictCellMap: dict[ int, npt.NDArray[ np.int64 ] ] = {} # Logger. self.logger: Logger @@ -94,7 +94,6 @@ def __init__( 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. @@ -110,12 +109,14 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: "The logger already has an handler, to use yours set the argument 'speHandler' to True during the filter initialization." ) + def GetCellMap( self: Self ) -> dict[ int, npt.NDArray[ np.int64 ] ]: + """Getter of cell mapping dictionary. - def GetCellMap( self: Self ) -> npt.NDArray[ np.int64 ]: - """Getter of cell map.""" + Returns: + self.dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary. + """ return self.dictCellMap - def applyFilter( self: Self ) -> bool: """Map attributes from the source mesh (meshFrom) to the other (meshTo) for the shared cells. @@ -127,7 +128,7 @@ def applyFilter( self: Self ) -> bool: if len( self.attributeNames ) == 0: self.logger.warning( "Please enter at least one attribute to transfer." ) self.logger.warning( f"The filter { self.logger.name } has not been used." ) - return False + return False wrongAttributeNames: list[ str ] = [] attributesAlreadyInMeshTo: list = [] @@ -136,17 +137,20 @@ def applyFilter( self: Self ) -> bool: for attributeName in self.attributeNames: if attributeName not in attributesInMeshFrom: wrongAttributeNames.append( attributeName ) - + if attributeName in attributesInMeshTo: attributesAlreadyInMeshTo.append( attributeName ) - + if len( wrongAttributeNames ) > 0: - self.logger.error( f"The attributes { wrongAttributeNames } are not in the mesh from where to transfer attributes." ) + self.logger.error( + f"The attributes { wrongAttributeNames } are not in the mesh from where to transfer attributes." ) self.logger.error( f"The filter { self.logger.name } failed." ) return False - + if len( attributesAlreadyInMeshTo ) > 0: - self.logger.error( f"The attributes { attributesAlreadyInMeshTo } are already in the mesh where attributes must be transferred." ) + self.logger.error( + f"The attributes { attributesAlreadyInMeshTo } are already in the mesh where attributes must be transferred." + ) self.logger.error( f"The filter { self.logger.name } failed." ) return False @@ -155,24 +159,24 @@ def applyFilter( self: Self ) -> bool: for attributeName in self.attributeNames: if not isAttributeGlobal( self.meshFrom, attributeName, False ): partialAttributes.append( attributeName ) - + if len( partialAttributes ) > 0: self.logger.error( f"All attributes to transfer must be global, { partialAttributes } are partials." ) self.logger.error( f"The filter { self.logger.name } failed." ) - + self.dictCellMap = computeCellMapping( self.meshFrom, self.meshTo ) sharedCell: bool = False - for key in self.dictCellMap.keys(): + for key in self.dictCellMap: if np.any( self.dictCellMap[ key ] > -1 ): sharedCell = True - + if not sharedCell: self.logger.warning( "The two meshes do not have any shared cell." ) self.logger.warning( f"The filter { self.logger.name } has not been used." ) return False for attributeName in self.attributeNames: - componentNames: tuple[ str, ... ] = getComponentNames( self.meshFrom, attributeName, False ) + componentNames: tuple[ str, ...] = getComponentNames( self.meshFrom, attributeName, False ) vtkDataType: int = getVtkDataTypeInObject( self.meshFrom, attributeName, False ) defaultValue: Any @@ -181,44 +185,49 @@ def applyFilter( self: Self ) -> bool: elif vtkDataType in ( VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE ): defaultValue = -1 elif vtkDataType in ( VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT, - VTK_UNSIGNED_LONG_LONG ): + VTK_UNSIGNED_LONG_LONG ): defaultValue = 0 - + if isinstance( self.meshTo, vtkDataSet ): - if not self._transferAttribute( self.meshFrom, self.meshTo, attributeName, componentNames, vtkDataType, defaultValue ): + if not self._transferAttribute( self.meshFrom, self.meshTo, attributeName, componentNames, vtkDataType, + defaultValue ): return False - elif isinstance( self.meshTo, ( vtkMultiBlockDataSet, vtkCompositeDataSet ) ): + elif isinstance( self.meshTo, vtkMultiBlockDataSet ): nbBlocksTo: int = self.meshTo.GetNumberOfBlocks() for idBlockTo in range( nbBlocksTo ): blockTo: vtkDataSet = vtkDataSet.SafeDownCast( self.meshTo.GetBlock( idBlockTo ) ) - if not self._transferAttribute( self.meshFrom, blockTo, attributeName, componentNames, vtkDataType, defaultValue, idBlockTo ): + if not self._transferAttribute( self.meshFrom, blockTo, attributeName, componentNames, vtkDataType, + defaultValue, idBlockTo ): return False - else: - self.logger.error( "The working mesh data type is not vtkDataSet nor vtkMultiBlockDataSet." ) - self.logger.error( f"The filter { self.logger.name } failed." ) - return False - + # Log the output message. self._logOutputMessage() return True - - def _transferAttribute( - self: Self, - meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], - dataSetTo: vtkDataSet, - attributeName: str, - componentNames: tuple[ str, ... ], - vtkDataType: int, - defaultValue: Any, - idBlockTo: int = 0, - ) -> None: + def _transferAttribute( + self: Self, + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], + dataSetTo: vtkDataSet, + attributeName: str, + componentNames: tuple[ str, ...], + vtkDataType: int, + defaultValue: Any, + idBlockTo: int = 0, + ) -> bool: """Transfer attributes from the source mesh to the working meshes using cell mapping. + dataSetTo is considered as block of a vtkMultiblockDataSet, if not, 0 is use as block index. + Args: - sourceMesh (vtkUnstructuredGrid): The mesh with attributes to transfer. - workingMesh (vtkUnstructuredGrid): The mesh where to copy attributes. + meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with attributes to transfer. + dataSetTo (vtkDataSet): The mesh where to transfer attributes. + attributeName (str): The name of the attribute to transfer. + componentNames (tuple[str, ...]): The name of the component of the attributes to transfer. If no component, set an empty tuple. + vtkDataType (int): The vtk type of the attribute to transfer. + defaultValue (Any): The value to use for the cell not mapped. + idBlockTo (int, Optional): The block index of the dataSetTo. + Defaults to 0. Returns: bool: True if transfer successfully ended. @@ -233,7 +242,7 @@ def _transferAttribute( arrayTo = np.full( ( nbCellTo, nbComponents ), defaultValue, dtype=valueType ) else: arrayTo = np.array( [ defaultValue for _ in range( nbCellTo ) ], dtype=valueType ) - + for idCellTo in range( nbCellTo ): value: Any = defaultValue idCellFrom: int = self.dictCellMap[ idBlockTo ][ idCellTo ][ 1 ] @@ -247,11 +256,15 @@ def _transferAttribute( arrayFrom = vnp.vtk_to_numpy( blockFrom.GetCellData().GetArray( attributeName ) ) value = arrayFrom[ idCellFrom ] arrayTo[ idCellTo ] = value - - return createAttribute( dataSetTo, arrayTo, attributeName, componentNames, onPoints=False, vtkDataType=vtkDataType, logger=self.logger ) - + return createAttribute( dataSetTo, + arrayTo, + attributeName, + componentNames, + onPoints=False, + vtkDataType=vtkDataType, + logger=self.logger ) + def _logOutputMessage( self: Self ) -> None: """Create and log result messages of the filter.""" self.logger.info( f"The filter { self.logger.name } succeed." ) - From 4d6b52103ea3d3718077237fc07b0a12c33d05b2 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Sep 2025 17:36:20 +0200 Subject: [PATCH 16/45] clean for ci --- .../src/geos/pv/plugins/PVAttributeMapping.py | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index 09dad9058..4ef9c9532 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -5,13 +5,13 @@ import sys from pathlib import Path from typing import Union - from typing_extensions import Self # 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() from geos.mesh.processing.AttributeMapping import AttributeMapping @@ -24,7 +24,7 @@ ) 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.vtkCommonCore import ( vtkDataArraySelection, @@ -115,9 +115,10 @@ def RequestInformation( attributeNames: set[ str ] = getAttributeSet( inData, False ) for attributeName in attributeNames: - if isinstance( inData, vtkMultiBlockDataSet ) and isAttributeGlobal( inData, attributeName, False ) or isinstance( inData, vtkDataSet ): - if not self.m_attributes.ArrayExists( attributeName ): - self.m_attributes.AddArray( attributeName, False ) + if isinstance( inData, vtkMultiBlockDataSet ) and isAttributeGlobal( inData, attributeName, False ) \ + or isinstance( inData, vtkDataSet ) \ + and not self.m_attributes.ArrayExists( attributeName ): + self.m_attributes.AddArray( attributeName, False ) self.m_firstUse = False return 1 @@ -162,12 +163,9 @@ def RequestData( Returns: int: 1 if calculation successfully ended, 0 otherwise. """ - meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet, - vtkCompositeDataSet ] = self.GetInputData( inInfoVec, 0, 0 ) - meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet, - vtkCompositeDataSet ] = self.GetInputData( inInfoVec, 1, 0 ) - outData: Union[ vtkDataSet, vtkMultiBlockDataSet, - vtkCompositeDataSet ] = self.GetOutputData( outInfoVec, 0 ) + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCompositeDataSet ] = self.GetInputData( inInfoVec, 0, 0 ) + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCompositeDataSet ] = self.GetInputData( inInfoVec, 1, 0 ) + outData: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCompositeDataSet ] = self.GetOutputData( outInfoVec, 0 ) assert meshTo is not None, "Input mesh (meshTo) to transfer attributed is null." assert meshFrom is not None, "Input mesh (meshFrom) with attributes to transfer is null." @@ -176,7 +174,7 @@ def RequestData( outData.ShallowCopy( meshTo ) attributeNames: set[ str ] = set( getArrayChoices( self.a02GetAttributeToTransfer() ) ) - + filter: AttributeMapping = AttributeMapping( meshFrom, outData, attributeNames, True ) if not filter.logger.hasHandlers(): filter.setLoggerHandler( VTKHandler() ) @@ -184,4 +182,3 @@ def RequestData( filter.applyFilter() return 1 - From ad3ba4d6d6640af1d0623f4f63664216998881c6 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 3 Sep 2025 17:43:08 +0200 Subject: [PATCH 17/45] update doc files --- docs/geos_mesh_docs/processing.rst | 8 ++++++++ docs/geos_posp_docs/PVplugins.rst | 5 ----- docs/geos_posp_docs/filters.rst | 7 ------- docs/geos_pv_docs/processing.rst | 6 ++++++ 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/geos_mesh_docs/processing.rst b/docs/geos_mesh_docs/processing.rst index fec7f4b70..34904a031 100644 --- a/docs/geos_mesh_docs/processing.rst +++ b/docs/geos_mesh_docs/processing.rst @@ -3,6 +3,14 @@ Processing filters The `processing` module of `geos-mesh` package contains filters to process meshes. +geos.mesh.processing.AttributeMapping module +-------------------------------------------------------- + +.. automodule:: geos.mesh.processing.AttributeMapping + :members: + :undoc-members: + :show-inheritance: + geos.mesh.processing.FillPartialArrays filter ---------------------------------------------- diff --git a/docs/geos_posp_docs/PVplugins.rst b/docs/geos_posp_docs/PVplugins.rst index 91c859b68..120a03d32 100644 --- a/docs/geos_posp_docs/PVplugins.rst +++ b/docs/geos_posp_docs/PVplugins.rst @@ -10,11 +10,6 @@ The plugins include: * Processing plugins to compute additional geomechanical properties; * Visualization plugins to plot Mohr's circles and cross plots using Paraview Python View. -PVAttributeMapping plugin ------------------------------------ - -.. automodule:: PVplugins.PVAttributeMapping - PVCreateConstantAttributePerRegion plugin --------------------------------------------------- diff --git a/docs/geos_posp_docs/filters.rst b/docs/geos_posp_docs/filters.rst index da4b0e559..20fd785e0 100644 --- a/docs/geos_posp_docs/filters.rst +++ b/docs/geos_posp_docs/filters.rst @@ -3,13 +3,6 @@ vtk Filters This package defines vtk filters that allows to process Geos outputs. -geos_posp.filters.AttributeMappingFromCellCoords module --------------------------------------------------------- - -.. automodule:: geos_posp.filters.AttributeMappingFromCellCoords - :members: - :undoc-members: - :show-inheritance: geos_posp.filters.AttributeMappingFromCellId module ----------------------------------------------------------- diff --git a/docs/geos_pv_docs/processing.rst b/docs/geos_pv_docs/processing.rst index 439200789..c13342d65 100644 --- a/docs/geos_pv_docs/processing.rst +++ b/docs/geos_pv_docs/processing.rst @@ -1,6 +1,12 @@ Post-/Pre-processing ========================= +PVAttributeMapping +-------------------- + +.. automodule:: geos.pv.plugins.PVAttributeMapping + + PVFillPartialArrays -------------------- .. automodule:: geos.pv.plugins.PVFillPartialArrays From 3521290cfedc9bef11e6fd198d7ed8ac0640de03 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 5 Sep 2025 15:15:19 +0200 Subject: [PATCH 18/45] clean tests for computeElementMapping --- geos-mesh/tests/test_arrayHelpers.py | 31 ++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/geos-mesh/tests/test_arrayHelpers.py b/geos-mesh/tests/test_arrayHelpers.py index 35951f74c..f78f578cd 100644 --- a/geos-mesh/tests/test_arrayHelpers.py +++ b/geos-mesh/tests/test_arrayHelpers.py @@ -1,11 +1,11 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. -# SPDX-FileContributor: Paloma Martinez +# SPDX-FileContributor: Paloma Martinez, Romain Baville # SPDX-License-Identifier: Apache 2.0 # ruff: noqa: E402 # disable Module level import not at top of file # mypy: disable-error-code="operator, attr-defined" import pytest -from typing import Tuple +from typing import Tuple, Union import numpy as np import numpy.typing as npt @@ -17,6 +17,33 @@ from geos.mesh.utils import arrayHelpers +@pytest.mark.parametrize( "meshFromName, meshToName, points", [ + ( "multiblock", "emptymultiblock", False ), + ( "multiblock", "emptyFracture", False ), + ( "dataset", "emptyFracture", False ), + ( "fracture", "emptyFracture", True ), + ( "fracture", "emptyFracture", False ), + ( "fracture", "emptymultiblock", False ), +] ) +def test_computeElementMapping( + dataSetTest: vtkDataSet, + getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], + meshFromName: str, + meshToName: str, + points: bool, +) -> None: + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshFromName ) + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshToName ) + elementMapComputed: dict[ int, npt.NDArray[ np.int64 ] ] = arrayHelpers.computeElementMapping( meshFrom, meshTo, points ) + elementMapTest: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( meshFromName, meshToName, points ) + + keysComputed: list[ int ] = list( elementMapComputed.keys() ) + keysTest: list[ int ] = list( elementMapTest.keys() ) + assert keysComputed == keysTest + + for key in keysTest: + assert np.all( elementMapComputed[ key ] == elementMapTest[ key ] ) + @pytest.mark.parametrize( "onpoints, expected", [ ( True, { 'GLOBAL_IDS_POINTS': 1, From d414a2dea1d43b1caada70a6df4a46b93d3013e1 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 5 Sep 2025 15:19:08 +0200 Subject: [PATCH 19/45] Add the possibility to chose to map cell or point and use flat index no more block --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 269 ++++++++++-------- geos-mesh/tests/conftest.py | 55 +++- 2 files changed, 205 insertions(+), 119 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index c877e3e61..a7586a66d 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -12,7 +12,7 @@ from vtkmodules.vtkCommonCore import vtkDataArray, vtkPoints from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkFieldData, vtkMultiBlockDataSet, vtkDataSet, vtkCompositeDataSet, vtkDataObject, vtkPointData, vtkCellData, - vtkDataObjectTreeIterator, vtkPolyData, vtkCell ) + vtkDataObjectTreeIterator, vtkPolyData ) from vtkmodules.vtkFiltersCore import vtkCellCenters from geos.mesh.utils.multiblockHelpers import ( getBlockElementIndexesFlatten, getBlockFromFlatIndex ) @@ -20,187 +20,228 @@ ArrayHelpers module contains several utilities methods to get information on arrays in VTK datasets. These methods include: - + - mesh element localization mapping by indexes - array getters, with conversion into numpy array or pandas dataframe - boolean functions to check whether an array is present in the dataset - bounds getter for vtu and multiblock datasets """ -def computeCellMapping( +def computeElementMapping( meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], + points: bool, ) -> dict[ int, npt.NDArray ]: - """Compute the cell index mapping dictionary from the mesh meshFrom to the mesh meshTo. + """Compute the elementMap from the meshFrom to the meshTo. - The cell mapping dictionary keep the mapping cell index where: - - Keys are the block index of the meshTo. - - Items are arrays of size (nb cell in the block, 2). + If meshFrom is a vtkDataSet, the flat index (flatIdDataSetFrom) is set to 0. + If meshTo is a vtkDataSet, the flat index (flatIdDataSetTo) is set to 0. - For each cell (idCellTo) of each block (idBlockTo) of meshTo: - - if a cell (idCellFrom) of a block (idBlockFrom) of meshFrom has the same bounds coordinates, dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. - - else, dictCellMap[idBlockTo][idCellTo] = [-1, -1]. + The elementMap is a dictionary where: + - Keys are the flat index of all the datasets of the meshTo. + - Items are arrays of size (nb elements in datasets, 2). - If meshes are vtkDataSet, 0 is use as block index. + For each element (idElementTo) of each dataset (flatIdDataSetTo) of meshTo, + if the points coordinates of an element (idElementFrom) of one meshFrom's dataSet (flatIdDataSetFrom) + are the same as the points coordinates of the elementTo, + elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom] + else, elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. Args: - meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the cell indexes to map. - meshTo (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the reference cell indexes. + meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the element to map. + meshTo (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the reference element. + points (bool): True if elements to map are points, False if they are cells. Returns: - dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary. + elementMap (dict[int, npt.NDArray[np.int64]]): The elementMap with the element indexes in the two meshes. """ - dictCellMap: dict[ int, npt.NDArray ] = {} + elementMap: dict[ int, npt.NDArray ] = {} if isinstance( meshTo, vtkDataSet ): - UpdateCellMappingToDataSet( meshFrom, meshTo, dictCellMap ) + UpdateElementMappingToDataSet( meshFrom, meshTo, elementMap, points ) elif isinstance( meshTo, vtkMultiBlockDataSet ): - UpdateCellMappingToMultiBlockDataSet( meshFrom, meshTo, dictCellMap ) + UpdateElementMappingToMultiBlockDataSet( meshFrom, meshTo, elementMap, points ) - return dictCellMap + return elementMap -def UpdateCellMappingToMultiBlockDataSet( +def UpdateElementMappingToMultiBlockDataSet( meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], multiBlockDataSetTo: vtkMultiBlockDataSet, - dictCellMap: dict[ int, npt.NDArray ], + elementMap: dict[ int, npt.NDArray ], + points: bool, ) -> None: - """Update the cell index mapping dictionary from the mesh meshFrom to the mesh multiBlockDataSetTo. + """Update the elementMap from the meshFrom to the multiBlockDataSetTo. - Add the mapping for each block (idBlockTo) of the multiBlockDataSetTo: - - Keys are the block index of the multiBlockDataSetTo. - - Items are arrays of size (nb cell in block, 2). + If meshFrom is a vtkDataSet, the flat index (flatIdDataSetFrom) is set to 0. - For each cell (idCellTo) of each block (idBlockTo) of multiBlockDataSetTo: - - if a cell (idCellFrom) of a block (idBlockFrom) of meshFrom has the same bounds coordinates, dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. - - else, dictCellMap[idBlockTo][idCellTo] = [-1, -1]. + Add the mapping for of the multiBlockDataSetTo: + - Keys are the flat index of all the datasets of the multiBlockDataSetTo. + - Items are arrays of size (nb elements in datasets, 2). - If meshFrom is a vtkDataSet, 0 is use as block index. + For each element (idElementTo) of each dataset (flatIdDataSetTo) of multiBlockDataSetTo, + if the points coordinates of an element (idElementFrom) of one meshFrom's dataSet (flatIdDataSetFrom) + are the same as the points coordinates of the elementTo, + elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom] + else, elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. Args: - meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the cell indexes to map. - multiBlockDataSetTo (vtkMultiBlockDataSet): The mesh with the reference cell indexes. - dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary to update. + meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the element to map. + multiBlockDataSetTo (vtkMultiBlockDataSet): The mesh with the reference element. + elementMap (dict[int, npt.NDArray[np.int64]]): The elementMap to update. + points (bool): True if elements to map are points, False if they are cells. """ - nbBlocksTo: int = multiBlockDataSetTo.GetNumberOfBlocks() - for idBlockTo in range( nbBlocksTo ): - blockTo: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTo.GetBlock( idBlockTo ) ) - UpdateCellMappingToDataSet( meshFrom, blockTo, dictCellMap, idBlockTo=idBlockTo ) + listFlatIdMultiBlockDataSetTo: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetTo ) + for flatIdDataSetTo in listFlatIdMultiBlockDataSetTo: + dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTo.GetDataSet( flatIdDataSetTo ) ) + UpdateElementMappingToDataSet( meshFrom, dataSetTo, elementMap, points, flatIdDataSetTo=flatIdDataSetTo ) -def UpdateCellMappingToDataSet( +def UpdateElementMappingToDataSet( meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], dataSetTo: vtkDataSet, - dictCellMap: dict[ int, npt.NDArray ], - idBlockTo: int = 0, + elementMap: dict[ int, npt.NDArray ], + points: bool, + flatIdDataSetTo: int = 0, ) -> None: - """Update the cell index mapping dictionary from the mesh meshFrom to the mesh dataSetTo. + """Update the elementMap from the meshFrom to the dataSetTo. - Add the mapping for the dataSetTo: - - The keys is the block index of the dataSetTo (idBlockTo). - - The item is an array of size (nb cell in dataSetTo, 2). + If meshFrom is a vtkDataSet, the flat index (flatIdDataSetFrom) is set to 0. + dataSetTo is considered as block of vtkMultiblockDataSet, if not the flat index (flatIdDataSetTo) is set to 0. - For each cell (idCellTo) of the mesh dataSetTo: - - if a cell (idCellFrom) of the meshFrom or one of its block (idBlockFrom) has the same bounds coordinates, dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. - - else, dictCellMap[idBlockTo][idCellTo] = [-1, -1]. + Add the mapping for the dataSetTo: + - The keys is the flat index of the dataSetTo (flatIdDataSetTo). + - The item is an array of size (nb elements in dataSetTo, 2). - If meshFrom is a vtkDataSet, 0 is use as block index. - dataSetTo is considered as block of a vtkMultiblockDataSet, if not, 0 is use as block index. + For each element (idElementTo) of the mesh dataSetTo, + if the points coordinates of an element (idElementFrom) of one meshFrom's dataSet (flatIdDataSetFrom) + are the same as the points coordinates of the elementTo, + elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom] + else, elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. Args: - meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the cell indexes to map. - dataSetTo (vtkDataSet): The mesh with the reference cell indexes. - dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary to update. - idBlockTo (int, Optional): The block index of the dataSetTo. + meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the element to map. + dataSetTo (vtkDataSet): The dataset with the reference element. + elementMap (dict[int, npt.NDArray[np.int64]]): The elementMap to update. + points (bool): True if elements to map are points, False if they are cells. + flatIdDataSetTo (int, Optional): The flat index of the dataSetTo. Defaults to 0. """ - dictCellMap[ idBlockTo ] = np.full( ( dataSetTo.GetNumberOfCells(), 2 ), -1, int ) + nbElementsTo: int = dataSetTo.GetNumberOfPoints() if points else dataSetTo.GetNumberOfCells() + elementMap[ flatIdDataSetTo ] = np.full( ( nbElementsTo, 2 ), -1, np.int64 ) if isinstance( meshFrom, vtkDataSet ): - UpdateCellMappingFromDataSetToDataSet( meshFrom, dataSetTo, dictCellMap, idBlockTo=idBlockTo ) + UpdateDictElementMappingFromDataSetToDataSet( meshFrom, dataSetTo, elementMap, points, flatIdDataSetTo=flatIdDataSetTo ) elif isinstance( meshFrom, vtkMultiBlockDataSet ): - UpdateCellMappingFromMultiBlockDataSetToDataSet( meshFrom, dataSetTo, dictCellMap, idBlockTo=idBlockTo ) + UpdateElementMappingFromMultiBlockDataSetToDataSet( meshFrom, dataSetTo, elementMap, points, flatIdDataSetTo=flatIdDataSetTo ) -def UpdateCellMappingFromMultiBlockDataSetToDataSet( +def UpdateElementMappingFromMultiBlockDataSetToDataSet( multiBlockDataSetFrom: vtkMultiBlockDataSet, dataSetTo: vtkDataSet, - dictCellMap: dict[ int, npt.NDArray ], - idBlockTo: int = 0, + elementMap: dict[ int, npt.NDArray ], + points: bool, + flatIdDataSetTo: int = 0, ) -> None: - """Update the cell index mapping dictionary from the mesh multiBlockDataSetFrom to the mesh dataSetTo. + """Update the elementMap from the multiBlockDataSetFrom to the dataSetTo. - For each cell (idCellTo) of the mesh dataSetTo not yet mapped (dictCellMap[idBlockTo][idCellTo] = [-1, -1]), - if a cell (idCellFrom) of a block (idBlockFrom) of multiBlockDataSetFrom has the same bounds coordinates, - the dictCellMap is update to dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. + dataSetTo is considered as block of a vtkMultiblockDataSet, if not the flat index (flatIdDataSetTo) is set to 0. - dataSetTo is considered as block of a vtkMultiblockDataSet, if not, 0 is use as block index. + For each element (idElementTo) of the dataSetTo not yet mapped (elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]), + if the points coordinates of an element (idElementFrom) of a block (flatIdDataSetFrom) of multiBlockDataSetFrom + are the same as the points coordinates of the elementTo, + the elementMap is update to elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. Args: - multiBlockDataSetFrom (vtkMultiBlockDataSet): The mesh with the cell indexes to map. - dataSetTo (vtkDataSet): The mesh with the reference cell indexes. - dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary to update with; - The block index of the dataSetTo as keys. - An array of size (nb cell in dataSetTo, 2) as item. - idBlockTo (int, Optional): The block index of the dataSetTo. + multiBlockDataSetFrom (vtkMultiBlockDataSet): The mesh with the element to map. + dataSetTo (vtkDataSet): The dataset with the reference element. + elementMap (dict[int, npt.NDArray[np.int64]]): The elementMap to update with; + The flat index of the dataSetTo as keys. + An array of size (nb elements in dataSetTo, 2) as item. + points (bool): True if elements to map are points, False if they are cells. + flatIdDataSetTo (int, Optional): The flat index of the dataSetTo. Defaults to 0. """ - nbBlocksFrom: int = multiBlockDataSetFrom.GetNumberOfBlocks() - for idBlockFrom in range( nbBlocksFrom ): - blockFrom: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetFrom.GetBlock( idBlockFrom ) ) - UpdateCellMappingFromDataSetToDataSet( blockFrom, - dataSetTo, - dictCellMap, - idBlockFrom=idBlockFrom, - idBlockTo=idBlockTo ) + listFlatIdMultiBlockDataSetFrom: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetFrom ) + for flatIdDataSetFrom in listFlatIdMultiBlockDataSetFrom: + dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetFrom.GetDataSet( flatIdDataSetFrom ) ) + UpdateDictElementMappingFromDataSetToDataSet( dataSetFrom, + dataSetTo, + elementMap, + points, + flatIdDataSetFrom=flatIdDataSetFrom, + flatIdDataSetTo=flatIdDataSetTo ) -def UpdateCellMappingFromDataSetToDataSet( +def UpdateDictElementMappingFromDataSetToDataSet( dataSetFrom: vtkDataSet, dataSetTo: vtkDataSet, - dictCellMap: dict[ int, npt.NDArray[ np.int64 ] ], - idBlockFrom: int = 0, - idBlockTo: int = 0, + elementMap: dict[ int, npt.NDArray[ np.int64 ] ], + points: bool, + flatIdDataSetFrom: int = 0, + flatIdDataSetTo: int = 0, ) -> None: - """Update the cell index mapping dictionary from the mesh dataSetFrom to the mesh dataSetTo. + """Update the elementMap from the dataSetFrom to the dataSetTo. - For each cell (idCellTo) of the mesh dataSetTo not yet mapped (dictCellMap[idBlockTo][idCellTo] = [-1, -1]), - if a cell (idCellFrom) of the mesh dataSetFrom has the same bounds coordinates, - the dictCellMap is update to dictCellMap[idBlockTo][idCellTo] = [idBlockFrom, idCellFrom]. + dataSetFrom is considered as block of vtkMultiblockDataSet, if not the flat index (flatIdDataSetFrom) is set to 0. + dataSetTo is considered as block of vtkMultiblockDataSet, if not the flat index (flatIdDataSetTo) is set to 0. - Meshes are considered as block of vtkMultiblockDataSet, if not, 0 is use as block index. + For each element (idElementTo) of the dataSetTo not yet mapped (elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]), + if the points coordinates of an element (idElementFrom) of the dataSetFrom + are the same as the points coordinates of the elementTo, + the elementMap is update to elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. Args: - dataSetFrom (vtkDataSet): The mesh with the cell indexes to map. - dataSetTo (vtkDataSet): The mesh with the reference cell indexes. - dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary to update with; - The block index of the dataSetTo as keys. - An array of size (nb cell in dataSetTo, 2) as item. - idBlockFrom (int, Optional): The block index of the dataSetFrom. + dataSetFrom (vtkDataSet): The dataset with the element to map. + dataSetTo (vtkDataSet): The dataset with the reference element. + elementMap (dict[int, npt.NDArray[np.int64]]): The elementMap to update with; + The flat index of the dataSetTo as keys. + An array of size (nb elements in dataSetTo, 2) as item. + points (bool): True if elements to map are points, False if they are cells. + flatIdDataSetFrom (int, Optional): The flat index of the dataSetFrom. Defaults to 0. - idBlockTo (int, Optional): The block index of the dataSetTo. + flatIdDataSetTo (int, Optional): The flat index of the dataSetTo. Defaults to 0. """ - idCellFromFund: list[ int ] = [] - nbCellsTo: int = dataSetTo.GetNumberOfCells() - nbCellsFrom: int = dataSetFrom.GetNumberOfCells() - for idCellTo in range( nbCellsTo ): - # Test if the cell is already mapped. - if -1 in dictCellMap[ idBlockTo ][ idCellTo ]: - cellTo: vtkCell = dataSetTo.GetCell( idCellTo ) - boundsCellTo: tuple[ float, ...] = cellTo.GetBounds() - - idCellFrom: int = 0 - cellFund: bool = False - while idCellFrom < nbCellsFrom and not cellFund: - # Test if the cell is already mapped. - if idCellFrom not in idCellFromFund: - cellFrom: vtkCell = dataSetFrom.GetCell( idCellFrom ) - boundsCellFrom: tuple[ float, ...] = cellFrom.GetBounds() - if boundsCellFrom == boundsCellTo: - dictCellMap[ idBlockTo ][ idCellTo ] = [ idBlockFrom, idCellFrom ] - cellFund = True - idCellFromFund.append( idCellFrom ) - - idCellFrom += 1 + idElementsFromFund: list[ int ] = [] + nbElementsTo: int = len( elementMap[ flatIdDataSetTo ] ) + nbElementsFrom: int = dataSetFrom.GetNumberOfPoints() if points else dataSetFrom.GetNumberOfCells() + for idElementTo in range( nbElementsTo ): + # Test if the element of the dataSetTo is already mapped. + if -1 in elementMap[ flatIdDataSetTo ][ idElementTo ]: + coordElementTo: tuple[ float, ...] + if points: + coordElementTo = dataSetTo.GetPoint( idElementTo ) + else: + # Get the coordinates of each points of the cell. + nbPointsTo: int = dataSetTo.GetCell( idElementTo ).GetNumberOfPoints() + cellPointsTo: vtkPoints = dataSetTo.GetCell( idElementTo ).GetPoints() + coordPointsTo: list = [] + for idPointTo in range( nbPointsTo ): + coordPointsTo.extend( cellPointsTo.GetPoint( idPointTo ) ) + coordElementTo = tuple( coordPointsTo ) + + idElementFrom: int = 0 + ElementFromFund: bool = False + while idElementFrom < nbElementsFrom and not ElementFromFund: + # Test if the element of the dataSetFrom is already mapped. + if idElementFrom not in idElementsFromFund: + coordElementFrom: tuple[ float, ...] + if points: + coordElementFrom = dataSetFrom.GetPoint( idElementFrom ) + else: + # Get the coordinates of each points of the cell. + nbPointsFrom: int = dataSetFrom.GetCell( idElementFrom ).GetNumberOfPoints() + cellPointsFrom: vtkPoints = dataSetFrom.GetCell( idElementFrom ).GetPoints() + coordPointsFrom: list = [] + for idPointFrom in range( nbPointsFrom ): + coordPointsFrom.extend( cellPointsFrom.GetPoint( idPointFrom ) ) + coordElementFrom = tuple( coordPointsFrom ) + + if coordElementFrom == coordElementTo: + elementMap[ flatIdDataSetTo ][ idElementTo ] = [ flatIdDataSetFrom, idElementFrom ] + ElementFromFund = True + idElementsFromFund.append( idElementFrom ) + + idElementFrom += 1 def has_array( mesh: vtkUnstructuredGrid, array_names: list[ str ] ) -> bool: diff --git a/geos-mesh/tests/conftest.py b/geos-mesh/tests/conftest.py index ea0810593..2e9dc030a 100644 --- a/geos-mesh/tests/conftest.py +++ b/geos-mesh/tests/conftest.py @@ -160,19 +160,25 @@ def _get_dataset( datasetType: str ) -> Union[ vtkMultiBlockDataSet, vtkPolyData """ reader: Union[ vtkXMLMultiBlockDataReader, vtkXMLUnstructuredGridReader ] if datasetType == "multiblock": - reader: vtkXMLMultiBlockDataReader = vtkXMLMultiBlockDataReader() + reader = vtkXMLMultiBlockDataReader() vtkFilename = "data/displacedFault.vtm" elif datasetType == "emptymultiblock": - reader: vtkXMLMultiBlockDataReader = vtkXMLMultiBlockDataReader() + reader = vtkXMLMultiBlockDataReader() vtkFilename = "data/displacedFaultempty.vtm" + elif datasetType == "fracture": + reader = vtkXMLUnstructuredGridReader() + vtkFilename = "data/fracture_res5_id.vtu" + elif datasetType == "emptyFracture": + reader = vtkXMLUnstructuredGridReader() + vtkFilename = "data/fracture_res5_id_empty.vtu" elif datasetType == "dataset": - reader: vtkXMLUnstructuredGridReader = vtkXMLUnstructuredGridReader() + reader = vtkXMLUnstructuredGridReader() vtkFilename = "data/domain_res5_id.vtu" elif datasetType == "emptydataset": - reader: vtkXMLUnstructuredGridReader = vtkXMLUnstructuredGridReader() + reader = vtkXMLUnstructuredGridReader() vtkFilename = "data/domain_res5_id_empty.vtu" elif datasetType == "polydata": - reader: vtkXMLUnstructuredGridReader = vtkXMLUnstructuredGridReader() + reader = vtkXMLUnstructuredGridReader() vtkFilename = "data/triangulatedSurface.vtu" datapath: str = os.path.join( os.path.dirname( os.path.realpath( __file__ ) ), vtkFilename ) reader.SetFileName( datapath ) @@ -181,3 +187,42 @@ def _get_dataset( datasetType: str ) -> Union[ vtkMultiBlockDataSet, vtkPolyData return reader.GetOutput() return _get_dataset + + +@pytest.fixture +def getElementMap() -> Any: + """Get the element indexes mapping dictionary using the function _get_elementMap() between two meshes. + + Returns: + elementMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary. + """ + + def _get_elementMap( meshFromName: str, meshToName: str, points: bool ) -> dict[ int, npt.NDArray[ np.int64 ] ]: + """Get the element indexes mapping dictionary between two meshes + + Args: + meshFromName (str): The name of the meshFrom. + meshToName (str): The name of the meshTo. + points (bool): True if elements to map is points, False if it is cells. + + Returns: + elementMap (dict[int, npt.NDArray[np.int64]]): The element mapping dictionary. + """ + elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = {} + nbElements: tuple[ int, int ] = ( 4092, 212 ) if points else ( 1740, 156 ) + if meshFromName == "multiblock" and meshToName == "emptymultiblock": + elementMap[ 1 ] = np.array( [ [ 1, element ] for element in range( nbElements[ 0 ] ) ] ) + elementMap[ 2 ] = np.array( [ [ 2, element ] for element in range( nbElements[ 1 ] ) ] ) + elif meshFromName == "multiblock" and meshToName == "emptyFracture": + elementMap[ 0 ] = np.array( [ [ 2, element ] for element in range( nbElements[ 1 ] ) ] ) + elif meshFromName == "dataset" and meshToName == "emptyFracture": + elementMap[ 0 ] = np.full( ( nbElements[ 1 ], 2), -1, np.int64 ) + elif meshFromName == "fracture" and meshToName == "emptyFracture": + elementMap[ 0 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 1 ] ) ] ) + elif meshFromName == "fracture" and meshToName == "emptymultiblock": + elementMap[ 1 ] = np.full( ( nbElements[ 0 ], 2 ), -1, np.int64 ) + elementMap[ 2 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 1 ] ) ] ) + + return elementMap + + return _get_elementMap From 2bb18c1a457068a8e95554ab6e08d27729e7f725 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 5 Sep 2025 15:59:15 +0200 Subject: [PATCH 20/45] add teh function to transfer an attribute from a mesh to another with a map --- .../src/geos/mesh/utils/arrayModifiers.py | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index e5c8b0a6a..3d64f27a4 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -41,6 +41,7 @@ isAttributeGlobal, getVtkArrayTypeInObject, getVtkArrayTypeInMultiBlock, + getVtkDataTypeInObject, getNumberOfComponentsMultiBlock, ) from geos.mesh.utils.multiblockHelpers import ( @@ -690,6 +691,164 @@ def copyAttributeDataSet( return createAttribute( dataSetTo, npArray, attributeNameTo, componentNames, onPoints, vtkArrayType, logger ) +def transferAttributeToDataSetWithElementMap( + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], + dataSetTo: vtkDataSet, + dictElementMap: dict[ int, npt.NDArray[ np.int64] ], + attributeName: str, + onPoints: bool, + flatIdDataSetTo: int = 0, + logger: Union[ Logger, Any ] = None, +) -> bool: + """Transfer attributes from the meshFrom to the dataSetTo using a mapping. + + If meshFrom is a vtkDataSet, the flat index (flatIdDataSetFrom) is set to 0. + dataSetTo is considered as block of vtkMultiblockDataSet, if not the flat index (flatIdDataSetTo) is set to 0. + + The map used to transfer the attribute is a dictionary where: + - The keys is the flat index of the dataSetTo (flatIdDataSetTo). + - The item is an array of size (nb elements in dataSetTo, 2). + + If an element (idElementTo) of the dataSetTo (flatIdDataSetTo) is mapped with no element of the meshFrom: + - dictElementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. + - The value of the attribute for this element depends of the type of the value of the attribute (0 for unit, -1 for int, nan for float). + + If an element (idElementTo) of the dataSetTo (flatIdDataSetTo) is mapped with an element (idElementFrom) of one of the meshFrom's dataset (flatIdDataSetFrom): + - dictElementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. + - The value of the attribute for this element is the value of the element (idElementFrom) of the meshFrom on on the dataSetFrom (flatIdDataSetFrom). + + Args: + meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the attribute to transfer. + dataSetTo (vtkDataSet): The mesh where to transfer the attribute. + dictElementMap (dict[int, npt.NDArray[np.int64]]): The elementMap. + attributeName (str): The name of the attribute to transfer. + onPoints (bool): True if the attribute is on points, False if it is on cells. + flatIdDataSetTo (int, Optional): The flat index of the dataSetTo. + Defaults to 0. + logger (Union[Logger, None], optional): A logger to manage the output messages. + Defaults to None, an internal logger is used. + + Returns: + bool: True if transfer successfully ended. + """ + # Check if an external logger is given. + if logger is None: + logger = getLogger( "transferAttributeToDataSetWithElementMap", True ) + + if flatIdDataSetTo not in dictElementMap.keys(): + logger.error( f"The map is incomplete, there is no data for the dataSetTo (flat index { flatIdDataSetTo })." ) + return False + + nbElementsTo: int = dataSetTo.GetNumberOfPoints() if onPoints else dataSetTo.GetNumberOfCells() + if len( dictElementMap[ flatIdDataSetTo ] ) != nbElementsTo: + logger.error( f"The map is wrong, there is { nbElementsTo } elements in the the dataSetTo (flat index { flatIdDataSetTo })\ + but { len( dictElementMap[ flatIdDataSetTo ] ) } elements in the map." ) + return False + + componentNames: tuple[ str, ...] = getComponentNames( meshFrom, attributeName, onPoints ) + nbComponents: int = len( componentNames ) + + vtkDataType: int = getVtkDataTypeInObject( meshFrom, attributeName, onPoints ) + defaultValue: Any + if vtkDataType in ( VTK_FLOAT, VTK_DOUBLE ): + defaultValue = np.nan + elif vtkDataType in ( VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE ): + defaultValue = -1 + elif vtkDataType in ( VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT, + VTK_UNSIGNED_LONG_LONG ): + defaultValue = 0 + + typeMapping: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap() + valueType: type = typeMapping[ vtkDataType ] + + arrayTo: npt.NDArray[ Any ] + if nbComponents > 1: + defaultValue = [ defaultValue ] * nbComponents + arrayTo = np.full( ( nbElementsTo, nbComponents ), defaultValue, dtype=valueType ) + else: + arrayTo = np.array( [ defaultValue for _ in range( nbElementsTo ) ], dtype=valueType ) + + for idElementTo in range( nbElementsTo ): + valueToTransfer: Any = defaultValue + idElementFrom: int = dictElementMap[ flatIdDataSetTo ][ idElementTo ][ 1 ] + if idElementFrom != -1: + flatIdDataSetFrom = dictElementMap[ flatIdDataSetTo ][ idElementTo ][ 0 ] + dataFrom: Union[ vtkPointData, vtkCellData ] + if isinstance( meshFrom, vtkDataSet ): + dataFrom = meshFrom.GetPointData() if onPoints else meshFrom.GetCellData() + elif isinstance( meshFrom, vtkMultiBlockDataSet ): + dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( meshFrom.GetDataSet( flatIdDataSetFrom ) ) + dataFrom = dataSetFrom.GetPointData() if onPoints else dataSetFrom.GetCellData() + arrayFrom: npt.NDArray[ Any ] = vnp.vtk_to_numpy( dataFrom.GetArray( attributeName ) ) + valueToTransfer = arrayFrom[ idElementFrom ] + arrayTo[ idElementTo ] = valueToTransfer + + return createAttribute( dataSetTo, + arrayTo, + attributeName, + componentNames, + onPoints=onPoints, + vtkDataType=vtkDataType, + logger=logger ) + + +def transferAttributeWithElementMap( + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], + dictElementMap: dict[ int, npt.NDArray[ np.int64] ], + attributeName: str, + onPoints: bool, + logger: Union[ Logger, Any ] = None, +) -> bool: + """Transfer attributes from the meshFrom to the meshTo using a mapping. + + If meshFrom is a vtkDataSet, the flat index (flatIdDataSetFrom) is set to 0. + If meshTo is a vtkDataSet, the flat index (flatIdDataSetTo) is set to 0. + + The elementMap is a dictionary where: + - Keys are the flat index of all the datasets of the meshTo. + - Items are arrays of size (nb elements in datasets, 2). + + If an element (idElementTo) of one of the meshTo's dataSet (flatIdDataSetTo) is mapped with no element of the meshFrom: + - dictElementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. + - The value of the attribute for this element depends of the type of the value of the attribute (0 for unit, -1 for int, nan for float). + + If an element (idElementTo) of one of the meshTo's dataSet (flatIdDataSetTo) is mapped with an element (idElementFrom) of one of the meshFrom's dataset (flatIdDataSetFrom): + - dictElementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. + - The value of the attribute for this element is the value of the element (idElementFrom) of the meshFrom on on the dataSetFrom (flatIdDataSetFrom). + + Args: + meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the attribute to transfer. + meshTo (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh where to transfer the attribute. + dictElementMap (dict[int, npt.NDArray[np.int64]]): The elementMap. + attributeName (str): The name of the attribute to transfer. + onPoints (bool): True if the attribute is on points, False if it is on cells. + flatIdDataSetTo (int, Optional): The flat index of the dataSetTo. + Defaults to 0. + logger (Union[Logger, None], optional): A logger to manage the output messages. + Defaults to None, an internal logger is used. + + Returns: + bool: True if transfer successfully ended. + """ + # Check if an external logger is given. + if logger is None: + logger = getLogger( "transferAttributeWithElementMap", True ) + + if isinstance( meshTo, vtkDataSet ): + return transferAttributeToDataSetWithElementMap( meshFrom, meshTo, dictElementMap, attributeName, onPoints, logger=logger ) + elif isinstance( meshTo, vtkMultiBlockDataSet ): + listFlatIdMultiBlockDataSetTo: list[ int ] = getBlockElementIndexesFlatten( meshTo ) + for flatIdDataSetTo in range( listFlatIdMultiBlockDataSetTo ): + dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( meshTo.GetDataSet( flatIdDataSetTo ) ) + if not transferAttributeToDataSetWithElementMap( meshFrom, dataSetTo, dictElementMap, attributeName, onPoints, flatIdDataSetTo=flatIdDataSetTo, logger=logger ): + logger.error( f"The attribute transfer has failed for the dataset with the flat index { flatIdDataSetTo } of the meshTo.") + return False + return True + + return False + + def renameAttribute( object: Union[ vtkMultiBlockDataSet, vtkDataSet ], attributeName: str, From 2f30167fcd23690d7f4ced6a341188f4ef6aec94 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 5 Sep 2025 16:37:51 +0200 Subject: [PATCH 21/45] update to choose the piece of the attribute to transefer --- .../geos/mesh/processing/AttributeMapping.py | 161 +++++------------- 1 file changed, 42 insertions(+), 119 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py index 352e73ebc..631aef2e3 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py @@ -5,29 +5,24 @@ import numpy as np import numpy.typing as npt from geos.utils.Logger import logging, Logger, getLogger -from typing_extensions import Self, Union, Any -import vtkmodules.util.numpy_support as vnp +from typing_extensions import Self, Union -from vtk import ( # type: ignore[import-untyped] - VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT, VTK_UNSIGNED_LONG_LONG, - VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE, VTK_FLOAT, VTK_DOUBLE, -) from vtkmodules.vtkCommonDataModel import ( vtkDataSet, vtkMultiBlockDataSet, ) -from geos.mesh.utils.arrayModifiers import createAttribute -from geos.mesh.utils.arrayHelpers import ( computeCellMapping, getAttributeSet, getComponentNames, - getVtkDataTypeInObject, isAttributeGlobal ) +from geos.mesh.utils.arrayModifiers import transferAttributeWithElementMap +from geos.mesh.utils.arrayHelpers import ( computeElementMapping, getAttributeSet, isAttributeGlobal ) __doc__ = """ -AttributeMapping is a vtk filter that transfer global attributes from a source mesh (meshFrom) to another (meshTo) for each -cell of the two meshes with the same bounds coordinates. +AttributeMapping is a vtk filter that transfer global attributes from a meshFrom to a meshTo for each +cell or point of the two meshes with the same coordinates. For cell, the coordinates of the points in the cell are compared. The filter update the mesh where attributes are transferred directly, no copy is created. Input and output meshes can be vtkDataSet or vtkMultiBlockDataSet. The names of the attributes to transfer are give with a set of string. +The localization of the attributes to transfer is a bool, True for points, False for cells. All the attributes must be on the same piece. To use a handler of yours, set the variable 'speHandler' to True and add it using the member function addLoggerHandler. To use the filter: @@ -36,17 +31,19 @@ from filters.AttributeMapping import AttributeMapping - # filter inputs. + # Filter inputs. meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] attributeNames: set[ str ] # Optional inputs. - speHandler: bool + onPoints: bool # defaults to False + speHandler: bool # defaults to False - # instantiate the filter + # Instantiate the filter filter :AttributeMapping = AttributeMapping( meshFrom, meshTo, attributeNames, + onPoints, speHandler, ) @@ -68,23 +65,28 @@ def __init__( meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], attributeNames: set[ str ], + onPoints: bool = False, speHandler: bool = False, ) -> None: - """Map attributes of the source mesh (meshFrom) to the other mesh (meshTo). + """Transfer global attributes from the meshFrom to the meshTo mapping the piece of the attributes to transfer. Args: meshFrom (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The mesh with attributes to transfer. - meshTo (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The mesh where to copy attributes. + meshTo (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The mesh where to transfer attributes. attributeNames (set[str]): Names of the attributes to transfer. + onPoints (bool): True if attributes are on points, False if they are on cells. + Defaults to False. speHandler (bool, optional): True to use a specific handler, False to use the internal handler. Defaults to False. """ self.meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] = meshFrom self.meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] = meshTo self.attributeNames: set[ str ] = attributeNames + self.onPoints: bool = onPoints + self.piece: str = "points" if self.onPoints else "cells" # cell map - self.dictCellMap: dict[ int, npt.NDArray[ np.int64 ] ] = {} + self.ElementMap: dict[ int, npt.NDArray[ np.int64 ] ] = {} # Logger. self.logger: Logger @@ -109,16 +111,18 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: "The logger already has an handler, to use yours set the argument 'speHandler' to True during the filter initialization." ) - def GetCellMap( self: Self ) -> dict[ int, npt.NDArray[ np.int64 ] ]: - """Getter of cell mapping dictionary. + def GetElementMap( self: Self ) -> dict[ int, npt.NDArray[ np.int64 ] ]: + """Getter of the element mapping dictionary. + + If attribute to transfer are on points it will be a pointMap, else it will be a cellMap. Returns: - self.dictCellMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary. + self.elementMap (dict[int, npt.NDArray[np.int64]]): The element mapping dictionary. """ - return self.dictCellMap + return self.ElementMap def applyFilter( self: Self ) -> bool: - """Map attributes from the source mesh (meshFrom) to the other (meshTo) for the shared cells. + """Transfer attributes on element from the meshFrom to the meshTo with the elementMap. Returns: boolean (bool): True if calculation successfully ended, False otherwise. @@ -126,14 +130,14 @@ def applyFilter( self: Self ) -> bool: self.logger.info( f"Apply filter { self.logger.name }." ) if len( self.attributeNames ) == 0: - self.logger.warning( "Please enter at least one attribute to transfer." ) + self.logger.warning( f"Please enter at least one { self.piece } attribute to transfer." ) self.logger.warning( f"The filter { self.logger.name } has not been used." ) return False wrongAttributeNames: list[ str ] = [] attributesAlreadyInMeshTo: list = [] - attributesInMeshFrom: set[ str ] = getAttributeSet( self.meshFrom, False ) - attributesInMeshTo: set[ str ] = getAttributeSet( self.meshTo, False ) + attributesInMeshFrom: set[ str ] = getAttributeSet( self.meshFrom, self.onPoints ) + attributesInMeshTo: set[ str ] = getAttributeSet( self.meshTo, self.onPoints ) for attributeName in self.attributeNames: if attributeName not in attributesInMeshFrom: wrongAttributeNames.append( attributeName ) @@ -143,13 +147,13 @@ def applyFilter( self: Self ) -> bool: if len( wrongAttributeNames ) > 0: self.logger.error( - f"The attributes { wrongAttributeNames } are not in the mesh from where to transfer attributes." ) + f"The { self.piece } attributes { wrongAttributeNames } are not in the mesh from where to transfer attributes." ) self.logger.error( f"The filter { self.logger.name } failed." ) return False if len( attributesAlreadyInMeshTo ) > 0: self.logger.error( - f"The attributes { attributesAlreadyInMeshTo } are already in the mesh where attributes must be transferred." + f"The { self.piece } attributes { attributesAlreadyInMeshTo } are already in the mesh where attributes must be transferred." ) self.logger.error( f"The filter { self.logger.name } failed." ) return False @@ -157,114 +161,33 @@ def applyFilter( self: Self ) -> bool: if isinstance( self.meshFrom, vtkMultiBlockDataSet ): partialAttributes: list[ str ] = [] for attributeName in self.attributeNames: - if not isAttributeGlobal( self.meshFrom, attributeName, False ): + if not isAttributeGlobal( self.meshFrom, attributeName, self.onPoints ): partialAttributes.append( attributeName ) if len( partialAttributes ) > 0: - self.logger.error( f"All attributes to transfer must be global, { partialAttributes } are partials." ) + self.logger.error( f"All { self.piece } attributes to transfer must be global, { partialAttributes } are partials." ) self.logger.error( f"The filter { self.logger.name } failed." ) - self.dictCellMap = computeCellMapping( self.meshFrom, self.meshTo ) - sharedCell: bool = False - for key in self.dictCellMap: - if np.any( self.dictCellMap[ key ] > -1 ): - sharedCell = True + self.ElementMap = computeElementMapping( self.meshFrom, self.meshTo, self.onPoints ) + sharedElement: bool = False + for key in self.ElementMap: + if np.any( self.ElementMap[ key ] > -1 ): + sharedElement = True - if not sharedCell: - self.logger.warning( "The two meshes do not have any shared cell." ) + if not sharedElement: + self.logger.warning( f"The two meshes do not have any shared { self.piece }." ) self.logger.warning( f"The filter { self.logger.name } has not been used." ) return False for attributeName in self.attributeNames: - componentNames: tuple[ str, ...] = getComponentNames( self.meshFrom, attributeName, False ) - - vtkDataType: int = getVtkDataTypeInObject( self.meshFrom, attributeName, False ) - defaultValue: Any - if vtkDataType in ( VTK_FLOAT, VTK_DOUBLE ): - defaultValue = np.nan - elif vtkDataType in ( VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE ): - defaultValue = -1 - elif vtkDataType in ( VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT, - VTK_UNSIGNED_LONG_LONG ): - defaultValue = 0 - - if isinstance( self.meshTo, vtkDataSet ): - if not self._transferAttribute( self.meshFrom, self.meshTo, attributeName, componentNames, vtkDataType, - defaultValue ): - return False - elif isinstance( self.meshTo, vtkMultiBlockDataSet ): - nbBlocksTo: int = self.meshTo.GetNumberOfBlocks() - for idBlockTo in range( nbBlocksTo ): - blockTo: vtkDataSet = vtkDataSet.SafeDownCast( self.meshTo.GetBlock( idBlockTo ) ) - if not self._transferAttribute( self.meshFrom, blockTo, attributeName, componentNames, vtkDataType, - defaultValue, idBlockTo ): - return False + if not transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, self.onPoints, self.logger ): + return False # Log the output message. self._logOutputMessage() return True - def _transferAttribute( - self: Self, - meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], - dataSetTo: vtkDataSet, - attributeName: str, - componentNames: tuple[ str, ...], - vtkDataType: int, - defaultValue: Any, - idBlockTo: int = 0, - ) -> bool: - """Transfer attributes from the source mesh to the working meshes using cell mapping. - - dataSetTo is considered as block of a vtkMultiblockDataSet, if not, 0 is use as block index. - - Args: - meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with attributes to transfer. - dataSetTo (vtkDataSet): The mesh where to transfer attributes. - attributeName (str): The name of the attribute to transfer. - componentNames (tuple[str, ...]): The name of the component of the attributes to transfer. If no component, set an empty tuple. - vtkDataType (int): The vtk type of the attribute to transfer. - defaultValue (Any): The value to use for the cell not mapped. - idBlockTo (int, Optional): The block index of the dataSetTo. - Defaults to 0. - - Returns: - bool: True if transfer successfully ended. - """ - nbCellTo: int = dataSetTo.GetNumberOfCells() - nbComponents: int = len( componentNames ) - typeMapping: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap() - valueType: type = typeMapping[ vtkDataType ] - arrayTo: npt.NDArray[ Any ] - if nbComponents > 1: - defaultValue = [ defaultValue ] * nbComponents - arrayTo = np.full( ( nbCellTo, nbComponents ), defaultValue, dtype=valueType ) - else: - arrayTo = np.array( [ defaultValue for _ in range( nbCellTo ) ], dtype=valueType ) - - for idCellTo in range( nbCellTo ): - value: Any = defaultValue - idCellFrom: int = self.dictCellMap[ idBlockTo ][ idCellTo ][ 1 ] - if idCellFrom != -1: - idBlockFrom = self.dictCellMap[ idBlockTo ][ idCellTo ][ 0 ] - arrayFrom: npt.NDArray[ Any ] - if isinstance( meshFrom, vtkDataSet ): - arrayFrom = vnp.vtk_to_numpy( meshFrom.GetCellData().GetArray( attributeName ) ) - elif isinstance( meshFrom, vtkMultiBlockDataSet ): - blockFrom: vtkDataSet = vtkDataSet.SafeDownCast( meshFrom.GetBlock( idBlockFrom ) ) - arrayFrom = vnp.vtk_to_numpy( blockFrom.GetCellData().GetArray( attributeName ) ) - value = arrayFrom[ idCellFrom ] - arrayTo[ idCellTo ] = value - - return createAttribute( dataSetTo, - arrayTo, - attributeName, - componentNames, - onPoints=False, - vtkDataType=vtkDataType, - logger=self.logger ) - def _logOutputMessage( self: Self ) -> None: """Create and log result messages of the filter.""" self.logger.info( f"The filter { self.logger.name } succeed." ) From 8438fc7895c6517c814e34e60bffcde8f74170ea Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 5 Sep 2025 17:22:11 +0200 Subject: [PATCH 22/45] test of the vtk filter AttributeMapping --- geos-mesh/tests/test_AttributeMapping.py | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 geos-mesh/tests/test_AttributeMapping.py diff --git a/geos-mesh/tests/test_AttributeMapping.py b/geos-mesh/tests/test_AttributeMapping.py new file mode 100644 index 000000000..ce053509b --- /dev/null +++ b/geos-mesh/tests/test_AttributeMapping.py @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. +# SPDX-FileContributor: Romain Baville +# ruff: noqa: E402 # disable Module level import not at top of file +import pytest + +from typing import Union, Any +from geos.mesh.utils.arrayModifiers import fillAllPartialAttributes +from geos.mesh.processing.AttributeMapping import AttributeMapping + +from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet, vtkDataSet + +@pytest.mark.parametrize( "meshFromName, meshToName, attributeNames, onPoints", [ + ( "fracture", "emptyFracture", set( [ "collocated_nodes" ] ), True ), + ( "multiblock", "emptyFracture", set( [ "FAULT" ] ), False ), + ( "multiblock", "emptymultiblock", set( [ "FAULT" ] ), False ), + ( "dataset", "emptymultiblock", set( [ "FAULT" ] ), False ), + ( "dataset", "emptydataset", set( [ "FAULT" ] ), False ), +] ) +def test_AttributeMapping( + dataSetTest: Any, + meshFromName: str, + meshToName: str, + attributeNames: set[ str ], + onPoints: bool, +) -> None: + meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshFromName ) + meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshToName ) + if isinstance( meshFrom, vtkMultiBlockDataSet ): + fillAllPartialAttributes( meshFrom ) + + filter = AttributeMapping( meshFrom, meshTo, attributeNames, onPoints ) + assert filter.applyFilter() From b90e8f711a740d910949e33d480a7f600048a0de Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 5 Sep 2025 17:23:54 +0200 Subject: [PATCH 23/45] fix nullblocks issues --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 16 +++++++++------- geos-mesh/src/geos/mesh/utils/arrayModifiers.py | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index a7586a66d..92078a493 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -559,14 +559,16 @@ def isAttributeGlobal( object: vtkMultiBlockDataSet, attributeName: str, onPoint Returns: bool: True if the attribute is global, False if not. """ - isOnBlock: bool - nbBlock: int = object.GetNumberOfBlocks() - for idBlock in range( nbBlock ): - block: vtkDataSet = vtkDataSet.SafeDownCast( object.GetBlock( idBlock ) ) - isOnBlock = isAttributeInObjectDataSet( block, attributeName, onPoints ) - if not isOnBlock: + iterator: vtkDataObjectTreeIterator = vtkDataObjectTreeIterator() + iterator.SetDataSet( object ) + iterator.VisitOnlyLeavesOn() + iterator.GoToFirstItem() + while iterator.GetCurrentDataObject() is not None: + dataSet: vtkDataSet = vtkDataSet.SafeDownCast( iterator.GetCurrentDataObject() ) + if not isAttributeInObjectDataSet( dataSet, attributeName, onPoints ): return False - + + iterator.GoToNextItem() return True diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 3d64f27a4..62ee65dcc 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -839,7 +839,7 @@ def transferAttributeWithElementMap( return transferAttributeToDataSetWithElementMap( meshFrom, meshTo, dictElementMap, attributeName, onPoints, logger=logger ) elif isinstance( meshTo, vtkMultiBlockDataSet ): listFlatIdMultiBlockDataSetTo: list[ int ] = getBlockElementIndexesFlatten( meshTo ) - for flatIdDataSetTo in range( listFlatIdMultiBlockDataSetTo ): + for flatIdDataSetTo in listFlatIdMultiBlockDataSetTo: dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( meshTo.GetDataSet( flatIdDataSetTo ) ) if not transferAttributeToDataSetWithElementMap( meshFrom, dataSetTo, dictElementMap, attributeName, onPoints, flatIdDataSetTo=flatIdDataSetTo, logger=logger ): logger.error( f"The attribute transfer has failed for the dataset with the flat index { flatIdDataSetTo } of the meshTo.") From 147351ef7babfcccee97a550c9a2064aab5bdc30 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 5 Sep 2025 19:10:53 +0200 Subject: [PATCH 24/45] add the test for the function transferAttribute --- geos-mesh/tests/test_arrayModifiers.py | 53 ++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index 91ecc423e..0324a8890 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -479,6 +479,59 @@ def test_copyAttributeDataSet( assert vtkDataTypeCopied == vtkDataTypeTest +@pytest.mark.parametrize( "meshFromName, meshToName, attributeName, onPoints, defaultValueTest", [ + ( "fracture", "emptyFracture", "collocated_nodes", True, [ -1, -1 ] ), + ( "multiblock", "emptyFracture", "FAULT", False, -1 ), + ( "multiblock", "emptymultiblock", "FAULT", False, -1 ), + ( "dataset", "emptymultiblock", "FAULT", False, -1 ), + ( "dataset", "emptydataset", "FAULT", False, -1 ), +] ) +def test_transferAttributeWithElementMap( + dataSetTest: Any, + getElementMap: dict[ int, npt.NDArray[ np.int64 ] ], + meshFromName: str, + meshToName: str, + attributeName: str, + onPoints: bool, + defaultValueTest: Any, +) -> None: + "Test to transfer attributes from the meshFrom to the dataSetTo using an elementMap." + meshFrom: Union[ vtkMultiBlockDataSet, vtkDataSet ] = dataSetTest( meshFromName ) + if isinstance( meshFrom, vtkMultiBlockDataSet ): + arrayModifiers.fillAllPartialAttributes( meshFrom ) + + meshTo: Union[ vtkMultiBlockDataSet, vtkDataSet ] = dataSetTest( meshToName ) + elementMap: dict[ int, npt.NDArray[ np.int64] ] = getElementMap( meshFromName, meshToName, onPoints ) + + assert arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, attributeName, onPoints ) + + for flatIdDataSetTo in elementMap: + dataTo: Union[ vtkPointData, vtkCellData ] + if isinstance( meshTo, vtkDataSet ): + dataTo = meshTo.GetPointData() if onPoints else meshTo.GetCellData() + elif isinstance( meshTo, vtkMultiBlockDataSet ): + dataSetTo: vtkDataSet = meshTo.GetDataSet( flatIdDataSetTo ) + dataTo = dataSetTo.GetPointData() if onPoints else dataSetTo.GetCellData() + + arrayTo: npt.NDArray[ Any ] = vnp.vtk_to_numpy( dataTo.GetArray( attributeName ) ) + for idElementTo in range( len( arrayTo ) ): + idElementFrom: int = elementMap[ flatIdDataSetTo ][ idElementTo ][ 1 ] + if idElementFrom == -1: + assert arrayTo[ idElementTo ] == defaultValueTest + + else: + dataFrom: Union[ vtkPointData, vtkCellData ] + if isinstance( meshFrom, vtkDataSet ): + dataFrom= meshFrom.GetPointData() if onPoints else meshFrom.GetCellData() + elif isinstance( meshFrom, vtkMultiBlockDataSet ): + flatIdDataSetFrom: int = elementMap[ flatIdDataSetTo ][ idElementTo ][ 0 ] + dataSetFrom: vtkDataSet = meshFrom.GetDataSet( flatIdDataSetFrom ) + dataFrom = dataSetFrom.GetPointData() if onPoints else dataSetFrom.GetCellData() + + arrayFrom: npt.NDArray[ Any ] = vnp.vtk_to_numpy( dataFrom.GetArray( attributeName ) ) + assert np.all( arrayTo[ idElementTo ] == arrayFrom[ idElementFrom ] ) + + @pytest.mark.parametrize( "attributeName, onPoints", [ ( "CellAttribute", False ), ( "PointAttribute", True ), From c93e188de881eaff248d7d842681226f2a40591b Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 5 Sep 2025 19:11:33 +0200 Subject: [PATCH 25/45] fix for test --- .../src/geos/mesh/utils/arrayModifiers.py | 38 +++++++++---------- geos-mesh/tests/conftest.py | 5 +++ 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 62ee65dcc..9a59533b2 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -694,13 +694,13 @@ def copyAttributeDataSet( def transferAttributeToDataSetWithElementMap( meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], dataSetTo: vtkDataSet, - dictElementMap: dict[ int, npt.NDArray[ np.int64] ], + elementMap: dict[ int, npt.NDArray[ np.int64] ], attributeName: str, onPoints: bool, flatIdDataSetTo: int = 0, logger: Union[ Logger, Any ] = None, ) -> bool: - """Transfer attributes from the meshFrom to the dataSetTo using a mapping. + """Transfer attributes from the meshFrom to the dataSetTo using an elementMap. If meshFrom is a vtkDataSet, the flat index (flatIdDataSetFrom) is set to 0. dataSetTo is considered as block of vtkMultiblockDataSet, if not the flat index (flatIdDataSetTo) is set to 0. @@ -710,17 +710,17 @@ def transferAttributeToDataSetWithElementMap( - The item is an array of size (nb elements in dataSetTo, 2). If an element (idElementTo) of the dataSetTo (flatIdDataSetTo) is mapped with no element of the meshFrom: - - dictElementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. + - elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. - The value of the attribute for this element depends of the type of the value of the attribute (0 for unit, -1 for int, nan for float). If an element (idElementTo) of the dataSetTo (flatIdDataSetTo) is mapped with an element (idElementFrom) of one of the meshFrom's dataset (flatIdDataSetFrom): - - dictElementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. + - elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. - The value of the attribute for this element is the value of the element (idElementFrom) of the meshFrom on on the dataSetFrom (flatIdDataSetFrom). Args: meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the attribute to transfer. dataSetTo (vtkDataSet): The mesh where to transfer the attribute. - dictElementMap (dict[int, npt.NDArray[np.int64]]): The elementMap. + elementMap (dict[int, npt.NDArray[np.int64]]): The elementMap. attributeName (str): The name of the attribute to transfer. onPoints (bool): True if the attribute is on points, False if it is on cells. flatIdDataSetTo (int, Optional): The flat index of the dataSetTo. @@ -735,14 +735,14 @@ def transferAttributeToDataSetWithElementMap( if logger is None: logger = getLogger( "transferAttributeToDataSetWithElementMap", True ) - if flatIdDataSetTo not in dictElementMap.keys(): + if flatIdDataSetTo not in elementMap.keys(): logger.error( f"The map is incomplete, there is no data for the dataSetTo (flat index { flatIdDataSetTo })." ) return False nbElementsTo: int = dataSetTo.GetNumberOfPoints() if onPoints else dataSetTo.GetNumberOfCells() - if len( dictElementMap[ flatIdDataSetTo ] ) != nbElementsTo: + if len( elementMap[ flatIdDataSetTo ] ) != nbElementsTo: logger.error( f"The map is wrong, there is { nbElementsTo } elements in the the dataSetTo (flat index { flatIdDataSetTo })\ - but { len( dictElementMap[ flatIdDataSetTo ] ) } elements in the map." ) + but { len( elementMap[ flatIdDataSetTo ] ) } elements in the map." ) return False componentNames: tuple[ str, ...] = getComponentNames( meshFrom, attributeName, onPoints ) @@ -770,17 +770,19 @@ def transferAttributeToDataSetWithElementMap( for idElementTo in range( nbElementsTo ): valueToTransfer: Any = defaultValue - idElementFrom: int = dictElementMap[ flatIdDataSetTo ][ idElementTo ][ 1 ] + idElementFrom: int = elementMap[ flatIdDataSetTo ][ idElementTo ][ 1 ] if idElementFrom != -1: - flatIdDataSetFrom = dictElementMap[ flatIdDataSetTo ][ idElementTo ][ 0 ] dataFrom: Union[ vtkPointData, vtkCellData ] if isinstance( meshFrom, vtkDataSet ): dataFrom = meshFrom.GetPointData() if onPoints else meshFrom.GetCellData() elif isinstance( meshFrom, vtkMultiBlockDataSet ): + flatIdDataSetFrom: int = elementMap[ flatIdDataSetTo ][ idElementTo ][ 0 ] dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( meshFrom.GetDataSet( flatIdDataSetFrom ) ) dataFrom = dataSetFrom.GetPointData() if onPoints else dataSetFrom.GetCellData() + arrayFrom: npt.NDArray[ Any ] = vnp.vtk_to_numpy( dataFrom.GetArray( attributeName ) ) valueToTransfer = arrayFrom[ idElementFrom ] + arrayTo[ idElementTo ] = valueToTransfer return createAttribute( dataSetTo, @@ -795,12 +797,12 @@ def transferAttributeToDataSetWithElementMap( def transferAttributeWithElementMap( meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], - dictElementMap: dict[ int, npt.NDArray[ np.int64] ], + elementMap: dict[ int, npt.NDArray[ np.int64] ], attributeName: str, onPoints: bool, logger: Union[ Logger, Any ] = None, ) -> bool: - """Transfer attributes from the meshFrom to the meshTo using a mapping. + """Transfer attributes from the meshFrom to the meshTo using an elementMap. If meshFrom is a vtkDataSet, the flat index (flatIdDataSetFrom) is set to 0. If meshTo is a vtkDataSet, the flat index (flatIdDataSetTo) is set to 0. @@ -810,17 +812,17 @@ def transferAttributeWithElementMap( - Items are arrays of size (nb elements in datasets, 2). If an element (idElementTo) of one of the meshTo's dataSet (flatIdDataSetTo) is mapped with no element of the meshFrom: - - dictElementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. + - elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. - The value of the attribute for this element depends of the type of the value of the attribute (0 for unit, -1 for int, nan for float). If an element (idElementTo) of one of the meshTo's dataSet (flatIdDataSetTo) is mapped with an element (idElementFrom) of one of the meshFrom's dataset (flatIdDataSetFrom): - - dictElementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. + - elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. - The value of the attribute for this element is the value of the element (idElementFrom) of the meshFrom on on the dataSetFrom (flatIdDataSetFrom). Args: meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the attribute to transfer. meshTo (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh where to transfer the attribute. - dictElementMap (dict[int, npt.NDArray[np.int64]]): The elementMap. + elementMap (dict[int, npt.NDArray[np.int64]]): The elementMap. attributeName (str): The name of the attribute to transfer. onPoints (bool): True if the attribute is on points, False if it is on cells. flatIdDataSetTo (int, Optional): The flat index of the dataSetTo. @@ -836,18 +838,16 @@ def transferAttributeWithElementMap( logger = getLogger( "transferAttributeWithElementMap", True ) if isinstance( meshTo, vtkDataSet ): - return transferAttributeToDataSetWithElementMap( meshFrom, meshTo, dictElementMap, attributeName, onPoints, logger=logger ) + return transferAttributeToDataSetWithElementMap( meshFrom, meshTo, elementMap, attributeName, onPoints, logger=logger ) elif isinstance( meshTo, vtkMultiBlockDataSet ): listFlatIdMultiBlockDataSetTo: list[ int ] = getBlockElementIndexesFlatten( meshTo ) for flatIdDataSetTo in listFlatIdMultiBlockDataSetTo: dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( meshTo.GetDataSet( flatIdDataSetTo ) ) - if not transferAttributeToDataSetWithElementMap( meshFrom, dataSetTo, dictElementMap, attributeName, onPoints, flatIdDataSetTo=flatIdDataSetTo, logger=logger ): + if not transferAttributeToDataSetWithElementMap( meshFrom, dataSetTo, elementMap, attributeName, onPoints, flatIdDataSetTo=flatIdDataSetTo, logger=logger ): logger.error( f"The attribute transfer has failed for the dataset with the flat index { flatIdDataSetTo } of the meshTo.") return False return True - return False - def renameAttribute( object: Union[ vtkMultiBlockDataSet, vtkDataSet ], diff --git a/geos-mesh/tests/conftest.py b/geos-mesh/tests/conftest.py index 2e9dc030a..2f37be2af 100644 --- a/geos-mesh/tests/conftest.py +++ b/geos-mesh/tests/conftest.py @@ -215,6 +215,8 @@ def _get_elementMap( meshFromName: str, meshToName: str, points: bool ) -> dict[ elementMap[ 2 ] = np.array( [ [ 2, element ] for element in range( nbElements[ 1 ] ) ] ) elif meshFromName == "multiblock" and meshToName == "emptyFracture": elementMap[ 0 ] = np.array( [ [ 2, element ] for element in range( nbElements[ 1 ] ) ] ) + elif meshFromName == "dataset" and meshToName == "emptydataset": + elementMap[ 0 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 0 ] ) ] ) elif meshFromName == "dataset" and meshToName == "emptyFracture": elementMap[ 0 ] = np.full( ( nbElements[ 1 ], 2), -1, np.int64 ) elif meshFromName == "fracture" and meshToName == "emptyFracture": @@ -222,6 +224,9 @@ def _get_elementMap( meshFromName: str, meshToName: str, points: bool ) -> dict[ elif meshFromName == "fracture" and meshToName == "emptymultiblock": elementMap[ 1 ] = np.full( ( nbElements[ 0 ], 2 ), -1, np.int64 ) elementMap[ 2 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 1 ] ) ] ) + elif meshFromName == "dataset" and meshToName == "emptymultiblock": + elementMap[ 1 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 0 ] ) ] ) + elementMap[ 2 ] = np.full( ( nbElements[ 1 ], 2 ), -1, np.int64 ) return elementMap From 424938ee1da4cb76dd64331b4fefc192f15dbd60 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 8 Sep 2025 18:24:04 +0200 Subject: [PATCH 26/45] Fix double apply --- .../src/geos/pv/plugins/PVAttributeMapping.py | 130 ++++++++++-------- 1 file changed, 74 insertions(+), 56 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index 4ef9c9532..77bf5186d 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -38,8 +38,8 @@ ) __doc__ = """ -AttributeMapping is a paraview plugin that transfer global attributes from a source mesh to the other mesh for each -cell of the two meshes with the same bounds coordinates. +AttributeMapping is a paraview plugin that transfer global attributes from a meshFrom to a meshTo for each +cell or point of the two meshes with the same coordinates. For cell, the coordinates of the points in the cell are compared. Input and output meshes can be vtkDataSet or vtkMultiBlockDataSet. To use it: @@ -47,8 +47,8 @@ * Load the module in Paraview: Tools>Manage Plugins...>Load new>PVAttributeMapping. * Select the mesh to transfer the global attributes (meshTo). * Search and Select Attribute Mapping Filter. -* Select the source mesh with global attributes to transfer (meshFrom). -* Select global attributes to transfer from the source mesh (meshFrom) to the other mesh (meshTo). +* Select the mesh with global attributes to transfer (meshFrom). +* Select global attributes to transfer from the meshFrom to the meshTo. * Apply. """ @@ -72,56 +72,73 @@ def __init__( self: Self ) -> None: """Map attributes of the source mesh (meshFrom) to the other mesh (meshTo).""" super().__init__( nInputPorts=2, nOutputPorts=1, inputType="vtkObject", outputType="vtkObject" ) - # boolean to check if first use of the filter for attribute list initialization - self.m_firstUse = True + self.onPoints: bool = False + + self._initArraySelections: bool = True + self.cellAttributeNames: vtkDataArraySelection = vtkDataArraySelection() + self.pointAttributeNames: vtkDataArraySelection = vtkDataArraySelection() + + self.clearAttributeNames = True + self.attributeNames: list[ str ] = [] + + @smproperty.intvector( + name="AttributeType", + default_values=1, + number_of_elements=1, + ) + @smdomain.xml( """ + + + + + + """ ) + def e01SetFieldAssociation( self: Self, value: int ) -> None: + """Set attribute type. - # list of attribute names to transfer - self.m_attributes: vtkDataArraySelection = vtkDataArraySelection() - self.m_attributes.AddObserver( 0, createModifiedCallback( self ) ) - - @smproperty.dataarrayselection( name="AttributesToTransfer" ) - def a02GetAttributeToTransfer( self: Self ) -> vtkDataArraySelection: - """Get selected attribute names to transfer. - - Returns: - vtkDataArraySelection: selected attribute names. + Args: + value (int): 0 if on points, 1 if on cells. """ + self.onPoints = bool( value ) self.Modified() - return self.m_attributes - - def RequestInformation( - self: Self, - request: vtkInformation, # noqa: F841 - inInfoVec: list[ vtkInformationVector ], # noqa: F841 - outInfoVec: vtkInformationVector, - ) -> int: - """Inherited from VTKPythonAlgorithmBase::RequestInformation. + + @smproperty.stringvector( + name="SelectAttributeToTransfer", + label="Select Attribute To Transfer", + repeat_command=1, + number_of_elements_per_command="1", + element_types="2", + default_values="None", + ) + @smdomain.xml( """ + + + + + + + + Select attributes to transfer from the meshFrom To the meshTo. + + + + + """ ) + def a02SelectMultipleAttribute( self: Self, name: str ) -> None: + """Set the attribute to transfer from the meshFrom to the meshTo. Args: - request (vtkInformation): request - inInfoVec (list[vtkInformationVector]): input objects - outInfoVec (vtkInformationVector): output objects - - Returns: - int: 1 if calculation successfully ended, 0 otherwise. + name (str): The name of the attribute to transfer. """ - # only at initialization step, no change later - if self.m_firstUse: - # get cell ids - inData = self.GetInputData( inInfoVec, 1, 0 ) - assert isinstance( inData, ( vtkDataSet, vtkMultiBlockDataSet ) ), "Working mesh type is not supported." - - # update vtkDAS - attributeNames: set[ str ] = getAttributeSet( inData, False ) - - for attributeName in attributeNames: - if isinstance( inData, vtkMultiBlockDataSet ) and isAttributeGlobal( inData, attributeName, False ) \ - or isinstance( inData, vtkDataSet ) \ - and not self.m_attributes.ArrayExists( attributeName ): - self.m_attributes.AddArray( attributeName, False ) - - self.m_firstUse = False - return 1 + if self.clearAttributeNames: + self.attributeNames = [] + self.clearAttributeNames = False + + if name != "None": + self.attributeNames.append( name ) + self.Modified() def RequestDataObject( self: Self, @@ -139,14 +156,15 @@ def RequestDataObject( Returns: int: 1 if calculation successfully ended, 0 otherwise. """ - inData = self.GetInputData( inInfoVec, 0, 0 ) + inDataTo = 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 ) + assert inDataTo is not None + if outData is None or ( not outData.IsA( inDataTo.GetClassName() ) ): + outData = inDataTo.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 @@ -173,12 +191,12 @@ def RequestData( outData.ShallowCopy( meshTo ) - attributeNames: set[ str ] = set( getArrayChoices( self.a02GetAttributeToTransfer() ) ) - - filter: AttributeMapping = AttributeMapping( meshFrom, outData, attributeNames, True ) + filter: AttributeMapping = AttributeMapping( meshFrom, outData, set(self.attributeNames), self.onPoints, True ) if not filter.logger.hasHandlers(): filter.setLoggerHandler( VTKHandler() ) filter.applyFilter() + self.clearAttributeNames = True return 1 + From da835248508702ee445652eb451d4a79e1191a0d Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 9 Sep 2025 08:48:02 +0200 Subject: [PATCH 27/45] Clean for ci --- .../geos/mesh/processing/AttributeMapping.py | 9 +++-- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 16 ++++++-- .../src/geos/mesh/utils/arrayModifiers.py | 40 +++++++++++++------ geos-mesh/tests/conftest.py | 4 +- geos-mesh/tests/test_AttributeMapping.py | 24 ++++++----- geos-mesh/tests/test_arrayHelpers.py | 5 ++- geos-mesh/tests/test_arrayModifiers.py | 12 +++--- .../src/geos/pv/plugins/PVAttributeMapping.py | 15 +++---- 8 files changed, 75 insertions(+), 50 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py index 631aef2e3..e0e7d3617 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py @@ -147,7 +147,8 @@ def applyFilter( self: Self ) -> bool: if len( wrongAttributeNames ) > 0: self.logger.error( - f"The { self.piece } attributes { wrongAttributeNames } are not in the mesh from where to transfer attributes." ) + f"The { self.piece } attributes { wrongAttributeNames } are not in the mesh from where to transfer attributes." + ) self.logger.error( f"The filter { self.logger.name } failed." ) return False @@ -165,7 +166,8 @@ def applyFilter( self: Self ) -> bool: partialAttributes.append( attributeName ) if len( partialAttributes ) > 0: - self.logger.error( f"All { self.piece } attributes to transfer must be global, { partialAttributes } are partials." ) + self.logger.error( + f"All { self.piece } attributes to transfer must be global, { partialAttributes } are partials." ) self.logger.error( f"The filter { self.logger.name } failed." ) self.ElementMap = computeElementMapping( self.meshFrom, self.meshTo, self.onPoints ) @@ -180,7 +182,8 @@ def applyFilter( self: Self ) -> bool: return False for attributeName in self.attributeNames: - if not transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, self.onPoints, self.logger ): + if not transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, + self.onPoints, self.logger ): return False # Log the output message. diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index 92078a493..9eb342c95 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -129,9 +129,17 @@ def UpdateElementMappingToDataSet( nbElementsTo: int = dataSetTo.GetNumberOfPoints() if points else dataSetTo.GetNumberOfCells() elementMap[ flatIdDataSetTo ] = np.full( ( nbElementsTo, 2 ), -1, np.int64 ) if isinstance( meshFrom, vtkDataSet ): - UpdateDictElementMappingFromDataSetToDataSet( meshFrom, dataSetTo, elementMap, points, flatIdDataSetTo=flatIdDataSetTo ) + UpdateDictElementMappingFromDataSetToDataSet( meshFrom, + dataSetTo, + elementMap, + points, + flatIdDataSetTo=flatIdDataSetTo ) elif isinstance( meshFrom, vtkMultiBlockDataSet ): - UpdateElementMappingFromMultiBlockDataSetToDataSet( meshFrom, dataSetTo, elementMap, points, flatIdDataSetTo=flatIdDataSetTo ) + UpdateElementMappingFromMultiBlockDataSetToDataSet( meshFrom, + dataSetTo, + elementMap, + points, + flatIdDataSetTo=flatIdDataSetTo ) def UpdateElementMappingFromMultiBlockDataSetToDataSet( @@ -146,7 +154,7 @@ def UpdateElementMappingFromMultiBlockDataSetToDataSet( dataSetTo is considered as block of a vtkMultiblockDataSet, if not the flat index (flatIdDataSetTo) is set to 0. For each element (idElementTo) of the dataSetTo not yet mapped (elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]), - if the points coordinates of an element (idElementFrom) of a block (flatIdDataSetFrom) of multiBlockDataSetFrom + if the points coordinates of an element (idElementFrom) of a block (flatIdDataSetFrom) of multiBlockDataSetFrom are the same as the points coordinates of the elementTo, the elementMap is update to elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. @@ -567,7 +575,7 @@ def isAttributeGlobal( object: vtkMultiBlockDataSet, attributeName: str, onPoint dataSet: vtkDataSet = vtkDataSet.SafeDownCast( iterator.GetCurrentDataObject() ) if not isAttributeInObjectDataSet( dataSet, attributeName, onPoints ): return False - + iterator.GoToNextItem() return True diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 9a59533b2..b899cf3b1 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -694,7 +694,7 @@ def copyAttributeDataSet( def transferAttributeToDataSetWithElementMap( meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], dataSetTo: vtkDataSet, - elementMap: dict[ int, npt.NDArray[ np.int64] ], + elementMap: dict[ int, npt.NDArray[ np.int64 ] ], attributeName: str, onPoints: bool, flatIdDataSetTo: int = 0, @@ -708,7 +708,7 @@ def transferAttributeToDataSetWithElementMap( The map used to transfer the attribute is a dictionary where: - The keys is the flat index of the dataSetTo (flatIdDataSetTo). - The item is an array of size (nb elements in dataSetTo, 2). - + If an element (idElementTo) of the dataSetTo (flatIdDataSetTo) is mapped with no element of the meshFrom: - elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. - The value of the attribute for this element depends of the type of the value of the attribute (0 for unit, -1 for int, nan for float). @@ -735,13 +735,14 @@ def transferAttributeToDataSetWithElementMap( if logger is None: logger = getLogger( "transferAttributeToDataSetWithElementMap", True ) - if flatIdDataSetTo not in elementMap.keys(): + if flatIdDataSetTo not in elementMap: logger.error( f"The map is incomplete, there is no data for the dataSetTo (flat index { flatIdDataSetTo })." ) return False - + nbElementsTo: int = dataSetTo.GetNumberOfPoints() if onPoints else dataSetTo.GetNumberOfCells() if len( elementMap[ flatIdDataSetTo ] ) != nbElementsTo: - logger.error( f"The map is wrong, there is { nbElementsTo } elements in the the dataSetTo (flat index { flatIdDataSetTo })\ + logger.error( + f"The map is wrong, there is { nbElementsTo } elements in the the dataSetTo (flat index { flatIdDataSetTo })\ but { len( elementMap[ flatIdDataSetTo ] ) } elements in the map." ) return False @@ -755,7 +756,7 @@ def transferAttributeToDataSetWithElementMap( elif vtkDataType in ( VTK_CHAR, VTK_SIGNED_CHAR, VTK_SHORT, VTK_LONG, VTK_INT, VTK_LONG_LONG, VTK_ID_TYPE ): defaultValue = -1 elif vtkDataType in ( VTK_BIT, VTK_UNSIGNED_CHAR, VTK_UNSIGNED_SHORT, VTK_UNSIGNED_LONG, VTK_UNSIGNED_INT, - VTK_UNSIGNED_LONG_LONG ): + VTK_UNSIGNED_LONG_LONG ): defaultValue = 0 typeMapping: dict[ int, type ] = vnp.get_vtk_to_numpy_typemap() @@ -774,11 +775,11 @@ def transferAttributeToDataSetWithElementMap( if idElementFrom != -1: dataFrom: Union[ vtkPointData, vtkCellData ] if isinstance( meshFrom, vtkDataSet ): - dataFrom = meshFrom.GetPointData() if onPoints else meshFrom.GetCellData() + dataFrom = meshFrom.GetPointData() if onPoints else meshFrom.GetCellData() elif isinstance( meshFrom, vtkMultiBlockDataSet ): flatIdDataSetFrom: int = elementMap[ flatIdDataSetTo ][ idElementTo ][ 0 ] dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( meshFrom.GetDataSet( flatIdDataSetFrom ) ) - dataFrom = dataSetFrom.GetPointData() if onPoints else dataSetFrom.GetCellData() + dataFrom = dataSetFrom.GetPointData() if onPoints else dataSetFrom.GetCellData() arrayFrom: npt.NDArray[ Any ] = vnp.vtk_to_numpy( dataFrom.GetArray( attributeName ) ) valueToTransfer = arrayFrom[ idElementFrom ] @@ -797,7 +798,7 @@ def transferAttributeToDataSetWithElementMap( def transferAttributeWithElementMap( meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], - elementMap: dict[ int, npt.NDArray[ np.int64] ], + elementMap: dict[ int, npt.NDArray[ np.int64 ] ], attributeName: str, onPoints: bool, logger: Union[ Logger, Any ] = None, @@ -810,7 +811,7 @@ def transferAttributeWithElementMap( The elementMap is a dictionary where: - Keys are the flat index of all the datasets of the meshTo. - Items are arrays of size (nb elements in datasets, 2). - + If an element (idElementTo) of one of the meshTo's dataSet (flatIdDataSetTo) is mapped with no element of the meshFrom: - elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. - The value of the attribute for this element depends of the type of the value of the attribute (0 for unit, -1 for int, nan for float). @@ -838,13 +839,26 @@ def transferAttributeWithElementMap( logger = getLogger( "transferAttributeWithElementMap", True ) if isinstance( meshTo, vtkDataSet ): - return transferAttributeToDataSetWithElementMap( meshFrom, meshTo, elementMap, attributeName, onPoints, logger=logger ) + return transferAttributeToDataSetWithElementMap( meshFrom, + meshTo, + elementMap, + attributeName, + onPoints, + logger=logger ) elif isinstance( meshTo, vtkMultiBlockDataSet ): listFlatIdMultiBlockDataSetTo: list[ int ] = getBlockElementIndexesFlatten( meshTo ) for flatIdDataSetTo in listFlatIdMultiBlockDataSetTo: dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( meshTo.GetDataSet( flatIdDataSetTo ) ) - if not transferAttributeToDataSetWithElementMap( meshFrom, dataSetTo, elementMap, attributeName, onPoints, flatIdDataSetTo=flatIdDataSetTo, logger=logger ): - logger.error( f"The attribute transfer has failed for the dataset with the flat index { flatIdDataSetTo } of the meshTo.") + if not transferAttributeToDataSetWithElementMap( meshFrom, + dataSetTo, + elementMap, + attributeName, + onPoints, + flatIdDataSetTo=flatIdDataSetTo, + logger=logger ): + logger.error( + f"The attribute transfer has failed for the dataset with the flat index { flatIdDataSetTo } of the meshTo." + ) return False return True diff --git a/geos-mesh/tests/conftest.py b/geos-mesh/tests/conftest.py index 2f37be2af..988254864 100644 --- a/geos-mesh/tests/conftest.py +++ b/geos-mesh/tests/conftest.py @@ -198,7 +198,7 @@ def getElementMap() -> Any: """ def _get_elementMap( meshFromName: str, meshToName: str, points: bool ) -> dict[ int, npt.NDArray[ np.int64 ] ]: - """Get the element indexes mapping dictionary between two meshes + """Get the element indexes mapping dictionary between two meshes. Args: meshFromName (str): The name of the meshFrom. @@ -218,7 +218,7 @@ def _get_elementMap( meshFromName: str, meshToName: str, points: bool ) -> dict[ elif meshFromName == "dataset" and meshToName == "emptydataset": elementMap[ 0 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 0 ] ) ] ) elif meshFromName == "dataset" and meshToName == "emptyFracture": - elementMap[ 0 ] = np.full( ( nbElements[ 1 ], 2), -1, np.int64 ) + elementMap[ 0 ] = np.full( ( nbElements[ 1 ], 2 ), -1, np.int64 ) elif meshFromName == "fracture" and meshToName == "emptyFracture": elementMap[ 0 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 1 ] ) ] ) elif meshFromName == "fracture" and meshToName == "emptymultiblock": diff --git a/geos-mesh/tests/test_AttributeMapping.py b/geos-mesh/tests/test_AttributeMapping.py index ce053509b..17c522601 100644 --- a/geos-mesh/tests/test_AttributeMapping.py +++ b/geos-mesh/tests/test_AttributeMapping.py @@ -10,24 +10,26 @@ from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet, vtkDataSet + @pytest.mark.parametrize( "meshFromName, meshToName, attributeNames, onPoints", [ - ( "fracture", "emptyFracture", set( [ "collocated_nodes" ] ), True ), - ( "multiblock", "emptyFracture", set( [ "FAULT" ] ), False ), - ( "multiblock", "emptymultiblock", set( [ "FAULT" ] ), False ), - ( "dataset", "emptymultiblock", set( [ "FAULT" ] ), False ), - ( "dataset", "emptydataset", set( [ "FAULT" ] ), False ), + ( "fracture", "emptyFracture", ( [ "collocated_nodes" ] ), True ), + ( "multiblock", "emptyFracture", ( [ "FAULT" ] ), False ), + ( "multiblock", "emptymultiblock", ( [ "FAULT" ] ), False ), + ( "dataset", "emptymultiblock", ( [ "FAULT" ] ), False ), + ( "dataset", "emptydataset", ( [ "FAULT" ] ), False ), ] ) def test_AttributeMapping( - dataSetTest: Any, - meshFromName: str, - meshToName: str, - attributeNames: set[ str ], - onPoints: bool, + dataSetTest: Any, + meshFromName: str, + meshToName: str, + attributeNames: set[ str ], + onPoints: bool, ) -> None: + """Test mapping an attribute between two meshes.""" meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshFromName ) meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshToName ) if isinstance( meshFrom, vtkMultiBlockDataSet ): fillAllPartialAttributes( meshFrom ) - + filter = AttributeMapping( meshFrom, meshTo, attributeNames, onPoints ) assert filter.applyFilter() diff --git a/geos-mesh/tests/test_arrayHelpers.py b/geos-mesh/tests/test_arrayHelpers.py index f78f578cd..b233b99a0 100644 --- a/geos-mesh/tests/test_arrayHelpers.py +++ b/geos-mesh/tests/test_arrayHelpers.py @@ -17,6 +17,7 @@ from geos.mesh.utils import arrayHelpers + @pytest.mark.parametrize( "meshFromName, meshToName, points", [ ( "multiblock", "emptymultiblock", False ), ( "multiblock", "emptyFracture", False ), @@ -32,9 +33,11 @@ def test_computeElementMapping( meshToName: str, points: bool, ) -> None: + """Test getting the map between two meshes element.""" meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshFromName ) meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] = dataSetTest( meshToName ) - elementMapComputed: dict[ int, npt.NDArray[ np.int64 ] ] = arrayHelpers.computeElementMapping( meshFrom, meshTo, points ) + elementMapComputed: dict[ int, npt.NDArray[ np.int64 ] ] = arrayHelpers.computeElementMapping( + meshFrom, meshTo, points ) elementMapTest: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( meshFromName, meshToName, points ) keysComputed: list[ int ] = list( elementMapComputed.keys() ) diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index 0324a8890..098797ffc 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -495,13 +495,13 @@ def test_transferAttributeWithElementMap( onPoints: bool, defaultValueTest: Any, ) -> None: - "Test to transfer attributes from the meshFrom to the dataSetTo using an elementMap." + """Test to transfer attributes from the meshFrom to the dataSetTo using an elementMap.""" meshFrom: Union[ vtkMultiBlockDataSet, vtkDataSet ] = dataSetTest( meshFromName ) if isinstance( meshFrom, vtkMultiBlockDataSet ): arrayModifiers.fillAllPartialAttributes( meshFrom ) - + meshTo: Union[ vtkMultiBlockDataSet, vtkDataSet ] = dataSetTest( meshToName ) - elementMap: dict[ int, npt.NDArray[ np.int64] ] = getElementMap( meshFromName, meshToName, onPoints ) + elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = getElementMap( meshFromName, meshToName, onPoints ) assert arrayModifiers.transferAttributeWithElementMap( meshFrom, meshTo, elementMap, attributeName, onPoints ) @@ -510,7 +510,7 @@ def test_transferAttributeWithElementMap( if isinstance( meshTo, vtkDataSet ): dataTo = meshTo.GetPointData() if onPoints else meshTo.GetCellData() elif isinstance( meshTo, vtkMultiBlockDataSet ): - dataSetTo: vtkDataSet = meshTo.GetDataSet( flatIdDataSetTo ) + dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( meshTo.GetDataSet( flatIdDataSetTo ) ) dataTo = dataSetTo.GetPointData() if onPoints else dataSetTo.GetCellData() arrayTo: npt.NDArray[ Any ] = vnp.vtk_to_numpy( dataTo.GetArray( attributeName ) ) @@ -522,10 +522,10 @@ def test_transferAttributeWithElementMap( else: dataFrom: Union[ vtkPointData, vtkCellData ] if isinstance( meshFrom, vtkDataSet ): - dataFrom= meshFrom.GetPointData() if onPoints else meshFrom.GetCellData() + dataFrom = meshFrom.GetPointData() if onPoints else meshFrom.GetCellData() elif isinstance( meshFrom, vtkMultiBlockDataSet ): flatIdDataSetFrom: int = elementMap[ flatIdDataSetTo ][ idElementTo ][ 0 ] - dataSetFrom: vtkDataSet = meshFrom.GetDataSet( flatIdDataSetFrom ) + dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( meshFrom.GetDataSet( flatIdDataSetFrom ) ) dataFrom = dataSetFrom.GetPointData() if onPoints else dataSetFrom.GetCellData() arrayFrom: npt.NDArray[ Any ] = vnp.vtk_to_numpy( dataFrom.GetArray( attributeName ) ) diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index 77bf5186d..3cd3460e9 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -15,10 +15,6 @@ update_paths() from geos.mesh.processing.AttributeMapping import AttributeMapping -from geos.mesh.utils.arrayHelpers import ( getAttributeSet, isAttributeGlobal ) - -from geos.pv.utils.checkboxFunction import createModifiedCallback -from geos.pv.utils.paraviewTreatments import getArrayChoices from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, ) @@ -77,7 +73,7 @@ def __init__( self: Self ) -> None: self._initArraySelections: bool = True self.cellAttributeNames: vtkDataArraySelection = vtkDataArraySelection() self.pointAttributeNames: vtkDataArraySelection = vtkDataArraySelection() - + self.clearAttributeNames = True self.attributeNames: list[ str ] = [] @@ -101,7 +97,7 @@ def e01SetFieldAssociation( self: Self, value: int ) -> None: """ self.onPoints = bool( value ) self.Modified() - + @smproperty.stringvector( name="SelectAttributeToTransfer", label="Select Attribute To Transfer", @@ -161,10 +157,9 @@ def RequestDataObject( assert inDataTo is not None if outData is None or ( not outData.IsA( inDataTo.GetClassName() ) ): outData = inDataTo.NewInstance() - outInfoVec.GetInformationObject( 0 ).Set( outData.DATA_OBJECT(), outData ) + 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 @@ -191,7 +186,8 @@ def RequestData( outData.ShallowCopy( meshTo ) - filter: AttributeMapping = AttributeMapping( meshFrom, outData, set(self.attributeNames), self.onPoints, True ) + filter: AttributeMapping = AttributeMapping( meshFrom, outData, set( self.attributeNames ), self.onPoints, + True ) if not filter.logger.hasHandlers(): filter.setLoggerHandler( VTKHandler() ) @@ -199,4 +195,3 @@ def RequestData( self.clearAttributeNames = True return 1 - From 9d04059397c45074ae0de44a137da5551281ba49 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Tue, 9 Sep 2025 08:54:19 +0200 Subject: [PATCH 28/45] Remove AttributeMappingFromCellId and clean the doc --- docs/geos_mesh_docs/processing.rst | 4 +- docs/geos_posp_docs/filters.rst | 9 - .../filters/AttributeMappingFromCellId.py | 188 ------------------ 3 files changed, 2 insertions(+), 199 deletions(-) delete mode 100644 geos-posp/src/geos_posp/filters/AttributeMappingFromCellId.py diff --git a/docs/geos_mesh_docs/processing.rst b/docs/geos_mesh_docs/processing.rst index 34904a031..781677e29 100644 --- a/docs/geos_mesh_docs/processing.rst +++ b/docs/geos_mesh_docs/processing.rst @@ -3,8 +3,8 @@ Processing filters The `processing` module of `geos-mesh` package contains filters to process meshes. -geos.mesh.processing.AttributeMapping module --------------------------------------------------------- +geos.mesh.processing.AttributeMapping filter +--------------------------------------------- .. automodule:: geos.mesh.processing.AttributeMapping :members: diff --git a/docs/geos_posp_docs/filters.rst b/docs/geos_posp_docs/filters.rst index 20fd785e0..496c33819 100644 --- a/docs/geos_posp_docs/filters.rst +++ b/docs/geos_posp_docs/filters.rst @@ -3,15 +3,6 @@ vtk Filters This package defines vtk filters that allows to process Geos outputs. - -geos_posp.filters.AttributeMappingFromCellId module ------------------------------------------------------------ - -.. automodule:: geos_posp.filters.AttributeMappingFromCellId - :members: - :undoc-members: - :show-inheritance: - geos_posp.filters.GeomechanicsCalculator module --------------------------------------------------- diff --git a/geos-posp/src/geos_posp/filters/AttributeMappingFromCellId.py b/geos-posp/src/geos_posp/filters/AttributeMappingFromCellId.py deleted file mode 100644 index ceae63135..000000000 --- a/geos-posp/src/geos_posp/filters/AttributeMappingFromCellId.py +++ /dev/null @@ -1,188 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. -# SPDX-FileContributor: Raphaël Vinour, Martin Lemay -# ruff: noqa: E402 # disable Module level import not at top of file -import numpy as np -import numpy.typing as npt -from geos.utils.Logger import Logger, getLogger -from typing_extensions import Self -from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase -from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector -from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid - -from geos.mesh.utils.arrayModifiers import createAttribute -from geos.mesh.utils.arrayHelpers import getArrayInObject - -__doc__ = """ -AttributeMappingFromCellId module is a vtk filter that transfer a attribute from a -server mesh to a client mesh. - -Filter input and output types are vtkPolyData. - -To use the filter: - -.. code-block:: python - - from filters.AttributeMappingFromCellId import AttributeMappingFromCellId - - # filter inputs - logger :Logger - input :vtkPolyData - TransferAttributeName : str - - # instanciate the filter - filter :AttributeMappingFromCellId = AttributeMappingFromCellId() - # set the logger - filter.SetLogger(logger) - # set input data object - filter.SetInputDataObject(input) - # set Attribute to transfer - filter.SetTransferAttributeName(AttributeName) - # set Attribute to compare - filter.SetIDAttributeName(AttributeName) - # do calculations - filter.Update() - # get output object - output :vtkPolyData = filter.GetOutputDataObject(0) - # get created attribute names - newAttributeNames :set[str] = filter.GetNewAttributeNames() -""" - - -class AttributeMappingFromCellId( VTKPythonAlgorithmBase ): - - def __init__( self: Self ) -> None: - """Map the properties of a source mesh to a receiver mesh.""" - super().__init__( nInputPorts=2, nOutputPorts=1, outputType="vtkUnstructuredGrid" ) - - # Transfer Attribute name - self.m_transferedAttributeName: str = "" - # ID Attribute name - self.m_idAttributeName: str = "" - # logger - self.m_logger: Logger = getLogger( "Attribute Mapping From Cell Id Filter" ) - - def SetLogger( self: Self, logger: Logger ) -> None: - """Set filter logger. - - Args: - logger (Logger): logger - """ - self.m_logger = logger - self.Modified() - - def SetTransferAttributeName( self: Self, name: str ) -> None: - """Set Transfer attribute name.""" - self.m_transferedAttributeName = name - self.Modified() - - def SetIDAttributeName( self: Self, name: str ) -> None: - """Set ID attribute name.""" - self.m_idAttributeName = name - 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 - 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: - serverMesh: vtkUnstructuredGrid = vtkUnstructuredGrid.GetData( inInfoVec[ 0 ] ) - clientMesh: vtkUnstructuredGrid = vtkUnstructuredGrid.GetData( inInfoVec[ 1 ] ) - outData: vtkUnstructuredGrid = self.GetOutputData( outInfoVec, 0 ) - - assert serverMesh is not None, "Server mesh is null." - assert clientMesh is not None, "Client mesh is null." - assert outData is not None, "Output pipeline is null." - - outData.ShallowCopy( clientMesh ) - - cellIdMap: npt.NDArray[ np.int64 ] = self.getCellMap( serverMesh, clientMesh ) - - attributeArrayServer: npt.NDArray[ np.float64 ] = getArrayInObject( serverMesh, - self.m_transferedAttributeName, False ) - attributeArrayClient: npt.NDArray[ np.float64 ] = np.full_like( attributeArrayServer, np.nan ) - for i in range( serverMesh.GetNumberOfCells() ): - k: int = cellIdMap[ i ] - attributeArrayClient[ k ] = attributeArrayServer[ i ] - createAttribute( - clientMesh, - attributeArrayClient, - self.m_transferedAttributeName, - (), - False, - ) - outData.Modified() - - except AssertionError as e: - mess1: str = "Attribute mapping failed due to:" - self.m_logger.error( mess1 ) - self.m_logger.error( e, exc_info=True ) - return 0 - except Exception as e: - mess0: str = "Attribute mapping failed due to:" - self.m_logger.critical( mess0 ) - self.m_logger.critical( e, exc_info=True ) - return 0 - mess2: str = ( f"Attribute {self.m_transferedAttributeName} was successfully transferred." ) - self.m_logger.info( mess2 ) - - return 1 - - def getCellMap( self: Self, serverMesh: vtkUnstructuredGrid, - clientMesh: vtkUnstructuredGrid ) -> npt.NDArray[ np.int64 ]: - """Compute the mapping between both mesh from cell ids. - - Args: - serverMesh (vtkUnstructuredGrid): server mesh - clientMesh (vtkUnstructuredGrid): client mesh - - Returns: - npt.NDArray[np.int64]: the map where for each cell index of the - client mesh, the cell index of the server mesh. - - """ - cellIdArrayServer: npt.NDArray[ np.int64 ] = getArrayInObject( serverMesh, self.m_idAttributeName, - False ).astype( int ) - cellIdArrayClient: npt.NDArray[ np.int64 ] = getArrayInObject( clientMesh, self.m_idAttributeName, - False ).astype( int ) - cellMap: npt.NDArray[ np.int64 ] = np.zeros( serverMesh.GetNumberOfCells() ).astype( int ) - for i, cellId in enumerate( cellIdArrayServer ): - k: int = np.argwhere( cellIdArrayClient == cellId )[ 0 ] - cellMap[ i ] = k - return cellMap From de2e64e4ef09693260527c7ace8b5555562a1431 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Wed, 10 Sep 2025 09:14:11 +0200 Subject: [PATCH 29/45] fix block id --- geos-mesh/tests/conftest.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/geos-mesh/tests/conftest.py b/geos-mesh/tests/conftest.py index 988254864..76cfb9203 100644 --- a/geos-mesh/tests/conftest.py +++ b/geos-mesh/tests/conftest.py @@ -212,9 +212,9 @@ def _get_elementMap( meshFromName: str, meshToName: str, points: bool ) -> dict[ nbElements: tuple[ int, int ] = ( 4092, 212 ) if points else ( 1740, 156 ) if meshFromName == "multiblock" and meshToName == "emptymultiblock": elementMap[ 1 ] = np.array( [ [ 1, element ] for element in range( nbElements[ 0 ] ) ] ) - elementMap[ 2 ] = np.array( [ [ 2, element ] for element in range( nbElements[ 1 ] ) ] ) + elementMap[ 3 ] = np.array( [ [ 3, element ] for element in range( nbElements[ 1 ] ) ] ) elif meshFromName == "multiblock" and meshToName == "emptyFracture": - elementMap[ 0 ] = np.array( [ [ 2, element ] for element in range( nbElements[ 1 ] ) ] ) + elementMap[ 0 ] = np.array( [ [ 3, element ] for element in range( nbElements[ 1 ] ) ] ) elif meshFromName == "dataset" and meshToName == "emptydataset": elementMap[ 0 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 0 ] ) ] ) elif meshFromName == "dataset" and meshToName == "emptyFracture": @@ -223,10 +223,10 @@ def _get_elementMap( meshFromName: str, meshToName: str, points: bool ) -> dict[ elementMap[ 0 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 1 ] ) ] ) elif meshFromName == "fracture" and meshToName == "emptymultiblock": elementMap[ 1 ] = np.full( ( nbElements[ 0 ], 2 ), -1, np.int64 ) - elementMap[ 2 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 1 ] ) ] ) + elementMap[ 3 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 1 ] ) ] ) elif meshFromName == "dataset" and meshToName == "emptymultiblock": elementMap[ 1 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 0 ] ) ] ) - elementMap[ 2 ] = np.full( ( nbElements[ 1 ], 2 ), -1, np.int64 ) + elementMap[ 3 ] = np.full( ( nbElements[ 1 ], 2 ), -1, np.int64 ) return elementMap From 8a2ef89d0b17872aed099229f9e0988b9d587af9 Mon Sep 17 00:00:00 2001 From: Romain Baville <126683264+RomainBaville@users.noreply.github.com> Date: Thu, 11 Sep 2025 14:31:19 +0200 Subject: [PATCH 30/45] Apply suggestions from Paloma's review Co-authored-by: paloma-martinez <104762252+paloma-martinez@users.noreply.github.com> --- .../geos/mesh/processing/AttributeMapping.py | 25 +++++++++++-------- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 2 +- .../src/geos/pv/plugins/PVAttributeMapping.py | 9 +++---- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py index e0e7d3617..c74824eb9 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py @@ -16,14 +16,17 @@ from geos.mesh.utils.arrayHelpers import ( computeElementMapping, getAttributeSet, isAttributeGlobal ) __doc__ = """ -AttributeMapping is a vtk filter that transfer global attributes from a meshFrom to a meshTo for each -cell or point of the two meshes with the same coordinates. For cell, the coordinates of the points in the cell are compared. -The filter update the mesh where attributes are transferred directly, no copy is created. +AttributeMapping is a vtk filter that transfer global attributes from an initial mesh to another mesh with same point/cell coordinates. The final mesh is updated directly, without creation of a copy. -Input and output meshes can be vtkDataSet or vtkMultiBlockDataSet. -The names of the attributes to transfer are give with a set of string. -The localization of the attributes to transfer is a bool, True for points, False for cells. All the attributes must be on the same piece. -To use a handler of yours, set the variable 'speHandler' to True and add it using the member function addLoggerHandler. +Input meshes can be vtkDataSet or vtkMultiBlockDataSet. + +.. Warning:: + For one application of the filter, the attributes to transfer should all be located on the same piece (all on points or all on cells). + +.. Note:: + For cell, the coordinates of the points in the cell are compared. + +To use a logger handler of yours, set the variable 'speHandler' to True and add it using the member function addLoggerHandler. To use the filter: @@ -68,7 +71,7 @@ def __init__( onPoints: bool = False, speHandler: bool = False, ) -> None: - """Transfer global attributes from the meshFrom to the meshTo mapping the piece of the attributes to transfer. + """Transfer global attributes from an initial mesh to another mapping the piece of the attributes to transfer. Args: meshFrom (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The mesh with attributes to transfer. @@ -147,14 +150,14 @@ def applyFilter( self: Self ) -> bool: if len( wrongAttributeNames ) > 0: self.logger.error( - f"The { self.piece } attributes { wrongAttributeNames } are not in the mesh from where to transfer attributes." + f"The { self.piece } attributes { wrongAttributeNames } are not present in the initial mesh." ) self.logger.error( f"The filter { self.logger.name } failed." ) return False if len( attributesAlreadyInMeshTo ) > 0: self.logger.error( - f"The { self.piece } attributes { attributesAlreadyInMeshTo } are already in the mesh where attributes must be transferred." + f"The { self.piece } attributes { attributesAlreadyInMeshTo } are already present in the final mesh." ) self.logger.error( f"The filter { self.logger.name } failed." ) return False @@ -193,4 +196,4 @@ def applyFilter( self: Self ) -> bool: def _logOutputMessage( self: Self ) -> None: """Create and log result messages of the filter.""" - self.logger.info( f"The filter { self.logger.name } succeed." ) + self.logger.info( f"The filter { self.logger.name } succeeded." ) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index 2ebad31cd..17dc15e08 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -194,7 +194,7 @@ def UpdateDictElementMappingFromDataSetToDataSet( For each element (idElementTo) of the dataSetTo not yet mapped (elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]), if the points coordinates of an element (idElementFrom) of the dataSetFrom are the same as the points coordinates of the elementTo, - the elementMap is update to elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. + the elementMap is updated to elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. Args: dataSetFrom (vtkDataSet): The dataset with the element to map. diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index 3cd3460e9..61c048f7b 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -34,15 +34,14 @@ ) __doc__ = """ -AttributeMapping is a paraview plugin that transfer global attributes from a meshFrom to a meshTo for each -cell or point of the two meshes with the same coordinates. For cell, the coordinates of the points in the cell are compared. +AttributeMapping is a paraview plugin that transfers global attributes from an initial mesh (meshFrom) to a another mesh (meshTo) for each cell or point of the two meshes with the same coordinates. For cell, the coordinates of the points in the cell are compared. Input and output meshes can be vtkDataSet or vtkMultiBlockDataSet. To use it: * Load the module in Paraview: Tools>Manage Plugins...>Load new>PVAttributeMapping. * Select the mesh to transfer the global attributes (meshTo). -* Search and Select Attribute Mapping Filter. +* Select Filters > 4- Geos Utils > Attribute Mapping. * Select the mesh with global attributes to transfer (meshFrom). * Select global attributes to transfer from the meshFrom to the meshTo. * Apply. @@ -89,7 +88,7 @@ def __init__( self: Self ) -> None: """ ) - def e01SetFieldAssociation( self: Self, value: int ) -> None: + def a01SetFieldAssociation( self: Self, value: int ) -> None: """Set attribute type. Args: @@ -180,7 +179,7 @@ def RequestData( meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCompositeDataSet ] = self.GetInputData( inInfoVec, 1, 0 ) outData: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCompositeDataSet ] = self.GetOutputData( outInfoVec, 0 ) - assert meshTo is not None, "Input mesh (meshTo) to transfer attributed is null." + assert meshTo is not None, "Input mesh (meshTo) to transfer attributes is null." assert meshFrom is not None, "Input mesh (meshFrom) with attributes to transfer is null." assert outData is not None, "Output pipeline is null." From e01211eef6327f8ba848c69cde3ca4ad8d32ed15 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 11 Sep 2025 14:46:31 +0200 Subject: [PATCH 31/45] Clean the doc --- .../geos/mesh/processing/AttributeMapping.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py index c74824eb9..89d2b9984 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py @@ -16,7 +16,7 @@ from geos.mesh.utils.arrayHelpers import ( computeElementMapping, getAttributeSet, isAttributeGlobal ) __doc__ = """ -AttributeMapping is a vtk filter that transfer global attributes from an initial mesh to another mesh with same point/cell coordinates. The final mesh is updated directly, without creation of a copy. +AttributeMapping is a vtk filter that transfer global attributes from a source mesh to a final mesh with same point/cell coordinates. The final mesh is updated directly, without creation of a copy. Input meshes can be vtkDataSet or vtkMultiBlockDataSet. @@ -26,13 +26,13 @@ .. Note:: For cell, the coordinates of the points in the cell are compared. -To use a logger handler of yours, set the variable 'speHandler' to True and add it using the member function addLoggerHandler. +To use a logger handler of yours, set the variable 'speHandler' to True and add it using the member function setLoggerHandler. To use the filter: .. code-block:: python - from filters.AttributeMapping import AttributeMapping + from geos.mesh.processing.AttributeMapping import AttributeMapping # Filter inputs. meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] @@ -43,7 +43,7 @@ speHandler: bool # defaults to False # Instantiate the filter - filter :AttributeMapping = AttributeMapping( meshFrom, + filter: AttributeMapping = AttributeMapping( meshFrom, meshTo, attributeNames, onPoints, @@ -71,11 +71,11 @@ def __init__( onPoints: bool = False, speHandler: bool = False, ) -> None: - """Transfer global attributes from an initial mesh to another mapping the piece of the attributes to transfer. + """Transfer global attributes from a source mesh to a final mesh, mapping the piece of the attributes to transfer. Args: - meshFrom (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The mesh with attributes to transfer. - meshTo (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The mesh where to transfer attributes. + meshFrom (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The source mesh with attributes to transfer. + meshTo (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The final mesh where to transfer attributes. attributeNames (set[str]): Names of the attributes to transfer. onPoints (bool): True if attributes are on points, False if they are on cells. Defaults to False. @@ -114,7 +114,7 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: "The logger already has an handler, to use yours set the argument 'speHandler' to True during the filter initialization." ) - def GetElementMap( self: Self ) -> dict[ int, npt.NDArray[ np.int64 ] ]: + def getElementMap( self: Self ) -> dict[ int, npt.NDArray[ np.int64 ] ]: """Getter of the element mapping dictionary. If attribute to transfer are on points it will be a pointMap, else it will be a cellMap. @@ -125,7 +125,7 @@ def GetElementMap( self: Self ) -> dict[ int, npt.NDArray[ np.int64 ] ]: return self.ElementMap def applyFilter( self: Self ) -> bool: - """Transfer attributes on element from the meshFrom to the meshTo with the elementMap. + """Transfer global attributes from a source mesh to a final mesh, mapping the piece of the attributes to transfer. Returns: boolean (bool): True if calculation successfully ended, False otherwise. @@ -150,7 +150,7 @@ def applyFilter( self: Self ) -> bool: if len( wrongAttributeNames ) > 0: self.logger.error( - f"The { self.piece } attributes { wrongAttributeNames } are not present in the initial mesh." + f"The { self.piece } attributes { wrongAttributeNames } are not present in the source mesh." ) self.logger.error( f"The filter { self.logger.name } failed." ) return False From 6a62c73b87cc513f6e589ee937bb4cc0a92e6607 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 11 Sep 2025 14:51:09 +0200 Subject: [PATCH 32/45] Add info to the logger --- geos-mesh/src/geos/mesh/processing/AttributeMapping.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py index 89d2b9984..d5dbab499 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py @@ -197,3 +197,6 @@ def applyFilter( self: Self ) -> bool: def _logOutputMessage( self: Self ) -> None: """Create and log result messages of the filter.""" self.logger.info( f"The filter { self.logger.name } succeeded." ) + self.logger.info( + f"The { self.piece } attributes { self.attributeNames } have been transferred from the source mesh to the final mesh with the { self.piece } mapping." + ) From 36291c6b7818cbbeac7a2b4ae43862b0f55f396b Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 11 Sep 2025 16:56:02 +0200 Subject: [PATCH 33/45] Clean the doc --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 146 +++++++++--------- 1 file changed, 75 insertions(+), 71 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index 17dc15e08..4a989ca87 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -31,28 +31,30 @@ def computeElementMapping( meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], points: bool, ) -> dict[ int, npt.NDArray ]: - """Compute the elementMap from the meshFrom to the meshTo. + """Compute the map of points/cells between the source mesh and the final mesh. - If meshFrom is a vtkDataSet, the flat index (flatIdDataSetFrom) is set to 0. - If meshTo is a vtkDataSet, the flat index (flatIdDataSetTo) is set to 0. + If the source mesh is a vtkDataSet, its flat index (flatIdDataSetFrom) is set to 0. + If the final mesh is a vtkDataSet, its flat index (flatIdDataSetTo) is set to 0. The elementMap is a dictionary where: - - Keys are the flat index of all the datasets of the meshTo. - - Items are arrays of size (nb elements in datasets, 2). + - Keys are the flat index of all the datasets of the final mesh. + - Items are arrays of size (nb elements in datasets of the final mesh, 2). - For each element (idElementTo) of each dataset (flatIdDataSetTo) of meshTo, - if the points coordinates of an element (idElementFrom) of one meshFrom's dataSet (flatIdDataSetFrom) - are the same as the points coordinates of the elementTo, + For each element (idElementTo) of each dataset (flatIdDataSetTo) of the final mesh, + if the coordinates of an element (idElementFrom) of one dataset (flatIdDataSetFrom) of the source mesh + are the same as the coordinates of the element of the final mesh, elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom] else, elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. + For cells, the coordinates of the points in the cell are compared. + Args: - meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the element to map. - meshTo (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the reference element. + meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The source mesh with the element to map. + meshTo (Union[vtkDataSet, vtkMultiBlockDataSet]): The final mesh with the reference element coordinates. points (bool): True if elements to map are points, False if they are cells. Returns: - elementMap (dict[int, npt.NDArray[np.int64]]): The elementMap with the element indexes in the two meshes. + elementMap (dict[int, npt.NDArray[np.int64]]): The map of points/cells between the two meshes. """ elementMap: dict[ int, npt.NDArray ] = {} if isinstance( meshTo, vtkDataSet ): @@ -69,28 +71,30 @@ def UpdateElementMappingToMultiBlockDataSet( elementMap: dict[ int, npt.NDArray ], points: bool, ) -> None: - """Update the elementMap from the meshFrom to the multiBlockDataSetTo. + """Update the map of points/cells between the source mesh and the final mesh. - If meshFrom is a vtkDataSet, the flat index (flatIdDataSetFrom) is set to 0. + If the source mesh is a vtkDataSet, its flat index (flatIdDataSetFrom) is set to 0. - Add the mapping for of the multiBlockDataSetTo: - - Keys are the flat index of all the datasets of the multiBlockDataSetTo. - - Items are arrays of size (nb elements in datasets, 2). + Add the mapping for of the final mesh: + - Keys are the flat index of all the datasets of the final mesh. + - Items are arrays of size (nb elements in datasets of the final mesh, 2). - For each element (idElementTo) of each dataset (flatIdDataSetTo) of multiBlockDataSetTo, - if the points coordinates of an element (idElementFrom) of one meshFrom's dataSet (flatIdDataSetFrom) - are the same as the points coordinates of the elementTo, + For each element (idElementTo) of each dataset (flatIdDataSetTo) of final mesh, + if the coordinates of an element (idElementFrom) of one dataset (flatIdDataSetFrom) of the source mesh + are the same as the coordinates of the element of the final mesh, elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom] else, elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. + For cells, the coordinates of the points in the cell are compared. + Args: - meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the element to map. - multiBlockDataSetTo (vtkMultiBlockDataSet): The mesh with the reference element. - elementMap (dict[int, npt.NDArray[np.int64]]): The elementMap to update. + meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The source mesh with the element to map. + multiBlockDataSetTo (vtkMultiBlockDataSet): The final mesh with the reference element coordinates. + elementMap (dict[int, npt.NDArray[np.int64]]): The map of points/cells to update. points (bool): True if elements to map are points, False if they are cells. """ - listFlatIdMultiBlockDataSetTo: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetTo ) - for flatIdDataSetTo in listFlatIdMultiBlockDataSetTo: + listFlatIdDataSetTo: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetTo ) + for flatIdDataSetTo in listFlatIdDataSetTo: dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetTo.GetDataSet( flatIdDataSetTo ) ) UpdateElementMappingToDataSet( meshFrom, dataSetTo, elementMap, points, flatIdDataSetTo=flatIdDataSetTo ) @@ -102,28 +106,29 @@ def UpdateElementMappingToDataSet( points: bool, flatIdDataSetTo: int = 0, ) -> None: - """Update the elementMap from the meshFrom to the dataSetTo. + """Update the map of points/cells between the source mesh and the final mesh. - If meshFrom is a vtkDataSet, the flat index (flatIdDataSetFrom) is set to 0. - dataSetTo is considered as block of vtkMultiblockDataSet, if not the flat index (flatIdDataSetTo) is set to 0. + If the source mesh is a vtkDataSet, its flat index (flatIdDataSetFrom) is set to 0. - Add the mapping for the dataSetTo: - - The keys is the flat index of the dataSetTo (flatIdDataSetTo). - - The item is an array of size (nb elements in dataSetTo, 2). + Add the mapping for the final mesh: + - The key is the flat index of the final mesh. + - The item is an array of size (nb elements in the final mesh, 2). - For each element (idElementTo) of the mesh dataSetTo, - if the points coordinates of an element (idElementFrom) of one meshFrom's dataSet (flatIdDataSetFrom) - are the same as the points coordinates of the elementTo, + For each element (idElementTo) of the final mesh, + if the coordinates of an element (idElementFrom) of one dataset (flatIdDataSetFrom) of the source mesh + are the same as the coordinates of the element of the final mesh, elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom] else, elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. + For cells, the coordinates of the points in the cell are compared. + Args: - meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the element to map. - dataSetTo (vtkDataSet): The dataset with the reference element. - elementMap (dict[int, npt.NDArray[np.int64]]): The elementMap to update. + meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The source mesh with the element to map. + dataSetTo (vtkDataSet): The final mesh with the reference element coordinates. + elementMap (dict[int, npt.NDArray[np.int64]]): The map of points/cells to update. points (bool): True if elements to map are points, False if they are cells. - flatIdDataSetTo (int, Optional): The flat index of the dataSetTo. - Defaults to 0. + flatIdDataSetTo (int, Optional): The flat index of the final mesh considered as a dataset of a vtkMultiblockDataSet. + Defaults to 0 for final meshes who are not datasets of vtkMultiBlockDataSet. """ nbElementsTo: int = dataSetTo.GetNumberOfPoints() if points else dataSetTo.GetNumberOfCells() elementMap[ flatIdDataSetTo ] = np.full( ( nbElementsTo, 2 ), -1, np.int64 ) @@ -148,27 +153,27 @@ def UpdateElementMappingFromMultiBlockDataSetToDataSet( points: bool, flatIdDataSetTo: int = 0, ) -> None: - """Update the elementMap from the multiBlockDataSetFrom to the dataSetTo. + """Update the map of points/cells between the source mesh and the final mesh. - dataSetTo is considered as block of a vtkMultiblockDataSet, if not the flat index (flatIdDataSetTo) is set to 0. + For each element (idElementTo) of the final mesh not yet mapped (elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]), + if the coordinates of an element (idElementFrom) of one dataset (flatIdDataSetFrom) of the source mesh + are the same as the coordinates of the element of the final mesh, + the map of points/cells is update: elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. - For each element (idElementTo) of the dataSetTo not yet mapped (elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]), - if the points coordinates of an element (idElementFrom) of a block (flatIdDataSetFrom) of multiBlockDataSetFrom - are the same as the points coordinates of the elementTo, - the elementMap is update to elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. + For cells, the coordinates of the points in the cell are compared. Args: - multiBlockDataSetFrom (vtkMultiBlockDataSet): The mesh with the element to map. - dataSetTo (vtkDataSet): The dataset with the reference element. - elementMap (dict[int, npt.NDArray[np.int64]]): The elementMap to update with; - The flat index of the dataSetTo as keys. - An array of size (nb elements in dataSetTo, 2) as item. + multiBlockDataSetFrom (vtkMultiBlockDataSet): The source mesh with the element to map. + dataSetTo (vtkDataSet): The final mesh with the reference element coordinates. + elementMap (dict[int, npt.NDArray[np.int64]]): The map of points/cells to update with; + The key is the flat index of the final mesh. + The item is an array of size (nb elements in the final mesh, 2). points (bool): True if elements to map are points, False if they are cells. - flatIdDataSetTo (int, Optional): The flat index of the dataSetTo. - Defaults to 0. + flatIdDataSetTo (int, Optional): The flat index of the final mesh considered as a dataset of a vtkMultiblockDataSet. + Defaults to 0 for final meshes who are not datasets of vtkMultiBlockDataSet. """ - listFlatIdMultiBlockDataSetFrom: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetFrom ) - for flatIdDataSetFrom in listFlatIdMultiBlockDataSetFrom: + listFlatIDataSetFrom: list[ int ] = getBlockElementIndexesFlatten( multiBlockDataSetFrom ) + for flatIdDataSetFrom in listFlatIDataSetFrom: dataSetFrom: vtkDataSet = vtkDataSet.SafeDownCast( multiBlockDataSetFrom.GetDataSet( flatIdDataSetFrom ) ) UpdateDictElementMappingFromDataSetToDataSet( dataSetFrom, dataSetTo, @@ -186,33 +191,32 @@ def UpdateDictElementMappingFromDataSetToDataSet( flatIdDataSetFrom: int = 0, flatIdDataSetTo: int = 0, ) -> None: - """Update the elementMap from the dataSetFrom to the dataSetTo. + """Update the map of points/cells between the source mesh and the final mesh. - dataSetFrom is considered as block of vtkMultiblockDataSet, if not the flat index (flatIdDataSetFrom) is set to 0. - dataSetTo is considered as block of vtkMultiblockDataSet, if not the flat index (flatIdDataSetTo) is set to 0. + For each element (idElementTo) of the final mesh not yet mapped (elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]), + if the coordinates of an element (idElementFrom) of the source mesh + are the same as the coordinates of the element of the final mesh, + the map of points/cells is update: elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. - For each element (idElementTo) of the dataSetTo not yet mapped (elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]), - if the points coordinates of an element (idElementFrom) of the dataSetFrom - are the same as the points coordinates of the elementTo, - the elementMap is updated to elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. + For cells, the coordinates of the points in the cell are compared. Args: - dataSetFrom (vtkDataSet): The dataset with the element to map. - dataSetTo (vtkDataSet): The dataset with the reference element. - elementMap (dict[int, npt.NDArray[np.int64]]): The elementMap to update with; - The flat index of the dataSetTo as keys. - An array of size (nb elements in dataSetTo, 2) as item. + dataSetFrom (vtkDataSet): The source mesh with the element to map. + dataSetTo (vtkDataSet): The final mesh with the reference element coordinates. + elementMap (dict[int, npt.NDArray[np.int64]]): The map of points/cells to update with; + The key is the flat index of the final mesh. + The item is an array of size (nb elements in the final mesh, 2). points (bool): True if elements to map are points, False if they are cells. - flatIdDataSetFrom (int, Optional): The flat index of the dataSetFrom. - Defaults to 0. - flatIdDataSetTo (int, Optional): The flat index of the dataSetTo. - Defaults to 0. + flatIdDataSetFrom (int, Optional): The flat index of the source mesh considered as a dataset of a vtkMultiblockDataSet. + Defaults to 0 for source meshes who are not datasets of vtkMultiBlockDataSet. + flatIdDataSetTo (int, Optional): The flat index of the final mesh considered as a dataset of a vtkMultiblockDataSet. + Defaults to 0 for final meshes who are not datasets of vtkMultiBlockDataSet. """ idElementsFromFund: list[ int ] = [] nbElementsTo: int = len( elementMap[ flatIdDataSetTo ] ) nbElementsFrom: int = dataSetFrom.GetNumberOfPoints() if points else dataSetFrom.GetNumberOfCells() for idElementTo in range( nbElementsTo ): - # Test if the element of the dataSetTo is already mapped. + # Test if the element of the final mesh is already mapped. if -1 in elementMap[ flatIdDataSetTo ][ idElementTo ]: coordElementTo: tuple[ float, ...] if points: @@ -229,7 +233,7 @@ def UpdateDictElementMappingFromDataSetToDataSet( idElementFrom: int = 0 ElementFromFund: bool = False while idElementFrom < nbElementsFrom and not ElementFromFund: - # Test if the element of the dataSetFrom is already mapped. + # Test if the element of the source mesh is already mapped. if idElementFrom not in idElementsFromFund: coordElementFrom: tuple[ float, ...] if points: From 4865557ab299b8160b7b4cfefd342ce6e87eee93 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 11 Sep 2025 17:16:32 +0200 Subject: [PATCH 34/45] clean the doc --- .../src/geos/mesh/utils/arrayModifiers.py | 67 ++++++++++--------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py index 25756abdb..10797f6d5 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayModifiers.py @@ -51,6 +51,8 @@ These methods include: - filling partial VTK arrays with values (useful for block merge) - creation of new VTK array, empty or with a given data array + - copy VTK array from a source mesh to a final mesh + - transfer VTK array from a source mesh to a final mesh with a element map - transfer from VTK point data to VTK cell data """ @@ -681,31 +683,30 @@ def transferAttributeToDataSetWithElementMap( flatIdDataSetTo: int = 0, logger: Union[ Logger, Any ] = None, ) -> bool: - """Transfer attributes from the meshFrom to the dataSetTo using an elementMap. + """Transfer attributes from the source mesh to the final mesh using a map of points/cells. - If meshFrom is a vtkDataSet, the flat index (flatIdDataSetFrom) is set to 0. - dataSetTo is considered as block of vtkMultiblockDataSet, if not the flat index (flatIdDataSetTo) is set to 0. + If the source mesh is a vtkDataSet, its flat index (flatIdDataSetFrom) is set to 0. - The map used to transfer the attribute is a dictionary where: - - The keys is the flat index of the dataSetTo (flatIdDataSetTo). - - The item is an array of size (nb elements in dataSetTo, 2). + The map of points/cells used to transfer the attribute is a dictionary where: + - The key is the flat index of the final mesh. + - The item is an array of size (nb elements in the final mesh, 2). - If an element (idElementTo) of the dataSetTo (flatIdDataSetTo) is mapped with no element of the meshFrom: + If an element (idElementTo) of the final mesh is mapped with no element of the source mesh: - elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. - The value of the attribute for this element depends of the type of the value of the attribute (0 for unit, -1 for int, nan for float). - If an element (idElementTo) of the dataSetTo (flatIdDataSetTo) is mapped with an element (idElementFrom) of one of the meshFrom's dataset (flatIdDataSetFrom): + If an element (idElementTo) of the final mesh is mapped with an element (idElementFrom) of one of the dataset (flatIdDataSetFrom) of the source mesh: - elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. - - The value of the attribute for this element is the value of the element (idElementFrom) of the meshFrom on on the dataSetFrom (flatIdDataSetFrom). + - The value of the attribute for this element is the value of the element (idElementFrom) of the dataset (flatIdDataSetFrom) of the source mesh. Args: - meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the attribute to transfer. - dataSetTo (vtkDataSet): The mesh where to transfer the attribute. - elementMap (dict[int, npt.NDArray[np.int64]]): The elementMap. + meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The source mesh with the attribute to transfer. + dataSetTo (vtkDataSet): The final mesh where to transfer the attribute. + elementMap (dict[int, npt.NDArray[np.int64]]): The map of points/cells. attributeName (str): The name of the attribute to transfer. onPoints (bool): True if the attribute is on points, False if it is on cells. - flatIdDataSetTo (int, Optional): The flat index of the dataSetTo. - Defaults to 0. + flatIdDataSetTo (int, Optional): The flat index of the final mesh considered as a dataset of a vtkMultiblockDataSet. + Defaults to 0 for final meshes who are not datasets of vtkMultiBlockDataSet. logger (Union[Logger, None], optional): A logger to manage the output messages. Defaults to None, an internal logger is used. @@ -717,13 +718,13 @@ def transferAttributeToDataSetWithElementMap( logger = getLogger( "transferAttributeToDataSetWithElementMap", True ) if flatIdDataSetTo not in elementMap: - logger.error( f"The map is incomplete, there is no data for the dataSetTo (flat index { flatIdDataSetTo })." ) + logger.error( f"The map is incomplete, there is no data for the final mesh (flat index { flatIdDataSetTo })." ) return False nbElementsTo: int = dataSetTo.GetNumberOfPoints() if onPoints else dataSetTo.GetNumberOfCells() if len( elementMap[ flatIdDataSetTo ] ) != nbElementsTo: logger.error( - f"The map is wrong, there is { nbElementsTo } elements in the the dataSetTo (flat index { flatIdDataSetTo })\ + f"The map is wrong, there is { nbElementsTo } elements in the final mesh (flat index { flatIdDataSetTo })\ but { len( elementMap[ flatIdDataSetTo ] ) } elements in the map." ) return False @@ -784,31 +785,30 @@ def transferAttributeWithElementMap( onPoints: bool, logger: Union[ Logger, Any ] = None, ) -> bool: - """Transfer attributes from the meshFrom to the meshTo using an elementMap. + """Transfer attributes from the source mesh to the final mesh using a map of points/cells. - If meshFrom is a vtkDataSet, the flat index (flatIdDataSetFrom) is set to 0. - If meshTo is a vtkDataSet, the flat index (flatIdDataSetTo) is set to 0. + If the source mesh is a vtkDataSet, its flat index (flatIdDataSetFrom) is set to 0. + If the final mesh is a vtkDataSet, its flat index (flatIdDataSetTo) is set to 0. - The elementMap is a dictionary where: - - Keys are the flat index of all the datasets of the meshTo. - - Items are arrays of size (nb elements in datasets, 2). + The map of points/cells used to transfer the attribute is a dictionary where: + - Keys are the flat index of all the datasets of the final mesh. + - Items are arrays of size (nb elements in datasets of the final mesh, 2). - If an element (idElementTo) of one of the meshTo's dataSet (flatIdDataSetTo) is mapped with no element of the meshFrom: + If an element (idElementTo) of one dataset (flatIdDataSetTo) of the final mesh is mapped with no element of the source mesh: - elementMap[flatIdDataSetTo][idElementTo] = [-1, -1]. - The value of the attribute for this element depends of the type of the value of the attribute (0 for unit, -1 for int, nan for float). - If an element (idElementTo) of one of the meshTo's dataSet (flatIdDataSetTo) is mapped with an element (idElementFrom) of one of the meshFrom's dataset (flatIdDataSetFrom): + If an element (idElementTo) of one dataset (flatIdDataSetTo) of the final mesh is mapped with an element (idElementFrom) of one of the dataset (flatIdDataSetFrom) of the source mesh: - elementMap[flatIdDataSetTo][idElementTo] = [flatIdDataSetFrom, idElementFrom]. - - The value of the attribute for this element is the value of the element (idElementFrom) of the meshFrom on on the dataSetFrom (flatIdDataSetFrom). + - The value of the attribute for this element is the value of the element (idElementFrom) of the dataset (flatIdDataSetFrom) of the source mesh. Args: - meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh with the attribute to transfer. - meshTo (Union[vtkDataSet, vtkMultiBlockDataSet]): The mesh where to transfer the attribute. - elementMap (dict[int, npt.NDArray[np.int64]]): The elementMap. + meshFrom (Union[vtkDataSet, vtkMultiBlockDataSet]): The source mesh with the attribute to transfer. + meshTo (Union[vtkDataSet, vtkMultiBlockDataSet]): The final mesh where to transfer the attribute. + elementMap (dict[int, npt.NDArray[np.int64]]): The map of points/cells. attributeName (str): The name of the attribute to transfer. onPoints (bool): True if the attribute is on points, False if it is on cells. - flatIdDataSetTo (int, Optional): The flat index of the dataSetTo. - Defaults to 0. + Defaults to 0 for final meshes who are not datasets of vtkMultiBlockDataSet. logger (Union[Logger, None], optional): A logger to manage the output messages. Defaults to None, an internal logger is used. @@ -827,8 +827,8 @@ def transferAttributeWithElementMap( onPoints, logger=logger ) elif isinstance( meshTo, vtkMultiBlockDataSet ): - listFlatIdMultiBlockDataSetTo: list[ int ] = getBlockElementIndexesFlatten( meshTo ) - for flatIdDataSetTo in listFlatIdMultiBlockDataSetTo: + listFlatIdDataSetTo: list[ int ] = getBlockElementIndexesFlatten( meshTo ) + for flatIdDataSetTo in listFlatIdDataSetTo: dataSetTo: vtkDataSet = vtkDataSet.SafeDownCast( meshTo.GetDataSet( flatIdDataSetTo ) ) if not transferAttributeToDataSetWithElementMap( meshFrom, dataSetTo, @@ -838,8 +838,9 @@ def transferAttributeWithElementMap( flatIdDataSetTo=flatIdDataSetTo, logger=logger ): logger.error( - f"The attribute transfer has failed for the dataset with the flat index { flatIdDataSetTo } of the meshTo." + f"The attribute transfer has failed for the dataset with the flat index { flatIdDataSetTo } of the final mesh." ) + logger.warning( "The final mesh may has been modify for the other datasets." ) return False return True From 484dcdd175cc176113968791679533948d53c433 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 11 Sep 2025 17:18:32 +0200 Subject: [PATCH 35/45] clean the doc --- geos-mesh/tests/test_arrayModifiers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geos-mesh/tests/test_arrayModifiers.py b/geos-mesh/tests/test_arrayModifiers.py index b4eac3d36..e03a5b57f 100644 --- a/geos-mesh/tests/test_arrayModifiers.py +++ b/geos-mesh/tests/test_arrayModifiers.py @@ -497,7 +497,7 @@ def test_transferAttributeWithElementMap( onPoints: bool, defaultValueTest: Any, ) -> None: - """Test to transfer attributes from the meshFrom to the dataSetTo using an elementMap.""" + """Test to transfer attributes from the source mesh to the final mesh using a map of points/cells.""" meshFrom: Union[ vtkMultiBlockDataSet, vtkDataSet ] = dataSetTest( meshFromName ) if isinstance( meshFrom, vtkMultiBlockDataSet ): arrayModifiers.fillAllPartialAttributes( meshFrom ) From b2cbc5e50520dd37e0b0920e0e9bb1d66012e641 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 11 Sep 2025 17:35:28 +0200 Subject: [PATCH 36/45] Clean the doc and the plugin --- .../geos/mesh/processing/AttributeMapping.py | 2 +- .../src/geos/pv/plugins/PVAttributeMapping.py | 57 ++++++++++--------- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py index d5dbab499..73ebda68e 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py @@ -16,7 +16,7 @@ from geos.mesh.utils.arrayHelpers import ( computeElementMapping, getAttributeSet, isAttributeGlobal ) __doc__ = """ -AttributeMapping is a vtk filter that transfer global attributes from a source mesh to a final mesh with same point/cell coordinates. The final mesh is updated directly, without creation of a copy. +AttributeMapping is a vtk filter that transfers global attributes from a source mesh to a final mesh with same point/cell coordinates. The final mesh is updated directly, without creation of a copy. Input meshes can be vtkDataSet or vtkMultiBlockDataSet. diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index 61c048f7b..d6efdc8b4 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -34,16 +34,24 @@ ) __doc__ = """ -AttributeMapping is a paraview plugin that transfers global attributes from an initial mesh (meshFrom) to a another mesh (meshTo) for each cell or point of the two meshes with the same coordinates. For cell, the coordinates of the points in the cell are compared. +AttributeMapping is a paraview plugin that transfers global attributes from a source mesh to a final mesh with same point/cell coordinates. + Input and output meshes can be vtkDataSet or vtkMultiBlockDataSet. +.. Warning:: + For one application of the plugin, the attributes to transfer should all be located on the same piece (all on points or all on cells). + +.. Note:: + For cell, the coordinates of the points in the cell are compared. + To use it: * Load the module in Paraview: Tools>Manage Plugins...>Load new>PVAttributeMapping. * Select the mesh to transfer the global attributes (meshTo). * Select Filters > 4- Geos Utils > Attribute Mapping. -* Select the mesh with global attributes to transfer (meshFrom). -* Select global attributes to transfer from the meshFrom to the meshTo. +* Select the source mesh with global attributes to transfer (meshFrom). +* Select the on witch element (onPoints/onCells) the attributes to transfer are. +* Select the global attributes to transfer from the source mesh to the final mesh. * Apply. """ @@ -64,20 +72,15 @@ class PVAttributeMapping( VTKPythonAlgorithmBase ): def __init__( self: Self ) -> None: - """Map attributes of the source mesh (meshFrom) to the other mesh (meshTo).""" + """Map attributes of the source mesh (meshFrom) to the final mesh (meshTo).""" super().__init__( nInputPorts=2, nOutputPorts=1, inputType="vtkObject", outputType="vtkObject" ) self.onPoints: bool = False - - self._initArraySelections: bool = True - self.cellAttributeNames: vtkDataArraySelection = vtkDataArraySelection() - self.pointAttributeNames: vtkDataArraySelection = vtkDataArraySelection() - self.clearAttributeNames = True self.attributeNames: list[ str ] = [] @smproperty.intvector( - name="AttributeType", + name="AttributePiece", default_values=1, number_of_elements=1, ) @@ -88,13 +91,13 @@ def __init__( self: Self ) -> None: """ ) - def a01SetFieldAssociation( self: Self, value: int ) -> None: - """Set attribute type. + def setAttributePiece( self: Self, piece: int ) -> None: + """Set attributes piece (points or cells). Args: - value (int): 0 if on points, 1 if on cells. + piece (int): 0 if on points, 1 if on cells. """ - self.onPoints = bool( value ) + self.onPoints = bool( piece ) self.Modified() @smproperty.stringvector( @@ -107,22 +110,22 @@ def a01SetFieldAssociation( self: Self, value: int ) -> None: ) @smdomain.xml( """ + attribute_type="array" + input_domain_name="onPiece_Attribute_List"> - Select attributes to transfer from the meshFrom To the meshTo. + Select attributes to transfer from the source mesh to the final mesh. """ ) - def a02SelectMultipleAttribute( self: Self, name: str ) -> None: - """Set the attribute to transfer from the meshFrom to the meshTo. + def selectMultipleAttribute( self: Self, name: str ) -> None: + """Set the attribute to transfer from the source mesh to the final mesh. Args: name (str): The name of the attribute to transfer. @@ -144,9 +147,9 @@ def RequestDataObject( """Inherited from VTKPythonAlgorithmBase::RequestDataObject. Args: - request (vtkInformation): request - inInfoVec (list[vtkInformationVector]): input objects - outInfoVec (vtkInformationVector): output objects + request (vtkInformation): Request. + inInfoVec (list[vtkInformationVector]): Input objects. + outInfoVec (vtkInformationVector): Output objects. Returns: int: 1 if calculation successfully ended, 0 otherwise. @@ -168,9 +171,9 @@ def RequestData( """Inherited from VTKPythonAlgorithmBase::RequestData. Args: - request (vtkInformation): request - inInfoVec (list[vtkInformationVector]): input objects - outInfoVec (vtkInformationVector): output objects + request (vtkInformation): Request. + inInfoVec (list[vtkInformationVector]): Input objects. + outInfoVec (vtkInformationVector): Output objects. Returns: int: 1 if calculation successfully ended, 0 otherwise. @@ -179,8 +182,8 @@ def RequestData( meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCompositeDataSet ] = self.GetInputData( inInfoVec, 1, 0 ) outData: Union[ vtkDataSet, vtkMultiBlockDataSet, vtkCompositeDataSet ] = self.GetOutputData( outInfoVec, 0 ) - assert meshTo is not None, "Input mesh (meshTo) to transfer attributes is null." - assert meshFrom is not None, "Input mesh (meshFrom) with attributes to transfer is null." + assert meshTo is not None, "The final mesh (meshTo) where to transfer attributes is null." + assert meshFrom is not None, "The source mesh (meshFrom) with attributes to transfer is null." assert outData is not None, "Output pipeline is null." outData.ShallowCopy( meshTo ) From fb325b7d6039875fed0c5d666a62dbe4c2edc991 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 11 Sep 2025 18:19:57 +0200 Subject: [PATCH 37/45] Apply comments from Jacques review. --- .../geos/mesh/processing/AttributeMapping.py | 15 +++----- geos-mesh/tests/conftest.py | 37 ++++++++++--------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py index 73ebda68e..25f6c226e 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py @@ -137,17 +137,8 @@ def applyFilter( self: Self ) -> bool: self.logger.warning( f"The filter { self.logger.name } has not been used." ) return False - wrongAttributeNames: list[ str ] = [] - attributesAlreadyInMeshTo: list = [] attributesInMeshFrom: set[ str ] = getAttributeSet( self.meshFrom, self.onPoints ) - attributesInMeshTo: set[ str ] = getAttributeSet( self.meshTo, self.onPoints ) - for attributeName in self.attributeNames: - if attributeName not in attributesInMeshFrom: - wrongAttributeNames.append( attributeName ) - - if attributeName in attributesInMeshTo: - attributesAlreadyInMeshTo.append( attributeName ) - + wrongAttributeNames: set[ str ] = self.attributeNames.difference( attributesInMeshFrom ) if len( wrongAttributeNames ) > 0: self.logger.error( f"The { self.piece } attributes { wrongAttributeNames } are not present in the source mesh." @@ -155,6 +146,8 @@ def applyFilter( self: Self ) -> bool: self.logger.error( f"The filter { self.logger.name } failed." ) return False + attributesInMeshTo: set[ str ] = getAttributeSet( self.meshTo, self.onPoints ) + attributesAlreadyInMeshTo: set[ str ] = self.attributeNames.intersection( attributesInMeshTo ) if len( attributesAlreadyInMeshTo ) > 0: self.logger.error( f"The { self.piece } attributes { attributesAlreadyInMeshTo } are already present in the final mesh." @@ -187,6 +180,8 @@ def applyFilter( self: Self ) -> bool: for attributeName in self.attributeNames: if not transferAttributeWithElementMap( self.meshFrom, self.meshTo, self.ElementMap, attributeName, self.onPoints, self.logger ): + self.logger.error( f"The attribute { attributeName } has not been mapped." ) + self.logger.error( f"The filter { self.logger.name } failed." ) return False # Log the output message. diff --git a/geos-mesh/tests/conftest.py b/geos-mesh/tests/conftest.py index 76cfb9203..e85c8be47 100644 --- a/geos-mesh/tests/conftest.py +++ b/geos-mesh/tests/conftest.py @@ -210,23 +210,26 @@ def _get_elementMap( meshFromName: str, meshToName: str, points: bool ) -> dict[ """ elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = {} nbElements: tuple[ int, int ] = ( 4092, 212 ) if points else ( 1740, 156 ) - if meshFromName == "multiblock" and meshToName == "emptymultiblock": - elementMap[ 1 ] = np.array( [ [ 1, element ] for element in range( nbElements[ 0 ] ) ] ) - elementMap[ 3 ] = np.array( [ [ 3, element ] for element in range( nbElements[ 1 ] ) ] ) - elif meshFromName == "multiblock" and meshToName == "emptyFracture": - elementMap[ 0 ] = np.array( [ [ 3, element ] for element in range( nbElements[ 1 ] ) ] ) - elif meshFromName == "dataset" and meshToName == "emptydataset": - elementMap[ 0 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 0 ] ) ] ) - elif meshFromName == "dataset" and meshToName == "emptyFracture": - elementMap[ 0 ] = np.full( ( nbElements[ 1 ], 2 ), -1, np.int64 ) - elif meshFromName == "fracture" and meshToName == "emptyFracture": - elementMap[ 0 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 1 ] ) ] ) - elif meshFromName == "fracture" and meshToName == "emptymultiblock": - elementMap[ 1 ] = np.full( ( nbElements[ 0 ], 2 ), -1, np.int64 ) - elementMap[ 3 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 1 ] ) ] ) - elif meshFromName == "dataset" and meshToName == "emptymultiblock": - elementMap[ 1 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 0 ] ) ] ) - elementMap[ 3 ] = np.full( ( nbElements[ 1 ], 2 ), -1, np.int64 ) + if meshFromName == "multiblock": + if meshToName == "emptymultiblock": + elementMap[ 1 ] = np.array( [ [ 1, element ] for element in range( nbElements[ 0 ] ) ] ) + elementMap[ 3 ] = np.array( [ [ 3, element ] for element in range( nbElements[ 1 ] ) ] ) + elif meshToName == "emptyFracture": + elementMap[ 0 ] = np.array( [ [ 3, element ] for element in range( nbElements[ 1 ] ) ] ) + elif meshFromName == "dataset": + if meshToName == "emptydataset": + elementMap[ 0 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 0 ] ) ] ) + elif meshToName == "emptyFracture": + elementMap[ 0 ] = np.full( ( nbElements[ 1 ], 2 ), -1, np.int64 ) + elif meshToName == "emptymultiblock": + elementMap[ 1 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 0 ] ) ] ) + elementMap[ 3 ] = np.full( ( nbElements[ 1 ], 2 ), -1, np.int64 ) + elif meshFromName == "fracture": + if meshToName == "emptyFracture": + elementMap[ 0 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 1 ] ) ] ) + elif meshToName == "emptymultiblock": + elementMap[ 1 ] = np.full( ( nbElements[ 0 ], 2 ), -1, np.int64 ) + elementMap[ 3 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 1 ] ) ] ) return elementMap From f49880aad6160423d8fee0b27e9412e9edf58aef Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 12 Sep 2025 09:07:27 +0200 Subject: [PATCH 38/45] Clean for ci --- geos-mesh/src/geos/mesh/processing/AttributeMapping.py | 6 ++---- geos-pv/src/geos/pv/plugins/PVAttributeMapping.py | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py index 25f6c226e..3d0c21724 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py @@ -141,8 +141,7 @@ def applyFilter( self: Self ) -> bool: wrongAttributeNames: set[ str ] = self.attributeNames.difference( attributesInMeshFrom ) if len( wrongAttributeNames ) > 0: self.logger.error( - f"The { self.piece } attributes { wrongAttributeNames } are not present in the source mesh." - ) + f"The { self.piece } attributes { wrongAttributeNames } are not present in the source mesh." ) self.logger.error( f"The filter { self.logger.name } failed." ) return False @@ -150,8 +149,7 @@ def applyFilter( self: Self ) -> bool: attributesAlreadyInMeshTo: set[ str ] = self.attributeNames.intersection( attributesInMeshTo ) if len( attributesAlreadyInMeshTo ) > 0: self.logger.error( - f"The { self.piece } attributes { attributesAlreadyInMeshTo } are already present in the final mesh." - ) + f"The { self.piece } attributes { attributesAlreadyInMeshTo } are already present in the final mesh." ) self.logger.error( f"The filter { self.logger.name } failed." ) return False diff --git a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py index d6efdc8b4..2ab26f4ea 100644 --- a/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py +++ b/geos-pv/src/geos/pv/plugins/PVAttributeMapping.py @@ -23,7 +23,6 @@ ) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from vtkmodules.vtkCommonCore import ( - vtkDataArraySelection, vtkInformation, vtkInformationVector, ) From e2822eca89d73921e4195c9ca0012c55d60c7ddb Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 12 Sep 2025 09:20:15 +0200 Subject: [PATCH 39/45] Fix the tests --- geos-mesh/tests/test_AttributeMapping.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/geos-mesh/tests/test_AttributeMapping.py b/geos-mesh/tests/test_AttributeMapping.py index 17c522601..2d541a5b1 100644 --- a/geos-mesh/tests/test_AttributeMapping.py +++ b/geos-mesh/tests/test_AttributeMapping.py @@ -12,11 +12,11 @@ @pytest.mark.parametrize( "meshFromName, meshToName, attributeNames, onPoints", [ - ( "fracture", "emptyFracture", ( [ "collocated_nodes" ] ), True ), - ( "multiblock", "emptyFracture", ( [ "FAULT" ] ), False ), - ( "multiblock", "emptymultiblock", ( [ "FAULT" ] ), False ), - ( "dataset", "emptymultiblock", ( [ "FAULT" ] ), False ), - ( "dataset", "emptydataset", ( [ "FAULT" ] ), False ), + ( "fracture", "emptyFracture", { "collocated_nodes" }, True ), + ( "multiblock", "emptyFracture", { "FAULT" }, False ), + ( "multiblock", "emptymultiblock", { "FAULT" }, False ), + ( "dataset", "emptymultiblock", { "FAULT" }, False ), + ( "dataset", "emptydataset", { "FAULT" }, False ), ] ) def test_AttributeMapping( dataSetTest: Any, From 8866dbaedb460d4b321a782f2dc4d9c25e85eaf4 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 12 Sep 2025 09:20:48 +0200 Subject: [PATCH 40/45] Fix the tests --- geos-mesh/tests/test_AttributeMapping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geos-mesh/tests/test_AttributeMapping.py b/geos-mesh/tests/test_AttributeMapping.py index 2d541a5b1..019492f18 100644 --- a/geos-mesh/tests/test_AttributeMapping.py +++ b/geos-mesh/tests/test_AttributeMapping.py @@ -12,7 +12,7 @@ @pytest.mark.parametrize( "meshFromName, meshToName, attributeNames, onPoints", [ - ( "fracture", "emptyFracture", { "collocated_nodes" }, True ), + ( "fracture", "emptyFracture", { "collocated_nodes" }, True ), ( "multiblock", "emptyFracture", { "FAULT" }, False ), ( "multiblock", "emptymultiblock", { "FAULT" }, False ), ( "dataset", "emptymultiblock", { "FAULT" }, False ), From c9320aa422cd1104e56169743cdf0e9ba7bb5d47 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 12 Sep 2025 09:50:50 +0200 Subject: [PATCH 41/45] Move files to new directory --- .../src/geos/mesh/processing}/TransferAttributesVolumeSurface.py | 0 .../src/geos/pv/plugins}/PVTransferAttributesVolumeSurface.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {geos-posp/src/geos_posp/filters => geos-mesh/src/geos/mesh/processing}/TransferAttributesVolumeSurface.py (100%) rename {geos-posp/src/PVplugins => geos-pv/src/geos/pv/plugins}/PVTransferAttributesVolumeSurface.py (100%) diff --git a/geos-posp/src/geos_posp/filters/TransferAttributesVolumeSurface.py b/geos-mesh/src/geos/mesh/processing/TransferAttributesVolumeSurface.py similarity index 100% rename from geos-posp/src/geos_posp/filters/TransferAttributesVolumeSurface.py rename to geos-mesh/src/geos/mesh/processing/TransferAttributesVolumeSurface.py diff --git a/geos-posp/src/PVplugins/PVTransferAttributesVolumeSurface.py b/geos-pv/src/geos/pv/plugins/PVTransferAttributesVolumeSurface.py similarity index 100% rename from geos-posp/src/PVplugins/PVTransferAttributesVolumeSurface.py rename to geos-pv/src/geos/pv/plugins/PVTransferAttributesVolumeSurface.py From bca66e785d6caaaf8edfc9ec98a2b28036faacd7 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 12 Sep 2025 11:51:40 +0200 Subject: [PATCH 42/45] Add a vtp file --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 10 +-- .../geos/mesh/utils/multiblockModifiers.py | 6 +- geos-mesh/tests/conftest.py | 13 ++-- geos-mesh/tests/data/displacedFault.vtm | 2 +- geos-mesh/tests/data/displacedFaultempty.vtm | 2 +- geos-mesh/tests/data/fracture_res5_id.vtp | 64 +++++++++++++++++++ .../tests/data/fracture_res5_id_empty.vtp | 54 ++++++++++++++++ geos-mesh/tests/test_arrayHelpers.py | 14 ++-- 8 files changed, 139 insertions(+), 26 deletions(-) create mode 100644 geos-mesh/tests/data/fracture_res5_id.vtp create mode 100644 geos-mesh/tests/data/fracture_res5_id_empty.vtp diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index 802a552bf..12731f5c4 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -652,24 +652,26 @@ def getComponentNamesMultiBlock( return () -def getAttributeValuesAsDF( surface: vtkPolyData, attributeNames: tuple[ str, ...] ) -> pd.DataFrame: +def getAttributeValuesAsDF( surface: vtkPolyData, attributeNames: tuple[ str, ...], onPoints: bool = False ) -> pd.DataFrame: """Get attribute values from input surface. Args: surface (vtkPolyData): Mesh where to get attribute values. attributeNames (tuple[str,...]): Tuple of attribute names to get the values. + onPoints (bool, optional): True if attributes are on points, False if they are on cells. + Defaults to False. Returns: pd.DataFrame: DataFrame containing property names as columns. """ - nbRows: int = surface.GetNumberOfCells() + nbRows: int = surface.GetNumberOfPoints() if onPoints else surface.GetNumberOfCells() data: pd.DataFrame = pd.DataFrame( np.full( ( nbRows, len( attributeNames ) ), np.nan ), columns=attributeNames ) for attributeName in attributeNames: - if not isAttributeInObject( surface, attributeName, False ): + if not isAttributeInObject( surface, attributeName, onPoints ): logging.warning( f"Attribute {attributeName} is not in the mesh." ) continue - array: npt.NDArray[ np.float64 ] = getArrayInObject( surface, attributeName, False ) + array: npt.NDArray[ np.float64 ] = getArrayInObject( surface, attributeName, onPoints ) if len( array.shape ) > 1: for i in range( array.shape[ 1 ] ): diff --git a/geos-mesh/src/geos/mesh/utils/multiblockModifiers.py b/geos-mesh/src/geos/mesh/utils/multiblockModifiers.py index 5f00afb82..f579af291 100644 --- a/geos-mesh/src/geos/mesh/utils/multiblockModifiers.py +++ b/geos-mesh/src/geos/mesh/utils/multiblockModifiers.py @@ -3,7 +3,7 @@ # SPDX-FileContributor: Martin Lemay from typing import Union from vtkmodules.vtkCommonDataModel import ( vtkCompositeDataSet, vtkDataObjectTreeIterator, vtkMultiBlockDataSet, - vtkUnstructuredGrid ) + vtkDataSet ) from vtkmodules.vtkFiltersCore import vtkAppendDataSets from geos.mesh.utils.arrayModifiers import fillAllPartialAttributes @@ -14,7 +14,7 @@ def mergeBlocks( input: Union[ vtkMultiBlockDataSet, vtkCompositeDataSet ], keepPartialAttributes: bool = False, -) -> vtkUnstructuredGrid: +) -> vtkDataSet: """Merge all blocks of a multi block mesh. Args: @@ -38,7 +38,7 @@ def mergeBlocks( iter.VisitOnlyLeavesOn() iter.GoToFirstItem() while iter.GetCurrentDataObject() is not None: - block: vtkUnstructuredGrid = vtkUnstructuredGrid.SafeDownCast( iter.GetCurrentDataObject() ) + block: vtkDataSet = vtkDataSet.SafeDownCast( iter.GetCurrentDataObject() ) af.AddInputData( block ) iter.GoToNextItem() af.Update() diff --git a/geos-mesh/tests/conftest.py b/geos-mesh/tests/conftest.py index ea0810593..0c8aebc2d 100644 --- a/geos-mesh/tests/conftest.py +++ b/geos-mesh/tests/conftest.py @@ -10,7 +10,7 @@ import numpy.typing as npt from vtkmodules.vtkCommonDataModel import vtkDataSet, vtkMultiBlockDataSet, vtkPolyData -from vtkmodules.vtkIOXML import vtkXMLUnstructuredGridReader, vtkXMLMultiBlockDataReader +from vtkmodules.vtkIOXML import vtkXMLGenericDataObjectReader @pytest.fixture @@ -158,22 +158,19 @@ def _get_dataset( datasetType: str ) -> Union[ vtkMultiBlockDataSet, vtkPolyData Returns: (vtkMultiBlockDataSet, vtkPolyData, vtkDataSet): The vtk object. """ - reader: Union[ vtkXMLMultiBlockDataReader, vtkXMLUnstructuredGridReader ] + reader: vtkXMLGenericDataObjectReader = vtkXMLGenericDataObjectReader() if datasetType == "multiblock": - reader: vtkXMLMultiBlockDataReader = vtkXMLMultiBlockDataReader() vtkFilename = "data/displacedFault.vtm" elif datasetType == "emptymultiblock": - reader: vtkXMLMultiBlockDataReader = vtkXMLMultiBlockDataReader() vtkFilename = "data/displacedFaultempty.vtm" elif datasetType == "dataset": - reader: vtkXMLUnstructuredGridReader = vtkXMLUnstructuredGridReader() vtkFilename = "data/domain_res5_id.vtu" elif datasetType == "emptydataset": - reader: vtkXMLUnstructuredGridReader = vtkXMLUnstructuredGridReader() vtkFilename = "data/domain_res5_id_empty.vtu" elif datasetType == "polydata": - reader: vtkXMLUnstructuredGridReader = vtkXMLUnstructuredGridReader() - vtkFilename = "data/triangulatedSurface.vtu" + vtkFilename = "data/fracture_res5_id.vtp" + elif datasetType == "emptypolydata": + vtkFilename = "data/fracture_res5_id_empty.vtp" datapath: str = os.path.join( os.path.dirname( os.path.realpath( __file__ ) ), vtkFilename ) reader.SetFileName( datapath ) reader.Update() diff --git a/geos-mesh/tests/data/displacedFault.vtm b/geos-mesh/tests/data/displacedFault.vtm index cb28f0e8d..8998b1d96 100644 --- a/geos-mesh/tests/data/displacedFault.vtm +++ b/geos-mesh/tests/data/displacedFault.vtm @@ -3,7 +3,7 @@ - + diff --git a/geos-mesh/tests/data/displacedFaultempty.vtm b/geos-mesh/tests/data/displacedFaultempty.vtm index bea56fc6b..5946185af 100644 --- a/geos-mesh/tests/data/displacedFaultempty.vtm +++ b/geos-mesh/tests/data/displacedFaultempty.vtm @@ -3,7 +3,7 @@ - + diff --git a/geos-mesh/tests/data/fracture_res5_id.vtp b/geos-mesh/tests/data/fracture_res5_id.vtp new file mode 100644 index 000000000..0c6b93c19 --- /dev/null +++ b/geos-mesh/tests/data/fracture_res5_id.vtp @@ -0,0 +1,64 @@ + + + + + + + + + 3905.8931117 + + + 5326.4624283 + + + + + + + + + + + + + + + 0 + + + 2304.8861143 + + + + + 0 + + + 2304.8861143 + + + + + + + + + + + + + + + + + + + + + + + + _AQAAAAAAAAAAgAAAAAAAAEANAAAAAAAABgMAAAAAAAA=eF4tyMVSFgAAhVFduHDh+Ah2d2GD2I0NdnchJnZiYmCLgYmtKHZid3djgo0dG2f8z918c27B7Jn+LyVzoIX4BBfmk1yET3FRPs3F+AwX57N8Tkv4S+p5fym+wKX5IpfhS1yWL3M5vsJBfJWvaXl/Bb3ur8g3uBLf5Mp8i6vwba7KdziY7/I9DfFX0/v+UH7A1fkh1+BHXJMfcy1+wrX5KT/TOv66muqvx8+5Pr/gBvySG/IrbsSvuTG/4TQN8zfRdH9TfsvN+B035/fcgj9wS/7IrfgTf9Zwf4Rm+FvzF27DX7ktf+N2/J1nZgm0vX8Wd+BY7siddLa/M8/hudrFP4+7chx34/ncnRdwD17IPbmXLvL35sW8RPv4l3JfXsb9OJ7783IewCt4IEfqSv8gXsUJGuVfzYN5DQ/htTyU1/EwXs/DeYRu8EdzIm/Ukf5NPIo382jewmN4K4/lbTyOx+t2/wTewTt1oj+JJ/Eunsy7eQoncwzv4ak8Tff6p/M+3q8z/Ad4Jh/kWXyIY/kwz+YjPIfn6lH/PD7Gcdwya6DzuRUv4HCO0IX+1ryI2/BiXqJt/Uu5HS/j9hzPHXg5d+ROusLfmVdyF17FCdrVv5q78Rruzmu5B6/jntxL1/t78wbuw4m8Ufv6N3E/3sz9eQsP4K08kCN1m38Qb+co3sE7dbA/iYfwLh7Ku3kYJ/NwHqF7/NG8l0fyPt6vo/wHeDQf5DF8iMfyYR7H4/WIfwIf5Yl8jI/rJH8KT+YTPIVPcgyf4qk8TU/7p/MZPqs5sgWaU8/5c/F5vqC5/Xn0ov+S5vVf5nx8hfPzVS7ABfWavxBf5xta2F9Eb/pvaVH/bS7Gd7g43+USXFLv+UvxfX6gpf1l9KH/kZb1P+Zy/ISD+CmX5wr6zF+RU7kSP+fK/IJfahX/K67KrzmY33AIV9M0fyinc3V+yzX4Hb/Xmv4PXIs/cm3+xHW4rn721+MMrs9fuAF/5W/a0P+dG/EPbsw/OYyb6C9/U/7NzfgPN+e//A+qS/z/AQAAAAAAAAAAgAAAAAAAAKAGAAAAAAAAbgEAAAAAAAA=eF4txdciEAAAAEBRUmlpK9q0aEpbey/tTUMb0d57T6VBO+2NSGjvoflDHrp7uYCA/6o40EGu6moOdnWHuIZrupZDXdt1XNf1XN9hbuCGbuTGbuKmbuZwN3cLRzjSLd3Krd3Gbd3O7R3laHdwR3dyZ3dxjGPd1d3c3T3c070c596Odx/3dT/39wAP9CAneLCHeKiHebhHeKRHebTHeKzHebwneKInebITPcVTPc3TPcMzPcuzPcdzPc/zvcBJTvZCL/JiL3GKl3qZl3uFV3qVVzvVaU73Gmc402u9zuu9wRu9yZu9xVu9zdu9wzu9y7u9x3u9z/t9wAd9yId9xEd9zMd9wid9ylk+7TPO9lmf83lfcI5zfdGXfNlXfNXXfN03nOebvuXbvuO7vuf7fuCHfuTHfuKnzneBC/3MRS72c5f4hUtd5nK/9Cu/9hu/9Tu/9wd/9Cd/9hd/9Td/9w9X+Kd/+bf/+K//uRLqf1dfAQAAAAAAAAAAgAAAAAAAAOAEAAAAAAAAEgEAAAAAAAA=eF4txddCCAAAAMAiozSkoaGh0NAeqGhrSNEg7SFkJKFhlIhoaCBUP9tDdy8XEHAo0Ed81EE+5uM+4ZMOdohPOdRhDneETzvSZxzlaMc41mcd53gnONHnnORkpzjV553mdF/wRV9yhjOd5Wxfdo5zned8F7jQRS52iUt9xVd9zWUud4Wv+4YrXeVq17jWda73TTe40U1u9i23+LZb3eY7vut2d7jTXb7n++72A/e4133u94AHPeRhj3jUDz3mR37sJx73Uz/zc7/whF960q885dd+47ee9oxnPed3fu8P/uh5L/iTF/3ZX7zkr/7mZX/3D6941Wte909veNNb3vYv//Yf7/iv//m/d73nfR8ARZMvOw==AQAAAAAAAAAAgAAAAAAAAPAJAAAAAAAAagIAAAAAAAA=eF5t1aFqK1EUheGJqokoEdVVl3mKoSS3b5AHqC4E+gjja6qihxEjbkxUdZM2LpS62FA/UFlG3Z1N9v5XoVEfw5xz1hwW2UVhv2a8K+JXXuD1CD98v6XrHj994uaA13v8ssEfz/i4wl8NLpb48hFf1/juXvLMJUMl55Zy1kT2H16TN++4bsULXFyJl9vi199yiq9myXohbvHNOy4GfJz8Tb+UuKlwPcd39/i6xpePuFjirwYfV/jjWc7d4PVeMhzw06fk6fHDt6wd3abLC9yM1bvVvz+z8zvYupe27qWte2nrXtq6x9o9tu6lrXtp617aupe27qWte2nrXtq6R565ZKjk3FLOmsj+w2vausc+rXiBrXuSbYuLQp5P5f2Z7CNusXVP8mDrnuTH1j35Xmzdk/vB1j3Jhq17cv/Yuse5G2zdI8MBW/fI02PrHmtHt2nrHvuM1daxLt7B3r0u9nxLe/e6yIC9e7F2j717XXwj9u51cSfYu9fFHWLvXhd3LnnmkqGSc0s5ayL7n7p3tncv9mnFC+zdy2xbfOpePp/K+zPZR9xi717mwd69zI+9e/m92LuX94O9e5kNe/fy/rF3L87dYO9eZDhg717k6bF3L9aeune2dy/2Gat3+b/vM3eItdhn7hBnYZ+5Q2TDPnOH+BbsM3eIb8c+c2P2LLHP3CHuFvvMjTxzyVDJuaWcNZH9TzP3bJ+5sU8rXmCfuZlt+2NG8nwq789kH3GLfeZmHuwzN/Njn7n5vdhnbt4P9pmb2bDP3DhrhX3mxrkb7DM3Mhywz9zI02OfubH2NHPP9pkb+4zd/wFdP06cAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAgAAAAAAAAIATAAAAAAAAZwMAAAAAAAA=eF5d1cMWGGYABeHUSm3bbmqmtu3Utp3atpvadpsitfVCXXS+Rf+7mXPmzP5OmvDfNo6nxVPjRnHS4E+JGw4df3LcYOj4k+L6Q8efGNcbOv6EuO7Q8cfHdYaOPy6uPXT8lLjW0PHHxjWHjj8mrjF0/NFx9aHjj4qrDR1/ZFx16Pgj4ipDxx8eVx46/rC40tDxh8YVh44/JK4wdPzBcfmh4w+Kyw0df2Bcduj4A+IyQ8fvH5ceOn6/uNTQ8fvGJYeO3ycuMXT83nHxoeP3iosNHb9nXHTo+D3iIkPH7x4XHjp+t7jQ0PG7xgWHjt8lLjB0/M5x/qHjd4rzDR2/Y5x36Pgd4sSh47eP8wwdPznOPXT8dnGuoeO3jXMOHb9NnGPo+K3j7EPHbxVnGzp+yzjr0PFbxFmGjt88zjx0/GZxpqHjN53w/+n4TaI/8TtPxSejP9HxT0R/ouMfj/5Exz8W/YmOfzT6Ex3/SPQnOv7h6E90/ENxytDxD0Z/ouMfiP5Ex98f/YmOvy/6Ex1/b/QnOv6e6E90/N3Rn+j4u6I/0fF3Rn+i4++I/kTH3x79iY6/LfoTHX9r9Cc6/pboT3T8zdGf6Piboj/R8TdGf6Ljb4j+RMdfH/2Jjr8u+hMdf230Jzp+avQnOv6a6E90/NXRn+j4q6I/0fFXRn+i46+I/kTHXx79iY6/LPoTHX9p9Cc6/pLoT3T8xXHy0PEXRX+i4y+M/kTHXxD9iY4/P/oTHX9e9Cc6/tzoT3T8OdGf6Pizoz/R8WdFf6Ljz4z+RMefEf2Mjj89+hO/80/8O/oTHf9X9Cc6/s/oT3T8H9Gf6Pjfoz/R8b9Ff6Ljf43+RMf/Ev2Jjv85+hMd/1P0Jzr+x+hPdPwP0Z/o+O+jP9Hx30V/ouO/jf5Ex38T/YmOnxH9iY7/OvoTHf9V9Cc6/svoT3T8F9Gf6Pjp0Z/o+M+jP9Hxn0V/ouM/jf5Ex38S/YmO/zj6Ex3/UfQnOv7DOHXo+A+iP9Hx70d/ouPfi/5Ex78b/YmOfyf6Ex3/dvQnOv6t6E90/JvRn+j4N6I/0fGvR3+i41+L/kTHvxr9iY5/JfoTHf9y9Cc6/qXoT3T8i9Gf6PgXoj/R8c9Hf6Ljn4v+RMc/G/2Jjp8W/YmOfyb6GR3/dPwXoR4BOA==AQAAAAAAAAAAgAAAAAAAAOAEAAAAAAAADgEAAAAAAAA=eF4txRFwAgAAAMC2C4IgCIIgCIIgCIIgCIIgCIIgCIIgCIIgCIIgCIIgCIIgCAaDwSAIgiAIgiAIBkEQDPqXDwbeQg474qhjjjvhpFNOO+Osc8674KJLLrviqmuuu+GmW26746577nvgoUcee+KpZ5574aVXXnvjrb/87R//eue9Dz765LMvvvrmu//88NMvBz7eBR1y2BFHHXPcCSedctoZZ51z3gUXXXLZFVddc90NN91y2x133XPfAw898tgTTz3z3AsvvfLaG2/95W//+Nc7733w0SefffHVN9/954effjnw+S7okMOOOOqY40446ZTTzjjrnPMuuOiSy6646prrbrjpltvu+B9fwUXT + + diff --git a/geos-mesh/tests/data/fracture_res5_id_empty.vtp b/geos-mesh/tests/data/fracture_res5_id_empty.vtp new file mode 100644 index 000000000..e3b8bddd8 --- /dev/null +++ b/geos-mesh/tests/data/fracture_res5_id_empty.vtp @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + 0 + + + 2304.8861143 + + + + + 0 + + + 2304.8861143 + + + + + + + + + + + + + + + + + + + + + + + + _AQAAAAAAAAAAgAAAAAAAAKAGAAAAAAAAbgEAAAAAAAA=eF4txdciEAAAAEBRUmlpK9q0aEpbey/tTUMb0d57T6VBO+2NSGjvoflDHrp7uYCA/6o40EGu6moOdnWHuIZrupZDXdt1XNf1XN9hbuCGbuTGbuKmbuZwN3cLRzjSLd3Krd3Gbd3O7R3laHdwR3dyZ3dxjGPd1d3c3T3c070c596Odx/3dT/39wAP9CAneLCHeKiHebhHeKRHebTHeKzHebwneKInebITPcVTPc3TPcMzPcuzPcdzPc/zvcBJTvZCL/JiL3GKl3qZl3uFV3qVVzvVaU73Gmc402u9zuu9wRu9yZu9xVu9zdu9wzu9y7u9x3u9z/t9wAd9yId9xEd9zMd9wid9ylk+7TPO9lmf83lfcI5zfdGXfNlXfNXXfN03nOebvuXbvuO7vuf7fuCHfuTHfuKnzneBC/3MRS72c5f4hUtd5nK/9Cu/9hu/9Tu/9wd/9Cd/9hd/9Td/9w9X+Kd/+bf/+K//uRLqf1dfAQAAAAAAAAAAgAAAAAAAAOAEAAAAAAAAEgEAAAAAAAA=eF4txddCCAAAAMAiozSkoaGh0NAeqGhrSNEg7SFkJKFhlIhoaCBUP9tDdy8XEHAo0Ed81EE+5uM+4ZMOdohPOdRhDneETzvSZxzlaMc41mcd53gnONHnnORkpzjV553mdF/wRV9yhjOd5Wxfdo5zned8F7jQRS52iUt9xVd9zWUud4Wv+4YrXeVq17jWda73TTe40U1u9i23+LZb3eY7vut2d7jTXb7n++72A/e4133u94AHPeRhj3jUDz3mR37sJx73Uz/zc7/whF960q885dd+47ee9oxnPed3fu8P/uh5L/iTF/3ZX7zkr/7mZX/3D6941Wte909veNNb3vYv//Yf7/iv//m/d73nfR8ARZMvOw==AQAAAAAAAAAAgAAAAAAAAPAJAAAAAAAAagIAAAAAAAA=eF5t1aFqK1EUheGJqokoEdVVl3mKoSS3b5AHqC4E+gjja6qihxEjbkxUdZM2LpS62FA/UFlG3Z1N9v5XoVEfw5xz1hwW2UVhv2a8K+JXXuD1CD98v6XrHj994uaA13v8ssEfz/i4wl8NLpb48hFf1/juXvLMJUMl55Zy1kT2H16TN++4bsULXFyJl9vi199yiq9myXohbvHNOy4GfJz8Tb+UuKlwPcd39/i6xpePuFjirwYfV/jjWc7d4PVeMhzw06fk6fHDt6wd3abLC9yM1bvVvz+z8zvYupe27qWte2nrXtq6x9o9tu6lrXtp617aupe27qWte2nrXtq6R565ZKjk3FLOmsj+w2vausc+rXiBrXuSbYuLQp5P5f2Z7CNusXVP8mDrnuTH1j35Xmzdk/vB1j3Jhq17cv/Yuse5G2zdI8MBW/fI02PrHmtHt2nrHvuM1daxLt7B3r0u9nxLe/e6yIC9e7F2j717XXwj9u51cSfYu9fFHWLvXhd3LnnmkqGSc0s5ayL7n7p3tncv9mnFC+zdy2xbfOpePp/K+zPZR9xi717mwd69zI+9e/m92LuX94O9e5kNe/fy/rF3L87dYO9eZDhg717k6bF3L9aeune2dy/2Gat3+b/vM3eItdhn7hBnYZ+5Q2TDPnOH+BbsM3eIb8c+c2P2LLHP3CHuFvvMjTxzyVDJuaWcNZH9TzP3bJ+5sU8rXmCfuZlt+2NG8nwq789kH3GLfeZmHuwzN/Njn7n5vdhnbt4P9pmb2bDP3DhrhX3mxrkb7DM3Mhywz9zI02OfubH2NHPP9pkb+4zd/wFdP06cAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAgAAAAAAAAIATAAAAAAAAZwMAAAAAAAA=eF5d1cMWGGYABeHUSm3bbmqmtu3Utp3atpvadpsitfVCXXS+Rf+7mXPmzP5OmvDfNo6nxVPjRnHS4E+JGw4df3LcYOj4k+L6Q8efGNcbOv6EuO7Q8cfHdYaOPy6uPXT8lLjW0PHHxjWHjj8mrjF0/NFx9aHjj4qrDR1/ZFx16Pgj4ipDxx8eVx46/rC40tDxh8YVh44/JK4wdPzBcfmh4w+Kyw0df2Bcduj4A+IyQ8fvH5ceOn6/uNTQ8fvGJYeO3ycuMXT83nHxoeP3iosNHb9nXHTo+D3iIkPH7x4XHjp+t7jQ0PG7xgWHjt8lLjB0/M5x/qHjd4rzDR2/Y5x36Pgd4sSh47eP8wwdPznOPXT8dnGuoeO3jXMOHb9NnGPo+K3j7EPHbxVnGzp+yzjr0PFbxFmGjt88zjx0/GZxpqHjN53w/+n4TaI/8TtPxSejP9HxT0R/ouMfj/5Exz8W/YmOfzT6Ex3/SPQnOv7h6E90/ENxytDxD0Z/ouMfiP5Ex98f/YmOvy/6Ex1/b/QnOv6e6E90/N3Rn+j4u6I/0fF3Rn+i4++I/kTH3x79iY6/LfoTHX9r9Cc6/pboT3T8zdGf6Piboj/R8TdGf6Ljb4j+RMdfH/2Jjr8u+hMdf230Jzp+avQnOv6a6E90/NXRn+j4q6I/0fFXRn+i46+I/kTHXx79iY6/LPoTHX9p9Cc6/pLoT3T8xXHy0PEXRX+i4y+M/kTHXxD9iY4/P/oTHX9e9Cc6/tzoT3T8OdGf6Pizoz/R8WdFf6Ljz4z+RMefEf2Mjj89+hO/80/8O/oTHf9X9Cc6/s/oT3T8H9Gf6Pjfoz/R8b9Ff6Ljf43+RMf/Ev2Jjv85+hMd/1P0Jzr+x+hPdPwP0Z/o+O+jP9Hx30V/ouO/jf5Ex38T/YmOnxH9iY7/OvoTHf9V9Cc6/svoT3T8F9Gf6Pjp0Z/o+M+jP9Hxn0V/ouM/jf5Ex38S/YmO/zj6Ex3/UfQnOv7DOHXo+A+iP9Hx70d/ouPfi/5Ex78b/YmOfyf6Ex3/dvQnOv6t6E90/JvRn+j4N6I/0fGvR3+i41+L/kTHvxr9iY5/JfoTHf9y9Cc6/qXoT3T8i9Gf6PgXoj/R8c9Hf6Ljn4v+RMc/G/2Jjp8W/YmOfyb6GR3/dPwXoR4BOA==AQAAAAAAAAAAgAAAAAAAAOAEAAAAAAAADgEAAAAAAAA=eF4txRFwAgAAAMC2C4IgCIIgCIIgCIIgCIIgCIIgCIIgCIIgCIIgCIIgCIIgCAaDwSAIgiAIgiAIBkEQDPqXDwbeQg474qhjjjvhpFNOO+Osc8674KJLLrviqmuuu+GmW26746577nvgoUcee+KpZ5574aVXXnvjrb/87R//eue9Dz765LMvvvrmu//88NMvBz7eBR1y2BFHHXPcCSedctoZZ51z3gUXXXLZFVddc90NN91y2x133XPfAw898tgTTz3z3AsvvfLaG2/95W//+Nc7733w0SefffHVN9/954effjnw+S7okMOOOOqY40446ZTTzjjrnPMuuOiSy6646prrbrjpltvu+B9fwUXT + + diff --git a/geos-mesh/tests/test_arrayHelpers.py b/geos-mesh/tests/test_arrayHelpers.py index cbfaf50d4..0ce711cfe 100644 --- a/geos-mesh/tests/test_arrayHelpers.py +++ b/geos-mesh/tests/test_arrayHelpers.py @@ -274,18 +274,14 @@ def test_getComponentNamesMultiBlock( assert obtained == expected -@pytest.mark.parametrize( "attributeNames, expected_columns", [ - ( ( "CellAttribute1", ), ( "CellAttribute1_0", "CellAttribute1_1", "CellAttribute1_2" ) ), - ( ( - "CellAttribute1", - "CellAttribute2", - ), ( "CellAttribute2", "CellAttribute1_0", "CellAttribute1_1", "CellAttribute1_2" ) ), +@pytest.mark.parametrize( "attributeNames, onPoints, expected_columns", [ + ( ( "collocated_nodes", ), True, ( "collocated_nodes_0", "collocated_nodes_1" ) ), ] ) def test_getAttributeValuesAsDF( dataSetTest: vtkPolyData, attributeNames: Tuple[ str, ...], - expected_columns: Tuple[ str, ...] ) -> None: + onPoints: bool, expected_columns: Tuple[ str, ...] ) -> None: """Test getting an attribute from a polydata as a dataframe.""" - polydataset: vtkPolyData = dataSetTest( "polydata" ) - data: pd.DataFrame = arrayHelpers.getAttributeValuesAsDF( polydataset, attributeNames ) + polydataset: vtkPolyData = vtkPolyData.SafeDownCast( dataSetTest( "polydata" ) ) + data: pd.DataFrame = arrayHelpers.getAttributeValuesAsDF( polydataset, attributeNames, onPoints ) obtained_columns = data.columns.values.tolist() assert obtained_columns == list( expected_columns ) From 9849529f8636eee68ce14103e928476f1c6cc339 Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Fri, 12 Sep 2025 15:51:15 +0200 Subject: [PATCH 43/45] map volume to surface mesh --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index c77fa9b6b..fcdd87881 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -218,36 +218,37 @@ def UpdateDictElementMappingFromDataSetToDataSet( for idElementTo in range( nbElementsTo ): # Test if the element of the final mesh is already mapped. if -1 in elementMap[ flatIdDataSetTo ][ idElementTo ]: - coordElementTo: tuple[ float, ...] + coordElementTo: list[ tuple[ float, ...] ] = [] if points: - coordElementTo = dataSetTo.GetPoint( idElementTo ) + coordElementTo.append( dataSetTo.GetPoint( idElementTo ) ) else: # Get the coordinates of each points of the cell. nbPointsTo: int = dataSetTo.GetCell( idElementTo ).GetNumberOfPoints() cellPointsTo: vtkPoints = dataSetTo.GetCell( idElementTo ).GetPoints() - coordPointsTo: list = [] for idPointTo in range( nbPointsTo ): - coordPointsTo.extend( cellPointsTo.GetPoint( idPointTo ) ) - coordElementTo = tuple( coordPointsTo ) + coordElementTo.append( cellPointsTo.GetPoint( idPointTo ) ) idElementFrom: int = 0 ElementFromFund: bool = False while idElementFrom < nbElementsFrom and not ElementFromFund: # Test if the element of the source mesh is already mapped. if idElementFrom not in idElementsFromFund: - coordElementFrom: tuple[ float, ...] + coordElementFrom: list[ tuple[ float, ...] ] = [] if points: - coordElementFrom = dataSetFrom.GetPoint( idElementFrom ) + coordElementFrom.append( dataSetFrom.GetPoint( idElementFrom ) ) else: # Get the coordinates of each points of the cell. nbPointsFrom: int = dataSetFrom.GetCell( idElementFrom ).GetNumberOfPoints() cellPointsFrom: vtkPoints = dataSetFrom.GetCell( idElementFrom ).GetPoints() - coordPointsFrom: list = [] for idPointFrom in range( nbPointsFrom ): - coordPointsFrom.extend( cellPointsFrom.GetPoint( idPointFrom ) ) - coordElementFrom = tuple( coordPointsFrom ) + coordElementFrom.append( cellPointsFrom.GetPoint( idPointFrom ) ) - if coordElementFrom == coordElementTo: + pointShared: bool = True + for coordPointsTo in coordElementTo: + if coordPointsTo not in coordElementFrom: + pointShared = False + + if pointShared: elementMap[ flatIdDataSetTo ][ idElementTo ] = [ flatIdDataSetFrom, idElementFrom ] ElementFromFund = True idElementsFromFund.append( idElementFrom ) From cc730e4820df2c66151801ea9b9879e4fd8e503c Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Mon, 15 Sep 2025 10:00:16 +0200 Subject: [PATCH 44/45] Fix ci --- geos-mesh/src/geos/mesh/utils/arrayHelpers.py | 8 +- geos-mesh/tests/conftest.py | 320 +++++++++--------- geos-mesh/tests/test_arrayHelpers.py | 4 +- 3 files changed, 167 insertions(+), 165 deletions(-) diff --git a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py index 7f0017507..31f6ecdb1 100644 --- a/geos-mesh/src/geos/mesh/utils/arrayHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/arrayHelpers.py @@ -250,7 +250,7 @@ def UpdateDictElementMappingFromDataSetToDataSet( pointShared: bool = True if dataSetTo.GetClassName() == dataSetFrom.GetClassName(): - if not coordElementTo == coordElementFrom: + if coordElementTo != coordElementFrom: pointShared = False elif isinstance( dataSetTo, vtkPolyData ): for coordPointsTo in coordElementTo: @@ -260,7 +260,7 @@ def UpdateDictElementMappingFromDataSetToDataSet( for coordPointsFrom in coordElementFrom: if coordPointsFrom not in coordElementTo: pointShared = False - + if pointShared: elementMap[ flatIdDataSetTo ][ idElementTo ] = [ flatIdDataSetFrom, idElementFrom ] ElementFromFund = True @@ -896,7 +896,9 @@ def getComponentNamesMultiBlock( return () -def getAttributeValuesAsDF( surface: vtkPolyData, attributeNames: tuple[ str, ...], onPoints: bool = False ) -> pd.DataFrame: +def getAttributeValuesAsDF( surface: vtkPolyData, + attributeNames: tuple[ str, ...], + onPoints: bool = False ) -> pd.DataFrame: """Get attribute values from input surface. Args: diff --git a/geos-mesh/tests/conftest.py b/geos-mesh/tests/conftest.py index 766dc002e..b358d9104 100644 --- a/geos-mesh/tests/conftest.py +++ b/geos-mesh/tests/conftest.py @@ -217,163 +217,164 @@ def _get_elementMap( meshFromName: str, meshToName: str, points: bool ) -> dict[ elif meshToName == "emptyFracture": elementMap[ 0 ] = np.full( ( nbElements[ 1 ], 2 ), -1, np.int64 ) elif meshToName == "emptypolydata": - elementMap[ 0 ] = np.array( [ [ 0, 0 ], - [ 0, 1 ], - [ 0, 2 ], - [ 0, 3 ], - [ 0, 4 ], - [ 0, 5 ], - [ 0, 6 ], - [ 0, 7 ], - [ 0, 8 ], - [ 0, 9 ], - [ 0, 10 ], - [ 0, 11 ], - [ 0, 12 ], - [ 0, 13 ], - [ 0, 14 ], - [ 0, 15 ], - [ 0, 16 ], - [ 0, 17 ], - [ 0, 18 ], - [ 0, 19 ], - [ 0, 20 ], - [ 0, 21 ], - [ 0, 22 ], - [ 0, 23 ], - [ 0, 48 ], - [ 0, 50 ], - [ 0, 51 ], - [ 0, 54 ], - [ 0, 56 ], - [ 0, 57 ], - [ 0, 58 ], - [ 0, 59 ], - [ 0, 60 ], - [ 0, 61 ], - [ 0, 62 ], - [ 0, 63 ], - [ 0, 64 ], - [ 0, 65 ], - [ 0, 66 ], - [ 0, 67 ], - [ 0, 68 ], - [ 0, 69 ], - [ 0, 70 ], - [ 0, 71 ], - [ 0, 72 ], - [ 0, 73 ], - [ 0, 74 ], - [ 0, 75 ], - [ 0, 76 ], - [ 0, 77 ], - [ 0, 78 ], - [ 0, 79 ], - [ 0, 580 ], - [ 0, 581 ], - [ 0, 582 ], - [ 0, 583 ], - [ 0, 584 ], - [ 0, 585 ], - [ 0, 586 ], - [ 0, 587 ], - [ 0, 588 ], - [ 0, 589 ], - [ 0, 590 ], - [ 0, 591 ], - [ 0, 592 ], - [ 0, 593 ], - [ 0, 594 ], - [ 0, 595 ], - [ 0, 596 ], - [ 0, 597 ], - [ 0, 598 ], - [ 0, 599 ], - [ 0, 600 ], - [ 0, 601 ], - [ 0, 602 ], - [ 0, 603 ], - [ 0, 628 ], - [ 0, 630 ], - [ 0, 631 ], - [ 0, 634 ], - [ 0, 636 ], - [ 0, 637 ], - [ 0, 638 ], - [ 0, 639 ], - [ 0, 640 ], - [ 0, 641 ], - [ 0, 642 ], - [ 0, 643 ], - [ 0, 644 ], - [ 0, 645 ], - [ 0, 646 ], - [ 0, 647 ], - [ 0, 648 ], - [ 0, 649 ], - [ 0, 650 ], - [ 0, 651 ], - [ 0, 652 ], - [ 0, 653 ], - [ 0, 654 ], - [ 0, 655 ], - [ 0, 656 ], - [ 0, 657 ], - [ 0, 658 ], - [ 0, 659 ], - [ 0, 1160 ], - [ 0, 1161 ], - [ 0, 1162 ], - [ 0, 1163 ], - [ 0, 1164 ], - [ 0, 1165 ], - [ 0, 1166 ], - [ 0, 1167 ], - [ 0, 1168 ], - [ 0, 1169 ], - [ 0, 1170 ], - [ 0, 1171 ], - [ 0, 1172 ], - [ 0, 1173 ], - [ 0, 1174 ], - [ 0, 1175 ], - [ 0, 1176 ], - [ 0, 1177 ], - [ 0, 1178 ], - [ 0, 1179 ], - [ 0, 1180 ], - [ 0, 1181 ], - [ 0, 1182 ], - [ 0, 1183 ], - [ 0, 1208 ], - [ 0, 1210 ], - [ 0, 1211 ], - [ 0, 1214 ], - [ 0, 1216 ], - [ 0, 1217 ], - [ 0, 1218 ], - [ 0, 1219 ], - [ 0, 1220 ], - [ 0, 1221 ], - [ 0, 1222 ], - [ 0, 1223 ], - [ 0, 1224 ], - [ 0, 1225 ], - [ 0, 1226 ], - [ 0, 1227 ], - [ 0, 1228 ], - [ 0, 1229 ], - [ 0, 1230 ], - [ 0, 1231 ], - [ 0, 1232 ], - [ 0, 1233 ], - [ 0, 1234 ], - [ 0, 1235 ], - [ 0, 1236 ], - [ 0, 1237 ], - [ 0, 1238 ], - [ 0, 1239 ], - ] ) + elementMap[ 0 ] = np.array( [ + [ 0, 0 ], + [ 0, 1 ], + [ 0, 2 ], + [ 0, 3 ], + [ 0, 4 ], + [ 0, 5 ], + [ 0, 6 ], + [ 0, 7 ], + [ 0, 8 ], + [ 0, 9 ], + [ 0, 10 ], + [ 0, 11 ], + [ 0, 12 ], + [ 0, 13 ], + [ 0, 14 ], + [ 0, 15 ], + [ 0, 16 ], + [ 0, 17 ], + [ 0, 18 ], + [ 0, 19 ], + [ 0, 20 ], + [ 0, 21 ], + [ 0, 22 ], + [ 0, 23 ], + [ 0, 48 ], + [ 0, 50 ], + [ 0, 51 ], + [ 0, 54 ], + [ 0, 56 ], + [ 0, 57 ], + [ 0, 58 ], + [ 0, 59 ], + [ 0, 60 ], + [ 0, 61 ], + [ 0, 62 ], + [ 0, 63 ], + [ 0, 64 ], + [ 0, 65 ], + [ 0, 66 ], + [ 0, 67 ], + [ 0, 68 ], + [ 0, 69 ], + [ 0, 70 ], + [ 0, 71 ], + [ 0, 72 ], + [ 0, 73 ], + [ 0, 74 ], + [ 0, 75 ], + [ 0, 76 ], + [ 0, 77 ], + [ 0, 78 ], + [ 0, 79 ], + [ 0, 580 ], + [ 0, 581 ], + [ 0, 582 ], + [ 0, 583 ], + [ 0, 584 ], + [ 0, 585 ], + [ 0, 586 ], + [ 0, 587 ], + [ 0, 588 ], + [ 0, 589 ], + [ 0, 590 ], + [ 0, 591 ], + [ 0, 592 ], + [ 0, 593 ], + [ 0, 594 ], + [ 0, 595 ], + [ 0, 596 ], + [ 0, 597 ], + [ 0, 598 ], + [ 0, 599 ], + [ 0, 600 ], + [ 0, 601 ], + [ 0, 602 ], + [ 0, 603 ], + [ 0, 628 ], + [ 0, 630 ], + [ 0, 631 ], + [ 0, 634 ], + [ 0, 636 ], + [ 0, 637 ], + [ 0, 638 ], + [ 0, 639 ], + [ 0, 640 ], + [ 0, 641 ], + [ 0, 642 ], + [ 0, 643 ], + [ 0, 644 ], + [ 0, 645 ], + [ 0, 646 ], + [ 0, 647 ], + [ 0, 648 ], + [ 0, 649 ], + [ 0, 650 ], + [ 0, 651 ], + [ 0, 652 ], + [ 0, 653 ], + [ 0, 654 ], + [ 0, 655 ], + [ 0, 656 ], + [ 0, 657 ], + [ 0, 658 ], + [ 0, 659 ], + [ 0, 1160 ], + [ 0, 1161 ], + [ 0, 1162 ], + [ 0, 1163 ], + [ 0, 1164 ], + [ 0, 1165 ], + [ 0, 1166 ], + [ 0, 1167 ], + [ 0, 1168 ], + [ 0, 1169 ], + [ 0, 1170 ], + [ 0, 1171 ], + [ 0, 1172 ], + [ 0, 1173 ], + [ 0, 1174 ], + [ 0, 1175 ], + [ 0, 1176 ], + [ 0, 1177 ], + [ 0, 1178 ], + [ 0, 1179 ], + [ 0, 1180 ], + [ 0, 1181 ], + [ 0, 1182 ], + [ 0, 1183 ], + [ 0, 1208 ], + [ 0, 1210 ], + [ 0, 1211 ], + [ 0, 1214 ], + [ 0, 1216 ], + [ 0, 1217 ], + [ 0, 1218 ], + [ 0, 1219 ], + [ 0, 1220 ], + [ 0, 1221 ], + [ 0, 1222 ], + [ 0, 1223 ], + [ 0, 1224 ], + [ 0, 1225 ], + [ 0, 1226 ], + [ 0, 1227 ], + [ 0, 1228 ], + [ 0, 1229 ], + [ 0, 1230 ], + [ 0, 1231 ], + [ 0, 1232 ], + [ 0, 1233 ], + [ 0, 1234 ], + [ 0, 1235 ], + [ 0, 1236 ], + [ 0, 1237 ], + [ 0, 1238 ], + [ 0, 1239 ], + ] ) elif meshToName == "emptymultiblock": elementMap[ 1 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 0 ] ) ] ) elementMap[ 3 ] = np.full( ( nbElements[ 1 ], 2 ), -1, np.int64 ) @@ -383,9 +384,8 @@ def _get_elementMap( meshFromName: str, meshToName: str, points: bool ) -> dict[ elif meshToName == "emptymultiblock": elementMap[ 1 ] = np.full( ( nbElements[ 0 ], 2 ), -1, np.int64 ) elementMap[ 3 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 1 ] ) ] ) - elif meshFromName == "polydata": - if meshToName == "emptypolydata": - elementMap[ 0 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 1 ] ) ] ) + elif meshFromName == "polydata" and meshToName == "emptypolydata": + elementMap[ 0 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 1 ] ) ] ) return elementMap diff --git a/geos-mesh/tests/test_arrayHelpers.py b/geos-mesh/tests/test_arrayHelpers.py index 2ef7ba88a..b370d43b3 100644 --- a/geos-mesh/tests/test_arrayHelpers.py +++ b/geos-mesh/tests/test_arrayHelpers.py @@ -310,8 +310,8 @@ def test_getComponentNamesMultiBlock( @pytest.mark.parametrize( "attributeNames, onPoints, expected_columns", [ ( ( "collocated_nodes", ), True, ( "collocated_nodes_0", "collocated_nodes_1" ) ), ] ) -def test_getAttributeValuesAsDF( dataSetTest: vtkPolyData, attributeNames: Tuple[ str, ...], - onPoints: bool, expected_columns: Tuple[ str, ...] ) -> None: +def test_getAttributeValuesAsDF( dataSetTest: vtkPolyData, attributeNames: Tuple[ str, ...], onPoints: bool, + expected_columns: Tuple[ str, ...] ) -> None: """Test getting an attribute from a polydata as a dataframe.""" polydataset: vtkPolyData = vtkPolyData.SafeDownCast( dataSetTest( "polydata" ) ) data: pd.DataFrame = arrayHelpers.getAttributeValuesAsDF( polydataset, attributeNames, onPoints ) From e0a46c935e50e23a1d167e48497ee4038a18946d Mon Sep 17 00:00:00 2001 From: Romain Baville Date: Thu, 25 Sep 2025 15:13:28 +0200 Subject: [PATCH 45/45] Apply Paloma and Jacques suggestions --- .../geos/mesh/processing/AttributeMapping.py | 31 +-- geos-mesh/tests/conftest.py | 201 ++++-------------- geos-mesh/tests/test_arrayHelpers.py | 1 - 3 files changed, 54 insertions(+), 179 deletions(-) diff --git a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py index 0f6ff2411..465f986b5 100644 --- a/geos-mesh/src/geos/mesh/processing/AttributeMapping.py +++ b/geos-mesh/src/geos/mesh/processing/AttributeMapping.py @@ -4,8 +4,10 @@ # ruff: noqa: E402 # disable Module level import not at top of file import numpy as np import numpy.typing as npt -from geos.utils.Logger import logging, Logger, getLogger -from typing_extensions import Self, Union +import logging + +from geos.utils.Logger import ( Logger, getLogger ) +from typing_extensions import Self, Union, Set, List, Dict from vtkmodules.vtkCommonDataModel import ( vtkDataSet, @@ -38,7 +40,7 @@ # Filter inputs. meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] - attributeNames: set[ str ] + attributeNames: Set[ str ] # Optional inputs. onPoints: bool # defaults to False speHandler: bool # defaults to False @@ -68,7 +70,7 @@ def __init__( self: Self, meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ], meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ], - attributeNames: set[ str ], + attributeNames: Set[ str ], onPoints: bool = False, speHandler: bool = False, ) -> None: @@ -77,7 +79,7 @@ def __init__( Args: meshFrom (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The source mesh with attributes to transfer. meshTo (Union[ vtkDataSet, vtkMultiBlockDataSet ]): The final mesh where to transfer attributes. - attributeNames (set[str]): Names of the attributes to transfer. + attributeNames (Set[str]): Names of the attributes to transfer. onPoints (bool): True if attributes are on points, False if they are on cells. Defaults to False. speHandler (bool, optional): True to use a specific handler, False to use the internal handler. @@ -85,12 +87,13 @@ def __init__( """ self.meshFrom: Union[ vtkDataSet, vtkMultiBlockDataSet ] = meshFrom self.meshTo: Union[ vtkDataSet, vtkMultiBlockDataSet ] = meshTo - self.attributeNames: set[ str ] = attributeNames + self.attributeNames: Set[ str ] = attributeNames self.onPoints: bool = onPoints + #TODO/refact (@RomainBaville) make it an enum self.piece: str = "points" if self.onPoints else "cells" # cell map - self.ElementMap: dict[ int, npt.NDArray[ np.int64 ] ] = {} + self.ElementMap: Dict[ int, npt.NDArray[ np.int64 ] ] = {} # Logger. self.logger: Logger @@ -115,13 +118,13 @@ def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: "The logger already has an handler, to use yours set the argument 'speHandler' to True during the filter initialization." ) - def getElementMap( self: Self ) -> dict[ int, npt.NDArray[ np.int64 ] ]: + def getElementMap( self: Self ) -> Dict[ int, npt.NDArray[ np.int64 ] ]: """Getter of the element mapping dictionary. If attribute to transfer are on points it will be a pointMap, else it will be a cellMap. Returns: - self.elementMap (dict[int, npt.NDArray[np.int64]]): The element mapping dictionary. + self.elementMap (Dict[int, npt.NDArray[np.int64]]): The element mapping dictionary. """ return self.ElementMap @@ -138,16 +141,16 @@ def applyFilter( self: Self ) -> bool: self.logger.warning( f"The filter { self.logger.name } has not been used." ) return False - attributesInMeshFrom: set[ str ] = getAttributeSet( self.meshFrom, self.onPoints ) - wrongAttributeNames: set[ str ] = self.attributeNames.difference( attributesInMeshFrom ) + attributesInMeshFrom: Set[ str ] = getAttributeSet( self.meshFrom, self.onPoints ) + wrongAttributeNames: Set[ str ] = self.attributeNames.difference( attributesInMeshFrom ) if len( wrongAttributeNames ) > 0: self.logger.error( f"The { self.piece } attributes { wrongAttributeNames } are not present in the source mesh." ) self.logger.error( f"The filter { self.logger.name } failed." ) return False - attributesInMeshTo: set[ str ] = getAttributeSet( self.meshTo, self.onPoints ) - attributesAlreadyInMeshTo: set[ str ] = self.attributeNames.intersection( attributesInMeshTo ) + attributesInMeshTo: Set[ str ] = getAttributeSet( self.meshTo, self.onPoints ) + attributesAlreadyInMeshTo: Set[ str ] = self.attributeNames.intersection( attributesInMeshTo ) if len( attributesAlreadyInMeshTo ) > 0: self.logger.error( f"The { self.piece } attributes { attributesAlreadyInMeshTo } are already present in the final mesh." ) @@ -155,7 +158,7 @@ def applyFilter( self: Self ) -> bool: return False if isinstance( self.meshFrom, vtkMultiBlockDataSet ): - partialAttributes: list[ str ] = [] + partialAttributes: List[ str ] = [] for attributeName in self.attributeNames: if not isAttributeGlobal( self.meshFrom, attributeName, self.onPoints ): partialAttributes.append( attributeName ) diff --git a/geos-mesh/tests/conftest.py b/geos-mesh/tests/conftest.py index b358d9104..5d4b74d51 100644 --- a/geos-mesh/tests/conftest.py +++ b/geos-mesh/tests/conftest.py @@ -5,7 +5,7 @@ # ruff: noqa: E402 # disable Module level import not at top of file import os import pytest -from typing import Union, Any +from typing import Union, Any, Tuple, Dict import numpy as np import numpy.typing as npt @@ -189,10 +189,10 @@ def getElementMap() -> Any: """Get the element indexes mapping dictionary using the function _get_elementMap() between two meshes. Returns: - elementMap (dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary. + elementMap (Dict[int, npt.NDArray[np.int64]]): The cell mapping dictionary. """ - def _get_elementMap( meshFromName: str, meshToName: str, points: bool ) -> dict[ int, npt.NDArray[ np.int64 ] ]: + def _get_elementMap( meshFromName: str, meshToName: str, points: bool ) -> Dict[ int, npt.NDArray[ np.int64 ] ]: """Get the element indexes mapping dictionary between two meshes. Args: @@ -201,10 +201,10 @@ def _get_elementMap( meshFromName: str, meshToName: str, points: bool ) -> dict[ points (bool): True if elements to map is points, False if it is cells. Returns: - elementMap (dict[int, npt.NDArray[np.int64]]): The element mapping dictionary. + elementMap (Dict[int, npt.NDArray[np.int64]]): The element mapping dictionary. """ - elementMap: dict[ int, npt.NDArray[ np.int64 ] ] = {} - nbElements: tuple[ int, int ] = ( 4092, 212 ) if points else ( 1740, 156 ) + elementMap: Dict[ int, npt.NDArray[ np.int64 ] ] = {} + nbElements: Tuple[ int, int ] = ( 4092, 212 ) if points else ( 1740, 156 ) if meshFromName == "multiblock": if meshToName == "emptymultiblock": elementMap[ 1 ] = np.array( [ [ 1, element ] for element in range( nbElements[ 0 ] ) ] ) @@ -217,164 +217,37 @@ def _get_elementMap( meshFromName: str, meshToName: str, points: bool ) -> dict[ elif meshToName == "emptyFracture": elementMap[ 0 ] = np.full( ( nbElements[ 1 ], 2 ), -1, np.int64 ) elif meshToName == "emptypolydata": - elementMap[ 0 ] = np.array( [ - [ 0, 0 ], - [ 0, 1 ], - [ 0, 2 ], - [ 0, 3 ], - [ 0, 4 ], - [ 0, 5 ], - [ 0, 6 ], - [ 0, 7 ], - [ 0, 8 ], - [ 0, 9 ], - [ 0, 10 ], - [ 0, 11 ], - [ 0, 12 ], - [ 0, 13 ], - [ 0, 14 ], - [ 0, 15 ], - [ 0, 16 ], - [ 0, 17 ], - [ 0, 18 ], - [ 0, 19 ], - [ 0, 20 ], - [ 0, 21 ], - [ 0, 22 ], - [ 0, 23 ], - [ 0, 48 ], - [ 0, 50 ], - [ 0, 51 ], - [ 0, 54 ], - [ 0, 56 ], - [ 0, 57 ], - [ 0, 58 ], - [ 0, 59 ], - [ 0, 60 ], - [ 0, 61 ], - [ 0, 62 ], - [ 0, 63 ], - [ 0, 64 ], - [ 0, 65 ], - [ 0, 66 ], - [ 0, 67 ], - [ 0, 68 ], - [ 0, 69 ], - [ 0, 70 ], - [ 0, 71 ], - [ 0, 72 ], - [ 0, 73 ], - [ 0, 74 ], - [ 0, 75 ], - [ 0, 76 ], - [ 0, 77 ], - [ 0, 78 ], - [ 0, 79 ], - [ 0, 580 ], - [ 0, 581 ], - [ 0, 582 ], - [ 0, 583 ], - [ 0, 584 ], - [ 0, 585 ], - [ 0, 586 ], - [ 0, 587 ], - [ 0, 588 ], - [ 0, 589 ], - [ 0, 590 ], - [ 0, 591 ], - [ 0, 592 ], - [ 0, 593 ], - [ 0, 594 ], - [ 0, 595 ], - [ 0, 596 ], - [ 0, 597 ], - [ 0, 598 ], - [ 0, 599 ], - [ 0, 600 ], - [ 0, 601 ], - [ 0, 602 ], - [ 0, 603 ], - [ 0, 628 ], - [ 0, 630 ], - [ 0, 631 ], - [ 0, 634 ], - [ 0, 636 ], - [ 0, 637 ], - [ 0, 638 ], - [ 0, 639 ], - [ 0, 640 ], - [ 0, 641 ], - [ 0, 642 ], - [ 0, 643 ], - [ 0, 644 ], - [ 0, 645 ], - [ 0, 646 ], - [ 0, 647 ], - [ 0, 648 ], - [ 0, 649 ], - [ 0, 650 ], - [ 0, 651 ], - [ 0, 652 ], - [ 0, 653 ], - [ 0, 654 ], - [ 0, 655 ], - [ 0, 656 ], - [ 0, 657 ], - [ 0, 658 ], - [ 0, 659 ], - [ 0, 1160 ], - [ 0, 1161 ], - [ 0, 1162 ], - [ 0, 1163 ], - [ 0, 1164 ], - [ 0, 1165 ], - [ 0, 1166 ], - [ 0, 1167 ], - [ 0, 1168 ], - [ 0, 1169 ], - [ 0, 1170 ], - [ 0, 1171 ], - [ 0, 1172 ], - [ 0, 1173 ], - [ 0, 1174 ], - [ 0, 1175 ], - [ 0, 1176 ], - [ 0, 1177 ], - [ 0, 1178 ], - [ 0, 1179 ], - [ 0, 1180 ], - [ 0, 1181 ], - [ 0, 1182 ], - [ 0, 1183 ], - [ 0, 1208 ], - [ 0, 1210 ], - [ 0, 1211 ], - [ 0, 1214 ], - [ 0, 1216 ], - [ 0, 1217 ], - [ 0, 1218 ], - [ 0, 1219 ], - [ 0, 1220 ], - [ 0, 1221 ], - [ 0, 1222 ], - [ 0, 1223 ], - [ 0, 1224 ], - [ 0, 1225 ], - [ 0, 1226 ], - [ 0, 1227 ], - [ 0, 1228 ], - [ 0, 1229 ], - [ 0, 1230 ], - [ 0, 1231 ], - [ 0, 1232 ], - [ 0, 1233 ], - [ 0, 1234 ], - [ 0, 1235 ], - [ 0, 1236 ], - [ 0, 1237 ], - [ 0, 1238 ], - [ 0, 1239 ], - ] ) + elementMap[ 0 ] = np.array( [ [ 0, 0 ], [ 0, 1 ], [ 0, 2 ], [ 0, 3 ], [ 0, 4 ], [ 0, 5 ], [ 0, 6 ], + [ 0, 7 ], [ 0, 8 ], [ 0, 9 ], [ 0, 10 ], [ 0, 11 ], [ 0, 12 ], [ 0, 13 ], + [ 0, 14 ], [ 0, 15 ], [ 0, 16 ], [ 0, 17 ], [ 0, 18 ], [ 0, + 19 ], [ 0, 20 ], + [ 0, 21 ], [ 0, 22 ], [ 0, 23 ], [ 0, 48 ], [ 0, 50 ], [ 0, + 51 ], [ 0, 54 ], + [ 0, 56 ], [ 0, 57 ], [ 0, 58 ], [ 0, 59 ], [ 0, 60 ], [ 0, + 61 ], [ 0, 62 ], + [ 0, 63 ], [ 0, 64 ], [ 0, 65 ], [ 0, 66 ], [ 0, 67 ], [ 0, + 68 ], [ 0, 69 ], + [ 0, 70 ], [ 0, 71 ], [ 0, 72 ], [ 0, 73 ], [ 0, 74 ], + [ 0, 75 ], [ 0, 76 ], [ 0, 77 ], [ 0, 78 ], [ 0, 79 ], [ 0, 580 ], + [ 0, 581 ], [ 0, 582 ], [ 0, 583 ], [ 0, 584 ], [ 0, 585 ], [ 0, 586 ], + [ 0, 587 ], [ 0, 588 ], [ 0, 589 ], [ 0, 590 ], [ 0, 591 ], [ 0, 592 ], + [ 0, 593 ], [ 0, 594 ], [ 0, 595 ], [ 0, 596 ], [ 0, 597 ], [ 0, 598 ], + [ 0, 599 ], [ 0, 600 ], [ 0, 601 ], [ 0, 602 ], [ 0, 603 ], [ 0, 628 ], + [ 0, 630 ], [ 0, 631 ], [ 0, 634 ], [ 0, 636 ], [ 0, 637 ], [ 0, 638 ], + [ 0, 639 ], [ 0, 640 ], [ 0, 641 ], [ 0, 642 ], [ 0, 643 ], [ 0, 644 ], + [ 0, 645 ], [ 0, 646 ], [ 0, 647 ], [ 0, 648 ], [ 0, 649 ], [ 0, 650 ], + [ 0, 651 ], [ 0, 652 ], [ 0, 653 ], [ 0, 654 ], [ 0, 655 ], [ 0, 656 ], + [ 0, 657 ], [ 0, 658 ], [ 0, 659 ], [ 0, 1160 ], [ 0, 1161 ], [ 0, 1162 ], + [ 0, 1163 ], [ 0, 1164 ], [ 0, 1165 ], [ 0, 1166 ], [ 0, 1167 ], + [ 0, 1168 ], [ 0, 1169 ], [ 0, 1170 ], [ 0, 1171 ], [ 0, 1172 ], + [ 0, 1173 ], [ 0, 1174 ], [ 0, 1175 ], [ 0, 1176 ], [ 0, 1177 ], + [ 0, 1178 ], [ 0, 1179 ], [ 0, 1180 ], [ 0, 1181 ], [ 0, 1182 ], + [ 0, 1183 ], [ 0, 1208 ], [ 0, 1210 ], [ 0, 1211 ], [ 0, 1214 ], + [ 0, 1216 ], [ 0, 1217 ], [ 0, 1218 ], [ 0, 1219 ], [ 0, 1220 ], + [ 0, 1221 ], [ 0, 1222 ], [ 0, 1223 ], [ 0, 1224 ], [ 0, 1225 ], + [ 0, 1226 ], [ 0, 1227 ], [ 0, 1228 ], [ 0, 1229 ], [ 0, 1230 ], + [ 0, 1231 ], [ 0, 1232 ], [ 0, 1233 ], [ 0, 1234 ], [ 0, 1235 ], + [ 0, 1236 ], [ 0, 1237 ], [ 0, 1238 ], [ 0, 1239 ] ] ) elif meshToName == "emptymultiblock": elementMap[ 1 ] = np.array( [ [ 0, element ] for element in range( nbElements[ 0 ] ) ] ) elementMap[ 3 ] = np.full( ( nbElements[ 1 ], 2 ), -1, np.int64 ) diff --git a/geos-mesh/tests/test_arrayHelpers.py b/geos-mesh/tests/test_arrayHelpers.py index b370d43b3..e1d58ab38 100644 --- a/geos-mesh/tests/test_arrayHelpers.py +++ b/geos-mesh/tests/test_arrayHelpers.py @@ -48,7 +48,6 @@ def test_computeElementMapping( assert keysComputed == keysTest for key in keysTest: - print( elementMapComputed[ key ] ) assert np.all( elementMapComputed[ key ] == elementMapTest[ key ] )