|
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 | +""" |
2 | 5 |
|
3 | 6 | __license__ = """ |
4 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy |
|
20 | 23 | THE SOFTWARE. |
21 | 24 | """ |
22 | 25 |
|
| 26 | +from dataclasses import dataclass |
| 27 | +from os import PathLike |
| 28 | +from typing import BinaryIO, List, Optional, TextIO, Type, Union |
| 29 | + |
23 | 30 | import numpy as np |
24 | 31 |
|
25 | 32 | from gmsh_interop.reader import ( # noqa: F401 |
26 | 33 | FileSource, GmshMeshReceiverBase, GmshSimplexElementBase, |
27 | 34 | GmshTensorProductElementBase, LiteralSource, ScriptSource, ScriptWithFilesSource) |
28 | 35 |
|
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 |
30 | 40 |
|
31 | 41 |
|
32 | 42 | __doc__ = """ |
|
40 | 50 | .. autofunction:: from_meshpy |
41 | 51 | .. autofunction:: from_vertices_and_simplices |
42 | 52 | .. autofunction:: to_json |
| 53 | +.. autofunction:: read_via_meshio |
43 | 54 |
|
44 | 55 | """ |
45 | 56 |
|
@@ -455,4 +466,256 @@ def nodal_adjacency_to_json(mesh): |
455 | 466 | # }}} |
456 | 467 |
|
457 | 468 |
|
| 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 | + |
458 | 721 | # vim: foldmethod=marker |
0 commit comments