Skip to content

Commit cde7299

Browse files
committed
merge master and start fixing stuff
2 parents 633b6d9 + 0c05f4d commit cde7299

16 files changed

+224
-61
lines changed

.github/dependabot.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
2+
version: 2
3+
updates:
4+
- package-ecosystem: "github-actions"
5+
directory: "/" # Location of package manifests
6+
schedule:
7+
interval: "monthly"

.github/workflows/ci.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ jobs:
1111
fail-fast: false
1212
matrix:
1313
version:
14-
- '1.9'
15-
- '1'
14+
- '1.10'
1615
os:
1716
- ubuntu-latest
1817
- macOS-latest
@@ -32,7 +31,7 @@ jobs:
3231
with:
3332
version: ${{ matrix.version }}
3433
arch: ${{ matrix.arch }}
35-
- uses: actions/cache@v1
34+
- uses: actions/cache@v4
3635
env:
3736
cache-name: cache-artifacts
3837
with:
@@ -47,7 +46,7 @@ jobs:
4746
env:
4847
JULIA_NUM_THREADS: ${{ matrix.nthreads }}
4948
- uses: julia-actions/julia-processcoverage@v1
50-
- uses: codecov/codecov-action@v1
49+
- uses: codecov/codecov-action@v5
5150
with:
5251
file: lcov.info
5352
docs:

Project.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
name = "WaterLily"
22
uuid = "ed894a53-35f9-47f1-b17f-85db9237eebd"
33
authors = ["Gabriel Weymouth <gabriel.weymouth@gmail.com>"]
4-
version = "1.2"
4+
version = "1.3.0"
55

66
[deps]
77
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
88
EllipsisNotation = "da5c29d0-fa7d-589e-88eb-ea29b0a81949"
99
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
1010
KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c"
1111
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
12+
LoggingExtras = "e6f89c97-d47a-5376-807f-9c37f3926c36"
1213
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
1314
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
1415
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
@@ -20,22 +21,25 @@ AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e"
2021
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
2122
ReadVTK = "dc215faf-f008-4882-a9f7-a79a826fadc3"
2223
WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192"
24+
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
2325

2426
[extensions]
2527
WaterLilyAMDGPUExt = "AMDGPU"
2628
WaterLilyCUDAExt = "CUDA"
2729
WaterLilyReadVTKExt = "ReadVTK"
2830
WaterLilyWriteVTKExt = "WriteVTK"
31+
WaterLilyPlotsExt = "Plots"
2932

3033
[compat]
3134
DocStringExtensions = "0.9"
3235
EllipsisNotation = "1.8"
3336
ForwardDiff = "^0.10.18"
3437
KernelAbstractions = "0.9.1"
38+
LoggingExtras = "1.1"
3539
Reexport = "^1.2.2"
3640
Requires = "1.3"
3741
StaticArrays = "^1.1.0"
38-
julia = "1.6"
42+
julia = "1.9"
3943

