Coverage for  / dolfinx-env / lib / python3.12 / site-packages / io4dolfinx / backends / __init__.py: 89%

37 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-02-26 18:16 +0000

1from enum import Enum 

2from importlib import import_module 

3from pathlib import Path 

4from typing import Any, Protocol 

5 

6from mpi4py import MPI 

7 

8import dolfinx 

9import numpy as np 

10import numpy.typing as npt 

11 

12from ..structures import ArrayData, FunctionData, MeshData, MeshTagsData, ReadMeshData 

13 

14__all__ = ["FileMode", "IOBackend", "get_backend"] 

15 

16 

17class ReadMode(Enum): 

18 serial = 10 # This means that all data is read in on root rank 

19 

20 # Total number of data P, num processes = i + 1. 

21 # All processes reads at least `P // (i+1)` items 

22 # The first j=P%(i+1) processes reads `P // (i+1) + 1` items 

23 # ```python 

24 # def compute_partitioning(P, J): 

25 # min_num = P // J 

26 # num_per_proc = np.full(J, min_num) 

27 # rem = P % J 

28 # num_per_proc[:int(rem)] += 1 

29 # assert(sum(num_per_proc)) == P 

30 # return num_per_proc 

31 # ``` 

32 parallel = 20 

33 

34 

35class FileMode(Enum): 

36 """Filen mode used for opening files.""" 

37 

38 append = 10 #: Append data to file 

39 write = 20 #: Write data to file 

40 read = 30 #: Read data from file 

41 

42 

43# See https://peps.python.org/pep-0544/#modules-as-implementations-of-protocols 

44class IOBackend(Protocol): 

45 read_mode: ReadMode 

46 

47 def get_default_backend_args(self, arguments: dict[str, Any] | None) -> dict[str, Any]: 

48 """Get default backend arguments given a set of input arguments. 

49 

50 Args: 

51 arguments: Input backend arguments 

52 

53 Returns: 

54 Updated backend arguments 

55 """ 

56 

57 def write_attributes( 

58 self, 

59 filename: Path | str, 

60 comm: MPI.Intracomm, 

61 name: str, 

62 attributes: dict[str, np.ndarray], 

63 backend_args: dict[str, Any] | None, 

64 ): 

65 """Write attributes to file. 

66 

67 Args: 

68 filename: Path to file to write to 

69 comm: MPI communicator used in storage 

70 name: Name of the attribute group 

71 attributes: Dictionary of attributes to write 

72 backend_args: Arguments to backend 

73 """ 

74 

75 def read_attributes( 

76 self, 

77 filename: Path | str, 

78 comm: MPI.Intracomm, 

79 name: str, 

80 backend_args: dict[str, Any] | None, 

81 ) -> dict[str, Any]: 

82 """Read attributes from file. 

83 

84 Args: 

85 filename: Path to file to read from 

86 comm: MPI communicator used in storage 

87 name: Name of the attribute group 

88 backend_args: Arguments to backend 

89 

90 Returns: 

91 Dictionary of attributes read from file 

92 """ 

93 

94 def read_timestamps( 

95 self, 

96 filename: Path | str, 

97 comm: MPI.Intracomm, 

98 function_name: str, 

99 backend_args: dict[str, Any] | None, 

100 ) -> npt.NDArray[np.float64 | str]: # type: ignore[type-var] 

101 """Read timestamps from file. 

102 

103 Args: 

104 filename: Path to file to read from 

105 comm: MPI communicator used in storage 

106 function_name: Name of the function to read timestamps for 

107 backend_args: Arguments to backend 

108 

109 Returns: 

110 Numpy array of timestamps read from file 

111 """ 

112 

113 def write_mesh( 

114 self, 

115 filename: Path | str, 

116 comm: MPI.Intracomm, 

117 mesh: MeshData, 

118 backend_args: dict[str, Any] | None, 

119 mode: FileMode, 

120 time: float, 

121 ): 

122 """ 

123 Write a mesh to file. 

124 

125 Args: 

126 comm: MPI communicator used in storage 

127 mesh: Internal data structure for the mesh data to save to file 

128 filename: Path to file to write to 

129 backend_args: Arguments to backend 

130 mode: File-mode to store the mesh 

131 time: Time stamp associated with the mesh 

132 """ 

133 

134 def write_meshtags( 

135 self, 

136 filename: str | Path, 

137 comm: MPI.Intracomm, 

138 data: MeshTagsData, 

139 backend_args: dict[str, Any] | None, 

140 ): 

141 """Write mesh tags to file. 

142 

143 Args: 

144 filename: Path to file to write to 

145 comm: MPI communicator used in storage 

146 data: Internal data structure for the mesh tags to save to file 

147 backend_args: Arguments to backend 

148 """ 

