From 5cc26c66745f0bf0ccdeeaaf7c126a52f3fb34ce Mon Sep 17 00:00:00 2001 From: DENEL Bertrand Date: Wed, 6 May 2026 11:11:43 -0500 Subject: [PATCH 1/3] Draft --- .../src/geos/mesh_doctor/actions/euler.py | 664 +++++++++--------- .../geos/mesh_doctor/parsing/eulerParsing.py | 306 ++++---- 2 files changed, 485 insertions(+), 485 deletions(-) diff --git a/mesh-doctor/src/geos/mesh_doctor/actions/euler.py b/mesh-doctor/src/geos/mesh_doctor/actions/euler.py index 18394f70..b72504ab 100644 --- a/mesh-doctor/src/geos/mesh_doctor/actions/euler.py +++ b/mesh-doctor/src/geos/mesh_doctor/actions/euler.py @@ -1,382 +1,414 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. -"""Compute Solid Euler Characteristic for mesh files (3D elements only).""" +"""Compute Euler characteristic for mesh files (3D solid and/or 2D surface).""" -from dataclasses import dataclass +from __future__ import annotations +from collections import Counter +from dataclasses import dataclass, field +from typing import Optional + +import numpy as np import vtk from tqdm import tqdm +from vtk.util.numpy_support import vtk_to_numpy from geos.mesh_doctor.parsing.cliParsing import setupLogger from geos.mesh.io.vtkIO import readUnstructuredGrid - -@dataclass( frozen=True ) -class Options: - """Options for Euler characteristic computation.""" - pass +# --- Options & Result types --------------------------------------------------- @dataclass( frozen=True ) -class Result: - """Result of solid Euler characteristic computation. +class Options: + """Options for Euler characteristic computation. Attributes: - numVertices: Number of vertices (V) in 3D mesh - numEdges: Number of unique edges (E) in 3D mesh - numFaces: Number of unique faces (F) in 3D mesh - numCells: Number of 3D cells (C) - solidEulerCharacteristic: Solid Euler characteristic (chi = V - E + F - C) - num3dCells: Number of 3D volumetric cells in input - num2dCells: Number of 2D surface cells in input (ignored) - numOtherCells: Number of other cells in input - numBoundaryEdges: Number of boundary edges on surface - numNonManifoldEdges: Number of non-manifold edges on surface - numConnectedComponents: Number of disconnected 3D mesh regions + mode: "solid" -> analyze 3D cells only (chi = V - E + F - C). + "surface" -> analyze 2D cells only (chi = V - E + F), + always per connected component. + "all" -> both, in one report; missing dimension is silently + skipped. + tagArray: Cell-data array used to group surface cells (e.g. + "FaultMask"). If None, all 2D cells are analyzed as one + group. Ignored in `solid` mode. + tagValue: If set together with `tagArray`, only that value is + analyzed. If None and `tagArray` is set, all distinct + non-zero values present in the array are screened. """ + mode: str = "solid" + tagArray: Optional[ str ] = None + tagValue: Optional[ float ] = None + + +@dataclass( frozen=True ) +class SurfaceComponent: + """Per-component topology for a 2D surface.""" + componentId: int + numCells: int numVertices: int numEdges: int numFaces: int - numCells: int - solidEulerCharacteristic: int - num3dCells: int - num2dCells: int - numOtherCells: int + eulerCharacteristic: int numBoundaryEdges: int numNonManifoldEdges: int - numConnectedComponents: int + interpretation: str -def __countConnectedComponents( mesh: vtk.vtkUnstructuredGrid ) -> int: - """Count number of disconnected mesh components. - - Args: - mesh: Input unstructured grid - - Returns: - Number of disconnected regions - """ - setupLogger.info( "Checking for disconnected 3D components..." ) - - connectivity = vtk.vtkConnectivityFilter() - connectivity.SetInputData( mesh ) - connectivity.SetExtractionModeToAllRegions() - connectivity.ColorRegionsOn() - connectivity.Update() +@dataclass( frozen=True ) +class SurfaceGroup: + """Surface analysis for one tag value, or the whole 2D mesh if no tag.""" + tagArray: Optional[ str ] + tagValue: Optional[ float ] + numCells: int + numVertices: int + numEdges: int + numFaces: int + eulerCharacteristic: int + numBoundaryEdges: int + numNonManifoldEdges: int + components: tuple[ SurfaceComponent, ...] - numRegions = connectivity.GetNumberOfExtractedRegions() - setupLogger.info( f"Found {numRegions} disconnected 3D component(s)" ) +@dataclass( frozen=True ) +class Result: + """Result of Euler characteristic computation. - return numRegions + The `solid*` fields are populated when `mode in {"solid","all"}` and the + input mesh has 3D cells. The `surface*` fields are populated when + `mode in {"surface","all"}` and the input mesh has 2D cells. + """ + mode: str + # cell breakdown of the input + num3dCells: int + num2dCells: int + numOtherCells: int + # solid + solidComputed: bool = False + numVertices: int = 0 + numEdges: int = 0 + numFaces: int = 0 + numCells: int = 0 + solidEulerCharacteristic: int = 0 + numBoundaryEdges: int = 0 + numNonManifoldEdges: int = 0 + numConnectedComponents: int = 0 + # surface + surfaceComputed: bool = False + surfaceGroups: tuple[ SurfaceGroup, ...] = field( default_factory=tuple ) -def __filter3dElements( mesh: vtk.vtkUnstructuredGrid ) -> tuple[ vtk.vtkUnstructuredGrid, int, int, int, bool ]: - """Filter only 3D volumetric elements from unstructured grid. +# --- Cell-classification helpers --------------------------------------------- - Removes 2D faces, 1D edges, and 0D vertices. - Args: - mesh: Input unstructured grid +def __classifyCells( mesh: vtk.vtkUnstructuredGrid ) -> tuple[ list[ int ], list[ int ], int ]: + """Classify cells by topological dimension. Returns: - Tuple of (filtered_mesh, n_3d, n_2d, n_other, has_3d) + (cell3dIds, cell2dIds, nOther) """ - # Classify cells by dimension - cell3dIds = [] - n3d = 0 - n2d = 0 + cell3dIds: list[ int ] = [] + cell2dIds: list[ int ] = [] nOther = 0 - - setupLogger.info( "Classifying cell types..." ) for i in tqdm( range( mesh.GetNumberOfCells() ), desc="Scanning cells" ): - cell = mesh.GetCell( i ) - cellDim = cell.GetCellDimension() - - if cellDim == 3: + d = mesh.GetCell( i ).GetCellDimension() + if d == 3: cell3dIds.append( i ) - n3d += 1 - elif cellDim == 2: - n2d += 1 + elif d == 2: + cell2dIds.append( i ) else: nOther += 1 + return cell3dIds, cell2dIds, nOther - setupLogger.info( "Cell type breakdown:" ) - setupLogger.info( f" 3D volumetric cells: {n3d}" ) - setupLogger.info( f" 2D surface cells: {n2d}" ) - setupLogger.info( f" Other cells: {nOther}" ) - # Check if we have 3D cells - has3d = n3d > 0 +def __extractCells( mesh: vtk.vtkUnstructuredGrid, cellIds: list[ int ] ) -> vtk.vtkUnstructuredGrid: + """Return a new unstructured grid containing only the listed cells.""" + idList = vtk.vtkIdList() + for c in cellIds: + idList.InsertNextId( c ) + ext = vtk.vtkExtractCells() + ext.SetInputData( mesh ) + ext.SetCellList( idList ) + ext.Update() + return ext.GetOutput() - if not has3d: - setupLogger.warning( "No 3D volumetric elements found!" ) - setupLogger.warning( "This appears to be a pure surface mesh." ) - setupLogger.warning( "Cannot compute solid Euler characteristic." ) - return mesh, n3d, n2d, nOther, False - if n2d > 0: - setupLogger.info( f"Filtering out {n2d} 2D boundary cells..." ) - setupLogger.info( f"Using only {n3d} volumetric elements" ) +def __filterByTagValue( mesh: vtk.vtkUnstructuredGrid, tagArray: str, tagValue: float ) -> vtk.vtkUnstructuredGrid: + """Keep only cells whose `tagArray` equals `tagValue`.""" + th = vtk.vtkThreshold() + th.SetInputData( mesh ) + th.SetInputArrayToProcess( 0, 0, 0, vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS, tagArray ) + th.SetLowerThreshold( float( tagValue ) ) + th.SetUpperThreshold( float( tagValue ) ) + th.SetThresholdFunction( vtk.vtkThreshold.THRESHOLD_BETWEEN ) + th.Update() + return th.GetOutput() - # Extract only 3D cells using vtkExtractCells - idList = vtk.vtkIdList() - for cellId in cell3dIds: - idList.InsertNextId( cellId ) - extractor = vtk.vtkExtractCells() - extractor.SetInputData( mesh ) - extractor.SetCellList( idList ) - extractor.Update() +# --- Solid path -------------------------------------------------------------- - filteredMesh = extractor.GetOutput() - return filteredMesh, n3d, n2d, nOther, has3d +def __countConnectedComponents( mesh: vtk.vtkUnstructuredGrid ) -> int: + """Count disconnected regions (cells connected via shared points).""" + cf = vtk.vtkConnectivityFilter() + cf.SetInputData( mesh ) + cf.SetExtractionModeToAllRegions() + cf.ColorRegionsOn() + cf.Update() + return cf.GetNumberOfExtractedRegions() def __countUniqueEdgesAndFaces( mesh: vtk.vtkUnstructuredGrid ) -> tuple[ int, int ]: - """Count unique edges and faces in 3D mesh (NumPy optimized). - - Args: - mesh: 3D unstructured grid - - Returns: - Tuple of (num_edges, num_faces) - """ + """Count unique edges and faces in a 3D mesh.""" setupLogger.info( "Counting unique edges and faces in 3D mesh..." ) - - try: - import numpy as np - use_numpy = True - except ImportError: - use_numpy = False - - numCells = mesh.GetNumberOfCells() - - if use_numpy: - # Use numpy for faster operations - edge_list = [] - face_list = [] - - for i in tqdm( range( numCells ), desc="Processing cells", mininterval=1.0 ): - - cell = mesh.GetCell( i ) - - # Edges - numEdges = cell.GetNumberOfEdges() - for edge_idx in range( numEdges ): - edge = cell.GetEdge( edge_idx ) - p0 = edge.GetPointId( 0 ) - p1 = edge.GetPointId( 1 ) - edge_list.append( ( min( p0, p1 ), max( p0, p1 ) ) ) - - # Faces - numFaces = cell.GetNumberOfFaces() - for face_idx in range( numFaces ): - face = cell.GetFace( face_idx ) - num_pts = face.GetNumberOfPoints() - point_ids = tuple( sorted( [ face.GetPointId( j ) for j in range( num_pts ) ] ) ) - face_list.append( point_ids ) - - # Use numpy unique for deduplication (faster than set) - setupLogger.info( " Deduplicating edges and faces..." ) - - # For edges: convert to array and use unique - edge_array = np.array( edge_list, dtype=np.int64 ) - unique_edges = np.unique( edge_array, axis=0 ) - num_edges = len( unique_edges ) - - # For faces: use set (numpy doesn't handle variable-length well) - num_faces = len( set( face_list ) ) - - else: - # Fallback to optimized set-based approach - edge_set = set() - face_set = set() - - for i in tqdm( range( numCells ), desc="Processing cells", mininterval=0.5 ): - cell = mesh.GetCell( i ) - - numEdges = cell.GetNumberOfEdges() - for edge_idx in range( numEdges ): - edge = cell.GetEdge( edge_idx ) - p0 = edge.GetPointId( 0 ) - p1 = edge.GetPointId( 1 ) - edge_set.add( ( min( p0, p1 ), max( p0, p1 ) ) ) - - numFaces = cell.GetNumberOfFaces() - for face_idx in range( numFaces ): - face = cell.GetFace( face_idx ) - num_pts = face.GetNumberOfPoints() - face_set.add( tuple( sorted( [ face.GetPointId( j ) for j in range( num_pts ) ] ) ) ) - - num_edges = len( edge_set ) - num_faces = len( face_set ) - - setupLogger.info( f" Unique edges: {num_edges:,}" ) - setupLogger.info( f" Unique faces: {num_faces:,}" ) - - return num_edges, num_faces - - -def __extractSurface( mesh: vtk.vtkUnstructuredGrid ) -> vtk.vtkPolyData: - """Extract surface from unstructured grid (3D elements only). - - Args: - mesh: Input unstructured grid (3D elements) - - Returns: - Surface as polydata - """ - setupLogger.info( "Extracting boundary surface from 3D elements..." ) + edgeList: list[ tuple[ int, int ] ] = [] + faceSet: set[ tuple[ int, ...] ] = set() + for i in tqdm( range( mesh.GetNumberOfCells() ), desc="Processing cells", mininterval=1.0 ): + cell = mesh.GetCell( i ) + for k in range( cell.GetNumberOfEdges() ): + e = cell.GetEdge( k ) + p0 = e.GetPointId( 0 ) + p1 = e.GetPointId( 1 ) + edgeList.append( ( min( p0, p1 ), max( p0, p1 ) ) ) + for k in range( cell.GetNumberOfFaces() ): + f = cell.GetFace( k ) + faceSet.add( tuple( sorted( f.GetPointId( j ) for j in range( f.GetNumberOfPoints() ) ) ) ) + edges = np.unique( np.asarray( edgeList, dtype=np.int64 ), axis=0 ) + return len( edges ), len( faceSet ) + + +def __surfaceQualityOfSolidBoundary( mesh3d: vtk.vtkUnstructuredGrid ) -> tuple[ int, int ]: + """Count boundary and non-manifold edges on the boundary of a 3D mesh.""" surfaceFilter = vtk.vtkDataSetSurfaceFilter() - surfaceFilter.SetInputData( mesh ) + surfaceFilter.SetInputData( mesh3d ) surfaceFilter.Update() - return surfaceFilter.GetOutput() - - -def __checkMeshQuality( surface: vtk.vtkPolyData ) -> tuple[ int, int ]: - """Check for boundary edges and non-manifold features. - - Args: - surface: Surface mesh - - Returns: - Tuple of (boundary_edges, non_manifold_edges) - """ - setupLogger.info( "Checking mesh quality..." ) - - # Count boundary edges - featureEdgesBoundary = vtk.vtkFeatureEdges() - featureEdgesBoundary.SetInputData( surface ) - featureEdgesBoundary.BoundaryEdgesOn() - featureEdgesBoundary.ManifoldEdgesOff() - featureEdgesBoundary.NonManifoldEdgesOff() - featureEdgesBoundary.FeatureEdgesOff() - featureEdgesBoundary.Update() - boundaryEdges = featureEdgesBoundary.GetOutput().GetNumberOfCells() - - # Count non-manifold edges - featureEdgesNm = vtk.vtkFeatureEdges() - featureEdgesNm.SetInputData( surface ) - featureEdgesNm.BoundaryEdgesOff() - featureEdgesNm.ManifoldEdgesOff() - featureEdgesNm.NonManifoldEdgesOn() - featureEdgesNm.FeatureEdgesOff() - featureEdgesNm.Update() - nonManifoldEdges = featureEdgesNm.GetOutput().GetNumberOfCells() - - return boundaryEdges, nonManifoldEdges + surf = surfaceFilter.GetOutput() + fb = vtk.vtkFeatureEdges() + fb.SetInputData( surf ) + fb.BoundaryEdgesOn() + fb.ManifoldEdgesOff() + fb.NonManifoldEdgesOff() + fb.FeatureEdgesOff() + fb.Update() + boundary = fb.GetOutput().GetNumberOfCells() + fn = vtk.vtkFeatureEdges() + fn.SetInputData( surf ) + fn.BoundaryEdgesOff() + fn.ManifoldEdgesOff() + fn.NonManifoldEdgesOn() + fn.FeatureEdgesOff() + fn.Update() + nonManifold = fn.GetOutput().GetNumberOfCells() + return boundary, nonManifold + + +# --- Surface path ------------------------------------------------------------- + + +def __interpretSurface( chi: int, boundaryEdges: int, nonManifoldEdges: int ) -> str: + """Short topology label for a surface component.""" + if nonManifoldEdges > 0: + return f"non-manifold ({nonManifoldEdges} edge(s))" + if boundaryEdges == 0: + if chi == 2: + return "closed sphere" + if chi == 0: + return "torus (closed, genus 1)" + if chi < 0 and chi % 2 == 0: + return f"closed surface (genus {( 2 - chi ) // 2})" + return f"closed (chi={chi}, unusual)" + if chi == 1: + return "disk" + if chi == 0: + return "annulus / cylinder" + if chi < 0: + return f"surface with multiple boundaries / holes (chi={chi})" + return f"open (chi={chi}, unusual)" + + +def __surfaceComponentsFromColored( colored: vtk.vtkUnstructuredGrid ) -> list[ SurfaceComponent ]: + """Compute (V, E, F, chi, boundary, non-manifold) per RegionId of a colored 2D mesh.""" + rid = vtk_to_numpy( colored.GetCellData().GetArray( "RegionId" ) ).astype( np.int64 ) + cells = colored.GetCells() + conn = vtk_to_numpy( cells.GetConnectivityArray() ).astype( np.int64, copy=False ) + off = vtk_to_numpy( cells.GetOffsetsArray() ).astype( np.int64, copy=False ) + + components: list[ SurfaceComponent ] = [] + sizes = sorted( Counter( rid.tolist() ).items(), key=lambda kv: -kv[ 1 ] ) + for regionId, _nCells in sizes: + sel = np.flatnonzero( rid == regionId ) + verts: set[ int ] = set() + edgeCount: dict[ tuple[ int, int ], int ] = {} + for cid in sel: + pts = conn[ off[ cid ]:off[ cid + 1 ] ] + K = len( pts ) + for v in pts: + verts.add( int( v ) ) + for i in range( K ): + a, b = int( pts[ i ] ), int( pts[ ( i + 1 ) % K ] ) + ek = ( a, b ) if a < b else ( b, a ) + edgeCount[ ek ] = edgeCount.get( ek, 0 ) + 1 + V = len( verts ) + E = len( edgeCount ) + F = int( len( sel ) ) + bE = sum( 1 for c in edgeCount.values() if c == 1 ) + nm = sum( 1 for c in edgeCount.values() if c > 2 ) + chi = V - E + F + components.append( + SurfaceComponent( componentId=int( regionId ), + numCells=F, + numVertices=V, + numEdges=E, + numFaces=F, + eulerCharacteristic=chi, + numBoundaryEdges=bE, + numNonManifoldEdges=nm, + interpretation=__interpretSurface( chi, bE, nm ) ) ) + return components + + +def __runSurfaceGroup( mesh2d: vtk.vtkUnstructuredGrid, tagArray: Optional[ str ], + tagValue: Optional[ float ] ) -> SurfaceGroup: + """Filter (optionally) and per-component analyze one surface group.""" + if tagValue is not None and tagArray is not None: # noqa: SIM108 + sub = __filterByTagValue( mesh2d, tagArray, tagValue ) + else: + sub = mesh2d + nCells = sub.GetNumberOfCells() + if nCells == 0: + return SurfaceGroup( tagArray=tagArray, + tagValue=tagValue, + numCells=0, + numVertices=0, + numEdges=0, + numFaces=0, + eulerCharacteristic=0, + numBoundaryEdges=0, + numNonManifoldEdges=0, + components=() ) + cf = vtk.vtkConnectivityFilter() + cf.SetInputData( sub ) + cf.SetExtractionModeToAllRegions() + cf.ColorRegionsOn() + cf.Update() + components = __surfaceComponentsFromColored( cf.GetOutput() ) + V = sum( c.numVertices for c in components ) + E = sum( c.numEdges for c in components ) + F = sum( c.numFaces for c in components ) + bE = sum( c.numBoundaryEdges for c in components ) + nm = sum( c.numNonManifoldEdges for c in components ) + return SurfaceGroup( tagArray=tagArray, + tagValue=tagValue, + numCells=nCells, + numVertices=V, + numEdges=E, + numFaces=F, + eulerCharacteristic=V - E + F, + numBoundaryEdges=bE, + numNonManifoldEdges=nm, + components=tuple( components ) ) + + +def __discoverTagValues( mesh: vtk.vtkUnstructuredGrid, tagArray: str, skipZero: bool = True ) -> list[ float ]: + """Return the sorted distinct values of `tagArray`, optionally skipping 0.""" + arr = mesh.GetCellData().GetArray( tagArray ) + if arr is None: + setupLogger.warning( f"Tag array '{tagArray}' not found on input mesh." ) + return [] + values = vtk_to_numpy( arr ) + unique = sorted( { float( v ) for v in np.asarray( values ).ravel() } ) + if skipZero: + unique = [ v for v in unique if v != 0.0 ] + return unique + + +# --- Top-level dispatch ------------------------------------------------------ + + +def __solidAction( mesh3d: vtk.vtkUnstructuredGrid ) -> dict: + """Run the existing solid analysis and return the resulting fields.""" + nComponents = __countConnectedComponents( mesh3d ) + V = mesh3d.GetNumberOfPoints() + C = mesh3d.GetNumberOfCells() + E, F = __countUniqueEdgesAndFaces( mesh3d ) + chi = V - E + F - C + boundary, nonManifold = __surfaceQualityOfSolidBoundary( mesh3d ) + return { + "numVertices": V, + "numEdges": E, + "numFaces": F, + "numCells": C, + "solidEulerCharacteristic": chi, + "numBoundaryEdges": boundary, + "numNonManifoldEdges": nonManifold, + "numConnectedComponents": nComponents, + } + + +def __surfaceAction( mesh2d: vtk.vtkUnstructuredGrid, options: Options ) -> list[ SurfaceGroup ]: + """Run the surface analysis. Returns one or more SurfaceGroups.""" + if options.tagArray is None: + return [ __runSurfaceGroup( mesh2d, None, None ) ] + if options.tagValue is not None: + return [ __runSurfaceGroup( mesh2d, options.tagArray, options.tagValue ) ] + # discover all distinct non-zero values and screen them + values = __discoverTagValues( mesh2d, options.tagArray, skipZero=True ) + return [ __runSurfaceGroup( mesh2d, options.tagArray, v ) for v in values ] def meshAction( mesh: vtk.vtkUnstructuredGrid, options: Options ) -> Result: - """Compute solid Euler characteristic for a mesh. - - Only considers 3D volumetric elements. Computes chi_solid = V - E + F - C. + """Compute Euler characteristic for a mesh. Args: mesh: Input unstructured grid - options: Computation options + options: Configuration options Returns: - Result with solid Euler characteristic and topology information + Result with the requested topology information. """ - setupLogger.info( "Starting solid Euler characteristic computation..." ) - setupLogger.info( f"Input mesh: {mesh.GetNumberOfPoints()} points, {mesh.GetNumberOfCells()} cells" ) - - # Filter to 3D elements only - mesh3d, n3d, n2d, nOther, has3d = __filter3dElements( mesh ) - - if not has3d: - raise RuntimeError( "Cannot compute solid Euler - no 3D cells found" ) - - # Count connected components - numComponents = __countConnectedComponents( mesh3d ) - - # Get basic counts - V = mesh3d.GetNumberOfPoints() - C = mesh3d.GetNumberOfCells() - - # Count unique edges and faces in 3D mesh - E, F = __countUniqueEdgesAndFaces( mesh3d ) - - setupLogger.info( "Solid mesh topology:" ) - setupLogger.info( f" Vertices (V): {V:,}" ) - setupLogger.info( f" Edges (E): {E:,}" ) - setupLogger.info( f" Faces (F): {F:,}" ) - setupLogger.info( f" Cells (C): {C:,}" ) - - # Calculate solid Euler characteristic - solidEuler = V - E + F - C - - setupLogger.info( f"Solid Euler characteristic (chi = V - E + F - C): {solidEuler}" ) - - # Interpret result - setupLogger.info( "Topology interpretation:" ) - setupLogger.info( f" 3D connected components: {numComponents}" ) - - if numComponents == 1: - if solidEuler == 1: - setupLogger.info( " chi = 1: Contractible (solid ball topology)" ) - setupLogger.info( " VALID simple 3D region for simulation" ) - elif solidEuler == 0: - setupLogger.warning( " chi = 0: Solid torus (has through-hole)" ) - setupLogger.warning( " Verify this matches your domain geometry" ) - elif solidEuler == 2: - setupLogger.warning( " chi = 2: Hollow shell or internal cavity topology" ) - setupLogger.warning( " Expected chi = 1 for simple solid ball" ) - setupLogger.warning( " This suggests internal void or nested structure" ) - setupLogger.warning( " 3D cells ARE connected (verified above) - verify intended" ) - else: - setupLogger.warning( f" chi = {solidEuler}: Unusual topology" ) - setupLogger.warning( " Verify mesh integrity" ) - else: - setupLogger.error( f" Mesh has {numComponents} disconnected 3D components!" ) - setupLogger.error( " This is NOT suitable for simulation without fixing" ) - - # Check mesh quality - surface = __extractSurface( mesh3d ) - boundaryEdges, nonManifoldEdges = __checkMeshQuality( surface ) - - setupLogger.info( "Mesh quality:" ) - setupLogger.info( f" Boundary edges: {boundaryEdges:,}" ) - setupLogger.info( f" Non-manifold edges: {nonManifoldEdges:,}" ) - - # Final validation - if numComponents == 1 and boundaryEdges == 0 and nonManifoldEdges == 0: - if solidEuler == 1: - setupLogger.info( " Perfect: single connected volume, simple topology - READY!" ) - else: - setupLogger.warning( f" Connected volume but chi = {solidEuler}" ) - setupLogger.warning( " Verify internal features are intended" ) - elif numComponents > 1: - setupLogger.error( " Multiple disconnected regions - INVALID!" ) - elif boundaryEdges > 0: - setupLogger.error( " Open surface detected - INVALID!" ) - elif nonManifoldEdges > 0: - setupLogger.error( " Non-manifold geometry detected - INVALID!" ) - - return Result( numVertices=V, - numEdges=E, - numFaces=F, - numCells=C, - solidEulerCharacteristic=solidEuler, + setupLogger.info( "Starting Euler characteristic computation..." ) + setupLogger.info( f"Input mesh: {mesh.GetNumberOfPoints():,} points, " + f"{mesh.GetNumberOfCells():,} cells, mode={options.mode}" ) + + cell3dIds, cell2dIds, nOther = __classifyCells( mesh ) + n3d = len( cell3dIds ) + n2d = len( cell2dIds ) + setupLogger.info( f"Cell breakdown: 3D={n3d:,}, 2D={n2d:,}, other={nOther:,}" ) + + wantSolid = options.mode in { "solid", "all" } + wantSurface = options.mode in { "surface", "all" } + if options.mode == "solid" and n3d == 0: + raise RuntimeError( "Cannot compute solid Euler characteristic, no 3D cells." ) + if options.mode == "surface" and n2d == 0: + raise RuntimeError( "Cannot compute surface Euler characteristic, no 2D cells." ) + + solidFields: dict = {} + solidComputed = False + if wantSolid and n3d > 0: + mesh3d = __extractCells( mesh, cell3dIds ) + solidFields = __solidAction( mesh3d ) + solidComputed = True + elif wantSolid: + setupLogger.info( "No 3D cells in input, skipping solid analysis." ) + + surfaceGroups: list[ SurfaceGroup ] = [] + surfaceComputed = False + if wantSurface and n2d > 0: + mesh2d = __extractCells( mesh, cell2dIds ) + surfaceGroups = __surfaceAction( mesh2d, options ) + surfaceComputed = True + elif wantSurface: + setupLogger.info( "No 2D cells in input, skipping surface analysis." ) + + return Result( mode=options.mode, num3dCells=n3d, num2dCells=n2d, numOtherCells=nOther, - numBoundaryEdges=boundaryEdges, - numNonManifoldEdges=nonManifoldEdges, - numConnectedComponents=numComponents ) + solidComputed=solidComputed, + **solidFields, + surfaceComputed=surfaceComputed, + surfaceGroups=tuple( surfaceGroups ) ) def action( vtuInputFile: str, options: Options ) -> Result: - """Compute solid Euler characteristic for a VTU file. - - Args: - vtuInputFile: Path to input VTU file - options: Computation options - - Returns: - Result with solid Euler characteristic and topology information - """ - mesh = readUnstructuredGrid( vtuInputFile ) - return meshAction( mesh, options ) + """Compute Euler characteristic for a VTU file.""" + return meshAction( readUnstructuredGrid( vtuInputFile ), options ) diff --git a/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py b/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py index a55a298c..e6ce0aa6 100644 --- a/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py +++ b/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py @@ -1,203 +1,171 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. -"""Command line parsing for solid Euler characteristic computation.""" +"""Command line parsing for the Euler characteristic action.""" from __future__ import annotations import argparse from typing import Any -from geos.mesh_doctor.actions.euler import Options, Result +from geos.mesh_doctor.actions.euler import Options, Result, SurfaceGroup from geos.mesh_doctor.parsing import EULER from geos.mesh_doctor.parsing.cliParsing import setupLogger, addVtuInputFileArgument +__MODE = "mode" +__TAG_ARRAY = "tagArray" +__TAG_VALUE = "tagValue" -def convert( parsedOptions: dict[ str, Any ] ) -> Options: - """Convert parsed command-line options to Options object. - - Args: - parsedOptions: Dictionary of parsed command-line options. - Returns: - Options: Configuration options for Euler computation. - """ - return Options() +def convert( parsedOptions: dict[ str, Any ] ) -> Options: + """Convert parsed command-line options to Options object.""" + return Options( + mode=parsedOptions.get( __MODE, "solid" ), + tagArray=parsedOptions.get( __TAG_ARRAY ), + tagValue=parsedOptions.get( __TAG_VALUE ), + ) def fillSubparser( subparsers: argparse._SubParsersAction[ Any ] ) -> None: - """Fill the argument parser for the solid Euler characteristic action. - - Args: - subparsers: Subparsers from the main argument parser - """ - p = subparsers.add_parser( - EULER, - help="Compute solid Euler characteristic (chi = V - E + F - C) for 3D mesh topology validation.", - description="""\ -Computes the solid Euler characteristic for a 3D mesh by: - 1. Filtering to 3D volumetric elements only (ignores 2D boundary cells) - 2. Counting vertices (V), edges (E), faces (F), and cells (C) in the 3D mesh - 3. Computing chi = V - E + F - C - -Solid Euler characteristic values: - chi = 1 Simple solid region (contractible, ball-like) - standard - chi = 0 Solid torus (has through-hole) - chi = 2 Complex internal topology (may indicate internal features) - chi > 2 Multiple components or complex structure - -Primary validation criteria (what matters for simulation): - - 3D connectivity = 1 region (cells properly connected via shared faces) - - Boundary edges = 0 (closed manifold surface) - - Non-manifold edges = 0 (no overlapping cells) - -The Euler characteristic provides topological information but is secondary. -3D connectivity and manifold properties are the definitive validation checks. + """Fill the argument parser for the Euler characteristic action.""" + p = subparsers.add_parser( EULER, + help="Compute Euler characteristic for 3D solids and/or 2D surfaces.", + description="""\ +Compute the Euler characteristic of a mesh. + +Modes: + solid : 3D cells only, chi = V - E + F - C (volumetric topology). + surface : 2D cells only, chi = V - E + F (always per connected component). + all : both, in one report. Missing dimension is silently skipped. + +Surface analysis is always reported per connected component, because a +single global chi over multiple components hides defects (e.g. one stray +triangle plus one clean disk both pass a global "chi == 2" check). + +Tag-based grouping (surface analysis only): + --tagArray NAME screen each distinct non-zero value of NAME + separately. Useful for FaultMask, HorizonMask, + or any per-cell label. + --tagArray NAME --tagValue V restrict to a single value. + +Examples: + mesh-doctor euler -i mesh.vtu --mode solid + mesh-doctor euler -i mesh.vtu --mode surface + mesh-doctor euler -i mesh.vtu --mode all --tagArray FaultMask + mesh-doctor euler -i mesh.vtu --mode surface --tagArray FaultMask --tagValue 12 """ ) addVtuInputFileArgument( p ) - - -def displayResults( options: Options, result: Result ) -> None: - """Display the results of the solid Euler characteristic computation. - - Args: - options: The options used for the computation. - result: The result of the computation. - """ - setupLogger.results( "=" * 80 ) - setupLogger.results( "SOLID EULER CHARACTERISTIC RESULTS" ) + p.add_argument( "--" + __MODE, + type=str, + choices=( "solid", "surface", "all" ), + default="solid", + help="What to analyze. Default: solid (back-compat)." ) + p.add_argument( "--" + __TAG_ARRAY, + type=str, + default=None, + metavar="NAME", + help="(surface) Cell-data array used to group surface " + "cells; e.g. FaultMask. If set without --tagValue, " + "every distinct non-zero value is screened." ) + p.add_argument( "--" + __TAG_VALUE, + type=float, + default=None, + metavar="V", + help="(surface) Restrict surface analysis to cells where " + "--tagArray equals this value." ) + + +# --- Display helpers --------------------------------------------------------- + + +def __displaySolid( result: Result ) -> None: setupLogger.results( "=" * 80 ) - setupLogger.results( f"3D Mesh Topology (from {result.num3dCells:,} 3D elements):" ) - setupLogger.results( f" Vertices (V): {result.numVertices:,}" ) - setupLogger.results( f" Edges (E): {result.numEdges:,}" ) - setupLogger.results( f" Faces (F): {result.numFaces:,}" ) - setupLogger.results( f" Cells (C): {result.numCells:,}" ) - setupLogger.results( "-" * 80 ) - setupLogger.results( f" Euler Characteristic (chi = V - E + F - C): {result.solidEulerCharacteristic}" ) - setupLogger.results( f" 3D Connected Components: {result.numConnectedComponents}" ) + setupLogger.results( "SOLID EULER CHARACTERISTIC" ) setupLogger.results( "=" * 80 ) - - # PRIMARY VALIDATION: 3D connectivity - setupLogger.results( "" ) - setupLogger.results( "3D CONNECTIVITY CHECK:" ) - setupLogger.results( "-" * 80 ) - - if result.numConnectedComponents == 1: - setupLogger.results( "PASS: Single connected 3D region" ) - setupLogger.results( " All 3D cells form one continuous volume via shared faces" ) - else: - setupLogger.results( f"FAIL: {result.numConnectedComponents} disconnected 3D regions!" ) - setupLogger.results( " Mesh has separate volumes not connected by shared faces" ) - setupLogger.results( " NOT suitable for simulation - requires fixing" ) - - # TOPOLOGY INTERPRETATION - setupLogger.results( "" ) - setupLogger.results( "TOPOLOGY INTERPRETATION:" ) - setupLogger.results( "-" * 80 ) - - if result.numConnectedComponents == 1: - # Single connected region - interpret Euler + setupLogger.results( f"3D mesh topology (from {result.num3dCells:,} 3D cells):" ) + setupLogger.results( f" V = {result.numVertices:,}" ) + setupLogger.results( f" E = {result.numEdges:,}" ) + setupLogger.results( f" F = {result.numFaces:,}" ) + setupLogger.results( f" C = {result.numCells:,}" ) + setupLogger.results( f" chi (V - E + F - C) = {result.solidEulerCharacteristic}" ) + setupLogger.results( f" 3D connected components: {result.numConnectedComponents}" ) + setupLogger.results( f" Boundary edges : {result.numBoundaryEdges:,}" ) + setupLogger.results( f" Non-manifold edges : {result.numNonManifoldEdges:,}" ) + + has_single = result.numConnectedComponents == 1 + is_closed = result.numBoundaryEdges == 0 + is_manifold = result.numNonManifoldEdges == 0 + if has_single and is_closed and is_manifold: if result.solidEulerCharacteristic == 1: - setupLogger.results( "chi = 1: Simple solid ball (contractible)" ) - setupLogger.results( " Standard topology for simulation meshes" ) - - elif result.solidEulerCharacteristic == 0: - setupLogger.results( "chi = 0: Solid torus topology" ) - setupLogger.results( " Domain has through-hole or tunnel" ) - setupLogger.results( " Verify this matches your expected geometry" ) - - elif result.solidEulerCharacteristic == 2: - setupLogger.results( "chi = 2: Hollow shell or internal cavity" ) - setupLogger.results( " Expected chi = 1 for simple solid ball" ) - setupLogger.results( " Suggests internal void or nested structure" ) - setupLogger.results( " 3D cells ARE connected (verified above)" ) - setupLogger.results( " ACCEPTABLE if internal structure is intentional" ) - + setupLogger.results( " STATUS: PERFECT (chi=1, single closed manifold ball)" ) else: - setupLogger.results( f"chi = {result.solidEulerCharacteristic}: Unusual value" ) - if result.solidEulerCharacteristic > 2: - setupLogger.results( " May indicate complex internal structure" ) - setupLogger.results( " Verify mesh structure and topology are correct" ) + setupLogger.results( f" STATUS: VALID with complex topology (chi={result.solidEulerCharacteristic})" ) else: - # Multiple disconnected regions - expectedEuler = result.numConnectedComponents # Each component contributes - setupLogger.results( "Multiple regions: topology analysis not meaningful" ) - setupLogger.results( - f"Expected chi approx {expectedEuler} for {result.numConnectedComponents} disconnected balls" ) - setupLogger.results( f"Actual chi = {result.solidEulerCharacteristic}" ) - - # MESH QUALITY - setupLogger.results( "" ) - setupLogger.results( "MESH QUALITY:" ) - setupLogger.results( "-" * 80 ) - setupLogger.results( f" Boundary edges: {result.numBoundaryEdges:,}" ) - setupLogger.results( f" Non-manifold edges: {result.numNonManifoldEdges:,}" ) + setupLogger.results( " STATUS: ISSUES" ) + if not has_single: + setupLogger.results( f" - {result.numConnectedComponents} disconnected 3D regions" ) + if not is_closed: + setupLogger.results( f" - {result.numBoundaryEdges:,} boundary edges (open surface)" ) + if not is_manifold: + setupLogger.results( f" - {result.numNonManifoldEdges:,} non-manifold edges" ) - if result.numBoundaryEdges == 0: - setupLogger.results( " Closed manifold (no open boundaries)" ) - else: - setupLogger.results( " Open boundaries detected" ) - if result.numNonManifoldEdges == 0: - setupLogger.results( " Manifold geometry (no overlapping cells)" ) +def __displaySurfaceGroup( g: SurfaceGroup ) -> None: + """Display global aggregate followed by per-component breakdown.""" + if g.tagArray is None: + head = f"surface (all 2D cells, {g.numCells:,} cells, {len(g.components)} component(s))" else: - setupLogger.results( " Non-manifold edges (overlapping/duplicate cells)" ) - - # FINAL VALIDATION + head = ( f"{g.tagArray} = {int(g.tagValue) if g.tagValue.is_integer() else g.tagValue} " + f"({g.numCells:,} cells, {len(g.components)} component(s))" ) setupLogger.results( "" ) - setupLogger.results( "FINAL VALIDATION:" ) + setupLogger.results( head ) + if g.numCells == 0: + return + if len( g.components ) > 1: + setupLogger.results( f" GLOBAL: V={g.numVertices:,} E={g.numEdges:,} F={g.numFaces:,} " + f"chi={g.eulerCharacteristic} ∂E={g.numBoundaryEdges:,} " + f"NM={g.numNonManifoldEdges:,}" ) + setupLogger.results( " " + f"{'comp':>5} {'cells':>9} {'V':>8} {'E':>8} {'F':>8} {'chi':>5} " + f"{'∂E':>6} {'NM':>4} interpretation" ) + setupLogger.results( " " + "-" * 76 ) + for c in g.components: + setupLogger.results( " " + f"{c.componentId:>5} {c.numCells:>9,} {c.numVertices:>8,} {c.numEdges:>8,} " + f"{c.numFaces:>8,} {c.eulerCharacteristic:>5} {c.numBoundaryEdges:>6,} " + f"{c.numNonManifoldEdges:>4,} {c.interpretation}" ) + if len( g.components ) > 1: + setupLogger.results( f" WARNING: {len(g.components)} components — verify isolated cells" ) + + +def __displaySurface( result: Result ) -> None: setupLogger.results( "=" * 80 ) + setupLogger.results( "SURFACE EULER CHARACTERISTIC" ) + setupLogger.results( "=" * 80 ) + if not result.surfaceGroups: + setupLogger.results( "(no surface groups produced)" ) + return + for g in result.surfaceGroups: + __displaySurfaceGroup( g ) - # Check all three critical criteria - has_single_component = result.numConnectedComponents == 1 - is_closed = result.numBoundaryEdges == 0 - is_manifold = result.numNonManifoldEdges == 0 - - if has_single_component and is_closed and is_manifold: - # All critical checks pass - if result.solidEulerCharacteristic == 1: - setupLogger.results( "STATUS: PERFECT" ) - setupLogger.results( " Single connected region: YES" ) - setupLogger.results( " Closed manifold: YES" ) - setupLogger.results( " Simple topology (chi = 1): YES" ) - setupLogger.results( " READY for simulation" ) - else: - setupLogger.results( "STATUS: VALID (with complex topology)" ) - setupLogger.results( " Single connected region: YES" ) - setupLogger.results( " Closed manifold: YES" ) - setupLogger.results( f" Complex topology (chi = {result.solidEulerCharacteristic}): WARNING" ) - setupLogger.results( " ACCEPTABLE for simulation" ) - setupLogger.results( " Verify internal structure is intentional" ) - else: - # Failed critical checks - setupLogger.results( "STATUS: INVALID" ) - setupLogger.results( " Mesh has critical issues:" ) - - if not has_single_component: - setupLogger.results( f" Multiple disconnected regions ({result.numConnectedComponents}): FAIL" ) - else: - setupLogger.results( " Single connected region: PASS" ) - - if not is_closed: - setupLogger.results( f" Open boundaries ({result.numBoundaryEdges:,} boundary edges): FAIL" ) - else: - setupLogger.results( " Closed manifold: PASS" ) - if not is_manifold: - setupLogger.results( f" Non-manifold geometry ({result.numNonManifoldEdges:,} edges): FAIL" ) - else: - setupLogger.results( " Manifold geometry: PASS" ) - - setupLogger.results( "" ) - setupLogger.results( " NOT SUITABLE for simulation without fixing" ) +def displayResults( options: Options, result: Result ) -> None: + """Display the results of the Euler characteristic computation.""" + if result.solidComputed: + __displaySolid( result ) + elif options.mode in ( "solid", "all" ): + setupLogger.results( "=" * 80 ) + setupLogger.results( "SOLID EULER CHARACTERISTIC: skipped (no 3D cells in input)" ) + setupLogger.results( "=" * 80 ) + + if result.surfaceComputed: + __displaySurface( result ) + elif options.mode in ( "surface", "all" ): + setupLogger.results( "=" * 80 ) + setupLogger.results( "SURFACE EULER CHARACTERISTIC: skipped (no 2D cells in input)" ) + setupLogger.results( "=" * 80 ) - # Show input cell breakdown if result.num2dCells > 0 or result.numOtherCells > 0: setupLogger.results( "" ) setupLogger.results( "Input cell breakdown:" ) - setupLogger.results( f" 3D volumetric cells: {result.num3dCells:,} (used for analysis)" ) - if result.num2dCells > 0: - setupLogger.results( f" 2D surface cells: {result.num2dCells:,} (filtered out)" ) - if result.numOtherCells > 0: - setupLogger.results( f" Other cells: {result.numOtherCells:,} (filtered out)" ) - - setupLogger.results( "=" * 80 ) + setupLogger.results( f" 3D cells : {result.num3dCells:,}" ) + setupLogger.results( f" 2D cells : {result.num2dCells:,}" ) + setupLogger.results( f" Other cells: {result.numOtherCells:,}" ) + setupLogger.results( "=" * 80 ) \ No newline at end of file From 99f9f39f409442ac2947f16c56711f9323ffbae5 Mon Sep 17 00:00:00 2001 From: jacques franc Date: Tue, 19 May 2026 10:01:40 +0200 Subject: [PATCH 2/3] yapf --- mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py b/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py index e6ce0aa6..1ad7dcfa 100644 --- a/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py +++ b/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py @@ -168,4 +168,4 @@ def displayResults( options: Options, result: Result ) -> None: setupLogger.results( f" 3D cells : {result.num3dCells:,}" ) setupLogger.results( f" 2D cells : {result.num2dCells:,}" ) setupLogger.results( f" Other cells: {result.numOtherCells:,}" ) - setupLogger.results( "=" * 80 ) \ No newline at end of file + setupLogger.results( "=" * 80 ) From 5872c72089e5db8003e1b97258b1f8bf9b7378e1 Mon Sep 17 00:00:00 2001 From: Jacques Franc <49998870+jafranc@users.noreply.github.com> Date: Tue, 2 Jun 2026 11:11:27 +0200 Subject: [PATCH 3/3] feat: apply jafranc review comments on PR#251 (euler surface) (#256) * Merge main history * Update eulerParsing.py * revert ascending order * adding a toGlobalEdgeId helper * Update euler.py * yapf --------- Co-authored-by: Claude --- .github/workflows/python-package.yml | 2 +- .github/workflows/test_geos_integration.yml | 63 ++- .github/workflows/typing-check.yml | 2 +- .gitignore | 5 +- geos-trame/README.rst | 16 +- geos-trame/configure.sh | 4 + geos-trame/pyproject.toml | 16 +- geos-trame/src/geos/trame/app/core.py | 42 +- geos-trame/src/geos/trame/app/io/hpc_tools.py | 99 ++++ .../trame/app/io/jinja_t/local_copyback.jinja | 15 + .../trame/app/io/jinja_t/local_slurm.jinja | 27 ++ .../trame/app/io/jinja_t/p4_copyback.jinja | 15 + .../geos/trame/app/io/jinja_t/p4_slurm.jinja | 27 ++ .../trame/app/io/jinja_t/pine_copyback.jinja | 15 + .../trame/app/io/jinja_t/pine_slurm.jinja | 28 ++ .../src/geos/trame/app/io/simulation.py | 311 +++++++++++++ geos-trame/src/geos/trame/app/io/ssh_tools.py | 307 ++++++++++++ geos-trame/src/geos/trame/app/main.py | 24 +- geos-trame/src/geos/trame/app/ui/plotting.py | 2 +- .../src/geos/trame/app/ui/simulation_view.py | 410 ++++++++++++++++ .../src/geos/trame/app/ui/viewer/boxViewer.py | 3 +- .../src/geos/trame/app/ui/viewer/viewer.py | 4 +- .../trame/app/utils/async_file_watcher.py | 65 +++ geos-trame/src/geos/trame/assets/cluster.json | 68 +++ install_packages.sh | 12 +- .../geos/mesh_doctor/actions/convertMD2SG.py | 438 ++++++++++++++++++ .../src/geos/mesh_doctor/actions/euler.py | 21 +- .../src/geos/mesh_doctor/parsing/__init__.py | 1 + .../parsing/convertMD2SGParsing.py | 76 +++ .../geos/mesh_doctor/parsing/eulerParsing.py | 7 + mesh-doctor/src/geos/mesh_doctor/register.py | 2 +- mesh-doctor/tests/data/base_hexa_shift_2.vtu | Bin 0 -> 736501 bytes mesh-doctor/tests/data/base_tetra_shift.vtm | 7 + .../domain_fracture_2_ids.vtu | 55 +++ .../data/base_tetra_shift/domain_ids.vtu | 44 ++ mesh-doctor/tests/test_convertMD2SG.py | 65 +++ 36 files changed, 2241 insertions(+), 57 deletions(-) create mode 100644 geos-trame/configure.sh create mode 100644 geos-trame/src/geos/trame/app/io/hpc_tools.py create mode 100644 geos-trame/src/geos/trame/app/io/jinja_t/local_copyback.jinja create mode 100644 geos-trame/src/geos/trame/app/io/jinja_t/local_slurm.jinja create mode 100644 geos-trame/src/geos/trame/app/io/jinja_t/p4_copyback.jinja create mode 100644 geos-trame/src/geos/trame/app/io/jinja_t/p4_slurm.jinja create mode 100644 geos-trame/src/geos/trame/app/io/jinja_t/pine_copyback.jinja create mode 100644 geos-trame/src/geos/trame/app/io/jinja_t/pine_slurm.jinja create mode 100644 geos-trame/src/geos/trame/app/io/simulation.py create mode 100644 geos-trame/src/geos/trame/app/io/ssh_tools.py create mode 100644 geos-trame/src/geos/trame/app/ui/simulation_view.py create mode 100644 geos-trame/src/geos/trame/app/utils/async_file_watcher.py create mode 100644 geos-trame/src/geos/trame/assets/cluster.json create mode 100644 mesh-doctor/src/geos/mesh_doctor/actions/convertMD2SG.py create mode 100644 mesh-doctor/src/geos/mesh_doctor/parsing/convertMD2SGParsing.py create mode 100644 mesh-doctor/tests/data/base_hexa_shift_2.vtu create mode 100644 mesh-doctor/tests/data/base_tetra_shift.vtm create mode 100644 mesh-doctor/tests/data/base_tetra_shift/domain_fracture_2_ids.vtu create mode 100644 mesh-doctor/tests/data/base_tetra_shift/domain_ids.vtu create mode 100644 mesh-doctor/tests/test_convertMD2SG.py diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 54d08e11..f533a136 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -158,7 +158,7 @@ jobs: check_force_integration_label: runs-on: ubuntu-latest - needs: [build] + # needs: [build] outputs: has_geos_integration_force_label: ${{ steps.set-label.outputs.has_label }} steps: diff --git a/.github/workflows/test_geos_integration.yml b/.github/workflows/test_geos_integration.yml index 9671e519..5edaafd8 100644 --- a/.github/workflows/test_geos_integration.yml +++ b/.github/workflows/test_geos_integration.yml @@ -25,7 +25,7 @@ jobs: container: # using this image to get access to python 3.10+ - image: geosx/ubuntu22.04-gcc12:${{ needs.setup.outputs.tag }} + image: geosx/ubuntu24.04-gcc13:${{ needs.setup.outputs.tag }} steps: - name: Checkout geosPythonPackages @@ -44,22 +44,25 @@ jobs: - name: Install Build Dependencies run: | apt-get update - apt-get install -y make python3-numpy python3-dev python3-venv python3-pip + apt-get install -y make python3-numpy python3-dev python3-venv python3-setuptools # Ensure pip installs scripts to /usr/local/bin export PATH="/usr/local/bin:$PATH" echo "PATH=/usr/local/bin:$PATH" >> $GITHUB_ENV + # For Ubuntu 24.04 with Python 3.12+, setuptools is required (distutils was removed) + # Upgrade setuptools to latest versions + python3 -m pip install --break-system-packages --upgrade setuptools + # Set environment variables to handle setuptools/distutils issues - echo "SETUPTOOLS_USE_DISTUTILS=stdlib" >> $GITHUB_ENV echo "PIP_DISABLE_PIP_VERSION_CHECK=1" >> $GITHUB_ENV echo "PYTHONDONTWRITEBYTECODE=1" >> $GITHUB_ENV - + - name: Setup test environment run: | echo "Setting up test environment..." GEOS_ROOT="$(pwd)/GEOS" - SETUP_PYTHON_ENVIRONMENT_SCRIPT="$GEOS_ROOT/scripts/setupPythonEnvironment.bash" + SETUP_PYTHON_ENVIRONMENT_SCRIPT="${GEOS_ROOT}/scripts/setupPythonEnvironment.bash" if [ -n "${{ github.head_ref }}" ]; then CURRENT_GEOSPYTHONPACKAGES_BRANCH_NAME="${{ github.head_ref }}" @@ -100,7 +103,7 @@ jobs: -DENABLE_YAPF=OFF \ -DGEOS_ENABLE_TESTS=OFF \ -DGEOS_ENABLE_CONTACT=OFF \ - -DGEOS_ENABLE_FLUIDFLOW=ON \ + -DGEOS_ENABLE_FLUIDFLOW=OFF \ -DGEOS_ENABLE_INDUCEDSEISMICITY=OFF \ -DGEOS_ENABLE_MULTIPHYSICS=OFF \ -DGEOS_ENABLE_SIMPLEPDE=OFF \ @@ -119,6 +122,11 @@ jobs: run: | echo "=== Test 1: Direct setupPythonEnvironment.bash execution ===" mkdir -p bin_direct + + # # encapsulate env + python3 -m venv geos-venv + . geos-venv/bin/activate + python -m pip install --upgrade pip setuptools wheel # The setup script searches specific paths but pip installs to /usr/local/bin # We need to patch the setup script to also search /usr/local/bin @@ -130,8 +138,22 @@ jobs: # Add /usr/local/bin to the MOD_SEARCH_PATH array right after the python bin directory # We insert /usr/local/bin as the second entry - sed -i '/^declare -a MOD_SEARCH_PATH=.*PYTHON_TARGET)"/a\ "/usr/local/bin"' "$TEMP_SETUP_SCRIPT" - + # sed -i '/^declare -a MOD_SEARCH_PATH=.*PYTHON_TARGET)"/a\ "/usr/local/bin"' "$TEMP_SETUP_SCRIPT" + # Only add --break-system-packages for Python >= 3.12 + # PYTHON_VERSION=$($(which python3) -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') + # if [[ "$PYTHON_VERSION" == "3.12" || "$PYTHON_VERSION" > "3.12" ]]; then + # sed -i 's/${PIP_CMD} install/${PIP_CMD} install --break-system-packages/g' "$TEMP_SETUP_SCRIPT" + # sed -i 's/$PYTHON_TARGET -m pip install --upgrade pip setuptools wheel/$PYTHON_TARGET -m pip install --upgrade setuptools/' "$TEMP_SETUP_SCRIPT" + # fi + cat "$TEMP_SETUP_SCRIPT" + export PIP_NO_BUILD_ISOLATION=1 + bash "$TEMP_SETUP_SCRIPT" \ + -p $(which python) \ + -b "$(pwd)/bin_direct" \ + --python-pkg-branch "$CURRENT_GEOSPYTHONPACKAGES_BRANCH_NAME" \ + --verbose + cat "$TEMP_SETUP_SCRIPT" + export PIP_NO_BUILD_ISOLATION=1 bash "$TEMP_SETUP_SCRIPT" \ -p $(which python3) \ -b "$(pwd)/bin_direct" \ @@ -210,6 +232,8 @@ jobs: echo "Creating symlink from PyGEOSX virtual environment..." ln -s "$(pwd)/lib/PYGEOSX/bin/test_geosx_xml_tools" python/geosx/bin/test_geosx_xml_tools else + # If the real test script is not found, create a placeholder so the test step can proceed. + # This ensures the workflow does not fail due to a missing test script, but signals that a real test should be provided. echo "Creating placeholder test script..." echo '#!/usr/bin/env python3' > python/geosx/bin/test_geosx_xml_tools echo 'import sys' >> python/geosx/bin/test_geosx_xml_tools @@ -252,6 +276,16 @@ jobs: # Ensure geosx_python_tools is built (provides format_xml) echo "Building geosx_python_tools dependency..." make geosx_python_tools -j8 + + # The geosx_format_all_xml_files target has a bug - it depends on 'geosx_xml_tools' which doesn't exist + # It should depend on 'geosx_python_tools'. Since we can't modify the CMakeLists.txt, + # we'll run the formatting command directly instead + # To solve the bug in cmake, we would have to change: + # add_custom_target( geosx_format_all_xml_files + # COMMAND bash ${CMAKE_SOURCE_DIR}/../scripts/formatXMLFiles.bash -g ${CMAKE_BINARY_DIR}/bin/format_xml ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/../examples + # WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + # DEPENDS geosx_python_tools # ← Change this! + # ) echo "Running XML formatting directly (bypassing broken make target)..." if [ -f "bin/format_xml" ] && [ -f "$GEOS_ROOT/scripts/formatXMLFiles.bash" ]; then @@ -274,7 +308,16 @@ jobs: run: | if [[ "${{ needs.test_geos_integration.result }}" == "success" ]]; then echo "All integration tests passed successfully!" >> $GITHUB_STEP_SUMMARY - else + elif [[ "${{ needs.test_geos_integration.result }}" == "failure" ]]; then echo "Integration tests failed. Please review the logs." >> $GITHUB_STEP_SUMMARY exit 1 - fi \ No newline at end of file + elif [[ "${{ needs.test_geos_integration.result }}" == "cancelled" ]]; then + echo "Integration tests were cancelled." >> $GITHUB_STEP_SUMMARY + exit 0 + elif [[ "${{ needs.test_geos_integration.result }}" == "skipped" ]]; then + echo "Integration tests were skipped." >> $GITHUB_STEP_SUMMARY + exit 0 + else + echo "Integration tests ended with unknown status: ${{ needs.test_geos_integration.result }}" >> $GITHUB_STEP_SUMMARY + exit 1 + fi diff --git a/.github/workflows/typing-check.yml b/.github/workflows/typing-check.yml index 940fb9fc..b8482468 100644 --- a/.github/workflows/typing-check.yml +++ b/.github/workflows/typing-check.yml @@ -30,7 +30,7 @@ jobs: # working-directory: ./${{ matrix.package-name }} run: | python -m pip install --upgrade pip - python -m pip install mypy ruff types-PyYAML types-pytz + python -m pip install mypy ruff types-PyYAML types-paramiko types-pytz - name: Typing check with mypy # working-directory: ./${{ matrix.package-name }} diff --git a/.gitignore b/.gitignore index 5f08477d..6275d935 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,9 @@ MANIFEST *.manifest *.spec +#env (do not port user-specs) +.env + # Installer logs pip-log.txt pip-delete-this-directory.txt @@ -165,4 +168,4 @@ cython_debug/ #.idea/ # VSCode -.vscode \ No newline at end of file +.vscode diff --git a/geos-trame/README.rst b/geos-trame/README.rst index a1c54deb..6e519cda 100644 --- a/geos-trame/README.rst +++ b/geos-trame/README.rst @@ -21,7 +21,21 @@ Build and install the Vue components cd vue-components npm i npm run build - cd - + cd .. + +then configure the .env + + sh configure.sh + +this will generate a `dotenv` environement file defining useful path to trame, + +.. code-block:: console + + cat .env + TEMPLATE_DIR=/path/to/geosPythonPackages/geos-trame/src/geos/trame/io/jinja_t + ASSETS_DIR=/path/to/geosPythonPackages/geos-trame/src/geos/trame/assets + +those will have lower precedence than local environement variables if defined Install the application diff --git a/geos-trame/configure.sh b/geos-trame/configure.sh new file mode 100644 index 00000000..f46d8ff9 --- /dev/null +++ b/geos-trame/configure.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +echo "TEMPLATE_DIR=${PWD}/src/geos/trame/app/io/jinja_t/" >> ${PWD}/src/geos/trame/assets/.env +echo "ASSETS_DIR=${PWD}/src/geos/trame/assets/" >> ${PWD}/src/geos/trame/assets/.env \ No newline at end of file diff --git a/geos-trame/pyproject.toml b/geos-trame/pyproject.toml index 73d7a028..e751b49b 100644 --- a/geos-trame/pyproject.toml +++ b/geos-trame/pyproject.toml @@ -31,18 +31,19 @@ keywords = [ dependencies = [ "setuptools", - "typing-extensions==4.12.2", "trame==3.6.5", - "trame-vuetify==2.7.1", + "trame-vuetify==3.1.0", "trame-code==1.0.1", "trame-server==3.2.3", - "trame-client==3.5.0", + "trame-client==3.11.2", "trame-simput==2.4.3", - "trame-vtk>=2.8.14", + "trame-vtk==2.10.0", "matplotlib==3.9.4", "trame-matplotlib==2.0.3", "trame-components==2.4.2", + "python-dotenv>=1.2.1", "mpld3<0.5.11", + "paramiko==4.0.0", "xsdata[cli]>=25.4", "xsdata-pydantic[lxml]==24.5", "pyvista==0.45.2", @@ -51,7 +52,7 @@ dependencies = [ "funcy==2.0", "pytz==2025.2", "typing_inspect==0.9.0", - "typing_extensions>=4.12", + "typing_extensions>=4.15.0", "PyYAML", ] @@ -73,7 +74,8 @@ test = [ "pixelmatch==0.3.0", "Pillow==11.0.0", "pytest-mypy==0.10.3", - "pytest-xprocess==1.0.2" + "pytest-xprocess==1.0.2", + "playwright==1.59.0" ] [project.readme] @@ -94,7 +96,7 @@ include-package-data = true # include = ['geos-trame*'] [tool.setuptools.package-data] -"*" = ["*.js", "*.css"] +"*" = ["*.js", "*.css","assets/*","*.jinja","*.json",".env"] [tool.pytest.ini_options] addopts = [ diff --git a/geos-trame/src/geos/trame/app/core.py b/geos-trame/src/geos/trame/app/core.py index 3baf7d38..7452782e 100644 --- a/geos-trame/src/geos/trame/app/core.py +++ b/geos-trame/src/geos/trame/app/core.py @@ -1,7 +1,8 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. # SPDX-FileContributor: Lionel Untereiner, Jacques Franc - +# ignore context collapsing as it is clearer this way +# ruff: noqa: SIM117 from trame.ui.vuetify3 import VAppLayout from trame.decorators import TrameApp from trame.widgets import html, simput @@ -24,6 +25,9 @@ from geos.trame.app.ui.viewer.viewer import DeckViewer from geos.trame.app.components.alertHandler import AlertHandler +from geos.trame.app.io.simulation import Simulation +from geos.trame.app.ui.simulation_view import define_simulation_view + import sys @@ -38,10 +42,12 @@ def __init__( self, server: Server, file_name: str ) -> None: self.deckEditor: DeckEditor | None = None self.timelineEditor: TimelineEditor | None = None self.deckInspector: DeckInspector | None = None + self.simulationLauncher: Simulation | None = None self.server = server server.enable_module( module ) self.state.input_file = file_name + self.state.user_id = None # TODO handle hot_reload @@ -67,6 +73,9 @@ def __init__( self, server: Server, file_name: str ) -> None: self.region_viewer = RegionViewer() self.well_viewer = WellViewer( 5, 5 ) + ######## Simulation runner + self.simulation = Simulation( server=server ) + # Data loader self.data_loader = DataLoader( self.tree, self.region_viewer, self.well_viewer, trame_server=server ) @@ -177,24 +186,6 @@ def build_ui( self ) -> None: ): vuetify.VIcon( "mdi-content-save-outline" ) - with html.Div( - style= - "height: 100%; width: 300px; display: flex; align-items: center; justify-content: space-between;", - v_if=( "tab_idx == 1", ), - ): - vuetify.VBtn( - "Run", - style="z-index: 1;", - ) - vuetify.VBtn( - "Kill", - style="z-index: 1;", - ) - vuetify.VBtn( - "Clear", - style="z-index: 1;", - ) - # input file editor with vuetify.VCol( v_show=( "tab_idx == 0", ), classes="flex-grow-1 pa-0 ma-0" ): if self.tree.input_file is not None: @@ -208,3 +199,16 @@ def build_ui( self ) -> None: "The file " + self.state.input_file + " cannot be parsed.", file=sys.stderr, ) + + with vuetify.VCol( v_show=( "tab_idx == 1" ), classes="flex-grow-1 pa-0 ma-0" ): + if self.simulation is not None: + define_simulation_view( self.server ) + else: + self.ctrl.on_add_error( + "Error", + "The execution context " + self.state.exec_context + " is not consistent.", + ) + print( + "The execution context " + self.state.exec_context + " is not consistent.", + file=sys.stderr, + ) diff --git a/geos-trame/src/geos/trame/app/io/hpc_tools.py b/geos-trame/src/geos/trame/app/io/hpc_tools.py new file mode 100644 index 00000000..99ffec48 --- /dev/null +++ b/geos-trame/src/geos/trame/app/io/hpc_tools.py @@ -0,0 +1,99 @@ +from geos.trame.app.io.ssh_tools import SimulationConstant + + +class SuggestDecomposition: + + def __init__( self, selected_cluster: SimulationConstant, n_unknowns: int, job_type: str = 'cpu' ) -> None: + """Initialize the decomposition hinter for HPC.""" + self.selected_cluster: SimulationConstant = selected_cluster + self.n_unknowns: int = n_unknowns + self.job_type: str = job_type #TODO should be an enum + self.sd: list[ dict ] = [] + + @staticmethod + def compute( n_unknowns: int, + memory_per_unknown_bytes: int, + node_memory_gb: int, + cores_per_node: int, + min_unknowns_per_rank: int = 10000, + strong_scaling: bool = True ) -> list[ dict ]: + """Suggests node/rank distribution for a cluster computation. + + Parameters: + - n_unknowns: total number of unknowns + - memory_per_unknown_bytes: estimated memory per unknown + - node_memory_gb: available memory per node + - cores_per_node: cores available per node + - min_unknowns_per_rank: minimum for efficiency + - strong_scaling: True if problem size is fixed + + Note: + - 10,000-100,000 unknowns per rank is often a sweet spot for many PDE solvers + - Use power-of-2 decompositions when possible (helps with communication patterns) + - For 3D problems, try to maintain cubic subdomains (minimizes surface-to-volume ratio, reducing communication) + - Don't oversubscribe: avoid using more ranks than provide parallel efficiency + + """ + # Memory constraint + node_memory_bytes = node_memory_gb * 1e9 + max_unknowns_per_node = int( 0.8 * node_memory_bytes / memory_per_unknown_bytes ) + + # Compute minimum nodes needed + min_nodes = max( 1, ( n_unknowns + max_unknowns_per_node - 1 ) // max_unknowns_per_node ) + + # Determine ranks per node + unknowns_per_node = n_unknowns // min_nodes + unknowns_per_rank = max( min_unknowns_per_rank, unknowns_per_node // cores_per_node ) + + # Calculate total ranks needed + n_ranks = max( 1, n_unknowns // unknowns_per_rank ) + + # Distribute across nodes + ranks_per_node = min( cores_per_node, ( n_ranks + min_nodes - 1 ) // min_nodes ) + n_nodes = ( n_ranks + ranks_per_node - 1 ) // ranks_per_node + + return [ + { + 'id': 1, + 'nodes': n_nodes, + 'ranks_per_node': ranks_per_node, + 'total_ranks': n_nodes * ranks_per_node, + 'unknowns_per_rank': n_unknowns // ( n_nodes * ranks_per_node ) + }, + { + 'id': 2, + 'nodes': n_nodes * 2, + 'ranks_per_node': ranks_per_node // 2, + 'total_ranks': n_nodes * ranks_per_node, + 'unknowns_per_rank': n_unknowns // ( n_nodes * ranks_per_node ) + }, + ] + + def get_sd( self ) -> list[ dict ]: + """Get the suggested decomposition popoulated.""" + if self.job_type == 'cpu' and self.selected_cluster: #make it an enum + self.sd = SuggestDecomposition.compute( self.n_unknowns, 64, self.selected_cluster.mem_per_node, + self.selected_cluster.cores_per_node ) + self.sd = [ { + **item, 'label': f"{self.selected_cluster.name} : {item['nodes']} x {item['ranks_per_node']}" + } for item in self.sd ] + else: + self.sd = [ + { + 'id': -1, + 'label': 'No: 0x0', + 'nodes': 0, + 'ranks_per_node': 0, + 'total_ranks': 0, + 'unknowns_per_rank': 0 + }, + ] + # elif job_type == 'gpu': + # selected_cluster['n_nodes']*selected_cluster['gpu']['per_node'] + + return self.sd + + # def to_list( self ) -> list[ str ]: + # """Pretty printer to list of string for display in UI.""" + # sd = self.get_sd() + # return [ f"{self.selected_cluster.name} : {sd_item['nodes']} x {sd_item['ranks_per_node']}" for sd_item in sd ] diff --git a/geos-trame/src/geos/trame/app/io/jinja_t/local_copyback.jinja b/geos-trame/src/geos/trame/app/io/jinja_t/local_copyback.jinja new file mode 100644 index 00000000..fb360583 --- /dev/null +++ b/geos-trame/src/geos/trame/app/io/jinja_t/local_copyback.jinja @@ -0,0 +1,15 @@ +#!/bin/sh +#SBATCH --job-name="{{ job_name }}" +#SBATCH --ntasks={{ ntasks }} +#SBATCH --partition={{ partition }} +#SBATCH --comment={{ comment_gr }} +#SBATCH --account={{ account }} +#SBATCH --nodes={{ nodes }} +#SBATCH --time={{ time | default('00:10:00') }} +#SBATCH --mem={{ mem }} +#SBATCH --output=job_GEOS_%j.out +#SBATCH --error=job_GEOS_%j.err +#SBATCH --dependency=afterok:{{ dep_job_id }} + +srun tar cfz {{ dep_job_id }}.tgz Outputs_{{ dep_job_id }}/ log_{{ dep_job_id }}.out +srun mkdir -p {{ target_dl_path }} && mv -v {{ dep_job_id }}.tgz {{ target_dl_path }} \ No newline at end of file diff --git a/geos-trame/src/geos/trame/app/io/jinja_t/local_slurm.jinja b/geos-trame/src/geos/trame/app/io/jinja_t/local_slurm.jinja new file mode 100644 index 00000000..21f6ff6f --- /dev/null +++ b/geos-trame/src/geos/trame/app/io/jinja_t/local_slurm.jinja @@ -0,0 +1,27 @@ +#!/bin/sh +#SBATCH --job-name="{{ job_name }}" +#SBATCH --ntasks={{ ntasks }} +#SBATCH --partition={{ partition }} +#SBATCH --comment={{ comment_gr }} +#SBATCH --account={{ account }} +#SBATCH --nodes={{ nodes }} +#SBATCH --time={{ time | default('00:10:00') }} +#SBATCH --mem={{ mem }} +#SBATCH --output=job_GEOS_%j.out +#SBATCH --error=job_GEOS_%j.err + +ulimit -s unlimited +ulimit -c unlimited + +module purge +module use {{ geos_module }} +module load {{ geos_load_list }} + +export HDF5_USE_FILE_LOCKING=FALSE +export OMP_NUM_THREADS=1 +export EXEC={{ geos_path }} + +srun --hint=nomultithread \ + -n {{ ntasks }} ${EXEC} \ + -o Outputs_${SLURM_JOBID} \ + -i {{ input_file | default('geosDeck.xml') }} | tee Outputs_${SLURM_JOBID}/log_${SLURM_JOBID}.out \ No newline at end of file diff --git a/geos-trame/src/geos/trame/app/io/jinja_t/p4_copyback.jinja b/geos-trame/src/geos/trame/app/io/jinja_t/p4_copyback.jinja new file mode 100644 index 00000000..35cc2b79 --- /dev/null +++ b/geos-trame/src/geos/trame/app/io/jinja_t/p4_copyback.jinja @@ -0,0 +1,15 @@ +#!/bin/sh +#SBATCH --job-name="{{ job_name }}" +#SBATCH --ntasks={{ ntasks }} +#SBATCH --partition={{ partition }} +#SBATCH --comment={{ comment_gr }} +#SBATCH --account={{ account }} +#SBATCH --nodes={{ nodes }} +#SBATCH --time={{ time | default('00:10:00') }} +#SBATCH --mem={{ mem }} +#SBATCH --output=job_GEOS_%j.out +#SBATCH --err=job_GEOS_%j.err +#SBATCH --dependency=afterok:{{ dep_job_id }} + +srun tar cfz {{ dep_job_id }}.tgz Outputs_{{ dep_job_id }}/ log_{{ dep_job_id }}.out +srun mkdir -p {{ target_dl_path }} && mv -v {{ dep_job_id }}.tgz {{ target_dl_path }} diff --git a/geos-trame/src/geos/trame/app/io/jinja_t/p4_slurm.jinja b/geos-trame/src/geos/trame/app/io/jinja_t/p4_slurm.jinja new file mode 100644 index 00000000..bb331e45 --- /dev/null +++ b/geos-trame/src/geos/trame/app/io/jinja_t/p4_slurm.jinja @@ -0,0 +1,27 @@ +#!/bin/sh +#SBATCH --job-name="{{ job_name }}" +#SBATCH --ntasks={{ ntasks }} +#SBATCH --partition={{ partition }} +#SBATCH --comment={{ comment_gr }} +#SBATCH --account={{ account }} +#SBATCH --nodes={{ nodes }} +#SBATCH --time={{ time | default('00:10:00') }} +#SBATCH --mem={{ mem }} +#SBATCH --output=job_GEOS_%j.out +#SBATCH --error=job_GEOS_%j.err + +ulimit -s unlimited +ulimit -c unlimited + +module purge +module use {{ geos_module }} +module load {{ geos_load_list }} + +export HDF5_USE_FILE_LOCKING=FALSE +export OMP_NUM_THREADS=1 +export EXEC={{ geos_path }} + +srun --mpi=pmix_v3 --hint=nomultithread \ + -n {{ ntasks }} ${EXEC} \ + -o Outputs_${SLURM_JOBID} \ + -i {{ input_file | default('geosDeck.xml') }} | tee log_${SLURM_JOBID}.out diff --git a/geos-trame/src/geos/trame/app/io/jinja_t/pine_copyback.jinja b/geos-trame/src/geos/trame/app/io/jinja_t/pine_copyback.jinja new file mode 100644 index 00000000..e849c07d --- /dev/null +++ b/geos-trame/src/geos/trame/app/io/jinja_t/pine_copyback.jinja @@ -0,0 +1,15 @@ +#!/bin/sh +#SBATCH --job-name="{{ job_name }}" +#SBATCH --ntasks={{ ntasks }} +#SBATCH --partition={{ partition }} +#SBATCH --comment={{ comment_gr }} +#SBATCH --account={{ account }} +#SBATCH --nodes={{ nodes }} +#SBATCH --time={{ time | default('00:10:00') }} +#SBATCH --mem={{ mem }} +#SBATCH --output=job_GEOS_%j.out +#SBATCH --error=job_GEOS_%j.err +#SBATCH --dependency=afterok:{{ dep_job_id }} + +srun tar cfz {{ dep_job_id }}.tgz Outputs_{{ dep_job_id }}/ log_{{ dep_job_id }}.out +srun mkdir -p {{ target_dl_path }} && mv -v {{ dep_job_id }}.tgz {{ target_dl_path }} diff --git a/geos-trame/src/geos/trame/app/io/jinja_t/pine_slurm.jinja b/geos-trame/src/geos/trame/app/io/jinja_t/pine_slurm.jinja new file mode 100644 index 00000000..0c47ae29 --- /dev/null +++ b/geos-trame/src/geos/trame/app/io/jinja_t/pine_slurm.jinja @@ -0,0 +1,28 @@ +#!/bin/sh +#SBATCH --job-name="{{ job_name }}" +#SBATCH --ntasks={{ ntasks }} +#SBATCH --partition={{ partition }} +#SBATCH --comment={{ comment_gr }} +#SBATCH --account={{ account }} +#SBATCH --nodes={{ nodes }} +#SBATCH --time={{ time | default('00:10:00') }} +#SBATCH --mem={{ mem }} +#SBATCH --output=job_GEOS_%j.out +#SBATCH --error=job_GEOS_%j.err + +ulimit -s unlimited +ulimit -c unlimited + +module purge +module use {{ geos_module }} +module load {{ geos_load_list }} + +export HDF5_USE_FILE_LOCKING=FALSE +export OMP_NUM_THREADS=1 +export EXEC={{ geos_path }} + +mkdir -p Outputs_${SLURM_JOBID} && touch log_${SLURM_JOBID}.out +mpirun -mca coll_hcoll_enable 0 -x UCX_RNDV_THRESH=131072 \ + -n {{ ntasks }} ${EXEC} \ + -o Outputs_${SLURM_JOBID} \ + -i {{ input_file | default('geosDeck.xml') }} | tee Outputs_${SLURM_JOBID}/log_${SLURM_JOBID}.out diff --git a/geos-trame/src/geos/trame/app/io/simulation.py b/geos-trame/src/geos/trame/app/io/simulation.py new file mode 100644 index 00000000..e4afac10 --- /dev/null +++ b/geos-trame/src/geos/trame/app/io/simulation.py @@ -0,0 +1,311 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. +# SPDX-FileContributor: Jacques Franc + +from pathlib import Path +from enum import Enum, unique, auto +from typing import Optional, Any +from trame_server.core import Server + +from geos.trame.app.io.ssh_tools import Authentificator +from geos.trame.app.utils.async_file_watcher import AsyncPeriodicRunner + +from jinja2 import Environment, FileSystemLoader +import paramiko +import re +import os + + +@unique +class SimulationStatus( Enum ): + SCHEDULED = auto() + RUNNING = auto() + COMPLETING = auto() + COPY_BACK = auto() + DONE = auto() + NOT_RUN = auto() + UNKNOWN = auto() + + +@unique +class SlurmJobStatus( Enum ): + PENDING = "PEND" + RUNNING = "R" + COMPLETING = "CG" + COMPLETED = "CD" + SUSPENDED = "S" + UNKNOWN = "UNKNOWN" + + +class Simulation: + """Simulation component. + + Fills the UI with the screenshot as read from the simulation outputs folder and a graph with the time series + from the simulation. + Requires a simulation runner providing information on the output path of the simulation to monitor and ways to + trigger the simulation. + """ + + def __init__( self, server: Server, sim_info_dir: Optional[ Path ] = None ) -> None: + """Initialize the Simulation object with logging and sim triggers among other callbacks.""" + self._server = server + controller = server.controller + self._sim_info_dir = sim_info_dir + server.state.job_ids = [] + server.state.selected_cluster = None + server.state.nunknowns = 1 + + server.state.status_colors = { + "PENDING": "#4CAF50", #PD + "RUNNING": "#3F51B5", #R + "CANCELLED": "#FFC107", #CA + "COMPLETED": "#484B45", #CD + "FAILED": "#E53935", #F + } + self._job_status_watcher: Optional[ AsyncPeriodicRunner ] = None + self._job_status_watcher_period_ms = 2000 + + #define triggers + @controller.trigger( "run_try_login" ) + def run_try_login() -> None: + + # if server.state.key: + Authentificator.ssh_client = Authentificator._create_ssh_client( + Authentificator.get_cluster( server.state.selected_cluster_name ).host, #test + Authentificator.get_cluster( server.state.selected_cluster_name ).port, + server.state.login, + key=Authentificator.get_key( server.state.login, server.state.password, server.state.key_path, + server.state.selected_cluster_name ) ) + + if Authentificator.ssh_client: + server.state.access_granted = True + + @controller.trigger( "run_simulation" ) + def run_simulation() -> None: + + # if server.state.access_granted and server.state.sd and server.state.simulation_xml_filename: + if server.state.access_granted and server.state.simulation_xml_filename and server.state.decomposition: + if Authentificator.ssh_client: + # create remote path + try: + sftp = Authentificator.ssh_client.open_sftp() + sftp.stat( server.state.simulation_remote_path ) + except FileNotFoundError: + import posixpath + jpart = '/' + for part in server.state.simulation_remote_path.split( '/' )[ 1: ]: + try: + jpart = posixpath.join( jpart, part ) + sftp.stat( str( jpart ) ) # exists? + except FileNotFoundError: + sftp.mkdir( str( jpart ) ) + except PermissionError: + print( f"Permission error creating root folder at {jpart}" ) + raise + except: + print( f"Error creating root folder at {jpart}" ) + raise + + # create local path + os.makedirs( server.state.simulation_dl_path, exist_ok=True ) + + Authentificator._sftp_copy_tree( Authentificator.ssh_client, + Simulation.gen_tree( server.state.simulation_xml_filename ), + server.state.simulation_remote_path ) + + cluster_name = Authentificator.get_cluster( server.state.selected_cluster_name ).name + cluster_part = Authentificator.get_cluster( server.state.selected_cluster_name ).partition + cluster_trans_part = Authentificator.get_cluster( + server.state.selected_cluster_name ).partition_transfert + run_id: str = Simulation.render_and_run( + f'{cluster_name}_slurm.jinja', + 'job.slurm', + server, + job_name=server.state.simulation_job_name, + input_file=[ + item for item in server.state.simulation_xml_filename if item.get( 'type' ) == 'text/xml' + ][ 0 ].get( 'name' ), + nodes=server.state.decomposition[ 'nodes' ], + ntasks=server.state.decomposition[ 'nodes' ] * server.state.decomposition[ 'ranks_per_node' ], + geos_module=Authentificator.get_cluster( server.state.selected_cluster_name ).geos_module, + geos_load_list=" ".join( + Authentificator.get_cluster( server.state.selected_cluster_name ).geos_load_list ), + geos_path=Authentificator.get_cluster( server.state.selected_cluster_name ).geos_path, + mem="0", + comment_gr=server.state.slurm_comment, + partition=cluster_part, + account=server.state.slurm_comment ) + + Simulation.render_and_run( f'{cluster_name}_copyback.jinja', + 'copyback.slurm', + server, + job_name=server.state.simulation_job_name, + input_file=[ + item for item in server.state.simulation_xml_filename + if item.get( 'type' ) == 'text/xml' + ][ 0 ].get( 'name' ), + nodes=1, + ntasks=1, + mem="0", + dep_job_id=run_id, + target_dl_path=server.state.simulation_dl_path, + comment_gr=server.state.slurm_comment, + partition=cluster_trans_part, + account=server.state.slurm_comment ) + + self._start_result_streams() + + else: + raise paramiko.SSHException + + @controller.trigger( "kill_all_simulations" ) + def kill_all_simulations() -> None: + # exec scancel jobid + for jobs in server.state.job_ids: + Authentificator.kill_job( jobs[ 'job_id' ] ) + + def __del__( self ) -> None: + """Clean up running streams on destruction.""" + self._stop_result_streams() + + def set_status_watcher_period_ms( self, period_ms: int ) -> None: + """Set the watcher period in ms.""" + self._job_status_watcher_period_ms = period_ms + if self._job_status_watcher: + self._job_status_watcher.set_period_ms( period_ms ) + + def _stop_result_streams( self ) -> None: + if self._job_status_watcher is not None: + self._job_status_watcher.stop() + + def _start_result_streams( self ) -> None: + self._stop_result_streams() + self._job_status_watcher = AsyncPeriodicRunner( self.check_jobs, period_ms=self._job_status_watcher_period_ms ) + + def check_jobs( self ) -> None: + """Check on running jobs and update their names and progresses.""" + if Authentificator.ssh_client: + jid = self._server.state.job_ids + for index, job in enumerate( jid ): + job_id = job[ 'job_id' ] + _, sout, _ = Authentificator._execute_remote_command( + Authentificator.ssh_client, f'sacct -j {job_id} -o JobID,JobName,State --noheader' ) + job_line = sout.strip().split( "\n" )[ -1 ] + + jid[ index ][ 'status' ] = job_line.split()[ 2 ] + jid[ index ][ 'name' ] = job_line.split()[ 1 ] + + if ( jid[ index ][ 'status' ] == 'RUNNING' ): + _, sout, _ = Authentificator._execute_remote_command( + Authentificator.ssh_client, + f"sacct -j {job_id} -o ElapsedRaw,TimelimitRaw --noheader --parsable2 | head -n 1 " ) + progress_line = sout.strip().split( "|" ) + jid[ index ][ 'slprogress' ] = str( + float( progress_line[ 0 ] ) / float( progress_line[ 1 ] ) / 60 * 100 ) + + # getthe completed status + pattern = re.compile( r'\((\d+(?:\.\d+)?)%\s*completed\)' ) + _, sout, _ = Authentificator._execute_remote_command( + Authentificator.ssh_client, + f"grep \"completed\" {self._server.state.simulation_remote_path}/job_GEOS_{job_id}.out | tail -1" + ) + m = pattern.search( sout.strip() ) + if m: + jid[ index ][ 'simprogress' ] = str( m.group( 1 ) ) + + print( + f"{job_line}-{job_id}\n job id:{jid[index]['job_id']}\n status:{jid[index]['status']}\n name:{jid[index]['name']} \n --- \n" + ) + self._server.state.job_ids = jid + self._server.state.dirty( "job_ids" ) + self._server.state.flush() + + return None + + @staticmethod + def render_and_run( template_name: str, dest_name: str, server: Server, **kwargs: Any ) -> str: + """Render the slurm template and run it. Return it job_id.""" + if server.state.access_granted and server.state.simulation_xml_filename: + template = Environment( + loader=FileSystemLoader( f'{os.getenv("TEMPLATE_DIR")}' ) ).get_template( template_name ) + rendered = template.render( kwargs ) + + if Authentificator.ssh_client: + #write slurm directly on remote + try: + sftp = Authentificator.ssh_client.open_sftp() + remote_path = Path( server.state.simulation_remote_path ) / Path( dest_name ) + with sftp.file( str( remote_path ), 'w' ) as f: + f.write( rendered ) + + except PermissionError as e: + print( f"Permission error: {e}" ) + except IOError as e: + print( f"Error accessing remote file or path: {e}" ) + except Exception as e: + print( f"An error occurred during SFTP: {e}" ) + + _, sout, _ = Authentificator._execute_remote_command( + Authentificator.ssh_client, f'cd {server.state.simulation_remote_path} && sbatch {dest_name}' ) + job_lines = sout.strip() + job_id = re.search( r"Submitted batch job (\d+)", job_lines ) + if job_id: + server.state.job_ids.append( { 'job_id': job_id.group( 1 ) } ) + return job_id.group( 1 ) + else: + return "-1" + else: + return "-1" + else: + return "-1" + + @staticmethod + def gen_tree( xml_filename: Any ) -> dict: + """Generate file tree to be copied on remote from files uploaded.""" + import re + xml_pattern = re.compile( r"\.xml$", re.IGNORECASE ) + mesh_pattern = re.compile( r"\.(vtu|vtm|pvtu|pvtm)$", re.IGNORECASE ) + table_pattern = re.compile( r"\.(txt|dat|csv|geos)$", re.IGNORECASE ) + xml_matches = [] + mesh_matches = [] + table_matches = [] + + pattern_file = r"[\w\-.]+\.(?:vtu|pvtu|dat|txt|xml|geos)\b" # all files + pattern_xml_path = r"\"(.*/)*([\w\-.]+\.(?:xml))\b" + pattern_mesh_path = r"\"(.*/)*([\w\-.]+\.(?:vtu|pvtu|vtm|pvtm))\b" + pattern_table_curly_path = r"((?:[\w\-/]+/)+)*([\w\-.]+\.(?:geos|csv|dat|txt))" + + for file in xml_filename: + if xml_pattern.search( file.get( "name", "" ) ): + xml_matches.append( file ) + elif mesh_pattern.search( file.get( "name", "" ) ): + mesh_matches.append( file ) + elif table_pattern.search( file.get( "name", "" ) ): + table_matches.append( file ) + + #assume the first XML is the main xml + xml_expected_file_matches = re.findall( pattern_file, xml_matches[ 0 ][ 'content' ].decode( "utf-8" ) ) + + #TODO all the needed files + test_assert = { item.get( "name" ) for item in xml_filename }.intersection( set( xml_expected_file_matches ) ) + assert test_assert + + decoded = re.sub( pattern_xml_path, r'"\2', xml_matches[ 0 ][ 'content' ].decode( "utf-8" ) ) + decoded = re.sub( pattern_mesh_path, r'"mesh/\2', decoded ) + decoded = re.sub( pattern_table_curly_path, r"tables/\2", decoded ) + + xml_matches[ 0 ][ 'content' ] = decoded.encode( "utf-8" ) + + FILE_TREE = { + 'root': '.', + "structure": { + "files": xml_matches, + "subfolders": { + "mesh": mesh_matches, + "tables": table_matches + } + } + } + + print( f"Generated FILE_TREE: {FILE_TREE}" ) + return FILE_TREE diff --git a/geos-trame/src/geos/trame/app/io/ssh_tools.py b/geos-trame/src/geos/trame/app/io/ssh_tools.py new file mode 100644 index 00000000..ff5ef654 --- /dev/null +++ b/geos-trame/src/geos/trame/app/io/ssh_tools.py @@ -0,0 +1,307 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. +# SPDX-FileContributor: Jacques Franc + +from typing import Optional +from pathlib import Path +import paramiko +import os +import json +from dataclasses import dataclass + + +@dataclass +class SimulationConstant: + name: str + host: str + partition: str + partition_transfert: str + port: int + geos_path: str + geos_module: str + geos_load_list: list + remote_home_base: str # for ssh key + simulation_default_filename: str + simulation_remote_path: str + simulation_dl_default_path: str + simulation_information_default_path: str + n_nodes: int + cores_per_node: int + mem_per_node: int + + +#If proxyJump are needed +# +# proxy_cmd = "ssh -W {host}:{port} proxyuser@bastion.example.com".format( +# host=ssh_host, port=ssh_port +# ) +# from paramiko import ProxyCommand +# sock = ProxyCommand(proxy_cmd) + +# client = paramiko.SSHClient() +# client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +# client.connect( +# hostname=ssh_host, +# port=ssh_port, +# username=username, +# key_filename=keyfile, +# sock=sock, # <— tunnel created by ProxyCommand +# ) + + +class Authentificator: + + ssh_client: Optional[ paramiko.SSHClient ] = None + + sim_constants: list = [] + + @staticmethod + def reload_simconstants() -> None: + """Reload the cluster configuration from cluster.json file.""" + Authentificator.sim_constants = [ + SimulationConstant( **item ) + for item in json.load( open( f'{os.getenv("ASSETS_DIR")}/cluster.json', 'r' ) ) # noqa: SIM115 + ] + + @staticmethod + def get_cluster( name: str ) -> Optional[ SimulationConstant ]: + """Return the structured meta for cluster selected.""" + match = next( ( item for item in Authentificator.sim_constants if item.name == name ), None ) + return match + + @staticmethod + def _sftp_copy_tree( ssh_client: paramiko.SSHClient, file_tree: dict, remote_root: str ) -> None: + """Copy the file tree at remote root using ssh_client.""" + # Connect to remote server + sftp = ssh_client.open_sftp() + + Authentificator.dfs_tree( file_tree[ "structure" ], file_tree[ "root" ], sftp=sftp, remote_root=remote_root ) + + sftp.close() + + @staticmethod + def dfs_tree( node: list | dict, path: str, sftp: paramiko.SFTPClient, remote_root: str ) -> None: + """Create the tree represented by node at local path in remote pointed by sftp client at remote_root.""" + if path is None or remote_root is None: + return # type:ignore[unreachable] + + lp = Path( path ) + rp = Path( remote_root ) / lp + + if isinstance( node, list ): + for file in node: + print( f"copying {lp/Path(file.get('name'))} to {rp/Path(file.get('name'))}" ) + try: + content = file.get( 'content' ) + mode = "wb" if isinstance( content, ( bytes, bytearray ) ) else "w" + with sftp.file( str( rp / Path( file.get( 'name' ) ) ), mode ) as f: + f.write( content ) + except Exception as e: + print( f"Error copying {lp/Path(file.get('name'))} to {rp/Path(file.get('name'))}: {e}" ) + raise + elif isinstance( node, dict ): + if "files" in node: + files = node[ 'files' ] + for file in files: + print( f"copying {lp/Path(file.get('name'))} to {rp/Path(file.get('name'))}" ) + try: + content = file.get( 'content' ) + mode = "wb" if isinstance( content, ( bytes, bytearray ) ) else "w" + with sftp.file( str( rp / Path( file.get( 'name' ) ) ), mode ) as f: + f.write( content ) + except Exception as e: + print( f"Error copying {lp/Path(file.get('name'))} to {rp/Path(file.get('name'))}: {e}" ) + raise + if "subfolders" in node: + for subfolder, content in node[ "subfolders" ].items(): + try: + sftp.stat( str( rp / Path( subfolder ) ) ) + except FileNotFoundError: + try: + print( f"creating {rp/Path(subfolder)}" ) + sftp.mkdir( str( rp / Path( subfolder ) ) ) + except: + print( f"Error creating {rp/Path(subfolder)} on remote." ) + raise + Authentificator.dfs_tree( content, str( lp / Path( subfolder ) ), sftp, remote_root ) + + for folder, content in node.items(): + if folder not in [ "files", "subfolders" ]: + try: + sftp.stat( str( rp / Path( folder ) ) ) + except FileNotFoundError: + print( f"creating {rp/Path(folder)}" ) + try: + sftp.mkdir( str( rp / Path( folder ) ) ) + except: + print( f"Error creating {rp/Path(subfolder)} on remote." ) + raise + Authentificator.dfs_tree( content, str( lp / Path( folder ) ), sftp, remote_root ) + + @staticmethod + def kill_job( id: int ) -> None: + """Cancel job identified by id in slurm schedulder.""" + if Authentificator.ssh_client: + Authentificator._execute_remote_command( Authentificator.ssh_client, f"scancel {id}" ) + return None + + @staticmethod + def get_key( id: str, pword: str, key_path: str, cluster_name: str ) -> paramiko.RSAKey: + """Return the ssh key if found or create and dispatch one.""" + try: + PRIVATE_KEY = paramiko.RSAKey.from_private_key_file( key_path ) + return PRIVATE_KEY + except paramiko.SSHException as e: + print( f"Error loading private key: {e}\n" ) + raise paramiko.SSHException( f"Error loading private key: {e}\n" ) from e + except FileNotFoundError as e: + print( f"Private key not found: {e}\n Generating key at ... {key_path}" ) + PRIVATE_KEY = Authentificator.gen_key( key_path ) + temp_client = paramiko.SSHClient() + temp_client.set_missing_host_key_policy( paramiko.AutoAddPolicy() ) + + clusterByName = Authentificator.get_cluster( cluster_name ) + if clusterByName is None: + raise ValueError( f"Cluster '{cluster_name}' not found in configuration." ) from e + + host = clusterByName.host + port = clusterByName.port + temp_client.connect( host, port, username=id, password=pword, timeout=10 ) + remote_base = clusterByName.remote_home_base + Authentificator._transfer_file_sftp( temp_client, f"{key_path.split('/')[-1]}.pub", + f"{remote_base}/{id}/.ssh/{key_path.split('/')[-1]}.pub" ) + Authentificator._execute_remote_command( + temp_client, + f" cat {remote_base}/.ssh/{key_path.split('/')[-1]}.pub | tee -a {clusterByName.remote_home_base}/.ssh/authorized_keys" + ) + + return PRIVATE_KEY + + @staticmethod + def gen_key( key_path: str ) -> paramiko.RSAKey: + """Generate RSAKey for SSH protocol.""" + # home = os.environ.get( "HOME" ) + # file_path = f"{home}/.ssh/id_trame" + key = paramiko.RSAKey.generate( bits=4096 ) + key.write_private_key_file( key_path ) + + # Get public key in OpenSSH format + public_key = f"{key.get_name()} {key.get_base64()}" + with open( key_path + ".pub", "w" ) as pub_file: + pub_file.write( public_key ) + + suffix = key_path.split( '/' )[ -1 ] + print( f"SSH key pair generated: {suffix} (private), {suffix}.pub (public)" ) + + return key + + @staticmethod + def _create_ssh_client( host: str, + port: int, + username: str, + password: str | None = None, + key: paramiko.RSAKey | None = None ) -> paramiko.SSHClient | None: + """Initializes and returns an SSH client connection. + + Uses context manager for automatic cleanup. + """ + client = paramiko.SSHClient() + # Automatically adds the hostname and new host keys to the host files (~/.ssh/known_hosts) + client.set_missing_host_key_policy( paramiko.AutoAddPolicy() ) + + try: + print( f"Connecting to {host} using key-based authentication..." ) + client.connect( host, port, username, pkey=key, timeout=10 ) + + return client + except paramiko.AuthenticationException: + print( "Authentication failed. Check your credentials or key." ) + return None + except paramiko.SSHException as e: + print( f"Could not establish SSH connection: {e}" ) + return None + except Exception as e: + print( f"An unexpected error occurred: {e}" ) + return None + + @staticmethod + def _execute_remote_command( client: paramiko.SSHClient, command: str ) -> tuple[ int, str, str ]: + """Executes a single command on the remote server and prints the output.""" + if not client: + return ( -1, "", "" ) + + print( f"\n--- Executing Command: '{command}' ---" ) + try: + # Executes the command. stdin, stdout, and stderr are file-like objects. + # Ensure command ends with a newline character for some shell environments. + stdin, stdout, stderr = client.exec_command( command ) + + # Wait for the command to finish and read the output + exit_status = stdout.channel.recv_exit_status() + + # Print standard output + stdout_data = stdout.read().decode().strip() + if stdout_data: + print( "STDOUT:" ) + print( stdout_data ) + + # Print standard error (if any) + stderr_data = stderr.read().decode().strip() + if stderr_data: + print( "STDERR:" ) + print( stderr_data ) + + print( f"Command exited with status: {exit_status}" ) + return ( exit_status, stdout_data, stderr_data ) + + except PermissionError as e: + print( f"Permission error: {e}" ) + return ( -1, "", "" ) + except IOError as e: + print( f"Error accessing remote file or path: {e}" ) + return ( -1, "", "" ) + except Exception as e: + print( f"An error occurred during SFTP: {e}" ) + return ( -1, "", "" ) + + @staticmethod + def _transfer_file_sftp( client: paramiko.SSHClient, + local_path: str, + remote_path: str, + direction: str = "put" ) -> Optional[ bool ]: + """Transfers a file using SFTP (Secure File Transfer Protocol). + + Direction can be 'put' (upload) or 'get' (download). + """ + if not client: + return None + + print( f"\n--- Starting SFTP Transfer ({direction.upper()}) ---" ) + + try: + # Establish an SFTP connection session + sftp = client.open_sftp() + + if direction == "put": + print( f"Uploading '{local_path}' to '{remote_path}'..." ) + sftp.put( local_path, remote_path ) + print( "Upload complete." ) + elif direction == "get": + print( f"Downloading '{remote_path}' to '{local_path}'..." ) + sftp.get( remote_path, local_path ) + print( "Download complete." ) + else: + print( "Invalid transfer direction. Use 'put' or 'get'." ) + + sftp.close() + return True + + except FileNotFoundError: + print( f"Error: Local file '{local_path}' not found." ) + return False + except IOError as e: + print( f"Error accessing remote file or path: {e}" ) + return False + except Exception as e: + print( f"An error occurred during SFTP: {e}" ) + return False diff --git a/geos-trame/src/geos/trame/app/main.py b/geos-trame/src/geos/trame/app/main.py index 2ad3b293..cada4bee 100644 --- a/geos-trame/src/geos/trame/app/main.py +++ b/geos-trame/src/geos/trame/app/main.py @@ -1,13 +1,17 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. -# SPDX-FileContributor: Lionel Untereiner +# SPDX-FileContributor: Lionel Untereiner, Jacques Franc from pathlib import Path from typing import Any +from dotenv import load_dotenv +import os from trame.app import get_server # type: ignore from trame_server import Server +#do not override if existing from geos.trame.app.core import GeosTrame +from geos.trame.app.io.ssh_tools import Authentificator def main( server: Server = None, **kwargs: Any ) -> None: @@ -24,13 +28,23 @@ def main( server: Server = None, **kwargs: Any ) -> None: # parse args parser = server.cli - parser.add_argument( "-I", "--input", help="Input file (.xml)" ) + parser.add_argument( "-I", "--input", help="Input file (.xml)", required=True ) + parser.add_argument( "-e", "--env", help="dot_env file", required=False ) ( args, _unknown ) = parser.parse_known_args() - if args.input is None: - print( "Usage: \n\tgeos-trame -I /path/to/input/file" ) - return + if args.env: + if not load_dotenv( dotenv_path=Path( args.env ) ): + raise SystemExit( f"Failed to load environment file: {args.env}" ) + else: + env_path = Path( __file__ ).parent.parent / "assets/.env" + if not load_dotenv( dotenv_path=env_path ): + raise SystemExit( f"Failed to load environment file: {env_path}" ) + + Authentificator.reload_simconstants() + + print( f"TEMPLATE_DIR .. {os.getenv('TEMPLATE_DIR')}" ) + print( f"ASSETS_DIR .. {os.getenv('ASSETS_DIR')}" ) file_name = str( Path( args.input ).absolute() ) diff --git a/geos-trame/src/geos/trame/app/ui/plotting.py b/geos-trame/src/geos/trame/app/ui/plotting.py index 1ca2bbb9..790203c2 100644 --- a/geos-trame/src/geos/trame/app/ui/plotting.py +++ b/geos-trame/src/geos/trame/app/ui/plotting.py @@ -57,7 +57,7 @@ def _figure_size( self ) -> dict: "dpi": dpi, } - def _permeability( self, **kwargs: Any ) -> Figure: + def _permeability( self, **kwargs: Any ) -> type[ Figure ]: # read data assert self.source.input_file is not None if self.source.input_file is None: diff --git a/geos-trame/src/geos/trame/app/ui/simulation_view.py b/geos-trame/src/geos/trame/app/ui/simulation_view.py new file mode 100644 index 00000000..627b4757 --- /dev/null +++ b/geos-trame/src/geos/trame/app/ui/simulation_view.py @@ -0,0 +1,410 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. +# SPDX-FileContributor: Jacques Franc +# ignore context collapsing as it is clearer this way +# ruff: noqa: SIM117 +from typing import Any + +from trame.widgets import html +from trame.widgets import vuetify3 as vuetify +from trame_server import Server + +from geos.trame.app.io.simulation import Authentificator +from geos.trame.app.io.hpc_tools import SuggestDecomposition + +#rough estimate of n unknowns would be better from GEOS's dry-run +# unknowns (oncell,onpoint) +# for now do not take into account wells as dep on the num of wells (neg vs matrix elmts) +# for now do not take into account frac as dep on the num of frac elmts (prob neg vs matrix elmts) +solvers_to_unknowns = { + "CompositionalMultiphaseFVM": ( 3, 0 ), + "CompositionalMultiphaseHybridFVM": ( 4, 0 ), + "CompositionalMultiphaseReservoirPoromechanics": ( 3, 3 ), + "CompositionalMultiphaseReservoirPoromechanicsConformingFractures": ( 3, 6 ), + "CompositionalMultiphaseWell": ( 3, 0 ), + "ElasticFirstOrderSEM": ( 0, 3 ), + "ElasticSEM": ( 0, 3 ), + "ImmiscibleMultiphaseFlow": ( 3, 0 ), + "LaplaceFEM": ( 0, 3 ), + "MultiphasePoromechanics": ( 3, 3 ), + "MultiphasePoromechanicsReservoir": ( 3, 3 ), #?? + "MultiphasePoromechanicsConformingFractures": ( 3, 6 ), + "SinglePhaseFVM": ( 2, 0 ), + "SinglePhaseHybridFVM": ( 3, 0 ), + "SinglePhasePoromechanics": ( 2, 3 ), + "SinglePhasePoromechanicsConformingFractures": ( 2, 3 ), + "SinglePhasePoromechanicsConformingFracturesALM": ( 2, 3 ), + "SinglePhaseWell": ( 2, 0 ), + "SolidMechanicsEmbeddedFractures": ( 0, 3 ), + "SolidMechanicsAugmentedLagrangianContact": ( 0, 3 ), + "SolidMechanicsLagrangeContact": ( 0, 3 ), + "SolidMechanicsLagrangeContactBubbleStab": ( 0, 3 ), + "SolidMechanicsLagrangianFEM": ( 0, 3 ) +} + + +# helpers +def _what_solver( bcontent: dict ) -> tuple[ int, int ]: + from xml.etree.ElementTree import Element, fromstring + sim_xml: Element = fromstring( bcontent[ 'content' ] ) + solver = sim_xml.find( 'Solvers' ) + nunk: list[ tuple[ int, int ] ] = [ solvers_to_unknowns.get( elt.tag, ( 1, 0 ) ) + for elt in solver ] if solver else [ ( 0, 0 ) ] + return max( nunk ) + + +def _how_many_cells( bcontent: dict ) -> tuple[ int, int ]: + import vtk + name = bcontent[ 'name' ] + if name.endswith( ".vtp" ): + reader = vtk.vtkXMLPolyDataReader() + elif name.endswith( ".vtu" ): + reader = vtk.vtkXMLUnstructuredGridReader() + elif name.endswith( ".vtm" ): + reader = vtk.vtkXMLMultiBlockDataReader() + else: + raise ValueError( "Unsupported kind (use 'vtp', 'vtu', or 'vtm')." ) + + reader.SetReadFromInputString( 1 ) + reader.SetInputString( bcontent[ 'content' ] ) + reader.Update() + output = reader.GetOutput() + return ( output.GetNumberOfCells(), output.GetNumberOfPoints() ) + + +def _has_internalMesh( bcontent: dict ) -> bool: + from xml.etree.ElementTree import Element, fromstring + sim_xml: Element = fromstring( bcontent[ 'content' ] ) + return bool( sim_xml.find( 'Mesh/InternalMesh' ) is not None ) + + +def _what_internalMesh( bcontent: dict ) -> tuple[ int, int ]: + from xml.etree.ElementTree import Element, fromstring + import re + sim_xml: Element = fromstring( bcontent[ 'content' ] ) + + mesh = sim_xml.find( 'Mesh/InternalMesh' ) + + def _parse_sum( value: str | None ) -> int: + if value is None: + return 0 + return sum( int( el ) for el in re.findall( r'-?\d+(?:\.\d+)?', value ) ) + + if mesh is None: + nx = ny = nz = 0 + else: + nx = _parse_sum( mesh.get( 'nx' ) ) + ny = _parse_sum( mesh.get( 'ny' ) ) + nz = _parse_sum( mesh.get( 'nz' ) ) + + return ( nx * ny * nz, ( nx + 1 ) * ( ny + 1 ) * ( nz + 1 ) ) + + +#TODO a class from it +def define_simulation_view( server: Server ) -> None: + """Functional definition of UI elements.""" + + @server.state.change( "other_widget_selected_file" ) + def on_other_widget_file_ready( other_widget_selected_file: dict, **_: Any ) -> None: + if not other_widget_selected_file: + return + + current = list( server.state.simulation_xml_filename ) + existing_names = { f.get( "name" ) for f in current } + + if other_widget_selected_file.get( "name" ) not in existing_names: + current.append( other_widget_selected_file ) + server.state.simulation_xml_filename = current + + @server.state.change( "selected_cluster_name" ) + def on_cluster_change( selected_cluster_name: str, **_: Any ) -> None: + print( f"selecting {selected_cluster_name}" ) + cluster = Authentificator.get_cluster( selected_cluster_name ) + if cluster is None: + print( f"Error: Cluster '{selected_cluster_name}' not found in configuration." ) + return + + server.state.decompositions = SuggestDecomposition( cluster, server.state.nunknowns ).get_sd() + + server.state.simulation_remote_path = cluster.simulation_remote_path + + server.state.simulation_dl_path = cluster.simulation_dl_default_path + + # @server.state.change( "decomposition" ) + # def on_decomposition_selected( decomposition: str, **_: Any ) -> None: + # = SuggestDecomposition( Authentificator.get_cluster( server.state.selected_cluster_name ), server.state.nunknowns ).get_sd() + # # if server.state.decomposition: + # except: + # server.state.sd = { 'nodes': 0, 'total_ranks': 0 } + + @server.state.change( "simulation_xml_temp" ) + def on_temp_change( simulation_xml_temp: list, **_: Any ) -> None: + current_list = server.state.simulation_xml_filename + new_list = current_list + simulation_xml_temp + + server.state.simulation_xml_filename = new_list + server.state.simulation_xml_temp = [] + + @server.state.change( "nunknowns" ) + def on_nunknowns_change( nunknowns: int, **_: Any ) -> None: + #re-gen list + if len( server.state.decompositions ) > 0: + server.state.decompositions = SuggestDecomposition( + Authentificator.get_cluster( server.state.selected_cluster_name ), nunknowns ).get_sd() + print( f'unknowns changed : {server.state.nunknowns} -> {nunknowns}' ) + server.state.nunknowns = nunknowns + + @server.state.change( "simulation_xml_filename" ) + def on_simfiles_change( simulation_xml_filename: list, **_: Any ) -> None: + import re + has_xml = [ file.get( "type", "" ) == 'text/xml' for file in simulation_xml_filename ] + + has_external_mesh = [ + bool( file.get( "name", "" ).endswith( ( ".vtu", ".vtm", ".vtp" ) ) ) for file in simulation_xml_filename + ] + + has_internal_mesh = False + for i, _ in enumerate( has_xml ): + if has_xml[ i ]: + has_internal_mesh = _has_internalMesh( simulation_xml_filename[ i ] ) + + if any( has_xml ): + uc = up = nc = np = 0 + # compute unknowns and cells only for xml files, if external mesh do not take into account internal mesh info even if present, if no external mesh try to take into account internal mesh info if present + # useful for decomposition suggestion + for i, _ in enumerate( has_xml ): + if has_external_mesh[ i ]: + nc, np = _how_many_cells( simulation_xml_filename[ i ] ) + elif has_xml[ i ]: + uc, up = _what_solver( simulation_xml_filename[ i ] ) + if has_internal_mesh: + nc, np = _what_internalMesh( simulation_xml_filename[ i ] ) + + if all( i is not None for i in ( uc, nc, up, np ) ): + server.state.nunknowns = uc * nc + up * np + + if any( has_xml ): + xml_pattern = re.compile( r"\.xml$", re.IGNORECASE ) + mesh_pattern = re.compile( r"\.(vtu|vtm|pvtu|pvtm)$", re.IGNORECASE ) + table_pattern = re.compile( r"\.(txt|dat|csv|geos)$", re.IGNORECASE ) + + xml_matches, mesh_matches, table_matches = [], [], [] + + pattern_file = r"[\w\-.]+\.(?:vtu|pvtu|dat|txt|xml|geos)\b" + + # Fix: use enumerate instead of .index() to handle duplicates safely + for i, file in enumerate( simulation_xml_filename ): + if not has_xml[ i ]: + continue + name = file.get( "name", "" ) + if xml_pattern.search( name ): + xml_matches.append( file ) + elif mesh_pattern.search( name ): + mesh_matches.append( file ) + elif table_pattern.search( name ): + table_matches.append( file ) + + if xml_matches: + already_have = { file.get( "name", "" ) for file in simulation_xml_filename } + required = set( re.findall( pattern_file, xml_matches[ 0 ][ 'content' ].decode( "utf-8" ) ) ) + required -= already_have + # Fix: store as list of dicts so the UI can use {{ file.name }} + server.state.simulation_xml_required = [ { "name": f } for f in sorted( required ) ] + else: + server.state.simulation_xml_required = [] + + server.state.is_valid_jobfiles = any( has_xml ) + server.state.all_req_files = any( has_xml ) and len( server.state.simulation_xml_required ) == 0 + + def kill_job( index_to_remove: int ) -> None: + # for now just check there is an xml + jid = list( server.state.job_ids ) + if 0 <= index_to_remove < len( jid ): + removed_id = jid[ index_to_remove ][ 'job_id' ] + Authentificator.kill_job( removed_id ) + del jid[ index_to_remove ] + + server.state.job_ids = jid + print( f"Job {removed_id} kill. Still running: {len(jid)}" ) + else: + print( f"Error: supress index does not exist ({index_to_remove})." ) + + def run_remove_jobfile( index_to_remove: int ) -> None: + current_files = list( server.state.simulation_xml_filename ) + if 0 <= index_to_remove < len( current_files ): + del current_files[ index_to_remove ] + + server.state.simulation_xml_filename = current_files + print( f"File at {index_to_remove} deleted. New files: {len(current_files)}" ) + else: + print( f"Erreur: Wrong deletion index ({index_to_remove})." ) + + with vuetify.VContainer(): + with vuetify.VRow(): + with vuetify.VCol( cols=4 ): + vuetify.VTextField( v_model=( + "login", + None, + ), + label="Login", + dense=True, + hide_details=True, + clearable=True, + prepend_icon="mdi-login" ) + with vuetify.VCol( cols=4 ): + vuetify.VTextField( v_model=( + "password", + None, + ), + label="Password", + type="password", + dense=True, + hide_details=True, + clearable=True, + prepend_icon="mdi-onepassword" ) + + # + server.state.access_granted = False + server.state.is_valid_jobfiles = False + server.state.simulation_xml_filename = [] + server.state.simulation_xml_required = [] + server.state.selected_cluster_names = [ cluster.name for cluster in Authentificator.sim_constants ] + # server.state.decompositions = [] + + # --------------------------- auth block -----------------------# + vuetify.VDivider( vertical=True, thickness=5, classes="mx-4" ) + with vuetify.VCol( cols=1 ): + vuetify.VSelect( label="Cluster", + items=( "selected_cluster_names", ), + v_model=( "selected_cluster_name", 'p4' ) ) + vuetify.VDivider( vertical=True, thickness=5, classes="mx-4" ) + with vuetify.VCol( cols=1 ): + vuetify.VSelect( label="Decomposition", + items=( "decompositions", [] ), + v_model=( "decomposition", None ), + item_title="label", + item_value="id", + return_object=True ) + + with vuetify.VRow(): + with vuetify.VCol( cols=8 ): + vuetify.VTextField( v_model=( + "key_path", + "/users/$USER/.ssh/id_trame", + ), + label="Path to ssh key", + dense=True, + hide_details=True, + clearable=True, + prepend_icon="mdi-key-chain-variant" ) + + # + vuetify.VDivider( vertical=True, thickness=5, classes="mx-4" ) + with vuetify.VCol( cols=1 ): + vuetify.VBtn( "Log in", click="trigger('run_try_login')", + disabled=( "access_granted", ) ) # type: ignore + # + vuetify.VDivider( vertical=True, thickness=5, classes="mx-4" ) + with vuetify.VCol( cols=1 ): + vuetify.VTextField( + v_model=( "slurm_comment", None ), + label="Comment to slurm", + dense=True, + hide_details=True, + clearable=True, + ) # type: ignore + + # --------------------------- simulation block -----------------------# + vuetify.VDivider( thickness=5, classes="my-4" ) + + with vuetify.VRow(): + with vuetify.VCol( cols=4 ): + vuetify.VFileUpload( + v_model=( "simulation_xml_temp", [] ), + title="Simulation file name", + density='comfortable', + hide_details=True, + # clearable=True, + multiple=True, + filter_by_type='.xml,.vtu,.vtm,.pvtu,.pvtm,.dat,.csv,.txt,.geos,.vtk', + # readonly=True, + disabled=( "!access_granted", ) ) + with vuetify.VCol( cols=4 ), vuetify.VList(): + with vuetify.VListItem( v_for=( "(file,i) in simulation_xml_filename" ), + key="i", + value="file", + prepend_icon="mdi-minus-circle-outline", + click=( run_remove_jobfile, "[i]" ) ): + vuetify.VListItemTitle( "{{ file.name }}" ) + vuetify.VListItemSubtitle( "{{ file.size ? (file.size / 1024).toFixed(1) + ' KB' : 'URL' }}" ) + vuetify.VDivider( thickness=2, classes="my-2" ) + + with vuetify.VListItem( + v_for=( "(file,i) in simulation_xml_required" ), + key="i", + value="file", + classes="bg-red-lighten-4 text-red-darken-4", + # base_color="red-lighten-4", + # style="background-color: rgb(var(--v-theme-error-lighten-4));", + prepend_icon="mdi-alert-circle-outline" ): + vuetify.VListItemTitle( "{{ file.name }} (required)" ) + + with vuetify.VRow(), vuetify.VCol(): + vuetify.VTextField( v_model=( "simulation_remote_path", None ), + label="Path where to write files and launch code", + prepend_icon="mdi-upload", + dense=True, + hide_details=True, + clearable=True, + disabled=( "!access_granted", ) + # TODO callback validation of path + ) + + with vuetify.VRow(), vuetify.VCol(): + vuetify.VTextField( v_model=( "simulation_dl_path", None ), + label="Simulation download path", + dense=True, + clearable=True, + prepend_icon="mdi-download", + disabled=( "!access_granted", ) + # TODO callback validation of path + ) + + with vuetify.VRow(): + with vuetify.VCol( cols=4 ): + vuetify.VTextField( v_model=( "simulation_job_name", "geosJob" ), + label="Job Name", + dense=True, + hide_details=True, + clearable=True, + disabled=( "!access_granted", ) ) + + vuetify.VSpacer() + with vuetify.VCol( cols=1 ): + vuetify.VBtn( "Run", + click="trigger('run_simulation')", + disabled=( "!is_valid_jobfiles || !all_req_files", ), + classes="ml-auto" ), # type: ignore + + # ------------------------------- Status block ----------------------------- # + vuetify.VDivider( thickness=5, classes="my-4" ) + + with vuetify.VRow(): + vuetify.VSpacer() + with vuetify.VCol( cols=1 ): + vuetify.VBtn( "Kill All", click="trigger('kill_all_simulations')" ), # type: ignore + + color_expression = "status_colors[job_ids[i].status] || '#607D8B'" + with vuetify.VRow(), vuetify.VCol( cols=4 ), vuetify.VList(): + with vuetify.VListItem( v_for=( "(jobs,i) in job_ids" ), + key="i", + value="jobs", + base_color=( color_expression, ), + prepend_icon="mdi-minus-circle-outline", + click=( kill_job, "[i]" ) ): + vuetify.VListItemTitle( "{{ jobs.status }} -- {{ jobs.name }} -- {{ jobs.job_id }}" ) + vuetify.VProgressLinear( v_model=( "jobs.simprogress", "0" ), ) + vuetify.VProgressLinear( v_model=( "jobs.slprogress", "0" ), ) + + with vuetify.VRow( v_if="simulation_error" ): + html.Div( "An error occurred while running simulation :
{{simulation_error}}", style="color:red;" ) diff --git a/geos-trame/src/geos/trame/app/ui/viewer/boxViewer.py b/geos-trame/src/geos/trame/app/ui/viewer/boxViewer.py index e51f5035..4b5310e1 100644 --- a/geos-trame/src/geos/trame/app/ui/viewer/boxViewer.py +++ b/geos-trame/src/geos/trame/app/ui/viewer/boxViewer.py @@ -4,6 +4,7 @@ import pyvista as pv from geos.trame.schema_generated.schema_mod import Box +from typing import Any import re @@ -102,7 +103,7 @@ def _compute_intersected_cell( self ) -> None: if len( saved_ids ) > 0: self._extracted_cells = self._mesh.extract_cells( saved_ids ) - def _check_cell_inside_box( self, cell: pv.Cell, box_bounds: list[ float ] ) -> bool: + def _check_cell_inside_box( self, cell: pv.Cell, box_bounds: Any ) -> bool: """Check if the cell is inside the box bounds. A cell is considered inside the box if his bounds are completely diff --git a/geos-trame/src/geos/trame/app/ui/viewer/viewer.py b/geos-trame/src/geos/trame/app/ui/viewer/viewer.py index 4ea3c705..46ceddb3 100644 --- a/geos-trame/src/geos/trame/app/ui/viewer/viewer.py +++ b/geos-trame/src/geos/trame/app/ui/viewer/viewer.py @@ -401,8 +401,8 @@ def _update_box( self, active_block: Box, show_obj: bool ) -> None: return if self.box_engine is not None and active_block.name in self.box_engine: - box_polydata_actor: pv.Actor = self.box_engine[ active_block.name ].get_box_polydata_actor() - extracted_cell_actor: pv.Actor = self.box_engine[ active_block.name ].get_extracted_cells_actor() + box_polydata_actor: pv.BasePlotter = self.box_engine[ active_block.name ].get_box_polydata_actor() + extracted_cell_actor: pv.BasePlotter = self.box_engine[ active_block.name ].get_extracted_cells_actor() self.plotter.remove_actor( box_polydata_actor ) self.plotter.remove_actor( extracted_cell_actor ) del self.box_engine[ active_block.name ] diff --git a/geos-trame/src/geos/trame/app/utils/async_file_watcher.py b/geos-trame/src/geos/trame/app/utils/async_file_watcher.py new file mode 100644 index 00000000..f8960e58 --- /dev/null +++ b/geos-trame/src/geos/trame/app/utils/async_file_watcher.py @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. +# SPDX-FileContributor: Jacques Franc + +import asyncio +from asyncio import CancelledError, ensure_future +from typing import Callable + +from trame_server.utils import asynchronous + + +class AsyncPeriodicRunner: + """While started, runs given callback at given period.""" + + def __init__( self, callback: Callable, period_ms: int = 100 ) -> None: + """Init the async watcher object.""" + self.last_m_time = None + self.callback = callback + self.period_ms = period_ms + self.task = None + self.start() + + def __del__( self ) -> None: + """Clean up async watch on destruction.""" + self.stop() + + def set_period_ms( self, period_ms: int ) -> None: + """Set the async watch period. + + :params:period_ms period in ms + """ + self.period_ms = period_ms + + def start( self ) -> None: + """Stop existing async watch and start a new stream.""" + self.stop() + self.task = asynchronous.create_task( self._runner() ) + + def stop( self ) -> None: + """Stop the async watch.""" + if not self.task: + return + + ensure_future( self._wait_for_cancel() ) # type:ignore[unreachable] + + async def _wait_for_cancel( self ) -> None: + """Cancel and await cancel error for the task. + + If cancel is done outside async, it may raise warnings as cancelled exception may be triggered outside async + loop. + """ + if not self.task or self.task.done() or self.task.cancelled(): # type:ignore[unreachable] + self.task = None + return + + try: # type:ignore[unreachable] + self.task.cancel() + await self.task + except CancelledError: + self.task = None + + async def _runner( self ) -> None: + while True: + self.callback() + await asyncio.sleep( self.period_ms / 1000.0 ) diff --git a/geos-trame/src/geos/trame/assets/cluster.json b/geos-trame/src/geos/trame/assets/cluster.json new file mode 100644 index 00000000..221dd11d --- /dev/null +++ b/geos-trame/src/geos/trame/assets/cluster.json @@ -0,0 +1,68 @@ +[ + { + "name": "p4", + "host": "p4log01", + "partition": "p4_general", + "partition_transfert": "p4_transfer", + "port": 22, + "geos_path": "/workrd/users/$USER/GEOS/build-spack-generated-debug/bin/geosx", + "geos_module": "/workrd/users/$USER/modulesRHEL88", + "geos_load_list": [ + "geos-daily-rhel88" + ], + "remote_home_base": "/users/$USER", + "simulation_default_filename": "geosDeck.xml", + "simulation_remote_path": "/workrd/users/$USER/Example", + "simulation_dl_default_path": "/users/$USER/Example", + "simulation_information_default_path": "/users/$USER/.trame-logs", + "n_nodes": 212, + "cores_per_node": 192, + "mem_per_node": 747 + }, + { + "name": "pine", + "host": "pine-1", + "partition": "pine", + "partition_transfert": "pine", + "port": 22, + "geos_path": "/shared/data1/Users/$USER/codes/GEOS-2025-11-03/build-pine-1.pine.cluster-linux-rocky9-zen4-gcc@11.4.1-release/bin/geosx", + "geos_module": "/apps/modules/modulefiles3", + "geos_load_list": [ + "genesis", + "common", + "proxy", + "slurm", + "gcc/11.4.1", + "openmpi-gcc/5.0.5", + "cmake/3.27.9" + ], + "remote_home_base": "/home/$USER", + "simulation_default_filename": "geosDeck.xml", + "simulation_remote_path": "/shared/data1/Users/$USER/Example", + "simulation_dl_default_path": "/shared/data1/Users/$USER/Example", + "simulation_information_default_path": "/home/$USER/.trame-logs", + "n_nodes": 48, + "cores_per_node": 64, + "mem_per_node": 768 + }, + { + "name": "local", + "host": "127.0.0.1", + "partition": "debug", + "partition_transfert": "debug", + "port": 22, + "geos_path": "/opt/GEOS/build-spack-generated-debug/bin/geosx", + "geos_module": "/workrd/users/$USER/geos-generated", + "geos_load_list": [ + "geos-toolchains" + ], + "remote_home_base": "/home/$USER", + "simulation_default_filename": "geosDeck.xml", + "simulation_remote_path": "/work/", + "simulation_dl_default_path": "/data/", + "simulation_information_default_path": "/home/.trame-logs", + "n_nodes": 1, + "cores_per_node": 8, + "mem_per_node": 32 + } +] \ No newline at end of file diff --git a/install_packages.sh b/install_packages.sh index 3ded0635..3f9a454a 100755 --- a/install_packages.sh +++ b/install_packages.sh @@ -11,8 +11,10 @@ python -m pip install --upgrade ./mesh-doctor python -m pip install --upgrade ./pygeos-tools python -m pip install --upgrade ./geos-pv #! trame install requires npm -# cd ./geos-trame/vue-components -# npm i -# npm run build -# cd ../../ -# python -m pip install ./geos-trame \ No newline at end of file +cd ./geos-trame/vue-components +npm i +npm run build +cd .. +sh configure.sh +cd .. +python -m pip install ./geos-trame diff --git a/mesh-doctor/src/geos/mesh_doctor/actions/convertMD2SG.py b/mesh-doctor/src/geos/mesh_doctor/actions/convertMD2SG.py new file mode 100644 index 00000000..0e90fe10 --- /dev/null +++ b/mesh-doctor/src/geos/mesh_doctor/actions/convertMD2SG.py @@ -0,0 +1,438 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. +# SPDX-FileContributor: GitHub Copilot, Jacques Franc +from dataclasses import dataclass +from typing import Optional, Union + +from vtkmodules.vtkCommonDataModel import VTK_TRIANGLE, VTK_TRIANGLE_STRIP, VTK_QUAD, VTK_POLYGON, vtkPolyData, vtkKdTree, vtkCell +from vtkmodules.vtkCommonCore import vtkIdTypeArray, vtkPoints, reference, vtkUnsignedIntArray, vtkDataArray, vtkIdList +from vtkmodules.vtkCommonDataModel import vtkSelection, vtkSelectionNode, vtkUnstructuredGrid, vtkMultiBlockDataSet +from vtkmodules.vtkFiltersCore import vtkAppendPolyData, vtkCleanPolyData +from vtkmodules.vtkFiltersExtraction import vtkExtractSelection +from vtkmodules.vtkFiltersGeometry import vtkGeometryFilter + +from geos.mesh_doctor.parsing.cliParsing import setupLogger +from geos.mesh.io.vtkIO import readMesh, VtkOutput, writeMesh + + +@dataclass( frozen=True ) +class Options: + meshVtkOutput: VtkOutput + attrs: tuple[ int, ...] = () + skipCleanCollocated: bool = False + skipFilterVolumeCells: bool = False + + +@dataclass( frozen=True ) +class Result: + outputMesh: Optional[ vtkUnstructuredGrid ] + bounds: tuple[ float, float, float, float, float, float ] + numPoints: int + numCells: int + attrs: tuple[ int, ...] + skipCleanCollocated: bool + nCleanCollocated: int + skipFilterVolumeCells: bool + nFilterVolumeCells: int + nColors: int + + +TOLERANCE = 1e-6 + + +def is_surface_cell_type( t: int ) -> bool: + """Checks if the given VTK cell type is a surface cell type (2D).""" + surface_types = { + VTK_TRIANGLE, + VTK_QUAD, + VTK_POLYGON, + VTK_TRIANGLE_STRIP, + } + return t in surface_types + + +def __process_block( block: Union[ vtkMultiBlockDataSet, vtkUnstructuredGrid, vtkPolyData ], + append_filter: vtkAppendPolyData, attrs: list[ int ] ) -> None: + """Recursively processes a block of the multi-block dataset, extracting surface cells that match the given attributes and adding them to the append filter.""" + if isinstance( block, vtkMultiBlockDataSet ): + for i in range( block.GetNumberOfBlocks() ): + child: Union[ vtkMultiBlockDataSet, vtkUnstructuredGrid, + vtkPolyData ] = block.GetBlock( i ) # type: ignore[assignment] + if child is not None: + __process_block( child, append_filter, attrs ) + return + + if not hasattr( block, "GetNumberOfCells" ): + return + if block.GetNumberOfCells() == 0: + return + + cell_types = set() + for i in range( block.GetNumberOfCells() ): + cell_types.add( block.GetCellType( i ) ) + + if all( is_surface_cell_type( ct ) for ct in cell_types ): + cell_attributes = block.GetCellData().GetArray( "attribute" ) + if len( attrs ) == 0 or ( cell_attributes is not None and cell_attributes.GetTuple1( 0 ) in attrs ): + if isinstance( block, vtkPolyData ): + append_filter.AddInputData( block ) + else: + gf = vtkGeometryFilter() + gf.SetInputData( block ) + gf.Update() + append_filter.AddInputData( gf.GetOutput() ) + + +def __extract_first_vol( block: Union[ vtkMultiBlockDataSet, vtkUnstructuredGrid ] ) -> Optional[ vtkUnstructuredGrid ]: + """Recursively searches for the first volumetric block in the multi-block dataset.""" + if isinstance( block, vtkMultiBlockDataSet ): + for i in range( block.GetNumberOfBlocks() ): + child: Union[ vtkMultiBlockDataSet, vtkUnstructuredGrid ] = block.GetBlock( i ) # type: ignore[assignment] + if child is not None: + found = __extract_first_vol( child ) + if found is not None: + return found + return None + + if not hasattr( block, "GetNumberOfCells" ): + return None + if block.GetNumberOfCells() == 0: + return None + + is_vol = False + for i in range( block.GetNumberOfCells() ): + cell_type = block.GetCellType( i ) + is_vol = not is_surface_cell_type( cell_type ) + if is_vol: + break + return block if is_vol else None + + +def _filterVolumeCells( mesh: vtkUnstructuredGrid, + attrs: list[ int ] ) -> tuple[ vtkUnstructuredGrid, vtkUnstructuredGrid, int ]: + """Filters out volume cells from the input mesh, keeping only surface cells that match the given attributes. Returns the filtered volume mesh, the extracted surface mesh, and the number of cells removed.""" + volumeIds = vtkIdTypeArray() + surfaceIds = vtkIdTypeArray() + nVolume = nSurface = nOther = 0 + + cell_attributes = mesh.GetCellData().GetArray( "attribute" ) + assert cell_attributes is not None, "Input mesh must have a 'attribute' cell data array." + for i in range( mesh.GetNumberOfCells() ): + dim = mesh.GetCell( i ).GetCellDimension() + if dim == 3: + volumeIds.InsertNextValue( i ) + nVolume += 1 + elif dim == 2: + if cell_attributes.GetTuple1( i ) in attrs or len( attrs ) == 0: + surfaceIds.InsertNextValue( i ) + nSurface += 1 + else: + nOther += 1 + + if cell_attributes is not None: + setupLogger.info( + f"Mesh contains {nVolume} volume cells, {nSurface} surface cells matching attributes {attrs}, and {nOther} other cells." + ) + else: + setupLogger.info( + f"Mesh contains {nVolume} volume cells, {nSurface} surface cells, and {nOther} other cells (no 'attribute' array for filtering)." + ) + + if nSurface == 0 and nOther == 0: + setupLogger.info( "No filtering needed (all cells are 3D)" ) + return mesh, mesh.NewInstance(), nSurface + nOther + + sn = vtkSelectionNode() + sn.SetFieldType( vtkSelectionNode.CELL ) + sn.SetContentType( vtkSelectionNode.INDICES ) + sn.SetSelectionList( volumeIds ) + Esn = vtkSelectionNode() + Esn.SetFieldType( vtkSelectionNode.CELL ) + Esn.SetContentType( vtkSelectionNode.INDICES ) + Esn.SetSelectionList( surfaceIds ) + + sel = vtkSelection() + sel.AddNode( sn ) + Esel = vtkSelection() + Esel.AddNode( Esn ) + + ext = vtkExtractSelection() + ext.SetInputData( 0, mesh ) + ext.SetInputData( 1, sel ) + ext.Update() + Eext = vtkExtractSelection() + Eext.SetInputData( 0, mesh ) + Eext.SetInputData( 1, Esel ) + Eext.Update() + + setupLogger.info( f"Filtered → {nVolume} cells (removed {nSurface + nOther})" ) + + if nVolume > 0: + if nSurface > 0: + return ext.GetOutput(), Eext.GetOutput(), nSurface + nOther + return ext.GetOutput(), mesh.NewInstance(), nSurface + nOther + + return mesh.NewInstance(), mesh.NewInstance(), nSurface + nOther + + +def __clean_collocated( main: vtkUnstructuredGrid ) -> tuple[ vtkUnstructuredGrid, int ]: + """Cleans collocated points in the input mesh, returning a new mesh with unique points and updated cell connectivity, as well as the number of points cleaned.""" + clean_point_set: dict[ tuple[ float, float, float ], int ] = {} + reverse_map: dict[ int, int ] = {} + + for pid in range( main.GetNumberOfPoints() ): + pt = main.GetPoints().GetPoint( pid ) + reverse_map[ pid ] = clean_point_set.get( pt, pid ) + clean_point_set.setdefault( pt, pid ) + + old_to_new: dict[ int, int ] = {} + clean_points = vtkPoints() + for pt, ids in clean_point_set.items(): + old_to_new[ ids ] = clean_points.InsertNextPoint( pt ) + + rewrite_mesh = vtkUnstructuredGrid() + rewrite_mesh.SetPoints( clean_points ) + + for cell_id in range( main.GetNumberOfCells() ): + cell: vtkCell = main.GetCell( cell_id ) + new_ids: list[ int ] = [] + for i in range( cell.GetNumberOfPoints() ): + pid = cell.GetPointId( i ) + new_ids.append( old_to_new.get( reverse_map.get( pid, pid ), pid ) ) + rewrite_mesh.InsertNextCell( cell.GetCellType(), len( new_ids ), new_ids ) + + rewrite_mesh.GetCellData().ShallowCopy( main.GetCellData() ) + + for array_i in range( main.GetPointData().GetNumberOfArrays() ): + arr = main.GetPointData().GetArray( array_i ) + new_arr = vtkDataArray.CreateDataArray( arr.GetDataType() ) + new_arr.SetName( arr.GetName() ) + new_arr.SetNumberOfComponents( arr.GetNumberOfComponents() ) + new_arr.SetNumberOfTuples( clean_points.GetNumberOfPoints() ) + for old_id, new_id in old_to_new.items(): + new_arr.SetTuple( new_id, arr.GetTuple( old_id ) ) + rewrite_mesh.GetPointData().AddArray( new_arr ) + + nCleanCollocated = main.GetNumberOfPoints() - rewrite_mesh.GetNumberOfPoints() + return rewrite_mesh, nCleanCollocated + + +def __paintNodes( + main: vtkUnstructuredGrid, + frac_polys: list[ vtkUnstructuredGrid ] ) -> tuple[ vtkUnstructuredGrid, list[ vtkUnstructuredGrid ] ]: + """Paints the nodes of the main mesh that are close to the fracture polygons, returning the modified main mesh and the list of fracture polygons.""" + kd = vtkKdTree() + kd.BuildLocatorFromPoints( main ) + + narray = vtkUnsignedIntArray() + narray.SetNumberOfComponents( 1 ) + narray.SetNumberOfTuples( main.GetNumberOfPoints() ) + + for i in range( main.GetNumberOfPoints() ): + narray.SetTuple1( i, 0 ) + + count = 0 + setupLogger.info( f"Number of fracpolys: {len(frac_polys)}" ) + for poly in frac_polys: + setupLogger.info( + f"Processing fracpoly with {poly.GetNumberOfPoints()} points and {poly.GetNumberOfCells()} cells." ) + for i in range( poly.GetNumberOfPoints() ): + dist = reference( 0.0 ) + id_source = kd.FindClosestPoint( poly.GetPoint( i ), dist ) # type: ignore[call-overload] + if dist > TOLERANCE: # type: ignore[operator] + setupLogger.warning( + f"[too far point] main point ({id_source}) is too far from frac point ({i}) = ({dist} > {TOLERANCE})" + ) + narray.SetTuple1( id_source, 1 ) + count += 1 + + setupLogger.info( f"Painted {count}/{narray.GetNumberOfTuples()} nodes based on proximity to fracture polygons." ) + narray.SetName( "faultNodes" ) + main.GetPointData().AddArray( narray ) + return main, frac_polys + + +#TODO refactor with other coloring function to avoid code duplication +def __coloringNodes( main: vtkUnstructuredGrid ) -> tuple[ vtkUnstructuredGrid, int ]: + """Colors the nodes of the main mesh based on their point-connectivity, one array per connected component of faultNodes==1 points. Returns the modified mesh and the number of connected components found.""" + fault_array = main.GetPointData().GetArray( "faultNodes" ) + n_pts = main.GetNumberOfPoints() + + # Collect only the fault-node ids up front + fault_pids = { pid for pid in range( n_pts ) if fault_array.GetTuple1( pid ) == 1 } + setupLogger.info( f"Found {len(fault_pids)} fault nodes to color based on connectivity." ) + + visited: set[ int ] = set() + color = 0 + + for seed in fault_pids: + if seed in visited: + continue + + # One fresh, zero-filled array per connected component + color_array = vtkUnsignedIntArray() + color_array.SetNumberOfComponents( 1 ) + color_array.SetNumberOfTuples( n_pts ) + color_array.Fill( 0 ) + + # Iterative DFS — avoids Python recursion-depth limit + stack = [ seed ] + count = 0 + while stack: + pid = stack.pop() + if pid in visited: + continue + visited.add( pid ) + color_array.SetTuple1( pid, 1 ) # mark this point as belonging to component + count += 1 + + cells = vtkIdList() + main.GetPointCells( pid, cells ) + for ci in range( cells.GetNumberOfIds() ): + cell = main.GetCell( cells.GetId( ci ) ) + for vi in range( cell.GetNumberOfPoints() ): + nbr = cell.GetPointId( vi ) + if nbr not in visited and nbr in fault_pids: + stack.append( nbr ) + + color_array.SetName( f"faultNodes_{color}" ) + setupLogger.info( f"Connected component {color}: {count} points" ) + + main.GetPointData().AddArray( color_array ) + color += 1 + + return main, color + + +def polydata_to_ugrid( poly: vtkUnstructuredGrid ) -> vtkUnstructuredGrid: + """Converts a vtkPolyData to a vtkUnstructuredGrid by copying points, cells, and data.""" + ugrid = vtkUnstructuredGrid() + ugrid.SetPoints( poly.GetPoints() ) + for cid in range( poly.GetNumberOfCells() ): + cell = poly.GetCell( cid ) + ugrid.InsertNextCell( cell.GetCellType(), cell.GetPointIds() ) + ugrid.GetPointData().ShallowCopy( poly.GetPointData() ) + ugrid.GetCellData().ShallowCopy( poly.GetCellData() ) + return ugrid + + +def meshDoctor_to_surfaceGen( hierachical_mesh: vtkMultiBlockDataSet, attrs: tuple[ int, ...], + skip_clean_collocated: bool ) -> tuple[ vtkUnstructuredGrid, int, int ]: + """Converts a mesh-doctor multi-block dataset to a surface mesh compatible with SurfaceGen by extracting surface cells that match the given attributes, optionally cleaning collocated points, and returning the resulting unstructured grid along with the number of points cleaned. + + Args: + hierachical_mesh: The input multi-block dataset containing the mesh. + attrs: A tuple of attribute values to filter surface cells. If empty, all surface cells are included. + skip_clean_collocated: If True, skips the step of cleaning collocated points. If False, collocated points will be cleaned and the number of points cleaned will be returned. + + Returns: + A tuple containing the converted surface mesh as a vtkUnstructuredGrid and the number of points cleaned from collocated points (if skip_clean_collocated is False) or 0 (if skip + """ + append_filter = vtkAppendPolyData() + __process_block( hierachical_mesh, append_filter, list( attrs ) ) + + main = __extract_first_vol( hierachical_mesh ) + if main is None: + raise ValueError( "No volumetric block found in the multi-block mesh." ) + + nCleanCollocated, nColors = 0, 0 + if not skip_clean_collocated: + main, nCleanCollocated = __clean_collocated( main ) + + append_filter.Update() + clean = vtkCleanPolyData() + clean.SetInputConnection( append_filter.GetOutputPort() ) + clean.Update() + + painted_main, _ = __paintNodes( main, [ clean.GetOutput() ] ) + colored_main, nColors = __coloringNodes( painted_main ) + return polydata_to_ugrid( colored_main ), nCleanCollocated, nColors + + +def toSurfaceGen( hierachical_mesh: vtkUnstructuredGrid, attrs: tuple[ int, ...], skip_clean_collocated: bool, + skip_filter_volume_cells: bool ) -> tuple[ vtkUnstructuredGrid, int, int, int ]: + """Converts a single unstructured grid mesh to a surface mesh compatible with SurfaceGen by optionally filtering out volume cells, extracting surface cells that match the given attributes, optionally cleaning collocated points, and returning the resulting unstructured grid along with the number of points cleaned and cells filtered. + + Args: + hierachical_mesh: The input unstructured grid mesh. + attrs: A tuple of attribute values to filter surface cells. If empty, all surface cells are included. + skip_clean_collocated: If True, skips the step of cleaning collocated points. If False, collocated points will be cleaned and the number of points cleaned will be returned. + skip_filter_volume_cells: If True, skips the step of filtering out volume cells and extracting surface cells. If False, volume cells will be filtered out and surface cells matching the attributes will be extracted, and the number of cells filtered will be returned. + + Returns: + A tuple containing the converted surface mesh as a vtkUnstructuredGrid, the number of points cleaned from collocated points (if skip_clean_collocated is False) or 0 (if skip clean_collocated is True), and the number of cells filtered out as volume cells (if skip_filter_volume_cells is False) or 0 (if skip_filter_volume_cells is True). + """ + nCleanCollocated, nFilteredVolumeCells, nColors = 0, 0, 0 + if skip_filter_volume_cells: + main = hierachical_mesh + surfaces: list[ vtkUnstructuredGrid ] = [] + else: + main, surfs, nFilteredVolumeCells = _filterVolumeCells( hierachical_mesh, list( attrs ) ) + surfaces = [ surfs ] + + if not skip_clean_collocated: + main, nCleanCollocated = __clean_collocated( main ) + + painted_main, _ = __paintNodes( main, surfaces ) + setupLogger.info( f"Has Array faultNodes: {painted_main.GetPointData().HasArray('faultNodes')}" ) + setupLogger.info( + f"Range of faultNodes: {painted_main.GetPointData().GetArray('faultNodes').GetRange() if painted_main.GetPointData().HasArray('faultNodes') else 'N/A'}" + ) + colored_main, nColors = __coloringNodes( painted_main ) + return polydata_to_ugrid( colored_main ), nCleanCollocated, nFilteredVolumeCells, nColors + + +def meshAction( mesh: Union[ vtkMultiBlockDataSet, vtkUnstructuredGrid ], options: Options ) -> Result: + """Performs the conversion of the input mesh to a surface mesh compatible with SurfaceGen using the specified options, and returns the result containing the output file path, bounds, number of points and cells, and details about the cleaning and filtering steps. + + Args: + mesh: The input mesh to be converted, which can be either a vtkMultiBlockDataSet or a vtkUnstructuredGrid. + options: The options for the conversion, including attributes to filter, whether to skip cleaning collocated points, and whether to skip filtering volume cells. + + Returns: + A Result object containing the output file path, bounds, number of points and cells in the converted mesh, the attributes used for filtering, whether cleaning collocated points was skipped, whether filtering volume cells was skipped, and the number of points cleaned and cells filtered if those steps were performed. + + """ + if isinstance( mesh, vtkMultiBlockDataSet ): + converted, nCleanCollocated, nColors = meshDoctor_to_surfaceGen( mesh, options.attrs, + options.skipCleanCollocated ) + nFilteredVolumeCells = 0 + elif isinstance( mesh, vtkUnstructuredGrid ): + converted, nCleanCollocated, nFilteredVolumeCells, nColors = toSurfaceGen( mesh, options.attrs, + options.skipCleanCollocated, + options.skipFilterVolumeCells ) + else: + raise TypeError( f"Unsupported mesh type {type( mesh )}." ) + + return Result( outputMesh=converted, + bounds=converted.GetBounds(), + numPoints=converted.GetNumberOfPoints(), + numCells=converted.GetNumberOfCells(), + attrs=options.attrs, + skipCleanCollocated=options.skipCleanCollocated, + skipFilterVolumeCells=options.skipFilterVolumeCells, + nCleanCollocated=nCleanCollocated, + nFilterVolumeCells=nFilteredVolumeCells, + nColors=nColors ) + + +def action( vtuInputFile: str, options: Options ) -> Result: + """Reads a mesh from the input file, converts it to a surface mesh compatible with SurfaceGen using the specified options, and returns the result containing the output file path, bounds, number of points and cells, and details about the cleaning and filtering steps. + + Args: + vtuInputFile: The path to the input VTU file containing the mesh to be converted. + options: The options for the conversion, including attributes to filter, whether to skip cleaning collocated points, and whether to skip filtering volume cells. + + Returns: + A Result object containing the output file path, bounds, number of points and cells in the converted mesh, the attributes used for filtering, whether cleaning collocated points was skipped, whether filtering volume cells was skipped, and the number of points cleaned and cells filtered if those steps were performed. + """ + if vtuInputFile is None: + raise ValueError( "An input file must be provided." ) + + mesh = readMesh( vtuInputFile ) + result = meshAction( mesh, options ) + + setupLogger.info( f"Writing converted mesh to {options.meshVtkOutput.output}" ) + writeMesh( result.outputMesh, options.meshVtkOutput ) + + return result diff --git a/mesh-doctor/src/geos/mesh_doctor/actions/euler.py b/mesh-doctor/src/geos/mesh_doctor/actions/euler.py index b72504ab..8da86e29 100644 --- a/mesh-doctor/src/geos/mesh_doctor/actions/euler.py +++ b/mesh-doctor/src/geos/mesh_doctor/actions/euler.py @@ -52,6 +52,7 @@ class SurfaceComponent: numBoundaryEdges: int numNonManifoldEdges: int interpretation: str + nonManifoldEdgeEndpoints: tuple[ tuple[ int, int ], ...] = field( default_factory=tuple ) @dataclass( frozen=True ) @@ -225,10 +226,24 @@ def __interpretSurface( chi: int, boundaryEdges: int, nonManifoldEdges: int ) -> return f"open (chi={chi}, unusual)" +def _toGlobalEdgeId( mesh: vtk.vtkUnstructuredGrid, edge: tuple[ int, int ] ) -> tuple[ int, int ]: + """Return a global edge ID for a pair of point IDs.""" + p0, p1 = edge + ids = mesh.GetPointData().GetGlobalIds() + if ids is not None: + ids = vtk_to_numpy( ids ).astype( np.int64, copy=False ) + return ( ids[ p0 ], ids[ p1 ] ) + else: + setupLogger.warning( "No globalIds found. Falling back to local id for non-manifold edge detection.", + stacklevel=2 ) + return edge + + def __surfaceComponentsFromColored( colored: vtk.vtkUnstructuredGrid ) -> list[ SurfaceComponent ]: """Compute (V, E, F, chi, boundary, non-manifold) per RegionId of a colored 2D mesh.""" rid = vtk_to_numpy( colored.GetCellData().GetArray( "RegionId" ) ).astype( np.int64 ) cells = colored.GetCells() + # GetConnectivityArray / GetOffsetsArray require VTK 9+ conn = vtk_to_numpy( cells.GetConnectivityArray() ).astype( np.int64, copy=False ) off = vtk_to_numpy( cells.GetOffsetsArray() ).astype( np.int64, copy=False ) @@ -251,7 +266,8 @@ def __surfaceComponentsFromColored( colored: vtk.vtkUnstructuredGrid ) -> list[ E = len( edgeCount ) F = int( len( sel ) ) bE = sum( 1 for c in edgeCount.values() if c == 1 ) - nm = sum( 1 for c in edgeCount.values() if c > 2 ) + nmEdges = tuple( _toGlobalEdgeId( colored, ek ) for ek, c in edgeCount.items() if c > 2 ) + nm = len( nmEdges ) chi = V - E + F components.append( SurfaceComponent( componentId=int( regionId ), @@ -262,7 +278,8 @@ def __surfaceComponentsFromColored( colored: vtk.vtkUnstructuredGrid ) -> list[ eulerCharacteristic=chi, numBoundaryEdges=bE, numNonManifoldEdges=nm, - interpretation=__interpretSurface( chi, bE, nm ) ) ) + interpretation=__interpretSurface( chi, bE, nm ), + nonManifoldEdgeEndpoints=nmEdges ) ) return components diff --git a/mesh-doctor/src/geos/mesh_doctor/parsing/__init__.py b/mesh-doctor/src/geos/mesh_doctor/parsing/__init__.py index ecdb80d9..f25f20ec 100644 --- a/mesh-doctor/src/geos/mesh_doctor/parsing/__init__.py +++ b/mesh-doctor/src/geos/mesh_doctor/parsing/__init__.py @@ -19,6 +19,7 @@ ORPHAN_2D = "orphan2d" CHECK_INTERNAL_TAGS = "checkInternalTags" EULER = "euler" +CONVERT_MD2SG = "convertMD2SG" @dataclass( frozen=True ) diff --git a/mesh-doctor/src/geos/mesh_doctor/parsing/convertMD2SGParsing.py b/mesh-doctor/src/geos/mesh_doctor/parsing/convertMD2SGParsing.py new file mode 100644 index 00000000..06447e7a --- /dev/null +++ b/mesh-doctor/src/geos/mesh_doctor/parsing/convertMD2SGParsing.py @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. +# SPDX-FileContributor: GitHub Copilot, Jacques Franc +from __future__ import annotations +from argparse import _SubParsersAction +from typing import Any + +from geos.mesh_doctor.actions.convertMD2SG import Options, Result +from geos.mesh_doctor.parsing import CONVERT_MD2SG +from geos.mesh_doctor.parsing._sharedChecksParsingLogic import getOptionsUsedMessage +from geos.mesh_doctor.parsing.cliParsing import setupLogger, addVtuInputFileArgument +from geos.mesh.io.vtkIO import VtkOutput + +__ATTRS = "attrs" +__OUTPUT_FILE = "outputFile" +__SKIP_CLEAN = "skipCleanCollocated" +__SKIP_FILTER = "skipFilterVolumeCells" + + +def convert( parsedOptions: dict[ str, Any ] ) -> Options: + """Convert parsed command-line options to Options object. + + Args: + parsedOptions: Dictionary of parsed command-line options. + + Returns: + Options: Converted options object. + """ + return Options( attrs=tuple( parsedOptions.get( __ATTRS, [] ) ), + skipCleanCollocated=parsedOptions.get( __SKIP_CLEAN, False ), + skipFilterVolumeCells=parsedOptions.get( __SKIP_FILTER, False ), + meshVtkOutput=VtkOutput( output=parsedOptions.get( __OUTPUT_FILE ), isDataModeBinary=True ) ) + + +def fillSubparser( subparsers: _SubParsersAction[ Any ] ) -> None: + """Fill the argument parser for the convertMD2SG action. + + Args: + subparsers: Subparsers from the main argument parser. + """ + p = subparsers.add_parser( CONVERT_MD2SG, + help="Convert a mesh-doctor dataset to a SurfaceGen-compatible VTU file." ) + addVtuInputFileArgument( p ) + p.add_argument( '-z', + '--' + __ATTRS, + type=int, + nargs='+', + default=[], + help="[int ...]: Attributes to include when filtering surface cells." ) + p.add_argument( '--' + __OUTPUT_FILE, + type=str, + default="converted.vtu", + help="[string]: Optional output VTU file path." ) + p.add_argument( '--' + __SKIP_CLEAN, action='store_true', help="Skip the collocated node cleanup step." ) + p.add_argument( '--' + __SKIP_FILTER, + action='store_true', + help="Skip the surface/volume extraction step when input is a single VTU file." ) + + +def displayResults( options: Options, result: Result ) -> None: + """Display the results of the convertMD2SG action. + + Args: + options: The options used for the conversion. + result: The results of the conversion. + """ + setupLogger.results( getOptionsUsedMessage( options ) ) + setupLogger.results( "Converted mesh saved to: {0}".format( options.meshVtkOutput.output ) ) + setupLogger.results( f" Points: {result.numPoints:,}" ) + setupLogger.results( f" Cells: {result.numCells:,}" ) + setupLogger.results( f" Bounds: {result.bounds}" ) + setupLogger.results( + f" Skip clean collocated(npoints cleaned): {result.skipCleanCollocated} ({result.nCleanCollocated})" ) + setupLogger.results( + f" Skip filter volume cells(ncells removed): {result.skipFilterVolumeCells} ({result.nFilterVolumeCells})" ) + setupLogger.results( f" Number of connected components for faultNodes: {result.nColors}" ) diff --git a/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py b/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py index 1ad7dcfa..d6bbbf2e 100644 --- a/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py +++ b/mesh-doctor/src/geos/mesh_doctor/parsing/eulerParsing.py @@ -131,6 +131,13 @@ def __displaySurfaceGroup( g: SurfaceGroup ) -> None: setupLogger.results( " " + f"{c.componentId:>5} {c.numCells:>9,} {c.numVertices:>8,} {c.numEdges:>8,} " f"{c.numFaces:>8,} {c.eulerCharacteristic:>5} {c.numBoundaryEdges:>6,} " f"{c.numNonManifoldEdges:>4,} {c.interpretation}" ) + if c.nonManifoldEdgeEndpoints: + _MAX_DISPLAY = 10 + shown = c.nonManifoldEdgeEndpoints[ :_MAX_DISPLAY ] + setupLogger.results( " non-manifold edge endpoints (point id pairs): " + ", ".join( f"({a},{b})" + for a, b in shown ) + + ( f" … (+{len(c.nonManifoldEdgeEndpoints)-_MAX_DISPLAY} more)" + if len( c.nonManifoldEdgeEndpoints ) > _MAX_DISPLAY else "" ) ) if len( g.components ) > 1: setupLogger.results( f" WARNING: {len(g.components)} components — verify isolated cells" ) diff --git a/mesh-doctor/src/geos/mesh_doctor/register.py b/mesh-doctor/src/geos/mesh_doctor/register.py index 99675376..41d96a60 100644 --- a/mesh-doctor/src/geos/mesh_doctor/register.py +++ b/mesh-doctor/src/geos/mesh_doctor/register.py @@ -59,7 +59,7 @@ def registerParsingActions( parsing.FIX_ELEMENTS_ORDERINGS, parsing.GENERATE_CUBE, parsing.GENERATE_FRACTURES, parsing.GENERATE_GLOBAL_IDS, parsing.MAIN_CHECKS, parsing.NON_CONFORMAL, parsing.SELF_INTERSECTING_ELEMENTS, parsing.SUPPORTED_ELEMENTS, parsing.ORPHAN_2D, - parsing.CHECK_INTERNAL_TAGS, parsing.EULER ): + parsing.CHECK_INTERNAL_TAGS, parsing.EULER, parsing.CONVERT_MD2SG ): __HELPERS[ actionName ] = actionName __ACTIONS[ actionName ] = actionName diff --git a/mesh-doctor/tests/data/base_hexa_shift_2.vtu b/mesh-doctor/tests/data/base_hexa_shift_2.vtu new file mode 100644 index 0000000000000000000000000000000000000000..8b1febf6722508349cf12993c0241dfcd9693f19 GIT binary patch literal 736501 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
1f1N*k~qE`KrisCh7- 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 OxBsq>e(N`W?f(ZckpqYT literal 0 HcmV?d00001 diff --git a/mesh-doctor/tests/data/base_tetra_shift.vtm b/mesh-doctor/tests/data/base_tetra_shift.vtm new file mode 100644 index 00000000..72cfda3f --- /dev/null +++ b/mesh-doctor/tests/data/base_tetra_shift.vtm @@ -0,0 +1,7 @@ + + + + + + + diff --git a/mesh-doctor/tests/data/base_tetra_shift/domain_fracture_2_ids.vtu b/mesh-doctor/tests/data/base_tetra_shift/domain_fracture_2_ids.vtu new file mode 100644 index 00000000..bd71da8e --- /dev/null +++ b/mesh-doctor/tests/data/base_tetra_shift/domain_fracture_2_ids.vtu @@ -0,0 +1,55 @@ + + + + + + + AQAAAACAAABwEgAAMAMAAA==eJxd1Vd0CFYAh/GEBInYM0GqgpAYsXdpFVWqtLQ1g9iJPWMGbemw2mprFNUqNUqncU4nnUaH0dqxZ8QeidFz5MvLd1/+5/e93nvO7RwU8PA84HSRu8rd5O5yDzle7in3knvLCXIfua/cT+4vD5AHyoPkRDlJHiwPkYfKw+Th8gh5pDxKHi2PkcfK4+Rkebw8QZ4oT5Iny1PkFHmqPE2eLr8svyK/Ks+QZ8qvya/Lb8hvyrPk2fIcea48T35Lflt+R54vvyu/J78vL5AXyovkxfIH8hJ5qbxM/lBeLn8kfyyvkD+RV8qr5E/l1fIaea28Tv5MXi9vkD+Xv5C/lL+Sv5a/kTfKm+TN8hb5W/lwSJa/ox/B3+Mf2KP0VPZH+jH8Ez6Ot+ITeBs+iX/Gp/Av+DT+Ff/GnqGfZX+nn8N/4PN4O76Ad+CLeCdOw7vwJfwn/otNp19m/6Zfwf/gq3g3vob34Ot4L76B9+Gb+F/8H3uLfpvdT7+DD+AMfBBn4kP4Lj6M7+Ej+D4+ilOz3ws9IDRrjtED8XGcA5/AOfFJHIRP4WB8GufCZ/BZNjc9D3uOHoLP41B8AefFF3EYTsP58CWcH6fjy2wBekH2Cr0QvooL42u4CL6Oi+IbuBi+iYvjW/g2W4Jekr1DD8cZOAJn4lL4Li6N7+Ey+D6OxA9wQHDWPEIvywbSH8U5cDmcE0fhIFweB+MKOBeuiHPjPGw0vRIbQq+MQ3EMzotjcRiugvPhqjg/roYL4IJsdXocW4heAxfGNXERXAsXxbVxMVwHF8d1cQlckq1Hr8+G0xvgCNwQl8KNcGncGJfBTXBk9j2z2f9DOTlKLi9XkCvK0XIlubIcI8fKVeSqcjW5uhwn15BryrXk2nIdua5cT64vN5Abyo3kxnIT+TG5qdxMflx+Qm4uPym3kFvKreSn5Nby03Ibua38jNxOflZuL3eQn5OflzvKneQX5Bfll+TOche5q9xN7i73kOPlnnIvubecIPeR+8r95P7yAHmgPEhOlJPkwfIQeag8TB4uj5BHyqPk0fIYeaw8Tk6Wx8sT5InyJHmyPEVOkafK0+Tp8v+bFOsf + + + 1113.0004492 + + + 2930.1435118 + + + + + AQAAAACAAAA4CQAA/AEAAA==eJw11dViEAQAQFFiwMZGrmDEyAGC0g0iIAKSEtLSKN0tJaGoSEu3dHdIl9IonUp3t+QDx/tyPuHGivW+2IzDuAxgPMZnAgYyiAkZzBAmYmImYVImY3KGMozhjGAkUzAlo5iKqZmGaRnNdEzPDMzITMzMGGZhVmbjB8zOHPyQHzEnczE38zAv8zE/C7AgC7Ewi7Aoi7E4P2YJfsKSLMXS/JRl+BnLshzL83NWYEVWYmVWYVV+wWqszhqsyS9Zi7VZh3VZj/XZgF+xIRuxMZuwKZuxOVvwa37DlmzF1mzDtmzH9uzAjuzEzuzCruzG7uzBnuzF3vyWfdiX/difA/gdB3IQB3MIv+cPHMof+RN/5jD+wuEcwZEcxdEcw7H8leM4nhM4kZM4mVM4ldM4nTM4k7P4G2dzDudyHudzARdyERdzCZdyGZdzBVdyFVdzDddyHddzA3/nRm7iZm7hVm7jdu7gTu7ibv7BP7mHe7mP+3mAB3mIh/kX/+YRHuUxHucJnuQpnuYZnuU5nuc//JcXeJGXeJlXeJXXeJ03eJO3eJt3eJf3eJ8P+JCP+JhP+JTP+Jwv+B9f8hVf8w3f8v/xx2YcxmUA4zE+EzCQQUzIYIYwERMzCZMyGZMzlGEMZwQjmYIpGcVUTM00TMtopmN6ZmBGZmJmxvAdK1+CjQ== + + + + + AQAAAACAAAAwDAAAHwAAAA==eJztxaENAAAMArCF/4+ewHFDa5qr2LZt2/b8qo4DDQ== + + + AQAAAACAAAAwDAAAiQIAAA==eJw11dViEAQAQFEYMWB05+jO0bkRG6O7YXR3jhjdHUoqjQFKd2OQJiihdCihNCqNPHC4L+cTbqxY74rNAMZhXMZjfAYyARMyEYOYmEmYlMmYnCmYkqmYmmmYlumYnhmYkZmYmVkYzKzMxuzMwZzMxdzMw7zMx/wswIIsxMIswqIsxhAWZwmWZCmWZhmWZTmWZwVWZCWGMoyVWYVVWY3hjGB1RrIGa7IWa7MO67Ie67MBG7IRG7MJm7IZm7MFW7IVW7MNo9iW7dieHdiRndiZXdiV3didPdiTvdibfdiX/difAziQgziYQxjNoRzG4RzBGI7kKI7mGI7lOI7nBE7kJE7mFE7lNE7nDM7kLM7mHM7lPH7ADzmfC7iQi7iYS/gRP+ZSLuNyruBKruJqruEn/JSf8XOu5Tp+wS+5nhu4kZu4mVu4ldu4nTu4k7u4m3u4l/u4nwd4kIf4Fb/mN/yWh3mER3mMx3mC3/F7/sAf+RN/5kme4i/8lad5hmd5jr/xd57nBV7kJV7mFV7lNV7nDf7BP3mTt3ibd/gX/+Zd3uN9PuBDPuJjPuE//Jf/8Smf8Tlf8CVf8TXf8H++H39sBjAO4zIe4zOQCZiQiRjExEzCpEzG5EzBlEzF1EzDtEzH9MzAjMzEzMzCYGZlNmZnDuZkLuZmHuZlPuZnARZkIRZmERZlMYawOEuwJEuxNMuwLMuxPCuwIisxlGGszCqsymoMZwSrM5I1WJO1WJt1WJf1WJ8N2JCN2JhN2JTN2Jwt2JKt2JptGMW2bMf27MCO7MTO7MKu7Mbu7MGe7MXe7MO+7Mf+HMCBHMTBHMJoDuUwDucIxnAkR3E0x3Asx3E8J3AiJ/Eti52i1g== + + + + + AQAAAACAAACoGwAA2AMAAA==eJx1lj1MFVEQRq2pscUWa22VUlusqbXVFlupsaCBwgaaryExmiiJISIYxABRQZ8oKD+KKIgV1ro7vpiZj7PNhsPL3rtzzyZnYry5NDDR3scHbvdOX9kZHvv393/+uG/kcPDxPePx+ynj+e/6nGnjR4PNf+7D8x8a7+lM3rzU8wjWnTF+fmj02Un/E9jPrPGpv0/vTD6Ffc4Zv9Be87D/BePzJ/1/d/Qc3mvR+LV2oy/gfZeM7ww3C7+EOSwbv9W87s0VmM+q8TPt9Qrm5vxqu6HXME/nzS6HRt/AnNeMt2PrOI/5rxs/17xW31s4F+c3rjfXOzivjvFm1d5p53GO743/bpY9cR73DeOX24U/wLk7b1935CP4sGl8Zbm5nIcnW8bPti/8Cfxx3i479Bm82oa5OQ/fnMc8nYeHOzBn5+Gn85j/LnjrPJZ1Hj7vwXk5D92cxzl+Af+dx/k6j+/iK5y78/henIcP+/AdOQ9PnMf39Q38cR7fnfPw6gC8ch5eOQ+vnIdX38Er5+GV8/Dqh/G7rVfOwyvn4dUheOU8vHIeXh2BV87DK+fh1U/wynl45Ty8OgavnIdXzsOrX+CV8/Cqcv2bwx3gtaMEHSXoKL6f3lGCjhJ0lKCjuvfaUYKOEnSUoKMEHSXoKEFHCTpK0FGCjurea0cJOkrQUYKOEnSUoKMEHSXoKEFHCTpK0FGCjhJ0lKCjBB0l6ChBRwk6StBRgo7q3mtHCTpK0FGCjhJ0lKCjBB0l6ChBRwk6StBRgo4SdJSgowQd1b07P72jBB0l6ChBRwk6StBRgo4SdJSgowQdJegoQUcJOkrQUYKOEnSUoKMEHSXoKEFHCTpK0FGCjhJ0VPfu/PSOEnSUoKMEHSXoKEFHCTpK0FGCjhJ0lKCjBB0l6ChBRwk6StBRgo4SdJSsoy62831gHZX5mPHaUfn3U8brPvJzpo3XjsrPf2i8dlRed8Z47ai8n1njtaPyPueM147K+18wXjsqv9ei8dpR+X2XjNeOynNYNl47Ks9n1XjtqDw357Wj8jyd147Kc14zXjsqz3/deO2ofC7Oa0fl8+oYrx2Vz/G98dpR+Xw3jNeOyufuvHZU9mHTeO2o7MmW8dpR2R/ntaOyV9swN+e1o/I8ndeOynN2Xjsqz38XvHVeOyr7vAfn5bx2VD7HL8ZrR+XzdV47Kp+789pR2Yd947WjsifOa0dlf5zXjspeHYBXzmtHZa+c147KXjmvHZW9+mG8dlT2ynl4dQheOa8dlb06Aq+c147KXv0Er5zXjspeHYNXzmtHZa+8i7od9Qfhzwss + + + 0.059371710435 + + + 0.99252204006 + + + + + + + AQAAAACAAACQJAAAAgcAAA==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 + + + AQAAAACAAAAwDAAAlwIAAA==eJwtxVlgCAQAAFDmmixX5Mo95FjkWLRF7vuKMEeGyNUc0WTO3OfGWDnKrZWQ3EOpue8ULbSaVkpuUpPiY+/9vGxZMuV0bgc5nwu6sIu6hEu5rINdyVUc4hqu5VDXc7gbuJGbuoVbu507urO7urt7OdL9PMCDPNTDPNKjPcYxnuDJnuoZnu15jnO8E7zEy73Cq73Oid7gTd7ibd7pJO/zfif7kI/6hE/7rM85xRed6jSn+4qv+rpv+a7vO8MP/cgBWTPP4UDncV4XcCEXcXGXdBmXd0VXdjVXd03XcV2Hub4buombu5XbuoM7uYsj3NO93df9PdBDHOURHuVoj/V4T/IUT/csz3WsF3qx3/cyf+hVXuuP/Ik3+jNv9Q7v9l5/6a990Ed83Kf8jb/z977gH/2zf/Fv/sPXfNN3/Jf/8b/+31kDMs/uXH7CTzq/n/LTLuZnXNrlXMHPuqqf8/Ou7Rf8ol/yy27sZm7pNm7vV/yqu7mHX3Mfv+43PNhverjf8tt+x+M80e96mmd6jud7gRf5PS/1B17pNV7vj/2pN/tzb/cu7/EX/soHfNjHfNJn/K3P+wdf8k++7F/9u//0Dd/2Pf/tB/7PWbLJOZ3bQc7ngi7soi7hUi7rYFdyFYe4hms51PUc7gZu5KZu4dZu547u7K7u7l6OdD8P8CAP9TCP9GiPcYwneLKneoZne57jHO8EL/Fyr/Bqr3OiN3iTt3ibdzrJ+7zfyT7koz7h0z7rc07xRac6zem+4qu+7lu+6/vO8EM/ckD2zHM40Hmc1wVcyEVc3CVdxuVd0ZVdzdVd03Vc12Gu74Zu4uZu5bbu4E7u4gj3dG/3dX8P9BBHeYRHOdpjPd6TPMXTPctzHeuFfgyLILtF + + + AQAAAACAAACGAQAADQAAAA==eJxjZx8FAw8AJtEKqw== + + + + + diff --git a/mesh-doctor/tests/data/base_tetra_shift/domain_ids.vtu b/mesh-doctor/tests/data/base_tetra_shift/domain_ids.vtu new file mode 100644 index 00000000..958a15a5 --- /dev/null +++ b/mesh-doctor/tests/data/base_tetra_shift/domain_ids.vtu @@ -0,0 +1,44 @@ + + + + + + + AQAAAACAAADwUQAAnA8AAA==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 + + + + + AgAAAACAAADQdQAA5Q8AADAPAAA=eJyNnD2MXWcVRVOnhja0oYYWUkIb6tTQhja0SR0KN6GgyTSnGSliJHKkyMLEyBhNrAEbxgYb/BfHdvyTJtRk5vJxtfc+S+I1T15+uu/e8+0nLW0d+5VX/r/XL751+KN777z7xswvBP/4tfe+fPPjX8PnD4L/6oOzV8F1DoM/e/Psbz6C6x8Ff/X0w7d/8Opv4Xs7+Hffev/3X7/+CdzPxeAH31z99MPfwX1eCv6989encP+Xg3/69evf3NEf4LmuBP/J+Y3+EZ73avB775x98Z9gDsfBf372uG9/BvO5Fnx7ncDckv/4/Ib+DPNMfnaXb73/F5jz9eDnYztNvs3/RvDvnD3Wa3+Fc0n+s5+evf4G53Ua/Oxbv3WYfDvHm8H/ffa1XyffzvdW8B+ef/Hf4dyTnz/ue/+APNwO/tnx2Sv5lpM7wb99/sD/hPwkP//at/4FuboLc0u+5S35Ns/kWw7vwZyTb/lMvs3/PuQ2+fa1ybc8P4DzSr7FLfl2jg8h/8m3802+/S4+h3NPvv1ekm95eAS/o+RbTpJvv68vID/Jt99d8u31GHKVfMtV8i1XybdcPYFcJd9ylXzL1dPgvzzPVfItV8m3XH0JuUq+5Sr5lqtnkKvkW66Sb7l6DrlKvuUq+ZarF5Cr5Fuukm+5egm5Sr7lKvn2+up/fDvvDyAHH7zh3qX8QnD3Lv38QXD3Lr3OYXD3Lr3+UXD3Lv3eDu7epfdzMbh7l97npeDuXXr/l4O7d+lzXQnu3qXPezW4e5fO4Ti4e5fO5xrk6gTmlty9S+eZ3L1L53w9uHuXzv9GcPcuPZfk7l16XqfB3bv0HG8Gd+/S870V3L1Lzz25e5fm4XZw9y7NyZ3g7l2an+TuXZqruzC35O5dOs/k7l065+TuXTr/+5Db5O5dmucHcF7J3bv0HB8Gd+/S803u3qXnnty9S/PwKLh7l+YkuXuX5ie5e5fm6jHkKrl7l+YquXuX5iq5e5fm6mlw9y7NVXL3Ls1VcvcuzdUzyFVy9y7N1XPIVXL3Ls3VC8hVcvcuzdVLyFVy9y7NlXtX9k2Lz95V4F0F3lXgXQXeVeBdBd5V4F0F3lXgXQXeVeBdBd5V4F0F3lXgXQXeVeBdBd5V4F0F3lXgXQXeVeBd61zdrwq8q8C7CryrwLsKvKvAuwq8q8C7CryrwLsKvKvAuwq8q8C7CryrwLsKvKvAuwq8q8C7CryrwLsKvKvAu1au3JcKvKvAuwq8q8C7CryrwLsKvKvAuwq8q8C7CryrwLsKvKvAuwq8q8C7CryrwLsKvKvAuwq8q8C7CryrwLtWrtyXCryrwLsKvKvAuwq8q8C7CryrwLsKvKvAuwq8q8C7CryrwLsKvKvAuwq8q8C7CryrwLsKvKvAuwq8q8C7Vq527/r++dx/E961uHuX8gvB3bv08wfB3bv0OofB3bv0+kfB3bv0ezu4e5fez8Xg7l16n5eCu3fp/V8O7t6lz3UluHuXPu/V4O5dOofj4O5dOp9rkKsTmFty9y6dZ3L3Lp3z9eDuXTr/G8Hdu/Rckrt36XmdBnfv0nO8Gdy9S8/3VnD3Lj335O5dmofbwd27NCd3grt3aX6Su3dpru7C3JK7d+k8k7t36ZyTu3fp/O9DbpO7d2meH8B5JXfv0nN8GNy9S883uXuXnnty9y7Nw6Pg7l2ak+TuXZqf5O5dmqvHkKvk7l2aq+TuXZqr5O5dmqunwd27NFfJt1x9CblK7t6luXoGuUru3qW5eg65Su7epbl6AblK7t6luXoJuUru3qW58r4r95wWn/uuhr6roe9q6Lsa+q6Gvquh72rouxr6roa+q6Hvaui7Gvquhr6roe9q6Lsa+q6Gvquh72rouxr6roa+q6Hvaui71ny912rouxr6roa+q6Hvaui7Gvquhr6roe9q6Lsa+q6Gvquh72rouxr6roa+q6Hvaui7Gvquhr6roe9q6Lsa+q6Gvquh71q58p6qoe9q6Lsa+q6Gvquh72rouxr6roa+q6Hvaui7Gvquhr6roe9q6Lsa+q6Gvquh72rouxr6roa+q6Hvaui7Gvquhr5r5cp7qoa+q6Hvaui7Gvquhr6roe9q6Lsa+q6Gvquh72rouxr6roa+q6Hvaui7Gvquhr6roe9q6Lsa+q6Gvquh72rouxr6rpWrr/57zrzXtf68vGvmF4Iv75o/fxB8edd8ncPgy7vm6x8FX941f28HX94138/F4Mu75vu8FHx513z/l4Mv75qf60rw5V3z814NvrxrnsNx8OVd83yuQa5OYG7Jl3fN80y+vGue8/Xgy7vm+d8IvrxrPpfky7vm8zoNvrxrPsebwZd3zed7K/jyrvncky/vmvNwO/jyrjknd4Iv75rzk3x515yruzC35Mu75nkmX941zzn58q55/vcht8mXd815fgDnlXx513yODyH/yZd3zb+Lz+Hcky/vmvPwCH5HyZd3zb+vLyA/yZd3zbl6DLlKvrxrzlXy5V1zrpIv75pz9TT48q45V8mXd825Sr68a87VM8hV8uVdc66eQ66SL++ac/UCcpV8edecq5eQq+TLu+Zc7d41n//O3buUXwju3qWfPwju3qXXOQzu3qXXPwru3qXvHdy9S+/nYnD3Lr3PS8Hdu/T+Lwd379LnuhLcvUvfrwZ379I5HAd379L5XAu+peoE5pbcvUvnmdy9S+d8Pbh7l87/RnD3Lj2X5O5del6nwd279BxvBnfv0vdbwd279NyTu3dpHm4Hd+/SnNwJ7t6l+Unu3qW5ugtzS+7epfNM7t6l78ndu3T+94O7d+m5JHfv0vNK7t6l5/gwuHuXnm9y9y499+TuXZqHR8HduzQnyd27ND/J3bs0V4+Du3dprpK7d2mungTf3pO7d2mungZ379JcJXfv0lwld+/SXD0L7t6luUru3qW5Su7epbl6Edy9S3OV3L1Lc5XcvUtz5d6V/55w8dm7CryrwLsKvKvs3a/j3lXgXQXeVeBd+16989m7CryrwLsKvKvAuwq8q8C7CryrwLv2vXrns3cVeFeBdxV4V4F3FXhXgXcVeFeBdxV4V4F3FXhXgXcVeFeBdxV4V4F3FXhXgXfte/XOZ+8q8K4C7yrwrgLvKvCuAu8q8K4C7yrwrgLvKvCuAu8q8K59r37OZ/LZuwq8q8C7CryrwLsKvKvAuwq8q8C7CryrwLsKvKvAuwq8q8C7CryrwLsKvKvAuwq8q8C7CryrwLv2vfo5V8ln7yrwrgLvKvCuAu8q8K4C7yrwrgLvKvCuAu8q8K4C7yrwrgLvKvCuAu8q8K4K7/K9eufuXb5X79y9y/fqnWvu/DqHwd27fK/euXuX79U7d+/yvXrn7l2+V+/cvcv36p27d/levXP3Lt+rd+7e5Xv1zt27fK9+ztUJzC25e5fv1Tt37/K9eufuXb5X79y9y/fqnbt3+V69c/cu36t37t7le/XO3bt8r965e5fv1Tt37/K9eufuXb5X79y9y/fq57kld+/yvfo5h/dgzsndu3yvfs5tcvcu36ufzyu5e5fv1c/5T+7e5Xv187knd+/yvfr5d5Tcvcv36uf8JHfv8r36OVfJ3bt8r37O1RPIVXL3Lt+rd+7e5Xv1c66+hFwld+/yvfo5V8ndu3yvfs5Vcvcu36ufc5Xcvcv36udcJXfv8r169bH8f6wWn/uuhr6roe9q6LuW93jf1dB3NfRdDX1XQ9/V9r7zue9q6Lsa+q6Gvquh72rouxr6roa+q6HvWs/pfVdD39XQdzX0XQ19V0Pf1dB3NfRdDX1XQ9/V0Hc19F0NfVdD39XQdzX0XQ19V0Pf1dB3NfRd63y972rouxr6roa+q6Hvaui7Gvquhr6roe9q6Lsa+q6Gvquh72rouxr6rjXn5HPf1dB3NfRdDX1XQ9/V0Hc19F0NfVdD39XQdzX0XQ19V0Pf1dB3NfRdDX1XQ9/V0Hc19F0NfVdD39XQdzX0XQ1918pV8rnvaui7Gvquhr6roe9q6Lsa+q6Gvquh72rouxr6roa+q6Hvaui7Gvquhr6roe9q6Lsa+i7fq+f/t2v9Wb3L+YXg6l3++YPg68/zdQ6Dq3f59Y+Cq3flv5t0rt7l93MxuHqX3+el4Opdfv+Xg6t3+XNdCa7e5c97Nbh6l8/hOLh6l8/nGuTqBOaWXL3L55lcvcvnfD24epfP/0Zw9S4/l+TqXX5ep8HVu/wcbwZX7/LzvRVcvcvPPbl6l+fhdnD1Ls/JneDqXZ6f5Opdnqu7MLfk6l0+z+TqXT7n5OpdPv/7kNvk6l2e5wdwXsnVu/wcH0L+k6t3+e/iczj35OpdnodHwdW7PCfJ1bs8P8nVuzxXjyFXydW7PFfJ1bs8V8nVuzxXT4Ord3mukqt3ea6Sq3d5rp5BrpKrd3munkOukqt3ea5eQK6Sq3d5rl5CrpKrd3mudu9aXrb+xrl7l/ILwd279PMHwd279DqHwd279PpHwd279Huzb3Pv0vu5GNy9S+/zUnD3Lr3/y8Hdu/S5rgR379LnvRrcvUvncBzcvUvncy34lqoTmFty9y6dZ3L3Lp3z9eDuXTr/G8Hdu/Rckrt36XmdBnfv0nO8Gdy9S8/3VnD3Lj335O5dmofbwd27NCd3grt3aX6Su3dpru7C3JK7d+k8k7t36ZyTu3fp/O8Hd+/Sc0nu3qXnldy9S8/xYXD3Lj3f5O5deu7J3bs0D4+Cu3dpTpK7d2l+krt3aa4eB3fv0lwld+/SXD0Jvr0nd+/SXD0N7t6luUru3qW5Su7epbl6Fty9S3OV3L1Lc5XcvUtz9SK4e5fmKrl7l+YquXuX5sq9i/un2bt8r37ns3f5Xr17Vr7P3uV79X599y7fq/fvce/yvXq/H/cu36v3+3Tv8r16v3/3Lt+r9+dy7/K9en9e9y7fq/c5uHf5Xr3Px73L9+p9bsln7/K9+p3P3uV79Tufvcv36nc+e5fv1e989i7fq9/57F2+V7/z2bvWu3uX79X7uSefvcv36nc+e5fv1e989i7fq9/57F2+V+9zSz57l+/Vew7do9Z78tm7fK/ec5t89i7fq/fzSj57l+/Ve/6Tz97le/V+7sln7/K9ev8dJZ+9y/fqPT/JZ+/yvXrPVfLZu3yv3nPlHrXek8/e5Xv1O5+9y/fqPVfuUb5X77lKPnuX79V7rpLP3uV79Z6r5LN3+V695yr57F2+V++5Su7eNe/Vl+3FvRufd++a9+r98wfB/f7mvfqyPcOP4PpHwd275r36sv3AT+B+LgZ375r36sv2GD+F+78c3L1r3qvfuXvXvFe/c/euea++bL/xM5jPNcjVCcwtuXvXvFe/c/euea9+5+5d8179zt275r36nbt3zXv1O3fvmvfqd+7eNe/V79y9a96r37l717xXv3P3rnmvfufuXfNe/c7du+a9ep/bzv8DnI84jXichZ09jF5XFUVTpw4ttKFO2uAS2lCnhja0oSU1FG5MQcM0pxkJYYlcCVk4NjKDJpZhDOMBD0zGP3Hs8TgN1GTm5uZlr322mObJK5/ez7n7fVk677v3vf3Wxd9vr+y8/87rh78+ufLLaxd/deXtr/h/33zv5x//x/kPPv3gjd3vf2p8/tv52bsfffvDF86/9+VR33/n1PiXB/3yyM7nYZ1fbt56ZPzDFxcHdn5x1NcPnX/34nLffGz8pxeH/cD5J/sXf86/c3ngJ8Z/clHm953furjcj51/6+Kwbzw1/uMfXfw5v6jyux85vyzz658Zv7zc95xfHnbX+WuXf8+M//DywM5nrpzPXDmfufo85Mr5zJXzmavnxn9xmSvnM1fOZ65ehFw5n7lyPnN1FnLlfObK+czVy5Ar5zNXzmeuzkOunM9cOZ+5ehVy5XzmyvnM1Rdf87kdX/HXXiOfdfuZfX7yq8bn+f8qfH7HuP6b+9k1PnP4m7D/68Zn3X4Xjsvjj6/y8/twPjeMz/vuD+E8bxqfOb8Vzv+28ZmrP4brumN8fj/8KVzvnvF5P/451GHf+Mz/J6E+d0Ou7oW6OZ/fS38J9XQ+///111DnA+Pze8P5rP994/N74G9hXJzP+/rvYbwOjc/71PkcxwfG5/e587k9Mj6/n/8Rxt35/L79Z8jDQ+Pz+9P5zMmx8fl9+K+QH+fz++3fIVcnoW7O6V1aT+f0Lq2zc3qX1v/UOL1Lx8X5zPMj4/Quzblzepfm3zm9S++LJ8bpXXq/OKd36X3knN6l99dnxuldet85p3dprpzTuzRXzuldmivn9C7N1XPj9C7NlXN6l+bKOb1Lc3VmnN6luXJO79JcOad3aa7OjdO7NFfO6V2aK+f0Ls3VF/Cw7Y98eVfPrxpf3tV/fsf4Or9+P7vGl3f1+79ufHlXf9xhfHlXfz43jC/v6s/zpvHlXf353za+vKu/rjvGl3f117tnfHlXX4d948u7+vrcDbm6F+rmfHlXX0/ny7v6Oh8YX97V1/++8eVd/bg4X97Vj9eh8eVd/Tg+ML68qx/fI+PLu/pxd768q8/DQ+PLu/qcHBtf3tXnx/nyrj5XJ6Fuzpd39fV0vryrr7Pz5V19/U9Dbp0v7+rz/CiMl/PlXf04Pg75d768q78vnoRxd768q8/D03AfOV/e1d9fn4X8OF/e1efqWciV8+Vdfa6cL+/qc+V8eVefq+fGl3f1uXI+c/Ui5Mr58q4+V2chV86Xd/W5ehly5Xx5V5+r85Ar58u7+ly9Crlyvryrz9XmXXO8r33938npXcqvGqd36ed3jNO7dD+7xulduv/rxuldetxhnN6l53PDOL1Lz/Om8ZnzW+H8bxund+l13TFO79Lr3TNO79I67Bund2l97oZc3Qt1c07v0no6p3dpnQ+M07u0/veN07t0XJzTu3S8Do3Tu3QcHxind+n4Hhmnd+m4O6d3aR4eGqd3aU6OjdO7ND/O6V2aq5NQN+f0Lq2nc3qX1tk5vUvrfxpy65zepXl+FMbLOb1Lx/FxyL9zepfeF0/CuDund2kenhqnd2lOnNO7ND/O6V2aq2chV87pXZor5/QuzZVzepfm6rlxepfmyvnM1YuQK+f0Ls3VWciVc3qX5uplyJVzepfm6jzkyjm9S3P1KuTKOb1Lc0XvquBdFbyrgndV8K4K3rU9X+z3Q++q4F0VvKuCd239P/Leuyp4VwXvquBdFbyrgndV8K4K3lXBu9b10rsqeFcF76rgXRW8q4J3VfCuCt5VwbsqeFcF76rgXRW8q4J3VfCuCt5VwbsqeFcF76rgXWt86V0VvKuCd1XwrgreVcG7KnhXBe+q4F0VvKuCd1XwrgreVcG7KnjXqrPz3rsqeFcF76rgXRW8q4J3VfCuCt5VwbsqeFcF76rgXRW8q4J3VfCuCt5VwbsqeFcF76rgXRW8q4J3VfCuCt61cuW8964K3lXBuyp4VwXvquBdFbyrgndV8K4K3lXBuyp4VwXvquBdFbyrgndV8K4K3lXBu8q8a23pXWtL71J+1Ti9Sz+/Y5zepfvZNU7v0v1fN07v0uMO4/QuPZ8bxuldep43jV9uvuFdev63jdO79LruGKd36fXuGad3aR32jdO7tD53jdO7tG7O6V1aT+f0Lq3zgXF6l9b/vnF6l46Lc3qXjtehcXqXjuMD4/QuHd8j4/QuHXfn9C7Nw0Pj9C7NybFxepfmxzm9S3N1EurmnN6l9XRO79I6O6d3af1PjdO7dFycX26+4V06Xs7pXTqOj43Tu3R8ndO7dNyd07s0D0+N07s0J87pXZof5/QuzdUz4/QuzZVzepfm6nPj9C7NlXN619rSuzRXzi833/AuzZVzepfm6sw4vUtz5ZzepblyTu/SXJ0bp3dprpzTuzRXzuldmiv2u0bod43Q7xqh3zVCv2uEftcw79L9sN81Qr9rhH7XCP2uYd61/t33u0bod43Q7xqh3zVCv2uEftcI/a4R+l0j9LuGedfifb9rhH7XCP2uEfpdI/S7Ruh3jdDvGqHfNUK/a4R+1wj9rhH6XSP0u0bod43Q7xqh3zVCv2uEftcI/a7td/Xkfb9rhH7XCP2uEfpdI/S7Ruh3jdDvGqHfNUK/a4R+1wj9rhH6XSP0u0bod22/q+/z6bzvd43Q7xqh3zVCv2uEftcI/a4R+l0j9LtG6HeN0O8aod81Qr9rhH7XCP2uEfpdI/S7Ruh3jdDvGqHfNUK/a4R+1wj9rhH6Xdvv6vtcOe/7XSP0u0bod43Q7xqh3zVCv2uEftcI/a4R+l0j9LtG6HeN0O8aod81Qr9rhH7XCP2uEfpdI/S7tt/V07fWH7nOZyS/alznM/LzO8aXd/X72TWu8xm5/+vGdT4jjzuM63xGns8N4zqfked50/jM+a1w/reN63xGXtcd4zqfkde7Z1znM7IO+8Z1PiPrczfk6l6om3Odz8h6Otf5jKzzgXGdz8j63zeu8xk5Ls51PiPH69C4zmfkOD4wrvMZOb5HxnU+I8fduc5nZB4eGtf5jMzJsXGdz8j8ONf5jMzVSaibc53PyHo61/mMrLNznc/I+p+G3DrX+YzM86MwXs51PiPH8XHIv3Odz8j74kkYd+c6n5F5eGpc5zMyJ851PiPz41znMzJXz0KunOt8RubKuc5nZK6c63xG5uq5cZ3PyFw5X97V58q5zmdkrs5CrpzrfEbm6mXIlXOdz8hcnYdcOdf5jMzVq5Ar5zqfkbnavGtur2H8N07vUn7VOL1LP79jnN6l+9k1Tu/S/V83Tu/S43I9iWtX6F16PjeM07v0PG8ap3fp+d82Tu/S67pjnN6l17tnnN6lddg3Tu/S+tw1PlN1L9TNOb1L6+mc3qV1PjBO79L63zdO79JxcU7v0vE6NE7v0nF8YJzepeN7ZJzepePunN6leXhonN6lOTk2Tu/S/Dind2muTkLdnNO7tJ7O6V1aZ+f0Lq3/qXF6l46Lc3qXjpdzepeO42Pj9C4dX+f0Lh135/QuzcNT4/QuzYlzepfmxzm9S3P1zDi9S3PlnN6lufrc+Nw6p3dprp4bp3dprpzTuzRXzuldmqsz4/QuzZVzepfmyjm9S3N1bpzepblyTu/SXDmnd2mu6F0VvKuCd1XwrgreVcG7fN0u3Q+9q4J3VfCuCt6V1/HqvauCd1XwrgreVcG7KnhXBe+q4F0VvGv7XT15710VvKuCd1XwrgreVcG7KnhXBe+q4F0VvKuCd1XwrgreVcG7KnhXBe+q4F0VvKuCd22/qyfvvauCd1XwrgreVcG7KnhXBe+q4F0VvKuCd1XwrgreVcG7KnjX9rv6Pp/Oe++q4F0VvKuCd1XwrgreVcG7KnhXBe+q4F0VvKuCd1XwrgreVcG7KnhXBe+q4F0VvKuCd1XwrgreVcG7tt/V97ly3ntXBe+q4F0VvKuCd1XwrgreVcG7KnhXBe+q4F0VvKuCd1XwrgreVcG7KnhXBe8q8y793Zeva0XvUn7VOL1LP79jXHPH/ewap3fp/q8bp3f9v9950bv0fG4Yp3fped40Tu/S879tnN6l13XHOL1Lr3fPOL1L67BvnN6l9bkbcnUv1M05vUvr6ZzepXU+ME7v0vrfN07v0nFxTu/S8To0Tu/ScXxgnN6l43tknN6l4+6c3qV5eGic3qU5OTZO79L8OKd3aa5OQt2c07u0ns7pXVpn5/Qurf9pyK1zepfm+VEYL+f0Lh3Hx8bpXTq+zuldOu7O6V2ah6fG6V2aE+f0Ls2Pc3qX5upZyJVzepfmyjm9S3PlnN6luXpunN6luXI+c/Ui5Mo5vUtzdRZy5Zzepbl6GXLlnN6luToPuXJO79JcvQq5ck7v0lyx35V/99T3u7he/cb7fhfXq/c+U78f9ru4Xj33z34X16vncX3b97u4Xv3G+34X16vf+Mw5+11cr37jfb+L69VvvO93rS37XVyvnnVgv4vr1bM+7HdxvXrWzXnf7+J69Rvv+11cr37jfb+L69VvvO93cb36jff9Lq5Xv/G+38X16jfe97vWlv0urlfPcXfe97u4Xv3G+34X16vfeN/v4nr1G+/7XVyvnnVz3ve7uF49c8j+1do67/tdXK+euXXe97u4Xj3Hy3nf7+J69cy/877fxfXqOe7O+34X16vnfeS873dxvXrmx3nf7+J69cyV877fxfXqmSv2r9bWed/v4nr1G+/7XVyvnrli/4rr1TNXzvt+F9erZ66c9/0urlfPXDnv+11cr565ct73u7hePXPlXL1re87Xv/eHzw35eb73J2/Vozbev/eHzw03rh6Vn1Oubf/eHz433Hj/3h8+N9y4ehTPn+/94XNDXhff+8PnhrzePePqUawD3/vD54asD9/7w+eGrJvz/r0/fG64cfUo1vnAuHoU68/3/vC5IcfFef/eHz433Lh6FMeR7/fhc0OO75Hx/r0/fG64cfUo5oHv9+FzQ+aE7/3hc0Pmx7l6FHPF9/XwuSHz5rx/7w+fG7LOzvv3/vC5IXPrXD2KeeZ7fPjckDl33r/3h88NOb7O+/f+8Lkh7xfn/Xt/+NyQOXGuHsX8OFePYq74vh4+N2SunPfv/eFzQ+bKef/eHz433Hj/3h8+N2Su+B4fPjdkrpz37/3hc0Pmynn/3h8+N2SunPfv/eFzQ+bKuXoUc/Xqyv8AleogWQ== + + + 0 + + + 1.0099504938 + + + + + + + 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YZc99vnDAYccccwJp5xxzgWXXHHNDbfccc8DjzzxzAuvvPHOB5988c3Pn4HOv/9LgEGGGGaEUcYY5zcTTDLFNDPMMsc8CyyyxDIrrLLGOhtsssU2O+yyxz5/OOCQI4454ZQzzrngkiuuueGWO+554JEnnnnhlTfe+eCTL7754dc/+CXAIEMMM8IoY4zzmwkmmWKaGWaZY54FFllimRVWWWOdDTbZYpsddtljnz8ccMgRx5xwyhnnXHDJFdfccMsd9zzwyBPPvPDKG+988MkX3/zw6//7JcAgQwwzwihjjPObCSaZYpoZZpljngUWWWKZFVZZY50NNtlimx122WOfPxxwyBHHnHDKGedccMkV19xwyx33PPDIE8+88Mob73zwyRff/PDPQH+AQYYYZoRRxhjnNxNMMsU0M8wyxzwLLLLEMiusssY6G2yyxTY77LLHPn844JAjjjnhlDPOueCSK6654ZY77nngkSeeeeGVN9754JMvvvn5s8jxD38JMMgQw4wwyhjj/GaCSaaYZoZZ5phngUWWWGaFVdZYZ4NNtthmh1322OcPBxxyxDEnnHLGORdccsU1N9xyxz0PPPLEMy+88sY7H3zyxTc//PpHvwQYZIhhRhhljHF+M8EkU0wzwyxzzLPAIksss8Iqa6yzwSZbbLPDLnvs84cDDjnimBNOOeOcCy654pobbrnjngceeeKZF155450PPvnimx9+/eNfAgwyxDAjjDLGOL+ZYJIppplhljnmWWCRJZZZYZU11tlgky222WGXPfb5wwGHHHHMCaeccc4Fl1xxzQ233HHPA4888cwLr7zxzgeffPHND/8s8gUYZIhhRhhljHF+M8EkU0wzwyxzzLPAIksss8Iqa6yzwSZbbLPDLnvs84cDDjnimBNOOeOcCy654pobbrnjngceeeKZF155450PPvnim58/C5z/5JcAgwwxzAijjDHObyaYZIppZphljnkWWGSJZVZYZY11Nthki2122GWPff5wwCFHHHPCKWecc8ElV1xzwy133PPAI08888Irb7zzwSdffPPDr3/6S4BBhhhmhFHGGOc3E0wyxTQzzDLHPAssssQyK6yyxjobbLLFNjvsssc+fzjgkCOOOeGUM8654JIrrrnhljvueeCRJ5554ZU33vngky+++eHXP/slwCBDDDPCKGOM85sJJplimhlmmWOeBRZZYpkVVlljnQ022WKbHXbZY58/HHDIEceccMoZ51xwyRXX3HDLHfc88MgTz7zwyhvvfPDJF9/88M8Cf4BBhhhmhFHGGOc3E0wyxTQzzDLHPAssssQyK6yyxjobbLLFNjvsssc+fzjgkCOOOeGUM8654JIrrrnhljvueeCRJ5554ZU33vngky+++eHXP/8lwCBDDDPCKGOM85sJJplimhlmmWOeBRZZYpkVVlljnQ022WKbHXbZY58/HHDIEceccMoZ51xwyRXX3HDLHfc88MgTz7zwyhvvfPDJF9/88Otf/BJgkCGGGWGUMcb5zQSTTDHNDLPMMc8CiyyxzAqrrLHOBptssc0Ou+yxzx8OOOSIY0445YxzLrjkimtuuOWOex545IlnXnjljXc++OSLb3749S9/CTDIEMOMMMoY4/xmgkmmmGaGWeaYZ4FFllhmhVXWWGeDTbbYZodd9tjnDwcccsQxJ5xyxjkXXHLFNTfccsc9DzzyxDMvvPLGOx988sU3P/wT3BNgkCGGGWGUMcb5zQSTTDHNDLPMMc8CiyyxzAqrrLHOBptssc0Ou+yxzx8OOOSIY0445YxzLrjkimtuuOWOex545IlnXnjljXc++OSLb3749a9+CTDIEMOMMMoY4/xmgkmmmGaGWeaYZ4FFllhmhVXWWGeDTbbYZodd9tjnDwcccsQxJ5xyxjkXXHLFNTfccsc9DzzyxDMvvPLGOx988sU3P/z6178EGGSIYUYYZYxxfjPBJFNMM8Msc8yzwCJLLLPCKmuss8EmW2yzwy577POHAw454pgTTjnjnAsuueKaG265454HHnnimRdeeeOdDz754psffv2bXwIMMsQwI4wyxji/mWCSKaaZYZY55llgkSWWWWGVNdbZYJMtttlhlz32+cMBhxxxzAmnnHHOBZdccc0Nt9xxzwOPPPHMC6+88c4Hn3zxzQ//BPYFGGSIYUYYZYxxfjPBJFNMM8Msc8yzwCJLLLPCKmuss8EmW2yzwy577POHAw454pgTTjnjnAsuueKaG265454HHnnimRdeeeOdDz754psffv3bXwIMMsQwI4wyxji/mWCSKaaZYZY55llgkSWWWWGVNdbZYJMtttlhlz32+cMBhxxxzAmnnHHOBZdccc0Nt9xxzwOPPPHMC6+88c4Hn3zxzQ+//t0vAQYZYpgRRhljnN9MMMkU08wwyxzzLLDIEsussMoa62ywyRbb7LDLHvv84YBDjjjmhFPOOOeCS6645oZb7rjngUeeeOaFV95454NPvvjmh1///pcAgwwxzAijjDHObyaYZIppZphljnkWWGSJZVZYZY11Nthki2122GWPff5wwCFHHHPCKWecc8ElV1xzwy133PPAI08888Irb7zzwSdffPPDP0G9AQYZYpgRRhljnN9MMMkU08wwyxzzLLDIEsussMoa62ywyRbb7LDLHvv84YBDjjjmhFPOOOeCS6645oZb7rjngUeeeOaFV95454NPvvjmh1//4ZcAgwwxzAijjDHObyaYZIppZphljnkWWGSJZVZYZY11Nthki2122GWPff5wwCFHHHPCKWecc8ElV1xzwy133PPAI08888Irb7zzwSdffPPDr//4S4BBhhhmhFHGGOc3E0wyxTQzzDLHPAssssQyK6yyxjobbLLFNjvsssc+fzjgkCOOOeGUM8654JIrrrnhljvueeCRJ5554ZU33vngky+++eHXf/olwCBDDDPCKGOM85sJJplimhlmmWOeBRZZYpkVVlljnQ022WKbHXbZY58/HHDIEceccMoZ51xwyRXX3HDLHfc88MgTz7zwyhvvfPDJF9/88E9Af4BBhhhmhFHGGOc3E0wyxTQzzDLHPAssssQyK6yyxjobbLLFNjvsssc+fzjgkCOOOeGUM8654JIrrrnhljvueeCRJ5554ZU33vngky+++eHX//9LgEGGGGaEUcYY5zcTTDLFNDPMMsc8CyyyxDIrrLLGOhtsssU2O+yyxz5/OOCQI4454ZQzzrngkiuuueGWO+554JEnnnnhlTfe+eCTL7754dd//iXAIEMMM8IoY4zzmwkmmWKaGWaZY54FFllimRVWWWOdDTbZYpsddtljnz8ccMgRx5xwyhnnXHDJFdfccMsd9zzwyBPPvPDKG+988MkX3/zw67/8EmCQIYYZYZQxxvnNBJNMMc0Ms8wxzwKLLLHMCqussc4Gm2yxzQ677LHPHw445IhjTjjljHMuuOSKa2645Y57HnjkiWdeeOWNdz745ItvfvjnME+AQYYYZoRRxhjnNxNMMsU0M8wyxzwLLLLEMiusssY6G2yyxTY77LLHPn844JAjjjnhlDPOueCSK6654ZY77nngkSeeeeGVN9754JMvvvnh13/9JcAgQwwzwihjjPObCSaZYpoZZpljngUWWWKZFVZZY50NNtlimx122WOfPxxwyBHHnHDKGedccMkV19xwyx33PPDIE8+88Mob73zwyRff/PDrv/0SYJAhhhlhlDHG+c0Ek0wxzQyzzDHPAossscwKq6yxzgabbLHNDrvssc8fDjjkiGNOOOWMcy645IprbrjljnseeOSJZ1545Y13Pvjki29++PXffwkwyBDDjDDKGOP8ZoJJpphmhlnmmGeBRZZYZoVV1lhng0222GaHXfbY5w8HHHLEMSeccsY5F1xyxTU33HLHPQ888sQzL7zyxjsffPLFNz/8c5AvwCBDDDPCKGOM85sJJplimhlmmWOeBRZZYpkVVlljnQ022WKbHXbZY58/HHDIEceccMoZ51xwyRXX3HDLHfc88MgTz7zwyhvvfPDJF9/88Ot//BJgkCGGGWGUMcb5zQSTTDHNDLPMMc8CiyyxzAqrrLHOBptssc0Ou+yxzx8OOOSIY0445YxzLrjkimtuuOWOex545IlnXnjljXc++OSLb3749T9/CTDIEMOMMMoY4/xmgkmmmGaGWeaYZ4FFllhmhVXWWGeDTbbYZodd9tjnDwcccsQxJ5xyxjkXXHLFNTfccsc9DzzyxDMvvPLGOx988sU3P/z6X78EGGSIYUYYZYxxfjPBJFNMM8Msc8yzwCJLLLPCKmuss8EmW2yzwy577POHAw454pgTTjnjnAsuueKaG265454HHnnimRdeeeOdDz754psf/jnEG2CQIYYZYZQxxvnNBJNMMc0Ms8wxzwKLLLHMCqussc4Gm2yxzQ677LHPHw445IhjTjjljHMuuOSKa2645Y57HnjkiWdeeOWNdz745Itvfvj1v38JMMgQw4wwyhjj/GaCSaaYZoZZ5phngUWWWGaFVdZYZ4NNtthmh1322OcPBxxyxDEnnHLGORdccsU1N9xyxz0PPPLEMy+88sY7H3zyxTc//Po/vwQYZIhhRhhljHF+M8EkU0wzwyxzzLPAIksss8Iqa6yzwSZbbLPDLnvs84cDDjnimBNOOeOcCy654pobbrnjngceeeKZF155450PPvnimx9+/d9fAgwyxDAjjDLGOL+ZYJIppplhljnmWWCRJZZZYZU11tlgky222WGXPfb5wwGHHHHMCaeccc4Fl1xxzQ233HHPA4888cwLr7zxzgeffPHND79yv/w/dLzYyHicLcUhuAEAACBmQRAEQRAEQRAEQRAEd7OdbbbZZnd2ZzvbbLPNNtvczW7uThCEFwRBEARBEARBEARBEF4QBEEQBEEQhBf2ffP/5Q8WAv+/kMOOOOqY40446ZTTzjjrnPMuuOiSy6646prrbrjpltvuuOue+x546JF/PPbEU88898JLr7z2xlvvvPfBR5/867Mvvvrmux9++uW3P/5z4B98CzrksCOOOua4E0465bQzzjrnvAsuuuSyK6665robbrrltjvuuue+Bx565B+PPfHUM8+98NIrr73x1jvvffDRJ//67Iuvvvnuh59++e2P/xz4h9+CDjnsiKOOOe6Ek0457YyzzjnvgosuueyKq6657oabbrntjrvuue+Bhx75x2NPPPXMcy+89Mprb7z1znsffPTJvz774qtvvvvhp19+++M/B/6db0GHHHbEUcccd8JJp5x2xlnnnHfBRZdcdsVV11x3w0233HbHXffc98BDj/zjsSeeeua5F1565bU33nrnvQ8++uRfn33x1Tff/fDTL7/98Z8DxW9Bhxx2xFHHHHfCSaecdsZZ55x3wUWXXHbFVddcd8NNt9x2x1333PfAQ4/847EnnnrmuRdeeuW1N956570PPvrkX5998dU33/3w0y+//fGfA//ut6BDDjviqGOOO+GkU04746xzzrvgoksuu+Kqa6674aZbbrvjrnvue+ChR/7x2BNPPfPcCy+98tobb73z3gcfffKvz7746pvvfvjpl9/++M+Bf+9b0CGHHXHUMcedcNIpp51x1jnnXXDRJZddcdU1191w0y233XHXPfc98NAj/3jsiaeeee6Fl1557Y233nnvg48++ddnX3z1zXc//PTLb3/858A/+hZ0yGFHHHXMcSecdMppZ5x1znkXXHTJZVdcdc11N9x0y2133HXPfQ889Mg/HnviqWeee+GlV15746133vvgo0/+9dkXX33z3Q8//fLbH/85UPoWdMhhRxx1zHEnnHTKaWecdc55F1x0yWVXXHXNdTfcdMttd9x1z30PPPTIPx574qlnnnvhpVdee+Otd9774KNP/vXZF199890PP/3y2x//OfDvfws65LAjjjrmuBNOOuW0M84657wLLrrksiuuuua6G2665bY77rrnvgceeuQfjz3x1DPPvfDSK6+98dY7733w0Sf/+uyLr7757oeffvntj/8c+A++BR1y2BFHHXPcCSedctoZZ51z3gUXXXLZFVddc90NN91y2x133XPfAw898o/HnnjqmedeeOmV19546533Pvjok3999sVX33z3w0+//PbHfw78h9+CDjnsiKOOOe6Ek0457YyzzjnvgosuueyKq6657oabbrntjrvuue+Bhx75x2NPPPXMcy+89Mprb7z1znsffPTJvz774qtvvvvhp19+++M/B8rfgg457IijjjnuhJNOOe2Ms84574KLLrnsiquuue6Gm2657Y677rnvgYce+cdjTzz1zHMvvPTKa2+89c57H3z0yb8+++Krb7774adffvvjPwf+o29Bhxx2xFHHHHfCSaecdsZZ55x3wUWXXHbFVddcd8NNt9x2x1333PfAQ4/847EnnnrmuRdeeuW1N956570PPvrkX5998dU33/3w0y+//fGfA//xt6BDDjviqGOOO+GkU04746xzzrvgoksuu+Kqa6674aZbbrvjrnvue+ChR/7x2BNPPfPcCy+98tobb73z3gcfffKvz7746pvvfvjpl9/++M+B/+Rb0CGHHXHUMcedcNIpp51x1jnnXXDRJZddcdU1191w0y233XHXPfc98NAj/3jsiaeeee6Fl1557Y233nnvg48++ddnX3z1zXc//PTLb3/850DlW9Ahhx1x1DHHnXDSKaedcdY5511w0SWXXXHVNdfdcNMtt91x1z33PfDQI/947ImnnnnuhZdeee2Nt95574OPPvnXZ1989c13P/z0y29//OfAf/ot6JDDjjjqmONOOOmU084465zzLrjoksuuuOqa62646Zbb7rjrnvseeOiRfzz2xFPPPPfCS6+89sZb77z3wUef/OuzL7765rsffvrltz/+c+A/+xZ0yGFHHHXMcSecdMppZ5x1znkXXHTJZVdcdc11N9x0y2133HXPfQ889Mg/HnviqWeee+GlV15746133vvgo0/+9dkXX33z3Q8//fLbH/858J9/CzrksCOOOua4E0465bQzzjrnvAsuuuSyK6665robbrrltjvuuue+Bx565B+PPfHUM8+98NIrr73x1jvvffDRJ//67Iuvvvnuh59++e2P/xyofgs65LAjjjrmuBNOOuW0M84657wLLrrksiuuuua6G2665bY77rrnvgceeuQfjz3x1DPPvfDSK6+98dY7733w0Sf/+uyLr7757oeffvntj/8c+C++BR1y2BFHHXPcCSedctoZZ51z3gUXXXLZFVddc90NN91y2x133XPfAw898o/HnnjqmedeeOmV19546533Pvjok3999sVX33z3w0+//PbHfw78429Bhxx2xFHHHHfCSaecdsZZ55x3wUWXXHbFVddcd8NNt9x2x1333PfAQ4/847EnnnrmuRdeeuW1N956570PPvrkX5998dU33/3w0y+//fGfA//kW9Ahhx1x1DHHnXDSKaedcdY5511w0SWXXXHVNdfdcNMtt91x1z33PfDQI/947ImnnnnuhZdeee2Nt95574OPPvnXZ1989c13P/z0y29//OdA7VvQIYcdcdQxx51w0imnnXHWOeddcNEll11x1TXX3XDTLbfdcdc99z3w0CP/eOyJp5557oWXXnntjbfeee+Djz7512dffPXNdz/89Mtvf/znwH/5LeiQw4446pjjTjjplNPOOOuc8y646JLLrrjqmutuuOmW2+646577HnjokX889sRTzzz3wkuvvPbGW++898FHn/zrsy+++ua7H3765bc//nPgn34LOuSwI4465rgTTjrltDPOOue8Cy665LIrrrrmuhtuuuW2O+66574HHnrkH4898dQzz73w0iuvvfHWO+998NEn//rsi6+++e6Hn3757Y//HPhn34IOOeyIo4457oSTTjntjLPOOe+Ciy657Iqrrrnuhptuue2Ou+6574GHHvnHY0889cxzL7z0ymtvvPXOex989Mm/Pvviq2++++GnX3774z8H6t+CDjnsiKOOOe6Ek0457YyzzjnvgosuueyKq6657oabbrntjrvuue+Bhx75x2NPPPXMcy+89Mprb7z1znsffPTJvz774qtvvvvhp19+++M/B/6rb0GHHHbEUcccd8JJp5x2xlnnnHfBRZdcdsVV11x3w0233HbHXffc98BDj/zjsSeeeua5F1565bU33nrnvQ8++uRfn33x1Tff/fDTL7/98Z8D//W3oEMOO+KoY4474aRTTjvjrHPOu+CiSy674qprrrvhpltuu+Oue+574KFH/vHYE08989wLL73y2htvvfPeBx998q/Pvvjqm+9++OmX3/74z4F//i3okMOOOOqY40446ZTTzjjrnPMuuOiSy6646prrbrjpltvuuOue+x546JF/PPbEU88898JLr7z2xlvvvPfBR5/867Mvvvrmux9++uW3P/5zoPEt6JDDjjjqmONOOOmU084465zzLrjoksuuuOqa62646Zbb7rjrnvseeOiRfzz2xFPPPPfCS6+89sZb77z3wUef/OuzL7765rsffvrltz/+c+C/+RZ0yGFHHHXMcSecdMppZ5x1znkXXHTJZVdcdc11N9x0y2133HXPfQ889Mg/HnviqWeee+GlV15746133vvgo0/+9dkXX33z3Q8//fLbH/858N9+CzrksCOOOua4E0465bQzzjrnvAsuuuSyK6665robbrrltjvuuue+Bx565B+PPfHUM8+98NIrr73x1jvvffDRJ//67Iuvvvnuh59++e2P/xz4774FHXLYEUcdc9wJJ51y2hlnnXPeBRddctkVV11z3Q033XLbHXfdc98DDz3yj8eeeOqZ51546ZXX3njrnfc++OiTf332xVfffPfDT7/89sd/DjS/BR1y2BFHHXPcCSedctoZZ51z3gUXXXLZFVddc90NN91y2x133XPfAw898o/HnnjqmedeeOmV19546533Pvjok3999sVX33z3w0+//PbHfw7899+CDjnsiKOOOe6Ek0457YyzzjnvgosuueyKq6657oabbrntjrvuue+Bhx75x2NPPPXMcy+89Mprb7z1znsffPTJvz774qtvvvvhp19+++M/B/6Hb0GHHHbEUcccd8JJp5x2xlnnnHfBRZdcdsVV11x3w0233HbHXffc98BDj/zjsSeeeua5F1565bU33nrnvQ8++uRfn33x1Tff/fDTL7/98Z8D/+O3oEMOO+KoY4474aRTTjvjrHPOu+CiSy674qprrrvhpltuu+Oue+574KFH/vHYE08989wLL73y2htvvfPeBx998q/Pvvjqm+9++OmX3/74z4HWt6BDDjviqGOOO+GkU04746xzzrvgoksuu+Kqa6674aZbbrvjrnvue+ChR/7x2BNPPfPcCy+98tobb73z3gcfffKvz7746pvvfvjpl9/++M+B/+lb0CGHHXHUMcedcNIpp51x1jnnXXDRJZddcdU1191w0y233XHXPfc98NAj/3jsiaeeee6Fl1557Y233nnvg48++ddnX3z1zXc//PTLb3/858D//C3okMOOOOqY40446ZTTzjjrnPMuuOiSy6646prrbrjpltvuuOue+x546JF/PPbEU88898JLr7z2xlvvvPfBR5/867Mvvvrmux9++uW3P/5z4H/5FnTIYUccdcxxJ5x0ymlnnHXOeRdcdMllV1x1zXU33HTLbXfcdc99Dzz0yD8ee+KpZ5574aVXXnvjrXfe++CjT/712RdfffPdDz/98tsf/znQ/hZ0yGFHHHXMcSecdMppZ5x1znkXXHTJZVdcdc11N9x0y2133HXPfQ889Mg/HnviqWeee+GlV15746133vvgo0/+9dkXX33z3Q8//fLbH/858L9+CzrksCOOOua4E0465bQzzjrnvAsuuuSyK6665robbrrltjvuuue+Bx565B+PPfHUM8+98NIrr73x1jvvffDRJ//67Iuvvvnuh59++e2P/xz4374FHXLYEUcdc9wJJ51y2hlnnXPeBRddctkVV11z3Q033XLbHXfdc98DDz3yj8eeeOqZ51546ZXX3njrnfc++OiTf332xVfffPfDT7/89sd/Dvzv34IOOeyIo4457oSTTjntjLPOOe+Ciy657Iqrrrnuhptuue2Ou+6574GHHvnHY0889cxzL7z0ymtvvPXOex989Mm/Pvviq2++++GnX3774z8HOt+CDjnsiKOOOe6Ek0457YyzzjnvgosuueyKq6657oabbrntjrvuue+Bhx75x2NPPPXMcy+89Mprb7z1znsffPTJvz774qtvvvvhp19+++M/B/6Pb0GHHHbEUcccd8JJp5x2xlnnnHfBRZdcdsVV11x3w0233HbHXffc98BDj/zjsSeeeua5F1565bU33nrnvQ8++uRfn33x1Tff/fDTL7/98Z8D/+e3oEMOO+KoY4474aRTTjvjrHPOu+CiSy674qprrrvhpltuu+Oue+574KFH/vHYE08989wLL73y2htvvfPeBx998q/Pvvjqm+9++OmX3/74z4H/61vQIYcdcdQxx51w0imnnXHWOeddcNEll11x1TXX3XDTLbfdcdc99z3w0CP/eOyJp5557oWXXnntjbfeee+Djz7512dffPXNdz/89Mtvf/znQPdb0CGHHXHUMcedcNIpp51x1jnnXXDRJZddcdU1191w0y233XHXPfc98NAj/3jsiaeeee6Fl1557Y233nnvg48++ddnX3z1zXc//PTLb3/858C/+BZ0yGFHHHXMcSecdMppZ5x1znkXXHTJZVdcdc11N9x0y2133HXPfQ889Mg/HnviqWeee+GlV15746133vvgo0/+9dkXX33z3Q8//fLbH/858C+/BR1y2BFHHXPcCSedctoZZ51z3gUXXXLZFVddc90NN91y2x133XPfAw898o/HnnjqmedeeOmV19546533Pvjok3999sVX33z3w0+//PbHfw7839+CDjnsiKOOOe6Ek0457YyzzjnvgosuueyKq6657oabbrntjrvuue+Bhx75x2NPPPXMcy+89Mprb7z1znsffPTJvz774qtvvvvhp19+++M/B3rfgg457IijjjnuhJNOOe2Ms84574KLLrnsiquuue6Gm2657Y677rnvgYce+cdjTzz1zHMvvPTKa2+89c57H3z0yb8+++Krb7774adffvvjPwf+n29Bhxx2xFHHHHfCSaecdsZZ55x3wUWXXHbFVddcd8NNt9x2x1333PfAQ4/847EnnnrmuRdeeuW1N956570PPvrkX5998dU33/3w0y+//fGfA//qW9Ahhx1x1DHHnXDSKaedcdY5511w0SWXXXHVNdfdcNMtt91x1z33PfDQI/947ImnnnnuhZdeee2Nt95574OPPvnXZ1989c13P/z0y29//OfA//st6JDDjjjqmONOOOmU084465zzLrjoksuuuOqa62646Zbb7rjrnvseeOiRfzz2xFPPPPfCS6+89sZb77z3wUef/OuzL7765rsffvrltz/+c6D/LeiQw4446pjjTjjplNPOOOuc8y646JLLrrjqmutuuOmW2+646577HnjokX889sRTzzz3wkuvvPbGW++898FHn/zrsy+++ua7H3765bc//nPgX38LOuSwI4465rgTTjrltDPOOue8Cy665LIrrrrmuhtuuuW2O+66574HHnrkH4898dQzz73w0iuvvfHWO+998NEn//rsi6+++e6Hn3757Y//HPg334IOOeyIo4457oSTTjntjLPOOe+Ciy657Iqrrrnuhptuue2Ou+6574GHHvnHY0889cxzL7z0ymtvvPXOex989Mm/Pvviq2++++GnX3774z8H/u23oEMOO+KoY4474aRTTjvjrHPOu+CiSy674qprrrvhpltuu+Oue+574KFH/vHYE08989wLL73y2htvvfPeBx998q/Pvvjqm+9++OmX3/74z4HBt/8PdlHZBHicLcVxnAOKQADgo8niaLI4miyOJoujyeJosjiaLI4mi6PJYjRZHE3mvb1nz5tnGIZLp1YunVq5NLWyNLVy6dTKpVMrl6ZWlk6tXPj92vf98wVqa/8v6HWHHPaGI4560zHHveWEk952ymnvOOOsd51z3nsuuOiSy65431XXXHfDTbfcdscHPnTXRz52zyfue+ChRx771Gee+NwXnvrSM8+98JWXvvba61YFHPS6Qw57wxFHvemY495ywklvO+W0d5xx1rvOOe89F1x0yWVXvO+qa6674aZbbrvjAx+66yMfu+cT9z3w0COPfeozT3zuC0996ZnnXvjKS1977YZVAQe97pDD3nDEUW865ri3nHDS20457R1nnPWuc857zwUXXXLZFe+76prrbrjpltvu+MCH7vrIx+75xH0PPPTIY5/6zBOf+8JTX3rmuRe+8tLXXrtxVcBBrzvksDcccdSbjjnuLSec9LZTTnvHGWe965zz3nPBRZdcdsX7rrrmuhtuuuW2Oz7wobs+8rF7PnHfAw898tinPvPE577w1Jeeee6Fr7z0tdfqqwIOet0hh73hiKPedMxxbznhpLedcto7zjjrXeec954LLrrksived9U1191w0y233fGBD931kY/d84n7Hnjokcc+9ZknPveFp770zHMvfOWlr71206qAg153yGFvOOKoNx1z3FtOOOltp5z2jjPOetc5573ngosuueyK9111zXU33HTLbXd84EN3feRj93zivgceeuSxT33mic994akvPfPcC1956Wuv3bwq4KDXHXLYG4446k3HHPeWE0562ymnveOMs951znnvueCiSy674n1XXXPdDTfdctsdH/jQXR/52D2fuO+Bhx557FOfeeJzX3jqS88898JXXvraa69fFXDQ6w457A1HHPWmY457ywknve2U095xxlnvOue891xw0SWXXfG+q6657oabbrntjg986K6PfOyeT9z3wEOPPPapzzzxuS889aVnnnvhKy997bXGqoCDXnfIYW844qg3HXPcW0446W2nnPaOM8561znnveeCiy657Ir3XXXNdTfcdMttd3zgQ3d95GP3fOK+Bx565LFPfeaJz33hqS8989wLX3npa6/dsirgoNcdctgbjjjqTccc95YTTnrbKae944yz3nXOee+54KJLLrvifVddc90NN91y2x0f+NBdH/nYPZ+474GHHnnsU5954nNfeOpLzzz3wlde+tprb1gVcNDrDjnsDUcc9aZjjnvLCSe97ZTT3nHGWe8657z3XHDRJZdd8b6rrrnuhptuue2OD3zoro987J5P3PfAQ4889qnPPPG5Lzz1pWeee+ErL33ttVtXBRz0ukMOe8MRR73pmOPecsJJbzvltHeccda7zjnvPRdcdMllV7zvqmuuu+GmW2674wMfuusjH7vnE/c98NAjj33qM0987gtPfemZ5174yktfe625KuCg1x1y2BuOOOpNxxz3lhNOetspp73jjLPedc5577ngoksuu+J9V11z3Q033XLbHR/40F0f+dg9n7jvgYceeexTn3nic1946kvPPPfCV1762mtvXBVw0OsOOewNRxz1pmOOe8sJJ73tlNPeccZZ7zrnvPdccNEll13xvquuue6Gm2657Y4PfOiuj3zsnk/c98BDjzz2qc888bkvPPWlZ5574Ssvfe2121YFHPS6Qw57wxFHvemY495ywklvO+W0d5xx1rvOOe89F1x0yWVXvO+qa6674aZbbrvjAx+66yMfu+cT9z3w0COPfeozT3zuC0996ZnnXvjKS1977U2rAg563SGHveGIo950zHFvOeGkt51y2jvOOOtd55z3ngsuuuSyK9531TXX3XDTLbfd8YEP3fWRj93zifseeOiRxz71mSc+94WnvvTMcy985aWvvdZaFXDQ6w457A1HHPWmY457ywknve2U095xxlnvOue891xw0SWXXfG+q6657oabbrntjg986K6PfOyeT9z3wEOPPPapzzzxuS889aVnnnvhKy997bU3rwo46HWHHPaGI4560zHHveWEk952ymnvOOOsd51z3nsuuOiSy65431XXXHfDTbfcdscHPnTXRz52zyfue+ChRx771Gee+NwXnvrSM8+98JWXvvbaW1YFHPS6Qw57wxFHvemY495ywklvO+W0d5xx1rvOOe89F1x0yWVXvO+qa6674aZbbrvjAx+66yMfu+cT9z3w0COPfeozT3zuC0996ZnnXvjKS1977a2rAg563SGHveGIo950zHFvOeGkt51y2jvOOOtd55z3ngsuuuSyK9531TXX3XDTLbfd8YEP3fWRj93zifseeOiRxz71mSc+94WnvvTMcy985aWvvdZeFXDQ6w457A1HHPWmY457ywknve2U095xxlnvOue891xw0SWXXfG+q6657oabbrntjg986K6PfOyeT9z3wEOPPPapzzzxuS889aVnnnvhKy997bW3rQo46HWHHPaGI4560zHHveWEk952ymnvOOOsd51z3nsuuOiSy65431XXXHfDTbfcdscHPnTXRz52zyfue+ChRx771Gee+NwXnvrSM8+98JWXvvZXffu3r76Dg76z7+q7Oex7+t6+j6O+vx/oBznuh/rhfoSTfrQf68c57Sf6yX6Ks366n+lnOe/n+vl+gYt+sV/ql7niV/rVfo1rvtE3+xY3/Sa/xW9zx+/2e/xed/0+v98fcM8f9If8YQ/8EX/UH/PYH/cn/ElP/Cl/2p/x1J/15/x5z/0Ff9Ff8tJf9ld8u3esDviOvpPv4pDv7nv4Xo74vr6fH+CYH+yH+GFO+JF+lB/jlB/vJ/hJzvipfpqf4Zyf7ef4eS74hX6RX+KyX+5X+FWu+rW+wTe54Vt9m9/stt/hd/nnfOhf9C/7V3zsX/dv+rfc9+/69/0HHvmP/af+M5/5L/3X/htf+O/9j/4nz/yv/nf/h6/83/5f/5/XOqu+zl/vb/C6v9Hf5G/2hr/F3+pv86a/3d/h7/SWv8vf7e/xtr/X3+fv945/wD/oH/Kuf9g/4h/1nn/MP+6fcMk/6Z/yT3vfP+Of9etc9+v9Br/RLb/Vb/c7feCf9y/4l3zkX/Wv+Td84t/27/j3PPQf+o/8Jz71n/sv/Fc+99/67/wPvvQ/+1/8b174P/1f/h9f+6u+/TtX38FB39l39d0c9j19b9/HUd/fD/SDHPdD/XA/wkk/2o/145z2E/1kP8VZP93P9LOc93P9fL/ARb/YL/XLXPEr/Wq/xjXf6Jt9i5t+k9/it7njd/s9fq+7fp/f7w+45w/6Q/6wB/6IP+qPeeyP+xP+pCf+lD/tz3jqz/pz/rzn/oK/6C956S/7K77du1YHfEffyXdxyHf3PXwvR3xf388PcMwP9kP8MCf8SD/Kj3HKj/cT/CRn/FQ/zc9wzs/2c/w8F/xCv8gvcdkv9yv8Klf9Wt/gm9zwrb7Nb3bbXwMukFCt + + + AQAAAACAAAAHJwAAJQAAAA==eJztwaEBAAAMAqC04v8H7weTAUgAAAAAAAAAAAAoHBMecq9+rg== + + + + + diff --git a/mesh-doctor/tests/test_convertMD2SG.py b/mesh-doctor/tests/test_convertMD2SG.py new file mode 100644 index 00000000..3c3bfe4f --- /dev/null +++ b/mesh-doctor/tests/test_convertMD2SG.py @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies. +# SPDX-FileContributor: GitHub Copilot, Jacques Franc +import argparse + +from geos.mesh_doctor.parsing.convertMD2SGParsing import convert, fillSubparser +from geos.mesh_doctor.actions.convertMD2SG import Result, action + + +def test_convert_md2sg_parser() -> None: + """Test the convertMD2SG parser and conversion function.""" + parser = argparse.ArgumentParser( description='Testing.' ) + subparsers = parser.add_subparsers() + fillSubparser( subparsers ) + + args = parser.parse_args( [ 'convertMD2SG', '-i', 'mesh-doctor/tests/data/base_tetra_shift.vtm', '-z', '2' ] ) + + options = convert( vars( args ) ) + assert options.attrs == ( 2, ) + assert options.skipCleanCollocated is False + assert options.skipFilterVolumeCells is False + assert options.meshVtkOutput.output == "converted.vtu" + + +def test_convertion() -> None: + """Test the convertMD2SG action.""" + parser = argparse.ArgumentParser( description='Testing.' ) + subparsers = parser.add_subparsers() + fillSubparser( subparsers ) + + args = parser.parse_args( [ + 'convertMD2SG', '-i', 'mesh-doctor/tests/data/base_tetra_shift.vtm', '-z', '2', '--outputFile', 'converted.vtu' + ] ) + + options = convert( vars( args ) ) + actionsResult = action( vars( args )[ 'vtuInputFile' ], options ) + assert isinstance( actionsResult, Result ) + assert actionsResult.outputMesh is not None + assert actionsResult.nCleanCollocated != 0 + assert actionsResult.outputMesh.GetNumberOfCells() > 0 + assert actionsResult.outputMesh.GetPointData().HasArray( "faultNodes_0" ) + assert actionsResult.outputMesh.GetPointData().GetArray( "faultNodes_0" ).GetRange() == ( 0.0, 1.0 ) + + +def test_convertion_cc() -> None: + """Test the convertMD2SG action from vtu file with multiple connected components.""" + parser = argparse.ArgumentParser( description='Testing.' ) + subparsers = parser.add_subparsers() + fillSubparser( subparsers ) + + args = parser.parse_args( [ + 'convertMD2SG', '-i', 'mesh-doctor/tests/data/base_hexa_shift_2.vtu', '-z', '2', '3', '--outputFile', + 'converted_cc.vtu', '--skipCleanCollocated' + ] ) + + options = convert( vars( args ) ) + actionsResult = action( vars( args )[ 'vtuInputFile' ], options ) + assert isinstance( actionsResult, Result ) + assert actionsResult.outputMesh is not None + assert actionsResult.nCleanCollocated == 0 + assert actionsResult.outputMesh.GetNumberOfCells() > 0 + assert actionsResult.outputMesh.GetPointData().HasArray( "faultNodes_0" ) + assert actionsResult.outputMesh.GetPointData().GetArray( "faultNodes_0" ).GetRange() == ( 0.0, 1.0 ) + assert actionsResult.outputMesh.GetPointData().HasArray( "faultNodes_1" ) + assert actionsResult.outputMesh.GetPointData().GetArray( "faultNodes_1" ).GetRange() == ( 0.0, 1.0 )