# Dyad Builder, FMUs

**URL:** <https://discourse.julialang.org/t/dyad-builder-fmus/132792>\
**Category:** Modelling & Simulations\
**Tags:** question, dyad\
**Created:** [September 30, 2025, 6:21pm UTC](https://discourse.julialang.org/t/dyad-builder-fmus/132792 "2025-09-30T18:21:43Z")\
**Posts on this page:** 1\
**Showing post:** 9

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [October 1, 2025, 9:33pm UTC](https://discourse.julialang.org/t/dyad-builder-fmus/132792/9 "2025-10-01T21:33:13Z")

</div>

Small update: The second example works. I had to change the “communication interval” in Simulink from auto to 0.01 to make it work.

In addition, I added these lines to copy the FMU file to the desired directory:

```julia-auto
# Save the FMU to the output directory
output_dir = joinpath(@ __DIR__ , "..", "output")
mkpath(output_dir) # Create output directory if it doesn't exist
fmu_filename = basename(fmu.fmu_path)
fmu_source = fmu.fmu_path
if isfile(fmu_source)
    fmu_dest = joinpath(output_dir, fmu_filename)
    cp(fmu_source, fmu_dest, force=true)
    println("✅ FMU copied to: $fmu_dest")
end 

```

Further, the directory with the FMU file must be in the Matlab path.

The first example also works now, but the example in the documentation is broken. This code works:

```julia-auto
using FMUGeneration
using OrdinaryDiffEq

lv_expr = :(function (dx, x, u, p, t)
    x1, x2 = x
    u1, u2 = u
    alpha, beta, gamma, delta = p
    dx[1] = alpha * x1 - beta * x1 * x2 + u1
    dx[2] = delta * x1 * x2 - gamma * x2 - u2
    return nothing # In-place modification
end);

initial_states = [1.0, 1.0]
initial_inputs = [0.01, 0.02]
default_parameters = [2.0, 1.875, 2.0, 1.875]
tspan = (0.0, 10.0)
param_names = ["alpha", "beta", "gamma", "delta"]
input_names = ["u_1" , "u_2"]
state_names = ["x_1" , "x_2"]
output_names = ["y_1", "y_2"] # Different names to avoid conflict with states

# Define observable function to compute outputs
observable_expr = :(function (x, u, p, t)
    # Return the states as individual outputs (y_1 = x_1, y_2 = x_2)
    y1 = x[1]
    y2 = x[2]
    return [y1, y2]
end)

fmu = JuliaFMU(
    # REQUIRED ARGUMENTS

    # We specify the FMU name
    "lotka-volterra",
    # We specify the FMI version
    FMI_V3, # or v2,
    # We specify the FMU operating types supported
    [FMI_MODELEXCHANGE, FMI_COSIMULATION];

    # OPTIONAL ARGUMENTS

    # We optionally specify the default time-space of the FMU operation
    default_tspan = tspan,
    # We optionally specify the recommended step size
    # default_stepsize = 1e-3,
    # We optionally specify the recommended solver tolerance
    # default_tolerance = 1e-6,

    # Metadata: inputs, parameters and states respectively
    inputs = [
        (name=input_names[i], start=initial_inputs[i]) for i in 1:length(input_names)
    ],
    parameters = [
        (name=param_names[i], start=default_parameters[i]) for i in 1:length(param_names)
    ],
    states = [
        (name=state_names[i], start=initial_states[i]) for i in 1:length(state_names)
    ],
    outputs = [
        (name=output_names[i],) for i in 1:length(output_names)
    ],

    # We define the dependencies required for the FMU. Here, we need OrdinaryDiffEq for the solver used to run the FMU in CS mode.
    dependencies = @deps([OrdinaryDiffEq]),
    # We define the ODE function expression with the signature `(dx, u, p, t) -> begin ... end` where the function is expected to be inplace. Here, it is the `double_pendulum_expr` defined earlier. This is an essential kwarg for all ME FMUs.
    ode_function = lv_expr,
    # We optionally define function to compute the outputs with the signature `(x, u, p, t) -> outs`.
    observables_function = observable_expr,
    # We specify which solver to use for cosimulation. We default to `OrdinaryDiffEq.AutoTsit5(OrdinaryDiffEq.FBDF())` if not provided.
    cosimulator_solver = :(OrdinaryDiffEq.AutoTsit5(OrdinaryDiffEq.FBDF())),
    # If we had inputs, we would also had to specify it like below:
    # inputs = [
    # (name="input_1", start=1.0),
    # ...
    # ],

    # If we had to initialize out states in a specific manner, we could also that like below:
    # state_initializer = :((x, u, p, t) -> initialize_x)

    # We could also optionally provide integrator options:
    # cosimulator_integrator_options=(abstol=1e-6, reltol=1e-6),

    # We could also optionally provide objects from user space needed for the FMU dynamics to operate
    # objects=@objects([test_obj]),

    # We optionally define the number of threads to start the FMU with to capitalize on multi-threaded acceleration of matmul operations. This is only relevant if your computation is heavy on matrix operations.
    # n_octavian_threads = 4
)

# Save the FMU to the output directory
output_dir = joinpath(@ __DIR__ , "..", "output")
mkpath(output_dir) # Create output directory if it doesn't exist
fmu_filename = basename(fmu.fmu_path)
fmu_source = fmu.fmu_path
if isfile(fmu_source)
    fmu_dest = joinpath(output_dir, fmu_filename)
    cp(fmu_source, fmu_dest, force=true)
    println("✅ FMU copied to: $fmu_dest")
end 

```

Key changes:

- do not use greek letters, only ascii
- add an output function (Simulink crashes if there is no output function)
- fix the ODE definition. It did not modify `dx` in-place.

---

_[View the full topic](https://discourse.julialang.org/t/dyad-builder-fmus/132792)._
