Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/src/systems/total_lagrangian_sph.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,26 @@ density are present, instabilities in the fluid can be induced by the structure.
In these cases, artificial viscosity is effective at stabilizing the fluid close to the
structure, and we recommend using it in combination with penalty force to both
prevent hourglass modes and stabilize the fluid close to the fluid-structure interface.


## [Velocity Averaging](@id velocity_averaging)

In challenging FSI cases with very stiff structures, the two techniques above might not be
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
In challenging FSI cases with very stiff structures, the two techniques above might not be
In FSI cases with very stiff structures, the two techniques above might not be

sufficient to prevent instabilities.
High-frequency noise in the structure velocity can trigger instabilities or spurious
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
High-frequency noise in the structure velocity can trigger instabilities or spurious
High-frequency noise of the structure velocity can trigger instabilities or spurious

pressure waves (due to aliasing) in the fluid.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pressure waves (due to aliasing) in the fluid.
pressure waves in the fluid due to aliasing.

Another stabilization technique is an exponential moving average (EMA) of the structure
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Another stabilization technique is an exponential moving average (EMA) of the structure
Another stabilization technique is to use an exponential moving average (EMA) of the structure

velocity used **only** for the fluid-structure viscous coupling (no-slip boundary condition).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
velocity used **only** for the fluid-structure viscous coupling (no-slip boundary condition).
velocity used **only** for the fluid-structure viscous coupling (i.e. the no-slip boundary condition).


The averaged velocity ``\bar v`` is updated (by the [`UpdateCallback`](@ref)
or in every sub-step of the [`SplitIntegrationCallback`](@ref) if it is used) as
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
or in every sub-step of the [`SplitIntegrationCallback`](@ref) if it is used) as
or at every sub-step of the [`SplitIntegrationCallback`](@ref) if it is used) as

```math
\bar v^{n+1} = (1-\alpha)\,\bar v^n + \alpha\, v^{n+1}, \qquad
\alpha = 1 - \exp(-\Delta t/\tau),
```
where ``\tau`` is the time constant.

```@autodocs
Modules = [TrixiParticles]
Pages = [joinpath("schemes", "structure", "total_lagrangian_sph", "velocity_averaging.jl")]
```
3 changes: 2 additions & 1 deletion examples/fsi/dam_break_plate_2d.jl
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ structure_system = TotalLagrangianSPHSystem(structure,
E, nu, boundary_model=boundary_model_structure,
clamped_particles=1:nparticles(clamped_particles),
acceleration=(0.0, -gravity),
penalty_force=PenaltyForceGanzenmueller(alpha=0.01))
penalty_force=PenaltyForceGanzenmueller(alpha=0.01),
velocity_averaging=nothing)

