Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non-obvious Julia errors #92

Open
Samuel-amap opened this issue Sep 20, 2024 · 8 comments
Open

Non-obvious Julia errors #92

Samuel-amap opened this issue Sep 20, 2024 · 8 comments
Labels
documentation Improvements or additions to documentation

Comments

@Samuel-amap
Copy link
Collaborator

If PlantSimEngine is intended for use by non-programmers, or people with very little background in Julia, it might be worth adding some specific pages to the documentation that help novices quickly identify common sources of errors, or learn about specific quirks of the language that might be non-obvious pitfalls, that PlantSimEngine usage makes more likely to encounter.

Perhaps examples of errors that could be worth clarifying, documenting or anticipating could be added to this issue in the future.

@Samuel-amap Samuel-amap added the documentation Improvements or additions to documentation label Sep 20, 2024
@Samuel-amap
Copy link
Collaborator Author

One error that is not obvious (and can presumably be improved upon) happens when a modeler doesn't declare a single-variable tuple properly, eg typing :

function PlantSimEngine.inputs_(::HardDepSameScaleAvalModel)
    (e2 = -Inf)
end

instead of :

function PlantSimEngine.inputs_(::HardDepSameScaleAvalModel)
    (e2 = -Inf,)
end

[ERROR: MethodError: no method matching merge(::Float64, ::@NamedTuple{g::Float64})

Closest candidates are:
merge(::NamedTuple{()}, ::NamedTuple)
@ Base namedtuple.jl:337
merge(::NamedTuple{an}, ::NamedTuple{bn}) where {an, bn}
@ Base namedtuple.jl:324
merge(::NamedTuple, ::NamedTuple, NamedTuple...)
@ Base namedtuple.jl:343
...

Stacktrace:
[1] variables_multiscale(node::PlantSimEngine.HardDependencyNode{…}, organ::String, vars_mapping::Dict{…}, st::@NamedTuple{})
@ PlantSimEngine ......./src/dependencies/dependency_graph.jl:107
[2] (::PlantSimEngine.var"#92#103"{Dict{…}, String})(x::PlantSimEngine.HardDependencyNode{HardDepSameScaleAvalModel})
@ PlantSimEngine ......./src/dependencies/hard_dependencies.jl:152
[3] traverse_dependency_graph!(node::PlantSimEngine.HardDependencyNode{…}, f::PlantSimEngine.var"#92#103"{…}, var::Vector{…}; visit_hard_dep::Bool)
@ PlantSimEngine ......./src/dependencies/traversal.jl:122
[4] traverse_dependency_graph!(node::PlantSimEngine.HardDependencyNode{…}, f::Function, var::Vector{…})
@ PlantSimEngine ......./src/dependencies/traversal.jl:116
[5] hard_dependencies(mapping::Dict{String, Tuple{Any, Any}}; verbose::Bool)
@ PlantSimEngine ......./src/dependencies/hard_dependencies.jl:152
[6] to_initialize(mapping::Dict{String, Tuple{Any, Any}}, graph::Nothing)
@ PlantSimEngine ......./src/processes/model_initialisation.jl:154
[7] to_initialize(mapping::Dict{String, Tuple{Any, Any}})
@ PlantSimEngine ......./src/processes/model_initialisation.jl:147
[8] top-level scope
@ .........mypkg/test_pse.jl:53](url)

This specific error is on the modeler side, but the user might encounter such problems when declaring statuses, or global simulation outputs. It seems at the very least worth documenting.

@Samuel-amap
Copy link
Collaborator Author

Samuel-amap commented Oct 17, 2024

#77 also relates to an unintuitive error for the user

Here's another one which could be slightly improved upon :

If there is a need to collect variables at two different scales, and one scale is completely absent from the mapping, the error occurs on the Julia side :

"E2" => (
        #HardScaleEchelle2Model(),
        MultiScaleModel(
        model = HardDepSameScaleEchelle2Model(),
        mapping = [:c => "E1" => :c,], #:e3 => "E3" => :e3, :f3 => "E3" => :f3,], 
        ),
        HardDepSameScaleAvalModel(),

       #Status(a = 1.0),
    ),
# No E3 in the mapping !

