shift_future_epw() is the recommended user-facing
interface in Create Future EPW
Files. Use shift_plan() and
shift_explain() when you want to inspect the stages before
running them. shift_morph() and shift_epw()
remain the lower-level wrappers around EpwMorpher, the
store-native morphing engine.
Use EpwMorpher directly when you need to inspect monthly
summaries, preview factor calculations, diagnose missing variables,
choose morphing case grouping, change the statistical downscaling
backend, or rerun only the morph/write part of a workflow.
EpwMorpher consumes completed extraction outputs in an
EsgStore and a baseline EPW file. The examples below assume
the store already has a future extraction plan named region
and a historical reference extraction plan named
reference_region.
store <- EsgStore$new("~/cmip6-singapore-store", create = FALSE)
epw <- system.file(
"extdata", "examples", "SGP_Singapore.486980_IWEC.epw",
package = "epwshiftr",
mustWork = TRUE
)
morpher <- epw_morpher(
store = store,
epw = epw,
site_id = "SIN",
recipe = epw_morph_recipe("belcher"),
label = "Singapore baseline"
)The helper functions used by the high-level workflow are available directly:
epw_morph_variables("minimal")
epw_morph_variables("recommended")
epw_morph_variables("extended")
periods <- epw_morph_periods(`2060s` = 2060L)
reference_spec <- historical_reference(1995:2014)
request <- shift_cmip6_scenario(
source = "BCC-CSM2-MR",
scenario = c("ssp126", "ssp585"),
years = 2055:2065,
variables = "belcher",
frequency = "mon"
)"recommended" is the strict Belcher recipe set used by
the main workflow. "minimal" is useful for relaxed
demonstrations, and "extended" adds related variables for
future recipes.
When you already have a recipe or backend object, ask that object for its variables instead of choosing a named set manually:
belcher_recipe <- epw_morph_recipe("belcher")
epw_morph_variables(belcher_recipe)
epw_morph_variables(belcher_recipe, include_optional = TRUE)
epw_morph_variables(epw_morph_backend("belcher"))The enhanced Belcher profile is the default. Required variables block
strict execution when absent; optional tasmax,
tasmin, and snd inputs improve the result when
matching future/reference data exist but do not otherwise block it. The
high-level shift_cmip6(table = NULL) workflow maps
atmospheric variables to Amon and snd to
LImon automatically.
A recipe selects a backend and carries backend-specific profile,
options, and method overrides. The default recipe is
"belcher", which applies the Belcher-style statistical
downscaling steps used by the high-level workflow.
enhanced_recipe <- epw_morph_recipe(
"belcher",
options = belcher_options(
transition_hours = 72L,
snow_depth = "auto",
design_conditions = "drop"
)
)
# Reproduce the numerical and header behavior from before enhanced profiles.
legacy_recipe <- epw_morph_recipe("belcher", profile = "legacy")Backends describe executable statistical engines, while the complete recipe catalog describes named scientific methods assembled from those engines and the seven component stages. Inspecting the catalog does not run a method or serialize any function:
epw_morph_recipes()
epw_morph_recipe_spec("epwshiftr_daily_power")
epw_morph_recipe_spec("epwshiftr_daily_btws")
epw_morph_recipe_spec("eames_monthly_temperature")
epw_morph_recipe_spec("ek_daily_factors")
epw_morph_recipe_spec("monthly_percentile_temperature")The initial catalog distinguishes the following complete methods:
| Recipe | Policy | Implementation | Purpose |
|---|---|---|---|
belcher_monthly |
paper_faithful |
Existing backend adapter | Belcher monthly comparison with matching historical and future model data |
epwshiftr_monthly |
harmonized |
Existing backend adapter | Enhanced monthly workflow with shared epwshiftr physical and output policies |
epwshiftr_daily_power |
harmonized |
Seven-stage pipeline | Calendar-neutral daily temperature signal and constrained power projection |
epwshiftr_daily_btws |
harmonized |
Seven-stage pipeline | Existing daily CMIP6 temperature signal combined with the BTWS hourly projection |
eames_monthly_temperature |
harmonized |
Seven-stage pipeline | Eames monthly mean/average-daily-extrema changes and BTWS, with monthly statistics derived from daily CMIP6 inputs |
ek_daily_factors |
paper_faithful,
harmonized
|
Seven-stage pipeline | Ek day-of-year mean/DTR factors and combined hourly temperature transform without an added smoothing window |
monthly_percentile_temperature |
paper_faithful,
harmonized
|
Seven-stage pipeline | Arima month-wise model CDF change functions selected by each TMY day’s percentile in a multi-year observed CDF |
sobie_curry_daily |
paper_faithful,
harmonized
|
Seven-stage pipeline | Published daily mean/DTR transformations with independent or shared humidity closure |
Create a configured recipe by its stable catalog name. The selected policy, catalog version, backend profile, and component names are persisted with the workflow:
monthly_comparison <- epw_morph_recipe(
"belcher_monthly",
policy = "paper_faithful"
)
daily_power <- epw_morph_recipe(
"epwshiftr_daily_power",
policy = "harmonized"
)
daily_btws_comparison <- epw_morph_recipe(
"epwshiftr_daily_btws",
policy = "harmonized"
)
eames_monthly_temperature <- epw_morph_recipe(
"eames_monthly_temperature",
policy = "harmonized"
)
ek_daily <- epw_morph_recipe(
"ek_daily_factors",
policy = "paper_faithful"
)
monthly_percentile_temperature <- epw_morph_recipe(
"monthly_percentile_temperature",
policy = "paper_faithful"
)
sobie_curry <- epw_morph_recipe(
"sobie_curry_daily",
policy = "paper_faithful"
)
sobie_curry_harmonized <- epw_morph_recipe(
"sobie_curry_daily",
policy = "harmonized"
)Catalog input roles are validated before backend execution. The two
monthly entries remain explicitly labelled backend adapters because
their established runner has not yet been decomposed into executable
components; the daily entries are checked against their registered
seven-stage pipelines. Ad hoc recipes such as
epw_morph_recipe("belcher") remain supported and preserve
their previous defaults.
The BTWS comparison requires daily tas,
tasmin, and tasmax from matching historical
and future model periods. It reuses epwshiftr’s calendar-neutral daily
target signal, baseline EPW sequence, specific-humidity closure, and
output stages, while replacing only the hourly stage with equations
(7)–(16) from Eames et al. (2024). The complete recipe is therefore
intentionally labelled as a combination rather than a reproduction of
the paper’s monthly UKCP18 workflow. The paper directs implementations
to reduce m for a positive stretch or n for a
negative stretch when the default m = n = 1 would leave
[0, 1], but does not publish solver code. epwshiftr uses
deterministic bisection to retain the largest admissible exponent and
records S, m, n, closure errors,
and every mean-shift fallback reason.
At the high-level workflow, select the same daily temperature method and change only its hourly reconstruction component:
btws_method <- daily_temperature(
historical_reference(years = 1995:2014),
reconstruction = "btws"
)Eames et al. did not drive BTWS with daily CMIP6 series. Their paper
applies monthly UKCP18 change factors for mean temperature and the
monthly averages of daily minimum and maximum temperature to an hourly
baseline month, with BTWS executed day by day.
eames_monthly_temperature implements that temporal
structure for temperature: it aggregates matching daily CMIP6
tas, tasmin, and tasmax into the
same three monthly statistics and maps one factor set to every EPW day
in the corresponding month:
eames_method <- eames_temperature(
historical_reference(years = 1995:2014)
)The CMIP6 aggregation is a source adaptation because the paper used
UKCP18. The recipe is also explicitly temperature-only: it reuses
epwshiftr’s specific-humidity closure and does not implement the paper’s
radiation, cloud, pressure, or other non-temperature transformations.
The epwshiftr_daily_btws recipe remains available to
compare hourly reconstruction algorithms under the same daily-varying
CMIP6 signal.
Ek et al. (2018) instead form a climate baseline for each day of the
annual cycle and apply daily Belcher-style change factors to the
inherited hourly weather sequence. The temperature recipe derives mean
temperature and DTR from matching tasmin and
tasmax, applies no additional smoothing window, and uses
the combined shift-and-stretch equation:
Here alpha_DTR is implemented as the relative modeled
DTR change. This is the interpretation that makes zero climate change an
identity and satisfies the paper’s stated daily mean and variance
behavior; the original publication’s generic factor equations and
variance wording are not fully self-consistent, so the interpretation is
retained explicitly in recipe provenance.
ek_method <- ek_daily_temperature(
historical_reference(years = 1995:2014),
policy = "paper_faithful"
)The initial recipe is temperature-only. Its
paper_faithful policy preserves the baseline humidity
fields so the Ek temperature transformation can be compared directly;
policy = "harmonized" applies the shared specific-humidity
closure. Wind, cloud, radiation, and other transformations are not
included where the source publication is contradictory or
underspecified.
Arima et al. (2024) use four distinct inputs: the baseline TMY, historical and future daily model values, and multi-year observed daily weather for the target location. For temperature, historical and future inverse CDFs are constructed within each month and subtracted at common percentiles:
The change function is smoothed with an endpoint-aware nine-point moving mean repeated three times. The baseline TMY daily mean is then located in the observed monthly empirical CDF, and the selected additive change is applied to all 24 hours of that day. Model calendars therefore contribute samples to monthly distributions; this method does not map model dates directly onto the 365 EPW days.
arima_method <- arima_temperature(
reference = historical_reference(years = 1995:2014),
observed_reference = shift_reference_plan(
plan_id = observed_plan_ids,
periods = epw_morph_periods(observed = 1995:2014)
),
policy = "paper_faithful"
)The publications do not specify empirical plotting positions, quantile interpolation, or endpoint evaluation. The implementation therefore records its midpoint probability grid, type-7 quantiles, linear factor interpolation, and endpoint clamping in every result. The initial recipe covers additive dry-bulb temperature for one model case; multi-model averaging and the published non-temperature transformations remain separate extensions.
The Sobie-Curry recipe requires daily tas,
tasmin, tasmax, huss, and surface
pressure ps from matching historical and future model
periods. It uses a circular 21-day window on the shared 365-day
annual-phase grid, preserves the baseline CWEC/EPW sequence, and adjusts
dry-bulb temperature and atmospheric pressure. Paper-faithful mode
independently adjusts dew point and relative humidity. Harmonized mode
applies the smoothed future-minus-historical daily huss
change to baseline EPW specific humidity, clips the target only at zero
or saturation, and derives relative humidity and dew point from
projected temperature and pressure. Other EPW fields are unchanged. The
paper-faithful dew-point standard-deviation factor is implemented as
sigma_future / sigma_historical - 1: this preserves the
paper’s stated past-future difference and makes zero climate change an
identity transform. The printed ratio without - 1, when
added to the original anomaly as shown in the paper, would double hourly
anomalies under zero change.
The profiles resolve to a complete, persisted option contract:
| Option | Enhanced default | Legacy default | Meaning and allowed values |
|---|---|---|---|
transition_hours |
72 |
0 |
Total cyclic smoothing window centered on each month boundary;
integer 0–336. Precipitation is never
smoothed. |
humidity_source |
"auto" |
"hurs" |
Whole-case humidity source: "auto",
"huss", or "hurs". |
diffuse_model |
"rbl_2010" |
"preserve_fraction" |
Recalculate diffuse radiation with Ridley–Boland–Lauret 2010 or retain the baseline diffuse fraction. |
illuminance_model |
"perez_1990" |
"preserve" |
Recalculate illuminance/luminance with Pérez 1990 or preserve the prior relation. |
snow_depth |
"auto" |
"off" |
Use snd when both future and reference cases contain
it, require it, or do not query it: "auto",
"required", "off". |
ground_temperatures |
"recalculate" |
"preserve" |
Recalculate the EPW ground-temperature header with Kusuda–Achenbach or retain it. |
typical_extreme_periods |
"recalculate" |
"preserve" |
Recalculate six hemisphere-aware periods or retain the baseline header. |
design_conditions |
"drop" |
"preserve" |
Write DESIGN CONDITIONS,0 or retain the baseline design
conditions. |
belcher_options() creates a complete option list using
enhanced defaults. For a legacy profile, pass a partial named list when
only selected legacy defaults should change, for example
belcher(profile = "legacy", options = list(snow_depth = "auto")).
Use print(belcher(), verbose = TRUE) to inspect all
resolved values that will be stored with a task.
Enhanced tdb = "auto" uses combined morphing month by
month when complete tasmax and tasmin are
available and falls back to shift with a diagnostic otherwise.
humidity_source = "auto" selects one source for the whole
case: complete huss + tas + ps is preferred for the default
shift humidity method, while an explicit non-shift RH override keeps the
HURS path. Radiation and EPW header policies are likewise persisted in
the recipe so resumed jobs cannot change behavior when package defaults
evolve.
Temperature "combined" uses the mean daily range of the
baseline EPW,
rather than the difference between one absolute monthly maximum and minimum. The hourly transformation remains
where alpha is the future-minus-reference DTR change
divided by
.
A month with missing extrema, a non-finite factor, or
uses shift for that month and records the fallback in
factors/diagnostics.
The default 72-hour cyclic transition is centered on every month boundary, including December–January. A constrained smoothstep system retains the original monthly mean of each factor. Combined temperature also corrects the baseline-temperature/time-varying-alpha covariance; precipitation is excluded from smoothing so monthly totals and the baseline wet-hour mask remain intact.
For state-based humidity, the baseline EPW dry bulb, RH, and station
pressure are converted to hourly specific humidity. The monthly
future-minus-reference huss change is applied before RH and
dew point are recovered using morphed temperature and pressure.
Saturation clipping enforces 0 <= RH <= 100 and
Tdew <= Tdb. The "auto" source decision is
made once for a complete case, so future and reference data never mix
HUSS and HURS paths.
The enhanced short-wave chain integrates solar geometry at one-minute steps over each EPW interval to recalculate extraterrestrial horizontal and direct normal irradiation (N10/N11). RBL 2010 partitions morphed GHI into DHI and DNI; the result is bounded, non-negative, and closed against the effective solar projection. Pérez 1990 then recalculates N16–N19 illuminance/luminance, with night values and EPW sentinels handled explicitly.
When enabled, snd is converted from CMIP metres to EPW
centimetres and scales the existing EPW snow-depth sequence by the
future/reference monthly ratio. Zero reference snow or a baseline
without snow does not synthesize new snow events. Final writing derives
header updates from the persisted hourly Parquet artifact: ground
temperatures use Kusuda–Achenbach, typical/extreme periods use complete
rolling seven-day windows and hemisphere-aware seasons, and design
conditions follow the selected drop/preserve policy.
epw_morph_backends()
belcher <- epw_morph_backend("belcher")
belcher$label
belcher$required_variables()
belcher$methods()
belcher$rules()The backend rules are the contract between planning and execution.
Each rule declares a backend step, the EPW weather field it produces or
checks, required and optional CMIP variables, method choices, and
whether the step is derived from other backend outputs.
preview_plan(), summarise_baseline(), and
epw_morph_variables() read these rules; they do not keep a
separate hard-coded list of Belcher fields.
Use method overrides when the algorithm is still Belcher but you want a different transformation for selected primary variables:
belcher_shift_recipe <- epw_morph_recipe(
"belcher",
methods = c(
tdb = "shift",
rh = "shift"
)
)
morpher <- epw_morpher(
store = store,
epw = epw,
site_id = "SIN",
recipe = belcher_shift_recipe,
label = "Singapore baseline"
)The override names must be backend step names. For the built-in
Belcher backend, the primary overrideable steps are tdb,
rh, p, hor_ir,
glob_rad, and wind; allowed values are
"shift", "stretch", and
"combined", with "auto" additionally available
for tdb. Precipitation is handled by a dedicated
conservative step that preserves the baseline wet-hour timing. Derived
steps such as dew point, direct normal radiation, and precipitation rate
are declared in the rules but are not independent method overrides.
Register a new backend when the variable requirements, output fields,
or execution algorithm change. A backend is an
EpwMorphBackend R6 object with a rule table and a runner
function. The built-in "belcher" backend is just the first
registration; it is not a special execution path.
The runner receives one canonical context:
context$inputs: role-addressable future-weather inputs.
Its weather_template, observed_reference,
model_historical, and model_future properties
remain distinct even when one role is absent;context$epw: the package’s internal baseline EPW
representation;context$climate: store-native climate rows with
variable_id, time, period,
year, lon, lat,
dist, units, value, and case
metadata;context$reference_climate: optional matching historical
model rows;context$observed_reference: optional observations or
reanalysis rows;context$recipe: the selected recipe, including method
overrides and rules;context$by, context$case,
context$strict, and context$warning.context$epw, context$climate, and
context$reference_climate remain available for existing
backends. New components should select inputs by their role through
context$inputs instead of interpreting a generic
reference.
Future-weather methods can be assembled from seven ordered component stages. Each component declares its required and optional input roles, supported representations, frequencies, calendars, variable alternatives, input and output kinds, dimensional scope, and whether it is stochastic. Compatibility is checked from stage order and the intermediate kinds exchanged between components.
| Component | Used for | Main function | Problem addressed |
|---|---|---|---|
preprocess |
Preparing every source before climate calculations | Normalize variables, units, missing values, spatial selections, and source metadata into method-ready inputs | Raw EPW, observations, reanalysis, and climate-model data otherwise have incompatible schemas and units |
calendar |
Placing sources on a shared annual and temporal coordinate | Preserve each source calendar, calculate annual phase, form circular windows, and align 360-, 365-, and 366-day inputs | Direct date matching either loses days or assigns different seasonal positions to nominally similar dates |
signal |
Estimating or transferring the future climate signal | Apply delta, scaling, quantile-based, weather-typing, or other statistical transformations to aligned groups | Different methods require different reference inputs and assumptions but need one validated, comparable execution contract |
sequence |
Constructing the order of future days or weather states | Preserve, resample, rearrange, or generate daily sequences, including stochastic and analogue methods | A climatology or corrected distribution does not define event order, persistence, spells, or compound extremes |
hourly |
Converting daily targets or selected days into hourly weather | Reconstruct 24-hour profiles from an EPW template, an hourly library, or a method-specific disaggregation model | Daily CMIP data cannot be written directly as an hourly EPW and naive interpolation damages diurnal shape and extrema |
physics |
Closing related weather variables after transformation | Enforce temperature statistics, humidity closure, radiation balance, bounds, and cross-variable consistency | Independently transformed variables can violate thermodynamic, radiative, or EPW constraints |
output |
Producing the final weather artifact and its record | Assemble fields and headers, write EPW or intermediate artifacts, and retain method settings, provenance, and diagnostics | Numerically valid series are not yet a standards-compliant, reproducible, and auditable future weather file |
These components run inside the existing high-level workflow rather
than replacing it. shift_request(), collection, download,
and extraction obtain and persist the source data.
shift_morph() builds the four role-addressable inputs,
compiles the component names stored in the selected recipe, and executes
the scientific stages through the backend. shift_epw()
remains the durable final file writer. The built-in
daily_temperature() and sobie_curry_daily()
methods use all seven stages, including explicit sequence
components that preserve the baseline EPW day order. The
ek_daily_temperature() method uses the same seven-stage
execution boundary. arima_temperature() also uses all seven
stages, but its calendar component forms native-calendar
monthly distributions and its signal component requires
both historical model output and a separate observed reference.
Bias-adjustment implementations belong to the signal
stage. Their common apply operation validates the
role-addressable inputs, resolves variable-specific settings, and
executes aligned groups. Each method supplies only an
apply_group kernel receiving the group’s input payloads,
resolved settings, and key. Published defaults retain their references,
experimental defaults are labelled and warned about, and user overrides
do not change that provenance. The execution result records the settings
actually used after overrides. Calendar mapping and group alignment
happen before the kernel, so a method cannot silently impose a
Gregorian, no-leap, or fixed 366-day interpretation. Group failures
either abort or return an explicit diagnostic with a NULL
result; they are never silently converted to missing numeric values.
Eight package-native standalone signals currently use this contract:
| Registry name | Correction | Output backbone | Variable-profile evidence |
|---|---|---|---|
linear_scaling_daily |
Monthly observed-minus-model mean bias, additive for temperature and multiplicative for precipitation | model_future |
Published defaults for tas,
tasmin, tasmax, and pr
|
delta_change_daily |
Monthly historical-to-future mean change, additive for temperature and multiplicative for precipitation | observed_reference |
Published defaults for tas,
tasmin, tasmax, and pr
|
quantile_mapping_daily |
in calendar-neutral circular daily windows | model_future |
Published method-variable profiles for tas
and pr; implementation-selected defaults for
hurs, psl, rlds,
sfcWind, tasmin, and tasmax
remain experimental |
quantile_delta_mapping_daily |
Transfer or onto , where | model_future |
Published absolute-change temperature and relative-change precipitation profiles; implementation-selected defaults for other supported variables remain experimental |
equidistant_cdf_matching_daily |
Apply , where , using the Li et al. parametric distributions | model_future |
Li et al. published monthly profiles for
tas and pr; native calendar-month daily
pooling is an experimental epwshiftr adaptation |
scaled_distribution_mapping_daily |
Parametric distribution and recurrence-interval scaling, absolute for temperature and relative for precipitation | model_future |
Published profiles for tas and
pr; applying the Normal branch to tasmin and
tasmax remains experimental |
cdf_transform_daily |
Construct , then quantile match the future-model sequence to that target CDF | model_future |
Published daily profiles for pr,
tas, tasmin, tasmax,
rsds, and sfcWind
|
isimip3basd_daily |
Generate pseudo-future observations by transferring modeled quantile changes, then map the future-model distribution onto the pseudo-future target | model_future |
Published ISIMIP3BASD 3.0.x profiles for eight direct
variables and the three prsnratio, tasrange,
and tasskew reconstruction components |
quantile_mapping_daily uses linear empirical-CDF
interpolation with average-rank ties, type-7 inverse quantiles, and
endpoint clamping. Its default 31-day window is selected by canonical
annual phase, so 360-, 365-, and 366-day source calendars are not paired
by nominal date. Precipitation uses a mixed dry-day/positive-amount
hurdle distribution rather than the continuous mapping unchanged.
Dry-day randomization is controlled by a recorded seed and a fixed
method-local generator, leaving R’s global random-number state
untouched. Bounds, sample coverage, tail use, clipping, dry-day
frequencies, and the resolved stochastic settings are retained in result
provenance.
quantile_delta_mapping_daily differs from ordinary
Quantile Mapping by estimating each projected value’s probability from a
time-dependent future distribution. Its default calendar-neutral 91-day
seasonal window represents the published three-month pool, while a
symmetric 31-year future window makes the publication’s 30-year
moving-period calculation executable on whole model years. Temperature
transfers absolute changes in quantiles and precipitation transfers
relative changes. Precipitation values at or below the published 0.05
mm/day trace threshold are replaced by deterministic positive uniforms
before adjustment and censored back to zero afterward. Window sample
counts, transferred changes, clipping, censoring, and effective seeds
are retained in provenance.
equidistant_cdf_matching_daily implements the additive
equation published by Li et al. (2010):
, where
.
Cannon et al. (2015) showed that this equation is mathematically
equivalent to the absolute form of Quantile Delta Mapping. The component
remains separate because it preserves the Li distribution model:
temperature uses four-parameter Beta distributions whose observed range
is extended by half a sample standard deviation, while precipitation
uses a point mass at zero plus a zero-location Gamma distribution for
wet values. The original method was applied to monthly fields, not daily
series. epwshiftr therefore marks its native CF-calendar month pooling
of daily values as an experimental frequency adaptation rather than a
published daily default. Negative adjusted precipitation values are
clipped to zero. Fitted parameters, monthly sample coverage, probability
clamping, dry counts, clipping, and this frequency provenance remain
inspectable in the result.
scaled_distribution_mapping_daily instead fits the
parametric distributions and recurrence-interval changes from Switanek
et al. (2017) within each calendar month. Temperature is linearly
detrended, fitted with Normal distributions, adjusted through two-tailed
recurrence intervals, and restored with the projected trend and
observed-minus-historical model mean bias. Precipitation values below
0.1 mm/day are dry; positive amounts use zero-location Gamma
maximum-likelihood fits, multiplicative quantile scaling, one-tailed
recurrence intervals, and the published expected wet-day equation. The
projected period is divided into disjoint 10-year output blocks fitted
on surrounding 30-year windows where data exist. Edge truncation,
distribution parameters, sample coverage, corrected wet counts, and the
method’s inability to turn additional dry projected days into wet days
are explicit in result provenance. Native 360-, 365-, and 366-day
calendars contribute samples to their own calendar months without
Gregorian date coercion.
cdf_transform_daily differs from both ordinary Quantile
Mapping and QDM by first estimating a complete future observed-reference
CDF from the historical observed/model relation and the future-model
CDF. The implementation aligns the historical and future model samples
by the same observed-minus-historical-model mean shift used in the
method authors’ R package. It then evaluates the chained empirical CDF
on an explicit grid, extends unsupported tails through the published
constant-correction rule, and uses the left endpoint of CDF plateaus as
the generalized inverse. Following the Famien et al. (2018) daily Africa
application, each native calendar month is fitted in a 17-year future
window and writes only its central 9-year block. Those numbers are
application defaults rather than requirements of the original CDF-t
transformation. Famien et al. do not specify how to supply the four-year
flanks at the start and end of a requested series, so epwshiftr
truncates unavailable flanks and records this package-selected edge
policy explicitly. For precipitation, Singularity Stochastic Removal
replaces sub-threshold values with deterministic uniforms below
kg m-2 s-1 before CDF-t and returns adjusted sub-threshold values to
zero afterward. The generator and effective role-specific seeds are
recorded without changing R’s global random state.
isimip3basd_daily implements the daily marginal
bias-adjustment stage from Lange (2019) with the official ISIMIP3BASD
3.0.x variable configuration. It first transfers the modeled
historical-to-future change at common quantiles onto historical
observations to form a pseudo-future target, then maps the future-model
sequence to that target. Variable profiles select additive,
multiplicative/mixed, or bounded change transfer; Normal, Gamma,
Weibull, or empirical distribution mapping; physical threshold
frequencies; optional annual-mean detrending; snow-ratio missing-value
imputation; and the short-wave upper-bound climatology. Short-wave
rsds bounds apply to its dimensionless upper-bound
fraction; the final result is rescaled to the future physical radiation
magnitude. The registered direct variables are hurs,
pr, ps, psl, rlds,
rsds, sfcWind, and tas.
prsnratio, tasrange, and tasskew
are adjusted as published intermediate components; reconstructing
prsn, tasmin, and tasmax from
them remains a separate physics-stage operation rather than three
independent marginal adjustments.
The original software requires a proleptic Gregorian time coordinate. epwshiftr instead assigns each native 360-, 365-, or 366-day observation to a calendar-neutral annual phase and applies the published 31-day circular window on a common 365-day target grid. This calendar adaptation, every effective seed, parameter fit or empirical fallback, transferred bound frequency, detrending decision, short-wave upper-bound cycle, and clipping count are retained in result provenance. The component implements marginal bias adjustment only; the ISIMIP MBCnSD spatial downscaling workflow is a separate stage and is not implied by selecting this signal.
The runner returns epw_morph_result() with complete
hourly EPW weather data. EpwMorpher$run() writes that data
as Parquet and adds the case metadata columns needed by
write_epw().
Rule tables must include step, epw_field,
method, and required. They can use either the
compact scalar columns variable_id and
optional_variable_id or list columns
required_variables and optional_variables. Use
a method_choices list column when one step supports a
narrower set of method values than the backend as a whole.
This minimal backend copies the baseline EPW and offsets dry-bulb
temperature. It is deliberately simple: a real backend would read
context$climate, validate its required variables, and
calculate weather fields from those rows.
offset_rules <- data.table::data.table(
step = "dry",
epw_field = "dry_bulb_temperature",
variable_id = "tas",
optional_variable_id = NA_character_,
method = "plus_one",
required = TRUE,
derived = FALSE,
method_choices = list(c("plus_one", "plus_two"))
)
offset_runner <- function(context, backend) {
epw <- context$epw$clone()
suppressMessages(epw$drop_unit())
weather <- data.table::as.data.table(epw$data())
amount <- switch(
context$recipe$methods[["dry"]],
plus_two = 2,
plus_one = 1
)
weather[, dry_bulb_temperature := dry_bulb_temperature + amount]
epw_morph_result(context, epw = epw, data = weather)
}
offset_backend <- EpwMorphBackend$new(
name = "constant_offset",
label = "Constant dry-bulb offset",
methods = c(dry = "plus_one"),
method_choices = c("plus_one", "plus_two"),
rules = offset_rules,
runner = offset_runner
)
epw_morph_register_backend("constant_offset", offset_backend)
offset_recipe <- epw_morph_recipe(
"constant_offset",
backend = "constant_offset",
methods = c(dry = "plus_two")
)
epw_morph_variables(offset_recipe)Use the recipe exactly like the built-in one:
morpher <- epw_morpher(
store = store,
epw = epw,
site_id = "SIN",
recipe = offset_recipe,
label = "Singapore baseline"
)Prefer registering a new backend name over overwriting
"belcher". Use overwrite = TRUE only for
interactive experiments where replacing an existing registration is
intentional.
Preflight checks extraction coverage and baseline readiness without modifying store state.
diagnostics <- morpher$preflight(
plan_id = region$plan_id,
periods = periods,
reference_plan_id = reference_region$plan_id,
reference_periods = reference_periods,
strict = TRUE
)
diagnosticsBlocking diagnostics should be fixed before planning. Common causes are missing required variables, incomplete monthly coverage, missing baseline EPW fields, or using periods that are not covered by the extracted climate data.
Morphing uses monthly summary statistics. The climate summary comes from store extraction outputs; the baseline summary comes from the EPW file.
climate <- morpher$summarise_climate(
plan_id = region$plan_id,
periods = periods,
strict = TRUE,
overwrite = FALSE
)
reference_climate <- morpher$summarise_climate(
plan_id = reference_region$plan_id,
periods = reference_periods,
strict = TRUE,
overwrite = FALSE
)
baseline <- morpher$summarise_baseline(overwrite = FALSE)
unique(climate$summary_id)
unique(reference_climate$summary_id)
unique(baseline$baseline_id)The summary IDs are stable for the selected extraction plans, periods, baseline, and recipe. Reusing them lets you preview or rerun later stages without re-extracting climate data.
preview_plan() calculates plan rows, factor rows, and
diagnostics without writing them. plan() persists the
selected plan and factor rows.
preview <- morpher$preview_plan(
summary_id = unique(climate$summary_id),
reference_summary_id = unique(reference_climate$summary_id),
baseline_id = unique(baseline$baseline_id),
by = c("source_id", "experiment_id", "variant_label", "period"),
strict = TRUE
)
preview$plan
preview$factors
preview$diagnostics
plan <- morpher$plan(
summary_id = unique(climate$summary_id),
reference_summary_id = unique(reference_climate$summary_id),
baseline_id = unique(baseline$baseline_id),
by = c("source_id", "experiment_id", "variant_label", "period"),
strict = TRUE
)The by columns define morphing cases. The default
creates one case per model, experiment, member, and period. Add or
remove grouping columns only when those columns exist in the climate
summary and reflect the cases you intend to write.
Run diagnostics before executing strict plans.
morph_id <- plan$morph_id[[1L]]
morpher$diagnose(morph_id)
morpher$check(morph_id)
results <- morpher$run(
morph_id,
overwrite = FALSE,
resume = TRUE
)run() writes hourly morphed weather as Parquet artifacts
under the store. It does not write EPW text files yet.
Write EnergyPlus Weather files after morphing succeeds.
outputs <- morpher$write_epw(
morph_id,
dir = "outputs/future-epw",
separate = TRUE,
overwrite = FALSE,
resume = TRUE
)
morpher$status(morph_id)
morpher$outputs(morph_id)The high-level shift_epw() function wraps this step and
returns a ShiftOutputs stage. Use
shift_outputs() and shift_data() when you do
not need lower-level manifest IDs.
For the enhanced profile, final EPW writing uses the persisted hourly
Parquet artifact to recalculate ground temperatures and six
hemisphere-aware typical/extreme periods. Stale design conditions are
written as DESIGN CONDITIONS,0; set the corresponding
belcher_options() policies to "preserve" when
the original headers must remain untouched.
For most scripts, use the task-oriented API and supply matching historical CMIP6 data when they are available. epwshiftr resolves the store stages, EPW writing, and final export path:
run <- shift_future_epw(
epw = epw,
climate = shift_cmip6(
model = "BCC-CSM2-MR",
scenarios = c("ssp126", "ssp585")
),
periods = list(`2060s` = 2055:2065),
method = belcher(
reference = historical_reference(1995:2014)
),
dir = "~/Downloads/epwshiftr-test"
)
shift_status(run)
shift_outputs(run)
shift_missing(run)If no suitable historical reference data exist,
belcher() is the fallback: it uses the EPW climatology
directly and does not infer a historical request.
When you already have lower-level extraction plan IDs and need direct
control of the morphing engine, use workflow().
result <- morpher$workflow(
plan_id = region$plan_id,
periods = periods,
reference_plan_id = reference_region$plan_id,
reference_periods = reference_periods,
by = c("source_id", "experiment_id", "variant_label", "period"),
strict = TRUE,
dir = "outputs/future-epw",
overwrite = FALSE,
resume = TRUE
)
names(result)For most users, the equivalent high-level path is still clearer:
plan <- shift_future_epw(
epw = epw,
climate = shift_cmip6(
model = "BCC-CSM2-MR",
scenarios = c("ssp126", "ssp585")
),
periods = list(`2060s` = 2055:2065),
method = belcher(
reference = historical_reference(1995:2014)
),
dir = "~/Downloads/epwshiftr-test",
store = "~/cmip6-singapore-store",
dry_run = TRUE
)
shift_explain(plan)
run <- shift_run(plan)EpwMorpher.