Skip to content

Commit 37673c8

Browse files
authored
Merge pull request #997 from nschloe/hmf
hmf
2 parents c279288 + a68ec41 commit 37673c8

File tree

9 files changed

+234
-31
lines changed

9 files changed

+234
-31
lines changed

meshio/__about__.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
# Python 3.8
33
from importlib import metadata
44
except ImportError:
5-
import importlib_metadata as metadata
5+
try:
6+
import importlib_metadata as metadata
7+
except ImportError:
8+
__version__ = "unknown"
69

710
try:
811
__version__ = metadata.version("meshio")

meshio/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
flac3d,
1010
gmsh,
1111
h5m,
12+
hmf,
1213
mdpa,
1314
med,
1415
medit,
@@ -44,6 +45,7 @@
4445
"flac3d",
4546
"gmsh",
4647
"h5m",
48+
"hmf",
4749
"mdpa",
4850
"med",
4951
"medit",

meshio/hmf/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from ._hmf import read, write
2+
3+
__all__ = ["read", "write"]

meshio/hmf/_hmf.py

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import warnings
2+
3+
import meshio
4+
5+
from .._common import cell_data_from_raw, raw_from_cell_data
6+
from .._helpers import register
7+
from ..xdmf.common import meshio_to_xdmf_type, xdmf_to_meshio_type
8+
9+
10+
def read(filename):
11+
import h5py
12+
13+
with h5py.File(filename, "r") as f:
14+
assert f.attrs["type"] == "hmf"
15+
assert f.attrs["version"] == "0.1-alpha"
16+
17+
assert len(f) == 1, "only one domain supported for now"
18+
domain = f["domain"]
19+
20+
assert len(domain) == 1, "only one grid supported for now"
21+
grid = domain["grid"]
22+
23+
points = None
24+
cells = {}
25+
point_data = {}
26+
cell_data_raw = {}
27+
28+
for key, value in grid.items():
29+
if key[:8] == "Topology":
30+
cell_type = value.attrs["TopologyType"]
31+
cells[xdmf_to_meshio_type[cell_type]] = value[()]
32+
33+
elif key == "Geometry":
34+
# TODO is GeometryType really needed?
35+
assert value.attrs["GeometryType"] in ["X", "XY", "XYZ"]
36+
points = value[()]
37+
38+
elif key == "CellAttributes":
39+
for name, ca in value.items():
40+
cell_data_raw[name] = ca[()]
41+
42+
else:
43+
assert key == "NodeAttributes"
44+
for name, na in value.items():
45+
point_data[name] = na[()]
46+
47+
cell_data = cell_data_from_raw(cells, cell_data_raw)
48+
49+
return meshio.Mesh(
50+
points,
51+
cells,
52+
point_data=point_data,
53+
cell_data=cell_data,
54+
)
55+
56+
57+
def write_points_cells(filename, points, cells, **kwargs):
58+
write(filename, meshio.Mesh(points, cells), **kwargs)
59+
60+
61+
def write(filename, mesh, compression="gzip", compression_opts=4):
62+
import h5py
63+
64+
warnings.warn("Experimental file format. Format can change at any time.")
65+
with h5py.File(filename, "w") as h5_file:
66+
h5_file.attrs["type"] = "hmf"
67+
h5_file.attrs["version"] = "0.1-alpha"
68+
domain = h5_file.create_group("domain")
69+
grid = domain.create_group("grid")
70+
71+
_write_points(grid, mesh.points, compression, compression_opts)
72+
_write_cells(mesh.cells, grid, compression, compression_opts)
73+
_write_point_data(mesh.point_data, grid, compression, compression_opts)
74+
_write_cell_data(mesh.cell_data, grid, compression, compression_opts)
75+
76+
77+
def _write_points(grid, points, compression, compression_opts):
78+
geo = grid.create_dataset(
79+
"Geometry",
80+
data=points,
81+
compression=compression,
82+
compression_opts=compression_opts,
83+
)
84+
geo.attrs["GeometryType"] = "XYZ"[: points.shape[1]]
85+
86+
87+
def _write_cells(cell_blocks, grid, compression, compression_opts):
88+
for k, cell_block in enumerate(cell_blocks):
89+
xdmf_type = meshio_to_xdmf_type[cell_block.type][0]
90+
topo = grid.create_dataset(
91+
f"Topology{k}",
92+
data=cell_block.data,
93+
compression=compression,
94+
compression_opts=compression_opts,
95+
)
96+
topo.attrs["TopologyType"] = xdmf_type
97+
98+
99+
# In XDMF, the point/cell data are stored as
100+
#
101+
# <Attribute Name="phi" AttributeType="Scalar" Center="Node">
102+
# <DataItem DataType="Float" Dimensions="4" Format="HDF" Precision="8">
103+
# out.h5:/data2
104+
# </DataItem>
105+
# </Attribute>
106+
#
107+
# We cannot register multiple entries with the same name in HDF, so instead of
108+
# "Attribute", use
109+
# ```
110+
# NodeAttributes
111+
# -> name0 + data0
112+
# -> name1 + data0
113+
# -> ...
114+
# CellAttributes
115+
# -> ...
116+
# ```
117+
# Alternative:
118+
# ```
119+
# NodeAttribute0
120+
# -> name
121+
# -> data
122+
# NodeAttribute1
123+
# -> name
124+
# -> data
125+
# ...
126+
# ```
127+
# It's done similarly for Topologies (cells).
128+
#
129+
def _write_point_data(point_data, grid, compression, compression_opts):
130+
na = grid.create_group("NodeAttributes")
131+
for name, data in point_data.items():
132+
na.create_dataset(
133+
name,
134+
data=data,
135+
compression=compression,
136+
compression_opts=compression_opts,
137+
)
138+
139+
140+
def _write_cell_data(cell_data, grid, compression, compression_opts):
141+
raw = raw_from_cell_data(cell_data)
142+
ca = grid.create_group("CellAttributes")
143+
for name, data in raw.items():
144+
ca.create_dataset(
145+
name,
146+
data=data,
147+
compression=compression,
148+
compression_opts=compression_opts,
149+
)
150+
151+
152+
# TODO register all xdmf except hdf outside this try block
153+
register(
154+
"hmf",
155+
[".hmf"],
156+
read,
157+
{"hmf": write},
158+
)

