Skip to content

Commit 51ccec9

Browse files
committed
Add a meshio reader
Closes gh-405
1 parent 11db134 commit 51ccec9

File tree

6 files changed

+481
-2
lines changed

6 files changed

+481
-2
lines changed

.test-conda-env-py3.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ dependencies:
3131
# for xdmf/hdf5 visualizer
3232
- h5py=*=mpi_openmpi*
3333

34+
# To test meshio reader
35+
- meshio
36+
3437
# Only needed to make pylint succeed
3538
- matplotlib-base
3639

meshmode/mesh/io.py

Lines changed: 265 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
__copyright__ = "Copyright (C) 2014 Andreas Kloeckner"
1+
__copyright__ = """
2+
Copyright (C) 2014 University of Illinois Board of Trustees
3+
Copyright (C) 2020 Miche Jansen (for the meshio cell node locations TikZ)
4+
"""
25

36
__license__ = """
47
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -20,13 +23,20 @@
2023
THE SOFTWARE.
2124
"""
2225

26+
from dataclasses import dataclass
27+
from os import PathLike
28+
from typing import BinaryIO, List, Optional, TextIO, Type, Union
29+
2330
import numpy as np
2431

2532
from gmsh_interop.reader import ( # noqa: F401
2633
FileSource, GmshMeshReceiverBase, GmshSimplexElementBase,
2734
GmshTensorProductElementBase, LiteralSource, ScriptSource, ScriptWithFilesSource)
2835

29-
from meshmode.mesh import Mesh
36+
from meshmode.mesh import (
37+
Mesh, MeshElementGroup, SimplexElementGroup, TensorProductElementGroup,
38+
_ModepyElementGroup)
39+
from meshmode.mesh.tools import find_point_permutation
3040

3141

3242
__doc__ = """
@@ -40,6 +50,7 @@
4050
.. autofunction:: from_meshpy
4151
.. autofunction:: from_vertices_and_simplices
4252
.. autofunction:: to_json
53+
.. autofunction:: read_via_meshio
4354
4455
"""
4556

@@ -455,4 +466,256 @@ def nodal_adjacency_to_json(mesh):
455466
# }}}
456467

457468

