Coverage for /dolfinx-env/lib/python3.14/site-packages/io4dolfinx/backends/med/backend.py: 96%
161 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-23 13:30 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-23 13:30 +0000
1"""
2MED interface to io4dolfinx.
4MED is the HDF5 based mesh format of Salome and Code_Aster. See
5https://docs.salome-platform.org/latest/dev/MEDCoupling/developer/med-file.html
7Families and groups, in DOLFINx terms:
9- Every entity stores one *family* id (``MAI/<type>/FAM`` for elements, negative;
10 ``NOE/FAM`` for nodes, positive; ``0`` for none). Per element type, these ids are
11 the values of a :class:`dolfinx.mesh.MeshTags`, with family 0 meaning untagged.
12- ``FAS/<mesh>`` maps each family id to the named *groups* it belongs to, which
13 ``MeshTags`` has no equivalent of.
14- A family is one combination of groups, so overlapping groups (an entity in both
15 ``inlet`` and ``wall``) get a family of their own. A group is the union of its
16 families, read as a separate ``MeshTags`` with value 1.
18Element family ids are negated when read, as integral subdomain ids in DOLFINx/FFCx
19must be non-negative.
21SPDX License identifier: MIT
23Copyright: Jørgen S. Dokken and Simula Research Laboratory
24"""
26from pathlib import Path
27from typing import Any, Literal
29from mpi4py import MPI
31import basix
32import dolfinx
33import h5py
34import numpy as np
35import numpy.typing as npt
37from ...structures import ArrayData, FunctionData, MeshData, MeshTagsData, ReadMeshData
38from ...utils import check_file_exists, compute_local_range
39from .. import DEFAULT_MESH_NAME, FileMode, ReadMode, get_mesh_name
40from ..h5py.backend import h5pyfile, read_component_major_rows
42read_mode = ReadMode.parallel
44# MED element type -> (DOLFINx cell type, degree)
45_med_cell_types = {
46 "PO1": (dolfinx.mesh.CellType.point, 1),
47 "SE2": (dolfinx.mesh.CellType.interval, 1),
48 "SE3": (dolfinx.mesh.CellType.interval, 2),
49 "TR3": (dolfinx.mesh.CellType.triangle, 1),
50 "TR6": (dolfinx.mesh.CellType.triangle, 2),
51 "TR7": (dolfinx.mesh.CellType.triangle, 2),
52 "QU4": (dolfinx.mesh.CellType.quadrilateral, 1),
53 "QU8": (dolfinx.mesh.CellType.quadrilateral, 2),
54 "QU9": (dolfinx.mesh.CellType.quadrilateral, 2),
55 "TE4": (dolfinx.mesh.CellType.tetrahedron, 1),
56 "T10": (dolfinx.mesh.CellType.tetrahedron, 2),
57 "PY5": (dolfinx.mesh.CellType.pyramid, 1),
58 "P13": (dolfinx.mesh.CellType.pyramid, 2),
59 "PE6": (dolfinx.mesh.CellType.prism, 1),
60 "P15": (dolfinx.mesh.CellType.prism, 2),
61 "P18": (dolfinx.mesh.CellType.prism, 2),
62 "HE8": (dolfinx.mesh.CellType.hexahedron, 1),
63 "H20": (dolfinx.mesh.CellType.hexahedron, 2),
64 "H27": (dolfinx.mesh.CellType.hexahedron, 2),
65}
67# Node permutation ``p`` with ``dolfinx[i] = med[p[i]]`` for each supported MED element
68# type. Derived from MEDCoupling's (LGPL) MED cell model: the nodes it adds in
69# ``MEDCouplingUMesh.convertLinearCellsToQuadratic`` and the face centres of
70# ``buildDescendingConnectivity`` (for ``P18``), matched to the Basix reference points.
71# ``test_med_permutations_match_medcoupling`` checks the table when medcoupling is
72# installed.
73_med_to_dolfinx = {
74 "SE2": [0, 1],
75 "SE3": [0, 1, 2],
76 "TR3": [0, 1, 2],
77 "TR6": [0, 1, 2, 4, 5, 3],
78 "QU4": [0, 1, 3, 2],
79 "QU8": [0, 1, 3, 2, 4, 7, 5, 6],
80 "QU9": [0, 1, 3, 2, 4, 7, 5, 6, 8],
81 "TE4": [0, 1, 2, 3],
82 "T10": [0, 1, 2, 3, 9, 8, 5, 7, 6, 4],
83 "PY5": [0, 1, 3, 2, 4],
84 "PE6": [0, 1, 2, 3, 4, 5],
85 "P18": [0, 1, 2, 3, 4, 5, 6, 8, 12, 7, 13, 14, 9, 11, 10, 15, 17, 16],
86 "HE8": [0, 1, 3, 2, 4, 5, 7, 6],
87 "H20": [0, 1, 3, 2, 4, 5, 7, 6, 8, 11, 16, 9, 17, 10, 19, 18, 12, 15, 13, 14],
88 "H27": [0, 1, 3, 2, 4, 5, 7, 6, 8, 11, 16, 9, 17, 10, 19, 18, 12, 15, 13, 14]
89 + [20, 21, 24, 22, 23, 25, 26],
90}
92# Layouts with the nodal serendipity coordinate element
93_med_serendipity = ("QU8", "H20")
96def geometry_layout(med_type: str) -> tuple[dolfinx.mesh.CellType, int, bool]:
97 """Get the coordinate element layout of a MED element type.
99 Args:
100 med_type: MED element type, e.g. ``"T10"``
102 Returns:
103 Tuple containing:
104 - The DOLFINx cell type
105 - The degree of the coordinate element
106 - True if the coordinate element is the nodal serendipity element
107 (``QU8``, ``H20``), False if it is Lagrange
108 """
109 cell_type, degree = _med_cell_types[med_type]
110 return cell_type, degree, med_type in _med_serendipity
113def get_mesh_step_and_families(
114 h5file: h5py.File,
115 filename: Path | str,
116 backend_args: dict[str, Any] | None,
117 time: str | float | None,
118) -> tuple[h5py.Group, h5py.Group]:
119 """Get the groups holding the nodes, connectivity and families of a mesh.
121 In DOLFINx terms, ``NOE`` (*nœuds*) holds the geometry nodes, i.e. the coordinates
122 in ``mesh.geometry.x``, and ``MAI`` (*mailles*) the connectivity: one block per
123 element type, listing the nodes of each cell, facet, edge or point element.
125 A time-dependent mesh has one group per computation step, named by its step and
126 iteration numbers so that the names sort in time order. A step only stores data
127 that changed, and links to the earlier step for the rest.
129 Args:
130 h5file: The MED file
131 filename: Path to the file, used in error messages
132 backend_args: Arguments to backend. ``"name"`` selects the mesh; if it is not
133 given and the file holds a single mesh, that mesh is used.
134 time: Time of the step to read. Ignored for a mesh with a single step, and
135 ``None`` selects the first step.
137 Returns:
138 Tuple containing:
139 - The step group, holding ``NOE`` and ``MAI``
140 - The families of the mesh (``FAS/<mesh>``)
141 """
142 if "ENS_MAA" not in h5file.keys():
143 raise ValueError(f"Could not find a set of meshes (ENS_MAA) in {filename}.")
144 meshes = sorted(h5file["ENS_MAA"].keys())
145 name = get_mesh_name(backend_args)
146 if name not in meshes:
147 if name == DEFAULT_MESH_NAME and len(meshes) == 1:
148 name = meshes[0]
149 else:
150 raise KeyError(f"Could not find mesh '{name}' in {filename}. Available: {meshes}")
151 mesh = h5file["ENS_MAA"][name]
152 # Mesh type 0 is unstructured, 1 is a structured grid without connectivity
153 if mesh.attrs.get("TYP", 0) != 0:
154 raise NotImplementedError(f"Structured MED meshes are not supported ('{name}').")
155 if "FAS" not in h5file.keys() or name not in h5file["FAS"].keys():
156 raise ValueError(f"Could not find the families (FAS) of mesh '{name}' in {filename}.")
158 if "NOE" in mesh.keys():
159 step = mesh
160 else:
161 steps = [mesh[key] for key in sorted(mesh.keys())]
162 if len(steps) == 1 or time is None:
163 step = steps[0]
164 else:
165 times = np.array([float(s.attrs["PDT"]) for s in steps])
166 match = np.flatnonzero(np.isclose(times, float(time)))
167 if len(match) != 1:
168 raise KeyError(
169 f"Expected one step of mesh '{name}' at time {time} in {filename}, found "
170 f"{len(match)}. Available times: {times.tolist()}"
171 )
172 step = steps[match[0]]
173 if unsupported := sorted(set(step["MAI"].keys()) - _med_cell_types.keys()):
174 raise NotImplementedError(f"MED element types {unsupported} are not supported.")
175 return step, h5file["FAS"][name]
178def read_entities(
179 element_blocks: h5py.Group, dim: int, comm: MPI.Comm
180) -> tuple[str, list[int], npt.NDArray[np.int64]] | None:
181 """Read the entities of one topological dimension, distributed over all processes.
183 Args:
184 element_blocks: The group holding one connectivity block per element type
185 (``MAI`` in the MED file)
186 dim: Topological dimension of the entities
187 comm: MPI communicator to distribute the entities over
189 Returns:
190 ``None`` if the mesh has no entities of dimension ``dim``, otherwise a tuple
191 containing:
192 - The MED element type of the entities, e.g. ``"TR3"``
193 - The range ``[start, end)`` of entities read on this process
194 - Their connectivity as global input node indices in DOLFINx node order,
195 of shape ``(end - start, num_nodes_per_entity)``
196 """
197 blocks = sorted(
198 key
199 for key in element_blocks.keys()
200 if dolfinx.mesh.cell_dim(_med_cell_types[key][0]) == dim
201 )
202 if len(blocks) == 0:
203 return None
204 if len(blocks) > 1:
205 raise NotImplementedError(
206 f"Mixed element types {blocks} of dimension {dim} are not supported."
207 )
208 med_type = blocks[0]
209 block = element_blocks[med_type]
211 # Read a contiguous chunk of the entities on each process
212 num_entities_global = num_entities(block)
213 local_range = compute_local_range(comm, num_entities_global)
214 nodes = read_component_major_rows(block["NOD"], num_entities_global, local_range)
216 # MED node indices are 1-based
217 connectivity = nodes.astype(np.int64) - 1
219 # Permute the nodes of each entity from MED to DOLFINx ordering
220 if med_type not in _med_to_dolfinx:
221 raise NotImplementedError(
222 f"MED element type {med_type} has no Lagrange or nodal serendipity layout."
223 )
224 perm = _med_to_dolfinx[med_type]
225 if connectivity.shape[1] != len(perm):
226 raise ValueError(f"Expected {len(perm)} nodes per {med_type}, got {connectivity.shape[1]}.")
227 return med_type, local_range, connectivity[:, perm]
230def read_entity_families(block: h5py.Group, local_range: list[int]) -> npt.NDArray[np.int32]:
231 """Read the family id of each entity of one MED element type on this process.
233 Each entity has exactly one family, so this is a tag value per entity. Which
234 groups a family belongs to is read with :func:`read_family_groups`.
236 Args:
237 block: Connectivity block of the element type (``MAI/<med_type>``)
238 local_range: Range ``[start, end)`` of entities to read on this process
240 Returns:
241 Family ids of shape ``(end - start,)``. A block without ``FAM`` has every
242 entity in family 0, i.e. in no group.
243 """
244 if "FAM" in block.keys():
245 return block["FAM"][slice(*local_range)].astype(np.int32)
246 return np.zeros(local_range[1] - local_range[0], dtype=np.int32)
249def num_entities(block: h5py.Group) -> int:
250 """Get the global number of entities of one MED element type.
252 Args:
253 block: Connectivity block of the element type (``MAI/<med_type>``)
255 Returns:
256 The number of entities, stored as the ``NBR`` attribute of the connectivity
257 """
258 return int(block["NOD"].attrs["NBR"])
261def read_family_groups(
262 families: h5py.Group, kind: Literal["ELEME", "NOEUD"]
263) -> dict[int, list[str]]:
264 """Read which named groups each family of a mesh belongs to.
266 A family is one combination of groups, so one family can belong to several
267 groups. The family of each entity is read with :func:`read_entity_families`.
269 Args:
270 families: The families of the mesh (``FAS/<mesh>``)
271 kind: ``"ELEME"`` (*éléments*) for element families, ``"NOEUD"`` (*nœuds*)
272 for node families
274 Returns:
275 Map from family id to the names of its groups. Empty if the mesh has no
276 groups of this kind, in which case MED does not write ``kind``.
277 """
278 family_groups: dict[int, list[str]] = {}
279 if kind not in families.keys():
280 return family_groups
281 for family in families[kind].values():
282 groups = []
283 if "GRO" in family.keys():
284 for row in family["GRO"]["NOM"][()]:
285 raw = np.asarray(row).astype(np.uint8).tobytes()
286 groups.append(raw.split(b"\x00")[0].decode().rstrip())
287 family_groups[int(family.attrs["NUM"])] = groups
288 return family_groups
291def read_groups(
292 filename: Path | str, comm: MPI.Comm, backend_args: dict[str, Any] | None = None
293) -> dict[str, list[int]]:
294 """Read the element groups of a MED mesh and the families that make them up.
296 Use this to find the group names accepted by :func:`read_meshtags_data`, or to
297 decode the (negated) family ids returned for ``"cell"`` and ``"facet"``.
299 Args:
300 filename: Path to file
301 comm: MPI communicator used in storage
302 backend_args: Arguments to backend, ``"name"`` selects the mesh
304 Returns:
305 Map from group name to the negated family ids that belong to the group
306 """
307 check_file_exists(filename)
308 with h5pyfile(filename, "r", comm=comm) as h5file:
309 _, families = get_mesh_step_and_families(h5file, filename, backend_args, None)
310 family_groups = read_family_groups(families, "ELEME")
311 groups: dict[str, list[int]] = {}
312 for family, names in sorted(family_groups.items(), reverse=True):
313 for group in names:
314 groups.setdefault(group, []).append(-family)
315 return groups
318def get_default_backend_args(arguments: dict[str, Any] | None) -> dict[str, Any]:
319 args = arguments or {}
320 return args
323def write_attributes(
324 filename: Path | str,
325 comm: MPI.Comm,
326 name: str,
327 attributes: dict[str, np.ndarray],
328 backend_args: dict[str, Any] | None = None,
329):
330 """Write attributes to file.
332 Args:
333 filename: Path to file to write to
334 comm: MPI communicator used in storage
335 name: Name of the attributes
336 attributes: Dictionary of attributes to write to file
337 backend_args: Arguments to backend
338 """
339 raise NotImplementedError("The MED backend cannot write attributes.")
342def read_attributes(
343 filename: Path | str,
344 comm: MPI.Comm,
345 name: str,
346 backend_args: dict[str, Any] | None = None,
347) -> dict[str, Any]:
348 """Read attributes from file.
350 Args:
351 filename: Path to file to read from
352 comm: MPI communicator used in storage
353 name: Name of the attributes
354 backend_args: Arguments to backend
355 Returns:
356 The attributes
357 """
358 raise NotImplementedError("The MED backend cannot read attributes.")
361def snapshot_checkpoint(
362 filename: Path | str,
363 mode: FileMode,
364 u: dolfinx.fem.Function,
365 backend_args: dict[str, Any] | None,
366):
367 """Create a snapshot checkpoint of a dolfinx function.
369 Args:
370 filename: Path to file to read from
371 mode: File-mode to store the function
372 u: dolfinx function to create a snapshot checkpoint for
373 backend_args: Arguments to backend
374 """
375 raise NotImplementedError("The MED backend cannot make checkpoints.")
378def read_hdf5_array(
379 comm: MPI.Comm,
380 filename: Path | str,
381 group: str,
382 backend_args: dict[str, Any] | None,
383) -> tuple[np.ndarray, int]:
384 """Read an array from an HDF5 file.
386 Args:
387 comm: MPI communicator used in storage
388 filename: Path to file to read from
389 group: Group in HDF5 file where array is stored
390 backend_args: Arguments to backend
392 Returns:
393 Tuple containing:
394 - Numpy array read from file
395 - Global starting point on the process.
396 Process 0 has [0, M), process 1 [M, N), process 2 [N, O) etc.
397 """
398 raise NotImplementedError("The MED backend cannot read HDF5 arrays.")
401def read_timestamps(
402 filename: Path | str,
403 comm: MPI.Comm,
404 function_name: str,
405 backend_args: dict[str, Any] | None = None,
406) -> npt.NDArray[np.float64 | str]: # type: ignore[type-var]
407 """Read time-stamps from a checkpoint file.
409 Args:
410 filename: Path to file
411 comm: MPI communicator
412 function_name: Name of the function to read time-stamps for
413 backend_args: Arguments for backend, for instance file type.
415 Returns:
416 The time-stamps
417 """
418 raise NotImplementedError("The MED backend cannot read timestamps.")
421def write_mesh(
422 filename: Path | str,
423 comm: MPI.Comm,
424 mesh: MeshData,
425 backend_args: dict[str, Any] | None = None,
426 mode: FileMode = FileMode.write,
427 time: float = 0.0,
428):
429 """Write a mesh to file.
431 Args:
432 filename: Path to file to write to.
433 mesh: Internal data structure for the mesh data to save to file
434 comm: MPI communicator used in storage
435 backend_args: Arguments to backend
436 mode: Mode to use (write or append)
437 time: Time stamp
438 """
439 raise NotImplementedError("The MED backend cannot write meshes.")
442def read_mesh_data(
443 filename: Path | str,
444 comm: MPI.Comm,
445 time: str | float | None = 0.0,
446 read_from_partition: bool = False,
447 backend_args: dict[str, Any] | None = None,
448) -> ReadMeshData:
449 """Read mesh data from a MED file.
451 Only cells of the highest topological dimension are read; these must all be of
452 the same type. Lagrange layouts up to degree 2 (e.g. ``TR6``, ``T10``, ``H27``) and
453 the serendipity ``QU8`` and ``H20`` are supported. Node and cell data is read in
454 contiguous chunks on each process.
456 Args:
457 filename: Path to input file
458 comm: The MPI communciator to distribute the mesh over
459 time: Time of the step to read from a time-dependent mesh. Ignored for a mesh
460 with a single step, and ``None`` selects the first step.
461 read_from_partition: Read mesh with partition from file (not supported)
462 backend_args: Arguments to backend, ``"name"`` selects the mesh
463 Returns:
464 The mesh topology, geometry, UFL domain and partition function
465 """
466 if read_from_partition:
467 raise NotImplementedError("The MED backend cannot read partitioning data.")
468 check_file_exists(filename)
469 with h5pyfile(filename, "r", comm=comm) as h5file:
470 step, _ = get_mesh_step_and_families(h5file, filename, backend_args, time)
471 coo = step["NOE"]["COO"]
472 num_nodes = int(coo.attrs["NBR"])
473 x = read_component_major_rows(coo, num_nodes, compute_local_range(comm, num_nodes))
475 element_blocks = step["MAI"]
476 tdim = max(dolfinx.mesh.cell_dim(_med_cell_types[key][0]) for key in element_blocks.keys())
477 entities = read_entities(element_blocks, tdim, comm)
478 assert entities is not None
479 med_type, _, cells = entities
480 cell_type, degree, serendipity = geometry_layout(med_type)
481 return ReadMeshData(
482 cells=cells,
483 cell_type=dolfinx.mesh.to_string(cell_type),
484 x=x,
485 lvar=int(basix.LagrangeVariant.equispaced),
486 degree=degree,
487 partition_graph=None,
488 serendipity=serendipity,
489 )
492def write_meshtags(
493 filename: str | Path,
494 comm: MPI.Comm,
495 data: MeshTagsData,
496 backend_args: dict[str, Any] | None = None,
497):
498 """Write mesh tags to file.
500 Args:
501 filename: Path to file to write to
502 comm: MPI communicator used in storage
503 data: Internal data structure for the mesh tags to save to file
504 backend_args: Arguments to backend
505 """
506 raise NotImplementedError("The MED backend cannot write meshtags.")
509def read_meshtags_data(
510 filename: str | Path,
511 comm: MPI.Comm,
512 name: str,
513 backend_args: dict[str, Any] | None = None,
514) -> MeshTagsData:
515 """Read mesh tags from a MED file.
517 Args:
518 filename: Path to file to read from
519 comm: MPI communicator used in storage
520 name: ``"cell"`` or ``"facet"`` for the entities of the highest topological
521 dimension or one below, otherwise the name of a MED group (see
522 :func:`read_groups`)
523 backend_args: Arguments to backend, ``"name"`` selects the mesh and ``"time"`` the
524 step of a time-dependent mesh (the first step if not given)
526 Returns:
527 Internal data structure for the mesh tags read from file. For ``"cell"`` and
528 ``"facet"``, each entity is tagged with its negated MED family id, and entities
529 without a family (id 0) are skipped. For a group, its entities are tagged with
530 ``1``.
531 """
532 check_file_exists(filename)
533 with h5pyfile(filename, "r", comm=comm) as h5file:
534 time = None if backend_args is None else backend_args.get("time")
535 step, families = get_mesh_step_and_families(h5file, filename, backend_args, time)
536 element_blocks = step["MAI"]
537 keys = sorted(element_blocks.keys())
538 tdim = max(dolfinx.mesh.cell_dim(_med_cell_types[key][0]) for key in keys)
539 group_families: list[int] | None = None
540 if name in ("cell", "facet"):
541 dim = tdim if name == "cell" else tdim - 1
542 else:
543 family_groups = read_family_groups(families, "ELEME")
544 group_families = [f for f, groups in family_groups.items() if name in groups]
545 if len(group_families) == 0:
546 nodes = read_family_groups(families, "NOEUD")
547 if any(name in groups for groups in nodes.values()):
548 raise NotImplementedError(f"Group '{name}' only holds nodes.")
549 available = sorted({g for groups in family_groups.values() for g in groups})
550 raise KeyError(
551 f"Could not find group '{name}' in {filename}. Available: {available}"
552 )
554 # Find the blocks holding entities of the group. FAS does not record which
555 # element type a family is used on, and each process only reads its chunk
556 # of every block, so the reduction is needed for all processes to agree on
557 # the blocks and dimension before the collective meshtag creation.
558 in_block = np.zeros(len(keys), dtype=np.int32)
559 for i, key in enumerate(keys):
560 r = compute_local_range(comm, num_entities(element_blocks[key]))
561 in_block[i] = np.isin(
562 read_entity_families(element_blocks[key], r), group_families
563 ).any()
564 comm.Allreduce(MPI.IN_PLACE, in_block, op=MPI.MAX)
565 blocks = [key for key, found in zip(keys, in_block) if found]
566 dims = {dolfinx.mesh.cell_dim(_med_cell_types[key][0]) for key in blocks}
567 if len(dims) != 1:
568 raise RuntimeError(
569 f"Group '{name}' must hold entities of a single dimension, got {dims}."
570 )
571 dim = dims.pop()
573 entities = read_entities(element_blocks, dim, comm)
574 if entities is None:
575 # No entities of this dimension; read_meshtags sets the number of nodes
576 return MeshTagsData(
577 name=name,
578 values=np.zeros(0, dtype=np.int32),
579 indices=np.zeros((0, 0), dtype=np.int64),
580 dim=dim,
581 )
582 med_type, local_range, connectivity = entities
583 family = read_entity_families(element_blocks[med_type], local_range)
585 if group_families is None:
586 keep = family != 0
587 values = -family[keep]
588 else:
589 keep = np.isin(family, group_families)
590 values = np.ones(np.count_nonzero(keep), dtype=np.int32)
591 return MeshTagsData(name=name, values=values, indices=connectivity[keep], dim=dim)
594def read_dofmap(
595 filename: str | Path,
596 comm: MPI.Comm,
597 name: str,
598 backend_args: dict[str, Any] | None,
599) -> dolfinx.graph.AdjacencyList:
600 """Read the dofmap of a function with a given name.
602 Args:
603 filename: Path to file to read from
604 comm: MPI communicator used in storage
605 name: Name of the function to read the dofmap for
606 backend_args: Arguments to backend
608 Returns:
609 Dofmap as an :class:`dolfinx.graph.AdjacencyList`
610 """
611 raise NotImplementedError("The MED backend cannot read dofmap.")
614def read_dofs(
615 filename: str | Path,
616 comm: MPI.Comm,
617 name: str,
618 time: float,
619 backend_args: dict[str, Any] | None,
620) -> tuple[npt.NDArray[np.float32 | np.float64 | np.complex64 | np.complex128], int]:
621 """Read the dofs (values) of a function with a given name from a given timestep.
623 Args:
624 filename: Path to file to read from
625 comm: MPI communicator used in storage
626 name: Name of the function to read the dofs for
627 time: Time stamp associated with the function to read
628 backend_args: Arguments to backend
630 Returns:
631 Contiguous sequence of degrees of freedom (with respect to input data)
632 and the global starting point on the process.
633 Process 0 has [0, M), process 1 [M, N), process 2 [N, O) etc.
634 """
635 raise NotImplementedError("The MED backend cannot read dofs.")
638def read_cell_perms(
639 comm: MPI.Comm, filename: Path | str, backend_args: dict[str, Any] | None
640) -> npt.NDArray[np.uint32]:
641 """
642 Read cell permutation from file with given communicator,
643 Split in continuous chunks based on number of cells in the input data.
645 Args:
646 comm: MPI communicator used in storage
647 filename: Path to file to read from
648 backend_args: Arguments to backend
650 Returns:
651 Contiguous sequence of permutations (with respect to input data)
652 Process 0 has [0, M), process 1 [M, N), process 2 [N, O) etc.
653 """
654 raise NotImplementedError("The MED backend cannot read cell perms.")
657def write_function(
658 filename: str | Path,
659 comm: MPI.Comm,
660 u: FunctionData,
661 time: float,
662 mode: FileMode,
663 backend_args: dict[str, Any] | None = None,
664):
665 """Write a function to file.
667 Args:
668 filename: Path to file to write to
669 comm: MPI communicator used in storage
670 u: Internal data structure for the function data to save to file
671 time: Time stamp associated with function
672 mode: File-mode to store the function
673 backend_args: Arguments to backend
674 """
675 raise NotImplementedError("The MED backend cannot write functions.")
678def read_legacy_mesh(
679 filename: Path | str, comm: MPI.Comm, group: str
680) -> tuple[npt.NDArray[np.int64], npt.NDArray[np.floating], str | None]:
681 """Read in the mesh topology, geometry and (optionally) cell type from a
682 legacy DOLFIN HDF5-file.
684 Args:
685 filename: Path to file to read from
686 comm: MPI communicator used in storage
687 group: Group in HDF5 file where mesh is stored
689 Returns:
690 Tuple containing:
691 - Topology as a (num_cells, num_vertices_per_cell) array of global vertex indices
692 - Geometry as a (num_vertices, geometric_dimension) array of vertex coordinates
693 - Cell type as a string (e.g. "tetrahedron") or None if not found
694 """
695 raise NotImplementedError("The MED backend cannot read legacy meshes.")
698def read_point_data(
699 filename: Path | str,
700 name: str,
701 comm: MPI.Comm,
702 time: float | str | None,
703 backend_args: dict[str, Any] | None,
704) -> tuple[np.ndarray, int]:
705 """Read data from the nodes of a mesh.
707 Args:
708 filename: Path to file
709 name: Name of point data
710 comm: Communicator to launch IO on.
711 time: The time stamp
712 backend_args: The backend arguments
714 Returns:
715 Data local to process (contiguous, no mpi comm) and local start range
716 """
717 raise NotImplementedError("The MED backend cannot read point data.")
720def read_cell_data(
721 filename: Path | str,
722 name: str,
723 comm: MPI.Comm,
724 time: str | float | None,
725 backend_args: dict[str, Any] | None,
726) -> tuple[npt.NDArray[np.int64], np.ndarray]:
727 """Read data from the cells of a mesh.
729 Args:
730 filename: Path to file
731 name: Name of point data
732 comm: Communicator to launch IO on.
733 time: The time stamp
734 backend_args: The backend arguments
735 Returns:
736 A tuple (topology, dofs) where topology contains the
737 vertex indices of the cells, dofs the degrees of
738 freedom within that cell.
739 """
740 raise NotImplementedError("The MED backend cannot read cell data.")
743def read_function_names(
744 filename: Path | str, comm: MPI.Comm, backend_args: dict[str, Any] | None
745) -> list[str]:
746 """Read all function names from a file.
748 Args:
749 filename: Path to file
750 comm: MPI communicator to launch IO on.
751 backend_args: Arguments to backend
753 Returns:
754 A list of function names.
755 """
756 raise NotImplementedError("The MED backend cannot read function names.")
759def write_data(
760 filename: Path | str,
761 array_data: ArrayData,
762 comm: MPI.Comm,
763 time: str | float | None,
764 mode: FileMode,
765 backend_args: dict[str, Any] | None,
766):
767 """Write a 2D-array to file (distributed across proceses with MPI).
769 Args:
770 filename: Path to file
771 array_data: Data to write to file
772 comm: MPI communicator to open the file with
773 time: Time-stamp for data.
774 mode: Append or write
775 backend_args: The backend arguments
776 """
777 raise NotImplementedError("The MED backend cannot write data.")