Skip to content

Commit

Permalink
Add title to math var and expr components (#603)
Browse files Browse the repository at this point in the history
  • Loading branch information
brynpickering authored Jun 30, 2024
1 parent 389653c commit 3a63bc1
Show file tree
Hide file tree
Showing 13 changed files with 123 additions and 62 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

### User-facing changes

|new| Decision variables and global expressions can have a `title` defined, which will be available in the model results as attributes of those components and can be used for e.g. visualisation (#582).
Parameter titles from the model definition schema will also propagate to the model inputs.

|fixed| Backend parameter updates propagate correctly through global expressions in the order those expressions were defined (#616).

|fixed| If setting `model.backend.verbose_strings()`, rebuilt model components from making backend parameter updates will automatically have verbose strings (#623).
Expand Down
6 changes: 3 additions & 3 deletions docs/user_defined_math/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ variables:
```

1. It needs a unique name (`storage_cap` in the example above).
1. Ideally, it has a long-form `description` and a `unit` added.
1. Ideally, it has a long-form name (`title`), a `description` and a `unit` added.
These are not required, but are useful metadata for later reference.
1. It can have a top-level `foreach` list and `where` string.
Without a `foreach`, it becomes an un-indexed variable.
Expand Down Expand Up @@ -54,7 +54,7 @@ global_expressions:
Global expressions are by no means necessary to include, but can make more complex linear expressions easier to keep track of and can reduce post-processing requirements.

1. It needs a unique name (`cost` in the above example).
1. Ideally, it has a long-form `description` and a `unit` added.
1. Ideally, it has a long-form name (`title`), a `description` and a `unit` added.
These are not required, but are useful metadata for later reference.
1. It can have a top-level `foreach` list and `where` string.
Without a `foreach`, it becomes an un-indexed expression.
Expand All @@ -78,7 +78,7 @@ constraints:
```

1. It needs a unique name (`set_storage_initial` in the above example).
1. Ideally, it has a long-form `description` and a `unit` added.
1. Ideally, it has a long-form `description` added.
These are not required, but are useful metadata for later reference.
1. It can have a top-level `foreach` list and `where` string.
Without a `foreach`, it becomes an un-indexed constraint.
Expand Down
3 changes: 2 additions & 1 deletion src/calliope/backend/backend_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ class BackendModelGenerator(ABC):
"""Helper class for backends."""

_VALID_COMPONENTS: tuple[_COMPONENTS_T, ...] = typing.get_args(_COMPONENTS_T)
_COMPONENT_ATTR_METADATA = ["description", "unit", "default"]
_COMPONENT_ATTR_METADATA = ["description", "unit", "default", "title"]

_PARAM_TITLES = extract_from_schema(MODEL_SCHEMA, "title")
_PARAM_DESCRIPTIONS = extract_from_schema(MODEL_SCHEMA, "description")
_PARAM_UNITS = extract_from_schema(MODEL_SCHEMA, "x-unit")

Expand Down
1 change: 1 addition & 0 deletions src/calliope/backend/latex_backend_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ def add_parameter( # noqa: D102, override
use_inf_as_na: bool = False,
) -> None:
attrs = {
"title": self._PARAM_TITLES.get(parameter_name, None),
"description": self._PARAM_DESCRIPTIONS.get(parameter_name, None),
"unit": self._PARAM_UNITS.get(parameter_name, None),
}
Expand Down
2 changes: 2 additions & 0 deletions src/calliope/backend/parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class UnparsedConstraintDict(TypedDict):
class UnparsedExpressionDict(UnparsedConstraintDict):
"""Unparsed expression checker class."""

title: NotRequired[str]
unit: NotRequired[str]


Expand All @@ -69,6 +70,7 @@ class UnparsedVariableBoundDict(TypedDict):
class UnparsedVariableDict(TypedDict):
"""Unparsed variable checker class."""

title: NotRequired[str]
description: NotRequired[str]
unit: NotRequired[str]
foreach: list[str]
Expand Down
1 change: 1 addition & 0 deletions src/calliope/backend/pyomo_backend_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def add_parameter( # noqa: D102, override

parameter_da.attrs["original_dtype"] = parameter_values.dtype
attrs = {
"title": self._PARAM_TITLES.get(parameter_name, None),
"description": self._PARAM_DESCRIPTIONS.get(parameter_name, None),
"unit": self._PARAM_UNITS.get(parameter_name, None),
"default": default,
Expand Down
5 changes: 5 additions & 0 deletions src/calliope/config/math_schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ properties:
additionalProperties: false
required: ["equations"]
properties:
title: &title
type: string
description: The component long name, for use in visualisation.
description: &description
type: string
description: A verbose description of the component.
Expand Down Expand Up @@ -96,6 +99,7 @@ properties:
additionalProperties: false
required: ["equations"]
properties:
title: *title
description: *description
active: *active
unit: &unit
Expand All @@ -122,6 +126,7 @@ properties:
description: A named variable.
additionalProperties: false
properties:
title: *title
description: *description
active: *active
unit: *unit
Expand Down
2 changes: 1 addition & 1 deletion src/calliope/config/model_def_schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ properties:
type: ["null", string]
x-resample_method: first
x-type: str
title: Technology longname.
title: Technology long-name.
description: Long name of technology, which can be used in post-processing (e.g., plotting).
default: .nan

Expand Down
26 changes: 26 additions & 0 deletions src/calliope/math/base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ constraints:

variables:
flow_cap:
title: Technology flow (a.k.a. nominal) capacity
description: >-
A technology's flow capacity, also known as its nominal or nameplate capacity.
default: 0
Expand All @@ -499,6 +500,7 @@ variables:
max: flow_cap_max

link_flow_cap:
title: Link flow capacity
description: >-
A transmission technology's flow capacity, also known as its nominal or nameplate capacity.
default: 0
Expand All @@ -510,6 +512,7 @@ variables:
max: .inf

flow_out:
title: Carrier outflow
description: >-
The outflow of a technology per timestep,
also known as the flow discharged (from `storage` technologies)
Expand All @@ -523,6 +526,7 @@ variables:
max: .inf

flow_in:
title: Carrier inflow
description: >-
The inflow to a technology per timestep,
also known as the flow consumed (by `storage` technologies)
Expand All @@ -536,6 +540,7 @@ variables:
max: .inf

flow_export:
title: Carrier export
description: >-
The flow of a carrier exported outside the system boundaries by a technology per timestep.
default: 0
Expand All @@ -547,6 +552,7 @@ variables:
max: .inf

area_use:
title: Area utilisation
description: >-
The area in space utilised directly (e.g., solar PV panels)
or indirectly (e.g., biofuel crops) by a technology.
Expand All @@ -559,6 +565,7 @@ variables:
max: area_use_max

source_use:
title: Source flow use
description: >-
The carrier flow consumed from outside the system boundaries by a `supply` technology.
default: 0
Expand All @@ -570,6 +577,7 @@ variables:
max: .inf

source_cap:
title: Source flow capacity
description: >-
The upper limit on a flow that can be consumed from outside the system boundaries
by a `supply` technology in each timestep.
Expand All @@ -583,6 +591,7 @@ variables:

# --8<-- [start:variable]
storage_cap:
title: Stored carrier capacity
description: >-
The upper limit on a carrier that can
be stored by a technology in any timestep.
Expand All @@ -598,6 +607,7 @@ variables:
# --8<-- [end:variable]

storage:
title: Stored carrier
description: >-
The carrier stored by a `storage` technology in each timestep.
default: 0
Expand All @@ -609,6 +619,7 @@ variables:
max: .inf

purchased_units:
title: Number of purchased units
description: |
Integer number of a technology that has been purchased,
for any technology set to require integer capacity purchasing.
Expand All @@ -633,6 +644,7 @@ variables:
max: purchased_units_max

operating_units:
title: Number of operating units
description: >-
Integer number of a technology that is operating in each timestep,
for any technology set to require integer capacity purchasing.
Expand All @@ -646,6 +658,7 @@ variables:
max: .inf

available_flow_cap:
title: Available carrier flow capacity
description: >-
Flow capacity that will be set to zero if the technology is not operating in a given
timestep and will be set to the value of the decision variable `flow_cap` otherwise.
Expand All @@ -658,6 +671,7 @@ variables:
max: .inf

async_flow_switch:
title: Asynchronous carrier flow switch
description: >-
Binary switch to force asynchronous outflow/consumption of technologies with
both `flow_in` and `flow_out` defined.
Expand All @@ -673,6 +687,7 @@ variables:
max: 1

unmet_demand:
title: Unmet demand (load shedding)
description: >-
Virtual source of carrier flow to ensure model feasibility.
This should only be considered a debugging rather than a modelling tool as it may
Expand All @@ -688,6 +703,7 @@ variables:
max: .inf

unused_supply:
title: Unused supply (curtailment)
description: >-
Virtual sink of carrier flow to ensure model feasibility.
This should only be considered a debugging rather than a modelling tool as it may
Expand Down Expand Up @@ -743,6 +759,7 @@ objectives:

global_expressions:
flow_out_inc_eff:
title: Carrier outflow including losses
description: >-
Outflows after taking efficiency losses into account.
default: 0
Expand All @@ -759,6 +776,7 @@ global_expressions:
expression: flow_out / (flow_out_eff * flow_out_parasitic_eff)

flow_in_inc_eff:
title: Carrier inflow including losses
description: >-
Inflows after taking efficiency losses into account.
default: 0
Expand All @@ -771,6 +789,7 @@ global_expressions:
expression: flow_in * flow_in_eff

cost_var:
title: Variable operating costs
description: >-
The operating costs per timestep of a technology.
default: 0
Expand All @@ -794,6 +813,7 @@ global_expressions:
- expression: sum(cost_flow_out * flow_out, over=carriers)

cost_investment_flow_cap:
title: Flow capacity investment costs
description: >-
The investment costs associated with the nominal/rated capacity of a technology.
default: 0
Expand All @@ -809,6 +829,7 @@ global_expressions:
expression: cost_flow_cap

cost_investment_storage_cap:
title: Storage capacity investment costs
description: >-
The investment costs associated with the storage capacity of a technology.
default: 0
Expand All @@ -818,6 +839,7 @@ global_expressions:
- expression: cost_storage_cap * storage_cap

cost_investment_source_cap:
title: Source flow capacity investment costs
description: >-
The investment costs associated with the source consumption capacity of a technology.
default: 0
Expand All @@ -827,6 +849,7 @@ global_expressions:
- expression: cost_source_cap * source_cap

cost_investment_area_use:
title: Area utilisation investment costs
description: >-
The investment costs associated with the area used by a technology.
default: 0
Expand All @@ -836,6 +859,7 @@ global_expressions:
- expression: cost_area_use * area_use

cost_investment_purchase:
title: Binary purchase investment costs
description: >-
The investment costs associated with the binary purchase of a technology.
default: 0
Expand All @@ -848,6 +872,7 @@ global_expressions:
expression: cost_purchase * purchased_units

cost_investment:
title: Total investment costs
description: >-
The installation costs of a technology, including annualised investment costs and annual maintenance costs.
default: 0
Expand Down Expand Up @@ -883,6 +908,7 @@ global_expressions:
# --8<-- [start:expression]
cost:
title: Total costs
description: >-
The total annualised costs of a technology,
including installation and operation costs.
Expand Down
4 changes: 4 additions & 0 deletions src/calliope/math/storage_inter_cluster.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ constraints:

variables:
storage:
title: Virtual stored carrier
description: >-
The virtual carrier stored by a `supply_plus` or `storage` technology in each timestep of a clustered day.
Stored carrier can be negative so long as it does not go below the carrier stored in `storage_inter_cluster`.
Expand All @@ -107,6 +108,7 @@ variables:
min: -.inf

storage_inter_cluster:
title: Virtual inter-cluster stored carrier
description: >-
The virtual carrier stored by a `supply_plus` or `storage` technology between days of the entire timeseries.
Only together with `storage` does this variable's values gain physical significance.
Expand All @@ -117,6 +119,7 @@ variables:
max: .inf

storage_intra_cluster_max:
title: Virtual maximum intra-cluster stored carrier
description: >-
Virtual variable to limit the maximum value of `storage` in a given representative day.
unit: energy
Expand All @@ -127,6 +130,7 @@ variables:
max: .inf

storage_intra_cluster_min:
title: Virtual minimum intra-cluster stored carrier
description: >-
Virtual variable to limit the minimum value of `storage` in a given representative day.
unit: energy
Expand Down
6 changes: 6 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
ALL_DIMS = {"nodes", "techs", "carriers", "costs", "timesteps"}


@pytest.fixture(scope="session")
def dummy_int() -> int:
"""Dummy integer value that will never be confused by a model value/default."""
return 0xDEADBEEF


@pytest.fixture(
scope="session",
params=set(
Expand Down
Loading

0 comments on commit 3a63bc1

Please sign in to comment.