Skip to content

Commit dbeaec6

Browse files
committed
Add submerge key-word to panelize
I realized that it's pretty easy to find the z=0 point on a given strip and force strip below this point. This will let people trim ship hulls very easily. Also fixed up a bad test and the Wigley hull README example.
1 parent 0af0673 commit dbeaec6

File tree

6 files changed

+108
-39
lines changed

6 files changed

+108
-39
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
/Manifest.toml
2+
.vscode/settings.json

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,10 @@ See the docs for `NKPanelSystem`, `kelvin`, and the cited references for details
278278

279279
As a final application, let's simulate the classic Wigley hull with `NKPanels`
280280
```julia
281-
wigley(hᵤ;B=1/8,D=1/16,hᵥ=0.5hᵤ/D) = measure.(
282-
(u,v)->SA[u-0.5,-2B*u*(1-u)*(v)*(2-v),D*(v-1)],
283-
0.5hᵤ:hᵤ:1,(0.5hᵥ:hᵥ:1)',hᵤ,hᵥ)
281+
function wigley(hᵤ,hᵥ=hᵤ;B=1/8,D=1/6,kwargs...)
282+
S(u,v) = SA[u-0.5,-2B*u*(1-u)*(v)*(2-v),D*(v-1)]
283+
panelize(S;hᵤ,hᵥ,kwargs...)
284+
end
284285
NKsys = NKPanelSystem(wigley(0.025);ℓ=1/2π,sym_axes=2,contour=true) |> directsolve!
285286
viz(NKsys)
286287
```

examples/NDLPwriteup.jl

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,9 @@ The NDLP segmentation consistently produces a max deviation which is tight to th
143143

