Skip to content

Commit 430c986

Browse files
authored
Merge pull request #110 from sintefmath/dev
Fixes to domain decomposition, non-scalar variable updates
2 parents 967a0d3 + 0b8bef6 commit 430c986

File tree

17 files changed

+492
-49
lines changed

17 files changed

+492
-49
lines changed

Project.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "Jutul"
22
uuid = "2b460a1a-8a2b-45b2-b125-b5c536396eb9"
33
authors = ["Olav Møyner <[email protected]>"]
4-
version = "0.3.2"
4+
version = "0.3.3"
55

66
[deps]
77
AlgebraicMultigrid = "2169fc97-5a83-5252-b627-83903c6c433c"
@@ -41,6 +41,7 @@ Tullio = "bc48ee85-29a4-5162-ae0b-a64e1601d4bc"
4141
AMGCLWrap = "4f76b812-4ba5-496d-b042-d70715554288"
4242
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
4343
GraphMakie = "1ecd5474-83a3-4783-bb4f-06765db800d2"
44+
Gmsh = "705231aa-382f-11e9-3f0c-b7cb4346fdeb"
4445
HYPRE = "b5ffcf37-a2bd-41ab-a3da-4bd9bc8ad771"
4546
KaHyPar = "2a6221f6-aa48-11e9-3542-2d9e0ef01880"
4647
LayeredLayouts = "f4a74d36-062a-4d48-97cd-1356bad1de4e"
@@ -54,6 +55,7 @@ PartitionedArrays = "5a9dfac6-5c52-46f7-8278-5e2210713be9"
5455
JutulAMGCLWrapExt = "AMGCLWrap"
5556
JutulGLMakieExt = "GLMakie"
5657
JutulGraphMakieExt = ["GraphMakie", "NetworkLayout", "LayeredLayouts", "Makie"]
58+
JutulGmshExt = "Gmsh"
5759
JutulHYPREExt = "HYPRE"
5860
JutulKaHyParExt = "KaHyPar"
5961
JutulMakieExt = "Makie"
@@ -101,6 +103,7 @@ julia = "1.7"
101103
CPUSummary = "2a0fbf3d-bb9c-48f3-b0a9-814d99fd7ab9"
102104
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
103105
GraphMakie = "1ecd5474-83a3-4783-bb4f-06765db800d2"
106+
Gmsh = "705231aa-382f-11e9-3f0c-b7cb4346fdeb"
104107
HYPRE = "b5ffcf37-a2bd-41ab-a3da-4bd9bc8ad771"
105108
KaHyPar = "2a6221f6-aa48-11e9-3542-2d9e0ef01880"
106109
LayeredLayouts = "f4a74d36-062a-4d48-97cd-1356bad1de4e"

ext/JutulGmshExt/JutulGmshExt.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module JutulGmshExt
2+
using Jutul
3+
import Jutul: UnstructuredMesh, IndirectionMap, check_equal_perm
4+
using Gmsh: Gmsh, gmsh
5+
6+
using StaticArrays
7+
using OrderedCollections
8+
9+
const QUAD_T = SVector{4, Int}
10+
const TRI_T = SVector{3, Int}
11+
12+
include("utils.jl")
13+
include("interface.jl")
14+
end

