Skip to content

Commit 12c859c

Browse files
efaulhabersvchbCopilotLasNikas
authored
Implement velocity averaging for TLSPH (#1069)
* Implement velocity averaging for TLSPH * Fix averaged velocity * Fix * Add docs * Reformat * Add test * Fix tests * Add unittests Co-authored-by: Copilot <copilot@github.com> * Fix duplicate velocity averaging with `UpdateCallback` and split integration * Implement suggestions * Reformat * Fix tests * Fix tests * Reformat * Implement suggestions --------- Co-authored-by: Sven Berger <berger.sven@gmail.com> Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Niklas Neher <73897120+LasNikas@users.noreply.github.com>
1 parent 4099559 commit 12c859c

16 files changed

Lines changed: 417 additions & 17 deletions

File tree

docs/src/systems/total_lagrangian_sph.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,26 @@ density are present, instabilities in the fluid can be induced by the structure.
129129
In these cases, artificial viscosity is effective at stabilizing the fluid close to the
130130
structure, and we recommend using it in combination with penalty force to both
131131
prevent hourglass modes and stabilize the fluid close to the fluid-structure interface.
132+
133+
134+
## [Velocity Averaging](@id velocity_averaging)
135+
136+
In FSI cases with very stiff structures, the two techniques above might not be
137+
sufficient to prevent instabilities.
138+
High-frequency noise in the structure velocity can trigger instabilities or spurious
139+
pressure waves in the fluid due to aliasing.
140+
Another stabilization technique is to use an exponential moving average (EMA) of the structure
141+
velocity used **only** for the fluid-structure viscous coupling (i.e., the no-slip boundary condition).
142+
143+
The averaged velocity ``\bar v`` is updated (by the [`UpdateCallback`](@ref)
144+
or at every sub-step of the [`SplitIntegrationCallback`](@ref) if it is used) as
145+
```math
146+
\bar v^{n+1} = (1-\alpha)\,\bar v^n + \alpha\, v^{n+1}, \qquad
147+
\alpha = 1 - \exp(-\Delta t/\tau),
148+
```
149+
where ``\tau`` is the time constant.
150+
151+
```@autodocs
152+
Modules = [TrixiParticles]
153+
Pages = [joinpath("schemes", "structure", "total_lagrangian_sph", "velocity_averaging.jl")]
154+
```

examples/fsi/dam_break_plate_2d.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ structure_system = TotalLagrangianSPHSystem(structure;
138138
boundary_model=boundary_model_structure,
139139
clamped_particles=1:nparticles(clamped_particles),
140140
acceleration=(0.0, -gravity),
141-
penalty_force=PenaltyForceGanzenmueller(alpha=0.01))
141+
penalty_force=PenaltyForceGanzenmueller(alpha=0.01),
142+
velocity_averaging=nothing)
142143

143144
# ==========================================================================================
144145
# ==== Simulation

src/TrixiParticles.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export InfoCallback, SolutionSavingCallback, DensityReinitializationCallback,
7979
export ContinuityDensity, SummationDensity
8080
export PenaltyForceGanzenmueller, TransportVelocityAdami, ParticleShiftingTechnique,
8181
ParticleShiftingTechniqueSun2017, ConsistentShiftingSun2019,
82-
ContinuityEquationTermSun2019, MomentumEquationTermSun2019
82+
ContinuityEquationTermSun2019, MomentumEquationTermSun2019, VelocityAveraging
8383
export SchoenbergCubicSplineKernel, SchoenbergQuarticSplineKernel,
8484
SchoenbergQuinticSplineKernel, GaussianKernel, WendlandC2Kernel, WendlandC4Kernel,
8585
WendlandC6Kernel, SpikyKernel, Poly6Kernel, LaguerreGaussKernel

