@@ -7,10 +7,33 @@ to the buffer zones (inflow and outflow) using ghost nodes.
77The position of the ghost nodes is obtained by mirroring the boundary particles
88into 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
2548end
2649
2750update_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
253432end
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
263449end
0 commit comments