meshio/vtu/_vtu.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"""
66
import base64
77
import logging
8-
import lzma
98
import re
109
import sys
1110
import zlib
@@ -25,6 +24,12 @@
2524
from .._helpers import register
2625
from .._mesh import CellBlock, Mesh
2726

27+
# Paraview 5.8.1's built-in Python doesn't have lzma.
28+
try:
29+
import lzma
30+
except ModuleNotFoundError:
31+
lzma = None
32+
2833

2934
def num_bytes_to_num_base64_chars(num_bytes):
3035
# Rounding up in integer division works by double negation since Python

meshio/xdmf/main.py

+12-16
Original file line numberDiff line numberDiff line change
@@ -351,11 +351,11 @@ def __init__(
351351
# grid, "Information", Name="Information", Value=str(len(mesh.field_data))
352352
# )
353353

354-
self.points(grid, mesh.points)
354+
self.write_points(grid, mesh.points)
355355
# self.field_data(mesh.field_data, information)
356-
self.cells(mesh.cells, grid)
357-
self.point_data(mesh.point_data, grid)
358-
self.cell_data(mesh.cell_data, grid)
356+
self.write_cells(mesh.cells, grid)
357+
self.write_point_data(mesh.point_data, grid)
358+
self.write_cell_data(mesh.cell_data, grid)
359359

360360
ET.register_namespace("xi", "https://www.w3.org/2001/XInclude/")
361361

@@ -388,15 +388,11 @@ def numpy_to_xml_string(self, data):
388388
)
389389
return os.path.basename(self.h5_filename) + ":/" + name
390390

391-
def points(self, grid, points):
392-
if points.shape[1] == 1:
393-
geometry_type = "X"
394-
elif points.shape[1] == 2:
395-
geometry_type = "XY"
396-
else:
397-
if points.shape[1] != 3:
398-
raise WriteError()
399-
geometry_type = "XYZ"
391+
def write_points(self, grid, points):
392+
if points.shape[1] > 3:
393+
raise WriteError("Can only write points up to dimension 3.")
394+
395+
geometry_type = "XYZ"[: points.shape[1]]
400396

401397
geo = ET.SubElement(grid, "Geometry", GeometryType=geometry_type)
402398
dt, prec = numpy_to_xdmf_dtype[points.dtype.name]
@@ -411,7 +407,7 @@ def points(self, grid, points):
411407
)
412408
data_item.text = self.numpy_to_xml_string(points)
413409

414-
def cells(self, cells, grid):
410+
def write_cells(self, cells, grid):
415411
if len(cells) == 1:
416412
meshio_type = cells[0].type
417413
num_cells = len(cells[0].data)
@@ -474,7 +470,7 @@ def cells(self, cells, grid):
474470
)
475471
data_item.text = self.numpy_to_xml_string(cd)
476472

477-
def point_data(self, point_data, grid):
473+
def write_point_data(self, point_data, grid):
478474
for name, data in point_data.items():
479475
att = ET.SubElement(
480476
grid,
@@ -495,7 +491,7 @@ def point_data(self, point_data, grid):
495491
)
496492
data_item.text = self.numpy_to_xml_string(data)
497493

498-
def cell_data(self, cell_data, grid):
494+
def write_cell_data(self, cell_data, grid):
499495
raw = raw_from_cell_data(cell_data)
500496
for name, data in raw.items():
501497
att = ET.SubElement(

setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = meshio
3-
version = 4.3.7
3+
version = 4.3.8
44
author = Nico Schlömer et al.
55
author_email = [email protected]
66
description = I/O for many mesh formats

test/test_hmf.py

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import helpers
2+
import numpy
3+
import pytest
4+
5+
import meshio
6+
7+
test_set_full = [
8+
helpers.line_mesh,
9+
helpers.tri_mesh,
10+
helpers.line_tri_mesh,
11+
helpers.tri_mesh_2d,
12+
helpers.triangle6_mesh,
13+
helpers.quad_mesh,
14+
helpers.quad8_mesh,
15+
helpers.tri_quad_mesh,
16+
helpers.tet_mesh,
17+
helpers.tet10_mesh,
18+
helpers.hex_mesh,
19+
helpers.hex20_mesh,
20+
helpers.add_point_data(helpers.tri_mesh, 1),
21+
helpers.add_cell_data(helpers.tri_mesh, [("a", (), numpy.float64)]),
22+
]
23+
24+
25+
@pytest.mark.parametrize("mesh", test_set_full)
26+
@pytest.mark.parametrize("compression", [None, "gzip"])
27+
def test_xdmf3(mesh, compression):
28+
def write(*args, **kwargs):
29+
return meshio.xdmf.write(*args, compression=compression, **kwargs)
30+
31+
helpers.write_read(write, meshio.xdmf.read, mesh, 1.0e-14)
32+
33+
34+
def test_generic_io():
35+
helpers.generic_io("test.hmf")
36+
# With additional, insignificant suffix:
37+
helpers.generic_io("test.0.hmf")

test/test_xdmf.py

+11-12
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,18 @@
3434

3535

3636
@pytest.mark.parametrize("mesh", test_set_full)
37-
@pytest.mark.parametrize("data_format", ["XML", "Binary", "HDF"])
38-
def test_xdmf3(mesh, data_format):
37+
@pytest.mark.parametrize(
38+
"kwargs0",
39+
[
40+
{"data_format": "XML"},
41+
{"data_format": "Binary"},
42+
{"data_format": "HDF", "compression": None},
43+
{"data_format": "HDF", "compression": "gzip"},
44+
],
45+
)
46+
def test_xdmf3(mesh, kwargs0):
3947
def write(*args, **kwargs):
40-
return meshio.xdmf.write(*args, data_format=data_format, **kwargs)
41-
42-
helpers.write_read(write, meshio.xdmf.read, mesh, 1.0e-14)
43-
44-
45-
# HDF5 compressed I/O
46-
@pytest.mark.parametrize("mesh", test_set_full)
47-
def test_compression(mesh):
48-
def write(*args, **kwargs):
49-
return meshio.xdmf.write(*args, data_format="HDF", compression="gzip", **kwargs)
48+
return meshio.xdmf.write(*args, **{**kwargs0, **kwargs})
5049

5150
helpers.write_read(write, meshio.xdmf.read, mesh, 1.0e-14)
5251

0 commit comments

Comments
 (0)