Skip to contents

Moves a model's time-varying data from the calendar it was written against onto calendar, so the model can be built and solved at another time resolution. The time mirror of subset_model_regions() and aggregate_model_regions().

Usage

recast_model_calendar(
  object,
  calendar,
  rules = NULL,
  year = NULL,
  verbose = isVerbose()
)

Arguments

object

a model.

calendar

the target calendar; coarser or finer than the model's own.

rules

optional named character vector of timescales::CALENDAR_RULES keyed by value column, overriding the rate default for those columns (e.g. c(demand = "sum")).

year

model year used to materialise both calendars on a shared datetime grid. NULL (default) takes the model's first horizon milestone. It matters only where the calendars align to real dates (days of the year, weekdays), and then only across a leap year.

verbose

print per-table messages.

Value

the recast model, carrying calendar.

Details

Every timeslice-indexed value in energyRt is a rate: the equations multiply it by the timeslice weight to obtain an annual quantity. Aggregating therefore takes the weighted mean, which conserves the weighted annual total exactly, and disaggregating copies the rate into each child timeslice. rules overrides this per column where a value is not a rate (a declared quantity to be summed, a standard deviation).

Rows are recast by their own timeframe, not by their table: only rows at the source calendar's leaf timeframe move. A row declared at ANNUAL, or at any timeframe calendar also has, is left exactly as it is.

A @timeframe naming a timeframe calendar does not have falls back to the finest timeframe that survives, and is reported – otherwise the model references a timeframe its calendar cannot resolve.

Comparability

A recast model is not a smaller answer to the same question. Collapsing timeslices removes the variation inside them, which is what a model prices: peaks flatten, variable resources become firmer than they are, and storage loses the cycle it was built to serve. Its dispatch, costs and objective are its own. Use it to run, plot and teach the shape of a solution, not to report the source model's numbers.

Examples

mod <- newModel("TOPIA", data = topia$modules$electricity$repo,
                calendar = topia$modules$calendars$s4_h24,
                region = topia$modules$electricity$regions,
                horizon = topia$modules$horizons$BY2025,
                discount = 0.05)

# 96 hourly timeslices collapse to 4 seasons: the same model, small enough
# to solve in a second
light <- recast_model_calendar(mod, calendar = calendars$s4)
#> recast onto calendar 's4' (96 -> 4 timeslices): 4 table(s), 8,448 -> 352 row(s); 6 timeframe declaration(s) -> SEASON (verbose = TRUE lists them).
#> Dispatch, costs and the objective are the recast model's own, not the source model's.
getCalendar(light)
#> An object of class "calendar"
#> Slot "name":
#> [1] "s4"
#> 
#> Slot "desc":
#> [1] "SEASON calendar (4 timeslices; complete, regular) (timescales 0.6.0)"
#> 
#> Slot "timeframes":
#> $ANNUAL
#> [1] "ANNUAL"
#> 
#> $SEASON
#> [1] "WIN" "SPR" "SUM" "FAL"
#> 
#> 
#> Slot "year_fraction":
#> [1] 1
#> 
#> Slot "year_start":
#> $month
#> [1] 1
#> 
#> $day
#> [1] 1
#> 
#> 
#> Slot "utc_offset_minutes":
#> [1] 0
#> 
#> Slot "timetable":
#>    ANNUAL SEASON timeslice     share weight
#>    <char> <char>    <char>     <num>  <num>
#> 1: ANNUAL    WIN       WIN 0.2465753      1
#> 2: ANNUAL    SPR       SPR 0.2520548      1
#> 3: ANNUAL    SUM       SUM 0.2520548      1
#> 4: ANNUAL    FAL       FAL 0.2493151      1
#> 
#> Slot "timeslice_share":
#>    timeslice     share weight
#>       <char>     <num>  <num>
#> 1:    ANNUAL 1.0000000      1
#> 2:       WIN 0.2465753      1
#> 3:       SPR 0.2520548      1
#> 4:       SUM 0.2520548      1
#> 5:       FAL 0.2493151      1
#> 
#> Slot "default_timeframe":
#> [1] "SEASON"
#> 
#> Slot "timeframe_rank":
#> ANNUAL SEASON 
#>      1      2 
#> 
#> Slot "timeslices_in_frame":
#> ANNUAL SEASON 
#>      1      4 
#> 
#> Slot "timeslice_family":
#>    parent  child
#>    <char> <char>
#> 1: ANNUAL    WIN
#> 2: ANNUAL    SPR
#> 3: ANNUAL    SUM
#> 4: ANNUAL    FAL
#> 
#> Slot "timeslice_ancestry":
#>    parent  child
#>    <char> <char>
#> 1: ANNUAL    WIN
#> 2: ANNUAL    SPR
#> 3: ANNUAL    SUM
#> 4: ANNUAL    FAL
#> 
#> Slot "next_in_timeframe":
#>    timeslice timeslicep
#>       <char>     <char>
#> 1:       WIN        SPR
#> 2:       SPR        SUM
#> 3:       SUM        FAL
#> 4:       FAL        WIN
#> 
#> Slot "next_in_year":
#>    timeslice timeslicep
#>       <char>     <char>
#> 1:       WIN        SPR
#> 2:       SPR        SUM
#> 3:       SUM        FAL
#> 4:       FAL        WIN
#> 
#> Slot "misc":
#> $pTimesliceWeight
#> NULL
#> 
#> 

# the hourly demand profile, and the four seasonal rates it becomes
dem <- getObject(light, name = "DEM_ELC", drop = TRUE)
head(dem@demand)
#>   cluster region year timeslice   demand
#> 1    <NA>     C1 2025       WIN 432.5774
#> 2    <NA>     C1 2025       SPR 391.8829
#> 3    <NA>     C1 2025       SUM 405.4693
#> 4    <NA>     C1 2025       FAL 395.0703
#> 5    <NA>     C1 2030       WIN 475.8351
#> 6    <NA>     C1 2030       SPR 431.0712