Note
You can download this example as a Jupyter notebook or start it in interactive mode.
Components class#
Version 0.33
of PyPSA introduces a structural refactoring of how component data is stored and accessed. The new structure adds an extra layer to move all component-specific data from the networks class to a new component class. With version 0.33
, most of these changes will be unnoticeable to the user.
But this makes it easy to add new features. Below are some simple examples to show which other features could be added in the future. If you have any ideas, wishes, feedback or suggestions, please let us know via PyPSA/PyPSA#issues.
Note that this is experimental. Features will be added and the newly introduced API may change. You can still use PyPSA as usual. Major API changes will not be introduced before version 1.0
. While the Components
class does not introduce any breaking changes, the ``ComponentsStore`` leads to slightly different behavior for ``n.components``. See the explanation below.
Also, while all classes and methods have docstrings, there is no dedicated documentation yet.
General#
[1]:
import pypsa
n = pypsa.examples.scigrid_de()
WARNING:pypsa.io:Importing network from PyPSA version v0.17.1 while current version is v0.33.0. Read the release notes at https://pypsa.readthedocs.io/en/latest/release_notes.html to prepare your network for import.
INFO:pypsa.io:Imported network scigrid-de.nc has buses, generators, lines, loads, storage_units, transformers
Components class#
So far, components data was directly attached to the network object (e.g. n.generators
, n.generators_t
etc.). While you still can access the data there, both actually sit now in a new Components
class:
[2]:
c = n.components.generators # also via alias n.c.generators
c
[2]:
PyPSA 'Generator' Components
----------------------------
Attached to PyPSA Network 'scigrid-de'
Components: 1423
The datasets for static and dynamic data can be accessed via the class now, but also still via the old network properties:
[3]:
c.static.head()
[3]:
bus | control | type | p_nom | p_nom_mod | p_nom_extendable | p_nom_min | p_nom_max | p_min_pu | p_max_pu | ... | min_up_time | min_down_time | up_time_before | down_time_before | ramp_limit_up | ramp_limit_down | ramp_limit_start_up | ramp_limit_shut_down | weight | p_nom_opt | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Generator | |||||||||||||||||||||
1 Gas | 1 | PQ | 121.0 | 0.0 | False | 0.0 | inf | 0.0 | 1.0 | ... | 0 | 0 | 1 | 0 | NaN | NaN | 1.0 | 1.0 | 1.0 | 0.0 | |
1 Hard Coal | 1 | PQ | 272.0 | 0.0 | False | 0.0 | inf | 0.0 | 1.0 | ... | 0 | 0 | 1 | 0 | NaN | NaN | 1.0 | 1.0 | 1.0 | 0.0 | |
102 Gas | 102 | PQ | 67.9 | 0.0 | False | 0.0 | inf | 0.0 | 1.0 | ... | 0 | 0 | 1 | 0 | NaN | NaN | 1.0 | 1.0 | 1.0 | 0.0 | |
108 Run of River | 108 | PQ | 63.1 | 0.0 | False | 0.0 | inf | 0.0 | 1.0 | ... | 0 | 0 | 1 | 0 | NaN | NaN | 1.0 | 1.0 | 1.0 | 0.0 | |
108 Waste | 108 | PQ | 38.0 | 0.0 | False | 0.0 | inf | 0.0 | 1.0 | ... | 0 | 0 | 1 | 0 | NaN | NaN | 1.0 | 1.0 | 1.0 | 0.0 |
5 rows × 37 columns
[4]:
c.dynamic.keys()
[4]:
dict_keys(['p_min_pu', 'p_max_pu', 'p_set', 'q_set', 'marginal_cost', 'marginal_cost_quadratic', 'efficiency', 'stand_by_cost', 'ramp_limit_up', 'ramp_limit_down', 'p', 'q', 'status', 'mu_upper', 'mu_lower', 'mu_p_set', 'mu_ramp_limit_up', 'mu_ramp_limit_down'])
[5]:
# Both ways refer to the same DataFrame/ Dict Container of
print(c.static is n.generators)
print(c.dynamic is n.generators_t)
True
True
Components Store#
There have been some major changes to n.components
, which is now the basic store for all components. Before version 0.33
, n.components
was a dictionary containing only the default component data, and no static or dynamic data. Now it contains both (as described above), while still allowing access to the default data:
[6]:
print(f"List name: '{n.components['Generator'].list_name}'")
print(f"Description: '{n.components['Generator'].description}'")
List name: 'generators'
Description: 'Power generator.'
But the iteration behaviour is different. While a dictionary only returns the keys, the new `ComponentsStore’ object returns the components themselves (similar to a list). This leads to a break when using it:
[7]:
for comp in n.components:
break
/tmp/ipykernel_4629/3163957973.py:1: DeprecationWarning: Iterating over `n.components` yields the values instead of keys from v0.33.0. This behavior might be breaking. Use `n.components.keys()` to iterate over the keys. To suppress this warning set `pypsa.options.warnings.components_store_iter = False`.
for comp in n.components:
and __contains__
/ x in n.components is not supported anymore:
[8]:
"x" in n.components
---------------------------------------------------------------------------
DeprecationWarning Traceback (most recent call last)
Cell In[8], line 1
----> 1 "x" in n.components
File ~/checkouts/readthedocs.org/user_builds/pypsa/envs/latest/lib/python3.13/site-packages/pypsa/definitions/components.py:207, in ComponentsStore.__contains__(self, item)
197 """
198 Check if component is in store.
199 """
200 msg = (
201 "Checking if a component is in `n.components` using the 'in' operator "
202 "is deprecated. Use `item in n.components.keys()` to retain the old "
(...)
205 "notes for more information."
206 )
--> 207 raise DeprecationWarning(msg)
DeprecationWarning: Checking if a component is in `n.components` using the 'in' operator is deprecated. Use `item in n.components.keys()` to retain the old behavior. But with v0.33.0 custom components are deprecated and therefore keys in `n.components` never change. Check the release notes for more information.
Examples#
[9]:
c = n.components.generators
Simple alias properties#
[10]:
# Basic component information
print(f"Component name: '{c.name}'")
print(f"Component list name: '{c.list_name}'")
print(f"Component type: '{c.type}'")
# Other maybe useful properties
print(f"Nominal attribute: '{c.nominal_attr}'")
Component name: 'Generator'
Component list name: 'generators'
Component type: 'controllable_one_port'
Nominal attribute: 'p_nom'
[11]:
# Quick access to attribute units
c.units.head()
[11]:
unit | |
---|---|
attribute | |
p_nom | MW |
p_nom_mod | MW |
p_nom_min | MW |
p_nom_max | MW |
p_min_pu | per unit |
[12]:
# Get ports of component (e.g. for multiport components)
n.c.links.ports
[12]:
['0', '1']
[13]:
# Check if component is attached to network
if c.attached:
print(f"{c} is attached to {c.n}")
PyPSA 'Generator' Components is attached to PyPSA Network 'scigrid-de'
Rename components and propagate new names through network#
[14]:
# Old names
print(f"Old bus names: {', '.join(c.static.head(2).index)}")
Old bus names: 1 Gas, 1 Hard Coal
[15]:
# Rename "1 Gas" component
c = n.components.buses
rename_map = {"1": "Super Bus"}
c.rename_component_names(**rename_map)
[16]:
# New names
print(f"New bus names: {', '.join(c.static.head(2).index)}")
New bus names: Super Bus, 2
[17]:
# Changes in other components of network
n.c.generators.static.head(2)
[17]:
bus | control | type | p_nom | p_nom_mod | p_nom_extendable | p_nom_min | p_nom_max | p_min_pu | p_max_pu | ... | min_up_time | min_down_time | up_time_before | down_time_before | ramp_limit_up | ramp_limit_down | ramp_limit_start_up | ramp_limit_shut_down | weight | p_nom_opt | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Generator | |||||||||||||||||||||
1 Gas | Super Bus | PQ | 121.0 | 0.0 | False | 0.0 | inf | 0.0 | 1.0 | ... | 0 | 0 | 1 | 0 | NaN | NaN | 1.0 | 1.0 | 1.0 | 0.0 | |
1 Hard Coal | Super Bus | PQ | 272.0 | 0.0 | False | 0.0 | inf | 0.0 | 1.0 | ... | 0 | 0 | 1 | 0 | NaN | NaN | 1.0 | 1.0 | 1.0 | 0.0 |
2 rows × 37 columns
Calculate line length from attached buses#
[18]:
c = n.c.lines
c.calculate_line_length()
[18]:
0 34432.796096
1 59701.666027
2 32242.741010
3 30559.154647
4 21574.543367
...
847 608.501911
848 774.034099
849 781.865928
850 816.599959
851 836.332814
Length: 852, dtype: float64
Those are just a couple of simple examples. Many other features could be added. If you have any ideas, wishes, feedback or suggestions, please let us know via the PyPSA/PyPSA#issues.