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.
Matrix (MPS) mm or PYOMO (as MPS) HiGHS in R or Julia; cuOpt on a local GPU (WSL) or a cloud GPU open-source / service Experimental. The problem is written as an MPS file and solved by multimod::solve_mps(); the cloud presets run it on a rented GPU through ‘mmcloud’.

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.

Matrix route: local and cloud GPU

Experimental — in testing. Where NEOS dispatches the model text, this route writes the problem as an MPS file locally and solves that file with multimod::solve_mps(), decoding the solution back into the scenario. The matrix is written by multimod (lang = "mm", positional names resolved by its index files) or by the Pyomo formulation (pyomo_* presets). The preset names the writer, the solver and the place:

preset solver place
mm_highs, mm_highs_barrier HiGHS in the R session
mm_julia_highs_barrier HiGHS through Julia (HiPO interior point) local
mm_cuopt_wsl cuOpt PDLP local GPU through WSL (Windows)
mm_cuopt_cloud cuOpt PDLP rented GPU, through ‘mmcloud’
pyomo_cuopt_cloud cuOpt PDLP on a Pyomo-written matrix rented GPU, through ‘mmcloud’
solve_model(model, solver = solver_options$mm_cuopt_wsl)
cloud <- solver_options$mm_cuopt_cloud
cloud$cloud_namespace <- "my-org"   # the account billed for the job
cloud$cloud_flavor <- "h200"
solve_model(model, solver = cloud)
# a crossover variant of each cuOpt preset recovers a basic solution and duals
solve_model(model, solver = solver_options$mm_cuopt_cloud_crossover)

On the multimod writer the activity and output definitions (eqTechAfFx, eqTechActSng) and the per-process totals are substituted while the matrix is built, so their rows and the columns they define never enter the file; the preset field substitute names the equations (character() writes them all). The matrix file is removed once the solution is decoded; keep_mps = TRUE in the preset keeps it.

Being experimental, results are provisional; a smaller variant should be cross-checked against a local backend before they are relied on. The cloud presets need a token for the service (mmcloud::set_hf_token()), and carry the machine flavour and a wall-clock timeout, so a run that exceeds it returns nothing rather than partial results. The former preset names mmcloud, mmcloud_crossover, mmcloud_pyomo and mmcloud_pyomo_crossover stop with a message naming their replacement.

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 multimod (MPS) HiGHS default
mm_highs_barrier multimod (MPS) HiGHS default
mm_highs_barrier_bs3 multimod (MPS) HiGHS default
mm_julia_highs_barrier multimod (MPS) HiGHS default
mm_julia_highs_barrier_bs3 multimod (MPS) HiGHS default
mm_cuopt_wsl multimod (MPS) cuOpt local GPU (WSL) default
mm_cuopt_wsl_crossover multimod (MPS) cuOpt local GPU (WSL) default
mm_cuopt_cloud multimod (MPS) cuOpt cloud GPU default
mm_cuopt_cloud_crossover multimod (MPS) cuOpt cloud GPU default
pyomo_cuopt_cloud Python/Pyomo appsi_highs cloud GPU default
pyomo_cuopt_cloud_crossover Python/Pyomo appsi_highs cloud GPU default
julia_mps Julia/JuMP HiGHS default
julia_cuopt_cloud Julia/JuMP HiGHS cloud GPU 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