Binder

Osier Technology Tutorial#

An osier.Technology is the fundamental object in osier, representing power producing technologies such as power plants or energy storage. Other modules use and modify osier.Technology objects. This object contains all of the information one needs to run an energy system model.

Objectives:

  • Learn how to import a pre-defined technology from the osier technology library.

  • Modify the attributes of a technology object in the current instance.

  • Create your own osier technology!

[1]:
# recommended imports
from unyt import MW, GW, hr, year
from unyt import unyt_array
import numpy as np

Importing from the Technology Library#

Osier comes with some generic, pre-defined technology classes that you can use immediately in your simulations. The cost data comes from the National Renewable Energy Laboratory’s (NREL) Annual Technology Baseline (ATB).

[2]:
# import the catalog to see what technologies are available
from osier.tech_library import catalog

display(catalog())
Import Name Technology Name
0 battery Battery
1 biomass Biomass
2 coal Coal_Conv
3 coal_adv Coal_Adv
4 natural_gas NaturalGas_Conv
5 natural_gas_adv NaturalGas_Adv
6 nuclear Nuclear
7 nuclear_adv Nuclear_Adv
8 solar SolarPanel
9 wind WindTurbine
[3]:
# import some specific technologies
from osier.tech_library import battery, nuclear, solar

# printing the technology will show the currently specified capacity.
print(nuclear)
Nuclear: 18609.404000000002 MW

All of the associated technology data can be viewed in a dataframe format using osier.Technology.to_dataframe().

[4]:
nuclear.to_dataframe()
[4]:
technology_category technology_type dispatchable renewable fuel_type ramp_up_rate (1/hr) ramp_down_rate (1/hr) lifetime capacity (MW) capacity_factor capacity_credit efficiency capital_cost (1/kW) om_cost_fixed (1/kW) om_cost_variable (1/(MW*hr)) fuel_cost (1/(MW*hr)) co2_rate (megatonnes/(MW*hr)) lifecycle_co2_rate (megatonnes/(GW*hr)) land_intensity (km/MW**2) heat_rate
technology_name
Nuclear thermal production True False None 0 0 25 1.86e+04 1 1 1 5e-05 0.000178 0 5.81e-06 0 5.1e-06 0 None
[5]:
# one can also import a list of all technologies
from osier.tech_library import all_technologies

display(all_technologies())
[Battery: 815.3412599999999 MW,
 Biomass: 0.0 MW,
 Coal_Conv: 0.0 MW,
 Coal_Adv: 0.0 MW,
 NaturalGas_Conv: 8375.1331 MW,
 NaturalGas_Adv: 0.0 MW,
 Nuclear: 18609.404000000002 MW,
 Nuclear_Adv: 0.0 MW,
 SolarPanel: 2810.3015 MW,
 WindTurbine: 0.0 MW]

Lastly, if you want to view all of the technology data in a single dataframe, you can simply import technology_dataframe from osier.utils.

[6]:
from osier.utils import technology_dataframe

technology_dataframe(all_technologies())
[6]:
technology_category technology_type dispatchable renewable fuel_type lifetime capacity (MW) capacity_factor capacity_credit efficiency ... fuel_cost (1/(MW*hr)) co2_rate (megatonnes/(MW*hr)) lifecycle_co2_rate (megatonnes/(GW*hr)) land_intensity (km/MW**2) storage_duration (hr) initial_storage (MW*hr) ramp_up_rate (1/hr) ramp_down_rate (1/hr) heat_rate lifecycle_co2_rate (megatonnes/(MW*hr))
technology_name
Battery base storage True False None 25 815 1 0.5 0.85 ... 0 0 3.3e-05 0 4 0 NaN NaN NaN NaN
Biomass thermal production True True None 25 0 1 1 1 ... 4.7e-05 0 0.00023 0 NaN NaN 1 1 None NaN
Coal_Conv thermal production True False None 25 0 1 1 1 ... 2.14e-05 0 0.001 0 NaN NaN 0.5 0.5 None NaN
Coal_Adv thermal production True False None 25 0 1 1 1 ... 3.66e-05 0 0.00037 0 NaN NaN 0.5 0.5 None NaN
NaturalGas_Conv thermal production True False None 25 8.38e+03 1 1 1 ... 2.24e-05 0 0.00049 0 NaN NaN 1 1 None NaN
NaturalGas_Adv thermal production True False None 25 0 1 1 1 ... 2.75e-05 0 0.00013 0 NaN NaN 1 1 None NaN
Nuclear thermal production True False None 25 1.86e+04 1 1 1 ... 5.81e-06 0 5.1e-06 0 NaN NaN 0 0 None NaN
Nuclear_Adv thermal production True False None 25 0 1 1 1 ... 9.16e-06 0 NaN 0 NaN NaN 0.25 0.25 None 5.1e-09
SolarPanel base production False True solar 25 2.81e+03 1 0.19 1 ... 0 0 3.7e-05 0 NaN NaN NaN NaN NaN NaN
WindTurbine base production False True wind 25 0 1 0.35 1 ... 0 0 1.2e-05 0 NaN NaN NaN NaN NaN NaN

10 rows × 23 columns

Entries with NaN values means that technology does not have that attribute.

Entries with None values means that technology possesses that attribute, but it has not been assigned a value!

Modifying Standard Technologies#

If you want to test different cost assumptions or optimize over an attribute that is not currently present in the technology, you can add or adjust at will. However, unless you modify the source code, these changes will not be saved if you restart the Python instance.

When modifying a capacity or cost attribute, specifying the units with the unyt library is recommended!

[7]:
from osier.tech_library import wind

# modify the capacity
display(wind)
wind.capacity = 5*GW
display(wind)
WindTurbine: 0.0 MW
WindTurbine: 5000.0 MW
[8]:
# add a new attribute

print(f"Is `readiness` present in the `wind` technology dataframe? {'readiness' in wind.to_dataframe().columns}")

wind.readiness = 9
print(f"The `wind` technology now has a `readiness` level of {wind.readiness}!")

print(f"Is `readiness` present in the `wind` technology dataframe? {'readiness' in wind.to_dataframe().columns}")
Is `readiness` present in the `wind` technology dataframe? False
The `wind` technology now has a `readiness` level of 9!
Is `readiness` present in the `wind` technology dataframe? True

Creating your own osier.Technology object#

Why would you want to create your own technology?

  • The technologies in osier.tech_library are too generic or you want to model a specific technology version.

  • There is a technology that is not represented.

The data in the osier.tech_library primarily comes from NREL’s Annual Technology Baseline. This representation is generic, though, and you may be interested in creating a vendor specific technology. Or perhaps an “idealized” technology that doesn’t exist, yet (e.g., fusion?).

osier offers several sub-classes of the osier.Technology class as well:

  • osier.RampingTechnology: A general class for technologies that typically have ramping constraints (e.g., nuclear, hydroelectric dams)

  • osier.ThermalTechnology: A class that inherits from osier.RampingTechnology for technologies that have heat rates. osier’s current implementation makes this redundant, but may be useful in the future.

  • osier.StorageTechnology: A general class for technologies that primarily store energy rather than produce it.

Only the name is a required input.

[9]:
# import base class
from osier import Technology
[10]:
alien_technology = Technology(technology_name="AlienTechnology")

But that’s too simple. So let’s add some more values.

Note: When applying units from unyt, \(\frac{\$}{GW}\) is best expressed as \(\$*GW^{-1}\). unyt currently does not handle currencies due to exchange rate issues and unit conversion is one of unyt’s primary functionalities.

[11]:
alien_technology = Technology(technology_name="AlienTechnology",
                              technology_type="production",
                              dispatchable=True,
                              renewable=True,
                              capital_cost=5e4*GW**-1,
                              fuel_cost=1e2*(MW*hr)**-1,
                              capacity_factor=1.0,
                              lifecycle_co2_rate=0.0,
                              lifetime=1000,
                              om_cost_fixed=90*(GW)**-1)

If you’re ever unsure of what parameters a function or class take, you can always call the Python help() function!

If you’re using a jupyter notebook or ipykernel, you can use the magic command ? after the function or class, as shown below.

[12]:
# help(Technology)

Technology?
Init signature:
Technology(
    technology_name,
    technology_type='production',
    technology_category='base',
    dispatchable=True,
    renewable=False,
    capital_cost=0.0,
    om_cost_fixed=0.0,
    om_cost_variable=0.0,
    fuel_cost=0.0,
    fuel_type=None,
    capacity=0.0,
    capacity_factor=1.0,
    capacity_credit=1.0,
    co2_rate=0.0,
    lifecycle_co2_rate=0.0,
    land_intensity=0.0,
    efficiency=1.0,
    lifetime=25.0,
    default_power_units=MW,
    default_time_units=hr,
    default_energy_units=None,
    default_length_units=km,
    default_volume_units=m**3,
    default_mass_units=megatonnes,
) -> None
Docstring:
The :class:`Technology` base class contains the minimum required
data to solve an energy systems problem. Many optional data are
included here as well. All other technologies in
:mod:`osier` inherit from this class.

