13. API

MUSE model.

13.1. Market Clearing Algorithm

13.1.1. Main MCA

class FindEquilibriumResults(converged, market, sectors)[source]

Result of find equilibrium.

converged

Alias for field number 0

market

Alias for field number 1

sectors

Alias for field number 2

class MCA(sectors, market, outputs=None, outputs_cache=None, time_framework=[2010, 2020, 2030, 2040, 2050, 2060, 2070, 2080, 2090], equilibrium=True, equilibrium_variable='demand', maximum_iterations=100, tolerance=0.1, tolerance_unmet_demand=-0.1, excluded_commodities=None, carbon_budget=None, carbon_commodities=None, debug=False, control_undershoot=False, control_overshoot=False, carbon_method='bisection', method_options=None)[source]

Market Clearing Algorithm.

The market clearing algorithm is the main object implementing the MUSE model. It is responsible for orchestrating how the sectors are run, how they interface with one another, with the general market and the carbon market.

classmethod factory(settings)[source]

Loads MCA from input settings and input files.

Parameters:

settings – namedtuple with the global MUSE input settings.

Returns:

The loaded MCA

find_equilibrium(market)[source]

Specialised version of the find_equilibrium function.

Parameters:

market – Commodities market, with the prices, supply, consumption and demand.

Returns:

A tuple with the updated market (prices, supply, consumption and demand) and sector.

run()[source]

Initiates the calculation, starting with the loop over years.

This method starts the main MUSE loop, going over the years of the simulation. Internally, it runs the carbon budget loop, which updates the carbon prices, if needed, and the equilibrium loop, which tries to reach an equilibrium between prices, demand and supply.

Returns:

None

update_carbon_budget(market, year_idx)[source]

Specialised version of the update_carbon_budget function.

Parameters:
  • market – Commodities market, with the prices, supply, consumption and demand.

  • year_idx – Index of the year of interest.

Returns:

An updated market with prices, supply, consumption and demand.

update_carbon_price(market)[source]

Calculates the updated carbon price.

Parameters:

market – Market with the prices, supply, consumption and demand.

Returns:

The new carbon price.

class SingleYearIterationResult(market, sectors, updated_prices)[source]

Result of iterating over sectors for a year.

Convenience tuple naming naming the return values from of single_year_iteration().

market

Alias for field number 0

sectors

Alias for field number 1

updated_prices

Alias for field number 2

check_demand_fulfillment(market, tol)[source]

Checks if the supply will fulfill all the demand in the future.

If it does not, it logs a warning.

Parameters:
  • market – Commodities market, with the prices, supply, consumption and demand.

  • tol – Tolerance for the unmet demand.

Returns:

True if the supply fulfils the demand; False otherwise

check_equilibrium(market, int_market, tolerance, equilibrium_variable, year=None)[source]

Checks if equilibrium has been reached.

This function checks if the difference in either the demand or the prices between iterations if smaller than certain tolerance. If is, then it is assumed that the process has converged.

Parameters:
  • market – The market values in this iteration.

  • int_market – The market values in the previous iteration.

  • tolerance – Tolerance for reaching equilibrium.

  • equilibrium_variable – Variable to use to calculate the equilibrium condition.

  • year – year for which to check changes. Default to minimum year in market.

Returns:

True if converged, False otherwise.

find_equilibrium(market, sectors, maxiter=3, tol=0.1, equilibrium_variable='demand', tol_unmet_demand=-0.1, excluded_commodities=None, equilibrium=True)[source]

Runs the equilibrium loop.

If convergence is reached, then the function returns the new market. If the maximum number of iterations are reached, then a warning issued in the log and the function returns with the current status.

Parameters:
  • market – Commodities market, with the prices, supply, consumption and demand.

  • sectors – A list of the sectors participating in the simulation.

  • maxiter – Maximum number of iterations.

  • tol – Tolerance for reaching equilibrium.

  • equilibrium_variable – Variable to use to calculate the equilibrium condition.

  • tol_unmet_demand – Tolerance for the unmet demand.

  • excluded_commodities – Commodities to be excluded in check_demand_fulfillment

  • equilibrium – if equilibrium should be reached. Useful to testing.

Returns:

A tuple with the updated market (prices, supply, consumption and demand), sectors, and convergence status.

single_year_iteration(market, sectors)[source]

Runs one iteration of the sectors (runs each sector once).

Parameters:
  • market – An initial market with prices, supply, consumption.

  • sectors – A list of the sectors participating in the simulation.

Returns:

A tuple with the new market, sectors and updated prices.

13.1.2. Carbon Budget

CARBON_BUDGET_FITTERS = {'Exponential': <function exponential>, 'Linear': <function linear>, 'exponential': <function exponential>, 'linear': <function linear>}

Dictionary of carbon budget fitters.

CARBON_BUDGET_FITTERS_SIGNATURE

carbon budget fitters signature.

alias of Callable[[ndarray, ndarray, int], float]

CARBON_BUDGET_METHODS = {'Bisection': <function bisection>, 'Fitting': <function fitting>, 'bisection': <function bisection>, 'fitting': <function fitting>}

Dictionary of carbon budget methods checks.

CARBON_BUDGET_METHODS_SIGNATURE

carbon budget fitters signature.

alias of Callable[[Dataset, Callable, DataArray, list], float]

class EmissionsCache(market, equilibrium, commodities)[source]

Cache of emissions at different price points for bisection algorithm.

If a price is queried that is not in the cache, it calculates the emissions at that price using solve_market and stores the result in the cache.

adjust_bounds(lb_price, ub_price, emissions_cache, target, resolution=2)[source]

Adjust the bounds of the carbon price for the bisection algorithm.

As emissions can be a discontinuous function of the carbon price, this method is used to improve the solution search when discontinuities are met, improving the bounds search.

Parameters:
  • lb_price – Value of carbon price at lower bound

  • ub_price – Value of carbon price at upper bound

  • emissions_cache – Dictionary of emissions at different price points

  • target – Carbon budget

  • resolution – Number of decimal places to solve the carbon price to

Returns:

New lower and upper bounds for the carbon price.

bisect_bounds(lb_price, ub_price, emissions_cache, target, resolution=2)[source]

Bisects the bounds of the carbon price.

bisect_bounds_inverted(lb_price, ub_price, emissions_cache, target, resolution=2)[source]

Bisects the bounds of the carbon price, in the case of inverted bounds.

bisection(market, equilibrium, carbon_budget, commodities, refine_price=False, price_too_high_threshold=10, max_iterations=5, tolerance=0.1, early_termination_count=5, resolution=2, price_penalty=0.1)[source]

Applies bisection algorithm to escalate carbon price and meet the budget.

A carbon market is meant as a pool of emissions for all the modelled regions; therefore, the carbon price applies to all modelled regions. Bisection applies an iterative estimations of the emissions varying the carbon price until convergence or stop criteria are met. Builds on ‘register_carbon_budget_method’.

Parameters:
  • market – Market, with the prices, supply, consumption and demand

  • equilibrium – Method for searching market equilibrium

  • carbon_budget – DataArray with the carbon budget

  • commodities – List of carbon-related commodities

  • refine_price – Boolean to decide on whether carbon price should be capped, with the upper bound given by price_too_high_threshold

  • price_too_high_threshold – Upper limit for carbon price

  • max_iterations – Maximum number of iterations for bisection

  • tolerance – Maximum permitted deviation of emissions from the budget

  • early_termination_count – Will terminate the loop early if the last n solutions are the same

  • resolution – Number of decimal places to solve the carbon price to

  • price_penalty – Penalty factor applied to carbon price when selecting optimal solution when convergence isn’t reached. Higher values favor lower prices when emissions are similar.

Returns:

New value of global carbon price

create_sample(carbon_price, current_emissions, budget, size=4)[source]

Calculates a sample of carbon prices to estimate the adjusted carbon price.

For each of these prices, the equilibrium loop will be run, obtaining a new value for the emissions. Out of those price-emissions pairs, the final carbon price will be estimated.

Parameters:
  • carbon_price – Current carbon price,

  • current_emissions – Current emissions,

  • budget – Carbon budget,

  • size – Number of points in the sample.

Returns:

An array with the sample prices.

decrease_bounds(lb_price, ub_price, emissions_cache, target, resolution=2)[source]

Decreases the lb of the carbon price, and sets the ub to the previous lb.

exp_guess_and_weights(prices, emissions, budget)[source]

Estimates initial values for the exponential fitting algorithm and the weights.

The points closest to the budget are used to estimate the initial guess. They also have the highest weight.

Parameters:
  • prices – An array with the sample carbon prices,

  • emissions – An array with the corresponding emissions,

  • budget – The carbon budget for the time period.

Returns:

The initial guess and weights

exponential(prices, emissions, budget)[source]

Fits the prices-emissions pairs to an exponential function.

Once that is done, an optimal carbon price is estimated

Parameters:
  • prices – An array with the sample carbon prices,

  • emissions – An array with the corresponding emissions,

  • budget – The carbon budget for the time period.

Returns:

The optimal carbon price.

fitting(market, equilibrium, carbon_budget, commodities, refine_price=False, price_too_high_threshold=10, sample_size=5, fitter='linear', resolution=2)[source]

Used to solve the carbon market.

Given the emission of a period, adjusts carbon price to meet the budget. A carbon market is meant as a pool of emissions for all the modelled regions; therefore, the carbon price applies to all modelled regions. The method solves an equation applying a fitting of the emission-carbon price relation.

Parameters:
  • market – Market, with the prices, supply, and consumption

  • equilibrium – Method for searching market equilibrium

  • carbon_budget – limit on emissions

  • commodities – list of commodities to limit (ie. emissions)

  • refine_price – Boolean to decide on whether carbon price should be capped, with the upper bound given by price_too_high_threshold

  • price_too_high_threshold – threshold on carbon price

  • sample_size – sample size for fitting

  • fitter – method to fit emissions with carbon price

  • resolution – Number of decimal places to solve the carbon price to

Returns:

Adjusted carbon price to meet budget

increase_bounds(lb_price, ub_price, emissions_cache, target, resolution=2)[source]

Increases the ub of the carbon price, and sets the lb to the previous ub.

linear(prices, emissions, budget)[source]

Fits the prices-emissions pairs to a linear function.

Once that is done, an optimal carbon price is estimated

Parameters:
  • prices – An array with the sample carbon prices,

  • emissions – An array with the corresponding emissions,

  • budget – The carbon budget for the time period.

Returns:

The optimal carbon price.

linear_guess_and_weights(prices, emissions, budget)[source]

Estimates initial values for the linear fitting algorithm and the weights.

The points closest to the budget are used to estimate the initial guess. They also have the highest weight.

Parameters:
  • prices – An array with the sample carbon prices,

  • emissions – An array with the corresponding emissions,

  • budget – The carbon budget for the time period.

Returns:

The initial guess and weights

register_carbon_budget_fitter(function=None)[source]

Decorator to register a carbon budget function.

register_carbon_budget_method(function=None)[source]

Decorator to register a carbon budget function.

solve_market(market, equilibrium, commodities, carbon_price)[source]

Solves the market with a new carbon price and returns the emissions.

Parameters:
  • market – Market, with the prices, supply, consumption and demand

  • equilibrium – Method for searching market equilibrium

  • commodities – List of carbon-related commodities

  • carbon_price – New carbon price

Returns:

Emissions at the new carbon price.

update_carbon_budget(carbon_budget, emissions, year_idx, over=True, under=True)[source]

Adjust the carbon budget in the far future if emissions too high or low.

This feature can allow to simulate overshoot shifting.

Parameters:
  • carbon_budget – budget for future year,

  • emissions – emission for future year,

  • year_idx – index of year for estimation,

  • over – if True, allows overshoot,

  • under – if True, allows undershoot.

Returns:

An adjusted threshold for the future year

13.2. Sectors and associated functionality

Define a sector, e.g. aggregation of agents.

There are three main kinds of sectors classes, encompassing three use cases:

  • Sector: The main workhorse sector of the model. It contains only on kind of data, namely the agents responsible for holding assets and investing in new assets.

  • PresetSector: A sector that is meant to generate demand for the sectors above using a fixed formula or schedule.

All the sectors derive from AbstractSector. The AbstractSector defines two abstract functions which should be declared by derived sectors. Abstract here means a common programming practice where some concept in the code (e.g. a sector) is given an explicit interface, with the goal of making it easier for other programmers to use and implement the concept.

  • AbstractSector.factory(): Creates a sector from input data

  • AbstractSector.next(): A function which takes a market (demand, supply, prices) and returns a market. What happens within could be anything, though it will likely consists of dispatch and investment.

New sectors can be registered with the MUSE input files using muse.sectors.register.register_sector().

@register_sector(sector_class=None, name=None)[source]

Registers a sector so it is available MUSE-wide.

Example

>>> from muse.sectors import AbstractSector, register_sector
>>> @register_sector(name="MyResidence")
... class ResidentialSector(AbstractSector):
...     pass

13.2.1. AbstractSector

class AbstractSector[source]

Abstract base class for sectors.

Sectors are part of type hierarchy with AbstractSector at the apex: all sectors should derive from AbstractSector directly or indirectly.

MUSE only requires two things of a sector. Sector should be instanstiable via a factory() function. And they should be callable via next().

AbstractSector declares an interface with these two functions. Sectors which derive from it will be warned if either method is not implemented.

abstractmethod classmethod factory(name, settings)[source]

Creates class from settings named-tuple.

abstractmethod next(mca_market)[source]

Advance sector by one time period.

13.2.2. Sector

class Sector(name, technologies, supply_prod, subsectors=[], commodities=[], interactions=None, outputs=None, timeslice_level=None)[source]

Base class for all sectors.

property agents

Iterator over all agents in the sector.

property capacity

Aggregates capacity across agents.

The capacities are aggregated leaving only two dimensions: asset (technology, installation date, region), year.

convert_to_global_timeslicing(market)[source]

Converts market data to global timeslicing.

convert_to_sector_timeslicing(market)[source]

Converts market data to sector timeslicing.

classmethod factory(name, settings)[source]

Creates class from settings named-tuple.

interactions

A function for outputting data for post-mortem analysis.

market_variables(market, technologies)[source]

Computes resulting market: production, consumption, and costs.

name

Timeslice level for the sector (e.g. “month”).

next(mca_market)[source]

Advance sector by one time period.

Parameters:

mca_market – Market with demand, supply, and prices.

Returns:

A market containing the supply offered by the sector, it’s attendant consumption of fuels and materials and the associated costs.

output_data

Commodities that the sector is in charge of producing.

outputs

Computes production as used to return the supply to the MCA.

It can be anything registered with @register_production.

save_outputs(year)[source]

Calls the outputs function with the current output data.

subsectors

Parameters describing the sector’s technologies.

supply_prod

Full supply, consumption and costs data for the most recent year.

timeslice_level

Subsectors controlled by this object.

13.2.3. Subsector

class Subsector(agents, commodities, demand_share, constraints, investment, name='subsector', expand_market_prices=False, timeslice_level=None)[source]

Agent group servicing a subset of the sectorial commodities.

13.2.4. PresetSector

class PresetSector(presets, interpolation_mode='linear', name='preset')[source]

Sector with outcomes fixed from the start.

classmethod factory(name, settings)[source]

Constructs a PresetSectors from input data.

interpolation_mode

Interpolation method

name

Name by which to identify a sector

next(mca_market)[source]

Advance sector by one time period.

presets

Market across time and space.

13.2.5. Dispatch

Various ways and means to compute production.

Production is the amount of commodities produced by an asset. However, depending on the context, it could be computed several ways. For instance, it can be obtained straight from the capacity of the asset. Or it can be obtained by matching for the same commodities with a set of assets.

Production methods can be registered via the @register_production production decorator. Registering a function makes the function accessible from MUSE’s input file. Production methods are not expected to modify their arguments. Furthermore they should conform the following signatures:

@register_production
def production(
    demand: xr.DataArray, capacity: xr.DataArray, technologies: xr.Dataset, **kwargs
) -> xr.DataArray:
    pass
param demand:

The demand for each commodity.

param capacity:

The capacity of each asset within a market.

param technologies:

A dataset characterising the technologies of the same assets.

param **kwargs:

Any number of keyword arguments

returns:

A xr.DataArray with the amount produced for each good from each asset.

class PRODUCTION_SIGNATURE(*args, **kwargs)[source]

Callable protocol for registered production methods.

