Skip to content

Commit d6dbf7b

Browse files
authored
Merge pull request #112 from JuliaGeodynamics/bk-gmsh-support
Import Gmsh meshes
2 parents 8bb0a7c + 57ea6e8 commit d6dbf7b

24 files changed

+2127
-140
lines changed

Project.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@ WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192"
3131
[weakdeps]
3232
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
3333
GMT = "5752ebe1-31b9-557e-87aa-f909b540aa54"
34+
GridapGmsh = "3025c34a-b394-11e9-2a55-3fee550c04c8"
3435

3536
[extensions]
3637
GLMakie_Visualisation = "GLMakie"
3738
GMT_utils = "GMT"
39+
Gmsh_utils = "GridapGmsh"
3840

3941
[compat]
4042
Colors = "0.9 - 0.12"
@@ -47,10 +49,11 @@ GeoParams = "0.2 - 0.5"
4749
Geodesy = "1"
4850
GeometryBasics = "0.1 - 0.4"
4951
Glob = "1.2 - 1.3"
52+
GridapGmsh = "0.5 - 0.7"
5053
ImageIO = "0.1 - 0.6"
5154
Interpolations = "0.14, 0.15"
52-
LightXML = "0.8, 0.9"
5355
JLD2 = "0.4"
56+
LightXML = "0.8, 0.9"
5457
MeshIO = "0.1 - 0.4"
5558
NearestNeighbors = "0.2 - 0.4"
5659
Parameters = "0.9 - 0.12"
@@ -66,4 +69,4 @@ StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3"
6669
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
6770

6871
[targets]
69-
test = ["Test", "GMT", "StableRNGs"]
72+
test = ["Test", "GMT", "StableRNGs","GridapGmsh"]

docs/make.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,9 @@ makedocs(;
120120
"Gravity code" => "man/gravity_code.md",
121121
"Numerical model setups" => "man/geodynamic_setups.md",
122122
"LaMEM" => "man/lamem.md",
123+
"pTatin" => "man/lamem.md",
123124
"Profile Processing" => "man/profile_processing.md",
125+
"Gmsh" => "man/gmsh.md",
124126
"Movies" => "man/movies.md"
125127
],
126128
"List of functions" => "man/listfunctions.md",

docs/src/man/datastructures.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ For plotting, we transfer this into the `ParaviewData` structure, which has cart
88
GeoData
99
CartData
1010
UTMData
11+
Q1Data
12+
FEData
1113
ParaviewData
1214
lonlatdepth_grid
1315
xyz_grid

