Defining piecewise linear constraints¶
In this tutorial, we use the national scale example model to implement a piecewise linear constraint. This constraint will represent a non-linear relationship between capacity and cost per unit capacity of Concentrating Solar Power (CSP).
In [1]:
Copied!
import numpy as np
import plotly.express as px
import calliope
calliope.set_log_verbosity("INFO", include_solver_output=False)
import numpy as np
import plotly.express as px
import calliope
calliope.set_log_verbosity("INFO", include_solver_output=False)
Model setup¶
Defining our piecewise curve¶
In the base national scale model, the CSP has a maximum rated capacity of 10,000 kW and a cost to invest in that capacity of 1000 USD / kW.
In our updated model, the cost to invest in capacity will vary from 5000 USD / kW to 500 USD / kW as the CSP capacity increases:
In [2]:
Copied!
capacity_steps = [0, 2500, 5000, 7500, 10000]
cost_steps = [0, 3.75e6, 6e6, 7.5e6, 8e6]
cost_per_cap = np.nan_to_num(np.divide(cost_steps, capacity_steps)).astype(int)
fig = px.line(
x=capacity_steps,
y=cost_steps,
labels={"x": "Capacity (kW)", "y": "Investment cost (USD)"},
markers="o",
range_y=[0, 10e6],
text=[f"{i} USD/kW" for i in cost_per_cap],
)
fig.update_traces(textposition="top center")
fig.show()
capacity_steps = [0, 2500, 5000, 7500, 10000]
cost_steps = [0, 3.75e6, 6e6, 7.5e6, 8e6]
cost_per_cap = np.nan_to_num(np.divide(cost_steps, capacity_steps)).astype(int)
fig = px.line(
x=capacity_steps,
y=cost_steps,
labels={"x": "Capacity (kW)", "y": "Investment cost (USD)"},
markers="o",
range_y=[0, 10e6],
text=[f"{i} USD/kW" for i in cost_per_cap],
)
fig.update_traces(textposition="top center")
fig.show()
[2024-12-16 11:51:26] WARNING /tmp/ipykernel_2989/173276940.py:4: RuntimeWarning: invalid value encountered in divide cost_per_cap = np.nan_to_num(np.divide(cost_steps, capacity_steps)).astype(int)
We can then provide this data when we load our model:
Note
We must index our piecewise data over "breakpoints".
In [3]:
Copied!
new_params = f"""
parameters:
capacity_steps:
data: {capacity_steps}
index: [0, 1, 2, 3, 4]
dims: "breakpoints"
cost_steps:
data: {cost_steps}
index: [0, 1, 2, 3, 4]
dims: "breakpoints"
"""
print(new_params)
new_params_as_dict = calliope.AttrDict.from_yaml_string(new_params)
m = calliope.examples.national_scale(override_dict=new_params_as_dict)
new_params = f"""
parameters:
capacity_steps:
data: {capacity_steps}
index: [0, 1, 2, 3, 4]
dims: "breakpoints"
cost_steps:
data: {cost_steps}
index: [0, 1, 2, 3, 4]
dims: "breakpoints"
"""
print(new_params)
new_params_as_dict = calliope.AttrDict.from_yaml_string(new_params)
m = calliope.examples.national_scale(override_dict=new_params_as_dict)
parameters: capacity_steps: data: [0, 2500, 5000, 7500, 10000] index: [0, 1, 2, 3, 4] dims: "breakpoints" cost_steps: data: [0, 3750000.0, 6000000.0, 7500000.0, 8000000.0] index: [0, 1, 2, 3, 4] dims: "breakpoints" [2024-12-16 11:51:26] INFO Model: initialising
[2024-12-16 11:51:26] INFO Model: preprocessing stage 1 (model_run)
[2024-12-16 11:51:29] INFO Model: preprocessing stage 2 (model_data)
[2024-12-16 11:51:29] INFO Model: preprocessing complete
In [4]:
Copied!
m.inputs.capacity_steps
m.inputs.capacity_steps
Out[4]:
<xarray.DataArray 'capacity_steps' (breakpoints: 5)> Size: 40B array([ 0., 2500., 5000., 7500., 10000.]) Coordinates: * breakpoints (breakpoints) int64 40B 0 1 2 3 4 Attributes: is_result: False
In [5]:
Copied!
m.inputs.cost_steps
m.inputs.cost_steps
Out[5]: