///|
fn standard_tariff() -> IntSeries {
IntSeries::new("time-of-use tariff", "micro/kWh", 60, [
280000, 260000, 240000, 220000, 200000, 220000, 300000, 420000, 480000, 360000,
260000, 180000, 140000, 120000, 130000, 180000, 320000, 520000, 680000, 720000,
620000, 480000, 360000, 300000,
])
}
///|
fn standard_carbon() -> IntSeries {
IntSeries::new("grid carbon intensity", "g/kWh", 60, [
620, 600, 580, 560, 540, 520, 500, 470, 430, 390, 340, 300, 260, 240, 250, 300,
390, 520, 650, 720, 700, 680, 660, 640,
])
}
///|
fn standard_solar() -> IntSeries {
IntSeries::new("rooftop solar forecast", "W", 60, [
0, 0, 0, 0, 0, 0, 100, 350, 800, 1400, 2200, 3000, 3400, 3200, 2600, 1800, 900,
300, 0, 0, 0, 0, 0, 0,
])
}
///|
fn standard_base_load() -> IntSeries {
IntSeries::new("household base load", "W", 60, [
260, 240, 230, 220, 220, 260, 420, 620, 480, 360, 320, 300, 320, 340, 360, 400,
520, 780, 1100, 1250, 980, 700, 480, 340,
])
}
///|
fn standard_grid_limit(limit_w : Int) -> IntSeries {
IntSeries::new("connection limit", "W", 60, Array::make(24, limit_w))
}
///|
/// Full household example used by the CLI, README, and web engine.
pub fn home_day_example() -> PlanningInput {
{
title: "Sunny weekday household",
slot_minutes: 60,
horizon_slots: 24,
tariff_micro_per_kwh: standard_tariff(),
carbon_g_per_kwh: standard_carbon(),
solar_w: standard_solar(),
base_load_w: standard_base_load(),
grid_limit_w: standard_grid_limit(5000),
tasks: [
LoadTask::fixed(
"router",
"Router and communications",
35,
24,
0,
priority=Critical,
).with_tag("communications"),
LoadTask::fixed(
"refrigerator-cycle",
"Refrigerator defrost cycle",
180,
2,
2,
priority=High,
).with_tag("food-safety"),
LoadTask::shiftable("washer", "Washing machine", 850, 2, 8, 20, 18).with_tag(
"laundry",
),
LoadTask::shiftable("dishwasher", "Dishwasher", 1100, 2, 9, 24, 21).with_tag(
"kitchen",
),
LoadTask::shiftable(
"water-heater",
"Domestic hot-water heater",
2200,
2,
10,
22,
18,
priority=High,
).with_tag("hot-water"),
LoadTask::interruptible(
"ev", "Electric vehicle top-up", 1800, 4, 0, 24, 20, 2,
).with_tag("mobility"),
LoadTask::optional("dehumidifier", "Dehumidifier", 420, 3, 8, 20, 15).with_tag(
"optional-comfort",
),
],
battery: Some({
..BatterySpec::new("Home LFP battery", 10000, 5200, 2500, 3000, 3000),
minimum_wh: 1000,
maximum_wh: 9500,
cycle_cost_micro_per_kwh: 12000,
}),
outages: [],
weights: ObjectiveWeights::balanced(),
allow_grid_export: true,
export_credit_micro_per_kwh: 80000,
random_seed: 20260808U,
}
}
///|
/// Small apartment without solar or storage.
pub fn compact_apartment_example() -> PlanningInput {
let base = PlanningInput::empty("Compact apartment", 12)
{
..base,
tariff_micro_per_kwh: IntSeries::new("tariff", "micro/kWh", 60, [
250000, 230000, 220000, 200000, 180000, 160000, 180000, 260000, 420000, 550000,
480000, 320000,
]),
carbon_g_per_kwh: IntSeries::new("carbon", "g/kWh", 60, [
520, 500, 480, 450, 400, 350, 360, 430, 560, 650, 620, 580,
]),
base_load_w: IntSeries::new("base", "W", 60, [
160, 150, 145, 140, 150, 180, 240, 420, 600, 550, 380, 240,
]),
grid_limit_w: IntSeries::new("grid", "W", 60, Array::make(12, 2600)),
tasks: [
LoadTask::shiftable("washer", "Compact washer", 700, 2, 0, 12, 8),
LoadTask::shiftable(
"rice-cooker",
"Rice cooker",
650,
1,
4,
10,
8,
priority=High,
),
LoadTask::optional("vacuum", "Robot vacuum", 120, 2, 2, 10, 6),
],
weights: ObjectiveWeights::for_policy(LowestCost),
random_seed: 17U,
}
}
///|
/// Household storm scenario with a six-hour evening blackout.
pub fn storm_outage_example() -> PlanningInput {
let base = home_day_example()
{
..base,
title: "Storm outage resilience",
solar_w: base.solar_w.scale_permille(600),
outages: [
OutageEvent::blackout(
"storm-evening",
17,
23,
description="forecast distribution-grid outage",
),
],
weights: ObjectiveWeights::for_policy(HighestResilience),
random_seed: 20260809U,
}
}
///|
/// All built-in examples with stable identifiers.
pub fn built_in_examples() -> Array[(String, PlanningInput)] {
[
("home", home_day_example()),
("compact", compact_apartment_example()),
("outage", storm_outage_example()),
]
}
///|
pub fn example_by_id(id : String) -> PlanningInput? {
for item in built_in_examples() {
if item.0 == id {
return Some(item.1)
}
}
None
}