hub/venv/lib/python3.7/site-packages/trimesh/interfaces/gmsh.py

211 lines
7.1 KiB
Python
Raw Normal View History

import tempfile
import numpy as np
def load_gmsh(file_name, gmsh_args=None):
"""
Returns a surface mesh from CAD model in Open Cascade
Breap (.brep), Step (.stp or .step) and Iges formats
Or returns a surface mesh from 3D volume mesh using gmsh.
For a list of possible options to pass to GMSH, check:
http://gmsh.info/doc/texinfo/gmsh.html
An easy way to install the GMSH SDK is through the `gmsh-sdk`
package on PyPi, which downloads and sets up gmsh:
>>> pip install gmsh-sdk
Parameters
--------------
file_name : str
Location of the file to be imported
gmsh_args : (n, 2) list
List of (parameter, value) pairs to be passed to
gmsh.option.setNumber
max_element : float or None
Maximum length of an element in the volume mesh
Returns
------------
mesh : trimesh.Trimesh
Surface mesh of input geometry
"""
# use STL as an intermediate format
from ..exchange.stl import load_stl
# do import here to avoid very occasional segfaults
import gmsh
# start with default args for the meshing step
# Mesh.Algorithm=2 MeshAdapt/Delaunay, there are others but they may include quads
# With this planes are meshed using Delaunay and cylinders are meshed
# using MeshAdapt
args = [("Mesh.Algorithm", 2),
("Mesh.CharacteristicLengthFromCurvature", 1),
("Mesh.MinimumCirclePoints", 32)]
# add passed argument tuples last so we can override defaults
if gmsh_args is not None:
args.extend(gmsh_args)
# formats GMSH can load
supported = ['.brep', '.stp', '.step', '.igs', '.iges',
'.bdf', '.msh', '.inp', '.diff', '.mesh']
# check extensions to make sure it is supported format
if file_name is not None:
if not any(file_name.lower().endswith(e)
for e in supported):
raise ValueError(
'Supported formats are: BREP (.brep), STEP (.stp or .step), ' +
'IGES (.igs or .iges), Nastran (.bdf), Gmsh (.msh), Abaqus (*.inp), ' +
'Diffpack (*.diff), Inria Medit (*.mesh)')
else:
raise ValueError('No import since no file was provided!')
# if we initialize with sys.argv it could be anything
gmsh.initialize()
gmsh.option.setNumber("General.Terminal", 1)
gmsh.model.add('Surface_Mesh_Generation')
gmsh.open(file_name)
# create a temporary file for the results
out_data = tempfile.NamedTemporaryFile(suffix='.stl', delete=False)
# windows gets mad if two processes try to open the same file
out_data.close()
# we have to mesh the surface as these are analytic BREP formats
if any(file_name.lower().endswith(e)
for e in ['.brep', '.stp', '.step', '.igs', '.iges']):
gmsh.model.geo.synchronize()
# loop through our numbered args which do things, stuff
for arg in args:
gmsh.option.setNumber(*arg)
# generate the mesh
gmsh.model.mesh.generate(2)
# write to the temporary file
gmsh.write(out_data.name)
else:
gmsh.plugin.run("NewView")
gmsh.plugin.run("Skin")
gmsh.view.write(1, out_data.name)
# load the data from the temporary outfile
with open(out_data.name, 'rb') as f:
kwargs = load_stl(f)
return kwargs
def to_volume(mesh,
file_name=None,
max_element=None,
mesher_id=1):
"""
Convert a surface mesh to a 3D volume mesh generated by gmsh.
An easy way to install the gmsh sdk is through the gmsh-sdk
package on pypi, which downloads and sets up gmsh:
pip install gmsh-sdk
Algorithm details, although check gmsh docs for more information:
The "Delaunay" algorithm is split into three separate steps.
First, an initial mesh of the union of all the volumes in the model is performed,
without inserting points in the volume. The surface mesh is then recovered using H.
Si's boundary recovery algorithm Tetgen/BR. Then a three-dimensional version of the
2D Delaunay algorithm described above is applied to insert points in the volume to
respect the mesh size constraints.
The Frontal" algorithm uses J. Schoeberl's Netgen algorithm.
The "HXT" algorithm is a new efficient and parallel reimplementaton
of the Delaunay algorithm.
The "MMG3D" algorithm (experimental) allows to generate
anisotropic tetrahedralizations
Parameters
--------------
mesh : trimesh.Trimesh
Surface mesh of input geometry
file_name : str or None
Location to save output, in .msh (gmsh) or .bdf (Nastran) format
max_element : float or None
Maximum length of an element in the volume mesh
mesher_id : int
3D unstructured algorithms:
1: Delaunay, 4: Frontal, 7: MMG3D, 10: HXT
Returns
------------
data : None or bytes
MSH data, only returned if file_name is None
"""
# do import here to avoid very occasional segfaults
import gmsh
# checks mesher selection
if mesher_id not in [1, 4, 7, 10]:
raise ValueError('unavilable mesher selected!')
else:
mesher_id = int(mesher_id)
# set max element length to a best guess if not specified
if max_element is None:
max_element = np.sqrt(np.mean(mesh.area_faces))
if file_name is not None:
# check extensions to make sure it is supported format
if not any(file_name.lower().endswith(e)
for e in ['.bdf', '.msh', '.inp', '.diff', '.mesh']):
raise ValueError(
'Only Nastran (.bdf), Gmsh (.msh), Abaqus (*.inp), ' +
'Diffpack (*.diff) and Inria Medit (*.mesh) formats ' +
'are available!')
# exports to disk for gmsh to read using a temp file
mesh_file = tempfile.NamedTemporaryFile(suffix='.stl', delete=False)
mesh_file.close()
mesh.export(mesh_file.name)
# starts Gmsh Python API script
gmsh.initialize()
gmsh.option.setNumber("General.Terminal", 1)
gmsh.model.add('Nastran_stl')
gmsh.merge(mesh_file.name)
dimtag = gmsh.model.getEntities()[0]
dim = dimtag[0]
tag = dimtag[1]
surf_loop = gmsh.model.geo.addSurfaceLoop([tag])
gmsh.model.geo.addVolume([surf_loop])
gmsh.model.geo.synchronize()
# We can then generate a 3D mesh...
gmsh.option.setNumber("Mesh.Algorithm3D", mesher_id)
gmsh.option.setNumber("Mesh.CharacteristicLengthMax", max_element)
gmsh.model.mesh.generate(3)
dimtag2 = gmsh.model.getEntities()[1]
dim2 = dimtag2[0]
tag2 = dimtag2[1]
p2 = gmsh.model.addPhysicalGroup(dim2, [tag2])
gmsh.model.setPhysicalName(dim, p2, 'Nastran_bdf')
data = None
# if file name is None, return msh data using a tempfile
if file_name is None:
out_data = tempfile.NamedTemporaryFile(suffix='.msh', delete=False)
# windows gets mad if two processes try to open the same file
out_data.close()
gmsh.write(out_data.name)
with open(out_data.name, 'rb') as f:
data = f.read()
else:
gmsh.write(file_name)
# close up shop
gmsh.finalize()
return data