PyPSA

Following sections give detailed instructions on how to create ready-to-use energy systems using pypsa.Network objects.

Concept

PyPSA aims to be a free software toolbox simulating and optimizing energy supply systems with the focus on electrical power flow expecially on large scale networks and timeframes.

It is developed by a small, dedicated development team formerly at the Frankfurt Institute for Advanced Studies (FIAS, german) now at the Karlsruhe Institut of Technologie (KIT).

It does NOT try to be as open as possible in the sense of making it easy for the community to take part in developing. Meaning there is no dedicated Development section inside PyPSA’s docuemntation (although a very small Contributing entry exists, ‘’Welcoming anyone interested in contributing to this project’’) and there is API style documentation like in Tessif or Oemof.

Purpose

Within the context of tessif, PyPSA serves as yet another pyomo solver interface but with a focus on electrical energy flows, respecting electrical constraints like direct or alternating current, voltage angles and magnitudes and more.

PyPSA also allows optimizing an energy system using their own optimizer which supposedly works much faster and needs much less memory (see pypsa.Network.lopf(); Parameter pyomo).

Using PyPSA through tessif is somewhat of a challenge especially regarding specific electrical constraints. Since PyPSA was not build to be hackable/accessible via other interfaces like i.e Oemof or Tessif are, but rather focuses on tabular interfaces, direct use and low overhead.

Specialized problem constraints (like the beforenamed elictrical constraints) however, will be implemented in the near future through tessif’s energy system components. The respective parameters will be marked and it will be stated which models can make use of them.

Examples

Minimum Working Example

Note

The exaxt same energy system can be accessed using tessif.examples.data.pypsa.py_hard.create_mwe().

  1. Import the needed packages:

    # standard library
    import os
    import pathlib
    
    # third pary
    import pypsa
    
    # local
    import tessif.frused.namedtuples as nts
    from tessif.frused.paths import write_dir
    import tessif.write.tools as write_tools
    
  2. Create a simulation time frame of two timesteps (PyPSA assumes hourly resolution by default, altough it actually only cares about the number of timesteps not about their difference in real-time):

    timesteps = range(2)
    
  3. Create an energy system / network object:

    es = pypsa.Network()
    
    • Enforce the number of timesteps:

      es.set_snapshots(timesteps)
      
    • Add the main power bus Uid for utilizing Tessif’s Labeling Concept:

      power_bus_uid = nts.Uid(
          name='Power Line', latitude=53, longitude=10,
          region='Germany', sector='Power', carrier='Electricity',
          component='Bus', node_type='AC_Bus')
      
    • Add a Bus as ideal power line:

      es.add(class_name='Bus',
          # tessif's uid representation
          name=power_bus_uid,
          # pypsa's representation:
          x=10, y=53, carrier='AC'
          )
      
    • Add a Demand needing 10 energy units per timestep:

      es.add(class_name='Load',
          bus=str(power_bus_uid),
          name=nts.Uid(
              name='Demand', latitude=53, longitude=10,
              region='Germany', sector='Power', carrier='Electricity',
              component='Sink', node_type='AC_Sink'),
          p_set=10,
        )
      
    • Add a renewable source

      producing 8 and 2 energy units with a cost of 9:

      es.add(class_name='Generator',
          bus=str(power_bus_uid),
          name=nts.Uid(
              name='Renewable', latitude=53, longitude=10,
              region='Germany', sector='Power', carrier='Electricity',
              component='Source', node_type='AC_Source'),
          p_nom=10, p_max_pu=[0.8, 0.2], marginal_cost=9
        )
      
    • Add a conventional engergy transformer producing up to 10 energy units for a cost of 10:

      es.add(
          class_name='Generator',
          bus=str(power_bus_uid),
          name=nts.Uid(
              name='Gas Generator', latitude=53, longitude=10,
              region='Germany', sector='Power', carrier='gas',
              component='Transformer', node_type='gas_powerplant'),
          p_nom=10, marginal_cost=10)
      
  4. Optimize model:

    es.lopf()
    
  5. Store the energy system into tessif/src/tessf/write/pypsa/mwe:

    import os
    import pathlib
    
    from tessif.frused.paths import write_dir
    
    d = os.path.join(write_dir, 'pypsa', 'mwe')
    pathlib.Path(d).mkdir(parents=True, exist_ok=True)
    es.export_to_csv_folder(d)
    
  6. Using this newly generated energy system in a different python context by importing it:

Note

The code of steps 1 to 5 is wrapped in a create_mwe() function for convenience meaning it is copy pastable.

  1. Import the wrapper functionality:

    >>> from tessif.examples.data.pypsa.py_hard import create_mwe
    >>> esys = create_mwe()
    
  2. Confirm the expected output:

    >>> pypsa_nodes = [
    ...     *esys.buses.index,
    ...     *esys.generators.index,
    ...     *esys.lines.index,
    ...     *esys.links.index,
    ...     *esys.loads.index,
    ...     *esys.storage_units.index,
    ...     *esys.stores.index,
    ... ]
    >>> for node in pypsa_nodes:
    ...     print(node)
    Power Line
    Renewable
    Gas Generator
    Demand
    
  1. For examples on how to extract result information out ouf the optimized energy system using tessif, see tessif.transform.es2mapping.pypsa