dispatch_by_merit_order(demand, minprod, maxprod, technology_costs)[source]

Dispatch assets in order of increasing cost until demand is met.

For example, we have the following three assets (ordered from cheapest to most expensive): - Asset A: minprod=20, maxprod=50, cost=10 - Asset B: minprod=10, maxprod=100, cost=5 - Asset C: minprod=0, maxprod=30, cost=15

If the total demand is 140 units, we would dispatch as follows: - First we dispatch Assets A and B at their minimum production, which gives us 30 units of supply, leaving 110 units of remaining demand. - Next we dispatch Asset B up to its maximum, which gives us an additional 90 units of supply, leaving 20 units of remaining demand. - The remaining demand of 20 is less than the headroom of Asset A above its minimum (i.e. 50 - 20 = 30), so we dispatch Asset A for an additional 20 units, which meets the total demand of 140. - Asset C is not dispatched at all since it’s the most expensive and demand is already met by cheaper assets.

Parameters:
  • demand – DataArray of demand values, which should have a single ‘commodity’ dimension

  • minprod – DataArray of minimum production values for the candidate assets, with dimensions ‘asset’ and ‘commodity’

  • maxprod – DataArray of maximum production values for the candidate assets, with dimensions ‘asset’ and ‘commodity’

  • technology_costs – DataArray of technology costs for the candidate assets, which should have a single ‘asset’ dimension. This is the cost per unit of production for each asset, which determines the order of dispatch (cheapest first).

maximum_production(demand, capacity, technologies, timeslice_level=None, **kwargs)[source]

Production when running at full capacity.

Full capacity is limited by the utilization factor. For more details, see muse.quantities.maximum_production().

merit_order_production(demand, capacity, technologies, timeslice_level=None, *, prices, **kwargs)[source]

Service demand by preferentially dispatching the cheapest assets.

This function allocates production across a set of assets by dispatching them in order of increasing marginal cost (i.e. cheapest first), until demand is met. Each asset is operated between its minimum and maximum production constraints, and higher-cost assets are only used once cheaper options are exhausted.

The merit-order principle is widely used in systems where: - Multiple assets can supply a homogeneous or substitutable service - Supply is pooled or centrally coordinated - Short-run marginal cost is the dominant driver of dispatch decisions

A good example is the power sector (electricity production), where supply from all assets feeds into a common grid. Low cost generators are drawn on first, followed by more expensive generators as grid demand increases.

Each timeslice and year is treated independently, so the dispatch order can vary across timeslices and years. For a numerical example in a single timeslice/year/region, see the docstring of dispatch_by_merit_order().

register_production(function)[source]

Decorator to register a function as a production method.

See also

muse.dispatch

share_based_production(demand, capacity, technologies, timeslice_level=None, **kwargs)[source]

Production and emission for a given capacity servicing a given demand.

This method distributes the demand across assets in proportion to their maximum production. For example, if asset A can produce 10 units at full capacity and asset B can produce 20 units, then A will service 1/3 of the total demand and B will service 2/3of the total demand. If demand is lower than total maximum production, then the supply from each asset is reduced proportionally. If demand exceeds capacity, production is capped at each asset’s maximum.

This function is most appropriate for sectors where demand is inherently distributed across assets and cannot be shifted between them (e.g. residential heating systems, where each household meets its own demand).

Parameters:
  • demand – amount of each end-use required. The supply of each process will not exceed its share of the demand.

  • capacity – number/quantity of assets that can service the demand

  • technologies – factors bindings the capacity of an asset with its production of commodities and environmental pollutants.

  • timeslice_level – the desired timeslice level of the result (e.g. “hour”, “day”)

  • **kwargs – any number of keyword arguments (not used in this method)

Returns:

A data array where the commodity dimension only contains actual outputs (i.e. no input commodities).

13.2.6. Agent Interactions

Modes of interactions between agents.

Interactions between agents are modelled via two orthogonal concepts:

  • a net is a set of agents which interact in some way

  • an interaction proper is a function that takes a net and actually performs the interaction.

Hence, there are two registrators in this this module, register_interaction_net(), and register_agent_interaction(). The first registers functions that take the full set of agents as input and returns a sequence of nets. It is expected each net of the sequence will be applied the same interaction. The second registrator registers the interaction proper: it takes agents as arguments and returns nothing. It is expected to modify the agents in-place.

factory(inputs=None)[source]

Creates an interaction functor.

new_to_retro_net(agents, first_category='newcapa')[source]

Interactions between new and retrofit agents.

register_agent_interaction(function)[source]

Decorator to register an agent to agent(s) interaction function.

An agent interaction function takes at least two agents and makes them interact in some way.

An agent interaction function also takes as argument a sector object. This object should not be modified in any way. But it can be queried for parameters, if the specific agent interaction function requires it. This is most likely the same configuration object passed on to the interaction net function.

register_interaction_net(function)[source]

Decorator to register a function computing interaction nets.

An interaction net function takes as input the list of all agents and returns the list of all interactions, where an interaction is a list of at least two interacting agents.

An interactiont-net function also takes as argument a sector object. This object should not be modified in any way. But it can be queried for parameters, if the specific interaction-net function requires it.

transfer_assets(from_, to_)[source]

Transfer assets from first agent to second agent.

13.3. Agents and associated functionalities

Holds all building agents.

agents_factory(params_or_path, capacity, technologies, regions=None, year=None, **kwargs)[source]

Creates a list of agents for the chosen sector.

create_newcapa_agent(capacity, year, region, share, search_rules='all', merge_transform='new', quantity=0.3, housekeeping='clean', retrofit_present=True, **kwargs)[source]

Creates newcapa agent from muse primitives.

If there are no retrofit agents present in the sector, then the newcapa agent need to be initialised with the initial capacity of the sector.

create_retrofit_agent(technologies, capacity, share, year, region, decision='mean', **kwargs)[source]

Creates retrofit agent from muse primitives.

class AbstractAgent(name='Agent', region='', assets=None, category=None, quantity=1.0, timeslice_level=None)[source]

Base class for all agents.

assets

Current stock of technologies.

category

Attribute to classify different sets of agents.

filter_input(dataset, **kwargs)[source]

Filter inputs for usage in agent.

For instance, filters down to agent’s region, etc.

name

Name associated with the agent.

abstractmethod next(technologies, market, demand)[source]

Increments agent to the next time point (e.g. performing investments).

Performs investments to meet demands, and increments agent.year to the investment year.

Parameters:
  • technologies – dataset of technology parameters for the investment year

  • market – market dataset covering the current year and investment year

  • demand – data array of demand for the investment year

quantity

Attribute to classify different agents’ share of the population.

region

Region the agent operates in.

timeslice_level

Timeslice level for the agent.

tolerance = 1e-12

tolerance criteria for floating point comparisons.

uuid

A unique identifier for the agent.

class Agent(name='Agent', region='USA', assets=None, search_rules=None, objectives=None, decision=None, year=2010, maturity_threshold=0, housekeeping=None, merge_transform=None, demand_threshold=None, category=None, asset_threshold=0.0001, quantity=1.0, spend_limit=0, timeslice_level=None, **kwargs)[source]

Standard agent that does not perform investments.

_housekeeping

Transforms applied on the old and new assets.

It could mean using only the new assets, or merging old and new, etc… It can be any function registered with register_final_asset_transform().

asset_housekeeping()[source]

Reduces memory footprint of assets.

Performs tasks such as:

  • remove empty assets

  • remove years prior to current

decision

Transforms applied on the assets at the start of each iteration.

It could mean keeping the assets as are, or removing assets with no capacity in the current year and beyond, etc… It can be any function registered with register_initial_asset_transform().

demand_threshold

Threshold below which assets are not added.

merge_transform

Threshold below which the demand share is zero.

This criteria avoids fulfilling demand for very small values. If None, then the criteria is not applied.

next(technologies, market, demand)[source]

Increments agent to the next time point (e.g. performing investments).

Performs investments to meet demands, and increments agent.year to the investment year.

Parameters:
  • technologies – dataset of technology parameters for the investment year

  • market – market dataset covering the current year and investment year

  • demand – data array of demand for the investment year

objectives

Creates single decision objective from one or more objectives.

search_rules

Market share threshold.

Threshold when and if filtering replacement technologies with respect to market share.

spend_limit

One or more objectives by which to decide next investments.

year

Search rule(s) determining potential replacement technologies.

This is a string referring to a filter, or a sequence of strings referring to multiple filters, applied one after the other. Any function registered via muse.filters.register_filter can be used to filter the search space.

class InvestingAgent(*args, constraints=None, investment=None, **kwargs)[source]

Agent that performs investment for itself.

add_investments(technologies, investments, investment_year)[source]

Add new assets to the agent.

constraints

Creates a set of constraints limiting investment.

invest

Method to use when fulfilling demand from rated set of techs.

next(technologies, market, demand)[source]

Iterates agent one turn.

The goal is to figure out from market variables which technologies to invest in and by how much.

This function will modify self.assets and increment self.year. Other attributes are left unchanged. Arguments to the function are never modified.

13.3.1. Objectives

Valuation functions for replacement technologies.

Objectives are used to compare replacement technologies. They should correspond to a single well defined economic concept. Multiple objectives can later be combined via decision functions.

Objectives should be registered via the @register_objective decorator. This makes it possible to refer to them by name in agent input files, and nominally to set extra input parameters.

The factory() function creates a function that calls all objectives defined in its input argument and returns a dataset with each objective as a separate data array.

Objectives are not expected to modify their arguments. Furthermore they should conform the following signatures:

@register_objective
def comfort(
    technologies: xr.Dataset,
    demand: xr.DataArray,
    prices: xr.DataArray,
    **kwargs
) -> xr.DataArray:
    pass
param technologies:

A data set characterising the technologies from which the agent can draw assets. This has been pre-filtered according to the agent’s search space.

param demand:

Demand to fulfill.

param prices:

Commodity prices.

param kwargs:

Extra input parameters. These parameters are expected to be set from the input file.

Warning

The standard agent csv file does not allow to set these parameters.

returns:

A DataArray with at least two dimension corresponding to replacement and asset. A timeslice dimension may also be present.

capacity_to_service_demand(technologies, demand, timeslice_level=None, *args, **kwargs)[source]

Minimum capacity required to fulfill the demand.

capital_costs(technologies, demand, timeslice_level=None, *args, **kwargs)[source]

Capital costs for input technologies.

comfort(technologies, demand, *args, **kwargs)[source]

Comfort value provided by technologies.

efficiency(technologies, demand, *args, **kwargs)[source]

Efficiency of the technologies.

emission_cost(technologies, demand, prices, timeslice_level=None, *args, **kwargs)[source]

Emission cost for each technology when fulfilling whole demand.

Given the demand share \(D\), the emissions per amount produced \(E\), and the prices per emittant \(P\), then emissions costs \(C\) are computed as:

\[C = \sum_s \left(\sum_cD\right)\left(\sum_cEP\right),\]

with \(s\) the timeslices and \(c\) the commodity.

equivalent_annual_cost(technologies, demand, prices, timeslice_level=None, *args, **kwargs)[source]

Equivalent annual costs (or annualized cost) of a technology.

See muse.costs.equivalent_annual_cost() for more details.

factory(settings='LCOE')[source]

Creates a function computing multiple objectives.

The input can be a single objective defined by its name alone. Or it can be a single objective defined by a dictionary which must include at least a “name” item, as well as any extra parameters to pass to the objective. Or it can be a sequence of objectives defined by name or by dictionary.

fixed_costs(technologies, demand, timeslice_level=None, *args, **kwargs)[source]

Fixed costs associated with a technology.

Given a factor \(\alpha\) and an exponent \(\beta\), the fixed costs \(F\) are computed from the capacity fulfilling the current demand \(C\) as:

\[F = \alpha * C^\beta\]

\(\alpha\) and \(\beta\) are “fix_par” and “fix_exp” in Technodata, respectively.

fuel_consumption_cost(technologies, demand, prices, timeslice_level=None, *args, **kwargs)[source]

Cost of fuels when fulfilling whole demand.

lifetime_levelized_cost_of_energy(technologies, demand, prices, timeslice_level=None, *args, **kwargs)[source]

Levelized cost of energy (LCOE) of technologies over their lifetime.

See muse.costs.levelized_cost_of_energy() for more details.

The LCOE is set to zero for those timeslices where the production is zero, normally due to a zero utilization factor.

net_present_value(technologies, demand, prices, timeslice_level=None, *args, **kwargs)[source]

Net present value (NPV) of the relevant technologies.

See muse.costs.net_present_value() for more details.

register_objective(function)[source]

Decorator to register a function as a objective.

Registers a function as a objective so that it can be applied easily when sorting technologies one against the other.

The input name is expected to be in lower_snake_case, since it ought to be a python function. CamelCase, lowerCamelCase, and kebab-case names are also registered.

13.3.2. Search Rules

Various search-space filters.

Search-space filters return a modified matrix of booleans, with dimension (asset, replacement), where asset refer to technologies currently managed by the agent, and replacement to all technologies the agent could consider, prior to filtering.

Filters should be registered using the decorator register_filter(). The registration makes it possible to call then from the agent by specifying the search_rule attribute. The search_rule attribute is string or list of strings specifying the filters to apply one after the other when considering the search space.

Filters are not expected to modify any of their arguments. They should all follow the same signature:

@register_filter
def search_space_filter(
    agent: Agent,
    search_space: xr.DataArray,
    *,
    additional_argument1,
    additional_argument2,
    **kwargs,
) -> xr.DataArray:
    pass
param agent:

the agent relevant to the search space. The filters may need to query the agent for parameters, e.g. the current year, the tolerance, etc.

param search_space:

the current search space.

param any additional arguments must follow the * argument:

arguments.

param and must be passed as keyword:

arguments.

returns:

A new search space with the same data-type as the input search-space, but with potentially different values.

In practice, an initial search space is created by calling a function with the signature given below, and registered with register_initializer(). The initializer function returns a search space which is passed on to a chain of filters, as done in the factory() function.

Functions creating initial search spaces should have the following signature:

@register_initializer
def search_space_initializer(
    agent: Agent,
    demand: xr.DataArray,
    technologies: xr.Dataset,
) -> xr.DataArray:
    pass
param agent:

the agent relevant to the search space. The filters may need to query the agent for parameters, e.g. the current year, the tolerance, etc.

param demand:

share of the demand per existing reference technology (e.g. assets).

param technologies:

A data set characterising the technologies from which the agent can draw assets.

returns:

An initial search space

compress(agent, search_space, **kwargs)[source]

Compress search space to include only potential technologies.

This operation reduces the size of the search space along the replacement dimension, such that are left only technologies that will be considered as replacement for at least by one asset. Unlike most filters, it does not change the data, but rather changes how the data is represented. In other words, this is mostly an optimization for later steps, to avoid unnecessary computations.

currently_existing_tech(agent, search_space, *, market, **kwargs)[source]

Only consider technologies that currently exist in the market.

This filter only allows technologies that exists in the market and have non- zero capacity in the current year. See currently_referenced_tech for a similar filter that does not check the capacity.

currently_referenced_tech(agent, search_space, *, market, **kwargs)[source]

Only consider technologies that are currently referenced in the market.

This filter will allow any technology that exists in the market, even if it currently sits at zero capacity (unlike currently_existing_tech which requires non-zero capacity in the current year).

factory(settings=None, separator='->')[source]

Creates filters from input TOML data.

The input data is standardized to a list of dictionaries where each dictionary contains at least one member, “name”.

The first dictionary specifies the initial function which creates the search space from the demand share, the market, and the dataset describing technologies in the sectors.

The next entries are applied in turn and transform the search space in some way. In other words the process is more or less:

search_space = initial_filter(
    agent, demand, technologies=technologies, market=market
)
for afilter in filters:
    search_space = afilter(
        agent, search_space, technologies=technologies, market=market
    )
return search_space

initial_filter is simply first filter given on input, if that filter is registered with register_initializer(). Otherwise, initialize_from_technologies() is automatically inserted.

identity(agent, search_space, **kwargs)[source]

Returns search space as given.

initialize_from_technologies(agent, demand, *, technologies, **kwargs)[source]

Initialize a search space from existing technologies.

maturity(agent, search_space, *, market, **kwargs)[source]

Only allows technologies that have achieve a given market share.

Specifically, the market share refers to the capacity for each end- use.

reduce_asset(agent, search_space, **kwargs)[source]

Reduce over assets.

