1
1
using Parameters # lets you have defaults for fields
2
2
3
+ """
4
+ Lorenz '96 multiscale
5
+
6
+ Parameters:
7
+ - `K` : number of slow variables
8
+ - `J` : number of fast variables per slow variable
9
+ - `hx` : coupling term (in equations for slow variables)
10
+ - `hy` : coupling term (in equations for fast variables)
11
+ - `F` : forcing term for slow variables
12
+ - `eps` : scale separation constant
13
+
14
+ Other:
15
+ - `G` : functional closure for slow variables (usually a GPR-closure)
16
+ """
3
17
@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
18
K:: Int = 9
19
19
J:: Int = 8
20
20
hx:: Float64 = - 0.8
21
- hy:: Float64 = 1
22
- F:: Float64 = 10
21
+ hy:: Float64 = 1.0
22
+ F:: Float64 = 10.0
23
23
eps:: Float64 = 2 ^ (- 7 )
24
24
G = nothing
25
25
end
26
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)
27
+ """
28
+ Compute full RHS of the Lorenz '96 multiscale system.
29
+ The convention is that the first K variables are slow, while the rest K*J
30
+ variables are fast.
37
31
38
- Output:
39
- - `rhs` : RHS computed at `z`
32
+ Input:
33
+ - `z` : vector of size (K + K*J)
34
+ - `p` : parameters (L96m struct)
35
+ - `t` : time (not used here since L96m is autonomous)
40
36
41
- """
37
+ Output:
38
+ - `rhs` : RHS computed at `z`
42
39
40
+ """
41
+ function full (rhs:: Array{<:Real,1} , z:: Array{<:Real,1} , p:: L96m , t)
43
42
K = p. K
44
43
J = p. J
45
44
x = @view (z[1 : K])
@@ -83,22 +82,21 @@ function full(rhs::Array{<:Real,1}, z::Array{<:Real,1}, p::L96m, t)
83
82
return rhs
84
83
end
85
84
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)
85
+ """
86
+ Compute balanced RHS of the Lorenz '96 multiscale system; i.e. only slow
87
+ variables with the linear closure.
88
+ Both `rhs` and `x` are vectors of size p.K.
96
89
97
- Output:
98
- - `rhs` : balanced RHS computed at `x`
90
+ Input:
91
+ - `x` : vector of size K
92
+ - `p` : parameters (L96m struct)
93
+ - `t` : time (not used here since L96m is autonomous)
99
94
100
- """
95
+ Output:
96
+ - `rhs` : balanced RHS computed at `x`
101
97
98
+ """
99
+ function balanced (rhs:: Array{<:Real,1} , x:: Array{<:Real,1} , p:: L96m , t)
102
100
K = p. K
103
101
104
102
# three boundary cases
@@ -115,15 +113,119 @@ function balanced(rhs::Array{<:Real,1}, x::Array{<:Real,1}, p::L96m, t)
115
113
return rhs
116
114
end
117
115
116
+ """
117
+ Compute slow-variable closed RHS of the Lorenz '96 Multiscale system;
118
+ i.e. only slow variables with some closure instead of Yk.
119
+ Closure is taken from p.G.
120
+ Both `rhs` and `x` are vectors of size p.K.
121
+
122
+ Input:
123
+ - `x` : vector of size K
124
+ - `p` : parameters (L96m struct)
125
+ - `t` : time (not used here since L96m is autonomous)
126
+
127
+ Output:
128
+ - `rhs` : regressed RHS computed at `x`
129
+
130
+ """
131
+ function regressed (rhs:: Array{<:Real,1} , x:: Array{<:Real,1} , p:: L96m , t)
132
+ K = p. K
133
+
134
+ # three boundary cases
135
+ rhs[1 ] = - x[K] * (x[K- 1 ] - x[2 ]) - x[1 ]
136
+ rhs[2 ] = - x[1 ] * (x[K] - x[3 ]) - x[2 ]
137
+ rhs[K] = - x[K- 1 ] * (x[K- 2 ] - x[1 ]) - x[K]
138
+
139
+ # general case
140
+ rhs[3 : K- 1 ] = - x[2 : K- 2 ] .* (x[1 : K- 3 ] - x[4 : K]) - x[3 : K- 1 ]
141
+
142
+ # add forcing
143
+ rhs .+ = p. F
144
+
145
+ # add closure
146
+ rhs .+ = p. hx * p. G (x)
147
+
148
+ return rhs
149
+ end
150
+
151
+ """
152
+ Reshape a vector of y_{j,k} into a matrix, then sum along one dim and divide
153
+ by J to get averages
154
+ """
118
155
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
156
return dropdims (
124
157
sum ( reshape (z[p. K+ 1 : end ], p. J, p. K), dims = 1 ),
125
158
dims = 1
126
159
) / p. J
127
160
end
128
161
162
+ """
163
+ Set the closure `p.G` to a linear one with slope `slope`.
164
+ If unspecified, slope is equal to `p.hy`.
165
+ """
166
+ function set_G0 (p:: L96m ; slope = nothing )
167
+ if (slope == nothing ) || (! isa (slope, Real))
168
+ slope = p. hy
169
+ end
170
+ p. G = x -> slope * x
171
+ end
172
+
173
+ """
174
+ Wrapper for set_G0(p::L96m; slope = nothing).
175
+ """
176
+ function set_G0 (p:: L96m , slope:: Real )
177
+ set_G0 (p, slope = slope)
178
+ end
179
+
180
+ """
181
+ Gather (xk, Yk) pairs that are further used to train a GP regressor
182
+
183
+ Input:
184
+ - `p` : parameters (L96m struct)
185
+ - `sol` : time series of a solution; time steps are in the 2nd dimension
186
+ (usually, just `sol` output from a time-stepper)
187
+
188
+ Output:
189
+ - `pairs` : a 2-d array of size (N, 2) containing (xk, Yk) pairs, where N is
190
+ the 2nd dimension in `sol` (number of time steps)
191
+
192
+ """
193
+ function gather_pairs (p:: L96m , sol)
194
+ N = size (sol, 2 )
195
+ pairs = Array {Float64, 2} (undef, p. K * N, 2 )
196
+ for n in 1 : N
197
+ pairs[p. K * (n- 1 ) + 1 : p. K * n, 1 ] = sol[1 : p. K, n]
198
+ pairs[p. K * (n- 1 ) + 1 : p. K * n, 2 ] = compute_Yk (p, sol[:,n])
199
+ end
200
+ return pairs
201
+ end
202
+
203
+ """
204
+ Returns a randomly initialized array that can be used as an IC to ODE solver
205
+
206
+ The returned array is of size `p.K + p.K * p.J`.
207
+ The first `p.K` variables are slow and are drawn randomly ~ U[-5; 10]; each of
208
+ the fast variables corresponding to the same slow variable is set to the value
209
+ of that slow variable.
210
+
211
+ For example, if K == 2 and J = 3, the returned array will be
212
+ [ rand1, rand2, rand1, rand1, rand1, rand2, rand2, rand2 ]
213
+
214
+ Input:
215
+ - `p` : parameters (L96m struct)
216
+
217
+ Output:
218
+ - `z00` : array of size `p.K + p.K * p.J` with random values
219
+ """
220
+ function random_init (p:: L96m )
221
+ z00 = Array {Float64} (undef, p. K + p. K * p. J)
222
+
223
+ z00[1 : p. K] .= rand (p. K) * 15 .- 5
224
+ for k_ in 1 : p. K
225
+ z00[p. K+ 1 + (k_- 1 )* p. J : p. K + k_* p. J] .= z00[k_]
226
+ end
227
+
228
+ return z00
229
+ end
230
+
129
231
0 commit comments