Exporting a `RuntimeGeneratedFunction`

I have a ModelingToolkit.ODESystem in a package called AstrodynamicalModels. I want to export an ODEFunction, which uses RuntimeGeneratedFunctions to avoid world-age issues.

When I import AstrodynamicalModels, and try to call one of the exported ODEFunctions, I get errors like…

ERROR: KeyError: key (0x0f45875b, 0xbf13700d, 0x6254b25f, 0x472f60fd, 0x500c018f) not found
Stacktrace:
  [1] getindex(h::Dict{Any, Any}, key::NTuple{5, UInt32})
    @ Base ./dict.jl:482
  [2] (::RuntimeGeneratedFunctions.var"#8#9"{DataType, NTuple{5, UInt32}})()
    @ RuntimeGeneratedFunctions ~/.julia/packages/RuntimeGeneratedFunctions/3SZ1T/src/RuntimeGeneratedFunctions.jl:177
  [3] lock(f::RuntimeGeneratedFunctions.var"#8#9"{DataType, NTuple{5, UInt32}}, l::Base.Threads.SpinLock)
    @ Base ./lock.jl:187
  [4] _lookup_body(cache_tag::Type, id::NTuple{5, UInt32})
    @ RuntimeGeneratedFunctions ~/.julia/packages/RuntimeGeneratedFunctions/3SZ1T/src/RuntimeGeneratedFunctions.jl:175
  [5] generated_callfunc_body(argnames::Tuple{Symbol, Symbol, Symbol}, cache_tag::Type, id::NTuple{5, UInt32}, __args::Tuple{DataType, DataType, DataType})
    @ RuntimeGeneratedFunctions ~/.julia/packages/RuntimeGeneratedFunctions/3SZ1T/src/RuntimeGeneratedFunctions.jl:120
  [6] #s1#1
    @ ~/.julia/packages/RuntimeGeneratedFunctions/3SZ1T/src/RuntimeGeneratedFunctions.jl:208 [inlined]
  [7] var"#s1#1"(argnames::Any, cache_tag::Any, id::Any, ::Any, f::Any, __args::Any)
    @ ModelingToolkit ./none:0
  [8] (::Core.GeneratedFunctionStub)(::Any, ::Vararg{Any, N} where N)
    @ Core ./boot.jl:571
  [9] RuntimeGeneratedFunction
    @ ~/.julia/packages/RuntimeGeneratedFunctions/3SZ1T/src/RuntimeGeneratedFunctions.jl:112 [inlined]
 [10] WARNING: both BipartiteGraphs and Symbolics export "degree"; uses of it in module ModelingToolkit must be qualified
(::ModelingToolkit.var"#f#154"{RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(Symbol("##arg#257"), Symbol("##arg#258"), :t), ModelingToolkit.var"#_RGF_ModTag", ModelingToolkit.var"#_RGF_ModTag", (0x0f45875b, 0xbf13700d, 0x6254b25f, 0x472f60fd, 0x500c018f)}, RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(Symbol("##out#259"), Symbol("##arg#257"), Symbol("##arg#258"), :t), ModelingToolkit.var"#_RGF_ModTag", ModelingToolkit.var"#_RGF_ModTag", (0x05b84fb3, 0x7fd909c0, 0xe2ea4e2e, 0xd451c079, 0xdb963cd2)}})(u::Vector{Float64}, p::Vector{Float64}, t::Int64)
    @ ModelingToolkit ~/.julia/packages/ModelingToolkit/MuQfD/src/systems/diffeqs/abstractodesystem.jl:172
 [11] (::SciMLBase.ODEFunction{true, ModelingToolkit.var"#f#154"{RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(Symbol("##arg#257"), Symbol("##arg#258"), :t), ModelingToolkit.var"#_RGF_ModTag", ModelingToolkit.var"#_RGF_ModTag", (0x0f45875b, 0xbf13700d, 0x6254b25f, 0x472f60fd, 0x500c018f)}, RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(Symbol("##out#259"), Symbol("##arg#257"), Symbol("##arg#258"), :t), ModelingToolkit.var"#_RGF_ModTag", ModelingToolkit.var"#_RGF_ModTag", (0x05b84fb3, 0x7fd909c0, 0xe2ea4e2e, 0xd451c079, 0xdb963cd2)}}, LinearAlgebra.UniformScaling{Bool}, Nothing, ModelingToolkit.var"#_tgrad#156"{RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(Symbol("##arg#260"), Symbol("##arg#261"), :t), ModelingToolkit.var"#_RGF_ModTag", ModelingToolkit.var"#_RGF_ModTag", (0x5a70bcaf, 0x1cb7174b, 0x74cbb369, 0x6301c195, 0xec003f66)}, RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(Symbol("##out#262"), Symbol("##arg#260"), Symbol("##arg#261"), :t), ModelingToolkit.var"#_RGF_ModTag", ModelingToolkit.var"#_RGF_ModTag", (0x88bc7821, 0xa7965577, 0x7753fb47, 0x26894a1e, 0xdbcb458b)}}, ModelingToolkit.var"#_jac#158"{RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(Symbol("##arg#263"), Symbol("##arg#264"), :t), ModelingToolkit.var"#_RGF_ModTag", ModelingToolkit.var"#_RGF_ModTag", (0x41408862, 0x8ffcdf82, 0x4bb2beb5, 0x0e4f95da, 0xee6c5722)}, RuntimeGeneratedFunctions.RuntimeGeneratedFunction{(Symbol("##out#265"), Symbol("##arg#263"), Symbol("##arg#264"), :t), ModelingToolkit.var"#_RGF_ModTag", ModelingToolkit.var"#_RGF_ModTag", (0x07b2ef70, 0xd8ac1b87, 0x913b9755, 0x3da83bbe, 0xde306d03)}}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Vector{Symbol}, Symbol, ModelingToolkit.var"#159#generated_observed#161"{Bool, ModelingToolkit.ODESystem, Dict{Any, Any}}, Nothing})(::Vector{Float64}, ::Vararg{Any, N} where N)
    @ SciMLBase ~/.julia/packages/SciMLBase/kCcpg/src/scimlfunctions.jl:334
 [12] top-level scope
    @ REPL[2]:1