register_filter(function)[source]

Decorator to register a function as a filter.

Registers a function as a filter so that it can be applied easily when constraining the technology search-space.

The name that the function is registered with defaults to the function name. However, it can also be specified explicitly as a keyword argument. In any case, it must be unique amongst all search-space filters.

register_initializer(function)[source]

Decorator to register a function as a search-space initializer.

same_enduse(agent, search_space, *, technologies, **kwargs)[source]

Only allow for technologies with at least the same end-use.

same_fuels(agent, search_space, *, technologies, **kwargs)[source]

Filters technologies with the same fuel type.

Determines fuel type based on the energy commodities used as inputs by the technology.

similar_technology(agent, search_space, *, technologies, **kwargs)[source]

Filters technologies with the same type.

spend_limit(agent, search_space, *, technologies, **kwargs)[source]

Only allows technologies with a unit capital cost lower than the spend limit.

with_asset_technology(agent, search_space, **kwargs)[source]

Search space also contains its asset technology for each asset.

13.3.3. Decision Methods

Decision methods combining several objectives into ones.

Decisions methods create a single scalar from multiple objectives. To be available from the input, functions implementing decision methods should follow a specific signature:

@register_decision
def weighted_sum(objectives: Dataset, parameters: Any, **kwargs) -> DataArray:
    pass
param objectives:

An dataset where each array is a separate objective

param parameters:

parameters, such as weights, whether to minimize or maximize, the names of objectives to consider, etc.

param kwargs:

Extra input parameters. These parameters are expected to be set from the input file.

Warning

The standard agent csv file does not allow to set these parameters.

returns:

A data array with ranked replacement technologies.

epsilon_constraints(objectives, parameters, mask=None)[source]

Minimizes first objective subject to constraints on other objectives.

The parameters are a sequence of tuples (name, minimize, epsilon), where name is the name of the objective, minimize is True if minimizing and false if maximizing that objective, and epsilon is the constraint. The first objective is the one that will be minimized according to:

Given objectives \(O^{(i)}_t\), with \(i \in [|1, N|]\) and \(t\) the replacement technologies, this function computes the ranking with respect to \(t\):

\[\mathrm{ranking}_{O^{(i)}_t < \epsilon_i} O^{(0)}_t\]

The first tuple can be restricted to (name, minimize), since epsilon is ignored.

The result is the matrix \(O^{(0)}\) modified such minimizing over the replacement dimension value would take into account the constraints and the optimization direction (minimize or maximize). In other words, calling result.rank(‘replacement’) will yield the expected result.

factory(settings='mean')[source]

Creates a decision method based on the input settings.

lexical_comparison(objectives, parameters)[source]

Lexical comparison over the objectives.

Lexical comparison operates by binning the objectives into bins of width w_i = min_j(p_i o_i^j). Once binned, dimensions other than asset and technology are reduced by taking the max, e.g. the largest constraint. Finally, the objectives are ranked lexographically, in the order given by the parameters.

The result is an array of tuples which can subsequently be compared lexicographically.

mean(objectives, *args, **kwargs)[source]

Mean over objectives.

register_decision(function, name)[source]

Decorator to register a function as a decision.

Registers a function as a decision so that it can be applied easily when aggregating different objectives together.

retro_epsilon_constraints(objectives, parameters)[source]

Epsilon constraints where the current tech is included.

Modifies the parameters to the function such that the existing technologies are always competitive.

retro_lexical_comparison(objectives, parameters)[source]

Lexical comparison over the objectives.

Lexical comparison operates by binning the objectives into bins of width w_i = p_i o_i, where i are the current assets. Once binned, dimensions other than asset and replacement are reduced by taking the max, e.g. the largest constraint. Finally, the objectives are ranked lexographically, in the order given by the parameters.

The result is an array of tuples which can subsequently be compared lexicographically.

single_objective(objectives, parameters)[source]

Single objective decision method.

It only decides on minimization vs maximization and multiplies by a given factor. The input parameters can take the following forms:

  • Standard sequence [(objective, direction, factor)], in which case it must have only one element.

  • A single string: defaults to standard sequence [(string, 1, 1)]

  • A tuple (string, bool): defaults to standard sequence [(string, direction, 1)]

  • A tuple (string, bool, factor): defaults to standard sequence [(string, direction, factor)]

weighted_sum(objectives, parameters)[source]

Weighted sum over normalized objectives.

The objectives are each normalized to [-1, 1] over the replacement dimension by dividing by the maximum absolute value. Furthermore, the dimensions other than asset and replacement are reduced by taking the mean.

More specifically, the objective function is:

\[\sum_m c_m \frac{A_m - \min(A_m)}{\max(A_m) - \min(A_m)}\]

where sum runs over the different objectives, c_m is a scalar coefficient, A_m is a matrix with dimensions (existing tech, replacement tech). max(A) and min(A) return the largest and smallest component of the input matrix. If c_m is positive, then that particular objective is minimized, whereas if it is negative, that particular objective is maximized.

13.3.4. Investment Methods

Investment decision.

An investment determines which technologies to invest given a metric to determine preferred technologies, a corresponding search space of technologies, and the demand to fulfill.

Investments should be registered via the decorator register_investment. The registration makes it possible to call investments dynamically through compute_investment, by specifying the name of the investment. It is part of MUSE’s plugin platform.

Investments are not expected to modify any of their arguments. They should all have the following signature:

@register_investment
def investment(
    costs: xr.DataArray,
    search_space: xr.DataArray,
    technologies: xr.Dataset,
    constraints: List[Constraint],
    **kwargs
) -> xr.DataArray:
    pass
param costs:

specifies for each asset which replacement technology should be invested in preferentially. This should be an integer or floating point array with dimensions asset and replacement.

param search_space:

an asset by replacement matrix defining allowed and disallowed replacement technologies for each asset

param technologies:

a dataset containing all constant data characterizing the technologies.

param constraints:

a list of constraints as defined in constraints.

returns:

A data array with dimensions asset and technology specifying the amount of newly invested capacity.

INVESTMENT_SIGNATURE

Investment signature.

alias of Callable[[DataArray, DataArray, Dataset, list[Dataset], Any], DataArray | Dataset]

cliff_retirement_profile(technical_life, investment_year, **kwargs)[source]

Cliff-like retirement profile from current year.

Computes the retirement profile of all technologies in technical_life. Assets with a technical life smaller than the input time-period should automatically be renewed.

We could just return an array where each year is represented. Instead, to save memory, we return a compact view of the same where years where no change happens are removed.

Parameters:
  • technical_life – lifetimes for each technology

  • investment_year – The year in which the investment is made

  • **kwargs – arguments by which to filter technical_life, if any.

Returns:

A boolean DataArray where each each element along the year dimension is true if the technology is still not retired for the given year.

register_investment(function)[source]

Decorator to register a function as an investment.

The output of the function can be a DataArray, with the invested capacity, or a Dataset. In this case, it must contain a DataArray named “capacity” and, optionally, a DataArray named “production”. Only the invested capacity DataArray is returned to the calling function.

13.3.5. Demand Share

Demand share computations.

The demand share splits a demand amongst agents. It is used within a sector to assign part of the input MCA demand to each agent.

Demand shares functions should be registered via the decorator register_demand_share.

Demand share functions are not expected to modify any of their arguments. They should all have the following signature:

@register_demand_share
def demand_share(
    agents: Sequence[AbstractAgent],
    demand: xr.Dataarray,
    technologies: xr.Dataset,
    **kwargs
) -> xr.DataArray:
    pass
param agents:

a sequence of agent relevant to the demand share procedure. The agent can be queried for parameters specific to the demand share procedure. For instance, :py:func`new_and_retro` will query the agents for the assets they own, the region they are contained with, their category (new or retrofit), etc…

param demand:

DataArray of commodity demands for the current year and investment year.

param technologies:

a dataset containing all constant data characterizing the technologies.

param kwargs:

Additional keyword arguments.

returns:

A DataArray of demand shares.

DEMAND_SHARE_SIGNATURE

Demand share signature.

alias of Callable[[Sequence[AbstractAgent], DataArray, Dataset, Any], DataArray]

factory(name)[source]

Get a demand share function by name.

new_and_retro(agents, demand, technologies, timeslice_level=None)[source]

Splits demand across new and retro agents.

The input demand is split amongst both new and retrofit agents. New agents get a share of the increase in demand for the investment year, whereas retrofit agents are assigned a share of the demand that occurs from decommissioned assets.

Parameters:
  • agents – a list of all agents. This list should mainly be used to determine the type of an agent and the assets it owns. The agents will not be modified in any way.

  • demand – commodity demands for the current year and investment year.

  • technologies – quantities describing the technologies.

  • timeslice_level – the timeslice level of the sector (e.g. “hour”, “day”)

