-
Notifications
You must be signed in to change notification settings - Fork 11
/
msh2xdmf.py
executable file
·265 lines (243 loc) · 8.34 KB
/
msh2xdmf.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
#!/usr/bin/env python
import argparse
import meshio
import os
import numpy as np
from configparser import ConfigParser
try:
from dolfin import XDMFFile, Mesh, MeshValueCollection
from dolfin.cpp.mesh import MeshFunctionSizet
except ImportError:
print("Could not import dolfin. Continuing without Dolfin support.")
def msh2xdmf(mesh_name, dim=2, directory="."):
"""
Function converting a MSH mesh into XDMF files.
The XDMF files are:
- "domain.xdmf": the domain;
- "boundaries.xdmf": the boundaries physical groups from GMSH;
"""
# Get the mesh name has prefix
prefix = mesh_name.split('.')[0]
# Read the input mesh
msh = meshio.read("{}/{}".format(directory, mesh_name))
# Generate the domain XDMF file
export_domain(msh, dim, directory, prefix)
# Generate the boundaries XDMF file
export_boundaries(msh, dim, directory, prefix)
# Export association table
export_association_table(msh, prefix, directory)
def export_domain(msh, dim, directory, prefix):
"""
Export the domain XDMF file as well as the subdomains values.
"""
# Set cell type
if dim == 2:
cell_type = "triangle"
elif dim == 3:
cell_type = "tetra"
# Generate the cell block for the domain cells
data_array = [arr for (t, arr) in msh.cells if t == cell_type]
if len(data_array) == 0:
print("WARNING: No domain physical group found.")
return
else:
data = np.concatenate(data_array)
cells = [
meshio.CellBlock(
type=cell_type,
data=data,
)
]
# Generate the domain cells data (for the subdomains)
try:
cell_data = {
"subdomains": [
np.concatenate(
[
msh.cell_data["gmsh:physical"][i]
for i, cellBlock in enumerate(msh.cells)
if cellBlock.type == cell_type
]
)
]
}
except KeyError:
raise ValueError(
"""
No physical group found for the domain.
Define the domain physical group.
- if dim=2, the domain is a surface
- if dim=3, the domain is a volume
"""
)
# Generate a meshio Mesh for the domain
domain = meshio.Mesh(
points=msh.points[:, :dim],
cells=cells,
cell_data=cell_data,
)
# Export the XDMF mesh of the domain
meshio.write(
"{}/{}_{}".format(directory, prefix, "domain.xdmf"),
domain,
file_format="xdmf"
)
def export_boundaries(msh, dim, directory, prefix):
"""
Export the boundaries XDMF file.
"""
# Set the cell type
if dim == 2:
cell_type = "line"
elif dim == 3:
cell_type = "triangle"
# Generate the cell block for the boundaries cells
data_array = [arr for (t, arr) in msh.cells if t == cell_type]
if len(data_array) == 0:
print("WARNING: No boundary physical group found.")
return
else:
data = np.concatenate(data_array)
boundaries_cells = [
meshio.CellBlock(
type=cell_type,
data=data,
)
]
# Generate the boundaries cells data
cell_data = {
"boundaries": [
np.concatenate(
[
msh.cell_data["gmsh:physical"][i]
for i, cellBlock in enumerate(msh.cells)
if cellBlock.type == cell_type
]
)
]
}
# Generate the meshio Mesh for the boundaries physical groups
boundaries = meshio.Mesh(
points=msh.points[:, :dim],
cells=boundaries_cells,
cell_data=cell_data,
)
# Export the XDMF mesh of the lines boundaries
meshio.write(
"{}/{}_{}".format(directory, prefix, "boundaries.xdmf"),
boundaries,
file_format="xdmf"
)
def export_association_table(msh, prefix='mesh', directory='.', verbose=True):
"""
Display the association between the physical group label and the mesh
value.
"""
# Create association table
association_table = {}
# Display the correspondance
formatter = "|{:^20}|{:^20}|"
topbot = "+{:-^41}+".format("")
separator = "+{:-^20}+{:-^20}+".format("", "")
# Display
if verbose:
print('\n' + topbot)
print(formatter.format("GMSH label", "MeshFunction value"))
print(separator)
for label, arrays in msh.cell_sets.items():
# Get the index of the array in arrays
for i, array in enumerate(arrays):
if array.size != 0:
index = i
# Added check to make sure that the association table
# doesn't try to import irrelevant information.
if label != "gmsh:bounding_entities":
value = msh.cell_data["gmsh:physical"][index][0]
# Store the association table in a dictionnary
association_table[label] = value
# Display the association
if verbose:
print(formatter.format(label, value))
if verbose:
print(topbot)
# Export the association table
file_content = ConfigParser()
file_content["ASSOCIATION TABLE"] = association_table
file_name = "{}/{}_{}".format(directory, prefix, "association_table.ini")
with open(file_name, 'w') as f:
file_content.write(f)
def import_mesh(
prefix="mesh",
subdomains=False,
dim=2,
directory=".",
):
"""Function importing a dolfin mesh.
Arguments:
prefix (str, optional): mesh files prefix (eg. my_mesh.msh,
my_mesh_domain.xdmf, my_mesh_bondaries.xdmf). Defaults to "mesh".
subdomains (bool, optional): True if there are subdomains. Defaults to
False.
dim (int, optional): dimension of the domain. Defaults to 2.
directory (str, optional): directory of the mesh files. Defaults to ".".
Output:
- dolfin Mesh object containing the domain;
- dolfin MeshFunction object containing the physical lines (dim=2) or
surfaces (dim=3) defined in the msh file and the sub-domains;
- association table
"""
# Set the file name
domain = "{}_domain.xdmf".format(prefix)
boundaries = "{}_boundaries.xdmf".format(prefix)
# create 2 xdmf files if not converted before
if not os.path.exists("{}/{}".format(directory, domain)) or \
not os.path.exists("{}/{}".format(directory, boundaries)):
msh2xdmf("{}.msh".format(prefix), dim=dim, directory=directory)
# Import the converted domain
mesh = Mesh()
with XDMFFile("{}/{}".format(directory, domain)) as infile:
infile.read(mesh)
# Import the boundaries
boundaries_mvc = MeshValueCollection("size_t", mesh, dim=dim)
with XDMFFile("{}/{}".format(directory, boundaries)) as infile:
infile.read(boundaries_mvc, 'boundaries')
boundaries_mf = MeshFunctionSizet(mesh, boundaries_mvc)
# Import the subdomains
if subdomains:
subdomains_mvc = MeshValueCollection("size_t", mesh, dim=dim)
with XDMFFile("{}/{}".format(directory, domain)) as infile:
infile.read(subdomains_mvc, 'subdomains')
subdomains_mf = MeshFunctionSizet(mesh, subdomains_mvc)
# Import the association table
association_table_name = "{}/{}_{}".format(
directory, prefix, "association_table.ini")
file_content = ConfigParser()
file_content.read(association_table_name)
association_table = dict(file_content["ASSOCIATION TABLE"])
# Convert the value from string to int
for key, value in association_table.items():
association_table[key] = int(value)
# Return the Mesh and the MeshFunction objects
if not subdomains:
return mesh, boundaries_mf, association_table
else:
return mesh, boundaries_mf, subdomains_mf, association_table
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"msh_file",
help="input .msh file",
type=str,
)
parser.add_argument(
"-d",
"--dimension",
help="dimension of the domain",
type=int,
default=2,
)
args = parser.parse_args()
# Get current directory
current_directory = os.getcwd()
# Conert the mesh
msh2xdmf(args.msh_file, args.dimension, directory=current_directory)