Skip to content

Commit 971e688

Browse files
reykboerneroameye
andauthored
bugfix: frozen_system and docs update (#210)
* update install instructions in docs * fix frozen_system bug * bump MTK compat in test/Project.toml * format * replace mtkcompile with structural_simplify again * streamline RateSystem example * refine docs --------- Co-authored-by: Orjan Ameye <[email protected]>
1 parent 662819b commit 971e688

File tree

12 files changed

+162
-98
lines changed

12 files changed

+162
-98
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name = "CriticalTransitions"
22
uuid = "251e6cd3-3112-48a5-99dd-66efcfd18334"
33
authors = ["Reyk Börner, Orjan Ameye, Ryan Deeley, Raphael Römer", "George Datseris"]
44
repo = "https://github.com/juliadynamics/CriticalTransitions.jl.git"
5-
version = "0.7.0"
5+
version = "0.7.1"
66

77
[deps]
88
ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9"

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,20 @@
1212
A Julia package for the numerical investigation of **noise- and rate-induced transitions in dynamical systems**.
1313

1414
Building on [DynamicalSystems.jl](https://juliadynamics.github.io/DynamicalSystems.jl/stable/) and [DifferentialEquations.jl](https://diffeq.sciml.ai/stable/), this package aims to provide a toolbox for dynamical systems under time-dependent forcing, with a focus on tipping phenomena and metastability.
15+
16+
## Installation
17+
`CriticalTransitions` is a registered Julia package.
18+
19+
```console
20+
julia> ] add CriticalTransitions
21+
```
22+
23+
or
24+
25+
```julia
26+
using Pkg; Pkg.add("CriticalTransitions")
27+
```
28+
1529
## Usage
1630
See [package documentation](https://juliadynamics.github.io/CriticalTransitions.jl/stable/).
1731

docs/pages.jl

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22
pages = [
33
"Home" => "index.md",
44
"Getting started" => "quickstart.md",
5-
"Tutorial" => "examples/tutorial.md",
65
"Examples" => Any[
7-
"Defining stochastic systems" => "examples/stochastic-dynamics.md",
8-
"Large deviations: Maier-Stein system" => "examples/gMAM_Maierstein.md",
9-
"Simple gMAM: Kerr Parametric Oscillator" => "examples/sgMAM_KPO.md",
10-
"Transition Path Theory: Finite element method" => "examples/transition_path_theory_double_well.md",
11-
"Minimal action method: Optimal Control problem" => "examples/OC_mam.md",
12-
"Constructing rate system" => "examples/RateSystem.md",
6+
"Tutorial" => "examples/tutorial.md",
7+
"Defining a system" => Any[
8+
"Stochastic system" => "examples/stochastic-dynamics.md",
9+
"Nonautonomous system" => "examples/RateSystem.md",],
10+
"System analysis" => Any[
11+
"Large deviations: Maier-Stein system" => "examples/gMAM_Maierstein.md",
12+
"Simple gMAM: Kerr Parametric Oscillator" => "examples/sgMAM_KPO.md",
13+
"Minimal action method: Optimal Control problem" => "examples/OC_mam.md",
14+
"Transition path theory: Finite element method" => "examples/transition_path_theory_double_well.md",],
1315
],
1416
"Manual" => Any[
1517
"Define your system" => "man/system_construction.md",
116 KB
Loading

docs/src/examples/RateSystem.md

Lines changed: 98 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,149 @@
11
# Example: Defining a `RateSystem`
22

3+
## Basic concept
4+
35
Consider an autonomous deterministic dynamical system `ds` (e.g. a `CoupledODEs`) of which
46
you want to ramp one parameter, i.e. change the parameter's value over time.
57

6-
Using the `RateSystem` type, you can easily achieve this in a two-step process:
7-
1) Specify a forcing profile `p(t)` over an interval ``t\in I``. This profile, stored as a `ForcingProfile` type,
8-
describes the shape of the parameter ramping you would like to consider.
9-
2) Apply this parametric forcing to a given autonomous dynamical system by constructing a `RateSystem`.
10-
This yields a non-autonomous dynamical system in which the parameters are explicitly time-dependent.
8+
Using the `RateSystem` type, you can easily achieve this in two steps:
9+
1. Specify a [`ForcingProfile`](@ref) that describes the shape of the parameter ramping `p(t)` over an interval ``t\in I``
10+
2. Apply this parametric forcing to the system `ds` by constructing a [`RateSystem`](@ref), i.e. a nonautonomous system in which the parameters are explicitly time-dependent.
11+
12+
![Schematic explaining RateSystem construction](../assets/ratesystem_scheme.png)
1113

1214
You can rescale the forcing profile in system time units by specifying the start time and
13-
duration of the forcing change. Then:
14-
- for times `t < forcing_start_time`, the system is autonomous, with parameters given by the underlying autonomous system
15-
- for `forcing_start_time < t < forcing_start_time + forcing_duration`, the system is non-autonomous with the parameter change given by the `ForcingProfile`
16-
- for `t > forcing_start_time + forcing_duration`, the system is again autonomous, with parameters fixed at their values attained at the end of the forcing interval (i.e. `t = forcing_start_time + forcing_duration`).
15+
duration of the forcing change. Then, for
16+
- `t < forcing_start_time`
17+
- the system is autonomous, with parameters given by the underlying autonomous system
18+
- `forcing_start_time < t < forcing_start_time + forcing_duration`
19+
- the system is non-autonomous with the parameter change given by the `ForcingProfile`, scaled in magnitude by `forcing_scale`
20+
- `t > forcing_start_time + forcing_duration`
21+
- the system is again autonomous, with parameters fixed at their values attained at the end of the forcing interval (i.e. `t = forcing_start_time + forcing_duration`).
22+
1723
This setting is widely used and convenient for studying rate-dependent tipping.
1824

1925
## Example
2026

21-
Let us explore a simple prototypical example.
22-
23-
We consider the following one-dimensional autonomous system with one attractor, given by
27+
As a simple prototypical example, let's consider the following one-dimensional autonomous system with one stable fixed point, given by
2428
the ordinary differential equation:
2529
```math
2630
\begin{aligned}
2731
\dot{x} &= (x+p)^2 - 1
2832
\end{aligned}
2933
```
30-
The parameter ``p`` shifts the location of the extrema of the drift field.
34+
The parameter ``p`` shifts the location of the equilibria ``\dot x = 0``.
3135
We implement this system as follows:
3236

33-
````julia
37+
```@example MAIN
3438
using CriticalTransitions
35-
using CairoMakie
3639
37-
function f(u, p, t) # out-of-place
40+
function f(u, p, t)
3841
x = u[1]
3942
dx = (x + p[1])^2 - 1
4043
return SVector{1}(dx)
4144
end
4245
43-
x0 = [-1.0]
44-
ds = CoupledODEs(f, x0, [0.0])
45-
````
46+
x0 = [-1.0] # Initial state
47+
p0 = [0.0] # Initial parameter value
4648
47-
## Applying the parameter ramping
49+
ds = CoupledODEs(f, x0, p0) # Autonomous system
50+
```
4851

4952
Now, we want to explore a non-autonomous version of this system by applying a parameter
50-
shift s.t. speed and amplitude of the parameter shift are easily modifiable but the 'shape'
51-
of it is always the same - just linearly stretched or squeezed.
53+
change over a given time interval.
54+
55+
### Forcing profile
56+
First, create a `ForcingProfile` to specify the functional form of the parameter change `p(t)`, here a hyperbolic tangent:
57+
58+
```@example MAIN
59+
profile(t) = tanh(t)
60+
interval = (-5.0, 5.0)
61+
62+
fp = ForcingProfile(profile, interval)
63+
```
5264

53-
First specify a section of a function `p(t)` that you would like to use to ramp a
54-
parameter of `ds`:
65+
Let's plot the forcing profile:
5566

56-
````julia
57-
p(t) = tanh(t) # A monotonic function that describes the parameter shift
58-
interval = (-5.0, 5.0) # Domain interval of p(t) we want to consider
59-
fp = ForcingProfile(p, interval)
60-
````
67+
```@example MAIN
68+
using CairoMakie
69+
70+
time_range = range(fp.interval[1], fp.interval[2]; length=100);
71+
72+
fig = Figure();
73+
ax = Axis(fig[1, 1]; xlabel="t (arbitrary units)", ylabel="profile (arbitrary units)");
74+
lines!(ax, time_range, fp.profile.(time_range); linewidth=2);
75+
fig
76+
```
6177

62-
Then specify how you would like to use the `ForcingProfile` to shift the `pidx`'th parameter
63-
of ds:
78+
Note that the `interval` is given in arbitrary units - the profile is rescaled to your system's units in the next step.
6479

65-
````julia
66-
pidx = 1 # Index of the parameter within the parameter-container of ds
67-
forcing_start_time = -50.0 # Time when the parameter shift should start
68-
forcing_duration = 105.0 # Time interval over which p(interval) is spread out or squeezed
69-
forcing_scale = 3.0 # Amplitude of the ramping. `p` is then automatically rescaled
70-
t0 = -70.0 # Initial time of the resulting non-autonomous system (relevant to later compute trajectories)
80+
### Applying the forcing
7181

72-
rs = RateSystem(ds, fp, pidx; forcing_start_time, forcing_duration, forcing_scale, t0)
82+
Now, specify how the forcing profile `fp` should be applied to the `pidx`-th parameter of your system `ds` by constructing a `RateSystem`.
7383

74-
#note # Choosing different values of the `forcing_duration` allows us to vary the speed of the parameter ramping, while its shape remains the same, and it only gets stretched or squeezed.
84+
```@example MAIN
85+
pidx = 1 # parameter index
86+
forcing_start_time = 20.0 # system time units
87+
forcing_duration = 105.0 # system time units
88+
forcing_scale = 3.0
89+
t0 = 0.0 # system initial time
7590
76-
#note # If `p(t)` within the `ForcingProfile` is a monotonic function, the `forcing_scale` will give the total amplitude of the parameter ramping. For non-monotonic `p(t)`, the `forcing_scale` will only linearly scale the amplitude of the parameter ramping, but does not equal the total amplitude.
77-
````
91+
rs = RateSystem(ds, fp, pidx;
92+
forcing_start_time, forcing_duration, forcing_scale, t0)
93+
```
7894

79-
Now, we can compute trajectories of this new system `rate_system` and of the previous autonomous system `ds` in the familiar way:
95+
The `forcing_scale` is a multiplication factor that scales the profile `fp.profile`. Here, we have ``p(5)-p(-5) \approx 2``, so the amplitude of the parameter change is ``6`` after multiplying with `forcing_scale = 3`.
8096

81-
````julia
82-
T = forcing_duration + 40.0; # length of the trajectory that we want to compute
83-
auto_traj = trajectory(ds, T, x0);
84-
nonauto_traj = trajectory(rs, T, x0);
85-
````
97+
In the `RateSystem`, the time dependence of the parameter `p[pidx]` thus looks like this:
8698

87-
We plot the two trajectories:
99+
```@example MAIN
100+
T = forcing_duration + 40.0 # Total time
101+
t_points = range(t0, t0+T, length=100)
102+
103+
parameter(t) = parameters(rs, t)[pidx] # Returns the parameter value at time t
88104
89-
````julia
90105
fig = Figure();
91-
axs = Axis(fig[1, 1]; xlabel="t", ylabel="x");
106+
ax = Axis(fig[1, 1]; xlabel="Time (system units)", ylabel="p[1]");
107+
lines!(ax, t_points, parameter.(t_points); linewidth=2);
108+
fig
109+
```
110+
111+
!!! tip "Modifying the forcing"
112+
113+
In a `RateSystem`, the forcing can easily be modified to implement different forcing rates and forcing amplitudes.
114+
- Via `set_forcing_duration!(rs, length)`, you can change the length of the forcing interval and thus the rate of change of the forcing.
115+
- Via `set_forcing_scale!(rs, factor)`, you can change the magnitude of the forcing by a given factor.
116+
117+
### Simulating trajectories
118+
119+
The `RateSystem` type behaves just like the type of underlyinh autonomous system, in this case a `CoupledODEs`. Thus, we can simply call the `trajectory` function to simulate either the autonomous system `ds` or the nonautonomous system `rs`.
120+
121+
```@example MAIN
122+
traj_ds = trajectory(ds, T, x0)
123+
traj_rs = trajectory(rs, T, x0)
124+
```
125+
126+
Let's compare the two trajectories:
127+
128+
```@example MAIN
129+
fig = Figure();
130+
axs = Axis(fig[1, 1]; xlabel="Time", ylabel="x");
92131
lines!(
93132
axs,
94-
t0 .+ auto_traj[2],
95-
auto_traj[1][:, 1];
133+
t0 .+ traj_ds[2],
134+
traj_ds[1][:, 1];
96135
linewidth=2,
97-
label=L"\text{Autonomous system}",
136+
label="Autonomous CoupledODEs (ds)",
98137
);
99138
lines!(
100139
axs,
101-
nonauto_traj[2],
102-
nonauto_traj[1][:, 1];
140+
traj_rs[2],
141+
traj_rs[1][:, 1];
103142
linewidth=2,
104-
label=L"\text{Nonautonomous system}",
143+
label="Nonautonomous RateSystem (rs)",
105144
);
106-
axislegend(axs; position=:rc, labelsize=10);
145+
axislegend(axs; position=:lb);
107146
fig
108-
````
109-
110-
We can also plot the shifted parameter `p(t)`:
111-
112-
````julia
113-
time_range = range(t0, t0+T; length=Int(T+1));
114-
115-
fig = Figure();
116-
ax = Axis(fig[1, 1]; xlabel="t", ylabel="p(t)");
117-
lines!(ax, time_range, fp.profile.(time_range); linewidth=2);
118-
fig
119-
````
120-
121-
---
122-
123-
*This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*
147+
```
124148

149+
While the autonomous system `ds` remains at the fixed point ``x^*=-1``, the nonautonomous system tracks the moving equilibrium until reaching the stable fixed point ``x^*=-7`` of the future limit system (i.e. the autonomous limit system after the parameter change) where ``p=6``.

docs/src/examples/tutorial.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ fp1, fp2 = eqs[stab]
6969
```
7070

7171
### Stochastic simulation
72-
Using the [`trajectory`](@ref) function, we now run a simulation of our system for `100` time units starting out from the fixed point `fp1`:
72+
Using the [`trajectory`](@ref) function, we now run a simulation of our system for `200` time units starting out from the fixed point `fp1`:
7373

7474
```@example MAIN
75-
sim = trajectory(sys, 100, fp1)
75+
sim = trajectory(sys, 200, fp1)
7676
```
7777

7878
In the keyword arguments, we have specified at which interval the solution is saved. Further keyword arguments can be used to change the solver (the default is `SOSRA()` for stochastic integration) and other settings.

docs/src/man/system_construction.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ CoupledSDEs
3232
!!! info
3333
Note that nonlinear mixings of the Noise Process $\mathcal{W}$ fall into the class of random ordinary differential equations (RODEs) which have a separate set of solvers. See [this example](https://docs.sciml.ai/DiffEqDocs/stable/tutorials/rode_example/) of DifferentialEquations.jl.
3434

35-
### Accessing `CoupledSDEs` properties
35+
### `CoupledSDEs` API
3636

3737
```@docs
3838
solver
@@ -51,6 +51,10 @@ ForcingProfile
5151
frozen_system
5252
```
5353

54+
![Schematic explaining RateSystem construction](../assets/ratesystem_scheme.png)
55+
56+
### `RateSystem` API
57+
5458
```@docs
5559
parameters
5660
set_forcing_start!

docs/src/quickstart.md

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,41 @@
11
# Getting started
22

33
## Installation
4-
To install the Julia language, we recommend [juliaup](https://github.com/JuliaLang/juliaup).
4+
> To install the Julia language, we recommend [juliaup](https://github.com/JuliaLang/juliaup).
5+
6+
`CriticalTransitions` is a registered Julia package (as of `v0.7`) and can be installed with the Julia package manager:
7+
8+
```console
9+
julia> ]
10+
Pkg> add CriticalTransitions
11+
```
12+
or
513

6-
The `CriticalTransitions.jl` package can be installed from Github via the Julia package manager:
714
```julia
8-
using Pkg; Pkg.add(url="https://github.com/juliadynamics/CriticalTransitions.jl.git")
15+
using Pkg; Pkg.add("CriticalTransitions")
916
```
10-
You can then load the package with `using CriticalTransitions`.
1117

12-
The package is currently tested to be compatible with Julia versions `1.10` and `1.11`.
18+
The package is currently tested to be compatible with Julia versions `1.10-1.12`.
1319

1420
## Basic usage
15-
The general workflow of `CriticalTransitions.jl` essentially follows two steps, similar to `DynamicalSystems.jl`:
21+
The general workflow of CriticalTransitions.jl consists of two steps, similar to DynamicalSystems.jl:
1622

1723
1. Define your dynamical system (see [Defining a forced dynamical system](@ref))
18-
2. Investigate the system by calling functions (see [Index](@ref))
24+
2. Investigate the system by calling methods (see [Index](@ref))
1925

20-
Some functions are only loaded as extensions when loading other dependency packages (see [Extensions](@ref)).
26+
Some methods are only loaded as extensions when loading other dependency packages (see [Extensions](@ref)).
2127

2228
## Documentation
23-
The [Tutorial](@ref) and code examples in the *Examples* section illustrate some use cases of the package. All available functions and types are documented in the *Manual* section (see [Index](@ref) for an overview).
29+
The [Tutorial](@ref) and code examples in the *Examples* section illustrate some use cases of the package. All available methods and types are documented in the *Manual* section (see [Index](@ref) for an overview).
2430

2531
## Index
2632
A quick overview of available features:
2733

2834
**Defining a system and its forcing**
2935
- [`DynamicalSystemsBase.CoupledODEs`](@ref)
3036
- [`DynamicalSystemsBase.CoupledSDEs`](@ref) (alias for `StochSystem`)
31-
- [`RateSystem`](@ref)
32-
- [`ForcingProfile`](@ref)
37+
- [`CriticalTransitions.RateSystem`](@ref)
38+
- [`CriticalTransitions.ForcingProfile`](@ref)
3339

3440
**System analysis and simulation**
3541
```@index
File renamed without changes.

src/r_tipping/RateSystem.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ corresponding to the frozen system of the non-autonomous [`RateSystem`](@ref) `r
192192
time `t`.
193193
"""
194194
function frozen_system(rs::RateSystem, t)
195-
ds = CoupledODEs(rs.forcing.unforced_rule, current_state(ds), get_forcing(rs, t))
195+
ds = CoupledODEs(rs.forcing.unforced_rule, current_state(rs), parameters(rs, t))
196196
return ds
197197
end
198198

0 commit comments

Comments
 (0)