Skip to contents

energyRt formulates the optimization model once and then writes it out for one of several mathematical-programming backends, runs the solver, and reads the solution back. You pick the backend and the underlying solver with a single solver_options preset; the same model and scenario code works with all of them, which makes cross-checking results between backends easy.

The backends at a glance

Backend lang Solvers License Notes
GLPK / MathProg GLPK GLPK open-source Ships as the glpsol executable; easiest to install, slow on large models.
Python / Pyomo PYOMO CBC, GLPK, CPLEX open-source (CBC/GLPK) Convenient with a conda environment.
Julia / JuMP JuMP HiGHS, Cbc, GLPK, CPLEX open-source (HiGHS/Cbc/GLPK) HiGHS barrier is fast; recommended for large models.
Julia / cuOpt (GPU) JuMP NVIDIA cuOpt open-source Linux only. First-order (PDLP) needs no factorization, so it fits models whose barrier does not; add crossover for duals.
GAMS GAMS CPLEX, CBC proprietary Needs a GAMS license; supports GDX I/O and fine solver tuning.
NEOS (remote) PYOMO / GAMS CPLEX, CBC, … free service Solve on the NEOS server — no local commercial solver needed.
mmcloud_pyomo (cloud GPU) PYOMO (as MPS) HiGHS / cuOpt on a cloud GPU service Experimental. Writes the problem as MPS and solves it remotely, for models too large for local RAM.

See the Installation and Settings article for how to install each runtime and the library layer (en_check_dependencies(), en_install_deps()).

Choosing a backend

solver_options is a named list of ready-made presets — each one pins a backend, a solver, the data-exchange format, and any solver-specific tuning:

names(solver_options)
solver_options$glpk
solver_options$julia_highs_barrier

Use a preset in one of two ways:

# 1. Set it as the session default, then solve normally
set_default_solver(solver_options$julia_highs)
get_default_solver()                       # inspect the current default
scen <- solve_model(model)                   # uses the default

# 2. Pass it explicitly to a single solve
scen <- solve_model(model,  solver = solver_options$glpk)
scen <- solve_scenario(scen,  solver = solver_options$gams_gdx_cplex)

set_default_solver() lasts for the session. To make a default stick across sessions, save it with en_config_write(); en_config_show() reports the current default together with the solver paths it depends on.

solve_model() interpolates a model and solves it; solve_scenario() solves an already-interpolated scenario. With solver = NULL the scenario’s own settings or get_default_solver() are used.

The backends in detail

GLPK / MathProg

The GNU Linear Programming Kit. energyRt writes the model in GMPL/MathProg and calls the glpsol executable — the lowest-friction option and a good default for small and medium models.

solve_model(model, solver = solver_options$glpk)

Python / Pyomo

energyRt writes a Pyomo ConcreteModel and solves it with the configured solver. CBC is the open-source default; CPLEX and GLPK are also available. Model data and the solution are exchanged as Arrow files, which needs pyarrow in the Python environment (en_install_python_deps() installs it); solver_options$pyomo_cbc_sqlite selects the older SQLite exchange instead.

solve_model(model, solver = solver_options$pyomo_cbc)
solve_model(model, solver = solver_options$pyomo_cplex_barrier)   # CPLEX barrier

Julia / JuMP

energyRt writes a JuMP model and solves it in Julia. HiGHS (open-source) is fast and the recommended choice for large models; *_barrier, *_simplex and *_parallel presets select the HiGHS algorithm. Cbc, GLPK and CPLEX are also available. Model data and the solution are exchanged as Arrow files, read and written by Arrow.jl (en_install_julia_pkgs() installs it); solver_options$julia_highs_rdata selects the older RData exchange instead.

solve_model(model, solver = solver_options$julia_highs)           # default (auto)
solve_model(model, solver = solver_options$julia_highs_barrier)   # interior point
solve_model(model, solver = solver_options$julia_highs_parallel)  # parallel simplex

NVIDIA cuOpt (GPU)

