Broadcast inside of `@mtkmodel` macro causes precompilation failures when put into a package

While trying to implement a 3D version of the Mass-Spring model here in a package, I get the following error

ERROR: LoadError: Evaluation into the closed module ModelingToolkit breaks incremental compilation because the side effects will not be permanent. This is likely due to some other module mutating ModelingToolkit with eval during precompilation - don’t do this.

In my package, this error only occurs if the following @mtkmodel block is present.

@mtkmodel MassXYZ begin
    # Definition of the model icon
    #@icon "..."

    # Definition of the model parameters
    @parameters begin
        m, [description = "Mass of the moving rigid body in [kg]"]
        v_0[1:3] = zeros(3), [description = "Initial velocities in [m/s] along X, Y and Z axes of the rigid body"]
        s_0[1:3] = zeros(3), [description = "Initial positions in [m] along X, Y and Z axes of the rigid body"]
    end

    @variables begin
        (s(t))[1:3] = s_0, [description = "Positions in [m] along X, Y and Z axes of the rigid body"]
        (v(t))[1:3] = v_0, [description = "Velocities in [m/s] along X, Y and Z axes of the rigid body"]
    end

    @equations begin
        D.(s) .~ v
    end
end

Am I doing something inappropriate ? This code executes fine in the global scope in the same environment.

Complete stack trace

