Internal Functions

Swalbe.equilibrium!Method
equilibrium!(feq, height, velocity, gravity)

Calculation of the equilibrium distribution feq for the shallow water lattice Boltzmann method.

Arguments

  • feq :: Array{<:Number,3}: Equilibrium distribution function, to be calculated
  • height :: Array{<:Number,2}: The height field $h(\mathbf{x},t)$
  • velocity :: Array{<:Number,2}: x-component of the macroscopic velocity
  • gravity <: Number: Strength of the gravitational acceleration in lattice units

Examples

julia> using Swalbe, Test

julia> feq = zeros(10,3); ρ = ones(10); u = fill(0.1,10);

julia> Swalbe.equilibrium!(feq, ρ, u, 0.1) # Supply dummy u^2 as well.

julia> feq[:,1]
10-element Vector{Float64}:
 0.94
 0.94
 0.94
 0.94
 0.94
 0.94
 0.94
 0.94
 0.94
 0.94

julia> @test all(feq[:,1] .≈ 1 - 0.1/2 - 0.01)
Test Passed
source
Swalbe.equilibrium!Method
equilibrium!(feq, height,velocityx, velocityy, vsquare, gravity)

Calculation of the equilibrium distribution feq for the shallow water lattice Boltzmann method.

Arguments

  • feq :: Array{<:Number,3}: Equilibrium distribution function, to be calculated
  • height :: Array{<:Number,2}: The height field $h(\mathbf{x},t)$
  • velocityx :: Array{<:Number,2}: x-component of the macroscopic velocity
  • velocityy :: Array{<:Number,2}: y-component of the macroscopic velocity
  • vsquare :: Array{<:Number,2}: Dummy array that is preallocated to be filled with the square of the velocity vector
  • gravity <: Number: Strength of the gravitational acceleration in lattice units

Mathematics

The detailed motivation and derivation can be found in the article of Salmon. Similar to the standard Navier-Stokes approximating lattice Boltzmann methods the equilibrium distribution feq is an expansion to second order in velocity u. If you want it is an slow mode part of the shallow water theory and thus the equilibrium is given as

$f_i^{\text{eq}} = h \bigg(1 - \frac{5}{6}g h - \frac{2}{3}u^2\bigg),\quad i=0 \newline f_i^{\text{eq}} = w_i h \bigg(g h + 3 \mathbf{c}_i\cdot\mathbf{u} + \frac{9}{2}(\mathbf{c}_i\cdot\mathbf{u})^2) + \frac{3}{2}u^2\bigg),\quad else$

where $g$ is the gravitational acceleration (in lattice units) and $w_i, \mathbf{c}_i$ are the weights and lattice velocities.

It has been shown that it is possible to get rid of the gravity driven term in the equilibrium distribution, thus

$f_i^{\text{eq}} = h \bigg(1 - \frac{2}{3}u^2\bigg),\quad i=0 \newline f_i^{\text{eq}} = w_i h \bigg(3 \mathbf{c}_i\cdot\mathbf{u} + \frac{9}{2}(\mathbf{c}_i\cdot\mathbf{u})^2) + \frac{3}{2}u^2\bigg),\quad else$

then of course the topography gradient has to be included as a force term.

Examples

julia> using Swalbe, Test

julia> feq = zeros(5,5,9); ρ = ones(5,5); ux = fill(0.1,5,5); uy = zeros(5,5);

julia> Swalbe.equilibrium!(feq, ρ, ux, uy, zeros(5,5), 0.1) # Supply dummy u^2 as well.

julia> feq[:,:,1]
5×5 Matrix{Float64}:
 0.91  0.91  0.91  0.91  0.91
 0.91  0.91  0.91  0.91  0.91
 0.91  0.91  0.91  0.91  0.91
 0.91  0.91  0.91  0.91  0.91
 0.91  0.91  0.91  0.91  0.91

julia> Swalbe.equilibrium!(feq, ρ, ux, uy, zeros(5,5), 0.0) # Supply dummy u^2 as well.

julia> @test all(feq[:,:,1] .≈ 1 - 2/3 * 0.01)
Test Passed

References

source
Swalbe.BGKandStream!Method
BGKandStream!(fout, feq, ftemp, Fx, Fy, τ)

Performs a BGK collision operation with a WFM forcecorrection and a subsequent streaming of the resulting populations.

Arguments

  • fout :: Array{<:Number,3}: Streamed distribution after the collision processes
  • feq :: Array{<:Number,3}: Equilibrium distribution, computed with equilibrium!
  • ftemp :: Array{<:Number,3}: Temporary distribution from the time step before, only useful if $\tau \neq 1$
  • Fx :: Array{<:Number,2}: Sum of forces acting on the fluid in x-direction
  • Fy :: Array{<:Number,2}: Sum of forces acting on the fluid in y-direction
  • τ <: Number: Relaxtion time, if not supplied $\tau = 1$ assumed

Mathematics

The lattice Boltzmann equation in its discretized format is relatively simple to write down

$f_{\alpha}(\mathbf{x}+\mathbf{e}_{\alpha}\Delta t, t+\Delta t) - f_{\alpha}(\mathbf{x}, t) = -\frac{1}{\tau}(f_{\alpha}(\mathbf{x}, t) - f^{\text{eq}}_{\alpha}(\mathbf{x}, t)) + \Delta t \mathcal{S}_{\alpha},$

where the collision kernel is approximated with a BKG single relaxation time (SRT)

