quarto-dev / quarto-cli

Open-source scientific and technical publishing system built on Pandoc.
https://quarto.org
Other
3.77k stars 308 forks source link

Julia code (using ModelingToolkit package) that correctly runs in Julia's REPL fails in Quarto #10837

Closed hurak closed 1 hour ago

hurak commented 19 hours ago

Bug description

Julia code (using ModelingToolkit package) that correctly runs in Julia's REPL fails when run from within a Quarto qmd document. It is hard for me to decipher if it is Quarto or ModelingToolkit.jl package (used in the code) that is to blame here. But since the code runs smoothly outside Quarto (in Julia's REPL), I assume that the problem may be on Quarto's side.

Steps to reproduce

---
title: "Demonstration of the error"
format: html
engine: julia
---

``` {julia}
using ModelingToolkit, Plots, OrdinaryDiffEq
using ModelingToolkit: t_nounits as t
using ModelingToolkit: D_nounits as D

function plant(; name)
    @variables x₁(t)=0 x₂(t) = 0 u(t) y(t)
    eqs = [D(x₁) ~ x₂
           D(x₂) ~ -0.2x₂ + u
           y ~ x₁ + x₂]
    ODESystem(eqs, t; name = name)
end

function controller(; name) 
    @variables x(t)=0 u(t) y(t)
    eqs = [D(x) ~ -x + u
           y ~ x]
    ODESystem(eqs, t, name = name)
end

@named C = controller()
@named P = plant()

t_of_step = 1.0
r(t) = t >= t_of_step ? 1.0 : 0.0
@register_symbolic r(t)

connections = [C.u ~ r(t) - P.y
               C.y ~ P.u]

@named T = ODESystem(connections, t, systems = [C, P])

T = structural_simplify(T)

using DifferentialEquations: solve
prob = ODEProblem(complete(T), [], (0.0, 30.0), [])
sol = solve(prob, Tsit5(), saveat = 0.1)

Expected behavior

The code just should run and successfully finish.

Actual behavior

Instead of finishing the computation, an error message is generated:

➜  www quarto preview /Users/hurak/Documents/vyuka/hybrid/www/demo_error.qmd --no-browser --no-watch-inputs
pandoc 
  to: html
  output-file: demo_error.html
  standalone: true
  title-prefix: B(E)3M35HYS – Hybrid systems
  section-divs: true
  html-math-method: mathjax
  wrap: none
  default-image-extension: png
  css:
    - styles.css
  toc: true

metadata
  document-css: false
  link-citations: true
  date-format: long
  lang: en
  theme: cosmo
  title: Demonstration of the error

Output created: _site/demo_error.html

Preparing to preview
[1/1] classes_reset.qmd
Running [1/3] at line 74:  using OrdinaryDiffEq
Running [2/3] at line 119:  using OrdinaryDiffEq
Running [3/3] at line 166:  using ModelingToolkit, Plots, OrdinaryDiffEq
ERROR: Julia server returned error after receiving "run" command:
Failed to run notebook: /Users/hurak/Documents/vyuka/hybrid/www/classes_reset.qmd
ERROR: EvaluationError: Encountered 1 error during evaluation

Error 1 of 1
@ /Users/hurak/Documents/vyuka/hybrid/www/classes_reset.qmd:166
MethodError: no method matching occursin(::Vector{Float64}, ::Vector{Float64})

Closest candidates are:
  occursin(!Matched::SymbolicUtils.Symbolic, ::Any)
   @ SymbolicUtils ~/.julia/packages/SymbolicUtils/5UmB7/src/substitute.jl:51
  occursin(::Any, !Matched::SymbolicUtils.Symbolic)
   @ SymbolicUtils ~/.julia/packages/SymbolicUtils/5UmB7/src/substitute.jl:50
  occursin(::Any)
   @ Base strings/search.jl:724

Stacktrace:
 [1] _occursin(needle::Vector{Float64}, haystack::SymbolicUtils.BasicSymbolic{Real})
   @ SymbolicUtils ~/.julia/packages/SymbolicUtils/5UmB7/src/substitute.jl:58
 [2] occursin
   @ ~/.julia/packages/SymbolicUtils/5UmB7/src/substitute.jl:50 [inlined]
 [3] check_variables(dvs::Vector{SymbolicUtils.BasicSymbolic{Real}}, iv::Vector{Float64})
   @ ModelingToolkit ~/.julia/packages/ModelingToolkit/UXr3S/src/utils.jl:151
 [4] ODESystem(tag::UInt64, deqs::Vector{Equation}, iv::Vector{Float64}, dvs::Vector{SymbolicUtils.BasicSymbolic{Real}}, ps::Vector{Any}, tspan::Nothing, var_to_name::Dict{Any, Any}, ctrls::Vector{Any}, observed::Vector{Equation}, tgrad::Base.RefValue{Vector{Num}}, jac::Base.RefValue{Any}, ctrl_jac::Base.RefValue{Any}, Wfact::Base.RefValue{Matrix{Num}}, Wfact_t::Base.RefValue{Matrix{Num}}, name::Symbol, systems::Vector{ODESystem}, defaults::Dict{Any, Any}, guesses::Dict{Any, Any}, torn_matching::Nothing, initializesystem::Nothing, initialization_eqs::Vector{Equation}, schedule::Nothing, connector_type::Nothing, preface::Nothing, cevents::Vector{ModelingToolkit.SymbolicContinuousCallback}, devents::Vector{ModelingToolkit.SymbolicDiscreteCallback}, parameter_dependencies::Vector{Equation}, metadata::Nothing, gui_metadata::Nothing, tearing_state::Nothing, substitutions::Nothing, complete::Bool, index_cache::Nothing, discrete_subsystems::Nothing, solved_unknowns::Nothing, split_idxs::Nothing, parent::Nothing; checks::Bool)
   @ ModelingToolkit ~/.julia/packages/ModelingToolkit/UXr3S/src/systems/diffeqs/odesystem.jl:188
 [5] ODESystem(deqs::Vector{Equation}, iv::Vector{Float64}, dvs::Vector{Any}, ps::Vector{Any}; controls::Vector{Num}, observed::Vector{Equation}, systems::Vector{ODESystem}, tspan::Nothing, name::Symbol, default_u0::Dict{Any, Any}, default_p::Dict{Any, Any}, defaults::Dict{Any, Any}, guesses::Dict{Any, Any}, initializesystem::Nothing, initialization_eqs::Vector{Equation}, schedule::Nothing, connector_type::Nothing, preface::Nothing, continuous_events::Nothing, discrete_events::Nothing, parameter_dependencies::Vector{Equation}, checks::Bool, metadata::Nothing, gui_metadata::Nothing)
   @ ModelingToolkit ~/.julia/packages/ModelingToolkit/UXr3S/src/systems/diffeqs/odesystem.jl:269
 [6] ODESystem(eqs::Vector{Equation}, iv::Vector{Float64}; kwargs::@Kwargs{name::Symbol})
   @ ModelingToolkit ~/.julia/packages/ModelingToolkit/UXr3S/src/systems/diffeqs/odesystem.jl:343
 [7] controller(; name::Symbol)
   @ Main.Notebook ~/Documents/vyuka/hybrid/www/classes_reset.qmd:184
 [8] top-level scope
   @ ~/.julia/packages/ModelingToolkit/UXr3S/src/systems/abstractsystem.jl:1949

ERROR: Internal julia server error

Stack trace:
    at writeJuliaCommand (file:///Applications/quarto/bin/quarto.js:42026:19)
    at eventLoopTick (ext:core/01_core.js:153:7)
    at async executeJulia (file:///Applications/quarto/bin/quarto.js:41920:22)
    at async Object.execute (file:///Applications/quarto/bin/quarto.js:41657:20)
    at async renderExecute (file:///Applications/quarto/bin/quarto.js:78509:27)
    at async renderFileInternal (file:///Applications/quarto/bin/quarto.js:78677:43)
    at async renderFiles (file:///Applications/quarto/bin/quarto.js:78545:17)
    at async renderProject (file:///Applications/quarto/bin/quarto.js:78955:25)
    at async serveProject (file:///Applications/quarto/bin/quarto.js:100603:24)
    at async Command.actionHandler (file:///Applications/quarto/bin/quarto.js:101298:9)

Your environment

Quarto check output

Quarto 1.6.10 [✓] Checking versions of quarto binary dependencies... Pandoc version 3.2.0: OK Dart Sass version 1.70.0: OK Deno version 1.41.0: OK Typst version 0.11.0: OK [✓] Checking versions of quarto dependencies......OK [✓] Checking Quarto installation......OK Version: 1.6.10 Path: /Applications/quarto/bin

[✓] Checking tools....................OK TinyTeX: (not installed) Chromium: (not installed)

[✓] Checking LaTeX....................OK Using: Installation From Path Path: /Library/TeX/texbin Version: 2024

[✓] Checking basic markdown render....OK

[✓] Checking Python 3 installation....OK Version: 3.11.10 Path: /opt/homebrew/opt/python@3.11/bin/python3.11 Jupyter: 5.3.1 Kernels: julia-1.10

  NOTE: No Jupyter kernel for Python found

[✓] Checking R installation...........(None)

  Unable to locate an installed version of R.
  Install R from https://cloud.r-project.org/
cscheid commented 19 hours ago

I don't think this is a Quarto problem; I think you're running different versions of Julia in your REPL and in Quarto, and that is causing the problem. See

MethodError: no method matching occursin(::Vector{Float64}, ::Vector{Float64})

Closest candidates are:
  occursin(!Matched::SymbolicUtils.Symbolic, ::Any)
   @ SymbolicUtils ~/.julia/packages/SymbolicUtils/5UmB7/src/substitute.jl:51
  occursin(::Any, !Matched::SymbolicUtils.Symbolic)
   @ SymbolicUtils ~/.julia/packages/SymbolicUtils/5UmB7/src/substitute.jl:50
  occursin(::Any)
   @ Base strings/search.jl:724

This is not Quarto's code in any way; it's something with SymbolicUtils in that specific environment.

hurak commented 18 hours ago

Thanks. But I would swear I only have a single version of Julia on my machine.

I am also trying to pay attention to the environment within which the code is run. And it is the same in both cases.

And I am also trying to use both the Julia's REPL within a classical terminal, and Julia's REPL within vscode. No difference.

I have no clue how to debug the problem.

My full setup contains several code cells and I started to suspect that it may be some kind of scope issue. But here in the demo problem I only have a single code cell. And I get the error even after fresh start of the vscode and quarto.

cscheid commented 18 hours ago

I can't run your code so I can't attempt to repro:

$ quarto render issue-10837.qmd
Running [1/1] at line 7:  using ModelingToolkit, Plots, OrdinaryDiffEq
ERROR: Julia server returned error after receiving "run" command:
Failed to run notebook: /Users/cscheid/Desktop/daily-log/2024/09/18/issue-10837.qmd
ERROR: EvaluationError: Encountered 1 error during evaluation

Error 1 of 1
@ /Users/cscheid/Desktop/daily-log/2024/09/18/issue-10837.qmd:7
UndefVarError: `t` not defined
Stacktrace:
 [1] controller(; name::Symbol)
   @ Main.Notebook ~/Desktop/daily-log/2024/09/18/issue-10837.qmd:21
 [2] top-level scope
   @ ~/.julia/packages/ModelingToolkit/Nsxrf/src/systems/abstractsystem.jl:1135
hurak commented 18 hours ago

OK, feel free to close it here. Probably you are right that it is unrelated to Quarto.

For completeness, the first few lines of my Julia code are actually copy-paste from an example in the ModelingToolkit documentation (using ModelingToolkit: t_nounits as t), so I am surprised to read that it errors on your side.

But I got your advice to pay attention to the environments. I need to double check I am setting the environment right when generating preview for Quarto document.

Thanks again and let's close it here.

cscheid commented 18 hours ago

Before we give up, can you try the following document?

---
title: "Demonstration of the error"
format: html
engine: julia
---

```{julia}
using Pkg
using ModelingToolkit, Plots, OrdinaryDiffEq
Pkg.status("ModelingToolkit")
Pkg.status("Plots")
Pkg.status("OrdinaryDiffEq")

