Repository: trixi-framework/TrixiParticles.jl
Pull request #600: Transport Velocity Formulation for WCSPH
Author: login: LasNikas
Labels: name: bug
description: Something isn't working
Created: 2024-08-08T11:20:29Z | Merged: 2025-04-09T17:17:58Z



Diff:
diff --git a/examples/fluid/lid_driven_cavity_2d.jl b/examples/fluid/lid_driven_cavity_2d.jl
index 77b409588e..86b2ad315b 100644
--- a/examples/fluid/lid_driven_cavity_2d.jl
+++ b/examples/fluid/lid_driven_cavity_2d.jl
@@ -43,14 +43,28 @@ lid = RectangularShape(particle_spacing, (lid_length, 3),
 
 # ==========================================================================================
 # ==== Fluid
-
+wcsph = true
 smoothing_length = 1.0 * particle_spacing
 smoothing_kernel = SchoenbergQuinticSplineKernel{2}()
 
-fluid_system = EntropicallyDampedSPHSystem(cavity.fluid, smoothing_kernel, smoothing_length,
-                                           density_calculator=ContinuityDensity(),
-                                           sound_speed, viscosity=viscosity,
-                                           transport_velocity=TransportVelocityAdami(pressure))
+if wcsph
+    density_calculator = ContinuityDensity()
+    state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density,
+                                       exponent=1)
+    fluid_system = WeaklyCompressibleSPHSystem(cavity.fluid, density_calculator,
+                                               state_equation, smoothing_kernel,
+                                               pressure_acceleration=TrixiParticles.inter_particle_averaged_pressure,
+                                               smoothing_length, viscosity=viscosity,
+                                               transport_velocity=TransportVelocityAdami(pressure))
+else
+    state_equation = nothing
+    density_calculator = ContinuityDensity()
+    fluid_system = EntropicallyDampedSPHSystem(cavity.fluid, smoothing_kernel,
+                                               smoothing_length,
+                                               density_calculator=density_calculator,
+                                               sound_speed, viscosity=viscosity,
+                                               transport_velocity=TransportVelocityAdami(pressure))
+end
 
 # ==========================================================================================
 # ==== Boundary
@@ -65,11 +79,13 @@ boundary_model_cavity = BoundaryModelDummyParticles(cavity.boundary.density,
                                                     cavity.boundary.mass,
                                                     AdamiPressureExtrapolation(),
                                                     viscosity=viscosity,
+                                                    state_equation=state_equation,
                                                     smoothing_kernel, smoothing_length)
 
 boundary_model_lid = BoundaryModelDummyParticles(lid.density, lid.mass,
                                                  AdamiPressureExtrapolation(),
                                                  viscosity=viscosity,
+                                                 state_equation=state_equation,
                                                  smoothing_kernel, smoothing_length)
 
 boundary_system_cavity = BoundarySPHSystem(cavity.boundary, boundary_model_cavity)
diff --git a/examples/fluid/taylor_green_vortex_2d.jl b/examples/fluid/taylor_green_vortex_2d.jl
index fc4ad9e63d..c1f0f72fd4 100644
--- a/examples/fluid/taylor_green_vortex_2d.jl
+++ b/examples/fluid/taylor_green_vortex_2d.jl
@@ -51,6 +51,8 @@ n_particles_xy = round(Int, box_length / particle_spacing)
 
 # ==========================================================================================
 # ==== Fluid
+wcsph = false
+
 nu = U * box_length / reynolds_number
 
 background_pressure = sound_speed^2 * fluid_density
@@ -65,13 +67,28 @@ fluid = RectangularShape(particle_spacing, (n_particles_xy, n_particles_xy), (0.
                          coordinates_perturbation=perturb_coordinates ? 0.2 : nothing, # To avoid stagnant streamlines when not using TVF.
                          density=fluid_density, pressure=initial_pressure_function,
                          velocity=initial_velocity_function)
-
-density_calculator = SummationDensity()
-fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length,
-                                           sound_speed,
-                                           density_calculator=density_calculator,
-                                           transport_velocity=TransportVelocityAdami(background_pressure),
-                                           viscosity=ViscosityAdami(; nu))
+if wcsph
+    # Using `SummationDensity()` with `perturb_coordinates = true` introduces noise in the simulation
+    # due to bad density estimates resulting from perturbed particle positions.
+    # Adami et al. 2013 use the final particle distribution from an relaxation step for the initial condition
+    # and impose the analytical velocity profile.
+    density_calculator = ContinuityDensity()
+    state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density,
+                                       exponent=1)
+    fluid_system = WeaklyCompressibleSPHSystem(fluid, density_calculator,
+                                               state_equation, smoothing_kernel,
+                                               pressure_acceleration=TrixiParticles.inter_particle_averaged_pressure,
+                                               smoothing_length,
+                                               viscosity=ViscosityAdami(; nu),
+                                               transport_velocity=TransportVelocityAdami(background_pressure))
+else
+    density_calculator = SummationDensity()
+    fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length,
+                                               sound_speed,
+                                               density_calculator=density_calculator,
+                                               transport_velocity=TransportVelocityAdami(background_pressure),
+                                               viscosity=ViscosityAdami(; nu))
+end
 
 # ==========================================================================================
 # ==== Simulation