ext/JutulGmshExt/interface.jl

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
2+
function Jutul.mesh_from_gmsh(pth; verbose = false, kwarg...)
3+
Gmsh.initialize()
4+
ext = pth |> splitext |> last
5+
gmsh.open(pth)
6+
if lowercase(ext) == ".geo"
7+
gmsh.model.mesh.generate()
8+
end
9+
10+
dim = gmsh.model.getDimension()
11+
dim == 3 || error("Only 3D models are supported")
12+
13+
gmsh.model.mesh.removeDuplicateNodes()
14+
node_tags, pts, = gmsh.model.mesh.getNodes()
15+
node_remap = Dict{UInt64, Int}()
16+
for (i, tag) in enumerate(node_tags)
17+
tag::UInt64
18+
node_remap[tag] = i
19+
end
20+
remaps = (
21+
nodes = node_remap,
22+
faces = Dict{UInt64, Int}(),
23+
cells = Dict{UInt64, Int}()
24+
)
25+
pts = reshape(pts, Int(dim), :)
26+
pts_s = collect(vec(reinterpret(SVector{3, Float64}, pts)))
27+
28+
@assert size(pts, 2) == length(node_tags)
29+
faces_to_nodes = parse_faces(remaps, verbose = verbose)
30+
face_lookup = generate_face_lookup(faces_to_nodes)
31+
32+
cells_to_faces = parse_cells(remaps, faces_to_nodes, face_lookup, verbose = verbose)
33+
# We are done with Gmsh.jl at this point, finalize it.
34+
Gmsh.finalize()
35+
36+
neighbors = build_neighbors(cells_to_faces, faces_to_nodes, face_lookup)
37+
38+
# Make both of these in case we have rogue faces that are not connected to any cell.
39+
bnd_faces = Int[]
40+
int_faces = Int[]
41+
n_bad = 0
42+
for i in axes(neighbors, 2)
43+
l, r = neighbors[:, i]
44+
l_bnd = l == 0
45+
r_bnd = r == 0
46+
47+
if l_bnd || r_bnd
48+
if l_bnd && r_bnd
49+
n_bad += 1
50+
else
51+
push!(bnd_faces, i)
52+
end
53+
else
54+
push!(int_faces, i)
55+
end
56+
end
57+
bnd_neighbors, bnd_faces_to_nodes, bnd_cells_to_faces = split_boundary(neighbors, faces_to_nodes, cells_to_faces, bnd_faces, boundary = true)
58+
int_neighbors, int_faces_to_nodes, int_cells_to_faces = split_boundary(neighbors, faces_to_nodes, cells_to_faces, int_faces, boundary = false)
59+
60+
c2f = IndirectionMap(int_cells_to_faces)
61+
c2b = IndirectionMap(bnd_cells_to_faces)
62+
f2n = IndirectionMap(int_faces_to_nodes)
63+
b2n = IndirectionMap(bnd_faces_to_nodes)
64+
print_message("Mesh parsed successfully:\n $(length(c2f)) cells\n $(length(f2n)) internal faces\n $(length(b2n)) boundary faces\n $(length(pts_s)) nodes", verbose)
65+
return UnstructuredMesh(c2f, c2b, f2n, b2n, pts_s, int_neighbors, bnd_neighbors; kwarg...)
66+
end

