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.
- 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.
- 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 dataAbstractSector.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
AbstractSectorat the apex: all sectors should derive fromAbstractSectordirectly or indirectly.MUSE only requires two things of a sector. Sector should be instanstiable via a
factory()function. And they should be callable vianext().AbstractSectordeclares an interface with these two functions. Sectors which derive from it will be warned if either method is not implemented.
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.
- 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, andprices.- Returns:
A market containing the
supplyoffered by the sector, it’s attendantconsumptionof fuels and materials and the associatedcosts.
- 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.
- 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
13.2.4. PresetSector
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
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.
- 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.
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.
- 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.
- 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_filteris simply first filter given on input, if that filter is registered withregister_initializer(). Otherwise,initialize_from_technologies()is automatically inserted.
- 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.
- 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.
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.
- 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.
- 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.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:
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:
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.
- lp_constraint(constraint, lpcosts)[source]
Transforms the constraint to LP data.
The goal is to create from
lpcosts.capacity,constraint.capacity, andconstraint.ba 2d-matrixconstraintvsdecision variables.- The dimensions of
constraint.bare the constraint dimensions. They are renamed
"c(xxx)".
- The dimensions of
- The dimensions of
lpcostsare the decision-variable dimensions. They are renamed
"d(xxx)".
- The dimensions of
set(b.dims).intersection(lpcosts.xxx.dims)are diagonalin constraint dimensions and decision variables dimension, with
xxxthe capacity or the production
set(constraint.xxx.dims) - set(lpcosts.xxx.dims) - set(b.dims)are reduced bysummation, with
xxxthe capacity or the production
set(lpcosts.xxx.dims) - set(constraint.xxx.dims) - set(b.dims)are added forexpansion, with
xxxthe 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, andba 2d-matrix of constraints vs decision variables.- The dimensions of
bare the constraint dimensions. They are renamed "c(xxx)".
- The dimensions of
- The dimensions of
lpcostsare the decision-variable dimensions. They are renamed
"d(xxx)".
- The dimensions of
set(b.dims).intersection(lpcosts.dims)are diagonalin constraint dimensions and decision variables dimension
set(constraint.dims) - set(lpcosts.dims) - set(b.dims)are reduced bysummation
set(lpcosts.dims) - set(constraint.dims) - set(b.dims)are added forexpansion
set(b.dims) - set(constraint.dims) - set(lpcosts.dims)are added forexpansion. 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.
- 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.
- 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 aPathas input, and returns the relevant data structure (usually an xarray). The process is generally broken down into two functions that are called byread_x:
read_x_csv: This takes a path to a csv file as input and returns a pandasDataFrame. 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 isdone. It takes the DataFrame from
read_x_csvand 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_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_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_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_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
Reads and processes timeslice shares data 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
- 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.
- 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:
set_index: A dictionary or any argument accepted by
pandas.DataFrame.set_index(). Ignored if not specified. If specified, a call topandas.DataFrame.reset_index()is made first.sort_index: A dictionary or any argument accepted by
pandas.DataFrame.sort_index(). Ignored if not specified.keep_columns: a string or a list of strings with the names of the columns to keep. Ignored if not specified.
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
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.
- 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]
- 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”).
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.
- 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]
- 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_emission_costs(market, sectors, year, **kwargs)[source]
Current emission costs across all sectors.
- metric_lcoe(market, sectors, year, **kwargs)[source]
Current lifetime levelised cost across all sectors.
- register_output_quantity(function=None)[source]
Registers a function to compute an output quantity.
- 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.
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.
- 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)
- 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:
- 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
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.
technologies – technologies 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.
The basic algorithm proceeds as follows:
sort all costs \(C_{d, i}\) across both \(d\) and \(i\)
for each cost \(c_0\) in order:
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\]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.sort all costs \(C_{d, m}\) across both \(d\) and \(m\)
for each cost \(c_0\) in order:
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} \]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.
Loop over each constraint \(\alpha\). Below we drop the index \(\alpha\) over constraints for simplicity.
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\}\]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} \]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”.
- 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,CommodityUsagecentralizes 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.CONSUMABLEis a commodity that is both consumed and produced by a sector.CommodityUsage.ENVIRONMENTAL | CommodityUsage.ENERGY | CommodityUsage.CONSUMABLEis an environmental energy commodity consumed by the sector.CommodityUsage.OTHERis 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]
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
- 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.
See also
- 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.
See also
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.
See also
- 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
See also
- 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
xarrayperforms 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
yeardimension indatacorresponds to the year that the asset was installed. This will commonly be the case for most technology parameters (e.g.var_par/fix_parare specified for the year that an asset is installed, and fixed for the lifetime of the asset). In this case,datamust have ayearcoordinate for every possibleinstalledyear in the template. Conversely, if the values indataapply to the year of activity, rather than the year of installation,installed_as_yearshould be False. An example would be commodity prices, which can change over the lifetime of an asset. In this case, ifyearis present as a dimension indata, 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.
- 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
Datashould 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") ... )
futureis an array with exactly one year in itsyearcoordinate, or that coordinate must correspond to a scalar. That one year should also be present indata.>>> future = xr.DataArray( ... [1.2, -3.95], coords=dict(fuel=coords['fuel'], year=2025), dims="fuel", ... )
This function propagates into
datavalues fromfuture, 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
futuredo not have to match exactly those ofdata. 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.
- multiindex_to_coords(data, dimension='asset')[source]
Flattens multi-index dimension into multi-coord dimension.
- 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 byoperation, 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
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()