Deploying a Double Pendulum Model with Output Transform
This tutorial will demonstrate how to deploy a double pendulum model with a transformation of the outputs. The system evolves with states as polar coordinates of the position, but we will add a transformation such that the FMU outputs are Cartesian coordinates.
We start with importing FMUGeneration
and OrdinaryDiffEq
into our environment.
using FMUGeneration
using OrdinaryDiffEq
Let us clear our deployment workspace before we begin with the tutorial.
clear_deployment_workspace()
[ Info: Cleared Deployment Workspace!
We first define the system and the transforming function using @define
. The function double_pendulum
is our system. The function polar2cart
is the transforming function that converts the polar coordinates into cartesian. This example is taken from the DifferentialEquations.jl docs
@define begin
function double_pendulum(u, x, p, t)
m₁, m₂, L₁, L₂, g = p
du1 = u[2]
du2 = -((g * (2 * m₁ + m₂) * sin(u[1]) +
m₂ * (g * sin(u[1] - 2 * u[3]) +
2 * (L₂ * u[4]^2 + L₁ * u[2]^2 * cos(u[1] - u[3])) * sin(u[1] - u[3]))) /
(2 * L₁ * (m₁ + m₂ - m₂ * cos(u[1] - u[3])^2)))
du3 = u[4]
du4 = (((m₁ + m₂) * (L₁ * u[2]^2 + g * cos(u[1])) +
L₂ * m₂ * u[4]^2 * cos(u[1] - u[3])) * sin(u[1] - u[3])) /
(L₂ * (m₁ + m₂ - m₂ * cos(u[1] - u[3])^2))
[du1, du2, du3, du4]
end
function polar2cart(u, x, p, t)
l1 = p[3]
l2 = p[4]
vars = (2, 4)
p1 = l1 * u[vars[1]]
p2 = l2 * u[vars[2]]
x1 = l1 * sin(p1)
y1 = l1 * -cos(p1)
[x1 + l2 * sin.(p2), y1 - l2 * cos.(p2)]
end
end
Now that we have defined our system and transformation into the FMU Package, let us define the initial states, default parameters, and timespan for the system.
initial_states = [0, π / 3, 0, 3pi / 5]
m₁, m₂, L₁, L₂, g = 1, 2, 1, 2, 9.8
default_parameters = [m₁, m₂, L₁, L₂, g]
tspan = (0.0, 50.0)
tend = tspan[end]
param_names = ["m1", "m2", "L1", "L2", "g"]
state_names = ["alpha", "Lalpha", "beta", "Lbeta"]
output_names = ["x", "y"]
2-element Vector{String}:
"x"
"y"
We call set_default
and pass the keyword arguments necessary.
set_default(; initial_states = initial_states, default_parameters = default_parameters, tspan = tspan)
Now we define our continuous model
, in this case, it is the double_pendulum
function we have defined before.
@continuous_model double_pendulum(u, x, p, t)
And then, we define the output transform
that would convert the polar coordinates to cartesian coordinates.
@output_transform polar2cart(u, x, p, t)
We will add OrdinaryDiffEq
package to the FMU package using @add_packages
@add_packages(["OrdinaryDiffEq", "SciMLBase"])
[ Info: Pkg is already available in the sysimage.
[ Info: Base64 is already available in the sysimage.
[ Info: Future is already available in the sysimage.
[ Info: Sockets is already available in the sysimage.
[ Info: Markdown is already available in the sysimage.
[ Info: Tar is already available in the sysimage.
[ Info: UUIDs is already available in the sysimage.
[ Info: SHA is already available in the sysimage.
[ Info: LazyArtifacts is already available in the sysimage.
[ Info: LinearAlgebra is already available in the sysimage.
[ Info: ArgTools is already available in the sysimage.
[ Info: LibGit2 is already available in the sysimage.
[ Info: Artifacts is already available in the sysimage.
[ Info: Dates is already available in the sysimage.
[ Info: NetworkOptions is already available in the sysimage.
[ Info: MKL_jll is not explicitly added to the environment.
[ Info: Printf is already available in the sysimage.
[ Info: nghttp2_jll is already available in the sysimage.
[ Info: Test is already available in the sysimage.
[ Info: Random is already available in the sysimage.
[ Info: Libdl is already available in the sysimage.
[ Info: Serialization is already available in the sysimage.
[ Info: REPL is already available in the sysimage.
[ Info: libblastrampoline_jll is already available in the sysimage.
[ Info: MozillaCACerts_jll is already available in the sysimage.
[ Info: Mmap is already available in the sysimage.
[ Info: LibCURL_jll is already available in the sysimage.
[ Info: Logging is already available in the sysimage.
[ Info: TOML is already available in the sysimage.
[ Info: Downloads is already available in the sysimage.
[ Info: OpenBLAS_jll is already available in the sysimage.
[ Info: Distributed is already available in the sysimage.
[ Info: FileWatching is already available in the sysimage.
[ Info: LibCURL is already available in the sysimage.
[ Info: SharedArrays is already available in the sysimage.
[ Info: SparseArrays is already available in the sysimage.
[ Info: Unicode is already available in the sysimage.
[ Info: InteractiveUtils is already available in the sysimage.
[ Info: p7zip_jll is already available in the sysimage.
We then generate the FMU Package code using generate_fmu_code
generate_fmu_code()
We will import the package and get the package name using @import_fmu_pkg
pkg_name, FMI2Binary = @import_fmu_pkg FMUGeneration.lib_path
Now lets define the XML metadeta for the FMU with generateXML
We donot have inputs for this system and hence we define an empty vector for inputs.
inputs = NamedTuple[]
params = [(name = "$(param_names[i])", description = "desc p$i", start = default_parameters[i]) for i in 1:length(default_parameters)]
conts = [(name = "$(state_names[i])", unit = "unit c$i", description = "desc c$i", start = initial_states[i])
for i in 1:length(initial_states)]
outputs = [(name = "$o", unit = "unit c$i", description = "desc c$i")
for (i, o) in enumerate(["x", "y"])]
generateXML(inputs, params, conts, outputs, tend; sysname = "double_pendulum")
Now that we have defined our model, generated the FMU package, and generated the metadata, we are ready to compile a FMU. We call compile_fmu
fmu_dir_path, fmu_path, fmu_so_file_path = compile_fmu()
Pkg.rm(String(pkg_name)) # hide
nothing # hide