ext/JutulGmshExt/utils.jl

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
2+
function add_next!(faces, remap, tags, numpts, offset)
3+
vals = Int[]
4+
for j in 1:numpts
5+
push!(vals, remap[tags[offset + j]])
6+
end
7+
push!(faces, vals)
8+
end
9+
10+
function parse_faces(remaps; verbose = false)
11+
node_remap = remaps.nodes
12+
face_remap = remaps.faces
13+
faces = Vector{Int}[]
14+
for (dim, tag) in gmsh.model.getEntities()
15+
if dim != 2
16+
continue
17+
end
18+
type = gmsh.model.getType(dim, tag)
19+
name = gmsh.model.getEntityName(dim, tag)
20+
elemTypes, elemTags, elemNodeTags = gmsh.model.mesh.getElements(dim, tag)
21+
type = gmsh.model.getType(dim, tag)
22+
ename = gmsh.model.getEntityName(dim, tag)
23+
for (etypes, etags, enodetags) in zip(elemTypes, elemTags, elemNodeTags)
24+
name, dim, order, numv, parv, _ = gmsh.model.mesh.getElementProperties(etypes)
25+
if name == "Quadrilateral 4"
26+
numpts = 4
27+
elseif name == "Triangle 3"
28+
numpts = 3
29+
else
30+
error("Unsupported element type $name for faces.")
31+
end
32+
@assert length(enodetags) == numpts*length(etags)
33+
print_message("Faces: Processing $(length(etags)) tags of type $name", verbose)
34+
for (i, etag) in enumerate(etags)
35+
offset = (i-1)*numpts
36+
add_next!(faces, node_remap, enodetags, numpts, offset)
37+
face_remap[etag] = length(faces)
38+
end
39+
print_message("Added $(length(etags)) faces of type $name with $(length(unique(enodetags))) unique nodes", verbose)
40+
end
41+
end
42+
return faces
43+
end
44+
45+
function get_cell_decomposition(name)
46+
if name == "Hexahedron 8"
47+
tris = Tuple{}()
48+
quads = (
49+
QUAD_T(0, 4, 7, 3),
50+
QUAD_T(1, 2, 6, 5),
51+
QUAD_T(0, 1, 5, 4),
52+
QUAD_T(2, 3, 7, 6),
53+
QUAD_T(0, 3, 2, 1),
54+
QUAD_T(4, 5, 6, 7)
55+
)
56+
numpts = 8
57+
elseif name == "Tetrahedron 4"
58+
tris = (
59+
TRI_T(0, 1, 3),
60+
TRI_T(0, 2, 1),
61+
TRI_T(0, 3, 2),
62+
TRI_T(1, 2, 3)
63+
)
64+
quads = Tuple{}()
65+
numpts = 4
66+
elseif name == "Pyramid 5"
67+
# TODO: Not really tested.
68+
tris = (
69+
TRI_T(0, 1, 4),
70+
TRI_T(0, 4, 3),
71+
TRI_T(3, 4, 2),
72+
TRI_T(1, 2, 4)
73+
)
74+
quads = (QUAD_T(0, 3, 2, 1),)
75+
numpts = 4
76+
elseif name == "Prism 6"
77+
# TODO: Not really tested.
78+
tris = (
79+
TRI_T(0, 2, 1),
80+
TRI_T(3, 4, 5)
81+
)
82+
quads = (
83+
QUAD_T(0, 1, 4, 3),
84+
QUAD_T(0, 3, 5, 2),
85+
QUAD_T(1, 2, 5, 4)
86+
)
87+
numpts = 6
88+
else
89+
error("Unsupported element type $name for cells.")
90+
end
91+
return (tris, quads, numpts)
92+
end
93+
94+
function print_message(msg, verbose)
95+
if verbose
96+
println(msg)
97+
end
98+
end
99+
100+
function parse_cells(remaps, faces, face_lookup; verbose = false)
101+
node_remap = remaps.nodes
102+
face_remap = remaps.faces
103+
cell_remap = remaps.cells
104+
cells = Vector{Tuple{Int, Int}}[]
105+
for (dim, tag) in gmsh.model.getEntities()
106+
if dim != 3
107+
continue
108+
end
109+
type = gmsh.model.getType(dim, tag)
110+
name = gmsh.model.getEntityName(dim, tag)
111+
# Get the mesh elements for the entity (dim, tag):
112+
elemTypes, elemTags, elemNodeTags = gmsh.model.mesh.getElements(dim, tag)
113+
# * Type and name of the entity:
114+
type = gmsh.model.getType(dim, tag)
115+
ename = gmsh.model.getEntityName(dim, tag)
116+
for (etypes, etags, enodetags) in zip(elemTypes, elemTags, elemNodeTags)
117+
name, dim, order, numv, parv, _ = gmsh.model.mesh.getElementProperties(etypes)
118+
tris, quads, numpts = get_cell_decomposition(name)
119+
print_message("Cells: Processing $(length(etags)) tags of type $name", verbose)
120+
@assert length(enodetags) == numpts*length(etags)
121+
nadded = 0
122+
for (i, etag) in enumerate(etags)
123+
offset = (i-1)*numpts
124+
pt_range = (offset+1):(offset+numpts)
125+
@assert length(pt_range) == numpts
126+
pts = map(i -> node_remap[enodetags[i]], pt_range)
127+
cell = Tuple{Int, Int}[]
128+
cellno = length(cells)
129+
for face_t in (tris, quads)
130+
for (fno, face) in enumerate(face_t)
131+
face_pts = map(i -> pts[i+1], face)
132+
face_pts_sorted = sort(face_pts)
133+
faceno = get(face_lookup, face_pts_sorted, 0)
134+
if faceno == 0
135+
nadded += 1
136+
push!(faces, face_pts)
137+
faceno = length(faces)
138+
face_lookup[face_pts_sorted] = faceno
139+
sgn = 1
140+
else
141+
sgn = check_equal_perm(face_pts, faces[faceno]) ? 1 : 2
142+
end
143+
push!(cell, (faceno, sgn))
144+
end
145+
end
146+
push!(cells, cell)
147+
end
148+
print_message("Added $(length(etags)) new cells of type $name and $nadded new faces.", verbose)
149+
end
150+
end
151+
return cells
152+
end
153+
154+
function build_neighbors(cells, faces, face_lookup)
155+
neighbors = zeros(Int, 2, length(faces))
156+
for (cellno, cell_to_faces) in enumerate(cells)
157+
for (face_index, lr) in cell_to_faces
158+
face_pts = faces[face_index]
159+
face_pts_sorted = sort(face_pts)
160+
faceno = face_lookup[face_pts_sorted]
161+
face_ref = faces[faceno]
162+
oldn = neighbors[lr, faceno]
163+
oldn == 0 || error("Cannot overwrite face neighbor for cell $cellno - was already defined as $oldn for index $lr: $(neighbors[:, faceno])")
164+
neighbors[lr, faceno] = cellno
165+
end
166+
end
167+
return neighbors
168+
end
169+
170+
function generate_face_lookup(faces)
171+
face_lookup = Dict{Union{QUAD_T, TRI_T}, Int}()
172+
173+
for (i, face) in enumerate(faces)
174+
n = length(face)
175+
if n == 3
176+
ft = sort(TRI_T(face[1], face[2], face[3]))
177+
elseif n == 4
178+
ft = sort(QUAD_T(face[1], face[2], face[3], face[4]))
179+
else
180+
error("Unsupported face type with $n nodes, only 3 (for tri) and 4 (for quad) are known.")
181+
end
182+
@assert issorted(ft)
183+
face_lookup[ft] = i
184+
end
185+
return face_lookup
186+
end
187+
188+
function split_boundary(neighbors, faces_to_nodes, cells_to_faces, active_ix::Vector{Int}; boundary::Bool)
189+
remap = OrderedDict{Int, Int}()
190+
for (i, ix) in enumerate(active_ix)
191+
remap[ix] = i
192+
end
193+
# is_active = [false for _ in eachindex(faces_to_nodes)]
194+
# is_active[active_ix] .= true
195+
# Make renumbering here.
196+
if boundary
197+
new_neighbors = Int[]
198+
for ix in active_ix
199+
l, r = neighbors[:, ix]
200+
@assert l == 0 || r == 0
201+
push!(new_neighbors, max(l, r))
202+
end
203+
else
204+
new_neighbors = Tuple{Int, Int}[]
205+
for ix in active_ix
206+
l, r = neighbors[:, ix]
207+
@assert l != 0 && r != 0
208+
push!(new_neighbors, (l, r))
209+
end
210+
end
211+
new_faces_to_nodes = map(copy, faces_to_nodes[active_ix])
212+
# Handle cells -> current type of faces
213+
new_cells_to_faces = Vector{Int}[]
214+
for cell_to_faces in cells_to_faces
215+
new_cell = Int[]
216+
for (face, sgn) in cell_to_faces
217+
if haskey(remap, face)
218+
push!(new_cell, remap[face])
219+
end
220+
end
221+
push!(new_cells_to_faces, new_cell)
222+
end
223+
224+
return (new_neighbors, new_faces_to_nodes, new_cells_to_faces)
225+
end

ext/JutulMakieExt/mesh_plots.jl

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,28 @@ function Jutul.plot_mesh_impl!(ax, m;
2727
has_face_filter = !isnothing(faces)
2828
has_bface_filter = !isnothing(boundaryfaces)
2929
if has_cell_filter || has_face_filter || has_bface_filter
30+
keep_cells = Dict{Int, Bool}()
31+
keep_faces = Dict{Int, Bool}()
32+
keep_bf = Dict{Int, Bool}()
33+
3034
if eltype(cells) == Bool
3135
@assert length(cells) == number_of_cells(m)
3236
cells = findall(cells)
3337
end
38+
if has_cell_filter
39+
for c in cells
40+
keep_cells[c] = true
41+
end
42+
end
3443
if eltype(faces) == Bool
3544
@assert length(faces) == number_of_faces(m)
3645
faces = findall(faces)
3746
end
47+
if has_face_filter
48+
for f in faces
49+
keep_faces[f] = true
50+
end
51+
end
3852
if eltype(boundaryfaces) == Bool
3953
@assert length(boundaryfaces) == number_of_boundary_faces(m)
4054
boundaryfaces = findall(boundaryfaces)
@@ -43,6 +57,9 @@ function Jutul.plot_mesh_impl!(ax, m;
4357
nf = number_of_faces(m)
4458
boundaryfaces = deepcopy(boundaryfaces)
4559
boundaryfaces .+= nf
60+
for f in boundaryfaces
61+
keep_bf[f] = true
62+
end
4663
end
4764
ntri = size(tri, 1)
4865
keep = fill(false, ntri)
@@ -53,13 +70,13 @@ function Jutul.plot_mesh_impl!(ax, m;
5370
tri_tmp = tri[i, 1]
5471
keep_this = true
5572
if has_cell_filter
56-
keep_this = keep_this && cell_ix[tri_tmp] in cells
73+
keep_this = keep_this && haskey(keep_cells, cell_ix[tri_tmp])
5774
end
5875
if has_face_filter
59-
keep_this = keep_this && face_ix[tri_tmp] in faces
76+
keep_this = keep_this && haskey(keep_faces, face_ix[tri_tmp])
6077
end
6178
if has_bface_filter
62-
keep_this = keep_this && face_ix[tri_tmp] in boundaryfaces
79+
keep_this = keep_this && haskey(keep_bf, face_ix[tri_tmp])
6380
end
6481
keep[i] = keep_this
6582
end

0 commit comments

Comments
 (0)