Skip to content

Commit 047bf7a

Browse files
committed
Fix branch 3 direction
1 parent b1e4d4d commit 047bf7a

3 files changed

Lines changed: 149 additions & 17 deletions

File tree

src/filament.jl

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,17 +84,22 @@ function velocity_3D_bound_vortex!(
8484
@debug "inside core radius"
8585
@debug "distance from control point to filament: $(nr1Xr0 / nr0)"
8686

87-
# Project onto core radius
88-
cross3!(r2Xr0, r2, r0)
87+
# Project onto core cylinder along the radial direction
88+
# (perpendicular component of r1 to r0). r1 and r2 share the
89+
# same radial component, so one radial unit vector serves both.
8990
nr0sq = nr0 * nr0
90-
nr2Xr0 = norm3(r2Xr0)
9191
d_r1_r0 = dot3(r1, r0)
9292
d_r2_r0 = dot3(r2, r0)
93+
r_rad = r1Xr0 # reuse buffer; no longer needed below
94+
@inbounds for k in 1:3
95+
r_rad[k] = r1[k] - d_r1_r0 * r0[k] / nr0sq
96+
end
97+
nr_rad = norm3(r_rad)
9398
@inbounds for k in 1:3
9499
r1_proj[k] = d_r1_r0 * r0[k] / nr0sq +
95-
epsilon * r1Xr0[k] / nr1Xr0
100+
epsilon * r_rad[k] / nr_rad
96101
r2_proj[k] = d_r2_r0 * r0[k] / nr0sq +
97-
epsilon * r2Xr0[k] / nr2Xr0
102+
epsilon * r_rad[k] / nr_rad
98103
end
99104
cross3!(r1_projXr2_proj, r1_proj, r2_proj)
100105

@@ -289,13 +294,14 @@ function velocity_3D_trailing_vortex_semiinfinite!(
289294
else
290295
r1_proj = work_vectors[4]
291296
cross_tmp = work_vectors[5]
292-
# temp = r1/nr1 - Vf
297+
# Radial direction: perpendicular component of r1 to Vf.
298+
nVfsq = nVf * nVf
293299
@inbounds for k in 1:3
294-
cross_tmp[k] = r1[k]/nr1 - Vf[k]
300+
cross_tmp[k] = r1[k] - d_r1_Vf * Vf[k] / nVfsq
295301
end
296302
n_tmp = norm3(cross_tmp)
297303
@inbounds for k in 1:3
298-
r1_proj[k] = d_r1_Vf * Vf[k] +
304+
r1_proj[k] = d_r1_Vf * Vf[k] / nVfsq +
299305
epsilon * cross_tmp[k] / n_tmp
300306
end
301307
cross3!(cross_tmp, r1_proj, Vf)

test/filament/test_bound_filament.jl

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,35 +133,35 @@ end
133133
@testset "Around Core Radius" begin
134134
filament = create_test_filament()
135135
delta = 1e-5
136-
136+
137137
points = [
138138
[0.5, core_radius_fraction - delta, 0.0],
139139
[0.5, core_radius_fraction, 0.0],
140140
[0.5, core_radius_fraction + delta, 0.0]
141141
]
142-
142+
143143
velocities = [zeros(3) for p in points]
144144
[
145145
velocity_3D_bound_vortex!(velocities[i], filament, p, gamma, core_radius_fraction, work_vectors)
146146
for (i, p) in enumerate(points)
147147
]
148-
148+
149149
# Check for NaN and finite values
150150
@test all(!any(isnan.(v)) for v in velocities)
151151
@test all(all(isfinite.(v)) for v in velocities)
152-
152+
153153
# Check magnitude is maximum at core radius
154154
@test norm(velocities[2]) > norm(velocities[1])
155155
@test norm(velocities[2]) > norm(velocities[3])
156-
156+
157157
# Check continuity around core radius
158158
@test isapprox(velocities[1], velocities[2], rtol=1e-2)
159-
159+
160160
# Check non-zero velocities
161161
@test !all(isapprox.(velocities[1], zeros(3), atol=1e-10))
162162
@test !all(isapprox.(velocities[2], zeros(3), atol=1e-10))
163163
@test !all(isapprox.(velocities[3], zeros(3), atol=1e-10))
164-
164+
165165
# Check symmetry
166166
v_neg = zeros(3)
167167
velocity_3D_bound_vortex!(
@@ -174,4 +174,82 @@ end
174174
)
175175
@test isapprox(velocities[2], -v_neg)
176176
end
177+
178+
@testset "Velocity is azimuthal (perpendicular to axis and radius)" begin
179+
# A real vortex's induced velocity is purely azimuthal:
180+
# perpendicular to BOTH the filament axis r0 AND the radial
181+
# direction from the axis to the field point. The old Branch 3
182+
# projected the field point in the azimuthal direction instead
183+
# of the radial direction, producing a velocity with a nonzero
184+
# radial component. This test catches that.
185+
filament = create_test_filament()
186+
r0 = [1.0, 0.0, 0.0]
187+
188+
# Probe at multiple distances spanning both branches (in-core
189+
# and outside-core) and azimuthal positions.
190+
for d in (0.25, 0.5, 0.99, 1.0, 1.01, 2.0) .* core_radius_fraction
191+
for phi in (0.0, π/4, π/2, π, -π/3)
192+
p = [0.5, d * cos(phi), d * sin(phi)]
193+
v = zeros(3)
194+
velocity_3D_bound_vortex!(
195+
v, filament, p, gamma,
196+
core_radius_fraction, work_vectors)
197+
198+
# Radial direction at p (perpendicular to axis)
199+
r_radial = [0.0, p[2], p[3]]
200+
201+
# v must be perpendicular to both axis and radial
202+
@test isapprox(dot(v, r0), 0.0; atol=1e-10)
203+
@test isapprox(dot(v, r_radial), 0.0; atol=1e-8)
204+
end
205+
end
206+
end
207+
208+
@testset "Solid-body rotation inside core" begin
209+
# Inside the core, velocity magnitude scales linearly with
210+
# d_perp (Branch 3 = linear ramp from 0 on axis to peak at
211+
# boundary). Direction stays constant along a radial line.
212+
filament = create_test_filament()
213+
214+
v_half = zeros(3)
215+
v_quarter = zeros(3)
216+
velocity_3D_bound_vortex!(
217+
v_half, filament,
218+
[0.5, 0.5 * core_radius_fraction, 0.0],
219+
gamma, core_radius_fraction, work_vectors)
220+
velocity_3D_bound_vortex!(
221+
v_quarter, filament,
222+
[0.5, 0.25 * core_radius_fraction, 0.0],
223+
gamma, core_radius_fraction, work_vectors)
224+
225+
# Magnitudes scale linearly with d_perp
226+
@test isapprox(norm(v_quarter), 0.5 * norm(v_half); rtol=1e-3)
227+
# Directions are parallel
228+
@test isapprox(normalize(v_quarter), normalize(v_half); atol=1e-8)
229+
end
230+
231+
@testset "Branch 1 / Branch 3 agree at core boundary" begin
232+
# The two branches are designed to be continuous at d_perp =
233+
# epsilon. Probe just inside and just outside the boundary with
234+
# enough delta to clearly land in different branches; results
235+
# must agree in both magnitude AND direction. Old code returned
236+
# orthogonal directions here.
237+
filament = create_test_filament()
238+
delta = 0.05 * core_radius_fraction # 5% in/out
239+
v_inside = zeros(3)
240+
v_outside = zeros(3)
241+
velocity_3D_bound_vortex!(
242+
v_inside, filament,
243+
[0.5, core_radius_fraction - delta, 0.0],
244+
gamma, core_radius_fraction, work_vectors)
245+
velocity_3D_bound_vortex!(
246+
v_outside, filament,
247+
[0.5, core_radius_fraction + delta, 0.0],
248+
gamma, core_radius_fraction, work_vectors)
249+
250+
# Magnitudes within a few % of each other (peak is at boundary)
251+
@test isapprox(norm(v_inside), norm(v_outside); rtol=0.1)
252+
# Directions match — would fail with old azimuthal-projection bug
253+
@test isapprox(normalize(v_inside), normalize(v_outside); atol=1e-2)
254+
end
177255
end

test/filament/test_semi_infinite_filament.jl

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ end
118118
filament = create_test_filament2()
119119
vel_pos = zeros(3)
120120
vel_neg = zeros(3)
121-
121+
122122
velocity_3D_trailing_vortex_semiinfinite!(
123123
vel_pos,
124124
filament,
@@ -137,7 +137,55 @@ end
137137
filament.vel_mag,
138138
work_vectors
139139
)
140-
140+
141141
@test isapprox(vel_pos, -vel_neg)
142142
end
143+
144+
@testset "Velocity is azimuthal (perpendicular to axis and radius)" begin
145+
# Vortex induced velocity is purely azimuthal: perpendicular
146+
# to BOTH the filament axis and the radial direction. Old
147+
# Branch 3 projected along the azimuthal direction instead of
148+
# the radial direction, giving a non-azimuthal velocity.
149+
filament = create_test_filament2()
150+
direction = filament.direction
151+
152+
# Lamb-Oseen epsilon at x=0.5 is ~sqrt(4·α₀·ν·0.5) ≈ 6e-3.
153+
# Probe across that range.
154+
for d in (1e-4, 1e-3, 5e-3, 1e-2, 1e-1)
155+
for phi in (0.0, π/4, π/2, π, -π/3)
156+
p = [0.5, d * cos(phi), d * sin(phi)]
157+
v = zeros(3)
158+
velocity_3D_trailing_vortex_semiinfinite!(
159+
v, filament, direction, p, gamma,
160+
filament.vel_mag, work_vectors)
161+
162+
r_radial = [0.0, p[2], p[3]]
163+
@test isapprox(dot(v, direction), 0.0; atol=1e-10)
164+
# Looser tol — semi-infinite velocity has an axial
165+
# contribution from the (1 + r1·Vf/|r1|) factor.
166+
@test isapprox(dot(v, r_radial) / norm(v), 0.0; atol=1e-6)
167+
end
168+
end
169+
end
170+
171+
@testset "Solid-body rotation inside core" begin
172+
# At small d_perp (inside the Lamb-Oseen core), Branch 3 gives
173+
# a linear ramp in magnitude with constant direction.
174+
filament = create_test_filament2()
175+
v_a = filament.vel_mag
176+
177+
# epsilon at x=0.5 ≈ sqrt(4·α₀·ν·0.5/v_a) ≈ 6.1e-3
178+
# Probe well inside the core.
179+
d_inside = 1e-4 # ~60× smaller than epsilon — definitely inside
180+
v1 = zeros(3); v2 = zeros(3)
181+
velocity_3D_trailing_vortex_semiinfinite!(
182+
v1, filament, filament.direction,
183+
[0.5, d_inside, 0.0], gamma, v_a, work_vectors)
184+
velocity_3D_trailing_vortex_semiinfinite!(
185+
v2, filament, filament.direction,
186+
[0.5, 2 * d_inside, 0.0], gamma, v_a, work_vectors)
187+
188+
@test isapprox(norm(v2), 2 * norm(v1); rtol=1e-3)
189+
@test isapprox(normalize(v2), normalize(v1); atol=1e-8)
190+
end
143191
end

0 commit comments

Comments
 (0)