Skip to content

Commit 352851e

Browse files
committed
Resolve jorgensd/adios4dolfinx#17 by adding interfaces for legacy meshtags in both adios2 and h5py backend
1 parent 925bcc5 commit 352851e

9 files changed

Lines changed: 225 additions & 165 deletions

File tree

src/adios4dolfinx/backends/adios2/backend.py

Lines changed: 111 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ def get_default_backend_args(arguments: dict[str, Any] | None) -> dict[str, Any]
3131
args = arguments or {}
3232
if "engine" not in args.keys():
3333
args["engine"] = "BP4"
34+
if "legacy" not in args.keys():
35+
args["legacy"] = False # Only used for legacy HDF5 meshtags
3436
return args
3537

3638

@@ -71,7 +73,7 @@ def write_attributes(
7173
filename=filename,
7274
mode=adios2.Mode.Append,
7375
io_name="AttributeWriter",
74-
**backend_args,
76+
engine=backend_args["engine"],
7577
) as adios_file:
7678
adios_file.file.BeginStep()
7779

@@ -105,7 +107,7 @@ def read_attributes(
105107
adios=adios,
106108
filename=filename,
107109
mode=adios2.Mode.Read,
108-
**backend_args,
110+
engine=backend_args["engine"],
109111
io_name="AttributesReader",
110112
) as adios_file:
111113
adios_file.file.BeginStep()
@@ -143,7 +145,7 @@ def read_timestamps(
143145
adios=adios,
144146
filename=filename,
145147
mode=adios2.Mode.Read,
146-
**backend_args,
148+
engine=backend_args["engine"],
147149
io_name="TimestepReader",
148150
) as adios_file:
149151
time_name = f"{function_name}_time"
@@ -195,7 +197,8 @@ def write_mesh(
195197
filename=filename,
196198
mode=mode,
197199
comm=comm,
198-
**backend_args,
200+
engine=backend_args["engine"],
201+
io_name=backend_args["io_name"],
199202
) as adios_file:
200203
adios_file.file.BeginStep()
201204
# Write geometry
@@ -473,73 +476,126 @@ def read_meshtags_data(
473476
"""
474477

475478
adios = adios2.ADIOS(comm)
476-
backend_args = {} if backend_args is None else backend_args
479+
backend_args = get_default_backend_args(backend_args)
477480
io_name = backend_args.get("io_name", "MeshTagsReader")
478-
engine = backend_args.get("engine", "BP4")
481+
engine = backend_args["engine"]
482+
legacy = backend_args["legacy"]
479483
with ADIOSFile(
480484
adios=adios,
481485
filename=filename,
482486
mode=adios2.Mode.Read,
483487
engine=engine,
484488
io_name=io_name,
485489
) as adios_file:
486-
# Get mesh cell type
487-
dim_attr_name = f"{name}_dim"
488-
step = 0
489-
for i in range(adios_file.file.Steps()):
490-
adios_file.file.BeginStep()
491-
if dim_attr_name in adios_file.io.AvailableAttributes().keys():
492-
step = i
493-
break
494-
adios_file.file.EndStep()
495-
if dim_attr_name not in adios_file.io.AvailableAttributes().keys():
496-
raise KeyError(f"{dim_attr_name} not found in {filename}")
490+
if not legacy:
491+
# Get mesh cell type
492+
dim_attr_name = f"{name}_dim"
493+
step = 0
494+
for i in range(adios_file.file.Steps()):
495+
adios_file.file.BeginStep()
496+
if dim_attr_name in adios_file.io.AvailableAttributes().keys():
497+
step = i
498+
break
499+
adios_file.file.EndStep()
500+
if dim_attr_name not in adios_file.io.AvailableAttributes().keys():
501+
raise KeyError(f"{dim_attr_name} not found in {filename}")
497502

498-
m_dim = adios_file.io.InquireAttribute(dim_attr_name)
499-
dim = int(m_dim.Data()[0])
503+
m_dim = adios_file.io.InquireAttribute(dim_attr_name)
504+
dim = int(m_dim.Data()[0])
500505

501-
# Get mesh tags entites
502-
topology_name = f"{name}_topology"
503-
for i in range(step, adios_file.file.Steps()):
504-
if i > step:
505-
adios_file.file.BeginStep()
506-
if topology_name in adios_file.io.AvailableVariables().keys():
507-
break
508-
adios_file.file.EndStep()
509-
if topology_name not in adios_file.io.AvailableVariables().keys():
510-
raise KeyError(f"{topology_name} not found in {filename}")
506+
# Get mesh tags entites
507+
topology_name = f"{name}_topology"
508+
for i in range(step, adios_file.file.Steps()):
509+
if i > step:
510+
adios_file.file.BeginStep()
511+
if topology_name in adios_file.io.AvailableVariables().keys():
512+
break
513+
adios_file.file.EndStep()
514+
if topology_name not in adios_file.io.AvailableVariables().keys():
515+
raise KeyError(f"{topology_name} not found in {filename}")
516+
517+
topology = adios_file.io.InquireVariable(topology_name)
518+
top_shape = topology.Shape()
519+
topology_range = compute_local_range(comm, top_shape[0])
520+
521+
topology.SetSelection(
522+
[
523+
[topology_range[0], 0],
524+
[topology_range[1] - topology_range[0], top_shape[1]],
525+
]
526+
)
527+
mesh_entities = np.empty(
528+
(topology_range[1] - topology_range[0], top_shape[1]), dtype=np.int64
529+
)
530+
adios_file.file.Get(topology, mesh_entities, adios2.Mode.Deferred)
531+
532+
# Get mesh tags values
533+
values_name = f"{name}_values"
534+
if values_name not in adios_file.io.AvailableVariables().keys():
535+
raise KeyError(f"{values_name} not found")
536+
537+
values = adios_file.io.InquireVariable(values_name)
538+
val_shape = values.Shape()
539+
assert val_shape[0] == top_shape[0]
540+
values.SetSelection([[topology_range[0]], [topology_range[1] - topology_range[0]]])
541+
tag_values = np.empty(
542+
(topology_range[1] - topology_range[0]), dtype=values.Type().strip("_t")
543+
)
544+
adios_file.file.Get(values, tag_values, adios2.Mode.Deferred)
511545

512-
topology = adios_file.io.InquireVariable(topology_name)
513-
top_shape = topology.Shape()
514-
topology_range = compute_local_range(comm, top_shape[0])
546+
adios_file.file.PerformGets()
547+
adios_file.file.EndStep()
548+
else:
549+
# Get mesh cell type
550+
dim_attr_name = f"{name}_dim"
551+
assert adios_file.file.Steps() == 0
552+
if (ct_key := f"/{name}/topology/celltype") in adios_file.io.AvailableAttributes():
553+
cell_type = adios_file.io.InquireAttribute(ct_key)
554+
else:
555+
raise ValueError(f"Celltype not found for meshtags {name} in {filename}.")
556+
dim = dolfinx.mesh.cell_dim(dolfinx.mesh.to_type(cell_type.DataString()[0]))
557+
558+
# Get mesh tags entites
559+
if (top_key := f"/{name}/topology") in adios_file.io.AvailableVariables():
560+
topology = adios_file.io.InquireVariable(top_key)
561+
else:
562+
raise ValueError(f"Topology not found for meshtags {name} in {filename}.")
563+
564+
top_shape = topology.Shape()
565+
topology_range = compute_local_range(comm, top_shape[0])
566+
567+
topology.SetSelection(
568+
[
569+
[topology_range[0], 0],
570+
[topology_range[1] - topology_range[0], top_shape[1]],
571+
]
572+
)
573+
mesh_entities = np.empty(
574+
(topology_range[1] - topology_range[0], top_shape[1]), dtype=np.int64
575+
)
576+
adios_file.file.Get(topology, mesh_entities, adios2.Mode.Deferred)
515577

516-
topology.SetSelection(
517-
[
518-
[topology_range[0], 0],
519-
[topology_range[1] - topology_range[0], top_shape[1]],
520-
]
521-
)
522-
mesh_entities = np.empty(
523-
(topology_range[1] - topology_range[0], top_shape[1]), dtype=np.int64
524-
)
525-
adios_file.file.Get(topology, mesh_entities, adios2.Mode.Deferred)
578+
# Get mesh tags values
579+
if (val_key := f"/{name}/values") in adios_file.io.AvailableVariables():
580+
values = adios_file.io.InquireVariable(val_key)
581+
else:
582+
raise ValueError(f"Values not found for meshtags {name} in {filename}.")
526583

527-
# Get mesh tags values
528-
values_name = f"{name}_values"
529-
if values_name not in adios_file.io.AvailableVariables().keys():
530-
raise KeyError(f"{values_name} not found")
584+
val_shape = values.Shape()
585+
assert val_shape[0] == top_shape[0]
531586

532-
values = adios_file.io.InquireVariable(values_name)
533-
val_shape = values.Shape()
534-
assert val_shape[0] == top_shape[0]
535-
values.SetSelection([[topology_range[0]], [topology_range[1] - topology_range[0]]])
536-
tag_values = np.empty((topology_range[1] - topology_range[0]), dtype=np.int32)
537-
adios_file.file.Get(values, tag_values, adios2.Mode.Deferred)
587+
values.SetSelection([[topology_range[0]], [topology_range[1] - topology_range[0]]])
588+
tag_values = np.empty(
589+
(topology_range[1] - topology_range[0]), dtype=values.Type().strip("_t")
590+
)
591+
adios_file.file.Get(values, tag_values, adios2.Mode.Deferred)
538592

539-
adios_file.file.PerformGets()
540-
adios_file.file.EndStep()
593+
adios_file.file.PerformGets()
594+
adios_file.file.EndStep()
541595

542-
return MeshTagsData(name=name, values=tag_values, indices=mesh_entities, dim=dim)
596+
return MeshTagsData(
597+
name=name, values=tag_values.astype(np.int32), indices=mesh_entities, dim=dim
598+
)
543599

544600

545601
def read_dofmap(

src/adios4dolfinx/backends/h5py/backend.py

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from mpi4py import MPI
1414

1515
import dolfinx
16+
import h5py
1617
import numpy as np
1718
import numpy.typing as npt
1819
from dolfinx.graph import adjacencylist
@@ -23,6 +24,10 @@
2324

2425
read_mode = ReadMode.parallel
2526

27+
# try:
28+
# except ModuleNotFoundError:
29+
# raise ModuleNotFoundError("This backend requires h5py to be installed.")
30+
2631

2732
@contextlib.contextmanager
2833
def h5pyfile(h5name, filemode="r", force_serial: bool = False, comm=None):
@@ -35,8 +40,6 @@ def h5pyfile(h5name, filemode="r", force_serial: bool = False, comm=None):
3540
comm: The MPI communicator
3641
3742
"""
38-
import h5py
39-
4043
if comm is None:
4144
comm = MPI.COMM_WORLD
4245

@@ -58,13 +61,7 @@ def h5pyfile(h5name, filemode="r", force_serial: bool = False, comm=None):
5861

5962

6063
def get_default_backend_args(arguments: dict[str, Any] | None) -> dict[str, Any]:
61-
args = arguments or {}
62-
63-
if arguments:
64-
# Currently no default arguments for h5py backend
65-
# TODO: Pehaps we would like to make this into a warning instead?
66-
raise RuntimeError("Unexpected backend arguments to h5py backend")
67-
64+
args = arguments or {"legacy": False} # If meshtags is read from legacy
6865
return args
6966

7067

@@ -384,20 +381,32 @@ def read_meshtags_data(
384381
Internal data structure for the mesh tags read from file
385382
"""
386383
backend_args = get_default_backend_args(backend_args)
384+
legacy = backend_args["legacy"]
387385
with h5pyfile(filename, filemode="r", comm=comm, force_serial=False) as h5file:
388-
if "mesh" not in h5file.keys():
389-
raise KeyError("No mesh found")
390-
mesh = h5file["mesh"]
391-
if "tags" not in mesh.keys():
392-
raise KeyError("Could not find 'tags' in file, are you sure this is a checkpoint?")
393-
tags = mesh["tags"]
394-
if name not in tags.keys():
395-
raise KeyError(f"Could not find {name} in '/mesh/tags/' in {filename}")
396-
tag = tags[name]
397-
398-
dim = tag.attrs["dim"]
399-
topology = tag["Topology"]
400-
values = tag["Values"]
386+
if legacy:
387+
if name not in h5file.keys():
388+
raise RuntimeError(f"MeshTag {name} not found in {filename}.")
389+
mesh = h5file[name]
390+
topology = mesh["topology"]
391+
cell_type = topology.attrs["celltype"]
392+
if isinstance(cell_type, np.bytes_):
393+
cell_type = cell_type.decode("utf-8")
394+
dim = dolfinx.mesh.cell_dim(dolfinx.mesh.to_type(cell_type))
395+
values = mesh["values"]
396+
else:
397+
if "mesh" not in h5file.keys():
398+
raise KeyError("No mesh found")
399+
mesh = h5file["mesh"]
400+
if "tags" not in mesh.keys():
401+
raise KeyError("Could not find 'tags' in file, are you sure this is a checkpoint?")
402+
tags = mesh["tags"]
403+
if name not in tags.keys():
404+
raise KeyError(f"Could not find {name} in '/mesh/tags/' in {filename}")
405+
tag = tags[name]
406+
407+
dim = tag.attrs["dim"]
408+
topology = tag["Topology"]
409+
values = tag["Values"]
401410
num_entities_global = topology.shape[0]
402411
topology_range = compute_local_range(comm, num_entities_global)
403412
indices = topology[slice(*topology_range), :].astype(np.int64)

src/adios4dolfinx/backends/pyvista/backend.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
try:
1111
import pyvista
1212
except ImportError:
13-
raise ModuleNotFoundError("This module requires pyvista")
13+
raise ModuleNotFoundError("The PyVista-backend requires pyvista")
1414
from pathlib import Path
1515

1616
from mpi4py import MPI
@@ -217,10 +217,10 @@ def read_point_data(
217217
else:
218218
num_components = dataset.shape[1]
219219
if np.issubdtype(dataset.dtype, np.integer):
220-
gtype = in_data.points.dtype
220+
gtype = grid.points.dtype
221221
dataset = dataset.astype(gtype)
222222
else:
223-
gtype = in_data.dtype
223+
gtype = dataset.dtype
224224
num_components, gtype = comm.bcast((num_components, gtype), root=0)
225225
local_range_start = 0
226226
else:

src/adios4dolfinx/backends/vtkhdf/backend.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from adios4dolfinx.utils import check_file_exists, compute_local_range
1818

1919
from .. import FileMode, ReadMode
20-
from ..h5py.backend import convert_file_mode, h5pyfile
20+
from .._h5py.backend import convert_file_mode, h5pyfile
2121
from ..pyvista.backend import _arbitrary_lagrange_vtk, _cell_degree, _first_order_vtk
2222

2323
read_mode = ReadMode.parallel

src/adios4dolfinx/backends/xdmf/backend.py

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
Module that uses DOLFINx/H%py to import XDMF files.
33
"""
44

5-
import contextlib
65
from pathlib import Path
76
from typing import Any
87
from xml.etree import ElementTree
@@ -18,43 +17,11 @@
1817
from adios4dolfinx.utils import check_file_exists, compute_local_range
1918

2019
from .. import FileMode, ReadMode
20+
from ..h5py.backend import h5pyfile
2121

2222
read_mode = ReadMode.parallel
2323

2424

25-
@contextlib.contextmanager
26-
def h5pyfile(h5name, filemode="r", force_serial: bool = False, comm=None):
27-
"""Context manager for opening an HDF5 file with h5py.
28-
29-
Args:
30-
h5name: The name of the HDF5 file.
31-
filemode: The file mode.
32-
force_serial: Force serial access to the file.
33-
comm: The MPI communicator
34-
35-
"""
36-
import h5py
37-
38-
if comm is None:
39-
comm = MPI.COMM_WORLD
40-
41-
if h5py.h5.get_config().mpi and not force_serial:
42-
h5file = h5py.File(h5name, filemode, driver="mpio", comm=comm)
43-
else:
44-
if comm.size > 1 and not force_serial:
45-
raise ValueError(
46-
f"h5py is not installed with MPI support, while using {comm.size} processes.",
47-
"If you really want to do this, turn on the `force_serial` flag.",
48-
)
49-
h5file = h5py.File(h5name, filemode)
50-
51-
try:
52-
yield h5file
53-
finally:
54-
if h5file.id:
55-
h5file.close()
56-
57-
5825
def extract_function_names_and_timesteps(filename: Path | str) -> dict[str, list[str]]:
5926
tree = ElementTree.parse(filename)
6027
root = tree.getroot()

0 commit comments

Comments
 (0)