Coverage for /opt/hostedtoolcache/Python/3.11.10/x64/lib/python3.11/site-packages/pysagas/geometry/parsers.py: 0%
155 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-10-30 04:27 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-10-30 04:27 +0000
1import pandas as pd
2from stl import mesh
3from tqdm import tqdm
4import multiprocess as mp
5import xml.etree.ElementTree as ET
6from abc import ABC, abstractmethod
7from typing import List, Optional, Union
8from pysagas.geometry import Cell, Vector
9from pysagas.utilities import add_sens_data
12class AbstractParser(ABC):
13 """Interface for a geometry parser."""
15 filetype = None
17 @abstractmethod
18 def __init__(self, **kwargs) -> None:
19 pass
21 def __repr__(self) -> str:
22 return f"PySAGAS {self.filetype} parser"
24 def __str__(self) -> str:
25 return f"PySAGAS {self.filetype} parser"
27 @property
28 @abstractmethod
29 def filetype(self):
30 # This is a placeholder for a class variable defining the parser file type
31 pass
33 @abstractmethod
34 def load(self) -> List[Cell]:
35 """Load cells from file."""
37 @classmethod
38 @abstractmethod
39 def load_from_file(self) -> List[Cell]:
40 """Convenience method for loading cells from file."""
43class Parser(AbstractParser):
44 def __init__(self, filepath: str, verbosity: int = 1) -> None:
45 self.filepath = filepath
46 self.verbosity = verbosity
48 @classmethod
49 def load_from_file(
50 cls,
51 filepath: str,
52 geom_sensitivities: Optional[Union[str, pd.DataFrame]] = None,
53 verbosity: Optional[int] = 1,
54 **kwargs,
55 ) -> List[Cell]:
56 """Convenience method for loading cells from file.
58 Parameters
59 ----------
60 filepath : str
61 The filepath to the geometry.
63 geom_sensitivities : str | DataFrame, optional
64 The geometry sensitivity data, to optionally add to the loaded cells. This can
65 be provided as a path to the data in csv format, or directly as a Pandas
66 DataFrame. The default is None.
68 verbosity : int, optional
69 The verbosity of the code. The defualt is 1.
71 **kwargs
72 Additional keyword arguments can be provided to control the sensitivity matching
73 algorithm.
75 See Also
76 --------
77 pysagas.utilities.add_sens_data
78 """
80 # Create parser instance
81 parser = cls(filepath, verbosity)
83 # Load file
84 cells = parser.load()
86 if geom_sensitivities:
87 # Check input type
88 if isinstance(geom_sensitivities, str):
89 # File path provided, load into dataframe
90 geom_sensitivities = pd.read_csv(geom_sensitivities)
92 elif not isinstance(geom_sensitivities, pd.DataFrame):
93 raise TypeError("Invalid data provided for 'geom_sensitivities'.")
95 # Add sensitivity data to cells
96 add_sens_data(
97 cells=cells,
98 data=geom_sensitivities,
99 verbosity=verbosity,
100 **kwargs,
101 )
102 return cells
105class STL(Parser):
106 filetype = "STL"
108 def load(self) -> List[Cell]:
109 # Load the STL
110 mesh_obj = mesh.Mesh.from_file(self.filepath)
112 cells = []
113 # TODO - can face ids be inferred?
114 if self.verbosity > 0:
115 print("\nTranscribing cells:")
116 pbar = tqdm(
117 total=len(mesh_obj.vectors),
118 position=0,
119 leave=True,
120 desc=" Cell transcription progress",
121 )
123 for vector_triple in mesh_obj.vectors:
124 vertices = [Vector.from_coordinates(v) for v in vector_triple]
125 try:
126 cell = Cell.from_points(vertices)
127 cells.append(cell)
128 except:
129 pass
131 # Update progress bar
132 if self.verbosity > 0:
133 pbar.update(1)
135 if self.verbosity > 0:
136 pbar.close()
137 print("Done.")
139 return cells
142class MeshIO(Parser):
143 """Meshio parser"""
145 filetype = "meshio mesh"
147 def __init__(self, filepath: str, verbosity: int = 1) -> None:
148 # Import meshio
149 try:
150 import meshio
151 except ModuleNotFoundError:
152 raise Exception("Could not find meshio. Please install it and try again.")
153 self._meshio = meshio
154 super().__init__(filepath, verbosity)
156 def load(self) -> List[Cell]:
157 def mp_wrapper(face):
158 vertices = [Vector.from_coordinates(mesh_vertices[i]) for i in face]
159 try:
160 cell = Cell.from_points(vertices, face_ids=face)
161 except:
162 cell = None
163 return cell
165 # Load the STL
166 mesh_obj = self._meshio.read(self.filepath)
168 if self.verbosity > 0:
169 print("\nTranscribing cells.")
171 # Create multiprocessing pool to construct cells
172 cells = []
173 pool = mp.Pool()
174 mesh_vertices = mesh_obj.points
175 for result in pool.map(mp_wrapper, mesh_obj.cells[0].data):
176 if result is not None:
177 cells.append(result)
179 if self.verbosity > 0:
180 print("Done.")
182 return cells
185class PyMesh(Parser):
186 filetype = "PyMesh STL"
188 def __init__(self, filepath: str, verbosity: int = 1) -> None:
189 # Import PyMesh
190 try:
191 import pymesh
192 except ModuleNotFoundError:
193 raise Exception(
194 "Could not find pymesh. Please follow the "
195 + "installation instructions at "
196 + "https://pymesh.readthedocs.io/en/latest/installation.html"
197 )
198 self._pymesh = pymesh
199 super().__init__(filepath, verbosity)
201 def load(self) -> List[Cell]:
202 def mp_wrapper(face):
203 vertices = [Vector.from_coordinates(mesh_vertices[i]) for i in face]
204 try:
205 cell = Cell.from_points(vertices, face_ids=face)
206 except:
207 cell = None
208 return cell
210 # Load the STL
211 mesh_obj = self._pymesh.load_mesh(self.filepath)
213 if self.verbosity > 0:
214 print("\nTranscribing cells.")
216 # Create multiprocessing pool to construct cells
217 cells = []
218 pool = mp.Pool()
219 mesh_vertices = mesh_obj.vertices
220 for result in pool.map(mp_wrapper, mesh_obj.faces):
221 if result is not None:
222 cells.append(result)
224 if self.verbosity > 0:
225 print("Done.")
227 return cells
230class TRI(Parser):
231 filetype = ".tri"
233 def load(self) -> List[Cell]:
234 # Parse .tri file
235 tree = ET.parse(self.filepath)
236 root = tree.getroot()
237 grid = root[0]
238 piece = grid[0]
239 points = piece[0]
240 cells = piece[1]
242 points_data = points[0].text
243 cells_data = cells[0].text
245 points_data_list = [el.split() for el in points_data.splitlines()[1:]]
246 points_data_list = [[float(j) for j in i] for i in points_data_list]
248 cells_data_list = [el.split() for el in cells_data.splitlines()[1:]]
249 cells_data_list = [[int(j) for j in i] for i in cells_data_list]
251 cells = []
252 if self.verbosity > 0:
253 print("\nTranscribing cells:")
254 pbar = tqdm(
255 total=len(cells_data_list),
256 position=0,
257 leave=True,
258 desc=" Cell transcription progress",
259 )
260 for vertex_idxs in cells_data_list:
261 vertices = [
262 Vector.from_coordinates(points_data_list[i]) for i in vertex_idxs
263 ]
264 cell = Cell.from_points(vertices, face_ids=vertex_idxs)
265 cells.append(cell)
267 # Update progress bar
268 if self.verbosity > 0:
269 pbar.update(1)
271 if self.verbosity > 0:
272 pbar.close()
273 print("Done.")
275 return cells
277 @classmethod
278 def load_from_file(cls, filepath: str, verbosity: int = 1, **kwargs) -> List[Cell]:
279 """Convenience method for loading cells from file."""
280 # Create parser instance
281 parser = cls(filepath, verbosity)
283 # Load file
284 cells = parser.load()
286 return cells