Skip to content

Commit b117d92

Browse files
authored
Merge pull request #15 from JuliaGeometry/sjk/refactor1
Sjk/refactor1
2 parents 138dbb1 + 0af32d1 commit b117d92

File tree

4 files changed

+113
-83
lines changed

4 files changed

+113
-83
lines changed

src/distmeshnd.jl

Lines changed: 69 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ function distmesh(fdist::Function,
3535
distmesh(fdist, fh, h, setup, o, w, fp, Val(stats), VT)
3636
end
3737

38+
"""
39+
DistMeshResult
40+
41+
A struct returned from the `distmesh` function that includes point, simplex,
42+
and interation statistics.
43+
"""
44+
struct DistMeshResult{PT, TT, STATS}
45+
points::Vector{PT}
46+
tetrahedra::Vector{TT}
47+
stats::STATS
48+
end
49+
3850
function distmesh(fdist::Function,
3951
fh,
4052
h::Number,
@@ -71,14 +83,18 @@ function distmesh(fdist::Function,
7183
facecenteredcubic!(fdist, p, pt_dists, h, setup.iso, origin, widths, VertType)
7284
end
7385

86+
# Result struct for holding points, simplices, and iteration statistics
87+
result = DistMeshResult(p,
88+
GeometryBasics.SimplexFace{4,Int32}[],
89+
stats ? DistMeshStatistics() : nothing)
90+
7491
# initialize arrays
7592
pair_set = Set{Tuple{Int32,Int32}}() # set used for ensure we have a unique set of edges
7693
pair = Tuple{Int32,Int32}[] # edge indices (Int32 since we use Tetgen)
7794
dp = zeros(VertType, length(p)) # displacement at each node
7895
bars = VertType[] # the vector of each edge
7996
L = eltype(VertType)[] # vector length of each edge
8097
L0 = non_uniform ? eltype(VertType)[] : nothing # desired edge length computed by dh (edge length function)
81-
t = GeometryBasics.SimplexFace{4,Int32}[] # tetrahedra indices from delaunay triangulation
8298
maxmove = typemax(eltype(VertType)) # stores an iteration max movement for retriangulation
8399

84100
# arrays for tracking quality metrics
@@ -87,41 +103,28 @@ function distmesh(fdist::Function,
87103
qualities = eltype(VertType)[]
88104

89105
# information on each iteration
90-
statsdata = DistMeshStatistics()
91106
lcount = 0 # iteration counter
92107
triangulationcount = 0 # triangulation counter
93108

94109
@inbounds while true
95110
# if large move, retriangulation
96111
if maxmove>setup.ttol*h
97112

98-
# use hilbert sort to improve cache locality of points
99-
if setup.sort && iszero(triangulationcount % setup.sort_interval)
100-
hilbertsort!(p)
101-
end
102-
103113
# compute a new delaunay triangulation
104-
# we use the default random insertion method
105-
delaunayn!(fdist, p, t, geps, false)
114+
retriangulate!(fdist, result, geps, setup, triangulationcount, pt_dists)
106115

107-
tet_to_edges!(pair, pair_set, t) # Describe each edge by a unique pair of nodes
116+
tet_to_edges!(pair, pair_set, result.tetrahedra) # Describe each edge by a unique pair of nodes
108117

109118
# resize arrays for new pair counts
110119
resize!(bars, length(pair))
111120
resize!(L, length(pair))
112121
non_uniform && resize!(L0, length(pair))
113122

114-
# if the points were sorted we need to update the distance cache
115-
if setup.sort && iszero(triangulationcount % setup.sort_interval)
116-
for i in eachindex(p)
117-
pt_dists[i] = fdist(p[i])
118-
end
119-
end
120123
triangulationcount += 1
121-
stats && push!(statsdata.retriangulations, lcount)
124+
stats && push!(result.stats.retriangulations, lcount)
122125
end
123126

124-
compute_displacements!(fh, dp, pair, L, L0, bars, p, setup, VertType)
127+
compute_displacements!(fh, dp, pair, L, L0, bars, result.points, setup, VertType)
125128

126129
# Zero out forces on fix points
127130
if have_fixed
@@ -144,7 +147,7 @@ function distmesh(fdist::Function,
144147
# complex distance functions and large node counts
145148
move = sqrt(sum((p[i]-p0).^2)) # compute movement from the displacement
146149
d_est = pt_dists[i] + move # apply the movement to our cache point
147-
d = d_est < -geps ? d_est : fdist(p[i]) # determine if we need correct or approximate distance
150+
d = d_est < -geps ? d_est : fdist(result.points[i]) # determine if we need correct or approximate distance
148151
pt_dists[i] = d # store distance
149152

150153
if d < -geps
@@ -167,20 +170,59 @@ function distmesh(fdist::Function,
167170

168171
# save iteration stats
169172
if stats
170-
push!(statsdata.maxmove,maxmove)
171-
push!(statsdata.maxdp,maxdp)
172-
triangle_qualities!(tris,triset,qualities,p,t)
173+
push!(result.stats.maxmove,maxmove)
174+
push!(result.stats.maxdp,maxdp)
175+
triangle_qualities!(tris,triset,qualities,result.points,result.tetrahedra)
173176
sort!(qualities) # sort for median calc and robust summation
174177
mine, maxe = extrema(qualities)
175-
push!(statsdata.average_qual, sum(qualities)/length(qualities))
176-
push!(statsdata.median_qual, qualities[round(Int,length(qualities)/2)])
177-
push!(statsdata.minimum_qual, mine)
178-
push!(statsdata.maximum_qual, maxe)
178+
push!(result.stats.average_qual, sum(qualities)/length(qualities))
179+
push!(result.stats.median_qual, qualities[round(Int,length(qualities)/2)])
180+
push!(result.stats.minimum_qual, mine)
181+
push!(result.stats.maximum_qual, maxe)
179182
end
180183

181184
# Termination criterion
182185
if maxdp<setup.ptol*h
183-
return p, t, statsdata
186+
return result
184187
end
185188
end
186189
end
190+
191+
"""
192+
retriangulate!
193+
194+
195+
Given a point set, generate a delaunay triangulation, and other requirements.
196+
This includes:
197+
- Spatial sorting of points
198+
- Delaunay triangulation
199+
- Filtering of invalid tetrahedra outside the boundary
200+
"""
201+
function retriangulate!(fdist, result::DistMeshResult, geps, setup, triangulationcount, pt_dists)
202+
# use hilbert sort to improve cache locality of points
203+
if setup.sort && iszero(triangulationcount % setup.sort_interval)
204+
hilbertsort!(result.points, pt_dists)
205+
end
206+
207+
t = result.tetrahedra
208+
p = result.points
209+
triangulation = delaunayn(p)
210+
t_d = triangulation.tetrahedra
211+
resize!(t, length(t_d))
212+
copyto!(t, t_d) # we need to copy since we have a shared reference with tetgen
213+
214+
# average points to get mid point of each tetrahedra
215+
# if the mid point of the tetrahedra is outside of
216+
# the boundary we remove it.
217+
# TODO: this is an inlined filter call. Would be good to revert
218+
# TODO: can we use the point distance array to pass boundary points to
219+
# tetgen so this call is no longer required?
220+
j = firstindex(t)
221+
for ai in t
222+
t[j] = ai
223+
pm = (p[ai[1]].+p[ai[2]].+p[ai[3]].+p[ai[4]])./4
224+
j = ifelse(fdist(pm) <= -geps, nextind(t, j), j)
225+
end
226+
j <= lastindex(t) && resize!(t, j-1)
227+
nothing
228+
end

src/hilbertsort.jl

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ nextnext3d(c) = (c + 1) % 3 + 1
2020
const forward = true
2121
const backward = false
2222

23-
function select!(direction, coord, v::Array{T,1}, k::Integer, lo::Integer, hi::Integer) where T<:AbstractVector
23+
function select!(direction, coord, v::Array{T,1}, k::Integer, lo::Integer, hi::Integer, carry::CT) where {T<:AbstractVector, CT}
2424
#lo <= k <= hi || error("select index $k is out of range $lo:$hi")
2525
if direction == forward
2626
@inbounds while lo < hi
2727
if isone(hi-lo)
2828
if v[hi][coord] < v[lo][coord]
2929
v[lo], v[hi] = v[hi], v[lo]
30+
if CT !== Nothing; carry[lo], carry[hi] = carry[hi], carry[lo]; end
3031
end
3132
return #v[k]
3233
end
@@ -38,6 +39,7 @@ function select!(direction, coord, v::Array{T,1}, k::Integer, lo::Integer, hi::I
3839
while pivot_elt < v[j][coord]; j -= 1; end
3940
i <= j || break
4041
v[i], v[j] = v[j], v[i]
42+
if CT !== Nothing; carry[i], carry[j] = carry[j], carry[i]; end
4143
i += 1; j -= 1
4244
end
4345
if k <= j
@@ -53,6 +55,7 @@ function select!(direction, coord, v::Array{T,1}, k::Integer, lo::Integer, hi::I
5355
if isone(hi-lo)
5456
if v[hi][coord] > v[lo][coord]
5557
v[lo], v[hi] = v[hi], v[lo]
58+
if CT !== Nothing; carry[lo], carry[hi] = carry[hi], carry[lo]; end
5659
end
5760
return #v[k]
5861
end
@@ -64,6 +67,7 @@ function select!(direction, coord, v::Array{T,1}, k::Integer, lo::Integer, hi::I
6467
while pivot_elt > v[j][coord]; j -= 1; end
6568
i <= j || break
6669
v[i], v[j] = v[j], v[i]
70+
if CT !== Nothing; carry[i], carry[j] = carry[j], carry[i]; end
6771
i += 1; j -= 1
6872
end
6973
if k <= j
@@ -100,7 +104,7 @@ end
100104
# return a
101105
# end
102106

103-
function hilbertsort!(directionx, directiony, directionz, coordinate, a::Vector, lo::Integer, hi::Integer, lim::Integer=8)
107+
function hilbertsort!(directionx, directiony, directionz, coordinate, a::Vector, lo::Integer, hi::Integer, lim::Integer, carry)
104108
hi-lo <= lim && return a
105109

106110
i4 = (lo+hi)>>>1
@@ -111,27 +115,31 @@ function hilbertsort!(directionx, directiony, directionz, coordinate, a::Vector,
111115
i5 = (i4+i6)>>>1
112116
i7 = (i6+hi)>>>1
113117

114-
select!(directionx, coordinate, a, i4, lo, hi)
115-
select!(directiony, next3d(coordinate), a, i2, lo, i4)
116-
select!(directionz, nextnext3d(coordinate), a, i1, lo, i2)
117-
select!(!directionz, nextnext3d(coordinate), a, i3, i2, i4)
118-
select!(!directiony, next3d(coordinate), a, i6, i4, hi)
119-
select!(directionz, nextnext3d(coordinate), a, i5, i4, i6)
120-
select!(!directionz, nextnext3d(coordinate), a, i7, i6, hi)
121-
122-
hilbertsort!( directionz, directionx, directiony, nextnext3d(coordinate), a, lo, i1, lim)
123-
hilbertsort!( directiony, directionz, directionx, next3d(coordinate), a, i1, i2, lim)
124-
hilbertsort!( directiony, directionz, directionx, next3d(coordinate), a, i2, i3, lim)
125-
hilbertsort!( directionx, !directiony, !directionz, coordinate, a, i3, i4, lim)
126-
hilbertsort!( directionx, !directiony, !directionz, coordinate, a, i4, i5, lim)
127-
hilbertsort!(!directiony, directionz, !directionx, next3d(coordinate), a, i5, i6, lim)
128-
hilbertsort!(!directiony, directionz, !directionx, next3d(coordinate), a, i6, i7, lim)
129-
hilbertsort!(!directionz, !directionx, directiony, nextnext3d(coordinate), a, i7, hi, lim)
118+
select!(directionx, coordinate, a, i4, lo, hi, carry)
119+
select!(directiony, next3d(coordinate), a, i2, lo, i4, carry)
120+
select!(directionz, nextnext3d(coordinate), a, i1, lo, i2, carry)
121+
select!(!directionz, nextnext3d(coordinate), a, i3, i2, i4, carry)
122+
select!(!directiony, next3d(coordinate), a, i6, i4, hi, carry)
123+
select!(directionz, nextnext3d(coordinate), a, i5, i4, i6, carry)
124+
select!(!directionz, nextnext3d(coordinate), a, i7, i6, hi, carry)
125+
126+
hilbertsort!( directionz, directionx, directiony, nextnext3d(coordinate), a, lo, i1, lim, carry)
127+
hilbertsort!( directiony, directionz, directionx, next3d(coordinate), a, i1, i2, lim, carry)
128+
hilbertsort!( directiony, directionz, directionx, next3d(coordinate), a, i2, i3, lim, carry)
129+
hilbertsort!( directionx, !directiony, !directionz, coordinate, a, i3, i4, lim, carry)
130+
hilbertsort!( directionx, !directiony, !directionz, coordinate, a, i4, i5, lim, carry)
131+
hilbertsort!(!directiony, directionz, !directionx, next3d(coordinate), a, i5, i6, lim, carry)
132+
hilbertsort!(!directiony, directionz, !directionx, next3d(coordinate), a, i6, i7, lim, carry)
133+
hilbertsort!(!directionz, !directionx, directiony, nextnext3d(coordinate), a, i7, hi, lim, carry)
130134

131135
return a
132136
end
133137

134138
#hilbertsort!(a::Array{T,1}) where {T<:AbstractPoint2D} = hilbertsort!(backward, backward, coordinatey, a, 1, length(a))
135139
#hilbertsort!(a::Array{T,1}, lo::Int64, hi::Int64, lim::Int64) where {T<:AbstractPoint2D} = hilbertsort!(backward, backward, coordinatey, a, lo, hi, lim)
136-
hilbertsort!(a::Vector) = hilbertsort!(backward, backward, backward, coordinatez, a, 1, length(a))
140+
"""
141+
Hilbert Sorting. If `carry` is specified, this array will be permuted in line with the
142+
specified array.
143+
"""
144+
hilbertsort!(a::Vector, carry=nothing) = hilbertsort!(backward, backward, backward, coordinatez, a, 1, length(a), 8, carry)
137145
hilbertsort!(a::Vector, lo::Int64, hi::Int64, lim::Int64) = hilbertsort!(backward, backward, backward, coordinatey, a, lo, hi, lim)

src/tetgen.jl

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,6 @@ function delaunayn_nosort(points)
1515
tetio
1616
end
1717

18-
function delaunayn!(fdist, p, t, geps, sorted_pts)
19-
triangulation = sorted_pts ? delaunayn_nosort(p) : delaunayn(p)
20-
t_d = triangulation.tetrahedra
21-
resize!(t, length(t_d))
22-
copyto!(t, t_d) # we need to copy since we have a shared reference with tetgen
23-
24-
# average points to get mid point of each tetrahedra
25-
# if the mid point of the tetrahedra is outside of
26-
# the boundary we remove it.
27-
# TODO: this is an inlined filter call. Would be good to revert
28-
# TODO: can we use the point distance array to pass boundary points to
29-
# tetgen so this call is no longer required?
30-
j = firstindex(t)
31-
for ai in t
32-
t[j] = ai
33-
pm = (p[ai[1]].+p[ai[2]].+p[ai[3]].+p[ai[4]])./4
34-
j = ifelse(fdist(pm) <= -geps, nextind(t, j), j)
35-
end
36-
j <= lastindex(t) && resize!(t, j-1)
37-
nothing
38-
end
39-
4018
# needs some tweaks, gives garbage results, might need to twek the julia wrapper?
4119
function reconstruct!(points, tets)
4220
tetio = tetrahedralize(TetGen.TetgenIO(points,tetrahedrons=tets), "Qr") # Q- Quiet, r- retriangulate

test/runtests.jl

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -65,30 +65,32 @@ end
6565

6666
@testset "distmesh 3D" begin
6767
d(p) = sqrt(sum(p.^2))-1
68-
p,t,_ = distmesh(d,HUniform(),0.2)
69-
@test length(p) == 485
70-
@test length(t) == 2207
68+
result = distmesh(d,HUniform(),0.2)
69+
@test length(result.points) == 485
70+
@test length(result.tetrahedra) == 2207
7171

72-
p,t,_ = distmesh(d,HUniform(),0.2, DistMeshSetup(distribution=:packed))
73-
@test length(p) == 742
74-
@test length(t) == 3472
72+
result = distmesh(d,HUniform(),0.2, DistMeshSetup(distribution=:packed))
73+
@test length(result.points) == 742
74+
@test length(result.tetrahedra) == 3472
7575

7676
# test stats is not messing
77-
p,t,s = distmesh(d,HUniform(),0.2, stats=true)
78-
@test length(p) == 485
79-
@test length(t) == 2207
77+
result = distmesh(d,HUniform(),0.2, stats=true)
78+
@test length(result.points) == 485
79+
@test length(result.tetrahedra) == 2207
8080

81-
p,t,s = distmesh(d,HUniform(),0.4, stats=true)
82-
@test length(p) == 56
83-
@test length(t) == 186
84-
for fn in fieldnames(typeof(s))
85-
@test isapprox(getproperty(s,fn), getproperty(stat_04,fn))
81+
result = distmesh(d,HUniform(),0.4, stats=true)
82+
@test length(result.points) == 56
83+
@test length(result.tetrahedra) == 186
84+
for fn in fieldnames(typeof(result.stats))
85+
@test isapprox(getproperty(result.stats,fn), getproperty(stat_04,fn))
8686
end
8787
end
8888

8989
@testset "dihedral metrics" begin
9090
d(p) = sqrt(sum(p.^2))-1
91-
p,t,_ = distmesh(d,HUniform(),0.2)
91+
result = distmesh(d,HUniform(),0.2)
92+
p = result.points
93+
t = result.tetrahedra
9294
all_angs = DistMesh.dihedral_angles(p,t)
9395
min_angs = DistMesh.min_dihedral_angles(p,t)
9496
ax = extrema(all_angs)

0 commit comments

Comments
 (0)