149 

150 def read_mesh_data( 

151 self, 

152 filename: Path | str, 

153 comm: MPI.Intracomm, 

154 time: str | float | None, 

155 read_from_partition: bool, 

156 backend_args: dict[str, Any] | None, 

157 ) -> ReadMeshData: 

158 """Read mesh data from file. 

159 

160 Args: 

161 filename: Path to file to read from 

162 comm: MPI communicator used in storage 

163 time: Time stamp associated with the mesh to read 

164 read_from_partition: Whether to read partition information 

165 backend_args: Arguments to backend 

166 

167 Returns: 

168 Internal data structure for the mesh data read from file 

169 """ 

170 

171 def read_meshtags_data( 

172 self, 

173 filename: str | Path, 

174 comm: MPI.Intracomm, 

175 name: str, 

176 backend_args: dict[str, Any] | None, 

177 ) -> MeshTagsData: 

178 """Read mesh tags from file. 

179 

180 Args: 

181 filename: Path to file to read from 

182 comm: MPI communicator used in storage 

183 name: Name of the mesh tags to read 

184 backend_args: Arguments to backend 

185 

186 Returns: 

187 Internal data structure for the mesh tags read from file 

188 """ 

189 

190 def read_dofmap( 

191 self, 

192 filename: str | Path, 

193 comm: MPI.Intracomm, 

194 name: str, 

195 backend_args: dict[str, Any] | None, 

196 ) -> dolfinx.graph.AdjacencyList: 

197 """Read the dofmap of a function with a given name. 

198 

199 Args: 

200 filename: Path to file to read from 

201 comm: MPI communicator used in storage 

202 name: Name of the function to read the dofmap for 

203 backend_args: Arguments to backend 

204 

205 Returns: 

206 Dofmap as an {py:class}`dolfinx.graph.AdjacencyList` 

207 """ 

208 

209 def read_dofs( 

210 self, 

211 filename: str | Path, 

212 comm: MPI.Intracomm, 

213 name: str, 

214 time: float, 

215 backend_args: dict[str, Any] | None, 

216 ) -> tuple[npt.NDArray[np.float32 | np.float64 | np.complex64 | np.complex128], int]: 

217 """Read the dofs (values) of a function with a given name from a given timestep. 

218 

219 Args: 

220 filename: Path to file to read from 

221 comm: MPI communicator used in storage 

222 name: Name of the function to read the dofs for 

223 time: Time stamp associated with the function to read 

224 backend_args: Arguments to backend 

225 

226 Returns: 

227 Contiguous sequence of degrees of freedom (with respect to input data) 

228 and the global starting point on the process. 

229 Process 0 has [0, M), process 1 [M, N), process 2 [N, O) etc. 

230 """ 

231 

232 def read_cell_perms( 

233 self, comm: MPI.Intracomm, filename: Path | str, backend_args: dict[str, Any] | None 

234 ) -> npt.NDArray[np.uint32]: 

235 """ 

236 Read cell permutation from file with given communicator, 

237 Split in continuous chunks based on number of cells in the input data. 

238 

239 Args: 

240 comm: MPI communicator used in storage 

241 filename: Path to file to read from 

242 backend_args: Arguments to backend 

243 

244 Returns: 

245 Contiguous sequence of permutations (with respect to input data) 

246 Process 0 has [0, M), process 1 [M, N), process 2 [N, O) etc. 

247 """ 

248 

249 def write_function( 

250 self, 

251 filename: Path, 

252 comm: MPI.Intracomm, 

253 u: FunctionData, 

254 time: float, 

255 mode: FileMode, 

256 backend_args: dict[str, Any] | None, 

257 ): 

258 """Write a function to file. 

259 

260 Args: 

261 comm: MPI communicator used in storage 

262 u: Internal data structure for the function data to save to file 

263 filename: Path to file to write to 

264 time: Time stamp associated with function 

265 mode: File-mode to store the function 

266 backend_args: Arguments to backend 

267 """ 

268 

269 def read_legacy_mesh( 

270 self, filename: Path | str, comm: MPI.Intracomm, group: str 

271 ) -> tuple[npt.NDArray[np.int64], npt.NDArray[np.floating], str | None]: 

272 """Read in the mesh topology, geometry and (optionally) cell type from a 

273 legacy DOLFIN HDF5-file. 

274 

275 Args: 

276 filename: Path to file to read from 

277 comm: MPI communicator used in storage 

278 group: Group in HDF5 file where mesh is stored 

279 

280 Returns: 

281 Tuple containing: 

282 - Topology as a (num_cells, num_vertices_per_cell) array of global vertex indices 

283 - Geometry as a (num_vertices, geometric_dimension) array of vertex coordinates 

284 - Cell type as a string (e.g. "tetrahedron") or None if not found 

285 """ 