144144
# ╔═╡ b38c2b1c-a48c-4f1f-954b-95faaba680d7
145145
begin
146-
function metrics(method, r, u₀, u₁, Δs, dₙ)
146+
function metrics(method, r, u₀, u₁, N′, dₙ)
147147
L = seg_len(r, u₀, u₁) # total length of curve `r`
148-
Δs *= L # scale by L to compare across curves
148+
Δs = L/N′ # segment length limit for `r`
149149
u = method(r, u₀, u₁, Δs, dₙ) # sample points using `method`
150150
dl,δ = seg_len(r,u),seg_dev(r,u) # segment lengths and deviations
151151
return (;δ∞ = maximum(δ)/(Δs*dₙ), # scaled max deviation, should => 1!
@@ -189,7 +189,7 @@ begin
189189
end
190190
function NDLP(r, u₀, u₁, Δs, dₙ) # one-shot sampling! 🤓
191191
speed(u) = max(arcspeed(r)(u),√(Δs*aₙ(r,u)/8dₙ))
192-
rtol = 1e-6Δs # only needed since convergence study lets Δs→0
192+
rtol = 1e-6Δs # only needed since sensitivity study lets Δs→0
193193
S,s⁻¹ = ∫speed(speed, u₀, u₁; rtol)
194194
return s⁻¹.(range(0, S, round(Int,S/Δs)+1))
195195
end
@@ -220,21 +220,21 @@ end; plot!(aspect_ratio=:equal,xlabel="x",ylabel="y")
220220
221221
# ╔═╡ f7a8cdf1-2cdb-4d6b-a10f-3f2d980c836e
222222
let
223-
invΔs = 33; dₙ = 1/100; # held constant for all methods/test_curves
224-
println("Metrics using Δs = L/$invΔs and dₙ=$dₙ")
223+
N′ = 33; dₙ = 1/100; # held constant for all methods/test_curves
224+
println("Metrics using Δs = L/$N′ and dₙ=$dₙ")
225225
for method in (subdivision,κ_weighted,NDLP)
226226
println("\nMethod: $method")
227227
map(test_curves) do (name, r, range, _)
228-
(;name,metrics(method,r,range...,1/invΔs,dₙ)...)
228+
(;name,metrics(method,r,range...,N′,dₙ)...)
229229
end |> Table |> display
230230
end
231231
end
232232
233233
# ╔═╡ 561a1373-57a4-4b67-be5a-9d94c9496360
234234
md"""
235-
## Convergence study
235+
## Sampling sensitivity study
236236
237-
We also evaluate the convergence of the NDLP segmentation metrics as the $\Delta s$ and $d_n$ limits vary. We use the cubic spline fish as a representative curve.
237+
We also evaluate the sensitivity of the NDLP sampling as the $\Delta s$ and $d_n$ limits vary. We use the cubic spline fish as a representative curve.
238238
239239
Holding $d_n=1\%$ constant and *reducing* $\Delta s$ shows two distance phases.
240240
- In the first phase, the deviation $\max(\delta)$ goes rapidly to the limit $d_n\Delta s$ and holds steady while the excess number of segments and total variation in the panel lengths drops to zero with $\Delta s$.
@@ -244,14 +244,14 @@ Holding $d_n=1\%$ constant and *reducing* $\Delta s$ shows two distance phases.
244244
# ╔═╡ dcb68886-2771-4b37-91b6-9c983e118728
245245
let
246246
dₙ = 1e-2
247-
convergeΔs = map(logrange(1e-1,1e-4,70)) do Δs
248-
(;Δs,metrics(NDLP,fish_spline,0,1,Δs,dₙ)...)
247+
N′sweep = map(logrange(10,10000,70)) do N′
248+
(;N′,metrics(NDLP,fish_spline,0,1,N′,dₙ)...)
249249
end |> Table
250-
plot(convergeΔs.Δs,convergeΔs.δ∞,label="max(δ)/dₙΔs")
251-
plot!(convergeΔs.Δs,convergeΔs.σ,label="NΔs/L-1")
252-
plot!(convergeΔs.Δs,convergeΔs.Rₜᵥ,label="sum(R)/L")
253-
plot!(convergeΔs.Δs,convergeΔs.R∞,label="max(R)/Δs")
254-
plot!(ylabel="scaled metrics",xlabel="Δs",xscale=:log10,xflip=true)
250+
plot(N′sweep.N′,N′sweep.δ∞,label="max(δ)/dₙΔs")
251+
plot!(N′sweep.N′,N′sweep.σ,label="NΔs/L-1")
252+
plot!(N′sweep.N′,N′sweep.Rₜᵥ,label="sum(R)/L")
253+
plot!(N′sweep.N′,N′sweep.R∞,label="max(R)/Δs")
254+
plot!(ylabel="scaled metrics",xlabel="L/Δs",xscale=:log10)
255255
plot!(title="NDLP segmentation metrics with fixed dₙ=$dₙ")
256256
end
257257
@@ -264,16 +264,16 @@ Holding $\Delta s=L/100$ and _increasing_ $d_n$ shows a similar trend.
264264
265265
# ╔═╡ f67fc432-8031-4230-899b-b24fcb7b44f4
266266
let
267-
invΔs = 100
268-
convergedₙ = map(logrange(1,5e-4,100)) do dₙ
269-
(;dₙ,metrics(NDLP,fish_spline,0,1,1/invΔs,dₙ)...)
267+
N′ = 100
268+
dₙsweep = map(logrange(1,5e-4,100)) do dₙ
269+
(;dₙ,metrics(NDLP,fish_spline,0,1,N′,dₙ)...)
270270
end |> Table
271-
plot(convergedₙ.dₙ,convergedₙ.δ∞,label="max(δ)/dₙΔs")
272-
plot!(convergedₙ.dₙ,convergedₙ.σ,label="NΔs/L-1")
273-
plot!(convergedₙ.dₙ,convergedₙ.Rₜᵥ,label="sum(R)/L")
274-
plot!(convergedₙ.dₙ,convergedₙ.R∞,label="max(R)/Δs")
275-
plot!(ylabel="scaled metrics",xlabel="dₙ",xscale=:log10)
276-
plot!(title="NDLP segmentation metrics with fixed Δs=L/$invΔs")
271+
plot(dₙsweep.dₙ,dₙsweep.δ∞,label="max(δ)/dₙΔs")
272+
plot!(dₙsweep.dₙ,dₙsweep.σ,label="NΔs/L-1")
273+
plot!(dₙsweep.dₙ,dₙsweep.Rₜᵥ,label="sum(R)/L")
274+
plot!(dₙsweep.dₙ,dₙsweep.R∞,label="max(R)/Δs")
275+
plot!(ylabel="scaled metrics",xlabel="dₙ",xscale=:log10,ylims=(-0.04,1.8))
276+
plot!(title="NDLP segmentation metrics with fixed Δs=L/$N′")
277277
end
278278
279279
# ╔═╡ 6094d19e-e64a-4434-b6db-e5647fd71e78
@@ -309,7 +309,7 @@ TypedTables = "~1.4.6"
309309
PLUTO_MANIFEST_TOML_CONTENTS = """
310310
# This file is machine-generated - editing it directly is not advised
311311
312-
julia_version = "1.11.5"
312+
julia_version = "1.11.2"
313313
manifest_format = "2.0"
314314
project_hash = "84beccf13d730d2ef8c64387b8e5b55843d0e9dd"
315315
@@ -1082,7 +1082,7 @@ version = "0.3.27+1"
10821082
[[deps.OpenLibm_jll]]
10831083
deps = ["Artifacts", "Libdl"]
10841084
uuid = "05823500-19ac-5b8b-9628-191a04bc5112"
1085-
version = "0.8.5+0"
1085+
version = "0.8.1+2"
10861086
10871087
[[deps.OpenSSL]]
10881088
deps = ["BitFlags", "Dates", "MozillaCACerts_jll", "NetworkOptions", "OpenSSL_jll", "Sockets"]

examples/readme.jl

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using NeumannKelvin
2+
h = 0.1 # spacing
3+
freesurf = measure.((u,v)->SA[u,v,0],2:-h:-4,(2:-h:-2)',h,h)
4+
S(θ₁,θ₂) = SA[0.5cos(θ₁),-0.1sin(θ₂)*sin(θ₁),0.1cos(θ₂)*sin(θ₁)-0.15]
5+
body = panelize(S,0,π,0,2π,hᵤ=h)
6+
sys = BodyPanelSystem(body) |> directsolve!
7+
8+
bigbody = panelize(S,0,π,0,2π,hᵤ=1/200,N_max=Inf); #20k panels
9+
sys = BodyPanelSystem(bigbody,wrap=PanelTree) |> gmressolve!
10+
11+
halfbody = panelize(S,0,π,0,π,hᵤ=1/200,N_max=Inf); # half the θ₂ range
12+
BodyPanelSystem(halfbody,wrap=PanelTree,sym_axes=2) |> gmressolve!
13+
14+
directsolve!(BodyPanelSystem(body,U=SA[0,-1,0]),verbose=false) |> addedmass
15+
16+
= 1/4; h = 0.3# Froude-length and spacing
17+
# Make the free-surface grid using y-symmetry and resolving ℓ
18+
freesurf = measure.((u,v)->SA[u,-v,0],2:-h:-4,(h/2:h:2)',h,h)
19+
halfbody = panelize(S,0,π,0,π,hᵤ=h)
20+
21+
FSsys = FSPanelSystem(halfbody,freesurf;
22+
ℓ,sym_axes=2,θ²=16) |> gmressolve!
23+
steadyforce(FSsys)
24+
25+
# Read mesh, place it, and filter out panels intersecting z=0
26+
using GeometryBasics,FileIO
27+
function affine(mesh, A, b) # rotate, scale, and shift the mesh
28+
position = [Point3f(A * p + b) for p in mesh.position]
29+
GeometryBasics.Mesh(position, mesh.faces)
30+
end
31+
dolphin = let
32+
mesh = load("examples//LowPolyDolphin.stl")
33+
mesh = affine(mesh, SA[0 -1 0;1 0 0;0 0 1]/65,SA[0.043,0,-0.09])
34+
filter(p->abs(p.x[3])>0.01, panelize(mesh))
35+
end
36+
37+
# The rest of the system matches the example above
38+
h = 0.04; freesurf = measure.((u,v)->SA[u,-v,0],2/3:-h:-4/3,(-2/3:h:2/3)',h,h,T=Float32); # Float32 to match the Mesh
39+
Meshsys = FSPanelSystem(dolphin,freesurf;ℓ=9f-2) |> gmressolve!
40+
41+
using GLMakie # can also use Plots, or WGLMakie (for browsers)
42+
viz(Meshsys)
43+
44+
= 1/4; h = 0.3# Froude-length and spacing
45+
halfbody = panelize(S,0,π,0,π,hᵤ=h)
46+
NKsys = NKPanelSystem(halfbody;ℓ,sym_axes=2) |> directsolve!
47+
steadyforce(NKsys)
48+
49+
DBsys = BodyPanelSystem(halfbody;sym_axes=(2,3)) |> directsolve! |> steadyforce
50+
51+
function wigley(hᵤ,hᵥ=hᵤ;B=1/8,D=1/6,kwargs...)
52+
S(u,v) = SA[u-0.5,-2B*u*(1-u)*(v)*(2-v),D*(v-1)]
53+
panelize(S;hᵤ,hᵥ,kwargs...)
54+
end
55+
NKsys = NKPanelSystem(wigley(0.025);
56+
=1/2π,sym_axes=2,contour=true) |> directsolve!
57+
viz(NKsys)

src/panels.jl

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
"""
2-
panelize(surface,u₀=0,u₁=1,v₀=0,v₁=1;hᵤ=1,hᵥ=hᵤ,devlimit=0.05,transpose=false,flip=false,N_max=1000,kwargs...)
2+
panelize(surface,u₀=0,u₁=1,v₀=0,v₁=1;hᵤ=1,hᵥ=hᵤ,devlimit=0.05,
3+
transpose=false,flip=false,submerge=false,N_max=1000,kwargs...)
34
45
Panelize a parametric `surface` of `u∈[u₀,u₁]` and `v∈[v₀,v₁]`, returning a `Table` of panels.
56
67
The surface is split into strips roughly `hᵤ` wide which are split into panels roughly `hᵥ` high which are then `measure`d.
7-
Use `transpose=true` to change the strip direction and `flip=true` to flip the normal direction.
8-
The parameter `devlimit` sets the max deviation of a flat panel from the surface by reducing panel size in regions of high curvature.
8+
The parameter `devlimit` sets the max deviation `δ/(hᵤ+hᵥ)` from the surface by reducing panel size in regions of high curvature.
9+
Use `transpose=true` to change the strip direction, `flip=true` to flip the normal direction, and submerge=`true` to trim surface for `z≥0`.
910
This function throws an error if the adaptive routine gives more than `N_max` panels.
1011
"""
1112
function panelize(surface,u₀=0.,u₁=1.,v₀=0.,v₁=1.;hᵤ=1.,hᵥ=hᵤ,devlimit=0.05,
12-
transpose=false,flip=false,N_max=1000,verbose=false,T=Float64,kwargs...)
13+
transpose=false,flip=false,N_max=1000,verbose=false,submerge=false,T=Float64,kwargs...)
1314
# Transpose arguments u,v -> v,u
14-
transpose && return panelize((v,u)->surface(u,v),v₀,v₁,u₀,u₁,;hᵤ=hᵥ,hᵥ=hᵤ,devlimit,
15+
transpose && return panelize((v,u)->surface(u,v),v₀,v₁,u₀,u₁,;hᵤ,hᵥ,devlimit,
1516
transpose=false,flip=!flip,N_max,verbose,T,kwargs...)
1617

1718
# Check inputs and get output type
@@ -37,7 +38,8 @@ function panelize(surface,u₀=0.,u₁=1.,v₀=0.,v₁=1.;hᵤ=1.,hᵥ=hᵤ,devl
3738
u,du = 0.5uᵢ[i+1]+0.5uᵢ[i],uᵢ[i+1]-uᵢ[i] # Parametric center & width
3839

3940
# Find equidistant points along strip center
40-
S,s⁻¹ = arclength(v->surface(u,v),hᵥ,devlimit,v₀,v₁)
41+
uv₀,uv₁ = submerge ? newlimits(v->surface(u,v),v₀,v₁) : (v₀,v₁)
42+
S,s⁻¹ = arclength(v->surface(u,v),hᵥ,devlimit,uv₀,uv₁)
4143
verbose && @show i,S
4244
S 0.5hᵥ && return init # not enough height
4345
ve = s⁻¹(range(0,S,round(Int,S/hᵥ)+1)) # panel endpoints
@@ -53,7 +55,11 @@ function panelize(surface,u₀=0.,u₁=1.,v₀=0.,v₁=1.;hᵤ=1.,hᵥ=hᵤ,devl
5355
length(panels) N_max && return Table(panels)
5456
throw(ArgumentError("length(panels)=$(length(panels))>$N_max. Increase hᵤ,hᵥ,devlimit and/or N_max."))
5557
end
56-
58+
function newlimits(r,low,high)
59+
z(u) = r(u)[3]+√eps(u)
60+
zero = find_zero(z,(low,high))
61+
return z(low)<0 ? (low,zero) : (zero,high)
62+
end
5763
using QuadGK,DataInterpolations
5864
"""
5965
arclength(r, Δs, devlimit, low, high) -> S, u(s)

test/runtests.jl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,13 @@ end
6868
area_checks(panels.dA,0.18)
6969
@test sum(panels.dA) 4π^2*0.3
7070

71-
# Check sign is correct when flipped
72-
panels_bad = panelize(torus,0,2pi,0,2pi,hᵤ=0.6,hᵥ=0.3,devlimit=Inf)
73-
@test panels[1].n panels_bad[1].n > 0.99
71+
# Check sign is flipped
72+
panels = panelize(sphere,0,pi,0,2pi,hᵤ=0.6,flip=true)
73+
@test all(p->p.x'p.n<-0.9,panels)
74+
75+
# Check submerged
76+
panels = panelize(spheroid,0,2pi/3,0,2pi,hᵤ=0.6,hᵥ=0.3,transpose=true,submerge=true)
77+
@test all(x->x[3]<0,panels.verts)
7478
7579
# Check inputs
7680
@test_throws ArgumentError panelize(spheroid,0,pi,0,2pi,hᵤ=0)

0 commit comments

Comments
 (0)