import numpy as np
import pyvista as pv
import pandas as pd
import os
import h5py
#resultsPath="/Users/alessandromastrofini/LocalFiles/acePlaqueGlobalResults/FinalOne/"
[docs]
def loadMeshFromHDF5(name, customPath=None):
"""
Loads a 3D mesh from an HDF5 file for the specified patient.
Args:
name (str): The name of the patient.
customPath (str, optional): Custom path to the patient's data. Defaults to None.
Returns:
pyvista.UnstructuredGrid: The loaded 3D mesh representing the patient's anatomy.
Raises:
ValueError: If `customPath` is not provided, it prints a message to specify it.
Details:
- The function first builds the file path to the HDF5 file containing the mesh data.
- It reads the nodes and elements from the HDF5 file, supporting two mesh types: `QuadraticTetra` and `LinearTetra`.
- For `QuadraticTetra`, 10 points per element are processed, while for `LinearTetra`, 4 points are used.
- The elements are adjusted to match PyVista's expected format (zero-indexed).
- Finally, the function creates an unstructured grid using PyVista and returns the mesh.
"""
# Determine the paths based on whether a custom path is provided
if customPath is not None:
hdf5Path = os.path.join(customPath,name,"mesh","mesh.h5")
#print(hdf5Path)
else:
print("NO CUSTOM PATH SET, SPECIFY IT!")
meshType = 'QuadraticTetra' # or 'LinearTetra'
# Read the HDF5 file
with h5py.File(hdf5Path, 'r') as h5file:
# Load the nodes dataset
#print(h5file.keys()) # List all top-level objects in the HDF5 file
nodes = h5file['/nodes'][:]
# Load the elements dataset (Element_O2)
elements = h5file['/Elements_O2'][:]
# Determine the number of points per element based on mesh type
if meshType == 'QuadraticTetra':
nPts = 10 # number of points for each element
cellType = np.full(len(elements), pv.CellType.QUADRATIC_TETRA, dtype=np.int8)
elif meshType == 'LinearTetra':
nPts = 4
cellType = np.full(len(elements), pv.CellType.TETRA, dtype=np.int8)
# Adjust the elements array to match PyVista's expected format
inElements = np.zeros((len(elements), nPts + 1), dtype=np.uint)
for i in range(len(elements)):
if meshType == 'QuadraticTetra':
data = [elements[i][j] - 1 for j in range(10)] # zero-indexed for PyVista
elif meshType == 'LinearTetra':
data = [elements[i][j] - 1 for j in range(4)] # zero-indexed for PyVista
data.insert(0, nPts) # insert the number of points at the beginning
inElements[i] = data
# Create the unstructured grid
mesh = pv.UnstructuredGrid(np.ravel(inElements), cellType, nodes)
return mesh
[docs]
def loadMeshFromTXT(name, customPath=None):
"""
Loads a 3D mesh from a TXT file for the specified patient.
Args:
name (str): The name of the patient.
customPath (str, optional): Custom path to the patient's data. Defaults to None.
Returns:
pyvista.UnstructuredGrid: The loaded 3D mesh representing the patient's anatomy.
Details:
- The function first builds the file paths for the nodes and elements from the provided directory.
- It reads the nodes from the TXT file and stores them in a NumPy array.
- For elements, it supports both `QuadraticTetra` (10 points per element) and `LinearTetra` (4 points per element).
- The elements are adjusted to match PyVista's expected format (zero-indexed).
- Finally, the function creates an unstructured grid using PyVista and returns the mesh.
"""
if customPath is not None:
nodesPath=customPath+'/'+name+"/mesh/nodes.txt"
elementsPath=customPath+'/'+name+"/mesh/Elements_O2.txt"
else:
nodesPath="/Users/alessandromastrofini/LocalFiles/acePlaque-preprod/T02_Patients/"+name+"/mesh/nodes.txt"
elementsPath="/Users/alessandromastrofini/LocalFiles/acePlaque-preprod/T02_Patients/"+name+"/mesh/Elements_O2.txt"
meshFileName=''
meshType='QuadraticTetra' # or 'LinearTetra'
# read all the nodes
if meshFileName=='':
with open(nodesPath, 'r') as f:
inPointsString = f.readlines()
inPoints=np.zeros((len(inPointsString),3)) #3=dimensions
for i in range(len(inPointsString)):
inPoints[i]=list(map(float, inPointsString[i].rstrip().split(' ')))
if meshFileName=='':
with open(elementsPath, 'r') as f:
inElementsString = f.readlines()
nOfElements=len(inElementsString)
if meshType=='QuadraticTetra':
nPts=10 #number of points for each element
inCellType=np.full(nOfElements,[pv.CellType.QUADRATIC_TETRA], np.int8)
elif meshType=='LinearTetra':
nPts=4
inCellType=np.full(nOfElements,[pv.CellType.TETRA], np.int8)
# load all the elements
inElements=np.zeros((nOfElements,nPts+1), dtype=np.uint)
for i in range(0,nOfElements):
tmpdata=list(map(int, inElementsString[i].rstrip().split(' ')))
if meshType=='QuadraticTetra':
data=[tmpdata[0]-1,tmpdata[1]-1,tmpdata[2]-1,tmpdata[3]-1,tmpdata[4]-1,tmpdata[5]-1,tmpdata[6]-1,tmpdata[7]-1,tmpdata[8]-1,tmpdata[9]-1]
elif meshType=='LinearTetra':
data=[tmpdata[0]-1,tmpdata[1]-1,tmpdata[2]-1,tmpdata[3]-1]
data.insert(0,nPts)
inElements[i]=data
if meshFileName=='':
mesh=pv.UnstructuredGrid(np.ravel(inElements), inCellType, inPoints)
else:
mesh=pv.read(meshFileName)
return mesh
[docs]
def loadMaterialFromHDF5(mesh, name, customPath=None):
"""
Loads material properties from an HDF5 file and assigns them to a given mesh.
Args:
mesh (pyvista.UnstructuredGrid): The patient's mesh to which the material data will be applied.
name (str): The name of the patient.
customPath (str, optional): Custom path to the patient's data. Defaults to None.
Returns:
pyvista.UnstructuredGrid: The mesh with material properties added as point data.
Raises:
ValueError: If `customPath` is not provided, it prints a message to specify it.
Details:
- The function reads the HDF5 file containing material data (e.g., c2 values).
- The material data is assigned to the mesh's `point_data` under the field name 'mats'.
"""
if customPath is not None:
hdf5Path = os.path.join(customPath,name,"Results","materials.h5")
else:
print("NO CUSTOM PATH SET, SPECIFY IT!")
# Step 1: Retrieve all dataset keys
with h5py.File(hdf5Path, 'r') as hdf:
data = hdf['/c2'][:]
mesh.point_data['mats'] = data[0:len(mesh.points)]
return mesh
[docs]
def loadMaterialFromTXT(mesh, pt, customPath=None):
"""
Loads material properties from a CSV, Parquet, or HDF5 file and assigns them to a given mesh.
Args:
mesh (pyvista.UnstructuredGrid): The patient's mesh to which the material data will be applied.
pt (str): The name of the patient or patient-specific identifier.
customPath (str, optional): Custom path to the patient's data. Defaults to None.
Returns:
pyvista.UnstructuredGrid: The mesh with material properties added as point data.
Raises:
FileNotFoundError: If no material file is found in supported formats (.csv, .parquet, .hdf5).
Details:
- The function searches for the material file in CSV, Parquet, or HDF5 formats in the given directory.
- Once the correct file is found, it reads the material data into a DataFrame.
- The material data (e.g., c2 values) is then assigned to the mesh's `point_data` under the field name 'mats'.
"""
if customPath is not None:
loadPath = os.path.join(customPath, pt)
else:
loadPath = os.path.join(resultsPath, pt)
# Trova il file materials con l'estensione corretta
file_found = None
for ext in ['csv', 'parquet', 'hdf5']:
file_path = os.path.join(loadPath, f'materials.{ext}')
if os.path.exists(file_path):
file_found = file_path
break
if file_found is None:
raise FileNotFoundError("Il file materials non è stato trovato in nessuno dei formati supportati (.csv, .parquet, .hdf5).")
# Legge il file nel formato corretto
if file_found.endswith('.csv'):
df2 = pd.read_csv(file_found)
elif file_found.endswith('.parquet'):
df2 = pd.read_parquet(file_found)
elif file_found.endswith('.hdf5'):
df2 = pd.read_hdf(file_found, key='df') # Assumendo che il key sia 'df', puoi modificarlo se necessario
# Aggiunge i dati al mesh
mesh.point_data['mats'] = df2['c2'][0:len(mesh.points)]
return mesh