docs/src/man/gmsh.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Gmsh
2+
3+
[Gmsh](https://gmsh.info) is a widely used 3D unstructured mesh generator that produces tetrahedral meshes which can be tagged by region. It is possible to import such meshes and use that to set region info in a `GMG` data set, or use it to generate pTatin input.
4+
5+
```@docs
6+
GeophysicalModelGenerator.import_Gmsh
7+
```

docs/src/man/ptatin.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# pTatin
2+
3+
We provide a few routines that allow exporting GMG input data sets to a format that can be read by the 3D geodynamic modelling software [pTatin](https://bitbucket.org/dmay/ptatin-total-dev.git/src), which is an open-source Cartesian code to perform crustal and lithospheric-scale simulations.
4+
5+
The `Q1Data` format can be saved directly to pTatin.
6+
7+
```@docs
8+
GeophysicalModelGenerator.write_pTatin_mesh
9+
GeophysicalModelGenerator.swap_yz_dims
10+
```

ext/Gmsh_utils.jl

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Support for Gmsh meshes
2+
3+
module Gmsh_utils
4+
5+
using GridapGmsh, StaticArrays
6+
7+
import GeophysicalModelGenerator: import_Gmsh, FEData, CartData
8+
9+
10+
"""
11+
fe_data, tag_names = import_Gmsh(fname::String)
12+
13+
Reads a Gmsh file and returns a `FEData` object with info about the mesh. `tag_names` contains the names of the regions of the FE mesh
14+
"""
15+
function import_Gmsh(fname::String)
16+
17+
mesh = GmshDiscreteModel(fname, renumber=false)
18+
19+
# Extract vertices
20+
nverts = length(mesh.grid.node_coordinates);
21+
dims = length(mesh.grid.node_coordinates[1])
22+
vertices = [mesh.grid.node_coordinates[n][i] for i=1:dims,n=1:nverts]
23+
24+
# write coords as 1D double array
25+
nvertices_cell = length(mesh.grid.cell_node_ids[1])
26+
connectivity = [c[i] for i=1:nvertices_cell, c in mesh.grid.cell_node_ids]
27+
28+
# extract tag of each of the tetrahedrons
29+
regions, tag_names = cell_tags_from_gmsh(mesh)
30+
31+
cellfields = (regions=regions,)
32+
fields = nothing
33+
34+
return FEData(vertices,connectivity, fields, cellfields), tag_names
35+
end
36+
37+
38+
"""
39+
tags, tag_names = cell_tags_from_gmsh(mesh::GmshDiscreteModel)
40+
41+
Returns a list with integers that are the tags for each of the cells
42+
"""
43+
function cell_tags_from_gmsh(mesh)
44+
cell_entities = mesh.face_labeling.d_to_dface_to_entity[4] # volumetric entities
45+
cell_entities_unique = unique(cell_entities)
46+
tag_unique = zeros(Int64,size(cell_entities_unique))
47+
48+
for i=1:length(cell_entities_unique)
49+
for (n,tag) in enumerate(mesh.face_labeling.tag_to_entities)
50+
if any(tag .== cell_entities_unique[i])
51+
tag_unique[i] = n
52+
end
53+
end
54+
end
55+
56+
# create tags for cells
57+
tags = zeros(Int64,length(cell_entities))
58+
for (i,entity) in enumerate(cell_entities_unique)
59+
id = findall(cell_entities.==entity)
60+
tags[id] .= tag_unique[i]
61+
end
62+
63+
return tags, mesh.face_labeling.tag_to_name
64+
end
65+
66+
67+
68+
69+
end

src/GeophysicalModelGenerator.jl

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ include("Paraview_collection.jl")
3939
include("transformation.jl")
4040
include("voxel_gravity.jl")
4141
include("LaMEM_io.jl")
42+
include("pTatin_IO.jl")
4243
include("Setup_geometry.jl")
4344
include("stl.jl")
4445
include("ProfileProcessing.jl")
@@ -70,10 +71,18 @@ export import_topo, import_GeoTIFF
7071
visualise
7172
Interactive widget that allows you to explore a 3D data set `DataSet` in an interactive manner.
7273
It requires you to load `GLMakie`.
73-
"""
74+
""";
7475
function visualise end
7576
export visualise
7677

7778

79+
"""
80+
import_Gmsh(fname::String)
81+
82+
Reads a Gmsh file. Requires loading `GridapGmsh`.
83+
"""
84+
function import_Gmsh end
85+
export import_Gmsh
86+
7887

7988
end

src/LaMEM_io.jl

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,11 +1016,15 @@ function create_partitioning_file(LaMEM_input::String,NumProc::Int64; LaMEM_dir:
10161016
end
10171017

10181018
"""
1019-
X,Y,Z = coordinate_grids(Data::LaMEM_grid)
1019+
X,Y,Z = coordinate_grids(Data::LaMEM_grid; cell=false)
10201020
10211021
Returns 3D coordinate arrays
10221022
"""
1023-
function coordinate_grids(Data::LaMEM_grid)
1024-
1025-
return Data.X, Data.Y, Data.Z
1023+
function coordinate_grids(Data::LaMEM_grid; cell=false)
1024+
X,Y,Z = Data.X, Data.Y, Data.Z
1025+
if cell
1026+
X,Y,Z = average_q1(X),average_q1(Y), average_q1(Z)
1027+
end
1028+
1029+
return X,Y,Z
10261030
end

src/Paraview_output.jl

Lines changed: 171 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,174 @@ function movie_paraview(; name="Movie", pvd=nothing, Finalize::Bool=false, Initi
194194
end
195195

196196

197-
197+
"""
198+
write_paraview(DataSet::Q1Data, filename="test"; directory=nothing, pvd=nothing, time=nothing, verbose=true)
199+
Writes a `Q1Data` dataset to disk, which has cell and vertex field
200+
"""
201+
function write_paraview(DataSet::Q1Data, filename="test"; directory=nothing, pvd=nothing, time=nothing, verbose=true)
202+
203+
# Error checking
204+
if !(length(size(DataSet.x))==length(size(DataSet.y))==length(size(DataSet.z)))
205+
error("The X/Y/Z should be 3 dimensional")
206+
end
207+
208+
# Create directory if required
209+
if !isnothing(directory)
210+
mkpath(directory)
211+
filename = joinpath(directory, filename); # add directory name to pathname
212+
end
213+
214+
# Create VT* file
215+
vtkfile = vtk_grid(filename, ustrip.(DataSet.x.val), ustrip.(DataSet.y.val), ustrip.(DataSet.z.val))
216+
217+
# Add vertex data fields to VT* file
218+
names = String.(collect(keys(DataSet.fields))); # this is how to retrieve the names of the data fields
219+
for (index, name) in enumerate(names)
220+
221+
if typeof(DataSet.fields[index])<: Tuple
222+
# if we do a tuple of velocities, it appears difficult to deal with units
223+
# This will require some more work
224+
unit_name = ""
225+
Data = DataSet.fields[index]
226+
if unit(Data[1][1])!=NoUnits
227+
error("potential error as vector data fields have units; please save them with no units!")
228+
end
229+
else
230+
unit_name = unit(DataSet.fields[index][1])
231+
Data = ustrip.(DataSet.fields[index])
232+
end
233+
234+
name_with_units = join([name," [$(unit_name)]"]); # add units to the name of the field
235+
#if PointsData
236+
# vtkfile[name_with_units, VTKPointData()] = Data[:];
237+
#else
238+
vtkfile[name_with_units] = Data;
239+
#end
240+
end
241+
242+
# Add cell data fields to VT* file
243+
names = String.(collect(keys(DataSet.cellfields))); # this is how to retrieve the names of the data fields
244+
for (index, name) in enumerate(names)
245+
246+
if typeof(DataSet.cellfields[index])<: Tuple
247+
# if we do a tuple of velocities, it appears difficult to deal with units
248+
# This will require some more work
249+
unit_name = ""
250+
Data = DataSet.cellfields[index]
251+
if unit(Data[1][1])!=NoUnits
252+
error("potential error as vector data fields have units; please save them with no units!")
253+
end
254+
else
255+
unit_name = unit(DataSet.cellfields[index][1])
256+
Data = ustrip.(DataSet.cellfields[index])
257+
end
258+
259+
name_with_units = join([name," [$(unit_name)]"]); # add units to the name of the field
260+
vtkfile[name_with_units, VTKCellData()] = Data[:];
261+
262+
end
263+
264+
265+
outfiles = vtk_save(vtkfile);
266+
if verbose
267+
println("Saved file: $(outfiles[1])")
268+
end
269+
if !isnothing(pvd)
270+
# Write movie
271+
pvd[time] = vtkfile
272+
end
273+
274+
return pvd
275+
end
276+
277+
278+
279+
280+
"""
281+
write_paraview(DataSet::FEData, filename="test"; directory=nothing, pvd=nothing, time=nothing, verbose=true)
282+
Writes a `FEData` dataset (general finite element) to disk, which has cell and vertex field
283+
"""
284+
function write_paraview(DataSet::FEData, filename="test"; directory=nothing, pvd=nothing, time=nothing, verbose=true)
285+
286+
# Create directory if required
287+
if !isnothing(directory)
288+
mkpath(directory)
289+
filename = joinpath(directory, filename); # add directory name to pathname
290+
end
291+
292+
connectivity = DataSet.connectivity
293+
if size(DataSet.connectivity,1) == 4
294+
celltype = VTKCellTypes.VTK_TETRA
295+
296+
elseif size(DataSet.connectivity,1) == 8
297+
celltype = VTKCellTypes.VTK_HEXAHEDRON
298+
299+
# we need to reorder this as pTatin uses a different ordering than VTK
300+
id_reorder = [1,2,4,3,5,6,8,7]
301+
connectivity = connectivity[id_reorder,:]
302+
else
303+
error("This element is not yet implemented")
304+
end
305+
306+
# Create VTU file
307+
points = DataSet.vertices
308+
cells = MeshCell[];
309+
for i = 1: size(connectivity,2)
310+
push!(cells, MeshCell(celltype, connectivity[:,i]))
311+
end
312+
313+
vtkfile = vtk_grid(filename, points, cells)
314+
315+
# Add vertex data fields to VT* file
316+
names = String.(collect(keys(DataSet.fields))); # this is how to retrieve the names of the data fields
317+
for (index, name) in enumerate(names)
318+
319+
if typeof(DataSet.fields[index])<: Tuple
320+
# if we do a tuple of velocities, it appears difficult to deal with units
321+
# This will require some more work
322+
unit_name = ""
323+
Data = DataSet.fields[index]
324+
if unit(Data[1][1])!=NoUnits
325+
error("potential error as vector data fields have units; please save them with no units!")
326+
end
327+
else
328+
unit_name = unit(DataSet.fields[index][1])
329+
Data = ustrip.(DataSet.fields[index])
330+
end
331+
332+
name_with_units = join([name," [$(unit_name)]"]); # add units to the name of the field
333+
vtkfile[name_with_units] = Data;
334+
end
335+
336+
# Add cell data fields to VT* file
337+
names = String.(collect(keys(DataSet.cellfields))); # this is how to retrieve the names of the data fields
338+
for (index, name) in enumerate(names)
339+
340+
if typeof(DataSet.cellfields[index])<: Tuple
341+
# if we do a tuple of velocities, it appears difficult to deal with units
342+
# This will require some more work
343+
unit_name = ""
344+
Data = DataSet.cellfields[index]
345+
if unit(Data[1][1])!=NoUnits
346+
error("potential error as vector data fields have units; please save them with no units!")
347+
end
348+
else
349+
unit_name = unit(DataSet.cellfields[index][1])
350+
Data = ustrip.(DataSet.cellfields[index])
351+
end
352+
353+
name_with_units = join([name," [$(unit_name)]"]); # add units to the name of the field
354+
vtkfile[name_with_units, VTKCellData()] = Data[:];
355+
end
356+
357+
outfiles = vtk_save(vtkfile);
358+
if verbose
359+
println("Saved file: $(outfiles[1])")
360+
end
361+
if !isnothing(pvd)
362+
# Write movie
363+
pvd[time] = vtkfile
364+
end
365+
366+
return pvd
367+
end

0 commit comments

Comments
 (0)