Exception has occurred: KeyError
*
KeyError: key "E3" not found
Stacktrace:
[1] hard_dependencies(mapping::Dict{String, Tuple{Any, Any}}; verbose::Bool)
@ PlantSimEngine ......./src/dependencies/hard_dependencies.jl:175
[2] kwcall(::@NamedTuple{verbose::Bool}, ::typeof(PlantSimEngine.hard_dependencies), mapping::Dict{String, Tuple{Any, Any}})
@ PlantSimEngine ......./src/dependencies/hard_dependencies.jl:115
[3] init_simulation(mtg::Node{NodeMTG, Dict{Symbol, Any}}, mapping::Dict{String, Tuple{Any, Any}}; nsteps::Int64, outputs::Dict{String, Tuple{Symbol, Symbol, Vararg{Symbol}}}, type_promotion::Nothing, check::Bool, verbose::Bool)
@ PlantSimEngine ......./src/mtg/initialisation.jl:310
[4] kwcall(::@NamedTuple{nsteps::Int64, outputs::Dict{String, Tuple{Symbol, Symbol, Vararg{Symbol}}}, type_promotion::Nothing, check::Bool, verbose::Bool}, ::typeof(PlantSimEngine.init_simulation), mtg::Node{NodeMTG, Dict{Symbol, Any}}, mapping::Dict{String, Tuple{Any, Any}})
@ PlantSimEngine ......./src/mtg/initialisation.jl:307
[5] PlantSimEngine.GraphSimulation(graph::Node{NodeMTG, Dict{Symbol, Any}}, mapping::Dict{String, Tuple{Any, Any}}; nsteps::Int64, outputs::Dict{String, Tuple{Symbol, Symbol, Vararg{Symbol}}}, type_promotion::Nothing, check::Bool, verbose::Bool)
@ PlantSimEngine ......./src/mtg/GraphSimulation.jl:31
[6] kwcall(::@NamedTuple{nsteps::Int64, check::Bool, outputs::Dict{String, Tuple{Symbol, Symbol, Vararg{Symbol}}}}, ::Type{PlantSimEngine.GraphSimulation}, graph::Node{NodeMTG, Dict{Symbol, Any}}, mapping::Dict{String, Tuple{Any, Any}})
@ PlantSimEngine ......./src/mtg/GraphSimulation.jl:30
[7] run!(object::Node{NodeMTG, Dict{Symbol, Any}}, mapping::Dict{String, Tuple{Any, Any}}, meteo::TimeStepTable{Atmosphere{(:date, :duration, :T, :Wind, :P, :Rh, :Precipitations, :Cₐ, :e, :eₛ, :VPD, :ρ, :λ, :γ, :ε, :Δ, :clearness, :Ri_SW_f, :Ri_PAR_f, :Ri_NIR_f, :Ri_TIR_f, :Ri_custom_f), Tuple{Dates.DateTime, Dates.Second, Vararg{Float64, 20}}}}, constants::Constants{Float64}, extra::Nothing; nsteps::Nothing, outputs::Dict{String, Tuple{Symbol, Symbol, Vararg{Symbol}}}, check::Bool, executor::ThreadedEx{@NamedTuple{}})
@ PlantSimEngine ......./src/run.jl:331
[8] kwcall(::@NamedTuple{outputs::Dict{String, Tuple{Symbol, Symbol, Vararg{Symbol}}}}, ::typeof(run!), object::Node{NodeMTG, Dict{Symbol, Any}}, mapping::Dict{String, Tuple{Any, Any}}, meteo::TimeStepTable{Atmosphere{(:date, :duration, :T, :Wind, :P, :Rh, :Precipitations, :Cₐ, :e, :eₛ, :VPD, :ρ, :λ, :γ, :ε, :Δ, :clearness, :Ri_SW_f, :Ri_PAR_f, :Ri_NIR_f, :Ri_TIR_f, :Ri_custom_f), Tuple{Dates.DateTime, Dates.Second, Vararg{Float64, 20}}}}, constants::Constants{Float64}, extra::Nothing)
@ PlantSimEngine ......./src/run.jl:318
[9] kwcall(::@NamedTuple{outputs::Dict{String, Tuple{Symbol, Symbol, Vararg{Symbol}}}}, ::typeof(run!), object::Node{NodeMTG, Dict{Symbol, Any}}, mapping::Dict{String, Tuple{Any, Any}}, meteo::TimeStepTable{Atmosphere{(:date, :duration, :T, :Wind, :P, :Rh, :Precipitations, :Cₐ, :e, :eₛ, :VPD, :ρ, :λ, :γ, :ε, :Δ, :clearness, :Ri_SW_f, :Ri_PAR_f, :Ri_NIR_f, :Ri_TIR_f, :Ri_custom_f), Tuple{Dates.DateTime, Dates.Second, Vararg{Float64, 20}}}})
@ PlantSimEngine ......./src/run.jl:318
[10] top-level scope
@ mypkgtest_pse.jl:1