Linux only. cuOpt solves the same JuMP model on an NVIDIA GPU through cuOpt.jl. It ships no Windows build, so on Windows the Julia process must run inside WSL. Install cuOpt itself first (pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu12), put libcuopt.so on LD_LIBRARY_PATH, then add the wrapper with en_install_julia_pkgs("cuOpt"). cuOpt.jl supports one cuOpt release at a time, so the installed cuopt-cu12 must match it.

solve_model(model, solver = solver_options$julia_cuopt_pdlp)      # first-order, GPU
solve_model(model, solver = solver_options$julia_cuopt_crossover) # + basic solution
solve_model(model, solver = solver_options$julia_cuopt_concurrent)# race all three

method selects the algorithm: 0 races PDLP and barrier on the GPU against dual simplex on the CPU and returns whichever finishes first, 1 is PDLP alone. Which preset to use follows from how the solution is consumed:

  • julia_cuopt_pdlp – PDLP never factorizes, so GPU memory grows linearly with non-zeros. This is the preset for models whose barrier factorization does not fit in GPU memory. The solution carries a duality gap of about 1e-4, which is fine for capacities and dispatch but not for prices.
  • julia_cuopt_crossover – adds a crossover to a basic solution, which is what duals (and therefore shadow prices) require. Costs a little extra time and recovers the exact optimum.
  • julia_cuopt_concurrent – fastest on small and medium models, but the barrier leg dominates GPU memory and the winning algorithm can differ between runs, so the same model may not return byte-identical results twice.

GAMS

If you have a GAMS license, energyRt can write and run a GAMS model. The gams_gdx_* presets exchange data as GDX (fast, native); the gams_csv_* presets use text/CSV. The *_barrier and *_parallel presets inject a tuned cplex.opt (LP method, threads, crossover, …).

solve_model(model, solver = solver_options$gams_gdx_cplex)
solve_model(model, solver = solver_options$gams_gdx_cplex_barrier)

NEOS (remote solve)

The NEOS Server runs commercial solvers (CPLEX, …) for free over the internet. The model is built locally but the solve is dispatched to NEOS, so you can use CPLEX without a local license. Two families are provided — via Pyomo (neos_pyomo_*) and via GAMS (neos_gams_*, sent as inlined text so no local GAMS install is needed).

NEOS requires an email address, set once via the NEOS_EMAIL environment variable or the helper (this is the one energyRt setting whose environment variable is not prefixed ENERGYRT_, because the Pyomo subprocess reads NEOS_EMAIL directly):

set_neos_email("you@example.org")
solve_model(model, solver = solver_options$neos_gams_cplex)
solve_model(model, solver = solver_options$neos_pyomo_cplex_barrier)

The low-level NEOS interface (submit/poll/fetch a job, list solvers, …) is also exported — see ?neos and the neos_* functions.

mmcloud_pyomo (cloud GPU)

Experimental — in testing. Where NEOS dispatches the model text, this route writes the problem as an MPS file locally and sends that to a GPU solver in the cloud, decoding the solution back into the scenario. It exists for models whose build or solve does not fit a laptop: the MPS is produced by the Pyomo formulation, so nothing new has to be written for the model itself.

solve_model(model, solver = solver_options$mmcloud_pyomo)
# or, to recover duals from a first-order solve:
solve_model(model, solver = solver_options$mmcloud_pyomo_crossover)

Being experimental, treat results as provisional and cross-check a smaller variant against a local backend before relying on them. Access is via a token (get_hf_token()), and the preset carries the machine flavour and a wall-clock timeout, so a run that exceeds it returns nothing rather than partial results.

All preset solver_options

The presets shipped with the package (generated from the installed data):