Parameters
----------
technology_name : str
    The name identifier of the technology.
technology_type : str
    The string identifier for the type of technology.
    Two common types are: ["production", "storage"].
technology_category : str
    The string identifier the the technology category.
    For example: "renewable," "fossil," or "nuclear."
dispatchable : bool
    Indicates whether the technology can be dispatched by a
    grid operator, or if it produces variable electricity
    that must be used or stored the moment it is produced.
    For example, solar panels and wind turbines are not
    dispatchable, but nuclear and biopower are dispatchable.
    Default value is true.
renewable : bool
    Indicates whether the technology is considered "renewable."
    Useful for determining if a technology will contribute to
    a renewable portfolio standard (RPS).
capital_cost : float or :class:`unyt.array.unyt_quantity`
    Specifies the capital cost. If float,
    the default unit is $/MW.
om_cost_fixed : float or :class:`unyt.array.unyt_quantity`
    Specifies the fixed operating costs.
    If float, the default unit is $/MW.
om_cost_variable : float, :class:`unyt.array.unyt_quantity`, or array-like
    Specifies the variable operating costs. Users may pass timeseries data.
    However, :class:`pandas.DataFrame` is not supported by this feature.
    If float, the default unit is $/MWh.
fuel_cost : float, :class:`unyt.array.unyt_quantity`, or array-like
    Specifies the fuel costs. Users may pass timeseries data.
    However, :class:`pandas.DataFrame` is not supported by this feature.
    If float, the default unit is $/MWh.
fuel_type : str
    Specifies the type of fuel consumed by the technology.
capacity : float or :class:`unyt.array.unyt_quantity`
    Specifies the technology capacity.
    If float, the default unit is MW
capacity_factor : Optional, float
    Specifies the 'usable' fraction of a technology's capacity.
    Default is 1.0, i.e. all of the technology's capacity is
    usable all of the time.
capacity_credit : Optional, float
    Specifies the fraction of a technology's capacity that counts
    towards reliability requirements. Most frequently used for
    renewable technologies. For example, a solar farm might have
    a capacity credit of 0.2. This means that in order to meet a
    capacity requirement of 1 GW, 1.25 GW of solar would need to
    be installed.
    Default is 1.0, i.e. all of the technology's capacity contributes
    to capacity requirements.
co2_rate : float or :class:`unyt.array.unyt_quantity`
    Specifies the rate at which carbon dioxide is emitted during operation.
    Generally only applicable for fossil fueled plants.
    If float, the default units are megatonnes per MWh
lifecycle_co2_rate : float or :class:`unyt.array.unyt_quantity`
    Specifies the rate at which of CO2eq emissions over a typical lifetime.
    Unless you are reading this in a future where the economy is fully
    decarbonized, all technologies should have a non-zero value for this
    attribute.
    If float, the default units are megatonnes per MWh
land_intensity : float or :class:`unyt.array.unyt_quantity`
    The amount of land required per unit capacity. May be either lifecycle
    land use or from direct use. However, consistency between
    technologies is incumbent on the user.
efficiency : float
    The technology's energy conversion efficiency expressed as
    a fraction. Default is 1.0.
lifetime : float
    The technology's operational lifetime in years. Default is 25 years.
default_power_units : str or :class:`unyt.unit_object.Unit`
    An optional parameter, specifies the units
    for power. Default is megawatts [MW].
default_time_units : str or :class:`unyt.unit_object.Unit`
    An optional parameter, specifies the units
    for time. Default is hours [hr].
default_mass_units : str or :class:`unyt.unit_object.Unit`
    An optional parameter, specifies the units
    for mass. Default is hours [kg].
default_energy_units : str or :class:`unyt.unit_object.Unit`
    An optional parameter, specifies the units
    for energy. Default is megawatt-hours [MWh]
    Currently, `default_energy_units` is derived from the
    time and power units.

Notes
-----
Cost values are listed in the docs as [$ / physical unit]. However,
:class:`osier` does not currently have a currency handler, therefore the
units are technically [1 / physical unit].

The :class:`unyt` library may not be able to interpret strings for
inverse units. For example:

>>> my_unit = "10 / MW"
>>> my_unit = unyt_quantity.from_string(my_unit)
ValueError: Received invalid quantity expression '10/MW'.

Instead, try the more explicit approach:

>>> my_unit = "10 MW**-1"
>>> my_unit = unyt_quantity.from_string(my_unit)
unyt_quantity(10., '1/MW')

However, inverse MWh cannot be converted from a string.
File:           c:\users\sdotson\research\osier\osier\technology.py
Type:           type
Subclasses:     RampingTechnology, StorageTechnology