Turbulence utils: SGS model and on-the-fly temporal averaging#198
Turbulence utils: SGS model and on-the-fly temporal averaging#198
Conversation
|
Looking pretty clean |
|
This builds on #165, so we should integrate that first. |
…entral differences (cds) too.
Now users can pass a MeanFlow struct to sim_step to compute on-the-fly temporal averages. Need to add tests.
|
Needs to add tests, and time-averaging restart (I/O) based on JLD2 (once #213 is merged it can be added in WaterLilyJLD2Ext.jl). |
|
Here is MWE of how this works using Revise, WaterLily, GLMakie
function circle(n,m;Re=100,U=1)
radius, center = m/8, m/2-1
sdf(x,t) = √sum(abs2, x .- center) - radius
Simulation((n,m), (U,0), 2radius; ν=U*2radius/Re, body=AutoBody(sdf))
end
sim = circle(4*32,32)
sim_step!(sim, 40; remeasure=false, verbose=true) # developing flow
meanflow = MeanFlow(sim.flow)
sim_step!(sim, 190; remeasure=false, verbose=true, meanflow) # temporal average
function vort!(cpu_array, sim)
a = sim.flow.σ
WaterLily.@inside a[I] = WaterLily.curl(3,I,sim.flow.u)*sim.L/sim.U
copyto!(cpu_array, a[inside(a)]) # copy to CPU
end
viz!(sim, vort!; colormap=:balance, levels=50) # Flow plot
WaterLily.copy!(sim.flow, meanflow)
viz!(sim, vort!; colormap=:balance, levels=50) # MeanFlow plot |
|
There is something allocating when passing the convective scheme |
…locations. This is currently done using dispatch by Value, and requires repeated code. Without this, when trying to get automatically specialized flux functions, it always allocates during runtime.
|
Passing a flux function @inline ϕu(a,I,f,u,::Val{:quick}) = @inbounds u>0 ? u*quick(f[I-2δ(a,I)],f[I-δ(a,I)],f[I]) : u*quick(f[I+δ(a,I)],f[I],f[I-δ(a,I)])
@inline ϕuP(a,Ip,I,f,u,::Val{:quick}) = @inbounds u>0 ? u*quick(f[Ip],f[I-δ(a,I)],f[I]) : u*quick(f[I+δ(a,I)],f[I],f[I-δ(a,I)])
@inline ϕuL(a,I,f,u,::Val{:quick}) = @inbounds u>0 ? u*ϕ(a,I,f) : u*quick(f[I+δ(a,I)],f[I],f[I-δ(a,I)])
@inline ϕuR(a,I,f,u,::Val{:quick}) = @inbounds u<0 ? u*ϕ(a,I,f) : u*quick(f[I-2δ(a,I)],f[I-δ(a,I)],f[I])
@inline ϕu(a,I,f,u,::Val{:cds}) = @inbounds u>0 ? u*cds(f[I-2δ(a,I)],f[I-δ(a,I)],f[I]) : u*cds(f[I+δ(a,I)],f[I],f[I-δ(a,I)])
@inline ϕuP(a,Ip,I,f,u,::Val{:cds}) = @inbounds u>0 ? u*cds(f[Ip],f[I-δ(a,I)],f[I]) : u*cds(f[I+δ(a,I)],f[I],f[I-δ(a,I)])
@inline ϕuL(a,I,f,u,::Val{:cds}) = @inbounds u>0 ? u*ϕ(a,I,f) : u*cds(f[I+δ(a,I)],f[I],f[I-δ(a,I)])
@inline ϕuR(a,I,f,u,::Val{:cds}) = @inbounds u<0 ? u*ϕ(a,I,f) : u*cds(f[I-2δ(a,I)],f[I-δ(a,I)],f[I])Instead of the cleaner version @inline ϕu(a,I,f,u,λ::F) where {F} = @inbounds u>0 ? u*λ(f[I-2δ(a,I)],f[I-δ(a,I)],f[I]) : u*λ(f[I+δ(a,I)],f[I],f[I-δ(a,I)])
@inline ϕuP(a,Ip,I,f,u,λ::F) where {F} = @inbounds u>0 ? u*λ(f[Ip],f[I-δ(a,I)],f[I]) : u*λ(f[I+δ(a,I)],f[I],f[I-δ(a,I)])
@inline ϕuL(a,I,f,u,λ::F) where {F} = @inbounds u>0 ? u*ϕ(a,I,f) : u*λ(f[I+δ(a,I)],f[I],f[I-δ(a,I)])
@inline ϕuR(a,I,f,u,λ::F) where {F} = @inbounds u<0 ? u*ϕ(a,I,f) : u*λ(f[I-2δ(a,I)],f[I-δ(a,I)],f[I])since the latter version results in allocations, as measure in |
|
How are you passing it into |
|
The way I planned users to select the convection scheme is by passing themselves the function into I have tried to force specialization in |
|
It's hard to tell, without digging deeper into the code. I would use the allocation profiler to determine first where the allocations are steming from (potentially setting the sampling to 1). Then I would try not passing lambda through a keyword argument. That will certainly cause allocation at one point since you have to call the specialized function at some point |
|
cc @weymouth, I traced the allocations down, and it is a problem of the So, within @loop Φ[I] = ϕu(j,CI(I,i),u,ϕ(i,CI(I,j),u),λ) over I ∈ inside_u(N,j)is allocating when passing for I ∈ inside_u(N,j)
Φ[I] = ϕu(j,CI(I,i),u,ϕ(i,CI(I,j),u),λ)
endis not allocating. Thus, there is something not quite write with the macro not allowing specialization, as far as I can see. Maybe it's just that the macro results in a higher level function that ultimately calls function $kern($(rep.(sym)...),::Val{1})
@simd for $I ∈ $R
@fastmath @inbounds $ex
end
endbut then the |
Yeah that sounds about right. Julia specialization heuristics strike again. You could generate a kernel that specializes on each of the arguments? |
|
Do you mean like a macro generating function kern(a::A,b::B,c::C,...,::Val{1}) where {A,B,C,...}
...
endThat might work indeed, but I am not sure my metaprogramming skills are up to the task haha. Maybe @weymouth? 👀 |
|
Also, would this be something that benefits KA kernels, @vchuravy? Something like @kernel function $kern_($(rep.(sym)...),@Const(I0))
$I = @index(Global,Cartesian)
$I += I0
@fastmath @inbounds $ex
end
function $kern(a::A,b::B,...,_) where {A,B,...}
$kern_(get_backend($(sym[1])),64)($(sym...),$R[1]-oneunit($R[1]),ndrange=size($R))
end |
|
Another option for |
|
To do
|
…w be passed as a function without allocating. sim_step now takes an additional keyword argument, λ, which can be the quick or cds function, or any that the user wants to implement. Added a new allocation test for when λ is passed, and added a test for the new central difference scheme as well.
|
Benchmarks looking good, should be ready to merge. Benchmarks |


Implementing some utilities for turbulence modelling such as eddy viscosity models for subgrid stresses (based on #165), and averaging routines.