src/callbacks/split_integration.jl

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,24 @@ callback = SplitIntegrationCallback(CarpenterKennedy2N54(williamson_condition=fa
8282
│ stage_coupling: ……………………………………… true │
8383
│ predict_positions: ……………………………… true │
8484
│ dt: ……………………………………………………………………… 1.0 │
85-
│ callback: ……………………………………………………… StepsizeCallback(is_constant=true, cfl_number=1.6)
85+
│ callback: ……………………………………………………… CallbackSet{Tuple{}, Tuple{Discr…pdateAveragedVelocityCallback))
8686
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
8787
```
8888
"""
8989
function SplitIntegrationCallback(alg; stage_coupling=false, predict_positions=true,
9090
kwargs...)
91+
# Add lightweight callback to (potentially) update the averaged velocity
92+
# during the split integration.
93+
if haskey(kwargs, :callback)
94+
# Note that `CallbackSet`s can be nested
95+
kwargs = (; kwargs...,
96+
callback=CallbackSet(values(kwargs).callback,
97+
UpdateAveragedVelocityCallback()))
98+
else
99+
kwargs = (; kwargs..., callback=UpdateAveragedVelocityCallback())
100+
end
91101
split_integration_callback = SplitIntegrationCallback(alg, stage_coupling,
92-
predict_positions, kwargs)
102+
predict_positions, pairs(kwargs))
93103

94104
# The first one is the `condition`, the second the `affect!`
95105
return DiscreteCallback(split_integration_callback, split_integration_callback,
@@ -522,3 +532,53 @@ function Base.show(io::IO, ::MIME"text/plain",
522532
summary_box(io, "SplitIntegrationCallback", setup)
523533
end
524534
end
535+
536+
# === Non-public callback for updating the averaged velocity ===
537+
# When no split integration is used, this is done from the `UpdateCallback`.
538+
# With split integration, we use this lightweight callback to avoid updating the systems.
539+
function UpdateAveragedVelocityCallback()
540+
# The first one is the `condition`, the second the `affect!`
541+
return DiscreteCallback(update_averaged_velocity_callback!,
542+
update_averaged_velocity_callback!,
543+
initialize=(initialize_averaged_velocity_callback!),
544+
save_positions=(false, false))
545+
end
546+
547+
# `initialize`
548+
function initialize_averaged_velocity_callback!(cb, vu_ode, t, integrator)
549+
v_ode, u_ode = vu_ode.x
550+
semi = integrator.p.semi
551+
552+
foreach_system(semi) do system
553+
initialize_averaged_velocity!(system, v_ode, semi, t)
554+
end
555+
556+
return cb
557+
end
558+
559+
# `condition`
560+
function update_averaged_velocity_callback!(u, t, integrator)
561+
return true
562+
end
563+
564+
# `affect!`
565+
function update_averaged_velocity_callback!(integrator)
566+
t_new = integrator.t
567+
semi = integrator.p.semi
568+
v_ode, u_ode = integrator.u.x
569+
570+
foreach_system(semi) do system
571+
compute_averaged_velocity!(system, v_ode, semi, t_new)
572+
end
573+
574+
# Tell OrdinaryDiffEq that `integrator.u` has not been modified
575+
u_modified!(integrator, false)
576+
577+
return integrator
578+
end
579+
580+
function Base.show(io::IO,
581+
cb::DiscreteCallback{typeof(update_averaged_velocity_callback!)})
582+
@nospecialize cb # reduce precompilation time
583+
print(io, "UpdateAveragedVelocityCallback")
584+
end

src/callbacks/update.jl

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,21 @@ function initial_update!(cb, u, t, integrator)
5252
initial_update!(cb.affect!, u, t, integrator)
5353
end
5454

55-
function initial_update!(cb::UpdateCallback, u, t, integrator)
55+
function initial_update!(cb::UpdateCallback, vu_ode, t, integrator)
56+
v_ode, u_ode = vu_ode.x
5657
semi = integrator.p.semi
5758

5859
# Tell the semidiscretization that the `UpdateCallback` is used
5960
semi.update_callback_used[] = true
6061

62+
# If TLSPH is not integrated, the averaged velocity will be initialized in the
63+
# split integration.
64+
if semi.integrate_tlsph[]
65+
foreach_system(semi) do system
66+
initialize_averaged_velocity!(system, v_ode, semi, t)
67+
end
68+
end
69+
6170
return cb(integrator)
6271
end
6372

@@ -105,6 +114,14 @@ function (update_callback!::UpdateCallback)(integrator)
105114
particle_shifting_from_callback!(u_ode, shifting_technique(system), system,
106115
v_ode, semi, integrator)
107116
end
117+
118+
# If TLSPH is not integrated, the averaged velocity will be updated in the
119+
# split integration.
120+
if semi.integrate_tlsph[]
121+
foreach_system(semi) do system
122+
compute_averaged_velocity!(system, v_ode, semi, t)
123+
end
124+
end
108125
end
109126

110127
return integrator

src/general/semidiscretization.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,7 @@ end
764764
function check_update_callback(semi)
765765
foreach_system(semi) do system
766766
# This check will be optimized away if the system does not require the callback
767-
if requires_update_callback(system) && !semi.update_callback_used[]
767+
if requires_update_callback(system, semi) && !semi.update_callback_used[]
768768
system_name = system |> typeof |> nameof
769769
throw(ArgumentError("`UpdateCallback` is required for `$system_name`"))
770770
end

src/preprocessing/particle_packing/system.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ end
214214
return ndims(system)
215215
end
216216

217-
@inline requires_update_callback(system::ParticlePackingSystem) = true
217+
@inline requires_update_callback(system::ParticlePackingSystem, semi) = true
218218

219219
function write2vtk!(vtk, v, u, t, system::ParticlePackingSystem)
220220
vtk["velocity"] = [advection_velocity(v, system, particle)

src/schemes/boundary/open_boundary/system.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ end
245245
@inline buffer(system::OpenBoundarySystem) = system.buffer
246246

247247
# The `UpdateCallback` is required to update particle positions between time steps
248-
@inline requires_update_callback(system::OpenBoundarySystem) = true
248+
@inline requires_update_callback(system::OpenBoundarySystem, semi) = true
249249

250250
function smoothing_length(system::OpenBoundarySystem, particle)
251251
return system.smoothing_length

src/schemes/boundary/wall_boundary/dummy_particles.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -731,8 +731,10 @@ end
731731
(; volume, wall_velocity) = cache
732732

733733
# Prescribed velocity of the boundary particle.
734-
# This velocity is zero when not using moving boundaries.
735-
v_boundary = current_velocity(v, system, particle)
734+
# This velocity is zero when not using moving boundaries or TLSPH.
735+
# If not using TLSPH with velocity averaging, this function simply
736+
# forwards to `current_velocity`.
737+
v_boundary = velocity_for_viscosity(v, system, particle)
736738

737739
for dim in eachindex(v_boundary)
738740
# Compute the no-slip wall velocity using the formula v_w = 2 * v_a - ṽ_fluid,

src/schemes/fluid/shifting_techniques.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ abstract type AbstractShiftingTechnique end
55

66
# WARNING: Be careful if defining this function for a specific system type.
77
# The version for a specific system type will override this generic version.
8-
requires_update_callback(system) = requires_update_callback(shifting_technique(system))
8+
function requires_update_callback(system, semi)
9+
return requires_update_callback(shifting_technique(system))
10+
end
911
requires_update_callback(::Nothing) = false
1012
requires_update_callback(::AbstractShiftingTechnique) = false
1113

0 commit comments

Comments
 (0)