@@ -258,4 +258,37 @@ function interp(x::SVector{D}, varr::AbstractArray) where {D}
258258 # Shift to align with each staggered grid component and interpolate
259259 @inline shift (i) = SVector {D} (ifelse (i== j,0.5 ,0.0 ) for j in 1 : D)
260260 return SVector {D} (interp (x+ shift (i),@view (varr[.. ,i])) for i in 1 : D)
261+ end
262+
263+ # Turbulence modelling utils
264+ """
265+ sgs(flow, t; νₜ, S, C=0.16)
266+
267+ Implements a user-defined function `udf` to model subgrid-scale LES stresses based on the Boussinesq approximation
268+ τᵃᵢⱼ = τʳᵢⱼ - (1/3)τʳₖₖδᵢⱼ = -2νₜS̅ᵢⱼ
269+ where
270+ ▁▁▁▁
271+ τʳᵢⱼ = uᵢuⱼ - u̅ᵢu̅ⱼ
272+
273+ and we add -∂ⱼ(τᵃᵢⱼ) to the RHS as a body force (the isotropic part of the tensor is automatically modelled by the pressure gradient term).
274+ Users need to define the turbulent viscosity function `νₜ` and pass it as a keyword argument to this function together with rate-of-strain
275+ tensor array buffer `S` and model constant `C`. For example, the standard Smagorinsky–Lilly model for the sub-grid scale stresses
276+
277+ νₜ = (CΔ)²|√(2S̅ᵢⱼS̅ᵢⱼ)|
278+
279+ could be defined as
280+ `smagorinsky(I::CartesianIndex{m} where m, S; C=0.16) = @views C^2*sqrt(2dot(S[I,:,:],S[I,:,:]))`
281+ and passed into `sim_step!` as a keyword argument:
282+ `sim_step!(sim, ...; udf=sgs, νₜ=smagorinsky, S, C)`
283+ """
284+ function sgs! (flow, t; νₜ, S, C= 0.16 )
285+ N,n = size_u (flow. u)
286+ @loop S[I,:,:] .= WaterLily. S (I,flow. u) over I ∈ inside (flow. σ)
287+ for i ∈ 1 : n, j ∈ 1 : n
288+ WaterLily. @loop (
289+ flow. σ[I] = - νₜ (I,S;C)* ∂ (j,CI (I,i),flow. u);
290+ flow. f[I,i] += flow. σ[I];
291+ ) over I ∈ inside_u (N,j)
292+ WaterLily. @loop flow. f[I- δ (j,I),i] -= flow. σ[I] over I ∈ WaterLily. inside_u (N,j)
293+ end
261294end
0 commit comments