Skip to content

Commit 2ead2d7

Browse files
authored
Merge pull request #3 from scientificcomputing/dokken/add_read_legacy_meshtags
Make it possible to read MeshTags/MeshFunction from DOLFIN-Legacy with h5py and adios2 backend
2 parents 925bcc5 + ba30a65 commit 2ead2d7

File tree

9 files changed

+248
-168
lines changed

9 files changed

+248
-168
lines changed

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: 23 additions & 7 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
@@ -138,7 +138,11 @@ def read_mesh_data(
138138
grid = in_data
139139
elif isinstance(in_data, pyvista.core.composite.MultiBlock):
140140
# To handle multiblock like pvd
141-
pyvista._VTK_SNAKE_CASE_STATE = "allow"
141+
if hasattr(pyvista, "_VTK_SNAKE_CASE_STATE"):
142+
pyvista._VTK_SNAKE_CASE_STATE = "allow"
143+
else:
144+
# Compatibility with 0.47
145+
pyvista.core.vtk_snake_case._state = "allow"
142146
number_of_blocks = in_data.number_of_blocks
143147
assert number_of_blocks == 1
144148
b0 = in_data.get_block(0)
@@ -203,7 +207,11 @@ def read_point_data(
203207
grid = in_data
204208
elif isinstance(in_data, pyvista.core.composite.MultiBlock):
205209
# To handle multiblock like pvd
206-
pyvista._VTK_SNAKE_CASE_STATE = "allow"
210+
if hasattr(pyvista, "_VTK_SNAKE_CASE_STATE"):
211+
pyvista._VTK_SNAKE_CASE_STATE = "allow"
212+
else:
213+
# Compatibility with 0.47
214+
pyvista.core.vtk_snake_case._state = "allow"
207215
number_of_blocks = in_data.number_of_blocks
208216
assert number_of_blocks == 1
209217
b0 = in_data.get_block(0)
@@ -217,10 +225,10 @@ def read_point_data(
217225
else:
218226
num_components = dataset.shape[1]
219227
if np.issubdtype(dataset.dtype, np.integer):
220-
gtype = in_data.points.dtype
228+
gtype = grid.points.dtype
221229
dataset = dataset.astype(gtype)
222230
else:
223-
gtype = in_data.dtype
231+
gtype = dataset.dtype
224232
num_components, gtype = comm.bcast((num_components, gtype), root=0)
225233
local_range_start = 0
226234
else:
@@ -246,7 +254,11 @@ def read_cell_data(
246254
grid = in_data
247255
elif isinstance(in_data, pyvista.core.composite.MultiBlock):
248256
# To handle multiblock like pvd
249-
pyvista._VTK_SNAKE_CASE_STATE = "allow"
257+
if hasattr(pyvista, "_VTK_SNAKE_CASE_STATE"):
258+
pyvista._VTK_SNAKE_CASE_STATE = "allow"
259+
else:
260+
# Compatibility with 0.47
261+
pyvista.core.vtk_snake_case._state = "allow"
250262
number_of_blocks = in_data.number_of_blocks
251263
assert number_of_blocks == 1
252264
b0 = in_data.get_block(0)
@@ -351,7 +363,11 @@ def read_function_names(
351363
grid = in_data
352364
elif isinstance(in_data, pyvista.core.composite.MultiBlock):
353365
# To handle multiblock like pvd
354-
pyvista._VTK_SNAKE_CASE_STATE = "allow"
366+
if hasattr(pyvista, "_VTK_SNAKE_CASE_STATE"):
367+
pyvista._VTK_SNAKE_CASE_STATE = "allow"
368+
else:
369+
# Compatibility with 0.47
370+
pyvista.core.vtk_snake_case._state = "allow"
355371
number_of_blocks = in_data.number_of_blocks
356372
assert number_of_blocks == 1
357373
b0 = in_data.get_block(0)

0 commit comments

Comments
 (0)