Skip to content

Commit 485b3d5

Browse files
Add config obj replace schemas (#717)
* Added schemas for data tables, parameters, techs, nodes and model definition. * changed to / from to link_to / link_from --------- Co-authored-by: Bryn Pickering <17178478+brynpickering@users.noreply.github.com>
1 parent ed45bf7 commit 485b3d5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+9916
-9519
lines changed

CHANGELOG.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ This change has occurred to avoid confusion between data "sources" and model ene
3535

3636
### Internal changes
3737

38-
|changed| Model configuration now uses `pydantic`.
38+
|changed| updated transmission technologies to/from -> link_to/link_from to avoid conflicts with protected `python` terminology.
39+
40+
|changed| Model configuration, data tables, techs/nodes data, math and general model definition now uses `pydantic`.
3941

4042
|changed| Model definition reading is now defined in a single place (preprocess/model_definition.py).
4143

docs/advanced/constraints.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,14 @@ To force unidirectionality for a given technology along a given link, you have t
133133
```yaml
134134
techs:
135135
region1_to_region2:
136-
from: region1
137-
to: region2
136+
link_from: region1
137+
link_to: region2
138138
base_tech: transmission
139139
one_way: true
140140
```
141141

142142
This will only allow transmission from `region1` to `region2`.
143-
To swap the direction, `to` and `from` must be swapped.
143+
To swap the direction, `link_to` and `link_from` must be swapped.
144144

145145
## Per-distance transmission constraints
146146

docs/creating/nodes.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ In the above example, `node_flow_out_max` at `region1` could be used to create [
3636
## Understanding node-level parameters
3737

3838
`techs` is the only required parameter in a node.
39-
This can be an empty dictionary (`techs: {}`), which you may use if your node is just a junction for transmission technologies (which you [**do not define in the `techs` of a node**](techs.md#transmission-technologies) - rather, you define them as separate technologies that connect `from` one node `to` another node).
39+
This can be an empty dictionary (`techs: {}`), which you may use if your node is just a junction for transmission technologies (which you [**do not define in the `techs` of a node**](techs.md#transmission-technologies) - rather, you define them as separate technologies that connect a `link_from` node with a `link_to` node).
4040

4141
!!! info "See also"
4242

docs/creating/techs.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,14 @@ Instead, you associate transmission technologies with nodes in `techs`:
8585
```yaml
8686
techs:
8787
ac_transmission:
88-
from: region1 # (1)!
89-
to: region2
88+
link_from: region1 # (1)!
89+
link_to: region2
9090
flow_cap_max: 100
9191
...
9292
```
9393

94-
1. The region you specify in `from` or `to` is interchangeable unless you set the parameter `one_way: true`.
95-
In that case, flow along the transmission line is only allowed from the `from` region to the `to` region.
94+
1. The region you specify in `link_from` or `link_to` is interchangeable unless you set the parameter `one_way: true`.
95+
In that case, flow along the transmission line is only allowed from the `link_from` node to the `link_to` node.
9696

9797
## Understanding tech-level parameters
9898

@@ -103,7 +103,7 @@ There are _required_ parameters according to the technology `base_tech`:
103103
* `supply`: `base_tech` and `carrier_out`.
104104
* `demand`: `base_tech` and `carrier_in`.
105105
* `storage`: `base_tech` and `carrier_out` and `carrier_in`.
106-
* `transmission`: `base_tech` and `carrier_out`, `carrier_in`, `to`, and `from`.
106+
* `transmission`: `base_tech` and `carrier_out`, `carrier_in`, `link_to`, and `link_from`.
107107
* `conversion`: `base_tech` and `carrier_out` and `carrier_in`.
108108

109109
For `storage` and `transmission`, it may seem like unnecessary repetition to define both `carrier_out` and `carrier_in` as they are likely the same value.

docs/examples/loading_tabular_data.py

+10-10
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@
7878
# base_tech: transmission
7979
# carrier_in: electricity
8080
# carrier_out: electricity
81-
# from: A
82-
# to: B
81+
# link_from: A
82+
# link_to: B
8383
# flow_cap_max: 8
8484
#
8585
# nodes:
@@ -136,8 +136,8 @@
136136
base_tech: transmission
137137
carrier_in: electricity
138138
carrier_out: electricity
139-
from: A
140-
to: B
139+
link_from: A
140+
link_to: B
141141
flow_cap_max: 8
142142
143143
nodes:
@@ -215,8 +215,8 @@
215215
"demand_tech": {"base_tech": "demand"},
216216
"transmission_tech": {
217217
"base_tech": "transmission",
218-
"from": "A",
219-
"to": "B",
218+
"link_from": "A",
219+
"link_to": "B",
220220
"flow_cap_max": 8,
221221
},
222222
}
@@ -483,8 +483,8 @@
483483
# base_tech: transmission
484484
# carrier_in: electricity
485485
# carrier_out: electricity
486-
# from: A
487-
# to: B
486+
# link_from: A
487+
# link_to: B
488488
# flow_cap_max: 8
489489
#
490490
# nodes:
@@ -532,8 +532,8 @@
532532
# base_tech: transmission
533533
# carrier_in: electricity
534534
# carrier_out: electricity
535-
# from: A
536-
# to: B
535+
# link_from: A
536+
# link_to: B
537537
# flow_cap_max: 8
538538
#
539539
# nodes:

docs/hooks/dummy_model/model.yaml

+5-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ nodes:
1414

1515
techs:
1616
tech_transmission:
17-
from: A
18-
to: B
17+
base_tech: transmission
18+
carrier_in: foo
19+
carrier_out: foo
20+
link_from: A
21+
link_to: B
1922

2023
data_tables:
2124
techs:

docs/hooks/dummy_model/techs.csv

+3-3
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ transmission_tech,lifetime,25
5454
transmission_tech,one_way,1.0
5555
transmission_tech,flow_cap_max,1.0
5656
transmission_tech,flow_cap_min,1.0
57-
transmission_tech,from,A
58-
transmission_tech,to,B
57+
transmission_tech,link_from,A
58+
transmission_tech,link_to,B
5959
demand_tech,sink_unit,per_cap
6060
conversion_tech,base_tech,conversion
6161
conversion_tech,cap_method,integer
@@ -64,4 +64,4 @@ supply_tech,cap_method,integer
6464
supply_tech,source_unit,per_area
6565
storage_tech,base_tech,storage
6666
transmission_tech,base_tech,transmission
67-
demand_tech,base_tech,demand
67+
demand_tech,base_tech,demand

docs/hooks/generate_readable_schema.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,17 @@
1414
import jsonschema2md
1515
from mkdocs.structure.files import File
1616

17-
from calliope import config
17+
from calliope.schemas import config_schema, data_table_schema, math_schema
1818
from calliope.util import schema
1919

2020
TEMPDIR = tempfile.TemporaryDirectory()
2121

22+
# FIXME: should only use pydantic models instead of YAML
2223
SCHEMAS = {
23-
"config_schema": config.CalliopeConfig().model_no_ref_schema(),
24+
"config_schema": config_schema.CalliopeConfig.model_no_ref_schema(),
2425
"model_schema": schema.MODEL_SCHEMA,
25-
"math_schema": schema.MATH_SCHEMA,
26-
"data_table_schema": schema.DATA_TABLE_SCHEMA,
26+
"math_schema": math_schema.CalliopeMathDef.model_no_ref_schema(),
27+
"data_table_schema": data_table_schema.CalliopeDataTable.model_no_ref_schema(),
2728
}
2829

2930

docs/installation.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Calliope is tested on Linux, macOS, and Windows.
77
Running Calliope requires four things:
88

99
1. The Python programming language, version {{ min_python_version }} to {{ max_python_version }}.
10-
2. A number of Python add-on modules including [Pyomo](https://www.pyomo.org/), [Pandas](https://pandas.pydata.org/) and [Xarray](https://xarray.dev/).
10+
2. A number of Python add-on modules including [Pyomo](https://www.pyomo.org/), [Pandas](https://pandas.pydata.org/) and [Xarray](https://docs.xarray.dev/).
1111
3. An optimisation solver: Calliope has been tested with CBC, GLPK, Gurobi, and CPLEX. Any other solver that is compatible with Pyomo should also work.
1212
4. The Calliope software itself.
1313

docs/migrating.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ We have changed the nesting structure for defining technology costs so they are
270270
### `links` → transmission links defined in `techs`
271271

272272
The top-level key `links` no longer exists.
273-
Instead, links are defined as separate transmission technologies in `techs`, including `to`/`from` keys:
273+
Instead, links are defined as separate transmission technologies in `techs`, including `link_to`/`link_from` keys:
274274

275275
=== "v0.6"
276276

@@ -298,13 +298,13 @@ Instead, links are defined as separate transmission technologies in `techs`, inc
298298
```yaml
299299
techs:
300300
x1_to_x2_ac_transmission:
301-
from: X1
302-
to: X2
301+
link_from: X1
302+
link_to: X2
303303
base_tech: transmission
304304
flow_cap_max: 10
305305
x1_to_x2_dc_transmission:
306-
from: X1
307-
to: X2
306+
link_from: X1
307+
link_to: X2
308308
base_tech: transmission
309309
flow_cap_max: 5
310310
```

docs/user_defined_math/components.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Without a `where` string, all valid members (according to the `definition_matrix
3636
If a value for a valid variable member is undefined in the referenced parameter, the decision variable will be unbounded for this member.
3737
1. It can be deactivated so that it does not appear in the built optimisation problem by setting `active: false`.
3838
1. It can take on a `default` value that will be used in math operations to avoid `NaN` values creeping in.
39-
The default value should be set such that it has no impact on the optimisation problem if it is included (most of the time, this means setting it to zero).
39+
The default value should be set such that it has no impact on the optimisation problem if it is included (most of the time, this means `NaN`).
4040

4141
## Global Expressions
4242

@@ -63,7 +63,7 @@ Without a `where` string, all valid members (according to the `definition_matrix
6363
The equation expressions do _not_ have comparison operators; those are reserved for [constraints](#constraints)
6464
1. It can be deactivated so that it does not appear in the built optimisation problem by setting `active: false`.
6565
1. It can take on a `default` value that will be used in math operations to avoid `NaN` values creeping in.
66-
The default value should be set such that it has no impact on the optimisation problem if it is included (most of the time, this means setting it to zero).
66+
The default value should be set such that it has no impact on the optimisation problem if it is included (most of the time, this means `NaN`).
6767

6868
## Constraints
6969

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ max-complexity = 10
5454

5555
# Ignore `E402` (import violations) and `F401` (unused imports) in all `__init__.py` files
5656
[tool.ruff.lint.per-file-ignores]
57-
"__init__.py" = ["E402", "F401"]
57+
"__init__.py" = ["E402", "F401", "D104"]
5858
"*.ipynb" = ["E402"]
5959
"tests/*" = ["D"]
6060
"docs/examples/*" = ["D"]

src/calliope/backend/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
from calliope.preprocess import CalliopeMath
1616

1717
if TYPE_CHECKING:
18-
from calliope import config
1918
from calliope.backend.backend_model import BackendModel
19+
from calliope.schemas import config_schema
2020

2121

2222
def get_model_backend(
23-
build_config: "config.Build", data: xr.Dataset, math: CalliopeMath
23+
build_config: "config_schema.Build", data: xr.Dataset, math: CalliopeMath
2424
) -> "BackendModel":
2525
"""Assign a backend using the given configuration.
2626

src/calliope/backend/backend_model.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@
2626
import numpy as np
2727
import xarray as xr
2828

29-
from calliope import config, exceptions
29+
from calliope import exceptions
3030
from calliope.attrdict import AttrDict
3131
from calliope.backend import helper_functions, parsing
3232
from calliope.exceptions import warn as model_warn
3333
from calliope.io import load_config, to_yaml
3434
from calliope.preprocess.model_math import ORDERED_COMPONENTS_T, CalliopeMath
35+
from calliope.schemas import config_schema
3536
from calliope.util.schema import MODEL_SCHEMA, extract_from_schema
3637

3738
if TYPE_CHECKING:
@@ -66,7 +67,7 @@ class BackendModelGenerator(ABC):
6667
_PARAM_TYPE = extract_from_schema(MODEL_SCHEMA, "x-type")
6768

6869
def __init__(
69-
self, inputs: xr.Dataset, math: CalliopeMath, build_config: config.Build
70+
self, inputs: xr.Dataset, math: CalliopeMath, build_config: config_schema.Build
7071
):
7172
"""Abstract base class to build a representation of the optimisation problem.
7273
@@ -606,7 +607,7 @@ def __init__(
606607
self,
607608
inputs: xr.Dataset,
608609
math: CalliopeMath,
609-
build_config: config.Build,
610+
build_config: config_schema.Build,
610611
instance: T,
611612
) -> None:
612613
"""Abstract base class to build backend models that interface with solvers.

src/calliope/backend/gurobi_backend_model.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
import pandas as pd
1515
import xarray as xr
1616

17-
from calliope import config
1817
from calliope.backend import backend_model, parsing
1918
from calliope.exceptions import BackendError, BackendWarning
2019
from calliope.exceptions import warn as model_warn
2120
from calliope.preprocess import CalliopeMath
21+
from calliope.schemas import config_schema
2222

2323
if importlib.util.find_spec("gurobipy") is not None:
2424
import gurobipy
@@ -43,7 +43,7 @@ class GurobiBackendModel(backend_model.BackendModel):
4343
"""gurobipy-specific backend functionality."""
4444

4545
def __init__(
46-
self, inputs: xr.Dataset, math: CalliopeMath, build_config: config.Build
46+
self, inputs: xr.Dataset, math: CalliopeMath, build_config: config_schema.Build
4747
) -> None:
4848
"""Gurobi solver interface class.
4949

src/calliope/backend/latex_backend_model.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
import pandas as pd
1313
import xarray as xr
1414

15-
from calliope import config
1615
from calliope.backend import backend_model, parsing
1716
from calliope.exceptions import ModelError
1817
from calliope.preprocess import CalliopeMath
18+
from calliope.schemas import config_schema
1919

2020
ALLOWED_MATH_FILE_FORMATS = Literal["tex", "rst", "md"]
2121
LOGGER = logging.getLogger(__name__)
@@ -306,7 +306,7 @@ def __init__(
306306
self,
307307
inputs: xr.Dataset,
308308
math: CalliopeMath,
309-
build_config: config.Build,
309+
build_config: config_schema.Build,
310310
include: Literal["all", "valid"] = "all",
311311
) -> None:
312312
"""Interface to build a string representation of the mathematical formulation using LaTeX math notation.

src/calliope/backend/pyomo_backend_model.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@
2626
from pyomo.opt import SolverFactory # type: ignore
2727
from pyomo.util.model_size import build_model_size_report # type: ignore
2828

29-
from calliope import config
3029
from calliope.exceptions import BackendError, BackendWarning
3130
from calliope.exceptions import warn as model_warn
3231
from calliope.preprocess import CalliopeMath
32+
from calliope.schemas import config_schema
3333
from calliope.util.logging import LogWriter
3434

3535
from . import backend_model, parsing
@@ -60,7 +60,7 @@ class PyomoBackendModel(backend_model.BackendModel):
6060
"""Pyomo-specific backend functionality."""
6161

6262
def __init__(
63-
self, inputs: xr.Dataset, math: CalliopeMath, build_config: config.Build
63+
self, inputs: xr.Dataset, math: CalliopeMath, build_config: config_schema.Build
6464
) -> None:
6565
"""Pyomo solver interface class.
6666

src/calliope/backend/where_parser.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
import xarray as xr
1414
from typing_extensions import NotRequired, TypedDict
1515

16-
from calliope import config
1716
from calliope.backend import expression_parser
1817
from calliope.exceptions import BackendError
18+
from calliope.schemas import config_schema
1919
from calliope.util import tools
2020

2121
if TYPE_CHECKING:
@@ -35,7 +35,7 @@ class EvalAttrs(TypedDict):
3535
helper_functions: dict[str, Callable]
3636
apply_where: NotRequired[bool]
3737
references: NotRequired[set]
38-
build_config: config.Build
38+
build_config: config_schema.Build
3939

4040

4141
class EvalWhere(expression_parser.EvalToArrayStr):

src/calliope/cli.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,13 @@ def _run_setup_model(model_file, scenario, model_format, override_dict):
193193
f'extension for "{model_file}". Set format explicitly with '
194194
"--model_format."
195195
)
196+
if isinstance(override_dict, str):
197+
overrides = io.read_rich_yaml(override_dict)
198+
else:
199+
overrides = io.AttrDict()
196200

197201
if model_format == "yaml":
198-
model = Model(model_file, scenario=scenario, override_dict=override_dict)
202+
model = Model(model_file, scenario=scenario, override_dict=overrides)
199203
elif model_format == "netcdf":
200204
if scenario is not None or override_dict is not None:
201205
raise ValueError(

0 commit comments

Comments
 (0)