Skip to content

Commit f720fd1

Browse files
committed
Improved documentation
1 parent 4477e31 commit f720fd1

4 files changed

Lines changed: 55 additions & 58 deletions

File tree

docs/inputs/toml.rst

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ a whole.
9090
Carbon market
9191
-------------
9292

93-
This section contains the settings related to the modelling of the carbon market. If omitted, it defaults to not
94-
including the carbon market in the simulation.
93+
This section contains the settings related to the modelling of the carbon market.
94+
If omitted, it defaults to not including the carbon market in the simulation.
9595

9696
Example
9797

@@ -106,46 +106,49 @@ Example
106106
`time_framework` from the main section. If not given or an empty list, then the
107107
carbon market feature is disabled. Defaults to an empty list.
108108

109-
*method*
110-
Method used to equilibrate the carbon market. Available options are `fitting` and `bisection`, however this can be expanded with the `@register_carbon_budget_method` hook in `muse.carbon_budget`.
111-
112-
The market-clearing algorithm iterates over the sectors until the market reaches an equilibrium in the foresight period (the period next to the one analysed).
113-
This is represented by a stable variation of a commodity demand (or price) between iterations below a defined tolerance.
114-
The market-clearing algorithm samples a user-defined set of carbon prices.
115-
116-
When the `fitting` method is selected, this command builds a regression model of the emissions as a function of the carbon price.
117-
It applies to a pool of emissions for all the modelled regions. Therefore, the estimated carbon price applies to all the modelled regions.
118-
The regression model, the method calculates iteratively the emissions at pre-defined carbon price sample values.
119-
The emissions-carbon price couples are used to used to fit the emission-carbon price relation, is uer-defined (ie. linear or exponential fitter).
120-
The new carbon price is estimated as a root of the regression model estimated at the value of the emission equal to the user-defined emission cap in the foresight period.
121-
Alongside the selection of the method, the user can define a `sample_size`, representing the magnitude of the sample for the fitter.
122-
123-
When the `bisection` method is selected, this command applies a bisection method to solve the carbon market.
124-
Similarly to the `fitting` method, the carbon market includes a pool of all the modelled regions. The obtained carbon price
125-
applies to all the regions, as above. This method solves as a typical bisection algorithm.
126-
It is coded independently to use the internal signature of the `register_carbon_budget_method`. The algorithm aims to find a root of
127-
the function emissions-carbon price, as for the carbon price at which the carbon budget is met.
128-
The algorithm iteratively modifies the carbon price and estimates the corresponding emissions.
129-
It stops when the convergence or stop criteria are met.
130-
This happens for example either when the carbon budget or the maximum number of iterations are met.
131-
Alongside the selection of the method, the user can define a `sample_size`, representing the number of iterations of the bisection method.
132-
133109
*commodities*
134110
Commodities that make up the carbon market. Defaults to an empty list.
135111

136112
*control_undershoot*
137-
Whether to control carbon budget undershoots. This parameter allows for carbon tax credit from one year to be passed to the next in the case of less carbon being emitted than the budget. Defaults to True.
113+
Whether to control carbon budget undershoots. This parameter allows for carbon tax credit from one year to be passed to the next in the case of less carbon being emitted than the budget. Defaults to False.
138114

139115
*control_overshoot*
140-
Whether to control carbon budget overshoots. If the amount of carbon emitted is above the carbon budget, this parameter specifies whether this deficit is carried over to the next year. Defaults to True.
116+
Whether to control carbon budget overshoots. If the amount of carbon emitted is above the carbon budget, this parameter specifies whether this deficit is carried over to the next year. Defaults to False.
117+
118+
*method*
119+
Method used to equilibrate the carbon market. Available options are `fitting` and `bisection`, however this can be expanded with the `@register_carbon_budget_method` hook in `muse.carbon_budget`.
120+
121+
These methods solve the market with a number of different carbon prices, aiming to find the carbon price at which emissions (pooled across all regions) are equal to the carbon budget.
122+
The obtained carbon price applies to all regions.
123+
124+
The `fitting` method samples a number of different carbon prices to build a regression model (linear or exponential) of emissions as a function of carbon price.
125+
This regression model is then used to estimate the carbon price at which the carbon budget is met.
126+
127+
The `bisection` method uses an iterative approach to settle on a carbon price.
128+
Starting with a lower and upper-bound carbon price, it iteratively halves this price interval until the carbon budget is met to within a user-defined tolerance, or until the maximum number of iterations is reached.
129+
Generally, this method is more robust for markets with a complex, nonlinear relationship between emissions and carbon price, but may be slower to converge than the `fitting` method.
130+
131+
Defaults to `bisection`.
141132

