Skip to content

Commit e3dcacc

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

2 files changed

Lines changed: 233 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: 232 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,187 @@ 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, particle, boundary_zone)
180+
end
181+
182+
elseif correction_matrix[][1, 1] > eps()
183+
# Determinant is small, fallback to zero-th order mirroring
184+
shepard_coefficient = correction_matrix[][1, 1]
185+
186+
# pressure
187+
if prescribed_pressure
188+
pressure[particle] = reference_value(reference_pressure, pressure[particle],
189+
particle_coords, t)
190+
else
191+
pressure[particle] = first(extrapolated_pressure_correction[]) /
192+
shepard_coefficient
193+
end
194+
195+
# density
196+
if prescribed_density
197+
density[particle] = reference_value(reference_density, density[particle],
198+
particle_coords, t)
199+
else
200+
density[particle] = first(extrapolated_density_correction[]) /
201+
shepard_coefficient
202+
end
203+
204+
# velocity
205+
if prescribed_velocity
206+
v_particle = current_velocity(v_open_boundary, system, particle)
207+
v_ref = reference_value(reference_velocity, v_particle, particle_coords, t)
208+
@inbounds for dim in eachindex(v_ref)
209+
v_open_boundary[dim, particle] = v_ref[dim]
210+
end
211+
else
212+
velocity_interpolated = extrapolated_velocity_correction[][:, 1] /
213+
shepard_coefficient
214+
215+
@inbounds for dim in eachindex(velocity_interpolated)
216+
v_open_boundary[dim, particle] = velocity_interpolated[dim]
217+
end
218+
219+
# Project the velocity on the normal direction of the boundary zone (only for inflow boundaries).
220+
# See https://doi.org/10.1016/j.jcp.2020.110029 Section 3.3.:
221+
# "Because flow from the inlet interface occurs perpendicular to the boundary,
222+
# only this component of interpolated velocity is kept [...]"
223+
project_velocity_on_plane_normal!(v_open_boundary, particle, boundary_zone)
224+
end
225+
end
226+
end
227+
228+
if !(prescribed_velocity) && boundary_zone.average_inflow_velocity
229+
# When no velocity is prescribed at the inflow, the velocity is extrapolated from the fluid domain.
230+
# Thus, turbulent flows near the inflow can lead to non-uniform buffer particles distribution,
231+
# resulting in a potential numerical instability. Averaging mitigates these effects.
232+
average_velocity!(v_open_boundary, u_open_boundary, system, boundary_zone, semi)
233+
end
234+
235+
return system
236+
end
237+
238+
function extrapolate_values!(system, ::ZerothOrderMirroring,
239+
v_open_boundary, v_fluid, u_open_boundary, u_fluid,
240+
semi, t; prescribed_density=false,
241+
prescribed_pressure=false, prescribed_velocity=false)
242+
(; pressure, density, boundary_zone, reference_density,
243+
reference_velocity, reference_pressure) = system
244+
245+
fluid_system = corresponding_fluid_system(system, semi)
246+
247+
# Use the fluid-fluid nhs, since the boundary particles are mirrored into the fluid domain
248+
nhs = get_neighborhood_search(fluid_system, fluid_system, semi)
249+
250+
fluid_coords = current_coordinates(u_fluid, fluid_system)
251+
252+
# We cannot use `foreach_point_neighbor` here because we are looking for neighbors
253+
# of the ghost node positions of each particle.
254+
# We can do this because we require the neighborhood search to support querying neighbors
255+
# of arbitrary positions (see `PointNeighbors.requires_update`).
256+
@threaded semi for particle in each_moving_particle(system)
257+
particle_coords = current_coords(u_open_boundary, system, particle)
258+
ghost_node_position = mirror_position(particle_coords, boundary_zone)
259+
260+
# Use `Ref` to ensure the variables are accessible and mutable within the closure below
261+
# (see https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-captured).
262+
shepard_coefficient = Ref(zero(eltype(system)))
263+
interpolated_density = Ref(zero(eltype(system)))
264+
interpolated_pressure = Ref(zero(eltype(system)))
265+
interpolated_velocity = Ref(zero(particle_coords))
266+
267+
# TODO: Not public API
268+
PointNeighbors.foreach_neighbor(fluid_coords, nhs, particle, ghost_node_position,
269+
nhs.search_radius) do particle, neighbor, pos_diff,
270+
distance
271+
m_b = hydrodynamic_mass(fluid_system, neighbor)
272+
rho_b = current_density(v_fluid, fluid_system, neighbor)
273+
volume_b = m_b / rho_b
274+
pressure_b = current_pressure(v_fluid, fluid_system, neighbor)
275+
vel_b = current_velocity(v_fluid, fluid_system, neighbor)
276+
277+
W_ab = smoothing_kernel(fluid_system, distance, particle)
278+
279+
shepard_coefficient[] += volume_b * W_ab
280+
281+
if !prescribed_pressure
282+
interpolated_pressure[] += pressure_b * volume_b * W_ab
283+
end
284+
285+
if !prescribed_velocity
286+
interpolated_velocity[] += vel_b * volume_b * W_ab
287+
end
288+
289+
if !prescribed_density
290+
interpolated_density[] += rho_b * volume_b * W_ab
291+
end
292+
end
293+
294+
if shepard_coefficient[] > sqrt(eps())
295+
interpolated_density[] /= shepard_coefficient[]
296+
interpolated_pressure[] /= shepard_coefficient[]
297+
interpolated_velocity[] /= shepard_coefficient[]
298+
else
299+
interpolated_density[] = current_density(v_open_boundary, system, particle)
300+
interpolated_pressure[] = current_pressure(v_open_boundary, system, particle)
301+
interpolated_velocity[] = current_velocity(v_open_boundary, system, particle)
113302
end
114303

115304
pos_diff = particle_coords - ghost_node_position
116305

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.
124306
if prescribed_velocity
125307
v_particle = current_velocity(v_open_boundary, system, particle)
126308
v_ref = reference_value(reference_velocity, v_particle, particle_coords, t)
@@ -129,31 +311,28 @@ function extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary,
129311
end
130312
else
131313
@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)
314+
v_open_boundary[dim, particle] = interpolated_velocity[][dim]
136315
end
137-
end
138316

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)
317+
# Project the velocity on the normal direction of the boundary zone (only for inflow boundaries).
318+
# See https://doi.org/10.1016/j.jcp.2020.110029 Section 3.3.:
319+
# "Because flow from the inlet interface occurs perpendicular to the boundary,
320+
# only this component of interpolated velocity is kept [...]"
321+
project_velocity_on_plane_normal!(v_open_boundary, particle, boundary_zone)
147322
end
148323

149324
if prescribed_density
150325
density[particle] = reference_value(reference_density, density[particle],
151326
particle_coords, t)
152327
else
153-
f_d = L_inv * extrapolated_density_correction[]
154-
df_d = f_d[two_to_end]
328+
density[particle] = interpolated_density[]
329+
end
155330

156-
density[particle] = f_d[1] + dot(pos_diff, df_d)
331+
if prescribed_pressure
332+
pressure[particle] = reference_value(reference_pressure, pressure[particle],
333+
particle_coords, t)
334+
else
335+
pressure[particle] = interpolated_pressure[]
157336
end
158337
end
159338

@@ -252,12 +431,19 @@ function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, se
252431
return v
253432
end
254433

255-
project_velocity_on_plane_normal(vel, boundary_zone) = vel
434+
project_velocity_on_plane_normal!(v, particle, boundary_zone) = v
256435

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

0 commit comments

Comments
 (0)