I get this:

<img width="1551" alt="image" src="https://github.com/user-attachments/assets/03cb462b-f613-4754-b465-f541b393e7c9">

Specifically, the text output is

Status ~/.julia/scratchspaces/4c0109c6-14e9-4c88-93f0-2b974d3468f4/loader.1.9.2.1S0v7B8BA8K/Project.toml ⌃ [961ee093] ModelingToolkit v8.76.0 Info Packages marked with ⌃ have new versions available and may be upgradable. Status ~/.julia/scratchspaces/4c0109c6-14e9-4c88-93f0-2b974d3468f4/loader.1.9.2.1S0v7B8BA8K/Project.toml ⌃ [91a5bcdd] Plots v1.39.0 Info Packages marked with ⌃ have new versions available and may be upgradable. Status ~/.julia/scratchspaces/4c0109c6-14e9-4c88-93f0-2b974d3468f4/loader.1.9.2.1S0v7B8BA8K/Project.toml ⌅ [1dea7af3] OrdinaryDiffEq v6.66.0 Info Packages marked with ⌅ have new versions available but compatibility constraints restrict them from upgrading. To see why use status --outdated

hurak commented 17 hours ago

I suspect that it all must be due to my troubles to correctly specify the Julia env for Quarto.

I have just created a brand new folder tmp into which I copied the qmd file you propose. Then I change to this directory, start Julia, activate the . environment and install the three packages. As a result, the current Julia env is now occupied by the three packages:

(tmp) pkg> st
Status `~/Documents/tmp/Project.toml`
  [961ee093] ModelingToolkit v9.40.0
  [1dea7af3] OrdinaryDiffEq v6.89.0
  [91a5bcdd] Plots v1.40.8

Exiting Julia, but still within this folder I call on the command line ➜ tmp quarto preview demo_error_2.qmd

This is what I get

Running [1/1] at line 7:  using Pkg
ERROR: Julia server returned error after receiving "run" command:
Failed to run notebook: /Users/hurak/Documents/tmp/demo_error_2.qmd
ERROR: EvaluationError: Encountered 1 error during evaluation

Error 1 of 1
@ /Users/hurak/Documents/tmp/demo_error_2.qmd:7
ArgumentError: Package ModelingToolkit not found in current path.
- Run `import Pkg; Pkg.add("ModelingToolkit")` to install the ModelingToolkit package.
Stacktrace:
  [1] macro expansion
    @ ./loading.jl:1772 [inlined]
  [2] macro expansion
    @ ./lock.jl:267 [inlined]
  [3] __require(into::Module, mod::Symbol)
    @ Base ./loading.jl:1753
  [4] #invoke_in_world#3
    @ ./essentials.jl:926 [inlined]
  [5] invoke_in_world
    @ ./essentials.jl:923 [inlined]
  [6] require(into::Module, mod::Symbol)
    @ Base ./loading.jl:1746
  [7] eval
    @ ./boot.jl:385 [inlined]
  [8] (::QuartoNotebookWorker.var"#17#18"{Module, Expr})()
    @ QuartoNotebookWorker ~/.julia/packages/QuartoNotebookRunner/VUFgu/src/QuartoNotebookWorker/src/render.jl:219
  [9] (::QuartoNotebookWorker.Packages.IOCapture.var"#5#9"{DataType, QuartoNotebookWorker.var"#17#18"{Module, Expr}, IOContext{Base.PipeEndpoint}, IOContext{Base.PipeEndpoint}, IOContext{IOStream}, IOContext{Base.PipeEndpoint}})()
    @ QuartoNotebookWorker.Packages.IOCapture ~/.julia/packages/IOCapture/Y5rEA/src/IOCapture.jl:170
 [10] with_logstate(f::Function, logstate::Any)
    @ Base.CoreLogging ./logging.jl:515
 [11] with_logger
    @ ./logging.jl:627 [inlined]
 [12] capture(f::QuartoNotebookWorker.var"#17#18"{Module, Expr}; rethrow::Type, color::Bool, passthrough::Bool, capture_buffer::IOBuffer, io_context::Vector{Pair{Symbol, Any}})
    @ QuartoNotebookWorker.Packages.IOCapture ~/.julia/packages/IOCapture/Y5rEA/src/IOCapture.jl:167
 [13] include_str(mod::Module, code::String; file::String, line::Int64, cell_options::Dict{String, Any})
    @ QuartoNotebookWorker ~/.julia/packages/QuartoNotebookRunner/VUFgu/src/QuartoNotebookWorker/src/render.jl:199
 [14] #invokelatest#2
    @ ./essentials.jl:894 [inlined]
 [15] invokelatest
    @ ./essentials.jl:889 [inlined]
 [16] #6
    @ ~/.julia/packages/QuartoNotebookRunner/VUFgu/src/QuartoNotebookWorker/src/render.jl:18 [inlined]
 [17] with_inline_display(f::QuartoNotebookWorker.var"#6#7"{String, String, Int64, Dict{String, Any}}, cell_options::Dict{String, Any})
    @ QuartoNotebookWorker ~/.julia/packages/QuartoNotebookRunner/VUFgu/src/QuartoNotebookWorker/src/InlineDisplay.jl:26
 [18] _render_thunk(thunk::Function, code::String, cell_options::Dict{String, Any}, is_expansion_ref::Base.RefValue{Bool}; inline::Bool)
    @ QuartoNotebookWorker ~/.julia/packages/QuartoNotebookRunner/VUFgu/src/QuartoNotebookWorker/src/render.jl:42
 [19] _render_thunk
    @ ~/.julia/packages/QuartoNotebookRunner/VUFgu/src/QuartoNotebookWorker/src/render.jl:35 [inlined]
 [20] #render#5
    @ ~/.julia/packages/QuartoNotebookRunner/VUFgu/src/QuartoNotebookWorker/src/render.jl:15 [inlined]
 [21] render(code::String, file::String, line::Int64, cell_options::Dict{String, Any})
    @ QuartoNotebookWorker ~/.julia/packages/QuartoNotebookRunner/VUFgu/src/QuartoNotebookWorker/src/render.jl:1
 [22] render(::String, ::Vararg{Any}; kwargs::@Kwargs{})
    @ Main ~/.julia/packages/QuartoNotebookRunner/VUFgu/src/worker.jl:14
 [23] top-level scope
    @ none:1

