ppsa
Api
Transform a tessif energy system into a pypsa energy system. |
Internal Functionalities
Utility to infer how tessif transformers should be transformed into pypsa components. |
|
Utility to compute unneeded tessif components uids. |
|
Utility to parse flow related parameters from |
|
Creates pypsa buses out of tessif busses. |
|
Create pypsa generators out of tessif sinks. |
|
Create pypsa generators out of tessif sources. |
|
Create pypsa generators out of tessif transformers. |
|
Create pypsa links out of tessif transformers. |
|
Create pypsa links out of tessif connectors. |
|
Create pypsa generators out of tessif storages. |
tessif.transform.es2es.ppsa is a tessif module aggregating all
the functionality for automatically transforming a tessif energy system into a
pypsa energy system.
- tessif.transform.es2es.ppsa.infer_pypsa_transformer_types(tessif_transformers, forced_links=None)[source]
Utility to infer how tessif transformers should be transformed into pypsa components.
Tessif transformerscan have singular or multiple outputs and therefor be both:Pypsa links (if #(outputs) > 1)
Pypsa generators (if #(inputs) == #(outputs) == 1)
For a tessif transformer being transformed into a pypsa link, one of the following has to be true:
Transformer.node_typeis found insidetessif.frused.spellings.combined_heat_powernumber of
Transformer.outputs> 1
On the other hand, for a tessif transformer beeing transformed into a pypsa generator, one of the following has to be true:
Transformer.node_typeis found insidetessif.frused.spellings.power_plantor insidetessif.frused.spellings.heat_plant.number of
Transformer.outputsequals number ofTransformer.inputsequals 1.
- Parameters:
tessif_transformers¶ (Iterable) – Iterable of tessif transformers of which the appropriate transformation type is to be inferred.
forced_links¶ (Container, None, default=None) –
Container of uid representations of
tessif transformersto be transfrormed into pypsa links.
- Returns:
inferred_transformers – Mapping of
Tessif transformeruids keyed either bylinksor bygenerators- Return type:
Examples
Use the example hub’s
py_harddata base for showcasing basic usage:Using the mwe:
>>> from tessif.examples.data.tsf.py_hard import create_mwe
>>> tessif_es = create_mwe() >>> iptts = infer_pypsa_transformer_types(tessif_es.transformers) >>> for i, (key, transformer_uids) in enumerate(iptts.items()): ... if i > 0: ... print() ... print(f'{key}:') ... print(10*'-') ... for uid in transformer_uids: ... print(uid) generators: ---------- Generator links: ----------
- tessif.transform.es2es.ppsa.compute_unneeded_supply_chains(tessif_es, pypsa_generator_uids)[source]
Utility to compute unneeded tessif components uids.
Due to transforming certain
tessif transformersinto pypsa generators corresponding supply chains (usually atessif busand atessif source) are not needed and hence need to be removed, to allow succesfull optimization.- Parameters:
tessif_es¶ (
tessif.model.energy_system.AbstractEnergySystem) – The tessif energy system of which thegeneratorsupply chains are to be removed.pypsa_generator_uids¶ (Iterable) – Iterable of
tessif transformersof which the corresponding supply chains need to be removed. Returned byinfer_pypsa_transformer_types()[‘generators’] by design.
- Returns:
components_to_remove – Dictionairy of componend uid lists keyed by pypsa component type of components which are to be removed.
- Return type:
Example
Use the example hub’s
py_harddata base for showcasing basic usage:Using the mwe:
>>> from tessif.examples.data.tsf.py_hard import create_mwe
>>> tessif_es = create_mwe() >>> gens = infer_pypsa_transformer_types( ... tessif_es.transformers)['generators']
>>> ctrs = compute_unneeded_supply_chains( ... tessif_es=tessif_es, ... pypsa_generator_uids=gens, ... ) >>> for comp_type, components_to_remove in ctrs.items(): ... if comp_type != 'supply_chains': ... print(f'{comp_type}: {components_to_remove}') Bus: ['Pipeline'] Generator: ['Gas Station']
- tessif.transform.es2es.ppsa.parse_flow_parameters(component, interface)[source]
Utility to parse flow related parameters from
Tessif componentsto Pypsa components- Parameters:
component¶ (
tessif.model.components.AbstractEsComponent) – Tessif component of which it’s flow related parameters are parsed into pypsa recognizable parametersinterface¶ (str) – String representing the flow related interface from the components point of view. One of the components
interfaces.
- Returns:
parsed_flow_parameters – Dictionairy representing the parsed flow parameters ready to be used as key word arguments for creating pypsa components.
- Return type:
- Raises:
ValueError: – A value error is raised in case a nominal_value (max flow_rate value) is requested to be infinit AND in addition to milp parameters. This is due to the fact that an infinit nominal value is parsed into an expansion problem because pypsa can not deal with infinity values. Which in turn causes conflicts in dealing with milp parameters.
Examples
Parsing an fpwe component:
>>> import tessif.examples.data.tsf.py_hard as coded_tsf_examples >>> fpwe = coded_tsf_examples.create_fpwe() >>> parsed_fps = parse_flow_parameters( ... component=list(fpwe.transformers)[0], ... interface='electricity', ... ) >>> for attr, value in parsed_fps.items(): ... print(f"{attr} = {value}") p_nom = 15 p_min_pu = 0.0 p_max_pu = 1.0 marginal_cost = 10
Parsing an infinit value into an expansion problem:
>>> import tessif.model.components as tcomps >>> tsf_infinity_source = tcomps.Source( ... name='infinity', ... outputs=('electricity',), ... flow_rates={'electricity': (0, float('+inf'))}, ... flow_costs={'electricity': 0}, ... ) >>> parsed_fps = parse_flow_parameters( ... component=tsf_infinity_source, ... interface='electricity', ... ) >>> for attr, value in parsed_fps.items(): ... print(f"{attr} = {value}") marginal_cost = 0 p_nom = 0.0 p_nom_min = 0.0 p_nom_max = inf p_nom_extendable = True capital_cost = 0 p_min_pu = 0.0 p_max_pu = 1.0
Using an infinit nominal value while also requesting milp parameters:
>>> import tessif.model.components as tcomps >>> tsf_infinity_source = tcomps.Source( ... name='infinity', ... outputs=('electricity',), ... flow_rates={'electricity': (0, float('+inf'))}, ... milp={'electricity': True} ... ) >>> try: ... parsed_fps = parse_flow_parameters( ... component=tsf_infinity_source, ... interface='electricity', ... ) ... except ValueError as e: ... print(e) Pypsa cannot handle infinity nominal value and milp constraints at the same time for a singular flow.
- tessif.transform.es2es.ppsa.create_pypsa_busses(busses)[source]
Creates pypsa buses out of tessif busses.
- Parameters:
busses¶ (Sequence) – Sequence of
tessif.model.components.Busobjects that are to be transformed into bus objects.- Returns:
Sequence of pypsa dictionairies representing the bus components.
- Return type:
Example
>>> import tessif.examples.data.tsf.py_hard as tsf_examples
>>> tsf_es = tsf_examples.create_mwe() >>> pypsa_busses = create_pypsa_busses(tsf_es.busses) >>> for i, bus in enumerate(pypsa_busses): ... for attr, value in bus.items(): ... print(f"{attr} = {value}") ... if i < len(pypsa_busses) - 1: ... print() class_name = Bus name = Pipeline x = 0.0 y = 0.0 carrier = None class_name = Bus name = Powerline x = 0.0 y = 0.0 carrier = None
- tessif.transform.es2es.ppsa.create_pypsa_generators_from_sources(sources, tessif_busses)[source]
Create pypsa generators out of tessif sources.
- Parameters:
sources¶ (Sequence) – Sequence of
tessif.model.components.Sourceobjects that are to be transformed into generator objects.tessif_busses¶ (Sequence) – Sequence of
tessif.model.components.Busobjects present in the same energy system assources.
- Returns:
Sequence of pypsa dictionairies representing the generator components representing tessif sources.
- Return type:
class:~collections.abc.Sequence
Note
Tessif sources can have multiple outputs, pypsa generators not. So in case a tessif source requests to have multiple outputs, a singular pypsa generator is created for each output.
Example
>>> import tessif.examples.data.tsf.py_hard as tsf_examples
>>> tsf_es = tsf_examples.create_fpwe() >>> pypsa_sources = create_pypsa_generators_from_sources( ... tsf_es.sources, tsf_es.busses) >>> for i, source in enumerate(pypsa_sources): ... for attr, value in source.items(): ... print(f"{attr} = {value}") ... if i < len(pypsa_sources) - 1: ... print() class_name = Generator name = Gas Station bus = Pipeline carrier = Gas Station.carrier p_nom = 100 p_min_pu = 0.0 p_max_pu = 1.0 marginal_cost = 10 efficiency = 1.0 flow_emissions = 3 class_name = Generator name = Solar Panel bus = Powerline carrier = electricity p_nom = 20 p_min_pu = [0.6 0.15 0.35] p_max_pu = [0.6 0.15 0.35] marginal_cost = 0 efficiency = 1.0 flow_emissions = 0 class_name = Carrier name = Gas Station.carrier co2_emissions = 3.0
Note how an extra Carrier object gets parsed to accomodate for the emission constraints.
- tessif.transform.es2es.ppsa.create_pypsa_sinks(sinks, tessif_busses, timeseries='max')[source]
Create pypsa generators out of tessif sinks.
- Parameters:
sinks¶ (Sequence) – Sequence of
tessif.model.components.Sinkobjects that are to be transformed into load objects.tessif_busses¶ (Sequence) – Sequence of
tessif.model.components.Busobjects present in the same energy system assources.timeseries¶ (str, ['max', 'min', 'avg']) –
String specifying how to handle timeseries values. Since pypsa loads can only handle fixed timeseries values (in contrast to time varying range of lower and upper load limits), a flag has to be stated how timeseries values are to be handled.
Must be one of:
max(default):flow_rate.maxis used, for setting the timeseries values.min(default):flow_rate.minis used, for setting the timeseries values.avg:The average of each time step between
flow_rate.min and flow_rate.maxis used.
- Returns:
Sequence of pypsa dictionairies representing the load components representing tessif sinks.
- Return type:
Example
>>> import tessif.examples.data.tsf.py_hard as tsf_examples
>>> tsf_es = tsf_examples.create_fpwe() >>> pypsa_sinks = create_pypsa_sinks( ... tsf_es.sinks, tsf_es.busses) >>> for i, sink in enumerate(pypsa_sinks): ... for attr, value in sink.items(): ... print(f"{attr} = {value}") ... if i < len(pypsa_sinks) - 1: ... print() class_name = Load name = Demand bus = Powerline p_set = 11.0
- tessif.transform.es2es.ppsa.create_pypsa_excess_sinks(sinks, tessif_busses)[source]
Create pypsa generators out of tessif sinks.
- Parameters:
sinks¶ (Sequence) – Sequence of
tessif.model.components.Sinkobjects that are to be transformed into storage units objects.tessif_busses¶ (Sequence) – Sequence of
tessif.model.components.Busobjects present in the same energy system assources.
- Returns:
Sequence of pypsa dictionairies representing the storage unit components representing tessif excess sinks.
- Return type:
Example
>>> import tessif.examples.data.tsf.py_hard as tsf_examples
>>> tsf_es = tsf_examples.create_simple_transformer_grid_es() >>> tsf_excess_sinks = [sink for sink in tsf_es.sinks ... if str(sink.uid) in ("HV-XS", "MV-XS")] >>> pypsa_excess_sinks = create_pypsa_excess_sinks( ... tsf_excess_sinks, tsf_es.busses) >>> for i, sink in enumerate(pypsa_excess_sinks): ... for attr, value in sink.items(): ... print(f"{attr} = {value}") ... if i < len(pypsa_excess_sinks) - 1: ... print() class_name = Bus name = HV-XS-Bus type = ignore class_name = Link name = HV-XS-Link bus0 = HV-Bus bus1 = HV-XS-Bus type = ignore efficiency = 1.0 p_min_pu = -1.0 marginal_cost = 10 p_nom_extendable = True capital_cost = 0 class_name = StorageUnit name = HV-XS bus = HV-XS-Bus type = excess_sink marginal_cost = 10 p_nom = 0.0 p_nom_min = 0.0 p_nom_max = inf p_nom_extendable = True capital_cost = 0 p_min_pu = -1.0 p_max_pu = 0.0 class_name = Bus name = MV-XS-Bus type = ignore class_name = Link name = MV-XS-Link bus0 = MV-Bus bus1 = MV-XS-Bus type = ignore efficiency = 1.0 p_min_pu = -1.0 marginal_cost = 10 p_nom_extendable = True capital_cost = 0 class_name = StorageUnit name = MV-XS bus = MV-XS-Bus type = excess_sink marginal_cost = 10 p_nom = 0.0 p_nom_min = 0.0 p_nom_max = inf p_nom_extendable = True capital_cost = 0 p_min_pu = -1.0 p_max_pu = 0.0
- tessif.transform.es2es.ppsa.create_pypsa_generators_from_transformers(transformers, tessif_busses)[source]
Create pypsa generators out of tessif transformers.
- Parameters:
Sequence of
tessif.model.components.Transformerobjects that are to be transformed into generator objects.tessif_busses¶ (Sequence) – Sequence of
tessif.model.components.Busobjects present in the same energy system astransformers.
- Returns:
Sequence of pypsa dictionairies representing the generator components representing tessif transformers.
- Return type:
class:~collections.abc.Sequence
Note
Tessif transformers can have multiple outputs, pypsa generators not. So in case a tessif transformer requests to have multiple outputs, a singular pypsa generator is created for each output.
Example
>>> import tessif.examples.data.tsf.py_hard as tsf_examples
>>> tsf_es = tsf_examples.create_fpwe() >>> pypsa_transformers = create_pypsa_generators_from_transformers( ... tsf_es.transformers, tsf_es.busses) >>> for i, transformer in enumerate(pypsa_transformers): ... for attr, value in transformer.items(): ... print(f"{attr} = {value}") ... if i < len(pypsa_transformers) - 1: ... print() class_name = Generator name = Generator bus = Powerline carrier = Generator.carrier p_nom = 15 p_min_pu = 0.0 p_max_pu = 1.0 marginal_cost = 10 efficiency = 0.42 flow_emissions = 10 class_name = Carrier name = Generator.carrier co2_emissions = 4.2
Note how an extra Carrier object gets parsed to accomodate for the Generator allocated emission constraints.
- tessif.transform.es2es.ppsa.create_pypsa_links_from_transformers(transformers, tessif_busses)[source]
Create pypsa links out of tessif transformers.
- Parameters:
transformers¶ (Sequence) – Sequence of
tessif.model.components.Transformerobjects that are to be transformed into link objects.tessif_busses¶ (Sequence) – Sequence of
tessif.model.components.Busobjects present in the same energy system astransformers.
- Returns:
Sequence of pypsa dictionairies representing the link components representing tessif transformers.
- Return type:
class:~collections.abc.Sequence
Warning
In case of chp transformation pypsa uses the highest installed capacity as reference capacity to calculate outgoing flows using the respective conversion factors.
Make sure the conversion factors reflect the expected installed capacities. If target capacaties are 100 and 50, make sure conversion factors are of ration 2/1 (i.e 0.6 and 0.3).
- Raises:
NotImplementedError – Raised if more than 1 input is used, because tessif can’t handle this case yet.
Example
>>> import tessif.examples.data.tsf.py_hard as tsf_examples
Singular output Link:
>>> tsf_es = tsf_examples.create_fpwe() >>> pypsa_links = create_pypsa_links_from_transformers( ... tsf_es.transformers, tsf_es.busses) >>> for i, link in enumerate(pypsa_links): ... for attr, value in link.items(): ... print(f"{attr} = {value}") ... if i < len(pypsa_links) - 1: ... print() class_name = Link name = Generator efficiency = 0.42 bus0 = Pipeline bus1 = Powerline siso_transformer = True flow_costs = 10 expansion_costs = 0 flow_emissions = 10 p_nom = 35.714285714285715 p_min_pu = 0.0 p_max_pu = 1.0 marginal_cost = 4.2 capital_cost = 0 carrier = Generator.carrier class_name = Carrier name = Generator.carrier co2_emissions = 4.2
Note how an extra Carrier object gets parsed to accomodate for the Link allocated emission constraints.
Multiple output Links:
>>> tsf_es = tsf_examples.create_hhes() >>> tessif_transformers = [ ... t for t in tsf_es.transformers ... if ('biomass chp' in str(t.uid) or 'chp3' in str(t.uid))] >>> pypsa_links = create_pypsa_links_from_transformers( ... tessif_transformers, tsf_es.busses) >>> for i, link in enumerate(pypsa_links): ... for attr, value in link.items(): ... print(f"{attr} = {value}") ... if i < len(pypsa_links) - 1: ... print() class_name = Link name = chp3 efficiency2 = 0.4075 efficiency = 0.4 bus0 = coal supply line bus1 = district heating pipeline bus2 = powerline multiple_outputs = True flow_costs2 = 82.0 flow_costs = 19.68 expansion_costs2 = 0 expansion_costs = 0 flow_emissions2 = 0 flow_emissions = 0 p_nom = 732.5 p_min_pu = 0.0 p_max_pu = 1.0 marginal_cost = 41.287 capital_cost = 0 p_nom2 = 461.3496932515338 class_name = Link name = biomass chp efficiency2 = 0.3841269841269841 efficiency = 1.0 bus0 = biomass logistics bus1 = district heating pipeline bus2 = powerline multiple_outputs = True flow_costs2 = 61 flow_costs = 20 expansion_costs2 = 0 expansion_costs = 0 flow_emissions2 = 0 flow_emissions = 0 p_nom = 126.0 p_min_pu = 0.0 p_max_pu = 1.0 marginal_cost = 43.43174603174603 capital_cost = 0 p_nom2 = 126.0
- tessif.transform.es2es.ppsa.create_pypsa_connectors(connectors, tessif_busses)[source]
Create pypsa links out of tessif connectors.
- Parameters:
Sequence of
tessif.model.components.Connectorobjects that are to be transformed into link objects.tessif_busses¶ (Sequence) – Sequence of
tessif.model.components.Busobjects present in the same energy system asconnectors.
- Returns:
Sequence of pypsa dictionairies representing the link components representing tessif connectors.
- Return type:
class:~collections.abc.Sequence
Note
Transforming
tessif connectorsto pypsa links following compatibility issues are faced:Pypsa links are one-directional according to their efficiency calculations. For bidirectional tessif links to make sense, bidirectional pypsa links are set to an efficiency of 1.0.
Tessif connectors are not designed to constrain their flows, where as pypsa links are. Therefor pypsa links created from tessif connectors are parameterized as follows:
nominal capacity is extendable having no extension costs
specific min/max power flow is set to -1.0/1.0 respectively
flow specific costs are set to 0.0
Example
>>> import tessif.examples.data.tsf.py_hard as tsf_examples
>>> tsf_es = tsf_examples.create_connected_es() >>> pypsa_links = create_pypsa_connectors( ... tsf_es.connectors, tsf_es.busses) >>> for i, link in enumerate(pypsa_links): ... for attr, value in link.items(): ... print(f"{attr} = {value}") ... if i < len(pypsa_links) - 1: ... print() class_name = Link name = connector bus0 = bus-01 bus1 = bus-02 efficiency = 1.0 p_min_pu = -1.0 marginal_cost = 0 p_nom_extendable = True capital_cost = 0
- tessif.transform.es2es.ppsa.create_pypsa_storages(storages, tessif_busses)[source]
Create pypsa generators out of tessif storages.
- Parameters:
Sequence of
tessif.model.components.Storageobjects that are to be transformed into generator objects.tessif_busses¶ (Sequence) – Sequence of
tessif.model.components.Busobjects present in the same energy system asstorages.
- Returns:
Sequence of pypsa dictionairies representing the storage components representing tessif storages.
- Return type:
Note
Pypsa forces the ratio of capacity and output flow to be constant, even during expansion. Doing that it restricts possible capacty expansion in relation to out_flow expansion. Meaning, outflow expansion is subject to parameterization while capacity expansion is just a subsequent byproduct.
Since this seems quite unintuitive, tessif parameterizes it the other way round, meaning the values for capacity expansion are enforced on the flow rate expansion repsecting the stated ration of capacity to flow rate.
Example
>>> import tessif.examples.data.tsf.py_hard as tsf_examples
>>> tsf_es = tsf_examples.create_fpwe() >>> pypsa_storages = create_pypsa_storages( ... tsf_es.storages, tsf_es.busses) >>> for i, storage in enumerate(pypsa_storages): ... for attr, value in storage.items(): ... print(f"{attr} = {value}") ... if i < len(pypsa_storages) - 1: ... print() class_name = StorageUnit name = Battery bus = Powerline max_hours = 0.3333333333333333 efficiency_store = 1 efficiency_dispatch = 1 state_of_charge_initial = 10 cyclic_state_of_charge = False standing_loss = 0.1 inflow = 0 flow_emissions = 0 p_nom = 30 p_min_pu = -1.0 p_max_pu = 1.0 marginal_cost = 0
- tessif.transform.es2es.ppsa.transform(tessif_es, transformer_style='infer', forced_links=None, excess_sinks=None)[source]
Transform a tessif energy system into a pypsa energy system.
- Parameters:
tessif_es¶ (
tessif.model.energy_system.AbstractEnergySystem) – The tessif energy system that is to be transformed into a pypsa energy system.String specifying how tessif transformer should be transformed into pypsa components:
infer(default):Transformers are treated according to
infer_pypsa_transformer_types()with the exception offorced_links, which are treated as Pypsa linkslinks:All transformers are treated as links, giving up pypsa commitment simulations and plant related CO2 emissions, but preserving tessif energy system representation. (Meaning all nodes present inside the tessif energy system will be present inside the pypsa energy system.)
forced_links¶ (Container, None, default=None) –
Container of uid representations of
tessif transformersto be transfrormed into pypsa links.excess_sinks¶ (Container, None, default=None) – Container of uid representations of
tessif sinksto be transfrormed into pypsa store units.
- Returns:
pypsa_es – The pypsa energy system that was transformed out of the tessif energy system.
- Return type:
Examples
Use the example hub’s
create_mwe()utility for showcasing basic usage:Create the mwe:
>>> from tessif.examples.data.tsf.py_hard import create_mwe >>> tessif_es = create_mwe() >>> for node_uid in sorted([n.uid for n in tessif_es.nodes]): ... print(node_uid) Battery Demand Gas Station Generator Pipeline Powerline
Transform the
tessif energy system:>>> import tessif.transform.es2es.ppsa as tsf2pypsa >>> pypsa_es = tsf2pypsa.transform(tessif_es)
Simulate the
pypsa energy systemusingtessif's simulate wrapper:>>> import tessif.simulate >>> optimized_pypsa_es = tessif.simulate.ppsa_from_es(pypsa_es)
Show the simulation objective using pypsa’s native interface:
>>> print(optimized_pypsa_es.objective) 61.0