Skip to content

Commit e9e3ece

Browse files
committed
Without SciML
1 parent 1747581 commit e9e3ece

File tree

1 file changed

+88
-58
lines changed

1 file changed

+88
-58
lines changed

src/simulations/Simulations.jl

Lines changed: 88 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ module Simulations
22
using ClimaTimeSteppers
33
using ClimaComms
44
import ClimaComms: context, device
5-
using SciMLBase
65
using Dates
76
import ClimaUtilities.TimeManager: ITime, date
87
import ClimaDiagnostics
@@ -12,50 +11,67 @@ using ClimaLand.ModelSetup
1211
include("initial_conditions.jl")
1312

1413
"""
15-
LandSimulation{
16-
M <: ClimaLand.AbstractModel,
17-
T <: ClimaTimeSteppers.DistributedODEAlgorithm,
18-
UC,
19-
DI,
20-
RC,
21-
CA <: SciMLBase.CallbackSet,
22-
I <: SciMLBase.DEIntegrator,
23-
}
24-
25-
the ClimaLand LandSimulation struct, which specifies
14+
LandSimulation
15+
16+
The ClimaLand LandSimulation struct, which specifies
2617
- the discrete set of equations to solve (defined by the `model`);
27-
- the timestepping algorithm;
18+
- the timestepping algorithm (in `timestepper.algo`);
2819
- user callbacks (passed as a tuple) to be executed at specific times in the simulations;
2920
- the diagnostics to output (optional).
3021
3122
User callbacks are optional: examples currently include callbacks that estimate the time
3223
to solution and SYPD of the simulation as it runs, checkpoint the state, or check the solution
3324
for NaNs. Others can be added here.
3425
35-
Diagnostics are implemented as callbacks, and are also optional.
36-
However, a default is provided. `diagnostics` is expected to be a
37-
list of `ClimaDiagnostics.ScheduledDiagnostics`.
26+
`diagnostics` are provided as a list of `ClimaDiagnostics.ScheduledDiagnostics`,
27+
with default specified by `default_diagnostics`.
28+
29+
The private field `_required_callbacks` consists of callbacks that are required
30+
for the simulation to run correctly. Currently, this includes the callbacks
31+
which update the atmospheric forcing and update the LAI using prescribed data.
3832
39-
Finally, the private field _required_callbacks consists of callbacks that are required for the
40-
simulation to run correctly. Currently, this includes the callbacks which update the atmospheric
41-
forcing and update the LAI using prescribed data.
33+
Quick tips
34+
==========
35+
36+
1. Accessing the state
37+
```julia
38+
sim.u
39+
```
40+
2. Accessing the current time
41+
```julia
42+
sim.t
43+
```
44+
3. Accessing the current date
45+
```julia
46+
import ClimaUtilities.TimeManager: date
47+
date(sim.t)
48+
```
49+
4. Providing a specific new monthly diagnostics
50+
```julia
51+
diagnostic = ClimaLand.Diagnostics.monthly_average.(["lhf", "shf"])
52+
```
4253
"""
43-
struct LandSimulation{
54+
mutable struct LandSimulation{
55+
STATE,
56+
CACHE,
57+
TIME,
58+
TUP_TIME,
4459
M <: ClimaLand.AbstractModel,
45-
T <: ClimaTimeSteppers.DistributedODEAlgorithm,
46-
UC,
60+
T,
4761
DI,
4862
RC,
49-
CA <: SciMLBase.CallbackSet,
50-
I <: SciMLBase.DEIntegrator,
63+
UC,
5164
}
65+
u::STATE
66+
p::CACHE
67+
t::TIME
68+
dt::TIME
69+
tspan::TUP_TIME
5270
model::M
5371
timestepper::T
54-
user_callbacks::UC
55-
diagnostics::DI
72+
diagnostic_handler::DI
5673
required_callbacks::RC
57-
callbacks::CA
58-
_integrator::I
74+
user_callbacks::UC
5975
end
6076

6177
function LandSimulation(
@@ -128,53 +144,43 @@ function LandSimulation(
128144
jac_prototype = ClimaLand.FieldMatrixWithSolver(Y),
129145
Wfact = jacobian!,
130146
)
131-
T_imp! = SciMLBase.ODEFunction(imp_tendency!; jac_kwargs...)
147+
T_imp! = (; f = imp_tendency!, jac_kwargs...)
132148
end
133149

134150
# Create SciML ODE Problem
135-
problem = SciMLBase.ODEProblem(
136-
ClimaTimeSteppers.ClimaODEFunction(
151+
func = ClimaTimeSteppers.ClimaODEFunction(
137152
T_exp! = exp_tendency!,
138153
T_imp! = T_imp!,
139154
dss! = ClimaLand.dss!,
140-
),
141-
Y,
142-
(t0, tf),
143-
p,
144-
)
155+
)
145156

146157
# Required callbacks
147158
updateat = [promote(t0:(ITime(3600 * 3)):tf...)...]
148159
drivers = ClimaLand.get_drivers(model)
149160
updatefunc = ClimaLand.make_update_drivers(drivers)
150161
driver_cb = ClimaLand.DriverUpdateCallback(updateat, updatefunc)
151-
required_callbacks = (driver_cb,) # TBD: can we update each step?
162+
_required_callbacks = (driver_cb,) # TBD: can we update each step?
152163

153164
diagnostics = isnothing(diagnostics) ? () : diagnostics
154165
diagnostic_handler =
155166
ClimaDiagnostics.DiagnosticsHandler(diagnostics, Y, p, t0; dt = Δt)
156-
diag_cb = ClimaDiagnostics.DiagnosticsCallback(diagnostic_handler)
157-
158167

159-
# Collect all callbacks
160-
callbacks =
161-
SciMLBase.CallbackSet(user_callbacks..., required_callbacks..., diag_cb)
162-
163-
_integrator = SciMLBase.init(
164-
problem,
165-
timestepper;
166-
dt = Δt,
167-
callback = callbacks,
168-
adaptive = false,
169-
)
168+
algo = timestepper
169+
# u0 is used as prototype
170+
prob = (; u0 = Y, f = func)
171+
timestepper_cache = ClimaTimeSteppers.init_cache(prob, algo)
172+
isnothing(func.cache!) || func.cache!(Y, p, t0)
170173
return LandSimulation(
174+
Y,
175+
p,
176+
t0,
177+
Δt,
178+
(t0, tf),
171179
model,
172-
timestepper,
180+
(; algo, func, cache = timestepper_cache),
181+
diagnostic_handler,
182+
_required_callbacks,
173183
user_callbacks,
174-
diagnostics,
175-
required_callbacks,
176-
callbacks,
177-
_integrator,
178184
)
179185
end
180186

@@ -185,7 +191,29 @@ Advances the land simulation `landsim` forward in time by one step,
185191
updating `landsim` in place.
186192
"""
187193
function step!(landsim::LandSimulation)
188-
SciMLBase.step!(landsim._integrator)
194+
landsim.t += landsim.dt
195+
196+
ClimaTimeSteppers.step_u!(landsim, landsim.timestepper.cache)
197+
for callback in landsim._required_callbacks
198+
callback.condition(landsim.u, landsim.t, landsim) && callback.affect!(landsim)
199+
end
200+
for callback in landsim.user_callbacks
201+
callback.condition(landsim.u, landsim.t, landsim) && callback.affect!(landsim)
202+
end
203+
ClimaDiagnostics.orchestrate_diagnostics(landsim, landsim.diagnostic_handler)
204+
end
205+
206+
# Compatibility with SciML
207+
function Base.getproperty(landsim::LandSimulation, symbol::Symbol)
208+
if symbol === :alg
209+
return landsim.timestepper.algo
210+
elseif symbol === :step
211+
return landsim.t / landsim.dt
212+
elseif symbol === :sol
213+
return (; prob = (; f = landsim.timestepper.func, tspan = landsim.tspan))
214+
else
215+
return Base.getfield(landsim, symbol)
216+
end
189217
end
190218

191219
"""
@@ -195,7 +223,9 @@ Advances the land simulation `landsim` forward from the initial to final time,
195223
updating `landsim` in place.
196224
"""
197225
function solve!(landsim::LandSimulation)
198-
SciMLBase.solve!(landsim._integrator)
226+
while landsim.t < last(landsim.tspan)
227+
step!(landsim)
228+
end
199229
end
200230

201231

@@ -232,7 +262,7 @@ function Base.show(io::IO, landsim::LandSimulation)
232262
io,
233263
"$(model_type) Simulation\n",
234264
"├── Running on: $(device_type)\n",
235-
"└── Current date: $(date(landsim._integrator.t))\n",
265+
"└── Current date: $(date(landsim.t))\n",
236266
)
237267
end
238268
end#module

0 commit comments

Comments
 (0)