Note: This project seems inactive, I have not seen reasons to do great changes in it and use it on a routine basis. Will need to update soon to gmsh 4.0 msh format. Any help is appreciated.
[toc]
###Introduction
Parse gmsh .msh
files and execute python code accordingly. Most useful to
translate .msh
files into other file formats or into input for simulation software. Go to www.joseabell.com, all of the models I've created use gmshtranslator.
###Requirements
gmshtranslator
uses scipy. Obtain here.
###Installation
Run
python setup.py install
###Usage
Here is a short blog-post I wrote on using gmshtranslator with python and opensees to create models: http://www.joseabell.com/using-my-gmshtranslator-python-tool-to-interface-gmsh-with-opensees.html.
gmshtranslator
works by parsing a gmsh mesh in the .msh
format. Initialize by calling
from gmshtranslator import gmshtranslator
gt = gmshtranslator.gmshTranslator("filename.msh") #Initialize the gmshtranslator object, and call it `gt`.
In a nutshell, gmshtranslator
reads lines from a .msh
file, matches these lines to user defined
condition
(example condition: node belongs to a given physical group, or element of a certain type) and
executes a user defined action for the matching condition. A condition/action pair is called
a rule. Rules are classified into nodal rules and element rules, depending on what portion of
the .msh
file they're working on.
Conditions and actions are python functions (user defined) which have a given call signature. It
is by defining these rules that the user implements translation of .msh
files into some other
format. Nodal rules match patterns between the $Nodes
and $EndNodes
tags withing the .msh
file, and execute actions accordingly. Element rules do the same between the $Elements
and
$EndElements
tags. Contents of each line are passed into rules and actions functions and the
user implements what to do with them. More on this later.
Upon initializing, gmshtranslator
does an initial pass on the .msh file to recover some initial
useful information. The following member variables are available after initialization (gt
is the parser object)
gt.Nnodes
: (Integer) Number of nodes in the mesh.gt.Nelem
: (Integer) Number of elements in the mesh (total includes lines, surfaces and volume). Will typically be larger than the actual number of elements that will be used in an actual model.gt.physical_groups
: (Python list of integers) List of tags (integer identification numbers) of available physical groups defined in the file.gt.nodes_in_physical_groups
: (Python dictionary) Maps physical group number (tag, id) to nodes belonging to that group. In gmsh only elements are associated with physical tags, so the meaning of these nodes is that they connect to elements belonging to the given physical group. Therefore, as opposed to elements, nodes can belong to more than one physical group.
Once the file is initially read, the user can use the above information to do some initial memory
pre-allocation according to his or her needs. Then, the user implements rules for nodes and elements and informs gmshtranslator
of these rules. Finally, the gt.parse()
function is called
and the file is parsed using the user defined rules.
A nodal rule is composed of a condition and an action functions. A condition function has the following syntax:
nodal_condition_name(tag,x,y,z,physgroups)
gmshtranslator
calls the condition and with the arguments given above, and the function
should evaluate to True
or False
. tag
will be an integer with the node id; x
, y
, and z
will be double-precision numbers with the nodal coordinates, finally physgroups
is a list
with the physical group tags the node belongs to. An example nodal condition is given below:
def node_in_unit_box(tag,x,y,z,physgroups):
return (1.0 <= x) and ( x <= 1.0) and (1.0 <= y) and ( y <= 1.0) and (1.0 <= z) and ( z <= 1.0)
which returns True
if the node is within the unit box. Combining the different input arguments, the user can provide much functionality in the the kind of conditions that are met. Additionally,
using global variables, values can be stored for later use.
Nodal actions are functions which return no value, and are executed if the condition associated with it (within the same rule) returns true. The syntax is:
nodal_action_name(tag,x,y,z)
As before, the nodal action receives the node tag and its coordinates. Arbitrary python code can be executed at this point. The following example implementation prints out the tag and the coordinates.
def print_node_info(tag, x,y,z):
print "Node # {} is located at ({},{},{})".format(tag,x,y,z)
Element rules are analogous to nodal rules, with varying syntax, and must evaluate to a boolean thruth value. The syntax is:
element_condition_name(eletag,eletype,physgrp,nodes)
Here, eletag
is the (gmsh) element tag, eletype
is an integer which describes the type of element
associated with the line (line, brick, etc.), physgrp
is an integer tag indicating the
physical group that the element belongs to, and nodes
is an integer list with the tags of the
nodes which define the elements. (Warning FEA users!! the numbering of the nodes might not
match your application). gmshtranslator
defines some constants that can be used to simplify
the matching of eletype
to specific elements. For example, gt.hexahedron_8_node
gives the
integer tag cooresponding to an 8-node hexahedron element (5). More tags are available at the end of this
readme. As an example, the following element condition evaluates to true for 8-node bricks:
def is_hexahedron(eletag,eletype,physgrp,nodes):
return eletype==gt.hexahedron_8_node
Element actions are similar to nodal actions. The syntax is as follows:
element_action_name(eletag,eletype,physgrp,nodes)
Where eletag
is the integer element tag, eletype
is the element type as described before,
physgrp
is the integer physical group tag, and nodes
is a list of integers specifying the
nodes belonging to the element.
The following action prints a message for hexahedrons:
def print_brick_info(eletag,eletype,physgrp,nodes):
print "Hexahedron with tag # {} connects the nodes: {},{},{},{},{},{},{},{}".format(eletag, nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], nodes[6], nodes[7])
To inform gmshtranslator
of a given nodal rule call the add_nodes_rule(condition,action)
function
with the names of the user defined condition and action functions. Conditions and actions can be reused
between different rules. All of the matching rules are executed (rules are not automatically mutually exclusive).
For example, to add the above nodal rule do:
gt.add_nodes_rule(node_in_unit_box, print_node_info)
This will create a rule that will print the nodal info for all nodes within the unit box.
For elements, the function is add_elements_rule(condition,action)
. As before the actions and conditions
can be reused, and all matching rules will be executed. Nodal conditions and rules cannot be mixed with
element conditions and rules (it makes no sense, and the calling signatures are different).
For example:
gt.add_elements_rule(is_hexahedron, print_brick_info)
will add a rule that will print element info if it is a hexahedron. .msh
files usually will have
more elements that would be used or mapped directly into an FE mesh.
The following script reads mymodel.msh
and parses it. It will print out the nodal coordinates of
the nodes in the unit box and the element info only if it is an hexahedron:
from gmshtranslator import gmshtranslator
gt = gmshtranslator.gmshTranslator("filename.msh")
#Node rules
def node_in_unit_box(tag,x,y,z,physgroups):
return (1.0 <= x) and ( x <= 1.0) and (1.0 <= y) and ( y <= 1.0) and (1.0 <= z) and ( z <= 1.0)
def print_node_info(tag, x,y,z):
print "Node # {} is located at ({},{},{})".format(tag,x,y,z)
gt.add_nodes_rule(node_in_unit_box, print_node_info)
#Element rules
def is_hexahedron(eletag,eletype,physgrp,nodes):
return eletype==gt.hexahedron_8_node
def print_brick_info(eletag,eletype,physgrp,nodes):
print "Hexahedron with tag # {} connects the nodes: {},{},{},{},{},{},{},{}".format(eletag, nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], nodes[6], nodes[7])
gt.add_elements_rule(is_hexahedron, print_brick_info)
#Execute
gt.parse()
gmshtranlator
only stores the information detailed in the usage section (number of nodes, elements and physical group info).
Sometimes one needs to store some information and pass that information between the rules for more complex
mesh processing. For example, if you only want to print the brick info for the bricks that have a node
that belongs to the unit box (rather boringly assuming you don't have a phyical group set up for the unit box )
one can pass the necesary extra information to the condition using global variables.
The following script will do just that:
from gmshtranslator import gmshtranslator
gt = gmshtranslator.gmshTranslator("filename.msh")
nodes_in_unit_box = [] #A list to hold the node tags
#Node rules
def node_in_unit_box(tag,x,y,z,physgroups):
return (1.0 <= x) and ( x <= 1.0) and (1.0 <= y) and ( y <= 1.0) and (1.0 <= z) and ( z <= 1.0)
def print_node_info(tag, x,y,z):
global nodes_in_unit_box
nodes_in_unit_box.append(tag)
print "Node # {} is located at ({},{},{})".format(tag,x,y,z)
gt.add_nodes_rule(node_in_unit_box, print_node_info)
#Element rules
def is_hexahedron_in_the_unit_box(eletag,eletype,physgrp,nodes):
global nodes_in_unit_box
is_in_unit_box = False
for node in nodes:
if node in nodes_in_unit_box:
is_in_unit_box = True
break
#Will evaluate to true if element is a hexa and is in the unit box.
return eletype==gt.hexahedron_8_node and is_in_unit_box
def print_brick_info(eletag,eletype,physgrp,nodes):
print "Hexahedron with tag # {} connects the nodes: {},{},{},{},{},{},{},{}".format(eletag, nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], nodes[6], nodes[7])
gt.add_elements_rule(is_hexahedron, print_brick_info)
#Execute
gt.parse()
Avoid pitfall!! The variable nodes_in_unit_box
will only be filled when gt.parse()
is called, and
after the corresponding rule is matched. Therefore, looping over the list will only make sense after
gt.parse
is called.
As of gmsh 2.9.3 the integer element types are mapped as follows:
line_2_node
(1): 2-node line.triangle_3_node
(2): 3-node triangle.quadrangle_4_node
(3): 4-node quadrangle.tetrahedron_4_node
(4): 4-node tetrahedron.hexahedron_8_node
(5): 8-node hexahedron.prism_6_node
(6): 6-node prism.pyramid_5_node
(7): 5-node pyramid.line_3_node
(8): 3-node second order line (2 nodes associated with the vertices and 1 with the edge).triangle_6_node
(9): 6-node second order triangle (3 nodes associated with the vertices and 3 with the edges).quadrangle_9_node
(10): 9-node second order quadrangle (4 nodes associated with the vertices, 4 with the edges and 1 with the face).tetrahedron_10_node
(11): 10-node second order tetrahedron (4 nodes associated with the vertices and 6 with the edges).hexahedron_27_node
(12): 27-node second order hexahedron (8 nodes associated with the vertices, 12 with the edges, 6 with the faces and 1 with the volume).prism_18_node
(13): 18-node second order prism (6 nodes associated with the vertices, 9 with the edges and 3 with the quadrangular faces).pyramid_14_node
(14): 14-node second order pyramid (5 nodes associated with the vertices, 8 with the edges and 1 with the quadrangular face).point_1_node
(15): 1-node point.quadrangle_8_node
(16): 8-node second order quadrangle (4 nodes associated with the vertices and 4 with the edges).hexahedron_20_node
(17): 20-node second order hexahedron (8 nodes associated with the vertices and 12 with the edges).prism_15_node
(18): 15-node second order prism (6 nodes associated with the vertices and 9 with the edges).pyramid_13_node
(19): 13-node second order pyramid (5 nodes associated with the vertices and 8 with the edges).triangle_9_node_incomplete
(20): 9-node third order incomplete triangle (3 nodes associated with the vertices, 6 with the edges)triangle_10_node
(21): 10-node third order triangle (3 nodes associated with the vertices, 6 with the edges, 1 with the face)triangle_12_node_incomplete
(22): 12-node fourth order incomplete triangle (3 nodes associated with the vertices, 9 with the edges)triangle_15_node
(23): 15-node fourth order triangle (3 nodes associated with the vertices, 9 with the edges, 3 with the face)triangle_15_node_incomplete
(24): 15-node fifth order incomplete triangle (3 nodes associated with the vertices, 12 with the edges)triangle_21_node
(25): 21-node fifth order complete triangle (3 nodes associated with the vertices, 12 with the edges, 6 with the face)edge_4_node
(26): 4-node third order edge (2 nodes associated with the vertices, 2 internal to the edge)edge_5_node
(27): 5-node fourth order edge (2 nodes associated with the vertices, 3 internal to the edge)edge_6_node
(28): 6-node fifth order edge (2 nodes associated with the vertices, 4 internal to the edge)tetrahedron_20_node
(29): 20-node third order tetrahedron (4 nodes associated with the vertices, 12 with the edges, 4 with the faces)tetrahedron_35_node
(30): 35-node fourth order tetrahedron (4 nodes associated with the vertices, 18 with the edges, 12 with the faces, 1 in the volume)tetrahedron_56_node
(31): 56-node fifth order tetrahedron (4 nodes associated with the vertices, 24 with the edges, 24 with the faces, 4 in the volume)hexahedron_64_node
(92): 64-node third order hexahedron (8 nodes associated with the vertices, 24 with the edges, 24 with the faces, 8 in the volume)hexahedron_125_node
(93): 125-node fourth order hexahedron (8 nodes associated with the vertices, 36 with the edges, 54 with the faces, 27 in the volume).
Use any of these member values for parsing. For example, if gt
is the name of the parser object, then
gt.prism_15_node
evaluates to the integer number 18.