Architecture¶
The backbone of the climlab architecture are the Process
and TimeDependentProcess
classes.
All model components in climlab are instances of Process
.
Conceptually, a Process
object represents any
physical mechanism that can be described in terms of one or more state variables and
processes that modify those variables.
As all relevant procedures and events that can be modelled with climlab are expressed in Processes, they build the basic structure of the package.
For example, if you want to model the incoming solar radiation on Earth,
climlab implements it as a Process, namely in the Diagnostic Process _Insolation
(or one of its specific daughter classes).
Another example: the emitted energy of a surface can be computed through the
Boltzmann
class which is also a climlab
Process and implements the Stefan Boltzmann Law for a grey body.
Like that, all events and procedures that climlab can model are organized in Processes.
Note
The implementation of a whole model, for example an Energy Balance Model (EBM
), is also an instance of the Process
class in climlab.
For more information about models, see the climlab Models chapter.
A Process
object contains
a subprocess dictionary, which itself can contain an arbitraily complex
collection of other Process
objects.
A Process that represents a whole model will typically have some subprocesses which represent specific physical components of the model, for example the albedo or the insolation component. More details about subprocesses can be found below.
The state variables of a Process are always defined on a Domain which itself is based on Axes or a single Axis. The following section will give a basic introduction about their role in the package, their dependencies and their implementation.
Process¶
A Process is an instance of the class Process
. Most processes are time-dependent and therefore an instance of the daughter class TimeDependentProcess
.
Basic Dictionaries¶
A climlab.Process object has several iterable dictionaries (dict
) of named, gridded variables [1]:
process.state
contains the process’ state variables, which are usually time-dependent and which are major quantities that identify the condition and status of the process. This can be the (surface) temperature of a model for instance.
process.input
contains boundary conditions and other gridded quantities independent of the process. This dictionary is often set by a parent process.
process.param
contains parameter of the Process or model. Basically, this is the same as
process.input
but with scalar entries.
process.tendencies
is an iterable dictionary of time tendencies \((d/dt)\) for each state variable defined in
process.state
.Note
A non TimeDependentProcess (but instance of
Process
) does not have this dictionary.
process.diagnostics
contains any quantity derived from the current state. In an Energy Balance Model this dictionary can have entries like
'ASR'
,'OLR'
,'icelat'
,'net_radiation'
,'albedo'
or'insolation'
.
process.subprocess
holds subprocesses of the process. More about subprocesses is described below.
The process is fully described by contents of state, input and param dictionaries. tendencies and diagnostics are always computable from the current state.
Subprocesses¶
Subprocesses are representing and modeling certain components of the parent process. A model consists of many subprocesses which are usually defined on the same state variables, domains and axes as the parent process, at least partially.
- Example:
The subprocess tree of an EBM may look like this:
model_EBM #<head process> diffusion #<subprocess> LW #<subprocess> albedo #<subprocess> iceline #<sub-subprocess> cold_albedo #<sub-subprocess> warm_albedo #<sub-subprocess> insolation #<subprocess>
It can be seen that subprocesses can have subprocesses themselves, like albedo
in this case.
A subprocess
is similar to its parent process
an instance of the Process
class. That means a subprocess
has dictionaries and attributes with the same names as its parent process
. Not necessary all will be the same or have the same entries, but a subprocess
has at least the basic dictionaries and attributes created during initialization of the Process
instance.
Every subprocess should work independently of its parent process given appropriate input.
- Example:
Investigating an individual process (possibly with its own subprocesses) isolated from its parent can be done through:
newproc = climlab.process_like(procname.subprocess['subprocname']) newproc.compute()Thereby anything in the input dictionary of
'subprocname'
will remain fixed.
Process Integration over time¶
A TimeDependentProcess
can be integrated over time to see how the state variables and other diagnostic variables vary in time.
Time Dependency of a State Variable¶
For a state variable \(S\) which is dependendet on processes \(P_A\), \(P_B\), … the time dependency can be written as
When the state variable \(S\) is discretized over time like
the state tendency can be calculated through
and the new state of \(S\) after one timestep \(\Delta t\) is then:
Therefore, the new state of \(S\) is calculated by multiplying the process tendencies of \(S\) with the timestep and adding them up to the previous state of \(S\).
Time Dependency of an Energy Budget¶
The time dependency of an EBM energy budget is very similar to the above noted equations, just differing in a heat capacity factor \(C\). The state variable is temperature \(T\) in this case, which is altered by subprocesses \(SP_A\), \(SP_B\), …
Therefore, the new state of \(T\) after one timestep \(\Delta t\) can be written as:
The integration procedure is implemented in multiple nested function calls. The top functions for model integration are explained here, for details about computation of subprocess tendencies see Classification of Subprocess Types below.
compute()
is a method that computes tendencies \(d/dt\) for all state variablesit returns a dictionary of tendencies for all state variables
Temperature tendencies are \(\frac{SP_A(T)}{C}\), \(\frac{SP_B(T)}{C}\), … in this case, which are summed up like:
\[\textrm{tendencies}(T) = \frac{SP_A(T)}{C} + \frac{SP_B(T)}{C} + ...\]the keys for this dictionary are the same as keys of state dictionary
As temperature \(T\) is the only state variable in this energy budget, the tendencies dictionary also just has the one key, representing the state variable \(T\).
the tendency dictionary holds the total tendencies for each state including all subprocesses
In case subprocess \(SP_A\) itself has subprocesses, their \(T\) tendencies get included in tendency computation by
compute()
.the method only computes \(d/dt\) but does not apply changes (which is done by
step_forward()
)therefore, the method is relatively independent of the numerical scheme
method will update variables in
proc.diagnostic
dictionary. Therefore, it will also gather all diagnostics from the subprocesses
step_forward()
updates the state variablesit calls
compute()
to get current tendenciesthe method multiplies state tendencies with the timestep and adds them up to the state variables
integrate_years()
etc will automate time-stepping by calling thestep_forward
method multiple times. It also does the computation of time-average diagnostics.integrate_converge()
callsintegrate_years()
as long as the state variables keep changing over time.
- Example:
Integration of a climlab EBM model over time can look like this:
import climlab model = climlab.EBM() # integrate the model for one year model.integrate_years(1)
Classification of Subprocess Types¶
Processes can be classified in types: explicit, implicit, diagnostic and adjustment. This makes sense as subprocesses may have different impact on state variable tendencies (diagnostic processes don’t have a direct influence for instance) or the way their tendencies are computed differ (explixit and implicit).
Therefore, the compute()
method handles them seperately as well as in specific order. It calls private _compute()
methods that are specified in daugther classes of Process
namely DiagnosticProcess
,
EnergyBudget
(which are explicit processes) or
ImplicitProcess
.
The description of compute()
reveals the details how the different process types are handeled:
The function first computes all diagnostic processes. They don’t produce any tendencies directly but they may effect the other processes (such as change in solar distribution). Subsequently, all tendencies and diagnostics for all explicit processes are computed.
Tendencies due to implicit and adjustment processes need to be calculated from a state that is already adjusted after explicit alteration. For that reason the explicit tendencies are applied to the states temporarily. Now all tendencies from implicit processes are calculated by matrix inversions and similar to the explicit tendencies, the implicit ones are applied to the states temporarily. Subsequently, all instantaneous adjustments are computed.
Then the changes that were made to the states from explicit and implicit processes are removed again as this
compute()
function is supposed to calculate only tendencies and not apply them to the states.Finally, all calculated tendencies from all processes are collected for each state, summed up and stored in the dictionary
self.tendencies
, which is an attribute of the time-dependent-process object, for which thecompute()
method has been called.
Domain¶
A Domain defines an area or spatial base for a climlab Process
object. It consists of axes which
are Axis
objects that define the dimensions of the Domain.
In a Domain the heat capacity of grid points, bounds or cells/boxes is specified.
There are daughter classes Atmosphere
and Ocean
of the private _Domain
class implemented which themselves have daughter classes SlabAtmosphere
and SlabOcean
.
Every Process
needs to be defined on a Domain. If none is given during initialization but latitude lat
is specified, a default Domain is created.
Several methods are implemented that create Domains with special specifications. These are
Axis¶
An Axis
is an object where information of a _Domain
’s spacial dimension are specified.
These include the type of the axis, the number of points, location of points and bounds on the spatial dimension, magnitude of bounds differences delta as well as their unit.
The axes of a _Domain
are stored in the dictionary axes, so they can be accessed through dom.axes
if dom
is an instance of _Domain
.
Accessibility¶
For convenience with interactive work, each subprocess 'name'
should be accessible
as proc.subprocess.name
as well as the regular way through the subprocess dictionary proc.subprocess['name']
. Note that proc
is an instance of the Process
class here.
- Example:
import climlab model = climlab.EBM() # quick access longwave_subp = model.subprocess.LW # regular path longwave_subp = model.subprocess['LW']
climlab will remain (as much as possible) agnostic about the data formats. Variables within the dictionaries will behave as numpy.ndarray
objects.
Grid information and other domain details are accessible as attributes of each process.
These attributes are lat
, lat_bounds
, lon
, lon_bounds
, lev
, lev_bounds
, depth
and depth_bounds
.
- Example:
the latitude points of a process object that is describing an EBM model
import climlab model = climlab.EBM() # quick access lat_points = model.lat # regular path lat_points = model.domains['Ts'].axes['lat'].points
Shortcuts like proc.lat
will work where these are unambiguous, which means there is only a single axis of that type in the process.
Many variables will be accessible as process attributes proc.name
. This restricts to unique field names in the above dictionaries.
Warning
There may be other dictionaries that do have name conflicts: e.g. dictionary of tendencies proc.tendencies
, with same keys as proc.state
.
These will not be accessible as proc.name
, but will be accessible as proc.dict_name.name
(as well as regular dictionary interface proc.dict_name['name']
).