# ==========================================================================================
# ==== Simulation
Expand Down
2 changes: 1 addition & 1 deletion src/TrixiParticles.jl
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export InfoCallback, SolutionSavingCallback, DensityReinitializationCallback,
export ContinuityDensity, SummationDensity
export PenaltyForceGanzenmueller, TransportVelocityAdami, ParticleShiftingTechnique,
ParticleShiftingTechniqueSun2017, ConsistentShiftingSun2019,
ContinuityEquationTermSun2019, MomentumEquationTermSun2019
ContinuityEquationTermSun2019, MomentumEquationTermSun2019, VelocityAveraging
export SchoenbergCubicSplineKernel, SchoenbergQuarticSplineKernel,
SchoenbergQuinticSplineKernel, GaussianKernel, WendlandC2Kernel, WendlandC4Kernel,
WendlandC6Kernel, SpikyKernel, Poly6Kernel, LaguerreGaussKernel
Expand Down
63 changes: 62 additions & 1 deletion src/callbacks/split_integration.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,22 @@ callback = SplitIntegrationCallback(RDPK3SpFSAL49(), abstol=1e-6, reltol=1e-4)
│ alg: …………………………………………………………………… RDPK3SpFSAL49 │
│ abstol: …………………………………………………………… 1.0e-6 │
│ reltol: …………………………………………………………… 0.0001 │
│ callback: ……………………………………………………… UpdateAveragedVelocityCallback │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
```
"""
function SplitIntegrationCallback(alg; kwargs...)
split_integration_callback = SplitIntegrationCallback(nothing, alg, kwargs)
# Add lightweight callback to (potentially) update the averaged velocity
# during the split integration.
if haskey(kwargs, :callback)
# Note that `CallbackSet`s can be nested
kwargs = (; kwargs...,
callback=CallbackSet(values(kwargs).callback,
UpdateAveragedVelocityCallback()))
else
kwargs = (; kwargs..., callback=UpdateAveragedVelocityCallback())
end
split_integration_callback = SplitIntegrationCallback(nothing, alg, pairs(kwargs))

# The first one is the `condition`, the second the `affect!`
return DiscreteCallback(split_integration_callback, split_integration_callback,
Expand Down Expand Up @@ -356,3 +367,53 @@ function Base.show(io::IO, ::MIME"text/plain",
summary_box(io, "SplitIntegrationCallback", setup)
end
end

# === Non-public callback for updating the averaged velocity ===
# When no split integration is used, this is done from the `UpdateCallback`.
# With split integration, we use this lightweight callback to avoid updating the systems.
function UpdateAveragedVelocityCallback()
# The first one is the `condition`, the second the `affect!`
return DiscreteCallback(update_averaged_velocity_callback!,
update_averaged_velocity_callback!,
initialize=(initialize_averaged_velocity_callback!),
save_positions=(false, false))
end

# `initialize`
function initialize_averaged_velocity_callback!(cb, vu_ode, t, integrator)
v_ode, u_ode = vu_ode.x
semi = integrator.p.semi_split

foreach_system(semi) do system
initialize_averaged_velocity!(system, v_ode, semi, t)
end

return cb
end

# `condition`
function update_averaged_velocity_callback!(u, t, integrator)
return true
end

# `affect!`
function update_averaged_velocity_callback!(integrator)
t_new = integrator.t
semi = integrator.p.semi_split
v_ode, u_ode = integrator.u.x

foreach_system(semi) do system
compute_averaged_velocity!(system, v_ode, semi, t_new)
end

# Tell OrdinaryDiffEq that `integrator.u` has not been modified
u_modified!(integrator, false)

return integrator
end

function Base.show(io::IO,
cb::DiscreteCallback{typeof(update_averaged_velocity_callback!)})
@nospecialize cb # reduce precompilation time
print(io, "UpdateAveragedVelocityCallback")
end
11 changes: 10 additions & 1 deletion src/callbacks/update.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,17 @@ function initial_update!(cb, u, t, integrator)
initial_update!(cb.affect!, u, t, integrator)
end

function initial_update!(cb::UpdateCallback, u, t, integrator)
function initial_update!(cb::UpdateCallback, vu_ode, t, integrator)
v_ode, u_ode = vu_ode.x
semi = integrator.p

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

foreach_system(semi) do system
initialize_averaged_velocity!(system, v_ode, semi, t)
end

return cb(integrator)
end

Expand Down Expand Up @@ -105,6 +110,10 @@ function (update_callback!::UpdateCallback)(integrator)
particle_shifting_from_callback!(u_ode, shifting_technique(system), system,
v_ode, semi, integrator)
end

foreach_system(semi) do system
compute_averaged_velocity!(system, v_ode, semi, t)
end
end

return integrator
Expand Down
2 changes: 1 addition & 1 deletion src/general/semidiscretization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ end
function check_update_callback(semi)
foreach_system(semi) do system
# This check will be optimized away if the system does not require the callback
if requires_update_callback(system) && !semi.update_callback_used[]
if requires_update_callback(system, semi) && !semi.update_callback_used[]
system_name = system |> typeof |> nameof
throw(ArgumentError("`UpdateCallback` is required for `$system_name`"))
end
Expand Down
2 changes: 1 addition & 1 deletion src/preprocessing/particle_packing/system.jl
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ end
return ndims(system)
end

@inline requires_update_callback(system::ParticlePackingSystem) = true
@inline requires_update_callback(system::ParticlePackingSystem, semi) = true

function write2vtk!(vtk, v, u, t, system::ParticlePackingSystem)
vtk["velocity"] = [advection_velocity(v, system, particle)
Expand Down
2 changes: 1 addition & 1 deletion src/schemes/boundary/open_boundary/system.jl
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ end
@inline buffer(system::OpenBoundarySystem) = system.buffer

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

function smoothing_length(system::OpenBoundarySystem, particle)
return system.smoothing_length
Expand Down
6 changes: 4 additions & 2 deletions src/schemes/boundary/wall_boundary/dummy_particles.jl
Original file line number Diff line number Diff line change
Expand Up @@ -681,8 +681,10 @@ end
(; volume, wall_velocity) = cache

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

for dim in eachindex(v_boundary)
# The second term is the precalculated smoothed velocity field of the fluid
Expand Down
4 changes: 3 additions & 1 deletion src/schemes/fluid/shifting_techniques.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ abstract type AbstractShiftingTechnique end

# WARNING: Be careful if defining this function for a specific system type.
# The version for a specific system type will override this generic version.
requires_update_callback(system) = requires_update_callback(shifting_technique(system))
function requires_update_callback(system, semi)
return requires_update_callback(shifting_technique(system))
end
requires_update_callback(::Nothing) = false
requires_update_callback(::AbstractShiftingTechnique) = false

Expand Down
40 changes: 34 additions & 6 deletions src/schemes/structure/total_lagrangian_sph/system.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
acceleration=ntuple(_ -> 0.0, NDIMS),
penalty_force=nothing, viscosity=nothing,
source_terms=nothing, boundary_model=nothing,
self_interaction_nhs=:default)
self_interaction_nhs=:default,
velocity_averaging=nothing)

System for particles of an elastic structure.

Expand Down Expand Up @@ -61,6 +62,9 @@ See [Total Lagrangian SPH](@ref tlsph) for more details on the method.
`transpose_backend` of the [`PrecomputedNeighborhoodSearch`](@ref)
is set to `true` when running on GPUs and `false` on CPUs.
Alternatively, a user-defined neighborhood search can be passed here.
- `velocity_averaging`: Velocity averaging technique to be applied on the velocity field
to obtain an averaged velocity to be used in fluid-structure interaction.
See [the docs](@ref velocity_averaging) for details.

!!! note
If specifying the clamped particles manually (via `n_clamped_particles`),
Expand All @@ -83,7 +87,7 @@ See [Total Lagrangian SPH](@ref tlsph) for more details on the method.
where `beam` and `clamped_particles` are of type [`InitialCondition`](@ref).
"""
struct TotalLagrangianSPHSystem{BM, NDIMS, ELTYPE <: Real, IC, ARRAY1D, ARRAY2D, ARRAY3D,
YM, PR, LL, LM, K, PF, V, ST, M, IM, NHS,
YM, PR, LL, LM, K, PF, V, ST, M, IM, NHS, VA,
C} <: AbstractStructureSystem{NDIMS}
initial_condition :: IC
initial_coordinates :: ARRAY2D # Array{ELTYPE, 2}: [dimension, particle]
Expand All @@ -109,6 +113,7 @@ struct TotalLagrangianSPHSystem{BM, NDIMS, ELTYPE <: Real, IC, ARRAY1D, ARRAY2D,
clamped_particles_motion :: M
clamped_particles_moving :: IM
self_interaction_nhs :: NHS
velocity_averaging :: VA
cache :: C
end

Expand All @@ -121,7 +126,8 @@ function TotalLagrangianSPHSystem(initial_condition, smoothing_kernel, smoothing
ndims(smoothing_kernel)),
penalty_force=nothing, viscosity=nothing,
source_terms=nothing, boundary_model=nothing,
self_interaction_nhs=:default)
self_interaction_nhs=:default,
velocity_averaging=nothing)
NDIMS = ndims(initial_condition)
ELTYPE = eltype(initial_condition)
n_particles = nparticles(initial_condition)
Expand Down Expand Up @@ -186,7 +192,8 @@ function TotalLagrangianSPHSystem(initial_condition, smoothing_kernel, smoothing
initialize_prescribed_motion!(clamped_particles_motion, initial_condition_sorted,
n_clamped_particles)

cache = create_cache_tlsph(clamped_particles_motion, initial_condition_sorted)
cache = (; create_cache_tlsph(clamped_particles_motion, initial_condition_sorted)...,
create_cache_tlsph(velocity_averaging, initial_condition_sorted)...)

return TotalLagrangianSPHSystem(initial_condition_sorted, initial_coordinates,
current_coordinates, mass, correction_matrix,
Expand All @@ -197,7 +204,7 @@ function TotalLagrangianSPHSystem(initial_condition, smoothing_kernel, smoothing
smoothing_length, acceleration_, boundary_model,
penalty_force, viscosity, source_terms,
clamped_particles_motion, ismoving,
self_interaction_nhs, cache)
self_interaction_nhs, velocity_averaging, cache)
end

# Initialize self-interaction neighborhood search if not provided by the user
Expand Down Expand Up @@ -259,7 +266,8 @@ function initialize_self_interaction_nhs(system::TotalLagrangianSPHSystem,
system.viscosity, system.source_terms,
system.clamped_particles_motion,
system.clamped_particles_moving,
self_interaction_nhs, system.cache)
self_interaction_nhs, system.velocity_averaging,
system.cache)
end

extract_periodic_box(::Nothing) = nothing
Expand All @@ -274,6 +282,22 @@ function create_cache_tlsph(::PrescribedMotion, initial_condition)
return (; velocity, acceleration)
end

function create_cache_tlsph(::VelocityAveraging, initial_condition)
averaged_velocity = zero(initial_condition.velocity)
t_last_averaging = Ref(zero(eltype(initial_condition)))

return (; averaged_velocity, t_last_averaging)
end

@inline function requires_update_callback(system::TotalLagrangianSPHSystem, semi)
# Velocity averaging used and `integrate_tlsph == false` means that split integration
# is used and the averaged velocity is updated there.
# Velocity averaging used, `integrate_tlsph == true`, and no fluid system in the semi
# means we are inside the split integration
return !isnothing(system.velocity_averaging) && semi.integrate_tlsph[] &&
any(system -> system isa AbstractFluidSystem, semi.systems)
end

@inline function Base.eltype(::TotalLagrangianSPHSystem{<:Any, <:Any, ELTYPE}) where {ELTYPE}
return ELTYPE
end
Expand Down Expand Up @@ -384,6 +408,10 @@ end
return poisson_ratio[particle]
end

@inline function velocity_averaging(system::TotalLagrangianSPHSystem)
return system.velocity_averaging
end

function initialize!(system::TotalLagrangianSPHSystem, semi)
(; correction_matrix) = system

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Penalty force needs to be included first, so that `dv_penalty_force` is available
# in the closure of `foreach_point_neighbor`.
include("penalty_force.jl")
include("velocity_averaging.jl")
include("system.jl")
include("viscosity.jl")
Loading
Loading