From b93c7947a578db66d936fd5f833390e4bbde5f4d Mon Sep 17 00:00:00 2001 From: alexbenedicto Date: Fri, 15 Aug 2025 11:30:20 -0700 Subject: [PATCH 01/13] Add geos_dz script in PVPlugins as PVDMZFinder --- geos-pv/src/geos/pv/plugins/PVDMZFinder.py | 125 +++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 geos-pv/src/geos/pv/plugins/PVDMZFinder.py diff --git a/geos-pv/src/geos/pv/plugins/PVDMZFinder.py b/geos-pv/src/geos/pv/plugins/PVDMZFinder.py new file mode 100644 index 00000000..4613b07f --- /dev/null +++ b/geos-pv/src/geos/pv/plugins/PVDMZFinder.py @@ -0,0 +1,125 @@ +import numpy as np +from vtkmodules.vtkCommonDataModel import vtkDataSet +from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase +from vtkmodules.numpy_interface import dataset_adapter as dsa +from paraview.util.vtkAlgorithm import smproxy, smproperty, smdomain + + +__doc__ = """ +PVDMZFinder is a Paraview plugin that. + +Input and output types are vtkUnstructuredGrid. + +This filter results in a single output pipeline that contains the volume mesh. + +To use it: + +* Load the module in Paraview: Tools>Manage Plugins...>Load new>geos_dz.py +* Select the Geos output .pvd file loaded in Paraview. + +""" + + +@smproxy.filter(label="DZ Finder") +@smproperty.input(name="Input") +class PVDMZFinder( VTKPythonAlgorithmBase ): + + def __init__( self ): + VTKPythonAlgorithmBase.__init__( self, nInputPorts=1, nOutputPorts=1, outputType='vtkUnstructuredGrid' ) + # Default values. + self._DMZ_len = 50.0 + self._active_region = 80 + self._plane_point = np.array( [ 0.0, 0.0, 0.0 ] ) + self._plane_normal = np.array( [ 0.0, 0.0, 1.0 ] ) + + def RequestData( self, request, inInfo, outInfo ): + # Input data + input0 = dsa.WrapDataObject( vtkDataSet.GetData( inInfo[ 0 ] ) ) + self.CellDataLabels = input0.CellData.keys() + + print( f"Total number of cells: {input0.GetNumberOfCells()}" ) + + # call the main routine to identify the damage zone. + new_array = self.process_cells( input0 ) + + # Output + output = dsa.WrapDataObject( vtkDataSet.GetData( outInfo ) ) + output.ShallowCopy( input0.VTKObject ) + output.CellData.append( new_array, "Reservoir_with_DZ" ) + + return 1 + + def process_cells( self, input0 ): + """This function aims to traverse the model and identify the cells that are part of the damage zone, and in this process, form new regions in the domain's property map that represent the damage zone of the geological fault.""" + num_cells = input0.GetNumberOfCells() + cell_array = input0.CellData[ self.CellDataLabels[ 0 ] ] + vtk_dataset = input0.VTKObject + + new_array = np.zeros( num_cells ) + + for i in range( num_cells ): + original_value = cell_array[ i ] + new_value = original_value + + if self._active_region == new_value: + center = self.get_cell_centroid( vtk_dataset, i ) + flag = self.check_is_dmz( center ) + + if flag: + new_value += 10 + + new_array[ i ] = new_value + + return new_array + + def get_cell_centroid( self, vtk_dataset, cell_id ): + cell = vtk_dataset.GetCell( cell_id ) + num_points = cell.GetNumberOfPoints() + + coords = np.zeros( 3 ) + for j in range( num_points ): + point = cell.GetPoints().GetPoint( j ) + coords += np.array( point ) + + centroid = coords / num_points if num_points > 0 else np.array( [ 0.0, 0.0, 0.0 ] ) + # print(f" Centroid of Cell {cell_id}: ({centroid[0]:.3f}, {centroid[1]:.3f}, {centroid[2]:.3f})") + return centroid + + def check_is_dmz( self, cell_center ): + """ Returns True if the distance from the point to the defined plane is greater than the threshold. """ + if not hasattr( self, "_plane_point" ) or not hasattr( self, "_plane_normal" ): + print( "Plane has not been defined yet." ) + return False + + vec = cell_center - self._plane_point + distance = abs( np.dot( vec, self._plane_normal ) ) + return distance < self._DMZ_len + + @smproperty.intvector( name="Active Region", default_values=80, number_of_elements=1 ) + def SetActReg( self, value ): + self._active_region = int( value ) + print( f"The active region flag is: {self._active_region}" ) + self.Modified() + + @smproperty.doublevector( name="DZ Length", default_values=100.0, number_of_elements=1 ) + def SetDmzLen( self, value ): + self._DMZ_len = float( value ) + print( f"DMZ length set to: {self._DMZ_len}" ) + self.Modified() + + # ----------------------------------------------------------------- + # TEMPORARY IMPLEMENTATION ---- IN FUTURE THIS IS NOT GONNA BE USED + # ----------------------------------------------------------------- + @smproperty.doublevector( name="PlanePoint", default_values=[ 0.0, 0.0, 0.0 ], number_of_elements=3 ) + def SetPlanePoint( self, x, y, z ): + """Defines a point that lies on the plane (x, y, z)""" + self._plane_point = np.array( [ x, y, z ] ) + print( f"Point defined at: {self._plane_point}" ) + self.Modified() + + @smproperty.doublevector( name="PlaneNormal", default_values=[ 0.0, 0.0, 1.0 ], number_of_elements=3 ) + def SetPlaneNormal( self, x, y, z ): + """Defines the normal vector of the plane (nx, ny, nz)""" + self._plane_normal = np.array( [ x, y, z ] ) + print( f"Plane normal defined as: {self._plane_normal}" ) + self.Modified() From 10bce389aef36f85c04222e176e662a8e318b800 Mon Sep 17 00:00:00 2001 From: alexbenedicto Date: Mon, 18 Aug 2025 14:05:22 -0700 Subject: [PATCH 02/13] 1st version of DMZFinder VTK filter --- geos-mesh/src/geos/mesh/fault/DMZFinder.py | 105 +++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 geos-mesh/src/geos/mesh/fault/DMZFinder.py diff --git a/geos-mesh/src/geos/mesh/fault/DMZFinder.py b/geos-mesh/src/geos/mesh/fault/DMZFinder.py new file mode 100644 index 00000000..ad1e8844 --- /dev/null +++ b/geos-mesh/src/geos/mesh/fault/DMZFinder.py @@ -0,0 +1,105 @@ +import numpy as np +from vtkmodules.vtkCommonDataModel import vtkDataSet +from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase +from vtkmodules.numpy_interface import dataset_adapter as dsa + +__doc__ = """ +""" + + +class DMZFinder( VTKPythonAlgorithmBase ): + def __init__( self ): + VTKPythonAlgorithmBase.__init__( + self, nInputPorts=1, nOutputPorts=1, outputType='vtkUnstructuredGrid') + # Default values. + self._DMZ_len = 50.0 + self._active_region = 80 + self._plane_point = np.array([0.0, 0.0, 0.0]) + self._plane_normal = np.array([0.0, 0.0, 1.0]) + + def RequestData(self, request, inInfo, outInfo): + # Input data + input0 = dsa.WrapDataObject(vtkDataSet.GetData(inInfo[0])) + self.CellDataLabels = input0.CellData.keys() + + print(f"Total number of cells: {input0.GetNumberOfCells()}") + + # call the main routine to identify the damage zone. + new_array = self.process_cells(input0) + + # Output + output = dsa.WrapDataObject(vtkDataSet.GetData(outInfo)) + output.ShallowCopy(input0.VTKObject) + output.CellData.append(new_array, "Reservoir_with_DZ") + + return 1 + + def process_cells(self, input0): + """This function aims to traverse the model and identify the cells that are part of the damage zone, and in this process, form new regions in the domain's property map that represent the damage zone of the geological fault.""" + num_cells = input0.GetNumberOfCells() + cell_array = input0.CellData[self.CellDataLabels[0]] + vtk_dataset = input0.VTKObject + + new_array = np.zeros(num_cells) + + for i in range(num_cells): + original_value = cell_array[i] + new_value = original_value + + if self._active_region == new_value: + center = self.get_cell_centroid(vtk_dataset, i) + flag = self.check_is_dmz(center) + + if flag: + new_value += 10 + + new_array[i] = new_value + + return new_array + + def get_cell_centroid(self, vtk_dataset, cell_id): + cell = vtk_dataset.GetCell(cell_id) + num_points = cell.GetNumberOfPoints() + + coords = np.zeros(3) + for j in range(num_points): + point = cell.GetPoints().GetPoint(j) + coords += np.array(point) + + centroid = coords / num_points if num_points > 0 else np.array([0.0, 0.0, 0.0]) + # print(f" Centroid of Cell {cell_id}: ({centroid[0]:.3f}, {centroid[1]:.3f}, {centroid[2]:.3f})") + return centroid + + def check_is_dmz(self, cell_center): + """ Returns True if the distance from the point to the defined plane is greater than the threshold. """ + if not hasattr(self, "_plane_point") or not hasattr(self, "_plane_normal"): + print("Plane has not been defined yet.") + return False + + vec = cell_center - self._plane_point + distance = abs(np.dot(vec, self._plane_normal)) + return distance < self._DMZ_len + + def SetActReg(self, value): + self._active_region = int( value ) + self.Modified() + + def SetDmzLen(self, value): + self._DMZ_len = float(value) + print(f"DMZ length set to: {self._DMZ_len}") + self.Modified() + + # ----------------------------------------------------------------- + # TEMPORARY IMPLEMENTATION ---- IN FUTURE THIS IS NOT GONNA BE USED + # ----------------------------------------------------------------- + def SetPlanePoint(self, x, y, z): + """Defines a point that lies on the plane (x, y, z)""" + self._plane_point = np.array([x,y,z]) + print(f"Point defined at: {self._plane_point}") + self.Modified() + + def SetPlaneNormal(self, x, y, z): + """Defines the normal vector of the plane (nx, ny, nz)""" + self._plane_normal = np.array([x,y,z]) + print(f"Plane normal defined as: {self._plane_normal}") + self.Modified() From d30d6a230c283a86aa30150cae0ed3ac9e81b206 Mon Sep 17 00:00:00 2001 From: alexbenedicto Date: Tue, 19 Aug 2025 11:06:06 -0700 Subject: [PATCH 03/13] Update DMZFinder to use vtk functionalities --- geos-mesh/src/geos/mesh/fault/DMZFinder.py | 207 ++++++++++++--------- 1 file changed, 115 insertions(+), 92 deletions(-) diff --git a/geos-mesh/src/geos/mesh/fault/DMZFinder.py b/geos-mesh/src/geos/mesh/fault/DMZFinder.py index ad1e8844..585840f7 100644 --- a/geos-mesh/src/geos/mesh/fault/DMZFinder.py +++ b/geos-mesh/src/geos/mesh/fault/DMZFinder.py @@ -1,105 +1,128 @@ import numpy as np -from vtkmodules.vtkCommonDataModel import vtkDataSet -from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase +from vtkmodules.vtkCommonDataModel import vtkDataSet, vtkUnstructuredGrid +from vtkmodules.vtkFiltersCore import vtkCellCenters from vtkmodules.numpy_interface import dataset_adapter as dsa - -__doc__ = """ -""" +from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase class DMZFinder( VTKPythonAlgorithmBase ): - def __init__( self ): - VTKPythonAlgorithmBase.__init__( - self, nInputPorts=1, nOutputPorts=1, outputType='vtkUnstructuredGrid') - # Default values. - self._DMZ_len = 50.0 - self._active_region = 80 - self._plane_point = np.array([0.0, 0.0, 0.0]) - self._plane_normal = np.array([0.0, 0.0, 1.0]) - - def RequestData(self, request, inInfo, outInfo): - # Input data - input0 = dsa.WrapDataObject(vtkDataSet.GetData(inInfo[0])) - self.CellDataLabels = input0.CellData.keys() - - print(f"Total number of cells: {input0.GetNumberOfCells()}") - - # call the main routine to identify the damage zone. - new_array = self.process_cells(input0) - - # Output - output = dsa.WrapDataObject(vtkDataSet.GetData(outInfo)) - output.ShallowCopy(input0.VTKObject) - output.CellData.append(new_array, "Reservoir_with_DZ") - - return 1 - - def process_cells(self, input0): - """This function aims to traverse the model and identify the cells that are part of the damage zone, and in this process, form new regions in the domain's property map that represent the damage zone of the geological fault.""" - num_cells = input0.GetNumberOfCells() - cell_array = input0.CellData[self.CellDataLabels[0]] - vtk_dataset = input0.VTKObject - - new_array = np.zeros(num_cells) - for i in range(num_cells): - original_value = cell_array[i] - new_value = original_value + def __init__( self ) -> None: + super().__init__( nInputPorts=1, nOutputPorts=1, outputType='vtkUnstructuredGrid' ) + self._region_array_name: str = "attribute" # Default name for region in GEOS + self._fault_region_id: int = 0 # Default id for fault region attribute in GEOS + self._output_array_name: str = "isDMZ" # New CellArray to create with value 0 if cell not in DMZ, 1 if in DMZ + self._dmz_len: float = 50.0 + self._plane_point: list[ float ] = [ 0.0, 0.0, 0.0 ] + self._plane_normal: list[ float ] = [ 0.0, 0.0, 1.0 ] + + def RequestData( self, request, inInfo, outInfo ) -> None: + input_vtk_grid = vtkUnstructuredGrid.GetData( inInfo[ 0 ] ) + input_grid = dsa.WrapDataObject( input_vtk_grid ) + output_grid = dsa.WrapDataObject( vtkDataSet.GetData( outInfo ) ) + + if not self._check_inputs( input_grid ): + return 0 + + # Get the array that defines the geological regions + region_array = input_grid.CellData[ self._region_array_name ] + + # Use vtkCellCenters to get centroids + cell_centers_filter = vtkCellCenters() + cell_centers_filter.SetInputData( input_vtk_grid ) + # We don't want it to generate cell data, just the points + # The output is a vtkPolyData where each point is a centroid. + # We can grab these points as a NumPy array. + cell_centers_filter.VertexCellsOn() + cell_centers_filter.Update() + centers_polydata = dsa.WrapDataObject( cell_centers_filter.GetOutput() ) + all_centroids = centers_polydata.Points + + # Identify which cells are in the DMZ + dmz_mask = self._find_dmz_cells_vectorized( all_centroids ) + active_region_mask = ( region_array == self._fault_region_id ) + final_mask = np.logical_and( dmz_mask, active_region_mask ) + + # Create the new CellData array + new_region_id_array = np.zeros( region_array.shape, dtype=int ) + new_region_id_array[ final_mask ] = 1 + + # Add new isDMZ array to the output_grid + output_grid.ShallowCopy( input_vtk_grid ) + output_grid.CellData.append( new_region_id_array, self._output_array_name ) - if self._active_region == new_value: - center = self.get_cell_centroid(vtk_dataset, i) - flag = self.check_is_dmz(center) - - if flag: - new_value += 10 - - new_array[i] = new_value - - return new_array + return 1 - def get_cell_centroid(self, vtk_dataset, cell_id): - cell = vtk_dataset.GetCell(cell_id) - num_points = cell.GetNumberOfPoints() + def _check_inputs( self, dsa_mesh: dsa.UnstructuredGrid ) -> bool: + for attr in self.__dict__.keys(): + if self.__dict__[ attr ] is None: + print( f"Error: {attr} is not set." ) + return False - coords = np.zeros(3) - for j in range(num_points): - point = cell.GetPoints().GetPoint(j) - coords += np.array(point) + if self._output_array_name == self._region_array_name: + print( "Error: Output array name cannot be the same as region array name." ) + return False - centroid = coords / num_points if num_points > 0 else np.array([0.0, 0.0, 0.0]) - # print(f" Centroid of Cell {cell_id}: ({centroid[0]:.3f}, {centroid[1]:.3f}, {centroid[2]:.3f})") - return centroid + if dsa_mesh is None: + print( "Error: Input mesh is not set." ) + return False - def check_is_dmz(self, cell_center): - """ Returns True if the distance from the point to the defined plane is greater than the threshold. """ - if not hasattr(self, "_plane_point") or not hasattr(self, "_plane_normal"): - print("Plane has not been defined yet.") + region_array = dsa_mesh.CellData[ self._region_array_name ] + if region_array is None: + print( f"Error: Region array '{self._region_array_name}' is not found in input mesh." ) + return False + else: + if self._fault_region_id not in region_array: + print( f"Error: Fault region ID '{self._fault_region_id}' is not found in region array." ) + return False + + bounds = dsa_mesh.GetBounds() + isInBounds = ( bounds[ 0 ] <= self._plane_point[ 0 ] <= bounds[ 1 ] + and bounds[ 2 ] <= self._plane_point[ 1 ] <= bounds[ 3 ] + and bounds[ 4 ] <= self._plane_point[ 2 ] <= bounds[ 5 ] ) + if not isInBounds: + print( f"Error: Plane point {self._plane_point} is out of bounds of the mesh {bounds}." ) return False - vec = cell_center - self._plane_point - distance = abs(np.dot(vec, self._plane_normal)) - return distance < self._DMZ_len - - def SetActReg(self, value): - self._active_region = int( value ) - self.Modified() - - def SetDmzLen(self, value): - self._DMZ_len = float(value) - print(f"DMZ length set to: {self._DMZ_len}") - self.Modified() - - # ----------------------------------------------------------------- - # TEMPORARY IMPLEMENTATION ---- IN FUTURE THIS IS NOT GONNA BE USED - # ----------------------------------------------------------------- - def SetPlanePoint(self, x, y, z): - """Defines a point that lies on the plane (x, y, z)""" - self._plane_point = np.array([x,y,z]) - print(f"Point defined at: {self._plane_point}") - self.Modified() - - def SetPlaneNormal(self, x, y, z): - """Defines the normal vector of the plane (nx, ny, nz)""" - self._plane_normal = np.array([x,y,z]) - print(f"Plane normal defined as: {self._plane_normal}") - self.Modified() + return True + + def _find_dmz_cells_vectorized( self, cell_centers: np.ndarray ) -> np.ndarray: + """ + Determines which cells are within the DMZ using vectorized operations. + """ + vectors_to_centers = cell_centers - self._plane_point + distances = np.abs( np.dot( vectors_to_centers, self._plane_normal ) ) + return distances < self._dmz_len + + def SetFaultRegionID( self, value: int ) -> None: + if self._fault_region_id != value: + self._fault_region_id = value + self.Modified() + + def SetDmzLength( self, value: float ) -> None: + if self._dmz_len != value: + self._dmz_len = value + self.Modified() + + def SetOutputArrayName( self, name: str ) -> None: + if self._output_array_name != name: + self._output_array_name = name + self.Modified() + + def SetPlanePoint( self, x: float, y: float, z: float ) -> None: + new_point = np.array( [ x, y, z ] ) + if not np.array_equal( self._plane_point, new_point ): + self._plane_point = new_point + self.Modified() + + def SetPlaneNormal( self, x: float, y: float, z: float ) -> None: + norm = np.linalg.norm( [ x, y, z ] ) + if norm == 0: + print( "Warning: Plane normal cannot be a zero vector. Using [0,0,1]." ) + new_normal = np.array( [ 0.0, 0.0, 1.0 ] ) + else: + new_normal = np.array( [ x, y, z ] ) / norm + + if not np.array_equal( self._plane_normal, new_normal ): + self._plane_normal = new_normal + self.Modified() From 7f13f5ba2c476891ad28b15e8de7d2a5220102ea Mon Sep 17 00:00:00 2001 From: alexbenedicto Date: Tue, 19 Aug 2025 11:26:47 -0700 Subject: [PATCH 04/13] Update PVDMZFinder to inherit from DMZFinder --- geos-mesh/src/geos/mesh/fault/DMZFinder.py | 5 +- geos-pv/src/geos/pv/plugins/PVDMZFinder.py | 149 +++++++-------------- 2 files changed, 53 insertions(+), 101 deletions(-) diff --git a/geos-mesh/src/geos/mesh/fault/DMZFinder.py b/geos-mesh/src/geos/mesh/fault/DMZFinder.py index 585840f7..6643c2bf 100644 --- a/geos-mesh/src/geos/mesh/fault/DMZFinder.py +++ b/geos-mesh/src/geos/mesh/fault/DMZFinder.py @@ -8,7 +8,10 @@ class DMZFinder( VTKPythonAlgorithmBase ): def __init__( self ) -> None: - super().__init__( nInputPorts=1, nOutputPorts=1, outputType='vtkUnstructuredGrid' ) + super().__init__( nInputPorts=1, + nOutputPorts=1, + inputType='vtkUnstructuredGrid', + outputType='vtkUnstructuredGrid' ) self._region_array_name: str = "attribute" # Default name for region in GEOS self._fault_region_id: int = 0 # Default id for fault region attribute in GEOS self._output_array_name: str = "isDMZ" # New CellArray to create with value 0 if cell not in DMZ, 1 if in DMZ diff --git a/geos-pv/src/geos/pv/plugins/PVDMZFinder.py b/geos-pv/src/geos/pv/plugins/PVDMZFinder.py index 4613b07f..87e104e7 100644 --- a/geos-pv/src/geos/pv/plugins/PVDMZFinder.py +++ b/geos-pv/src/geos/pv/plugins/PVDMZFinder.py @@ -1,9 +1,16 @@ import numpy as np -from vtkmodules.vtkCommonDataModel import vtkDataSet -from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase -from vtkmodules.numpy_interface import dataset_adapter as dsa +import sys +from pathlib import Path from paraview.util.vtkAlgorithm import smproxy, smproperty, smdomain +# 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.fault.DMZFinder import DMZFinder __doc__ = """ PVDMZFinder is a Paraview plugin that. @@ -14,112 +21,54 @@ To use it: -* Load the module in Paraview: Tools>Manage Plugins...>Load new>geos_dz.py -* Select the Geos output .pvd file loaded in Paraview. +* Load the module in Paraview: Tools>Manage Plugins...>Load new>PVDMZFinder.py +* Select the .vtu grid loaded in Paraview. """ -@smproxy.filter(label="DZ Finder") -@smproperty.input(name="Input") -class PVDMZFinder( VTKPythonAlgorithmBase ): - - def __init__( self ): - VTKPythonAlgorithmBase.__init__( self, nInputPorts=1, nOutputPorts=1, outputType='vtkUnstructuredGrid' ) - # Default values. - self._DMZ_len = 50.0 - self._active_region = 80 - self._plane_point = np.array( [ 0.0, 0.0, 0.0 ] ) - self._plane_normal = np.array( [ 0.0, 0.0, 1.0 ] ) - - def RequestData( self, request, inInfo, outInfo ): - # Input data - input0 = dsa.WrapDataObject( vtkDataSet.GetData( inInfo[ 0 ] ) ) - self.CellDataLabels = input0.CellData.keys() - - print( f"Total number of cells: {input0.GetNumberOfCells()}" ) - - # call the main routine to identify the damage zone. - new_array = self.process_cells( input0 ) - - # Output - output = dsa.WrapDataObject( vtkDataSet.GetData( outInfo ) ) - output.ShallowCopy( input0.VTKObject ) - output.CellData.append( new_array, "Reservoir_with_DZ" ) - - return 1 - - def process_cells( self, input0 ): - """This function aims to traverse the model and identify the cells that are part of the damage zone, and in this process, form new regions in the domain's property map that represent the damage zone of the geological fault.""" - num_cells = input0.GetNumberOfCells() - cell_array = input0.CellData[ self.CellDataLabels[ 0 ] ] - vtk_dataset = input0.VTKObject - - new_array = np.zeros( num_cells ) +@smproxy.filter( name="PVDMZFinder", label="Damage Zone Finder" ) +@smproperty.input( name="Input", port_index=0 ) +@smdomain.datatype( dataTypes=[ "vtkUnstructuredGrid" ], composite_data_supported=True ) +class PVDMZFinder( DMZFinder ): - for i in range( num_cells ): - original_value = cell_array[ i ] - new_value = original_value + def __init__( self ) -> None: + super().__init__() - if self._active_region == new_value: - center = self.get_cell_centroid( vtk_dataset, i ) - flag = self.check_is_dmz( center ) + @smproperty.intvector( name="Region ID", default_values=0, number_of_elements=1 ) + def SetFaultRegionID( self, value: int ) -> None: + if self._fault_region_id != value: + self._fault_region_id = value + self.Modified() - if flag: - new_value += 10 - - new_array[ i ] = new_value - - return new_array - - def get_cell_centroid( self, vtk_dataset, cell_id ): - cell = vtk_dataset.GetCell( cell_id ) - num_points = cell.GetNumberOfPoints() - - coords = np.zeros( 3 ) - for j in range( num_points ): - point = cell.GetPoints().GetPoint( j ) - coords += np.array( point ) - - centroid = coords / num_points if num_points > 0 else np.array( [ 0.0, 0.0, 0.0 ] ) - # print(f" Centroid of Cell {cell_id}: ({centroid[0]:.3f}, {centroid[1]:.3f}, {centroid[2]:.3f})") - return centroid - - def check_is_dmz( self, cell_center ): - """ Returns True if the distance from the point to the defined plane is greater than the threshold. """ - if not hasattr( self, "_plane_point" ) or not hasattr( self, "_plane_normal" ): - print( "Plane has not been defined yet." ) - return False - - vec = cell_center - self._plane_point - distance = abs( np.dot( vec, self._plane_normal ) ) - return distance < self._DMZ_len + @smproperty.doublevector( name="DZ Length", default_values=100.0, number_of_elements=1 ) + def SetDmzLength( self, value: float ) -> None: + if self._dmz_len != value: + self._dmz_len = value + self.Modified() - @smproperty.intvector( name="Active Region", default_values=80, number_of_elements=1 ) - def SetActReg( self, value ): - self._active_region = int( value ) - print( f"The active region flag is: {self._active_region}" ) - self.Modified() + @smproperty.string( name="Output Array Name", default_values="DMZ", number_of_elements=1 ) + def SetOutputArrayName( self, name: str ) -> None: + if self._output_array_name != name: + self._output_array_name = name + self.Modified() - @smproperty.doublevector( name="DZ Length", default_values=100.0, number_of_elements=1 ) - def SetDmzLen( self, value ): - self._DMZ_len = float( value ) - print( f"DMZ length set to: {self._DMZ_len}" ) - self.Modified() - - # ----------------------------------------------------------------- - # TEMPORARY IMPLEMENTATION ---- IN FUTURE THIS IS NOT GONNA BE USED - # ----------------------------------------------------------------- @smproperty.doublevector( name="PlanePoint", default_values=[ 0.0, 0.0, 0.0 ], number_of_elements=3 ) - def SetPlanePoint( self, x, y, z ): - """Defines a point that lies on the plane (x, y, z)""" - self._plane_point = np.array( [ x, y, z ] ) - print( f"Point defined at: {self._plane_point}" ) - self.Modified() + def SetPlanePoint( self, x: float, y: float, z: float ) -> None: + new_point = np.array( [ x, y, z ] ) + if not np.array_equal( self._plane_point, new_point ): + self._plane_point = new_point + self.Modified() @smproperty.doublevector( name="PlaneNormal", default_values=[ 0.0, 0.0, 1.0 ], number_of_elements=3 ) - def SetPlaneNormal( self, x, y, z ): - """Defines the normal vector of the plane (nx, ny, nz)""" - self._plane_normal = np.array( [ x, y, z ] ) - print( f"Plane normal defined as: {self._plane_normal}" ) - self.Modified() + def SetPlaneNormal( self, x: float, y: float, z: float ) -> None: + norm = np.linalg.norm( [ x, y, z ] ) + if norm == 0: + print( "Warning: Plane normal cannot be a zero vector. Using [0,0,1]." ) + new_normal = np.array( [ 0.0, 0.0, 1.0 ] ) + else: + new_normal = np.array( [ x, y, z ] ) / norm + + if not np.array_equal( self._plane_normal, new_normal ): + self._plane_normal = new_normal + self.Modified() From 3fffbdd1c63edad90f3c8f7d4f4479ac45f12d01 Mon Sep 17 00:00:00 2001 From: alexbenedicto Date: Tue, 19 Aug 2025 11:33:39 -0700 Subject: [PATCH 05/13] Add region array name setter --- geos-mesh/src/geos/mesh/fault/DMZFinder.py | 5 +++++ geos-pv/src/geos/pv/plugins/PVDMZFinder.py | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/geos-mesh/src/geos/mesh/fault/DMZFinder.py b/geos-mesh/src/geos/mesh/fault/DMZFinder.py index 6643c2bf..7e83ee69 100644 --- a/geos-mesh/src/geos/mesh/fault/DMZFinder.py +++ b/geos-mesh/src/geos/mesh/fault/DMZFinder.py @@ -102,6 +102,11 @@ def SetFaultRegionID( self, value: int ) -> None: self._fault_region_id = value self.Modified() + def SetRegionArrayName( self, name: str ) -> None: + if self._region_array_name != name: + self._region_array_name = name + self.Modified() + def SetDmzLength( self, value: float ) -> None: if self._dmz_len != value: self._dmz_len = value diff --git a/geos-pv/src/geos/pv/plugins/PVDMZFinder.py b/geos-pv/src/geos/pv/plugins/PVDMZFinder.py index 87e104e7..364b7cf5 100644 --- a/geos-pv/src/geos/pv/plugins/PVDMZFinder.py +++ b/geos-pv/src/geos/pv/plugins/PVDMZFinder.py @@ -41,6 +41,12 @@ def SetFaultRegionID( self, value: int ) -> None: self._fault_region_id = value self.Modified() + @smproperty.string( name="Region Array Name", default_values="attribute", number_of_elements=1 ) + def SetRegionArrayName( self, name: str ) -> None: + if self._region_array_name != name: + self._region_array_name = name + self.Modified() + @smproperty.doublevector( name="DZ Length", default_values=100.0, number_of_elements=1 ) def SetDmzLength( self, value: float ) -> None: if self._dmz_len != value: From f1247a0c6f951bd9ff068864e1ec8cb10dbacd93 Mon Sep 17 00:00:00 2001 From: alexbenedicto Date: Tue, 19 Aug 2025 11:39:09 -0700 Subject: [PATCH 06/13] Fix string widget decorator --- geos-pv/src/geos/pv/plugins/PVDMZFinder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/geos-pv/src/geos/pv/plugins/PVDMZFinder.py b/geos-pv/src/geos/pv/plugins/PVDMZFinder.py index 364b7cf5..02e70964 100644 --- a/geos-pv/src/geos/pv/plugins/PVDMZFinder.py +++ b/geos-pv/src/geos/pv/plugins/PVDMZFinder.py @@ -41,7 +41,7 @@ def SetFaultRegionID( self, value: int ) -> None: self._fault_region_id = value self.Modified() - @smproperty.string( name="Region Array Name", default_values="attribute", number_of_elements=1 ) + @smproperty.stringvector( name="Region Array Name", default_values="attribute", number_of_elements=1 ) def SetRegionArrayName( self, name: str ) -> None: if self._region_array_name != name: self._region_array_name = name @@ -53,7 +53,7 @@ def SetDmzLength( self, value: float ) -> None: self._dmz_len = value self.Modified() - @smproperty.string( name="Output Array Name", default_values="DMZ", number_of_elements=1 ) + @smproperty.stringvector( name="Output Array Name", default_values="DMZ", number_of_elements=1 ) def SetOutputArrayName( self, name: str ) -> None: if self._output_array_name != name: self._output_array_name = name From a0b17071e2850f51b193a936d3a7b20f9f866da7 Mon Sep 17 00:00:00 2001 From: alexbenedicto Date: Tue, 19 Aug 2025 14:10:10 -0700 Subject: [PATCH 07/13] Change from fault to active name --- geos-mesh/src/geos/mesh/fault/DMZFinder.py | 14 +++++++------- geos-pv/src/geos/pv/plugins/PVDMZFinder.py | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/geos-mesh/src/geos/mesh/fault/DMZFinder.py b/geos-mesh/src/geos/mesh/fault/DMZFinder.py index 7e83ee69..e4532d53 100644 --- a/geos-mesh/src/geos/mesh/fault/DMZFinder.py +++ b/geos-mesh/src/geos/mesh/fault/DMZFinder.py @@ -13,7 +13,7 @@ def __init__( self ) -> None: inputType='vtkUnstructuredGrid', outputType='vtkUnstructuredGrid' ) self._region_array_name: str = "attribute" # Default name for region in GEOS - self._fault_region_id: int = 0 # Default id for fault region attribute in GEOS + self._active_region_id: int = 0 # Default id for active region attribute in GEOS self._output_array_name: str = "isDMZ" # New CellArray to create with value 0 if cell not in DMZ, 1 if in DMZ self._dmz_len: float = 50.0 self._plane_point: list[ float ] = [ 0.0, 0.0, 0.0 ] @@ -43,7 +43,7 @@ def RequestData( self, request, inInfo, outInfo ) -> None: # Identify which cells are in the DMZ dmz_mask = self._find_dmz_cells_vectorized( all_centroids ) - active_region_mask = ( region_array == self._fault_region_id ) + active_region_mask = ( region_array == self._active_region_id ) final_mask = np.logical_and( dmz_mask, active_region_mask ) # Create the new CellData array @@ -75,8 +75,8 @@ def _check_inputs( self, dsa_mesh: dsa.UnstructuredGrid ) -> bool: print( f"Error: Region array '{self._region_array_name}' is not found in input mesh." ) return False else: - if self._fault_region_id not in region_array: - print( f"Error: Fault region ID '{self._fault_region_id}' is not found in region array." ) + if self._active_region_id not in region_array: + print( f"Error: Active region ID '{self._active_region_id}' is not found in region array." ) return False bounds = dsa_mesh.GetBounds() @@ -97,9 +97,9 @@ def _find_dmz_cells_vectorized( self, cell_centers: np.ndarray ) -> np.ndarray: distances = np.abs( np.dot( vectors_to_centers, self._plane_normal ) ) return distances < self._dmz_len - def SetFaultRegionID( self, value: int ) -> None: - if self._fault_region_id != value: - self._fault_region_id = value + def SetActiveRegionID( self, value: int ) -> None: + if self._active_region_id != value: + self._active_region_id = value self.Modified() def SetRegionArrayName( self, name: str ) -> None: diff --git a/geos-pv/src/geos/pv/plugins/PVDMZFinder.py b/geos-pv/src/geos/pv/plugins/PVDMZFinder.py index 02e70964..f7e45e8c 100644 --- a/geos-pv/src/geos/pv/plugins/PVDMZFinder.py +++ b/geos-pv/src/geos/pv/plugins/PVDMZFinder.py @@ -36,9 +36,9 @@ def __init__( self ) -> None: super().__init__() @smproperty.intvector( name="Region ID", default_values=0, number_of_elements=1 ) - def SetFaultRegionID( self, value: int ) -> None: - if self._fault_region_id != value: - self._fault_region_id = value + def SetActiveRegionID( self, value: int ) -> None: + if self._active_region_id != value: + self._active_region_id = value self.Modified() @smproperty.stringvector( name="Region Array Name", default_values="attribute", number_of_elements=1 ) From 607a73b9a17c91007abbd8ff06c85135743c57cd Mon Sep 17 00:00:00 2001 From: alexbenedicto Date: Thu, 21 Aug 2025 15:29:47 -0700 Subject: [PATCH 08/13] Improve dmz cells search by using vtkKdTreePointLocator --- geos-mesh/src/geos/mesh/fault/DMZFinder.py | 34 ++++----- .../src/geos/mesh/utils/genericHelpers.py | 73 ++++++++++++++++++- 2 files changed, 83 insertions(+), 24 deletions(-) diff --git a/geos-mesh/src/geos/mesh/fault/DMZFinder.py b/geos-mesh/src/geos/mesh/fault/DMZFinder.py index e4532d53..7703aab6 100644 --- a/geos-mesh/src/geos/mesh/fault/DMZFinder.py +++ b/geos-mesh/src/geos/mesh/fault/DMZFinder.py @@ -1,8 +1,8 @@ import numpy as np from vtkmodules.vtkCommonDataModel import vtkDataSet, vtkUnstructuredGrid -from vtkmodules.vtkFiltersCore import vtkCellCenters from vtkmodules.numpy_interface import dataset_adapter as dsa from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase +from geos.mesh.utils.genericHelpers import find_2D_cell_ids, find_cells_near_faces class DMZFinder( VTKPythonAlgorithmBase ): @@ -30,20 +30,20 @@ def RequestData( self, request, inInfo, outInfo ) -> None: # Get the array that defines the geological regions region_array = input_grid.CellData[ self._region_array_name ] - # Use vtkCellCenters to get centroids - cell_centers_filter = vtkCellCenters() - cell_centers_filter.SetInputData( input_vtk_grid ) - # We don't want it to generate cell data, just the points - # The output is a vtkPolyData where each point is a centroid. - # We can grab these points as a NumPy array. - cell_centers_filter.VertexCellsOn() - cell_centers_filter.Update() - centers_polydata = dsa.WrapDataObject( cell_centers_filter.GetOutput() ) - all_centroids = centers_polydata.Points + all_mesh_face_ids: set[ int ] = find_2D_cell_ids( input_grid ) + if not all_mesh_face_ids: + print( "No 2D face cells found." ) + return 0 - # Identify which cells are in the DMZ - dmz_mask = self._find_dmz_cells_vectorized( all_centroids ) + number_cells: int = input_vtk_grid.GetNumberOfCells() + # identify which faces are in the active region + all_faces_mask = np.zeros( number_cells, dtype=bool )[ all_mesh_face_ids ] active_region_mask = ( region_array == self._active_region_id ) + active_region_faces_mask = np.logical_and( all_faces_mask, active_region_mask ) + active_region_faces: set[ int ] = set( np.where( active_region_faces_mask )[ 0 ] ) + cells_near_faces: set[ int ] = find_cells_near_faces( input_vtk_grid, active_region_faces, self._dmz_len ) + # Identify which cells are in the DMZ + dmz_mask = np.zeros( number_cells.GetNumberOfCells(), dtype=bool )[ cells_near_faces ] final_mask = np.logical_and( dmz_mask, active_region_mask ) # Create the new CellData array @@ -89,14 +89,6 @@ def _check_inputs( self, dsa_mesh: dsa.UnstructuredGrid ) -> bool: return True - def _find_dmz_cells_vectorized( self, cell_centers: np.ndarray ) -> np.ndarray: - """ - Determines which cells are within the DMZ using vectorized operations. - """ - vectors_to_centers = cell_centers - self._plane_point - distances = np.abs( np.dot( vectors_to_centers, self._plane_normal ) ) - return distances < self._dmz_len - def SetActiveRegionID( self, value: int ) -> None: if self._active_region_id != value: self._active_region_id = value diff --git a/geos-mesh/src/geos/mesh/utils/genericHelpers.py b/geos-mesh/src/geos/mesh/utils/genericHelpers.py index de0624fd..51f4f23b 100644 --- a/geos-mesh/src/geos/mesh/utils/genericHelpers.py +++ b/geos-mesh/src/geos/mesh/utils/genericHelpers.py @@ -4,10 +4,12 @@ import numpy as np import numpy.typing as npt from typing import Iterator, List, Sequence, Any, Union -from vtkmodules.util.numpy_support import numpy_to_vtk +from vtkmodules.util.numpy_support import numpy_to_vtk, vtk_to_numpy from vtkmodules.vtkCommonCore import vtkIdList, vtkPoints, reference -from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid, vtkMultiBlockDataSet, vtkPolyData, vtkDataSet, vtkDataObject, vtkPlane, vtkCellTypes, vtkIncrementalOctreePointLocator -from vtkmodules.vtkFiltersCore import vtk3DLinearGridPlaneCutter +from vtkmodules.vtkCommonDataModel import ( vtkUnstructuredGrid, vtkMultiBlockDataSet, vtkPolyData, vtkDataSet, + vtkDataObject, vtkPlane, vtkCellTypes, vtkIncrementalOctreePointLocator, + vtkKdTreePointLocator, VTK_QUAD, VTK_TRIANGLE ) +from vtkmodules.vtkFiltersCore import vtk3DLinearGridPlaneCutter, vtkCellCenters from geos.mesh.utils.multiblockHelpers import ( getBlockElementIndexesFlatten, getBlockFromFlatIndex ) __doc__ = """ @@ -53,6 +55,71 @@ def vtk_iter( vtkContainer: vtkIdList | vtkCellTypes ) -> Iterator[ Any ]: yield vtkContainer.GetCellType( i ) +def find_2D_cell_ids( mesh: vtkUnstructuredGrid ) -> set[ int ]: + """ + Find 2D cell IDs in a 3D unstructured grid. + + Args: + mesh (vtkUnstructuredGrid): The input 3D mesh. + + Returns: + set[int]: A set of 2D cell IDs. + """ + cell_types_vtk_array = mesh.GetCellTypesArray() + if not cell_types_vtk_array: + return set() + + cell_types_numpy_array = vtk_to_numpy( cell_types_vtk_array ) + triangle_ids = np.where( cell_types_numpy_array == VTK_TRIANGLE )[ 0 ] + quad_ids = np.where( cell_types_numpy_array == VTK_QUAD )[ 0 ] + return set( np.concatenate( ( triangle_ids, quad_ids ) ) ) + + +def find_cells_near_faces( mesh: vtkUnstructuredGrid, face_ids: set[ int ], distance: float ) -> set[ int ]: + """ + Given a vtkUnstructuredGrid, a list of face cell IDs, and a distance, find unique cell IDs + whose centroids are within the distance to at least one face centroid. + + Args: + mesh (vtkUnstructuredGrid) + face_ids (set[int]): cell IDs of the faces + distance (float): search radius + + Returns: + set(int): unique cell IDs + """ + # Compute cell centers for all cells + cell_centers_filter = vtkCellCenters() + cell_centers_filter.SetInputData( mesh ) + cell_centers_filter.Update() + centers_polydata: vtkPolyData = cell_centers_filter.GetOutput() + + # Build kd-tree point locator on the cell centers + locator = vtkKdTreePointLocator() + locator.SetDataSet( centers_polydata ) + locator.BuildLocator() + + # Use a set to collect unique cell IDs + nearby_cells = set() + + # For each face, get its centroid and find nearby points (cell IDs) + points: vtkPoints = centers_polydata.GetPoints() + for face_id in face_ids: + # Get face centroid + face_centroid = points.GetPoint( face_id ) + + # Find points within radius + result = vtkIdList() + locator.FindPointsWithinRadius( distance, face_centroid, result ) + + # Add to set + for j in range( result.GetNumberOfIds() ): + cell_id = result.GetId( j ) + nearby_cells.add( cell_id ) + + return nearby_cells + + def extractSurfaceFromElevation( mesh: vtkUnstructuredGrid, elevation: float ) -> vtkPolyData: """Extract surface at a constant elevation from a mesh. From a2105579d5d6c9c8ab1ae751c345d5fe0f755dca Mon Sep 17 00:00:00 2001 From: alexbenedicto Date: Thu, 21 Aug 2025 15:34:24 -0700 Subject: [PATCH 09/13] Small fix --- geos-mesh/src/geos/mesh/fault/DMZFinder.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/geos-mesh/src/geos/mesh/fault/DMZFinder.py b/geos-mesh/src/geos/mesh/fault/DMZFinder.py index 7703aab6..5bd9fc16 100644 --- a/geos-mesh/src/geos/mesh/fault/DMZFinder.py +++ b/geos-mesh/src/geos/mesh/fault/DMZFinder.py @@ -37,13 +37,15 @@ def RequestData( self, request, inInfo, outInfo ) -> None: number_cells: int = input_vtk_grid.GetNumberOfCells() # identify which faces are in the active region - all_faces_mask = np.zeros( number_cells, dtype=bool )[ all_mesh_face_ids ] + all_faces_mask = np.zeros( number_cells, dtype=bool ) + all_faces_mask[ all_mesh_face_ids ] = True active_region_mask = ( region_array == self._active_region_id ) active_region_faces_mask = np.logical_and( all_faces_mask, active_region_mask ) active_region_faces: set[ int ] = set( np.where( active_region_faces_mask )[ 0 ] ) cells_near_faces: set[ int ] = find_cells_near_faces( input_vtk_grid, active_region_faces, self._dmz_len ) # Identify which cells are in the DMZ - dmz_mask = np.zeros( number_cells.GetNumberOfCells(), dtype=bool )[ cells_near_faces ] + dmz_mask = np.zeros( number_cells.GetNumberOfCells(), dtype=bool ) + dmz_mask[ cells_near_faces ] = True final_mask = np.logical_and( dmz_mask, active_region_mask ) # Create the new CellData array From e2d1956a074c25cea3061c99ade660ff36d8f776 Mon Sep 17 00:00:00 2001 From: alexbenedicto Date: Thu, 21 Aug 2025 15:42:52 -0700 Subject: [PATCH 10/13] Fix index setting with list for np array --- geos-mesh/src/geos/mesh/fault/DMZFinder.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/geos-mesh/src/geos/mesh/fault/DMZFinder.py b/geos-mesh/src/geos/mesh/fault/DMZFinder.py index 5bd9fc16..b1e4a4ac 100644 --- a/geos-mesh/src/geos/mesh/fault/DMZFinder.py +++ b/geos-mesh/src/geos/mesh/fault/DMZFinder.py @@ -38,14 +38,14 @@ def RequestData( self, request, inInfo, outInfo ) -> None: number_cells: int = input_vtk_grid.GetNumberOfCells() # identify which faces are in the active region all_faces_mask = np.zeros( number_cells, dtype=bool ) - all_faces_mask[ all_mesh_face_ids ] = True + all_faces_mask[ list( all_mesh_face_ids ) ] = True active_region_mask = ( region_array == self._active_region_id ) active_region_faces_mask = np.logical_and( all_faces_mask, active_region_mask ) active_region_faces: set[ int ] = set( np.where( active_region_faces_mask )[ 0 ] ) cells_near_faces: set[ int ] = find_cells_near_faces( input_vtk_grid, active_region_faces, self._dmz_len ) # Identify which cells are in the DMZ - dmz_mask = np.zeros( number_cells.GetNumberOfCells(), dtype=bool ) - dmz_mask[ cells_near_faces ] = True + dmz_mask = np.zeros( number_cells, dtype=bool ) + dmz_mask[ list( cells_near_faces ) ] = True final_mask = np.logical_and( dmz_mask, active_region_mask ) # Create the new CellData array From d916678b4134400f449b2e319cb5d847c60664a1 Mon Sep 17 00:00:00 2001 From: alexbenedicto Date: Thu, 21 Aug 2025 18:09:26 -0700 Subject: [PATCH 11/13] 2nd version that now supports KDTree implementation --- geos-mesh/src/geos/mesh/fault/DMZFinder.py | 52 +++++++--------------- geos-pv/src/geos/pv/plugins/PVDMZFinder.py | 35 +++++---------- 2 files changed, 26 insertions(+), 61 deletions(-) diff --git a/geos-mesh/src/geos/mesh/fault/DMZFinder.py b/geos-mesh/src/geos/mesh/fault/DMZFinder.py index b1e4a4ac..9ee01345 100644 --- a/geos-mesh/src/geos/mesh/fault/DMZFinder.py +++ b/geos-mesh/src/geos/mesh/fault/DMZFinder.py @@ -14,10 +14,9 @@ def __init__( self ) -> None: outputType='vtkUnstructuredGrid' ) self._region_array_name: str = "attribute" # Default name for region in GEOS self._active_region_id: int = 0 # Default id for active region attribute in GEOS + self._active_fault_id: int = 0 # Default id for active fault attribute in GEOS self._output_array_name: str = "isDMZ" # New CellArray to create with value 0 if cell not in DMZ, 1 if in DMZ - self._dmz_len: float = 50.0 - self._plane_point: list[ float ] = [ 0.0, 0.0, 0.0 ] - self._plane_normal: list[ float ] = [ 0.0, 0.0, 1.0 ] + self._dmz_len: float = 100.0 def RequestData( self, request, inInfo, outInfo ) -> None: input_vtk_grid = vtkUnstructuredGrid.GetData( inInfo[ 0 ] ) @@ -30,7 +29,7 @@ def RequestData( self, request, inInfo, outInfo ) -> None: # Get the array that defines the geological regions region_array = input_grid.CellData[ self._region_array_name ] - all_mesh_face_ids: set[ int ] = find_2D_cell_ids( input_grid ) + all_mesh_face_ids: set[ int ] = find_2D_cell_ids( input_vtk_grid ) if not all_mesh_face_ids: print( "No 2D face cells found." ) return 0 @@ -40,9 +39,11 @@ def RequestData( self, request, inInfo, outInfo ) -> None: all_faces_mask = np.zeros( number_cells, dtype=bool ) all_faces_mask[ list( all_mesh_face_ids ) ] = True active_region_mask = ( region_array == self._active_region_id ) - active_region_faces_mask = np.logical_and( all_faces_mask, active_region_mask ) - active_region_faces: set[ int ] = set( np.where( active_region_faces_mask )[ 0 ] ) - cells_near_faces: set[ int ] = find_cells_near_faces( input_vtk_grid, active_region_faces, self._dmz_len ) + active_fault_mask = ( region_array == self._active_fault_id ) + active_fault_faces_mask = np.logical_and( all_faces_mask, active_fault_mask ) + active_fault_faces: set[ int ] = set( np.where( active_fault_faces_mask )[ 0 ] ) + cells_near_faces: set[ int ] = find_cells_near_faces( input_vtk_grid, active_fault_faces, self._dmz_len ) + # Identify which cells are in the DMZ dmz_mask = np.zeros( number_cells, dtype=bool ) dmz_mask[ list( cells_near_faces ) ] = True @@ -81,26 +82,18 @@ def _check_inputs( self, dsa_mesh: dsa.UnstructuredGrid ) -> bool: print( f"Error: Active region ID '{self._active_region_id}' is not found in region array." ) return False - bounds = dsa_mesh.GetBounds() - isInBounds = ( bounds[ 0 ] <= self._plane_point[ 0 ] <= bounds[ 1 ] - and bounds[ 2 ] <= self._plane_point[ 1 ] <= bounds[ 3 ] - and bounds[ 4 ] <= self._plane_point[ 2 ] <= bounds[ 5 ] ) - if not isInBounds: - print( f"Error: Plane point {self._plane_point} is out of bounds of the mesh {bounds}." ) - return False - return True + def SetActiveFaultID( self, value: int ) -> None: + if self._active_fault_id != value: + self._active_fault_id = value + self.Modified() + def SetActiveRegionID( self, value: int ) -> None: if self._active_region_id != value: self._active_region_id = value self.Modified() - def SetRegionArrayName( self, name: str ) -> None: - if self._region_array_name != name: - self._region_array_name = name - self.Modified() - def SetDmzLength( self, value: float ) -> None: if self._dmz_len != value: self._dmz_len = value @@ -111,20 +104,7 @@ def SetOutputArrayName( self, name: str ) -> None: self._output_array_name = name self.Modified() - def SetPlanePoint( self, x: float, y: float, z: float ) -> None: - new_point = np.array( [ x, y, z ] ) - if not np.array_equal( self._plane_point, new_point ): - self._plane_point = new_point - self.Modified() - - def SetPlaneNormal( self, x: float, y: float, z: float ) -> None: - norm = np.linalg.norm( [ x, y, z ] ) - if norm == 0: - print( "Warning: Plane normal cannot be a zero vector. Using [0,0,1]." ) - new_normal = np.array( [ 0.0, 0.0, 1.0 ] ) - else: - new_normal = np.array( [ x, y, z ] ) / norm - - if not np.array_equal( self._plane_normal, new_normal ): - self._plane_normal = new_normal + def SetRegionArrayName( self, name: str ) -> None: + if self._region_array_name != name: + self._region_array_name = name self.Modified() diff --git a/geos-pv/src/geos/pv/plugins/PVDMZFinder.py b/geos-pv/src/geos/pv/plugins/PVDMZFinder.py index f7e45e8c..4ca6abfb 100644 --- a/geos-pv/src/geos/pv/plugins/PVDMZFinder.py +++ b/geos-pv/src/geos/pv/plugins/PVDMZFinder.py @@ -1,4 +1,3 @@ -import numpy as np import sys from pathlib import Path from paraview.util.vtkAlgorithm import smproxy, smproperty, smdomain @@ -35,18 +34,18 @@ class PVDMZFinder( DMZFinder ): def __init__( self ) -> None: super().__init__() + @smproperty.intvector( name="Fault ID", default_values=0, number_of_elements=1 ) + def SetActiveFaultID( self, value: int ) -> None: + if self._active_fault_id != value: + self._active_fault_id = value + self.Modified() + @smproperty.intvector( name="Region ID", default_values=0, number_of_elements=1 ) def SetActiveRegionID( self, value: int ) -> None: if self._active_region_id != value: self._active_region_id = value self.Modified() - @smproperty.stringvector( name="Region Array Name", default_values="attribute", number_of_elements=1 ) - def SetRegionArrayName( self, name: str ) -> None: - if self._region_array_name != name: - self._region_array_name = name - self.Modified() - @smproperty.doublevector( name="DZ Length", default_values=100.0, number_of_elements=1 ) def SetDmzLength( self, value: float ) -> None: if self._dmz_len != value: @@ -59,22 +58,8 @@ def SetOutputArrayName( self, name: str ) -> None: self._output_array_name = name self.Modified() - @smproperty.doublevector( name="PlanePoint", default_values=[ 0.0, 0.0, 0.0 ], number_of_elements=3 ) - def SetPlanePoint( self, x: float, y: float, z: float ) -> None: - new_point = np.array( [ x, y, z ] ) - if not np.array_equal( self._plane_point, new_point ): - self._plane_point = new_point - self.Modified() - - @smproperty.doublevector( name="PlaneNormal", default_values=[ 0.0, 0.0, 1.0 ], number_of_elements=3 ) - def SetPlaneNormal( self, x: float, y: float, z: float ) -> None: - norm = np.linalg.norm( [ x, y, z ] ) - if norm == 0: - print( "Warning: Plane normal cannot be a zero vector. Using [0,0,1]." ) - new_normal = np.array( [ 0.0, 0.0, 1.0 ] ) - else: - new_normal = np.array( [ x, y, z ] ) / norm - - if not np.array_equal( self._plane_normal, new_normal ): - self._plane_normal = new_normal + @smproperty.stringvector( name="Region Array Name", default_values="attribute", number_of_elements=1 ) + def SetRegionArrayName( self, name: str ) -> None: + if self._region_array_name != name: + self._region_array_name = name self.Modified() From ccc313c8d24adaab3be421c92aaddc0b7cb70f86 Mon Sep 17 00:00:00 2001 From: alexbenedicto Date: Mon, 25 Aug 2025 15:43:56 -0700 Subject: [PATCH 12/13] Update filter to respect repository implementation --- geos-mesh/src/geos/mesh/fault/DMZFinder.py | 216 ++++++++++++++------- geos-pv/src/geos/pv/plugins/PVDMZFinder.py | 163 +++++++++++++--- 2 files changed, 290 insertions(+), 89 deletions(-) diff --git a/geos-mesh/src/geos/mesh/fault/DMZFinder.py b/geos-mesh/src/geos/mesh/fault/DMZFinder.py index 9ee01345..7c696133 100644 --- a/geos-mesh/src/geos/mesh/fault/DMZFinder.py +++ b/geos-mesh/src/geos/mesh/fault/DMZFinder.py @@ -1,48 +1,152 @@ +# ------------------------------------------------------------------------------------------------------------ +# SPDX-License-Identifier: LGPL-2.1-only +# +# Copyright (c) 2016-2024 Lawrence Livermore National Security LLC +# Copyright (c) 2018-2024 TotalEnergies +# Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University +# Copyright (c) 2023-2024 Chevron +# Copyright (c) 2019- GEOS/GEOSX Contributors +# Copyright (c) 2019- INRIA project-team Makutu +# All rights reserved +# +# See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. +# ------------------------------------------------------------------------------------------------------------ import numpy as np -from vtkmodules.vtkCommonDataModel import vtkDataSet, vtkUnstructuredGrid +from typing_extensions import Self +from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid from vtkmodules.numpy_interface import dataset_adapter as dsa -from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase from geos.mesh.utils.genericHelpers import find_2D_cell_ids, find_cells_near_faces +from geos.utils.Logger import logging, Logger, getLogger + +__doc__ = """ +Find Damage/Fractured Zone (DMZ) cells in mesh based on proximity to fault faces. + +Input mesh is vtkUnstructuredGrid and output is the same mesh with added DMZ array. + +The DMZ is identified by finding cells within a specified distance from fault faces +in a given active region. + +To use a handler of yours for the logger, set the variable 'speHandler' to True and add it to the filter +with the member function setLoggerHandler. + +To use it: + +.. code-block:: python + + from geos.mesh.fault.DMZFinder import DMZFinder + + # Filter inputs. + unstructuredGrid: vtkUnstructuredGrid + region_array_name: str = "attribute" + active_region_id: int = 0 + active_fault_id: int = 0 + output_array_name: str = "isDMZ" + dmz_length: float = 100.0 + # Optional inputs. + speHandler: bool + + # Instantiate the filter. + filter: DMZFinder = DMZFinder( unstructuredGrid, region_array_name, active_region_id, + active_fault_id, output_array_name, dmz_length, speHandler ) + + # Set the handler of yours (only if speHandler is True). + yourHandler: logging.Handler + filter.setLoggerHandler( yourHandler ) + + # Do calculations. + filter.applyFilter() +""" + +loggerTitle: str = "DMZ Finder" + + +class DMZFinder: + + def __init__( + self: Self, + unstructuredGrid: vtkUnstructuredGrid, + region_array_name: str = "attribute", + active_region_id: int = 0, + active_fault_id: int = 0, + output_array_name: str = "isDMZ", + dmz_length: float = 100.0, + speHandler: bool = False, + ) -> None: + """Find Damage/Fractured Zone (DMZ) cells in mesh. + + Args: + unstructuredGrid (vtkUnstructuredGrid): The mesh where to find DMZ cells. + region_array_name (str, optional): Name of the region array. Defaults to "attribute". + active_region_id (int, optional): ID of the active region. Defaults to 0. + active_fault_id (int, optional): ID of the active fault. Defaults to 0. + output_array_name (str, optional): Name of the output DMZ array. Defaults to "isDMZ". + dmz_length (float, optional): Distance for DMZ identification. Defaults to 100.0. + speHandler (bool, optional): True to use a specific handler, False to use the internal handler. + Defaults to False. + """ + self.unstructuredGrid: vtkUnstructuredGrid = unstructuredGrid + self.region_array_name: str = region_array_name + self.active_region_id: int = active_region_id + self.active_fault_id: int = active_fault_id + self.output_array_name: str = output_array_name + self.dmz_length: float = dmz_length + + # 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 used, .info, .error, .warning and .critical, + be sure to have at least the same 4 levels. -class DMZFinder( VTKPythonAlgorithmBase ): + 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 a handler, to use yours set the argument 'speHandler' to True " + "during the filter initialization." ) - def __init__( self ) -> None: - super().__init__( nInputPorts=1, - nOutputPorts=1, - inputType='vtkUnstructuredGrid', - outputType='vtkUnstructuredGrid' ) - self._region_array_name: str = "attribute" # Default name for region in GEOS - self._active_region_id: int = 0 # Default id for active region attribute in GEOS - self._active_fault_id: int = 0 # Default id for active fault attribute in GEOS - self._output_array_name: str = "isDMZ" # New CellArray to create with value 0 if cell not in DMZ, 1 if in DMZ - self._dmz_len: float = 100.0 + def applyFilter( self: Self ) -> bool: + """Apply the DMZ finding algorithm to the mesh. - def RequestData( self, request, inInfo, outInfo ) -> None: - input_vtk_grid = vtkUnstructuredGrid.GetData( inInfo[ 0 ] ) - input_grid = dsa.WrapDataObject( input_vtk_grid ) - output_grid = dsa.WrapDataObject( vtkDataSet.GetData( outInfo ) ) + Returns: + bool: True if calculation successfully ended, False otherwise. + """ + self.logger.info( f"Apply filter { self.logger.name }." ) + + input_grid = dsa.WrapDataObject( self.unstructuredGrid ) if not self._check_inputs( input_grid ): - return 0 + self.logger.error( f"The filter { self.logger.name } failed." ) + return False # Get the array that defines the geological regions - region_array = input_grid.CellData[ self._region_array_name ] + region_array = input_grid.CellData[ self.region_array_name ] - all_mesh_face_ids: set[ int ] = find_2D_cell_ids( input_vtk_grid ) + all_mesh_face_ids: set[ int ] = find_2D_cell_ids( self.unstructuredGrid ) if not all_mesh_face_ids: - print( "No 2D face cells found." ) - return 0 + self.logger.error( "No 2D face cells found." ) + self.logger.error( f"The filter { self.logger.name } failed." ) + return False - number_cells: int = input_vtk_grid.GetNumberOfCells() + number_cells: int = self.unstructuredGrid.GetNumberOfCells() # identify which faces are in the active region all_faces_mask = np.zeros( number_cells, dtype=bool ) all_faces_mask[ list( all_mesh_face_ids ) ] = True - active_region_mask = ( region_array == self._active_region_id ) - active_fault_mask = ( region_array == self._active_fault_id ) + active_region_mask = ( region_array == self.active_region_id ) + active_fault_mask = ( region_array == self.active_fault_id ) active_fault_faces_mask = np.logical_and( all_faces_mask, active_fault_mask ) active_fault_faces: set[ int ] = set( np.where( active_fault_faces_mask )[ 0 ] ) - cells_near_faces: set[ int ] = find_cells_near_faces( input_vtk_grid, active_fault_faces, self._dmz_len ) + cells_near_faces: set[ int ] = find_cells_near_faces( self.unstructuredGrid, active_fault_faces, + self.dmz_length ) # Identify which cells are in the DMZ dmz_mask = np.zeros( number_cells, dtype=bool ) @@ -53,58 +157,36 @@ def RequestData( self, request, inInfo, outInfo ) -> None: new_region_id_array = np.zeros( region_array.shape, dtype=int ) new_region_id_array[ final_mask ] = 1 - # Add new isDMZ array to the output_grid - output_grid.ShallowCopy( input_vtk_grid ) - output_grid.CellData.append( new_region_id_array, self._output_array_name ) + # Add new isDMZ array to the input_grid (which modifies the original mesh) + input_grid.CellData.append( new_region_id_array, self.output_array_name ) - return 1 + self.logger.info( f"The filter { self.logger.name } succeed." ) + return True - def _check_inputs( self, dsa_mesh: dsa.UnstructuredGrid ) -> bool: - for attr in self.__dict__.keys(): - if self.__dict__[ attr ] is None: - print( f"Error: {attr} is not set." ) - return False + def _check_inputs( self: Self, dsa_mesh: dsa.UnstructuredGrid ) -> bool: + """Check if inputs are valid. - if self._output_array_name == self._region_array_name: - print( "Error: Output array name cannot be the same as region array name." ) + Args: + dsa_mesh (dsa.UnstructuredGrid): The wrapped mesh to check. + + Returns: + bool: True if inputs are valid, False otherwise. + """ + if self.output_array_name == self.region_array_name: + self.logger.error( "Output array name cannot be the same as region array name." ) return False if dsa_mesh is None: - print( "Error: Input mesh is not set." ) + self.logger.error( "Input mesh is not set." ) return False - region_array = dsa_mesh.CellData[ self._region_array_name ] + region_array = dsa_mesh.CellData[ self.region_array_name ] if region_array is None: - print( f"Error: Region array '{self._region_array_name}' is not found in input mesh." ) + self.logger.error( f"Region array '{self.region_array_name}' is not found in input mesh." ) return False else: - if self._active_region_id not in region_array: - print( f"Error: Active region ID '{self._active_region_id}' is not found in region array." ) + if self.active_region_id not in region_array: + self.logger.error( f"Active region ID '{self.active_region_id}' is not found in region array." ) return False return True - - def SetActiveFaultID( self, value: int ) -> None: - if self._active_fault_id != value: - self._active_fault_id = value - self.Modified() - - def SetActiveRegionID( self, value: int ) -> None: - if self._active_region_id != value: - self._active_region_id = value - self.Modified() - - def SetDmzLength( self, value: float ) -> None: - if self._dmz_len != value: - self._dmz_len = value - self.Modified() - - def SetOutputArrayName( self, name: str ) -> None: - if self._output_array_name != name: - self._output_array_name = name - self.Modified() - - def SetRegionArrayName( self, name: str ) -> None: - if self._region_array_name != name: - self._region_array_name = name - self.Modified() diff --git a/geos-pv/src/geos/pv/plugins/PVDMZFinder.py b/geos-pv/src/geos/pv/plugins/PVDMZFinder.py index 4ca6abfb..60abfc58 100644 --- a/geos-pv/src/geos/pv/plugins/PVDMZFinder.py +++ b/geos-pv/src/geos/pv/plugins/PVDMZFinder.py @@ -1,6 +1,23 @@ +# ------------------------------------------------------------------------------------------------------------ +# SPDX-License-Identifier: LGPL-2.1-only +# +# Copyright (c) 2016-2024 Lawrence Livermore National Security LLC +# Copyright (c) 2018-2024 TotalEnergies +# Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University +# Copyright (c) 2023-2024 Chevron +# Copyright (c) 2019- GEOS/GEOSX Contributors +# Copyright (c) 2019- INRIA project-team Makutu +# All rights reserved +# +# See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. +# ------------------------------------------------------------------------------------------------------------ import sys from pathlib import Path -from paraview.util.vtkAlgorithm import smproxy, smproperty, smdomain +from typing_extensions import Self +from paraview.util.vtkAlgorithm import VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy +from paraview.detail.loghandler import VTKHandler +from vtkmodules.vtkCommonDataModel import vtkUnstructuredGrid +from vtkmodules.vtkCommonCore import vtkInformation, vtkInformationVector # update sys.path to load all GEOS Python Package dependencies geos_pv_path: Path = Path( __file__ ).parent.parent.parent.parent.parent @@ -12,54 +29,156 @@ from geos.mesh.fault.DMZFinder import DMZFinder __doc__ = """ -PVDMZFinder is a Paraview plugin that. +PVDMZFinder is a Paraview plugin that finds Damage/Fractured Zone (DMZ) cells in mesh. Input and output types are vtkUnstructuredGrid. -This filter results in a single output pipeline that contains the volume mesh. +This filter results in a single output pipeline that contains the volume mesh with added DMZ array. To use it: * Load the module in Paraview: Tools>Manage Plugins...>Load new>PVDMZFinder.py * Select the .vtu grid loaded in Paraview. +* Set the parameters (Fault ID, Region ID, DZ Length, etc.) +* Apply. """ @smproxy.filter( name="PVDMZFinder", label="Damage Zone Finder" ) +@smhint.xml( '' ) @smproperty.input( name="Input", port_index=0 ) @smdomain.datatype( dataTypes=[ "vtkUnstructuredGrid" ], composite_data_supported=True ) -class PVDMZFinder( DMZFinder ): +class PVDMZFinder( VTKPythonAlgorithmBase ): - def __init__( self ) -> None: - super().__init__() + def __init__( self: Self ) -> None: + """Find Damage/Fractured Zone (DMZ) cells in mesh.""" + super().__init__( nInputPorts=1, + nOutputPorts=1, + inputType="vtkUnstructuredGrid", + outputType="vtkUnstructuredGrid" ) + + self.region_array_name: str = "attribute" + self.active_region_id: int = 0 + self.active_fault_id: int = 0 + self.output_array_name: str = "isDMZ" + self.dmz_length: float = 100.0 @smproperty.intvector( name="Fault ID", default_values=0, number_of_elements=1 ) - def SetActiveFaultID( self, value: int ) -> None: - if self._active_fault_id != value: - self._active_fault_id = value + def SetActiveFaultID( self: Self, value: int ) -> None: + """Set the active fault ID. + + Args: + value (int): The fault ID to set. + """ + if self.active_fault_id != value: + self.active_fault_id = value self.Modified() @smproperty.intvector( name="Region ID", default_values=0, number_of_elements=1 ) - def SetActiveRegionID( self, value: int ) -> None: - if self._active_region_id != value: - self._active_region_id = value + def SetActiveRegionID( self: Self, value: int ) -> None: + """Set the active region ID. + + Args: + value (int): The region ID to set. + """ + if self.active_region_id != value: + self.active_region_id = value self.Modified() @smproperty.doublevector( name="DZ Length", default_values=100.0, number_of_elements=1 ) - def SetDmzLength( self, value: float ) -> None: - if self._dmz_len != value: - self._dmz_len = value + def SetDmzLength( self: Self, value: float ) -> None: + """Set the DMZ length. + + Args: + value (float): The DMZ length to set. + """ + if self.dmz_length != value: + self.dmz_length = value self.Modified() - @smproperty.stringvector( name="Output Array Name", default_values="DMZ", number_of_elements=1 ) - def SetOutputArrayName( self, name: str ) -> None: - if self._output_array_name != name: - self._output_array_name = name + @smproperty.stringvector( name="Output Array Name", default_values="isDMZ", number_of_elements=1 ) + def SetOutputArrayName( self: Self, name: str ) -> None: + """Set the output array name. + + Args: + name (str): The output array name to set. + """ + if self.output_array_name != name: + self.output_array_name = name self.Modified() @smproperty.stringvector( name="Region Array Name", default_values="attribute", number_of_elements=1 ) - def SetRegionArrayName( self, name: str ) -> None: - if self._region_array_name != name: - self._region_array_name = name + def SetRegionArrayName( self: Self, name: str ) -> None: + """Set the region array name. + + Args: + name (str): The region array name to set. + """ + if self.region_array_name != name: + self.region_array_name = 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. + """ + inputMesh: vtkUnstructuredGrid = self.GetInputData( inInfoVec, 0, 0 ) + outputMesh: vtkUnstructuredGrid = self.GetOutputData( outInfoVec, 0 ) + assert inputMesh is not None, "Input mesh is null." + assert outputMesh is not None, "Output pipeline is null." + + outputMesh.ShallowCopy( inputMesh ) + + filter: DMZFinder = DMZFinder( + outputMesh, + self.region_array_name, + self.active_region_id, + self.active_fault_id, + self.output_array_name, + self.dmz_length, + True, + ) + + if not filter.logger.hasHandlers(): + filter.setLoggerHandler( VTKHandler() ) + + filter.applyFilter() + + return 1 From b3e77111f9007c187dc6cadad1fc910596353b4e Mon Sep 17 00:00:00 2001 From: alexbenedicto Date: Mon, 25 Aug 2025 22:12:43 -0700 Subject: [PATCH 13/13] Corrected copyrights --- geos-mesh/src/geos/mesh/fault/DMZFinder.py | 1 - geos-pv/src/geos/pv/plugins/PVDMZFinder.py | 1 - 2 files changed, 2 deletions(-) diff --git a/geos-mesh/src/geos/mesh/fault/DMZFinder.py b/geos-mesh/src/geos/mesh/fault/DMZFinder.py index 7c696133..08129648 100644 --- a/geos-mesh/src/geos/mesh/fault/DMZFinder.py +++ b/geos-mesh/src/geos/mesh/fault/DMZFinder.py @@ -6,7 +6,6 @@ # Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University # Copyright (c) 2023-2024 Chevron # Copyright (c) 2019- GEOS/GEOSX Contributors -# Copyright (c) 2019- INRIA project-team Makutu # All rights reserved # # See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. diff --git a/geos-pv/src/geos/pv/plugins/PVDMZFinder.py b/geos-pv/src/geos/pv/plugins/PVDMZFinder.py index 60abfc58..e1ff8355 100644 --- a/geos-pv/src/geos/pv/plugins/PVDMZFinder.py +++ b/geos-pv/src/geos/pv/plugins/PVDMZFinder.py @@ -6,7 +6,6 @@ # Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University # Copyright (c) 2023-2024 Chevron # Copyright (c) 2019- GEOS/GEOSX Contributors -# Copyright (c) 2019- INRIA project-team Makutu # All rights reserved # # See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details.