ERROR: LoadError: Evaluation into the closed module ModelingToolkit breaks incremental compilation because the side effects will not be permanent. This is likely due to some other module mutating ModelingToolkit with eval during precompilation - don’t do this.
Stacktrace:
[1] eval
@ .\boot.jl:370 [inlined]
[2] eval
@ D:.julia\packages\ModelingToolkit\66mEN\src\ModelingToolkit.jl:4 [inlined]
[3] _broadcast_getindex_evalf
@ .\broadcast.jl:683 [inlined]
[4] _broadcast_getindex
@ .\broadcast.jl:656 [inlined]
[5] getindex
@ .\broadcast.jl:610 [inlined]
[6] copy
@ .\broadcast.jl:912 [inlined]
[7] materialize
@ .\broadcast.jl:873 [inlined]
[8] parse_variable_def!(dict::Dict{Symbol, Any}, mod::Module, arg::Expr, varclass::Symbol, kwargs::Vector{Any}; def::Expr, indices::Nothing)
@ ModelingToolkit D:.julia\packages\ModelingToolkit\66mEN\src\systems\model_parsing.jl:149
[9] parse_variable_def!(dict::Dict{Symbol, Any}, mod::Module, arg::Expr, varclass::Symbol, kwargs::Vector{Any}; def::Nothing, indices::Nothing)
@ ModelingToolkit D:.julia\packages\ModelingToolkit\66mEN\src\systems\model_parsing.jl:121
[10] parse_variable_def!
@ D:.julia\packages\ModelingToolkit\66mEN\src\systems\model_parsing.jl:86 [inlined]
[11] parse_variable_arg!(expr::Expr, vs::Vector{Any}, dict::Dict{Symbol, Any}, mod::Module, arg::Expr, varclass::Symbol, kwargs::Vector{Any})
@ ModelingToolkit D:.julia\packages\ModelingToolkit\66mEN\src\systems\model_parsing.jl:448
[12] parse_variables!(exprs::Vector{Any}, vs::Vector{Any}, dict::Dict{Symbol, Any}, mod::Module, body::Expr, varclass::Symbol, kwargs::Vector{Any})
@ ModelingToolkit D:.julia\packages\ModelingToolkit\66mEN\src\systems\model_parsing.jl:462
[13] parse_model!(exprs::Vector{Any}, comps::Vector{Symbol}, ext::Base.RefValue{Any}, eqs::Vector{Expr}, icon::Base.RefValue{Union{String, URIs.URI}}, vs::Vector{Any}, ps::Vector{Any}, sps::Vector{Any}, dict::Dict{Symbol, Any}, mod::Module, arg::Expr, kwargs::Vector{Any})
@ ModelingToolkit D:.julia\packages\ModelingToolkit\66mEN\src\systems\model_parsing.jl:256
[14] _model_macro(mod::Module, name::Symbol, expr::Expr, isconnector::Bool)
@ ModelingToolkit D:.julia\packages\ModelingToolkit\66mEN\src\systems\model_parsing.jl:49
[15] var"@mtkmodel"(source::LineNumberNode, module::Module, name::Symbol, body::Any)
@ ModelingToolkit D:.julia\packages\ModelingToolkit\66mEN\src\systems\model_parsing.jl:31
[16] include(mod::Module, _path::String)
@ Base .\Base.jl:457
[17] include(x::String)
@ D:\src.jl:1
[18] top-level scope
@ D:\src.jl:22
[19] include
@ .\Base.jl:457 [inlined]
[20] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt128}}, source::Nothing)
@ Base .\loading.jl:2010
[21] top-level scope
@ stdin:2
in expression starting at D:\src\mtk.jl:218
in expression starting at D:\src\mtk.jl:1
in expression starting at D:\src.jl:1
in expression starting at stdin:2
ERROR: Failed to precompile […] to “D:\.julia\compiled\v1.9\jl_8789.tmp”.
Stacktrace:
[1] error(s::String)
@ Base .\error.jl:35
[2] compilecache(pkg::Base.PkgId, path::String, internal_stderr::IO, internal_stdout::IO, keep_loaded_modules::Bool)
@ Base .\loading.jl:2260
[3] compilecache
@ .\loading.jl:2127 [inlined]
[4] _require(pkg::Base.PkgId, env::String)
@ Base .\loading.jl:1770
[5] _require_prelocked(uuidkey::Base.PkgId, env::String)
@ Base .\loading.jl:1625
[6] macro expansion
@ .\loading.jl:1613 [inlined]
[7] macro expansion
@ .\lock.jl:267 [inlined]
[8] require(into::Module, mod::Symbol)
@ Base .\loading.jl:1576
[9] eval
@ .\boot.jl:370 [inlined]
[10] eval
@ .\Base.jl:68 [inlined]
[11] repleval(m::Module, code::Expr, #unused#::String)
@ VSCodeServer D:.vscode\extensions\julialang.language-julia-1.60.2\scripts\packages\VSCodeServer\src\repl.jl:229
[12] (::VSCodeServer.var"#110#112"{Module, Expr, REPL.LineEditREPL, REPL.LineEdit.Prompt})()
@ VSCodeServer D:.vscode\extensions\julialang.language-julia-1.60.2\scripts\packages\VSCodeServer\src\repl.jl:192
[13] with_logstate(f::Function, logstate::Any)
@ Base.CoreLogging .\logging.jl:514
[14] with_logger
@ .\logging.jl:626 [inlined]
[15] (::VSCodeServer.var"#109#111"{Module, Expr, REPL.LineEditREPL, REPL.LineEdit.Prompt})()
@ VSCodeServer D:.vscode\extensions\julialang.language-julia-1.60.2\scripts\packages\VSCodeServer\src\repl.jl:193
[16] #invokelatest#2
@ .\essentials.jl:816 [inlined]
[17] invokelatest(::Any)
@ Base .\essentials.jl:813
[18] macro expansion
@ D:.vscode\extensions\julialang.language-julia-1.60.2\scripts\packages\VSCodeServer\src\eval.jl:34 [inlined]
[19] (::VSCodeServer.var"#62#63")()
@ VSCodeServer .\task.jl:514

Environment

st

[961ee093] ModelingToolkit v8.73.2

st -m

[47edcb42] ADTypes v0.2.5
⌅ [c3fe647b] AbstractAlgebra v0.33.0
[1520ce14] AbstractTrees v0.4.4
[7d9f7c33] Accessors v0.1.33
[79e6a3ab] Adapt v3.7.2
[ec485272] ArnoldiMethod v0.2.0
[4fba245c] ArrayInterface v7.6.1
[4c555306] ArrayLayouts v1.4.3
[e2ed5e7c] Bijections v0.1.6
[62783981] BitTwiddlingConvenienceFunctions v0.1.5
[2a0fbf3d] CPUSummary v0.2.4
[00ebfdb7] CSTParser v3.3.6
[49dc2e85] Calculus v0.5.1
[d360d2e6] ChainRulesCore v1.18.0
[fb6a15b2] CloseOpenIntervals v0.1.12
[861a8166] Combinatorics v1.0.2
[a80b9123] CommonMark v0.8.12
[38540f10] CommonSolve v0.2.4
[bbf7d656] CommonSubexpressions v0.3.0
[34da2185] Compat v4.10.1
[b152e2b5] CompositeTypes v0.1.3
[a33af91c] CompositionsBase v0.1.2
[2569d6c7] ConcreteStructs v0.2.3
[187b0558] ConstructionBase v1.5.4
[adafc99b] CpuId v0.3.1
[a8cc5b0e] Crayons v4.1.1
[9a962f9c] DataAPI v1.15.0
[864edb3b] DataStructures v0.18.15
[e2d170a0] DataValueInterfaces v1.0.0
[8bb1440f] DelimitedFiles v1.9.1
[2b5f629d] DiffEqBase v6.142.0
[459566f4] DiffEqCallbacks v2.34.0
[163ba53b] DiffResults v1.1.0
[b552c78f] DiffRules v1.15.1
[b4f34e82] Distances v0.10.11
[31c24e10] Distributions v0.25.104
[ffbed154] DocStringExtensions v0.9.3
⌅ [5b8099bc] DomainSets v0.6.7
[fa6b7ba4] DualNumbers v0.6.8
[7c1d4256] DynamicPolynomials v0.5.3
[4e289a0a] EnumX v1.0.4
[f151be2c] EnzymeCore v0.6.4
[d4d017d3] ExponentialUtilities v1.25.0
[e2ba6199] ExprTools v0.1.10
[7034ab61] FastBroadcast v0.2.8
[9aa1b823] FastClosures v0.3.2
[29a986be] FastLapackInterface v2.0.0
[1a297f60] FillArrays v1.9.2
[6a86dc24] FiniteDiff v2.21.1
[59287772] Formatting v0.4.2
[f6369f11] ForwardDiff v0.10.36
[069b7b12] FunctionWrappers v1.1.3
[77dc65aa] FunctionWrappersWrappers v0.1.3
[d9f16b24] Functors v0.4.5
[46192b85] GPUArraysCore v0.1.5
[c145ed77] GenericSchur v0.5.3
[c27321d9] Glob v1.3.1
[86223c79] Graphs v1.9.0
[0b43b601] Groebner v0.5.0
[d5909c97] GroupsCore v0.4.2
[3e5b6fbb] HostCPUFeatures v0.1.16
[34004b35] HypergeometricFunctions v0.3.23
[615f187c] IfElse v0.1.1
[d25df0c9] Inflate v0.1.4
[18e54dd8] IntegerMathUtils v0.1.2
[8197267c] IntervalSets v0.7.8
[3587e190] InverseFunctions v0.1.12
[92d709cd] IrrationalConstants v0.2.2
[82899510] IteratorInterfaceExtensions v1.0.0
[692b3bcd] JLLWrappers v1.5.0
[682c06a0] JSON v0.21.4
[98e50ef6] JuliaFormatter v1.0.43
[ccbc3e58] JumpProcesses v9.9.0
[ef3ab10e] KLU v0.4.1
[ba0b0d4f] Krylov v0.9.5
[b964fa9f] LaTeXStrings v1.3.1
[2ee39098] LabelledArrays v1.14.0
[984bce1d] LambertW v0.4.6
[23fbe1c1] Latexify v0.16.1
[73f95e8e] LatticeRules v0.0.1
[10f19ff3] LayoutPointers v0.1.15
[50d2b5c4] Lazy v0.15.1
[5078a376] LazyArrays v1.8.2
[d3d80556] LineSearches v7.2.0
[7ed4a6bd] LinearSolve v2.20.2
[2ab3a3ac] LogExpFunctions v0.3.26
[bdcacae8] LoopVectorization v0.12.166
[d8e11817] MLStyle v0.4.17
[1914dd2f] MacroTools v0.5.11
[d125e4d3] ManualMemory v0.1.8
[a3b82374] MatrixFactorizations v2.1.0
[bb5d69b7] MaybeInplace v0.1.1
[e1d29d7a] Missings v1.1.0
[961ee093] ModelingToolkit v8.73.2
[46d2c3a1] MuladdMacro v0.2.4
[102ac46a] MultivariatePolynomials v0.5.3
[d8a4904e] MutableArithmetics v1.4.0
[d41bc354] NLSolversBase v7.8.3
[2774e3e8] NLsolve v4.5.1
[77ba4419] NaNMath v1.0.2
[8913a72c] NonlinearSolve v3.0.1
[6fe1bfb0] OffsetArrays v1.12.10
[bac558e1] OrderedCollections v1.6.3
[1dea7af3] OrdinaryDiffEq v6.60.0
[90014a1f] PDMats v0.11.31
[65ce6f38] PackageExtensionCompat v1.0.2
[d96e819e] Parameters v0.12.3
[69de0a69] Parsers v2.8.0
[e409e4f3] PoissonRandom v0.4.4
[f517fe37] Polyester v0.7.9
[1d0040c9] PolyesterWeave v0.2.1
[d236fae5] PreallocationTools v0.4.12
[aea7be01] PrecompileTools v1.2.0
[21216c6a] Preferences v1.4.1
[27ebfcd6] Primes v0.5.5
[1fd47b50] QuadGK v2.9.1
[8a4e6c94] QuasiMonteCarlo v0.3.3
[fb686558] RandomExtensions v0.4.4
[e6cf234a] RandomNumbers v1.5.3
[3cdcf5f2] RecipesBase v1.3.4
[731186ca] RecursiveArrayTools v2.38.10
[f2c3362d] RecursiveFactorization v0.2.21
[189a3867] Reexport v1.2.2
[ae029012] Requires v1.3.0
[79098fc4] Rmath v0.7.1
[7e49a35a] RuntimeGeneratedFunctions v0.5.12
[fdea26ae] SIMD v3.4.6
[94e857df] SIMDTypes v0.1.0
[476501e8] SLEEFPirates v0.6.42
[0bca4576] SciMLBase v2.9.1
[e9a6253c] SciMLNLSolve v0.1.9
[c0aeaf25] SciMLOperators v0.3.7
[efcf1570] Setfield v1.1.1
[727e6d20] SimpleNonlinearSolve v1.0.2
[699a6c99] SimpleTraits v0.9.4
[ce78b400] SimpleUnPack v1.1.0
[ed01d8cd] Sobol v1.5.0
[a2af1166] SortingAlgorithms v1.2.0
[47a9eef4] SparseDiffTools v2.14.0
[e56a9233] Sparspak v0.3.9
[276daf66] SpecialFunctions v2.3.1
[aedffcd0] Static v0.8.8
[0d7ed370] StaticArrayInterface v1.4.1
[90137ffa] StaticArrays v1.7.0
[1e83bf80] StaticArraysCore v1.4.2
[82ae8749] StatsAPI v1.7.0
[2913bbd2] StatsBase v0.34.2
[4c63d2b9] StatsFuns v1.3.0
[7792a7ef] StrideArraysCore v0.5.2
[2efcf032] SymbolicIndexingInterface v0.2.2
[d1185830] SymbolicUtils v1.4.0
[0c5d862f] Symbolics v5.11.0
[3783bdb8] TableTraits v1.0.1
[bd369af6] Tables v1.11.1
[8290d209] ThreadingUtilities v0.5.2
[a759f4b9] TimerOutputs v0.5.23
[0796e94c] Tokenize v0.5.26
[d5829a12] TriangularSolve v0.1.20
[410a4b4d] Tricks v0.1.8
[781d530d] TruncatedStacktraces v1.4.0
[5c2747f8] URIs v1.5.1
[3a884ed6] UnPack v1.0.2
[1986cc42] Unitful v1.19.0
[a7c27f48] Unityper v0.1.5
[3d5dd08c] VectorizationBase v0.21.65
[19fa3120] VertexSafeGraphs v0.2.0
[1d5cc7b8] IntelOpenMP_jll v2024.0.0+0
[856f044c] MKL_jll v2024.0.0+0
[efe28fd5] OpenSpecFun_jll v0.5.5+0
[f50d1b31] Rmath_jll v0.4.0+0
[0dad84c5] ArgTools v1.1.1
[56f22d72] Artifacts
[2a0f44e3] Base64
[ade2ca70] Dates
[8ba89e20] Distributed
[f43a241f] Downloads v1.6.0
[7b1f6079] FileWatching
[9fa8497b] Future
[b77e0a4c] InteractiveUtils
[4af54fe1] LazyArtifacts
[b27032c2] LibCURL v0.6.3
[76f85450] LibGit2
[8f399da3] Libdl
[37e2e46d] LinearAlgebra
[56ddb016] Logging
[d6f4376e] Markdown
[a63ad114] Mmap
[ca575930] NetworkOptions v1.2.0
[44cfe95a] Pkg v1.9.0
[de0858da] Printf
[3fa0cd96] REPL
[9a3f8284] Random
[ea8e919c] SHA v0.7.0
[9e88b42a] Serialization
[1a1011a3] SharedArrays
[6462fe0b] Sockets
[2f01184e] SparseArrays
[10745b16] Statistics v1.9.0
[4607b0f0] SuiteSparse
[fa267f1f] TOML v1.0.3
[a4e569a6] Tar v1.10.0
[8dfed614] Test
[cf7118a7] UUIDs
[4ec0a83e] Unicode
[e66e0078] CompilerSupportLibraries_jll v1.0.2+0
[deac9b47] LibCURL_jll v7.84.0+0
[29816b5a] LibSSH2_jll v1.10.2+0
[c8ffd9c3] MbedTLS_jll v2.28.2+0
[14a3606d] MozillaCACerts_jll v2022.10.11
[4536629a] OpenBLAS_jll v0.3.21+4
[05823500] OpenLibm_jll v0.8.1+0
[bea87d4a] SuiteSparse_jll v5.10.1+6
[83775a58] Zlib_jll v1.2.13+0
[8e850b90] libblastrampoline_jll v5.7.0+0
[8e850ede] nghttp2_jll v1.48.0+0
[3f19e933] p7zip_jll v17.4.0+0

Julia Version 1.9.0
Commit 8e63055292 (2023-05-07 11:25 UTC)
Platform Info:
OS: Windows (x86_64-w64-mingw32)
CPU: 8 × 11th Gen Intel(R) Core™ i7-1185G7 @ 3.00GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-14.0.6 (ORCJIT, tigerlake)
Threads: 8 on 8 virtual cores
Environment:
JULIA_EDITOR = code

1 Like

Can you share the whole error?

Interesting. Can you please share ]st and your Julia version?