4044
[extras]
4145
AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e"

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
[![Examples](https://img.shields.io/badge/view-examples-blue.svg)](https://github.com/WaterLily-jl/WaterLily-Examples/)
55
[![CI](https://github.com/WaterLily-jl/WaterLily.jl/workflows/CI/badge.svg?branch=master&event=push)](https://github.com/WaterLily-jl/WaterLily.jl/actions)
66
[![codecov](https://codecov.io/gh/WaterLily-jl/WaterLily.jl/branch/master/graph/badge.svg?token=8XYFWKOUFN)](https://codecov.io/gh/WaterLily-jl/WaterLily.jl)
7+
[![DOI](https://zenodo.org/badge/DOI/10.48550/arXiv.2407.16032.svg)](https://doi.org/10.48550/arXiv.2407.16032)
78

89
![Julia flow](assets/julia.gif)
910

1011
## Overview
1112

1213
**WaterLily.jl** is a simple and fast fluid simulator written in pure Julia. This project is supported by awesome libraries developed within the Julia scientific community, and it aims to accelerate and enhance fluid simulations. Watch the JuliaCon2024 talk here:
1314

14-
[![JuliaCon2024 still and link](assets/JuliaCon2024.png)](https://www.youtube.com/live/qru5G5Yp77E?t=29074s)
15+
[![JuliaCon2024 still and link](assets/JuliaCon2024.png)](https://www.youtube.com/watch?v=FwMh2rq9kOU)
1516

1617
If you have used WaterLily for research, please __cite us__! The [2024 paper](https://physics.paperswithcode.com/paper/waterlily-jl-a-differentiable-and-backend) describes the main features of the solver and provides benchmarking, validation, and profiling results.
1718
```
@@ -42,14 +43,14 @@ function circle(n,m;Re=100,U=1)
4243
radius, center = m/8, m/2-1
4344
sdf(x,t) = sum(abs2, x .- center) - radius
4445

45-
Simulation((n,m), # domain size
46+
Simulation((n,m), # domain size
4647
(U,0), # domain velocity (& velocity scale)
4748
2radius; # length scale
48-
ν=U*2radius/Re, # fluid viscosity
49+
ν=U*2radius/Re, # fluid viscosity
4950
body=AutoBody(sdf)) # geometry
5051
end
5152
```
52-
The circle geometry is defined using a [signed distance function](https://en.wikipedia.org/wiki/Signed_distance_function#Applications). The `AutoBody` function uses [automatic differentiation](https://github.com/JuliaDiff/) to infer the other geometric parameters of the body automatically. Replace the circle's distance function with any other, and now you have the flow around something else... such as a [donut](https://github.com/WaterLily-jl/WaterLily-Examples/blob/main/examples/ThreeD_Donut.jl) or the [Julia logo](https://github.com/WaterLily-jl/WaterLily-Examples/blob/main/examples/TwoD_Julia.jl). For more complex geometries, [ParametricBodies.jl](https://github.com/WaterLily-jl/ParametricBodies.jl) defines a `body` using any parametric curve, such as a spline. See that repo (and the video above) for examples.
53+
The circle geometry is defined using a [signed distance function](https://en.wikipedia.org/wiki/Signed_distance_function#Applications). The `AutoBody` function uses [automatic differentiation](https://github.com/JuliaDiff/) to infer the other geometric parameters of the body automatically. Replace the circle's distance function with any other, and now you have the flow around something else... such as a [donut](https://github.com/WaterLily-jl/WaterLily-Examples/blob/main/examples/ThreeD_Donut.jl) or the [Julia logo](https://github.com/WaterLily-jl/WaterLily-Examples/blob/main/examples/TwoD_Julia.jl). For more complex geometries, [ParametricBodies.jl](https://github.com/WaterLily-jl/ParametricBodies.jl) defines a `body` using any parametric curve, such as a spline. See that repo (and the video above) for examples.
5354

5455
The code block above return a `Simulation` with the parameters we've defined. Now we can initialize a simulation (first line) and step it forward in time (second line)
5556
```julia
@@ -66,9 +67,9 @@ contourf(u') # transpose the array for the plot
6667
```
6768
![Initial velocity field](assets/u0.png)
6869

69-
As you can see, the velocity within the circle is zero, the velocity far from the circle is one, and there are accelerated and decelerated regions around the circle. The `sim_step!` has only taken a single time step, and this initial flow around our circle looks similar to the potential flow because the viscous boundary layer has not separated yet.
70+
As you can see, the velocity within the circle is zero, the velocity far from the circle is one, and there are accelerated and decelerated regions around the circle. The `sim_step!` has only taken a single time step, and this initial flow around our circle looks similar to the potential flow because the viscous boundary layer has not separated yet.
7071

71-
A set of [flow metric functions](https://github.com/WaterLily-jl/WaterLily.jl/blob/master/src/Metrics.jl) have been implemented, and we can use them to measure the simulation. The following code block defines a function to step the simulation to time `t` and then use the `pressure_force` metric to measure the force on the immersed body. The function is applied over a time range, and the forces are plotted.
72+
A set of [flow metric functions](https://github.com/WaterLily-jl/WaterLily.jl/blob/master/src/Metrics.jl) have been implemented, and we can use them to measure the simulation. The following code block defines a function to step the simulation to time `t` and then use the `pressure_force` metric to measure the force on the immersed body. The function is applied over a time range, and the forces are plotted.
7273
```Julia
7374
function get_forces!(sim,t)
7475
sim_step!(sim,t,remeasure=false)
@@ -81,7 +82,7 @@ time = 1:0.1:50 # time scale is sim.L/sim.U
8182
forces = [get_forces!(circ,t) for t in time];
8283

8384
#Plot it
84-
plot(time,[first.(forces), last.(forces)],
85+
plot(time,[first.(forces), last.(forces)],
8586
labels=permutedims(["drag","lift"]),
8687
xlabel="tU/L",
8788
ylabel="Pressure force coefficients")
@@ -109,7 +110,7 @@ As you can see, WaterLily correctly predicts that the flow is unsteady, with an
109110

110111
WaterLily uses [KernelAbstractions.jl](https://github.com/JuliaGPU/KernelAbstractions.jl) to multi-thread on CPU and run on GPU backends. The implementation method and speed-up are documented in the [2024 paper](https://physics.paperswithcode.com/paper/waterlily-jl-a-differentiable-and-backend), with costs as low as 1.44 nano-seconds measured per degree of freedom and time step!
111112

112-
Note that multi-threading requires _starting_ Julia with the `--threads` argument, see [the multi-threading section](https://docs.julialang.org/en/v1/manual/multi-threading/) of the manual. If you are running Julia with multiple threads, KernelAbstractions will detect this and multi-thread the loops automatically.
113+
Note that multi-threading requires _starting_ Julia with the `--threads` argument, see [the multi-threading section](https://docs.julialang.org/en/v1/manual/multi-threading/) of the manual. If you are running Julia with multiple threads, KernelAbstractions will detect this and multi-thread the loops automatically.
113114

114115
Running on a GPU requires initializing the `Simulation` memory on the GPU, and care needs to be taken to move the data back to the CPU for visualization. As an example, let's compare a **3D** GPU simulation of a sphere to the **2D** multi-threaded CPU circle defined above
115116
```Julia
@@ -123,7 +124,7 @@ function sphere(n,m;Re=100,U=1,T=Float64,mem=Array)
123124
mem) # memory type
124125
end
125126

126-
@assert CUDA.functional() # is your CUDA GPU working??
127+
@assert CUDA.functional() # is your CUDA GPU working??
127128
GPUsim = sphere(3*2^5,2^6;T=Float32,mem=CuArray); # 3D GPU sim!
128129
println(length(GPUsim.flow.u)) # 1.3M degrees-of freedom!
129130
sim_step!(GPUsim) # compile GPU code & run one step

assets/JuliaCon2024.png

-13.6 KB
Loading

ext/WaterLilyAMDGPUExt.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ Asserts AMDGPU is functional when loading this extension.
1616
"""
1717
__init__() = @assert AMDGPU.functional()
1818

19-
AMDGPU.allowscalar(false) # disallow scalar operations on GPU
20-
2119
"""
2220
L₂(a)
2321

ext/WaterLilyCUDAExt.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ Asserts CUDA is functional when loading this extension.
1616
"""
1717
__init__() = @assert CUDA.functional()
1818

19-
CUDA.allowscalar(false) # disallow scalar operations on GPU
20-
2119
"""
2220
L₂(a)
2321

ext/WaterLilyPlotsExt.jl

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
module WaterLilyPlotsExt
2+
3+
if isdefined(Base, :get_extension)
4+
using Plots; gr()
5+
else
6+
using ..Plots; gr()
7+
end
8+
9+
using WaterLily
10+
import WaterLily: flood,addbody,body_plot!,sim_gif!,plot_logger
11+
12+
"""
13+
flood(f)
14+
15+
Plot a filled contour plot of the 2D array `f`. The keyword arguments are passed to `Plots.contourf`.
16+
"""
17+
function flood(f::Array;shift=(0.,0.),cfill=:RdBu_11,clims=(),levels=10,kv...)
18+
if length(clims)==2
19+
@assert clims[1]<clims[2]
20+
@. f=min(clims[2],max(clims[1],f))
21+
else
22+
clims = (minimum(f),maximum(f))
23+
end
24+
Plots.contourf(axes(f,1).+shift[1],axes(f,2).+shift[2],f'|>Array,
25+
linewidth=0, levels=levels, color=cfill, clims = clims,
26+
aspect_ratio=:equal; kv...)
27+
end
28+
29+
addbody(x,y;c=:black) = Plots.plot!(Shape(x,y), c=c, legend=false)
30+
function body_plot!(sim;levels=[0],lines=:black,R=inside(sim.flow.p))
31+
WaterLily.measure_sdf!(sim.flow.σ,sim.body,WaterLily.time(sim))
32+
contour!(sim.flow.σ[R]'|>Array;levels,lines)
33+
end
34+
35+
"""
36+
sim_gif!(sim;duration=1,step=0.1,verbose=true,R=inside(sim.flow.p),
37+
remeasure=false,plotbody=false,kv...)
38+
39+
Make a gif of the simulation `sim` for `duration` seconds with `step` time steps. The keyword arguments are passed to `flood` and `body_plot!`.
40+
"""
41+
function sim_gif!(sim;duration=1,step=0.1,verbose=true,R=inside(sim.flow.p),
42+
remeasure=false,plotbody=false,kv...)
43+
t₀ = round(WaterLily.sim_time(sim))
44+
@time @gif for tᵢ in range(t₀,t₀+duration;step)
45+
WaterLily.sim_step!(sim,tᵢ;remeasure)
46+
@WaterLily.inside sim.flow.σ[I] = WaterLily.curl(3,I,sim.flow.u)*sim.L/sim.U
47+
flood(sim.flow.σ[R]|>Array; kv...)
48+
plotbody && body_plot!(sim)
49+
verbose && println("tU/L=",round(tᵢ,digits=4),
50+
", Δt=",round(sim.flow.Δt[end],digits=3))
51+
end
52+
end
53+
54+
55+
"""
56+
plot_logger(fname="WaterLily.log")
57+
58+
Plot the residuals and MG iterations from the log file `fname`.
59+
"""
60+
function plot_logger(fname="WaterLily.log")
61+
predictor = []; corrector = []
62+
open(ifelse(fname[end-3:end]==".log",fname[1:end-4],fname)*".log","r") do f
63+
readline(f) # read first line and dump it
64+
which = "p"
65+
while ! eof(f)
66+
s = split(readline(f) , ",")
67+
which = s[1] != "" ? s[1] : which
68+
push!(which == "p" ? predictor : corrector,parse.(Float64,s[2:end]))
69+
end
70+
end
71+
predictor = reduce(hcat,predictor)
72+
corrector = reduce(hcat,corrector)
73+
74+
# get index of all time steps
75+
idx = findall(==(0.0),@views(predictor[1,:]))
76+
# plot inital L∞ and L₂ norms of residuals for the predictor step
77+
p1=plot(1:length(idx),predictor[2,idx],color=:1,ls=:dash,alpha=0.8,label="predictor initial r∞",yaxis=:log,size=(800,400),dpi=600,
78+
xlabel="Time step",ylabel="L∞-norm",title="Residuals",ylims=(1e-8,1e0),xlims=(0,length(idx)))
79+
p2=plot(1:length(idx),predictor[2,idx],color=:1,ls=:dash,alpha=0.8,label="predictor initial r₂",yaxis=:log,size=(800,400),dpi=600,
80+
xlabel="Time step",ylabel="L₂-norm",title="Residuals",ylims=(1e-8,1e0),xlims=(0,length(idx)))
81+
# plot final L∞ and L₂norms of residuals for the predictor
82+
plot!(p1,1:length(idx),vcat(predictor[2,idx[2:end].-1],predictor[2,end]),color=:1,lw=2,alpha=0.8,label="predictor r∞")
83+
plot!(p2,1:length(idx),vcat(predictor[3,idx[2:end].-1],predictor[3,end]),color=:1,lw=2,alpha=0.8,label="predictor r₂")
84+
# plot the MG iterations for the predictor
85+
p3=plot(1:length(idx),clamp.(vcat(predictor[1,idx[2:end].-1],predictor[1,end]),1/2,32),lw=2,alpha=0.8,label="predictor",size=(800,400),dpi=600,
86+
xlabel="Time step",ylabel="Iterations",title="MG Iterations",ylims=(1/2,32),xlims=(0,length(idx)),yaxis=:log2)
87+
yticks!([1/2,1,2,4,8,16,32],["0","1","2","4","8","16","32"])
88+
# get index of all time steps
89+
idx = findall(==(0.0),@views(corrector[1,:]))
90+
# plot inital L∞ and L₂ norms of residuals for the corrector step
91+
plot!(p1,1:length(idx),corrector[2,idx],color=:2,ls=:dash,alpha=0.8,label="corrector initial r∞",yaxis=:log)
92+
plot!(p2,1:length(idx),corrector[3,idx],color=:2,ls=:dash,alpha=0.8,label="corrector initial r₂",yaxis=:log)
93+
# plot final L∞ and L₂ norms of residuals for the corrector step
94+
plot!(p1,1:length(idx),vcat(corrector[2,idx[2:end].-1],corrector[2,end]),color=:2,lw=2,alpha=0.8,label="corrector r∞")
95+
plot!(p2,1:length(idx),vcat(corrector[3,idx[2:end].-1],corrector[3,end]),color=:2,lw=2,alpha=0.8,label="corrector r₂")
96+
# plot MG iterations of the corrector
97+
plot!(p3,1:length(idx),clamp.(vcat(corrector[1,idx[2:end].-1],corrector[1,end]),1/2,32),lw=2,alpha=0.8,label="corrector")
98+
# plot all together
99+
plot(p1,p2,p3,layout=@layout [a b c])
100+
end
101+
102+
end # module

ext/WaterLilyReadVTKExt.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ The time step is also updated to match the time step of the vtk file, such that
2525
the simulation can be restarted and continued.
2626
"""
2727
squeeze(a::AbstractArray) = dropdims(a,dims=tuple(findall(size(a).==1)...))
28-
function restart_sim!(a::Simulation;fname::String="WaterLily.pvd",attrib=default_attrib())
28+
function restart_sim!(a::AbstractSimulation;fname::String="WaterLily.pvd",attrib=default_attrib())
2929
vtk = VTKFile(PVDFile(fname).vtk_filenames[end])
3030
extent = filter(!iszero,ReadVTK.get_whole_extent(vtk)[2:2:end]);
3131
# check dimensions match

ext/WaterLilyWriteVTKExt.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,16 @@ Returns a `Dict` containing the `name` and `bound_function` for the default attr
4545
The `name` is used as the key in the `vtk` file and the `bound_function` generates the data
4646
to put in the file. With this approach, any variable can be save to the vtk file.
4747
"""
48-
_velocity(a::Simulation) = a.flow.u |> Array;
49-
_pressure(a::Simulation) = a.flow.p |> Array;
48+
_velocity(a::AbstractSimulation) = a.flow.u |> Array;
49+
_pressure(a::AbstractSimulation) = a.flow.p |> Array;
5050
default_attrib() = Dict("Velocity"=>_velocity, "Pressure"=>_pressure)
5151
"""
52-
write!(w::VTKWriter, sim::Simulation)
52+
write!(w::VTKWriter, sim<:AbstractSimulation)
5353
5454
Write the simulation data at time `sim_time(sim)` to a `vti` file and add the file path
5555
to the collection file.
5656
"""
57-
function write!(w::VTKWriter,a::Simulation)
57+
function write!(w::VTKWriter,a::AbstractSimulation)
5858
k = w.count[1]; N=size(a.flow.p)
5959
vtk = vtk_grid(w.dir_name*@sprintf("/%s_%06i", w.fname, k), [1:n for n in N]...)
6060
for (name,func) in w.output_attrib

0 commit comments

Comments
 (0)