469+
# {{{ read_via_meshio
470+
471+
@dataclass(frozen=True)
472+
class _MeshIOCellTypeInfo:
473+
egrp_cls: Type[_ModepyElementGroup]
474+
dim: int
475+
order: int
476+
# in bi-unit coordinates
477+
unit_nodes: str
478+
479+
480+
_MESHIO_CELL_TYPE_INFO = {
481+
# https://github.com/inducer/meshio/blob/b2ee99842e119901349fdeee06b5bf61e01f450a/doc/cell_types.tex
482+
"line": _MeshIOCellTypeInfo(
483+
egrp_cls=SimplexElementGroup,
484+
dim=1,
485+
order=1,
486+
unit_nodes=r"""
487+
\cnode(n0,0,0,0,0,below right);
488+
\cnode(n1,2,0,0,1,below right);
489+
""",
490+
),
491+
"line3": _MeshIOCellTypeInfo(
492+
egrp_cls=SimplexElementGroup,
493+
dim=1,
494+
order=2,
495+
unit_nodes=r"""
496+
\cnode(n0,0,0,0,0,below right);
497+
\cnode(n1,2,0,0,1,below right);
498+
\cnode(n2,1,0,0,2,below right);
499+
""",
500+
),
501+
"triangle": _MeshIOCellTypeInfo(
502+
egrp_cls=SimplexElementGroup,
503+
dim=2,
504+
order=1,
505+
unit_nodes=r"""
506+
\cnode(n0,0,0,0,0,below right);
507+
\cnode(n1,2,0,0,1,below right);
508+
\cnode(n2,0,2,0,2,right);
509+
""",
510+
),
511+
"triangle6": _MeshIOCellTypeInfo(
512+
egrp_cls=SimplexElementGroup,
513+
dim=2,
514+
order=2,
515+
unit_nodes=r"""
516+
\cnode(n0,0,0,0,0,below right);
517+
\cnode(n1,2,0,0,1,below right);
518+
\cnode(n2,0,2,0,2,right);
519+
\cnode(n3,1,0,0,3,below right);
520+
\cnode(n4,1,1,0,4,right);
521+
\cnode(n5,0,1,0,5,below right);
522+
""",
523+
),
524+
"quad": _MeshIOCellTypeInfo(
525+
egrp_cls=TensorProductElementGroup,
526+
dim=2,
527+
order=1,
528+
unit_nodes=r"""
529+
\cnode(n0,0,0,0,0,below right);
530+
\cnode(n1,2,0,0,1,below right);
531+
\cnode(n2,2,2,0,2,below right);
532+
\cnode(n3,0,2,0,3,below right);
533+
""",
534+
),
535+
"quad9": _MeshIOCellTypeInfo(
536+
egrp_cls=TensorProductElementGroup,
537+
dim=2,
538+
order=2,
539+
unit_nodes=r"""
540+
\cnode(n0,0,0,0,0,below right);
541+
\cnode(n1,2,0,0,1,below right);
542+
\cnode(n2,2,2,0,2,below right);
543+
\cnode(n3,0,2,0,3,below right);
544+
\cnode(n4,1,0,0,4,below right);
545+
\cnode(n5,2,1,0,5,below right);
546+
\cnode(n6,1,2,0,6,below right);
547+
\cnode(n7,0,1,0,7,below right);
548+
\cnode(n8,1,1,0,8,below right);
549+
""",
550+
),
551+
"tetra": _MeshIOCellTypeInfo(
552+
egrp_cls=SimplexElementGroup,
553+
dim=3,
554+
order=1,
555+
unit_nodes=r"""
556+
\cnode(n0,0,0,0,0,below right);
557+
\cnode(n1,2,0,0,1,below right);
558+
\cnode(n2,0,2,0,2,below right);
559+
\cnode(n3,0,0,2,3,right);
560+
""",
561+
),
562+
"tetra10": _MeshIOCellTypeInfo(
563+
egrp_cls=SimplexElementGroup,
564+
dim=3,
565+
order=2,
566+
unit_nodes=r"""
567+
\cnode(n0,0,0,0,0,below right);
568+
\cnode(n1,2,0,0,1,below right);
569+
\cnode(n2,0,2,0,2,below right);
570+
\cnode(n3,0,0,2,3,right);
571+
\cnode(n4,1,0,0,4,below right);
572+
\cnode(n5,1,1,0,5,below right);
573+
\cnode(n6,0,1,0,6,below right);
574+
\cnode(n7,0,0,1,7,below right);
575+
\cnode(n8,1,0,1,8,below right);
576+
\cnode(n9,0,1,1,9,right);
577+
""",
578+
),
579+
"hexahedron": _MeshIOCellTypeInfo(
580+
egrp_cls=TensorProductElementGroup,
581+
dim=3,
582+
order=1,
583+
unit_nodes=r"""
584+
\cnode(n0,0,0,0,0,below right);
585+
\cnode(n1,2,0,0,1,below right);
586+
\cnode(n2,2,2,0,2,below right);
587+
\cnode(n3,0,2,0,3,below right);
588+
\cnode(n4,0,0,2,4,below right);
589+
\cnode(n5,2,0,2,5,below right);
590+
\cnode(n6,2,2,2,6,below right);
591+
\cnode(n7,0,2,2,7,below right);
592+
""",
593+
),
594+
"hexahedron27": _MeshIOCellTypeInfo(
595+
egrp_cls=TensorProductElementGroup,
596+
dim=3,
597+
order=2,
598+
unit_nodes=r""""
599+
\cnode(n0,0,0,0,0,below right);
600+
\cnode(n1,2,0,0,1,below right);
601+
\cnode(n2,2,2,0,2,below right);
602+
\cnode(n3,0,2,0,3,below right);
603+
\cnode(n4,0,0,2,4,below right);
604+
\cnode(n5,2,0,2,5,below right);
605+
\cnode(n6,2,2,2,6,below right);
606+
\cnode(n7,0,2,2,7,below right);
607+
\cnode(n8,1,0,0,8,below right);
608+
\cnode(n9,2,1,0,9,below right);
609+
\cnode(n10,1,2,0,10,below right);
610+
\cnode(n11,0,1,0,11,below right);
611+
\cnode(n12,1,0,2,12,below right);
612+
\cnode(n13,2,1,2,13,below right);
613+
\cnode(n14,1,2,2,14,below right);
614+
\cnode(n15,0,1,2,15,below right);
615+
\cnode(n16,0,0,1,16,below right);
616+
\cnode(n17,2,0,1,17,below right);
617+
\cnode(n18,2,2,1,18,below right);
618+
\cnode(n19,0,2,1,19,below right);
619+
\cnode(n20,0,1,1,20,below right);
620+
\cnode(n21,2,1,1,21,below right);
621+
\cnode(n22,1,0,1,22,below right);
622+
\cnode(n23,1,2,1,23,below right);
623+
\cnode(n24,1,1,0,24,below right);
624+
\cnode(n25,1,1,2,25,below right);
625+
\cnode(n26,1,1,1,26,below right);
626+
""",
627+
)
628+
}
629+
630+
631+
def _mio_parse_unit_nodes(dim: int, unode_str: str) -> np.ndarray:
632+
import re
633+
cnode_re = re.compile(r"^\\cnode\((.+)\);$")
634+
635+
result: List[List[int]] = []
636+
for ln in unode_str.split("\n"):
637+
ln = ln.strip()
638+
if not ln:
639+
continue
640+
match = cnode_re.match(ln)
641+
assert match
642+
node_id, c0, c1, c2, node_nr, _alignment = match.group(1).split(",")
643+
assert node_id == f"n{len(result)}"
644+
assert int(node_nr) == len(result)
645+
coords = [int(c) for c in [c0, c1, c2]]
646+
assert all(ci == 0 for ci in coords[dim:])
647+
coords = coords[:dim]
648+
result.append([c-1 for c in coords])
649+
650+
return np.array(result).T.copy()
651+
652+
653+
def _mio_cell_block_to_grp(
654+
mio_mesh, cblock, force_ambient_dim: Optional[int]
655+
) -> MeshElementGroup:
656+
try:
657+
info = _MESHIO_CELL_TYPE_INFO[cblock.type]
658+
except KeyError:
659+
raise NotImplementedError(f"meshio cell block type '{cblock.type}'")
660+
661+
unit_nodes = _mio_parse_unit_nodes(info.dim, info.unit_nodes)
662+
663+
shape = info.egrp_cls._modepy_shape_cls(info.dim)
664+
import modepy as mp
665+
ref_unit_vertices = mp.unit_vertices_for_shape(shape)
666+
unit_vertex_indices = find_point_permutation(ref_unit_vertices, unit_nodes)
667+
assert unit_vertex_indices is not None
668+
669+
assert cblock.data.shape[1] == unit_nodes.shape[1]
670+
671+
nodes = mio_mesh.points[cblock.data].transpose(2, 0, 1).copy()
672+
if force_ambient_dim is not None:
673+
assert (nodes[force_ambient_dim:] == 0).all()
674+
nodes = nodes[:force_ambient_dim].copy()
675+
676+
return info.egrp_cls.make_group(
677+
order=info.order,
678+
vertex_indices=cblock.data[:, unit_vertex_indices].astype(np.int32),
679+
nodes=nodes,
680+
unit_nodes=unit_nodes
681+
)
682+
683+
684+
def read_via_meshio(
685+
file: Union[str, PathLike, BinaryIO, TextIO],
686+
file_format: Optional[str] = None,
687+
force_ambient_dim: Optional[int] = None,
688+
) -> Mesh:
689+
from meshio import read
690+
mio_mesh = read(file, file_format)
691+
692+
max_dim = max(cblock.dim for cblock in mio_mesh.cells)
693+
694+
vertices = mio_mesh.points.T.copy()
695+
if force_ambient_dim is not None:
696+
assert (vertices[force_ambient_dim:] == 0).all()
697+
vertices = vertices[:force_ambient_dim].copy()
698+
699+
vol_groups = [
700+
_mio_cell_block_to_grp(
701+
mio_mesh, cb, force_ambient_dim=force_ambient_dim)
702+
for cb in mio_mesh.cells
703+
if cb.dim == max_dim]
704+
705+
# FIXME: This is heuristic.
706+
if len(vol_groups) == 1:
707+
is_conforming = True
708+
else:
709+
is_conforming = max_dim < 3
710+
711+
from meshmode.mesh import make_mesh
712+
return make_mesh(
713+
vertices=vertices,
714+
groups=vol_groups,
715+
is_conforming=is_conforming,
716+
)
717+
718+
# }}}
719+
720+
458721
# vim: foldmethod=marker

setup.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,6 @@ ignore_missing_imports = True
9696
9797
[mypy-pytential.*]
9898
ignore_missing_imports = True
99+
100+
[mypy-meshio.*]
101+
ignore_missing_imports = True

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def main():
5252
],
5353
extras_require={
5454
"visualization": ["h5py"],
55+
"meshio": ["meshio"],
5556
"test": ["pytest>=2.3"],
5657
},
5758
)

0 commit comments

Comments
 (0)