Sorry, I did not think it would require to go this far. I edited my original post with the environment information, and merge my last post with the stack trace. That should make everything more readable.

My guess is that you’re just having a versioning issue, but I’d need to see the full ]st and ]st -m to see what’s held back. That said, can you please clean out your environment and start from things that are updated?

Ok, so I removed all the code from the package and all other dependencies except ModelingToolkit, deleted my .julia/packages folder and instantiated. However, the problem still show up. I also tried to start a brand new environment from scratch without success. I put the new st and st -m outputs in my original post.

Is this a Julia v1.9.0 bug? What happens if you update to the latest patch release v1.9.3?

I just retested this in a brand new v1.9.4. No luck with that either… I have just a bare module with

module TestMtk

using ModelingToolkit

@mtkmodel MassXYZ begin
    # Definition of the model icon
    #@icon "..."

    # Definition of the model parameters
    @parameters begin
        m, [description = "Mass of the moving rigid body in [kg]"]
        v_0[1:3] = zeros(3), [description = "Initial velocities in [m/s] along X, Y and Z axes of the rigid body"]
        s_0[1:3] = zeros(3), [description = "Initial positions in [m] along X, Y and Z axes of the rigid body"]
    end

    @variables begin
        (s(t))[1:3] = s_0, [description = "Positions in [m] along X, Y and Z axes of the rigid body"]
        (v(t))[1:3] = v_0, [description = "Velocities in [m/s] along X, Y and Z axes of the rigid body"]
    end

    @equations begin
        D.(s) .~ v
    end
