Skip to content

Commit caeb924

Browse files
authored
Merge pull request #4 from climate-machine/lorenz96
Add Lorenz 96 model to examples
2 parents 2edeb00 + b697470 commit caeb924

File tree

15 files changed

+1347
-7
lines changed

15 files changed

+1347
-7
lines changed

Manifest.toml

Lines changed: 347 additions & 6 deletions
Large diffs are not rendered by default.

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ authors = ["Simon Byrne <[email protected]>"]
44
version = "0.1.0"
55

66
[deps]
7+
DifferentialEquations = "0c46a032-eb83-5123-abaf-570d42b7fbaa"
78
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
89
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
910
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
11+
NPZ = "15e1cf62-19b3-5cfa-8e77-841668bca605"
1012
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
1113
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

examples/L96m/L96m.jl

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
using Parameters # lets you have defaults for fields
2+
3+
@with_kw mutable struct L96m
4+
"""
5+
Lorenz '96 multiscale
6+
7+
Parameters:
8+
- `K` : number of slow variables
9+
- `J` : number of fast variables per slow variable
10+
- `hx` : coupling term (in equations for slow variables)
11+
- `hy` : coupling term (in equations for fast variables)
12+
- `F` : forcing term for slow variables
13+
- `eps` : scale separation term
14+
15+
Other:
16+
- `G` : functional closure for slow variables (usually a GPR-closure)
17+
"""
18+
K::Int = 9
19+
J::Int = 8
20+
hx::Float64 = -0.8
21+
hy::Float64 = 1
22+
F::Float64 = 10
23+
eps::Float64 = 2^(-7)
24+
G = nothing
25+
end
26+
27+
function full(rhs::Array{<:Real,1}, z::Array{<:Real,1}, p::L96m, t)
28+
"""
29+
Compute full RHS of the Lorenz '96 multiscale system.
30+
The convention is that the first K variables are slow, while the rest K*J
31+
variables are fast.
32+
33+
Input:
34+
- `z` : vector of size (K + K*J)
35+
- `p` : parameters
36+
- `t` : time (not used here since L96m is autonomous)
37+
38+
Output:
39+
- `rhs` : RHS computed at `z`
40+
41+
"""
42+
43+
K = p.K
44+
J = p.J
45+
x = @view(z[1:K])
46+
y = @view(z[K+1:end])
47+
48+
### slow variables subsystem ###
49+
# compute Yk averages
50+
Yk = compute_Yk(p, z)
51+
52+
# three boundary cases
53+
rhs[1] = -x[K] * (x[K-1] - x[2]) - x[1]
54+
rhs[2] = -x[1] * (x[K] - x[3]) - x[2]
55+
rhs[K] = -x[K-1] * (x[K-2] - x[1]) - x[K]
56+
57+
# general case
58+
rhs[3:K-1] = -x[2:K-2] .* (x[1:K-3] - x[4:K]) - x[3:K-1]
59+
60+
# add forcing
61+
rhs[1:K] .+= p.F
62+
63+
# add coupling w/ fast variables via averages
64+
rhs[1:K] .+= p.hx * Yk
65+
66+
### fast variables subsystem ###
67+
# three boundary cases
68+
rhs[K+1] = -y[2] * (y[3] - y[end] ) - y[1]
69+
rhs[end-1] = -y[end] * (y[1] - y[end-2]) - y[end-1]
70+
rhs[end] = -y[1] * (y[2] - y[end-1]) - y[end]
71+
72+
# general case
73+
rhs[K+2:end-2] = -y[3:end-1] .* (y[4:end] - y[1:end-3]) - y[2:end-2]
74+
75+
# add coupling w/ slow variables
76+
for k in 1:K
77+
rhs[K+1 + (k-1)*J : K + k*J] .+= p.hy * x[k]
78+
end
79+
80+
# divide by epsilon
81+
rhs[K+1:end] ./= p.eps
82+
83+
return rhs
84+
end
85+
86+
function balanced(rhs::Array{<:Real,1}, x::Array{<:Real,1}, p::L96m, t)
87+
"""
88+
Compute balanced RHS of the Lorenz '96 multiscale system; i.e. only slow
89+
variables with the linear closure.
90+
Both `rhs` and `x` are vectors of size p.K.
91+
92+
Input:
93+
- `x` : vector of size K
94+
- `p` : parameters
95+
- `t` : time (not used here since L96m is autonomous)
96+
97+
Output:
98+
- `rhs` : balanced RHS computed at `x`
99+
100+
"""
101+
102+
K = p.K
103+
104+
# three boundary cases
105+
rhs[1] = -x[K] * (x[K-1] - x[2]) - (1 - p.hx*p.hy) * x[1]
106+
rhs[2] = -x[1] * (x[K] - x[3]) - (1 - p.hx*p.hy) * x[2]
107+
rhs[K] = -x[K-1] * (x[K-2] - x[1]) - (1 - p.hx*p.hy) * x[K]
108+
109+
# general case
110+
rhs[3:K-1] = -x[2:K-2] .* (x[1:K-3] - x[4:K]) - (1 - p.hx*p.hy) * x[3:K-1]
111+
112+
# add forcing
113+
rhs .+= p.F
114+
115+
return rhs
116+
end
117+
118+
function compute_Yk(p::L96m, z::Array{<:Real,1})
119+
"""
120+
Reshape a vector of y_{j,k} into a matrix, then sum along one dim and divide
121+
by J to get averages
122+
"""
123+
return dropdims(
124+
sum( reshape(z[p.K+1:end], p.J, p.K), dims = 1 ),
125+
dims = 1
126+
) / p.J
127+
end
128+
129+