diff --git a/src/schemes/fluid/entropically_damped_sph/rhs.jl b/src/schemes/fluid/entropically_damped_sph/rhs.jl
index 912a627300..f0cf970462 100644
--- a/src/schemes/fluid/entropically_damped_sph/rhs.jl
+++ b/src/schemes/fluid/entropically_damped_sph/rhs.jl
@@ -49,7 +49,7 @@ function interact!(dv, v_particle_system, u_particle_system,
                                      particle, neighbor, pos_diff, distance,
                                      sound_speed, m_a, m_b, rho_a, rho_b, grad_kernel)
 
-        # Add convection term when using `TransportVelocityAdami`
+        # Add convection term (only when using `TransportVelocityAdami`)
         dv_convection = momentum_convection(particle_system, neighbor_system,
                                             v_particle_system, v_neighbor_system,
                                             rho_a, rho_b, m_a, m_b,
@@ -75,8 +75,9 @@ function interact!(dv, v_particle_system, u_particle_system,
                             particle, neighbor, pos_diff, distance,
                             sound_speed, m_a, m_b, p_a, p_b, rho_a, rho_b)
 
-        transport_velocity!(dv, particle_system, rho_a, rho_b, m_a, m_b,
-                            grad_kernel, particle)
+        # Apply the transport velocity (only when using a transport velocity)
+        transport_velocity!(dv, particle_system, neighbor_system, particle, neighbor,
+                            m_a, m_b, grad_kernel)
 
         continuity_equation!(dv, density_calculator, v_diff, particle, m_b, rho_a, rho_b,
                              particle_system, grad_kernel)
diff --git a/src/schemes/fluid/entropically_damped_sph/system.jl b/src/schemes/fluid/entropically_damped_sph/system.jl
index 96eeaf5ff6..51c50ceb1b 100644
--- a/src/schemes/fluid/entropically_damped_sph/system.jl
+++ b/src/schemes/fluid/entropically_damped_sph/system.jl
@@ -129,9 +129,8 @@ function EntropicallyDampedSPHSystem(initial_condition, smoothing_kernel,
 
     nu_edac = (alpha * smoothing_length * sound_speed) / 8
 
-    cache = create_cache_density(initial_condition, density_calculator)
-    cache = (;
-             create_cache_edac(initial_condition, transport_velocity)...,
+    cache = (; create_cache_density(initial_condition, density_calculator)...,
+             create_cache_tvf(Val(:edac), initial_condition, transport_velocity)...,
              create_cache_surface_normal(surface_normal_method, ELTYPE, NDIMS,
                                          n_particles)...,
              create_cache_surface_tension(surface_tension, ELTYPE, NDIMS,
@@ -140,8 +139,7 @@ function EntropicallyDampedSPHSystem(initial_condition, smoothing_kernel,
                                      smoothing_length)...,
              create_cache_correction(correction, initial_condition.density, NDIMS,
                                      n_particles)...,
-             color=Int(color_value),
-             cache...)
+             color=Int(color_value))
 
     # If the `reference_density_spacing` is set calculate the `ideal_neighbor_count`
     if reference_particle_spacing > 0
@@ -209,9 +207,9 @@ function Base.show(io::IO, ::MIME"text/plain", system::EntropicallyDampedSPHSyst
     end
 end
 
-create_cache_edac(initial_condition, ::Nothing) = (;)
+create_cache_tvf(::Val{:edac}, initial_condition, ::Nothing) = (;)
 
-function create_cache_edac(initial_condition, ::TransportVelocityAdami)
+function create_cache_tvf(::Val{:edac}, initial_condition, ::TransportVelocityAdami)
     pressure_average = copy(initial_condition.pressure)
     neighbor_counter = Vector{Int}(undef, nparticles(initial_condition))
     update_callback_used = Ref(false)
diff --git a/src/schemes/fluid/transport_velocity.jl b/src/schemes/fluid/transport_velocity.jl
index b79c48d181..2c40249867 100644
--- a/src/schemes/fluid/transport_velocity.jl
+++ b/src/schemes/fluid/transport_velocity.jl
@@ -101,28 +101,39 @@ end
     return volume_term * ((A_a + A_b) / 2) * grad_kernel
 end
 
-@inline transport_velocity!(dv, system, rho_a, rho_b, m_a, m_b, grad_kernel, particle) = dv
+@inline transport_velocity!(dv, system, neighbor_system, particle, neighbor, m_a, m_b,
+grad_kernel) = dv
 
-@inline function transport_velocity!(dv, system::FluidSystem,
-                                     rho_a, rho_b, m_a, m_b, grad_kernel, particle)
-    transport_velocity!(dv, system, system.transport_velocity, rho_a, rho_b, m_a, m_b,
-                        grad_kernel, particle)
+@inline function transport_velocity!(dv, system::FluidSystem, neighbor_system,
+                                     particle, neighbor, m_a, m_b, grad_kernel)
+    transport_velocity!(dv, system, neighbor_system, system.transport_velocity,
+                        particle, neighbor, m_a, m_b, grad_kernel)
 end
 
-@inline function transport_velocity!(dv, system, ::Nothing,
-                                     rho_a, rho_b, m_a, m_b, grad_kernel, particle)
+@inline function transport_velocity!(dv, system, neighbor_system, ::Nothing,
+                                     particle, neighbor, m_a, m_b, grad_kernel)
     return dv
 end
 
-@inline function transport_velocity!(dv, system, ::TransportVelocityAdami,
-                                     rho_a, rho_b, m_a, m_b, grad_kernel, particle)
+@inline function transport_velocity!(dv, system, neighbor_system, ::TransportVelocityAdami,
+                                     particle, neighbor, m_a, m_b, grad_kernel)
     (; transport_velocity) = system
     (; background_pressure) = transport_velocity
 
     NDIMS = ndims(system)
 
-    volume_a = m_a / rho_a
-    volume_b = m_b / rho_b
+    # The TVF is based on the assumption that the pressure gradient is only accurately
+    # computed when the particle distribution is isotropic.
+    # That means, the force contribution vanishes only if the particle distribution is
+    # isotropic AND the field being differentiated by the kernel gradient is spatially constant.
+    # So we must guarantee a constant field and therefore the reference density is used
+    # instead of the locally computed one.
+    # TODO:
+    # volume_a = particle_spacing(system, particle)^ndims(system)
+    # volume_b = particle_spacing(neighbor_system, neighbor)^ndims(neighbor_system)
+    volume_a = m_a / system.initial_condition.density[particle]
+    volume_b = m_b / neighbor_system.initial_condition.density[neighbor]
+
     volume_term = (volume_a^2 + volume_b^2) / m_a
 
     for dim in 1:NDIMS
diff --git a/src/schemes/fluid/weakly_compressible_sph/rhs.jl b/src/schemes/fluid/weakly_compressible_sph/rhs.jl
index fb35bcf02a..931c0f3acf 100644
--- a/src/schemes/fluid/weakly_compressible_sph/rhs.jl
+++ b/src/schemes/fluid/weakly_compressible_sph/rhs.jl
@@ -68,6 +68,12 @@ function interact!(dv, v_particle_system, u_particle_system,
                                                sound_speed, m_a, m_b, rho_a, rho_b,
                                                grad_kernel)
 
+        # Add convection term (only when using `TransportVelocityAdami`)
+        dv_convection = momentum_convection(particle_system, neighbor_system,
+                                            v_particle_system, v_neighbor_system,
+                                            rho_a, rho_b, m_a, m_b,
+                                            particle, neighbor, grad_kernel)
+
         dv_surface_tension = surface_tension_correction *
                              surface_tension_force(surface_tension_a, surface_tension_b,
                                                    particle_system, neighbor_system,
@@ -79,11 +85,16 @@ function interact!(dv, v_particle_system, u_particle_system,
 
         for i in 1:ndims(particle_system)
             @inbounds dv[i, particle] += dv_pressure[i] + dv_viscosity_[i] +
-                                         dv_surface_tension[i] + dv_adhesion[i]
+                                         dv_convection[i] + dv_surface_tension[i] +
+                                         dv_adhesion[i]
             # Debug example
             # debug_array[i, particle] += dv_pressure[i]
         end
 
+        # Apply the transport velocity (only when using a transport velocity)
+        transport_velocity!(dv, particle_system, neighbor_system, particle, neighbor,
+                            m_a, m_b, grad_kernel)
+
         # TODO If variable smoothing_length is used, this should use the neighbor smoothing length
         # Propagate `@inbounds` to the continuity equation, which accesses particle data
         @inbounds continuity_equation!(dv, density_calculator, v_particle_system,
diff --git a/src/schemes/fluid/weakly_compressible_sph/system.jl b/src/schemes/fluid/weakly_compressible_sph/system.jl
index a4b9113b15..bc69d432bb 100644
--- a/src/schemes/fluid/weakly_compressible_sph/system.jl
+++ b/src/schemes/fluid/weakly_compressible_sph/system.jl
@@ -3,6 +3,7 @@
                                 density_calculator, state_equation,
                                 smoothing_kernel, smoothing_length;
                                 viscosity=nothing, density_diffusion=nothing,
+                                transport_velocity=nothing,
                                 acceleration=ntuple(_ -> 0.0, NDIMS),
                                 buffer_size=nothing,
                                 correction=nothing, source_terms=nothing,
@@ -27,6 +28,7 @@ See [Weakly Compressible SPH](@ref wcsph) for more details on the method.
 # Keyword Arguments
 - `viscosity`:                  Viscosity model for this system (default: no viscosity).
                                 See [`ArtificialViscosityMonaghan`](@ref) or [`ViscosityAdami`](@ref).
+- `transport_velocity`:         [Transport Velocity Formulation (TVF)](@ref transport_velocity_formulation). Default is no TVF.
 - `density_diffusion`:          Density diffusion terms for this system. See [`DensityDiffusion`](@ref).
 - `acceleration`:               Acceleration vector for the system. (default: zero vector)
 - `buffer_size`:                Number of buffer particles.
@@ -50,9 +52,8 @@ See [Weakly Compressible SPH](@ref wcsph) for more details on the method.
 - `color_value`:                The value used to initialize the color of particles in the system.
 
 """
-struct WeaklyCompressibleSPHSystem{NDIMS, ELTYPE <: Real, IC, MA, P, DC, SE, K,
-                                   V, DD, COR, PF, ST, B, SRFT, SRFN, PR, C} <:
-       FluidSystem{NDIMS}
+struct WeaklyCompressibleSPHSystem{NDIMS, ELTYPE <: Real, IC, MA, P, DC, SE, K, V, DD, COR,
+                                   PF, TV, ST, B, SRFT, SRFN, PR, C} <: FluidSystem{NDIMS}
     initial_condition                 :: IC
     mass                              :: MA     # Array{ELTYPE, 1}
     pressure                          :: P      # Array{ELTYPE, 1}
@@ -64,7 +65,7 @@ struct WeaklyCompressibleSPHSystem{NDIMS, ELTYPE <: Real, IC, MA, P, DC, SE, K,
     density_diffusion                 :: DD
     correction                        :: COR
     pressure_acceleration_formulation :: PF
-    transport_velocity                :: Nothing # TODO
+    transport_velocity                :: TV
     source_terms                      :: ST
     surface_tension                   :: SRFT
     surface_normal_method             :: SRFN
@@ -79,6 +80,7 @@ function WeaklyCompressibleSPHSystem(initial_condition,
                                      density_calculator, state_equation,
                                      smoothing_kernel, smoothing_length;
                                      pressure_acceleration=nothing,
+                                     transport_velocity=nothing,
                                      buffer_size=nothing,
                                      viscosity=nothing, density_diffusion=nothing,
                                      acceleration=ntuple(_ -> zero(eltype(initial_condition)),
@@ -128,8 +130,7 @@ function WeaklyCompressibleSPHSystem(initial_condition,
                                                                      NDIMS, ELTYPE,
                                                                      correction)
 
-    cache = create_cache_density(initial_condition, density_calculator)
-    cache = (;
+    cache = (; create_cache_density(initial_condition, density_calculator)...,
              create_cache_correction(correction, initial_condition.density, NDIMS,
                                      n_particles)...,
              create_cache_surface_normal(surface_normal_method, ELTYPE, NDIMS,
@@ -138,7 +139,8 @@ function WeaklyCompressibleSPHSystem(initial_condition,
                                           n_particles)...,
              create_cache_refinement(initial_condition, particle_refinement,
                                      smoothing_length)...,
-             color=Int(color_value), cache...)
+             create_cache_tvf(Val(:wcsph), initial_condition, transport_velocity)...,
+             color=Int(color_value))
 
     # If the `reference_density_spacing` is set calculate the `ideal_neighbor_count`
     if reference_particle_spacing > 0
@@ -150,12 +152,11 @@ function WeaklyCompressibleSPHSystem(initial_condition,
 
     return WeaklyCompressibleSPHSystem(initial_condition, mass, pressure,
                                        density_calculator, state_equation,
-                                       smoothing_kernel,
-                                       acceleration_, viscosity,
-                                       density_diffusion, correction,
-                                       pressure_acceleration, nothing,
-                                       source_terms, surface_tension, surface_normal_method,
-                                       buffer, particle_refinement, cache)
+                                       smoothing_kernel, acceleration_, viscosity,
+                                       density_diffusion, correction, pressure_acceleration,
+                                       transport_velocity, source_terms, surface_tension,
+                                       surface_normal_method, buffer, particle_refinement,
+                                       cache)
 end
 
 function Base.show(io::IO, system::WeaklyCompressibleSPHSystem)
@@ -198,6 +199,8 @@ function Base.show(io::IO, ::MIME"text/plain", system::WeaklyCompressibleSPHSyst
         summary_line(io, "state equation", system.state_equation |> typeof |> nameof)
         summary_line(io, "smoothing kernel", system.smoothing_kernel |> typeof |> nameof)
         summary_line(io, "viscosity", system.viscosity)
+        summary_line(io, "tansport velocity formulation",
+                     system.transport_velocity |> typeof |> nameof)
         summary_line(io, "density diffusion", system.density_diffusion)
         summary_line(io, "surface tension", system.surface_tension)
         summary_line(io, "surface normal method", system.surface_normal_method)
@@ -210,6 +213,14 @@ function Base.show(io::IO, ::MIME"text/plain", system::WeaklyCompressibleSPHSyst
     end
 end
 
+create_cache_tvf(::Val{:wcsph}, initial_condition, ::Nothing) = (;)
+
+function create_cache_tvf(::Val{:wcsph}, initial_condition, ::TransportVelocityAdami)
+    update_callback_used = Ref(false)
+
+    return (; update_callback_used)
+end
+
 @inline function Base.eltype(::WeaklyCompressibleSPHSystem{<:Any, ELTYPE}) where {ELTYPE}
     return ELTYPE
 end
@@ -219,11 +230,11 @@ end
 end
 
 @inline function v_nvariables(system::WeaklyCompressibleSPHSystem, density_calculator)
-    return ndims(system)
+    return ndims(system) * factor_tvf(system)
 end
 
 @inline function v_nvariables(system::WeaklyCompressibleSPHSystem, ::ContinuityDensity)
-    return ndims(system) + 1
+    return ndims(system) * factor_tvf(system) + 1
 end
 
 system_correction(system::WeaklyCompressibleSPHSystem) = system.correction
diff --git a/test/examples/examples_fluid.jl b/test/examples/examples_fluid.jl
index f79d8aadc3..9e9159ac46 100644
--- a/test/examples/examples_fluid.jl
+++ b/test/examples/examples_fluid.jl
@@ -350,7 +350,7 @@
         @test count_rhs_allocations(sol, semi) == 0
     end
 
-    @trixi_testset "fluid/lid_driven_cavity_2d.jl" begin
+    @trixi_testset "fluid/lid_driven_cavity_2d.jl (EDAC)" begin
         @test_nowarn_mod trixi_include(@__MODULE__,
                                        joinpath(examples_dir(), "fluid",
                                                 "lid_driven_cavity_2d.jl"),
@@ -359,7 +359,16 @@
         @test count_rhs_allocations(sol, semi) == 0
     end
 
-    @trixi_testset "fluid/taylor_green_vortex_2d.jl" begin
+    @trixi_testset "fluid/lid_driven_cavity_2d.jl (WCSPH)" begin
+        @test_nowarn_mod trixi_include(@__MODULE__,
+                                       joinpath(examples_dir(), "fluid",
+                                                "lid_driven_cavity_2d.jl"),
+                                       tspan=(0.0, 0.1), wcsph=true)
+        @test sol.retcode == ReturnCode.Success
+        @test count_rhs_allocations(sol, semi) == 0
+    end
+
+    @trixi_testset "fluid/taylor_green_vortex_2d.jl (EDAC)" begin
         @test_nowarn_mod trixi_include(@__MODULE__,
                                        joinpath(examples_dir(), "fluid",
                                                 "taylor_green_vortex_2d.jl"),
@@ -368,6 +377,15 @@
         @test count_rhs_allocations(sol, semi) == 0
     end
 
+    @trixi_testset "fluid/taylor_green_vortex_2d.jl (WCSPH)" begin
+        @test_nowarn_mod trixi_include(@__MODULE__,
+                                       joinpath(examples_dir(), "fluid",
+                                                "taylor_green_vortex_2d.jl"),
+                                       tspan=(0.0, 0.1), wcsph=true)
+        @test sol.retcode == ReturnCode.Success
+        @test count_rhs_allocations(sol, semi) == 0
+    end
+
     @trixi_testset "fluid/sphere_surface_tension_2d.jl" begin
         @test_nowarn_mod trixi_include(@__MODULE__,
                                        joinpath(examples_dir(), "fluid",
diff --git a/test/systems/wcsph_system.jl b/test/systems/wcsph_system.jl
index 689cb21d2e..7bdc5562f7 100644
--- a/test/systems/wcsph_system.jl
+++ b/test/systems/wcsph_system.jl
@@ -205,6 +205,7 @@
         │ state equation: ……………………………………… Val                                                              │
         │ smoothing kernel: ………………………………… Val                                                              │
         │ viscosity: …………………………………………………… nothing                                                          │
+        │ tansport velocity formulation:  Nothing                                                          │
         │ density diffusion: ……………………………… Val{:density_diffusion}()                                        │
         │ surface tension: …………………………………… nothing                                                          │
         │ surface normal method: …………………… nothing                                                          │
diff --git a/validation/lid_driven_cavity_2d/validation_lid_driven_cavity_2d.jl b/validation/lid_driven_cavity_2d/validation_lid_driven_cavity_2d.jl
index 4d7cb6b236..4afbe55205 100644
--- a/validation/lid_driven_cavity_2d/validation_lid_driven_cavity_2d.jl
+++ b/validation/lid_driven_cavity_2d/validation_lid_driven_cavity_2d.jl
@@ -55,12 +55,18 @@ function interpolated_velocity(v, u, t, system::TrixiParticles.FluidSystem)
     return nothing
 end
 
-for particle_spacing in particle_spacings, reynolds_number in reynolds_numbers
+for particle_spacing in particle_spacings, reynolds_number in reynolds_numbers,
+    density_calculator in [SummationDensity(), ContinuityDensity()], wcsph in [false, true]
+
     n_particles_xy = round(Int, 1.0 / particle_spacing)
 
     Re = Int(reynolds_number)
 
-    global output_directory = joinpath("out_ldc",
+    name_density_calculator = density_calculator isa SummationDensity ?
+                              "summation_density" : "continuity_density"
+
+    global output_directory = joinpath("out_ldc", wcsph ? "wcsph" : "edac",
+                                       name_density_calculator,
                                        "validation_run_lid_driven_cavity_2d_nparticles_$(n_particles_xy)x$(n_particles_xy)_Re_$Re")
 
     saving_callback = SolutionSavingCallback(dt=0.1, output_directory=output_directory)
@@ -73,6 +79,7 @@ for particle_spacing in particle_spacings, reynolds_number in reynolds_numbers
     # Import variables into scope
     trixi_include(@__MODULE__,
                   joinpath(examples_dir(), "fluid", "lid_driven_cavity_2d.jl"),
+                  wcsph=wcsph, density_calculator=density_calculator,
                   saving_callback=saving_callback, tspan=tspan, pp_callback=pp_callback,
                   particle_spacing=particle_spacing, reynolds_number=reynolds_number)
 
diff --git a/validation/taylor_green_vortex_2d/validation_taylor_green_vortex_2d.jl b/validation/taylor_green_vortex_2d/validation_taylor_green_vortex_2d.jl
index 99b83ca03e..719cfa946a 100644
--- a/validation/taylor_green_vortex_2d/validation_taylor_green_vortex_2d.jl
+++ b/validation/taylor_green_vortex_2d/validation_taylor_green_vortex_2d.jl
@@ -9,7 +9,7 @@ particle_spacings = [0.02, 0.01, 0.005]
 tspan = (0.0, 5.0)
 reynolds_number = 100.0
 
-density_calculators = [SummationDensity(), ContinuityDensity()]
+density_calculators = [ContinuityDensity(), SummationDensity()]
 perturb_coordinates = [false, true]
 
 function compute_l1v_error(v, u, t, system)
@@ -64,15 +64,16 @@ function diff_p_loc_p_avg(v, u, t, system)
 end
 
 for density_calculator in density_calculators, perturbation in perturb_coordinates,
-    particle_spacing in particle_spacings
+    particle_spacing in particle_spacings, wcsph in [false, true]
 
     n_particles_xy = round(Int, 1.0 / particle_spacing)
 
     name_density_calculator = density_calculator isa SummationDensity ?
                               "summation_density" : "continuity_density"
-    name_perturbation = perturbation ? "perturbed" : ""
+    name_perturbation = perturbation ? "_perturbed" : ""
 
-    output_directory = joinpath("out_tgv", "$(name_density_calculator)_$name_perturbation",
+    output_directory = joinpath("out_tgv", name_density_calculator * name_perturbation,
+                                wcsph ? "wcsph" : "edac",
                                 "validation_run_taylor_green_vortex_2d_nparticles_$(n_particles_xy)x$(n_particles_xy)")
     saving_callback = SolutionSavingCallback(dt=0.02,
                                              output_directory=output_directory,
@@ -89,7 +90,7 @@ for density_calculator in density_calculators, perturbation in perturb_coordinat
     trixi_include(@__MODULE__,
                   joinpath(examples_dir(), "fluid", "taylor_green_vortex_2d.jl"),
                   density_calculator=density_calculator,
-                  perturb_coordinates=perturbation,
+                  perturb_coordinates=perturbation, wcsph=wcsph,
                   particle_spacing=particle_spacing, reynolds_number=reynolds_number,
                   tspan=tspan, saving_callback=saving_callback, pp_callback=pp_callback)
 end


Reviews:
user: login: svchb
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-04-09T09:25:37Z
user: login: efaulhaber
body: Please also add a test.
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-09T09:58:37Z
user: login: LasNikas
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-09T10:17:20Z
user: login: svchb
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-09T10:30:41Z
user: login: efaulhaber
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-09T12:14:21Z
user: login: LasNikas
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-09T14:18:26Z
user: login: efaulhaber
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-04-09T14:28:37Z
user: login: svchb
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-09T16:51:48Z

Review comments:
diff_hunk: @@ -72,8 +74,19 @@ fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_le
                                            density_calculator=density_calculator,
                                            transport_velocity=TransportVelocityAdami(background_pressure),
                                            viscosity=ViscosityAdami(; nu))
+if wcsph
+    state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density,
+                                       exponent=1)
+    density_calculator = ContinuityDensity()
+    fluid_system = WeaklyCompressibleSPHSystem(fluid, density_calculator,
+                                               state_equation, smoothing_kernel,
+                                               pressure_acceleration=TrixiParticles.inter_particle_averaged_pressure,
+                                               smoothing_length,
+                                               viscosity=ViscosityAdami(; nu),
+                                               transport_velocity=TransportVelocityAdami(background_pressure))
+end
 
-# ==========================================================================================
+# # ==========================================================================================
path: examples/fluid/taylor_green_vortex_2d.jl
user: login: efaulhaber
body: ?
created_at: 2025-04-09T09:46:57Z
author_association: MEMBER
diff_hunk: @@ -72,8 +74,19 @@ fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_le
                                            density_calculator=density_calculator,
                                            transport_velocity=TransportVelocityAdami(background_pressure),
                                            viscosity=ViscosityAdami(; nu))
+if wcsph
path: examples/fluid/taylor_green_vortex_2d.jl
user: login: efaulhaber
body: else?
created_at: 2025-04-09T09:47:02Z
author_association: MEMBER
diff_hunk: @@ -209,9 +207,9 @@ function Base.show(io::IO, ::MIME"text/plain", system::EntropicallyDampedSPHSyst
     end
 end
 
-create_cache_edac(initial_condition, ::Nothing) = (;)
+create_cache_tvf(::Val{:edac}, initial_condition, ::Nothing) = (;)
 
-function create_cache_edac(initial_condition, ::TransportVelocityAdami)
+function create_cache_tvf(::Val{:edac}, initial_condition, ::TransportVelocityAdami)
path: src/schemes/fluid/entropically_damped_sph/system.jl
user: login: efaulhaber
body: How is this better than the previous version?
created_at: 2025-04-09T09:50:46Z
author_association: MEMBER
diff_hunk: @@ -59,6 +59,11 @@ function interact!(dv, v_particle_system, u_particle_system,
                                             particle, neighbor,
                                             m_a, m_b, p_a, p_b, rho_a, rho_b, pos_diff,
                                             distance, grad_kernel, correction)
+        # Add convection term when using `TransportVelocityAdami`
path: src/schemes/fluid/weakly_compressible_sph/rhs.jl
user: login: efaulhaber
body: ```suggestion
        
        # Add convection term (only when using `TransportVelocityAdami`)
```
Also added an empty line.
created_at: 2025-04-09T09:53:44Z
author_association: MEMBER
diff_hunk: @@ -78,12 +83,16 @@ function interact!(dv, v_particle_system, u_particle_system,
                                      particle, neighbor, pos_diff, distance)
 
         for i in 1:ndims(particle_system)
-            @inbounds dv[i, particle] += dv_pressure[i] + dv_viscosity_[i] +
-                                         dv_surface_tension[i] + dv_adhesion[i]
+            @inbounds dv[i, particle] += dv_pressure[i] + dv_convection[i] +
+                                         dv_viscosity_[i] + dv_surface_tension[i] +
+                                         dv_adhesion[i]
             # Debug example
             # debug_array[i, particle] += dv_pressure[i]
         end
 
+        transport_velocity!(dv, particle_system, rho_a, rho_b, m_a, m_b,
path: src/schemes/fluid/weakly_compressible_sph/rhs.jl
user: login: efaulhaber
body: ```suggestion
        # Apply the transport velocity (only when using a transport velocity)
        transport_velocity!(dv, particle_system, rho_a, rho_b, m_a, m_b,
```
created_at: 2025-04-09T09:56:46Z
author_association: MEMBER
diff_hunk: @@ -78,12 +83,16 @@ function interact!(dv, v_particle_system, u_particle_system,
                                      particle, neighbor, pos_diff, distance)
 
         for i in 1:ndims(particle_system)
-            @inbounds dv[i, particle] += dv_pressure[i] + dv_viscosity_[i] +
-                                         dv_surface_tension[i] + dv_adhesion[i]
+            @inbounds dv[i, particle] += dv_pressure[i] + dv_convection[i] +
+                                         dv_viscosity_[i] + dv_surface_tension[i] +
+                                         dv_adhesion[i]
path: src/schemes/fluid/weakly_compressible_sph/rhs.jl
user: login: efaulhaber
body: ```suggestion
            @inbounds dv[i, particle] += dv_pressure[i] + dv_viscosity_[i] +
                                         dv_convection[i]  + dv_surface_tension[i] +
                                         dv_adhesion[i]
```
created_at: 2025-04-09T09:58:21Z
author_association: MEMBER
diff_hunk: @@ -209,9 +207,9 @@ function Base.show(io::IO, ::MIME"text/plain", system::EntropicallyDampedSPHSyst
     end
 end
 
-create_cache_edac(initial_condition, ::Nothing) = (;)
+create_cache_tvf(::Val{:edac}, initial_condition, ::Nothing) = (;)
 
-function create_cache_edac(initial_condition, ::TransportVelocityAdami)
+function create_cache_tvf(::Val{:edac}, initial_condition, ::TransportVelocityAdami)
path: src/schemes/fluid/entropically_damped_sph/system.jl
user: login: LasNikas
body: this is only intended for the TVF, why should it be called `created_cache_edac` and `create_cache_wcsph`?
created_at: 2025-04-09T10:17:20Z
author_association: COLLABORATOR
diff_hunk: @@ -209,9 +207,9 @@ function Base.show(io::IO, ::MIME"text/plain", system::EntropicallyDampedSPHSyst
     end
 end
 
-create_cache_edac(initial_condition, ::Nothing) = (;)
+create_cache_tvf(::Val{:edac}, initial_condition, ::Nothing) = (;)
 
-function create_cache_edac(initial_condition, ::TransportVelocityAdami)
+function create_cache_tvf(::Val{:edac}, initial_condition, ::TransportVelocityAdami)
path: src/schemes/fluid/entropically_damped_sph/system.jl
user: login: svchb
body: yes this follows the current convention that we have naming the cache functions after the relevant model.
created_at: 2025-04-09T10:30:41Z
author_association: COLLABORATOR
diff_hunk: @@ -67,26 +67,30 @@ fluid = RectangularShape(particle_spacing, (n_particles_xy, n_particles_xy), (0.
                          coordinates_perturbation=perturb_coordinates ? 0.2 : nothing, # To avoid stagnant streamlines when not using TVF.
                          density=fluid_density, pressure=initial_pressure_function,
                          velocity=initial_velocity_function)
-
-density_calculator = SummationDensity()
-fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length,
-                                           sound_speed,
-                                           density_calculator=density_calculator,
-                                           transport_velocity=TransportVelocityAdami(background_pressure),
-                                           viscosity=ViscosityAdami(; nu))
 if wcsph
+    # Using `SummationDensity()` with `perturb_coordinates = true` introduces noise in the simulation due to perturbed particle positions,
+    # resulting in bad density estimates.
path: examples/fluid/taylor_green_vortex_2d.jl
user: login: efaulhaber
body: ```suggestion
    # Using `SummationDensity()` with `perturb_coordinates = true` introduces noise in the simulation 
    # due to bad density estimates resulting from perturbed particle positions.
```
created_at: 2025-04-09T12:13:03Z
author_association: MEMBER
diff_hunk: @@ -101,28 +101,31 @@ end
     return volume_term * ((A_a + A_b) / 2) * grad_kernel
 end
 
-@inline transport_velocity!(dv, system, rho_a, rho_b, m_a, m_b, grad_kernel, particle) = dv
+@inline transport_velocity!(dv, system, particle, neighbor, m_a, m_b, grad_kernel) = dv
 
-@inline function transport_velocity!(dv, system::FluidSystem,
-                                     rho_a, rho_b, m_a, m_b, grad_kernel, particle)
-    transport_velocity!(dv, system, system.transport_velocity, rho_a, rho_b, m_a, m_b,
-                        grad_kernel, particle)
+@inline function transport_velocity!(dv, system::FluidSystem, particle, neighbor, m_a, m_b,
+                                     grad_kernel)
+    transport_velocity!(dv, system, system.transport_velocity, particle, neighbor, m_a, m_b,
+                        grad_kernel)
 end
 
-@inline function transport_velocity!(dv, system, ::Nothing,
-                                     rho_a, rho_b, m_a, m_b, grad_kernel, particle)
+@inline function transport_velocity!(dv, system, ::Nothing, particle, neighbor, m_a, m_b,
+                                     grad_kernel)
     return dv
 end
 
 @inline function transport_velocity!(dv, system, ::TransportVelocityAdami,
-                                     rho_a, rho_b, m_a, m_b, grad_kernel, particle)
+                                     particle, neighbor, m_a, m_b, grad_kernel)
     (; transport_velocity) = system
     (; background_pressure) = transport_velocity
 
     NDIMS = ndims(system)
 
-    volume_a = m_a / rho_a
-    volume_b = m_b / rho_b
+    # This is only used to determine the relative contribution from neighboring particles.
+    # A constant pressure field is required for the TVF to work.
+    volume_a = m_a / system.initial_condition.density[particle]
+    volume_b = m_b / system.initial_condition.density[neighbor]
path: src/schemes/fluid/transport_velocity.jl
user: login: efaulhaber
body: Can you add a source for why this is correct?
created_at: 2025-04-09T12:14:17Z
author_association: MEMBER
diff_hunk: @@ -101,28 +101,31 @@ end
     return volume_term * ((A_a + A_b) / 2) * grad_kernel
 end
 
-@inline transport_velocity!(dv, system, rho_a, rho_b, m_a, m_b, grad_kernel, particle) = dv
+@inline transport_velocity!(dv, system, particle, neighbor, m_a, m_b, grad_kernel) = dv
 
-@inline function transport_velocity!(dv, system::FluidSystem,
-                                     rho_a, rho_b, m_a, m_b, grad_kernel, particle)
-    transport_velocity!(dv, system, system.transport_velocity, rho_a, rho_b, m_a, m_b,
-                        grad_kernel, particle)
+@inline function transport_velocity!(dv, system::FluidSystem, particle, neighbor, m_a, m_b,
+                                     grad_kernel)
+    transport_velocity!(dv, system, system.transport_velocity, particle, neighbor, m_a, m_b,
+                        grad_kernel)
 end
 
-@inline function transport_velocity!(dv, system, ::Nothing,
-                                     rho_a, rho_b, m_a, m_b, grad_kernel, particle)
+@inline function transport_velocity!(dv, system, ::Nothing, particle, neighbor, m_a, m_b,
+                                     grad_kernel)
     return dv
 end
 
 @inline function transport_velocity!(dv, system, ::TransportVelocityAdami,
-                                     rho_a, rho_b, m_a, m_b, grad_kernel, particle)
+                                     particle, neighbor, m_a, m_b, grad_kernel)
     (; transport_velocity) = system
     (; background_pressure) = transport_velocity
 
     NDIMS = ndims(system)
 
-    volume_a = m_a / rho_a
-    volume_b = m_b / rho_b
+    # This is only used to determine the relative contribution from neighboring particles.
+    # A constant pressure field is required for the TVF to work.
+    volume_a = m_a / system.initial_condition.density[particle]
+    volume_b = m_b / system.initial_condition.density[neighbor]
path: src/schemes/fluid/transport_velocity.jl
user: login: LasNikas
body: It's not explicitly mentioned in the literature (AFAIK).
So I elaborated a bit more in the comment
created_at: 2025-04-09T14:18:26Z
author_association: COLLABORATOR

PR comments:
user: login: codecov[bot]
created_at: 2025-04-09T09:24:41Z
body: ## [Codecov](https://app.codecov.io/gh/trixi-framework/TrixiParticles.jl/pull/600?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=trixi-framework) Report
Attention: Patch coverage is `66.66667%` with `7 lines` in your changes missing coverage. Please review.
> Project coverage is 70.11%. Comparing base [(`54fd033`)](https://app.codecov.io/gh/trixi-framework/TrixiParticles.jl/commit/54fd0336b128279f18151ff324d4a460e9b9258c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=trixi-framework) to head [(`05d0749`)](https://app.codecov.io/gh/trixi-framework/TrixiParticles.jl/commit/05d07497a66825524b3fa7772cf865f579c80e95?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=trixi-framework).

| [Files with missing lines](https://app.codecov.io/gh/trixi-framework/TrixiParticles.jl/pull/600?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=trixi-framework) | Patch % | Lines |
|---|---|---|
| [src/schemes/fluid/transport\_velocity.jl](https://app.codecov.io/gh/trixi-framework/TrixiParticles.jl/pull/600?src=pr&el=tree&filepath=src%2Fschemes%2Ffluid%2Ftransport_velocity.jl&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=trixi-framework#diff-c3JjL3NjaGVtZXMvZmx1aWQvdHJhbnNwb3J0X3ZlbG9jaXR5Lmps) | 42.85% | [4 Missing :warning: ](https://app.codecov.io/gh/trixi-framework/TrixiParticles.jl/pull/600?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=trixi-framework) |
| [...rc/schemes/fluid/weakly\_compressible\_sph/system.jl](https://app.codecov.io/gh/trixi-framework/TrixiParticles.jl/pull/600?src=pr&el=tree&filepath=src%2Fschemes%2Ffluid%2Fweakly_compressible_sph%2Fsystem.jl&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=trixi-framework#diff-c3JjL3NjaGVtZXMvZmx1aWQvd2Vha2x5X2NvbXByZXNzaWJsZV9zcGgvc3lzdGVtLmps) | 62.50% | [3 Missing :warning: ](https://app.codecov.io/gh/trixi-framework/TrixiParticles.jl/pull/600?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=trixi-framework) |

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main     #600   +/-   ##
=======================================
  Coverage   70.10%   70.11%           
=======================================
  Files          99       99           
  Lines        6345     6350    +5     
=======================================
+ Hits         4448     4452    +4     
- Misses       1897     1898    +1     
```

| [Flag](https://app.codecov.io/gh/trixi-framework/TrixiParticles.jl/pull/600/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=trixi-framework) | Coverage Δ | |
|---|---|---|
| [unit](https://app.codecov.io/gh/trixi-framework/TrixiParticles.jl/pull/600/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=trixi-framework) | `70.11% <66.66%> (+<0.01%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=trixi-framework#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/trixi-framework/TrixiParticles.jl/pull/600?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=trixi-framework).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=trixi-framework).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: LasNikas
created_at: 2025-04-09T14:56:55Z
body: /run-gpu-tests
author_association: COLLABORATOR
user: login: LasNikas
created_at: 2025-04-09T17:06:45Z
body: /run-gpu-tests
author_association: COLLABORATOR