@@ -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,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
253435end
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
263453end
0 commit comments