Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
111c8f7
initial commit
Sep 6, 2024
81724f0
correct inflow mass flux
Sep 6, 2024
31086c2
fix accelerate!
Sep 9, 2024
e97d012
fix test and accelerate!
marinlauber Sep 13, 2024
2766de4
fix maintest error
marinlauber Sep 13, 2024
d451f1c
fix allocation issues, merge body_force! and update test
marinlauber Feb 27, 2025
633b6d9
tidy interface and update test
marinlauber Feb 27, 2025
cde7299
merge master and start fixing stuff
marinlauber Feb 27, 2025
ab62bbc
try solve compatibility issue
Feb 28, 2025
05336b5
fix Simulation constructor
Mar 3, 2025
3653bbf
fix BC time and constructor
Mar 4, 2025
5226d1a
Small adjustements on how to create u_BC and uλ in Simulation constru…
b-fg Mar 4, 2025
247c1a8
Reverted from zero(T) to 0 in defining uλ cause of GPU nonbits error
b-fg Mar 4, 2025
f4f4697
u_BC everywhere (instead of mixed with uBC) and same default Float32 …
b-fg Mar 4, 2025
be9e85f
Added a user defined function (udf) feature to pass functions into si…
b-fg Mar 4, 2025
24aa6b7
Fixed body_force tests. Remember to always runs all the tests...
b-fg Mar 4, 2025
a932639
removes body_force and defines acceleration g as a function of g(i,x,…
b-fg Mar 5, 2025
404ddab
Added test for when both g and U are present.
b-fg Mar 5, 2025
2e6a49b
Added assert for g function definition in Simulation constructor.
b-fg Mar 5, 2025
05a989d
Reactivated tests (forgot to do it in last commit).
b-fg Mar 5, 2025
35c1b23
Removed EllipsisNotation from Flow.jl (not needed anymore) and added …
b-fg Mar 5, 2025
c9a4db2
Uses arg names instead of underscore because it breaks (unsure why no…
b-fg Mar 7, 2025
5cb8752
Fixed tests.
b-fg Mar 7, 2025
3026334
Fixed viscous force tests.
b-fg Mar 10, 2025
341f046
Added type stable check for g function in Simulation constructor. Fix…
b-fg Mar 11, 2025
c80d198
improve tests for non-uniform BCs and improve error message
marinlauber Mar 12, 2025
311b5b1
Change tangential BCs and add rotating Ref test
weymouth Mar 14, 2025
9a7753d
fix typo in rotating reference test
weymouth Mar 18, 2025
8c55457
check type with x,t not only t
marinlauber Mar 27, 2025
561db0d
remove uλ from Simulation and Flow
marinlauber Mar 28, 2025
957aed0
remove old checks in WaterLily.jl
marinlauber Mar 28, 2025
bbbc15a
Name and doc changes
weymouth Mar 30, 2025
01d9a24
Slightly better Uλ doc string
weymouth Mar 30, 2025
399061a
Reverted behaviour to uBC and uλ for BCs and IC, respectively. Also c…
b-fg Apr 16, 2025
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
71 changes: 37 additions & 34 deletions src/Flow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,26 +59,16 @@ lowerBoundary!(r,u,Φ,ν,i,j,N,::Val{true}) = @loop (
Φ[I] = ϕuP(j,CIj(j,CI(I,i),N[j]-2),CI(I,i),u,ϕ(i,CI(I,j),u)) -ν*∂(j,CI(I,i),u); r[I,i] += Φ[I]) over I ∈ slice(N,2,j,2)
upperBoundary!(r,u,Φ,ν,i,j,N,::Val{true}) = @loop r[I-δ(j,I),i] -= Φ[CIj(j,I,2)] over I ∈ slice(N,N[j],j,2)

using EllipsisNotation
"""
accelerate!(r,dt,g)
accelerate!(r,t,g,U)

Add a uniform acceleration `gᵢ+dUᵢ/dt` at time `t=sum(dt)` to field `r`.
Accounts for applied and reference-frame acceleration using `rᵢ += g(i,x,t)+dU(i,x,t)/dt`
"""
accelerate!(r,dt,g::Function,::Tuple,t=sum(dt)) = for i ∈ 1:last(size(r))
r[..,i] .+= g(i,t)
end
accelerate!(r,dt,g::Nothing,U::Function) = accelerate!(r,dt,(i,t)->ForwardDiff.derivative(τ->U(i,τ),t),())
accelerate!(r,dt,g::Function,U::Function) = accelerate!(r,dt,(i,t)->g(i,t)+ForwardDiff.derivative(τ->U(i,τ),t),())
accelerate!(r,dt,::Nothing,::Tuple) = nothing
"""
BCTuple(U,dt,N)

Return BC tuple `U(i∈1:N, t=sum(dt))`.
"""
BCTuple(f::Function,dt,N,t=sum(dt))=ntuple(i->f(i,t),N)
BCTuple(f::Tuple,dt,N)=f

accelerate!(r,t,::Nothing,::Union{Nothing,Tuple}) = nothing
accelerate!(r,t,f::Function) = @loop r[Ii] += f(last(Ii),loc(Ii,eltype(r)),t) over Ii ∈ CartesianIndices(r)
accelerate!(r,t,g::Function,::Union{Nothing,Tuple}) = accelerate!(r,t,g)
accelerate!(r,t,::Nothing,U::Function) = accelerate!(r,t,(i,x,t)->ForwardDiff.derivative(τ->U(i,x,τ),t))
accelerate!(r,t,g::Function,U::Function) = accelerate!(r,t,(i,x,t)->g(i,x,t)+ForwardDiff.derivative(τ->U(i,x,τ),t))
"""
Flow{D::Int, T::Float, Sf<:AbstractArray{T,D}, Vf<:AbstractArray{T,D+1}, Tf<:AbstractArray{T,D+2}}

Expand All @@ -101,23 +91,25 @@ struct Flow{D, T, Sf<:AbstractArray{T}, Vf<:AbstractArray{T}, Tf<:AbstractArray{
μ₀:: Vf # zeroth-moment vector
μ₁:: Tf # first-moment tensor field
# Non-fields
U :: Union{NTuple{D,Number},Function} # domain boundary values
uBC :: Union{NTuple{D,Number},Function} # domain boundary values/function
Δt:: Vector{T} # time step (stored in CPU memory)
ν :: T # kinematic viscosity
g :: Union{Function,Nothing} # (possibly time-varying) uniform acceleration field
g :: Union{Function,Nothing} # acceleration field funciton
exitBC :: Bool # Convection exit
perdir :: NTuple # tuple of periodic direction
function Flow(N::NTuple{D}, U; f=Array, Δt=0.25, ν=0., g=nothing,
uλ::Function=(i, x) -> 0., perdir=(), exitBC=false, T=Float64) where D
function Flow(N::NTuple{D}, uBC; f=Array, Δt=0.25, ν=0., g=nothing,
uλ=nothing, perdir=(), exitBC=false, T=Float32) where D
Ng = N .+ 2
Nd = (Ng..., D)
u = Array{T}(undef, Nd...) |> f; apply!(uλ, u);
BC!(u,BCTuple(U,0.,D),exitBC,perdir); exitBC!(u,u,BCTuple(U,0.,D),0.)
u⁰ = copy(u);
isnothing(uλ) && (uλ = ic_function(uBC))
u = Array{T}(undef, Nd...) |> f
isa(uλ, Function) ? apply!(uλ, u) : apply!((i,x)->uλ[i], u)
BC!(u,uBC,exitBC,perdir); exitBC!(u,u,0.)
u⁰ = copy(u)
fv, p, σ = zeros(T, Nd) |> f, zeros(T, Ng) |> f, zeros(T, Ng) |> f
V, μ₀, μ₁ = zeros(T, Nd) |> f, ones(T, Nd) |> f, zeros(T, Ng..., D, D) |> f
BC!(μ₀,ntuple(zero, D),false,perdir)
new{D,T,typeof(p),typeof(u),typeof(μ₁)}(u,u⁰,fv,p,σ,V,μ₀,μ₁,U,T[Δt],ν,g,exitBC,perdir)
new{D,T,typeof(p),typeof(u),typeof(μ₁)}(u,u⁰,fv,p,σ,V,μ₀,μ₁,uBC,T[Δt],ν,g,exitBC,perdir)
end
end

Expand Down Expand Up @@ -150,21 +142,23 @@ end
Integrate the `Flow` one time step using the [Boundary Data Immersion Method](https://eprints.soton.ac.uk/369635/)
and the `AbstractPoisson` pressure solver to project the velocity onto an incompressible flow.
"""
@fastmath function mom_step!(a::Flow{N},b::AbstractPoisson) where N
a.u⁰ .= a.u; scale_u!(a,0); U = BCTuple(a.U,a.Δt,N)
@fastmath function mom_step!(a::Flow{N},b::AbstractPoisson;udf=nothing,kwargs...) where N
a.u⁰ .= a.u; scale_u!(a,0); t₁ = sum(a.Δt); t₀ = t₁-a.Δt[end]
# predictor u → u'
@log "p"
conv_diff!(a.f,a.u⁰,a.σ,ν=a.ν,perdir=a.perdir)
accelerate!(a.f,@view(a.Δt[1:end-1]),a.g,a.U)
BDIM!(a); BC!(a.u,U,a.exitBC,a.perdir)
a.exitBC && exitBC!(a.u,a.u⁰,U,a.Δt[end]) # convective exit
project!(a,b); BC!(a.u,U,a.exitBC,a.perdir)
udf!(a,udf,t₀; kwargs...)
accelerate!(a.f,t₀,a.g,a.uBC)
BDIM!(a); BC!(a.u,a.uBC,a.exitBC,a.perdir,t₁) # BC MUST be at t₁
a.exitBC && exitBC!(a.u,a.u⁰,a.Δt[end]) # convective exit
project!(a,b); BC!(a.u,a.uBC,a.exitBC,a.perdir,t₁)
# corrector u → u¹
@log "c"
conv_diff!(a.f,a.u,a.σ,ν=a.ν,perdir=a.perdir)
accelerate!(a.f,a.Δt,a.g,a.U)
BDIM!(a); scale_u!(a,0.5); BC!(a.u,U,a.exitBC,a.perdir)
project!(a,b,0.5); BC!(a.u,U,a.exitBC,a.perdir)
udf!(a,udf,t₁; kwargs...)
accelerate!(a.f,t₁,a.g,a.uBC)
BDIM!(a); scale_u!(a,0.5); BC!(a.u,a.uBC,a.exitBC,a.perdir,t₁)
project!(a,b,0.5); BC!(a.u,a.uBC,a.exitBC,a.perdir,t₁)
push!(a.Δt,CFL(a))
end
scale_u!(a,scale) = @loop a.u[Ii] *= scale over Ii ∈ inside_u(size(a.p))
Expand All @@ -180,3 +174,12 @@ end
end
return s
end

"""
udf!(flow::Flow,udf::Function,t)

User defined function using `udf::Function` to operate on `flow::Flow` during the predictor and corrector step, in sync with time `t`.
Keyword arguments must be passed to `sim_step!` for them to be carried over the actual function call.
"""
udf!(flow,::Nothing,t; kwargs...) = nothing
udf!(flow,force!::Function,t; kwargs...) = force!(flow,t; kwargs...)
8 changes: 4 additions & 4 deletions src/Metrics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,12 @@ function pressure_force(p,df,body,t=0,T=promote_type(Float64,eltype(p)))
end

"""
∇²u(I::CartesianIndex,u)
S(I::CartesianIndex,u)

Rate-of-strain tensor.
"""
∇²u(I::CartesianIndex{2},u) = @SMatrix [∂(i,j,I,u)+∂(j,i,I,u) for i ∈ 1:2, j ∈ 1:2]
∇²u(I::CartesianIndex{3},u) = @SMatrix [∂(i,j,I,u)+∂(j,i,I,u) for i ∈ 1:3, j ∈ 1:3]
S(I::CartesianIndex{2},u) = @SMatrix [0.5*(∂(i,j,I,u)+∂(j,i,I,u)) for i ∈ 1:2, j ∈ 1:2]
S(I::CartesianIndex{3},u) = @SMatrix [0.5*(∂(i,j,I,u)+∂(j,i,I,u)) for i ∈ 1:3, j ∈ 1:3]
"""
viscous_force(sim::Simulation)

Expand All @@ -115,7 +115,7 @@ viscous_force(sim) = viscous_force(sim.flow,sim.body)
viscous_force(flow,body) = viscous_force(flow.u,flow.ν,flow.f,body,time(flow))
function viscous_force(u,ν,df,body,t=0,T=promote_type(Float64,eltype(u)))
df .= zero(eltype(u))
@loop df[I,:] .= -ν*∇²u(I,u)*nds(body,loc(0,I,T),t) over I ∈ inside_u(u)
@loop df[I,:] .= -2ν*S(I,u)*nds(body,loc(0,I,T),t) over I ∈ inside_u(u)
sum(T,df,dims=ntuple(i->i,ndims(u)-1))[:] |> Array
end

Expand Down
39 changes: 20 additions & 19 deletions src/WaterLily.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,27 @@ include("Metrics.jl")

abstract type AbstractSimulation end
"""
Simulation(dims::NTuple, u_BC::Union{NTuple,Function}, L::Number;
U=norm2(u_BC), Δt=0.25, ν=0., ϵ=1, perdir=()
uλ::nothing, g=nothing, exitBC=false,
Simulation(dims::NTuple, uBC::Union{NTuple,Function}, L::Number;
U=norm2(), Δt=0.25, ν=0., ϵ=1, g=nothing,
perdir=(), exitBC=false,
body::AbstractBody=NoBody(),
T=Float32, mem=Array)

Constructor for a WaterLily.jl simulation:

- `dims`: Simulation domain dimensions.
- `u_BC`: Simulation domain velocity boundary conditions, either a
tuple `u_BC[i]=uᵢ, i=eachindex(dims)`, or a time-varying function `f(i,t)`
- `uBC`: Velocity field applied to boundary and acceleration conditions.
Define a `Tuple` for constant BCs, or a `Function` for space and time varying BCs `uBC(i,x,t)`.
- `L`: Simulation length scale.
- `U`: Simulation velocity scale.
- `U`: Simulation velocity scale. Required if using `Uλ::Function`.
- `Δt`: Initial time step.
- `ν`: Scaled viscosity (`Re=UL/ν`).
- `g`: Domain acceleration, `g(i,t)=duᵢ/dt`
- `g`: Domain acceleration, `g(i,x,t)=duᵢ/dt`
- `ϵ`: BDIM kernel width.
- `perdir`: Domain periodic boundary condition in the `(i,)` direction.
- `uλ`: Velocity field applied to the initial condition.
Define a Tuple for homogeneous (per direction) IC, or a `Function` for space varying IC `uλ(i,x)`.
- `exitBC`: Convective exit boundary condition in the `i=1` direction.
- `uλ`: Function to generate the initial velocity field.
- `body`: Immersed geometry.
- `T`: Array element type.
- `mem`: memory location. `Array`, `CuArray`, `ROCm` to run on CPU, NVIDIA, or AMD devices, respectively.
Expand All @@ -63,16 +64,14 @@ mutable struct Simulation <: AbstractSimulation
flow :: Flow
body :: AbstractBody
pois :: AbstractPoisson
function Simulation(dims::NTuple{N}, u_BC, L::Number;
function Simulation(dims::NTuple{N}, uBC, L::Number;
Δt=0.25, ν=0., g=nothing, U=nothing, ϵ=1, perdir=(),
uλ=nothing, exitBC=false, body::AbstractBody=NoBody(),
T=Float32, mem=Array) where N
@assert !(isa(u_BC,Function) && isa(uλ,Function)) "`u_BC` and `uλ` cannot be both specified as Function"
@assert !(isnothing(U) && isa(u_BC,Function)) "`U` must be specified if `u_BC` is a Function"
isa(u_BC,Function) && @assert all(typeof.(ntuple(i->u_BC(i,zero(T)),N)).==T) "`u_BC` is not type stable"
uλ = isnothing(uλ) ? ifelse(isa(u_BC,Function),(i,x)->u_BC(i,0.),(i,x)->u_BC[i]) : uλ
U = isnothing(U) ? √sum(abs2,u_BC) : U # default if not specified
flow = Flow(dims,u_BC;uλ,Δt,ν,g,T,f=mem,perdir,exitBC)
@assert !(isnothing(U) && isa(uBC,Function)) "`U` (velocity scale) must be specified if boundary conditions `uBC` is a `Function`"
isnothing(U) && (U = √sum(abs2,uBC))
check_fn(uBC,N,T,3); check_fn(g,N,T,3); check_fn(uλ,N,T,2)
flow = Flow(dims,uBC;uλ,Δt,ν,g,T,f=mem,perdir,exitBC)
measure!(flow,body;ϵ)
new(U,L,ϵ,flow,body,MultiLevelPoisson(flow.p,flow.μ₀,flow.σ;perdir))
end
Expand All @@ -94,18 +93,20 @@ sim_time(sim::AbstractSimulation) = time(sim)*sim.U/sim.L
Integrate the simulation `sim` up to dimensionless time `t_end`.
If `remeasure=true`, the body is remeasured at every time step.
Can be set to `false` for static geometries to speed up simulation.
A user-defined function `udf` can be passed to arbitrarily modify the `::Flow` during the predictor and corrector steps.
If the `udf` user keyword arguments, these needs to be included in the `sim_step!` call as well.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Probably want to add something in the documentation here about the type returned by these functions needing to match T.

"""
function sim_step!(sim::AbstractSimulation,t_end;remeasure=true,max_steps=typemax(Int),verbose=false)
function sim_step!(sim::AbstractSimulation,t_end;remeasure=true,max_steps=typemax(Int),udf=nothing,verbose=false,kwargs...)
steps₀ = length(sim.flow.Δt)
while sim_time(sim) < t_end && length(sim.flow.Δt) - steps₀ < max_steps
sim_step!(sim; remeasure)
sim_step!(sim; remeasure, udf, kwargs...)
verbose && println("tU/L=",round(sim_time(sim),digits=4),
", Δt=",round(sim.flow.Δt[end],digits=3))
end
end
function sim_step!(sim::AbstractSimulation;remeasure=true)
function sim_step!(sim::AbstractSimulation;remeasure=true,udf=nothing,kwargs...)
remeasure && measure!(sim)
mom_step!(sim.flow,sim.pois)
mom_step!(sim.flow, sim.pois; udf, kwargs...)
end

"""
Expand Down
33 changes: 24 additions & 9 deletions src/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@
boundary. For example `aₓ(x)=Aₓ ∀ x ∈ minmax(X)`. A zero Neumann condition
is applied to the tangential components.
"""
function BC!(a,A,saveexit=false,perdir=())
BC!(a,U,saveexit=false,perdir=(),t=0) = BC!(a,(i,x,t)->U[i],saveexit,perdir,t)
function BC!(a,uBC::Function,saveexit=false,perdir=(),t=0)
N,n = size_u(a)
for i ∈ 1:n, j ∈ 1:n
if j in perdir
Expand All @@ -198,26 +199,28 @@
else
if i==j # Normal direction, Dirichlet
for s ∈ (1,2)
@loop a[I,i] = A[i] over I ∈ slice(N,s,j)
@loop a[I,i] = uBC(i,loc(i,I),t) over I ∈ slice(N,s,j)

Check warning on line 202 in src/util.jl

View check run for this annotation

Codecov / codecov/patch

src/util.jl#L202

Added line #L202 was not covered by tests
end
(!saveexit || i>1) && (@loop a[I,i] = A[i] over I ∈ slice(N,N[j],j)) # overwrite exit
(!saveexit || i>1) && (@loop a[I,i] = uBC(i,loc(i,I),t) over I ∈ slice(N,N[j],j)) # overwrite exit
else # Tangential directions, Neumann
@loop a[I,i] = a[I+δ(j,I),i] over I ∈ slice(N,1,j)
@loop a[I,i] = a[I-δ(j,I),i] over I ∈ slice(N,N[j],j)
@loop a[I,i] = uBC(i,loc(i,I),t)+a[I+δ(j,I),i]-uBC(i,loc(i,I+δ(j,I)),t) over I ∈ slice(N,1,j)
@loop a[I,i] = uBC(i,loc(i,I),t)+a[I-δ(j,I),i]-uBC(i,loc(i,I-δ(j,I)),t) over I ∈ slice(N,N[j],j)

Check warning on line 207 in src/util.jl

View check run for this annotation

Codecov / codecov/patch

src/util.jl#L206-L207

Added lines #L206 - L207 were not covered by tests
end
end
end
end

"""
exitBC!(u,u⁰,U,Δt)

Apply a 1D convection scheme to fill the ghost cell on the exit of the domain.
"""
function exitBC!(u,u⁰,U,Δt)
function exitBC!(u,u⁰,Δt)
N,_ = size_u(u)
exitR = slice(N.-1,N[1],1,2) # exit slice excluding ghosts
@loop u[I,1] = u⁰[I,1]-U[1]*Δt*(u⁰[I,1]-u⁰[I-δ(1,I),1]) over I ∈ exitR
∮u = sum(u[exitR,1])/length(exitR)-U[1] # mass flux imbalance
U = sum(@view(u[slice(N.-1,2,1,2),1]))/length(exitR) # inflow mass flux
@loop u[I,1] = u⁰[I,1]-U*Δt*(u⁰[I,1]-u⁰[I-δ(1,I),1]) over I ∈ exitR

Check warning on line 222 in src/util.jl

View check run for this annotation

Codecov / codecov/patch

src/util.jl#L222

Added line #L222 was not covered by tests
∮u = sum(@view(u[exitR,1]))/length(exitR)-U # mass flux imbalance
@loop u[I,1] -= ∮u over I ∈ exitR # correct flux
end
"""
Expand Down Expand Up @@ -250,8 +253,20 @@
end
return s
end
using EllipsisNotation
function interp(x::SVector{D}, varr::AbstractArray) where {D}
# Shift to align with each staggered grid component and interpolate
@inline shift(i) = SVector{D}(ifelse(i==j,0.5,0.0) for j in 1:D)
return SVector{D}(interp(x+shift(i),@view(varr[..,i])) for i in 1:D)
end
end

check_fn(f,N,T,nargs) = nothing
function check_fn(f::Function,N,T,nargs)
@assert first(methods(f)).nargs==nargs+1 "$f signature needs $nargs arguments"
@assert all(typeof.(ntuple(i->f(i,xtargs(Val{}(nargs),N,T)...),N)).==T) "$f is not type stable"
end
xtargs(::Val{2},N,T) = (zeros(SVector{N,T}),)

Check warning on line 268 in src/util.jl

View check run for this annotation

Codecov / codecov/patch

src/util.jl#L268

Added line #L268 was not covered by tests
xtargs(::Val{3},N,T) = (zeros(SVector{N,T}),zero(T))

ic_function(uBC::Function) = (i,x)->uBC(i,x,0)
ic_function(uBC::Tuple) = (i,x)->uBC[i]
Loading
Loading