The following MWE is illustrative of what I’m trying to do, but because MyModule is defined directly in Main, this example actually works without error.

Is what I’m trying to do (export an ODEFunction to users) possible?

module MyModule

using StaticArrays
using LinearAlgebra
using ModelingToolkit
using RuntimeGeneratedFunctions
RuntimeGeneratedFunctions.init(@__MODULE__)


"""
A `ModelingToolkit.ODESystem` for the Restricted Two-body Problem. 


The Restricted Two-body Problem is a simplified dynamical model 
describing one small body (spacecraft, etc.) and one celestial 
body. The gravity of the celestial body exhibits a force on the 
small body. This model is commonly used as a simplification to 
descibe our solar systems' planets orbiting our sun, or a 
spacecraft orbiting Earth. 
"""
R2BP = let

    @parameters t μ 
    @variables x(t) y(t) z(t) ẋ(t) ẏ(t) ż(t)
    δ = Differential(t)
    r = @SVector [x,y,z]
    v = @SVector [ẋ,ẏ,ż]

    eqs = vcat(
        δ.(r) .~ v,
        δ.(v) .~ -μ .* (r ./ norm(r)^3)
    )

    @named R2BP = ODESystem(eqs, t, vcat(r,v), [μ])

end

"""
A `DifferentialEquations`-compatible `ODEFunction` for R2BP dynamics.
Note that this function has several methods, including an in-place 
method! Function signatures follow `ModelingToolkit` and `DifferentialEquations`
conventions.
"""
R2BPVectorField = ODEFunction(R2BP; jac = true, tgrad = true)

end # module

But when I try to call R2BPVectorField outside of MyModule, I get an error. I’m sure this is a user error, since my understanding of world age issues and RuntimeGeneratedFunction usage is a bit incomplete. Is there a way to return a "compiled’ or “runtime-generated” function to a user, from a module?

import MyModule
MyModule.R2BPVectorField(
  randn(6), # state
  randn(1), # params
  0.0       # time
)

I understand that I can export a function with returns a compiled ODEFunction, but I’d prefer to avoid that if I can.

Is there any way to generate a symbolic (eval_expression = false) ODEFunction in a module, and then provide a function to “compile” each symbolic ODEFunction so the user can call them?

The problem is likely the mix with precompilation, where you need to do something like this:

Thank you for this, the package now works. Even if I use the keyword arguments eval_expression=false, eval_module=@__MODULE__, if I don’t use const for the function I’d like to export, I get key not found errors.

Not sure why that’s the case, but like I said, I only have a cursory understanding for how world age issues and runtime generated functions work. Thank you for the help!

open an issue on RGFs