142133
*method_options*
143-
Additional options for the specific carbon method. In particular, the `refine_price` activate a sanity check on the adjusted carbon price.
144-
The sanity check applies an upper limit on the carbon price obtained from the algorithm (either `fitting` or `bisection`), called
145-
`price_too_high_threshold`, a user-defined threshold based on heuristics on the values of the carbon price, reflecting typical historical trends.
134+
Additional options for the specified carbon method.
135+
136+
Parameters for the `bisection` method:
137+
138+
- `max_iterations`: maximum number of iterations. Defaults to 5
139+
- `tolerance`: tolerance for convergence. E.g. 0.1 means that the algorithm will terminate when emissions are within 10% of the carbon budget. Defaults to 0.1.
140+
- `early_termination_count`: number of iterations with no change in the carbon price before the algorithm will terminate. Defaults to 5.
141+
142+
Parameters for the `fitting` method:
143+
144+
- `fitter`: the regression model used to approximate model emissions. Predefined options are `linear` (default) and `exponential`. Further options can be defined using the `@register_carbon_budget_fitter` hook in `muse.carbon_budget`.
145+
- `sample_size`: number of price samples used. Defaults to 5.
146+
147+
Shared parameters:
148+
149+
- `refine_price`: If True, applies an upper limit on the carbon price. Defaults to False.
150+
- `price_too_high_threshold`: upper limit on the carbon price. Defaults to 5.
146151

147-
*fitter*
148-
`fitter` specifies the regression model fit. The regression approximates the model emissions. Predefined options are `linear` and `exponential`. Further options can be defined using the `@register_carbon_budget_fitter` hook in `muse.carbon_budget`.
149152

150153
------------------
151154
Global input files