preset backend solver remote data exchange
glpk GLPK/MathProg — default
pyomo_cbc Python/Pyomo cbc default
pyomo_cbc_sqlite Python/Pyomo cbc SQLite
pyomo_cplex Python/Pyomo cplex default
pyomo_cplex_barrier Python/Pyomo cplex default
pyomo_glpk Python/Pyomo glpk default
pyomo_highs Python/Pyomo appsi_highs default
pyomo_highs_barrier Python/Pyomo appsi_highs default
pyomo_mps Python/Pyomo appsi_highs default
mm_highs NA HiGHS default
mm_highs_barrier NA HiGHS default
mm_highs_barrier_bs3 NA HiGHS default
mm_julia_highs_barrier NA HiGHS default
mm_julia_highs_barrier_bs3 NA HiGHS default
mmcloud NA HiGHS default
mmcloud_crossover NA HiGHS default
mmcloud_pyomo Python/Pyomo appsi_highs default
mmcloud_pyomo_crossover Python/Pyomo appsi_highs default
neos_pyomo_cplex Python/Pyomo — NEOS default
neos_pyomo_cplex_barrier Python/Pyomo — NEOS default
neos_pyomo_cbc Python/Pyomo — NEOS default
julia_cbc Julia/JuMP Cbc default
julia_cplex Julia/JuMP CPLEX default
julia_cplex_barrier Julia/JuMP CPLEX default
julia_highs Julia/JuMP HiGHS default
julia_highs_rdata Julia/JuMP HiGHS RData
julia_highs_barrier Julia/JuMP HiGHS default
julia_highs_barrier_bs3 Julia/JuMP HiGHS default
julia_glpk Julia/JuMP GLPK default
julia_highs_simplex Julia/JuMP HiGHS default
julia_highs_parallel Julia/JuMP HiGHS default
julia_highs_pdlp Julia/JuMP HiGHS default
julia_highs_hipdlp Julia/JuMP HiGHS default
julia_cuopt Julia/JuMP cuOpt default
julia_cuopt_concurrent Julia/JuMP cuOpt default
julia_cuopt_pdlp Julia/JuMP cuOpt default
julia_cuopt_crossover Julia/JuMP cuOpt default
gams_cplex GAMS CPLEX default
gams_gdx_cplex GAMS CPLEX GDX
gams_gdx_cplex_barrier GAMS CPLEX GDX
gams_gdx_cplex_parallel GAMS CPLEX GDX
gams_cbc GAMS CBC default
gams_gdx_cbc GAMS CBC GDX
neos_gams_cplex GAMS CPLEX NEOS default
neos_gams_cplex_barrier GAMS CPLEX NEOS default
neos_gams_cbc GAMS CBC NEOS default

A default in the last column means the preset names no format and follows the exchange_format option — Arrow IPC unless you change it.

Data exchange and storage

Two separate option families, easy to confuse because both once shared one option:

  • exchange — the files handed to a solver for one solve, in runs/[<variant>/]<solve>/input|output/. Written once, read once, deleted with the run. get_exchange_format() ("feather"), get_exchange_compression() ("lz4" — the codec is cheap because nothing here is kept).
  • storage — the tables that outlive the session: a scenario’s parameter and variable stores, and the model / repository / dataset stores. get_storage_format() ("feather"), get_storage_compression() ("zstd", level 15).
set_exchange_format("parquet")     # Pyomo only; Arrow.jl cannot read parquet
set_exchange_compression("uncompressed")
set_storage_format("parquet")      # smaller on disk; feather reads faster

A solver preset’s export_format / import_format overrides the exchange option for that solve. A store records the format it was written in and keeps it when rewritten, so changing the option never mixes codecs inside an existing store — it applies to what you write next.

Anatomy of a preset

Each preset is a plain list, so you can copy one and tweak it. Common fields:

Field Meaning
name preset name (informational)
lang backend: "GLPK", "PYOMO", "JuMP", "GAMS"
solver underlying solver (e.g. "HiGHS", "cbc", "CPLEX")
export_format / import_format data exchange in / out: feather (Arrow, the default), parquet (Pyomo only), SQLite / RData (legacy input), GDX, CSV/text. Unset on a JuMP/Pyomo preset means “follow get_exchange_format()”; the two directions are independent
backend "neos" for a remote NEOS solve
neos_solver, neos_category remote solver and NEOS problem category
inc3, inc4, … backend-specific code blocks injected into the model template (e.g. a cplex.opt for LP-method tuning)
# start from a preset and customise
my_solver <- solver_options$julia_highs
my_solver$name <- "julia_highs_custom"
# ... adjust an inc* block to set HiGHS attributes ...
solve_model(model, solver = my_solver)

See also