Construction Start Opt-In
This example shows that construction-start behavior is component-local and opt-in. The model contains two independent generators in the same Path:
delayed_genincludesVariableConstructionStart("output", energy, 2).ordinary_genhas deployment behavior but no construction-start behavior.
Both generators are optimized together. Only delayed_gen reports construction starts before its deployment years.
Model
using EnergyPathway
using HiGHS
using JuMP: objective_value, set_silent
function add_supply_case!(snap, cname, demand; construction_duration=nothing)
carrier = EnergyCarrier("$(cname)_power", sim(snap))
grid = Node("$(cname)_grid", carrier)
load = Component("$(cname)_load", Demand(carrier, demand))
connect!(snap, load, grid)
behaviors = Any[
VariableCapacity("output", energy),
VariableDeployment("output", energy),
]
if !isnothing(construction_duration)
push!(behaviors, VariableConstructionStart("output", energy, construction_duration))
end
append!(
behaviors,
[
VariableRetirement("output", energy),
Lifetime(30),
SingleCost(:capex, :deployment, "output", energy, 1.0, nothing),
],
)
generator = Component(cname, DispatchableSource(carrier), behaviors)
connect!(snap, generator, grid)
return snap
end
function build_pathway()
opt = PathOpt(; mesh=TimeMesh(fill(1 // 1, 2)), endyear=2030)
path = Path(HiGHS.Optimizer, opt)
set_silent(model(path))
for (year, delayed_demand, ordinary_demand) in [
(2020, 4.0, 6.0),
(2030, 7.0, 9.0),
]
snap = addsnapshot!(path, year)
add_supply_case!(snap, "delayed_gen", delayed_demand; construction_duration=2)
add_supply_case!(snap, "ordinary_gen", ordinary_demand)
end
optimize!(path, cost(path))
return extract(path), objective_value(model(path))
end
display_value(x) = round(x; digits=6)
function print_summary(path, objective)
println("Objective: ", display_value(objective))
println("First model year: ", firstyear(path))
println()
println("Component with VariableConstructionStart:")
println(" delayed_gen capacity in 2020: ", display_value(capacity(path, "delayed_gen", 2020)))
println(" delayed_gen capacity in 2030: ", display_value(capacity(path, "delayed_gen", 2030)))
println(" delayed_gen deployment in 2030: ", display_value(deployment(path, "delayed_gen", 2030)))
println(" delayed_gen construction start in 2018: ", display_value(constructionstart(path, "delayed_gen", 2018)))
println(" delayed_gen construction start in 2028: ", display_value(constructionstart(path, "delayed_gen", 2028)))
println()
println("Component without construction-start behavior:")
println(" ordinary_gen capacity in 2020: ", display_value(capacity(path, "ordinary_gen", 2020)))
println(" ordinary_gen capacity in 2030: ", display_value(capacity(path, "ordinary_gen", 2030)))
println(" ordinary_gen deployment in 2030: ", display_value(deployment(path, "ordinary_gen", 2030)))
println(" ordinary_gen construction start in 2018: ", display_value(constructionstart(path, "ordinary_gen", 2018)))
println(" ordinary_gen construction start in 2028: ", display_value(constructionstart(path, "ordinary_gen", 2028)))
end
result, objective = build_pathway()
print_summary(result, objective)Objective: 13.68348
First model year: 2018
Component with VariableConstructionStart:
delayed_gen capacity in 2020: 4.0
delayed_gen capacity in 2030: 7.0
delayed_gen deployment in 2030: 3.0
delayed_gen construction start in 2018: 4.0
delayed_gen construction start in 2028: 3.0
Component without construction-start behavior:
ordinary_gen capacity in 2020: 6.0
ordinary_gen capacity in 2030: 9.0
ordinary_gen deployment in 2030: 3.0
ordinary_gen construction start in 2018: 0.0
ordinary_gen construction start in 2028: 0.0firstyear(result) is 2018 because the construction-start behavior on delayed_gen creates a start event two years before the first 2020 deployment. The ordinary generator is unchanged: it deploys capacity in snapshot years, and constructionstart(result, "ordinary_gen", year) returns zero.