Repository: calliope-project/calliope (language: python)

Issue:
Requesting v0.7 documentation improvements/fixes
### Description

This is a list of things I found on the documentation of the new release that could be improved. Most are minor things.
1. YAML schema: description is missing tabs beyond the second level for some items, making it hard to read. Examples: `time_subset`, `parameters`, `tech_groups`.
2. Model configuration: it would be good to specify if the dates in `time_subset` are inclusive (does `['2005-01-01', '2005-01-05']` run up to or include May?)
3. Tab 2 or tab 4 usage is a bit inconsistent. There is a statement advocating for tab 2, but a good amount of examples use tab 4.
4. Specify checks for `techs`. For `cost_` parameters, the documentation mentions they _must_ define a cost class, but it's unclear what that means or what the code will check for. Is it an `index` or a `dims`? Will the library return an error if the user sets this incorrectly? `nodes` has something similar for `dims`: if the users specifies `nodes`, will the code catch it?
5. `nodes` deactivation: move the `nodes` deactivation comment from the `techs` section to the `nodes` section, to make it easier to find.
6. `nodes` using `constraints` to modify techs: in the nodes section it's mentioned that you overwrite `techs` settings by using `constraints`. I think this term is a bit confusing (it's not used anywhere else before, and it's seldom mentioned in the documentation). Is it really necessary? Otherwise, I'd just remove it since it's clear that anything below a `techs` will be a parameter, and `costs_` already have standardised naming.
7. Tabular data: I recommend showing the tabular data as tables instead of comma text, to make it easier to read.
8. Inbuilt math vs custom-math: I think the activation of Inter-cluster storage via custom math is a bit confusing and hard to find. Maybe moving it to a "pre-defined custom math" section would solve this. As it stands, the inbuilt math section mostly talks about custom math.
9. Math readability: most equations are quite hard to follow because they get "cut" due to the long names. Defining the conditions above the constraints would help with this:

> Initial conditions (for all timestep in timesteps... etc):
>- Equation_1... if condition_1...
>- Equation_2... if condition_2...

Or (for an even more readable alternative):
> Initial conditions:
> - if condition_1:
>   Equation_1

10. `foreach`, `where` math syntax examples: when reading the section, it's a bit difficult to see how these are used since there is no complete example. Adding one at the start would solve this. Or moving the `equation` section to the top, since it's where most of these terms are used.
11. Math components and Math syntax: the custom math section says to read syntax, then components, but the order and the way the text is written seems to suggest the opposite.


### Related links

1. https://calliope.readthedocs.io/en/v0.7.0.dev2/reference/config_schema/#model-configuration-schema
2. https://calliope.readthedocs.io/en/v0.7.0.dev2/creating/config/
3. https://calliope.readthedocs.io/en/v0.7.0.dev2/creating/techs/ (example of tab 4 usage)
4. https://calliope.readthedocs.io/en/v0.7.0.dev2/creating/techs/ (unclear if there are checks for `cost_`)
5. https://calliope.readthedocs.io/en/v0.7.0.dev2/creating/techs/ (`nodes` comment that is hard to find)
6. https://calliope.readthedocs.io/en/v0.7.0.dev2/creating/nodes/ (`constraints` to modify `techs`)
7. https://calliope.readthedocs.io/en/v0.7.0.dev2/creating/data_sources/#__tabbed_3_1 (showing tabular data as tables)
8. https://calliope.readthedocs.io/en/v0.7.0.dev2/math/ (inbuilt math vs custom math).
9. https://calliope.readthedocs.io/en/v0.7.0.dev2/math/base/ (making math more readable).
10. https://calliope.readthedocs.io/en/v0.7.0.dev2/custom_math/syntax/ (`where`, `foreach` usage is unclear)
11. https://calliope.readthedocs.io/en/v0.7.0.dev2/custom_math/ (reverse suggestion to read Math syntax then components)

### Version

v0.7.0

### Proposed change

Some suggested updates to the documentation.


Discussion / hints:
Thanks, this is very useful feedback!
@irm-codebase thanks for the feedback.

Some responses / requests for additional thoughts:

8. How about "pre-defined" math for everything we have already built into calliope and "user-defined" math instead of "custom" math?
9. This is somewhat intentional. We're trying to strictly match convention for defining math. It isn't pretty because of all the "cases" but we're not really trying to make it pretty. If you can provide LaTeX examples of how to structure math with cases as you've suggested then I'm open to changing the implementation to increase readability. 

10/11. @sjpfenninger and I went back and forth on this a few times, hence why there's some confusion between different pages! We opted for "components" to come first so that you get some context via more complex examples before going into detail on the "syntax". Would it have been sufficiently clear if you had read the "components" page first? 
8. That would make sense! I'd sub-divide into core math and then additional features (time aggregation, operational mode, pathways, etc.).
9. I suppose that makes sense, but it's still very hard to read. IMO documentation is only read if it's accessible... Here's a suggested version using `\quad` and `\qquad`:

$\forall tech \in  techs, node \in nodes, step \in steps, year \in years \quad \text{if} \quad  \exists (\mathbf{param}_{tech,node}):$
$\quad  \text{if some condition}:$

$\qquad var_{tech,node,step,year} = othervar_{tech,node,step,year}\cdot \mathbf{param}_{tech,node}$

10. (and 11): I think so! I'd just change the suggested order in the linked page.

______

Math code, in case GitHub does not allow you to see it:
```LaTeX
$\forall tech \in  techs, node \in nodes, step \in steps, year \in years \quad \text{if} \quad  \exists (\mathbf{param}_{tech,node}):$
$\quad  \text{if some condition}:$

$\qquad var_{tech,node,step,year} = othervar_{tech,node,step,year}\cdot \mathbf{param}_{tech,node}$
```

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dea68b40..803b882c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -90,13 +90,13 @@ Use `supply` and `conversion` technology base classes instead.
 Use `carrier_in` and `carrier_out` instead.
 
 |removed| `carrier_tiers` and `carrier_ratios`.
-Use indexed parameter definitions for `flow_out_eff` and custom math instead.
+Use indexed parameter definitions for `flow_out_eff` and your own math instead.
 
 |removed| `calliope.Model.get_formatted_array`.
 The Calliope internal representation of data now matches the format achieved by calling this method in v0.6.
 
 |removed| Group constraints.
-Use custom math instead.
+Use your own math instead.
 
 |removed| Configuration options for features we no longer support.
 
diff --git a/docs/advanced/backend.md b/docs/advanced/backend.md
index 102577cc..a4be7a42 100644
--- a/docs/advanced/backend.md
+++ b/docs/advanced/backend.md
@@ -28,7 +28,7 @@ This will not affect results at this stage, you'll need to rerun the backend (po
 1. Update decision variable bounds.
 Most of the time, decision variable bounds are actually input parameters (e.g., `flow_cap_max` for the upper bound of the `flow_cap` decision variable).
 Therefore, to update the bounds you will update the parameter with [`#!python model.backend.update_parameter`][calliope.backend.backend_model.BackendModel.update_parameter].
-If a fixed numeric value is instead used, e.g. in your own [custom math](../custom_math/index.md), you can update bounds using [`#!python model.backend.update_variable_bounds`][calliope.backend.backend_model.BackendModel.update_variable_bounds].
+If a fixed numeric value is instead used, e.g. in [math that you have additionally defined](../user_defined_math/index.md), you can update bounds using [`#!python model.backend.update_variable_bounds`][calliope.backend.backend_model.BackendModel.update_variable_bounds].
 For instance, to update `flow_out` lower bound to 70 for `battery` at `region2`:
 
     ```python
diff --git a/docs/advanced/constraints.md b/docs/advanced/constraints.md
index 89a36c59..77664a60 100644
--- a/docs/advanced/constraints.md
+++ b/docs/advanced/constraints.md
@@ -5,7 +5,7 @@ On this page, we look at some of the more advanced features of Calliope's math a
 !!! info "See also"
     [Base math formulation][base-math],
     [Model definition schema][model-definition-schema],
-    [Introducing custom math to your model](../custom_math/customise.md),
+    [Introducing your own math to your model](../user_defined_math/customise.md),
     ["MILP" example model](../examples/milp/index.md).
 
 ## Multiple input/output carriers
@@ -24,21 +24,21 @@ E.g.,
 
 ```yaml
 techs:
-    chp:
-        name: Combined heat and power (CHP) plant
-        carrier_in: gas
-        carrier_out: [electricity, heat]
-    ashp:
-        name: Air source heat pump
-        carrier_in: electricity
-        carrier_out: [cooling, heat]
-    slightly_cleaner_coal_plant:
-        name: Dual fuel coal fired power station
-        carrier_in: [coal, biofuel]
-        carrier_out: electricity
-    nuclear:
-        name: Nuclear power station
-        carrier_out: [electricity, nuclear_waste]
+  chp:
+    name: Combined heat and power (CHP) plant
+    carrier_in: gas
+    carrier_out: [electricity, heat]
+  ashp:
+    name: Air source heat pump
+    carrier_in: electricity
+    carrier_out: [cooling, heat]
+  slightly_cleaner_coal_plant:
+    name: Dual fuel coal fired power station
+    carrier_in: [coal, biofuel]
+    carrier_out: electricity
+  nuclear:
+    name: Nuclear power station
+    carrier_out: [electricity, nuclear_waste]
 ```
 
 The default math applied to these multiple carriers is that inflow carrier requirements are based on the sum of the outflow carriers and vice-versa (see [the base math][balance_conversion]).
@@ -46,50 +46,50 @@ This is valid for our heat pump and coal-fired power plant examples above, but n
 In these examples, the inflow is linked to a specific outflow (gas / nuclear fuel consumption is a function of electricity production).
 The other carrier outflows are then linked to the "primary" outflow.
 
-To capture this slightly different math, you will need to apply your own [custom math](../custom_math/index.md).
-For example, the CHP example is dealt with in our [urban scale example model](../examples/urban_scale/index.md#sparkles-interlude-custom-math) and in an [example custom math file][chp-plants].
+To capture this slightly different math, you will need to [apply your own math](../user_defined_math/index.md).
+For example, the CHP example is dealt with in our [urban scale example model](../examples/urban_scale/index.md#interlude-user-defined-math) and in an [example additional math file][chp-plants].
 
 No matter how you formulate your math, you can (and probably will need to) extend your technology parameters to account for these different carriers.
 For instance, to differentiate `flow_cap_max` between carriers or to assign different conversion efficiencies / costs:
 
 ```yaml
 techs:
-    chp:
-        name: Combined heat and power (CHP) plant
-        carrier_in: gas
-        carrier_out: [electricity, heat]
-        flow_cap_max:
-            data: 100
-            index: electricity
-            dims: carriers
-        cost_flow_cap:
-            data: 0.1
-            index: [[electricity, monetary]]
-            dims: [carriers, costs]
-    ashp:
-        name: Air source heat pump
-        carrier_in: electricity
-        carrier_out: [cooling, heat]
-        flow_out_eff:
-            data: [3, 4]
-            index: [cooling, heat]
-            dims: carriers
-    slightly_cleaner_coal_plant:
-        name: Dual fuel coal fired power station
-        carrier_in: [coal, biofuel]
-        carrier_out: electricity
-        cost_flow_in:
-            data: [0.1, 0.5]
-            index: [[coal, monetary], [biofuel, monetary]]
-            dims: [carriers, costs]
-    nuclear:
-        name: Nuclear power station
-        carrier_out: [electricity, nuclear_waste]
-        waste_per_flow_out: 0.1  # (1)!
+  chp:
+    name: Combined heat and power (CHP) plant
+    carrier_in: gas
+    carrier_out: [electricity, heat]
+    flow_cap_max:
+      data: 100
+      index: electricity
+      dims: carriers
+    cost_flow_cap:
+      data: 0.1
+      index: [[electricity, monetary]]
+      dims: [carriers, costs]
+  ashp:
+    name: Air source heat pump
+    carrier_in: electricity
+    carrier_out: [cooling, heat]
+    flow_out_eff:
+      data: [3, 4]
+      index: [cooling, heat]
+      dims: carriers
+  slightly_cleaner_coal_plant:
+    name: Dual fuel coal fired power station
+    carrier_in: [coal, biofuel]
+    carrier_out: electricity
+    cost_flow_in:
+      data: [0.1, 0.5]
+      index: [[coal, monetary], [biofuel, monetary]]
+      dims: [carriers, costs]
+  nuclear:
+    name: Nuclear power station
+    carrier_out: [electricity, nuclear_waste]
+    waste_per_flow_out: 0.1  # (1)!
 ```
 
 1. This is a user-defined parameter that you won't find in our [list of internal parameters][model-definition-schema].
-You can use it in your custom math to link nuclear waste outflow with electricity outflow.
+You can use it in your own math to link nuclear waste outflow with electricity outflow.
 
 ## Activating storage buffers in non-storage technologies
 
@@ -132,11 +132,11 @@ To force unidirectionality for a given technology along a given link, you have t
 
 ```yaml
 techs:
-    region1_to_region2:
-        from: region1
-        to: region2
-        base_tech: transmission
-        one_way: true
+  region1_to_region2:
+    from: region1
+    to: region2
+    base_tech: transmission
+    one_way: true
 ```
 
 This will only allow transmission from `region1` to `region2`.
@@ -148,14 +148,14 @@ Transmission technologies can additionally specify per-distance efficiency (loss
 
 ```yaml
 techs:
-    my_transmission_tech:
-        # "efficiency" (1-loss) per unit of distance
-        flow_out_eff_per_distance: 0.99
-        cost_flow_cap_per_distance:
-            data: 10
-            index: monetary
-            dims: costs
-        distance: 500
+  my_transmission_tech:
+    # "efficiency" (1-loss) per unit of distance
+    flow_out_eff_per_distance: 0.99
+    cost_flow_cap_per_distance:
+      data: 10
+      index: monetary
+      dims: costs
+    distance: 500
 ```
 
 The distance can be specified in transmission links (as above) or, if no distance is given, from node `latitude` and `longitude` coordinates.
diff --git a/docs/advanced/index.md b/docs/advanced/index.md
index 9b807ca9..449aef2b 100644
--- a/docs/advanced/index.md
+++ b/docs/advanced/index.md
@@ -1,4 +1,4 @@
 # Advanced features
 
-Once you're comfortable with [creating](../creating/index.md), [running](../running.md) and [analysing](../analysing.md) one of the built-in example models, you may want to explore Calliope's advanced functionality.
+Once you're comfortable with [creating](../creating/index.md), [running](../running.md) and [analysing](../analysing.md) one of the [built-in example models][calliope.examples], you may want to explore Calliope's advanced functionality.
 With these features, you will be able to build and run complex models in no time.
diff --git a/docs/advanced/mode.md b/docs/advanced/mode.md
index 8d56e123..55cc3685 100644
--- a/docs/advanced/mode.md
+++ b/docs/advanced/mode.md
@@ -48,8 +48,8 @@ This requires two additional configuration options to be defined:
 
 ```yaml
 config.build:
-    operate_horizon: 48h  # (1)!
-    operate_window: 24h
+  operate_horizon: 48h  # (1)!
+  operate_window: 24h
 ```
 
 1. This is a pandas frequency string.
@@ -99,30 +99,30 @@ In the [national scale example model](../examples/national_scale/index.md), this
 
 ```yaml
 tech_groups:
-    add_spores_score:
-        inherit: cost_dim_setter
-        cost_flow_cap:
-            data: [null, null]
-            index: ["monetary", "spores_score"]
-            dims: costs
-        cost_interest_rate:
-            data: [0.1, 1]
-            index: ["monetary", "spores_score"]
-            dims: costs
+  add_spores_score:
+    inherit: cost_dim_setter
+    cost_flow_cap:
+      data: [null, null]
+      index: ["monetary", "spores_score"]
+      dims: costs
+    cost_interest_rate:
+      data: [0.1, 1]
+      index: ["monetary", "spores_score"]
+      dims: costs
 
 techs:
-    ccgt:
-        inherit: add_spores_score
-        cost_flow_cap.data: [750, 0]
-    csp:
-        inherit: add_spores_score
-        cost_flow_cap.data: [1000, 0]
-    battery:
-        inherit: add_spores_score
-        cost_flow_cap.data: [null, 0]
-    region1_to_region2:
-        inherit: add_spores_score
-        cost_flow_cap.data: [10000, 0]
+  ccgt:
+    inherit: add_spores_score
+    cost_flow_cap.data: [750, 0]
+  csp:
+    inherit: add_spores_score
+    cost_flow_cap.data: [1000, 0]
+  battery:
+    inherit: add_spores_score
+    cost_flow_cap.data: [null, 0]
+  region1_to_region2:
+    inherit: add_spores_score
+    cost_flow_cap.data: [10000, 0]
 ```
 
 !!! note
diff --git a/docs/advanced/solver.md b/docs/advanced/solver.md
index 2ba6123a..35e177f2 100644
--- a/docs/advanced/solver.md
+++ b/docs/advanced/solver.md
@@ -8,10 +8,10 @@ For example:
 
 ```yaml
 config.solve:
-    solver: gurobi
-    solver_options:
-        Threads: 3
-        NumericFocus: 2
+  solver: gurobi
+  solver_options:
+    Threads: 3
+    NumericFocus: 2
 ```
 
 ## CPLEX
@@ -22,11 +22,11 @@ For example:
 
 ```yaml
 config.solve:
-    solver: cplex
-    solver_options:
-        mipgap: 0.01
-        mip_polishafter_absmipgap: 0.1
-        emphasis_mip: 1
-        mip_cuts: 2
-        mip_cuts_cliques: 3
+  solver: cplex
+  solver_options:
+    mipgap: 0.01
+    mip_polishafter_absmipgap: 0.1
+    emphasis_mip: 1
+    mip_cuts: 2
+    mip_cuts_cliques: 3
 ```
diff --git a/docs/advanced/time.md b/docs/advanced/time.md
index 14f43a73..65e8fe79 100644
--- a/docs/advanced/time.md
+++ b/docs/advanced/time.md
@@ -8,8 +8,8 @@ This default resolution can be adjusted by specifying time resolution adjustment
 
 ```yaml
 config:
-    init:
-        time_resample: 6H
+  init:
+    time_resample: 6H
 ```
 
 In the above example, this would resample all time series data to 6-hourly timesteps.
@@ -21,13 +21,13 @@ By supplying a file linking dates in your model timeseries with representative d
 
 ```yaml
 config:
-    init:
-        time_cluster: cluster_days.csv
+  init:
+    time_cluster: cluster_days.csv
 ```
 
 When using representative days, you may want to enable a number of additional constraints to improve how carriers are stored between representative days, based on the study undertaken by [Kotzur et al.](https://doi.org/10.1016/j.apenergy.2018.01.023).
 These constraints require a new decision variable `storage_inter_cluster`, which tracks storage between all the dates of the original timeseries.
-This particular math - detailed [here][inter-cluster-storage-math] - can be enabled by including `storage_inter_cluster` in your list of custom math.
+This particular math - detailed [here][inter-cluster-storage-math] - can be enabled by including `storage_inter_cluster` in your list of additional math.
 
 We no longer provide the functionality to infer representative days from your timeseries.
 Instead, we recommend you use other timeseries processing tools applied to your input CSV data or your built model dataset (`model.inputs`).
diff --git a/docs/creating/config.md b/docs/creating/config.md
index 82efff16..47caab78 100644
--- a/docs/creating/config.md
+++ b/docs/creating/config.md
@@ -6,13 +6,13 @@ This includes for example the choice of solver with which to actually solve the
 
 ```yaml
 config:
-    init:
-        name: 'My energy model'
-        time_subset: ['2005-01-01', '2005-01-05']
-    build:
-        mode: plan
-    solve:
-        solver: cbc
+  init:
+    name: 'My energy model'
+    time_subset: ['2005-01-01', '2005-01-05']
+  build:
+    mode: plan
+  solve:
+    solver: cbc
 ```
 
 The configuration is grouped into three top-level items:
@@ -77,9 +77,9 @@ In the case of Gurobi, for example, it is usually fastest to use the direct Pyth
 
 ```yaml
 config:
-    solve:
-        solver: gurobi
-        solver_io: python
+  solve:
+    solver: gurobi
+    solver_io: python
 ```
 
 !!! note
diff --git a/docs/creating/data_sources.md b/docs/creating/data_sources.md
index ea36f5bf..16f54b9c 100644
--- a/docs/creating/data_sources.md
+++ b/docs/creating/data_sources.md
@@ -44,23 +44,23 @@ In this section we will show some examples of loading data and provide the equiv
 
     ```yaml
     data_sources:
-        pv_capacity_factor_data:
-            source: data_sources/pv_resource.csv
-            rows: timesteps
-            add_dimensions:
-                techs: pv
-                parameters: source_use_equals
+      pv_capacity_factor_data:
+        source: data_sources/pv_resource.csv
+        rows: timesteps
+        add_dimensions:
+          techs: pv
+          parameters: source_use_equals
     ```
 
 === "YAML"
 
     ```yaml
     techs:
-        pv:
-            source_use_equals:
-                data: [15, 5, 20, 11, ...]
-                index: ["2005-01-01 12:00:00", "2005-01-01 13:00:00", "2005-01-01 12:00:00", "2005-01-01 13:00:00", ....]
-                dims: "timesteps"
+      pv:
+        source_use_equals:
+          data: [15, 5, 20, 11, ...]
+          index: ["2005-01-01 12:00:00", "2005-01-01 13:00:00", "2005-01-01 12:00:00", "2005-01-01 13:00:00", ....]
+          dims: "timesteps"
     ```
 
 ??? note "Getting timestamp formats right"
@@ -96,27 +96,27 @@ In this section we will show some examples of loading data and provide the equiv
 
     ```yaml
     data_sources:
-        tech_data:
-            source: data_sources/tech_data.csv
-            rows: [techs, parameters]
+      tech_data:
+        source: data_sources/tech_data.csv
+        rows: [techs, parameters]
     ```
 
 === "YAML"
 
     ```yaml
     techs:
-        tech1:
-            base_tech: supply
-            flow_cap_max: 100
-            flow_out_eff: 0.1
-            area_use_max: 500
-            area_use_per_flow_cap: 7
-        tech2:
-            base_tech: demand
-        tech3:
-            base_tech: storage
-            storage_cap_max: 200
-            flow_cap_max: 100
+      tech1:
+        base_tech: supply
+        flow_cap_max: 100
+        flow_out_eff: 0.1
+        area_use_max: 500
+        area_use_per_flow_cap: 7
+      tech2:
+        base_tech: demand
+      tech3:
+        base_tech: storage
+        storage_cap_max: 200
+        flow_cap_max: 100
     ```
 
 ### Loading technology cost data from file
@@ -139,48 +139,48 @@ In this section we will show some examples of loading data and provide the equiv
 
     ```yaml
     data_sources:
-        tech_data:
-            source: data_sources/tech_data.csv
-            rows: [techs, parameters]
-            add_dimensions:
-                costs: monetary
+      tech_data:
+        source: data_sources/tech_data.csv
+        rows: [techs, parameters]
+        add_dimensions:
+          costs: monetary
     ```
 
 === "YAML"
 
     ```yaml
     tech_groups:  # (1)!
-        cost_setter:
-            cost_interest_rate:
-                data: 0.1
-                index: monetary
-                dims: costs
-            cost_flow_cap:
-                data: null
-                index: monetary
-                dims: costs
-            cost_area_use:
-                data: null
-                index: monetary
-                dims: costs
-            cost_flow_out:
-                data: null
-                index: monetary
-                dims: costs
-            cost_storage_cap:
-                data: null
-                index: monetary
-                dims: costs
+      cost_setter:
+        cost_interest_rate:
+          data: 0.1
+          index: monetary
+          dims: costs
+        cost_flow_cap:
+          data: null
+          index: monetary
+          dims: costs
+        cost_area_use:
+          data: null
+          index: monetary
+          dims: costs
+        cost_flow_out:
+          data: null
+          index: monetary
+          dims: costs
+        cost_storage_cap:
+          data: null
+          index: monetary
+          dims: costs
 
     techs:
-        tech1:
-            inherit: cost_setter
-            cost_flow_cap.data: 100
-            cost_area_use.data: 50
-            cost_flow_out.data: 0.2
-        tech3:
-            cost_flow_cap.data: 20
-            cost_storage_cap.data: 150
+      tech1:
+        inherit: cost_setter
+        cost_flow_cap.data: 100
+        cost_area_use.data: 50
+        cost_flow_out.data: 0.2
+      tech3:
+        cost_flow_cap.data: 20
+        cost_storage_cap.data: 150
     ```
 
     1. To limit repetition, we have defined [technology groups](groups.md) for our costs.
@@ -206,12 +206,12 @@ YAML definition to load only data from nodes 1 and 2:
 
 ```yaml
 data_sources:
-    tech_data:
-        source: data_sources/tech_data.csv
-        rows: [techs, parameters]
-        columns: nodes
-        select:
-            nodes: [node1, node2]
+  tech_data:
+    source: data_sources/tech_data.csv
+    rows: [techs, parameters]
+    columns: nodes
+    select:
+      nodes: [node1, node2]
 ```
 
 You may also want to store scenarios in your file.
@@ -229,21 +229,21 @@ YAML definition to load only data from scenario 1:
 
 ```yaml
 data_sources:
-    tech_data:
-        source: data_sources/tech_data.csv
-        rows: [techs, parameters]
-        columns: scenarios
-        select:
-            scenarios: scenario1
-        drop: scenarios
+  tech_data:
+    source: data_sources/tech_data.csv
+    rows: [techs, parameters]
+    columns: scenarios
+    select:
+      scenarios: scenario1
+    drop: scenarios
 ```
 
 You can then also tweak just one line of your data source YAML with an [override](scenarios.md) to point to your other scenario:
 
 ```yaml
 override:
-    switch_to_scenario2:
-        data_sources.tech_data.select.scenarios: scenario2  # (1)!
+  switch_to_scenario2:
+    data_sources.tech_data.select.scenarios: scenario2  # (1)!
 ```
 
 1. We use the dot notation as a shorthand for [abbreviate nested dictionaries](yaml.md#abbreviated-nesting).
@@ -266,10 +266,10 @@ For example, to define costs for the parameter `cost_flow_cap`:
 
     ```yaml
     data_sources:
-        tech_data:
-            source: data_sources/tech_data.csv
-            rows: [techs, costs, parameters]
-            columns: nodes
+      tech_data:
+        source: data_sources/tech_data.csv
+        rows: [techs, costs, parameters]
+        columns: nodes
     ```
 
 === "With `add_dimensions`"
@@ -283,13 +283,13 @@ For example, to define costs for the parameter `cost_flow_cap`:
 
     ```yaml
     data_sources:
-        tech_data:
-            source: data_sources/tech_data.csv
-            rows: techs
-            columns: nodes
-            add_dimensions:
-                costs: monetary
-                parameters: cost_flow_cap
+      tech_data:
+        source: data_sources/tech_data.csv
+        rows: techs
+        columns: nodes
+        add_dimensions:
+          costs: monetary
+          parameters: cost_flow_cap
     ```
 
 Or to define the same timeseries source data for two technologies at different nodes:
@@ -307,10 +307,10 @@ Or to define the same timeseries source data for two technologies at different n
 
     ```yaml
     data_sources:
-        tech_data:
-            source: data_sources/tech_data.csv
-            rows: timesteps
-            columns: [nodes, techs, parameters]
+      tech_data:
+        source: data_sources/tech_data.csv
+        rows: timesteps
+        columns: [nodes, techs, parameters]
     ```
 
 
@@ -325,20 +325,20 @@ Or to define the same timeseries source data for two technologies at different n
 
     ```yaml
     data_sources:
-        tech_data_1:
-            source: data_sources/tech_data.csv
-            rows: timesteps
-            add_dimensions:
-                techs: tech1
-                nodes: node1
-                parameters: source_use_max
-        tech_data_2:
-            source: data_sources/tech_data.csv
-            rows: timesteps
-            add_dimensions:
-                techs: tech2
-                nodes: node2
-                parameters: source_use_max
+      tech_data_1:
+        source: data_sources/tech_data.csv
+        rows: timesteps
+        add_dimensions:
+          techs: tech1
+          nodes: node1
+          parameters: source_use_max
+      tech_data_2:
+        source: data_sources/tech_data.csv
+        rows: timesteps
+        add_dimensions:
+          techs: tech2
+          nodes: node2
+          parameters: source_use_max
     ```
 
 ## Loading CSV files vs `pandas` dataframes
@@ -364,12 +364,12 @@ And then you point to those dictionary keys in the `source` for your data source
 
 ```yaml
 data_sources:
-    ds1:
-        source: data_source_1
-        ...
-    ds2:
-        source: data_source_2
-        ...
+  ds1:
+    source: data_source_1
+    ...
+  ds2:
+    source: data_source_2
+    ...
 ```
 
 !!! note
@@ -402,10 +402,10 @@ Instead, at least list the techs available at each node in YAML.
 E.g.,
     ```yaml
     nodes:
-        node1.techs: {tech1, tech2, tech3}
-        node2.techs: {tech1, tech2}
+      node1.techs: {tech1, tech2, tech3}
+      node2.techs: {tech1, tech2}
     data_sources:
-        ...
+      ...
     ```
 
 ### Data you _cannot_ load in tabular format
diff --git a/docs/creating/groups.md b/docs/creating/groups.md
index aa9dfb49..d076c9b2 100644
--- a/docs/creating/groups.md
+++ b/docs/creating/groups.md
@@ -8,44 +8,44 @@ For example, if we want to set interest rate to `0.1` across all our technologie
 
 ```yaml
 tech_groups:
-    interest_rate_setter:
-        cost_interest_rate:
-            data: 0.1
-            index: monetary
-            dims: costs
+  interest_rate_setter:
+    cost_interest_rate:
+      data: 0.1
+      index: monetary
+      dims: costs
 techs:
-    ccgt:
-        inherit: interest_rate_setter
-        ...
-    ac_transmission:
-        inherit: interest_rate_setter
-        ...
+  ccgt:
+    inherit: interest_rate_setter
+    ...
+  ac_transmission:
+    inherit: interest_rate_setter
+    ...
 ```
 
 Similarly, if we want to allow the same technologies at all our nodes:
 
 ```yaml
 node_groups:
-    standard_tech_list:
-        techs: {ccgt, battery, demand_power}  # (1)!
+  standard_tech_list:
+    techs: {ccgt, battery, demand_power}  # (1)!
 nodes:
-    region1:
-        inherit: standard_tech_list
-        ...
-    region2:
-        inherit: standard_tech_list
-        ...
+  region1:
+    inherit: standard_tech_list
+    ...
+  region2:
+    inherit: standard_tech_list
     ...
-    region100:
-        inherit: standard_tech_list
+  ...
+  region100:
+    inherit: standard_tech_list
 ```
 
 1. this YAML syntax is shortform for:
     ```yaml
     techs:
-        ccgt:
-        battery:
-        demand_power:
+      ccgt:
+      battery:
+      demand_power:
     ```
 
 Inheritance chains can also be set up.
@@ -54,28 +54,28 @@ E.g.:
 
 ```yaml
 tech_groups:
-    interest_rate_setter:
-        cost_interest_rate:
-            data: 0.1
-            index: monetary
-            dims: costs
-    investment_cost_setter:
-        inherit: interest_rate_setter
-        cost_flow_cap:
-            data: 100
-            index: monetary
-            dims: costs
-        cost_area_use:
-            data: 1
-            index: monetary
-            dims: costs
+  interest_rate_setter:
+    cost_interest_rate:
+      data: 0.1
+      index: monetary
+      dims: costs
+  investment_cost_setter:
+    inherit: interest_rate_setter
+    cost_flow_cap:
+      data: 100
+      index: monetary
+      dims: costs
+    cost_area_use:
+       data: 1
+       index: monetary
+       dims: costs
 techs:
-    ccgt:
-        inherit: investment_cost_setter
-        ...
-    ac_transmission:
-        inherit: interest_rate_setter
-        ...
+  ccgt:
+    inherit: investment_cost_setter
+    ...
+  ac_transmission:
+    inherit: interest_rate_setter
+    ...
 ```
 
 Finally, inherited properties can always be overridden by the inheriting component.
@@ -83,25 +83,25 @@ This can be useful to streamline setting costs, e.g.:
 
 ```yaml
 tech_groups:
-    interest_rate_setter:
-        cost_interest_rate:
-            data: 0.1
-            index: monetary
-            dims: costs
-    investment_cost_setter:
-        inherit: interest_rate_setter
-        cost_interest_rate.data: 0.2  # this will replace `0.1` in the `interest_rate_setter`.
-        cost_flow_cap:
-            data: null
-            index: monetary
-            dims: costs
-        cost_area_use:
-            data: null
-            index: monetary
-            dims: costs
+  interest_rate_setter:
+    cost_interest_rate:
+      data: 0.1
+      index: monetary
+      dims: costs
+  investment_cost_setter:
+    inherit: interest_rate_setter
+    cost_interest_rate.data: 0.2  # this will replace `0.1` in the `interest_rate_setter`.
+    cost_flow_cap:
+      data: null
+      index: monetary
+      dims: costs
+    cost_area_use:
+      data: null
+      index: monetary
+      dims: costs
 techs:
-    ccgt:
-        inherit: investment_cost_setter
-        cost_flow_cap.data: 100  # this will replace `null` in the `investment_cost_setter`.
-        ...
+  ccgt:
+    inherit: investment_cost_setter
+    cost_flow_cap.data: 100  # this will replace `null` in the `investment_cost_setter`.
+    ...
 ```
diff --git a/docs/creating/nodes.md b/docs/creating/nodes.md
index 09a4d06e..9a12f200 100644
--- a/docs/creating/nodes.md
+++ b/docs/creating/nodes.md
@@ -9,34 +9,46 @@ The `nodes` section specifies each node:
 
 ```yaml
 nodes:
-    region1:
-        latitude: 40
-        longitude: -2
-        available_area: 100
-        node_flow_out_max:
-            data: [1000, 2000]
-            index: [electricity, gas]
-            dims: carriers
-        techs:
-            unmet_demand_power:
-            demand_power:
-            ccgt:
-                constraints:
-                    flow_cap_max: 30000
+  region1:
+    latitude: 40
+    longitude: -2
+    available_area: 100
+    node_flow_out_max:
+      data: [1000, 2000]
+      index: [electricity, gas]
+      dims: carriers
+    techs:
+      unmet_demand_power:
+      demand_power:
+      ccgt:
+        flow_cap_max: 30000
 ```
 
 For technologies to be installable at a node, they must be listed under `techs` for that node.
-As seen in the example above, each allowed tech must be listed, and can optionally specify additional node-specific parameters (constraints or costs).
-If given, node-specific parameters supersede any group constraints a technology defines in the `techs` section for that node.
+As seen in the example above, each allowed tech must be listed, and can optionally specify node-specific parameters.
+If given, node-specific parameters supersede any parameters given at the technology level.
 
 Nodes can optionally specify geographic coordinates (`latitude` and `longitude`) which are used in visualisation or to compute distances along transmission links.
 Nodes can also have any arbitrary parameter assigned which will be available in the optimisation problem, indexed over the `nodes` dimension.
 They can also have parameters that use the [indexed parameter syntax](parameters.md) to define node+other dimension data.
-In the above example, `node_flow_out_max` at `region1` could be used to create a [custom math](../custom_math/index.md) constraint that limits the total outflow of the carriers electricity and gas at that node.
+In the above example, `node_flow_out_max` at `region1` could be used to create [your own math](../user_defined_math/index.md) constraint that limits the total outflow of the carriers electricity and gas at that node.
 
 ## Understanding node-level parameters
 
 `techs` is the only required parameter in a node.
 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).
 
-Everything else about defining parameters can be found on our [`techs` page](techs.md#understanding-tech-level-parameters).
\ No newline at end of file
+!!! info "See also"
+
+    See the [`techs` page](techs.md#understanding-tech-level-parameters) for the different formats in which you can define parameters, which holds for node-level parameters too.
+
+### (De)activating nodes
+
+In an [override](scenarios.md) you may want to remove a node entirely from the model.
+The easiest way to do this is to set `active: false`.
+The resulting input dataset won't feature that node in any way.
+You can even do this to deactivate [technologies](techs.md) at a node.
+Conversely, setting `active: true` in an override will lead to the node reappearing.
+
+!!! note
+    When deactivating nodes, any transmission technologies that link to that node will also be deactivated.
diff --git a/docs/creating/parameters.md b/docs/creating/parameters.md
index 39463a9d..ff6906e2 100644
--- a/docs/creating/parameters.md
+++ b/docs/creating/parameters.md
@@ -1,51 +1,51 @@
 # Indexed parameters (`parameters`)
 
-Some data is not indexed over [technologies][technologies-techs] / [nodes][nodes-nodes].
+Some data is not indexed over [technologies](techs.md) / [nodes](nodes.md).
 This data can be defined under the top-level key `parameters`.
 This could be a single value:
 
 ```yaml
 parameters:
-    my_param: 10
+  my_param: 10
 ```
 
 or (equivalent):
 
 ```yaml
 parameters:
-    my_param:
-        data: 10
+  my_param:
+    data: 10
 ```
 
-which can then be accessed in the model inputs `model.inputs.my_param` and used in [custom math][custom-math-formulation] as `my_param`.
+which can then be accessed in the model inputs `model.inputs.my_param` and used in [any math you add](../user_defined_math/index.md) as `my_param`.
 
 Or, it can be indexed over one or more model dimension(s):
 
 ```yaml
 parameters:
-    my_indexed_param:
-        data: 100
-        index: monetary
-        dims: costs
-    my_multiindexed_param:
-        data: [2, 10]
-        index: [[monetary, electricity], [monetary, heat]]  # (1)!
-        dims: [costs, carriers]
+  my_indexed_param:
+    data: 100
+    index: monetary
+    dims: costs
+  my_multiindexed_param:
+    data: [2, 10]
+    index: [[monetary, electricity], [monetary, heat]]  # (1)!
+    dims: [costs, carriers]
 ```
 
 1. The length of the inner index lists is equal to the length of `dims`.
 The length of the outer list is equal to the length of `data`.
 
-which can be accessed in the model inputs and [custom math][custom-math-formulation], e.g., `model.inputs.my_multiindexed_param.sel(costs="monetary")` and `my_multiindexed_param`.
+which can be accessed in the model inputs and [any math you add](../user_defined_math/index.md), e.g., `model.inputs.my_multiindexed_param.sel(costs="monetary")` and `my_multiindexed_param`.
 
 You can also index over a new dimension:
 
 ```yaml
 parameters:
-    my_indexed_param:
-        data: 100
-        index: my_index_val
-        dims: my_new_dim
+  my_indexed_param:
+    data: 100
+    index: my_index_val
+    dims: my_new_dim
 ```
 
 Which will add the new dimension `my_new_dim` to your model: `model.inputs.my_new_dim` which you could choose to build a math component over:
diff --git a/docs/creating/scenarios.md b/docs/creating/scenarios.md
index 371a8c9e..0349e2c0 100644
--- a/docs/creating/scenarios.md
+++ b/docs/creating/scenarios.md
@@ -11,16 +11,16 @@ Both are specified at the top level of the model configuration, as in this examp
 
 ```yaml
 scenarios:
-    high_cost_2005: ["high_cost", "year2005"]
-    high_cost_2006: ["high_cost", "year2006"]
+  high_cost_2005: ["high_cost", "year2005"]
+  high_cost_2006: ["high_cost", "year2006"]
 
 overrides:
-    high_cost:
-        techs.onshore_wind.cost_flow_cap.data: 2000
-    year2005:
-        model.time_subset: ['2005-01-01', '2005-12-31']
-    year2006:
-        model.time_subset: ['2006-01-01', '2006-12-31']
+  high_cost:
+    techs.onshore_wind.cost_flow_cap.data: 2000
+  year2005:
+    model.time_subset: ['2005-01-01', '2005-12-31']
+  year2006:
+    model.time_subset: ['2006-01-01', '2006-12-31']
 
 config:
     ...
@@ -45,25 +45,25 @@ The following example illustrates this:
 
 ```yaml
 overrides:
-    some_override:
-        techs:
-            some_tech.constraints.flow_cap_max: 10
-        import: [additional_definitions.yaml]
+  some_override:
+    techs:
+      some_tech.constraints.flow_cap_max: 10
+    import: [additional_definitions.yaml]
 ```
 
 `additional_definitions.yaml`:
 
 ```yaml
 techs:
-    some_other_tech.constraints.flow_out_eff: 0.1
+  some_other_tech.constraints.flow_out_eff: 0.1
 ```
 
 This is equivalent to the following override:
 
 ```yaml
 overrides:
-    some_override:
-        techs:
-            some_tech.constraints.flow_cap_max: 10
-            some_other_tech.constraints.flow_out_eff: 0.1
+  some_override:
+    techs:
+      some_tech.constraints.flow_cap_max: 10
+      some_other_tech.constraints.flow_out_eff: 0.1
 ```
diff --git a/docs/creating/techs.md b/docs/creating/techs.md
index c5b8cdb8..b4c783a3 100644
--- a/docs/creating/techs.md
+++ b/docs/creating/techs.md
@@ -23,26 +23,26 @@ The following example shows the definition of a `ccgt` technology, i.e. a combin
 
 ```yaml
 ccgt:
-    name: 'Combined cycle gas turbine'
-    color: '#FDC97D'  # (1)!
-    base_tech: supply
-    carrier_out: power
-    source_use_max: .inf # (2)!
-    flow_out_eff: 0.5
-    flow_cap_max: 40000  # kW
-    lifetime: 25
-    cost_interest_rate:
-        data: 0.10  # (3)!
-        index: monetary
-        dims: costs
-    cost_flow_cap:
-        data: 750  # USD per kW
-        index: monetary
-        dims: costs
-    cost_flow_in:
-        data: 0.02  # USD per kWh
-        index: monetary
-        dims: costs
+  name: 'Combined cycle gas turbine'
+  color: '#FDC97D'  # (1)!
+  base_tech: supply
+  carrier_out: power
+  source_use_max: .inf # (2)!
+  flow_out_eff: 0.5
+  flow_cap_max: 40000  # kW
+  lifetime: 25
+  cost_interest_rate:
+    data: 0.10  # (3)!
+    index: monetary
+    dims: costs
+  cost_flow_cap:
+    data: 750  # USD per kW
+    index: monetary
+    dims: costs
+  cost_flow_in:
+    data: 0.02  # USD per kWh
+    index: monetary
+    dims: costs
 ```
 
 1. This is an example of when using quotation marks is important.
@@ -64,14 +64,15 @@ Additional cost classes can be created simply by adding them to the definition o
 
 ??? info "Costs in the objective function"
     By default, all defined cost classes are used in the objective function, i.e., the default objective is to minimize total costs.
-    Limiting the considered costs can be achieved by [customising the in-built objective function](../custom_math/customise.md) to only focus on e.g. monetary costs (`[monetary] in costs`), or updating the `objective_cost_weights` indexed parameter to have a weight of `0` for those cost classes you want to be ignored, e.g.:
+    Limiting the considered costs can be achieved by [customising the in-built objective function](../user_defined_math/customise.md) to only focus on e.g. monetary costs (`[monetary] in costs`),
+    or updating the `objective_cost_weights` indexed parameter to have a weight of `0` for those cost classes you want to be ignored, e.g.:
 
     ```yaml
     parameters:
-        objective_cost_weights:
-            data: [1, 0]
-            index: [monetary, co2_emissions]
-            dims: costs
+      objective_cost_weights:
+        data: [1, 0]
+        index: [monetary, co2_emissions]
+        dims: costs
     ```
 
 ## Transmission technologies