$\Omega_{\alpha} = \frac{1}{\tau}(f_{\alpha}(\mathbf{x}, t) - f^{\text{eq}}_{\alpha}(\mathbf{x}, t),$

and a source term $\mathcal{S}$ which is given by

$\mathcal{S}_{\alpha} = \frac{3 w_{\alpha}}{e_{\alpha x}e_{\alpha x}+e_{\alpha y}e_{\alpha y}}\mathbf{e_{\alpha}}\cdot\mathbf{F}_{\alpha} .$

The term $e_{\alpha x}e_{\alpha x}+e_{\alpha y}e_{\alpha y}$ is either zero for the zeroth population, 1 for the first four populations or 2 for the remaining ones.

Examples

julia> using Swalbe

julia> feq = ones(5,5,9); ftemp = zeros(5,5,9); fout = zeros(5,5,9);

julia> feq[1,1,:] .= 2.0 # To check the streaming process 
9-element view(::Array{Float64, 3}, 1, 1, :) with eltype Float64:
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0

julia> Swalbe.BGKandStream!(fout, feq, ftemp, zeros(5,5), zeros(5,5), 1.0)

julia> fout[:,:,6] # The value 2 should have moved one down and one to the right!
5×5 Matrix{Float64}:
 1.0  1.0  1.0  1.0  1.0
 1.0  2.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0  1.0

References

See also: Swalbe.equilibrium!

source
Swalbe.BGKandStream!Method
BGKandStream!(fout, feq, ftemp, F::Vector, τ)

Performs a BGK collision operation with a WFM forcecorrection and a subsequent streaming of the resulting populations.

Arguments

  • fout :: Array{<:Number,3}: Streamed distribution after the collision processes
  • feq :: Array{<:Number,3}: Equilibrium distribution, computed with equilibrium!
  • ftemp :: Array{<:Number,3}: Temporary distribution from the time step before, only useful if $\tau \neq 1$
  • F :: Vector{<:Number,2}: Sum of forces acting on the fluid
  • τ <: Number: Relaxtion time, if not supplied $\tau = 1$ assumed

See also: Swalbe.equilibrium!

source
Swalbe.BGKandStream!Method
BGKandStream!(state, sys; τ=sys.param.τ)

Performs a BGK collision operation with a WFM forcecorrection and a subsequent streaming of the resulting populations.

Arguments

  • state <: LBM_state: State data structure, works for every state related data structure
  • sys :: SysConst: SysConst data structure with τ as parameter
  • τ :: Float64: relaxation time, default τ=sys.param.τ

Examples

julia> using Swalbe

julia> sys = Swalbe.SysConst{Float64}(Lx=5, Ly=5, param=Swalbe.Taumucs()); state = Swalbe.Sys(sys, "CPU");

julia> state.feq[1,1,:] .= 2.0 # To check the streaming process 
9-element view(::Array{Float64, 3}, 1, 1, :) with eltype Float64:
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0

julia> Swalbe.BGKandStream!(state, sys)

julia> state.fout[:,:,6] # The value 2 should have moved one down and one to the right!
5×5 Matrix{Float64}:
 0.0  0.0  0.0  0.0  0.0
 0.0  2.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0

See also: Swalbe.equilibrium!

source
Swalbe.viewdistsMethod
viewdists(f)

Generates a view for all nine populations of a D2Q9 distribution function.

Examples

julia> using Swalbe, Test

julia> ftest = reshape(collect(1.0:225.0),5,5,9);

julia> f0, f1, f2, f3, f4, f5, f6, f7, f8 = Swalbe.viewdists(ftest);

julia> @test all(f3 .== ftest[:,:,4])
Test Passed

See also: Swalbe.BGKandStream!

source
Swalbe.viewdists_1DMethod
viewdists_1D(f)

Generates a view for all three populations of a D1Q3 distribution function.

Examples

julia> using Swalbe, Test

julia> ftest = reshape(collect(1.0:30.0),10,3);

julia> f0, f1, f2 = Swalbe.viewdists_1D(ftest);

julia> @test all(f1 .== ftest[:,2])
Test Passed

See also: Swalbe.BGKandStream!

source
Swalbe.moments!Method
moments!(height, velx, vely, fout)

Computation of the hydrodynamic moments, height and velocity.

Mathematics

The macroscopic quantities such as the height and the velocity are the moments of the distribution function,

$h(\mathbf{x},t) = \sum_{i=0}^8 f_i ,$

and

$\mathbf{v}(\mathbf{x},t) = \frac{1}{h}\sum_{i=0}^8 \mathbf{c}_i f_i .$

Examples

julia> using Swalbe, Test

julia> fout = zeros(5,5,9); fout[:,:,1] .= 1.0; fout[:,:,2] .= 0.1; # Dist with artifical velocity in x

julia> height = zeros(5,5); velx = zeros(5,5); vely = zeros(5,5);

julia> Swalbe.moments!(height,velx,vely,fout)

julia> @test all(height .== 1.1)
Test Passed

julia> @test all(velx .== 0.1/1.1)
Test Passed

julia> @test all(vely .== 0.0)
Test Passed

References

source
Swalbe.h∇p!Method
h∇p!(state)

Computation of the pressure gradient multiplied with the height.

Mathematics

The pressure gradient ($\nabla p$) is the driving force of the standard thin film equation $\partial_t h = (M(h)\nabla p)$. Our approach however does not solve the thin film equation directly, Instead we have to add the pressure gradient as a force which is given as

$F_{film} = -\frac{1}{\rho_0} h \nabla p_{film},$

where the term $p_{film}$ describes the film pressure

$p_{film} = -\gamma [\Delta h - \Pi(h)] .$

As such it is the combination of a laplacian term that minimizes the surface area as well as a interfacial potential between substrate and fluid.

Examples

julia> using Swalbe, Test

julia> state = Swalbe.Sys(Swalbe.SysConst(Lx=10, Ly=10, param=Swalbe.Taumucs()), "CPU");

julia> state.pressure .= reshape(collect(1:100),10,10);

julia> Swalbe.h∇p!(state)

julia> @test all(state.h∇px[1,:] .== -4.0) # at boundary
Test Passed

julia> @test all(state.h∇px[2,:] .== 1.0) # inside
Test Passed

References

See also: Swalbe.filmpressure!

source
Swalbe.inclination!Method

inclination!(α, state)

Force that mimics the effect of an inclined plate.

Simple model to add a body force on the fluid that should mimic the effect of an inclined plate. The model includes a smoothing tanh function to absorb shocks which might occure.

Arguments

-α :: Vector: Force vector that both hosts direction of the force as well as strength -state::State: Lattice Boltzmann state of the fluid, here we need the state.Fx, state.Fy fields -t::Int: Time step, used for the smoothing tanh factor -tstart::Int: Time delay at which the tanh becomes positve -tsmooth::Int: Time interval over which the tanh is smeared

Mathematics

This body force is simply force strength time the mass of the fluid or even simpler the height (assuming ρ=1). Thus the force becomes

$\mathbf{F} = \mathbf{\alpha} h \tanh\bigg(\frac{t-t_0}{t_s}\bigg),$

with $t_0$ being the time lag at which the tanh changes sign and $t_s$ is width of interval between -1 and 1.

See also: Swalbe.run_dropletforced

source
Swalbe.slippage!Method
slippage!(slipx, slipy, height, velx, vely, δ, μ)

Fluid substrate interaction that effectively mimics a velocity boundary condition at $h=0$.

Arguments

  • slipx :: Array{<:Number,2}: The x-component of the force due the velocity boundary condition
  • slipy :: Array{<:Number,2}: The y-component of the force due the velocity boundary condition
  • height::Array{<:Number,2}: Height field $h(\mathbf{x},t)$
  • velx::Array{<:Number,2}: x-component of the macroscopic velocity vector
  • vely::Array{<:Number,2}: y-component of the macroscopic velocity vector
  • δ <: Number: Extrapolation length into the substrate where the no-slip is met
  • μ <: Number: Kinematic viscosity of the simulation, dependent on the value of τ

Mathematics

With the velocity boundary condition at the fluid substrate interface we build the second main descriptor between our model and the thin film equation. One well studied assumption is that the fluid velocity vanishes at $h(\mathbf{x}) = 0$ which is called no-slip condition. In terms of thin film mobility M(h) this means

$M(h) = \frac{h^3}{3\mu}.$

This assumption, however, can be relaxed to allow for some slippage with a further parameter δ which determines the slip length. The here presented model matches the thin film equation by modification of the momentum equation of the shallow water model. Therefore the force we add to the momentum equation to compensate for the substrate fluid friction is given as

$F_{fric} = -\nu \alpha_{\delta}(h)\mathbf{u},$

where ν is the kinematic viscosity and α

$\alpha_{\delta}(h) = \frac{6h}{2h^2 + 6h\delta +3\delta^2},$

and δ being the slip length, or the distance inside the substrate where the fluids velocity vanishes.

References

source
Swalbe.thermal!Method
thermal!(fx, fy, height, kᵦT, μ, δ)

Computations of force due to thermal fluctuations.

Arguments

  • fx :: Array{<:Number,2}: x-component of the force due to the fluctuations
  • fy :: Array{<:Number,2}: y-component of the force due to the fluctuations
  • height :: Array{<:Number,2}: Height field $h(\mathbf{x},t)$
  • kᵦT <: Number: Strenght of thermal fluctuations in lattice units
  • μ <: Number: The kinetic viscosity
  • δ <: Number: Slip length, needed to normalize

Mathematics

The classical thin film equation is an equation without thermal noise which is defined as

$\partial_t h = \nabla\cdot(M(h)\nabla p).$

Classically thermal excitations are neglected for thin film flows. It is of course possible to introduce for example an envolving surface tension or viscosity but both do not account for e.g. the spectrum of thermal capillary waves. The reason for this short coming is the complex from the thin film equation takes if derived from the Landau-Lifshitz Navier-Stokes equation. One addition term due to the stochastic stress tensor makes it somewhat impossible to solve the eqaution self-consistently. However Grün et al. showed that it is not nessary to use the full stochastic stress tensor, but simple a multiplicative noise term

$\partial_t h = \nabla\cdot[M(h)(\nabla p + \sigma \mathcal{N}],$

where $\sigma$ and $\mathcal{N}$ are a dimensionless temperture and a gaussian white noise. Similar to the pressure gradient the addition of this term is introduced as force in our model

$F_{fluc} = \frac{1}{\rho_0}\sqrt{2k_BT\mu\alpha_{\delta}(h)}\mathcal{N},$

with $\alpha_{\delta}(h)$ being the force generated due to substrate slip.

Examples

julia> using Swalbe, Statistics, Test, Random

julia> Random.seed!(1234); # Set a seed

julia> x = ones(50,50); y = ones(50,50); h = ones(50,50);

julia> Swalbe.thermal!(x, y, h, 0.1, 1/6, 1)

julia> @test mean(x) ≈ 0.0 atol=1e-2
Test Passed

julia> @test mean(y) ≈ 0.0 atol=1e-2
Test Passed

julia> @test var(x) ≈ 2*0.1/11 atol=(2*0.1/11)/10 # var = 2kbt*6*μ/slip
Test Passed

References

See also: Swalbe.slippage!

source
Swalbe.∇γ!Method
surface_tension_gradient!(state)

Computes the gradient of a spatially resolved surface tension field.

source
Swalbe.filmpressure!Method
filmpressure!(pressure, height, γ, θ, n, m, hmin, hcrit)

Calculation of the capillary pressure which is given by $p = - γ∇²h+ Π(h)$.

Arguments

  • pressure :: Array{Number,2}: Array that store the result of the compuation
  • height :: Array{Number,2}: Height field $h(\mathbf{x},t)$
  • γ <: Number: Forcing strenght due to surface tension
  • θ <: Number: Equilibrium contact angle
  • n :: Int: Larger power law exponent for $Π(h)$
  • m :: Int: Smaller power law exponent for $Π(h)$
  • hmin <: Number: Parameter of $Π(h)$, in fact $Π(hmin) = 0$
  • hcrit <: Number: Numerical stabilizer for case $h(\mathbf{x},t) \ll hmin$

Mathematics

The capillary pressure $p_{\text{cap}}$ is the centeral angle to match our model with the thin film equation. It consists of two parts, first being the laplace pressure $\nabla^2 h$ and second being the derivative of the disjoining pontential $\Pi(h)$

$p_{\text{cap}} = -\gamma \nabla^2 h + \Pi(h).$

For the laplacian term we use the same nine point discretization as in Swlabe.∇²f!. $\Pi(h)$ on the other hand is given by

$\Pi(h) = \kappa(\theta)f(h),$

where $\kappa(\theta)$ is simply a measure for the Hamaker constant and given as

$\kappa(\theta) = \gamma(1- \cos(\theta))\frac{(n-1)(m-1)}{(n-m)h_{\text{min}}}.$

For $f(h)$ one can use various forms, a very common however is the power law given by

$f(h) = \bigg[\bigg(\frac{h_{\text{min}}}{h}\bigg)^n - \bigg(\frac{h_{\text{min}}}{h}\bigg)^m\bigg].$

Examples

julia> using Swalbe, Test

julia> h = reshape(collect(1.0:25.0),5,5) # A dummy height field
5×5 Matrix{Float64}:
 1.0   6.0  11.0  16.0  21.0
 2.0   7.0  12.0  17.0  22.0
 3.0   8.0  13.0  18.0  23.0
 4.0   9.0  14.0  19.0  24.0
 5.0  10.0  15.0  20.0  25.0

julia> pressure = zeros(5,5); θ = 0.0; # Fully wetting substrate

julia> Swalbe.filmpressure!(pressure, h, zeros(5,5,8), 0.01, 0.0, 3, 2, 0.1, 0.05) # default γ = 0.01

julia> result = [30.0 5.0 5.0 5.0 -20;
                 25.0 0.0 0.0 0.0 -25.0;
                 25.0 0.0 0.0 0.0 -25.0;
                 25.0 0.0 0.0 0.0 -25.0;
                 20.0 -5.0 -5.0 -5.0 -30.0];

julia> for i in eachindex(result)
           @test result[i] .≈ -100 .* pressure[i] atol=1e-12
       end

References

source
Swalbe.power_broadMethod
power_broad(arg, n)

Computes arg to the power n.

Actually this is useful because the ^ operator is much slower. Same thing I learned about the pow function in C, * yes it does what you want, but it is slow as fuck *.

Examples

julia> using Swalbe, Test

julia> Swalbe.power_broad(3, 3)
27

julia> Swalbe.power_broad.([2.0 5.0 6.0], 2) # Use the broadcasting operator `.`
1×3 Matrix{Float64}:
 4.0  25.0  36.0

See also: filmpressure!

source
Swalbe.viewneighborsMethod
viewneighbors(f)

Generates a view for all nine populations of a D2Q9 distribution function.

Examples

julia> using Swalbe, Test

julia> ftest = reshape(collect(1.0:5*5*8),5,5,8);

julia> f1, f2, f3, f4, f5, f6, f7, f8 = Swalbe.viewneighbors(ftest);

julia> @test all(f3 .== ftest[:,:,3])
Test Passed

See also: Swalbe.∇f!, Swalbe.filmpressure!

source
Swalbe.viewneighbors_1DMethod
viewneighbors_1D(f)

Generates a view for the two neighbors of a D1Q3 distribution function.

Examples

julia> using Swalbe, Test

julia> ftest = reshape(collect(1.0:30),10,3);

julia> f1, f2 = Swalbe.viewneighbors_1D(ftest);

julia> @test all(f2 .== ftest[:,2])
Test Passed

See also: Swalbe.∇f!, Swalbe.filmpressure!

source
Swalbe.∇f!Method
∇f!(outputx, outputy, f)

Gradient calculation with finite differences.

Computes both spatial first derivatives with a nine point stencil from an input f and writes the result to outputx and outputy. Since broadcasting is simple on the GPU we make use of circshift for the neighbors.

Mathematics

The gardient in two dimensions is given as

$\nabla f = \big(\frac{\partial f}{\partial x}, \frac{\partial f}{\partial y}\big)^T .$

Again with the nine point stencil this reduces to

$\frac{\partial f}{\partial x} = \frac{1}{3} (f_{i+1,j} - f_{i-1,j}) + \frac{1}{12}(f_{i+1,j+1} - f_{i-1,j+1} - f_{i-1,j-1} + f_{i+1,j-1}) ,$

and for the y component we get

$\frac{\partial f}{\partial y} = \frac{1}{3} (f_{i,j+1} - f_{i,j-1}) + \frac{1}{12}(f_{i+1,j+1} + f_{i-1,j+1} - f_{i-1,j-1} - f_{i+1,j-1}) .$

For the exact derivation feel free to read the reference by Junk and Klar.

Examples

julia> using Swalbe, Test

julia> arg = reshape(collect(1.0:25),5,5)
5×5 Matrix{Float64}:
 1.0   6.0  11.0  16.0  21.0
 2.0   7.0  12.0  17.0  22.0
 3.0   8.0  13.0  18.0  23.0
 4.0   9.0  14.0  19.0  24.0
 5.0  10.0  15.0  20.0  25.0

julia> resx = zeros(5,5); resy = zeros(5,5); Swalbe.∇f!(resx, resy, arg)

julia> whatXshouldbe = [-1.5 -1.5 -1.5 -1.5 -1.5;
                         1.0 1.0 1.0 1.0 1.0;
                         1.0 1.0 1.0 1.0 1.0;
                         1.0 1.0 1.0 1.0 1.0;
                        -1.5 -1.5 -1.5 -1.5 -1.5];

julia> for i in eachindex(resx) # Test the x-component
           @test resx[i] ≈ whatXshouldbe[i] atol=1e-10
       end

julia> whatYshouldbe = [-7.5 5.0 5.0 5.0 -7.5;
                        -7.5 5.0 5.0 5.0 -7.5;
                        -7.5 5.0 5.0 5.0 -7.5;
                        -7.5 5.0 5.0 5.0 -7.5;
                        -7.5 5.0 5.0 5.0 -7.5];

julia> for i in eachindex(resy) # Test the y-component
           @test resy[i] ≈ whatYshouldbe[i] atol=1e-10
       end

References

See also: Swalbe.∇²f!

source
Swalbe.∇²f!Method
∇²f!(output, f, γ)

Finite difference operator for a second derivative in two dimensions.

Computes the laplacian of an input f times a scalar γ and stores the result in output.

Mathematics

The laplacian operator in two dimensions can be written as

$\nabla^2 f = \frac{\partial^2 f}{\partial x^2} + \frac{\partial^2 f}{\partial y}.$

For the discretization of this operator we use a nine point stencil, such the neighbors as well as the diagonal elements. The concrete derivation can be found in the references below, we just show the final result

$\nabla^2 f = \frac{1}{6}\bigg[4(f_{i+1,j} + f_{i,j+1} + f_{i-1,j} + f_{i,j-1}) \newline \qquad\qquad +(f_{i+1,j+1} + f_{i-1,j+1} + f_{i-1,j-1} + f_{i+1,j-1}) \newline \qquad\qquad -20f_{i,j}\bigg] ,$

where we have used Julia conventions, downwards (left) is positive. The whole expression can be multiplied with a scalar γ if needed.

Examples

julia> using Swalbe, Test

julia> arg = reshape(collect(1.0:25),5,5)
5×5 Matrix{Float64}:
 1.0   6.0  11.0  16.0  21.0
 2.0   7.0  12.0  17.0  22.0
 3.0   8.0  13.0  18.0  23.0
 4.0   9.0  14.0  19.0  24.0
 5.0  10.0  15.0  20.0  25.0

julia> res = zeros(5,5); Swalbe.∇²f!(res, arg, -1.0)

julia> analytics = [-30.0 -5.0 -5.0 -5.0 20;
                    -25.0 0.0 0.0 0.0 25.0;
                    -25.0 0.0 0.0 0.0 25.0;
                    -25.0 0.0 0.0 0.0 25.0;
                    -20.0 5.0 5.0 5.0 30.0];

julia> for i in eachindex(analytics)
           @test analytics[i] ≈ res[i] atol=1e-10
       end

References

See also: Swalbe.∇f!

source
Swalbe.CuStateType
CuState

State data structure but for CUDA like memory.

Similar to CuState_thermal without the thermal fields.

Arguments

  • fout :: CuArray{T,N}: Output distribution function
  • ftemp :: CuArray{T,N}: Temporary distribution function, only used if sys.τ ≠ 1
  • feq :: CuArray{T,N}: Equilibrium distribution function
  • height :: CuArray{T}: Field that stores the scalar height values
  • velx :: CuMatrix{T}: Field that stores the x-component of the velocity vector
  • vely :: Matrix{T}: Field that stores the y-component of the velocity vector
  • vsq :: Matrix{T}: Field that stores the velocity squared, used in equilibrium!
  • pressure :: Matrix{T}: Pressure distribution computed using the filmpressure! function
  • Fx :: Matrix{T}: Total force acting on the fluid, x-component
  • Fy :: Matrix{T}: Total force acting on the fluid, y-component
  • slipx :: Matrix{T}: Friction force due to substrate slip, x-component
  • slipy :: Matrix{T}: Friction force due to substrate slip, y-component
  • h∇px :: Matrix{T}: Pressure gradient times the height, x-component
  • h∇py :: Matrix{T}: Pressure gradient times the height, y-component
  • dgrad :: Array{T,N}: Dummy allocation to store shifted arrays using circshift!
source
Swalbe.StateType
State{T, N}

Data structure for both macroscopic variables and distribution functions.

Arguments

  • fout :: Array{T,N}: Output distribution function
  • ftemp :: Array{T,N}: Temporary distribution function, only used if sys.τ ≠ 1
  • feq :: Array{T,N}: Equilibrium distribution function
  • height :: Matrix{T}: Field that stores the scalar height values
  • velx :: Matrix{T}: Field that stores the x-component of the velocity vector
  • vely :: Matrix{T}: Field that stores the y-component of the velocity vector
  • vsq :: Matrix{T}: Field that stores the velocity squared, used in equilibrium!
  • pressure :: Matrix{T}: Pressure distribution computed using the filmpressure! function
  • Fx :: Matrix{T}: Total force acting on the fluid, x-component
  • Fy :: Matrix{T}: Total force acting on the fluid, y-component
  • slipx :: Matrix{T}: Friction force due to substrate slip, x-component
  • slipy :: Matrix{T}: Friction force due to substrate slip, y-component
  • h∇px :: Matrix{T}: Pressure gradient times the height, x-component
  • h∇py :: Matrix{T}: Pressure gradient times the height, y-component
  • dgrad :: Array{T,N}: Dummy allocation to store shifted arrays using circshift!

See also: State_thermal{T, N}, CuState, State_1D{T, N}

source
Swalbe.State_1DType
State_1D{T, N}

Data structure that for a one dimensional simulation.

Arguments

  • fout :: Matrix{T}: Output distribution function
  • ftemp :: Matrix{T}: Temporary distribution function, only used if sys.τ ≠ 1
  • feq :: Matrix{T}: Equilibrium distribution function
  • height :: Vector{T}: Field that stores the scalar height values
  • vel :: Vector{T}: Field that stores the velocity
  • pressure :: Vector{T}: Pressure distribution computed using the filmpressure! function
  • F :: Vector{T}: Total force acting on the fluid
  • slip :: Vector{T}: Friction force due to substrate slip
  • h∇p :: Vector{T}: Pressure gradient times the height
  • dgrad :: Matrix{T}: Dummy allocation to store shifted arrays using circshift!
source
Swalbe.State_gamma_1DType
State_gamma_1D{T, N}

State_1D data structure with additional fields for surface tension γ and ∇γ.

Arguments

  • basestate :: State_1D{T}: Base data structure for one dimensional simulations
  • γ :: Vector{T}: Surface tension field
  • ∇γ :: Vector{T}: Surface tension gardient
source
Swalbe.State_thermalType
State_thermal{T, N}

Data structure that contains a State and fields for thermal fluctuations.

Arguments

  • basestate :: State{T,N}: State data structer
  • kbtx :: Matrix{T}: Force due to thermal fluctuations, x-component
  • kbty :: Matrix{T}: Force due to thermal fluctuations, y-component
source
Swalbe.State_thermal_1DType
State_thermal_1D{T, N}

State_1D data structure with additional fields for thermal fluctuations (noise).

Arguments

  • basestate :: State_1D{T}: Base data structure for one dimensional simulations
  • kbt :: Vector{T}: Surface tension field
source
Swalbe.SysConstType
SysConst{T}

Struct that contains the system size of a two dimensional system and the struct Taumucs.

Arguments

  • Lx :: Int: Length of the lattice sides in x-direction
  • Ly :: Int: Length of the lattice sides in y-direction
  • s :: Taumucs : Most of the run time constants

See also: SysConst_1D{T}, Taumucs{T}

source
Swalbe.SysConstWithBound_1DType
SysConstWithBound_1D{T}

Struct that contains all run time constants, e.g. lattice size, surface tension γ and so on.

Arguments

source
Swalbe.SysConst_1DType
SysConst_1D{T}

Struct that contains the system size of a one dimensional system and the struct Taumucs.

Arguments

  • L :: Int: Number of lattice points
  • s :: Taumucs: Most of the run time constants

See also: SysConst{T}, Taumucs{T}

source
Swalbe.TaumucsType
Taumucs{T}

Struct that contains most run time constants, e.g. surface tension γ, viscosity μ and so on.

Arguments

  • Tmax :: Int: Number of lattice Boltzmann time iterations
  • tdump :: Int: Dumping interval for e.g. data output
  • τ :: T: BGK relaxation rate
  • cₛ :: T: Lattice speed of sound, every physical velocity needs to be smaller than this!
  • μ :: T: Kinematic fluid viscosity
  • δ :: T: Slip length, defines how far the no-slip condition is interpolated into the substrate
  • kbt :: T: Thermal energy of the film, works with small values ≈ 10^(-7)
  • γ :: T: Surface tension
  • n :: Int: Greater exponent of the two used for the powerlaw in the disjoining pressure
  • m :: Int: Smaller exponent of the two used for the powerlaw in the disjoining pressure
  • hmin :: T: Height value at which the disjoining pressure functional vanishes
  • hcrit :: T: Numerical stabilizer for the disjoining pressure term
  • θ :: T: Contact angle in multiples of π
  • g :: T: gravitational acceleration, usually neglected in thin film simulations

See also: SysConst{T}, SysConst_1D{T}

source
Swalbe.SysMethod
Sys(sysc, T, kind)

Allocations of arrays used to run a one dimensional simulation.

Returns a State data structure based on kind the struct can be "simple", "thermal" or "gamma".

Arguments

  • sysc :: SysConst_1D: Stores the lattice size L
  • T <: Number: Optional numerical type, default is set to Float64
  • kind :: String: Optional, default is set to simple
source
Swalbe.SysMethod
Sys(sysc, device, exotic, T)

Mostly allocations of arrays used to run a simulation, but all within one function :)

Returns not a data structure such as state, but every array. Therefore it is somewhat outdated by now (2022).

Arguments

  • sysc :: SysConst: Needed for the lattice dimensions, Lx and Ly
  • device :: String: Use either CPU for computation of a CPU or GPU for computation on the GPU
  • exotic :: Bool: If true thermal fluctuations can be computed and saved to the fthermalx and fthermaly field
  • T <: Number: Numerical type, it is strongly suggested to use Float64
source
Swalbe.SysMethod
Sys(sysc, device; T, kind)

Allocations of arrays used to populate the State data structure.

Returns a State data structure based on sysc, either one dimensional or two dimensional.

Arguments

  • sysc :: SysConst: Needed for the lattice dimensions, Lx and Ly
  • device :: String: Use either CPU for computation of a CPU or GPU for computation on the GPU (GPU can only be used with a two dimensional system)
  • T <: Number: Numerical type, it is strongly suggested to use Float64
  • kind :: String: Indicator for different State's, default value is "simple" which creates a State data structure, valid options are ["simple", "thermal"]
source
Swalbe.obslist!Method
function obslist1D!(sys::SysConstWithBound; T=Float64, verbose=false)

Updates interior and border according to obs. See obslist().

source
Swalbe.obslist1DMethod

function obslist1D(obs) Returns a list containing the relevgant obstacle nodes constructed from interior, obsleft, obsright, obsup, obsdown, corneroutlu, corneroutld, corneroutru, corneroutrd, cornerru, cornerrd, cornerlu, cornerld

source
Swalbe.boxpatternMethod
boxpattern(θ, θ₀; center=(size(θ,1)÷2, size(θ,2)÷2), δₐ=1/36, side=20)

Defines a quadratic box around a center with side length side.

Arguments

  • θ::Array{undef,2}: Contact angle map
  • θ₀: Default contact angle θ
  • center::Tuple{Int, Int}: center of the box pattern, default value is (size(θ,1)÷2, size(θ,2)÷2)
  • δₐ::Float64: Contact angle contrast with the substrate, default is set to 1/36 ≈ 5 degrees difference
  • side::Int: Length of the sides, default is set to 20

Examples

julia> using Swalbe

julia> θ₀ = 1/9;

julia> pattern, polygon = Swalbe.boxpattern(ones(100,100), θ₀);

julia> polygon # Some cool thing we use to create the posize(θ,2)gones, a LazySet
LazySets.VPolygon{Float64, Vector{Float64}}([[40.0, 40.0], [60.0, 40.0], [60.0, 60.0], [40.0, 60.0]])

julia> pattern[50,50] == pattern[1,1] # In the center there is a different contact angle!
false

References

Really not much to say here, check out LazySets.jl.

source
Swalbe.ellipsepatternMethod
ellipsepattern(θ, θ₀; center=(size(θ,1)÷2, size(θ,2)÷2), δₐ=1/36, a=10, b=5)

Creates an ellipse shaped contact angle defect with contact angle mismatch δₐ.

Arguments

  • θ::Array{undef,2}: Contact angle map
  • θ₀: Default contact angle θ
  • center::Tuple{Int, Int}: center of the created pattern, default values is center = (size(θ,1)÷2, size(θ,2)÷2)
  • δₐ::Float64: contact angle mismatch between patch and rest of substrate, default is δₐ = 1/36 or 5 degrees
  • a::Int:: semimajor half ax of the ellipse, default value is a=10
  • b::Int:: semiminor half ax of the ellipse, default value is b=5

Examples

julia> using Swalbe, Test

julia> θ₀ = 1/9;

julia> θ, P = Swalbe.ellipsepattern(ones(100,100), θ₀); # per default the center is in the middle!

julia> @test θ[1,1] == θ₀
Test Passed

julia> @test θ[50,50] == θ₀ + 1/36 # The default increment, is about 5 degrees.
Test Passed

References

Nothing interesting here.

source
Swalbe.randinterface!Method
randinterface!(height, h₀, ϵ)

Creates a random height field with average height h₀ and displacement magnitude ϵ.

source
Swalbe.restart_from_heightMethod
restart_from_height(data)

Restarts a simulation from already generated height data.

Arguments

  • data::file: Some file with computed data
  • kind::String: File format, .bson or .jld2 are valid options
  • timestep::Int: The time step the height is read from file
  • size::Tuple{Int,Int}: x and y limits of the computational domain
julia> using Swalbe, FileIO, Test

julia> h1 = rand(10,10); h2 = rand(10,10);

julia> f = Dict() # For storage
Dict{Any, Any}()

julia> f["h_1"] = vec(h1); f["h_2"] = vec(h2);

julia> save("file.jld2", f)

julia> h = Swalbe.restart_from_height("file.jld2", timestep=1, size=(10,10));

julia> @test all(h .== h1)
Test Passed

julia> rm("file.jld2")
source
Swalbe.sinewave1D!Method
sinewave1D!(height, h₀, n, ϵ)

Creates a sine wave like height field with n full waves, average height h₀ and displacement magnitude ϵ.

source
Swalbe.singledropletMethod
singledroplet(T, size(θ,1), size(θ,2), radius, θ, center, device=false)

Generates a fluid configuration of a single droplet in the shape of spherical cap with contact angle θ, sphere radius radius and centered at center.

Arguments

  • height::Array{undef, 2}: numerical formate, either Float64 or Float32
  • radius::AbstractFloat: radius of the underlying sphere from which the spherical cap is cut off
  • θ::AbstractFloat: contact angle in multiples of π
  • center::Tuple{Int,Int}: x and y coordinates of the center of the droplet

Examples

julia> using Swalbe, Test

julia> rad = 50; θ = 1/3;

julia> height = Swalbe.singledroplet(ones(100,100), rad, θ, (50,50));

julia> @test maximum(height) == rad * (1 - cospi(θ)) # Simple geometry
Test Passed

julia> argmax(height) # Which is constistent with the center!
CartesianIndex(50, 50)

References

See also:

source
Swalbe.trianglepatternMethod
trianglepattern(θ, θ₀; center=(size(θ,1)÷2, size(θ,2)÷2), δₐ=1/36, side=60)

Generates an equilateral triangle centered around center with contact angle contrast δₐ and side length side.

Arguments

  • θ::Array{undef,2}: Contact angle map
  • θ₀: Default contact angle θ
  • center::Tuple(Int, Int): position of the center of the triangle
  • δₐ::Float64: contact angle contrast with the rest of the substrate
  • side::Int: length of the sides of the equilateral triangle

Examples

julia> using Swalbe

julia> θ, P = Swalbe.trianglepattern(ones(50,50), 1/9, side=20) # Returns a polygon and the contact angle field
([0.1111111111111111 0.1111111111111111 … 0.1111111111111111 0.1111111111111111; 0.1111111111111111 0.1111111111111111 … 0.1111111111111111 0.1111111111111111; … ; 0.1111111111111111 0.1111111111111111 … 0.1111111111111111 0.1111111111111111; 0.1111111111111111 0.1111111111111111 … 0.1111111111111111 0.1111111111111111], LazySets.VPolygon{Float64, Vector{Float64}}([[15.0, 19.226497308103742], [35.0, 19.226497308103742], [25.0, 36.547005383792516]]))

julia> P
LazySets.VPolygon{Float64, Vector{Float64}}([[15.0, 19.226497308103742], [35.0, 19.226497308103742], [25.0, 36.547005383792516]])

julia> θ[25,25]
0.1388888888888889

References

See also:

source
Swalbe.two_dropletsMethod
two_droplets(sys)

Generates a fluid configuration of a two droplets in the shape of spherical cap with contact angles θ₁, θ₂, sphere radius r₁, r₂ and centers at center.

This is work in progress, therefore so far it is only available for the lower dimension model.

Arguments

  • r₁::Float64: radius of the first sphere from which the cap is cut of
  • r₂::Float64: radius of the second sphere from which the cap is cut of
  • θ₁::Float64: contact angle of the first cap in multiples of π
  • θ₂::Float64: contact angle of the second cap in multiples of π
  • center::Tuple{Int,Int}: x coordinates of the centers of the droplets

Examples

julia> using Swalbe, Test

julia> rad = 45; θ = 1/4; sys = Swalbe.SysConst_1D(L=200, param=Swalbe.Taumucs());

julia> height = Swalbe.two_droplets(sys, r₁=rad, r₂=rad, θ₁=θ, θ₂=θ);

julia> @test maximum(height) ≈ rad * (1 - cospi(θ)) atol=0.01 # Simple geometry
Test Passed

References

See also:

source
Swalbe.fluid_dry!Method
fluid_dry!(fluid, dummy, height, t; hthresh = 0.055)

Tracks the location of the thin film as a boolean field.

source
Swalbe.snapshot!Method
snapshot!(snap, field, t; dumping)

Makes a copy of the current state of an input array in and saves the vectorized values as a column to out.

Function that fills a preallocated array out with a time series of system snap shots of e.g. the height field h.

Arguments

  • snap :: Array{Number,2}: Array that stores the snap shots as columns
  • field :: Array{Number,2}: Input argument, e.g. $h(\mathbf{x},t)$
  • t :: Int: The current time step
  • dumping :: Int: Sampling frequency

Examples

julia> using Swalbe, Test

julia> h1 = reshape(collect(1:25),5,5); h2 = reshape(collect(5:5:125),5,5);

julia> snapshot = zeros(2, 25);

julia> Swalbe.snapshot!(snapshot,h1,10,dumping=10)

julia> Swalbe.snapshot!(snapshot,h2,20,dumping=10)

julia> @test all(h1 .== reshape(snapshot[1,:],5,5))
Test Passed
julia> @test all(h2 .== reshape(snapshot[2,:],5,5))
Test Passed

References

See also: The scripts folder

source
Swalbe.surfacearea!Method
surfacearea!(area_lv, red_energy, height, θ, ∇hx, ∇hy, dgrad, surface,)

Measures the surface area of the liquid vapor interface and the reduced surface energy.

Arguments

  • area_lv :: Vector{Float64}: array to store the computed liquid vapor area
  • red_energy :: Vector{Float64}: array the stores the computed reduced surface energy
  • height :: Matrix{Float64}: current height configuration
  • θ :: Matrix{Float64}: contact angle field distribution
  • ∇hx :: Matrix{Float64}: height gradient x-component
  • ∇hy :: Matrix{Float64}: height gradient y-component
  • dgrad :: Array{Float64,3}: dummy array to store derivatives
  • surface :: Matrix{Float64}: array that computes locally the liquid vapor surface area
  • t :: Int: current time step
  • hthresh :: Float64: height threshold below which the substrate is considered dry
source
Swalbe.t0Method
t0(;hᵦ=0.07, γ=0.01, μ=1/6, θ=1/6)

Computes a characteristic time scale for an spinodally dewetting film.

Arguments

  • hᵦ :: Float64: height at which the disjoining pressure vanishes
  • γ :: Float64: surface tension value
  • μ :: Float64: kinematic viscosity, same as dynamic for ρ=1
  • θ :: Float64: highest contact angle given as radiant, e.g. θ=π/9 for 20 degrees

Mathematics

The charateristic time scale $t_0$ is set using the surface tension as well as the disjoining pressure $\Pi(h)$ as

$t_0 = \frac{3}{\gamma h_0^3 q_0^4}, \quad q_0 = \frac{\Pi'(h_0)}{2\gamma},$

where $\Pi'(h_0) = \frac{\partial\Pi}{\partial h}\bigg|_{h_0}$ is the derivative of the disjoining pressure with respect to some characteristic height.

References

source
Swalbe.wetted!Method
wetted!(area_size, drop_pos, maxheight, height, t; hthresh = 0.055)

Measures the wetted area and maximal height of the film at time step t.

source
Swalbe.∇f_simple!Method
∇f_simple!(outputx, outputy, f, dgrad)

Simple gradient calculation for the differential surface area.

source
Swalbe.run_flatMethod
run_flat(Sys::SysConst, device::String)

Performs a simulation of an flat interface without forces.

Arguments

  • verbos :: Bool: Enables consol output
  • T :: AbstractFloat: Precision of output, default Float64

Theory

Nothing at all should happen. As the initial state is falt and no force is applied the fluid has no way to flow. The equality $h(\mathbf{x},0) = h(\mathbf{x},\infty),$ should be satisfied for arbitrary many time steps.

Examples

julia> using Swalbe, Test

julia> sys = Swalbe.SysConst(Lx=100, Ly=100, Tmax=5000);

julia> h = Swalbe.run_flat(sys, "CPU", verbos=false);

julia> @test all(h.height .== 1.0) # Check if all height values are identical to 1.0 (initial condition)
Test passed
source
Swalbe.run_gammaMethod
run_(sys::Swalbe.SysConst_1D,
	 gamma::Vector;
	 r₁=115,
	 r₂=115, 
	 θ₀=1/9,  
	 verbos=true, 
	 dump = 100, 
	 fluid=zeros(sys.param.Tmax÷dump, sys.L))

Lattice Boltzmann simulation of coalescing droplets.

source
Swalbe.run_randomMethod
run_random(sys::SysConst, device::String)

Simulation of an random undulated interface

Arguments

  • h₀ :: Float : Average initial height
  • ϵ :: Float : Amplitude of the flucutation
  • verbos :: Bool: Enables consol output
  • T :: AbstractFloat: Precision of output, default Float64

Theory

Initial randomly perturbed fluid surface. Unstable wavemodes should grow while wavemodes larger q₀ should be damped out. Measuring this is on the TODO list.

Examples

julia> using Swalbe, Test

julia> sys = Swalbe.SysConst(Lx=100, Ly=100, Tmax=5000);

julia> Swalbe.randinterface!(height, h₀, ϵ)

julia> h = Swalbe.run_random(sys, "CPU", h₀=10, ϵ=0.1, verbos=false);
source
Swalbe.run_rayleightaylorMethod
run_rayleightaylor(sys::SysConst, device::String)

Simulation of an random undulated interface and a gravitanional pull.

Arguments

  • kx :: Int : wavemode in x-direction, kx=18 -> 18 sine waves fitting into the domain
  • ky :: Int : wavemode in y-direction
  • h₀ :: Float : Average initial height
  • ϵ :: Float : Amplitude of the flucutation
  • verbos :: Bool: Enables consol output
  • T :: AbstractFloat: Precision of output, default Float64

Theory

Initial randomly perturbed fluid surface hanging from a substrate. Here we have an interplay between gravity and surface tension. The critical wavemode can be computed according to $q_0 =$ Measuring this is on the TODO list.

Examples

julia> using Swalbe, Test

julia> sys = Swalbe.SysConst(Lx=100, Ly=100, Tmax=5000);

julia> Swalbe.randinterface!(height, h₀, ϵ)

julia> h = Swalbe.run_random(sys, "CPU", h₀=10, ϵ=0.1, verbos=false);
source
Swalbe.time_loopMethod
time_loop(sys, state)

Time stepping procedure for the lattice Boltzmann state state given parameters sys

source