Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework math handling #639

Merged
merged 25 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a47e1df
isolate _def_path, scenario overrides, and math object (not yet integ…
irm-codebase Jul 15, 2024
d2d70ce
Extract math documentation from model file
irm-codebase Jul 16, 2024
3f44bbf
add model math tests
irm-codebase Jul 16, 2024
28f8ebb
Improve model math tests, remove duplicates from test_core_model
irm-codebase Jul 17, 2024
2872060
extended validation function, added logging tests
irm-codebase Jul 17, 2024
31de31b
Add dict method, remove underscores in attributes
irm-codebase Jul 18, 2024
a6e8c7c
code now uses new math object (tests exected to fail)
irm-codebase Jul 18, 2024
fda1ea0
all tests passing
irm-codebase Jul 18, 2024
480bcc1
fix docs creation, add changelog
irm-codebase Jul 18, 2024
6e3e334
removed _model_def_dict
irm-codebase Jul 19, 2024
8da8dac
Trigger CI (and minor logging string fix)
brynpickering Jul 19, 2024
6730409
PR: now CalliopeMath, better backend init, small fixes
irm-codebase Jul 19, 2024
09b09f7
PR: comment improvements
irm-codebase Jul 19, 2024
1530b8f
update changelog
irm-codebase Jul 19, 2024
41b7fe5
Merge branch 'rework-def-dict' into rework-math
irm-codebase Jul 19, 2024
434236d
Merge branch 'main' into rework-math
brynpickering Jul 22, 2024
06efe33
Post-merge fixes
brynpickering Jul 22, 2024
a2ee8f0
Move math to `build` step; fix clustering issues
brynpickering Jul 29, 2024
3284a55
PR improvements: math components in CalliopeMath, small fixes (#665)
irm-codebase Aug 19, 2024
a615252
Merge branch 'main' into rework-math
brynpickering Sep 26, 2024
d024911
Merge remote-tracking branch 'origin/main' into rework-math
irm-codebase Sep 30, 2024
ccf0950
`pd.notnull` -> `pd.notna`
brynpickering Sep 30, 2024
ab757e5
Merge branch 'main' into rework-math
brynpickering Sep 30, 2024
bff333e
Run pre-commit on _all_ files
brynpickering Sep 30, 2024
d923a4a
Merge branch 'main' into rework-math
brynpickering Sep 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

### User-facing changes

|new| Math has been removed from `model.math`, and can now be accessed via `model.math.data` (#639).

|new| (non-NaN) Default values and data types for parameters appear in math documentation (if they appear in the model definition schema) (#677).

|changed| `data_sources` -> `data_tables` and `data_sources.source` -> `data_tables.data`.
Expand Down Expand Up @@ -63,6 +65,12 @@ Parameter titles from the model definition schema will also propagate to the mod

### Internal changes

|changed| `model._model_def_dict` has been removed.

|new| `CalliopeMath` is a new helper class to handle math additions, including separate methods for pre-defined math, user-defined math and validation checks.

|changed| `MathDocumentation` has been extracted from `Model`/`LatexBackend`, and now is a postprocessing module which can take models as input.

|new| `gurobipy` is a development dependency that will be added as an optional dependency to the conda-forge calliope feedstock recipe.

|changed| Added any new math dicts defined with `calliope.Model.backend.add_[...](...)` to the backend math dict registry stored in `calliope.Model.backend.inputs.attrs["math"]`.
Expand Down
25 changes: 1 addition & 24 deletions docs/examples/calliope_model_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,33 +36,10 @@
# Get information on the model
print(m.info())

# %% [markdown]
# ## Model definition dictionary
#
# `m._model_def_dict` is a python dictionary that holds all the data from the model definition YAML files, restructured into one dictionary.
#
# The underscore before the method indicates that it defaults to being hidden (i.e. you wouldn't see it by trying a tab auto-complete and it isn't documented)

# %%
m._model_def_dict.keys()

# %% [markdown]
# `techs` hold only the information about a technology that is specific to that node

# %%
m._model_def_dict["techs"]["pv"]

# %% [markdown]
# `nodes` hold only the information about a technology that is specific to that node

# %%
m._model_def_dict["nodes"]["X2"]["techs"]["pv"]

# %% [markdown]
# ## Model data
#
# `m._model_data` is an xarray Dataset.
# Like `_model_def_dict` it is a hidden prperty of the Model as you are expected to access the data via the public property `inputs`
# `m._model_data` is an xarray Dataset, a hidden property of the Model as you are expected to access the data via the public property `inputs`

# %%
m.inputs
Expand Down
162 changes: 47 additions & 115 deletions docs/examples/piecewise_constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,20 @@
#

# %%
new_params = {
"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",
},
}
}
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)
m = calliope.examples.national_scale(override_dict=new_params)
new_params_as_dict = calliope.AttrDict.from_yaml_string(new_params)
m = calliope.examples.national_scale(override_dict=new_params_as_dict)

# %%
m.inputs.capacity_steps
Expand All @@ -95,55 +93,48 @@
# ## Creating our piecewise constraint
#
# We create the piecewise constraint by linking decision variables to the piecewise curve we have created.
# In this example, we require a new decision variable for investment costs that can take on the value defined by the curve at a given value of `flow_cap`.
# In this example, we need:
# 1. a new decision variable for investment costs that can take on the value defined by the curve at a given value of `flow_cap`;
# 1. to link that decision variable to our total cost calculation; and
# 1. to define the piecewise constraint.

# %%
m.math["variables"]["piecewise_cost_investment"] = {
"description": "Investment cost that increases monotonically",
"foreach": ["nodes", "techs", "carriers", "costs"],
"where": "[csp] in techs",
"bounds": {"min": 0, "max": np.inf},
"default": 0,
}

# %% [markdown]
# We also need to link that decision variable to our total cost calculation.

# %%
# Before
m.math["global_expressions"]["cost_investment_flow_cap"]["equations"]

# %%
# Updated - we split the equation into two expressions.
m.math["global_expressions"]["cost_investment_flow_cap"]["equations"] = [
{"expression": "$cost_sum * flow_cap", "where": "NOT [csp] in techs"},
{"expression": "piecewise_cost_investment", "where": "[csp] in techs"},
]

# %% [markdown]
# We then need to define the piecewise constraint:

# %%
m.math["piecewise_constraints"]["csp_piecewise_costs"] = {
"description": "Set investment costs values along a piecewise curve using special ordered sets of type 2 (SOS2).",
"foreach": ["nodes", "techs", "carriers", "costs"],
"where": "piecewise_cost_investment",
"x_expression": "flow_cap",
"x_values": "capacity_steps",
"y_expression": "piecewise_cost_investment",
"y_values": "cost_steps",
}

# %% [markdown]
# Then we can build our optimisation problem:
new_math = """
variables:
piecewise_cost_investment:
description: "Investment cost that increases monotonically"
foreach: ["nodes", "techs", "carriers", "costs"]
where: "[csp] in techs"
bounds:
min: 0
max: .inf
default: 0
global_expressions:
cost_investment_flow_cap:
equations:
- expression: "$cost_sum * flow_cap"
where: "NOT [csp] in techs"
- expression: "piecewise_cost_investment"
where: "[csp] in techs"
piecewise_constraints:
csp_piecewise_costs:
description: "Set investment costs values along a piecewise curve using special ordered sets of type 2 (SOS2)."
foreach: ["nodes", "techs", "carriers", "costs"]
where: "piecewise_cost_investment"
x_expression: "flow_cap"
x_values: "capacity_steps"
y_expression: "piecewise_cost_investment"
y_values: "cost_steps"
"""

# %% [markdown]
# # Building and checking the optimisation problem
#
# With our piecewise constraint defined, we can build our optimisation problem
# With our piecewise constraint defined, we can build our optimisation problem and inject this new math.

# %%
m.build()
new_math_as_dict = calliope.AttrDict.from_yaml_string(new_math)
m.build(add_math_dict=new_math_as_dict)

# %% [markdown]
# And we can see that our piecewise constraint exists in the built optimisation problem "backend"
Expand Down Expand Up @@ -190,65 +181,6 @@
)
fig.show()

# %% [markdown]
# ## YAML model definition
# We have updated the model parameters and math interactively in Python in this tutorial, the definition in YAML would look like:

# %% [markdown]
# ### Math
#
# Saved as e.g., `csp_piecewise_math.yaml`.
#
# ```yaml
# variables:
# piecewise_cost_investment:
# description: Investment cost that increases monotonically
# foreach: [nodes, techs, carriers, costs]
# where: "[csp] in techs"
# bounds:
# min: 0
# max: .inf
# default: 0
#
# piecewise_constraints:
# csp_piecewise_costs:
# description: >
# Set investment costs values along a piecewise curve using special ordered sets of type 2 (SOS2).
# foreach: [nodes, techs, carriers, costs]
# where: "[csp] in techs"
# x_expression: flow_cap
# x_values: capacity_steps
# y_expression: piecewise_cost_investment
# y_values: cost_steps
#
# global_expressions:
# cost_investment_flow_cap.equations:
# - expression: "$cost_sum * flow_cap"
# where: "NOT [csp] in techs"
# - expression: "piecewise_cost_investment"
# where: "[csp] in techs"
# ```

# %% [markdown]
# ### Scenario definition
#
# Loaded into the national-scale example model with: `calliope.examples.national_scale(scenario="piecewise_csp_cost")`
#
# ```yaml
# overrides:
# piecewise_csp_cost:
# config.init.add_math: [csp_piecewise_math.yaml]
# parameters:
# capacity_steps:
# data: [0, 2500, 5000, 7500, 10000]
# index: [0, 1, 2, 3, 4]
# dims: "breakpoints"
# cost_steps:
# data: [0, 3.75e6, 6e6, 7.5e6, 8e6]
# index: [0, 1, 2, 3, 4]
# dims: "breakpoints"
# ```

# %% [markdown]
# ## Troubleshooting
#
Expand Down
3 changes: 2 additions & 1 deletion docs/hooks/dummy_model/model.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ overrides:
storage_inter_cluster:
config.init:
name: inter-cluster storage
add_math: ["storage_inter_cluster"]
time_cluster: cluster_days.csv
config.build:
add_math: ["storage_inter_cluster"]

config.init.name: base

Expand Down
Loading