energyRt renders reports — self-contained
HTML/PDF/Word datasheets — for technologies and storages, whole models
and repositories, solved scenarios, and scenario comparisons. Reports
and the levelized costs (levcost()) that
feed them are derived artifacts: both are cached on disk and
re-computed only when their inputs change. This article tours the layer
— what renders where, how the caching works, and how to compare
scenarios.
Everything lands under a throwaway project here;
open_project() anchors the registry, the object stores, and
the two derived-artifact folders (reports/,
levcosts/) in one call:
proj <- file.path(tempdir(), "rep-demo")
dir.create(proj, showWarnings = FALSE)
open_project(proj)
#> energyRt project: C:/Users/admin/AppData/Local/Temp/Rtmp4k5uTS/rep-demo (0 registered scenarios)A technology to price and report on:
PP <- newTechnology(
"PP", desc = "Gas power plant",
input = list(comm = "GAS", unit = "GWh"),
output = list(comm = "ELC", unit = "GWh"),
ceff = data.frame(comm = "GAS", cinp2use = 0.55),
invcost = data.frame(invcost = 800),
fixom = data.frame(fixom = 20), varom = data.frame(varom = 2),
af = data.frame(af.up = 0.85),
vintage = data.frame(olife = 25L), cap2act = 8.76)Levelized costs, cached
levcost() prices a technology (LCOE), storage (LCOS), or
trade route (LCOT). method = "auto" uses a closed-form
engine when the process qualifies and falls back to a small solver run
otherwise:
lc <- levcost(PP, fuel_costs = c(GAS = 25), discount = 0.06,
base_year = 2025, verbose = FALSE)
round(as.numeric(lc$levcost_npv), 2)
#> [1] 58.55The result was also written to the on-disk cache, keyed by the technology’s content hash plus the assumptions. Re-pricing the identical question returns instantly from disk:
lc2 <- levcost(PP, fuel_costs = c(GAS = 25), discount = 0.06,
base_year = 2025)
#> levcost: cached result is current: C:/Users/admin/AppData/Local/Temp/Rtmp4k5uTS/rep-demo/levcosts/PP@a3f67aa4.rds (force = TRUE to recompute)
list.files(get_levcost_cache_path())
#> [1] "PP@a3f67aa4.rds" "PP@a3f67aa4.yml"Anything that changes the answer — the technology’s data,
discount, fuel prices, the method — is part of the cache
key, so a stale hit is impossible; force = TRUE recomputes
regardless, and cache = FALSE skips the cache entirely. For
objects saved in a store the cache moves into the owning folder
(<scenario>/levcost/, a model store entry’s
levcost/); the project-level levcosts/ above
is the tier for in-memory objects.
Rendering a datasheet
report() renders a technology datasheet — parameters,
the draw() diagram, and (with levcost =) the
levelized-cost section. The same rule places the output: saved objects
render inside their own folder’s reports/, in-memory
objects into the project-level reports/ tier:
f <- report(PP, levcost = lc, format = "html", open = FALSE)
#> Report written to: C:\Users\admin\AppData\Local\Temp\Rtmp4k5uTS\rep-demo\reports\report_PP.html
basename(f)
#> [1] "report_PP.html"A report is skip-if-current: the rendered file
carries a sidecar (*.report.yml) keying the object content,
the template content, the arguments, and the levcost identity. Repeating
the call re-renders nothing:
f <- report(PP, levcost = lc, format = "html", open = FALSE)
#> Report up to date: C:\Users\admin\AppData\Local\Temp\Rtmp4k5uTS\rep-demo\reports\report_PP.html (force = TRUE to re-render)Editing the technology, the template, or any argument invalidates the
key; force = TRUE re-renders unconditionally. Formats:
format = takes any of "html",
"pdf", "tex", "docx" (PDF needs a
LaTeX toolchain).
Model and scenario reports
Container reports split by role: report(model) documents
the assumptions (configuration, inventories,
per-process sheets), while report(scenario) documents the
results of a solved scenario — status,
generation/capacity charts, costs, emissions; run = renders
any recorded run, and template = "full" switches to the
comprehensive page-broken layout with maps and branding. Reporting a
single process from a container injects its container-priced levelized
cost:
report(scen) # results overview -> <scenario>/reports/
report(scen, run = "julia_highs") # another recorded run
report(scen, template = "full") # comprehensive, page-broken layout
report(scen, name = "PP") # one process, priced in this scenarioThe process sections follow your groups: a named list of
process names or regular expressions, passed as groups = or
stored once in mod@misc$report_groups (a scenario inherits
its model’s). Without groups the report shows a representative sample of
up to 12 processes; groups = "topology" groups
automatically by structure. levcost = TRUE adds a
levelized-cost comparison — an overall chart, one chart per group, and
the ranked table — ex-ante on a model, ex-post on a solved scenario; a
process datasheet requested from a container also compares the process
with its structural peers:
mod@misc$report_groups <- list(Coal = "_coal_", Wind = c("^R._win", "WOFF"))
report(mod) # one section per group + "Ungrouped"
report(mod, groups = FALSE) # ignore the stored groups: sample of 12
report(mod, levcost = TRUE) # ex-ante LCOE comparison, per group
report(scen, levcost = TRUE) # ex-post, from the solved scenario
report(mod, name = "PP") # datasheet + "vs peers" chartFigures in reports carry captions rather than in-plot titles, and
faceted charts cap at 16 panels with their height following the layout.
Custom templates get the same behaviour from
report_fig(p, width, caption = ).
The shipped templates are listed by report_templates();
custom .Rmd templates build on the exported
report_* helper family and are passed via
template = "<path>".
Element datasheets
Every model component has a report() method —
commodities, supply, demand, trade, import/export, weather, taxes and
subsidies, user constraints, calendars, horizons, configurations, and
user costs each render a one-object datasheet from their own shipped
template. A supply datasheet charts availability and cost by region
(autoplot(sup, style = "bar") /
style = "regions" draw the same views directly; an
unlimited availability shows as a translucent full-height bar):
report(COA) # a commodity datasheet
report(SUP_COA) # a supply datasheet
report(mod, name = "SUP_COA") # the same, found inside a model
report(mod, name = "X", class = "demand") # narrow a name lookupAn object can carry its own default template in
misc$report (also the techspec report: key) —
a template name or a file path; an explicit template =
argument always wins:
SUP_COA@misc$report <- "my_supply_sheet.Rmd"
report(SUP_COA) # renders my_supply_sheet.RmdComparing scenarios
compare_scenarios() takes a named list of solved
scenarios (or one scenario plus runs = to compare its
recorded runs) and returns a classed comparison — overview, per-variable
deltas against the base, and the largest movers; autoplot()
charts it and report() renders the comparative report:
cmp <- compare_scenarios(list(BASE = base, CO2CAP = capped))
cmp # printed overview + top deltas
autoplot(cmp) # faceted comparison charts
report(cmp, open = FALSE) # the comparative report (report_scenarios)compare_models() diffs two models’ declarations
(inventory by content hash, config, then per-slot drill-down), and
compare_inputs() diffs two interpolated scenarios’
parameter stores — the tool for “same model, different numbers?”
questions. For quick ad-hoc comparisons, plain getData()
across scenarios remains the one-liner (see the Workflow
article):
Where everything lands
<project>/
├── reports/ # reports of in-memory objects (temporary tier)
├── levcosts/ # levcost cache of in-memory objects
├── scenarios/<scen>/
│ ├── reports/ # this scenario's reports
│ └── levcost/ # its levcost cache
└── models/<name>/
├── reports/ # a stored model's reports
└── levcost/ # its levcost cache
Both tiers are caches of derived artifacts: safe to delete at any time, rebuilt on demand, and never consulted for anything but skipping work.
