--- title: "Measuring RPS stringency" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Measuring RPS stringency} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4) has_keys <- nzchar(Sys.getenv("EIA_API_KEY")) && nzchar(Sys.getenv("BEA_API_KEY")) ``` ```{r setup} library(rpspanel) ``` Many studies code a Renewable Portfolio Standard (RPS) as a yes/no dummy. Real policies differ in targets, timelines, covered utilities, eligible resources, carve-outs and out-of-state flexibility. `rps_stringency()` turns coded rules into comparable annual measures. ## The rule coding ```{r rules} rules <- rps_rules() rules head(rules$targets) ``` Each RPS state has one row in `rules$rules` (enactment and first compliance year, target type, coverage share, design features, sources and notes) and one row per legal target in `rules$targets`. States missing from the coding are *not yet coded*, and get `NA` rather than zero. > **Status.** The bundled coding covers all 50 states and DC. Rows marked > `verified = TRUE` have been checked against the statute, the DSIRE > program page and the LBNL RPS & CES data, with sources in the `source` > and `notes` columns. Single-number simplifications (one coverage share, > one schedule for states with utility-specific targets) are described in > `notes`. Please report corrections. You can supply your own coding with > `rps_rules(rules_path = ..., targets_path = ...)`. ## Methods | method | column(s) | definition | |---|---|---| | `adoption` | `rps_adoption` | 1 if an RPS is in force | | `nominal` | `rps_nominal` | required share of sales, interpolated between legal targets | | `coverage_adjusted` | `rps_coverage_adj` | nominal x share of sales covered | | `incremental` | `rps_incremental`, `rps_baseline_share` | coverage-adjusted requirement minus the existing renewable share at enactment (Yin and Powers 2010) | | `flexibility` | `rps_oos_rec`, `rps_carveout`, `rps_acp` | design indicators while an RPS is in force | ```{r stringency} s <- rps_stringency(2000:2030, states = c("CA", "OR", "WA", "NC", "MA")) head(s[s$state == "OR", ], 15) ``` ### Linear or step interpolation Legal schedules often step (for example Washington: 3% from 2012, 9% from 2016, 15% from 2020). `interpolation = "step"` follows the schedule exactly; `"linear"` (the default) spreads the ramp evenly between target years. ```{r interp} lin <- rps_stringency(2010:2022, "WA", method = "nominal") step <- rps_stringency(2010:2022, "WA", method = "nominal", interpolation = "step") plot(lin$year, lin$rps_nominal, type = "l", ylab = "Required share", xlab = "", main = "Washington RPS") lines(step$year, step$rps_nominal, type = "s", lty = 2) legend("topleft", c("linear", "step"), lty = 1:2, bty = "n") ``` ### Nominal requirements across states ```{r nominal-plot} cols <- c(CA = "#1b9e77", OR = "#d95f02", WA = "#7570b3", NC = "#e7298a", MA = "#66a61e") plot(NA, xlim = c(2000, 2030), ylim = c(0, 0.65), xlab = "", ylab = "Nominal requirement (share of sales)") for (st in names(cols)) { d <- s[s$state == st, ] lines(d$year, d$rps_nominal, col = cols[st], lwd = 2) } legend("topleft", names(cols), col = cols, lwd = 2, bty = "n") ``` ### Incremental requirement The incremental measure subtracts what the state already produced when it enacted the standard, separating new pressure from existing renewables. `build_panel()` fills the baseline from EIA generation data: the renewable share in the enactment year (or the first later year with data), measured the same way as the target. For New York, whose targets count existing hydro, the baseline includes hydro (about 19% in 2004, close to the 19.3% written into its RPS order); elsewhere it is the non-hydro share. A generation-based baseline is a weak guide for states that import most of their electricity, such as Vermont. You can always pass your own baseline: ```{r incremental} base <- data.frame(state = c("CA", "OR"), baseline_share = c(0.11, 0.03)) inc <- rps_stringency(c(2010, 2020, 2030), c("CA", "OR"), method = c("coverage_adjusted", "incremental"), baseline = base) inc ``` `OR` has `NA` coverage-adjusted and incremental values because its coverage share is not yet coded. ### Capacity-based standards (Texas and Iowa) Texas and Iowa set their standards in megawatts rather than as a share of sales. `rps_stringency()` converts them, like Berkeley Lab does, into the share of covered sales the capacity would supply: `MW x capacity factor x 8,760 hours / covered sales`. The capacity factor defaults to 0.33 (typical of wind) and can be changed. `build_panel()` supplies retail sales automatically; with `rps_stringency()` you pass them: ```{r capacity} tx_sales <- data.frame(state = "TX", year = c(2010, 2015), sales_gwh = c(358000, 380000)) rps_stringency(c(2010, 2015), "TX", method = "nominal", sales = tx_sales) rps_stringency(c(2010, 2015), "TX", method = "nominal", sales = tx_sales, capacity_factor = 0.4) ``` ## A template for Yin and Powers (2010)-style regressions Yin and Powers (2010) regress the non-hydro renewable share of generation on the incremental requirement with state and year fixed effects. The template below shows the workflow; it is not a replication, because their sample, coding and controls differ from the bundled starter coding. ```{r yp, eval = FALSE} library(fixest) p <- build_panel(years = 2001:2022, vars = c("re_share_nonhydro", "rps_incremental", "rps_adoption", "gdp_real_pc", "elec_price_real")) feols(re_share_nonhydro ~ rps_incremental + log(gdp_real_pc) + elec_price_real | state + year, data = as_pdata(p, "fixest"), cluster = ~state) ``` ## Reference Yin, H., & Powers, N. (2010). Do state renewable portfolio standards promote in-state renewable generation? *Energy Policy*, 38(2), 1140-1149.