Skip to content

Commit eff4a23

Browse files
committed
Bugfix: fix topological consistency for polytopal models
1 parent da40001 commit eff4a23

4 files changed

Lines changed: 199 additions & 59 deletions

File tree

src/Adaptivity.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,7 @@ function Adaptivity.coarsen(fmodel::DistributedDiscreteModel, ptopo::Distributed
909909
ctopo = GridapDistributed.DistributedGridTopology(
910910
map(Geometry.PolytopalGridTopology, cnode_coords, connectivity, polys), face_gids
911911
)
912+
_setup_consistent_faces!(ctopo)
912913
labels = Geometry.FaceLabeling(ctopo)
913914
cmodels = map(local_views(ctopo), local_views(labels)) do ctopo, labels
914915
cgrid = Geometry.PolytopalGrid(ctopo)

src/Geometry.jl

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,72 @@ function _setup_face_gids!(topo::DistributedGridTopology{Dc},dim) where {Dc}
9999
end
100100
end
101101

102+
# In some cases, the orientation of locally computed faces is NOT consistent.
103+
# The following functions can be used to check for consistent orientation and fix it.
104+
function _setup_consistent_faces!(topo::DistributedGridTopology)
105+
# Setting up consistent face-to-vertex maps should be enough
106+
# to guarantee consistent face orientation if it is done before
107+
# any other face-to-face map is setup. So we should call this function
108+
# just after creating the new models.
109+
D = num_cell_dims(topo)
110+
for dimfrom in 1:D-1
111+
_setup_consistent_faces!(topo, dimfrom, 0)
112+
end
113+
end
114+
115+
function _setup_consistent_faces!(topo::DistributedGridTopology, dimfrom::Integer, dimto::Integer)
116+
@check 0 <= dimto <= dimfrom <= num_cell_dims(topo)
117+
gids_from = partition(get_face_gids(topo, dimfrom))
118+
gids_to = partition(get_face_gids(topo, dimto))
119+
lfrom_to_gto = map(local_views(topo), gids_to) do topo, gids_to
120+
lfrom_to_lto = get_faces(topo, dimfrom, dimto)
121+
to_global!(lfrom_to_lto.data, gids_to)
122+
JaggedArray(lfrom_to_lto.data, lfrom_to_lto.ptrs)
123+
end
124+
wait(consistent!(PVector(lfrom_to_gto, gids_from)))
125+
map(lfrom_to_gto, gids_to) do lfrom_to_gto, gids_to
126+
to_local!(lfrom_to_gto.data, gids_to)
127+
end
128+
return nothing
129+
end
130+
131+
function isconsistent_faces(topo::DistributedGridTopology)
132+
D = num_cell_dims(topo)
133+
for dimfrom in 1:D-1
134+
for dimto in 0:dimfrom-1
135+
!isconsistent_faces(topo, dimfrom, dimto) && return false
136+
end
137+
end
138+
return true
139+
end
140+
141+
function isconsistent_faces(topo::DistributedGridTopology, dimfrom::Integer, dimto::Integer)
142+
@check 0 <= dimto <= dimfrom <= num_cell_dims(topo)
143+
gids_from = partition(get_face_gids(topo, dimfrom))
144+
gids_to = partition(get_face_gids(topo, dimto))
145+
146+
lfrom_to_lto = map(local_views(topo)) do topo
147+
get_faces(topo, dimfrom, dimto)
148+
end
149+
lfrom_to_gto = map(lfrom_to_lto, gids_to) do lfrom_to_lto, gids_to
150+
lto_gto = local_to_global(gids_to)
151+
JaggedArray(lto_gto[lfrom_to_lto.data],lfrom_to_lto.ptrs)
152+
end
153+
wait(consistent!(PVector(lfrom_to_gto, gids_from)))
154+
isconsistent = map(lfrom_to_lto, lfrom_to_gto, gids_to) do lfrom_to_lto, lfrom_to_gto, gids_to
155+
gto_to_lto = global_to_local(gids_to)
156+
lfrom_to_lto.data == gto_to_lto[lfrom_to_gto.data]
157+
end
158+
return reduce(&, isconsistent)
159+
end
160+
102161
function Geometry.get_isboundary_face(topo::DistributedGridTopology, d::Integer)
103162
face_gids = get_face_gids(topo, d)
104163
is_local_boundary = map(local_views(topo)) do topo
105164
get_isboundary_face(topo,d)
106165
end
107-
t = assemble!(&,PVector(is_local_boundary, partition(face_gids))) |> fetch |> consistent!
108-
is_global_boundary = partition(fetch(t))
166+
t = assemble!(&,PVector(is_local_boundary, partition(face_gids)))
167+
is_global_boundary = partition(fetch(consistent!(fetch(t))))
109168
return is_global_boundary
110169
end
111170

@@ -515,10 +574,12 @@ end
515574
# PolytopalDiscreteModel
516575

517576
function Geometry.PolytopalDiscreteModel(model::GenericDistributedDiscreteModel)
518-
return GenericDistributedDiscreteModel(
577+
pmodel = GenericDistributedDiscreteModel(
519578
map(Geometry.PolytopalDiscreteModel,local_views(model)),
520579
get_cell_gids(model)
521580
)
581+
_setup_consistent_faces!(get_grid_topology(pmodel))
582+
return pmodel
522583
end
523584

524585
# Simplexify

test/GeometryTests.jl

Lines changed: 110 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,75 @@ using PartitionedArrays
66
using LinearAlgebra
77
using Test
88

9-
function main(distribute,parts)
9+
using Gridap.Geometry
10+
11+
function test_local_part_face_labelings_consistency(lmodel::CartesianDiscreteModel{D},gids,gmodel) where {D}
12+
local_topology = lmodel.grid_topology
13+
global_topology = gmodel.grid_topology
14+
local_labelings = lmodel.face_labeling
15+
global_labelings = gmodel.face_labeling
16+
l_d_to_dface_to_entity = local_labelings.d_to_dface_to_entity
17+
g_d_to_dface_to_entity = global_labelings.d_to_dface_to_entity
18+
loc_to_glo = local_to_global(gids)
19+
#traverse local cells
20+
for cell_lid=1:num_cells(lmodel)
21+
cell_gid=loc_to_glo[cell_lid]
22+
for d = 0:D-1
23+
local_cell_to_faces = local_topology.n_m_to_nface_to_mfaces[D+1,d+1]
24+
global_cell_to_faces = global_topology.n_m_to_nface_to_mfaces[D+1,d+1]
25+
la = local_cell_to_faces.ptrs[cell_lid]
26+
lb = local_cell_to_faces.ptrs[cell_lid+1]
27+
ga = global_cell_to_faces.ptrs[cell_gid]
28+
gb = global_cell_to_faces.ptrs[cell_gid+1]
29+
@assert (lb-la)==(gb-ga)
30+
for i = 0:lb-la-1
31+
face_lid = local_cell_to_faces.data[la+i]
32+
face_gid = global_cell_to_faces.data[ga+i]
33+
local_entity = l_d_to_dface_to_entity[d+1][face_lid]
34+
global_entity = g_d_to_dface_to_entity[d+1][face_gid]
35+
if (local_entity != global_entity)
36+
return false
37+
end
38+
end
39+
end
40+
end
41+
return true
42+
end
43+
44+
function test_model(model)
45+
D = num_cell_dims(model)
46+
47+
grid = get_grid(model)
48+
labels = get_grid(model)
49+
50+
topo = get_grid_topology(model)
51+
GridapDistributed.isconsistent_faces(topo)
52+
53+
for d in 0:D
54+
fgids = get_face_gids(model,d)
55+
trian = Triangulation(no_ghost,ReferenceFE{d},model)
56+
@test num_cells(trian) == length(fgids)
57+
trian = Triangulation(with_ghost,ReferenceFE{d},model)
58+
@test num_cells(trian) == length(fgids)
59+
end
60+
61+
Ω = Triangulation(with_ghost,model)
62+
@test num_cells(Ω) == num_cells(model)
1063

64+
Ω = Triangulation(no_ghost,model)
65+
@test num_cells(Ω) == num_cells(model)
66+
67+
Γ = Boundary(with_ghost,model,tags="boundary")
68+
nbfacets = num_cells(Γ)
69+
70+
Γ = Boundary(no_ghost,model,tags="boundary")
71+
@test num_cells(Γ) == nbfacets
72+
73+
return true
74+
end
75+
76+
function main_cartesian(distribute,parts)
77+
ranks = distribute(LinearIndices((prod(parts),)))
1178
output = mkpath(joinpath(@__DIR__,"output"))
1279

1380
if length(parts) == 2
@@ -18,14 +85,12 @@ function main(distribute,parts)
1885
cells = (4,4,4)
1986
end
2087

21-
ranks = distribute(LinearIndices((prod(parts),)))
22-
23-
2488
model = CartesianDiscreteModel(ranks,parts,domain,cells)
2589
writevtk(model,joinpath(output,"model"))
2690

27-
@test num_cells(model)==prod(cells)
28-
@test num_vertices(model)==prod(cells .+ 1)
91+
@test test_model(model)
92+
@test num_cells(model) == prod(cells)
93+
@test num_vertices(model) == prod(cells .+ 1)
2994
@test num_cell_dims(model) == length(cells)
3095
@test num_point_dims(model) == length(cells)
3196

@@ -54,25 +119,6 @@ function main(distribute,parts)
54119
@test test_local_part_face_labelings_consistency(lmodel,gids,gmodel)
55120
end
56121

57-
grid = get_grid(model)
58-
labels = get_grid(model)
59-
60-
Ω = Triangulation(with_ghost,model)
61-
writevtk(Ω,joinpath(output,"Ω"))
62-
@test num_cells(Ω) == num_cells(model)
63-
64-
Ω = Triangulation(no_ghost,model)
65-
writevtk(Ω,joinpath(output,"Ω"))
66-
@test num_cells(Ω) == num_cells(model)
67-
68-
Γ = Boundary(with_ghost,model,tags="boundary")
69-
writevtk(Γ,joinpath(output,"Γ"))
70-
nbfacets = num_cells(Γ)
71-
72-
Γ = Boundary(no_ghost,model,tags="boundary")
73-
writevtk(Γ,joinpath(output,"Γ"))
74-
@test num_cells(Γ) == nbfacets
75-
76122
function is_in(coords)
77123
R = 1.6
78124
n = length(coords)
@@ -111,40 +157,48 @@ function main(distribute,parts)
111157

112158
# Multiple ghost layers
113159
model = CartesianDiscreteModel(ranks,parts,domain,cells;ghost=map(i->2,parts))
160+
@test test_model(model)
161+
162+
# Unstructured conversion
163+
model = Geometry.UnstructuredDiscreteModel(
164+
CartesianDiscreteModel(ranks,parts,domain,cells)
165+
)
166+
@test test_model(model)
167+
168+
# Simplexify
169+
model = simplexify(
170+
CartesianDiscreteModel(ranks,parts,domain,cells)
171+
)
172+
@test test_model(model)
114173

115174
end
116175

117-
function test_local_part_face_labelings_consistency(lmodel::CartesianDiscreteModel{D},gids,gmodel) where {D}
118-
local_topology = lmodel.grid_topology
119-
global_topology = gmodel.grid_topology
120-
local_labelings = lmodel.face_labeling
121-
global_labelings = gmodel.face_labeling
122-
l_d_to_dface_to_entity = local_labelings.d_to_dface_to_entity
123-
g_d_to_dface_to_entity = global_labelings.d_to_dface_to_entity
124-
loc_to_glo = local_to_global(gids)
125-
#traverse local cells
126-
for cell_lid=1:num_cells(lmodel)
127-
cell_gid=loc_to_glo[cell_lid]
128-
for d=0:D-1
129-
local_cell_to_faces = local_topology.n_m_to_nface_to_mfaces[D+1,d+1]
130-
global_cell_to_faces = global_topology.n_m_to_nface_to_mfaces[D+1,d+1]
131-
la = local_cell_to_faces.ptrs[cell_lid]
132-
lb = local_cell_to_faces.ptrs[cell_lid+1]
133-
ga = global_cell_to_faces.ptrs[cell_gid]
134-
gb = global_cell_to_faces.ptrs[cell_gid+1]
135-
@assert (lb-la)==(gb-ga)
136-
for i=0:lb-la-1
137-
face_lid = local_cell_to_faces.data[la+i]
138-
face_gid = global_cell_to_faces.data[ga+i]
139-
local_entity = l_d_to_dface_to_entity[d+1][face_lid]
140-
global_entity = g_d_to_dface_to_entity[d+1][face_gid]
141-
if (local_entity != global_entity)
142-
return false
143-
end
144-
end
145-
end
146-
end
147-
return true
176+
function main_polytopal(distribute,parts)
177+
ranks = distribute(LinearIndices((prod(parts),)))
178+
179+
if length(parts) == 2
180+
domain = (0,4,0,4)
181+
cells = (4,4)
182+
elseif length(parts) == 3
183+
domain = (0,4,0,4,0,4)
184+
cells = (4,4,4)
185+
end
186+
187+
model = Geometry.PolytopalDiscreteModel(
188+
CartesianDiscreteModel(ranks,parts,domain,cells)
189+
)
190+
@test test_model(model)
191+
192+
model = Geometry.PolytopalDiscreteModel(
193+
simplexify(CartesianDiscreteModel(ranks,parts,domain,cells))
194+
)
195+
@test test_model(model)
196+
197+
end
198+
199+
function main(distribute,parts)
200+
main_cartesian(distribute,parts)
201+
main_polytopal(distribute,parts)
148202
end
149203

150204
end # module

test/PolytopalCoarseningTests.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module PolytopalCoarseningTests
22

3+
using Test
34
using Gridap
45
using GridapDistributed, PartitionedArrays
56

@@ -28,7 +29,30 @@ function main(distribute, np)
2829
ptopo = Geometry.PatchTopology(get_grid_topology(fmodel), patch_cells)
2930

3031
cmodel, glues = Adaptivity.coarsen(fmodel,ptopo; return_glue=true)
32+
@test GridapDistributed.isconsistent_faces(get_grid_topology(cmodel))
3133
#writevtk(cmodel, "tmp/cmodel")
3234
end
3335

36+
# ranks = collect(1:2)
37+
#
38+
# fmodel_serial = Geometry.PolytopalDiscreteModel(CartesianDiscreteModel((0,1,0,1),(4,4)))
39+
# cmodel_serial = Adaptivity.coarsen(
40+
# fmodel_serial, Geometry.PatchTopology(
41+
# get_grid_topology(fmodel_serial), Table([[1,2,5,6],[3,4,7,8],[9,10,13,14],[11,12,15,16]])
42+
# )
43+
# )
44+
# fmodel_good = DiscreteModel(ranks,fmodel_serial,[1,1,2,2,1,1,2,2,1,1,2,2,1,1,2,2])
45+
# cmodel_good = DiscreteModel(ranks,cmodel_serial,[1,2,1,2])
46+
#
47+
# fmodel = Geometry.PolytopalDiscreteModel(CartesianDiscreteModel(ranks,(2,1),(0,1,0,1),(4,4)))
48+
# cmodel = Adaptivity.coarsen(
49+
# fmodel, Geometry.PatchTopology(
50+
# get_grid_topology(fmodel),[Table([[1,2,4,5],[7,8,10,11]]),Table([[2,3,5,6],[8,9,11,12]])]
51+
# )
52+
# )
53+
#
54+
# GridapDistributed.isconsistent_faces(get_grid_topology(fmodel))
55+
# GridapDistributed.isconsistent_faces(get_grid_topology(cmodel))
56+
57+
3458
end # module

0 commit comments

Comments
 (0)