@@ -82,11 +83,11 @@ Instead, you associate transmission technologies with nodes in `techs`:
 
 ```yaml
 techs:
-    ac_transmission:
-        from: region1  # (1)!
-        to: region2
-        flow_cap_max: 100
-        ...
+  ac_transmission:
+    from: region1  # (1)!
+    to: region2
+    flow_cap_max: 100
+    ...
 ```
 
 1. The region you specify in `from` or `to` is interchangeable unless you set the parameter `one_way: true`.
@@ -107,9 +108,9 @@ There are _required_ parameters according to the technology `base_tech`:
 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.
 However, doing so makes it much easier to process your model definition in Calliope!
 
-### Inbuilt parameters
+### Pre-defined parameters
 
-There is a long list of inbuilt parameters that we use in our [base math][base-math].
+There is a long list of pre-defined parameters that we use in our [base math][base-math].
 These are listed in full with descriptions and units in our [model definition reference page][math-formulation-schema].
 These parameters come with strict types, default values, and other attributes used in internal Calliope processing.
 Therefore they should always be your first port of call.
@@ -117,11 +118,29 @@ However, if you want to add your own parameters, that is also possible.
 
 ### Adding your own parameter
 
-You can also add any new parameter you like, which will then be available to use in any [custom math](../custom_math/index.md) you want to apply.
+You can also add any new parameter you like, which will then be available to use in any [math you want to additionally apply](../user_defined_math/index.md).
 The only requirements we apply are that it _cannot_ start with an underscore or a number.
 
 We also have a check for any parameter starting with `cost_`.
-These _must_ define a cost class.
+These _must_ define a cost class, e.g.,:
+
+```yaml
+techs:
+  tech1:
+    cost_custom:
+      data: 1
+      index: monetary
+      dims: costs
+```
+
+If you forget to use the [indexed parameter](parameters.md) format for a parameter starting with `cost_` then our YAML schema will raise an error.
+For example, this is not valid and will create an error:
+
+```yaml
+techs:
+  tech1:
+    cost_custom: 1
+```
 
 ### Using the indexed parameter format
 
@@ -134,47 +153,44 @@ We saw this above with `costs`, but you can add _any_ dimension _except_ `nodes`
 
     ```yaml
     techs:
-        tech1:
-            source_use_equals:
-                data: [15, 5]
-                index: ["2005-01-01 12:00:00", "2005-01-01 13:00:00"]
-                dims: timesteps
+      tech1:
+          source_use_equals:
+            data: [15, 5]
+            index: ["2005-01-01 12:00:00", "2005-01-01 13:00:00"]
+            dims: timesteps
     ```
 
     ```yaml
     techs:
-        tech1:
-            flow_cap_max:
-                data: [10, 100]
-                index: ["electricity", "heat"]
-                dims: carriers
+      tech1:
+        flow_cap_max:
+          data: [10, 100]
+          index: ["electricity", "heat"]
+          dims: carriers
     ```
 
     ```yaml
     techs:
-        tech1:
-            cost_flow_cap:
-                data: [4, 8]
-                index: [["electricity", "monetary"], ["heat", "monetary"]]
-                dims: [carriers, costs]
+      tech1:
+        cost_flow_cap:
+          data: [4, 8]
+          index: [["electricity", "monetary"], ["heat", "monetary"]]
+          dims: [carriers, costs]
     ```
 
     ```yaml
     techs:
-        tech1:
-            source_use_equals:
-                data: [15, 5]
-                index: ["foo", "bar"]
-                dims: my_new_dimension
+      tech1:
+        source_use_equals:
+          data: [15, 5]
+          index: ["foo", "bar"]
+          dims: my_new_dimension
     ```
 
-### (De)activing techs
+### (De)activating techs
 
 In an [override](scenarios.md) you may want to remove a technology entirely from the model.
 The easiest way to do this is to set `active: false`.
 The resulting input dataset won't feature that technology in any way.
 You can even do this to deactivate technologies at specific [nodes](nodes.md) and to deactivate nodes entirely.
-Conversely, setting `active: true` in an override will lead to the technology(/node) reappearing.
-
-!!! note
-    When deactivating nodes, any transmission technologies that link to that node will also be deactivated.
\ No newline at end of file
+Conversely, setting `active: true` in an override will lead to the technology reappearing.
\ No newline at end of file
diff --git a/docs/creating/yaml.md b/docs/creating/yaml.md
index ba4489b4..6264afa2 100644
--- a/docs/creating/yaml.md
+++ b/docs/creating/yaml.md
@@ -10,8 +10,8 @@ Calliope allows an abbreviated form for long, nested settings:
 
 ```yaml
 one:
-    two:
-        three: x
+  two:
+    three: x
 ```
 
 can be written as:
@@ -27,8 +27,8 @@ This can specify one or several YAML files to import, e.g.:
 
 ```yaml
 import:
-    - path/to/file_1.yaml
-    - path/to/file_2.yaml
+  - path/to/file_1.yaml
+  - path/to/file_2.yaml
 ```
 
 Data defined in the current and imported file(s) must be mutually exclusive.
@@ -39,9 +39,9 @@ In this case, our `model.yaml` file tends to have the following `import` stateme
 
 ```yaml
 import:
-    - 'model_definition/techs.yaml'
-    - 'model_definition/nodes.yaml'
-    - 'scenarios.yaml'
+  - 'model_definition/techs.yaml'
+  - 'model_definition/nodes.yaml'
+  - 'scenarios.yaml'
 ```
 
 This means that we have:
@@ -50,44 +50,44 @@ This means that we have:
 
     ```yaml
     import:
-        - 'model_definition/techs.yaml'
-        - 'model_definition/nodes.yaml'
-        - 'scenarios.yaml'
+      - 'model_definition/techs.yaml'
+      - 'model_definition/nodes.yaml'
+      - 'scenarios.yaml'
     config:
-        init:
-            ...
-        build:
-            ...
-        solve:
-            ...
+      init:
+        ...
+      build:
+        ...
+      solve:
+        ...
     ```
 
 === "model_definition/techs.yaml"
 
     ```yaml
     techs:
-        tech1:
-            ...
+      tech1:
         ...
+      ...
     ```
 
 === "model_definition/nodes.yaml"
 
     ```yaml
     nodes:
-        node1:
-            ...
+      node1:
         ...
+      ...
     ```
 
 === "scenarios.yaml"
 
     ```yaml
     overrides:
-        override1:
-        ...
+      override1:
+      ...
     scenarios:
-        scenario1: [override1, ...]
+      scenario1: [override1, ...]
     ...
     ```
 
@@ -95,29 +95,29 @@ Which Calliope will receive as:
 
 ```yaml
 import:
-    - 'model_definition/techs.yaml'
-    - 'model_definition/nodes.yaml'
-    - 'scenarios.yaml'
+  - 'model_definition/techs.yaml'
+  - 'model_definition/nodes.yaml'
+  - 'scenarios.yaml'
 config:
-    init:
-        ...
-    build:
-        ...
-    solve:
-        ...
+  init:
+    ...
+  build:
+    ...
+  solve:
+    ...
 techs:
-    tech1:
-        ...
+  tech1:
     ...
+  ...
 nodes:
-    node1:
-        ...
+  node1:
     ...
+  ...
 overrides:
-    override1:
-    ...
+  override1:
+  ...
 scenarios:
-    scenario1: [override1, ...]
+  scenario1: [override1, ...]
 ...
 ```
 
@@ -147,8 +147,8 @@ The above would lead to:
 
 ```yaml
 one.two:
-    three: x
-    four: y
+  three: x
+  four: y
 four.five.six: y
 ```
 
@@ -157,7 +157,7 @@ Now, using this override:
 
 ```yaml
 one._REPLACE_:
-    four: y
+  four: y
 four.five.six: y
 ```
 
@@ -204,8 +204,8 @@ key: [option1, option2]
 
 ```yaml
 key:
-    - option1
-    - option2
+  - option1
+  - option2
 ```
 
 Dictionaries can be of the form `{...}` or a series of lines _without_ a starting `-`.
@@ -217,8 +217,8 @@ key: {option1: value1, option2: value2}
 
 ```yaml
 key:
-    option1: value1
-    option2: value2
+  option1: value1
+  option2: value2
 ```
 
 To continue dictionary nesting, you can add more `{}` parentheses or you can indent your lines further.