examples/L96m/main.jl

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#!/usr/bin/julia --
2+
3+
import DifferentialEquations
4+
import PyPlot
5+
const DE = DifferentialEquations
6+
const plt = PyPlot
7+
include("L96m.jl")
8+
9+
10+
################################################################################
11+
# constants section ############################################################
12+
################################################################################
13+
const RPAD = 42
14+
const LPAD_INTEGER = 7
15+
const LPAD_FLOAT = 13
16+
const SOLVER = DE.Tsit5()
17+
18+
const T = 4 # integration time
19+
const T_compile = 1e-10 # force JIT compilation
20+
const T_conv = 100 # converging integration time
21+
#const T_learn = 15 # time to gather training data for GP
22+
#const T_hist = 10000 # time to gather histogram statistics
23+
24+
# integrator parameters
25+
const dtmax = 1e-3 # maximum step size
26+
#const tau = 1e-3 # maximum step size for histogram statistics
27+
#const dt_conv = 0.01 # maximum step size for converging to attractor
28+
const reltol = 1e-3 # relative tolerance
29+
const abstol = 1e-6 # absolute tolerance
30+
31+
const k = 1 # index of the slow variable to save etc.
32+
const j = 1 # index of the fast variable to save/plot etc.
33+
34+
const hx = -0.8
35+
36+
################################################################################
37+
# IC section ###################################################################
38+
################################################################################
39+
l96 = L96m(hx = hx, J = 8)
40+
41+
z00 = Array{Float64}(undef, l96.K + l96.K * l96.J)
42+
43+
# method 1
44+
z00[1:l96.K] .= rand(l96.K) * 15 .- 5
45+
for k_ in 1:l96.K
46+
z00[l96.K+1 + (k_-1)*l96.J : l96.K + k_*l96.J] .= z00[k_]
47+
end
48+
49+
################################################################################
50+
# main section #################################################################
51+
################################################################################
52+
53+
# force compilation of functions used in numerical integration
54+
print(rpad("(JIT compilation)", RPAD))
55+
elapsed_jit = @elapsed begin
56+
pb_jit = DE.ODEProblem(full, z00, (0.0, T_compile), l96)
57+
DE.solve(pb_jit, SOLVER, reltol = reltol, abstol = abstol, dtmax = dtmax)
58+
pb_jit = DE.ODEProblem(balanced, z00[1:l96.K], (0.0, T_compile), l96)
59+
DE.solve(pb_jit, SOLVER, reltol = reltol, abstol = abstol, dtmax = dtmax)
60+
end
61+
println(" " ^ (LPAD_INTEGER + 6),
62+
"\t\telapsed:", lpad(elapsed_jit, LPAD_FLOAT))
63+
64+
# full L96m integration (converging to attractor)
65+
print(rpad("(full, converging)", RPAD))
66+
elapsed_conv = @elapsed begin
67+
pb_conv = DE.ODEProblem(full, z00, (0.0, T_conv), l96)
68+
sol_conv = DE.solve(pb_conv,
69+
SOLVER,
70+
reltol = reltol,
71+
abstol = abstol,
72+
dtmax = dtmax
73+
)
74+
end
75+
println("steps:", lpad(length(sol_conv.t), LPAD_INTEGER),
76+
"\t\telapsed:", lpad(elapsed_conv, LPAD_FLOAT))
77+
z0 = sol_conv[:,end]
78+
79+
# full L96m integration
80+
print(rpad("(full)", RPAD))
81+
elapsed_dns = @elapsed begin
82+
pb_dns = DE.ODEProblem(full, z0, (0.0, T), l96)
83+
sol_dns = DE.solve(pb_dns,
84+
SOLVER,
85+
reltol = reltol,
86+
abstol = abstol,
87+
dtmax = dtmax
88+
)
89+
end
90+
println("steps:", lpad(length(sol_dns.t), LPAD_INTEGER),
91+
"\t\telapsed:", lpad(elapsed_dns, LPAD_FLOAT))
92+
93+
# balanced L96m integration
94+
print(rpad("(balanced)", RPAD))
95+
elapsed_bal = @elapsed begin
96+
pb_bal = DE.ODEProblem(balanced, z0[1:l96.K], (0.0, T), l96)
97+
sol_bal = DE.solve(pb_bal,
98+
SOLVER,
99+
reltol = reltol,
100+
abstol = abstol,
101+
dtmax = dtmax
102+
)
103+
end
104+
println("steps:", lpad(length(sol_bal.t), LPAD_INTEGER),
105+
"\t\telapsed:", lpad(elapsed_bal, LPAD_FLOAT))
106+
107+
################################################################################
108+
# plot section #################################################################
109+
################################################################################
110+
# plot DNS
111+
plt.plot(sol_dns.t, sol_dns[k,:], label = "DNS")
112+
plt.plot(sol_dns.t, sol_dns[l96.K + (k-1)*l96.J + j,:],
113+
lw = 0.6, alpha = 0.6, color="gray")
114+
115+
# plot balanced
116+
plt.plot(sol_bal.t, sol_bal[k,:], label = "balanced")
117+
118+
plt.legend()
119+
plt.show()
120+
121+

0 commit comments

Comments
 (0)