ERROR: Internal julia server error

Stack trace:
    at writeJuliaCommand (file:///Applications/quarto/bin/quarto.js:42026:19)
    at eventLoopTick (ext:core/01_core.js:153:7)
    at async executeJulia (file:///Applications/quarto/bin/quarto.js:41920:22)
    at async Object.execute (file:///Applications/quarto/bin/quarto.js:41657:20)
    at async renderExecute (file:///Applications/quarto/bin/quarto.js:78509:27)
    at async renderFileInternal (file:///Applications/quarto/bin/quarto.js:78677:43)
    at async renderFiles (file:///Applications/quarto/bin/quarto.js:78545:17)
    at async render (file:///Applications/quarto/bin/quarto.js:83409:21)
    at async renderForPreview (file:///Applications/quarto/bin/quarto.js:84436:26)
    at async render (file:///Applications/quarto/bin/quarto.js:84319:29)

It appears that by just calling quarto preview this way, internally called Julia is not aware of the environment of the current folder.

I am trying to get from https://quarto.org/docs/computations/julia.html#engine-options how the env is specified. But I am still failing.

Robinlovelace commented 8 hours ago

In case of use/interest, I'm hitting a similar issue with Julia in Quarto, with the following error message that seems to come from Quarto not Julia:

ERROR: Error
    at renderFiles (file:///opt/quarto/bin/quarto.js:78081:29)
    at eventLoopTick (ext:core/01_core.js:153:7)
    at async renderProject (file:///opt/quarto/bin/quarto.js:78479:25)
    at async renderForPublish (file:///opt/quarto/bin/quarto.js:109339:33)
    at async renderForPublish (file:///opt/quarto/bin/quarto.js:104866:24)
    at async Object.publish1 [as publish] (file:///opt/quarto/bin/quarto.js:105351:26)
    at async publishSite (file:///opt/quarto/bin/quarto.js:109376:38)
    at async publish7 (file:///opt/quarto/bin/quarto.js:109595:61)
    at async doPublish (file:///opt/quarto/bin/quarto.js:109555:13)
    at async publishAction (file:///opt/quarto/bin/quarto.js:109566:9)
Error: Process completed with exit code 1.

Reproducible example on GitHub actions, apologies it's not a minimal example, just came across this and thought it may be of use/interest. Cc @evetion and other Julia devs FYI https://github.com/Robinlovelace/cross_language_projects/actions/runs/10934984247/job/30355961740#step:9:89

cderv commented 6 hours ago

@hurak FWIW I can't reproduce. it work ok on my side.

It appears that by just calling quarto preview this way, internally called Julia is not aware of the environment of the current folder.

When do you modify Julia environment, you should try quarto render index.qmd --execute-daemon-restart to force a restart. This is because of worker process reuse (https://quarto.org/docs/computations/julia.html#worker-process-reuse) which could have side effect when the environment is changing.


@Robinlovelace Your problem is different because you are not using the engine: julia from Quarto. In your index.qmd, you are running a julia cell (and a python cell) within a document containing also r cell - so engine: knitr will be used. For Julia with knitr, the setup could be a bit different and you have to follow / check with JuliaCall doc

If you need help on this, please open a new discussion. Or ping me in https://github.com/Robinlovelace/cross_language_projects/issues/5

Robinlovelace commented 5 hours ago

Many thanks! This looks really useful: https://quarto.org/docs/computations/julia.html

hurak commented 3 hours ago

Thanks, Christophe @cderv. The --execute-daemon-restart helps get the stripped code (the one with just using the packages and printing their status) running. As I documented above, before this forced restart of the daamon, Julia run "within" Quarto could not even find the ModelingToolkit package. Now it can.

But I am still failing to run the original code with which I started this issue. Perhaps it is really more of a Julia issue. But the confusing thing is that it runs with no problems in REPL within the same environment. I will dig more.

cderv commented 3 hours ago

But I am still failing to run the original code with which I started this issue. Perhaps it is really more of a Julia issue. But the confusing thing is that it runs with no problems in REPL within the same environment. I will dig more.

To be clear, when I say I can't reproduce, I man I ran your initial example (from https://github.com/quarto-dev/quarto-cli/issues/10837#issue-2534616412) following the steps I mentioned.

As documented in the website, julia will run with --project=@.. This means that if a Project.toml is correctly in your Quarto project, it should be used.

hurak commented 1 hour ago

I can confirm the ultimate solution was indeed the use of --execute-daemon-restart. Many thanks.