@@ -51,19 +51,41 @@ p = [σ => 10.0
5151 ρ => 28.0
5252 β => 8 / 3 ]
5353tspan = (0.0 ,100.0 )
54- prob = ODEProblem (sys,u0,tspan,p;jac= true )
54+ prob = ODEProblem (sys,u0,tspan,p;jac= true ,sparse = true )
5555```
5656
5757Note that the additional ` jac=true ` tells the system to symbolically generate
58- an optimized Jacobian function to enhance the differential equation solvers.
58+ an optimized Jacobian function to enhance the differential equation solvers,
59+ and ` sparse ` tells it to build the ODEProblem with all of the enhancements
60+ setup for sparse Jacobians.
5961
60- ### Example 2: Building a Component-Based ODEProblem with Sparse Jacobians
62+ ### Example 2: Building a Component-Based ODEProblem
63+
64+ In addition, we can then use ModelingToolkit to compose multiple ODE subsystems.
65+ Let's define two interacting Lorenz equations:
66+
67+ ``` julia
68+ lorenz1 = ODESystem (eqs,name= :lorenz1 )
69+ lorenz2 = ODESystem (eqs,name= :lorenz2 )
70+
71+ @variables α
72+ @parameters γ
73+ connections = [0 ~ lorenz1. x + lorenz2. y + sin (α* γ)]
74+ connected = ODESystem (connections,[α],[γ],systems= [lorenz1,lorenz2])
75+ ```
76+
77+ which is now a differential-algebraic equation (DAE) of 7 variables which has
78+ two independent Lorenz systems and an algebraic equation that determines ` α `
79+ such that an implicit constraint holds. We can then define the resulting
80+ ` ODEProblem ` and send it over to DifferentialEquations.jl.
6181
6282### Example 3: Building Nonlinear Systems to Solve with NLsolve.jl
6383
64- We can also build nonlinear systems. Let's say we wanted to solve for the steady
65- state of the previous ODE. This is the nonlinear system defined by where the
66- derivatives are zero. We use (unknown) variables for our nonlinear system.
84+ In this example we will go one step deeper and showcase the direct function
85+ generation capabilities in ModelingToolkit.jl to build nonlinear systems.
86+ Let's say we wanted to solve for the steady state of the previous ODE. This is
87+ the nonlinear system defined by where the derivatives are zero. We use (unknown)
88+ variables for our nonlinear system.
6789
6890``` julia
6991using ModelingToolkit
@@ -150,6 +172,7 @@ which gives:
150172Now we can call ` nlsolve ` by enclosing our parameters into the functions:
151173
152174``` julia
175+ using NLsolve
153176nlsolve ((out, x) -> f (out, x, params), (out, x) -> j! (out, x, params), ones (3 ))
154177```
155178
@@ -174,7 +197,7 @@ Base.:~(::Expression, ::Expression)
174197
175198## Additional High Level Explanations and Tips
176199
177- ## The Auto-Detecting System Constructors
200+ ### The Auto-Detecting System Constructors
178201
179202For the high level interface, the system constructors such as ` ODESystem ` have
180203high level constructors which just take in the required equations and automatically
@@ -186,6 +209,114 @@ ODESystem(eqs)
186209NonlinearSystem (eqs)
187210```
188211
212+ ### Direct Tracing
213+
214+ Because the ModelingToolkit ` Expression ` types obey Julia-semantics, one can
215+ directly transform existing Julia functions into ModelingToolkit symbolic
216+ representations of the function by simply inputting the symbolic values into
217+ the function and using what is returned. For example, let's take the following
218+ numerical PDE discretization:
219+
220+ ``` julia
221+ using ModelingToolkit, LinearAlgebra, SparseArrays
222+
223+ # Define the constants for the PDE
224+ const α₂ = 1.0
225+ const α₃ = 1.0
226+ const β₁ = 1.0
227+ const β₂ = 1.0
228+ const β₃ = 1.0
229+ const r₁ = 1.0
230+ const r₂ = 1.0
231+ const _DD = 100.0
232+ const γ₁ = 0.1
233+ const γ₂ = 0.1
234+ const γ₃ = 0.1
235+ const N = 8
236+ const X = reshape ([i for i in 1 : N for j in 1 : N],N,N)
237+ const Y = reshape ([j for i in 1 : N for j in 1 : N],N,N)
238+ const α₁ = 1.0 .* (X.>= 4 * N/ 5 )
239+
240+ const Mx = Tridiagonal ([1.0 for i in 1 : N- 1 ],[- 2.0 for i in 1 : N],[1.0 for i in 1 : N- 1 ])
241+ const My = copy (Mx)
242+ Mx[2 ,1 ] = 2.0
243+ Mx[end - 1 ,end ] = 2.0
244+ My[1 ,2 ] = 2.0
245+ My[end ,end - 1 ] = 2.0
246+
247+ # Define the discretized PDE as an ODE function
248+ function f! (du,u,p,t)
249+ A = @view u[:,:,1 ]
250+ B = @view u[:,:,2 ]
251+ C = @view u[:,:,3 ]
252+ dA = @view du[:,:,1 ]
253+ dB = @view du[:,:,2 ]
254+ dC = @view du[:,:,3 ]
255+ mul! (MyA,My,A)
256+ mul! (AMx,A,Mx)
257+ @. DA = _DD* (MyA + AMx)
258+ @. dA = DA + α₁ - β₁* A - r₁* A* B + r₂* C
259+ @. dB = α₂ - β₂* B - r₁* A* B + r₂* C
260+ @. dC = α₃ - β₃* C + r₁* A* B - r₂* C
261+ end
262+ ```
263+
264+ We can then define the corresponding arrays as ModelingToolkit variables:
265+
266+ ``` julia
267+ # Define the initial condition as normal arrays
268+ @variables du[1 : N,1 : N,1 : 3 ] u[1 : N,1 : N,1 : 3 ] MyA[1 : N,1 : N] AMx[1 : N,1 : N] DA[1 : N,1 : N]
269+ f! (du,u,nothing ,0.0 )
270+ ```
271+
272+ The output, here the in-place modified ` du ` , is a symbolic representation of
273+ each output of the function. We can then utilize this in the ModelingToolkit
274+ functionality. For example, let's compute the sparse Jacobian function and
275+ compile a fast multithreaded version:
276+
277+ ``` julia
278+ jac = sparse (ModelingToolkit. jacobian (vec (du),vec (u),simplify= false ))
279+ multithreadedjac = eval (ModelingToolkit. build_function (vec (jac),u,multithread= true )[2 ])
280+ ```
281+
282+ ### modelingtoolkitize
283+
284+ For some ` DEProblem ` types, automatic tracing functionality is already included
285+ via the ` modelingtoolkitize ` function. Take for example the Robertson ODE
286+ defined as an ` ODEProblem ` for DifferentialEquations.jl:
287+
288+ ``` julia
289+ using DifferentialEquations
290+ function rober (du,u,p,t)
291+ y₁,y₂,y₃ = u
292+ k₁,k₂,k₃ = p
293+ du[1 ] = - k₁* y₁+ k₃* y₂* y₃
294+ du[2 ] = k₁* y₁- k₂* y₂^ 2 - k₃* y₂* y₃
295+ du[3 ] = k₂* y₂^ 2
296+ nothing
297+ end
298+ prob = ODEProblem (rober,[1.0 ,0.0 ,0.0 ],(0.0 ,1e5 ),(0.04 ,3e7 ,1e4 ))
299+ ```
300+
301+ If we want to get a symbolic representation, we can simply call ` modelingtoolkitize `
302+ on the ` prob ` which will return an ` ODESystem ` :
303+
304+ ``` julia
305+ sys = modelingtoolkitize (prob)
306+ ```
307+
308+ Using this, we can symbolically build the Jacobian and then rebuild the ODEProblem:
309+
310+ ``` julia
311+ jac = eval (ModelingToolkit. generate_jacobian (de... )[2 ])
312+ f = ODEFunction (rober, jac= jac)
313+ prob_jac = ODEProblem (f,[1.0 ,0.0 ,0.0 ],(0.0 ,1e5 ),(0.04 ,3e7 ,1e4 ))
314+ ```
315+
316+ ``` @docs
317+ modelingtoolkitize
318+ ```
319+
189320### Intermediate Calculations
190321
191322The system building functions can handle intermediate calculations by simply
0 commit comments