toolbox_gmsh.py 5.34 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
# coding: utf-8
"""
Created on 13/06/2019
@author: Baptiste Durand, baptiste.durand@enpc.fr

Collection of tools designed to help users working with gmsh python API.

"""

import logging
11 12 13
from pathlib import Path
import meshio
from subprocess import run
Baptiste Durand's avatar
Baptiste Durand committed
14
import gmsh
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

gmsh_logger = logging.getLogger("ho_homog.gmsh")


def process_gmsh_log(gmsh_log: list, detect_error=True):
    """Treatment of log messages emitted by the gmsh API."""
    err_msg, warn_msg = list(), list()
    for line in gmsh_log:
        if "error" in line.lower():
            err_msg.append(line)
        if "warning" in line.lower():
            warn_msg.append(line)
    gmsh_logger.info("**********")
    gmsh_logger.info(
        f"{len(gmsh_log)} logging messages from Gmsh : "
        f"{len(err_msg)} errors, {len(warn_msg)} warnings."
    )
    if err_msg:
        gmsh_logger.error("Gmsh errors details :")
        for line in err_msg:
            gmsh_logger.error(line)
    if warn_msg:
        gmsh_logger.warning("Gmsh warnings details :")
        for line in warn_msg:
            gmsh_logger.warning(line)
    gmsh_logger.debug("All gmsh messages :")
    gmsh_logger.debug(gmsh_log)
    gmsh_logger.info("**********")
    if detect_error and err_msg:
        raise AssertionError("Gmsh logging messages signal errors.")
45 46


47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
def conversion_to_xdmf(i_path, o_path, cell_reg, facet_reg, dim, subdomains=False):
    """Convert a ".msh" mesh generated with Gmsh to a xdmf mesh file.

    Parameters
    ----------
    i_path : Path
    o_path : Path
        Desired path for the converted mesh
    cell_reg : Path
        Desired path of the extra file for cell regions if subdomains are retained.
    facet_reg : Path
        Desired path of the extra file for facet regions if subdomains are retained.
    dim: int
        Geometrical dimension of the mesh (2D or 3D).
    subdomains : bool, optional
        If True, extra files are created to store information about subdomains.
        (default: False)
    """
    m = meshio.read(str(i_path))
    if dim == 2:
        m.points = m.points[:, :2]
        geo_only = meshio.Mesh(points=m.points, cells={"triangle": m.cells["triangle"]})
        cell = "triangle"
        face = "line"
    elif dim == 3:
        raise NotImplementedError("3D meshes are not supported yet.")
    else:
        ValueError
    meshio.write(str(o_path), geo_only)
    if subdomains:
        cell_funct = meshio.Mesh(
            points=m.points,
            cells={cell: m.cells[cell]},
            cell_data={cell: {"cell_data": m.cell_data[cell]["gmsh:physical"]}},
        )
        meshio.write(str(cell_reg), cell_funct)
        facet_funct = meshio.Mesh(
            points=m.points,
            cells={face: m.cells[face]},
            cell_data={face: {"facet_data": m.cell_data[face]["gmsh:physical"]}},
        )
        meshio.write(str(facet_reg), facet_funct)
    return True


92
def msh_conversion(
93 94 95 96 97
    mesh,
    format_: str = ".xdmf",
    output_dir=None,
    subdomains: bool = False,
    dim: int = 2,
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
):
    """
    Convert a ".msh" mesh generated with Gmsh to a format suitable for FEniCS.

    Parameters
    ----------
    mesh : Path or str
        Path that points to the existing mesh file.
    format : str
        Suffix desired for the mesh file. (default: ".xdmf")
        Supported suffixes :
            - ".xdmf"
            - ".xml" (DOLFIN xml format)
    output_dir : Path, optional
        Path of the directory where the converted mesh file must be written.
        If None, the converted file is written in the same directory
        as the input file. (default: None)
115
    subdomains : bool, optional
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
        If True, extra files are created to store information about subdomains.
        (default: False)
    dim: int, optional
        Geometrical dimension of the mesh (2D or 3D). (default: 2)

    Returns
    -------
    Path / tuple
    If subdomain conversion is not requested :
        - Path to the mesh file
    If subdomain conversion is requested :
        - Path to the mesh file,
        - Path to the extra files for subdomains if it exists else None,
        - Path to the extra files for facet regions if it exists else None,

    Warning
    -------
    A specific version of the MSH format should be use in accordance with the
    desired output format :
        - ".xml" output -> MSH file format version 2;
        - ".xdmf" output -> MSH file format version 4.
    """
    input_path = Path(mesh)
    name = input_path.stem

    if format_ not in (".xml", ".xdmf"):
        raise TypeError
    mesh_path = input_path.with_suffix(format_)
    if output_dir:
        mesh_path = output_dir.joinpath(mesh_path.name)
    physical_region = mesh_path.with_name(name + "_physical_region" + format_)
    facet_region = mesh_path.with_name(name + "_facet_region" + format_)
    if physical_region.exists():
        physical_region.unlink()
    if facet_region.exists():
        facet_region.unlink()

    if format_ == ".xml":
        cmd = f"dolfin-convert {input_path} {mesh_path}"
        run(cmd, shell=True, check=True)
    elif format_ == ".xdmf":
157 158 159
        conversion_to_xdmf(
            input_path, mesh_path, physical_region, facet_region, dim, subdomains
        )
160
    if subdomains:
161 162
        return (
            mesh_path,
163 164
            physical_region if physical_region.exists() else None,
            facet_region if facet_region.exists() else None,
165 166 167
        )
    else:
        return mesh_path