286 

287 def snapshot_checkpoint( 

288 self, 

289 filename: Path | str, 

290 mode: FileMode, 

291 u: dolfinx.fem.Function, 

292 backend_args: dict[str, Any] | None, 

293 ): 

294 """Create a snapshot checkpoint of a dolfinx function. 

295 

296 Args: 

297 filename: Path to file to read from 

298 mode: File-mode to store the function 

299 u: dolfinx function to create a snapshot checkpoint for 

300 backend_args: Arguments to backend 

301 """ 

302 

303 def read_hdf5_array( 

304 self, 

305 comm: MPI.Intracomm, 

306 filename: Path | str, 

307 group: str, 

308 backend_args: dict[str, Any] | None, 

309 ) -> tuple[np.ndarray, int]: 

310 """Read an array from an HDF5 file. 

311 

312 Args: 

313 comm: MPI communicator used in storage 

314 filename: Path to file to read from 

315 group: Group in HDF5 file where array is stored 

316 backend_args: Arguments to backend 

317 

318 Returns: 

319 Tuple containing: 

320 - Numpy array read from file 

321 - Global starting point on the process. 

322 Process 0 has [0, M), process 1 [M, N), process 2 [N, O) etc. 

323 """ 

324 

325 def read_point_data( 

326 self, 

327 filename: Path | str, 

328 name: str, 

329 comm: MPI.Intracomm, 

330 time: str | float | None, 

331 backend_args: dict[str, Any] | None, 

332 ) -> tuple[np.ndarray, int]: 

333 """Read data from the nodes of a mesh. 

334 

335 Args: 

336 filename: Path to file 

337 name: Name of point data 

338 comm: Communicator to launch IO on. 

339 time: The time stamp 

340 backend_args: The backend arguments 

341 Returns: 

342 Data local to process (contiguous, no mpi comm) and local start range 

343 """ 

344 ... 

345 

346 def read_function_names( 

347 self, filename: Path | str, comm: MPI.Intracomm, backend_args: dict[str, Any] | None 

348 ) -> list[str]: 

349 """Read all function names from a file. 

350 

351 Args: 

352 filename: Path to file 

353 comm: MPI communicator to launch IO on. 

354 backend_args: Arguments to backend 

355 

356 Returns: 

357 A list of function names. 

358 """ 

359 ... 

360 

361 def read_cell_data( 

362 self, 

363 filename: Path | str, 

364 name: str, 

365 comm: MPI.Intracomm, 

366 time: str | float | None, 

367 backend_args: dict[str, Any] | None, 

368 ) -> tuple[npt.NDArray[np.int64], np.ndarray]: 

369 """Read data from the cells of a mesh. 

370 

371 Args: 

372 filename: Path to file 

373 name: Name of point data 

374 comm: Communicator to launch IO on. 

375 time: The time stamp 

376 backend_args: The backend arguments 

377 Returns: 

378 A tuple (topology, dofs) where topology contains the 

379 vertex indices of the cells, dofs the degrees of 

380 freedom within that cell. 

381 """ 

382 ... 

383 

384 def write_data( 

385 self, 

386 filename: Path | str, 

387 array_data: ArrayData, 

388 comm: MPI.Intracomm, 

389 time: str | float | None, 

390 mode: FileMode, 

391 backend_args: dict[str, Any] | None, 

392 ): 

393 """Write a 2D-array to file. 

394 

395 

396 Args: 

397 filename: Path to file 

398 array_data: Data to write to file. 

399 comm: The MPI communicator to open the writer with. 

400 time: The time stamp 

401 mode: Append or write 

402 backend_args: The backend arguments 

403 """ 

404 ... 

405 

406 

407def get_backend(backend: str) -> IOBackend: 

408 """Get backend class from backend name. 

409 

410 Args: 

411 backend: Name of the backend to get 

412 

413 Returns: 

414 Backend class 

415 """ 

416 if backend == "h5py": 

417 from .h5py import backend as H5PYInterface 

418 

419 return H5PYInterface 

420 elif backend == "adios2": 

421 from .adios2 import backend as ADIOS2Interface 

422 

423 return ADIOS2Interface 

424 elif backend == "pyvista": 

425 from .pyvista import backend as PYVISTAInterface 

426 

427 return PYVISTAInterface 

428 elif backend == "xdmf": 

429 from .xdmf import backend as XDMFInterface 

430 

431 return XDMFInterface 

432 elif backend == "vtkhdf": 

433 from .vtkhdf import backend as VTKDHFInterface 

434 

435 return VTKDHFInterface 

436 elif backend == "exodus": 

437 from .exodus import backend as EXODUSInterface 

438 

439 return EXODUSInterface 

440 else: 

441 return import_module(backend)