Skip to content

Commit ac9108c

Browse files
author
LasNikas
committed
implement different mirror methods
1 parent a8b6555 commit ac9108c

2 files changed

Lines changed: 237 additions & 46 deletions

File tree

src/TrixiParticles.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export tensile_instability_control
8181
export BoundaryModelMonaghanKajtar, BoundaryModelDummyParticles, AdamiPressureExtrapolation,
8282
PressureMirroring, PressureZeroing, BoundaryModelLastiwka, BoundaryModelTafuni,
8383
BernoulliPressureExtrapolation
84+
export FirstOrderMirroring, ZerothOrderMirroring, SimpleMirroring
8485
export HertzContactModel, LinearContactModel
8586
export BoundaryMovement
8687
export examples_dir, validation_dir

src/schemes/boundary/open_boundary/mirroring.jl

Lines changed: 236 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,33 @@ to the buffer zones (inflow and outflow) using ghost nodes.
77
The position of the ghost nodes is obtained by mirroring the boundary particles
88
into the fluid along a direction that is normal to the open boundary.
99
"""
10-
struct BoundaryModelTafuni end
10+
struct BoundaryModelTafuni{MM}
11+
mirror_method::MM
12+
end
13+
14+
struct FirstOrderMirroring{ELTYPE}
15+
firstorder_tolerance::ELTYPE
16+
function FirstOrderMirroring(; firstorder_tolerance::ELTYPE=1e-3) where {ELTYPE}
17+
return new{typeof(firstorder_tolerance)}(firstorder_tolerance)
18+
end
19+
end
20+
21+
struct SimpleMirroring{ELTYPE}
22+
firstorder_tolerance::ELTYPE
23+
function SimpleMirroring(; firstorder_tolerance::Real=1e-3)
24+
return new{typeof(firstorder_tolerance)}(firstorder_tolerance)
25+
end
26+
end
1127

12-
function update_boundary_quantities!(system, ::BoundaryModelTafuni, v, u, v_ode, u_ode,
13-
semi, t)
28+
struct ZerothOrderMirroring end
29+
30+
function BoundaryModelTafuni(;
31+
mirror_method=FirstOrderMirroring(; firstorder_tolerance=1e-3))
32+
return BoundaryModelTafuni(mirror_method)
33+
end
34+
35+
function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni,
36+
v, u, v_ode, u_ode, semi, t)
1437
@trixi_timeit timer() "extrapolate and correct values" begin
1538
fluid_system = corresponding_fluid_system(system, semi)
1639

@@ -19,23 +42,23 @@ function update_boundary_quantities!(system, ::BoundaryModelTafuni, v, u, v_ode,
1942
u_open_boundary = wrap_u(u_ode, system, semi)
2043
u_fluid = wrap_u(u_ode, fluid_system, semi)
2144

22-
extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary, u_fluid,
23-
semi, t; system.cache...)
45+
extrapolate_values!(system, boundary_model.mirror_method, v_open_boundary, v_fluid,
46+
u_open_boundary, u_fluid, semi, t; system.cache...)
2447
end
2548
end
2649

2750
update_final!(system, ::BoundaryModelTafuni, v, u, v_ode, u_ode, semi, t) = system
2851

29-
function extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary, u_fluid,
52+
function extrapolate_values!(system,
53+
mirror_method::Union{FirstOrderMirroring, SimpleMirroring},
54+
v_open_boundary, v_fluid, u_open_boundary, u_fluid,
3055
semi, t; prescribed_density=false,
3156
prescribed_pressure=false, prescribed_velocity=false)
3257
(; pressure, density, boundary_zone, reference_density,
3358
reference_velocity, reference_pressure) = system
3459

3560
fluid_system = corresponding_fluid_system(system, semi)
3661

37-
state_equation = system_state_equation(fluid_system)
38-
3962
# Static indices to avoid allocations
4063
two_to_end = SVector{ndims(system)}(2:(ndims(system) + 1))
4164

@@ -74,13 +97,7 @@ function extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary,
7497
m_b = hydrodynamic_mass(fluid_system, neighbor)
7598
rho_b = current_density(v_fluid, fluid_system, neighbor)
7699
pressure_b = current_pressure(v_fluid, fluid_system, neighbor)
77-
v_b_ = current_velocity(v_fluid, fluid_system, neighbor)
78-
79-
# Project `v_b_` on the normal direction of the boundary zone (only for inflow boundaries).
80-
# See https://doi.org/10.1016/j.jcp.2020.110029 Section 3.3.:
81-
# "Because flow from the inlet interface occurs perpendicular to the boundary,
82-
# only this component of interpolated velocity is kept [...]"
83-
v_b = project_velocity_on_plane_normal(v_b_, boundary_zone)
100+
v_b = current_velocity(v_fluid, fluid_system, neighbor)
84101

85102
kernel_value = smoothing_kernel(fluid_system, distance, particle)
86103
grad_kernel = smoothing_kernel_grad(fluid_system, pos_diff, distance,
@@ -105,22 +122,189 @@ function extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary,
105122
end
106123
end
107124

108-
# See also `correction_matrix_inversion_step!` for an explanation
109-
if abs(det(correction_matrix[])) < 1.0f-9
110-
L_inv = typeof(correction_matrix[])(I)
111-
else
125+
pos_diff = particle_coords - ghost_node_position
126+
127+
# Mirror back the ghost node values to the boundary particles
128+
if abs(det(correction_matrix[])) >= mirror_method.firstorder_tolerance
112129
L_inv = inv(correction_matrix[])
130+
131+
# pressure
132+
if prescribed_pressure
133+
pressure[particle] = reference_value(reference_pressure, pressure[particle],
134+
particle_coords, t)
135+
else
136+
f_p = L_inv * extrapolated_pressure_correction[]
137+
df_p = f_p[two_to_end] # f_p[2:end] as SVector
138+
139+
gradient_part = mirror_method isa SimpleMirroring ? 0 : dot(pos_diff, df_p)
140+
141+
pressure[particle] = f_p[1] + gradient_part
142+
end
143+
144+
# density
145+
if prescribed_density
146+
density[particle] = reference_value(reference_density, density[particle],
147+
particle_coords, t)
148+
else
149+
f_d = L_inv * extrapolated_density_correction[]
150+
df_d = f_d[two_to_end] # f_d[2:end] as SVector
151+
152+
gradient_part = mirror_method isa SimpleMirroring ? 0 : dot(pos_diff, df_d)
153+
154+
density[particle] = f_d[1] + gradient_part
155+
end
156+
157+
# velocity
158+
if prescribed_velocity
159+
v_particle = current_velocity(v_open_boundary, system, particle)
160+
v_ref = reference_value(reference_velocity, v_particle, particle_coords, t)
161+
@inbounds for dim in eachindex(v_ref)
162+
v_open_boundary[dim, particle] = v_ref[dim]
163+
end
164+
else
165+
@inbounds for dim in eachindex(pos_diff)
166+
f_v = L_inv * extrapolated_velocity_correction[][dim, :]
167+
df_v = f_v[two_to_end] # f_v[2:end] as SVector
168+
169+
gradient_part = mirror_method isa SimpleMirroring ? 0 :
170+
dot(pos_diff, df_v)
171+
172+
v_open_boundary[dim, particle] = f_v[1] + gradient_part
173+
end
174+
175+
# Project the velocity on the normal direction of the boundary zone (only for inflow boundaries).
176+
# See https://doi.org/10.1016/j.jcp.2020.110029 Section 3.3.:
177+
# "Because flow from the inlet interface occurs perpendicular to the boundary,
178+
# only this component of interpolated velocity is kept [...]"
179+
project_velocity_on_plane_normal!(v_open_boundary, system, particle,
180+
boundary_zone)
181+
end
182+
183+
elseif correction_matrix[][1, 1] > eps()
184+
# Determinant is small, fallback to zero-th order mirroring
185+
shepard_coefficient = correction_matrix[][1, 1]
186+
187+
# pressure
188+
if prescribed_pressure
189+
pressure[particle] = reference_value(reference_pressure, pressure[particle],
190+
particle_coords, t)
191+
else
192+
pressure[particle] = first(extrapolated_pressure_correction[]) /
193+
shepard_coefficient
194+
end
195+
196+
# density
197+
if prescribed_density
198+
density[particle] = reference_value(reference_density, density[particle],
199+
particle_coords, t)
200+
else
201+
density[particle] = first(extrapolated_density_correction[]) /
202+
shepard_coefficient
203+
end
204+
205+
# velocity
206+
if prescribed_velocity
207+
v_particle = current_velocity(v_open_boundary, system, particle)
208+
v_ref = reference_value(reference_velocity, v_particle, particle_coords, t)
209+
@inbounds for dim in eachindex(v_ref)
210+
v_open_boundary[dim, particle] = v_ref[dim]
211+
end
212+
else
213+
velocity_interpolated = extrapolated_velocity_correction[][:, 1] /
214+
shepard_coefficient
215+
216+
@inbounds for dim in eachindex(velocity_interpolated)
217+
v_open_boundary[dim, particle] = velocity_interpolated[dim]
218+
end
219+
220+
# Project the velocity on the normal direction of the boundary zone (only for inflow boundaries).
221+
# See https://doi.org/10.1016/j.jcp.2020.110029 Section 3.3.:
222+
# "Because flow from the inlet interface occurs perpendicular to the boundary,
223+
# only this component of interpolated velocity is kept [...]"
224+
project_velocity_on_plane_normal!(v_open_boundary, system, particle,
225+
boundary_zone)
226+
end
227+
end
228+
end
229+
230+
if !(prescribed_velocity) && boundary_zone.average_inflow_velocity
231+
# When no velocity is prescribed at the inflow, the velocity is extrapolated from the fluid domain.
232+
# Thus, turbulent flows near the inflow can lead to non-uniform buffer particles distribution,
233+
# resulting in a potential numerical instability. Averaging mitigates these effects.
234+
average_velocity!(v_open_boundary, u_open_boundary, system, boundary_zone, semi)
235+
end
236+
237+
return system
238+
end
239+
240+
function extrapolate_values!(system, ::ZerothOrderMirroring,
241+
v_open_boundary, v_fluid, u_open_boundary, u_fluid,
242+
semi, t; prescribed_density=false,
243+
prescribed_pressure=false, prescribed_velocity=false)
244+
(; pressure, density, boundary_zone, reference_density,
245+
reference_velocity, reference_pressure) = system
246+
247+
fluid_system = corresponding_fluid_system(system, semi)
248+
249+
# Use the fluid-fluid nhs, since the boundary particles are mirrored into the fluid domain
250+
nhs = get_neighborhood_search(fluid_system, fluid_system, semi)
251+
252+
fluid_coords = current_coordinates(u_fluid, fluid_system)
253+
254+
# We cannot use `foreach_point_neighbor` here because we are looking for neighbors
255+
# of the ghost node positions of each particle.
256+
# We can do this because we require the neighborhood search to support querying neighbors
257+
# of arbitrary positions (see `PointNeighbors.requires_update`).
258+
@threaded semi for particle in each_moving_particle(system)
259+
particle_coords = current_coords(u_open_boundary, system, particle)
260+
ghost_node_position = mirror_position(particle_coords, boundary_zone)
261+
262+
# Use `Ref` to ensure the variables are accessible and mutable within the closure below
263+
# (see https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-captured).
264+
shepard_coefficient = Ref(zero(eltype(system)))
265+
interpolated_density = Ref(zero(eltype(system)))
266+
interpolated_pressure = Ref(zero(eltype(system)))
267+
interpolated_velocity = Ref(zero(particle_coords))
268+
269+
# TODO: Not public API
270+
PointNeighbors.foreach_neighbor(fluid_coords, nhs, particle, ghost_node_position,
271+
nhs.search_radius) do particle, neighbor, pos_diff,
272+
distance
273+
m_b = hydrodynamic_mass(fluid_system, neighbor)
274+
rho_b = current_density(v_fluid, fluid_system, neighbor)
275+
volume_b = m_b / rho_b
276+
pressure_b = current_pressure(v_fluid, fluid_system, neighbor)
277+
vel_b = current_velocity(v_fluid, fluid_system, neighbor)
278+
279+
W_ab = smoothing_kernel(fluid_system, distance, particle)
280+
281+
shepard_coefficient[] += volume_b * W_ab
282+
283+
if !prescribed_pressure
284+
interpolated_pressure[] += pressure_b * volume_b * W_ab
285+
end
286+
287+
if !prescribed_velocity
288+
interpolated_velocity[] += vel_b * volume_b * W_ab
289+
end
290+
291+
if !prescribed_density
292+
interpolated_density[] += rho_b * volume_b * W_ab
293+
end
294+
end
295+
296+
if shepard_coefficient[] > sqrt(eps())
297+
interpolated_density[] /= shepard_coefficient[]
298+
interpolated_pressure[] /= shepard_coefficient[]
299+
interpolated_velocity[] /= shepard_coefficient[]
300+
else
301+
interpolated_density[] = current_density(v_open_boundary, system, particle)
302+
interpolated_pressure[] = current_pressure(v_open_boundary, system, particle)
303+
interpolated_velocity[] = current_velocity(v_open_boundary, system, particle)
113304
end
114305

115306
pos_diff = particle_coords - ghost_node_position
116307

117-
# In Negi et al. (2020) https://doi.org/10.1016/j.cma.2020.113119,
118-
# they have modified the equation for extrapolation to
119-
#
120-
# f_0 = f_k - (r_0 - r_k) ⋅ ∇f_k
121-
#
122-
# in order to get zero gradient at the outlet interface.
123-
# Note: This modification is mentioned here for reference only and is NOT applied in this implementation.
124308
if prescribed_velocity
125309
v_particle = current_velocity(v_open_boundary, system, particle)
126310
v_ref = reference_value(reference_velocity, v_particle, particle_coords, t)
@@ -129,31 +313,29 @@ function extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary,
129313
end
130314
else
131315
@inbounds for dim in eachindex(pos_diff)
132-
f_v = L_inv * extrapolated_velocity_correction[][dim, :]
133-
df_v = f_v[two_to_end]
134-
135-
v_open_boundary[dim, particle] = f_v[1] + dot(pos_diff, df_v)
316+
v_open_boundary[dim, particle] = interpolated_velocity[][dim]
136317
end
137-
end
138318

139-
if prescribed_pressure
140-
pressure[particle] = reference_value(reference_pressure, pressure[particle],
141-
particle_coords, t)
142-
else
143-
f_d = L_inv * extrapolated_pressure_correction[]
144-
df_d = f_d[two_to_end]
145-
146-
pressure[particle] = f_d[1] + dot(pos_diff, df_d)
319+
# Project the velocity on the normal direction of the boundary zone (only for inflow boundaries).
320+
# See https://doi.org/10.1016/j.jcp.2020.110029 Section 3.3.:
321+
# "Because flow from the inlet interface occurs perpendicular to the boundary,
322+
# only this component of interpolated velocity is kept [...]"
323+
project_velocity_on_plane_normal!(v_open_boundary, system, particle,
324+
boundary_zone)
147325
end
148326

149327
if prescribed_density
150328
density[particle] = reference_value(reference_density, density[particle],
151329
particle_coords, t)
152330
else
153-
f_d = L_inv * extrapolated_density_correction[]
154-
df_d = f_d[two_to_end]
331+
density[particle] = interpolated_density[]
332+
end
155333

156-
density[particle] = f_d[1] + dot(pos_diff, df_d)
334+
if prescribed_pressure
335+
pressure[particle] = reference_value(reference_pressure, pressure[particle],
336+
particle_coords, t)
337+
else
338+
pressure[particle] = interpolated_pressure[]
157339
end
158340
end
159341

@@ -252,12 +434,20 @@ function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, se
252434
return v
253435
end
254436

255-
project_velocity_on_plane_normal(vel, boundary_zone) = vel
437+
project_velocity_on_plane_normal!(v, system, particle, boundary_zone) = v
256438

257-
function project_velocity_on_plane_normal(vel, boundary_zone::BoundaryZone{InFlow})
258-
# Project `v_b` on the normal direction of the boundary zone
439+
function project_velocity_on_plane_normal!(v, system, particle,
440+
boundary_zone::BoundaryZone{InFlow})
441+
# Project `vel` on the normal direction of the boundary zone
259442
# See https://doi.org/10.1016/j.jcp.2020.110029 Section 3.3.:
260443
# "Because flow from the inlet interface occurs perpendicular to the boundary,
261444
# only this component of interpolated velocity is kept [...]"
262-
return dot(vel, boundary_zone.plane_normal) * boundary_zone.plane_normal
445+
vel = current_velocity(v, system, particle)
446+
vel_ = dot(vel, boundary_zone.plane_normal) * boundary_zone.plane_normal
447+
448+
@inbounds for dim in eachindex(vel)
449+
v[dim, particle] = vel_[dim]
450+
end
451+
452+
return v
263453
end

0 commit comments

Comments
 (0)