Getting Started with JuliaSimCompiler
To use JuliaSimCompiler
as an MTK backend, one just needs to convert MTK systems into an IRSystem
. There really isn't much more to it!
As an example, let's take the the following MTK system of a motor. It's similar to the DC Motor example from the ModelingToolkitStandardLibrary, though without the controller. The code is as follows:
using ModelingToolkit, DifferentialEquations, Plots
using ModelingToolkitStandardLibrary.Electrical
using ModelingToolkitStandardLibrary.Mechanical.Rotational
using ModelingToolkitStandardLibrary.Blocks
@component function Motor(; name, R = 0.5, L = 4.5e-3, k = 0.5, J = 0.02, f = 0.01)
@named p = Pin()
@named n = Pin()
@named R1 = Resistor(R = R)
@named L1 = Inductor(L = L)
@named emf = EMF(k = k)
@named fixed = Fixed()
@named inertia = Inertia(J = J)
@named friction = Damper(d = f)
@named flange = Flange()
connections = [connect(fixed.flange, emf.support, friction.flange_b)
connect(emf.flange, friction.flange_a, inertia.flange_a)
connect(inertia.flange_b, flange)
connect(p, R1.p)
connect(R1.n, L1.p)
connect(L1.n, emf.p)
connect(emf.n, n)]
model = ODESystem(connections, t,
systems = [
flange,
p, n,
R1,
L1,
emf,
fixed,
inertia,
friction,
], name = name)
end
t = Blocks.t
@named motor = Motor()
@named ground = Ground()
@named load = Torque(use_support = false)
@named load_value = Blocks.Sine(frequency = 2)
@named source = Voltage()
@named voltage_constant = Blocks.Constant(k = 10)
@named speed_sensor = SpeedSensor()
eqs = [connect(voltage_constant.output, source.V)
connect(load_value.output, load.tau)
connect(motor.p, source.p)
connect(motor.n, source.n, ground.g)
connect(motor.flange, load.flange)
connect(motor.flange, speed_sensor.flange)]
@named complete_motor = ODESystem(eqs, t,
systems = [
motor,
ground,
load,
load_value,
source,
voltage_constant,
speed_sensor,
]);
sys = structural_simplify(complete_motor)
prob = ODEProblem(sys, [], (0, 10.0))
sol = solve(prob)
plot(sol, idxs = [speed_sensor.w.u], title = "Motor speed", xlab = "time (s)",
ylab = "speed (rad/s)", lab = "MTK")
In order to transform this into a system for JuliaSimCompiler, we simply transform the ODESystem
into an IRSystem
via the IRSystem
constructor. Let's convert to JuliaSimCompiler and perform structrual simplification on the IR form:
using JuliaSimCompiler
complete_motor_ir = IRSystem(complete_motor)
sys_ir = structural_simplify(complete_motor_ir)
States (3):
motor₊inertia₊phi
motor₊inertia₊w
motor₊L1₊i
Variables (79):
1 => motor₊R1₊p₊v
2 => motor₊R1₊n₊v
3 => motor₊R1₊v
4 => motor₊R1₊n₊i
5 => motor₊R1₊p₊i
6 => motor₊R1₊i
7 => motor₊L1₊p₊v
8 => motor₊L1₊n₊v
9 => motor₊L1₊v
10 => motor₊L1₊p₊i
⋮
71 => motor₊fixed₊flange₊tau
72 => motor₊emf₊support₊tau
73 => Dt(motor₊fixed₊flange₊phi, 1, true)
74 => Dt(motor₊friction₊flange_b₊phi, 1, true)
75 => Dt(motor₊emf₊phi, 2, true)
76 => Dt(motor₊inertia₊phi, 2, true)
77 => Dt(motor₊friction₊phi_rel, 2, true)
78 => Dt(motor₊fixed₊flange₊phi, 2, true)
79 => Dt(motor₊friction₊flange_b₊phi, 2, true)
Matched SystemStructure with 76 equations and 79 variables
# ∂ₜ eq # ∂ₜ v
1 [3, (7), 58] | 1 [(57)]
2 [(4), 6, 5] | 2 [(59)]
3 [(5), 10, 6] | 3 [1, (4), 5]
4 [(3), 6] | 4 [(2)]
5 [3, (9), 16, 58] | 5 [(3), 2]
6 [(11), 19, 10] | 6 [2, 4, 42, (60), 45, 58, 3]
7 [12, (19)] | 7 [(1), 59]
8 [9, (13)] | 8 [(61)]
9 [(14), 16] | 9 [(5), 8]
⋮ ⋮
71 23↓ [(42), 77] | 71 [(51)]
72 66↓ [75, (77)] | 72 [(65), 51]
73 67↓ [(75), 76, 79] | 73 [(68), 69]
74 68↓ [(78)] | 74 [67, (69)]
75 69↓ [78, (79)] | 75 [72, (73)]
76 55↓ [31, (60)] | 76 [(70), 73]
⋅ | 77 [71, (72)]
⋅ | 78 [(74), 75]
⋅ | 79 [73, (75)]
Legend: Solvable | (Solvable + Matched) | Unsolvable | (Unsolvable + Matched) | ∫ SelectedState
While such a system is internally different from the ModelingToolkit ODESystem
, its interfacces are the same. To show this, let's build the ODE problem, solve the model, and plot the system.
prob_ir = ODEProblem(sys_ir, [], (0, 10.0))
sol_ir = solve(prob_ir)
plot!(sol_ir, idxs = [speed_sensor.w.u], lab = "JuliaSimCompiler")
Notice that the code is exactly the same as if we had an ODESystem
. It's interopable and simply uses different internal representations to improve performance.