Pseudo-code:

  1. the capacity is reduced over agents and expanded over timeslices (extensive quantity) and aggregated over agents. Generally:

    \[A_{a, s}^r = w_s\sum_i A_a^{r, i}\]

    with \(w_s\) a weight associated with each timeslice and determined via muse.timeslices.distribute_timeslice().

  2. An intermediate quantity, the unmet demand \(U\) is defined from \(P[\mathcal{M}, \mathcal{A}]\), a function giving the production for a given market \(\mathcal{M}\), the associated consumption \(\mathcal{C}\), and aggregate assets \(\mathcal{A}\):

    \[U[\mathcal{M}, \mathcal{A}] = \max(\mathcal{C} - P[\mathcal{M}, \mathcal{A}], 0)\]

    where \(\max\) operates element-wise, and indices have been dropped for simplicity. The resulting expression has the same indices as the consumption \(\mathcal{C}_{c, s}^r\).

    \(P\) is the maximum production, given by <muse.quantities.maximum_production>`.

  3. the new demand \(N\) is defined as:

    \[N = \min\left( \mathcal{C}_{c, s}^r(y + \Delta y) - \mathcal{C}_{c, s}^r(y), U[\mathcal{M}^r(y + \Delta y), \mathcal{A}_{a, s}^r(y)] \right)\]
  4. the retrofit demand \(R\) is defined from the identity

    \[C_{c, s}^r(y + \Delta y) = P[\mathcal{M}^r(y+\Delta y), \mathcal{A}_{a, s}^r(y + \Delta y)] + N_{c, s}^r + R_{c, s}^r\]

    In other words, it is the share of the forecasted consumption that is serviced neither by the current assets still present in the investment year, nor by the new agent.

  5. then each new agent gets a share of \(N\) proportional to it’s

    share of the production, \(P[\mathcal{A}_{a, s}^{r, i}(y)]\). Then the share of the demand for new agent \(i\) is:

    \[N_{c, s, t}^{i, r}(y) = N_{c, s}^r \frac{\sum_\iota P[\mathcal{A}_{s, t, \iota}^{r, i}(y)]} {\sum_{i, t, \iota}P[\mathcal{A}_{s, t, \iota}^{r, i}(y)]}\]
  6. similarly, each retrofit agent gets a share of \(N\) proportional to its

    share of the decommissioning_demand, \(D^{r, i}_{t, c}\). Then the share of the demand for retrofit agent \(i\) is:

    \[R_{c, s, t}^{i, r}(y) = R_{c, s}^r \frac{\sum_\iota\mathcal{D}_{t, c, \iota}^{i, r}(y)} {\sum_{i, t, \iota}\mathcal{D}_{t, c, \iota}^{i, r}(y)}\]

Note that in the last two steps, the assets owned by the agent are aggregated over the installation year. The effect is that the demand serviced by agents is disaggregated over each technology, rather than not over each model of each technology (asset).

See also

decommissioning_demand, muse.quantities.maximum_production()

register_demand_share(function)[source]

Registers a demand share function with MUSE.

standard_demand(agents, demand, technologies, timeslice_level=None)[source]

Splits demand across new agents.

The input demand is split amongst new agents. New agents get a share of the increase in demand for the investment year, as well as the demand that occurs from decommissioned assets.

Parameters:
  • agents – a list of all agents. This list should mainly be used to determine the type of an agent and the assets it owns. The agents will not be modified in any way.

  • demand – commodity demands for the current year and investment year.

  • technologies – quantities describing the technologies.

  • timeslice_level – the timeslice level of the sector (e.g. “hour”, “day”)

unmet_demand(demand, capacity, technologies, timeslice_level=None)[source]

Share of the demand that cannot be serviced by the existing assets.

\[U[\mathcal{M}, \mathcal{A}] = \max(\mathcal{C} - P[\mathcal{M}, \mathcal{A}], 0)\]

\(\max\) operates element-wise, and indices have been dropped for simplicity. The resulting expression has the same indices as the consumption \(\mathcal{C}_{c, s}^r\).

\(P\) is the maximum production, given by muse.quantities.maximum_production().

unmet_forecasted_demand(agents, demand, technologies, timeslice_level=None)[source]

Forecast demand that cannot be serviced by non-decommissioned current assets.

13.3.6. Constraints:

Investment constraints.

Constraints on investments ensure that investments match some given criteria. For instance, the constraints could ensure that only so much of a new asset can be built every year.

Functions to compute constraints should be registered via the decorator register_constraints(). This registration step makes it possible for constraints to be declared in the TOML file.

Generally, LP solvers accept linear constraints defined as:

\[\begin{split}A x \\leq b\end{split}\]

with \(A\) a matrix, \(x\) the decision variables, and \(b\) a vector. However, these quantities are dimensionless. They do no have timeslices, assets, or replacement technologies, or any other dimensions that users have set up in their model. The crux is to translate from MUSE’s data-structures to a consistent dimensionless format.

In MUSE, users can register constraints functions that return fully dimensional quantities. The matrix operator is split over the capacity decision variables and the production decision variables:

\[\begin{split}A_c .* x_c + A_p .* x_p \\leq b\end{split}\]

The operator \(.*\) means the standard elementwise multiplication of xarray, including automatic broadcasting (adding missing dimensions by repeating the smaller matrix along the missing dimension). Constraint functions return the three quantities \(A_c\), \(A_p\), and \(b\). These three quantities will often not have the same dimension. E.g. one might include timeslices where another might not. The transformation from \(A_c\), \(A_p\), \(b\) to \(A\) and \(b\) happens as described below.

  • \(b\) remains the same. It defines the rows of \(A\).

  • \(x_c\) and \(x_p\) are concatenated one on top of the other and define the columns of \(A\).

  • \(A\) is split into a left submatrix for capacities and a right submatrix for production, following the concatenation of \(x_c\) and \(x_p\)

  • Any dimension in \(A_c .* x_c\) (\(A_p .* x_p\)) that is also in \(b\) defines diagonal entries into the left (right) submatrix of \(A\).

  • Any dimension in \(A_c .* x_c\) (\(A_p .* x_b\)) and missing from \(b\) is reduced by summation over a row in the left (right) submatrix of \(A\). In other words, those dimensions become part of a standard tensor reduction or matrix multiplication.

There are two additional rules. However, they are likely to be the result of an inefficient definition of \(A_c\), \(A_p\) and \(b\).

  • Any dimension in \(A_c\) (\(A_b\)) that is neither in \(b\) nor in \(x_c\) (\(x_p\)) is reduced by summation before consideration for the elementwise multiplication. For instance, if \(d\) is such a dimension, present only in \(A_c\), then the problem becomes \((\\sum_d A_c) .* x_c + A_p .* x_p \\leq b\).

  • Any dimension missing from \(A_c .* x_c\) (\(A_p .* x_p\)) and present in \(b\) is added by repeating the resulting row in \(A\).

Constraints are registered using the decorator register_constraints(). The decorated functions must follow the following signature:

@register_constraints
def constraints(
    demand: xr.DataArray,
    capacity: xr.DataArray,
    search_space: xr.DataArray,
    technologies: xr.Dataset,
    **kwargs,
) -> Constraint:
    pass
demand:

The demand for the sectors products in the investment year. In practice it is a demand share obtained in demand_share. It is a data-array with dimensions including asset, commodity, timeslice.

capacity:

A data-array with dimensions technology and year defining the existing capacity of each technology in the current year and investment year.

search_space:

A matrix asset vs replacement technology defining which replacement technologies will be considered for each existing asset.

technologies:

Technodata characterizing the competing technologies in the investment year.

**kwargs:

Any other parameter.

demand(demand, capacity, search_space, technologies, **kwargs)[source]

Constraints production to meet demand.

demand_limiting_capacity(demand_, capacity, search_space, technologies, *, timeslice_level=None, **kwargs)[source]

Limits the maximum combined capacity to match the demand.

This is a somewhat more restrictive constraint than the max_production constraint or the maximum capacity expansion. In this case, the combined new capacity of all assets must be sufficient to meet the demand of the most demanding timeslice, and no more.

Rather than coding from scratch the constraint, we can use the max_production constraint and the demand constraint to construct this constraint. Starting from the maximum production instead of the maximum capacity ensures that the constraint accounts for the utilization factor of the technologies.

factory(settings=None)[source]

Creates a list of constraints from standard settings.

The standard settings can be a string naming the constraint, a dictionary including at least “name”, or a list of strings and dictionaries.

max_capacity_expansion(demand, capacity, search_space, technologies, **kwargs)[source]

Max-capacity addition, max-capacity growth, and capacity limits constraints.

Limits by how much the capacity of each technology owned by an agent can grow in a given year. This is a constraint on the agent’s ability to invest in a technology.

Let \(L_t^r(y)\) be the total capacity limit for a given year, technology, and region. \(G_t^r(y)\) is the maximum growth. And \(W_t^r(y)\) is the maximum additional capacity. \(y=y_0\) is the current year and \(y=y_1\) is the year marking the end of the investment period.

Let \(\mathcal{A}^{i, r}_{t, \iota}(y)\) be the current assets, before investment, and let \(\Delta\mathcal{A}^{i,r}_t\) be the future investments. The the constraint on agent \(i\) are given as:

\[ \begin{align}\begin{aligned}L_t^r(y_0) - \sum_\iota \mathcal{A}^{i, r}_{t, \iota}(y_1) \geq \Delta\mathcal{A}^{i,r}_t\\(y_1 - y_0 + 1) G_t^r(y_0) \sum_\iota \mathcal{A}^{i, r}_{t, \iota}(y_0) - \sum_\iota \mathcal{A}^{i, r}_{t, \iota}(y_1) \geq \Delta\mathcal{A}^{i,r}_t\\(y_1 - y_0)W_t^r(y_0) \geq \Delta\mathcal{A}^{i,r}_t\end{aligned}\end{align} \]

The three constraints are combined into a single one which is returned as the maximum capacity expansion, \(\Gamma_t^{r, i}\). The maximum capacity expansion cannot impose negative investments: Maximum capacity addition:

\[\Gamma_t^{r, i} \geq 0\]

\(L_t^r(y)\), \(G_t^r(y)\) and \(W_t^r(y)\) default to np.inf if not provided (i.e. no constraint). If all three parameters are not provided, no constraint is applied (returns None).

max_production(demand, capacity, search_space, technologies, *, timeslice_level=None, **kwargs)[source]

Constructs constraint between capacity and maximum production.

Constrains the production decision variable by the maximum production for a given capacity.

minimum_service(demand, capacity, search_space, technologies, *, timeslice_level=None, **kwargs)[source]

Constructs constraint between capacity and minimum service.

register_constraints(function)[source]

Registers a constraint with MUSE.

See muse.constraints.

search_space(demand, capacity, search_space, technologies, **kwargs)[source]

Removes disabled technologies.

Utilities for adapting MUSE data structures to linear programming solvers.

This module provides utilities to convert MUSE’s xarray-based data structures to and from the format required by scipy’s linear programming solver.

class ScipyAdapter(c, capacity_template, production_template, bounds=(0, inf), A_ub=None, b_ub=None, A_eq=None, b_eq=None)[source]

Adapts MUSE data structures to scipy’s linear programming solver format.

This adapter converts data (costs and constraints) from xarray DataArrays to the format required by scipy’s linear programming solver, and back.

classmethod from_muse_data(capacity_costs, constraints, commodities, timeslice_level=None)[source]

Creates a ScipyAdapter from MUSE data structures.

property kwargs

Dictionary of kwargs for scipy.optimize.linprog.

to_muse(x)[source]

Convert scipy solver output back to MUSE format.

lp_constraint(constraint, lpcosts)[source]

Transforms the constraint to LP data.

The goal is to create from lpcosts.capacity, constraint.capacity, and constraint.b a 2d-matrix constraint vs decision variables.

  1. The dimensions of constraint.b are the constraint dimensions. They are

    renamed "c(xxx)".

  2. The dimensions of lpcosts are the decision-variable dimensions. They are

    renamed "d(xxx)".

  3. set(b.dims).intersection(lpcosts.xxx.dims) are diagonal

    in constraint dimensions and decision variables dimension, with xxx the capacity or the production

  4. set(constraint.xxx.dims) - set(lpcosts.xxx.dims) - set(b.dims) are reduced by

    summation, with xxx the capacity or the production

  5. set(lpcosts.xxx.dims) - set(constraint.xxx.dims) - set(b.dims) are added for

    expansion, with xxx the capacity or the production

See muse.lp_adapter.lp_constraint_matrix() for a more detailed explanation of the transformations applied here.

lp_constraint_matrix(b, constraint, lpcosts)[source]

Transforms one constraint block into an lp matrix.

The goal is to create from lpcosts, constraint, and b a 2d-matrix of constraints vs decision variables.

  1. The dimensions of b are the constraint dimensions. They are renamed

    "c(xxx)".

  2. The dimensions of lpcosts are the decision-variable dimensions. They are

    renamed "d(xxx)".

  3. set(b.dims).intersection(lpcosts.dims) are diagonal

    in constraint dimensions and decision variables dimension

  4. set(constraint.dims) - set(lpcosts.dims) - set(b.dims) are reduced by

    summation

  5. set(lpcosts.dims) - set(constraint.dims) - set(b.dims) are added for

    expansion

  6. set(b.dims) - set(constraint.dims) - set(lpcosts.dims) are added for

    expansion. Such dimensions only make sense if they consist of one point.

The result is the constraint matrix, expanded, reduced and diagonalized for the conditions above.

lp_costs(capacity_costs, commodities, timeslice_level=None)[source]

Creates dataset of costs for solving with scipy’s LP solver.

Importantly, this also defines the decision variables in the linear program.

The costs applied to the capacity decision variables are provided. This should have dimensions “asset” and “replacement”. In other words, capacity addition is solved for each replacement technology for each existing asset.

No cost is applied to the production decision variables. Thus, the production component of the resulting dataset is zero, with dimensions determining the production decision variables. This will have dimensions “asset”, “replacement”, “commodity”, and “timeslice”. In other words, production is solved for each replacement technology for each existing asset, for each commodity, and for each timeslice.

Parameters:
  • capacity_costs – DataArray with dimensions “asset” and “replacement” defining the costs of adding capacity to the system.

  • commodities – List of commodities to create production decision variables for.

  • timeslice_level – The timeslice level of the linear problem.

13.3.7. Initial and Final Asset Transforms

Pre and post hooks on agents.

asset_merge_factory(settings='new')[source]

Returns a function for merging new investments into assets.

Available merging functions should be registered with @register_final_asset_transform.

clean(agent, assets)[source]

Removes empty assets.

housekeeping_factory(settings='clean')[source]

Returns a function for performing initial housekeeping.

For instance, remove technologies with no capacity now or in the future. Available housekeeping functions should be registered with @register_initial_asset_transform.

merge_assets(old_assets, new_assets)[source]

Adds new assets to old along asset dimension.

New assets are assumed to be nonequivalent to any old_assets. Indeed, it is expected that the asset dimension does not have coordinates (i.e. it is a combination of coordinates, such as technology and installation year).

After merging the new assets, quantities are back-filled along the year dimension. Further missing values (i.e. future years the old_assets did not take into account) are set to zero.

new_assets_only(old_assets, new_assets)[source]

Returns newly invested assets and ignores old assets.

noop(agent, assets)[source]

Return assets as they are.

old_assets_only(old_assets, new_assets)[source]

Returns old assets and ignores newly invested assets.

register_final_asset_transform(function)[source]

Decorator to register a function to merge new investments into current assets.

The transform is applied a the very end of the agent iteration. It can be any function which takes as input the current set of assets, the new assets, and any number of keyword arguments. The function must return a “merge” of the two assets.

For instance, the new assets could completely replace the old assets (new_assets_only()), or they could be summed to the old assets (merge_assets()).

register_initial_asset_transform(function)[source]

Decorator to register a function for cleaning or transforming assets.

The transformation is applied at the start of each iteration. It any function which take an agent and assets as input and any number of keyword arguments, and returns the transformed assets. The agent should not be modified. It is only there to query the current year, the region, etc.

13.4. Reading the inputs

Ensemble of functions to read MUSE data.

read_settings(settings_file)[source]

Loads the input settings for any MUSE simulation.

Loads a MUSE settings file. This must be a TOML formatted file. Missing settings are loaded from the DEFAULT_SETTINGS. Custom Python modules, if present, are loaded and hooks are run to process and validate the settings and ensure that they are compatible with a MUSE simulation.

Parameters:

settings_file – A string or a Path to the settings file

Returns:

A dictionary with the settings

Ensemble of functions to read MUSE data.

In general, there are three functions per input file:

  • read_x: This is the overall function that is called to read the data. It takes a

    Path as input, and returns the relevant data structure (usually an xarray). The process is generally broken down into two functions that are called by read_x:

  • read_x_csv: This takes a path to a csv file as input and returns a pandas

    DataFrame. There are some consistency checks, such as checking data types and columns. There is also some minor processing at this stage, such as standardising column names, but no structural changes to the data. The general rule is that anything returned by this function should still be valid as an input file if saved to CSV.

  • process_x: This is where more major processing and reformatting of the data is

    done. It takes the DataFrame from read_x_csv and returns the final data structure (usually an xarray). There are also some more checks (e.g. checking for NaN values).

Most of the processing is shared by a few helper functions: - read_csv: reads a csv file and returns a dataframe - standardize_dataframe: standardizes the dataframe to a common format - create_multiindex: creates a multiindex from a dataframe - create_xarray_dataset: creates an xarray dataset from a dataframe

A few other helpers perform common operations on xarrays: - create_assets: creates assets from technologies - check_commodities: checks commodities and fills missing values

read_agent_parameters(path)[source]

Reads and processes agent parameters from a CSV file.

read_attribute_table(path)[source]

Reads and processes attribute table data from a CSV file.

read_csv(path, float_precision='high', required_columns=None, exclude_extra_columns=False, msg=None)[source]

Reads and standardizes a CSV file into a DataFrame.

Parameters:
  • path – Path to the CSV file

  • float_precision – Precision to use when reading floats

  • required_columns – List of column names that must be present (optional)

  • exclude_extra_columns – If True, exclude any columns not in required_columns list (optional). This can be important if extra columns can mess up the resulting xarray object.

  • msg – Message to log (optional)

Returns:

DataFrame containing the standardized data

read_existing_trade(path)[source]

Reads and processes existing trade data from a CSV file.

read_global_commodities(path)[source]

Reads and processes global commodities data from a CSV file.

read_initial_capacity(path)[source]

Reads and processes initial capacity data from a CSV file.

read_initial_market(projections_path, base_year_import_path=None, base_year_export_path=None, currency=None)[source]

Reads and processes initial market data.

Parameters:
  • projections_path – path to the projections file

  • base_year_import_path – path to the base year import file (optional)

  • base_year_export_path – path to the base year export file (optional)

  • currency – currency string (e.g. “USD”)

Returns:

Dataset containing initial market data.

Return type:

xr.Dataset

read_io_technodata(path)[source]

Reads and processes input/output technodata from a CSV file.

read_macro_drivers(path)[source]

Reads and processes macro drivers data from a CSV file.

read_presets(presets_paths)[source]

Reads and processes preset data from multiple CSV files.

Accepts a path pattern for presets files, e.g. Path(“path/to/*Consumption.csv”). The file name of each file must contain a year (e.g. “2020Consumption.csv”).

read_regression_parameters(path)[source]

Reads and processes regression parameters from a CSV file.

read_technodata_timeslices(path)[source]

Reads and processes technodata timeslices from a CSV file.

read_technodictionary(path)[source]

Reads and processes technodictionary data from a CSV file.

read_technologies(technodata_path, comm_out_path, comm_in_path, time_framework, interpolation_mode='linear', technodata_timeslices_path=None)[source]

Reads and processes technology data from multiple CSV files.

Will also interpolate data to the time framework if provided.

Parameters:
  • technodata_path – path to the technodata file

  • comm_out_path – path to the comm_out file

  • comm_in_path – path to the comm_in file

  • time_framework – list of years to interpolate data to

  • interpolation_mode – Interpolation mode to use

  • technodata_timeslices_path – path to the technodata_timeslices file

Returns:

Dataset containing the processed technology data. Any fields that differ by year will contain a “year” dimension interpolated to the time framework. Other fields will not have a “year” dimension.

Return type:

xr.Dataset

read_timeslice_shares(path)[source]

Reads and processes timeslice shares data from a CSV file.

read_trade_technodata(path)[source]

Reads and processes trade technodata from a CSV file.

13.5. Writing Outputs

13.5.1. Sinks

Sinks where output quantities can be stored.

Sinks take as argument a DataArray and store it somewhere. Additionally they take a dictionary as argument. This dictionary will always contains the items (‘quantity’, ‘sector’, ‘year’) referring to the name of the quantity, the name of the calling sector, the current year. They may contain additional parameters which depend on the actual sink, such as ‘filename’.

Optionally, a description of the storage (filename, etc) can be returned.

The signature of a sink is:

@register_output_sink(name="netcfd")
def to_netcfd(quantity: DataArray, config: Mapping) -> Optional[Text]:
    pass
exception FiniteResourceException[source]

Raised when a finite resource is exceeded.

OUTPUT_SINKS = {'Aggregate': <class 'muse.outputs.sinks.YearlyAggregate'>, 'Csv': <function to_csv>, 'Excel': <function to_excel>, 'FiniteResourceLogger': <function finite_resource_logger>, 'Nc': <function to_netcdf>, 'Netcdf': <function to_netcdf>, 'ToCsv': <function to_csv>, 'ToExcel': <function to_excel>, 'ToNetcdf': <function to_netcdf>, 'Xlsx': <function to_excel>, 'YearlyAggregate': <class 'muse.outputs.sinks.YearlyAggregate'>, 'Yearlyaggregate': <class 'muse.outputs.sinks.YearlyAggregate'>, 'aggregate': <class 'muse.outputs.sinks.YearlyAggregate'>, 'csv': <function to_csv>, 'excel': <function to_excel>, 'finite-resource-logger': <function finite_resource_logger>, 'finiteResourceLogger': <function finite_resource_logger>, 'finite_resource_logger': <function finite_resource_logger>, 'finiteresourcelogger': <function finite_resource_logger>, 'nc': <function to_netcdf>, 'netcdf': <function to_netcdf>, 'to-csv': <function to_csv>, 'to-excel': <function to_excel>, 'to-netcdf': <function to_netcdf>, 'toCsv': <function to_csv>, 'toExcel': <function to_excel>, 'toNetcdf': <function to_netcdf>, 'to_csv': <function to_csv>, 'to_excel': <function to_excel>, 'to_netcdf': <function to_netcdf>, 'tocsv': <function to_csv>, 'toexcel': <function to_excel>, 'tonetcdf': <function to_netcdf>, 'xlsx': <function to_excel>}

Stores a quantity somewhere.

OUTPUT_SINK_SIGNATURE

Signature of functions used to save quantities.

alias of Callable[[DataArray | DataFrame, int, Any], str | None]

class YearlyAggregate(final_sink=None, sector='', axis='year', **kwargs)[source]

Incrementally aggregates data from year to year.

register_output_sink(function=None)[source]

Registers a function to save quantities.

sink_to_file(suffix)[source]

Simplifies sinks to files.

The decorator takes care of figuring out the path to the file, as well as trims the configuration dictionary to include only parameters for the sink itself. The decorated function returns the path to the output file.

standardize_quantity(function)[source]

Helps standardize how the quantities are specified.

This decorator adds three keyword arguments to an input function:

The three functions are applied in the order given, assuming an input is specified.

to_csv(quantity, filename, **params)[source]

Saves data array to csv format, using pandas.to_csv.

Parameters:
  • quantity – The data to be saved

  • filename – File to which the data should be saved

  • params – A configuration dictionary accepting any argument to pandas.to_csv

to_excel(quantity, filename, **params)[source]

Saves data array to csv format, using pandas.to_excel.

Parameters:
  • quantity – The data to be saved

  • filename – File to which the data should be saved

  • params – A configuration dictionary accepting any argument to pandas.to_excel

to_netcdf(quantity, filename, **params)[source]

Saves data array to csv format, using xarray.to_netcdf.

Parameters:
  • quantity – The data to be saved

  • filename – File to which the data should be saved

  • params – A configuration dictionary accepting any argument to xarray.to_netcdf

13.5.2. Sectorial Outputs

Output quantities.

Functions that compute sectorial quantities for post-simulation analysis should all follow the same signature:

@register_output_quantity
def quantity(
    capacity: xr.DataArray,
    market: xr.Dataset,
) -> Union[xr.DataArray, DataFrame]:
    pass

They take as input the current capacity profile, aggregated across a sectoar, a dataset containing market-related quantities, and a dataset characterizing the technologies in the market. It returns a single xr.DataArray object.

The function should never modify it’s arguments.

OUTPUTS_PARAMETERS

Acceptable Datastructures for outputs parameters

alias of str | Mapping

OUTPUT_QUANTITIES = {'Capacity': <function capacity>, 'Consumption': <function consumption>, 'Costs': <function costs>, 'Supply': <function supply>, 'capacity': <function capacity>, 'consumption': <function consumption>, 'costs': <function costs>, 'supply': <function supply>}

Quantity for post-simulation analysis.

OUTPUT_QUANTITY_SIGNATURE

Signature of functions computing quantities for later analysis.

alias of Callable[[Dataset, DataArray, Dataset, Any], DataFrame | DataArray]

capacity(market, capacity, **kwargs)[source]

Current capacity.

consumption(market, capacity, sum_over=None, drop=None, **kwargs)[source]

Current consumption.

costs(market, capacity, sum_over=None, drop=None, *, commodities, **kwargs)[source]

Weighted average cost of production (filtered where total supply > 0).

factory(*parameters, sector_name='default')[source]

Creates outputs functions for post-mortem analysis.

Each parameter is a dictionary containing the following:

  • quantity (mandatory): name of the quantity to output. Mandatory.

  • sink (optional): name of the storage procedure, e.g. the file format or database format. When it cannot be guessed from filename, it defaults to “csv”.

  • filename (optional): path to a directory or a file where to store the quantity. In the latter case, if sink is not given, it will be determined from the file extension. The filename can incorporate markers. By default, it is “{default_output_dir}/{sector}{year}{quantity}{suffix}”.

  • any other parameter relevant to the sink, e.g. pandas.to_csv keyword arguments.

For simplicity, it is also possible to given lone strings as input. They default to {‘quantity’: string} (and the sink will default to “csv”).

register_output_quantity(function=None)[source]

Registers a function to compute an output quantity.

supply(market, capacity, sum_over=None, drop=None, **kwargs)[source]

Current supply.

13.5.3. Global Outputs

Output quantities.

Functions that compute MCA quantities for post-simulation analysis should all follow the same signature:

@register_output_quantity
def quantity(
    sectors: List[AbstractSector],
    market: xr.Dataset,
    year: int,
    **kwargs
) -> Union[pd.DataFrame, xr.DataArray]:
    pass

The function should never modify it’s arguments. It can return either a pandas dataframe or an xarray xr.DataArray.

OUTPUTS_PARAMETERS

Acceptable Datastructures for outputs parameters

alias of str | Mapping

OUTPUT_QUANTITIES = {'Capacity': <function capacity>, 'CapitalCosts': <function metric_capital_costs>, 'Consumption': <function consumption>, 'EAC': <function metric_eac>, 'Eac': <function metric_eac>, 'EmissionCosts': <function metric_emission_costs>, 'FuelCosts': <function metric_fuel_costs>, 'LCOE': <function metric_lcoe>, 'Lcoe': <function metric_lcoe>, 'MetricCapitalCosts': <function metric_capital_costs>, 'MetricEac': <function metric_eac>, 'MetricEmissionCosts': <function metric_emission_costs>, 'MetricFuelCosts': <function metric_fuel_costs>, 'MetricLcoe': <function metric_lcoe>, 'Prices': <function prices>, 'Supply': <function supply>, 'capacity': <function capacity>, 'capital-costs': <function metric_capital_costs>, 'capitalCosts': <function metric_capital_costs>, 'capital_costs': <function metric_capital_costs>, 'capitalcosts': <function metric_capital_costs>, 'consumption': <function consumption>, 'emission-costs': <function metric_emission_costs>, 'emissionCosts': <function metric_emission_costs>, 'emission_costs': <function metric_emission_costs>, 'emissioncosts': <function metric_emission_costs>, 'fuel-costs': <function metric_fuel_costs>, 'fuelCosts': <function metric_fuel_costs>, 'fuel_costs': <function metric_fuel_costs>, 'fuelcosts': <function metric_fuel_costs>, 'metric-capital-costs': <function metric_capital_costs>, 'metric-eac': <function metric_eac>, 'metric-emission-costs': <function metric_emission_costs>, 'metric-fuel-costs': <function metric_fuel_costs>, 'metric-lcoe': <function metric_lcoe>, 'metricCapitalCosts': <function metric_capital_costs>, 'metricEac': <function metric_eac>, 'metricEmissionCosts': <function metric_emission_costs>, 'metricFuelCosts': <function metric_fuel_costs>, 'metricLcoe': <function metric_lcoe>, 'metric_capital_costs': <function metric_capital_costs>, 'metric_eac': <function metric_eac>, 'metric_emission_costs': <function metric_emission_costs>, 'metric_fuel_costs': <function metric_fuel_costs>, 'metric_lcoe': <function metric_lcoe>, 'metriccapitalcosts': <function metric_capital_costs>, 'metriceac': <function metric_eac>, 'metricemissioncosts': <function metric_emission_costs>, 'metricfuelcosts': <function metric_fuel_costs>, 'metriclcoe': <function metric_lcoe>, 'prices': <function prices>, 'supply': <function supply>}

Quantity for post-simulation analysis.

OUTPUT_QUANTITY_SIGNATURE

Signature of functions computing quantities for later analysis.

alias of Callable[[Dataset, list[AbstractSector], Any], DataArray | DataFrame]

capacity(market, sectors, year, **kwargs)[source]

Current capacity across all sectors.

consumption(market, sectors, year, **kwargs)[source]

Current consumption.

factory(*parameters)[source]

Creates outputs functions for post-mortem analysis.

Each parameter is a dictionary containing the following:

  • quantity (mandatory): name of the quantity to output. Mandatory.

  • sink (optional): name of the storage procedure, e.g. the file format or database format. When it cannot be guessed from filename, it defaults to “csv”.

  • filename (optional): path to a directory or a file where to store the quantity. In the latter case, if sink is not given, it will be determined from the file extension. The filename can incorporate markers. By default, it is “{default_output_dir}/{sector}{year}{quantity}{suffix}”.

  • any other parameter relevant to the sink, e.g. pandas.to_csv keyword arguments.

For simplicity, it is also possible to give lone strings as input. They default to {‘quantity’: string} (and the sink will default to “csv”).

metric_capital_costs(market, sectors, year, **kwargs)[source]

Current capital costs across all sectors.

metric_eac(market, sectors, year, **kwargs)[source]

Current emission costs across all sectors.

metric_emission_costs(market, sectors, year, **kwargs)[source]

Current emission costs across all sectors.

metric_fuel_costs(market, sectors, year, **kwargs)[source]

Current fuel costs across all sectors.

metric_lcoe(market, sectors, year, **kwargs)[source]

Current lifetime levelised cost across all sectors.

prices(market, sectors, year, **kwargs)[source]

Current MCA market prices.

register_output_quantity(function=None)[source]

Registers a function to compute an output quantity.

sector_capacity(sector)[source]

Sector capacity with agent annotations.

sector_capital_costs(sector, market, year, **kwargs)[source]

Sector capital costs with agent annotations.

sector_eac(sector, market, year, **kwargs)[source]

Net Present Value of technologies over their lifetime.

sector_emission_costs(sector, market, year, **kwargs)[source]

Sector emission costs with agent annotations.

sector_fuel_costs(sector, market, year, **kwargs)[source]

Sector fuel costs with agent annotations.

sector_lcoe(sector, market, year, **kwargs)[source]

Levelized cost of energy () of technologies over their lifetime.

supply(market, sectors, year, **kwargs)[source]

Current supply.

13.5.4. Cache

Output cached quantities.

Functions that output the state of diverse quantities at intermediate steps of the calculation.

The core of the method is the OutputCache class that initiated by the MCA with input parameters defined in the TOML file, much like the existing output options but in a outputs_cache list, enables listening for data to be cached and, after each period, saved into disk via the consolidate_cache method.

Anywhere in the code, you can write:

cache_quantity(quantity_name=some_data)

If the quantity has been set as something to cache, the data will be stored and, eventually, save to disk after - possibly - aggregating the data and removing those entries corresponding to non-convergent investment attempts. This process of cleaning and aggregation is quantity specific.

See documentation for the muse.outputs.cache.cache_quantity() function as well as how to setup the toml input file to cache quantities. Users can customize and create further output quantities by registering with MUSE via muse.outputs.cache.register_cached_quantity().

CACHE_TOPIC_CHANNEL = 'cache_quantity'

Topic channel to use with the pubsub messaging system.

OUTPUT_QUANTITIES = {'Capacity': <function capacity>, 'Lcoe': <function lcoe>, 'LifetimeLevelizedCostOfEnergy': <function lcoe>, 'Production': <function production>, 'capacity': <function capacity>, 'lcoe': <function lcoe>, 'lifetime-levelized-cost-of-energy': <function lcoe>, 'lifetimeLevelizedCostOfEnergy': <function lcoe>, 'lifetime_levelized_cost_of_energy': <function lcoe>, 'lifetimelevelizedcostofenergy': <function lcoe>, 'production': <function production>}

Quantity for post-simulation analysis.

OUTPUT_QUANTITY_SIGNATURE

Signature of functions computing quantities for later analysis.

alias of Callable[[list[DataArray]], DataArray | DataFrame]

class OutputCache(*parameters, output_quantities=None, sectors=None, topic='cache_quantity')[source]

Creates outputs functions for post-mortem analysis of cached quantities.

Each parameter is a dictionary containing the following:

  • quantity (mandatory): name of the quantity to output. Mandatory.

  • sink (optional): name of the storage procedure, e.g. the file format or database format. When it cannot be guessed from filename, it defaults to “csv”.

  • filename (optional): path to a directory or a file where to store the quantity. In the latter case, if sink is not given, it will be determined from the file extension. The filename can incorporate markers. By default, it is “{default_output_dir}/{sector}{year}{quantity}{suffix}”.

  • any other parameter relevant to the sink, e.g. pandas.to_csv keyword arguments.

For simplicity, it is also possible to given lone strings as input. They default to {‘quantity’: string} (and the sink will default to “csv”).

Raises:

ValueError – If unknown quantities are requested to be cached.

cache(data)[source]

Caches the data into memory.

If the quantity has not been selected to be cached when configuring the MUSE simulation, it will be silently ignored if present as an input to this function.

Parameters:
  • data (Mapping[str, xr.DataArray]) – Dictionary with the quantities and

  • save. (DataArray values to)

consolidate_cache(year)[source]

Save the cached data into disk and flushes cache.

This method is meant to be called after each time period in the main loop of the MCA, just after market and sector quantities are saved.

Parameters:

year (int) – Year of interest.

cache_quantity(function=None, quantity=None, **kwargs)[source]

Cache one or more quantities to be post-processed later on.

This function can be used as a decorator, in which case the quantity input argument must be set, or directly called with any number of keyword arguments. In the former case, the matching between quantities and values to cached is done by the function ‘match_quantities’. When used in combination with other decorators, care must be taken to decide the order in which they are applied to make sure the appropriate output is cached.

Note that if the quantity has NOT been selected to be cached when configuring the MUSE simulation, it will be silently ignored if present as an input to this function.

Example

As a decorator, the quantity argument must be set:

>>> @cache_quantity(quantity="capacity")
... def some_calculation():
...     return xr.DataArray()

If returning a sequence of DataArrays, the number of quantities to record must be the same as the number of arrays. They are paired in the same order they are given and the ‘name’ attribute of the arrays, if present, is ignored.

>>> @cache_quantity(quantity=["capacity", "production"])
... def other_calculation():
...     return xr.DataArray(), xr.DataArray()

For a finer control of what is cached when there is a complex output, combine the DataArrays in a Dataset. In this case, the ‘quantity’ input argument can be either a string or a sequence of strings to record multiple variables in the Dataset.

>>> @cache_quantity(quantity=["capacity", "production"])
... def and_another_one():
...     return xr.Dataset(
...         {
...             "not cached": xr.DataArray(),
...             "capacity": xr.DataArray(),
...             "production": xr.DataArray(),
...         }
...     )

When this function is called directly and not used as a decorator, simply provide the name of the quantities and the DataArray to record as keyword arguments:

>>> cache_quantity(capacity=xr.DataArray(), production=xr.DataArray())
Parameters:
  • function (Optional[Callable]) – The decorated function, if any. Its output must be a DataArray, a sequence of DataArray or a Dataset. See ‘match_quantities’

  • quantity (Union[str, List[str], None]) – The name of the quantities to record.

  • **kwargs (xr.DataArray) – Keyword arguments of the form ‘quantity_name=quantity_value’.

Raises:
  • ValueError – If a function input argument is provided at the same time than

  • keyword arguments.

Returns:

(Optional[Callable]) The decorated function (or a dummy function if called directly).

capacity(cached, agents, **kwargs)[source]

Consolidates the cached capacities into a single DataFrame to save.

Parameters:
  • cached (List[xr.DataArray]) – The list of cached arrays

  • agents (MutableMapping[Text, MutableMapping[Text, Text]]) – Agents’ metadata.

  • kwargs – unused.

Returns:

DataFrame with the consolidated data.

Return type:

pd.DataFrame

consolidate_quantity(quantity, cached, agents)[source]

Consolidates the cached quantity into a single DataFrame to save.

Parameters:
  • quantity (Text) – The quantity to cache.

  • cached (List[xr.DataArray]) – The list of cached arrays

  • agents (MutableMapping[Text, MutableMapping[Text, Text]]) – Agents’ metadata.

Returns:

DataFrame with the consolidated data.

Return type:

pd.DataFrame

extract_agents(sectors)[source]

_summary_.

Parameters:

sectors (List[AbstractSector]) – _description_

Returns:

_description_

Return type:

Mapping[Text, Text]

extract_agents_internal(sector)[source]

Extract simple agent metadata from a sector.

Parameters:

sector (AbstractSector) – Sector to extract the metadata from.

Returns:

A dictionary with the uuid of each agent as keys and a dictionary with the name, agent type and agent sector as values.

Return type:

Mapping[Text, Text]

lcoe(cached, agents, **kwargs)[source]

Consolidates the cached LCOE into a single DataFrame to save.

Parameters:
  • cached (List[xr.DataArray]) – The list of cached arrays

  • agents (MutableMapping[Text, MutableMapping[Text, Text]]) – Agents’ metadata.

  • kwargs – unused.

Returns:

DataFrame with the consolidated data.

Return type:

pd.DataFrame

match_quantities(quantity, data)[source]

Matches the quantities with the corresponding data.

The possible name attribute in the DataArrays is ignored.

Parameters:
  • quantity (Union[str, Sequence[str]]) – The name(s) of the quantity(ies) to cache.

  • data (Union[xr.DataArray, xr.Dataset, Sequence[xr.DataArray]]) – The structure containing the data to cache.

Raises:
  • TypeError – If there is an invalid combination of input argument types.

  • ValueError – If the number of quantities does not match the length of the data.

  • KeyError – If the required quantities do not exist as variables in the dataset.

Returns:

(Mapping[str, xr.DataArray]) A dictionary matching the quantity names with the corresponding data.

production(cached, agents, **kwargs)[source]

Consolidates the cached production into a single DataFrame to save.

Parameters:
  • cached (List[xr.DataArray]) – The list of cached arrays

  • agents (MutableMapping[Text, MutableMapping[Text, Text]]) – Agents’ metadata.

  • kwargs – unused.

Returns:

DataFrame with the consolidated data.

Return type:

pd.DataFrame

register_cached_quantity(function)[source]

Registers a function to compute an output quantity.

13.6. Quantities

Collection of functions to compute model quantities.

This module is meant to collect functions computing quantities of interest to the model, e.g. maximum production for a given capacity, etc, especially where these functions are used in different areas of the model.

Functions for calculating costs (e.g. LCOE, EAC) are in the costs module.

capacity_in_use(production, technologies, max_dim='commodity', timeslice_level=None, **filters)[source]

Capacity-in-use for each asset, given production.

Conceptually, this operation is the inverse of production.

Parameters:
  • production – Production from each technology of interest.

  • technologies – xr.Dataset describing the features of the technologies of interests. It should contain fixed_outputs and utilization_factor.

  • max_dim – reduces the given dimensions using max. Defaults to “commodity”. If None, then no reduction is performed.

  • filters – keyword arguments are used to filter down the capacity and technologies. Filters not relevant to the quantities of interest, i.e. filters that are not a dimension of capacity or technologies, are silently ignored.

  • timeslice_level – the desired timeslice level of the result (e.g. “hour”, “day”)

Returns:

Capacity-in-use for each technology, whittled down by the filters.

capacity_to_service_demand(demand, technologies, timeslice_level=None)[source]

Minimum capacity required to fulfill the demand.

consumption(technologies, production, prices=None, timeslice_level=None)[source]

Commodity consumption when fulfilling the whole production.

Firstly, the degree of technology activity is calculated (i.e. the amount of technology flow required to meet the production). Then, the consumption of fixed commodities is calculated in proportion to this activity.

In addition, if there are flexible inputs, then the single lowest-cost option is selected (minimising price * quantity). If prices are not given, then flexible consumption is not considered.

Parameters:
  • technologies – Dataset of technology parameters. Must contain fixed_inputs, flexible_inputs, and fixed_outputs.

  • production – DataArray of production data. Must have “timeslice” and “commodity” dimensions.

  • prices – DataArray of prices for each commodity. Must have “timeslice” and “commodity” dimensions. If not given, then flexible inputs are not considered.

  • timeslice_level – the desired timeslice level of the result (e.g. “hour”, “day”)

Returns:

A data array containing the consumption of each commodity. Will have the same dimensions as production.

emission(production, technologies, timeslice_level=None)[source]

Computes emission from current products.

Parameters:
  • production – Commodity-level production for a series of assets.

  • technologiestechnologies dataset containing fixed_output.

  • timeslice_level – the desired timeslice level of the result (e.g. “hour”, “day”)

Returns:

A data array containing emissions (and only emissions).

maximum_production(technologies, capacity, timeslice_level=None, **filters)[source]

Production for a given capacity.

Given a capacity \(\mathcal{A}_{t, \iota}^r\), the utilization factor \(\alpha^r_{t, \iota}\) and the the fixed outputs of each technology \(\beta^r_{t, \iota, c}\), then the result production is:

\[P_{t, \iota}^r = \alpha^r_{t, \iota}\beta^r_{t, \iota, c}\mathcal{A}_{t, \iota}^r\]

The dimensions above are only indicative. The function should work with many different input values, e.g. with capacities expanded over time-slices \(t\) or agents \(i\).

Parameters:
  • capacity – Capacity of each technology of interest. In practice, the capacity can refer to asset capacity, the max capacity, or the capacity-in-use.

  • technologies – xr.Dataset describing the features of the technologies of interests. It should contain fixed_outputs and utilization_factor.

  • filters – keyword arguments are used to filter down the capacity and technologies. Filters not relevant to the quantities of interest, i.e. filters that are not a dimension of capacity or technologies, are silently ignored.

  • timeslice_level – the desired timeslice level of the result (e.g. “hour”, “day”)

Returns:

capacity * fixed_outputs * utilization_factor, whittled down according to the filters and the set of technologies in capacity.

minimum_production(technologies, capacity, timeslice_level=None, **filters)[source]

Minimum production for a given capacity.

Given a capacity \(\mathcal{A}_{t, \iota}^r\), the minimum service factor \(\alpha^r_{t, \iota}\) and the the fixed outputs of each technology \(\beta^r_{t, \iota, c}\), then the result production is:

\[P_{t, \iota}^r = \alpha^r_{t, \iota}\beta^r_{t, \iota, c}\mathcal{A}_{t, \iota}^r\]

The dimensions above are only indicative. The function should work with many different input values, e.g. with capacities expanded over time-slices \(t\) or agents \(i\).

Parameters:
  • capacity – Capacity of each technology of interest. In practice, the capacity can refer to asset capacity, the max capacity, or the capacity-in-use.

  • technologies – xr.Dataset describing the features of the technologies of interests. It should contain fixed_outputs and minimum_service_factor.

  • timeslices – xr.DataArray of the timeslicing scheme. Production data will be returned in this format.

  • filters – keyword arguments are used to filter down the capacity and technologies. Filters not relevant to the quantities of interest, i.e. filters that are not a dimension of capacity or technologies, are silently ignored.

  • timeslice_level – the desired timeslice level of the result (e.g. “hour”, “day”)

Returns:

capacity * fixed_outputs * minimum_service_factor, whittled down according to the filters and the set of technologies in capacity.

production_amplitude(production, technologies)[source]

Calculates the degree of technology activity based on production data.

We do this by dividing the production data by the output flow per unit of activity. Taking the max of this across all commodities, we get the minimum units of technology activity required to meet (at least) the specified production of all commodities.

For example: A technology has the following reaction: 1A -> 2B + 3C If production is 4B & 6C, this is equal to a production amplitude of 2

Parameters:
  • production – DataArray with commodity-level production for a set of technologies. Must have timeslice and commodity dimensions. May also have other dimensions e.g. region, year, etc.

  • technologies – Dataset of technology parameters

Returns:

DataArray with production amplitudes for each technology in each timeslice. Will have the same dimensions as production, minus the commodity dimension.

13.7. Demand Matching Algorithm

Collection of demand-matching algorithms.

At it’s simplest, the demand matching algorithm solves the following problem,

  • given a demand for a commodity \(D_d\), with \(d\in\mathcal{D}\)

  • given processes to supply these commodities, with an associated cost per process, \(C_{d, i}\), with \(i\in\mathcal{I}\)

Match demand and supply while minimizing the associated cost.

\[ \begin{align}\begin{aligned}\min_{X} \sum_{d, i} C_{d,i} X_{d, i}\\X_{d, i} \geq 0\\\sum_o X_o \geq D_d\end{aligned}\end{align} \]

The basic algorithm proceeds as follows:

  1. sort all costs \(C_{d, i}\) across both \(d\) and \(i\)

  2. for each cost \(c_0\) in order:

    1. find the set of indices \(\mathcal{C}\subseteq\mathcal{D}\cup\mathcal{I}\) for which

      \[\forall (d, i) \in \mathcal{C}\quad C_{d, i} == c_0\]
    2. determine the partial result for the current cost

      \[\forall (d, i) \in \mathcal{C}\quad X_{d, i} = \frac{D_d}{|i\in\mathcal{C}|}\]

      Where \(|i\in\mathcal{C}|\) indicates the number of indices \(i\) in \(\mathcal{C}\).

However, in practice, the problem to solve often contains constraints, e.g. a constraint on production \(\sum_d X_{d, i} \leq M_i\). The algorithms in this module try and solve these constrained problems one way or another.

demand_matching(demand, cost, *constraints, protected_dims=None)[source]

Demand matching over heterogeneous dimensions.

This algorithm enables demand matching while enforcing constraints on how much an asset can produce. Any set of dimensions can be matched. The algorithm is general with respect to the dimensions in demand and cost. It also enforces constraints over sets of indices.

\[ \begin{align}\begin{aligned}\min_{X} \sum_{d, i} C_{d, i} X_{d, i}\\X_{d, i} \geq 0\\\sum_i X_{d, i} \geq D_d\\M_{(d, i) \in \mathcal{R}^{(\alpha)}}^{(\alpha)} \geq \sum_{(d, i)\notin\mathcal{R}^{(\alpha)}} X_{d, i}\end{aligned}\end{align} \]

Where \(\alpha\) is an index running over constraints, \(\mathcal{R}^{(\alpha)}\subseteq\mathcal{D}\cup\mathcal{I}\) is a subset of indices.

The algorithm proceeds as described in muse.demand_matching. However, an extra step is added to ensure that the solutions falls within the convex-hull formed by the constraints. This projects the current solution onto the constraint. Hence, the solution will depend on the order in which the constraints are given.

  1. sort all costs \(C_{d, m}\) across both \(d\) and \(m\)

  2. for each cost \(c_0\) in order:

    1. find the set of indices \(\mathcal{C}\)

      \[ \begin{align}\begin{aligned}\mathcal{C}\subseteq\mathcal{D}\cup\mathcal{I}\\\forall (d, i) \in \mathcal{C}\quad C_{d, i} == c_0\end{aligned}\end{align} \]
    2. determine an interim partial result for the current cost

      \[\forall (d, i) \in \mathcal{C}\quad \delta X_{d, i} = \frac{1}{|i\in\mathcal{C}|}\left( D_d - \sum_{j \in \mathcal{I}} X_{d, j}\right)\]

      Where \(|i\in\mathcal{C}|\) indicates the number of \(i\) indices in \(\mathcal{C}\). The expression in the parenthesis is the currently unserviced demand.

    3. Loop over each constraint \(\alpha\). Below we drop the index \(\alpha\) over constraints for simplicity.

      1. Determine the excess over the constraint:

        \[E_{(d, i) \in \mathcal{R}} = \max\left\{ 0, \sum_{(d, i)\notin\mathcal{R}}\left( X_{d, i} + \delta X_{d, i} \right) - M_{(d, i) \in \mathcal{R}} \right\}\]
      2. Correct \(\delta X\) as follows:

        \[ \begin{align}\begin{aligned}\forall (d, i) \in \mathcal{C}\cap\mathcal{R}\quad \delta X\prime_{d, i} = E_{(d, i)} \frac{\delta X_{(d, i)}}{ \sum_{(e, j)\in \mathcal{C}\cap\mathcal{R}} \delta X_{(e,j)} }\\ \forall (d, i) \notin \mathcal{R}, (d, i)\in\mathcal{C} \quad \delta X\prime_{d, i} = 0\end{aligned}\end{align} \]
      3. Set \(\delta X = \max(0, \delta X - \delta X\prime)\)

A more complex problem would see independent dimensions for each quantity. In that, case we can reduce to the original problem as shown here

\[ \begin{align}\begin{aligned}C_{d, i, c} = \min_cC\prime_{d, i, c}\\D_d = \sum_{d\prime} D\prime_{d, d\prime}\\M_r = \sum_m M\prime_{r, m}\\X_{d, d\prime, i, m, c} = \left(C\prime_{d, i, c} == C_{d, i}\right) \frac{M\prime_{r, m}}{M_r} \frac{D\prime_{d, d\prime}}{D_d} X_{d, i}\end{aligned}\end{align} \]

A dimension could be shared by all quantities, in which case each point along that dimension is treated as independent.

Similarly, if a dimension is shared only by the demand and a constraint but not by the cost, then the problem can be reduced a set of problems independent along that direction.

Parameters:
  • demand – Demand to match with production. It should have the same physical units as max_production.

  • cost – Cost to minimize while fulfilling the demand.

  • *constraints – each item is a separate constraint \(M_r\).

  • protected_dims – Dimensions that will not be modified

Returns:

An array with the joint dimensionality of max_production, cost, and demand, containing the supply that fulfills the demand. The units of this supply are the same as demand and max_production.

13.8. Miscellaneous

13.8.1. Timeslices

Timeslice utility functions.

broadcast_timeslice(data, ts=None, level=None)[source]

Convert a non-timesliced array to a timesliced array by broadcasting.

If data is already timesliced in the appropriate scheme, it will be returned unchanged.

Parameters:
  • data – Array to broadcast.

  • ts – Dataarray with timeslice weights. If None, defaults to the global timeslice.

  • level – Level to broadcast to. If None, use the finest level of ts.

compress_timeslice(data, ts=None, level=None, operation='sum')[source]

Convert a fully timesliced array to a coarser level.

The operation can be either ‘sum’, or ‘mean’:

  • sum: sum values at each compressed timeslice level

  • mean: take a weighted average of values at each compressed timeslice level,

    according to the timeslice weights in ts

Parameters:
  • data – Timesliced array to compress. Must have the same timeslicing as ts.

  • ts – Dataarray with timeslice weights. If None, defaults to the global timeslice.

  • level – Level to compress to. If None, don’t compress.

  • operation – Operation to perform (“sum” or “mean”). Defaults to “sum”.

distribute_timeslice(data, ts=None, level=None)[source]

Convert a non-timesliced array to a timesliced array by distribution.

Takes non-timesliced data and distributes it over the timeslice dimension according to the timeslice weights in ts. The sum of the output over all timeslices will be equal to the input. If data is already timesliced in the appropriate scheme, it will be returned unchanged.

Parameters:
  • data – Array to distribute.

  • ts – Dataarray with timeslice weights. If None, defaults to the global timeslice.

  • level – Level to distribute to. If None, use the finest level of ts.

drop_timeslice(data)[source]

Drop the timeslice variable from a DataArray.

If the array doesn’t contain the timeslice variable, return the input unchanged.

expand_timeslice(data, ts=None, operation='distribute')[source]

Convert a timesliced array to a finer level.

The operation can be either ‘distribute’, or ‘broadcast’

  • distribute: distribute values over the new timeslice level(s) according to

    timeslice weights in ts, such that the sum of the output over all timeslices is equal to the sum of the input

  • broadcast: broadcast values across over the new timeslice level(s)

Parameters:
  • data – Timesliced array to expand.

  • ts – Dataarray with timeslice weights. If None, defaults to the global timeslice.

  • operation – Operation to perform (“distribute” or “broadcast”). Defaults to “distribute”.

get_level(data)[source]

Get the timeslice level of a DataArray.

setup_module(settings)[source]

Sets up module singletons.

sort_timeslices(data, ts=None)[source]

Sorts the timeslices of a DataArray according to a reference timeslice.

This will only sort timeslices to match the reference if the data is at the same timeslice level as the reference. Otherwise, it will sort timeslices in alphabetical order.

Parameters:
  • data – Timesliced DataArray to sort.

  • ts – Dataarray with reference timeslices in the appropriate order

timeslice_max(data, ts=None)[source]

Find the max value over the timeslice dimension, normalized for timeslice length.

This first annualizes the value in each timeslice by dividing by the fraction of the year that the timeslice occupies, then takes the maximum value

Parameters:
  • data – Timesliced DataArray to find the max of.

  • ts – Dataarray with relative timeslice lengths. If None, defaults to the global

  • timeslice.

13.8.2. Commodities

Methods and types around commodities.

class CommodityUsage(value)[source]

Flags to specify the different kinds of commodities.

For details on how enum’s work, see python’s documentation. In practice, CommodityUsage centralizes in one place the different kinds of commodities that are meaningful to the generalized sector, e.g. commodities that are consumed by the sector, and commodities that produced by the sectors, as well commodities that are, somehow, environmental.

With the exception of CommodityUsage.OTHER, flags can be combined in any fashion. CommodityUsage.PRODUCT | CommodityUsage.CONSUMABLE is a commodity that is both consumed and produced by a sector. CommodityUsage.ENVIRONMENTAL | CommodityUsage.ENERGY | CommodityUsage.CONSUMABLE is an environmental energy commodity consumed by the sector.

CommodityUsage.OTHER is an alias for no flag. It is meant for commodities that should be ignored by the sector.

CONSUMABLE = 1

Commodity which can be consumed by the sector.

ENERGY = 8

Commodity which is a fuel for this or another sector.

ENVIRONMENTAL = 4

Commodity which is a pollutant.

OTHER = 0

Not relevant for current sector.

PRODUCT = 2

Commodity which can be produced by the sector.

property name

Hack to get the name of the flag consistently across python versions.

check_usage(data, flag, match='all')[source]

Match usage flags with input data array.

Parameters:
  • data – sequence for which to match flags elementwise.

  • flag – flag or combination of flags to match. The input can be a string, such as “product | environmental”, or a CommodityUsage instance. Defaults to “other”.

  • match – one of: - “all”: should all flag match. Default. - “any”, should match at least one flags. - “exact”, should match each flag and nothing else.

Examples

>>> from muse.commodities import CommodityUsage, check_usage
>>> data = [
...     CommodityUsage.OTHER,
...     CommodityUsage.PRODUCT,
...     CommodityUsage.ENVIRONMENTAL | CommodityUsage.PRODUCT,
...     CommodityUsage.ENVIRONMENTAL,
... ]

Matching “all”:

>>> check_usage(data, CommodityUsage.PRODUCT).tolist()
[False, True, True, False]
>>> check_usage(data, CommodityUsage.ENVIRONMENTAL).tolist()
[False, False, True, True]
>>> check_usage(
...     data, CommodityUsage.ENVIRONMENTAL | CommodityUsage.PRODUCT
... ).tolist()
[False, False, True, False]

Matching “any”:

>>> check_usage(data, CommodityUsage.PRODUCT, match="any").tolist()
[False, True, True, False]
>>> check_usage(data, CommodityUsage.ENVIRONMENTAL, match="any").tolist()
[False, False, True, True]
>>> check_usage(data, "environmental | product", match="any").tolist()
[False, True, True, True]

Matching “exact”:

>>> check_usage(data, "PRODUCT", match="exact").tolist()
[False, True, False, False]
>>> check_usage(data, CommodityUsage.ENVIRONMENTAL, match="exact").tolist()
[False, False, False, True]
>>> check_usage(data, "ENVIRONMENTAL | PRODUCT", match="exact").tolist()
[False, False, True, False]

Finally, checking no flags has been set can be done with:

>>> check_usage(data, CommodityUsage.OTHER, match="exact").tolist()
[True, False, False, False]
>>> check_usage(data, None, match="exact").tolist()
[True, False, False, False]
is_consumable(data)[source]

Any consumable.

Examples

>>> from muse.commodities import CommodityUsage, is_consumable
>>> data = [
...     CommodityUsage.CONSUMABLE,
...     CommodityUsage.PRODUCT,
...     CommodityUsage.ENVIRONMENTAL,
...     CommodityUsage.PRODUCT | CommodityUsage.CONSUMABLE,
...     CommodityUsage.ENVIRONMENTAL | CommodityUsage.PRODUCT,
... ]
>>> is_consumable(data).tolist()
[True, False, False, True, False]
is_enduse(data)[source]

Non-environmental product.

Examples

>>> from muse.commodities import CommodityUsage, is_enduse
>>> data = [
...     CommodityUsage.CONSUMABLE,
...     CommodityUsage.PRODUCT,
...     CommodityUsage.ENVIRONMENTAL,
...     CommodityUsage.PRODUCT | CommodityUsage.CONSUMABLE,
...     CommodityUsage.ENVIRONMENTAL | CommodityUsage.PRODUCT,
... ]
>>> is_enduse(data).tolist()
[False, True, False, True, False]
is_fuel(data)[source]

Any consumable energy.

Examples

>>> from muse.commodities import CommodityUsage, is_fuel
>>> data = [
...     CommodityUsage.CONSUMABLE,
...     CommodityUsage.PRODUCT,
...     CommodityUsage.ENERGY,
...     CommodityUsage.ENERGY | CommodityUsage.CONSUMABLE,
...     CommodityUsage.ENERGY | CommodityUsage.CONSUMABLE
...         | CommodityUsage.ENVIRONMENTAL,
...     CommodityUsage.ENERGY | CommodityUsage.CONSUMABLE
...         | CommodityUsage.PRODUCT,
...     CommodityUsage.ENERGY | CommodityUsage.PRODUCT,
... ]
>>> is_fuel(data).tolist()
[False, False, False, True, True, True, False]
is_material(data)[source]

Any non-energy non-environmental consumable.

Examples

>>> from muse.commodities import CommodityUsage, is_material
>>> data = [
...     CommodityUsage.CONSUMABLE,
...     CommodityUsage.PRODUCT,
...     CommodityUsage.ENERGY,
...     CommodityUsage.ENERGY | CommodityUsage.CONSUMABLE,
...     CommodityUsage.CONSUMABLE | CommodityUsage.ENVIRONMENTAL,
...     CommodityUsage.ENERGY | CommodityUsage.CONSUMABLE
...         | CommodityUsage.PRODUCT,
...     CommodityUsage.CONSUMABLE | CommodityUsage.PRODUCT,
... ]
>>> is_material(data).tolist()
[True, False, False, False, False, False, True]
is_other(data)[source]

No flags are set.

Examples

>>> from muse.commodities import CommodityUsage, is_other
>>> data = [
...     CommodityUsage.OTHER,
...     CommodityUsage.PRODUCT,
...     CommodityUsage.PRODUCT | CommodityUsage.OTHER,
... ]
>>> is_other(data).tolist()
[True, False, False]
is_pollutant(data)[source]

Environmental product.

Examples

>>> from muse.commodities import CommodityUsage, is_pollutant
>>> data = [
...     CommodityUsage.CONSUMABLE,
...     CommodityUsage.PRODUCT,
...     CommodityUsage.ENVIRONMENTAL,
...     CommodityUsage.PRODUCT | CommodityUsage.CONSUMABLE,
...     CommodityUsage.ENVIRONMENTAL | CommodityUsage.PRODUCT,
... ]
>>> is_pollutant(data).tolist()
[False, False, False, False, True]
setup_module(commodities_path)[source]

Sets up global commodities.

13.8.3. Regression functions

Functions and functors to compute macro-drivers.

class Exponential(interpolation='linear', base_year=2010, **kwargs)[source]

Regression function: exponential

This functor is a regression function registered with MUSE as ‘exponential’.

class ExponentialAdj(interpolation='linear', base_year=2010, **kwargs)[source]

Regression function: exponentialadj

This functor is a regression function registered with MUSE as ‘exponentialadj’.

class Linear(interpolation='linear', base_year=2010, **kwargs)[source]

a * population + b * (gdp - gdp[2010]/population[2010] * population).

class Logistic(interpolation='linear', base_year=2010, **kwargs)[source]

Regression function: logistic

This functor is a regression function registered with MUSE as ‘logistic’.

class LogisticSigmoid(interpolation='linear', base_year=2010, **kwargs)[source]

Regression function: logisticsigmoid

This functor is a regression function registered with MUSE as ‘logisticsigmoid’.

class Loglog(interpolation='linear', base_year=2010, **kwargs)[source]

Regression function: loglog

This functor is a regression function registered with MUSE as ‘loglog’.

endogenous_demand(regression_parameters, drivers, sector=None, **kwargs)[source]

Endogenous demand based on macro drivers and regression parameters.

factory(regression_parameters, sector=None)[source]

Creates regression functor from standard MUSE data for given sector.

register_regression(Functor=None, name=None)[source]

Registers a functor with MUSE regressions.

Regression functors are registered with MUSE so that the functors can be called easily on created.

functor name that the functor is registered with defaults to the snake_case version of the functor name. However, it can also be specified explicitly as a keyword argument. In any case, it must be unique amongst all registered regression functor.

13.8.4. Functionality Registration

Registrators that allow pluggable data to logic transforms.

registrator(decorator=None, registry=None, logname=None, loglevel='Debug')[source]

A decorator to create a decorator that registers functions with MUSE.

This is a decorator that takes another decorator as an argument. Hence it returns a decorator. It simplifies and standardizes creating decorators to register functions with muse.

The registrator expects as non-optional keyword argument a registry where the resulting decorator will register functions.

Furthermore, the final function (the one passed to the decorator passed to this function) will emit a standardized log-call.

Example

At it’s simplest, creating a registrator and registering happens by first declaring a registry.

>>> REGISTRY = {}

In general, it will be a variable owned directly by a module, hence the all-caps. Creating the registrator then follows:

>>> from muse.registration import registrator
>>> @registrator(registry=REGISTRY, logname='my stuff',
...              loglevel='Info')
... def register_mystuff(function):
...     return function

This registrator does nothing more than register the function. A more interesting example is given below. Then a function can be registered:

>>> @register_mystuff(name='yoyo')
... def my_registered_function(a, b):
...     return a + b

The argument ‘yoyo’ is optional. It adds aliases for the function in the registry. In any case, functions are registered with default aliases corresponding to standard name variations, e.g. CamelCase, camelCase, and kebab-case, as illustrated below:

>>> REGISTRY['my_registered_function'] is my_registered_function
True
>>> REGISTRY['my-registered-function'] is my_registered_function
True
>>> REGISTRY['yoyo'] is my_registered_function
True

A more interesting case would involve the registrator automatically adding functionality to the input function. For instance, the inputs could be manipulated and the result of the function could be automatically transformed to a string:

>>> from muse.registration import registrator
>>> @registrator(registry=REGISTRY)
... def register_mystuff(function):
...     from functools import wraps
...
...     @wraps(function)
...     def decorated(a, b) -> str:
...         result = function(2 * a, 3 * b)
...         return str(result)
...
...     return decorated
>>> @register_mystuff
... def other(a, b):
...     return a + b
>>> isinstance(REGISTRY['other'](-3, 2), str)
True
>>> REGISTRY['other'](-3, 2) == "0"
True

13.8.5. Costs

Collection of functions for calculating cost metrics (e.g. LCOE, EAC).

All costs functions take a subset of the following arguments: - technologies: xr.Dataset of technology parameters - prices: xr.DataArray with commodity prices - capacity: xr.DataArray with the capacity of the technologies - production: xr.DataArray with commodity production by the technologies - consumption: xr.DataArray with commodity consumption by the technologies - method: “lifetime” or “annual”

Data should only be provided for a single year (i.e. no “year” dimension in any of the inputs). Prices, production and consumption data should be split across timeslices (i.e. have a “timeslice” dimension). Technology parameters may also be specified at the timeslice level, but capacity should not be.

The technologies input will usually contain data for multiple technologies and have a “technology” dimension (sometimes called “asset” or “replacement”). In this case, it’s important that the capacity, production, and consumption inputs have a similar dimension to ensure that costs are calculated for all technologies and to prevent unwanted broadcasting.

Additional dimensions (such as “region”) may be present in the inputs, but it’s up to the parent functions to ensure that these are consistent between inputs to prevent unwanted broadcasting.

The dimensions of the output will be the sum of all dimensions from the input data, minus “commodity”, plus “timeslice” (if not already present).

Some functions have a method argument, which can be either "annual" or "lifetime". In brief:

  • annual: calculates the cost in a single year.

  • lifetime: calculates the total cost over the lifetime of the technology, using the technical_life attribute from the technologies dataset. In this case, technology parameters, production, consumption, capacity and prices are assumed constant over the lifetime; annual costs are discounted using the interest_rate attribute from the technologies dataset and summed across years.

Capital costs are different, as these are a one-time cost for the lifetime of the technology. These can be annualized by dividing by technical_life.

Some functions can calculate both lifetime and annual costs (use the method argument to select); others implement only one of these modes (see individual function docstrings for details).

annual_to_lifetime(costs, technologies)[source]

Convert annual costs to lifetime costs.

Costs are provided for a single year. These same costs are assumed to apply for the full lifetime of the technologies, subject to a discount factor. The costs are then summed over the lifetime of the technologies.

Parameters:
  • costs – xr.DataArray of costs for a single year.

  • technologies – xr.Dataset of technology parameters

capital_costs(technologies, capacity, method='lifetime')[source]

Calculate capital costs for the relevant technologies.

This is the cost of installing each technology to the level specified by the capacity input.

Method can be “lifetime” or “annual”:

  • lifetime: returns the full capital costs.

  • annual: total capital costs are multiplied by the capital recovery factor to obtain annualized costs.

capital_recovery_factor(technologies)[source]

Capital recovery factor using interest rate and expected lifetime.

The capital recovery factor is computed using the expression given by HOMER Energy.

If the interest rate is zero, this simplifies to 1 / nyears

Parameters:

technologies – All the technologies

Returns:

xr.DataArray with the CRF calculated for the relevant technologies

cost(func)[source]

Decorator to validate the output dimensions of the cost functions.

discount_factor(years, interest_rate, mask=None)[source]

Calculate an array with of discount factor values over the years.

Parameters:
  • years – xr.DataArray with the years counting from the present year (i.e. current year = 0)

  • interest_rate – xr.DataArray with the interest rate for different technologies

  • mask – Optional mask to apply to the result (e.g. cutting to zero after the technology lifetime)

environmental_costs(technologies, prices, production)[source]

Calculate timeslice-level environmental costs for the relevant technologies.

This is the total production of pollutants (commodities flagged by is_pollutant) multiplied by their prices.

equivalent_annual_cost(technologies, prices, capacity, production, consumption, aggregate_timeslices=False)[source]

Equivalent annual costs (or annualized cost) of a technology.

This is the cost that, if it were to occur equally in every year of the project lifetime, would give the same net present cost as the actual cash flow sequence associated with that component. The cost is computed using the annualized cost expression given by HOMER Energy.

Parameters:
  • technologies – xr.Dataset of technology parameters

  • prices – xr.DataArray with commodity prices

  • capacity – xr.DataArray with the capacity of the relevant technologies

  • production – xr.DataArray with commodity production by the relevant technologies

  • consumption – xr.DataArray with commodity consumption by the relevant technologies

  • aggregate_timeslices – If True, the LCOE is aggregated over timeslices (result will not have a “timeslice” dimension)

Returns:

xr.DataArray with the EAC calculated for the relevant technologies

fixed_costs(technologies, capacity)[source]

Calculate annual fixed costs for the relevant technologies.

This is the fixed running cost over the course of a year corresponding to the fix_par and fix_exp technology parameters.

fuel_costs(technologies, prices, consumption)[source]

Calculate timeslice-level fuel costs for the relevant technologies.

This is the total consumption of fuels (commodities flagged by is_fuel) multiplied by their prices.

levelized_cost_of_energy(technologies, prices, capacity, production, consumption, method='lifetime', aggregate_timeslices=False)[source]

Levelized cost of energy (LCOE) of technologies over their lifetime.

It follows the simplified LCOE given by NREL.

Can calculate either a lifetime or annual LCOE.

  • lifetime: the average cost per unit of production over the entire lifetime of the technology. Annual running costs and production are calculated for the full lifetime and adjusted to present value using the discount rate. Total costs (running costs over the lifetime + initial capital costs) are divided by total production to obtain the average cost per unit.

  • annual: the average cost per unit of production in a single year. Annual running costs and production are calculated for a single year, capital costs are annualized using the capital recovery factor, and total costs are divided by production to obtain the average cost per unit.

Parameters:
  • technologies – xr.Dataset of technology parameters

  • prices – xr.DataArray with commodity prices

  • capacity – xr.DataArray with the capacity of the relevant technologies

  • production – xr.DataArray with commodity production by the relevant technologies

  • consumption – xr.DataArray with commodity consumption by the relevant technologies

  • method – “lifetime” or “annual”

  • aggregate_timeslices – If True, the LCOE is aggregated over timeslices (result will not have a “timeslice” dimension)

Returns:

xr.DataArray with the LCOE calculated for the relevant technologies

marginal_cost(technologies, prices, production, consumption)[source]

Marginal cost of technologies.

The average cost of producing one unit of output, excluding capital costs and fixed costs.

Parameters:
  • technologies – xr.Dataset of technology parameters

  • prices – xr.DataArray with commodity prices

  • capacity – xr.DataArray with the capacity of the relevant technologies

  • production – xr.DataArray with commodity production by the relevant technologies

  • consumption – xr.DataArray with commodity consumption by the relevant technologies

Returns:

xr.DataArray with marginal costs calculated for the relevant technologies

material_costs(technologies, prices, consumption)[source]

Calculate timeslice-level material costs for the relevant technologies.

This is the total consumption of materials (commodities flagged by is_material) multiplied by their prices.

net_present_cost(technologies, prices, capacity, production, consumption, aggregate_timeslices=False)[source]

Net present cost (NPC) of the relevant technologies.

The net present cost of a Component is the present value of all the costs of installing and operating the Component over the project lifetime, minus the present value of all the revenues that it earns over the project lifetime.

Parameters:
  • technologies – xr.Dataset of technology parameters

  • prices – xr.DataArray with commodity prices

  • capacity – xr.DataArray with the capacity of the relevant technologies

  • production – xr.DataArray with commodity production by the relevant technologies

  • consumption – xr.DataArray with commodity consumption by the relevant technologies

  • aggregate_timeslices – If True, the LCOE is aggregated over timeslices (result will not have a “timeslice” dimension)

Returns:

xr.DataArray with the NPC calculated for the relevant technologies

net_present_value(technologies, prices, capacity, production, consumption, aggregate_timeslices=False)[source]

Net present value (NPV) of the relevant technologies.

The net present value of a technology is the present value of all the revenues that a technology earns over its lifetime minus all the costs of installing and operating it. Follows the definition of the net present cost given by HOMER Energy.

  • energy commodities INPUTS are related to fuel costs

  • environmental commodities OUTPUTS are related to environmental costs

  • material and service commodities INPUTS are related to consumable costs

  • fixed and variable costs are given as technodata inputs and depend on the installed capacity and production (non-environmental), respectively

  • capacity costs are given as technodata inputs and depend on the installed capacity

Parameters:
  • technologies – xr.Dataset of technology parameters

  • prices – xr.DataArray with commodity prices

  • capacity – xr.DataArray with the capacity of the relevant technologies

  • production – xr.DataArray with commodity production by the relevant technologies

  • consumption – xr.DataArray with commodity consumption by the relevant technologies

  • aggregate_timeslices – If True, the LCOE is aggregated over timeslices (result will not have a “timeslice” dimension)

Returns:

xr.DataArray with the NPV calculated for the relevant technologies

running_costs(technologies, prices, capacity, production, consumption, aggregate_timeslices=False)[source]

Total annual running costs (excluding capital costs).

This is the sum of environmental, fuel, material, variable and (optionally) fixed costs.

supply_cost(production, lcoe, asset_dim='asset')[source]

Supply cost given production and the levelized cost of energy.

In practice, the supply cost is the weighted average LCOE over assets (asset_dim), where the weights are the production.

Very low costs are set to zero.

Parameters:
  • production – Amount of goods produced. In practice, production can be obtained from the capacity for each asset via the method muse.quantities.production.

  • lcoe – Levelized cost of energy for each good produced. In practice, it can be obtained from market prices via muse.costs.levelized_cost_of_energy.

  • asset_dim – Name of the dimension(s) holding assets, processes or technologies.

variable_costs(technologies, production)[source]

Calculate annual variable costs for the relevant technologies.

This is the cost associated with the var_par and var_exp technology parameters.

The production_amplitude function is first used to calculate technology activity based on production. This is then used to scale the variable costs.

13.8.6. Utilities

Collection of functions and stand-alone algorithms.

agent_concatenation(data, dim='asset', name='agent', fill_value=0)[source]

Concatenates input map along given dimension.

Example

Lets create sets of random assets to work with. We set the seed so that this test can be reproduced exactly.

>>> from muse.examples import random_agent_assets
>>> rng = np.random.default_rng(1234)
>>> assets = {i: random_agent_assets(rng) for i in range(5)}

The concatenation will create a new dataset (or datarray) combining all the inputs along the dimension “asset”. The origin of each datum is retained in a new coordinate “agent” with dimension “asset”.

>>> from muse.utilities import agent_concatenation
>>> aggregate = agent_concatenation(assets)
>>> aggregate
<xarray.Dataset> Size: 4kB
Dimensions:     (asset: 19, year: 12)
Coordinates:
    agent       (asset) int32 76B 0 0 0 0 0 1 1 1 2 2 2 2 3 3 3 4 4 4 4
  * year        (year) int64 96B 2033 2035 2036 2037 ... 2046 2047 2048 2049
    installed   (asset) int64 152B 2030 2025 2030 2010 ... 2025 2030 2010 2025
    technology  (asset) <U9 684B 'oven' 'stove' 'oven' ... 'oven' 'thermomix'
    region      (asset) <U9 684B 'Brexitham' 'Brexitham' ... 'Brexitham'
Dimensions without coordinates: asset
Data variables:
    capacity    (asset, year) float64 2kB 26.0 26.0 26.0 56.0 ... 62.0 62.0 62.0

Note that the dtype of the capacity has changed from integers to floating points. This is due to how xarray performs the operation.

We can check that all the data from each agent is indeed present in the aggregate.

>>> for agent, inventory in assets.items():
...    assert (aggregate.sel(asset=aggregate.agent == agent) == inventory).all()

However, it should be noted that the data is not always strictly equivalent: dimensions outside of “assets” (most notably “year”) will include all points from all agents. Missing values for the “year” dimension are forward filled (and backfilled with zeros). Others are left with “NaN”.

avoid_repetitions(data, dim='year')[source]

List of years such that there is no repetition in the data.

It removes the central year of any three consecutive years where all data is the same. This means the original data can be reobtained via a linear interpolation or a forward fill. See muse.utilities.interpolate_capacity().

The first and last year are always preserved.

broadcast_over_assets(data, template, installed_as_year=True)[source]

Broadcasts an array to the shape of a template containing asset-level data.

The dimensions of many arrays (such as technology datasets) are fully explicit, in that each concept (e.g. ‘technology’, ‘region’, ‘year’) is a separate dimension. However, other datasets (e.g capacity), are presented on a per-asset basis, containing a single ‘asset’ dimension with with coordinates such as ‘region’, ‘installed’ (year of installation), and ‘technology’. This latter representation is sparse if not all combinations of ‘region’, ‘installed’ and ‘technology’ are present.

This function broadcasts the first representation to the shape and coordinates of the second, selecting the appropriate values for each asset (see example below).

Note: this is not necessarily limited to technology datasets. For example, it could also be used on a dataset of commodity prices to select prices relevant to each asset (e.g. if assets exist in multiple regions).

Parameters:
  • data – The dataset/data-array to broadcast.

  • template – The dataset/data-array to use as a template.

  • installed_as_year – True means that the year dimension in data corresponds to the year that the asset was installed. This will commonly be the case for most technology parameters (e.g. var_par/fix_par are specified for the year that an asset is installed, and fixed for the lifetime of the asset). In this case, data must have a year coordinate for every possible installed year in the template. Conversely, if the values in data apply to the year of activity, rather than the year of installation, installed_as_year should be False. An example would be commodity prices, which can change over the lifetime of an asset. In this case, if year is present as a dimension in data, it will be maintained as a separate dimension in the output.

Example

Define the data array: >>> import xarray as xr >>> technologies = xr.DataArray( … data=[[1, 2, 3], [4, 5, 6]], … dims=[‘technology’, ‘region’], … coords={‘technology’: [‘gasboiler’, ‘heatpump’], … ‘region’: [‘R1’, ‘R2’, ‘R3’]}, … )

This array contains a value for every combination of technology and region (e.g. this could refer to the efficiency of each technology in each region). For simplicity, we are not including a “year” dimension in this example.

Define the assets template: >>> assets = xr.DataArray( … data=[10, 50], … dims=[“asset”], … coords={ … “region”: ([“asset”], [“R1”, “R2”]), … “technology”: ([“asset”], [“gasboiler”, “heatpump”])}, … )

We have two assets: a gas boiler in region R1 and a heat pump in region R2. In this case the values don’t matter, but could correspond to the installed capacity of each asset, for example.

We want to select the values from the technology array that correspond to each asset in the template. To do this, we perform broadcast_over_assets on technologies using assets as a template: >>> broadcast_over_assets(technologies, assets, installed_as_year=False) <xarray.DataArray (asset: 2)> Size: 16B array([1, 5]) Coordinates:

technology (asset) <U9 72B ‘gasboiler’ ‘heatpump’ region (asset) <U2 16B ‘R1’ ‘R2’

Dimensions without coordinates: asset

The output array has an “asset” dimension which matches the template. Each value in the output is the value in the original technology array that matches the technology & region of each asset.

broadcast_regions(data, template)[source]

Convert a non-regional array to a regional array by broadcasting.

If data is already regioned in the appropriate scheme, it will be returned unchanged.

Parameters:
  • data – Array to broadcast.

  • template – Dataarray with region coordinates to broadcast to.

broadcast_years(data, template)[source]

Convert a non-year array to a year array by broadcasting.

If data is already yeared in the appropriate scheme, it will be returned unchanged.

Parameters:
  • data – Array to broadcast.

  • template – Dataarray with year coordinates to broadcast to.

camel_to_snake(name)[source]

Transforms CamelCase to snake_case.

check_dimensions(data, required=(), optional=())[source]

Ensure that an array has the required dimensions.

This will check that all required dimensions are present, and that no other dimensions are present, apart from those listed as optional.

Parameters:
  • data – DataArray or Dataset to check dimensions of

  • required – List of dimension names that must be present

  • optional – List of dimension names that may be present

clean_assets(assets, year)[source]

Cleans up and prepares asset for current iteration.

  • removes data from before the specified year

  • removes assets for which there is no capacity now or in the future

coords_to_multiindex(data, dimension='asset')[source]

Creates a multi-index from flattened multiple coords.

future_propagation(data, future, threshold=1e-12)[source]

Propagates values into the future.

Example

Data should be an array with at least one dimension, “year”:

>>> coords = dict(year=list(range(2020, 2040, 5)), fuel=["gas", "coal"])
>>> data = xr.DataArray(
...     [list(range(4)), list(range(-5, -1))],
...     coords=coords,
...     dims=("fuel", "year")
... )

future is an array with exactly one year in its year coordinate, or that coordinate must correspond to a scalar. That one year should also be present in data.

>>> future = xr.DataArray(
...     [1.2, -3.95], coords=dict(fuel=coords['fuel'], year=2025), dims="fuel",
... )

This function propagates into data values from future, but only if those values differed for the current year beyond a given threshold:

>>> from muse.utilities import future_propagation
>>> future_propagation(data, future, threshold=0.1)
<xarray.DataArray (fuel: 2, year: 4)> Size: 64B
array([[ 0. ,  1.2,  1.2,  1.2],
       [-5. , -4. , -3. , -2. ]])
Coordinates:
  * year     (year) int32 16B 2020 2025 2030 2035
  * fuel     (fuel) <U4 32B 'gas' 'coal'

Above, the data for coal is not sufficiently different given the threshold. hence, the future values for coal remain as they where.

The dimensions of future do not have to match exactly those of data. Standard broadcasting is used if they do not match:

>>> future_propagation(
...    data, future.sel(fuel="gas", drop=True), threshold=0.1
... )
<xarray.DataArray (fuel: 2, year: 4)> Size: 64B
array([[ 0. ,  1.2,  1.2,  1.2],
       [-5. ,  1.2,  1.2,  1.2]])
Coordinates:
  * year     (year) int32 16B 2020 2025 2030 2035
  * fuel     (fuel) <U4 32B 'gas' 'coal'
>>> future_propagation(
...     data, future.sel(fuel="coal", drop=True), threshold=0.1
... )
<xarray.DataArray (fuel: 2, year: 4)> Size: 64B
array([[ 0.  , -3.95, -3.95, -3.95],
       [-5.  , -4.  , -3.  , -2.  ]])
Coordinates:
  * year     (year) int32 16B 2020 2025 2030 2035
  * fuel     (fuel) <U4 32B 'gas' 'coal'
interpolate_capacity(data, year)[source]

Interpolates capacity data to the given years.

Capacity between years is interpolated linearly. Capacity outside the range of the data is set to zero.

Parameters:
  • data – DataArray containing the capacity data

  • year – Year or years to interpolate to

interpolate_technodata(data, time_framework, interpolation_mode='linear')[source]

Interpolates technologies data to a given time framework.

The approach depends on the format of the data: - If the original data contains data for more than one year, then data will be interpolated with flat back/forward extension to cover the time period. - If the original data does not have a “year” dimension, or only has data for a single year, then this data will not be modified or duplicated, but a dummy “year” dimension covering the time framework will be added.

In both cases, data for any year in the time framework can be selected using data.sel(year=year) on the returned dataset.

Parameters:
  • data – Dataset to interpolate

  • time_framework – List of years to interpolate to

  • interpolation_mode – Interpolation mode to use. Must be one of: “linear”, “nearest”, “zero”, “slinear”, “quadratic”, “cubic”

Returns:

Dataset with the data interpolated to the time framework, sorted by year.

lexical_comparison(objectives, binsize, order=None, bin_last=True)[source]

Lexical comparison over the objectives.

Lexical comparison operates by binning the objectives into bins of width binsize. Once binned, dimensions other than asset and technology are reduced by taking the max, e.g. the largest constraint. Finally, the objectives are ranked lexographically, in the order given by the parameters.

Parameters:
  • objectives – xr.Dataset containing the objectives to rank

  • binsize – bin size, minimization direction (+ -> minimize, - -> maximize), and (optionally) order of lexicographical comparison. The order is the one given binsize.data_vars if the argument order is None.

  • order – Optional array indicating the order in which to rank the tuples.

  • bin_last – Whether the last metric should be binned, or whether it should be left as a the type it already is (e.g. no flooring and no turning to integer.)

Result:

An array of tuples which can subsequently be compared lexicographically.

merge_assets(capa_a, capa_b, dimension='asset')[source]

Merge two capacity arrays.

multiindex_to_coords(data, dimension='asset')[source]

Flattens multi-index dimension into multi-coord dimension.

nametuple_to_dict(nametup)[source]

Transforms a nametuple of type GenericDict into an OrderDict.

reduce_assets(assets, coords=None, dim='asset', operation=None)[source]

Combine assets along given asset dimension.

This method simplifies combining assets across multiple agents, or combining assets across a given dimension. By default, it will sum together assets from the same region which have the same technology and the same installation date. In other words, assets are identified by the technology, installation year and region. The reduction happens over other possible coordinates, e.g. the owning agent.

More specifically, assets are often indexed using what xarray calls a dimension without coordinates. In practice, there are still coordinates associated with assets, e.g. technology and installed (installation year or version), but the value associated with these coordinates are not unique. There may be more than one asset with the same technology or installation year.

For instance, with assets per agent defined as \(A^{i, r}_o\), with \(i\) an agent index, \(r\) a region, \(o\) is the coordinates identified in coords, and \(T\) the transformation identified by operation, then this function computes:

\[R_{r, o} = T[\{A^{i, r}_o; \forall i\}]\]

If \(T\) is the sum operation, then:

\[R_{r, o} = \sum_i A^{i, r}_o\]

Example

Lets construct assets that do have duplicates assets. First we construct the dimensions, using fake data:

>>> data = xr.Dataset()
>>> data['year'] = 'year', [2010, 2015, 2017]
>>> data['installed'] = 'asset', [1990, 1991, 1991, 1990]
>>> data['technology'] = 'asset', ['a', 'b', 'b', 'c']
>>> data['region'] = 'asset', ['x', 'x', 'x', 'y']
>>> data = data.set_coords(('installed', 'technology', 'region'))

We can check there are duplicate assets in this coordinate system:

>>> processes = set(
...     zip(data.installed.values, data.technology.values, data.region.values)
... )
>>> len(processes) < len(data.asset)
True

Now we can easily create a fake two dimensional quantity per process and per year:

>>> data['capacity'] = ('year', 'asset'), np.arange(3 * 4).reshape(3, 4)

The point of reduce_assets is to aggregate assets that refer to the same process:

>>> reduce_assets(data.capacity)
<xarray.DataArray 'capacity' (year: 3, asset: 3)> Size: 36B
array([[ 0,  3,  3],
       [ 4,  7, 11],
       [ 8, 11, 19]])
Coordinates:
  * year        (year) int32 12B 2010 2015 2017
    installed   (asset) int32 12B 1990 1990 1991
    technology  (asset) <U1 12B 'a' 'c' 'b'
    region      (asset) <U1 12B 'x' 'y' 'x'
Dimensions without coordinates: asset

We can also specify explicitly which coordinates in the ‘asset’ dimension should be reduced, and how:

>>> reduce_assets(
...     data.capacity,
...     coords=('technology', 'installed'),
...     operation = lambda x: x.mean(dim='asset')
... )
<xarray.DataArray 'capacity' (year: 3, asset: 3)> Size: 72B
array([[ 0. ,  1.5,  3. ],
       [ 4. ,  5.5,  7. ],
       [ 8. ,  9.5, 11. ]])
Coordinates:
  * year        (year) int32 12B 2010 2015 2017
    technology  (asset) <U1 12B 'a' 'b' 'c'
    installed   (asset) int32 12B 1990 1991 1990
Dimensions without coordinates: asset
tupled_dimension(array, axis)[source]

Transforms one axis into a tuples.

13.8.7. Examples

Example models and datasets.

Helps create and run small standard models from the command-line or directly from python.

To run from the command-line:

python -m muse --model default

Other models may be available. Check the command-line help:

python -m muse --help

The same models can be instantiated in a python script as follows:

from muse import example
model = example.model("default")
model.run()
model(name='default', test=False)[source]

Fully constructs a given example model.

File logging is added if test is False.

Parameters:
  • name – Name of the model to load.

  • test – If True, the logging to file is not added.

Returns:

The MCA model.

technodata(sector, model='default')[source]

Technology for a sector of a given example model.