Make example scripts runnable and expand README - #8
Conversation
examples/run.jl: - Replace the hard-coded Windows working directory with cd(@__DIR__) and take the instance path from ARGS, defaulting to the 168-hour instance. - Fail early when the instance directory is missing or when the solve returns no solution, instead of erroring later during result extraction. - Keep Crossover enabled locally: without it the barrier runs into numerical trouble on these instances. It stays disabled in the cluster scripts. - Fix the Expressions and plot_objective_values calls to match their current signatures, and set GKSwstype before Plots loads so it takes effect. - Write to output_local so local runs do not clash with the cluster output. examples/plot.jl: rewrite to re-plot from saved results without re-solving. It now reads the JSON written by run.jl (rebuilding the nested arrays back into their original dimensions), recomputes the expressions and drives perform_plotting. src/plotting.jl: correct the expressions argument type in the plot_generation_dispatch and plot_generation_capacities dispatch wrappers. README.md: document the model features and Specs fields, repository layout, instance data format, the result saving/loading options, the time series aggregation and data generation workflows, and the cluster scripts. The usage example was still showing the old API.
There was a problem hiding this comment.
Pull request overview
This PR updates the example workflows and documentation to reflect the current API and to support a clearer “solve once, plot from saved results” workflow for EnergySystemModeling.jl.
Changes:
- Updates
examples/run.jlto be instance-argument driven, write tooutput_local, fail early on missing instances / missing solutions, and align plotting/expression calls with current signatures. - Rewrites
examples/plot.jlto regenerate plots purely from saved JSON results without re-solving. - Expands
README.mdwith updated usage examples, repository layout, instance format notes, and workflow documentation; and adjusts plotting dispatch wrappers insrc/plotting.jl.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
src/plotting.jl |
Adjusts plotting wrapper dispatch signatures for expressions dictionaries. |
examples/run.jl |
Makes the run script path/instance-argument driven, improves failure modes, and updates plotting calls. |
examples/plot.jl |
Adds a “replot from saved JSON” workflow (reconstructing arrays and recomputing expressions). |
README.md |
Major documentation expansion and updates usage examples to the newer API. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| push!(LOAD_PATH, dirname(@__DIR__)) | ||
| using EnergySystemModeling | ||
| cd("D:\\Eigene Dateien\\Documents\\GitHub\\EnergySystemModeling.jl\\examples") | ||
|
|
||
| ENV["GKSwstype"]="nul" # Prevent opening plots windows (must be set before Plots loads) | ||
| cd(@__DIR__) |
There was a problem hiding this comment.
GR reads GKSwstype lazily rather than at import time, so setting it after using
is fine as long as it happens before the first plotting call. The env inspection
lives in GR.init() (GR.jl src/GR.jl), and the source comment there notes that
init(true) is deferred until load_libs, i.e. until GR actually starts drawing.
Verified empirically: the script runs headless on our cluster/login nodes with this
ordering and writes the PDF/PNG output without ever opening a window.
| @info "Extracting results" | ||
| variables = JuMPVar(model, VariablesDict) | ||
| objectives = JuMPObj(model, ObjectivesDict) | ||
| expressions = Expressions(parameters, variables) | ||
| expressions = Expressions(parameters, specs, variables) | ||
|
|
There was a problem hiding this comment.
Both points don't apply here.
JuMPVar builds its dictionary from a generator whose values have mixed
dimensionality — p_gnt is 3D while p̄_gn and σ_nt are 2D, and all three are
created unconditionally. Dict's runtime type widening typejoins those to exactly
Array{Float64}, so the result is Dict{String, Array{Float64}}, which is the
signature Expressions expects (src/model.jl:117). Checked on Julia 1.10.3:
julia> typeof(Dict(i => f(i) for i in keys(d)))
Dict{String, Array{Float64}}
The first in JuMPObj (src/model.jl:98) is required rather than a bug.
retrieve_data(::AffExpr) is JuMP.value.(a), and JuMP makes AffExpr broadcast as
a RefValue, so that returns a 0-dimensional array which first unwraps to the
number. For the retrieve_data(::Number) method first is a no-op
(first(3.0) === 3.0).
This exact path already produces scalar objective values, e.g. the objectives.json
written by a local run: {"f1":2.3377567708928177e10, "f2":2.4376739928541374e9, ...}.
| variables = JuMPVar(model, VariablesDict) | ||
| objectives = JuMPObj(model, ObjectivesDict) | ||
| expressions = Expressions(parameters, specs, variables) |
There was a problem hiding this comment.
Same as the thread on examples/run.jl — JuMPVar returns
Dict{String, Array{Float64}} (mixed variable dimensions widen to that), which
matches Expressions, and the first in JuMPObj unwraps the 0-dimensional array
that JuMP.value. returns for an AffExpr. No conversions are needed, so the README
snippet reflects what examples/run.jl actually runs.
| push!(LOAD_PATH, dirname(@__DIR__)) | ||
| using EnergySystemModeling, Parameters, JSON | ||
|
|
||
| output = "output" | ||
|
|
||
| parameters = load_json(EnergySystemModeling.Params, joinpath(output, "parameters.json")) | ||
| variables = load_json(EnergySystemModeling.Variables, joinpath(output, "variables.json")) | ||
| objectives = load_json(EnergySystemModeling.Objectives, joinpath(output, "objectives.json")) | ||
| expressions = Expressions(parameters, variables) | ||
|
|
||
| using Plots | ||
| using StatsPlots | ||
| pyplot() | ||
|
|
||
| savefig(plot_objective_values(objectives), | ||
| joinpath(output, "objectives.svg")) | ||
|
|
||
| for n in parameters.N | ||
| savefig(plot_generation_dispatch(parameters, variables, expressions, n), | ||
| joinpath(output, "generation_dispatch_n$n.svg")) | ||
| savefig(plot_generation_capacities(parameters, variables, expressions, n), | ||
| joinpath(output, "generation_capacities_n$n.svg")) | ||
| savefig(plot_storage_level(parameters, variables, expressions, n), | ||
| joinpath(output, "storage_n$n.svg")) | ||
| savefig(plot_box(parameters, variables, expressions, n), | ||
| joinpath(output, "boxplot$n.svg")) | ||
|
|
||
| using EnergySystemModeling, JSON | ||
|
|
||
| ENV["GKSwstype"]="nul" # Prevent opening plots windows (must be set before Plots loads) | ||
| cd(@__DIR__) |
There was a problem hiding this comment.
Same as the run.jl thread: GR reads GKSwstype in GR.init(), which is deferred
until the first plotting call, so setting it after using still takes effect. This
script runs headless as-is.
|
Thanks — I went through all four suggestions. They rest on two assumptions that don't |
examples/run.jl:
examples/plot.jl: rewrite to re-plot from saved results without re-solving. It now reads the JSON written by run.jl (rebuilding the nested arrays back into their original dimensions), recomputes the expressions and drives perform_plotting.
src/plotting.jl: correct the expressions argument type in the plot_generation_dispatch and plot_generation_capacities dispatch wrappers.
README.md: document the model features and Specs fields, repository layout, instance data format, the result saving/loading options, the time series aggregation and data generation workflows, and the cluster scripts. The usage example was still showing the old API.