# Wrap my function in an MTK component

**URL:** https://discourse.julialang.org/t/wrap-my-function-in-an-mtk-component/133767
**Category:** Modelling & Simulations
**Tags:** modelingtoolkit
**Created:** [November 9, 2025, 10:13pm UTC](https://discourse.julialang.org/t/wrap-my-function-in-an-mtk-component/133767 "2025-11-09T22:13:44Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)
#### Post date: [November 9, 2025, 10:13pm UTC](https://discourse.julialang.org/t/wrap-my-function-in-an-mtk-component/133767/1 "2025-11-09T22:13:44Z")

</div>

I am trying to wrap an arbitrary function in an MTK component and I am a bit lost in all the possibilities available these days.

Right now I have:

```julia
seconds_to_datetime(t, t0::DateTime) = t0 + Millisecond(round(Int, t * 1e3))

function SolarPosition.SolarPositionComponent(;
    name,
    t0::DateTime,
    observer::Observer,
    algorithm::SolarAlgorithm = PSA(),
    refraction::RefractionAlgorithm = NoRefraction(),
)
    pos(t) = solar_position(observer, seconds_to_datetime(t, t0), algorithm, refraction)

    # and now define the component?
end

```

where `solar_position` will return a struct like:

```julia
struct SolPos{T} <: AbstractSolPos where {T<:AbstractFloat}
    "Azimuth (degrees, 0=N, +clockwise, range [-180, 180])"
    azimuth::T
    "Elevation (degrees, range [-90, 90])"
    elevation::T
    "Zenith = 90 - elevation (degrees, range [0, 180])"
    zenith::T
end

```

I’m trying to build a component, where I can just refer to positions like:

```julia
@named sun = SolarPositionComponent(t0 = t0, observer = obs)
sun.azimuth
sun.elevation

```

Some pointing to the right docs and examples would be appreciated.

---

<div class="post-metadata">

### Author: ![SebastianM-C](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sebastianm-c/32/2480_2.png) [@SebastianM-C](https://discourse.julialang.org/u/SebastianM-C)
#### Post date: [November 10, 2025, 4:51pm UTC](https://discourse.julialang.org/t/wrap-my-function-in-an-mtk-component/133767/2 "2025-11-10T16:51:12Z")

</div>

An example would be ModelingToolkitNeuralNets.jl where `NeuralNetworkBlock` wraps a function call.

What you need to do it to create a `System` with a `pos ~ solar_position(observer, ...)` equation (or whatever relations would define the system) and then you would define the parameters of your system as the non-numeric constants that your function needs.

Currently using the struct symbolically might be a bit tricky, but if you have just have 3 separate variables, then the last code block would just work since `azimuth` and `elevation` would just be `@variables` in the system and you would have some equations for them.

---

<div class="post-metadata">

### Author: ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)
#### Post date: [November 10, 2025, 11:32pm UTC](https://discourse.julialang.org/t/wrap-my-function-in-an-mtk-component/133767/3 "2025-11-10T23:32:35Z")

</div>

> [@SebastianM-C](#):
>
> and then you would define the parameters of your system as the non-numeric constants that your function needs.

do you mean in an `@parameters` block inside the `SolarPositionComponent`?

> [@SebastianM-C](#):
>
> Currently using the struct symbolically might be a bit tricky, but if you have just have 3 separate variables, then the last code block would just work since `azimuth` and `elevation` would just be `@variables` in the system and you would have some equations for them.

Sorry I can’t really follow. Shouldn’t `azimuth` and `elevation` already be `@variables` inside my component?

---

<div class="post-metadata">

### Author: ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)
#### Post date: [November 11, 2025, 12:34am UTC](https://discourse.julialang.org/t/wrap-my-function-in-an-mtk-component/133767/4 "2025-11-11T00:34:06Z")

</div>

I tried the following approach:

```julia

seconds_to_datetime(t_sec, t0::DateTime) = t0 + Millisecond(round(Int, t_sec * 1e3))

@register_symbolic seconds_to_datetime(t_sec::Num, t0)
@register_symbolic solar_position(observer, time, algorithm, refraction)

function SolarPosition.SolarPositionBlock(; name)

    @parameters t0::DateTime [tunable = false] observer::Observer [tunable = false]
    @parameters algorithm::SolarAlgorithm = PSA() [tunable = false]
    @parameters refraction::RefractionAlgorithm = NoRefraction() [tunable = false]

    @variables pos(t) [output = true]

    eqs =
        [pos ~ solar_position(observer, seconds_to_datetime(t, t0), algorithm, refraction)]

    return System(eqs, t; name = name)
end

```

Which then fails with

```julia-auto
  Got exception outside of a @test
  MethodError: no method matching seconds_to_datetime(::Symbolics.Num, ::SymbolicUtils.BasicSymbolic{DateTime})
  The function `seconds_to_datetime` exists, but no method is defined for this combination of argument types.

```

I’m really not sure what I’m supposed to do here.

---

<div class="post-metadata">

### Author: ![SebastianM-C](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sebastianm-c/32/2480_2.png) [@SebastianM-C](https://discourse.julialang.org/u/SebastianM-C)
#### Post date: [November 11, 2025, 7:29pm UTC](https://discourse.julialang.org/t/wrap-my-function-in-an-mtk-component/133767/5 "2025-11-11T19:29:58Z")

</div>

For the function registration, try to register with

```julia-auto
@register_symbolic seconds_to_datetime(t_sec, t0::DateTime)
@register_symbolic solar_position(observer::Observer, time::DateTime, algorithm::SolarAlgorithm, refraction::RefractionAlgorithm)

```

i.e. specify all the types that are not symbolic

---

<div class="post-metadata">

### Author: ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)
#### Post date: [November 11, 2025, 9:58pm UTC](https://discourse.julialang.org/t/wrap-my-function-in-an-mtk-component/133767/6 "2025-11-11T21:58:03Z")

</div>

thank you!

I came to the following:

```julia
using SolarPosition: Observer, SolarAlgorithm, RefractionAlgorithm, PSA, NoRefraction
using ModelingToolkit: @parameters, @variables, System, @register_symbolic, t_nounits
using Symbolics
using Symbolics: term
using Dates: DateTime, Millisecond

import SolarPosition: SolarPositionBlock, solar_position

seconds_to_datetime(t_sec, t0::DateTime) = t0 + Millisecond(round(Int, t_sec * 1e3))

@register_symbolic seconds_to_datetime(t_sec, t0::DateTime)
@register_symbolic solar_position(
    observer::Observer,
    time::DateTime,
    algorithm::SolarAlgorithm,
    refraction::RefractionAlgorithm,
)

function SolarPositionBlock(; name)

    @parameters t0::DateTime [tunable = false] observer::Observer [tunable = false]
    @parameters algorithm::SolarAlgorithm = PSA() [tunable = false]
    @parameters refraction::RefractionAlgorithm = NoRefraction() [tunable = false]

    @variables pos(t_nounits) [output = true]

    time_expr = term(seconds_to_datetime, t_nounits, t0; type = DateTime)

    eqs = [pos ~ solar_position(observer, time_expr, algorithm, refraction)]

    return System(eqs, t_nounits; name = name)
end

```

Now `@named sun = SolarPositionBlock()` works and gives:

```julia
Model sun:
Equations (1):
  1 standard: see equations(sun)
Unknowns (1): see unknowns(sun)
  pos(t)
Parameters (4): see parameters(sun)
  observer
  algorithm [defaults to PSA(2020)]
  t0
  refraction [defaults to NoRefraction()]Variables: SymbolicUtils.BasicSymbolic[]

```

I don’t see how to get to `azimuth` and `elevation` from here.

---

<div class="post-metadata">

### Author: ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)
#### Post date: [November 11, 2025, 10:16pm UTC](https://discourse.julialang.org/t/wrap-my-function-in-an-mtk-component/133767/7 "2025-11-11T22:16:29Z")

</div>

After struggling a little bit more this seems to work beautifully now. Some critical feedback to the efficiency of this method would be greatly appreciated.

```julia

using SolarPosition: Observer, SolarAlgorithm, RefractionAlgorithm, PSA, NoRefraction
using ModelingToolkit: @parameters, @variables, System, @register_symbolic, t_nounits
using Symbolics
using Symbolics: term
using Dates: DateTime, Millisecond

import SolarPosition: SolarPositionBlock, solar_position

seconds_to_datetime(t_sec, t0::DateTime) = t0 + Millisecond(round(Int, t_sec * 1e3))

# Helper functions to extract fields from solar position
get_azimuth(pos) = pos.azimuth
get_elevation(pos) = pos.elevation
get_zenith(pos) = pos.zenith

@register_symbolic seconds_to_datetime(t_sec, t0::DateTime)
@register_symbolic solar_position(
    observer::Observer,
    time::DateTime,
    algorithm::SolarAlgorithm,
    refraction::RefractionAlgorithm,
)
@register_symbolic get_azimuth(pos)
@register_symbolic get_elevation(pos)
@register_symbolic get_zenith(pos)

function SolarPositionBlock(; name)

    @parameters t0::DateTime [tunable = false] observer::Observer [tunable = false]
    @parameters algorithm::SolarAlgorithm = PSA() [tunable = false]
    @parameters refraction::RefractionAlgorithm = NoRefraction() [tunable = false]

    @variables azimuth(t_nounits) [output = true]
    @variables elevation(t_nounits) [output = true]
    @variables zenith(t_nounits) [output = true]

    time_expr = term(seconds_to_datetime, t_nounits, t0; type = DateTime)
    pos = solar_position(observer, time_expr, algorithm, refraction)

    eqs = [
        azimuth ~ get_azimuth(pos),
        elevation ~ get_elevation(pos),
        zenith ~ get_zenith(pos),
    ]

    return System(eqs, t_nounits; name = name)
end

```

which I can then put in an `ODEProblem` like this:

```julia
@named sun = SolarPositionBlock()

sys = mtkcompile(sun)

pmap = [
    sys.observer => obs,
    sys.t0 => t0,
    sys.algorithm => PSA(),
    sys.refraction => NoRefraction(),
]

# create ODEProblem for 24 hours (86400 seconds)
tspan = (0.0, 86400.0)
prob = ODEProblem(sys, pmap, tspan)

sol = solve(prob; saveat = 60.0)

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/c/a/cafe52bd19e8732110e0a610895ff0545c524ff5.png)

---

<div class="post-metadata">

### Author: ![SebastianM-C](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sebastianm-c/32/2480_2.png) [@SebastianM-C](https://discourse.julialang.org/u/SebastianM-C)
#### Post date: [November 12, 2025, 2:22am UTC](https://discourse.julialang.org/t/wrap-my-function-in-an-mtk-component/133767/8 "2025-11-12T02:22:13Z")

</div>

I’m not sure if we can avoid the explicit `term`. I think this should be fine.
