{"spec_id":"line-load-duration","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' line-load-duration: Load Duration Curve for Energy Systems\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 93/100 | Created: 2026-06-10\n\nlibrary(ggplot2)\nlibrary(scales)\nlibrary(ragg)\n\nset.seed(42)\n\n# --- Theme tokens -----------------------------------------------------------\nTHEME       <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG     <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nELEVATED_BG <- if (THEME == \"light\") \"#FFFDF6\" else \"#242420\"\nINK         <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT    <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\nINK_MUTED   <- if (THEME == \"light\") \"#6B6A63\" else \"#A8A79F\"\n\n# Imprint palette (hybrid-v3 canonical order)\nIMPRINT_PALETTE <- c(\n  \"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n  \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"\n)\n\n# Region colors: semantic mapping (stable base=green, moderate=ochre, high peak=red)\nCOL_BASE <- IMPRINT_PALETTE[1]  # #009E73 — green, stable base load (first series)\nCOL_INT  <- IMPRINT_PALETTE[4]  # #BD8233 — ochre, intermediate load\nCOL_PEAK <- IMPRINT_PALETTE[5]  # #AE3030 — matte red, peak demand\n\nGRID_COLOR <- adjustcolor(INK, alpha.f = 0.12)\n\n# Capacity thresholds (MW)\nBASE_CAP <- 550\nINT_CAP  <- 900\n\n# --- Data -------------------------------------------------------------------\nn_hours <- 8760\nt       <- seq(0, 2 * pi, length.out = n_hours)\n\n# Synthetic annual load: base demand + seasonal cycle + random variation\nbase_demand  <- 700\nseasonal_var <- 150 * cos(t + 0.3)\nrandom_var   <- rnorm(n_hours, 0, 100)\nload_raw     <- base_demand + seasonal_var + random_var\n\n# Add extreme peak events (heat waves, cold snaps)\nspike_idx           <- sample(seq_len(n_hours), 450)\nload_raw[spike_idx] <- load_raw[spike_idx] + runif(450, 80, 380)\n\n# Sort descending — this defines the load duration curve\nload_sorted <- sort(pmin(pmax(load_raw, 380), 1250), decreasing = TRUE)\n\ndf <- data.frame(\n  hour    = 0:(n_hours - 1),\n  load_mw = load_sorted\n)\n\n# Summary statistics\ntotal_energy_twh <- round(sum(df$load_mw) / 1e6, 2)\n\npeak_mask  <- df$load_mw > INT_CAP\nint_mask   <- df$load_mw > BASE_CAP & df$load_mw <= INT_CAP\nbase_mask  <- df$load_mw <= BASE_CAP\n\npeak_hours <- sum(peak_mask)\nint_hours  <- sum(int_mask)\nbase_hours <- sum(base_mask)\n\n# X-midpoints for region labels (centered inside each region)\npeak_x_mid <- max(peak_hours / 2, 300)\nint_x_mid  <- peak_hours + int_hours / 2\nbase_x_mid <- peak_hours + int_hours + base_hours / 2\n\n# --- Plot -------------------------------------------------------------------\ntitle_str  <- \"Annual Load Duration Curve · line-load-duration · r · ggplot2 · anyplot.ai\"\ntitle_size <- max(7, round(12 * 67 / nchar(title_str)))\n\np <- ggplot(df, aes(x = hour)) +\n  # Fill regions stacked from bottom upward\n  geom_ribbon(\n    aes(ymin = 0, ymax = pmin(load_mw, BASE_CAP)),\n    fill = COL_BASE, alpha = 0.5\n  ) +\n  geom_ribbon(\n    aes(ymin = pmin(load_mw, BASE_CAP), ymax = pmin(load_mw, INT_CAP)),\n    fill = COL_INT, alpha = 0.5\n  ) +\n  geom_ribbon(\n    aes(ymin = pmin(load_mw, INT_CAP), ymax = load_mw),\n    fill = COL_PEAK, alpha = 0.5\n  ) +\n  # Load duration curve\n  geom_line(aes(y = load_mw), color = INK, linewidth = 0.9) +\n  # Capacity threshold lines\n  geom_hline(yintercept = BASE_CAP, linetype = \"dashed\",\n             color = INK_SOFT, linewidth = 0.5) +\n  geom_hline(yintercept = INT_CAP, linetype = \"dashed\",\n             color = INK_SOFT, linewidth = 0.5) +\n  # Region labels — centered inside each colored zone\n  annotate(\"text\", x = peak_x_mid,\n           y = INT_CAP + (max(df$load_mw) - INT_CAP) * 0.42,\n           label = \"Peak Load\",\n           color = INK, size = 3.8, hjust = 0.5, fontface = \"bold\") +\n  annotate(\"text\", x = int_x_mid,\n           y = BASE_CAP + (INT_CAP - BASE_CAP) * 0.35,\n           label = \"Intermediate\\nLoad\",\n           color = INK, size = 3.5, hjust = 0.5, fontface = \"bold\") +\n  annotate(\"text\", x = base_x_mid,\n           y = BASE_CAP * 0.42,\n           label = \"Base Load\",\n           color = INK, size = 3.8, hjust = 0.5, fontface = \"bold\") +\n  # Capacity tier labels (right-aligned near right edge, above each dashed line)\n  annotate(\"text\", x = 8680, y = BASE_CAP + 30,\n           label = sprintf(\"Base: %d MW\", BASE_CAP),\n           color = INK_MUTED, size = 2.8, hjust = 1) +\n  annotate(\"text\", x = 8680, y = INT_CAP + 30,\n           label = sprintf(\"Intermediate: %d MW\", INT_CAP),\n           color = INK_MUTED, size = 2.8, hjust = 1) +\n  # Total annual energy — upper-right empty space (above the curve at high x)\n  annotate(\"text\", x = 6800, y = 1120,\n           label = sprintf(\"Annual Energy\\n%.2f TWh\", total_energy_twh),\n           color = INK_MUTED, size = 3.2, hjust = 0.5) +\n  # Scales\n  scale_x_continuous(\n    name   = \"Hours per Year (sorted by descending load)\",\n    breaks = c(0, 2000, 4000, 6000, 8000, 8760),\n    labels = c(\"0\", \"2,000\", \"4,000\", \"6,000\", \"8,000\", \"8,760\"),\n    expand = expansion(mult = c(0.005, 0.02))\n  ) +\n  scale_y_continuous(\n    name   = \"Power Demand (MW)\",\n    breaks = seq(0, 1200, by = 200),\n    labels = scales::comma,\n    expand = expansion(mult = c(0, 0.1))\n  ) +\n  labs(title = title_str) +\n  # Theme\n  theme_minimal(base_size = 8) +\n  theme(\n    plot.background    = element_rect(fill = PAGE_BG, color = PAGE_BG),\n    panel.background   = element_rect(fill = PAGE_BG, color = NA),\n    panel.grid.major.y = element_line(color = GRID_COLOR, linewidth = 0.3),\n    panel.grid.major.x = element_blank(),\n    panel.grid.minor   = element_blank(),\n    panel.border       = element_blank(),\n    axis.line          = element_line(color = INK_SOFT, linewidth = 0.4),\n    axis.title         = element_text(color = INK,      size = 10),\n    axis.text          = element_text(color = INK_SOFT, size = 8),\n    plot.title         = element_text(color = INK,      size = title_size,\n                                      face = \"bold\",\n                                      margin = margin(b = 10)),\n    plot.margin        = margin(t = 15, r = 20, b = 10, l = 10, unit = \"pt\"),\n    legend.position    = \"none\"\n  )\n\n# --- Save -------------------------------------------------------------------\nggsave(\n  filename = sprintf(\"plot-%s.png\", THEME),\n  plot     = p,\n  device   = ragg::agg_png,\n  width    = 8,\n  height   = 4.5,\n  units    = \"in\",\n  dpi      = 400\n)\n"}