end


end # module TestMtk

but it still fails.

Okay that’s weird, and your registry is up to date? Try wiping your .julia and starting that over again. Maybe something in the caches got messed up.

Hum, so I followed you advice and wiped out my .julia. Took a bit of time, but after red-downloading and recompiling everything, I get the same error unfortunately.
The registry is up to date as far as I can tell.
However, I think I managed to narrow down a bit the error.

If I remove further lines form MassXYZ and do

@mtkmodel MassXYZ begin
    @parameters begin
        m
        v_0[1:3]
        s_0[1:3]
    end
end

it fails the same but if I remove the 1:3 and leave v_0 and s_0 as scalars, it compiles fine.

Wait, I thought you said precompilation fails, which means your code was irrelevant and it’s just using ModelingToolkit? You mean precompilation is not failing but your code is failing in some way? What’s the MWE then?

Okay I see, I fixed the title to be something more clear. So your precompilation works, it’s just when you use an array symbolic equals expression inside of the macro it fails, because of how dottable is handled. There are ways to work around this (like https://github.com/SciML/NeuralPDE.jl/blob/80a7849a2f66a04dfb91ff84a492a6c3aed42239/src/symbolic_utilities.jl#L3C1-L51), but the solution down the line needs to be that non-dotted ~ is supported. Can you open an issue?

Well, I am not very familiar with how precompilation works, but the error I mentioned in my original post definitely occurred when trying to precompile my package.
As far as I understood how @mtkmodel should be used, the goal is to be able to create new models in packages, which is precisely what I attempted there.

I think I see what you mean by the broadcasting problem, but I don’t understand why it is relevant here. In my test module,after stripping everything not necessary to produce the error, I just have

module TestModule
using ModelingToolkit

@mtkmodel MassXYZ begin
    @parameters begin
        m
        v_0[1:3]
        s_0[1:3]
    end
end

end

so there is no explicit ~ call, no @variables or @equations. Do you mean that having vector-valued parameters implicitly calls broadcasting operators ?

The additional thing is that if I

  • comment this piece of code
  • load the package
  • un comment the piece of code
  • revise

the package works even with vector valued parameters

This is the territory of a bug not a user issue. Just open an issue and we’ll dive in.

Ok, I opened an issue here : Broadcast inside of `@mtkmodel` macro causes precompilation failures when put into a package · Issue #2377 · SciML/ModelingToolkit.jl · GitHub

1 Like

@ChrisRackauckas I’ve seen that the issue has been worked on ! Thanks for the support !

In the meantime, I’ve been working on the code without integrating it into a package for the moment. And I have another question that may be related to broadcasting.
I saw that it is possible to define initial conditions with dictionaries associating a key made from a problem variable and an initial condition value such as

prob = ODEProblem(fol, [fol.x => 0.0], (0.0, 10.0), [fol.τ => 3.0])

from Getting Started with ModelingToolkit.jl · ModelingToolkit.jl

Should this work with vector-valued initial conditions ?

Symbolic vectors are one of the top features being worked on but they still have a lot more to do. In the meantime, do things like collect(x) to turn them from the O(1) representation to the O(n) vector of symbols representation as a workaround as necessary, though over time we are trying to iron out all of the cases where something could take a vector instead.

Ok, thanks for the advice.

So I guess somethign like

initial_conditions = reduce(vcat,[collect(mass.s .=> [0.0, 1.0, 1.0]), collect(mass.v .=> [1.0, 1.0, 0.0])])

is the way to go isn’t it ? When multiple vectors have first to be collected, then gathered together through reduce

EDIT : This was actually brought up in 2D arrays with ModelingToolkit - #7 by baggepinnen

Would you mind having a look at Understanding ModelingToolkit connectors and components - #2 by BambOoxX ? It looks like another broadcasting problem, but I figure you are the best person to say so.