Commit 4ad7f8b0 authored by Baptiste Durand's avatar Baptiste Durand

Merge branch 'xdmf_management' into 'master'

Xdmf management

See merge request !3
parents 0c380f40 528c5c3a
# coding: utf-8
"""
Created on 01/07/2019
@author: baptiste
1- Conversion of a mesh file MSH format (version 4) -> XDMF format,
2- Import of the mesh from the XDMF file,
3- Show facet regions and subdomains with matplotlib.
"""
import logging
import gmsh
import matplotlib.pyplot as plt
import dolfin as fe
from ho_homog import geometry, mesh_generate_2D, toolbox_FEniCS, toolbox_gmsh
# * Logging
logger = logging.getLogger(__name__) # http://sametmax.com/ecrire-des-logs-en-python/
logger.setLevel(logging.INFO)
formatter = logging.Formatter("%(asctime)s :: %(levelname)s :: %(message)s", "%H:%M")
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
stream_handler.setFormatter(formatter)
logger.addHandler(stream_handler)
geometry.init_geo_tools()
geometry.set_gmsh_option("Mesh.MshFileVersion", 4.1)
logger.info("Generating the geometry model")
a = 1
b, k = a, a / 3
r = 0.01 * a
rve_geo = mesh_generate_2D.Gmsh2DRVE.pantograph(a, b, k, r, soft_mat=True, name="panto")
logger.info("Generating the mesh")
lc_ratio = 1 / 3.0
d_min_max = (2 * r, a)
lc_min_max = (lc_ratio * r, lc_ratio * a)
rve_geo.main_mesh_refinement(d_min_max, lc_min_max, False)
rve_geo.soft_mesh_refinement(d_min_max, lc_min_max, False)
rve_geo.mesh_generate()
logger.info("Saving mesh with MSH 4 format")
gmsh.model.mesh.renumberNodes()
gmsh.model.mesh.renumberElements()
gmsh.write(str(rve_geo.mesh_abs_path))
logger.info("Mesh conversion MSH -> XDMF")
mesh_path, *_ = toolbox_gmsh.msh_conversion(
rve_geo.mesh_abs_path, ".xdmf", subdomains=True
)
logger.info("Import of mesh and partitions as MeshFunction instances")
mesh, subdomains, facet_regions = toolbox_FEniCS.xdmf_mesh(mesh_path, True)
plt.figure()
fe.plot(mesh, title="Mesh only")
plt.figure()
subdo_plt = fe.plot(subdomains, title="Subdomains")
plt.colorbar(subdo_plt)
nb_val, facets_val = toolbox_FEniCS.get_MeshFunction_val(facet_regions)
facets_val = facets_val[facets_val != 18446744073709551615]
facets_val = facets_val[facets_val != 0]
print(facets_val)
delta = max(facets_val) - max(facets_val)
cmap = plt.cm.get_cmap("viridis", max(facets_val) - min(facets_val)+1)
fig, ax = plt.subplots()
facets_plt = toolbox_FEniCS.facet_plot2d(
facet_regions, mesh, cmap=cmap, exclude_val=(0, 18446744073709551615)
)
ax.set_title("Facet regions")
cbar = fig.colorbar(facets_plt[0], ticks=list(facets_val))
cbar.ax.set_xticklabels(list(facets_val))
plt.show()
...@@ -29,7 +29,7 @@ from ho_homog import ( ...@@ -29,7 +29,7 @@ from ho_homog import (
periodicity, periodicity,
) )
from ho_homog.toolbox_FEniCS import function_errornorm from ho_homog.toolbox_FEniCS import function_errornorm
from ho_homog.toolbox_gmsh import process_gmsh_log from ho_homog.toolbox_gmsh import process_gmsh_log, msh_conversion
logger = logging.getLogger("demo_full_compare") logger = logging.getLogger("demo_full_compare")
logger_root = logging.getLogger() logger_root = logging.getLogger()
...@@ -86,7 +86,7 @@ gmsh.logger.stop() ...@@ -86,7 +86,7 @@ gmsh.logger.stop()
gmsh.model.mesh.renumberNodes() gmsh.model.mesh.renumberNodes()
gmsh.model.mesh.renumberElements() gmsh.model.mesh.renumberElements()
gmsh.write(str(rve_geo.mesh_abs_path)) gmsh.write(str(rve_geo.mesh_abs_path))
rve_path, *_ = mesh_tools.msh_conversion(rve_geo.mesh_abs_path, ".xdmf") rve_path, = msh_conversion(rve_geo.mesh_abs_path, ".xdmf")
# * Step 3 : Build the mesh of the part from the mesh of the RVE # * Step 3 : Build the mesh of the part from the mesh of the RVE
...@@ -94,7 +94,7 @@ gmsh.logger.start() ...@@ -94,7 +94,7 @@ gmsh.logger.start()
part_geo = mesh_generate_2D.Gmsh2DPartFromRVE(rve_geo, (75, 1)) part_geo = mesh_generate_2D.Gmsh2DPartFromRVE(rve_geo, (75, 1))
process_gmsh_log(gmsh.logger.get()) process_gmsh_log(gmsh.logger.get())
gmsh.logger.stop() gmsh.logger.stop()
part_path, *_ = mesh_tools.msh_conversion(part_geo.mesh_abs_path, ".xdmf") part_path, = msh_conversion(part_geo.mesh_abs_path, ".xdmf")
# * Step 4 : Defining the material properties # * Step 4 : Defining the material properties
E, nu = 1.0, 0.3 E, nu = 1.0, 0.3
......
...@@ -17,9 +17,6 @@ import numpy as np ...@@ -17,9 +17,6 @@ import numpy as np
import ho_homog.geometry as geo import ho_homog.geometry as geo
import gmsh import gmsh
from pathlib import Path
import meshio
from subprocess import run
# nice shortcuts # nice shortcuts
model = gmsh.model model = gmsh.model
...@@ -440,94 +437,4 @@ def order_curves(curves, dir_v, orientation=False): ...@@ -440,94 +437,4 @@ def order_curves(curves, dir_v, orientation=False):
return None return None
def msh_conversion(
mesh, format_: str = ".xdmf", output_dir=None, subdomain: bool = False, dim: int = 2
):
"""
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)
subdomain : bool, optional
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
-------
tuple
First element : Path to the main mesh file
Then paths to the extra files if subdomain conversion is requested.
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":
mesh = meshio.read(str(input_path))
if dim == 2:
mesh.points = mesh.points[:, :2]
geo_only = meshio.Mesh(
points=mesh.points, cells={"triangle": mesh.cells["triangle"]}
)
cell = "triangle"
face = "line"
elif dim == 3:
raise NotImplementedError("3D meshes are not supported yet.")
else:
ValueError
meshio.write(str(mesh_path), geo_only)
if subdomain:
cell_function = meshio.Mesh(
points=mesh.points,
cells={cell: mesh.cells[cell]},
cell_data={cell: {"cell_data": mesh.cell_data[cell]["gmsh:physical"]}},
)
meshio.write(str(physical_region), cell_function)
facet_function = meshio.Mesh(
points=mesh.points,
cells={face: mesh.cells[face]},
cell_data={face: {"facet_data": mesh.cell_data[face]["gmsh:physical"]}},
)
meshio.write(str(facet_region), facet_function)
if subdomain:
return (
mesh_path,
physical_region if physical_region.exist() else None,
facet_region if facet_region.exist() else None,
)
else:
return (mesh_path,)
...@@ -11,6 +11,7 @@ import logging ...@@ -11,6 +11,7 @@ import logging
import dolfin as fe import dolfin as fe
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
from pathlib import Path, PurePath
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -221,3 +222,83 @@ def _wrap_in_list(obj, name, types=type): ...@@ -221,3 +222,83 @@ def _wrap_in_list(obj, name, types=type):
"expected a (list of) %s as '%s' argument" % (str(types), name) "expected a (list of) %s as '%s' argument" % (str(types), name)
) )
return lst return lst
def xdmf_mesh(mesh_file, import_subdomains=False, facet_file="", physical_file=""):
"""Create a FeniCS mesh from a mesh file with xdmf format.
Parameters
----------
mesh_file : str or Path
Path of the file mesh (xdmf file)
import_subdomains : bool, optional
True if information about subdomains have to be imported.
The paths of the auxiliary files that contains information about subdomains
can be indicated with facet_file and physical_file.
The paths used by default are :
- "<mesh path>_facet_region.xdmf" and
- "<mesh path>_physical_region.xdmf" (for subdomains)
facet_file : str or Path, optional
Path to the mesh auxiliary file that contains subdomains data.
Defaults to "" i.e. the default path will be used.
physical_file : str or Path, optional
Path to the mesh auxiliary file that contains facet regions data.
Defaults to "" i.e. the default path will be used.
Returns
-------
Mesh / tuple
If subdomains are not requested :
- The Mesh instance
If subdomains are requested :
- The Mesh instance,
- The MeshFunction for subdomains if it exists else None,
- The MeshFunction for facets if it exists else None,
Source
------
Gist meshtagging_mvc.py, June 2018, Michal Habera
https://gist.github.com/michalhabera/bbe8a17f788192e53fd758a67cbf3bed
"""
if not isinstance(mesh_file, PurePath):
mesh_file = Path(mesh_file)
if not mesh_file.suffix == ".xdmf":
raise TypeError("Wrong suffix for the path to the mesh.")
mesh = fe.Mesh()
with fe.XDMFFile(str(mesh_file)) as f_in:
f_in.read(mesh)
if not import_subdomains:
return mesh
dim = mesh.geometric_dimension()
if not facet_file:
facet_file = mesh_file.with_name(f"{mesh_file.stem}_facet_region.xdmf")
else:
facet_file = Path(facet_file)
if not facet_file.suffix == ".xdmf":
raise TypeError("Wrong suffix for the path to facet regions.")
if not physical_file:
physical_file = mesh_file.with_name(f"{mesh_file.stem}_physical_region.xdmf")
else:
physical_file = Path(physical_file)
if not physical_file.suffix == ".xdmf":
raise TypeError("Wrong suffix for the path to subdomains.")
if not facet_file.exists():
facet_regions = None
else:
facet_vc = fe.MeshValueCollection("size_t", mesh, dim - 1)
with fe.XDMFFile(str(facet_file)) as f_in:
f_in.read(facet_vc, "facet_data")
facet_regions = fe.cpp.mesh.MeshFunctionSizet(mesh, facet_vc)
if not physical_file.exists():
subdomains = None
else:
cell_vc = fe.MeshValueCollection("size_t", mesh, dim - 1)
with fe.XDMFFile(str(physical_file)) as f_in:
f_in.read(cell_vc, "cell_data")
subdomains = fe.cpp.mesh.MeshFunctionSizet(mesh, cell_vc)
return mesh, subdomains, facet_regions
...@@ -8,6 +8,9 @@ Collection of tools designed to help users working with gmsh python API. ...@@ -8,6 +8,9 @@ Collection of tools designed to help users working with gmsh python API.
""" """
import logging import logging
from pathlib import Path
import meshio
from subprocess import run
gmsh_logger = logging.getLogger("ho_homog.gmsh") gmsh_logger = logging.getLogger("ho_homog.gmsh")
...@@ -38,3 +41,101 @@ def process_gmsh_log(gmsh_log: list, detect_error=True): ...@@ -38,3 +41,101 @@ def process_gmsh_log(gmsh_log: list, detect_error=True):
gmsh_logger.info("**********") gmsh_logger.info("**********")
if detect_error and err_msg: if detect_error and err_msg:
raise AssertionError("Gmsh logging messages signal errors.") raise AssertionError("Gmsh logging messages signal errors.")
def msh_conversion(
mesh, format_: str = ".xdmf", output_dir=None, subdomains: bool = False, dim: int = 2
):
"""
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)
subdomains : bool, optional
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":
mesh = meshio.read(str(input_path))
if dim == 2:
mesh.points = mesh.points[:, :2]
geo_only = meshio.Mesh(
points=mesh.points, cells={"triangle": mesh.cells["triangle"]}
)
cell = "triangle"
face = "line"
elif dim == 3:
raise NotImplementedError("3D meshes are not supported yet.")
else:
ValueError
meshio.write(str(mesh_path), geo_only)
if subdomains:
cell_function = meshio.Mesh(
points=mesh.points,
cells={cell: mesh.cells[cell]},
cell_data={cell: {"cell_data": mesh.cell_data[cell]["gmsh:physical"]}},
)
meshio.write(str(physical_region), cell_function)
facet_function = meshio.Mesh(
points=mesh.points,
cells={face: mesh.cells[face]},
cell_data={face: {"facet_data": mesh.cell_data[face]["gmsh:physical"]}},
)
meshio.write(str(facet_region), facet_function)
if subdomains:
return (
mesh_path,
physical_region if physical_region.exists() else None,
facet_region if facet_region.exists() else None,
)
else:
return mesh_path
...@@ -5,8 +5,8 @@ Created on 12/06/2019 ...@@ -5,8 +5,8 @@ Created on 12/06/2019
""" """
from ho_homog import geometry as geo from ho_homog import geometry as geo
from ho_homog import mesh_tools
from ho_homog import mesh_generate_2D as mesh2D from ho_homog import mesh_generate_2D as mesh2D
from ho_homog.toolbox_gmsh import msh_conversion
import gmsh import gmsh
geo.init_geo_tools() geo.init_geo_tools()
...@@ -30,5 +30,5 @@ def test_rve_2_part(): ...@@ -30,5 +30,5 @@ def test_rve_2_part():
gmsh.model.mesh.renumberElements() gmsh.model.mesh.renumberElements()
gmsh.write(str(panto_rve.mesh_abs_path)) gmsh.write(str(panto_rve.mesh_abs_path))
panto_part = mesh2D.Gmsh2DPartFromRVE(panto_rve, (2, 3)) panto_part = mesh2D.Gmsh2DPartFromRVE(panto_rve, (2, 3))
mesh_tools.msh_conversion(panto_rve.mesh_abs_path, ".xdmf") msh_conversion(panto_rve.mesh_abs_path, ".xdmf")
mesh_tools.msh_conversion(panto_part.mesh_abs_path, ".xdmf") msh_conversion(panto_part.mesh_abs_path, ".xdmf")
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment