Adding your own math to a model¶
Once you understand the math components and the formulation syntax, you'll be ready to introduce your own math to a model.
You can find examples of additional math that we have put together in our math example gallery.
Whenever you introduce your own math, it will be applied on top of the pre-defined math for your chosen run mode.
Therefore, you can override the pre-defined math as well as add new math.
For example, you may want to introduce a timeseries parameter to the pre-defined storage_max
constraint to limit maximum storage capacity on a per-timestep basis:
The other elements of the storage_max
constraints have not changed (foreach
, where
, ...), so we do not need to define them again when adding our own twist on the pre-defined math.
Note
If you prefer to start from scratch with your math, you can ask Calliope to not load the pre-defined math for your chosen run mode by setting config.build.ignore_mode_math: true
.
When defining your model, you can reference any number of YAML files containing the math you want to add in config.build
.
The paths are relative to your main model configuration file:
You can also define a mixture of your own math and the pre-defined math:
Finally, when working in an interactive Python session, you can add math as a dictionary at build time:
This will be applied after the pre-defined mode math and any math from file listed in config.build.add_math
.
Note
When working in an interactive Python session, you can view the final math dictionary that has been applied to build the optimisation problem by inspecting model.applied_math
after a successful call to model.build()
.
Adding your parameters to the YAML schema¶
Our YAML schemas are used to validate user inputs. The model definition schema includes metadata on all our pre-defined parameters, which you can find rendered in our reference page.
When you add your own math you are likely to be adding new parameters to the model.
You can update the Calliope model definition schema to include your new entries using calliope.util.schema.update_model_schema(...)
.
This ensures that your parameters have default values attached to them and if you choose to write your own documentation, your parameters will have this metadata added to their descriptions.
Entries in the schema look like this:
flow_cap_max:
$ref: "#/$defs/TechParamNullNumber" # (1)!
default: .inf
x-type: float
title: Maximum rated flow capacity.
description: >-
Limits `flow_cap` to a maximum.
x-unit: power.
- This is a cross-reference to a much longer schema entry that says the parameter type is either
None
, a simple number, or an indexed parameter dictionary with thedata
,index
, anddims
keys.
When you add your own parameters to the schema, you will need to know the top-level key under which the parameter will be found in your YAML definition: nodes
, techs
, or parameters
.
As a general rule, if it includes the techs
dimension, put it under techs
; if it includes nodes
but not techs
then put it under nodes
; if it includes neither dimension, put it under parameters
.
The dictionary you supply for each parameter can include the following:
- title (str): Short description of the parameter.
- description (str): Long description of the parameter.
- type (str or array): expected type of entry.
We recommend you use the pre-defined cross-reference
$ref: "#/$defs/TechParamNullNumber"
instead of explicitly using this key, to allow the parameter to be either numeric or an indexed parameter. If you are adding a cost, you can use the cross reference$ref: "#/$defs/TechCostNullNumber"
. If you want to allow non-numeric data (e.g., strings), you would settype: string
instead of using the cross-reference. - default (str): default value. This will be used in generating the optimisation problem.
- x-type (str): type of the non-NaN array entries in the internal calliope representation of the parameter.
This is usually one of
float
orstr
. - x-unit (str): Unit of the parameter to use in documentation.
- x-operate-param (bool): If True, this parameter's schema data will only be loaded into the optimisation problem if running in "operate" mode.
Note
Schema attributes which start with x-
are Calliope-specific.
They are not used at all for YAML validation and instead get picked up by us using the utility function calliope.util.schema.extract_from_schema.
Warning
The schema is updated in-place so your edits to it will remain active as long as you are running in the same session.
You can reset your updates to the schema and return to the pre-defined schema by calling calliope.util.schema.reset()
Writing your own math documentation¶
You can write your model's mathematical formulation to view it in a rich-text format (as we do for our pre-defined math in this documentation). To write a LaTeX, reStructuredText, or Markdown file that includes only the math valid for your model:
from calliope.postprocess.math_documentation import MathDocumentation
model = calliope.Model("path/to/model.yaml")
model.build()
math_documentation = MathDocumentation(model, include="valid")
math_documentation.write(filename="path/to/output/file.[tex|rst|md]")
You can then convert this to a PDF or HTML page using your renderer of choice. We recommend you only use HTML as the equations can become too long for a PDF page.
Note
You can add interactive elements to your documentation, if you are planning to host them online using MKDocs.
This includes tabs to flip between rich-text math and the input YAML snippet, and dropdown lists for math component cross-references.
Just set the mkdocs_features
argument to True
in math_documentation.write
.
We use this functionality in our pre-defined math.