@@ -234,7 +234,7 @@ key:
     option4: value4
 ```
 
-Which is equivalent in Python to `{"key": [{"option1": value1, "option2": value2}, {"option3": value3, "option4": value4}]}`.
+Which is equivalent in Python to `#!python {"key": [{"option1": value1, "option2": value2}, {"option3": value3, "option4": value4}]}`.
 
 !!! info "See also"
     See the [YAML website](https://yaml.org/) for more general information about YAML.
diff --git a/docs/examples/loading_tabular_data.py b/docs/examples/loading_tabular_data.py
index 3e3d069e..971dfc94 100644
--- a/docs/examples/loading_tabular_data.py
+++ b/docs/examples/loading_tabular_data.py
@@ -36,56 +36,56 @@
 #
 # ```yaml
 # techs:
-#     supply_tech:
-#         base_tech: supply
-#         carrier_out: electricity
-#         flow_cap_max: 10
-#         source_use_max:
-#             data: [10, 2]
-#             index: ["2020-01-01 00:00", "2020-01-01 01:00"]
-#             dims: timesteps
-#         cost_flow_cap:
-#             data: 2
-#             index: monetary
-#             dims: costs
-#
-#     storage_tech:
-#         base_tech: storage
-#         carrier_in: electricity
-#         carrier_out: electricity
-#         flow_cap_max: 6
-#         storage_cap_max: 7
-#         cost_storage_cap:
-#             data: 5
-#             index: monetary
-#             dims: costs
-#         cost_flow_out:
-#             data: 0.1
-#             index: monetary
-#             dims: costs
-#
-#     demand_tech:
-#         base_tech: demand
-#         carrier_in: electricity
-#         sink_use_equals:
-#             data: [4, 5]
-#             index: ["2020-01-01 00:00", "2020-01-01 01:00"]
-#             dims: timesteps
-
-#     transmission_tech:
-#         base_tech: transmission
-#         carrier_in: electricity
-#         carrier_out: electricity
-#         from: A
-#         to: B
-#         flow_cap_max: 8
+#   supply_tech:
+#     base_tech: supply
+#     carrier_out: electricity
+#     flow_cap_max: 10
+#     source_use_max:
+#       data: [10, 2]
+#       index: ["2020-01-01 00:00", "2020-01-01 01:00"]
+#       dims: timesteps
+#     cost_flow_cap:
+#       data: 2
+#       index: monetary
+#       dims: costs
+#
+#   storage_tech:
+#     base_tech: storage
+#     carrier_in: electricity
+#     carrier_out: electricity
+#     flow_cap_max: 6
+#     storage_cap_max: 7
+#     cost_storage_cap:
+#       data: 5
+#       index: monetary
+#       dims: costs
+#     cost_flow_out:
+#       data: 0.1
+#       index: monetary
+#       dims: costs
+#
+#   demand_tech:
+#     base_tech: demand
+#     carrier_in: electricity
+#     sink_use_equals:
+#       data: [4, 5]
+#       index: ["2020-01-01 00:00", "2020-01-01 01:00"]
+#       dims: timesteps
+#
+#   transmission_tech:
+#     base_tech: transmission
+#     carrier_in: electricity
+#     carrier_out: electricity
+#     from: A
+#     to: B
+#     flow_cap_max: 8
 #
 # nodes:
-#     A.techs: {supply_tech, storage_tech, demand_tech}
-#     B.techs:
-#         supply_tech:
-#             flow_cap_max: 8
-#         demand_tech:
+#   A.techs: {supply_tech, storage_tech, demand_tech}
+#   B.techs:
+#     supply_tech:
+#       flow_cap_max: 8
+#     demand_tech:
 # ```
 #
 # When this is used to initialise a Calliope model, it is processed into a set of data tables ([xarray.DataArray](https://docs.xarray.dev/en/stable/generated/xarray.DataArray.html)) internally:
@@ -94,56 +94,56 @@
 model_def = calliope.AttrDict.from_yaml_string(
     """
 techs:
-    supply_tech:
-        base_tech: supply
-        carrier_out: electricity
-        flow_cap_max: 10
-        source_use_max:
-            data: [10, 2]
-            index: ["2020-01-01 00:00", "2020-01-01 01:00"]
-            dims: timesteps
-        cost_flow_cap:
-            data: 2
-            index: monetary
-            dims: costs
-
-    storage_tech:
-        base_tech: storage
-        carrier_in: electricity
-        carrier_out: electricity
-        flow_cap_max: 6
-        storage_cap_max: 7
-        cost_storage_cap:
-            data: 5
-            index: monetary
-            dims: costs
-        cost_flow_out:
-            data: 0.1
-            index: monetary
-            dims: costs
-
-    demand_tech:
-        base_tech: demand
-        carrier_in: electricity
-        sink_use_equals:
-            data: [4, 5]
-            index: ["2020-01-01 00:00", "2020-01-01 01:00"]
-            dims: timesteps
-
-    transmission_tech:
-        base_tech: transmission
-        carrier_in: electricity
-        carrier_out: electricity
-        from: A
-        to: B
-        flow_cap_max: 8
+  supply_tech:
+    base_tech: supply
+    carrier_out: electricity
+    flow_cap_max: 10
+    source_use_max:
+      data: [10, 2]
+      index: ["2020-01-01 00:00", "2020-01-01 01:00"]
+      dims: timesteps
+    cost_flow_cap:
+      data: 2
+      index: monetary
+      dims: costs
+
+  storage_tech:
+    base_tech: storage
+    carrier_in: electricity
+    carrier_out: electricity
+    flow_cap_max: 6
+    storage_cap_max: 7
+    cost_storage_cap:
+      data: 5
+      index: monetary
+      dims: costs
+    cost_flow_out:
+      data: 0.1
+      index: monetary
+      dims: costs
+
+  demand_tech:
+    base_tech: demand
+    carrier_in: electricity
+    sink_use_equals:
+      data: [4, 5]
+      index: ["2020-01-01 00:00", "2020-01-01 01:00"]
+      dims: timesteps
+
+  transmission_tech:
+    base_tech: transmission
+    carrier_in: electricity
+    carrier_out: electricity
+    from: A
+    to: B
+    flow_cap_max: 8
 
 nodes:
-    A.techs: {supply_tech, storage_tech, demand_tech}
-    B.techs:
-        supply_tech:
-            flow_cap_max: 8
-        demand_tech:
+  A.techs: {supply_tech, storage_tech, demand_tech}
+  B.techs:
+    supply_tech:
+      flow_cap_max: 8
+    demand_tech:
 """
 )
 model_from_yaml = calliope.Model(model_def)
@@ -288,28 +288,28 @@
 #
 # ```yaml
 # data_sources:
-#     tech_data:
-#         source: outputs/loading_tabular_data/tech_data.csv
-#         rows: parameters
-#         columns: techs
-#     tech_node_data:
-#         source: outputs/loading_tabular_data/tech_node_data.csv
-#         rows: [techs, nodes, parameters]
-#     tech_timestep_data:
-#         source: outputs/loading_tabular_data/tech_timestep_data.csv
-#         rows: timesteps
-#         columns: [techs, parameters]
-#     tech_carrier_data:
-#         source: outputs/loading_tabular_data/tech_carrier_data.csv
-#         rows: [techs, parameters]
-#         add_dimensions:
-#             carriers: electricity
-#     tech_cost_data:
-#         source: outputs/loading_tabular_data/tech_cost_data.csv
-#         rows: parameters
-#         columns: techs
-#         add_dimensions:
-#             costs: monetary
+#   tech_data:
+#     source: outputs/loading_tabular_data/tech_data.csv
+#     rows: parameters
+#     columns: techs
+#   tech_node_data:
+#     source: outputs/loading_tabular_data/tech_node_data.csv
+#     rows: [techs, nodes, parameters]
+#   tech_timestep_data:
+#     source: outputs/loading_tabular_data/tech_timestep_data.csv
+#     rows: timesteps
+#     columns: [techs, parameters]
+#   tech_carrier_data:
+#     source: outputs/loading_tabular_data/tech_carrier_data.csv
+#     rows: [techs, parameters]
+#     add_dimensions:
+#       carriers: electricity
+#   tech_cost_data:
+#     source: outputs/loading_tabular_data/tech_cost_data.csv
+#     rows: parameters
+#     columns: techs
+#     add_dimensions:
+#       costs: monetary
 # ```
 #
 # When loading data sources, assigning techs to nodes is done automatically to some extent.
@@ -318,39 +318,39 @@
 #
 # ```yaml
 # nodes:
-#     A.techs: {supply_tech, storage_tech, demand_tech}
-#     B.techs: {supply_tech, demand_tech}
+#   A.techs: {supply_tech, storage_tech, demand_tech}
+#   B.techs: {supply_tech, demand_tech}
 # ```
 
 # %%
 model_def = calliope.AttrDict.from_yaml_string(
     """
 data_sources:
-    tech_data:
-        source: outputs/loading_tabular_data/tech_data.csv
-        rows: parameters
-        columns: techs
-    tech_node_data:
-        source: outputs/loading_tabular_data/tech_node_data.csv
-        rows: [techs, nodes, parameters]
-    tech_timestep_data:
-        source: outputs/loading_tabular_data/tech_timestep_data.csv
-        rows: timesteps
-        columns: [techs, parameters]
-    tech_carrier_data:
-        source: outputs/loading_tabular_data/tech_carrier_data.csv
-        rows: [techs, parameters]
-        add_dimensions:
-            carriers: electricity
-    tech_cost_data:
-        source: outputs/loading_tabular_data/tech_cost_data.csv
-        rows: parameters
-        columns: techs
-        add_dimensions:
-            costs: monetary
+  tech_data:
+    source: outputs/loading_tabular_data/tech_data.csv
+    rows: parameters
+    columns: techs
+  tech_node_data:
+    source: outputs/loading_tabular_data/tech_node_data.csv
+    rows: [techs, nodes, parameters]
+  tech_timestep_data:
+    source: outputs/loading_tabular_data/tech_timestep_data.csv
+    rows: timesteps
+    columns: [techs, parameters]
+  tech_carrier_data:
+    source: outputs/loading_tabular_data/tech_carrier_data.csv
+    rows: [techs, parameters]
+    add_dimensions:
+      carriers: electricity
+  tech_cost_data:
+    source: outputs/loading_tabular_data/tech_cost_data.csv
+    rows: parameters
+    columns: techs
+    add_dimensions:
+      costs: monetary
 nodes:
-    A.techs: {supply_tech, storage_tech, demand_tech}
-    B.techs: {supply_tech, demand_tech}
+  A.techs: {supply_tech, storage_tech, demand_tech}
+  B.techs: {supply_tech, demand_tech}
 """
 )
 model_from_data_sources = calliope.Model(model_def)
@@ -364,31 +364,31 @@
 model_def = calliope.AttrDict.from_yaml_string(
     """
 data_sources:
-    tech_data:
-        source: tech_data_df
-        rows: parameters
-        columns: techs
-    tech_node_data:
-        source: tech_node_data_df
-        rows: [techs, nodes, parameters]
-    tech_timestep_data:
-        source: tech_timestep_data_df
-        rows: timesteps
-        columns: [techs, parameters]
-    tech_carrier_data:
-        source: tech_carrier_data_df
-        rows: [techs, parameters]
-        add_dimensions:
-            carriers: electricity
-    tech_cost_data:
-        source: tech_cost_data_df
-        rows: parameters
-        columns: techs
-        add_dimensions:
-            costs: monetary
+  tech_data:
+    source: tech_data_df
+    rows: parameters
+    columns: techs
+  tech_node_data:
+    source: tech_node_data_df
+    rows: [techs, nodes, parameters]
+  tech_timestep_data:
+    source: tech_timestep_data_df
+    rows: timesteps
+    columns: [techs, parameters]
+  tech_carrier_data:
+    source: tech_carrier_data_df
+    rows: [techs, parameters]
+    add_dimensions:
+      carriers: electricity
+  tech_cost_data:
+    source: tech_cost_data_df
+    rows: parameters
+    columns: techs
+    add_dimensions:
+      costs: monetary
 nodes:
-    A.techs: {supply_tech, storage_tech, demand_tech}
-    B.techs: {supply_tech, demand_tech}
+  A.techs: {supply_tech, storage_tech, demand_tech}
+  B.techs: {supply_tech, demand_tech}
 """
 )
 model_from_data_sources = calliope.Model(
@@ -444,53 +444,53 @@
 #
 # ```yaml
 # data_sources:
-#     tech_timestep_data:
-#         source: outputs/loading_tabular_data/tech_timestep_data.csv
-#         rows: timesteps
-#         columns: [techs, parameters]
+#   tech_timestep_data:
+#     source: outputs/loading_tabular_data/tech_timestep_data.csv
+#     rows: timesteps
+#     columns: [techs, parameters]
 # techs:
-#     supply_tech:
-#         base_tech: supply
-#         carrier_out: electricity
-#         flow_cap_max: 10
-#         cost_flow_cap:
-#             data: 2
-#             index: monetary
-#             dims: costs
-#
-#     storage_tech:
-#         base_tech: storage
-#         carrier_in: electricity
-#         carrier_out: electricity
-#         flow_cap_max: 6
-#         storage_cap_max: 7
-#         cost_storage_cap:
-#             data: 5
-#             index: monetary
-#             dims: costs
-#         cost_flow_out:
-#             data: 0.1
-#             index: monetary
-#             dims: costs
-#
-#     demand_tech:
-#         base_tech: demand
-#         carrier_in: electricity
-#
-#     transmission_tech:
-#         base_tech: transmission
-#         carrier_in: electricity
-#         carrier_out: electricity
-#         from: A
-#         to: B
-#         flow_cap_max: 8
+#   supply_tech:
+#     base_tech: supply
+#     carrier_out: electricity
+#     flow_cap_max: 10
+#     cost_flow_cap:
+#       data: 2
+#       index: monetary
+#       dims: costs
+#
+#   storage_tech:
+#     base_tech: storage
+#     carrier_in: electricity
+#     carrier_out: electricity
+#     flow_cap_max: 6
+#     storage_cap_max: 7
+#     cost_storage_cap:
+#       data: 5
+#       index: monetary
+#       dims: costs
+#     cost_flow_out:
+#       data: 0.1
+#       index: monetary
+#       dims: costs
+#
+#   demand_tech:
+#     base_tech: demand
+#     carrier_in: electricity
+#
+#   transmission_tech:
+#     base_tech: transmission
+#     carrier_in: electricity
+#     carrier_out: electricity
+#     from: A
+#     to: B
+#     flow_cap_max: 8
 #
 # nodes:
-#     A.techs: {supply_tech, storage_tech, demand_tech}
-#     B.techs:
-#         supply_tech:
-#             flow_cap_max: 8
-#         demand_tech:
+#   A.techs: {supply_tech, storage_tech, demand_tech}
+#   B.techs:
+#     supply_tech:
+#       flow_cap_max: 8
+#     demand_tech:
 # ```
 #
 # For larger models, with lots of nodes and / or technologies, it is increasingly easier to store other data such as technology and node definitions in the tabular CSV format too.
@@ -499,47 +499,47 @@
 #
 # ```yaml
 # data_sources:
-#     tech_timestep_data:
-#         source: outputs/loading_tabular_data/tech_timestep_data.csv
-#         rows: timesteps
-#         columns: [techs, parameters]
-#     tech_cost_data:
-#         source: outputs/loading_tabular_data/tech_cost_data.csv
-#         rows: parameters
-#         columns: techs
-#         add_dimensions:
-#             costs: monetary
+#   tech_timestep_data:
+#     source: outputs/loading_tabular_data/tech_timestep_data.csv
+#     rows: timesteps
+#     columns: [techs, parameters]
+#   tech_cost_data:
+#     source: outputs/loading_tabular_data/tech_cost_data.csv
+#     rows: parameters
+#     columns: techs
+#     add_dimensions:
+#       costs: monetary
 # techs:
-#     supply_tech:
-#         base_tech: supply
-#         carrier_out: electricity
-#         flow_cap_max: 10
-#
-#     storage_tech:
-#         base_tech: storage
-#         carrier_in: electricity
-#         carrier_out: electricity
-#         flow_cap_max: 6
-#         storage_cap_max: 7
-#
-#     demand_tech:
-#         base_tech: demand
-#         carrier_in: electricity
-#
-#     transmission_tech:
-#         base_tech: transmission
-#         carrier_in: electricity
-#         carrier_out: electricity
-#         from: A
-#         to: B
-#         flow_cap_max: 8
+#   supply_tech:
+#     base_tech: supply
+#     carrier_out: electricity
+#     flow_cap_max: 10
+#
+#   storage_tech:
+#     base_tech: storage
+#     carrier_in: electricity
+#     carrier_out: electricity
+#     flow_cap_max: 6
+#     storage_cap_max: 7
+#
+#   demand_tech:
+#     base_tech: demand
+#     carrier_in: electricity
+#
+#   transmission_tech:
+#     base_tech: transmission
+#     carrier_in: electricity
+#     carrier_out: electricity
+#     from: A
+#     to: B
+#     flow_cap_max: 8
 #
 # nodes:
-#     A.techs: {supply_tech, storage_tech, demand_tech}
-#     B.techs:
-#         supply_tech:
-#             flow_cap_max: 8
-#         demand_tech:
+#   A.techs: {supply_tech, storage_tech, demand_tech}
+#   B.techs:
+#     supply_tech:
+#       flow_cap_max: 8
+#     demand_tech:
 # ```
 #
 # You can try these combinations - and others - yourself in this notebook and you will see that the result remains the same!
@@ -554,37 +554,37 @@
 #
 # ```yaml
 # data_sources:
-#     tech_data:
-#         source: outputs/loading_tabular_data/tech_data.csv
-#         rows: parameters
-#         columns: techs
-#     tech_node_data:
-#         source: outputs/loading_tabular_data/tech_node_data.csv
-#         rows: [techs, nodes, parameters]
-#     tech_timestep_data:
-#         source: outputs/loading_tabular_data/tech_timestep_data.csv
-#         rows: timesteps
-#         columns: [techs, parameters]
-#     tech_carrier_data:
-#         source: outputs/loading_tabular_data/tech_carrier_data.csv
-#         rows: [techs, parameters]
-#         add_dimensions:
-#             carriers: electricity
-#     tech_cost_data:
-#         source: outputs/loading_tabular_data/tech_cost_data.csv
-#         rows: parameters
-#         columns: techs
-#         add_dimensions:
-#             costs: monetary
+#   tech_data:
+#     source: outputs/loading_tabular_data/tech_data.csv
+#     rows: parameters
+#     columns: techs
+#   tech_node_data:
+#     source: outputs/loading_tabular_data/tech_node_data.csv
+#     rows: [techs, nodes, parameters]
+#   tech_timestep_data:
+#     source: outputs/loading_tabular_data/tech_timestep_data.csv
+#     rows: timesteps
+#     columns: [techs, parameters]
+#   tech_carrier_data:
+#     source: outputs/loading_tabular_data/tech_carrier_data.csv
+#     rows: [techs, parameters]
+#     add_dimensions:
+#       carriers: electricity
+#   tech_cost_data:
+#     source: outputs/loading_tabular_data/tech_cost_data.csv
+#     rows: parameters
+#     columns: techs
+#     add_dimensions:
+#       costs: monetary
 # techs:
-#     storage_tech:
-#         flow_cap_max: 5
-#     transmission_tech:
-#         flow_cap_max: 4
+#   storage_tech:
+#     flow_cap_max: 5
+#   transmission_tech:
+#     flow_cap_max: 4
 #
 # nodes:
-#     A.techs: {supply_tech, storage_tech, demand_tech}
-#     B.techs: {supply_tech, demand_tech}
+#   A.techs: {supply_tech, storage_tech, demand_tech}
+#   B.techs: {supply_tech, demand_tech}
 # ```
 #
 
@@ -592,37 +592,37 @@
 model_def = calliope.AttrDict.from_yaml_string(
     """
 data_sources:
-    tech_data:
-        source: outputs/loading_tabular_data/tech_data.csv
-        rows: parameters
-        columns: techs
-    tech_node_data:
-        source: outputs/loading_tabular_data/tech_node_data.csv
-        rows: [techs, nodes, parameters]
-    tech_timestep_data:
-        source: outputs/loading_tabular_data/tech_timestep_data.csv
-        rows: timesteps
-        columns: [techs, parameters]
-    tech_carrier_data:
-        source: outputs/loading_tabular_data/tech_carrier_data.csv
-        rows: [techs, parameters]
-        add_dimensions:
-            carriers: electricity
-    tech_cost_data:
-        source: outputs/loading_tabular_data/tech_cost_data.csv
-        rows: parameters
-        columns: techs
-        add_dimensions:
-            costs: monetary
+  tech_data:
+    source: outputs/loading_tabular_data/tech_data.csv
+    rows: parameters
+    columns: techs
+  tech_node_data:
+    source: outputs/loading_tabular_data/tech_node_data.csv
+    rows: [techs, nodes, parameters]
+  tech_timestep_data:
+    source: outputs/loading_tabular_data/tech_timestep_data.csv
+    rows: timesteps
+    columns: [techs, parameters]
+  tech_carrier_data:
+    source: outputs/loading_tabular_data/tech_carrier_data.csv
+    rows: [techs, parameters]
+    add_dimensions:
+      carriers: electricity
+  tech_cost_data:
+    source: outputs/loading_tabular_data/tech_cost_data.csv
+    rows: parameters
+    columns: techs
+    add_dimensions:
+      costs: monetary
 techs:
-    storage_tech:
-        flow_cap_max: 5
-    transmission_tech:
-        flow_cap_max: 4
+  storage_tech:
+    flow_cap_max: 5
+  transmission_tech:
+    flow_cap_max: 4
 
 nodes:
-    A.techs: {supply_tech, storage_tech, demand_tech}
-    B.techs: {supply_tech, demand_tech}
+  A.techs: {supply_tech, storage_tech, demand_tech}
+  B.techs: {supply_tech, demand_tech}
 """
 )
 model_from_data_sources_w_override = calliope.Model(model_def)
@@ -640,76 +640,75 @@
 #
 # ```yaml
 # data_sources:
-#     tech_data:
-#         source: outputs/loading_tabular_data/tech_data.csv
-#         rows: parameters
-#         columns: techs
-#     tech_node_data:
-#         source: outputs/loading_tabular_data/tech_node_data.csv
-#         rows: [techs, nodes, parameters]
-#     tech_timestep_data:
-#         source: outputs/loading_tabular_data/tech_timestep_data.csv
-#         rows: timesteps
-#         columns: [techs, parameters]
-#     tech_carrier_data:
-#         source: outputs/loading_tabular_data/tech_carrier_data.csv
-#         rows: [techs, parameters]
-#         add_dimensions:
-#             carriers: electricity
-#     tech_cost_data:
-#         source: outputs/loading_tabular_data/tech_cost_data.csv
-#         rows: parameters
-#         columns: techs
-#         add_dimensions:
-#             costs: monetary
+#   tech_data:
+#     source: outputs/loading_tabular_data/tech_data.csv
+#     rows: parameters
+#     columns: techs
+#   tech_node_data:
+#     source: outputs/loading_tabular_data/tech_node_data.csv
+#     rows: [techs, nodes, parameters]
+#   tech_timestep_data:
+#     source: outputs/loading_tabular_data/tech_timestep_data.csv
+#     rows: timesteps
+#     columns: [techs, parameters]
+#   tech_carrier_data:
+#     source: outputs/loading_tabular_data/tech_carrier_data.csv
+#     rows: [techs, parameters]
+#     add_dimensions:
+#       carriers: electricity
+#   tech_cost_data:
+#     source: outputs/loading_tabular_data/tech_cost_data.csv
+#     rows: parameters
+#     columns: techs
+#     add_dimensions:
+#       costs: monetary
 # techs:
-#     storage_tech:
-#         active: false
+#   storage_tech:
+#     active: false
 #
 # nodes:
-#     A.techs: {supply_tech, storage_tech, demand_tech}
-#     B.techs:
-#         supply_tech:
-#         demand_tech:
-#             active: false
+#   A.techs: {supply_tech, storage_tech, demand_tech}
+#   B.techs:
+#     supply_tech:
+#     demand_tech:
+#       active: false
 # ```
 
 # %%
 model_def = calliope.AttrDict.from_yaml_string(
     """
 data_sources:
-    tech_data:
-        source: outputs/loading_tabular_data/tech_data.csv
-        rows: parameters
-        columns: techs
-    tech_node_data:
-        source: outputs/loading_tabular_data/tech_node_data.csv
-        rows: [techs, nodes, parameters]
-    tech_timestep_data:
-        source: outputs/loading_tabular_data/tech_timestep_data.csv
-        rows: timesteps
-        columns: [techs, parameters]
-    tech_carrier_data:
-        source: outputs/loading_tabular_data/tech_carrier_data.csv
-        rows: [techs, parameters]
-        add_dimensions:
-            carriers: electricity
-    tech_cost_data:
-        source: outputs/loading_tabular_data/tech_cost_data.csv
-        rows: parameters
-        columns: techs
-        add_dimensions:
-            costs: monetary
+  tech_data:
+    source: outputs/loading_tabular_data/tech_data.csv
+    rows: parameters
+    columns: techs
+  tech_node_data:
+    source: outputs/loading_tabular_data/tech_node_data.csv
+    rows: [techs, nodes, parameters]
+  tech_timestep_data:
+    source: outputs/loading_tabular_data/tech_timestep_data.csv
+    rows: timesteps
+    columns: [techs, parameters]
+  tech_carrier_data:
+    source: outputs/loading_tabular_data/tech_carrier_data.csv
+    rows: [techs, parameters]
+    add_dimensions:
+      carriers: electricity
+  tech_cost_data:
+    source: outputs/loading_tabular_data/tech_cost_data.csv
+    rows: parameters
+    columns: techs
+    add_dimensions:
+      costs: monetary
 techs:
-    storage_tech:
-        active: false
-
+  storage_tech:
+    active: false
 nodes:
-    A.techs: {supply_tech, storage_tech, demand_tech}
-    B.techs:
-        supply_tech:
-        demand_tech:
-            active: false
+  A.techs: {supply_tech, storage_tech, demand_tech}
+  B.techs:
+    supply_tech:
+    demand_tech:
+      active: false
 """
 )
 model_from_data_sources_w_deactivations = calliope.Model(model_def)
diff --git a/docs/examples/national_scale/index.md b/docs/examples/national_scale/index.md
index 10a21a99..503ae1f3 100644
--- a/docs/examples/national_scale/index.md
+++ b/docs/examples/national_scale/index.md
@@ -112,7 +112,7 @@ The costs are more numerous as well, and include monetary costs for all relevant
 * carrier conversion capacity
 * variable operational and maintenance costs
 
-### :sparkles: Interlude: inheriting from technology groups
+### Interlude: inheriting from technology groups
 
 You will notice that the above technologies _inherit_ `cost_dim_setter`.
 Inheritance allows us to avoid excessive repetition in our model definition.
diff --git a/docs/examples/urban_scale/index.md b/docs/examples/urban_scale/index.md
index 5476e047..e083bf32 100644
--- a/docs/examples/urban_scale/index.md
+++ b/docs/examples/urban_scale/index.md
@@ -22,14 +22,13 @@ The diagram below gives an overview:
 We distinguish between model _configuration_ (the options provided to Calliope to do its work)
 and the model _definition_ (your representation of a physical system in YAML).
 
-
 ## Model configuration
 
 The model configuration file `model.yaml` is the place to tell Calliope about how to interpret the model definition and how to build and solve your model.
 It does not contain much data, but the scaffolding with which to construct and run your model.
 
-You will notice that we load a custom math file in `config.init`.
-You can find out more about this custom math [below](#sparkles-interlude-custom-math)
+You will notice that we load a "math" file in `config.init`.
+You can find out more about this user-defined math [below](#interlude-user-defined-math)
 
 ```yaml
 --8<-- "src/calliope/example_models/urban_scale/model.yaml:config"
@@ -128,7 +127,7 @@ The definition of this technology in the example model's configuration looks as
 --8<-- "src/calliope/example_models/urban_scale/model_config/techs.yaml:pv"
 ```
 
-### :sparkles: Interlude: inheriting from technology groups
+### Interlude: inheriting from technology groups
 
 You will notice that the above technologies _inherit_ `interest_rate_setter`.
 Inheritance allows us to avoid excessive repetition in our model definition.
@@ -188,20 +187,21 @@ Again, `chp` has the definitions for name, color, base_tech, and carrier_in/out.
 It has two carriers defined for its outflow.
 Note the parameter `heat_to_power_ratio`, which we set to 0.8.
 We will use this to create a link between the two output carriers.
-More importantly, it is a _custom parameter_ - Calliope itself does not define `heat_to_power_ratio`
-Therefore, for now, it will not have any effect - we need to introduce our own custom math.
+More importantly, it is a _custom parameter_ - Calliope does not come with `heat_to_power_ratio` pre-defined.
+Therefore, for now, it will not have any effect - we need to introduce our own math.
+
 In this case, we want to ensure that 0.8 units of heat are produced every time a unit of electricity is produced.
 Furthermore, while producing these units of energy - both electricity and heat -
 we want to ensure that gas consumption is only a function of electricity output.
 
-### :sparkles: Interlude: custom math
+### Interlude: user-defined math
 
-The base Calliope math does not have the capacity to handle our `chp` technology definition from above.
+The pre-defined Calliope math does not have the capacity to handle our `chp` technology definition from above.
 By default, setting two output carriers would mean that the choice is _between_ those technologies (e.g., a heat pump that can produce heat _or_ cooling).
-To ensure our `chp` will be constrained as we expect, we add custom math:
+To ensure our `chp` will be constrained as we expect, we add our own math:
 
 ```yaml
---8<-- "src/calliope/example_models/urban_scale/custom_math.yaml"
+--8<-- "src/calliope/example_models/urban_scale/additional_math.yaml"
 ```
 
 There are two things we have to do:
@@ -209,13 +209,14 @@ There are two things we have to do:
 1. Create a link between heat and electricity outflow.
 They both are produced simultaneously.
 We may prefer to have the heat output be set to a maximum equal to the `heat_to_power_ratio`, in which case the expression would become:
+
 ```yaml
 flow_out[carriers=electricity] * heat_to_power_ratio >= flow_out[carriers=heat]
 ```
 
 1. Unlink heat output from gas output.
-This requires updating a constraint that already exists in the base math.
-It is important that you understand the contents of the base math before you add custom math, to ensure you can override the math there appropriately.
+This requires updating a constraint that already exists in the pre-defined math.
+It is important that you understand the contents of the pre-defined math before you add your own, to ensure you can override the math there appropriately.
 
 ### Demand technologies
 
diff --git a/docs/hooks/add_notebooks.py b/docs/hooks/add_notebooks.py
index 1bb3c563..cc0f18cd 100644
--- a/docs/hooks/add_notebooks.py
+++ b/docs/hooks/add_notebooks.py
@@ -20,6 +20,8 @@ def on_files(files: list, config: dict, **kwargs):
     """Generate schema markdown reference sheets and attach them to the documentation."""
 
     for file in NOTEBOOK_DIR.glob("**/*.py"):
+        if any(part.startswith(".") for part in file.parts):
+            continue
         output_file = file.relative_to("docs").parent / file.stem / "notebook.ipynb"
         output_temp_file = Path(TEMPDIR.name) / output_file
         output_temp_file.parent.mkdir(parents=True, exist_ok=True)
diff --git a/docs/hooks/dummy_model/model.yaml b/docs/hooks/dummy_model/model.yaml
index 972e0fff..ffc6bc9d 100644
--- a/docs/hooks/dummy_model/model.yaml
+++ b/docs/hooks/dummy_model/model.yaml
@@ -2,7 +2,7 @@ overrides:
   storage_inter_cluster:
     config.init:
       name: inter-cluster storage
-      custom_math: ["storage_inter_cluster"]
+      add_math: ["storage_inter_cluster"]
       time_cluster: cluster_days.csv
 
 config.init.name: base
diff --git a/docs/hooks/generate_math_docs.py b/docs/hooks/generate_math_docs.py
index dd049cd5..b96b20b5 100644
--- a/docs/hooks/generate_math_docs.py
+++ b/docs/hooks/generate_math_docs.py
@@ -33,7 +33,7 @@ def on_files(files: list, config: dict, **kwargs):
         textwrap.dedent(
             """
         Complete base mathematical formulation for a Calliope model.
-        This math is _always_ applied but can be overridden using [custom math][introducing-custom-math-to-your-model].
+        This math is _always_ applied but can be overridden with pre-defined additional math or [your own math][adding-your-own-math-to-a-model].
         """
         ),
         base_model,
@@ -47,8 +47,8 @@ def on_files(files: list, config: dict, **kwargs):
             f"{override}.yaml",
             textwrap.dedent(
                 f"""
-            Inbuilt custom math to apply {custom_model.inputs.attrs['name']} math on top of the [base mathematical formulation][base-math].
-            This math is _only_ applied if referenced in the `config.init.custom_math` list as `{override}`.
+            Pre-defined additional math to apply {custom_model.inputs.attrs['name']} math on top of the [base mathematical formulation][base-math].
+            This math is _only_ applied if referenced in the `config.init.add_math` list as `{override}`.
             """
             ),
             custom_model,
@@ -93,10 +93,10 @@ def write_file(
     nav_reference = [
         idx
         for idx in config["nav"]
-        if isinstance(idx, dict) and set(idx.keys()) == {"Inbuilt math"}
+        if isinstance(idx, dict) and set(idx.keys()) == {"Pre-defined math"}
     ][0]
 
-    nav_reference["Inbuilt math"].append(output_file.as_posix())
+    nav_reference["Pre-defined math"].append(output_file.as_posix())
 
     math_doc = model.math_documentation.write(format="md")
     file_to_download = Path("..") / filename
@@ -118,7 +118,7 @@ def generate_base_math_model() -> calliope.Model:
         model_config (dict): Calliope model config.
 
     Returns:
-        calliope.Model: Base math model to use in generating custom math docs.
+        calliope.Model: Base math model to use in generating math docs.
     """
     model = calliope.Model(model_definition=MODEL_PATH)
     model.math_documentation.build()
@@ -128,7 +128,7 @@ def generate_base_math_model() -> calliope.Model:
 def generate_custom_math_model(
     base_model: calliope.Model, override: str
 ) -> calliope.Model:
-    """Generate model with documentation for a built-in custom math file, showing only the changes made
+    """Generate model with documentation for a pre-defined math file, showing only the changes made
     relative to the base math.
 
     Args:
@@ -143,7 +143,7 @@ def generate_custom_math_model(
 
 def _keep_only_changes(base_model: calliope.Model, model: calliope.Model) -> None:
     """Compare custom math model with base model and keep only the math strings that are
-    different between the two. Changes are made in-place in the custom math model docs
+    different between the two. Changes are made in-place in the custom math model docs.
 
     Args:
         base_model (calliope.Model): Calliope model with base math applied.
diff --git a/docs/hooks/generate_custom_math_examples.py b/docs/hooks/generate_math_examples.py
similarity index 82%
rename from docs/hooks/generate_custom_math_examples.py
rename to docs/hooks/generate_math_examples.py
index 2521c318..8e41ce52 100644
--- a/docs/hooks/generate_custom_math_examples.py
+++ b/docs/hooks/generate_math_examples.py
@@ -13,7 +13,10 @@
 
 TEMPDIR = tempfile.TemporaryDirectory()
 
-CUSTOM_MATH_PATH = Path("docs") / "custom_math" / "examples"
+CUSTOM_MATH_PATH = Path("docs") / "user_defined_math" / "examples"
+
+TOP_LEVEL_PAGE_NAME = "Defining your own math"
+EXAMPLES_PAGE_NAME = "Example additional math gallery"
 
 MD_EXAMPLE_STRING = """
 # {title}
@@ -39,19 +42,17 @@ def on_files(files: list, config: dict, **kwargs):
     top_level_nav_reference = [
         idx
         for idx in config["nav"]
-        if isinstance(idx, dict) and set(idx.keys()) == {"Custom math"}
+        if isinstance(idx, dict) and set(idx.keys()) == {TOP_LEVEL_PAGE_NAME}
     ][0]
     nav_reference = [
         idx
-        for idx in top_level_nav_reference["Custom math"]
-        if isinstance(idx, dict) and set(idx.keys()) == {"Example custom math gallery"}
+        for idx in top_level_nav_reference[TOP_LEVEL_PAGE_NAME]
+        if isinstance(idx, dict) and set(idx.keys()) == {EXAMPLES_PAGE_NAME}
     ][0]
 
     # Generate and attach the markdown files
     for file in sorted(CUSTOM_MATH_PATH.glob("*.yaml")):
-        files.append(
-            _process_file(file, config, nav_reference["Example custom math gallery"])
-        )
+        files.append(_process_file(file, config, nav_reference[EXAMPLES_PAGE_NAME]))
     return files
 
 
diff --git a/docs/hooks/generate_readable_schema.py b/docs/hooks/generate_readable_schema.py
index 4941fbb9..2a545964 100644
--- a/docs/hooks/generate_readable_schema.py
+++ b/docs/hooks/generate_readable_schema.py
@@ -41,7 +41,7 @@ def _schema_to_md(schema: dict, filename: str, config: dict) -> File:
     output_full_filepath.parent.mkdir(exist_ok=True, parents=True)
 
     parser = jsonschema2md.Parser()
-    parser.tab_size = 2
+    parser.tab_size = 4
 
     lines = parser.parse_schema(schema)
     lines = _customise_markdown(lines)
diff --git a/docs/img/math_syntax.svg b/docs/img/math_syntax.svg
new file mode 100644
index 00000000..cccc6172
--- /dev/null
+++ b/docs/img/math_syntax.svg
@@ -0,0 +1,1371 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   width="302.79623mm"
+   height="109.28297mm"
+   viewBox="2 2 302.79623 109.28296"
+   version="1.1"
+   id="svg1"
+   inkscape:version="1.3.2 (091e20e, 2023-11-25)"
+   sodipodi:docname="math_syntax.svg"
+   inkscape:export-filename="example_math.png"
+   inkscape:export-xdpi="96"
+   inkscape:export-ydpi="96"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <sodipodi:namedview
+     id="namedview1"
+     pagecolor="#ffffff"
+     bordercolor="#000000"
+     borderopacity="0.25"
+     inkscape:showpageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#d1d1d1"
+     inkscape:document-units="mm"
+     showguides="false"
+     inkscape:zoom="0.97352641"
+     inkscape:cx="437.58443"
+     inkscape:cy="222.90099"
+     inkscape:window-width="1440"
+     inkscape:window-height="1073"
+     inkscape:window-x="0"
+     inkscape:window-y="71"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="layer1"
+     inkscape:export-bgcolor="#ffffffff" />
+  <defs
+     id="defs1">
+    <marker
+       style="overflow:visible"
+       id="marker23"
+       refX="0"
+       refY="0"
+       orient="auto-start-reverse"
+       inkscape:stockid="Triangle arrow"
+       markerWidth="1"
+       markerHeight="1"
+       viewBox="0 0 1 1"
+       inkscape:isstock="true"
+       inkscape:collect="always"
+       preserveAspectRatio="xMidYMid">
+      <path
+         transform="scale(0.5)"
+         style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         id="path23" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="Triangle"
+       refX="0"
+       refY="0"
+       orient="auto-start-reverse"
+       inkscape:stockid="Triangle arrow"
+       markerWidth="1"
+       markerHeight="1"
+       viewBox="0 0 1 1"
+       inkscape:isstock="true"
+       inkscape:collect="always"
+       preserveAspectRatio="xMidYMid">
+      <path
+         transform="scale(0.5)"
+         style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         id="path135" />
+    </marker>
+    <inkscape:path-effect
+       effect="tiling"
+       id="path-effect2"
+       is_visible="true"
+       lpeversion="1"
+       unit="px"
+       seed="1;1"
+       lpesatellites=""
+       num_rows="5"
+       num_cols="4"
+       gapx="2"
+       gapy="2"
+       offset="0"
+       offset_type="false"
+       scale="0"
+       rotate="0"
+       mirrorrowsx="false"
+       mirrorrowsy="false"
+       mirrorcolsx="false"
+       mirrorcolsy="false"
+       mirrortrans="false"
+       shrink_interp="false"
+       split_items="false"
+       link_styles="false"
+       interpolate_scalex="false"
+       interpolate_scaley="true"
+       interpolate_rotatex="false"
+       interpolate_rotatey="true"
+       random_scale="false"
+       random_rotate="false"
+       random_gap_y="false"
+       random_gap_x="false"
+       transformorigin="" />
+    <inkscape:path-effect
+       effect="construct_grid"
+       id="path-effect1"
+       is_visible="true"
+       lpeversion="1"
+       nr_x="2"
+       nr_y="2" />
+    <inkscape:path-effect
+       effect="tiling"
+       id="path-effect2-3"
+       is_visible="true"
+       lpeversion="1"
+       unit="px"
+       seed="1;1"
+       lpesatellites=""
+       num_rows="5"
+       num_cols="4"
+       gapx="2"
+       gapy="2"
+       offset="0"
+       offset_type="false"
+       scale="0"
+       rotate="0"
+       mirrorrowsx="false"
+       mirrorrowsy="false"
+       mirrorcolsx="false"
+       mirrorcolsy="false"
+       mirrortrans="false"
+       shrink_interp="false"
+       split_items="false"
+       link_styles="false"
+       interpolate_scalex="false"
+       interpolate_scaley="true"
+       interpolate_rotatex="false"
+       interpolate_rotatey="true"
+       random_scale="false"
+       random_rotate="false"
+       random_gap_y="false"
+       random_gap_x="false"
+       transformorigin="" />
+    <marker
+       style="overflow:visible"
+       id="Triangle-5"
+       refX="0"
+       refY="0"
+       orient="auto-start-reverse"
+       inkscape:stockid="Triangle arrow"
+       markerWidth="1"
+       markerHeight="1"
+       viewBox="0 0 1 1"
+       inkscape:isstock="true"
+       inkscape:collect="always"
+       preserveAspectRatio="xMidYMid">
+      <path
+         transform="scale(0.5)"
+         style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         id="path135-7" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="marker23-9"
+       refX="0"
+       refY="0"
+       orient="auto-start-reverse"
+       inkscape:stockid="Triangle arrow"
+       markerWidth="1"
+       markerHeight="1"
+       viewBox="0 0 1 1"
+       inkscape:isstock="true"
+       inkscape:collect="always"
+       preserveAspectRatio="xMidYMid">
+      <path
+         transform="scale(0.5)"
+         style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         id="path23-0" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="marker23-5"
+       refX="0"
+       refY="0"
+       orient="auto-start-reverse"
+       inkscape:stockid="Triangle arrow"
+       markerWidth="1"
+       markerHeight="1"
+       viewBox="0 0 1 1"
+       inkscape:isstock="true"
+       inkscape:collect="always"
+       preserveAspectRatio="xMidYMid">
+      <path
+         transform="scale(0.5)"
+         style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         id="path23-2" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="marker23-5-9"
+       refX="0"
+       refY="0"
+       orient="auto-start-reverse"
+       inkscape:stockid="Triangle arrow"
+       markerWidth="1"
+       markerHeight="1"
+       viewBox="0 0 1 1"
+       inkscape:isstock="true"
+       inkscape:collect="always"
+       preserveAspectRatio="xMidYMid">
+      <path
+         transform="scale(0.5)"
+         style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         id="path23-2-6" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="marker23-5-3"
+       refX="0"
+       refY="0"
+       orient="auto-start-reverse"
+       inkscape:stockid="Triangle arrow"
+       markerWidth="1"
+       markerHeight="1"
+       viewBox="0 0 1 1"
+       inkscape:isstock="true"
+       inkscape:collect="always"
+       preserveAspectRatio="xMidYMid">
+      <path
+         transform="scale(0.5)"
+         style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+         d="M 5.77,0 -2.88,5 V -5 Z"
+         id="path23-2-3" />
+    </marker>
+  </defs>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-43.101666,-70.825926)">
+    <g
+       id="g7">
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2"
+         width="14.165628"
+         height="14.165628"
+         x="52.273975"
+         y="106.97897"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6"
+         width="14.165628"
+         height="14.165628"
+         x="67.560471"
+         y="106.97897"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2"
+         width="14.165628"
+         height="14.165628"
+         x="82.846962"
+         y="106.97897"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9"
+         width="14.165628"
+         height="14.165628"
+         x="98.133461"
+         y="106.97897"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-1"
+         width="14.165628"
+         height="14.165628"
+         x="52.273975"
+         y="122.20755"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-3"
+         width="14.165628"
+         height="14.165628"
+         x="67.560471"
+         y="122.20755"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-8"
+         width="14.165628"
+         height="14.165628"
+         x="82.846962"
+         y="122.20755"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-4"
+         width="14.165628"
+         height="14.165628"
+         x="98.133461"
+         y="122.20755"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-0"
+         width="14.165628"
+         height="14.165628"
+         x="52.273975"
+         y="137.43611"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-32"
+         width="14.165628"
+         height="14.165628"
+         x="67.560471"
+         y="137.43611"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-6"
+         width="14.165628"
+         height="14.165628"
+         x="82.846962"
+         y="137.43611"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-9"
+         width="14.165628"
+         height="14.165628"
+         x="98.133461"
+         y="137.43611"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-8"
+         width="14.165628"
+         height="14.165628"
+         x="52.273975"
+         y="152.66469"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-38"
+         width="14.165628"
+         height="14.165628"
+         x="67.560471"
+         y="152.66469"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-1"
+         width="14.165628"
+         height="14.165628"
+         x="82.846962"
+         y="152.66469"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-5"
+         width="14.165628"
+         height="14.165628"
+         x="98.133461"
+         y="152.66469"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-65"
+         width="14.165628"
+         height="14.165628"
+         x="52.273975"
+         y="167.89326"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-9"
+         width="14.165628"
+         height="14.165628"
+         x="67.560471"
+         y="167.89326"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-5"
+         width="14.165628"
+         height="14.165628"
+         x="82.846962"
+         y="167.89326"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-49"
+         width="14.165628"
+         height="14.165628"
+         x="98.133461"
+         y="167.89326"
+         sodipodi:type="rect" />
+    </g>
+    <g
+       id="g6"
+       transform="translate(1.5560659,0.08711243)">
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-88"
+         width="14.165628"
+         height="14.165628"
+         x="129.23419"
+         y="106.89186"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-35"
+         width="14.165628"
+         height="14.165628"
+         x="144.52069"
+         y="106.89186"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-2"
+         width="14.165628"
+         height="14.165628"
+         x="159.80717"
+         y="106.89186"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-2"
+         width="14.165628"
+         height="14.165628"
+         x="175.09367"
+         y="106.89186"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-1-6"
+         width="14.165628"
+         height="14.165628"
+         x="129.23419"
+         y="122.12044"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-3-6"
+         width="14.165628"
+         height="14.165628"
+         x="144.52069"
+         y="122.12044"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-8-7"
+         width="14.165628"
+         height="14.165628"
+         x="159.80717"
+         y="122.12044"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-4-8"
+         width="14.165628"
+         height="14.165628"
+         x="175.09367"
+         y="122.12044"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-0-4"
+         width="14.165628"
+         height="14.165628"
+         x="129.23419"
+         y="137.349"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-32-1"
+         width="14.165628"
+         height="14.165628"
+         x="144.52069"
+         y="137.349"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-6-7"
+         width="14.165628"
+         height="14.165628"
+         x="159.80717"
+         y="137.349"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-9-1"
+         width="14.165628"
+         height="14.165628"
+         x="175.09367"
+         y="137.349"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-8-8"
+         width="14.165628"
+         height="14.165628"
+         x="129.23419"
+         y="152.57758"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-38-7"
+         width="14.165628"
+         height="14.165628"
+         x="144.52069"
+         y="152.57758"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-1-8"
+         width="14.165628"
+         height="14.165628"
+         x="159.80717"
+         y="152.57758"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-5-7"
+         width="14.165628"
+         height="14.165628"
+         x="175.09367"
+         y="152.57758"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-65-7"
+         width="14.165628"
+         height="14.165628"
+         x="129.23419"
+         y="167.80615"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#22cb2c;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-9-4"
+         width="14.165628"
+         height="14.165628"
+         x="144.52069"
+         y="167.80615"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-5-4"
+         width="14.165628"
+         height="14.165628"
+         x="159.80717"
+         y="167.80615"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-49-9"
+         width="14.165628"
+         height="14.165628"
+         x="175.09367"
+         y="167.80615"
+         sodipodi:type="rect" />
+    </g>
+    <g
+       id="g5"
+       transform="translate(-1.0817235,0.49909973)">
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-88-5"
+         width="14.165628"
+         height="14.165628"
+         x="210.38826"
+         y="106.47987"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#ffa228;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-35-8"
+         width="14.165628"
+         height="14.165628"
+         x="225.67476"
+         y="106.47987"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#ffa228;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-2-2"
+         width="14.165628"
+         height="14.165628"
+         x="240.96124"
+         y="106.47987"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-2-9"
+         width="14.165628"
+         height="14.165628"
+         x="256.24774"
+         y="106.47987"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#ffa228;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-1-6-8"
+         width="14.165628"
+         height="14.165628"
+         x="210.38826"
+         y="121.70845"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#dd00dd;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-3-6-2"
+         width="14.165628"
+         height="14.165628"
+         x="225.67476"
+         y="121.70845"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-8-7-0"
+         width="14.165628"
+         height="14.165628"
+         x="240.96124"
+         y="121.70845"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-4-8-7"
+         width="14.165628"
+         height="14.165628"
+         x="256.24774"
+         y="121.70845"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#dd00dd;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-0-4-4"
+         width="14.165628"
+         height="14.165628"
+         x="210.38826"
+         y="136.93701"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-32-1-8"
+         width="14.165628"
+         height="14.165628"
+         x="225.67476"
+         y="136.93701"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#ffa228;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-6-7-5"
+         width="14.165628"
+         height="14.165628"
+         x="240.96124"
+         y="136.93701"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#dd00dd;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-9-1-8"
+         width="14.165628"
+         height="14.165628"
+         x="256.24774"
+         y="136.93701"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#dd00dd;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-8-8-3"
+         width="14.165628"
+         height="14.165628"
+         x="210.38826"
+         y="152.16559"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-38-7-0"
+         width="14.165628"
+         height="14.165628"
+         x="225.67476"
+         y="152.16559"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-1-8-6"
+         width="14.165628"
+         height="14.165628"
+         x="240.96124"
+         y="152.16559"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#dd00dd;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-5-7-2"
+         width="14.165628"
+         height="14.165628"
+         x="256.24774"
+         y="152.16559"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-65-7-2"
+         width="14.165628"
+         height="14.165628"
+         x="210.38826"
+         y="167.39417"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#ffa228;fill-opacity:1;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-9-4-5"
+         width="14.165628"
+         height="14.165628"
+         x="225.67476"
+         y="167.39417"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-5-4-2"
+         width="14.165628"
+         height="14.165628"
+         x="240.96124"
+         y="167.39417"
+         sodipodi:type="rect" />
+      <rect
+         style="fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-49-9-2"
+         width="14.165628"
+         height="14.165628"
+         x="256.24774"
+         y="167.39417"
+         sodipodi:type="rect" />
+    </g>
+    <g
+       id="g4"
+       transform="translate(0,0.71962738)">
+      <rect
+         style="opacity:1;fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-88-5-2"
+         width="14.165628"
+         height="14.165628"
+         x="287.82281"
+         y="106.25935"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#ffa228;fill-opacity:0.5;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-35-8-1"
+         width="14.165628"
+         height="14.165628"
+         x="303.10931"
+         y="106.25935"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#ffa228;fill-opacity:0.5;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-2-2-1"
+         width="14.165628"
+         height="14.165628"
+         x="318.39581"
+         y="106.25935"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-2-9-0"
+         width="14.165628"
+         height="14.165628"
+         x="333.68228"
+         y="106.25935"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#ffa228;fill-opacity:0.5;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-1-6-8-1"
+         width="14.165628"
+         height="14.165628"
+         x="287.82281"
+         y="121.48793"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#dd00dd;fill-opacity:0.5;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-3-6-2-8"
+         width="14.165628"
+         height="14.165628"
+         x="303.10931"
+         y="121.48793"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-8-7-0-7"
+         width="14.165628"
+         height="14.165628"
+         x="318.39581"
+         y="121.48793"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-4-8-7-6"
+         width="14.165628"
+         height="14.165628"
+         x="333.68228"
+         y="121.48793"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#dd00dd;fill-opacity:0.5;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-0-4-4-0"
+         width="14.165628"
+         height="14.165628"
+         x="287.82281"
+         y="136.71648"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-32-1-8-0"
+         width="14.165628"
+         height="14.165628"
+         x="303.10931"
+         y="136.71648"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#ffa228;fill-opacity:0.5;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-6-7-5-9"
+         width="14.165628"
+         height="14.165628"
+         x="318.39581"
+         y="136.71648"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#dd00dd;fill-opacity:0.5;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-9-1-8-5"
+         width="14.165628"
+         height="14.165628"
+         x="333.68228"
+         y="136.71648"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#dd00dd;fill-opacity:0.5;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-8-8-3-6"
+         width="14.165628"
+         height="14.165628"
+         x="287.82281"
+         y="151.94505"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-38-7-0-2"
+         width="14.165628"
+         height="14.165628"
+         x="303.10931"
+         y="151.94505"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-1-8-6-8"
+         width="14.165628"
+         height="14.165628"
+         x="318.39581"
+         y="151.94505"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#dd00dd;fill-opacity:0.5;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-5-7-2-5"
+         width="14.165628"
+         height="14.165628"
+         x="333.68228"
+         y="151.94505"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-65-7-2-9"
+         width="14.165628"
+         height="14.165628"
+         x="287.82281"
+         y="167.17363"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#ffa228;fill-opacity:0.5;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-6-9-4-5-4"
+         width="14.165628"
+         height="14.165628"
+         x="303.10931"
+         y="167.17363"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-2-5-4-2-3"
+         width="14.165628"
+         height="14.165628"
+         x="318.39581"
+         y="167.17363"
+         sodipodi:type="rect" />
+      <rect
+         style="opacity:1;fill:#f22d2d;fill-opacity:0;stroke:#000000;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         id="rect2-9-49-9-2-9"
+         width="14.165628"
+         height="14.165628"
+         x="333.68228"
+         y="167.17363"
+         sodipodi:type="rect" />
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;opacity:1;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="294.90149"
+         y="114.84905"
+         id="text2"><tspan
+           sodipodi:role="line"
+           id="tspan2"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1"
+           x="294.90149"
+           y="114.84905">NaN</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="325.47449"
+         y="130.07762"
+         id="text2-9"><tspan
+           sodipodi:role="line"
+           id="tspan2-7"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1"
+           x="325.47449"
+           y="130.07762">NaN</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="310.21072"
+         y="129.68903"
+         id="text2-9-4"><tspan
+           sodipodi:role="line"
+           id="tspan2-7-9"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+           x="310.21072"
+           y="129.68903">Expr1</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="310.12289"
+         y="114.48732"
+         id="text2-9-4-05"><tspan
+           sodipodi:role="line"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+           x="310.12289"
+           y="114.48732"
+           id="tspan3">Expr2</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="325.40939"
+         y="114.48732"
+         id="text2-9-4-05-7"><tspan
+           sodipodi:role="line"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+           x="325.40939"
+           y="114.48732"
+           id="tspan3-3">Expr2</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="294.8364"
+         y="129.7159"
+         id="text2-9-4-05-3"><tspan
+           sodipodi:role="line"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+           x="294.8364"
+           y="129.7159"
+           id="tspan3-7">Expr2</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="325.40939"
+         y="144.94444"
+         id="text2-9-4-05-37"><tspan
+           sodipodi:role="line"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+           x="325.40939"
+           y="144.94444"
+           id="tspan3-4">Expr2</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="310.12289"
+         y="175.4016"
+         id="text2-9-4-05-5"><tspan
+           sodipodi:role="line"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+           x="310.12289"
+           y="175.4016"
+           id="tspan3-6">Expr2</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="294.92422"
+         y="144.91757"
+         id="text2-9-4-6"><tspan
+           sodipodi:role="line"
+           id="tspan2-7-9-4"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+           x="294.92422"
+           y="144.91757">Expr1</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="294.92422"
+         y="160.14615"
+         id="text2-9-4-0"><tspan
+           sodipodi:role="line"
+           id="tspan2-7-9-0"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+           x="294.92422"
+           y="160.14615">Expr1</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="340.78369"
+         y="144.91757"
+         id="text2-9-4-9"><tspan
+           sodipodi:role="line"
+           id="tspan2-7-9-9"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+           x="340.78369"
+           y="144.91757">Expr1</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="340.78369"
+         y="160.14615"
+         id="text2-9-4-77"><tspan
+           sodipodi:role="line"
+           id="tspan2-7-9-3"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+           x="340.78369"
+           y="160.14615">Expr1</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="340.76096"
+         y="114.84905"
+         id="text2-0"><tspan
+           sodipodi:role="line"
+           id="tspan2-2"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1"
+           x="340.76096"
+           y="114.84905">NaN</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="310.18799"
+         y="145.30617"
+         id="text2-1"><tspan
+           sodipodi:role="line"
+           id="tspan2-5"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1"
+           x="310.18799"
+           y="145.30617">NaN</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="294.90149"
+         y="175.76332"
+         id="text2-6"><tspan
+           sodipodi:role="line"
+           id="tspan2-79"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1"
+           x="294.90149"
+           y="175.76332">NaN</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="310.18799"
+         y="160.53474"
+         id="text2-6-7"><tspan
+           sodipodi:role="line"
+           id="tspan2-79-8"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1"
+           x="310.18799"
+           y="160.53474">NaN</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="325.47449"
+         y="160.53474"
+         id="text2-6-71"><tspan
+           sodipodi:role="line"
+           id="tspan2-79-0"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1"
+           x="325.47449"
+           y="160.53474">NaN</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="340.76096"
+         y="130.07762"
+         id="text2-6-3"><tspan
+           sodipodi:role="line"
+           id="tspan2-79-5"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1"
+           x="340.76096"
+           y="130.07762">NaN</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="325.47449"
+         y="175.76332"
+         id="text2-12"><tspan
+           sodipodi:role="line"
+           id="tspan2-0"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1"
+           x="325.47449"
+           y="175.76332">NaN</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+         x="340.76096"
+         y="175.76332"
+         id="text2-97"><tspan
+           sodipodi:role="line"
+           id="tspan2-53"
+           style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:0.5;stroke:none;stroke-width:0.1"
+           x="340.76096"
+           y="175.76332">NaN</tspan></text>
+    </g>
+    <text
+       xml:space="preserve"
+       style="font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
+       x="51.9659"
+       y="75.426804"
+       id="text2-8"><tspan
+         sodipodi:role="line"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="51.9659"
+         y="75.426804"
+         id="tspan12">foreach: [dim1, dim2]</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
+       x="130.48225"
+       y="75.426804"
+       id="text2-8-9"><tspan
+         sodipodi:role="line"
+         id="tspan2-75-9"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="130.48225"
+         y="75.426804">foreach: [dim1, dim2]</tspan><tspan
+         sodipodi:role="line"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="130.48225"
+         y="80.09507"
+         id="tspan12-8">where: Condition</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
+       x="208.99852"
+       y="75.426804"
+       id="text2-8-0"><tspan
+         sodipodi:role="line"
+         id="tspan2-75-7"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="208.99852"
+         y="75.426804">foreach: [dim1, dim2]</tspan><tspan
+         sodipodi:role="line"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="208.99852"
+         y="80.09507"
+         id="tspan7-6">where: Condition</tspan><tspan
+         sodipodi:role="line"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="208.99852"
+         y="84.763344"
+         id="tspan8-8">equations:</tspan><tspan
+         sodipodi:role="line"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#d917b1;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="208.99852"
+         y="89.43161"
+         id="tspan9-3">  <tspan
+   style="font-size:3.70417px;fill:#dd00dd;fill-opacity:1;-inkscape-font-specification:Monospace;font-family:Monospace;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
+   id="tspan20">- where: SubCondition1</tspan></tspan><tspan
+         sodipodi:role="line"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#dd00dd;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="208.99852"
+         y="94.099884"
+         id="tspan10-8" /><tspan
+         sodipodi:role="line"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#ffa228;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="208.99852"
+         y="98.76815"
+         id="tspan11-6">  - where: SubCondition2</tspan><tspan
+         sodipodi:role="line"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#ffa228;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="208.99852"
+         y="103.43642"
+         id="tspan12-1" /></text>
+    <text
+       xml:space="preserve"
+       style="font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
+       x="287.51465"
+       y="75.426804"
+       id="text2-8-8"><tspan
+         sodipodi:role="line"
+         id="tspan2-75-97"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="287.51465"
+         y="75.426804">foreach: [dim1, dim2]</tspan><tspan
+         sodipodi:role="line"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="287.51465"
+         y="80.09507"
+         id="tspan7-5">where: Condition</tspan><tspan
+         sodipodi:role="line"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="287.51465"
+         y="84.763344"
+         id="tspan8-5">equations:</tspan><tspan
+         sodipodi:role="line"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#dd00dd;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="287.51465"
+         y="89.43161"
+         id="tspan9-2">  - where: SubCondition1</tspan><tspan
+         sodipodi:role="line"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#dd00dd;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="287.51465"
+         y="94.099884"
+         id="tspan10-4">    expression: Expression1</tspan><tspan
+         sodipodi:role="line"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#ffa228;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="287.51465"
+         y="98.76815"
+         id="tspan11-94">  - where: SubCondition2</tspan><tspan
+         sodipodi:role="line"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.70417px;font-family:Monospace;-inkscape-font-specification:Monospace;text-align:start;text-anchor:start;fill:#ffa228;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="287.51465"
+         y="103.43642"
+         id="tspan12-4">    expression: Expression2</tspan></text>
+    <path
+       style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.514464;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Triangle)"
+       d="M 52.802797,104.73763 H 110.13037"
+       id="path21"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.579529;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Triangle-5)"
+       d="m 50.125776,107.20405 v 72.74522"
+       id="path21-9"
+       sodipodi:nodetypes="cc" />
+    <text
+       xml:space="preserve"
+       style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+       x="57.939018"
+       y="102.51714"
+       id="text2-9-4-05-3-2"><tspan
+         sodipodi:role="line"
+         style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="57.939018"
+         y="102.51714"
+         id="tspan3-7-9">dim1</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+       x="186.127"
+       y="94.472031"
+       id="text2-9-4-05-3-2-2"><tspan
+         sodipodi:role="line"
+         style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="186.127"
+         y="94.472031"
+         id="tspan3-7-9-6">Condition is met</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+       x="-112.89868"
+       y="48.318005"
+       id="text2-9-4-05-3-2-4"
+       transform="rotate(-90)"><tspan
+         sodipodi:role="line"
+         style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="-112.89868"
+         y="48.318005"
+         id="tspan3-7-9-5">dim2</tspan></text>
+    <path
+       style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.499999;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker23)"
+       d="m 189.07105,95.365533 c -2.33598,3.820581 -7.69943,4.720507 -11.85541,5.170407 -7.40511,1.3083 -8.27873,4.09332 -8.27873,4.09332"
+       id="path22"
+       sodipodi:nodetypes="csc" />
+    <text
+       xml:space="preserve"
+       style="font-size:3.175px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+       x="228.7294"
+       y="104.27853"
+       id="text2-9-4-05-3-2-2-2"><tspan
+         sodipodi:role="line"
+         style="font-size:3.175px;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="228.7294"
+         y="104.27853"
+         id="tspan3-7-9-6-4">Condition <tspan
+   style="font-weight:bold;text-align:start;text-anchor:start"
+   id="tspan24">and</tspan> SubCondition2 is met</tspan></text>
+    <path
+       style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.3;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker23-5)"
+       d="m 228.12714,103.35847 c -1.05807,0.29353 -1.26778,1.10532 -1.30242,2.46422"
+       id="path22-9"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.3;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker23-5-3)"
+       d="m 273.56552,126.00492 c -0.92826,-3.35286 -1.6671,-0.26657 -3.36778,0.13822"
+       id="path22-9-4"
+       sodipodi:nodetypes="cc" />
+    <text
+       xml:space="preserve"
+       style="font-size:3.175px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+       x="-163.52322"
+       y="205.55814"
+       id="text2-9-4-05-3-2-2-2-1"
+       transform="rotate(-90)"><tspan
+         sodipodi:role="line"
+         style="font-size:3.175px;text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="-163.52322"
+         y="205.55814"
+         id="tspan3-7-9-6-4-5">Condition <tspan
+   style="font-weight:bold;text-align:start;text-anchor:start"
+   id="tspan24-8">and</tspan> SubCondition1 is met</tspan></text>
+    <path
+       style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.3;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker23-5-9)"
+       d="m 204.75554,163.96414 c 0.92826,3.35286 1.6671,0.26657 3.36778,-0.13822"
+       id="path22-9-0"
+       sodipodi:nodetypes="cc" />
+    <text
+       xml:space="preserve"
+       style="font-size:4.23333px;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1;stroke-dasharray:none;stroke-opacity:1"
+       x="141.59985"
+       y="97.855949"
+       id="text2-9-4-05-3-2-2-1"><tspan
+         sodipodi:role="line"
+         style="font-size:4.23333px;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.1"
+         x="141.59985"
+         y="97.855949"
+         id="tspan3-7-9-6-7">Condition is <tspan
+   style="fill:#f22d2d;fill-opacity:1"
+   id="tspan23">not</tspan> met</tspan></text>
+    <path
+       style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.499999;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker23-9)"
+       d="m 150.76789,98.459963 c -0.7845,2.790157 -4.37156,1.694597 -6.64462,1.696557 -6.53665,0.006 -6.54181,4.38281 -6.54181,4.38281"
+       id="path22-3"
+       sodipodi:nodetypes="csc" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.175px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:start;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.3;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1"
+       x="127.0239"
+       y="-276.89172"
+       id="text28"
+       transform="rotate(90)"><tspan
+         sodipodi:role="line"
+         style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.3"
+         x="127.0239"
+         y="-276.89172"
+         id="tspan29">Condition is met <tspan
+   style="font-weight:bold"
+   id="tspan31">but</tspan></tspan><tspan
+         sodipodi:role="line"
+         style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.3"
+         x="127.0239"
+         y="-272.92297"
+         id="tspan30">sub-conditions <tspan
+   style="fill:#f22d2d;fill-opacity:1"
+   id="tspan32">not</tspan> met</tspan></text>
+  </g>
+</svg>
diff --git a/docs/migrating.md b/docs/migrating.md
index 4f0eb2c3..a3d96e2f 100644
--- a/docs/migrating.md
+++ b/docs/migrating.md
@@ -467,7 +467,7 @@ If you prefer to keep your distance units in _metres_, set the configuration opt
 
 * To set the capacities in operate mode, you no longer need to set the `_max` constraints for your technologies (`area_use_max`, `flow_cap_max`, etc.); you can specify the decision variables as parameters directly.
 Therefore, you can define e.g. `flow_cap` as one of your technology parameters.
-This is because the operate mode custom math deactivates the decision variables with the same names, paving the way for the parameters to be used in the math formulation instead.
+This is because the additional math applied in operate mode deactivates the decision variables with the same names, paving the way for the parameters to be used in the math formulation instead.
 
     === "v0.6"
 
@@ -595,10 +595,10 @@ We have removed the `supply_plus` and `conversion_plus` base technology classes.
 Instead, `supply_plus` can be effectively represented by using `supply` as the base tech and setting [`include_storage: true`](#explicitly-triggering-milp-and-storage-decision-variablesconstraints) in the model definition.
 
 `conversion_plus` can be represented by using `conversion` as the base tech and using lists of carriers in `carrier_in` and/or `carrier_out`.
-To reimplement arbitrary links between carrier "tiers" (`in_2`, `out_2` etc.), you can define your own custom math, which is a simultaneously more powerful and more human-readable way of defining complex conversion technologies.
+To reimplement arbitrary links between carrier "tiers" (`in_2`, `out_2` etc.), you can [define your own math](user_defined_math/index.md), which is a simultaneously more powerful and more human-readable way of defining complex conversion technologies.
 
 !!! info "See also"
-    [Example of custom math to link carrier flows](examples/urban_scale/index.md#interlude-custom-math).
+    [Example of additional math to link carrier flows](examples/urban_scale/index.md#interlude-user-defined-math).
 
 ### `carrier` key
 
@@ -639,13 +639,13 @@ This is especially important now that you can [define different inflow/outflow c
 Carrier tiers were only used in `conversion_plus` technologies, yet led to a whole new model dimension.
 Additionally, `carrier_ratios` could be easily confused due to the complex nature of their application.
 With the [removal of the `conversion_plus` base class](#supply_plus-and-conversion_plus-technology-base-classes), we have simplified how multiple carriers in/out are defined.
-To achieve the same functionality as carrier tiers/ratios offered, you will need to apply your own custom math.
+To achieve the same functionality as carrier tiers/ratios offered, you will need to apply your own math.
 
-One form of carrier flow interactions is still possible without custom math.
+One form of carrier flow interactions _is_ still possible using only the pre-defined math.
 This is where there is a choice between inflow/outflow carriers instead of one carrier inflow/outflow _requiring_ the inflow/outflow of another carrier.
 You can do this with flow efficiencies indexed over carriers rather than using `carrier_ratios`.
 
-For instance, here's how you represent a reversible heat pump without custom math:
+For instance, here's how you represent a reversible heat pump without additional math:
 
 === "v0.6"
 
@@ -678,14 +678,14 @@ For instance, here's how you represent a reversible heat pump without custom mat
     ```
 
 !!! info "See also"
-    [Example of custom math to link carrier flows](examples/urban_scale/index.md#interlude-custom-math);
-    [Examples of complex CHP plant operating space custom math][chp-plants].
+    [Example of additional math to link carrier flows](examples/urban_scale/index.md#interlude-user-defined-math);
+    [Examples of complex CHP plant operating space math][chp-plants].
 
 ### Group constraints
 
-One driving reason to implement our [custom math](custom_math/index.md) syntax was to replace our "group constraints".
+One driving reason to implement our [own math syntax](user_defined_math/index.md) was to replace our "group constraints".
 These constraints were becoming more and more complex and it ultimately proved impossible to manage all the different ways users wanted to apply them.
-We have re-implemented all these constraints as tested custom math, which you can explore in our [example gallery](custom_math/examples/index.md).
+We have re-implemented all these constraints as tested additional math snippets, which you can explore in our [example gallery](user_defined_math/examples/index.md).
 
 ### Configuration options
 
@@ -694,7 +694,7 @@ Instead, data source filepaths should always be relative to the `model.yaml` fil
 * We have removed `run.relax_constraint` alongside [removing group constraints](#group-constraints).
 * We have removed `model.file_allowed`, which many users will not even know existed (it was a largely internal configuration option)!
 Instead, it is possible to index any parameter over the time dimension.
-It is up to you to ensure the math formulation is set up to handle this change, which may require [tweaking existing math](custom_math/customise.md#introducing-custom-math-to-your-model).
+It is up to you to ensure the math formulation is set up to handle this change, which may require [tweaking existing math](user_defined_math/customise.md#introducing-additional-math-to-your-model).
 * With the [removal of time clustering](#clustering), we have removed `model.random_seed` and `model.time` options.
 
 ### Plotting
@@ -731,7 +731,7 @@ We were previously using this in our internal clustering.
 
 On [removing `supply_plus`](#supply_plus-and-conversion_plus-technology-base-classes), we have opened up the option to have a storage "buffer" for any technology base class.
 This enables any flow into the technology to be stored across timesteps as it is in a `storage` technology.
-We have not yet enabled this for `demand` technologies, but [custom math](custom_math/index.md) could be readily added to enable it.
+We have not yet enabled this for `demand` technologies, but you could [add your own math](user_defined_math/index.md) to enable it.
 
 !!! warning
     Although our math should be set up to handle a storage buffer for a `conversion` or `transmission` technology, we do not have any direct tests to check possible edge cases.
@@ -954,7 +954,7 @@ nodes:
 
 !!! note
     1. Just defining new parameters is not enough to have an effect on the optimisation problem.
-    You also need [custom math](custom_math/index.md).
+    You also need to [define your own math](user_defined_math/index.md).
     2. Because we process your YAML files to create the `nodes` and `techs` dimensions you will find in your Calliope model, you cannot use `nodes`/`techs` as dimensions of indexed parameters under the `nodes` or `techs` keys.
     It _is_ possible to refer to `nodes` and `techs` as dimensions under the top-level `parameters` key.
 
@@ -974,7 +974,7 @@ Technically, you can now define all your data in tables (although we would still
 We have overhauled our internal mathematical formulation to remove the strong link to the Pyomo library.
 Now, all components of our internal math are defined in a readable YAML syntax that we have developed.
 
-You can add your own custom math to update the internal base math and to represent the physical system in ways we do not cover in our base math, or to apply new modelling methods and problem types (e.g., pathway or stochastic optimisation)!
+You can add your own math to update the pre-defined math and to represent the physical system in ways we do not cover in our base math, or to apply new modelling methods and problem types (e.g., pathway or stochastic optimisation)!
 
 !!! info "See also"
-    Our [inbuilt](math/index.md) and [custom](custom_math/index.md) math documentation.
+    Our [pre-defined](pre_defined_math/index.md) and [user-defined](user_defined_math/index.md) math documentation.
diff --git a/docs/math/index.md b/docs/pre_defined_math/index.md
similarity index 55%
rename from docs/math/index.md
rename to docs/pre_defined_math/index.md
index d40b7642..acfa659e 100644
--- a/docs/math/index.md
+++ b/docs/pre_defined_math/index.md
@@ -1,32 +1,32 @@
-# Inbuilt math
+# Pre-defined math
 
 As of Calliope version 0.7, the math used to build optimisation problems is stored in YAML files.
-The inbuilt math is a re-implementation of the formerly hardcoded math formulation in this YAML format.
+The pre-defined math is a re-implementation of the formerly hardcoded math formulation in this YAML format.
 
 The base math is _always_ applied to your model when you `build` the optimisation problem.
-In addition, there are other math formulation files built in which you can load into your model.
+We have also pre-defined some additional math, which you can _optionally_ load into your model.
 For instance, the [inter-cluster storage][inter-cluster-storage-math] math allows you to track storage levels in technologies more accurately when you are using timeseries clustering in your model.
 
-To load one of the built-in math files, you can reference it by name (_without_ the file extension) in your model configuration:
+To load optional, pre-defined math on top of the base math, you can reference it by name (_without_ the file extension) in your model configuration:
 
 ```yaml
 config:
   init:
-    custom_math: [storage_inter_cluster]
+    add_math: [storage_inter_cluster]
 ```
 
-When solving the model in a run mode other than `plan`, some built-in custom math will be applied automatically from a file of the same name (e.g., `spores` mode custom math is stored in [math/spores.yaml](https://github.com/calliope-project/calliope/blob/main/src/calliope/math/spores.yaml)).
+When solving the model in a run mode other than `plan`, some pre-defined additional math will be applied automatically from a file of the same name (e.g., `spores` mode math is stored in [math/spores.yaml](https://github.com/calliope-project/calliope/blob/main/src/calliope/math/spores.yaml)).
 
 !!! note
 
-    Custom math is applied in the order it appears in the `#!yaml config.init.custom_math` list.
-    By default, any run mode custom math will be applied as the final step.
-    If you want to apply your own custom math *after* the run mode custom math, you should add the name of the run mode explicitly to the `#!yaml config.init.custom_math` list, e.g., `#!yaml config.init.custom_math: [operate, my_custom_math.yaml]`.
+    Additional math is applied in the order it appears in the `#!yaml config.init.add_math` list.
+    By default, any run mode math will be applied as the final step.
+    If you want to apply your own math *after* the run mode math, you should add the name of the run mode explicitly to the `#!yaml config.init.add_math` list, e.g., `#!yaml config.init.add_math: [operate, user_defined_math.yaml]`.
 
 If you want to introduce new constraints, decision variables, or objectives, you can do so as part of the collection of YAML files describing your model.
-See the [custom math][custom-math-formulation] section for an in-depth guide to applying custom math.
+See the [user-defined math](../user_defined_math/index.md) section for an in-depth guide to applying your own math.
 
-The inbuilt math documentation can be explored in this section by selecting one of the options in the left-hand side table of contents.
+The pre-defined math can be explored in this section by selecting one of the options in the left-hand-side table of contents.
 
 ## A guide to math documentation
 
@@ -36,4 +36,4 @@ For each objective, constraint and global expression, a number of sub-conditions
 In the expressions, terms in **bold** font are decision variables and terms in *italic* font are parameters.
 A list of the decision variables is given at the end of this page.
 A detailed listing of parameters along with their units and default values is given in the [model definition reference sheet][model-definition-schema].
-Those parameters which are defined over time (`timesteps`) in the expressions can be defined by a user as a single, time invariant value, or as a timeseries that is [loaded from file or dataframe][loading-tabular-data-data_sources].
+Those parameters which are defined over time (`timesteps`) in the expressions can be defined by a user as a single, time invariant value, or as a timeseries that is [loaded from file or dataframe](../creating/data_sources.md).
diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md
index 9cf16829..7b51e022 100644
--- a/docs/troubleshooting.md
+++ b/docs/troubleshooting.md
@@ -110,33 +110,33 @@ These runs took place on the University of Cambridge high performance computing
 As can be seen, CBC is far superior to GLPK.
 If introducing binary constraints, although CBC is an improvement on GLPK, access to a commercial solver is preferable.
 
-**National scale example model size**
+`National scale example model size`
 
-- Variables : 420526 [Nneg: 219026, Free: 105140, Other: 96360]
-- Linear constraints : 586972 [Less: 315373, Greater: 10, Equal: 271589]
+:   * Variables : 420526 [Nneg: 219026, Free: 105140, Other: 96360]
+    * Linear constraints : 586972 [Less: 315373, Greater: 10, Equal: 271589]
 
-**MILP urban scale example model**
+`MILP urban scale example model`
 
-- Variables: 586996 [Nneg: 332913, Free: 78880, Binary: 2, General Integer: 8761, Other: 166440]
-- Linear constraints: 788502 [Less: 394226, Greater: 21, Equal: 394255]
+:   * Variables: 586996 [Nneg: 332913, Free: 78880, Binary: 2, General Integer: 8761, Other: 166440]
+    * Linear constraints: 788502 [Less: 394226, Greater: 21, Equal: 394255]
 
-**Solution time**
+`Solution time`
 
-| Solver             | National | Urban   |
-|--------------------|----------|---------|
-| GLPK               | 4:35:40  | >5hrs   |
-| CBC                | 0:04:45  | 0:52:13 |
-| Gurobi (1 thread)  | 0:02:08  | 0:03:21 |
-| CPLEX (1 thread)   | 0:04:55  | 0:05:56 |
-| Gurobi (4 thread)  | 0:02:27  | 0:03:08 |
-| CPLEX (4 thread)   | 0:02:16  | 0:03:26 |
+:   | Solver             | National | Urban   |
+    |--------------------|----------|---------|
+    | GLPK               | 4:35:40  | >5hrs   |
+    | CBC                | 0:04:45  | 0:52:13 |
+    | Gurobi (1 thread)  | 0:02:08  | 0:03:21 |
+    | CPLEX (1 thread)   | 0:04:55  | 0:05:56 |
+    | Gurobi (4 thread)  | 0:02:27  | 0:03:08 |
+    | CPLEX (4 thread)   | 0:02:16  | 0:03:26 |
 
 !!! info "See also"
-    [Specifying custom solver options][specifying-custom-solver-options]
+    [Specifying custom solver options](advanced/solver.md)
 
 ## Understanding infeasibility and numerical instability
 
-A good first step when faced with an infeasible model is often to remove constraints, in particular more complex constraints and any [custom math][custom-math-formulation] you may have applied.
+A good first step when faced with an infeasible model is often to remove constraints, in particular more complex constraints and any math you may have applied.
 For example, summing over nodes/carriers/techs in a constraint can easily introduce mutually exclusive requirements on capacities or output from specific technologies.
 Once a minimal model works, more complex constraints can be turned on again one after the other.
 
@@ -183,7 +183,6 @@ Particularly if your problem is large, you may not want to rebuild the backend t
 Instead you can interface directly with the backend using the `model.backend` functions, to update individual parameter values ([`model.backend.update_parameter`][calliope.backend.backend_model.BackendModel.update_parameter]) and variable bounds ([`model.backend.update_variable_bounds`][calliope.backend.backend_model.BackendModel.update_variable_bounds]).
 By rerunning the backend specifically, you can optimise your problem with these backend changes, without rebuilding the backend entirely: `model.solve(force=True)`.
 
-
 !!! note
     `model.solve(force=True)` will replace any existing results in `model.results`.
     You may want to save your model before doing this.
@@ -202,22 +201,22 @@ You can do so by setting `#!python calliope.set_log_verbosity("debug")` and then
 
 If you have a bit more Python experience, you can also consider accessing and working with loggers at different points in our internals using the `logging` package.
 
-- For input YAML and CSV file processing: `logging.getLogger("calliope.preprocess")`.
-- For processing of math syntax: `logging.getLogger("calliope.backend")`.
+* For input YAML and CSV file processing: `logging.getLogger("calliope.preprocess")`.
+* For processing of math syntax: `logging.getLogger("calliope.backend")`.
 
 For more examples of using loggers, see the [logging notebook example][calliope-logging-examples].
 
 ### Validating your math syntax
 
-You can do a (relatively) quick check of the validity of any custom math syntax in your model by running `#!python model.validate_math_strings(my_math_dict)`.
+You can do a (relatively) quick check of the validity of any math you want to add to your model by running `#!python model.validate_math_strings(my_math_dict)`.
 This will tell you if any of the syntax is malformed, although it cannot tell you if the model will build when set to the backend to generate an optimisation model.
 
 ### Inspecting private data structures
 
 There are private attributes of the Calliope `Model` object that you can access to gain additional insights into your model runs.
 
-- For all data in one place (i.e., the combination of `inputs` and `results`), the dataset `model._model_data`.
-- For the built backend objects (e.g., Pyomo objects) in an array format, the dataset `model.backend._dataset`.
+* For all data in one place (i.e., the combination of `inputs` and `results`), the dataset `model._model_data`.
+* For the built backend objects (e.g., Pyomo objects) in an array format, the dataset `model.backend._dataset`.
 
 !!! info
     If using Calliope in a Python session, we recommend reading up on the [Python debugger](https://docs.python.org/3/library/pdb.html) and making use of the [`%debug` magic](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-debug).
diff --git a/docs/custom_math/components.md b/docs/user_defined_math/components.md
similarity index 97%
rename from docs/custom_math/components.md
rename to docs/user_defined_math/components.md
index fa2a16aa..e7d43bb0 100644
--- a/docs/custom_math/components.md
+++ b/docs/user_defined_math/components.md
@@ -85,7 +85,7 @@ The equation expressions _must_ have comparison operators.
 
 ## Objectives
 
-With your constrained decision variables and a global expression that binds these variables to costs, you need an objective to minimise/maximise. The default built-in objective is `min_cost_optimisation` and looks as follows:
+With your constrained decision variables and a global expression that binds these variables to costs, you need an objective to minimise/maximise. The default, pre-defined objective is `min_cost_optimisation` and looks as follows:
 
 ```yaml
 objectives:
@@ -102,6 +102,5 @@ These expressions do _not_ have comparison operators.
 1. It can be deactivated so that it does not appear in the built optimisation problem by setting `active: false`.
 
 !!! warning
-
     You can only have one objective function activated in your math.
-    If you have defined multiple objective functions, you can deactivate unwanted ones using `active: false`, or you can set your top-level `where` string on each that leads to only one being valid for your particular problem.
\ No newline at end of file
+    If you have defined multiple objective functions, you can deactivate unwanted ones using `active: false`, or you can set your top-level `where` string on each that leads to only one being valid for your particular problem.
diff --git a/docs/custom_math/customise.md b/docs/user_defined_math/customise.md
similarity index 58%
rename from docs/custom_math/customise.md
rename to docs/user_defined_math/customise.md
index fa9745f6..6e1f7fd0 100644
--- a/docs/custom_math/customise.md
+++ b/docs/user_defined_math/customise.md
@@ -1,12 +1,12 @@
-# Introducing custom math to your model
+# Adding your own math to a model
 
-Once you understand the [math components](components.md) and the [formulation syntax](syntax.md), you'll be ready to introduce custom math to your model.
+Once you understand the [math components](components.md) and the [formulation syntax](syntax.md), you'll be ready to introduce your own math to a model.
 
-You can find examples of custom math that we have put together in the [custom math example gallery](examples/index.md).
+You can find examples of additional math that we have put together in our [math example gallery](examples/index.md).
 
 Whenever you introduce your own math, it will be applied on top of the [base math][base-math].
 Therefore, you can include base math overrides as well as add new math.
-For example, you may want to introduce a timeseries parameter to the built-in `storage_max` constraint to limit maximum storage capacity on a per-timestep basis:
+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:
 
 ```yaml
 storage_max:
@@ -14,27 +14,27 @@ storage_max:
     - expression: storage <= storage_cap * time_varying_parameter
 ```
 
-The other elements of the `storage_max` constraints have not changed (`foreach`, `where`, ...), so we do not need to define them again when overriding the custom math.
+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.
 
-When defining your model, you can reference any number of YAML files containing the custom math you want to add in `config.init`. The paths are relative to your main model configuration file:
+When defining your model, you can reference any number of YAML files containing the math you want to add in `config.init`. The paths are relative to your main model configuration file:
 
 ```yaml
 config:
   init:
-    custom_math: [my_new_math_1.yaml, my_new_math_2.yaml]
+    add_math: [my_new_math_1.yaml, my_new_math_2.yaml]
 ```
 
-You can also define a mixture of your custom math and the [inbuilt math](../math/index.md):
+You can also define a mixture of your own math and the [pre-defined math](../pre_defined_math/index.md):
 
 ```yaml
 config:
   init:
-    custom_math: [my_new_math_1.yaml, storage_inter_cluster, my_new_math_2.md]
+    add_math: [my_new_math_1.yaml, storage_inter_cluster, my_new_math_2.md]
 ```
 
 ## 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 [inbuilt math](../math/index.md) in this 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](../pre_defined_math/index.md) in this documentation).
 To write a LaTeX, reStructuredText, or Markdown file that includes only the math valid for your model:
 
 ```python
diff --git a/docs/custom_math/examples/annual_energy_balance.yaml b/docs/user_defined_math/examples/annual_energy_balance.yaml
similarity index 100%
rename from docs/custom_math/examples/annual_energy_balance.yaml
rename to docs/user_defined_math/examples/annual_energy_balance.yaml
diff --git a/docs/custom_math/examples/chp_htp.yaml b/docs/user_defined_math/examples/chp_htp.yaml
similarity index 100%
rename from docs/custom_math/examples/chp_htp.yaml
rename to docs/user_defined_math/examples/chp_htp.yaml
diff --git a/docs/custom_math/examples/demand_share_per_timestep_decision.yaml b/docs/user_defined_math/examples/demand_share_per_timestep_decision.yaml
similarity index 100%
rename from docs/custom_math/examples/demand_share_per_timestep_decision.yaml
rename to docs/user_defined_math/examples/demand_share_per_timestep_decision.yaml
diff --git a/docs/custom_math/examples/fuel_dist.yaml b/docs/user_defined_math/examples/fuel_dist.yaml
similarity index 100%
rename from docs/custom_math/examples/fuel_dist.yaml
rename to docs/user_defined_math/examples/fuel_dist.yaml
diff --git a/docs/custom_math/examples/index.md b/docs/user_defined_math/examples/index.md
similarity index 55%
rename from docs/custom_math/examples/index.md
rename to docs/user_defined_math/examples/index.md
index 1ce80498..1fd91a1c 100644
--- a/docs/custom_math/examples/index.md
+++ b/docs/user_defined_math/examples/index.md
@@ -1,11 +1,11 @@
-# Custom math example gallery
+# Additional math example gallery
 
-There are many use-cases for custom math that the Calliope team have already explored and applied.
+There are many use-cases for additional math that the Calliope team have already explored and applied.
 You can find a brief description of these use-cases in this section, along with the YAML math formulation that you can download and use yourself.
 
-If you have introduced custom math to your own model that you would like to be included in this gallery for others to use, we would welcome your contribution.
+If you have introduced your own math to a model that you would like to be included in this gallery for others to use, we would welcome your contribution.
 You can open an issue, or even better, a pull request, on our [GitHub repository](https://github.com/calliope-project/calliope).
 
 !!! note
 
-    These examples are only tested in a limited fashion and it is up to you, the user, to make sure you understand the maths when you apply them to your own model!
\ No newline at end of file
+    These examples are only tested in a limited fashion and it is up to you, the user, to make sure you understand the maths when you apply them to your own model!
diff --git a/docs/custom_math/examples/max_time_varying.yaml b/docs/user_defined_math/examples/max_time_varying.yaml
similarity index 100%
rename from docs/custom_math/examples/max_time_varying.yaml
rename to docs/user_defined_math/examples/max_time_varying.yaml
diff --git a/docs/custom_math/examples/net_import_share.yaml b/docs/user_defined_math/examples/net_import_share.yaml
similarity index 100%
rename from docs/custom_math/examples/net_import_share.yaml
rename to docs/user_defined_math/examples/net_import_share.yaml
diff --git a/docs/custom_math/examples/piecewise_linear_costs.yaml b/docs/user_defined_math/examples/piecewise_linear_costs.yaml
similarity index 100%
rename from docs/custom_math/examples/piecewise_linear_costs.yaml
rename to docs/user_defined_math/examples/piecewise_linear_costs.yaml
diff --git a/docs/custom_math/examples/piecewise_linear_efficiency.yaml b/docs/user_defined_math/examples/piecewise_linear_efficiency.yaml
similarity index 100%
rename from docs/custom_math/examples/piecewise_linear_efficiency.yaml
rename to docs/user_defined_math/examples/piecewise_linear_efficiency.yaml
diff --git a/docs/custom_math/examples/share_all_timesteps.yaml b/docs/user_defined_math/examples/share_all_timesteps.yaml
similarity index 100%
rename from docs/custom_math/examples/share_all_timesteps.yaml
rename to docs/user_defined_math/examples/share_all_timesteps.yaml
diff --git a/docs/custom_math/examples/share_per_timestep.yaml b/docs/user_defined_math/examples/share_per_timestep.yaml
similarity index 100%
rename from docs/custom_math/examples/share_per_timestep.yaml
rename to docs/user_defined_math/examples/share_per_timestep.yaml
diff --git a/docs/custom_math/examples/uptime_downtime_limits.yaml b/docs/user_defined_math/examples/uptime_downtime_limits.yaml
similarity index 100%
rename from docs/custom_math/examples/uptime_downtime_limits.yaml
rename to docs/user_defined_math/examples/uptime_downtime_limits.yaml
diff --git a/docs/custom_math/index.md b/docs/user_defined_math/index.md
similarity index 64%
rename from docs/custom_math/index.md
rename to docs/user_defined_math/index.md
index 059bc1ce..78724cc1 100644
--- a/docs/custom_math/index.md
+++ b/docs/user_defined_math/index.md
@@ -1,8 +1,8 @@
-# Custom math formulation
+# Defining your own math
 
 As of Calliope version 0.7, all of the internal math used to build optimisation problems is stored in YAML files.
 
-The same syntax used for the [inbuilt math](https://github.com/calliope-project/calliope/tree/main/src/calliope/math) can be used to define custom math.
+The same syntax used for the [pre-defined math](https://github.com/calliope-project/calliope/tree/main/src/calliope/math) can be used to define additional math.
 So, if you want to introduce new constraints, decision variables, or objectives, you can do so as part of the collection of YAML files describing your model.
 
 In brief, components of the math formulation are stored under named keys and contain information on:
@@ -19,17 +19,18 @@ Whenever we refer to a "math component" it could be a:
 * constraint (a way to limit the upper/lower bound of a decision variable using other decision variables/parameters/global expressions).
 * objective (the expression whose value you want to minimise/maximise in the optimisation).
 
-We recommend you first review the math formulation [syntax](syntax.md),
-then get an overview of the available [math components](components.md),
-before looking at how to add your own [custom math](customise.md) and browsing the [gallery of custom math examples](examples/index.md).
-A full reference for the allowed key-value pairs in your custom math YAML file is available in the [reference section of the documentation][math-formulation-schema].
+We recommend you first review the available [math components](components.md),
+then go into detail on the math formulation [syntax](syntax.md),
+before looking at how to [add your own math](customise.md) and browsing the [gallery of user-defined math examples](examples/index.md).
+A full reference for the allowed key-value pairs in a math YAML file is available in the [reference section of the documentation][math-formulation-schema].
 
 !!! note
 
-    Although we have tried to make a generalised syntax for all kinds of custom math, our focus was on building a system to implement Calliope's base math.
+    Although we have tried to make a generalised syntax for all kinds of user-defined math, our focus was on building a system to implement Calliope's base math.
     Therefore, we cannot guarantee that your math will be possible to implement.
 
 !!! warning
 
-    When writing custom math, remember that Calliope is a _linear_ modelling framework. It is possible that your desired math will create a nonlinear optimisation problem.
+    When writing your own math, remember that Calliope is a _linear_ modelling framework.
+    It is possible that your desired math will create a nonlinear optimisation problem.
     Usually, the solver will provide a clear error message when this is the case, although it may not be straightforward to pinpoint what part of your math is the culprit.
diff --git a/docs/custom_math/syntax.md b/docs/user_defined_math/syntax.md
similarity index 93%
rename from docs/custom_math/syntax.md
rename to docs/user_defined_math/syntax.md
index 7697bb66..b94c810b 100644
--- a/docs/custom_math/syntax.md
+++ b/docs/user_defined_math/syntax.md
@@ -1,17 +1,27 @@
 # Math syntax
 
-This page provides an overview of the syntax available to formulate math components. reference for the allowed key-value pairs in your custom math YAML file is available in the [reference section of the documentation][math-formulation-schema].
+This page provides an overview of the syntax available to formulate [math components](components.md).
+Using the math syntax, you can populate an N-dimensional matrix with math expressions.
+You use [foreach](#foreach-lists) to define the dimensions of the matrix.
+You use the top-level [where string](#where-strings) to subset the matrix to only elements of interest.
+You then populate the subset with any number of [equation expressions](#equations), each further subsetting the matrix.
+
+![Visual representation of math syntax application](../img/math_syntax.svg)
+
+!!! info "See also"
+
+    Reference for the allowed key-value pairs in your additional math YAML file is available in the [reference section of the documentation][math-formulation-schema].
 
 ## foreach lists
 
-If the math component is indexed over sets (e.g., `techs`, `nodes`, `timesteps`), then you need to define a `foreach` list of those sets.
+If the math component is indexed over dimensions (a.k.a. "sets" - e.g., `techs`, `nodes`, `timesteps`), then you need to define a `foreach` list of those sets.
 If the component is dimensionless, no `foreach` list needs to be defined.
 
 For example, `#!yaml foreach: [nodes, techs]` will build the component over all `nodes` and `techs` in the model.
 
-The available sets in Calliope are: `nodes`, `techs`, `carriers`, `costs`, `timesteps`.
+The available dimensions in Calliope are: `nodes`, `techs`, `carriers`, `costs`, `timesteps`.
 If using [time clustering](../advanced/time.md#time-clustering) and [inter-cluster storage math][inter-cluster-storage-math], there is also a `datesteps` set available.
-If you want to build over your own custom set, you will need to add it to the Calliope model dataset before building the optimisation problem, e.g. as a new indexed parameter.
+If you want to build over your own custom dimension, you will need to add it to the Calliope model dataset before building the optimisation problem, e.g. as a new indexed parameter.
 
 ## where strings
 
diff --git a/mkdocs.yml b/mkdocs.yml
index 6239f75a..34fad845 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -4,7 +4,7 @@ copyright: Copyright &copy; since 2013 <a href="https://github.com/calliope-proj
 repo_url: https://github.com/calliope-project/calliope
 hooks:
   - docs/hooks/changelog_highlight.py
-  - docs/hooks/generate_custom_math_examples.py
+  - docs/hooks/generate_math_examples.py
   - docs/hooks/generate_readable_schema.py
   - docs/hooks/generate_math_docs.py
   - docs/hooks/generate_plots.py
@@ -67,6 +67,7 @@ markdown_extensions:
   - attr_list
   - md_in_html
   - mkdocs-click
+  - def_list
   - toc:
       permalink: "¶"
       toc_depth: 3
@@ -105,15 +106,15 @@ nav:
   - Running a model: running.md
   - Analysing a model: analysing.md
   - Troubleshooting: troubleshooting.md
-  - Inbuilt math:
-    - index: math/index.md
-  - Custom math:
-    - custom_math/index.md
-    - custom_math/components.md
-    - custom_math/syntax.md
-    - custom_math/customise.md
-    - Example custom math gallery:
-      - custom_math/examples/index.md
+  - Pre-defined math:
+    - index: pre_defined_math/index.md
+  - Defining your own math:
+    - user_defined_math/index.md
+    - user_defined_math/components.md
+    - user_defined_math/syntax.md
+    - user_defined_math/customise.md
+    - Example additional math gallery:
+      - user_defined_math/examples/index.md
   - Examples and tutorials:
     - examples/index.md
     - National scale example model:
diff --git a/src/calliope/backend/backend_model.py b/src/calliope/backend/backend_model.py
index f44ff820..66911df0 100644
--- a/src/calliope/backend/backend_model.py
+++ b/src/calliope/backend/backend_model.py
@@ -205,7 +205,7 @@ def _check_inputs(self):
         )
 
     def _build(self) -> None:
-        self._add_run_mode_custom_math()
+        self._add_run_mode_math()
         # The order of adding components matters!
         # 1. Variables, 2. Global Expressions, 3. Constraints, 4. Objectives
         for components in [
@@ -224,22 +224,22 @@ def _build(self) -> None:
                 )
             LOGGER.info(f"Optimisation Model | {components} | Generated.")
 
-    def _add_run_mode_custom_math(self) -> None:
-        """If not given in the custom_math list, override model math with run mode math"""
+    def _add_run_mode_math(self) -> None:
+        """If not given in the add_math list, override model math with run mode math"""
 
         # FIXME: available modes should not be hardcoded here. They should come from a YAML schema.
         mode = self.inputs.attrs["config"].build.mode
-        custom_math = self.inputs.attrs["applied_custom_math"]
+        add_math = self.inputs.attrs["applied_additional_math"]
         not_run_mode = {"plan", "operate", "spores"}.difference([mode])
-        run_mode_mismatch = not_run_mode.intersection(custom_math)
+        run_mode_mismatch = not_run_mode.intersection(add_math)
         if run_mode_mismatch:
             exceptions.warn(
-                f"Running in {mode} mode, but run mode(s) {run_mode_mismatch} custom "
+                f"Running in {mode} mode, but run mode(s) {run_mode_mismatch} "
                 "math being loaded from file via the model configuration"
             )
 
-        if mode != "plan" and mode not in custom_math:
-            LOGGER.debug(f"Updating math formulation with {mode} mode custom math.")
+        if mode != "plan" and mode not in add_math:
+            LOGGER.debug(f"Updating math formulation with {mode} mode math.")
             filepath = importlib.resources.files("calliope") / "math" / f"{mode}.yaml"
             self.inputs.math.union(AttrDict.from_yaml(filepath), allow_override=True)
 
diff --git a/src/calliope/backend/helper_functions.py b/src/calliope/backend/helper_functions.py
index eff22403..a2fdc80c 100644
--- a/src/calliope/backend/helper_functions.py
+++ b/src/calliope/backend/helper_functions.py
@@ -189,27 +189,27 @@ def as_array(
             With:
             ```yaml
             node_groups:
-                foo:
-                    available_area: 1
+              foo:
+                available_area: 1
             tech_groups:
-                bar:
-                    flow_cap_max: 1
-                baz:
-                    inherits: bar
-                    flow_out_eff: 0.5
+              bar:
+                flow_cap_max: 1
+              baz:
+                inherits: bar
+                flow_out_eff: 0.5
             nodes:
-                node_1:
-                    inherits: foo
-                    techs: {tech_1, tech_2}
-                node_2:
-                    techs: {tech_1, tech_2}
+              node_1:
+                inherits: foo
+                techs: {tech_1, tech_2}
+              node_2:
+                techs: {tech_1, tech_2}
             techs:
-                tech_1:
-                    ...
-                    inherits: bar
-                tech_2:
-                    ...
-                    inherits: baz
+              tech_1:
+                ...
+                inherits: bar
+              tech_2:
+                ...
+                inherits: baz
             ```
 
             >>> inheritance(nodes=foo)
@@ -345,14 +345,14 @@ def as_array(
 
             ```yaml
             nodes:
-                node1:
-                    techs:
-                        tech1:
-                        tech3:
-                node2:
-                    techs:
-                        tech2:
-                        tech3:
+              node1:
+                techs:
+                  tech1:
+                  tech3:
+              node2:
+                techs:
+                  tech2:
+                  tech3:
             ```
             Then:
             ```
diff --git a/src/calliope/backend/latex_backend_model.py b/src/calliope/backend/latex_backend_model.py
index 4c8bc708..98cf804d 100644
--- a/src/calliope/backend/latex_backend_model.py
+++ b/src/calliope/backend/latex_backend_model.py
@@ -120,30 +120,33 @@ class LatexBackendModel(backend_model.BackendModelGenerator):
     # in \text. Curly braces need separating otherwise jinja2 gets confused.
     LATEX_EQUATION_ELEMENT = textwrap.dedent(
         r"""
-        \begin{array}{r}
+        \begin{array}{l}
         {% if sets is defined and sets %}
             \forall{}
         {% for set in sets %}
             \text{ {{set|removesuffix("s")}} }\negthickspace \in \negthickspace\text{ {{set + "," if not loop.last else set }} }
         {% endfor %}
-            \\
+        {% if (where is defined and where and where != "") or (sense is defined and sense) %}
+            \!\!,\\
+        {% else %}
+            \!\!:\\[2em]
+        {% endif %}
         {% endif %}
         {% if sense is defined and sense %}
-            {{sense}}
+            {{sense}}\!\!:\\[2em]
         {% endif %}
         {% if where is defined and where and where != "" %}
-            \text{if } {{where}}
+            \text{if } {{where}}\!\!:\\[2em]
         {% endif %}
-        \end{array}
-        \begin{cases}
         {% for equation in equations %}
-            {{equation["expression"]}}&\quad
         {% if "where" in equation and equation.where != "" %}
-            \text{if } {{equation["where"]}}
+            \quad \text{if } {{equation["where"]}}\!\!:\\
+            \qquad {{equation["expression"]}}\\[2em]
+        {% else %}
+            \quad {{equation["expression"]}}\\
         {% endif %}
-            \\
         {% endfor %}
-        \end{cases}
+        \end{array}
         """
     )
     RST_DOC = textwrap.dedent(
diff --git a/src/calliope/config/config_schema.yaml b/src/calliope/config/config_schema.yaml
index 7df6dd29..193d5783 100644
--- a/src/calliope/config/config_schema.yaml
+++ b/src/calliope/config/config_schema.yaml
@@ -35,7 +35,9 @@ properties:
                   type: string
                   description: ISO8601 format datetime strings of the form `YYYY-mm-dd HH:MM:SS` (e.g, '2005-01', '2005-01-01', '2005-01-01 00:00', ...)
             default: null
-            description: Subset of timesteps as a two-element list giving the inclusive range
+            description: >-
+              Subset of timesteps as an two-element list giving the **inclusive** range.
+              For example, ['2005-01', '2005-04'] will create a time subset from '2005-01-01 00:00:00' to '2005-04-31 23:59:59'.
           time_resample:
             type: ["null", string]
             default: null
@@ -49,16 +51,16 @@ properties:
             type: string
             default: "ISO8601"
             description: Timestamp format of all time series data when read from file. "ISO8601" means "%Y-%m-%d %H:%M:%S".
-          custom_math:
+          add_math:
             type: array
             default: []
-            description: List of references to files which contain custom mathematical formulations.
+            description: List of references to files which contain additional mathematical formulations to be applied on top of the base math.
             uniqueItems: true
             items:
               type: string
               description: >
-                If referring to an inbuilt Calliope custom math file (see documentation for available files), do not append the reference with ".yaml".
-                If referring to your own custom math file, ensure the file type is given as a suffix (".yaml" or ".yml").
+                If referring to an pre-defined Calliope math file (see documentation for available files), do not append the reference with ".yaml".
+                If referring to your own math file, ensure the file type is given as a suffix (".yaml" or ".yml").
                 Relative paths will be assumed to be relative to the model definition file given when creating a calliope Model (`calliope.Model(model_definition=...)`)
           distance_unit:
             type: string
@@ -93,7 +95,7 @@ properties:
           objective:
             type: string
             default: min_cost_optimisation
-            description: Name of internal objective function to use, from those defined in the base math and custom math (if applied).
+            description: Name of internal objective function to use, from those defined in the pre-defined math and any applied additional math.
           operate_window:
             type: string
             description: >-
diff --git a/src/calliope/example_models/urban_scale/custom_math.yaml b/src/calliope/example_models/urban_scale/additional_math.yaml
similarity index 100%
rename from src/calliope/example_models/urban_scale/custom_math.yaml
rename to src/calliope/example_models/urban_scale/additional_math.yaml
diff --git a/src/calliope/example_models/urban_scale/model.yaml b/src/calliope/example_models/urban_scale/model.yaml
index 850fbbb2..23b74b04 100644
--- a/src/calliope/example_models/urban_scale/model.yaml
+++ b/src/calliope/example_models/urban_scale/model.yaml
@@ -13,7 +13,7 @@ config:
     calliope_version: 0.7.0
     # Time series data path - can either be a path relative to this file, or an absolute path
     time_subset: ["2005-07-01", "2005-07-02"] # Subset of timesteps
-    custom_math: ["custom_math.yaml"]
+    add_math: ["additional_math.yaml"]
 
   build:
     mode: plan # Choices: plan, operate
diff --git a/src/calliope/model.py b/src/calliope/model.py
index 298a651d..ee3e16d2 100644
--- a/src/calliope/model.py
+++ b/src/calliope/model.py
@@ -223,7 +223,7 @@ def _init_from_model_def_dict(
 
         self._add_observed_dict("config", model_config)
 
-        math = self._add_math(init_config["custom_math"])
+        math = self._add_math(init_config["add_math"])
         self._add_observed_dict("math", math)
 
         self._model_data.attrs["name"] = init_config["name"]
@@ -314,17 +314,17 @@ def _add_observed_dict(self, name: str, dict_to_add: Optional[dict] = None) -> N
         self._model_data.attrs[name] = dict_to_add
         setattr(self, name, dict_to_add)
 
-    def _add_math(self, custom_math: list) -> AttrDict:
+    def _add_math(self, add_math: list) -> AttrDict:
         """
-        Load the base math and optionally override with custom math from a list of references to custom math files.
+        Load the base math and optionally override with additional math from a list of references to math files.
 
         Args:
-            custom_math (list):
-                List of references to files containing custom mathematical formulations that will be merged with the base formulation.
+            add_math (list):
+                List of references to files containing mathematical formulations that will be merged with the base formulation.
 
         Raises:
             exceptions.ModelError:
-                Referenced internal custom math files or user-defined custom math files must exist.
+                Referenced pre-defined math files or user-defined math files must exist.
 
         Returns:
             AttrDict: Dictionary of math (constraints, variables, objectives, and global expressions).
@@ -334,7 +334,7 @@ def _add_math(self, custom_math: list) -> AttrDict:
 
         file_errors = []
 
-        for filename in custom_math:
+        for filename in add_math:
             if not f"{filename}".endswith((".yaml", ".yml")):
                 yaml_filepath = math_dir / f"{filename}.yaml"
             else:
@@ -349,9 +349,9 @@ def _add_math(self, custom_math: list) -> AttrDict:
             base_math.union(override_dict, allow_override=True)
         if file_errors:
             raise exceptions.ModelError(
-                f"Attempted to load custom math that does not exist: {file_errors}"
+                f"Attempted to load additional math that does not exist: {file_errors}"
             )
-        self._model_data.attrs["applied_custom_math"] = custom_math
+        self._model_data.attrs["applied_additional_math"] = add_math
         return base_math
 
     def build(self, force: bool = False, **kwargs) -> None:
@@ -537,7 +537,7 @@ def info(self) -> str:
     def validate_math_strings(self, math_dict: dict) -> None:
         """Validate that `expression` and `where` strings of a dictionary containing string mathematical formulations can be successfully parsed.
 
-        This function can be used to test custom math before attempting to build the optimisation problem.
+        This function can be used to test user-defined math before attempting to build the optimisation problem.
 
         NOTE: strings are not checked for evaluation validity. Evaluation issues will be raised only on calling `Model.build()`.
 


Test patch:
diff --git a/tests/test_backend_latex_backend.py b/tests/test_backend_latex_backend.py
index e8b696de..ab8f60fd 100644
--- a/tests/test_backend_latex_backend.py
+++ b/tests/test_backend_latex_backend.py
@@ -280,12 +280,9 @@ def test_create_obj_list(self, dummy_latex_backend_model):
                     foobar
                     \begin{equation}
                     \resizebox{\ifdim\width>\linewidth0.95\linewidth\else\width\fi}{!}{$
-                    \begin{array}{r}
+                    \begin{array}{l}
+                        \quad 1 + 2\\
                     \end{array}
-                    \begin{cases}
-                        1 + 2&\quad
-                        \\
-                    \end{cases}
                     $}
                     \end{equation}
                     \end{document}"""
@@ -307,12 +304,9 @@ def test_create_obj_list(self, dummy_latex_backend_model):
                     .. container:: scrolling-wrapper
 
                         .. math::
-                            \begin{array}{r}
+                            \begin{array}{l}
+                                \quad 1 + 2\\
                             \end{array}
-                            \begin{cases}
-                                1 + 2&\quad
-                                \\
-                            \end{cases}
                     """
                 ),
             ),
@@ -327,12 +321,9 @@ def test_create_obj_list(self, dummy_latex_backend_model):
                     foobar
 
                     $$
-                    \begin{array}{r}
+                    \begin{array}{l}
+                        \quad 1 + 2\\
                     \end{array}
-                    \begin{cases}
-                        1 + 2&\quad
-                        \\
-                    \end{cases}
                     $$
                     """
                 ),
@@ -354,46 +345,38 @@ def test_generate_math_doc(self, dummy_model_data, format, expected):
                 {"sets": ["nodes", "techs"]},
                 textwrap.dedent(
                     r"""
-                \begin{array}{r}
+                \begin{array}{l}
                     \forall{}
                     \text{ node }\negthickspace \in \negthickspace\text{ nodes, }
                     \text{ tech }\negthickspace \in \negthickspace\text{ techs }
-                    \\
-                \end{array}
-                \begin{cases}
-                \end{cases}"""
+                    \!\!:\\[2em]
+                \end{array}"""
                 ),
             ),
             (
                 {"sense": r"\min{}"},
                 textwrap.dedent(
                     r"""
-                \begin{array}{r}
-                    \min{}
-                \end{array}
-                \begin{cases}
-                \end{cases}"""
+                \begin{array}{l}
+                    \min{}\!\!:\\[2em]
+                \end{array}"""
                 ),
             ),
             (
                 {"where": r"foo \land bar"},
                 textwrap.dedent(
                     r"""
-                \begin{array}{r}
-                    \text{if } foo \land bar
-                \end{array}
-                \begin{cases}
-                \end{cases}"""
+                \begin{array}{l}
+                    \text{if } foo \land bar\!\!:\\[2em]
+                \end{array}"""
                 ),
             ),
             (
                 {"where": r""},
                 textwrap.dedent(
                     r"""
-                \begin{array}{r}
-                \end{array}
-                \begin{cases}
-                \end{cases}"""
+                \begin{array}{l}
+                \end{array}"""
                 ),
             ),
             (
@@ -405,15 +388,11 @@ def test_generate_math_doc(self, dummy_model_data, format, expected):
                 },
                 textwrap.dedent(
                     r"""
-                \begin{array}{r}
-                \end{array}
-                \begin{cases}
-                    foo&\quad
-                    \text{if } bar
-                    \\
-                    foo + 1&\quad
-                    \\
-                \end{cases}"""
+                \begin{array}{l}
+                    \quad \text{if } bar\!\!:\\
+                    \qquad foo\\[2em]
+                    \quad foo + 1\\
+                \end{array}"""
                 ),
             ),
         ],
diff --git a/tests/test_backend_pyomo.py b/tests/test_backend_pyomo.py
index a594747f..15c75edc 100755
--- a/tests/test_backend_pyomo.py
+++ b/tests/test_backend_pyomo.py
@@ -1622,7 +1622,7 @@ def cluster_model(
         override = {
             "config.init.time_subset": ["2005-01-01", "2005-01-04"],
             "config.init.time_cluster": "data_sources/cluster_days.csv",
-            "config.init.custom_math": (
+            "config.init.add_math": (
                 ["storage_inter_cluster"] if storage_inter_cluster else []
             ),
             "config.build.cyclic_storage": cyclic,
@@ -1746,46 +1746,43 @@ def test_add_run_mode_custom_math(self, caplog, mode):
         base_math.union(mode_custom_math, allow_override=True)
 
         backend = PyomoBackendModel(m.inputs, mode=mode)
-        backend._add_run_mode_custom_math()
+        backend._add_run_mode_math()
 
-        assert f"Updating math formulation with {mode} mode custom math." in caplog.text
+        assert f"Updating math formulation with {mode} mode math." in caplog.text
 
         assert m.math != base_math
         assert backend.inputs.attrs["math"].as_dict() == base_math.as_dict()
 
     def test_add_run_mode_custom_math_before_build(self, caplog, temp_path):
-        """A user can override the run mode custom math by including it directly in the custom math string"""
+        """A user can override the run mode math by including it directly in the additional math list"""
         caplog.set_level(logging.DEBUG)
         custom_math = AttrDict({"variables": {"flow_cap": {"active": True}}})
         file_path = temp_path.join("custom-math.yaml")
         custom_math.to_yaml(file_path)
 
         m = build_model(
-            {"config.init.custom_math": ["operate", str(file_path)]},
+            {"config.init.add_math": ["operate", str(file_path)]},
             "simple_supply,two_hours,investment_costs",
         )
         backend = PyomoBackendModel(m.inputs, mode="operate")
-        backend._add_run_mode_custom_math()
+        backend._add_run_mode_math()
 
-        # We set operate mode explicitly in our custom math so it won't be added again
-        assert (
-            "Updating math formulation with operate mode custom math."
-            not in caplog.text
-        )
+        # We set operate mode explicitly in our additional math so it won't be added again
+        assert "Updating math formulation with operate mode math." not in caplog.text
 
-        # operate mode set it to false, then our custom math set it back to active
+        # operate mode set it to false, then our math set it back to active
         assert m.math.variables.flow_cap.active
-        # operate mode set it to false and our custom math did not override that
+        # operate mode set it to false and our math did not override that
         assert not m.math.variables.storage_cap.active
 
     def test_run_mode_mismatch(self):
         m = build_model(
-            {"config.init.custom_math": ["operate"]},
+            {"config.init.add_math": ["operate"]},
             "simple_supply,two_hours,investment_costs",
         )
         backend = PyomoBackendModel(m.inputs)
         with pytest.warns(exceptions.ModelWarning) as excinfo:
-            backend._add_run_mode_custom_math()
+            backend._add_run_mode_math()
 
         assert check_error_or_warning(
             excinfo, "Running in plan mode, but run mode(s) {'operate'}"
diff --git a/tests/test_core_model.py b/tests/test_core_model.py
index 33706bdf..a92b6d3f 100644
--- a/tests/test_core_model.py
+++ b/tests/test_core_model.py
@@ -64,11 +64,11 @@ def test_add_observed_dict_not_dict(self, national_scale_example):
         )
 
 
-class TestCustomMath:
+class TestAddMath:
     @pytest.fixture
     def storage_inter_cluster(self):
         return build_model(
-            {"config.init.custom_math": ["storage_inter_cluster"]},
+            {"config.init.add_math": ["storage_inter_cluster"]},
             "simple_supply,two_hours,investment_costs",
         )
 
@@ -97,11 +97,12 @@ def test_variable_bound(self, storage_inter_cluster):
     def test_allowed_internal_constraint(self, override, expected):
         with pytest.raises(calliope.exceptions.ModelError) as excinfo:
             build_model(
-                {"config.init.custom_math": override},
+                {"config.init.add_math": override},
                 "simple_supply,two_hours,investment_costs",
             )
         assert check_error_or_warning(
-            excinfo, f"Attempted to load custom math that does not exist: {expected}"
+            excinfo,
+            f"Attempted to load additional math that does not exist: {expected}",
         )
 
     def test_internal_override_from_yaml(self, temp_path):
@@ -118,7 +119,7 @@ def test_internal_override_from_yaml(self, temp_path):
         )
         new_constraint.to_yaml(temp_path.join("custom-math.yaml"))
         m = build_model(
-            {"config.init.custom_math": [str(temp_path.join("custom-math.yaml"))]},
+            {"config.init.add_math": [str(temp_path.join("custom-math.yaml"))]},
             "simple_supply,two_hours,investment_costs",
         )
         assert "constraint_name" in m.math["constraints"].keys()
@@ -134,7 +135,7 @@ def test_override_existing_internal_constraint(self, temp_path, simple_supply):
         )
         new_constraint.to_yaml(file_path)
         m = build_model(
-            {"config.init.custom_math": [str(file_path)]},
+            {"config.init.add_math": [str(file_path)]},
             "simple_supply,two_hours,investment_costs",
         )
         base = simple_supply.math["constraints"][
@@ -163,8 +164,7 @@ def test_override_order(self, temp_path, simple_supply):
             to_add.append(str(filepath))
 
         m = build_model(
-            {"config.init.custom_math": to_add},
-            "simple_supply,two_hours,investment_costs",
+            {"config.init.add_math": to_add}, "simple_supply,two_hours,investment_costs"
         )
 
         base = simple_supply.math["constraints"][
@@ -187,7 +187,7 @@ def test_override_existing_internal_constraint_merge(
         file_path = temp_path.join("custom-math.yaml")
         new_constraint.to_yaml(file_path)
         m = build_model(
-            {"config.init.custom_math": ["storage_inter_cluster", str(file_path)]},
+            {"config.init.add_math": ["storage_inter_cluster", str(file_path)]},
             "simple_supply,two_hours,investment_costs",
         )
         base = simple_supply.math["variables"]["storage"]
@@ -223,7 +223,7 @@ def test_base_math(self, caplog, simple_supply):
             ),
         ],
     )
-    def test_custom_math(self, caplog, simple_supply, equation, where):
+    def test_add_math(self, caplog, simple_supply, equation, where):
         with caplog.at_level(logging.INFO, logger=LOGGER):
             simple_supply.validate_math_strings(
                 {
@@ -244,7 +244,7 @@ def test_custom_math(self, caplog, simple_supply, equation, where):
         ],
     )
     @pytest.mark.parametrize("both_fail", [True, False])
-    def test_custom_math_fails(self, simple_supply, component_dict, both_fail):
+    def test_add_math_fails(self, simple_supply, component_dict, both_fail):
         math_dict = {"constraints": {"foo": component_dict}}
         errors_to_check = [
             "math string parsing (marker indicates where parsing stopped, which might not be the root cause of the issue; sorry...)",
@@ -263,7 +263,7 @@ def test_custom_math_fails(self, simple_supply, component_dict, both_fail):
         assert check_error_or_warning(excinfo, errors_to_check)
 
     @pytest.mark.parametrize("eq_string", ["1 = 1", "1 ==\n1[a]"])
-    def test_custom_math_fails_marker_correct_position(self, simple_supply, eq_string):
+    def test_add_math_fails_marker_correct_position(self, simple_supply, eq_string):
         math_dict = {"constraints": {"foo": {"equations": [{"expression": eq_string}]}}}
 
         with pytest.raises(calliope.exceptions.ModelError) as excinfo:
diff --git a/tests/test_math.py b/tests/test_math.py
index f14fa565..b8ded6c9 100644
--- a/tests/test_math.py
+++ b/tests/test_math.py
@@ -161,8 +161,10 @@ def test_all_math_registered(self, base_math):
 class CustomMathExamples(ABC):
     TEST_REGISTER: set = set()
 
-    #: source of all custom math files
-    CUSTOM_MATH_DIR = CALLIOPE_DIR.parent.parent / "docs" / "custom_math" / "examples"
+    #: source of all example math files
+    CUSTOM_MATH_DIR = (
+        CALLIOPE_DIR.parent.parent / "docs" / "user_defined_math" / "examples"
+    )
 
     @property
     @abstractmethod
@@ -203,7 +205,7 @@ def _build_and_compare(
                 overrides = {}
 
             model = build_test_model(
-                {"config.init.custom_math": [abs_filepath], **overrides}, scenario
+                {"config.init.add_math": [abs_filepath], **overrides}, scenario
             )
 
             compare_lps(model, custom_math, filename)