src/muse/carbon_budget.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ def fitting(
7373
equilibrium: Callable[[xr.Dataset], FindEquilibriumResults],
7474
carbon_budget: xr.DataArray,
7575
commodities: list,
76-
sample_size: int = 5,
7776
refine_price: bool = False,
7877
price_too_high_threshold: float = 10,
78+
sample_size: int = 5,
7979
fitter: str = "linear",
8080
) -> float:
8181
"""Used to solve the carbon market.
@@ -90,10 +90,10 @@ def fitting(
9090
equilibrium: Method for searching market equilibrium
9191
carbon_budget: limit on emissions
9292
commodities: list of commodities to limit (ie. emissions)
93-
sample_size: sample size for fitting
9493
refine_price: Boolean to decide on whether carbon price should be capped, with
9594
the upper bound given by price_too_high_threshold
9695
price_too_high_threshold: threshold on carbon price
96+
sample_size: sample size for fitting
9797
fitter: method to fit emissions with carbon price
9898
9999
Returns:
@@ -292,9 +292,9 @@ def bisection(
292292
equilibrium: Callable[[xr.Dataset], FindEquilibriumResults],
293293
carbon_budget: xr.DataArray,
294294
commodities: list,
295-
sample_size: int = 5,
296295
refine_price: bool = False,
297296
price_too_high_threshold: float = 10,
297+
max_iterations: int = 5,
298298
tolerance: float = 0.1,
299299
early_termination_count: int = 5,
300300
) -> float:
@@ -312,10 +312,10 @@ def bisection(
312312
equilibrium: Method for searching market equilibrium
313313
carbon_budget: DataArray with the carbon budget
314314
commodities: List of carbon-related commodities
315-
sample_size: Maximum number of iterations for bisection
316315
refine_price: Boolean to decide on whether carbon price should be capped, with
317316
the upper bound given by price_too_high_threshold
318317
price_too_high_threshold: Upper limit for carbon price
318+
max_iterations: Maximum number of iterations for bisection
319319
tolerance: Maximum permitted deviation of emissions from the budget
320320
early_termination_count: Will terminate the loop early if the last n solutions
321321
are the same
@@ -340,7 +340,7 @@ def bisection(
340340

341341
# Bisection loop
342342
emissions_cache = EmissionsCache(market, equilibrium, commodities)
343-
for _ in range(sample_size): # maximum number of iterations before terminating
343+
for _ in range(max_iterations): # maximum number of iterations before terminating
344344
# Cap prices between 0.01 and price_too_high_threshold
345345
if refine_price:
346346
ub_price = min(ub_price, price_too_high_threshold)

src/muse/data/default_settings.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ excluded_commodities = []
1919
# Carbon budget control
2020
[carbon_budget_control]
2121
budget = [] # Same length as time_framework
22-
commodities = []
23-
method = 'bisection'
2422

2523
# Custom modules
2624
# Rather than a section, it can also be a list, e.g.:

src/muse/mca.py

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ def factory(cls, settings: str | Path | Mapping | Any) -> MCA:
100100
for k, v in settings.carbon_budget_control._asdict().items()
101101
}
102102
for key in {"budget", "commodities", "method"}:
103-
carbon_kw[f"carbon_{key}"] = carbon_kw[key]
104-
carbon_kw.pop(key)
103+
if key in carbon_kw:
104+
carbon_kw[f"carbon_{key}"] = carbon_kw.pop(key)
105105
return cls(
106106
sectors=sectors,
107107
market=market,
@@ -128,9 +128,9 @@ def __init__(
128128
carbon_price: Sequence | None = None,
129129
carbon_commodities: Sequence[str] | None = None,
130130
debug: bool = False,
131-
control_undershoot: bool = True,
132-
control_overshoot: bool = True,
133-
carbon_method: str = "fitting",
131+
control_undershoot: bool = False,
132+
control_overshoot: bool = False,
133+
carbon_method: str = "bisection",
134134
method_options: Mapping | None = None,
135135
):
136136
"""Market clearing algorithm class which rules the whole MUSE."""
@@ -242,14 +242,14 @@ def update_carbon_budget(self, market: Dataset, year_idx: int) -> float:
242242
self.control_undershoot,
243243
)
244244

245-
def update_carbon_price(self, market) -> float | None:
245+
def update_carbon_price(self, market) -> float:
246246
"""Calculates the updated carbon price.
247247
248248
Arguments:
249249
market: Market with the prices, supply, consumption and demand.
250250
251251
Returns:
252-
The new carbon price. If None, the price is not updated.
252+
The new carbon price.
253253
"""
254254
new_carbon_price = self.carbon_method( # type: ignore
255255
market,
@@ -301,18 +301,14 @@ def run(self) -> None:
301301
# If we need to account for the carbon budget, we do it now.
302302
if check_carbon_budget:
303303
new_price = self.update_carbon_price(new_market)
304-
if new_price is not None:
305-
future_price = DataArray(new_price, coords=dict(year=years[1]))
306-
307-
new_market.prices.loc[dict(commodity=self.carbon_commodities)] = (
308-
future_propagation(
309-
new_market.prices.sel(commodity=self.carbon_commodities),
310-
future_price,
311-
)
312-
)
313-
self.carbon_price = future_propagation(
314-
self.carbon_price, future_price
304+
future_price = DataArray(new_price, coords=dict(year=years[1]))
305+
new_market.prices.loc[dict(commodity=self.carbon_commodities)] = (
306+
future_propagation(
307+
new_market.prices.sel(commodity=self.carbon_commodities),
308+
future_price,
315309
)
310+
)
311+
self.carbon_price = future_propagation(self.carbon_price, future_price)
316312

317313
_, new_market, self.sectors = self.find_equilibrium(new_market)
318314

0 commit comments

Comments
 (0)