@Samuel-amap
Copy link
Collaborator Author

Another non-intuitive error :

If a user declares inputs or outputs as empty (), the error Julia reports will be internal to PSE, when merging variables. (,) returns a syntax error.

The correct way of declaring an empty set of inputs or outputs is NamedTuple(). Definitely not obvious to a non-Julia programmer, and certainly worth documenting somewhere.

@Samuel-amap
Copy link
Collaborator Author

Samuel-amap commented Oct 31, 2024

A small improvement : no output provided in multi scale leads to an error reported by Julia. It should really be an error with a description we provide. Or there should be an intuitive default behaviour (returning all variables ? Seems like a natural approach and convenient for the user, but might warrant a warning when the output data could be huge).

@Samuel-amap
Copy link
Collaborator Author

Samuel-amap commented Dec 13, 2024

I've a new one, quite specific to PSE:
it's not that hard to accidentally write outputs=outputs in one of the run! functions in a multi-scale context
which, if outputs isn't defined in the scope somewhere, will (I believe) pass it the function PlantSimEngine.outputs, causing some Julia error further down the line in pre_allocate_outputs (as the outputs are then neither nothing nor properly defined).

ERROR: MethodError: no method matching keys(::typeof(outputs))
The function `keys` exists, but no method is defined for this combination of argument types.

Might need to rename a function or kwarg to prevent that kind of silent conflict from happening.

Edit : On closer inspection, it seems like the way this specific error manifests is not so straightforward to make for a beginner. It might occur when they transition from modellist to multiscale modelling. The kwarg error might manifest itself differently otherwise.

Edit (January 2025) : There is an example in test-mapping.jl that makes it easy to cause the error :
The function modellist_to_mapping returns a value that is used as the outputs kwarg for run! :

mtg, mapping, outputs_mapping = PlantSimEngine.modellist_to_mapping(models, st2; nsteps=nsteps, outputs=nothing) 
graphsim2 = PlantSimEngine.GraphSimulation(mtg, mapping, nsteps=nsteps, check=true, outputs=outputs_mapping)

Renaming outputs_mapping to outputs causes havoc.

@Samuel-amap
Copy link
Collaborator Author

Another error that results in puzzling output for a beginner would be supplying an arg parameter as a kwarg in one of the run! functions, causing some confusing errors.

@Samuel-amap
Copy link
Collaborator Author

Here's another one that might befuddle occasional users :
forgetting the model kwarg in MultiScaleModel declarations leads to a slightly unclear error :

ERROR: MethodError: no method matching MultiScaleModel(::ToyCurrenttimestepModel; mapping::Vector{PreviousTimeStep})
The type MultiScaleModel exists, but no method is defined for this combination of argument types when trying to construct it.

Closest candidates are:
MultiScaleModel(::T, ::Any) where T<:AbstractModel got unsupported keyword argument "mapping"`

referring to the next kwarg

Unless one guesses/knows that a kwarg might be missing one can't infer the cause of the error without an API lookup.

@Samuel-amap
Copy link
Collaborator Author

If one puts a parenthesis in the wrong spot when defining a mapping, some non-obvious Julia errors can pop up :

ERROR: ArgumentError: AbstractDict(kv): kv needs to be an iterator of 2-tuples or pairs

which occurs when

mapping_with_vector = Dict( "Scale" => (ToyAssimGrowthModel(0.0, 0.0, 0.0), ToyCAllocationModel(), Status( TT_cu=Vector(cumsum(meteo_day.TT))), ), )

is accidentally typed as

mapping_with_vector = Dict(( "Scale" => ToyAssimGrowthModel(0.0, 0.0, 0.0), ToyCAllocationModel(), Status( TT_cu=Vector(cumsum(meteo_day.TT))), ), )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

1 participant