From 937dfa19e95348d1617d674c2b9674161f46b30e Mon Sep 17 00:00:00 2001 From: ksyranid Date: Wed, 2 Aug 2017 17:10:26 +0200 Subject: [PATCH 001/135] Apply pytest with solver_name as a command line argument --- test/conftest.py | 13 +++++++++++++ test/readme.txt | 3 +++ test/test_ac_dc_lopf.py | 3 +-- test/test_opf_storage.py | 3 +-- test/test_sclopf_scigrid.py | 5 ++--- test/test_unit_commitment.py | 13 +++---------- 6 files changed, 23 insertions(+), 17 deletions(-) create mode 100644 test/conftest.py diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 000000000..6d95ce81f --- /dev/null +++ b/test/conftest.py @@ -0,0 +1,13 @@ +# static command line options from +# https://docs.pytest.org/en/latest/example/simple.html + +# content of conftest.py +import pytest + +def pytest_addoption(parser): + parser.addoption("--solver_name", action="store", default="glpk", + help="insert the solver name") + +@pytest.fixture +def solver_name(request): + return request.config.getoption("--solver_name") diff --git a/test/readme.txt b/test/readme.txt index 20dd02932..41a89c34e 100644 --- a/test/readme.txt +++ b/test/readme.txt @@ -16,6 +16,9 @@ Or to test all scripts just: py.test +Tests involving optimization can be performed with different solver by adding --solver_name=my_solver. +e.g.: py.test test_ac_dc_lopf.py --solver_name=gurobi + Note that PYPOWER 5.0 has a bug in the linear load flow, which was fixed in the github version in January 2016. diff --git a/test/test_ac_dc_lopf.py b/test/test_ac_dc_lopf.py index 68ec8c417..9ff9b9ff3 100644 --- a/test/test_ac_dc_lopf.py +++ b/test/test_ac_dc_lopf.py @@ -20,7 +20,7 @@ -def test_lopf(): +def test_lopf(solver_name): csv_folder_name = "../examples/ac-dc-meshed/ac-dc-data" @@ -33,7 +33,6 @@ def test_lopf(): #test results were generated with GLPK and other solvers may differ - solver_name = "glpk" snapshots = network.snapshots diff --git a/test/test_opf_storage.py b/test/test_opf_storage.py index 265bd2266..e4c09892b 100644 --- a/test/test_opf_storage.py +++ b/test/test_opf_storage.py @@ -19,7 +19,7 @@ -def test_opf(): +def test_opf(solver_name): csv_folder_name = "../examples/opf-storage-hvdc/opf-storage-data" @@ -27,7 +27,6 @@ def test_opf(): network = pypsa.Network(csv_folder_name=csv_folder_name) #test results were generated with GLPK and other solvers may differ - solver_name = "glpk" snapshots = network.snapshots diff --git a/test/test_sclopf_scigrid.py b/test/test_sclopf_scigrid.py index 4a1bd1efa..c953c7e59 100644 --- a/test/test_sclopf_scigrid.py +++ b/test/test_sclopf_scigrid.py @@ -6,7 +6,7 @@ import numpy as np -def test_sclopf(): +def test_sclopf(solver_name): csv_folder_name = "../examples/scigrid-de/scigrid-with-load-gen-trafos/" @@ -14,7 +14,6 @@ def test_sclopf(): network = pypsa.Network(csv_folder_name=csv_folder_name) #test results were generated with GLPK and other solvers may differ - solver_name = "glpk" #There are some infeasibilities without line extensions for line_name in ["316","527","602"]: @@ -25,7 +24,7 @@ def test_sclopf(): print("Performing security-constrained linear OPF:") - network.sclopf(network.snapshots[0],branch_outages=branch_outages) + network.sclopf(network.snapshots[0],branch_outages=branch_outages, solver_name=solver_name) #For the PF, set the P to the optimised P network.generators_t.p_set = network.generators_t.p.copy() diff --git a/test/test_unit_commitment.py b/test/test_unit_commitment.py index d99d85725..1aac22a08 100644 --- a/test/test_unit_commitment.py +++ b/test/test_unit_commitment.py @@ -8,7 +8,7 @@ -def test_part_load(): +def test_part_load(solver_name): """This test is based on https://pypsa.org/examples/unit-commitment.html and is not very comprehensive.""" @@ -36,8 +36,6 @@ def test_part_load(): nu.add("Load","load",bus="bus",p_set=[4000,6000,5000,800]) - solver_name = "glpk" - nu.lopf(nu.snapshots,solver_name=solver_name) expected_status = np.array([[1,1,1,0],[0,0,0,1]],dtype=float).T @@ -49,7 +47,7 @@ def test_part_load(): np.testing.assert_array_almost_equal(nu.generators_t.p.values,expected_dispatch) -def test_minimum_up_time(): +def test_minimum_up_time(solver_name): """This test is based on https://pypsa.org/examples/unit-commitment.html and is not very comprehensive.""" @@ -79,8 +77,6 @@ def test_minimum_up_time(): nu.add("Load","load",bus="bus",p_set=[4000,800,5000,3000]) - solver_name = "glpk" - nu.lopf(nu.snapshots,solver_name=solver_name) expected_status = np.array([[1,0,1,1],[1,1,1,0]],dtype=float).T @@ -93,7 +89,7 @@ def test_minimum_up_time(): -def test_minimum_down_time(): +def test_minimum_down_time(solver_name): """This test is based on https://pypsa.org/examples/unit-commitment.html and is not very comprehensive.""" @@ -122,9 +118,6 @@ def test_minimum_down_time(): nu.add("Load","load",bus="bus",p_set=[3000,800,3000,8000]) - - solver_name = "glpk" - nu.lopf(nu.snapshots,solver_name=solver_name) expected_status = np.array([[1,0,0,1],[0,1,1,0]],dtype=float).T From 648d1cf4815f24258534cc36698b259323b865f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Tue, 1 Aug 2017 12:06:21 +0200 Subject: [PATCH 002/135] Enable time-dependent marginal costs --- pypsa/component_attrs/generators.csv | 2 +- pypsa/component_attrs/links.csv | 2 +- pypsa/component_attrs/storage_units.csv | 4 +- pypsa/component_attrs/stores.csv | 2 +- pypsa/descriptors.py | 61 ++++++++++++++++++++++++- pypsa/opf.py | 20 +++++--- 6 files changed, 78 insertions(+), 13 deletions(-) diff --git a/pypsa/component_attrs/generators.csv b/pypsa/component_attrs/generators.csv index 2d0b27510..0046fae4a 100644 --- a/pypsa/component_attrs/generators.csv +++ b/pypsa/component_attrs/generators.csv @@ -13,7 +13,7 @@ p_set,static or series,MW,0.,active power set point (for PF),Input (optional) q_set,static or series,MVar,0.,reactive power set point (for PF),Input (optional) sign,float,n/a,1.,power sign,Input (optional) carrier,string,n/a,n/a,"Prime mover energy carrier (e.g. coal, gas, wind, solar); required for global constraints on primary energy in OPF",Input (optional) -marginal_cost,float,currency/MWh,0.,"Marginal cost of production of 1 MWh.",Input (optional) +marginal_cost,static or series,currency/MWh,0.,"Marginal cost of production of 1 MWh.",Input (optional) capital_cost,float,currency/MW,0.,"Capital cost of extending p_nom by 1 MW.",Input (optional) efficiency,float,per unit,1.,"Ratio between primary energy and electrical energy, e.g. takes value 0.4 MWh_elec/MWh_thermal for gas. This is required for global constraints on primary energy in OPF.",Input (optional) committable,boolean,n/a,False,Use unit commitment (only possible if p_nom is not extendable).,Input (optional) diff --git a/pypsa/component_attrs/links.csv b/pypsa/component_attrs/links.csv index 4611041cd..a8cd131a8 100644 --- a/pypsa/component_attrs/links.csv +++ b/pypsa/component_attrs/links.csv @@ -12,7 +12,7 @@ p_set,static or series,MW,0.,"The dispatch set point for p0 of the link in PF.", p_min_pu,static or series,per unit of p_nom,0.,"Minimal dispatch (can also be negative) per unit of p_nom for the link in OPF.",Input (optional) p_max_pu,static or series,per unit of p_nom,1.,"Maximal dispatch (can also be negative) per unit of p_nom for the link in OPF.",Input (optional) capital_cost,float,currency/MW,0.,"Capital cost of extending p_nom by 1 MW.",Input (optional) -marginal_cost,float,currency/MWh,0.,"Marginal cost of transfering 1 MWh (before efficiency losses) from bus0 to bus1. NB: marginal cost only makes sense in OPF if p_max_pu >= 0.",Input (optional) +marginal_cost,static or series,currency/MWh,0.,"Marginal cost of transfering 1 MWh (before efficiency losses) from bus0 to bus1. NB: marginal cost only makes sense in OPF if p_max_pu >= 0.",Input (optional) length,float,km,0.,"Length of line, useful for calculating the capital cost.",Input (optional) terrain_factor,float,per unit,1.,"Terrain factor for increasing capital cost.",Input (optional) p0,series,MW,0.,Active power at bus0 (positive if branch is withdrawing power from bus0).,Output diff --git a/pypsa/component_attrs/storage_units.csv b/pypsa/component_attrs/storage_units.csv index c306895f5..64962f0ff 100644 --- a/pypsa/component_attrs/storage_units.csv +++ b/pypsa/component_attrs/storage_units.csv @@ -13,7 +13,7 @@ p_set,static or series,MW,0.,active power set point (for PF),Input (optional) q_set,static or series,MVar,0.,reactive power set point (for PF),Input (optional) sign,float,n/a,1.,power sign,Input (optional) carrier,string,n/a,n/a,"Prime mover energy carrier (e.g. coal, gas, wind, solar); required for global constraints on primary energy in OPF",Input (optional) -marginal_cost,float,currency/MWh,0.,"Marginal cost of production of 1 MWh.",Input (optional) +marginal_cost,static or series,currency/MWh,0.,"Marginal cost of production of 1 MWh.",Input (optional) capital_cost,float,currency/MW,0.,"Capital cost of extending p_nom by 1 MW.",Input (optional) state_of_charge_initial,float,MWh,0.,State of charge before the snapshots in the OPF.,Input (optional) state_of_charge_set,static or series,MWh,NaN,State of charge set points for snapshots in the OPF.,Input (optional) @@ -27,4 +27,4 @@ p,series,MW,0.,active power at bus (positive if net generation),Output q,series,MVar,0.,reactive power (positive if net generation),Output state_of_charge,series,MWh,NaN,State of charge as calculated by the OPF.,Output spill,series,MW,0.,Spillage for each snapshot.,Output -p_nom_opt,float,MW,0.,Optimised nominal power.,Output \ No newline at end of file +p_nom_opt,float,MW,0.,Optimised nominal power.,Output diff --git a/pypsa/component_attrs/stores.csv b/pypsa/component_attrs/stores.csv index b67cee434..5d5e032a8 100644 --- a/pypsa/component_attrs/stores.csv +++ b/pypsa/component_attrs/stores.csv @@ -13,7 +13,7 @@ e_cyclic,boolean,n/a,False,"Switch: if True, then e_initial is ignored and the i p_set,static or series,MW,0.,active power set point (for PF),Input (optional) q_set,static or series,MVar,0.,reactive power set point (for PF),Input (optional) sign,float,n/a,1.,power sign,Input (optional) -marginal_cost,float,currency/MWh,0.,"Marginal cost of production of 1 MWh.",Input (optional) +marginal_cost,static or series,currency/MWh,0.,"Marginal cost of production of 1 MWh.",Input (optional) capital_cost,float,currency/MWh,0.,"Capital cost of extending e_nom by 1 MWh.",Input (optional) standing_loss,float,per unit,0.,Losses per hour to energy.,Input (optional) p,series,MW,0.,active power at bus (positive if net generation),Output diff --git a/pypsa/descriptors.py b/pypsa/descriptors.py index cb9ee9247..ed5b6bd0b 100644 --- a/pypsa/descriptors.py +++ b/pypsa/descriptors.py @@ -37,14 +37,13 @@ from weakref import WeakKeyDictionary from collections import OrderedDict +from itertools import repeat import networkx as nx import pandas as pd import numpy as np import re -import inspect - import logging logger = logging.getLogger(__name__) @@ -154,6 +153,8 @@ def get_switchable_as_dense(network, component, attr, snapshots=None, inds=None) network : pypsa.Network component : string Component object name, e.g. 'Generator' or 'Link' + attr : string + Attribute name snapshots : pandas.Index Restrict to these snapshots rather than network.snapshots. inds : pandas.Index @@ -188,6 +189,62 @@ def get_switchable_as_dense(network, component, attr, snapshots=None, inds=None) pnl[attr].loc[snapshots, varying_i] ], axis=1).reindex(columns=index)) +def get_switchable_as_iter(network, component, attr, snapshots, inds=None): + """ + Return an iterator over snapshots for a time-varying component + attribute with values for all non-time-varying components filled + in with the default values for the attribute. + + Parameters + ---------- + network : pypsa.Network + component : string + Component object name, e.g. 'Generator' or 'Link' + attr : string + Attribute name + snapshots : pandas.Index + Restrict to these snapshots rather than network.snapshots. + inds : pandas.Index + Restrict to these items rather than all of network.{generators,..}.index + + Returns + ------- + pandas.DataFrame + + Examples + -------- + >>> get_switchable_as_iter(network, 'Generator', 'p_max_pu', snapshots) + +""" + + df = network.df(component) + pnl = network.pnl(component) + + index = df.index + varying_i = pnl[attr].columns + fixed_i = df.index.difference(varying_i) + + if inds is not None: + inds = pd.Index(inds) + index = inds.intersection(index) + varying_i = inds.intersection(varying_i) + fixed_i = inds.intersection(fixed_i) + + # Short-circuit only fixed + if len(varying_i) == 0: + return repeat(df.loc[fixed_i, attr], len(snapshots)) + + def is_same_indices(i1, i2): return len(i1) == len(i2) and (i1 == i2).all() + if is_same_indices(fixed_i.append(varying_i), index): + def reindex_maybe(s): return s + else: + def reindex_maybe(s): return s.reindex(index) + + return ( + reindex_maybe(df.loc[fixed_i, attr].append(pnl[attr].loc[sn, varying_i])) + for sn in snapshots + ) + def allocate_series_dataframes(network, series): """ diff --git a/pypsa/opf.py b/pypsa/opf.py index 902878b37..998657500 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -54,7 +54,8 @@ patch_optsolver_free_model_before_solving, patch_optsolver_record_memusage_before_solving, empty_network) -from .descriptors import get_switchable_as_dense, allocate_series_dataframes +from .descriptors import (get_switchable_as_dense, get_switchable_as_iter, + allocate_series_dataframes) @@ -998,25 +999,32 @@ def define_linear_objective(network,snapshots): sdc_gens_i = network.generators.index[~network.generators.p_nom_extendable & network.generators.committable & (network.generators.shut_down_cost > 0)] + marginal_cost_it = zip(get_switchable_as_iter(network, 'Generator', 'marginal_cost', snapshots), + get_switchable_as_iter(network, 'StorageUnit', 'marginal_cost', snapshots), + get_switchable_as_iter(network, 'Store', 'marginal_cost', snapshots), + get_switchable_as_iter(network, 'Link', 'marginal_cost', snapshots)) + objective = LExpression() - for sn in snapshots: + for sn, marginal_cost in zip(snapshots, marginal_cost_it): + gen_mc, su_mc, st_mc, link_mc = marginal_cost + weight = network.snapshot_weightings[sn] for gen in network.generators.index: - coefficient = network.generators.at[gen, "marginal_cost"] * weight + coefficient = gen_mc.at[gen] * weight objective.variables.extend([(coefficient, model.generator_p[gen, sn])]) for su in network.storage_units.index: - coefficient = network.storage_units.at[su, "marginal_cost"] * weight + coefficient = su_mc.at[su] * weight objective.variables.extend([(coefficient, model.storage_p_dispatch[su,sn])]) for store in network.stores.index: - coefficient = network.stores.at[store, "marginal_cost"] * weight + coefficient = st_mc.at[store] * weight objective.variables.extend([(coefficient, model.store_p[store,sn])]) for link in network.links.index: - coefficient = network.links.at[link, "marginal_cost"] * weight + coefficient = link_mc.at[link] * weight objective.variables.extend([(coefficient, model.link_p[link,sn])]) From 45670b29915b68ff95c65ba7398b4797e1efd140 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Tue, 14 Nov 2017 17:53:23 +0100 Subject: [PATCH 003/135] components: Network.__init__() now takes generic import argument First argument changed from "csv_folder_name" to "import_name". Whether it is a CSV folder or HDF5 is recognised based on the name. --- examples/unit-commitment.py | 1 - pypsa/components.py | 16 ++++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/examples/unit-commitment.py b/examples/unit-commitment.py index 68540e7a4..9f4de05e0 100644 --- a/examples/unit-commitment.py +++ b/examples/unit-commitment.py @@ -239,4 +239,3 @@ nu.generators.initial_status nu.generators.loc["coal"] - diff --git a/pypsa/components.py b/pypsa/components.py index e6169e75f..4b8b79f46 100644 --- a/pypsa/components.py +++ b/pypsa/components.py @@ -125,8 +125,8 @@ class Network(Basic): Parameters ---------- - csv_folder_name : string - Name of folder from which to import CSVs of network data. + import_name : string + Name of HDF5 .h5 store or folder from which to import CSVs of network data. name : string, default "" Network name. ignore_standard_types : boolean, default False @@ -140,7 +140,8 @@ class Network(Basic): Examples -------- - >>> nw = pypsa.Network(csv_folder_name=/my/folder) + >>> nw1 = pypsa.Network("my_store.h5") + >>> nw2 = pypsa.Network("/my/folder") """ @@ -189,7 +190,7 @@ class Network(Basic): adjacency_matrix = adjacency_matrix - def __init__(self, csv_folder_name=None, name="", ignore_standard_types=False, **kwargs): + def __init__(self, import_name=None, name="", ignore_standard_types=False, **kwargs): from .__init__ import __version__ as pypsa_version @@ -240,8 +241,11 @@ def __init__(self, csv_folder_name=None, name="", ignore_standard_types=False, * self.read_in_default_standard_types() - if csv_folder_name is not None: - self.import_from_csv_folder(csv_folder_name) + if import_name is not None: + if import_name[-3:] == ".h5": + self.import_from_hdf5(import_name) + else: + self.import_from_csv_folder(import_name) for key, value in iteritems(kwargs): From 71850508cc3bb2a774e00916c4b6dee4684a84ea Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Tue, 14 Nov 2017 18:36:21 +0100 Subject: [PATCH 004/135] components, opf: Passive branches have new attribute s_max_pu s_max_pu is the maximum allowed absolute flow per unit of s_nom for the OPF. | flow | \leq s_max_pu * s_nom For lines this can represent an n-1 contingency factor or it can be time-varying to represent weather-dependent dynamic line rating. --- pypsa/component_attrs/generators.csv | 2 +- pypsa/component_attrs/lines.csv | 1 + pypsa/component_attrs/transformers.csv | 1 + pypsa/opf.py | 16 ++++++++++------ 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/pypsa/component_attrs/generators.csv b/pypsa/component_attrs/generators.csv index 2d0b27510..d5e681ef5 100644 --- a/pypsa/component_attrs/generators.csv +++ b/pypsa/component_attrs/generators.csv @@ -8,7 +8,7 @@ p_nom_extendable,boolean,n/a,False,Switch to allow capacity p_nom to be extended p_nom_min,float,MW,0.,"If p_nom is extendable in OPF, set its minimum value.",Input (optional) p_nom_max,float,MW,inf,"If p_nom is extendable in OPF, set its maximum value (e.g. limited by technical potential).",Input (optional) p_min_pu,static or series,per unit,0.,"The minimum output for each snapshot per unit of p_nom for the OPF (e.g. for variable renewable generators this can change due to weather conditions and compulsory feed-in; for conventional generators it represents a minimal dispatch). Note that if comittable is False and p_min_pu > 0, this represents a must-run condition.",Input (optional) -p_max_pu,static or series,per unit,1.,"The maximum output for each snapshot per unit of p_nom for the OPF (e.g. for varialbe renewable generators this can change due to weather conditions; for conventional generators it represents a maximum dispatch).",Input (optional) +p_max_pu,static or series,per unit,1.,"The maximum output for each snapshot per unit of p_nom for the OPF (e.g. for variable renewable generators this can change due to weather conditions; for conventional generators it represents a maximum dispatch).",Input (optional) p_set,static or series,MW,0.,active power set point (for PF),Input (optional) q_set,static or series,MVar,0.,reactive power set point (for PF),Input (optional) sign,float,n/a,1.,power sign,Input (optional) diff --git a/pypsa/component_attrs/lines.csv b/pypsa/component_attrs/lines.csv index 8ff92c199..bea874737 100644 --- a/pypsa/component_attrs/lines.csv +++ b/pypsa/component_attrs/lines.csv @@ -11,6 +11,7 @@ s_nom,float,MVA,0.,Limit of apparent power which can pass through branch.,Input s_nom_extendable,boolean,n/a,False,Switch to allow capacity s_nom to be extended in OPF.,Input (optional) s_nom_min,float,MVA,0.,"If s_nom is extendable in OPF, set its minimum value.",Input (optional) s_nom_max,float,MVA,inf,"If s_nom is extendable in OPF, set its maximum value (e.g. limited by potential).",Input (optional) +s_max_pu,static or series,per unit,1.,"The maximum allowed absolute flow per unit of s_nom for the OPF (e.g. can be set <1 to approximate n-1 factor, or can be time-varying to represent weather-dependent dynamic line rating for overhead lines).",Input (optional) capital_cost,float,currency/MVA,0.,"Capital cost of extending s_nom by 1 MVA.",Input (optional) length,float,km,0.,"Length of line used when ""type"" is set, also useful for calculating the capital cost.",Input (optional) terrain_factor,float,per unit,1.,"Terrain factor for increasing capital cost.",Input (optional) diff --git a/pypsa/component_attrs/transformers.csv b/pypsa/component_attrs/transformers.csv index 591da135d..a7f821965 100644 --- a/pypsa/component_attrs/transformers.csv +++ b/pypsa/component_attrs/transformers.csv @@ -12,6 +12,7 @@ s_nom,float,MVA,0.,Limit of apparent power which can pass through branch.,Input s_nom_extendable,boolean,n/a,False,Switch to allow capacity s_nom to be extended in OPF.,Input (optional) s_nom_min,float,MVA,0.,"If s_nom is extendable in OPF, set its minimum value.",Input (optional) s_nom_max,float,MVA,inf,"If s_nom is extendable in OPF, set its maximum value (e.g. limited by potential).",Input (optional) +s_max_pu,static or series,per unit,1.,"The maximum allowed absolute flow per unit of s_nom for the OPF.",Input (optional) capital_cost,float,currency/MVA,0.,"Capital cost of extending s_nom by 1 MVA.",Input (optional) num_parallel,float,n/a,1,"When ""type"" is set, this is the number of parallel transformers (can also be fractional). If ""type"" is empty """" this value is ignored.",Input (optional) tap_ratio,float,per unit,1.,"Ratio of per unit voltages at each bus for tap changed. Ignored if type defined.",Input (optional) diff --git a/pypsa/opf.py b/pypsa/opf.py index 05ac47c71..7575ba0da 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -822,17 +822,22 @@ def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False def define_passive_branch_constraints(network,snapshots): + from .components import passive_branch_components + passive_branches = network.passive_branches() extendable_branches = passive_branches[passive_branches.s_nom_extendable] fixed_branches = passive_branches[~ passive_branches.s_nom_extendable] + s_max_pu = pd.concat({c : get_switchable_as_dense(network,c,'s_max_pu') + for c in passive_branch_components}, axis=1) + flow_upper = {(b[0],b[1],sn) : [[(1,network.model.passive_branch_p[b[0],b[1],sn])], - "<=", fixed_branches.at[b,"s_nom"]] + "<=", s_max_pu.at[sn,b]*fixed_branches.at[b,"s_nom"]] for b in fixed_branches.index for sn in snapshots} flow_upper.update({(b[0],b[1],sn) : [[(1,network.model.passive_branch_p[b[0],b[1],sn]), - (-1,network.model.passive_branch_s_nom[b[0],b[1]])],"<=",0] + (-s_max_pu.at[sn,b],network.model.passive_branch_s_nom[b[0],b[1]])],"<=",0] for b in extendable_branches.index for sn in snapshots}) @@ -840,12 +845,12 @@ def define_passive_branch_constraints(network,snapshots): list(passive_branches.index), snapshots) flow_lower = {(b[0],b[1],sn) : [[(1,network.model.passive_branch_p[b[0],b[1],sn])], - ">=", -fixed_branches.at[b,"s_nom"]] + ">=", -s_max_pu.at[sn,b]*fixed_branches.at[b,"s_nom"]] for b in fixed_branches.index for sn in snapshots} flow_lower.update({(b[0],b[1],sn): [[(1,network.model.passive_branch_p[b[0],b[1],sn]), - (1,network.model.passive_branch_s_nom[b[0],b[1]])],">=",0] + (s_max_pu.at[sn,b],network.model.passive_branch_s_nom[b[0],b[1]])],">=",0] for b in extendable_branches.index for sn in snapshots}) @@ -1374,7 +1379,7 @@ def network_lopf_solve(network, snapshots=None, formulation="angles", solver_opt with empty_network(network): network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, options=solver_options) else: - network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, options=solver_options) + network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, options=solver_options) if logger.level > 0: network.results.write() @@ -1450,4 +1455,3 @@ def network_lopf(network, snapshots=None, solver_name="glpk", solver_io=None, network_lopf_prepare_solver(network, solver_name=solver_name, solver_io=solver_io) return network_lopf_solve(network, snapshots, formulation=formulation, solver_options=solver_options, keep_files=keep_files, free_memory=free_memory) - From f51e26c92a44e1902b333011b234592bec521e08 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Fri, 17 Nov 2017 13:30:34 +0100 Subject: [PATCH 005/135] doc, website: Update publications --- README.rst | 15 ++++++++------- doc/introduction.rst | 9 +++++---- website/index.org | 9 +++++---- website/publications/index.org | 4 +++- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/README.rst b/README.rst index 554930cd3..2a480719d 100644 --- a/README.rst +++ b/README.rst @@ -57,12 +57,12 @@ PyPSA can calculate: * static power flow (using both the full non-linear network equations and the linearised network equations) -* linear optimal power flow (optimisation of power plant and storage - dispatch within network constraints, using the linear network - equations, over several snapshots) +* linear optimal power flow (least-cost optimisation of power plant + and storage dispatch within network constraints, using the linear + network equations, over several snapshots) * security-constrained linear optimal power flow -* total electricity system investment optimisation (using linear - network equations, over several snapshots simultaneously for +* total electricity system least-cost investment optimisation (using + linear network equations, over several snapshots simultaneously for optimisation of generation and storage dispatch and investment in the capacities of generation, storage and transmission) @@ -183,8 +183,9 @@ Citing PyPSA If you use PyPSA for your research, we would appreciate it if you -would cite the following preprint paper (which has not yet been -through peer review): +would cite the following preprint paper (which has been accepted to +the `Journal of Open Research Software +`_): * T. Brown, J. Hörsch, D. Schlachtberger, `PyPSA: Python for Power System Analysis `_, 2017, diff --git a/doc/introduction.rst b/doc/introduction.rst index eb1423865..ab65a644f 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -32,11 +32,11 @@ PyPSA can calculate: * static power flow (using both the full non-linear network equations and the linearised network equations) -* linear optimal power flow (optimisation of power plant and storage +* linear optimal power flow (least-cost optimisation of power plant and storage dispatch within network constraints, using the linear network equations, over several snapshots) * security-constrained linear optimal power flow -* total electricity system investment optimisation (using linear +* total electricity system least-cost investment optimisation (using linear network equations, over several snapshots simultaneously for optimisation of generation and storage dispatch and investment in the capacities of generation, storage and transmission) @@ -190,8 +190,9 @@ Citing PyPSA If you use PyPSA for your research, we would appreciate it if you -would cite the following preprint paper (which has not yet been -through peer review): +would cite the following preprint paper (which has been accepted to +the `Journal of Open Research Software +`_): * T. Brown, J. Hörsch, D. Schlachtberger, `PyPSA: Python for Power System Analysis `_, 2017, diff --git a/website/index.org b/website/index.org index ed9802a98..fd1c286a1 100644 --- a/website/index.org +++ b/website/index.org @@ -49,11 +49,11 @@ PyPSA can calculate: - static power flow (using both the full non-linear network equations and the linearised network equations) -- linear optimal power flow (optimisation of power plant and storage +- linear optimal power flow (least-cost optimisation of power plant and storage dispatch within network constraints, using the linear network equations, over several snapshots) - security-constrained linear optimal power flow -- total electricity system investment optimisation (using linear +- total electricity system least-cost investment optimisation (using linear network equations, over several snapshots simultaneously for optimisation of generation and storage dispatch and investment in the capacities of generation, storage and transmission) @@ -177,8 +177,9 @@ PyPSA has a Google Group [[https://groups.google.com/group/pypsa][forum * Citing PyPSA If you use PyPSA for your research, we would appreciate it if you -would cite the following preprint paper (which has not yet been -through peer review): +would cite the following preprint paper (which has been accepted to +the [[https://openresearchsoftware.metajnl.com/][Journal of Open Research Software]]): + - T. Brown, J. H\ouml{}rsch, D. Schlachtberger, [[https://arxiv.org/abs/1707.09913][PyPSA: Python for Power System Analysis]], 2017, [[https://arxiv.org/abs/1707.09913][preprint arXiv:1707.09913]] diff --git a/website/publications/index.org b/website/publications/index.org index 696344777..4d3a72197 100644 --- a/website/publications/index.org +++ b/website/publications/index.org @@ -1,7 +1,8 @@ #+TITLE: Python for Power System Analysis: Publications #+OPTIONS: toc:nil no default TOC -There is a preprint paper describing PyPSA (which has not yet been through peer review): +There is a preprint paper describing PyPSA (which has been accepted +to the [[https://openresearchsoftware.metajnl.com/][Journal of Open Research Software]]): - T. Brown, J. H\ouml{}rsch, D. Schlachtberger, [[https://arxiv.org/abs/1707.09913][PyPSA: Python for Power System Analysis]], 2017, [[https://arxiv.org/abs/1707.09913][preprint arXiv:1707.09913]] @@ -16,6 +17,7 @@ linked from the overall PyPSA Zenodo DOI: The following research papers have used PyPSA: +- J. H\ouml{}rsch, Joanne Calitz, [[https://arxiv.org/abs/1710.11199][PyPSA-ZA: Investment and operation co-optimization of integrating wind and solar in South Africa at high spatial and temporal detail]], 2017, [[https://arxiv.org/abs/1710.11199][preprint]], [[https://github.com/FRESNA/pypsa-za][code]] - Markus Groissb\ouml{}ck, Alexandre Gusmao, [[https://arxiv.org/abs/1709.03761][Impact of high renewable penetration scenarios on system reliability: two case studies in the Kingdom of Saudi Arabia]], 2017, [[https://arxiv.org/abs/1709.03761][preprint]] - J. H\ouml{}rsch, T. Brown, [[https://doi.org/10.1109/EEM.2017.7982024][The role of spatial scale in joint optimisations of generation and transmission for European highly renewable scenarios]], 2017, accepted to [[http://eem2017.com/][14th International Conference on the European Energy Market - EEM 2017]], [[https://arxiv.org/abs/1705.07617][preprint]] - D. Schlachtberger, T. Brown, S. Schramm, M. Greiner, [[https://doi.org/10.1016/j.energy.2017.06.004][The Benefits of Cooperation in a Highly Renewable European Electricity Network]], Energy, Volume 134, 1 September 2017, Pages 469-481, [[https://arxiv.org/abs/1704.05492][preprint]], [[https://doi.org/10.5281/zenodo.804337][all input data, code, output data on Zenodo]] From e10bb848ef03f7b319a8718e9710336b149ac8ed Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Sun, 19 Nov 2017 14:53:07 +0100 Subject: [PATCH 006/135] Website: Add WindAc Africa reference Also add sub-network description so that components.csv renders on github. --- pypsa/components.csv | 2 +- website/publications/index.org | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pypsa/components.csv b/pypsa/components.csv index 0516c8f30..d82aa4cdf 100644 --- a/pypsa/components.csv +++ b/pypsa/components.csv @@ -1,6 +1,6 @@ component,list_name,description Network,networks,"Container for all components and functions which act upon the whole network." -SubNetwork,sub_networks +SubNetwork,sub_networks,"Subsets of buses and passive branches (i.e. lines and transformers) that are connected (i.e. synchronous areas)." Bus,buses,"Electrically fundamental node where x-port objects attach." Carrier,carriers,"Energy carrier, such as AC, DC, heat, wind, PV or coal. Buses have direct carriers and Generators indicate their primary energy carriers. The Carrier can track properties relevant for global constraints, such as CO2 emissions." GlobalConstraint,global_constraints,"Constraints for OPF that affect many components, such as CO2 emission constraints." diff --git a/website/publications/index.org b/website/publications/index.org index 4d3a72197..133edca82 100644 --- a/website/publications/index.org +++ b/website/publications/index.org @@ -17,7 +17,7 @@ linked from the overall PyPSA Zenodo DOI: The following research papers have used PyPSA: -- J. H\ouml{}rsch, Joanne Calitz, [[https://arxiv.org/abs/1710.11199][PyPSA-ZA: Investment and operation co-optimization of integrating wind and solar in South Africa at high spatial and temporal detail]], 2017, [[https://arxiv.org/abs/1710.11199][preprint]], [[https://github.com/FRESNA/pypsa-za][code]] +- J. H\ouml{}rsch, Joanne Calitz, [[https://arxiv.org/abs/1710.11199][PyPSA-ZA: Investment and operation co-optimization of integrating wind and solar in South Africa at high spatial and temporal detail]], presented at the [[http://windac-africa.com/][WindAc Africa Conference]], 2017, [[https://arxiv.org/abs/1710.11199][preprint]], [[https://github.com/FRESNA/pypsa-za][code]] - Markus Groissb\ouml{}ck, Alexandre Gusmao, [[https://arxiv.org/abs/1709.03761][Impact of high renewable penetration scenarios on system reliability: two case studies in the Kingdom of Saudi Arabia]], 2017, [[https://arxiv.org/abs/1709.03761][preprint]] - J. H\ouml{}rsch, T. Brown, [[https://doi.org/10.1109/EEM.2017.7982024][The role of spatial scale in joint optimisations of generation and transmission for European highly renewable scenarios]], 2017, accepted to [[http://eem2017.com/][14th International Conference on the European Energy Market - EEM 2017]], [[https://arxiv.org/abs/1705.07617][preprint]] - D. Schlachtberger, T. Brown, S. Schramm, M. Greiner, [[https://doi.org/10.1016/j.energy.2017.06.004][The Benefits of Cooperation in a Highly Renewable European Electricity Network]], Energy, Volume 134, 1 September 2017, Pages 469-481, [[https://arxiv.org/abs/1704.05492][preprint]], [[https://doi.org/10.5281/zenodo.804337][all input data, code, output data on Zenodo]] From 51fda3d66114c6160edd0212fd627d06fd9a6bf2 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Fri, 24 Nov 2017 16:23:10 +0100 Subject: [PATCH 007/135] opf: Change objective from "Lower bound" to "Upper bound" Because for MILP problems under certain circumstances CPLEX records the "Lower bound" as the relaxed value. "Upper bound" is correctly recorded as the integer objective value. See Issue #20. --- pypsa/opf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index 7575ba0da..62a3dfde5 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -1089,7 +1089,7 @@ def extract_optimisation_results(network, snapshots, formulation="angles"): 'Link': ['p0', 'p1', 'mu_lower', 'mu_upper']}) #get value of objective function - network.objective = network.results["Problem"][0]["Lower bound"] + network.objective = network.results["Problem"][0]["Upper bound"] model = network.model From 954e5a562963c701e108b494c20b4929b2cbd449 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Fri, 24 Nov 2017 16:32:47 +0100 Subject: [PATCH 008/135] components: All csv_folder_name to initiate Network but deprecate --- pypsa/components.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pypsa/components.py b/pypsa/components.py index 4b8b79f46..5d849b6ac 100644 --- a/pypsa/components.py +++ b/pypsa/components.py @@ -131,6 +131,8 @@ class Network(Basic): Network name. ignore_standard_types : boolean, default False If True, do not read in PyPSA standard types into standard types DataFrames. + csv_folder_name : string + Name of folder from which to import CSVs of network data. Overrides import_name. kwargs Any remaining attributes to set @@ -190,7 +192,7 @@ class Network(Basic): adjacency_matrix = adjacency_matrix - def __init__(self, import_name=None, name="", ignore_standard_types=False, **kwargs): + def __init__(self, import_name=None, name="", ignore_standard_types=False, csv_folder_name=None,**kwargs): from .__init__ import __version__ as pypsa_version @@ -241,12 +243,14 @@ def __init__(self, import_name=None, name="", ignore_standard_types=False, **kwa self.read_in_default_standard_types() - if import_name is not None: + if import_name is not None and csv_folder_name is None: if import_name[-3:] == ".h5": self.import_from_hdf5(import_name) else: self.import_from_csv_folder(import_name) - + elif csv_folder_name is not None: + logger.warning("The argument csv_folder_name for initiating Network() is deprecated, please use import_name instead.") + self.import_from_csv_folder(csv_folder_name) for key, value in iteritems(kwargs): setattr(self, key, value) From a141baa26f0216525aeb222b5ad87cef42b84280 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Wed, 29 Nov 2017 16:31:02 +0100 Subject: [PATCH 009/135] Work-around pandas 0.21.0 convention change pd.Series().sum() -> nan pandas 0.21.0 changes sum() behavior so that the result of applying sum over an empty DataFrame is NaN. See pandas-dev/pandas#9422 for context. The work-around introduces a zsum function on pd.Series, which returns 0 on an empty Series. Fixes #26. --- pypsa/descriptors.py | 9 +++++++++ pypsa/opf.py | 16 +++++++++------- pypsa/pf.py | 10 ++++++---- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/pypsa/descriptors.py b/pypsa/descriptors.py index cb9ee9247..25da6eb29 100644 --- a/pypsa/descriptors.py +++ b/pypsa/descriptors.py @@ -218,3 +218,12 @@ def allocate_series_dataframes(network, series): for attr in attributes: pnl[attr] = pnl[attr].reindex(columns=df.index, fill_value=network.components[component]["attrs"].at[attr,"default"]) + +def zsum(s, *args, **kwargs): + """ + pandas 0.21.0 changes sum() behavior so that the result of applying sum + over an empty DataFrame is NaN. + + Meant to be set as pd.Series.zsum = zsum. + """ + return 0 if s.empty else s.sum(*args, **kwargs) diff --git a/pypsa/opf.py b/pypsa/opf.py index 62a3dfde5..6ac9d2465 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -28,8 +28,8 @@ __author__ = "Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)" __copyright__ = "Copyright 2015-2017 Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS), GNU GPL 3" -import pandas as pd import numpy as np +import pandas as pd from scipy.sparse.linalg import spsolve from pyomo.environ import (ConcreteModel, Var, Objective, NonNegativeReals, Constraint, Reals, @@ -60,7 +60,9 @@ class PersistentSolver(): pass patch_optsolver_free_model_before_solving, patch_optsolver_record_memusage_before_solving, empty_network, free_pyomo_initializers) -from .descriptors import get_switchable_as_dense, allocate_series_dataframes +from .descriptors import get_switchable_as_dense, allocate_series_dataframes, zsum + +pd.Series.zsum = zsum @@ -1042,23 +1044,23 @@ def define_linear_objective(network,snapshots): objective.variables.extend([(extendable_generators.at[gen,"capital_cost"], model.generator_p_nom[gen]) for gen in extendable_generators.index]) - objective.constant -= (extendable_generators.capital_cost * extendable_generators.p_nom).sum() + objective.constant -= (extendable_generators.capital_cost * extendable_generators.p_nom).zsum() objective.variables.extend([(ext_sus.at[su,"capital_cost"], model.storage_p_nom[su]) for su in ext_sus.index]) - objective.constant -= (ext_sus.capital_cost*ext_sus.p_nom).sum() + objective.constant -= (ext_sus.capital_cost*ext_sus.p_nom).zsum() objective.variables.extend([(ext_stores.at[store,"capital_cost"], model.store_e_nom[store]) for store in ext_stores.index]) - objective.constant -= (ext_stores.capital_cost*ext_stores.e_nom).sum() + objective.constant -= (ext_stores.capital_cost*ext_stores.e_nom).zsum() objective.variables.extend([(extendable_passive_branches.at[b,"capital_cost"], model.passive_branch_s_nom[b]) for b in extendable_passive_branches.index]) - objective.constant -= (extendable_passive_branches.capital_cost * extendable_passive_branches.s_nom).sum() + objective.constant -= (extendable_passive_branches.capital_cost * extendable_passive_branches.s_nom).zsum() objective.variables.extend([(extendable_links.at[b,"capital_cost"], model.link_p_nom[b]) for b in extendable_links.index]) - objective.constant -= (extendable_links.capital_cost * extendable_links.p_nom).sum() + objective.constant -= (extendable_links.capital_cost * extendable_links.p_nom).zsum() ## Unit commitment costs diff --git a/pypsa/pf.py b/pypsa/pf.py index 94b2410a0..f3b4a23e8 100644 --- a/pypsa/pf.py +++ b/pypsa/pf.py @@ -43,7 +43,9 @@ from itertools import chain import time -from .descriptors import get_switchable_as_dense, allocate_series_dataframes, Dict +from .descriptors import get_switchable_as_dense, allocate_series_dataframes, Dict, zsum + +pd.Series.zsum = zsum def _as_snapshots(network, snapshots): if snapshots is None: @@ -413,7 +415,7 @@ def apply_line_types(network): lines_with_types_b = network.lines.type != "" - if lines_with_types_b.sum() == 0: + if lines_with_types_b.zsum() == 0: return for attr in ["r","x"]: @@ -446,7 +448,7 @@ def apply_transformer_types(network): trafos = network.transformers trafos_with_types_b = trafos.type != "" - if trafos_with_types_b.sum() == 0: + if trafos_with_types_b.zsum() == 0: return trafos.loc[trafos_with_types_b, "r"] = trafos.loc[trafos_with_types_b, "type"].map(network.transformer_types["vscr"])/100. @@ -505,7 +507,7 @@ def apply_transformer_t_model(network): ts_b = (network.transformers.model == "t") & (y_shunt != 0.) - if ts_b.sum() == 0: + if ts_b.zsum() == 0: return za,zb,zc = wye_to_delta(z_series.loc[ts_b]/2,z_series.loc[ts_b]/2,1/y_shunt.loc[ts_b]) From 87ccb2ba780f3030de4fc3053f760c939e931b8c Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Wed, 29 Nov 2017 16:51:06 +0100 Subject: [PATCH 010/135] opf: Use reindex instead of reindex_axis pandas 0.21.0 deprecated reindex_axis. --- pypsa/opf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index 6ac9d2465..641e2da4b 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -1134,7 +1134,7 @@ def set_from_series(df, series): .groupby(c.df.bus, axis=1).sum() for c in network.iterate_components(controllable_one_port_components)}) \ .sum(level=1) \ - .reindex_axis(network.buses_t.p.columns, axis=1, fill_value=0.) + .reindex(columns=network.buses_t.p.columns, fill_value=0.) # passive branches From f288ff118c7be1f6453a64520276dced1cfb7d61 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Mon, 23 Oct 2017 00:13:41 +0200 Subject: [PATCH 011/135] pf: find_bus_controls works without any PV buses --- pypsa/pf.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pypsa/pf.py b/pypsa/pf.py index f3b4a23e8..f9a75c7d2 100644 --- a/pypsa/pf.py +++ b/pypsa/pf.py @@ -594,9 +594,11 @@ def find_bus_controls(sub_network): network.buses.loc[buses_i, "control"] = "PQ" #find all buses with one or more gens with PV - pvs = gens[gens.control == 'PV'].index.to_series().groupby(gens.bus).first() - network.buses.loc[pvs.index, "control"] = "PV" - network.buses.loc[pvs.index, "generator"] = pvs + pvs = gens[gens.control == 'PV'].index.to_series() + if len(pvs) > 0: + pvs = pvs.groupby(gens.bus).first() + network.buses.loc[pvs.index, "control"] = "PV" + network.buses.loc[pvs.index, "generator"] = pvs network.buses.loc[sub_network.slack_bus, "control"] = "Slack" network.buses.loc[sub_network.slack_bus, "generator"] = sub_network.slack_generator From a4862d913e567de124af8746a3c3aea8c8c0cffd Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Fri, 20 Oct 2017 14:21:11 +0200 Subject: [PATCH 012/135] plot: Non-boolean argument to basemap is used as resolution --- pypsa/plot.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pypsa/plot.py b/pypsa/plot.py index 0ed073959..6286aa62e 100644 --- a/pypsa/plot.py +++ b/pypsa/plot.py @@ -135,11 +135,13 @@ def compute_bbox_with_margins(margin, x, y): y = y + np.random.uniform(low=-jitter, high=jitter, size=len(y)) if basemap and basemap_present: + resolution = 'l' if isinstance(basemap, bool) else basemap + if boundaries is None: (x1, y1), (x2, y2) = compute_bbox_with_margins(margin, x, y) else: x1, x2, y1, y2 = boundaries - bmap = Basemap(resolution='l', epsg=network.srid, + bmap = Basemap(resolution=resolution, epsg=network.srid, llcrnrlat=y1, urcrnrlat=y2, llcrnrlon=x1, urcrnrlon=x2, ax=ax) bmap.drawcountries() From dfefbf1059f41169966d82612a14ca366ffcbe3b Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Mon, 4 Sep 2017 17:00:50 +0200 Subject: [PATCH 013/135] opf: Improve readability of ramp constraints --- pypsa/opf.py | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index 641e2da4b..c1a06b0b5 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -271,44 +271,62 @@ def gen_p_nom_bounds(model, gen_name): ## Deal with ramp limits without unit commitment ## - sns = snapshots[1:] - - ru_gens = network.generators.index[~network.generators.ramp_limit_up.isnull()] + ru_gens = network.generators.index[network.generators.ramp_limit_up.notnull()] ru = {} for gen in ru_gens: - for i,sn in enumerate(sns): + for sn, sn_prev in zip(snapshots[1:], snapshots[:-1]): if network.generators.at[gen, "p_nom_extendable"]: - lhs = LExpression([(1, network.model.generator_p[gen,sn]), (-1, network.model.generator_p[gen,snapshots[i]]), (-network.generators.at[gen, "ramp_limit_up"], network.model.generator_p_nom[gen])]) + lhs = LExpression([(1, network.model.generator_p[gen,sn]), + (-1, network.model.generator_p[gen,sn_prev]), + (-network.generators.at[gen, "ramp_limit_up"], + network.model.generator_p_nom[gen])]) elif not network.generators.at[gen, "committable"]: - lhs = LExpression([(1, network.model.generator_p[gen,sn]), (-1, network.model.generator_p[gen,snapshots[i]])], -network.generators.at[gen, "ramp_limit_up"]*network.generators.at[gen, "p_nom"]) + lhs = LExpression([(1, network.model.generator_p[gen,sn]), + (-1, network.model.generator_p[gen,sn_prev])], + -network.generators.at[gen, "ramp_limit_up"]*network.generators.at[gen, "p_nom"]) else: - lhs = LExpression([(1, network.model.generator_p[gen,sn]), (-1, network.model.generator_p[gen,snapshots[i]]), ((network.generators.at[gen, "ramp_limit_start_up"] - network.generators.at[gen, "ramp_limit_up"])*network.generators.at[gen, "p_nom"], network.model.generator_status[gen,snapshots[i]]), (-network.generators.at[gen, "ramp_limit_start_up"]*network.generators.at[gen, "p_nom"], network.model.generator_status[gen,sn])]) + lhs = LExpression([(1, network.model.generator_p[gen,sn]), + (-1, network.model.generator_p[gen,sn_prev]), + ((network.generators.at[gen, "ramp_limit_start_up"] - network.generators.at[gen, "ramp_limit_up"])*network.generators.at[gen, "p_nom"], + network.model.generator_status[gen,sn_prev]), + (-network.generators.at[gen, "ramp_limit_start_up"]*network.generators.at[gen, "p_nom"], + network.model.generator_status[gen,sn])]) ru[gen,sn] = LConstraint(lhs,"<=") - l_constraint(network.model, "ramp_up", ru, list(ru_gens), sns) + l_constraint(network.model, "ramp_up", ru, list(ru_gens), snapshots[1:]) - rd_gens = network.generators.index[~network.generators.ramp_limit_down.isnull()] + rd_gens = network.generators.index[network.generators.ramp_limit_down.notnull()] rd = {} for gen in rd_gens: - for i,sn in enumerate(sns): + for sn, sn_prev in zip(snapshots[1:], snapshots[:-1]): if network.generators.at[gen, "p_nom_extendable"]: - lhs = LExpression([(1, network.model.generator_p[gen,sn]), (-1, network.model.generator_p[gen,snapshots[i]]), (network.generators.at[gen, "ramp_limit_down"], network.model.generator_p_nom[gen])]) + lhs = LExpression([(1, network.model.generator_p[gen,sn]), + (-1, network.model.generator_p[gen,sn_prev]), + (network.generators.at[gen, "ramp_limit_down"], + network.model.generator_p_nom[gen])]) elif not network.generators.at[gen, "committable"]: - lhs = LExpression([(1, network.model.generator_p[gen,sn]), (-1, network.model.generator_p[gen,snapshots[i]])], network.generators.loc[gen, "ramp_limit_down"]*network.generators.at[gen, "p_nom"]) + lhs = LExpression([(1, network.model.generator_p[gen,sn]), + (-1, network.model.generator_p[gen,sn_prev])], + network.generators.loc[gen, "ramp_limit_down"]*network.generators.at[gen, "p_nom"]) else: - lhs = LExpression([(1, network.model.generator_p[gen,sn]), (-1, network.model.generator_p[gen,snapshots[i]]), ((network.generators.at[gen, "ramp_limit_down"] - network.generators.at[gen, "ramp_limit_shut_down"])*network.generators.at[gen, "p_nom"], network.model.generator_status[gen,sn]), (network.generators.at[gen, "ramp_limit_shut_down"]*network.generators.at[gen, "p_nom"], network.model.generator_status[gen,snapshots[i]])]) + lhs = LExpression([(1, network.model.generator_p[gen,sn]), + (-1, network.model.generator_p[gen,sn_prev]), + ((network.generators.at[gen, "ramp_limit_down"] - network.generators.at[gen, "ramp_limit_shut_down"])*network.generators.at[gen, "p_nom"], + network.model.generator_status[gen,sn]), + (network.generators.at[gen, "ramp_limit_shut_down"]*network.generators.at[gen, "p_nom"], + network.model.generator_status[gen,sn_prev])]) rd[gen,sn] = LConstraint(lhs,">=") - l_constraint(network.model, "ramp_down", rd, list(rd_gens), sns) + l_constraint(network.model, "ramp_down", rd, list(rd_gens), snapshots[1:]) From b2d408fd45db8b440065a877b2cb4e10e4343d91 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Wed, 29 Nov 2017 17:32:11 +0100 Subject: [PATCH 014/135] opf: Only show results when logging is at least on info level (fixes #25) --- pypsa/opf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index c1a06b0b5..e34fe9aa1 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -1401,7 +1401,7 @@ def network_lopf_solve(network, snapshots=None, formulation="angles", solver_opt else: network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, options=solver_options) - if logger.level > 0: + if logger.isEnabledFor(logging.INFO): network.results.write() status = network.results["Solver"][0]["Status"].key From 3e4e84607dc118622d959fb178d83c86f99825e8 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Wed, 29 Nov 2017 19:18:35 +0100 Subject: [PATCH 015/135] pf: Fix pf slack bus calc with one bus sub_networks Pandas 0.21.0 changed the sum of the empty series to nan. --- pypsa/pf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pypsa/pf.py b/pypsa/pf.py index f9a75c7d2..9a87db76f 100644 --- a/pypsa/pf.py +++ b/pypsa/pf.py @@ -794,7 +794,7 @@ def aggregate_multi_graph(sub_network): attr_mean = ["capital_cost","length","terrain_factor"] for attr in attr_inv: - aggregated[attr] = 1/(1/lines[attr]).sum() + aggregated[attr] = 1./(1./lines[attr]).sum() for attr in attr_sum: aggregated[attr] = lines[attr].sum() @@ -985,7 +985,7 @@ def sub_network_lpf(sub_network, snapshots=None, skip_pre=False): network.buses_t.v_mag_pu.loc[snapshots, buses_o] = 1. # set slack bus power to pick up remained - slack_adjustment = (- network.buses_t.p.loc[snapshots, buses_o[1:]].sum(axis=1) + slack_adjustment = (- network.buses_t.p.loc[snapshots, buses_o[1:]].sum(axis=1).fillna(0.) - network.buses_t.p.loc[snapshots, buses_o[0]]) network.buses_t.p.loc[snapshots, buses_o[0]] += slack_adjustment From 85602772e6ea02f5ed68804ddaa6bf0d4f95467d Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Thu, 30 Nov 2017 21:09:18 +0100 Subject: [PATCH 016/135] PyPSA Version 0.12.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hyperlinked release notes can be found here: https://pypsa.org/doc/release_notes.html#pypsa-0-12-0-30th-november-2017 This release contains new features and bug fixes. * Support for Pyomo’s persistent solver interface, so if you’re making small changes to an optimisation model (e.g. tweaking a parameter), you don’t have to rebuild the model every time. To enable this, network_lopf has been internally split into build_model, prepare_solver and solve to allow more fine-grained control of the solving steps. Currently the new Pyomo PersistentSolver interface is not in the main Pyomo branch, see the pull request; you can obtain it with pip install git+https://github.com/Pyomo/pyomo@persistent_interfaces * Lines and transformers (i.e. passive branches) have a new attribute s_max_pu to restrict the flow in the OPF, just like p_max_pu for generators and links. It works by restricting the absolute value of the flow per unit of the nominal rating abs(flow) <= s_max_pu*s_nom. For lines this can represent an n-1 contingency factor or it can be time-varying to represent weather-dependent dynamic line rating. * The marginal_cost attribute of generators, storage units, stores and links can now be time dependent. * When initialising the Network object, i.e. network = pypsa.Network(), the first keyword argument is now import_name instead of csv_folder_name. With import_name PyPSA recognises whether it is a CSV folder or an HDF5 file based on the file name ending and deals with it appropriately. Example usage: nw1 = pypsa.Network("my_store.h5") and nw2 = pypsa.Network("/my/folder"). The keyword argument csv_folder_name is still there but is deprecated. * The value network.objective is now read from the Pyomo results attribute Upper Bound instead of Lower Bound. This is because for MILP problems under certain circumstances CPLEX records the Lower bound as the relaxed value. Upper bound is correctly recorded as the integer objective value. * Bug fix due to changes in pandas 0.21.0: A bug affecting various places in the code, including causing network.lopf to fail with GLPK, is fixed. This is because in pandas 0.21.0 the sum of an empty Series/DataFrame returns NaN, whereas before it returned zero. This is a subtle bug; we hope we’ve fixed all instances of it, but get in touch if you notice NaNs creeping in where they shouldn’t be. All our tests run fine. * Bug fix due to changes in scipy 2.0.0: For the new version of scipy, csgraph has to be imported explicit. * Bug fix: A bug whereby logging level was not always correctly being seen by the OPF results printout is fixed. * Bug fix: The storage unit spillage had a bug in the LOPF, whereby it was not respecting network.snapshot_weightings properly. We thank René Garcia Rosas, João Gorenstein Dedecca, Marko Kolenc, Matteo De Felice and Florian Kühnlenz for promptly notifying us about issues. --- doc/conf.py | 4 ++-- doc/release_notes.rst | 55 +++++++++++++++++++++++++++++++++++++++++++ pypsa/__init__.py | 2 +- setup.py | 2 +- 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 587aac62c..46619e83e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -63,9 +63,9 @@ # built documents. # # The short X.Y version. -version = u'0.11' +version = u'0.12' # The full version, including alpha/beta/rc tags. -release = u'0.11.0' +release = u'0.12.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/release_notes.rst b/doc/release_notes.rst index b657b616a..7ccad0842 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -3,6 +3,61 @@ Release Notes ####################### +PyPSA 0.12.0 (30th November 2017) +================================= + +This release contains new features and bug fixes. + +* Support for Pyomo's persistent solver interface, so if you're making + small changes to an optimisation model (e.g. tweaking a parameter), + you don't have to rebuild the model every time. To enable this, + ``network_lopf`` has been internally split into ``build_model``, + ``prepare_solver`` and ``solve`` to allow more fine-grained control of the + solving steps. Currently the new Pyomo PersistentSolver interface + is not in the main Pyomo branch, see + the `pull request `_; you can obtain it with + ``pip install git+https://github.com/Pyomo/pyomo@persistent_interfaces`` +* Lines and transformers (i.e. passive branches) have a new attribute + ``s_max_pu`` to restrict the flow in the OPF, just like ``p_max_pu`` + for generators and links. It works by restricting the absolute value + of the flow per unit of the nominal rating ``abs(flow) <= + s_max_pu*s_nom``. For lines this can represent an n-1 contingency + factor or it can be time-varying to represent weather-dependent + dynamic line rating. +* The ``marginal_cost`` attribute of generators, storage units, stores + and links can now be time dependent. +* When initialising the Network object, i.e. ``network = + pypsa.Network()``, the first keyword argument is now ``import_name`` + instead of ``csv_folder_name``. With ``import_name`` PyPSA + recognises whether it is a CSV folder or an HDF5 file based on the + file name ending and deals with it appropriately. Example usage: + ``nw1 = pypsa.Network("my_store.h5")`` and ``nw2 = + pypsa.Network("/my/folder")``. The keyword argument + ``csv_folder_name`` is still there but is deprecated. +* The value ``network.objective`` is now read from the Pyomo results + attribute ``Upper Bound`` instead of ``Lower Bound``. This is + because for MILP problems under certain circumstances CPLEX records + the ``Lower bound`` as the relaxed value. ``Upper bound`` is correctly + recorded as the integer objective value. +* Bug fix due to changes in pandas 0.21.0: A bug affecting various + places in the code, including causing ``network.lopf`` to fail with + GLPK, is fixed. This is because in pandas 0.21.0 the sum of an empty + Series/DataFrame returns NaN, whereas before it returned zero. This + is a subtle bug; we hope we've fixed all instances of it, but get in + touch if you notice NaNs creeping in where they shouldn't be. All + our tests run fine. +* Bug fix due to changes in scipy 2.0.0: For the new version of scipy, + ``csgraph`` has to be imported explicit. +* Bug fix: A bug whereby logging level was not always correctly being + seen by the OPF results printout is fixed. +* Bug fix: The storage unit spillage had a bug in the LOPF, whereby it + was not respecting ``network.snapshot_weightings`` properly. + +We thank René Garcia Rosas, João Gorenstein Dedecca, Marko Kolenc, +Matteo De Felice and Florian Kühnlenz for promptly notifying us about +issues. + + PyPSA 0.11.0 (21st October 2017) ================================ diff --git a/pypsa/__init__.py b/pypsa/__init__.py index 3c707fde7..b04dcfea1 100644 --- a/pypsa/__init__.py +++ b/pypsa/__init__.py @@ -33,6 +33,6 @@ import logging logging.basicConfig(level=logging.INFO) -__version__ = "0.11.0" +__version__ = "0.12.0" __author__ = "Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)" __copyright__ = "Copyright 2015-2017 Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS), GNU GPL 3" diff --git a/setup.py b/setup.py index cc79ee6cc..1b2d306a4 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ setup( name='pypsa', - version='0.11.0', + version='0.12.0', author='Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)', author_email='brown@fias.uni-frankfurt.de', description='Python for Power Systems Analysis', From 715c7b049446868d4b6ce6e30f12d0ccda22ed96 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Thu, 30 Nov 2017 21:30:00 +0100 Subject: [PATCH 017/135] doc: Can also install from github with pip Also fix release notes bug from scipy 2.0.0 to 1.0.0. --- doc/installation.rst | 4 ++++ doc/release_notes.rst | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/installation.rst b/doc/installation.rst index 886277cfb..e6ee35a47 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -49,6 +49,10 @@ If you have the Python package installer ``pip`` then just run:: pip install pypsa +If you're feeling adventurous, you can also install the latest master branch from github with:: + + pip install git+https://github.com/PyPSA/PyPSA.git + "Manual" installation with setuptools ===================================== diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 7ccad0842..c4a76cdc5 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -46,7 +46,7 @@ This release contains new features and bug fixes. is a subtle bug; we hope we've fixed all instances of it, but get in touch if you notice NaNs creeping in where they shouldn't be. All our tests run fine. -* Bug fix due to changes in scipy 2.0.0: For the new version of scipy, +* Bug fix due to changes in scipy 1.0.0: For the new version of scipy, ``csgraph`` has to be imported explicit. * Bug fix: A bug whereby logging level was not always correctly being seen by the OPF results printout is fixed. From cab8a1ae210d5bfb5d704898a7c0b226d389f758 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Fri, 1 Dec 2017 11:50:15 +0100 Subject: [PATCH 018/135] test: Remove testing of pyomo-hack The support for freeing the pyomo datastructures during pyomos solving phase was removed from pypsa in commit #5ac1325, since it proved to be too slow for productive use. --- test/test_ac_dc_lopf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_ac_dc_lopf.py b/test/test_ac_dc_lopf.py index e63991d06..bfac499f3 100644 --- a/test/test_ac_dc_lopf.py +++ b/test/test_ac_dc_lopf.py @@ -39,7 +39,7 @@ def test_lopf(): snapshots = network.snapshots for formulation, free_memory in product(["angles", "cycles", "kirchhoff", "ptdf"], - [{}, {"pypsa"}, {"pypsa", "pyomo-hack"}]): + [{}, {"pypsa"}]): network.lopf(snapshots=snapshots,solver_name=solver_name,formulation=formulation, free_memory=free_memory) print(network.generators_t.p.loc[:,network.generators.index]) print(network_r.generators_t.p.loc[:,network.generators.index]) From a6a40cf54fab3c035d42bd4fe682e1c1a91ca3d1 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Fri, 1 Dec 2017 15:23:48 +0100 Subject: [PATCH 019/135] pf, descriptors: Add degree function for compatibility with NX2.0 --- pypsa/descriptors.py | 7 +++++++ pypsa/pf.py | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/pypsa/descriptors.py b/pypsa/descriptors.py index d6f603ae9..739ae44f5 100644 --- a/pypsa/descriptors.py +++ b/pypsa/descriptors.py @@ -89,6 +89,13 @@ def __init__(self, data=None, **attr): else: raise ImportError("NetworkX version {} is too old. At least 1.10 is needed.".format(nx.__version__)) +if _nx_version >= '2.0': + def degree(G): + return G.degree() +else: + def degree(G): + return G.degree_iter() + class Dict(dict): """ Dict is a subclass of dict, which allows you to get AND SET diff --git a/pypsa/pf.py b/pypsa/pf.py index 9a87db76f..6ff83da9c 100644 --- a/pypsa/pf.py +++ b/pypsa/pf.py @@ -43,7 +43,7 @@ from itertools import chain import time -from .descriptors import get_switchable_as_dense, allocate_series_dataframes, Dict, zsum +from .descriptors import get_switchable_as_dense, allocate_series_dataframes, Dict, zsum, degree pd.Series.zsum = zsum @@ -836,7 +836,7 @@ def find_tree(sub_network, weight='x_pu'): sub_network.tree = nx.minimum_spanning_tree(graph) #find bus with highest degree to use as slack - tree_slack_bus, slack_degree = max(sub_network.tree.degree_iter(), key=itemgetter(1)) + tree_slack_bus, slack_degree = max(degree(sub_network.tree), key=itemgetter(1)) logger.info("Tree slack bus is %s with degree %d.", tree_slack_bus, slack_degree) #determine which buses are supplied in tree through branch from slack From 7e8d759469e3f5486073d6eeed408f7810a7083a Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Fri, 1 Dec 2017 15:40:05 +0100 Subject: [PATCH 020/135] test: Enable running test from other working directories --- test/test_ac_dc_lopf.py | 7 +++---- test/test_ac_dc_lpf.py | 10 ++++------ test/test_opf_storage.py | 4 ++-- test/test_sclopf_scigrid.py | 11 ++++------- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/test/test_ac_dc_lopf.py b/test/test_ac_dc_lopf.py index bfac499f3..21fc9013b 100644 --- a/test/test_ac_dc_lopf.py +++ b/test/test_ac_dc_lopf.py @@ -22,14 +22,13 @@ def test_lopf(): + csv_folder_name = os.path.join(os.path.dirname(__file__), "../examples/ac-dc-meshed/ac-dc-data") - csv_folder_name = "../examples/ac-dc-meshed/ac-dc-data" - - network = pypsa.Network(csv_folder_name=csv_folder_name) + network = pypsa.Network(csv_folder_name) results_folder_name = os.path.join(csv_folder_name,"results-lopf") - network_r = pypsa.Network(csv_folder_name=results_folder_name) + network_r = pypsa.Network(results_folder_name) #test results were generated with GLPK; solution should be unique, diff --git a/test/test_ac_dc_lpf.py b/test/test_ac_dc_lpf.py index df7aaa62c..d3774876f 100644 --- a/test/test_ac_dc_lpf.py +++ b/test/test_ac_dc_lpf.py @@ -21,15 +21,13 @@ def test_lpf(): + csv_folder_name = os.path.join(os.path.dirname(__file__), "../examples/ac-dc-meshed/ac-dc-data") + network = pypsa.Network(csv_folder_name) - csv_folder_name = "../examples/ac-dc-meshed/ac-dc-data" + results_folder_name = os.path.join(csv_folder_name, "results-lpf") - network = pypsa.Network(csv_folder_name=csv_folder_name) - - results_folder_name = os.path.join(csv_folder_name,"results-lpf") - - network_r = pypsa.Network(csv_folder_name=results_folder_name) + network_r = pypsa.Network(results_folder_name) for snapshot in network.snapshots[:2]: network.lpf(snapshot) diff --git a/test/test_opf_storage.py b/test/test_opf_storage.py index 265bd2266..62a21df35 100644 --- a/test/test_opf_storage.py +++ b/test/test_opf_storage.py @@ -22,9 +22,9 @@ def test_opf(): - csv_folder_name = "../examples/opf-storage-hvdc/opf-storage-data" + csv_folder_name = os.path.join(os.path.dirname(__file__), "../examples/opf-storage-hvdc/opf-storage-data") - network = pypsa.Network(csv_folder_name=csv_folder_name) + network = pypsa.Network(csv_folder_name) #test results were generated with GLPK and other solvers may differ solver_name = "glpk" diff --git a/test/test_sclopf_scigrid.py b/test/test_sclopf_scigrid.py index e68b5fd97..ae700ed1b 100644 --- a/test/test_sclopf_scigrid.py +++ b/test/test_sclopf_scigrid.py @@ -1,17 +1,14 @@ from __future__ import print_function, division from __future__ import absolute_import -import pypsa - +import os import numpy as np - +import pypsa def test_sclopf(): + csv_folder_name = os.path.join(os.path.dirname(__file__), "../examples/scigrid-de/scigrid-with-load-gen-trafos/") - - csv_folder_name = "../examples/scigrid-de/scigrid-with-load-gen-trafos/" - - network = pypsa.Network(csv_folder_name=csv_folder_name) + network = pypsa.Network(csv_folder_name) #test results were generated with GLPK and other solvers may differ solver_name = "cbc" From ce119e251f279575e3632255644b1694b29222a7 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Fri, 1 Dec 2017 15:42:23 +0100 Subject: [PATCH 021/135] Prepare for travis-ci - Includes conda requirements files - Loosely based on calliope from https://github.com/calliope-project/calliope --- .travis.yml | 44 ++++++++++++++++++++++++++++++++++++++++++++ MANIFEST.in | 2 ++ Makefile | 15 +++++++++++++++ requirements.yml | 15 +++++++++++++++ requirements_dev.yml | 13 +++++++++++++ 5 files changed, 89 insertions(+) create mode 100644 .travis.yml create mode 100644 Makefile create mode 100644 requirements.yml create mode 100644 requirements_dev.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..8968e0fa2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,44 @@ +# Modified from +# https://github.com/calliope-project/calliope/blob/master/.travis.yml + +language: python +sudo: false # Use container-based infrastructure + +matrix: + include: + - env: + - PYTHON_VERSION="2.7" + - env: + - PYTHON_VERSION="3.6" + +before_install: + - if [[ "$PYTHON_VERSION" == "2.7" ]]; then + wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O miniconda.sh; + else + wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; + fi + - bash miniconda.sh -b -p $HOME/miniconda + - export PATH="$HOME/miniconda/bin:$PATH" + - hash -r + - conda config --set always_yes yes --set changeps1 no + - conda update -q conda + # Useful for debugging any issues with conda + - conda info -a + +install: + - conda create -n pypsa python=$PYTHON_VERSION + - conda env update -n pypsa --file=requirements.yml + - conda env update -n pypsa --file=requirements_dev.yml + - source activate pypsa + - conda install -c conda-forge python-coveralls # don't install on appveyor + - pip install --no-cache-dir . + +# before_script: # configure a headless display to test plot generation +# - "export DISPLAY=:99.0" +# - "sh -e /etc/init.d/xvfb start" +# - sleep 3 # give xvfb some time to start + +script: "make test" + +after_success: + - coveralls diff --git a/MANIFEST.in b/MANIFEST.in index 97a50c19a..1ee75eeeb 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,5 @@ include pypsa/component_attrs/*.csv include pypsa/standard_types/*.csv include pypsa/components.csv +include README.rst LICENSE.txt +include requirements.yml diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..10928d116 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +.PHONY : test sdist upload clean dist + +test : + pytest --cov pypsa --cov-report term-missing + +sdist : + python setup.py sdist + +upload : + twine upload dist/* + +clean : + rm dist/* + +dist : sdist upload clean diff --git a/requirements.yml b/requirements.yml new file mode 100644 index 000000000..b76a7b961 --- /dev/null +++ b/requirements.yml @@ -0,0 +1,15 @@ +name: pypsa +channels: + - conda-forge +dependencies: + - python + - six + - numpy + - pyomo + - scipy + - pandas>=0.19.0 + - matplotlib + - networkx>=1.10 + - pyomo + - coincbc + - glpk diff --git a/requirements_dev.yml b/requirements_dev.yml new file mode 100644 index 000000000..01fb867ae --- /dev/null +++ b/requirements_dev.yml @@ -0,0 +1,13 @@ +name: pypsa + +channels: + - conda-forge + - anaconda + +dependencies: + - pytest + - pytest-cov + - twine + - pip: + - pypower + - pandapower From 7e83c85b69ac221b020cc11fea3fee1a4d67d766 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Fri, 1 Dec 2017 16:27:09 +0100 Subject: [PATCH 022/135] README: Add badges --- README.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.rst b/README.rst index 2a480719d..b2a4e8148 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,7 @@ +|badge_travis| |badge_pypi| |badge_license| + +----- + ################################ Python for Power System Analysis ################################ @@ -214,3 +218,17 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the `GNU General Public License `_ for more details. + +.. |link-latest-doi| image:: https://zenodo.org/badge/DOI/10.5281/zenodo.786605.svg +.. _link-latest-doi: https://doi.org/10.5281/zenodo.786605 + +.. |badge_pypi| image:: https://img.shields.io/pypi/v/pypsa.svg + :target: https://pypi.python.org/pypi/pypsa + :alt: PyPI version + +.. |badge_license| image:: https://img.shields.io/pypi/l/pypsa.svg + :target: #license + +.. |badge_travis| image:: https://img.shields.io/travis/PyPSA/PyPSA/master.svg + :target: https://travis-ci.org/PyPSA/PyPSA + :alt: Build status on Linux From 747e0dbd13615d41599ea133adef6d3cccc66f12 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Wed, 6 Dec 2017 17:10:01 +0100 Subject: [PATCH 023/135] doc: Mention Travis CI under unit testing section Also fix typo in README.rst --- README.rst | 2 +- doc/unit_testing.rst | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index b2a4e8148..7f4eefa45 100644 --- a/README.rst +++ b/README.rst @@ -27,7 +27,7 @@ well with large networks and long time series. As of 2017 PyPSA is under heavy development and therefore it is recommended to use caution when using it in a production environment. Some APIs may change - the changes in each PyPSA version are listed in -the `doc/release_notes.rst `_. +the `doc/release_notes.rst `_. diff --git a/doc/unit_testing.rst b/doc/unit_testing.rst index 0c3ec3e3a..b435e4fc5 100644 --- a/doc/unit_testing.rst +++ b/doc/unit_testing.rst @@ -7,4 +7,6 @@ Unit testing is performed with py.test. Tests can be found in pypsa/test/. -Power flow is tested against Pypower using its built-in cases. +Power flow is tested against Pypower (the Python implementation of MATPOWER) using its built-in cases. + +Unit testing of new GitHub commits is automated with `Travis CI `_. From 783dbb4725c4b04cb67ce857ebac8643067c07e1 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Sun, 17 Dec 2017 18:50:42 +0100 Subject: [PATCH 024/135] pf: Refactor line and transformer_types Raise an error, if any types are missing. --- pypsa/pf.py | 75 +++++++++++++++++++++++------------------------------ 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/pypsa/pf.py b/pypsa/pf.py index 6ff83da9c..bdf070070 100644 --- a/pypsa/pf.py +++ b/pypsa/pf.py @@ -414,28 +414,25 @@ def apply_line_types(network): """ lines_with_types_b = network.lines.type != "" - if lines_with_types_b.zsum() == 0: return - for attr in ["r","x"]: - attr_per_length = network.line_types[attr + "_per_length"] - network.lines.loc[lines_with_types_b,attr] = ( - network.lines.loc[lines_with_types_b, "type"].map(attr_per_length) - * network.lines.loc[lines_with_types_b, "length"] - / network.lines.loc[lines_with_types_b, "num_parallel"] - ) + missing_types = (pd.Index(network.lines.loc[lines_with_types_b, 'type'].unique()) + .difference(network.line_types.index)) + assert missing_types.empty, ("The type(s) {} do(es) not exist in network.line_types" + .format(", ".join(missing_types))) - factor = 2*np.pi*1e-9*network.lines.loc[lines_with_types_b, "type"].map(network.line_types.f_nom) - c_per_length = network.line_types["c_per_length"] - network.lines.loc[lines_with_types_b, "b"] = ( - factor - * network.lines.loc[lines_with_types_b, "type"].map(c_per_length) - * network.lines.loc[lines_with_types_b, "length"] - * network.lines.loc[lines_with_types_b, "num_parallel"] - ) + # Get a copy of the lines data + l = (network.lines.loc[lines_with_types_b, ["type", "length", "num_parallel"]] + .join(network.line_types, on='type')) + for attr in ["r","x"]: + l[attr] = l[attr + "_per_length"] * l["length"] / l["num_parallel"] + l["b"] = 2*np.pi*1e-9*l["f_nom"] * l["c_per_length"] * l["length"] * l["num_parallel"] + # now set calculated values on live lines + for attr in ["r", "x", "b"]: + network.lines.loc[lines_with_types_b, attr] = l[attr] @@ -445,50 +442,42 @@ def apply_transformer_types(network): """ - trafos = network.transformers - trafos_with_types_b = trafos.type != "" - + trafos_with_types_b = network.transformers.type != "" if trafos_with_types_b.zsum() == 0: return - trafos.loc[trafos_with_types_b, "r"] = trafos.loc[trafos_with_types_b, "type"].map(network.transformer_types["vscr"])/100. - - z = trafos.loc[trafos_with_types_b, "type"].map(network.transformer_types["vsc"])/100. + missing_types = (pd.Index(network.transformers.loc[trafos_with_types_b, 'type'].unique()) + .difference(network.transformer_types.index)) + assert missing_types.empty, ("The type(s) {} do(es) not exist in network.transformer_types" + .format(", ".join(missing_types))) - trafos.loc[trafos_with_types_b, "x"] = np.sqrt(z**2 - trafos.loc[trafos_with_types_b, "r"]**2) + # Get a copy of the transformers data + # (joining pulls in "phase_shift", "s_nom", "tap_side" from TransformerType) + t = (network.transformers.loc[trafos_with_types_b, ["type", "tap_position", "num_parallel"]] + .join(network.transformer_types, on='type')) - for attr in ["phase_shift","s_nom"]: - trafos.loc[trafos_with_types_b, attr] = trafos.loc[trafos_with_types_b, "type"].map(network.transformer_types[attr]) + t["r"] = t["vscr"] /100. + t["x"] = np.sqrt((t["vsc"]/100.)**2 - t["r"]**2) #NB: b and g are per unit of s_nom - trafos.loc[trafos_with_types_b, "g"] = trafos.loc[trafos_with_types_b, "type"].map(network.transformer_types["pfe"])/(1000. * trafos.loc[trafos_with_types_b, "s_nom"]) - - i0 = trafos.loc[trafos_with_types_b, "type"].map(network.transformer_types["i0"])/100. - - b_minus_squared = i0**2 - trafos.loc[trafos_with_types_b, "g"]**2 + t["g"] = t["pfe"]/(1000. * t["s_nom"]) #for some bizarre reason, some of the standard types in pandapower have i0^2 < g^2 - - b_minus_squared[b_minus_squared < 0.] = 0. - - trafos.loc[trafos_with_types_b, "b"] = - np.sqrt(b_minus_squared) - + t["b"] = - np.sqrt(((t["i0"]/100.)**2 - t["g"]**2).clip(lower=0)) for attr in ["r","x"]: - trafos.loc[trafos_with_types_b, attr] = trafos.loc[trafos_with_types_b, attr]/trafos.loc[trafos_with_types_b, "num_parallel"] + t[attr] /= t["num_parallel"] for attr in ["b","g"]: - trafos.loc[trafos_with_types_b, attr] = trafos.loc[trafos_with_types_b, attr]*trafos.loc[trafos_with_types_b, "num_parallel"] - + t[attr] *= t["num_parallel"] #deal with tap positions - trafos.loc[trafos_with_types_b, "tap_ratio"] = 1 + ( - trafos.loc[trafos_with_types_b, "tap_position"] - - trafos.loc[trafos_with_types_b, "type"].map(network.transformer_types["tap_neutral"])) * ( - trafos.loc[trafos_with_types_b, "type"].map(network.transformer_types["tap_step"])/100.) + t["tap_ratio"] = 1. + (t["tap_position"] - t["tap_neutral"]) * (t["tap_step"]/100.) - trafos.loc[trafos_with_types_b, "tap_side"] = trafos.loc[trafos_with_types_b, "type"].map(network.transformer_types["tap_side"]) + # now set calculated values on live transformers + for attr in ["r", "x", "g", "b", "phase_shift", "s_nom", "tap_side", "tap_ratio"]: + network.transformers.loc[trafos_with_types_b, attr] = t[attr] #TODO: status, rate_A From df2b5e83fc20edf0d7ca7a1dc471aa926e2ebfde Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Sun, 17 Dec 2017 18:57:24 +0100 Subject: [PATCH 025/135] io: Refactor importers and exporters in preparation for netcdf support --- pypsa/io.py | 544 ++++++++++++++++++++++++++-------------------------- 1 file changed, 274 insertions(+), 270 deletions(-) diff --git a/pypsa/io.py b/pypsa/io.py index f8ae69955..de8d4c144 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -16,159 +16,320 @@ """Functions for importing and exporting data. """ - # make the code as Python 3 compatible as possible from __future__ import division, absolute_import -from six import iteritems +from six import iteritems, iterkeys, string_types from six.moves import filter, range - __author__ = "Tom Brown (FIAS), Jonas Hoersch (FIAS)" __copyright__ = "Copyright 2015-2017 Tom Brown (FIAS), Jonas Hoersch (FIAS), GNU GPL 3" import logging logger = logging.getLogger(__name__) -from textwrap import dedent import os +from textwrap import dedent +from glob import glob import pandas as pd import pypsa import numpy as np - - -def export_to_csv_folder(network, csv_folder_name, encoding=None, export_standard_types=False): +class ImpExper(object): + ds = None + + def __enter__(self): + if self.ds is not None: + self.ds = self.ds.__enter__() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.finish() + ds = self.ds + if ds is not None: + ds.__exit__(exc_type, exc_val, exc_tb) + + def finish(self): + pass + +class Exporter(ImpExper): + def remove_static(self, list_name): + pass + + def remove_series(self, list_name, attr): + pass + +class Importer(ImpExper): + pass + +class ImporterCSV(Importer): + def __init__(self, csv_folder_name, encoding): + self.csv_folder_name = csv_folder_name + self.encoding = encoding + + assert os.path.isdir(csv_folder_name), "Directory {} does not exist.".format(csv_folder_name) + + def get_attributes(self): + fn = os.path.join(self.csv_folder_name, "network.csv") + if not os.path.isfile(fn): return None + return dict(pd.read_csv(fn, encoding=self.encoding).iloc[0]) + + def get_snapshots(self): + fn = os.path.join(self.csv_folder_name, "snapshots.csv") + return pd.read_csv(fn, index_col=0, encoding=self.encoding, parse_dates=True) + + def get_static(self, list_name): + fn = os.path.join(self.csv_folder_name, list_name + ".csv") + return (pd.read_csv(fn, index_col=0, encoding=self.encoding) + if os.path.isfile(fn) else None) + + def get_series(self, list_name): + for fn in os.listdir(self.csv_folder_name): + if fn.startswith(list_name+"-") and fn.endswith(".csv"): + attr = fn[len(list_name)+1:-4] + df = pd.read_csv(os.path.join(self.csv_folder_name, fn), + index_col=0, encoding=self.encoding, parse_dates=True) + yield attr, df + +class ExporterCSV(Exporter): + def __init__(self, csv_folder_name, encoding): + self.csv_folder_name = csv_folder_name + self.encoding = encoding + + #make sure directory exists + if not os.path.isdir(csv_folder_name): + logger.warning("Directory {} does not exist, creating it" + .format(csv_folder_name)) + os.mkdir(csv_folder_name) + + def save_attributes(self, attrs): + name = attrs.pop('name') + df = pd.DataFrame(attrs, index=pd.Index([name], name='name')) + fn = os.path.join(self.csv_folder_name, "network.csv") + df.to_csv(fn, encoding=self.encoding) + + def save_snapshots(self, snapshots): + fn = os.path.join(self.csv_folder_name, "snapshots.csv") + snapshots.to_csv(fn, encoding=self.encoding) + + def save_static(self, list_name, df): + fn = os.path.join(self.csv_folder_name, list_name + ".csv") + df.to_csv(fn, encoding=self.encoding) + + def save_series(self, list_name, attr, df): + fn = os.path.join(self.csv_folder_name, list_name + "-" + attr + ".csv") + df.to_csv(fn, encoding=self.encoding) + + def remove_static(self, list_name): + fns = glob(os.path.join(self.csv_folder_name, list_name) + "*.csv") + if fns: + for fn in fns: os.unlink(fn) + logger.warning("Stale csv file(s) {} removed".format(', '.join(fns))) + + def remove_series(self, list_name, attr): + fn = os.path.join(self.csv_folder_name, list_name + "-" + attr + ".csv") + if os.path.exists(fn): + os.unlink(fn) + +class ImporterHDF5(Importer): + def __init__(self, path): + self.ds = pd.HDFStore(path) + + def get_attributes(self): + return dict(self.ds["/network"].reset_index().iloc[0]) + + def get_snapshots(self): + return self.ds["/snapshots"] if "/snapshots" in self.ds else None + + def get_static(self, list_name): + return (self.ds["/" + list_name] + if "/" + list_name in self.ds else None) + + def get_series(self, list_name): + for tab in self.ds: + if tab.startswith('/' + list_name + '_t/'): + attr = tab[len('/' + list_name + '_t/'):] + yield attr, self.ds[tab] + +class ExporterHDF5(Exporter): + def __init__(self, path, **kwargs): + self.ds = pd.HDFStore(path, mode='w', **kwargs) + + def save_attributes(self, attrs): + name = attrs.pop('name') + self.ds.put('/network', + pd.DataFrame(attrs, index=pd.Index([name], name='name')), + format='table', index=False) + + def save_snapshots(self, snapshots): + self.ds.put('/snapshots', snapshots, format='table', index=False) + + def save_static(self, list_name, df): + self.ds.put('/' + list_name, df, format='table', index=False) + + def save_series(self, list_name, attr, df): + self.ds.put('/' + list_name + '_t/' + attr, df, format='table', index=False) + +def _export_to_exporter(network, exporter, basename, export_standard_types=False): """ - Export network and components to a folder of CSVs. + Export to exporter. Both static and series attributes of components are exported, but only if they have non-default values. - If csv_folder_name does not already exist, it is created. - Parameters ---------- - csv_folder_name : string - Name of folder to which to export. - encoding : str, default None - Encoding to use for UTF when reading (ex. 'utf-8'). `List of Python - standard encodings - `_ + exporter : Exporter + Initialized exporter instance + basename : str + Basename, used for logging export_standard_types : boolean, default False If True, then standard types are exported too (upon reimporting you should then set "ignore_standard_types" when initialising the netowrk). - - Examples - -------- - >>> export_to_csv(network,csv_folder_name) - OR - >>> network.export_to_csv(csv_folder_name) """ - #exportable component types #what about None???? - nan is float? allowed_types = [float,int,str,bool] + list(np.typeDict.values()) - #make sure directory exists - if not os.path.isdir(csv_folder_name): - logger.warning("Directory {} does not exist, creating it".format(csv_folder_name)) - os.mkdir(csv_folder_name) - - #first export network properties - - columns = [attr for attr in dir(network) if type(getattr(network,attr)) in allowed_types and attr != "name" and attr[:2] != "__"] - index = [network.name] - df = pd.DataFrame(index=index,columns=columns,data = [[getattr(network,col) for col in columns]]) - df.index.name = "name" - - df.to_csv(os.path.join(csv_folder_name,"network.csv"),encoding=encoding) + attrs = dict((attr, getattr(network, attr)) + for attr in dir(network) + if (attr[:2] != "__" and + type(getattr(network,attr)) in allowed_types)) + exporter.save_attributes(attrs) #now export snapshots - - df = pd.DataFrame(index=network.snapshots) - df["weightings"] = network.snapshot_weightings - df.index.name = "name" - - df.to_csv(os.path.join(csv_folder_name,"snapshots.csv"),encoding=encoding) - - #now export all other components + snapshots = pd.DataFrame(dict(weightings=network.snapshot_weightings), + index=pd.Index(network.snapshots, name="name")) + exporter.save_snapshots(snapshots) exported_components = [] - for component in pypsa.components.all_components - {"SubNetwork"}: list_name = network.components[component]["list_name"] attrs = network.components[component]["attrs"] + df = network.df(component) pnl = network.pnl(component) - if not export_standard_types and component in pypsa.components.standard_types: df = df.drop(network.components[component]["standard_types"].index) - - #first do static attributes - filename = os.path.join(csv_folder_name,list_name+".csv") + # first do static attributes df.index.name = "name" if df.empty: - if os.path.exists(filename): - os.unlink(filename) - - fns = [os.path.basename(filename)] - for attr in attrs.index[attrs.varying]: - fn = os.path.join(csv_folder_name,list_name+'-'+attr+'.csv') - if os.path.exists(fn): - os.unlink(fn) - fns.append(os.path.basename(fn)) - - logger.warning("Stale csv file(s) {} removed".format(', '.join(fns))) - + exporter.remove_static(list_name) continue col_export = [] for col in df.columns: - #do not export derived attributes - if col in ["sub_network","r_pu","x_pu","g_pu","b_pu"]: + # do not export derived attributes + if col in ["sub_network", "r_pu", "x_pu", "g_pu", "b_pu"]: continue - if col in attrs.index and pd.isnull(attrs.at[col,"default"]) and pd.isnull(df[col]).all(): + if col in attrs.index and pd.isnull(attrs.at[col, "default"]) and pd.isnull(df[col]).all(): continue if (col in attrs.index and df[col].dtype == attrs.at[col, 'dtype'] - and (df[col] == attrs.at[col,"default"]).all()): + and (df[col] == attrs.at[col, "default"]).all()): continue col_export.append(col) - df[col_export].to_csv(filename,encoding=encoding) - + exporter.save_static(list_name, df[col_export]) #now do varying attributes for attr in pnl: if attr not in attrs.index: col_export = pnl[attr].columns else: - default = attrs.at[attr,"default"] + default = attrs.at[attr, "default"] if pd.isnull(default): col_export = pnl[attr].columns[(~pd.isnull(pnl[attr])).any()] else: col_export = pnl[attr].columns[(pnl[attr] != default).any()] - filename = os.path.join(csv_folder_name,list_name+"-" + attr + ".csv") if len(col_export) > 0: - pnl[attr].loc[:,col_export].to_csv(filename,encoding=encoding) + df = pnl[attr][col_export] + exporter.save_series(list_name, attr, df) else: - if os.path.exists(filename): - os.unlink(filename) - logger.warning("Stale csv file {} removed" - .format(os.path.basename(filename))) + exporter.remove_series(list_name, attr) exported_components.append(list_name) - logger.info("Exported network {} has {}".format(os.path.basename(csv_folder_name), ", ".join(exported_components))) + logger.info("Exported network {} has {}".format(basename, ", ".join(exported_components))) + +def import_from_csv_folder(network, csv_folder_name, encoding=None, skip_time=False): + """ + Import network data from CSVs in a folder. + + The CSVs must follow the standard form, see pypsa/examples. + + Parameters + ---------- + csv_folder_name : string + Name of folder + encoding : str, default None + Encoding to use for UTF when reading (ex. 'utf-8'). `List of Python + standard encodings + `_ + skip_time : bool, default False + Skip reading in time dependent attributes + """ + + basename = os.path.basename(csv_folder_name) + with ImporterCSV(csv_folder_name, encoding=encoding) as importer: + _import_from_importer(network, importer, basename=basename, skip_time=skip_time) + +def export_to_csv_folder(network, csv_folder_name, encoding=None, export_standard_types=False): + """ + Export network and components to a folder of CSVs. + + Both static and series attributes of components are exported, but only + if they have non-default values. + + If csv_folder_name does not already exist, it is created. + + Parameters + ---------- + csv_folder_name : string + Name of folder to which to export. + encoding : str, default None + Encoding to use for UTF when reading (ex. 'utf-8'). `List of Python + standard encodings + `_ + export_standard_types : boolean, default False + If True, then standard types are exported too (upon reimporting you + should then set "ignore_standard_types" when initialising the netowrk). + + Examples + -------- + >>> export_to_csv(network,csv_folder_name) + OR + >>> network.export_to_csv(csv_folder_name) + """ + + basename = os.path.basename(csv_folder_name) + with ExporterCSV(csv_folder_name=csv_folder_name, encoding=encoding) as exporter: + _export_to_exporter(network, exporter, basename=basename, + export_standard_types=export_standard_types) + +def import_from_hdf5(network, path, skip_time=False): + """ + Import network data from HDF5 store at `path`. + + Parameters + ---------- + path : string + Name of HDF5 store + skip_time : bool, default False + Skip reading in time dependent attributes + """ + + basename = os.path.basename(path) + with ImporterHDF5(path) as importer: + _import_from_importer(network, importer, basename=basename, skip_time=skip_time) def export_to_hdf5(network, path, export_standard_types=False, **kwargs): """ @@ -196,105 +357,41 @@ def export_to_hdf5(network, path, export_standard_types=False, **kwargs): kwargs.setdefault('complevel', 4) - with pd.HDFStore(path, mode='w', **kwargs) as store: - #first export network properties - - #exportable component types - #what about None???? - nan is float? - allowed_types = [float,int,str,bool] + list(np.typeDict.values()) - - columns = [attr for attr in dir(network) - if (attr != "name" and attr[:2] != "__" and - type(getattr(network,attr)) in allowed_types)] - index = pd.Index([network.name], name="name") - store.put('/network', - pd.DataFrame(index=index, columns=columns, - data=[[getattr(network, col) for col in columns]]), - format='table', index=False) + basename = os.path.basename(path) + with ExporterHDF5(path, **kwargs) as exporter: + _export_to_exporter(network, exporter, basename=basename, + export_standard_types=export_standard_types) - #now export snapshots - store.put('/snapshots', - pd.DataFrame(dict(weightings=network.snapshot_weightings), - index=pd.Index(network.snapshots, name="name")), - format='table', index=False) - #now export all other components - exported_components = [] - for component in pypsa.components.all_components - {"SubNetwork"}: - list_name = network.components[component]["list_name"] - attrs = network.components[component]["attrs"] - df = network.df(component) - pnl = network.pnl(component) - if not export_standard_types and component in pypsa.components.standard_types: - df = df.drop(network.components[component]["standard_types"].index) - #first do static attributes - df.index.name = "name" - if df.empty: - continue - col_export = [] - for col in df.columns: - #do not export derived attributes - if col in ["sub_network", "r_pu", "x_pu", "g_pu", "b_pu"]: - continue - if col in attrs.index and pd.isnull(attrs.at[col, "default"]) and pd.isnull(df[col]).all(): - continue - if (col in attrs.index - and df[col].dtype == attrs.at[col, 'dtype'] - and (df[col] == attrs.at[col, "default"]).all()): - continue - - col_export.append(col) - - store.put('/' + list_name, df[col_export], format='table', index=False) - - #now do varying attributes - for attr in pnl: - if attr not in attrs.index: - col_export = pnl[attr].columns - else: - default = attrs.at[attr, "default"] - if pd.isnull(default): - col_export = pnl[attr].columns[(~pd.isnull(pnl[attr])).any()] - else: - col_export = pnl[attr].columns[(pnl[attr] != default).any()] - df = pnl[attr][col_export] - if not df.empty: - store.put('/' + list_name + '_t/' + attr, df, format='table', index=False) - exported_components.append(list_name) - logger.info("Exported network {} has {}".format(os.path.basename(path), ", ".join(exported_components))) - -def import_from_hdf5(network, path, skip_time=False): +def _import_from_importer(network, importer, basename, skip_time=False): """ - Import network data from HDF5 store at `path`. + Import network data from importer. Parameters ---------- - path : string - Name of HDF5 store + skip_time : bool + Skip importing time """ - with pd.HDFStore(path, mode='r') as store: - df = store['/network'] - logger.debug("/network") - logger.debug(df) - network.name = df.index[0] + attrs = importer.get_attributes() + if attrs is not None: + network.name = attrs.pop('name') ##https://docs.python.org/3/tutorial/datastructures.html#comparing-sequences-and-other-types current_pypsa_version = [int(s) for s in network.pypsa_version.split(".")] try: - pypsa_version = [int(s) for s in df.at[network.name, 'pypsa_version'].split(".")] - df = df.drop('pypsa_version', axis=1) + pypsa_version = [int(s) for s in attrs.pop("pypsa_version").split(".")] except KeyError: pypsa_version = None @@ -305,45 +402,41 @@ def import_from_hdf5(network, path, skip_time=False): carefully to prepare your network for import. """).format(network.pypsa_version)) - for col in df.columns: - setattr(network, col, df[col][network.name]) + for attr, val in iteritems(attrs): + setattr(network, attr, val) - #if there is snapshots.csv, read in snapshot data - - if '/snapshots' in store: - df = store['/snapshots'] - - network.set_snapshots(df.index) - if "weightings" in df.columns: - network.snapshot_weightings = df["weightings"].reindex(network.snapshots) + # if there is snapshots.csv, read in snapshot data + df = importer.get_snapshots() + if df is not None: + network.set_snapshots(df.index) + if "weightings" in df.columns: + network.snapshot_weightings = df["weightings"].reindex(network.snapshots) - imported_components = [] + imported_components = [] - #now read in other components; make sure buses and carriers come first - for component in ["Bus", "Carrier"] + sorted(pypsa.components.all_components - {"Bus", "Carrier", "SubNetwork"}): - list_name = network.components[component]["list_name"] + # now read in other components; make sure buses and carriers come first + for component in ["Bus", "Carrier"] + sorted(pypsa.components.all_components - {"Bus", "Carrier", "SubNetwork"}): + list_name = network.components[component]["list_name"] - if '/' + list_name not in store: - if component == "Bus": - logger.error("Error, no buses found") - return - else: - continue + df = importer.get_static(list_name) + if df is None: + if component == "Bus": + logger.error("Error, no buses found") + return + else: + continue - df = store['/' + list_name] - import_components_from_dataframe(network, df, component) + import_components_from_dataframe(network, df, component) - if not skip_time: - for attr in store: - if attr.startswith('/' + list_name + '_t/'): - attr_name = attr[len('/' + list_name + '_t/'):] - import_series_from_dataframe(network, store[attr], component, attr_name) + if not skip_time: + for attr, df in importer.get_series(list_name): + import_series_from_dataframe(network, df, component, attr) - logger.debug(getattr(network,list_name)) + logger.debug(getattr(network,list_name)) - imported_components.append(list_name) + imported_components.append(list_name) - logger.info("Imported network {} has {}".format(os.path.basename(path), ", ".join(imported_components))) + logger.info("Imported network{} has {}".format(" " + basename, ", ".join(imported_components))) def import_components_from_dataframe(network, dataframe, cls_name): """ @@ -467,95 +560,6 @@ def import_series_from_dataframe(network, dataframe, cls_name, attr): -def import_from_csv_folder(network, csv_folder_name, encoding=None, skip_time=False): - """ - Import network data from CSVs in a folder. - - The CSVs must follow the standard form, see pypsa/examples. - - Parameters - ---------- - csv_folder_name : string - Name of folder - encoding : str, default None - Encoding to use for UTF when reading (ex. 'utf-8'). `List of Python - standard encodings - `_ - skip_time : bool, default False - Skip reading in time dependent attributes - """ - - if not os.path.isdir(csv_folder_name): - logger.error("Directory {} does not exist.".format(csv_folder_name)) - return - - #if there is network.csv, read in network data - - file_name = os.path.join(csv_folder_name,"network.csv") - - if os.path.isfile(file_name): - df = pd.read_csv(file_name,index_col=0,encoding=encoding) - logger.debug("networks.csv:") - logger.debug(df) - network.name = df.index[0] - - ##https://docs.python.org/3/tutorial/datastructures.html#comparing-sequences-and-other-types - current_pypsa_version = [int(s) for s in network.pypsa_version.split(".")] - pypsa_version = None - for col in df.columns: - if col == "pypsa_version": - pypsa_version = [int(s) for s in df.at[network.name,"pypsa_version"].split(".")] - else: - setattr(network,col,df[col][network.name]) - - if pypsa_version is None or pypsa_version < current_pypsa_version: - logger.warning("Importing PyPSA from older version of PyPSA than current version {}.\n\ - Please read the release notes at https://pypsa.org/doc/release_notes.html\n\ - carefully to prepare your network for import.".format(network.pypsa_version)) - - #if there is snapshots.csv, read in snapshot data - - file_name = os.path.join(csv_folder_name,"snapshots.csv") - - if os.path.isfile(file_name): - df = pd.read_csv(file_name, index_col=0, encoding=encoding, parse_dates=True) - network.set_snapshots(df.index) - if "weightings" in df.columns: - network.snapshot_weightings = df["weightings"].reindex(network.snapshots) - - imported_components = [] - - #now read in other components; make sure buses and carriers come first - for component in ["Bus", "Carrier"] + sorted(pypsa.components.all_components - {"Bus","Carrier","SubNetwork"}): - - list_name = network.components[component]["list_name"] - - file_name = os.path.join(csv_folder_name,list_name+".csv") - - if not os.path.isfile(file_name): - if component == "Bus": - logger.error("Error, no buses found") - return - else: - continue - - df = pd.read_csv(file_name,index_col=0,encoding=encoding) - - import_components_from_dataframe(network,df,component) - - if not skip_time: - file_attrs = [n for n in os.listdir(csv_folder_name) if n.startswith(list_name+"-") and n.endswith(".csv")] - - for file_name in file_attrs: - df = pd.read_csv(os.path.join(csv_folder_name,file_name), index_col=0, encoding=encoding, parse_dates=True) - import_series_from_dataframe(network,df,component,file_name[len(list_name)+1:-4]) - - logger.debug(getattr(network,list_name)) - - imported_components.append(list_name) - - logger.info("Imported network {} has {}".format(os.path.basename(csv_folder_name), ", ".join(imported_components))) - def import_from_pypower_ppc(network, ppc, overwrite_zero_s_nom=None): """ Import network from PYPOWER PPC dictionary format version 2. From ed3208f85cbc0e2c5c0c15bb6508a8927dfff46e Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Mon, 18 Dec 2017 10:18:15 +0100 Subject: [PATCH 026/135] travis: Add --quiet to conda calls Worksaround the terminal problems conda is having with travis. See also: https://github.com/conda/conda/issues/6468 --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8968e0fa2..7f6deed33 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,11 +26,11 @@ before_install: - conda info -a install: - - conda create -n pypsa python=$PYTHON_VERSION - - conda env update -n pypsa --file=requirements.yml - - conda env update -n pypsa --file=requirements_dev.yml + - conda create -q -n pypsa python=$PYTHON_VERSION + - conda env update -q -n pypsa --file=requirements.yml + - conda env update -q -n pypsa --file=requirements_dev.yml - source activate pypsa - - conda install -c conda-forge python-coveralls # don't install on appveyor + - conda install -q -c conda-forge python-coveralls # don't install on appveyor - pip install --no-cache-dir . # before_script: # configure a headless display to test plot generation From b7f5b999e244473c2c51cf728eba68a271ddc3c1 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Mon, 18 Dec 2017 13:50:54 +0100 Subject: [PATCH 027/135] setup: Add dependency on PYOMO version 5.3 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1b2d306a4..9723fb62e 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ license='GPLv3', packages=find_packages(exclude=['doc', 'test']), include_package_data=True, - install_requires=['numpy','pyomo','scipy','pandas>=0.19.0','networkx>=1.10'], + install_requires=['numpy','pyomo>=5.3','scipy','pandas>=0.19.0','networkx>=1.10'], classifiers=[ 'Development Status :: 3 - Alpha', 'Environment :: Console', From 9bcfbc019a2cedc502c8fe1fd9fcbb956a83caa2 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Mon, 18 Dec 2017 14:36:28 +0100 Subject: [PATCH 028/135] io: Add netcdf importer and exporter --- pypsa/components.py | 23 ++++++--- pypsa/io.py | 116 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 8 deletions(-) diff --git a/pypsa/components.py b/pypsa/components.py index 5d849b6ac..8511c69c4 100644 --- a/pypsa/components.py +++ b/pypsa/components.py @@ -52,6 +52,7 @@ from .io import (export_to_csv_folder, import_from_csv_folder, export_to_hdf5, import_from_hdf5, + export_to_netcdf, import_from_netcdf, import_from_pypower_ppc, import_components_from_dataframe, import_series_from_dataframe, import_from_pandapower_net) @@ -160,6 +161,10 @@ class Network(Basic): export_to_hdf5 = export_to_hdf5 + import_from_netcdf = import_from_netcdf + + export_to_netcdf = export_to_netcdf + import_from_pypower_ppc = import_from_pypower_ppc import_from_pandapower_net = import_from_pandapower_net @@ -192,11 +197,15 @@ class Network(Basic): adjacency_matrix = adjacency_matrix - def __init__(self, import_name=None, name="", ignore_standard_types=False, csv_folder_name=None,**kwargs): + def __init__(self, import_name=None, name="", ignore_standard_types=False, **kwargs): - from .__init__ import __version__ as pypsa_version + if 'csv_folder_name' in kwargs: + logger.warning("The argument csv_folder_name for initiating Network() is deprecated, please use import_name instead.") + import_name = kwargs.pop('csv_folder_name') - Basic.__init__(self,name) + from . import __version__ as pypsa_version + + Basic.__init__(self, name) #this will be saved on export self.pypsa_version = pypsa_version @@ -242,15 +251,13 @@ def __init__(self, import_name=None, name="", ignore_standard_types=False, csv_f if not ignore_standard_types: self.read_in_default_standard_types() - - if import_name is not None and csv_folder_name is None: + if import_name is not None: if import_name[-3:] == ".h5": self.import_from_hdf5(import_name) + elif import_name[-3:] == ".nc": + self.import_from_netcdf(import_name) else: self.import_from_csv_folder(import_name) - elif csv_folder_name is not None: - logger.warning("The argument csv_folder_name for initiating Network() is deprecated, please use import_name instead.") - self.import_from_csv_folder(csv_folder_name) for key, value in iteritems(kwargs): setattr(self, key, value) diff --git a/pypsa/io.py b/pypsa/io.py index de8d4c144..71fed775d 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -35,6 +35,12 @@ import pypsa import numpy as np +try: + import xarray as xr + has_xarray = True +except ImportError: + has_xarray = False + class ImpExper(object): ds = None @@ -170,6 +176,76 @@ def save_static(self, list_name, df): def save_series(self, list_name, attr, df): self.ds.put('/' + list_name + '_t/' + attr, df, format='table', index=False) +if has_xarray: + class ImporterNetCDF(Importer): + def __init__(self, path): + self.path = path + if isinstance(path, string_types): + self.ds = xr.open_dataset(path) + + def __enter__(self): + if isinstance(self.path, string_types): + self.ds = super(ImporterNetCDF, self).__init__() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if isinstance(self.path, string_types): + super(ImporterNetCDF, self).__exit__(exc_type, exc_val, exc_tb) + + def get_attributes(self): + return {attr[len('network_'):]: val + for attr, val in iteritems(self.ds.attrs) + if attr.startswith('network_')} + + def get_snapshots(self): + return self.get_static('snapshots') + + def get_static(self, list_name): + t = list_name + '_' + i = len(t) + df = pd.DataFrame.from_dict({attr[i:]: self.ds[attr].to_pandas() + for attr in iterkeys(self.ds.data_vars) + if attr.startswith(t) and attr[i:i+2] != 't_'}) + df.index.name = 'name' + return df + + def get_series(self, list_name): + t = list_name + '_t_' + for attr in iterkeys(self.ds.data_vars): + if attr.startswith(t): + df = self.ds[attr].to_pandas() + df.index.name = 'name' + df.columns.name = 'name' + yield attr[len(t):], df + + class ExporterNetCDF(Exporter): + def __init__(self, path): + self.path = path + self.ds = xr.Dataset() + + def save_attributes(self, attrs): + self.ds.attrs.update(('network_' + attr, val) + for attr, val in iteritems(attrs)) + + def save_snapshots(self, snapshots): + snapshots.index.name = 'snapshots' + for attr in snapshots.columns: + self.ds['snapshots_' + attr] = snapshots[attr] + + def save_static(self, list_name, df): + df.index.name = list_name + '_i' + for attr in df.columns: + self.ds[list_name + '_' + attr] = df[attr] + + def save_series(self, list_name, attr, df): + df.index.name = 'snapshots' + df.columns.name = list_name + '_t_' + attr + '_i' + self.ds[list_name + '_t_' + attr] = df + + def finish(self): + if self.path is not None: + self.ds.to_netcdf(self.path) + def _export_to_exporter(network, exporter, basename, export_standard_types=False): """ Export to exporter. @@ -362,17 +438,57 @@ def export_to_hdf5(network, path, export_standard_types=False, **kwargs): _export_to_exporter(network, exporter, basename=basename, export_standard_types=export_standard_types) +def import_from_netcdf(network, path, skip_time=False): + """ + Import network data from netCDF file or xarray Dataset at `path`. + Parameters + ---------- + path : string|xr.Dataset + Path to netCDF dataset or instance of xarray Dataset + skip_time : bool, default False + Skip reading in time dependent attributes + """ + assert has_xarray, "xarray must be installed for netCDF support." + basename = os.path.basename(path) if isinstance(path, string_types) else None + with ImporterNetCDF(path=path) as importer: + _import_from_importer(network, importer, basename=basename, + skip_time=skip_time) +def export_to_netcdf(network, path=None, export_standard_types=False): + """ + Export network and components to a netCDF file. + Both static and series attributes of components are exported, but only + if they have non-default values. + If path does not already exist, it is created. + Parameters + ---------- + path : string|None + Name of netCDF file to which to export (if it exists, it is overwritten) + Returns + ------- + ds : xr.Dataset + Examples + -------- + >>> export_to_netcdf(network, filename) + OR + >>> network.export_to_netcdf(filename) + """ + assert has_xarray, "xarray must be installed for netCDF support." + basename = os.path.basename(path) if path is not None else None + with ExporterNetCDF(path=path) as exporter: + _export_to_exporter(network, exporter, basename=basename, + export_standard_types=export_standard_types) + return exporter.ds def _import_from_importer(network, importer, basename, skip_time=False): """ From 5bf03cc1f069851327c854d42566af5d07976fe3 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Mon, 18 Dec 2017 14:36:56 +0100 Subject: [PATCH 029/135] io: Only call finish in Importers and Exporter if no exception was raised --- pypsa/io.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pypsa/io.py b/pypsa/io.py index 71fed775d..9d906910c 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -50,10 +50,11 @@ def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): - self.finish() - ds = self.ds - if ds is not None: - ds.__exit__(exc_type, exc_val, exc_tb) + if exc_type is None: + self.finish() + + if self.ds is not None: + self.ds.__exit__(exc_type, exc_val, exc_tb) def finish(self): pass From a54b58cf8854475f5f96f5d95dc66a48a5bf6cdc Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Tue, 19 Dec 2017 00:16:18 +0100 Subject: [PATCH 030/135] io: Fix netcdf exporter --- pypsa/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypsa/io.py b/pypsa/io.py index 9d906910c..c800d1fae 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -186,7 +186,7 @@ def __init__(self, path): def __enter__(self): if isinstance(self.path, string_types): - self.ds = super(ImporterNetCDF, self).__init__() + super(ImporterNetCDF, self).__init__() return self def __exit__(self, exc_type, exc_val, exc_tb): From 352598bbc5e9755bcb1a911152b88bae8bf36660 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Tue, 19 Dec 2017 00:16:48 +0100 Subject: [PATCH 031/135] io: Add compression to series attributes in netcdf exporter --- pypsa/io.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pypsa/io.py b/pypsa/io.py index c800d1fae..06ec9bb73 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -220,8 +220,9 @@ def get_series(self, list_name): yield attr[len(t):], df class ExporterNetCDF(Exporter): - def __init__(self, path): + def __init__(self, path, least_significant_digit=3): self.path = path + self.least_significant_digit = least_significant_digit self.ds = xr.Dataset() def save_attributes(self, attrs): @@ -242,6 +243,11 @@ def save_series(self, list_name, attr, df): df.index.name = 'snapshots' df.columns.name = list_name + '_t_' + attr + '_i' self.ds[list_name + '_t_' + attr] = df + if self.least_significant_digit is not None: + self.ds.encoding.upate({ + 'zlib': True, + 'least_significant_digit': self.least_significant_digit + }) def finish(self): if self.path is not None: From 46c0a29d2fc54ca628e241f6f398cd733e1fba2c Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Tue, 19 Dec 2017 00:21:18 +0100 Subject: [PATCH 032/135] io: import_components_from_dataframe preserves types of foreign columns Fixes #27. --- pypsa/io.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pypsa/io.py b/pypsa/io.py index 06ec9bb73..5ae00909a 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -620,7 +620,10 @@ def import_components_from_dataframe(network, dataframe, cls_name): cls_name, missing) non_static_attrs_in_df = non_static_attrs.index.intersection(dataframe.columns) - new_df = pd.concat((network.df(cls_name), dataframe.drop(non_static_attrs_in_df, axis=1))) + old_df = network.df(cls_name) + new_df = dataframe.drop(non_static_attrs_in_df, axis=1) + if not old_df.empty: + new_df = pd.concat((old_df, new_df)) if not new_df.index.is_unique: logger.error("Error, new components for {} are not unique".format(cls_name)) From aa6924482c6c9d0e2ef5a0082ff54ffb310f0869 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Mon, 8 Jan 2018 10:16:06 -0500 Subject: [PATCH 033/135] changing test for comparison --- examples/minimal_example_lopf.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/minimal_example_lopf.py b/examples/minimal_example_lopf.py index 6288fa4ac..df51678da 100644 --- a/examples/minimal_example_lopf.py +++ b/examples/minimal_example_lopf.py @@ -6,6 +6,7 @@ from __future__ import print_function, division import pypsa import numpy as np +import pypsa.opf as opf network = pypsa.Network() @@ -56,7 +57,10 @@ def my_f(network,snapshots): print(snapshots) -network.lopf(extra_functionality=my_f) +network.set_snapshots(range(10000)) + +opf.network_lopf_build_model( network, network.snapshots) +#network.lopf(extra_functionality=my_f) #Cheap generator 1 cannot be fully dispatched because of network constraints, #so expensive generator 0 also has to dispatch @@ -78,4 +82,3 @@ def my_f(network,snapshots): #it from expensive generator 0, also some dispatch from cheap generator 1 has to be substituted from generator0 #to avoid overloading line 1. print(network.buses_t.marginal_price) - From 6e1eaca6a4ecd8c93172cf80c8953b6a86e980a9 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Mon, 8 Jan 2018 13:26:44 -0500 Subject: [PATCH 034/135] adding model build only example --- examples/build_model.py | 245 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 examples/build_model.py diff --git a/examples/build_model.py b/examples/build_model.py new file mode 100644 index 000000000..9fb388ac7 --- /dev/null +++ b/examples/build_model.py @@ -0,0 +1,245 @@ +## Demonstrate PyPSA unit commitment with a one-bus two-generator example +# +# +#To enable unit commitment on a generator, set its attribute committable = True. +# +# +#Available as a Jupyter notebook at http://www.pypsa.org/examples/unit-commitment.ipynb. + +import pypsa + +from pypsa.opt import network_lopf_build_model as build_model + +### Minimum part load demonstration +# +#In final hour load goes below part-load limit of coal gen (30%), forcing gas to commit. + +for test_i in range(100): + + nu = pypsa.Network() + + nu.set_snapshots(range(4)) + + nu.add("Bus","bus") + + + nu.add("Generator","coal",bus="bus", + committable=True, + p_min_pu=0.3, + marginal_cost=20, + p_nom=10000) + + nu.add("Generator","gas",bus="bus", + committable=True, + marginal_cost=70, + p_min_pu=0.1, + p_nom=1000) + + nu.add("Load","load",bus="bus",p_set=[4000,6000,5000,800]) + + build_model( nu, nu.snapshots) + + nu.generators_t.status + + nu.generators_t.p + + ### Minimum up time demonstration + # + #Gas has minimum up time, forcing it to be online longer + + nu = pypsa.Network() + + nu.set_snapshots(range(4)) + + nu.add("Bus","bus") + + + nu.add("Generator","coal",bus="bus", + committable=True, + p_min_pu=0.3, + marginal_cost=20, + p_nom=10000) + + nu.add("Generator","gas",bus="bus", + committable=True, + marginal_cost=70, + p_min_pu=0.1, + initial_status=0, + min_up_time=3, + p_nom=1000) + + nu.add("Load","load",bus="bus",p_set=[4000,800,5000,3000]) + + build_model( nu, nu.snapshots) + + nu.generators_t.status + + nu.generators_t.p + + ### Minimum down time demonstration + # + #Coal has a minimum down time, forcing it to go off longer. + + nu = pypsa.Network() + + nu.set_snapshots(range(4)) + + nu.add("Bus","bus") + + + nu.add("Generator","coal",bus="bus", + committable=True, + p_min_pu=0.3, + marginal_cost=20, + min_down_time=2, + p_nom=10000) + + nu.add("Generator","gas",bus="bus", + committable=True, + marginal_cost=70, + p_min_pu=0.1, + initial_status=0, + p_nom=4000) + + nu.add("Load","load",bus="bus",p_set=[3000,800,3000,8000]) + + build_model( nu, nu.snapshots) + + nu.objective + + nu.generators_t.status + + nu.generators_t.p + + ### Start up and shut down costs + # + #Now there are associated costs for shutting down, etc + + + + nu = pypsa.Network() + + nu.set_snapshots(range(4)) + + nu.add("Bus","bus") + + + nu.add("Generator","coal",bus="bus", + committable=True, + p_min_pu=0.3, + marginal_cost=20, + min_down_time=2, + start_up_cost=5000, + p_nom=10000) + + nu.add("Generator","gas",bus="bus", + committable=True, + marginal_cost=70, + p_min_pu=0.1, + initial_status=0, + shut_down_cost=25, + p_nom=4000) + + nu.add("Load","load",bus="bus",p_set=[3000,800,3000,8000]) + + build_model( nu, nu.snapshots) + + nu.objective + + nu.generators_t.status + + nu.generators_t.p + + ## Ramp rate limits + + import pypsa + + nu = pypsa.Network() + + nu.set_snapshots(range(6)) + + nu.add("Bus","bus") + + + nu.add("Generator","coal",bus="bus", + marginal_cost=20, + ramp_limit_up=0.1, + ramp_limit_down=0.2, + p_nom=10000) + + nu.add("Generator","gas",bus="bus", + marginal_cost=70, + p_nom=4000) + + nu.add("Load","load",bus="bus",p_set=[4000,7000,7000,7000,7000,3000]) + + build_model( nu, nu.snapshots) + + nu.generators_t.p + + import pypsa + + nu = pypsa.Network() + + nu.set_snapshots(range(6)) + + nu.add("Bus","bus") + + + nu.add("Generator","coal",bus="bus", + marginal_cost=20, + ramp_limit_up=0.1, + ramp_limit_down=0.2, + p_nom_extendable=True, + capital_cost=1e2) + + nu.add("Generator","gas",bus="bus", + marginal_cost=70, + p_nom=4000) + + nu.add("Load","load",bus="bus",p_set=[4000,7000,7000,7000,7000,3000]) + + build_model( nu, nu.snapshots) + + nu.generators.p_nom_opt + + nu.generators_t.p + + import pypsa + + nu = pypsa.Network() + + nu.set_snapshots(range(7)) + + nu.add("Bus","bus") + + + #Can get bad interactions if SU > RU and p_min_pu; similarly if SD > RD + + + nu.add("Generator","coal",bus="bus", + marginal_cost=20, + committable=True, + p_min_pu=0.05, + initial_status=0, + ramp_limit_start_up=0.1, + ramp_limit_up=0.2, + ramp_limit_down=0.25, + ramp_limit_shut_down=0.15, + p_nom=10000.) + + nu.add("Generator","gas",bus="bus", + marginal_cost=70, + p_nom=10000) + + nu.add("Load","load",bus="bus",p_set=[0.,200.,7000,7000,7000,2000,0]) + + build_model( nu, nu.snapshots) + + nu.generators_t.p + + nu.generators_t.status + + nu.generators.initial_status + + nu.generators.loc["coal"] From 472554a8c3a1dd88a4e1ca153057515866b54356 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Mon, 8 Jan 2018 13:55:38 -0500 Subject: [PATCH 035/135] updating build model test --- examples/build_model.py | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/examples/build_model.py b/examples/build_model.py index 9fb388ac7..4bde0d72d 100644 --- a/examples/build_model.py +++ b/examples/build_model.py @@ -8,13 +8,13 @@ import pypsa -from pypsa.opt import network_lopf_build_model as build_model +from pypsa.opf import network_lopf_build_model as build_model ### Minimum part load demonstration # #In final hour load goes below part-load limit of coal gen (30%), forcing gas to commit. -for test_i in range(100): +for test_i in range(10): nu = pypsa.Network() @@ -105,12 +105,6 @@ build_model( nu, nu.snapshots) - nu.objective - - nu.generators_t.status - - nu.generators_t.p - ### Start up and shut down costs # #Now there are associated costs for shutting down, etc @@ -144,11 +138,6 @@ build_model( nu, nu.snapshots) - nu.objective - - nu.generators_t.status - - nu.generators_t.p ## Ramp rate limits @@ -201,10 +190,6 @@ build_model( nu, nu.snapshots) - nu.generators.p_nom_opt - - nu.generators_t.p - import pypsa nu = pypsa.Network() @@ -235,11 +220,3 @@ nu.add("Load","load",bus="bus",p_set=[0.,200.,7000,7000,7000,2000,0]) build_model( nu, nu.snapshots) - - nu.generators_t.p - - nu.generators_t.status - - nu.generators.initial_status - - nu.generators.loc["coal"] From f2270c592bdbc401369cf05d1586da5cf5214f6f Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Mon, 8 Jan 2018 14:08:46 -0500 Subject: [PATCH 036/135] test many snapshots --- examples/build_model.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/examples/build_model.py b/examples/build_model.py index 4bde0d72d..ca3cd8cec 100644 --- a/examples/build_model.py +++ b/examples/build_model.py @@ -14,11 +14,14 @@ # #In final hour load goes below part-load limit of coal gen (30%), forcing gas to commit. -for test_i in range(10): +for test_i in range(1): + + snapshots = range( 1, 1000) + p_set = [ p*20 for p in snapshots] nu = pypsa.Network() - nu.set_snapshots(range(4)) + nu.set_snapshots(snapshots) nu.add("Bus","bus") @@ -35,7 +38,7 @@ p_min_pu=0.1, p_nom=1000) - nu.add("Load","load",bus="bus",p_set=[4000,6000,5000,800]) + nu.add("Load","load",bus="bus",p_set= p_set )#[4000,6000,5000,800]) build_model( nu, nu.snapshots) @@ -49,11 +52,9 @@ nu = pypsa.Network() - nu.set_snapshots(range(4)) - + nu.set_snapshots( snapshots) nu.add("Bus","bus") - nu.add("Generator","coal",bus="bus", committable=True, p_min_pu=0.3, @@ -68,7 +69,7 @@ min_up_time=3, p_nom=1000) - nu.add("Load","load",bus="bus",p_set=[4000,800,5000,3000]) + nu.add("Load","load",bus="bus",p_set= p_set )#[4000,800,5000,3000]) build_model( nu, nu.snapshots) @@ -82,7 +83,7 @@ nu = pypsa.Network() - nu.set_snapshots(range(4)) + nu.set_snapshots( snapshots) nu.add("Bus","bus") @@ -101,7 +102,7 @@ initial_status=0, p_nom=4000) - nu.add("Load","load",bus="bus",p_set=[3000,800,3000,8000]) + nu.add("Load","load",bus="bus",p_set= p_set )#[3000,800,3000,8000]) build_model( nu, nu.snapshots) @@ -113,7 +114,7 @@ nu = pypsa.Network() - nu.set_snapshots(range(4)) + nu.set_snapshots( snapshots) nu.add("Bus","bus") @@ -134,7 +135,7 @@ shut_down_cost=25, p_nom=4000) - nu.add("Load","load",bus="bus",p_set=[3000,800,3000,8000]) + nu.add("Load","load",bus="bus",p_set= p_set )#[3000,800,3000,8000]) build_model( nu, nu.snapshots) @@ -145,7 +146,7 @@ nu = pypsa.Network() - nu.set_snapshots(range(6)) + nu.set_snapshots( snapshots) nu.add("Bus","bus") @@ -160,7 +161,7 @@ marginal_cost=70, p_nom=4000) - nu.add("Load","load",bus="bus",p_set=[4000,7000,7000,7000,7000,3000]) + nu.add("Load","load",bus="bus",p_set= p_set )#[4000,7000,7000,7000,7000,3000]) build_model( nu, nu.snapshots) @@ -170,7 +171,7 @@ nu = pypsa.Network() - nu.set_snapshots(range(6)) + nu.set_snapshots( snapshots) nu.add("Bus","bus") @@ -186,7 +187,7 @@ marginal_cost=70, p_nom=4000) - nu.add("Load","load",bus="bus",p_set=[4000,7000,7000,7000,7000,3000]) + nu.add("Load","load",bus="bus",p_set= p_set )#[4000,7000,7000,7000,7000,3000]) build_model( nu, nu.snapshots) @@ -194,7 +195,7 @@ nu = pypsa.Network() - nu.set_snapshots(range(7)) + nu.set_snapshots( snapshots) nu.add("Bus","bus") @@ -217,6 +218,6 @@ marginal_cost=70, p_nom=10000) - nu.add("Load","load",bus="bus",p_set=[0.,200.,7000,7000,7000,2000,0]) + nu.add("Load","load",bus="bus",p_set= p_set )#[0.,200.,7000,7000,7000,2000,0]) build_model( nu, nu.snapshots) From 85d12b81c41508c3c4eb9a7d2b8e4f8f61f5068e Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Mon, 8 Jan 2018 14:25:49 -0500 Subject: [PATCH 037/135] try on kirchoff --- examples/build_model.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/build_model.py b/examples/build_model.py index ca3cd8cec..ed04e99a2 100644 --- a/examples/build_model.py +++ b/examples/build_model.py @@ -16,7 +16,7 @@ for test_i in range(1): - snapshots = range( 1, 1000) + snapshots = range( 1, 10000) p_set = [ p*20 for p in snapshots] nu = pypsa.Network() @@ -40,7 +40,7 @@ nu.add("Load","load",bus="bus",p_set= p_set )#[4000,6000,5000,800]) - build_model( nu, nu.snapshots) + build_model( nu, nu.snapshots, formulation = "kirchoff") nu.generators_t.status @@ -71,7 +71,7 @@ nu.add("Load","load",bus="bus",p_set= p_set )#[4000,800,5000,3000]) - build_model( nu, nu.snapshots) + build_model( nu, nu.snapshots, formulation = "kirchoff") nu.generators_t.status @@ -104,7 +104,7 @@ nu.add("Load","load",bus="bus",p_set= p_set )#[3000,800,3000,8000]) - build_model( nu, nu.snapshots) + build_model( nu, nu.snapshots, formulation = "kirchoff") ### Start up and shut down costs # @@ -137,7 +137,7 @@ nu.add("Load","load",bus="bus",p_set= p_set )#[3000,800,3000,8000]) - build_model( nu, nu.snapshots) + build_model( nu, nu.snapshots, formulation = "kirchoff") ## Ramp rate limits @@ -163,7 +163,7 @@ nu.add("Load","load",bus="bus",p_set= p_set )#[4000,7000,7000,7000,7000,3000]) - build_model( nu, nu.snapshots) + build_model( nu, nu.snapshots, formulation = "kirchoff") nu.generators_t.p @@ -189,7 +189,7 @@ nu.add("Load","load",bus="bus",p_set= p_set )#[4000,7000,7000,7000,7000,3000]) - build_model( nu, nu.snapshots) + build_model( nu, nu.snapshots, formulation = "kirchoff") import pypsa @@ -220,4 +220,4 @@ nu.add("Load","load",bus="bus",p_set= p_set )#[0.,200.,7000,7000,7000,2000,0]) - build_model( nu, nu.snapshots) + build_model( nu, nu.snapshots, formulation = "kirchoff") From 5877d4b9f97ede9268bf76632a3776b044a32ed7 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Mon, 8 Jan 2018 17:23:31 -0500 Subject: [PATCH 038/135] updating build_model test --- examples/build_model.py | 192 +++++++--------------------------------- 1 file changed, 31 insertions(+), 161 deletions(-) diff --git a/examples/build_model.py b/examples/build_model.py index ed04e99a2..596dd381b 100644 --- a/examples/build_model.py +++ b/examples/build_model.py @@ -10,13 +10,32 @@ from pypsa.opf import network_lopf_build_model as build_model +import argparse +import logging + +parser = argparse.ArgumentParser() + +parser.add_argument( + '-v', '--verbose', + help="Be verbose", + action="store_const", dest="loglevel", const=logging.INFO, +) +parser.add_argument( + '-d', '--debug', + help="Print lots of debugging statements", + action="store_const", dest="loglevel", const=logging.DEBUG, + default=logging.WARNING, +) +args = parser.parse_args() +logging.basicConfig( level=args.loglevel) + ### Minimum part load demonstration # #In final hour load goes below part-load limit of coal gen (30%), forcing gas to commit. for test_i in range(1): - snapshots = range( 1, 10000) + snapshots = range( 1, 1001) p_set = [ p*20 for p in snapshots] nu = pypsa.Network() @@ -25,18 +44,18 @@ nu.add("Bus","bus") + generator_p_nom = [ i for i in range( 100)] + generator_marginal_cost = [ 1000 - i for i in range(100) ] + p_min_pu = .3 - nu.add("Generator","coal",bus="bus", - committable=True, - p_min_pu=0.3, - marginal_cost=20, - p_nom=10000) - - nu.add("Generator","gas",bus="bus", - committable=True, - marginal_cost=70, - p_min_pu=0.1, - p_nom=1000) + for gen_i, gen in enumerate( generator_p_nom): + nu.add( "Generator", + "gas_" + str(gen_i), + bus = "bus", + committable = True, + p_min_pu = p_min_pu, + marginal_cost = generator_marginal_cost[gen_i], + p_nom = generator_p_nom[gen_i],) nu.add("Load","load",bus="bus",p_set= p_set )#[4000,6000,5000,800]) @@ -72,152 +91,3 @@ nu.add("Load","load",bus="bus",p_set= p_set )#[4000,800,5000,3000]) build_model( nu, nu.snapshots, formulation = "kirchoff") - - nu.generators_t.status - - nu.generators_t.p - - ### Minimum down time demonstration - # - #Coal has a minimum down time, forcing it to go off longer. - - nu = pypsa.Network() - - nu.set_snapshots( snapshots) - - nu.add("Bus","bus") - - - nu.add("Generator","coal",bus="bus", - committable=True, - p_min_pu=0.3, - marginal_cost=20, - min_down_time=2, - p_nom=10000) - - nu.add("Generator","gas",bus="bus", - committable=True, - marginal_cost=70, - p_min_pu=0.1, - initial_status=0, - p_nom=4000) - - nu.add("Load","load",bus="bus",p_set= p_set )#[3000,800,3000,8000]) - - build_model( nu, nu.snapshots, formulation = "kirchoff") - - ### Start up and shut down costs - # - #Now there are associated costs for shutting down, etc - - - - nu = pypsa.Network() - - nu.set_snapshots( snapshots) - - nu.add("Bus","bus") - - - nu.add("Generator","coal",bus="bus", - committable=True, - p_min_pu=0.3, - marginal_cost=20, - min_down_time=2, - start_up_cost=5000, - p_nom=10000) - - nu.add("Generator","gas",bus="bus", - committable=True, - marginal_cost=70, - p_min_pu=0.1, - initial_status=0, - shut_down_cost=25, - p_nom=4000) - - nu.add("Load","load",bus="bus",p_set= p_set )#[3000,800,3000,8000]) - - build_model( nu, nu.snapshots, formulation = "kirchoff") - - - ## Ramp rate limits - - import pypsa - - nu = pypsa.Network() - - nu.set_snapshots( snapshots) - - nu.add("Bus","bus") - - - nu.add("Generator","coal",bus="bus", - marginal_cost=20, - ramp_limit_up=0.1, - ramp_limit_down=0.2, - p_nom=10000) - - nu.add("Generator","gas",bus="bus", - marginal_cost=70, - p_nom=4000) - - nu.add("Load","load",bus="bus",p_set= p_set )#[4000,7000,7000,7000,7000,3000]) - - build_model( nu, nu.snapshots, formulation = "kirchoff") - - nu.generators_t.p - - import pypsa - - nu = pypsa.Network() - - nu.set_snapshots( snapshots) - - nu.add("Bus","bus") - - - nu.add("Generator","coal",bus="bus", - marginal_cost=20, - ramp_limit_up=0.1, - ramp_limit_down=0.2, - p_nom_extendable=True, - capital_cost=1e2) - - nu.add("Generator","gas",bus="bus", - marginal_cost=70, - p_nom=4000) - - nu.add("Load","load",bus="bus",p_set= p_set )#[4000,7000,7000,7000,7000,3000]) - - build_model( nu, nu.snapshots, formulation = "kirchoff") - - import pypsa - - nu = pypsa.Network() - - nu.set_snapshots( snapshots) - - nu.add("Bus","bus") - - - #Can get bad interactions if SU > RU and p_min_pu; similarly if SD > RD - - - nu.add("Generator","coal",bus="bus", - marginal_cost=20, - committable=True, - p_min_pu=0.05, - initial_status=0, - ramp_limit_start_up=0.1, - ramp_limit_up=0.2, - ramp_limit_down=0.25, - ramp_limit_shut_down=0.15, - p_nom=10000.) - - nu.add("Generator","gas",bus="bus", - marginal_cost=70, - p_nom=10000) - - nu.add("Load","load",bus="bus",p_set= p_set )#[0.,200.,7000,7000,7000,2000,0]) - - build_model( nu, nu.snapshots, formulation = "kirchoff") From 4d36c1be0820e7b87b4f7dc8d85af08d2deffcf9 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Tue, 9 Jan 2018 14:06:23 -0500 Subject: [PATCH 039/135] updating build model example to include links between random linese --- examples/build_model.py | 43 +++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/examples/build_model.py b/examples/build_model.py index 596dd381b..7b720f535 100644 --- a/examples/build_model.py +++ b/examples/build_model.py @@ -12,6 +12,7 @@ import argparse import logging +import random parser = argparse.ArgumentParser() @@ -25,6 +26,7 @@ help="Print lots of debugging statements", action="store_const", dest="loglevel", const=logging.DEBUG, default=logging.WARNING, + ) args = parser.parse_args() logging.basicConfig( level=args.loglevel) @@ -35,31 +37,52 @@ for test_i in range(1): - snapshots = range( 1, 1001) + snapshots = range( 1, 101) p_set = [ p*20 for p in snapshots] nu = pypsa.Network() nu.set_snapshots(snapshots) - nu.add("Bus","bus") - generator_p_nom = [ i for i in range( 100)] - generator_marginal_cost = [ 1000 - i for i in range(100) ] + n_gen = 100 + + generator_p_nom = [ i for i in range( n_gen)] + generator_marginal_cost = [ 1000 - i for i in range( n_gen)] p_min_pu = .3 for gen_i, gen in enumerate( generator_p_nom): + nu.add("Bus","bus" + str(gen_i)) + nu.add("Load","load" + str(gen_i),bus= "bus" + str(gen_i), + p_set= generator_p_nom[gen_i] / 2. )#[4000,6000,5000,800]) + nu.add( "Generator", "gas_" + str(gen_i), - bus = "bus", + bus = "bus" + str(gen_i), committable = True, p_min_pu = p_min_pu, marginal_cost = generator_marginal_cost[gen_i], p_nom = generator_p_nom[gen_i],) - - nu.add("Load","load",bus="bus",p_set= p_set )#[4000,6000,5000,800]) - - build_model( nu, nu.snapshots, formulation = "kirchoff") + if gen_i > 0: + nu.add("Line", + "{} - {} line".format( gen_i - 1, gen_i), + bus0= "bus" + str(gen_i - 1), + bus1= "bus" + str(gen_i), + x=0.1, + r=0.01) + random_gen_to_connect = random.randint( 0, n_gen - 1) + nu.add("Line", + "{} - {} line".format( gen_i, random_gen_to_connect), + bus0= "bus" + str(random_gen_to_connect), + bus1= "bus" + str(gen_i), + x = .2, + r = .08) + + print( nu.branches()) + + build_model( nu, nu.snapshots, formulation = "kirchhoff") +# build_model( nu, nu.snapshots, formulation = "angles") +# nu.lopf( nu.snapshots, formulation = "kirchhoff") nu.generators_t.status @@ -90,4 +113,4 @@ nu.add("Load","load",bus="bus",p_set= p_set )#[4000,800,5000,3000]) - build_model( nu, nu.snapshots, formulation = "kirchoff") + build_model( nu, nu.snapshots, formulation = "kirchhoff") From c98b5c558298713fa89444841643cff980824c45 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Tue, 9 Jan 2018 15:21:55 -0500 Subject: [PATCH 040/135] first implementation speeding up passive branch creation in kirchhoff --- pypsa/opf.py | 64 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index 3a78986f4..1420b5185 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -28,6 +28,7 @@ __author__ = "Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)" __copyright__ = "Copyright 2015-2017 Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS), GNU GPL 3" +import itertools import numpy as np import pandas as pd from scipy.sparse.linalg import spsolve @@ -729,6 +730,7 @@ def define_passive_branch_flows_with_PTDF(network,snapshots,ptdf_tolerance=0.): list(passive_branches.index), snapshots) + def define_passive_branch_flows_with_cycles(network,snapshots): for sub_network in network.sub_networks.obj: @@ -800,6 +802,16 @@ def define_passive_branch_flows_with_cycles(network,snapshots): list(passive_branches.index), snapshots) +def using_tocoo_izip(x): + """ from https://stackoverflow.com/questions/4319014/iterating-through-a-scipy-sparse-vector-or-matrix, + to more quickly iterate the dok matrix. + + Moved actual conversion into kirchhoff, to allow yield here + """ + + for i,j,v in itertools.izip(cx.row, cx.col, cx.data): + yield (i,j,v) + def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False): for sub_network in network.sub_networks.obj: @@ -819,24 +831,50 @@ def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False cycle_index = [] cycle_constraints = {} - for sn in network.sub_networks.obj: + for subnetwork in network.sub_networks.obj: - branches = sn.branches() - attribute = "r_pu" if network.sub_networks.at[sn.name,"carrier"] == "DC" else "x_pu" + branches = subnetwork.branches() + attribute = "r_pu" if network.sub_networks.at[subnetwork.name,"carrier"] == "DC" else "x_pu" - for j in range(sn.C.shape[1]): + #https://stackoverflow.com/questions/4319014/iterating-through-a-scipy-sparse-vector-or-matrix +# cx = subnetwork.C.tocoo() +# for cycle_i,j,value in itertools.izip(cx.row, cx.col, cx.data): +# cycle_is +# for snapshot in snapshots: +# lhs = LExpression([ (branches.at[branches.index[cycle_i],attribute]* +# (branches.at[branches.index[cycle_i],"tap_ratio"] if branches.index[cycle_i][0] == "Transformer" else 1.)*value, - cycle_is = sn.C[:,j].nonzero()[0] - if len(cycle_is) == 0: continue +# #*subnetwork.C[cycle_i,j], +# network.model.passive_branch_p[branches.index[cycle_i][0], branches.index[cycle_i][1], snapshot])]) - cycle_index.append((sn.name, j)) +# cycle_constraints[subnetwork.name, j, snapshot] = LConstraint( lhs, "==", LExpression()) - for snapshot in snapshots: - lhs = LExpression([(branches.at[branches.index[i],attribute]* - (branches.at[branches.index[i],"tap_ratio"] if branches.index[i][0] == "Transformer" else 1.)*sn.C[i,j], - network.model.passive_branch_p[branches.index[i][0], branches.index[i][1], snapshot]) - for i in cycle_is]) - cycle_constraints[sn.name,j,snapshot] = LConstraint(lhs,"==",LExpression()) + +#lhs = LExpression([(branches.at[branches.index[i],attribute]* +# (branches.at[branches.index[i],"tap_ratio"] if branches.index[i][0] == "Transformer" else 1.)*sn.C[i,j], +# network.model.passive_branch_p[branches.index[i][0], branches.index[i][1], snapshot]) +# for i in cycle_is]) + + for j in range(subnetwork.C.shape[1]): + +# cycle_is = subnetwork.C.getcol(j)#subnetwork.C[:,j].nonzero()[0] + cycle_is = subnetwork.C[:,j].nonzero()[0] +# print(cycle_is) + + if len(cycle_is) == 0: continue + + cycle_index.append((subnetwork.name, j)) + + branch_idxs = [ branches.index[cycle_i] for cycle_i in cycle_is ] + time_ind_params = [ branches.at[branch_idx,attribute]*(branches.at[branch_idx,"tap_ratio"] \ + if branch_idx[0] == "Transformer" else 1.)*subnetwork.C[cycle_i,j] for branch_idx in branch_idxs] + for snapshot in snapshots: + expression_list = [ (time_ind_param, + network.model.passive_branch_p[branch_idx[0], branch_idx[1], snapshot]) for (branch_idx, time_ind_param) in zip(branch_idxs, time_ind_params)] + + lhs = LExpression(expression_list) +# for cycle_i in cycle_is]) + cycle_constraints[subnetwork.name,j,snapshot] = LConstraint(lhs,"==",LExpression()) l_constraint(network.model, "cycle_constraints", cycle_constraints, cycle_index, snapshots) From 3a22ce6c3c458cbcdc4db46824c2de9e069fd227 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Wed, 10 Jan 2018 15:57:17 +0000 Subject: [PATCH 041/135] printing missing negatives --- pypsa/opf.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pypsa/opf.py b/pypsa/opf.py index 1420b5185..098196a30 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -813,7 +813,9 @@ def using_tocoo_izip(x): yield (i,j,v) def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False): + """ define passive branch flows with the kirchoff method """ + print( "running kirchoff passive branch flows") for sub_network in network.sub_networks.obj: find_tree(sub_network) find_cycles(sub_network) @@ -869,6 +871,8 @@ def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False time_ind_params = [ branches.at[branch_idx,attribute]*(branches.at[branch_idx,"tap_ratio"] \ if branch_idx[0] == "Transformer" else 1.)*subnetwork.C[cycle_i,j] for branch_idx in branch_idxs] for snapshot in snapshots: + print( time_ind_params) + print( network.model.passive_branch_p[branch_idx[0], branch_idx[1], snapshot]) expression_list = [ (time_ind_param, network.model.passive_branch_p[branch_idx[0], branch_idx[1], snapshot]) for (branch_idx, time_ind_param) in zip(branch_idxs, time_ind_params)] From 529c57f4bb7dc87ccff56a19f84786308ab4d2af Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Wed, 10 Jan 2018 17:39:59 +0000 Subject: [PATCH 042/135] removing unneeded comments --- pypsa/opf.py | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index 098196a30..3f14e861c 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -838,30 +838,9 @@ def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False branches = subnetwork.branches() attribute = "r_pu" if network.sub_networks.at[subnetwork.name,"carrier"] == "DC" else "x_pu" - #https://stackoverflow.com/questions/4319014/iterating-through-a-scipy-sparse-vector-or-matrix -# cx = subnetwork.C.tocoo() -# for cycle_i,j,value in itertools.izip(cx.row, cx.col, cx.data): -# cycle_is -# for snapshot in snapshots: -# lhs = LExpression([ (branches.at[branches.index[cycle_i],attribute]* -# (branches.at[branches.index[cycle_i],"tap_ratio"] if branches.index[cycle_i][0] == "Transformer" else 1.)*value, - -# #*subnetwork.C[cycle_i,j], -# network.model.passive_branch_p[branches.index[cycle_i][0], branches.index[cycle_i][1], snapshot])]) - -# cycle_constraints[subnetwork.name, j, snapshot] = LConstraint( lhs, "==", LExpression()) - - -#lhs = LExpression([(branches.at[branches.index[i],attribute]* -# (branches.at[branches.index[i],"tap_ratio"] if branches.index[i][0] == "Transformer" else 1.)*sn.C[i,j], -# network.model.passive_branch_p[branches.index[i][0], branches.index[i][1], snapshot]) -# for i in cycle_is]) - for j in range(subnetwork.C.shape[1]): -# cycle_is = subnetwork.C.getcol(j)#subnetwork.C[:,j].nonzero()[0] cycle_is = subnetwork.C[:,j].nonzero()[0] -# print(cycle_is) if len(cycle_is) == 0: continue @@ -869,15 +848,12 @@ def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False branch_idxs = [ branches.index[cycle_i] for cycle_i in cycle_is ] time_ind_params = [ branches.at[branch_idx,attribute]*(branches.at[branch_idx,"tap_ratio"] \ - if branch_idx[0] == "Transformer" else 1.)*subnetwork.C[cycle_i,j] for branch_idx in branch_idxs] + if branch_idx[0] == "Transformer" else 1.)*subnetwork.C[cycle_i, j] for branch_idx in branch_idxs] for snapshot in snapshots: - print( time_ind_params) - print( network.model.passive_branch_p[branch_idx[0], branch_idx[1], snapshot]) expression_list = [ (time_ind_param, network.model.passive_branch_p[branch_idx[0], branch_idx[1], snapshot]) for (branch_idx, time_ind_param) in zip(branch_idxs, time_ind_params)] lhs = LExpression(expression_list) -# for cycle_i in cycle_is]) cycle_constraints[subnetwork.name,j,snapshot] = LConstraint(lhs,"==",LExpression()) l_constraint(network.model, "cycle_constraints", cycle_constraints, From ff28b62e7bf6d085ebe3831cc71114841824e5b7 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Wed, 10 Jan 2018 18:51:26 +0000 Subject: [PATCH 043/135] fix indent --- pypsa/opf.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index 3f14e861c..a5c6f9031 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -825,6 +825,9 @@ def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False if len(sub_network.branches_i()) > 0: calculate_B_H(sub_network) + print( sub_network.T) + print( sub_network.C) + passive_branches = network.passive_branches() if not skip_vars: @@ -833,28 +836,29 @@ def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False cycle_index = [] cycle_constraints = {} - for subnetwork in network.sub_networks.obj: + for sn in network.sub_networks.obj: + + branches = sn.branches() + attribute = "r_pu" if network.sub_networks.at[sn.name,"carrier"] == "DC" else "x_pu" - branches = subnetwork.branches() - attribute = "r_pu" if network.sub_networks.at[subnetwork.name,"carrier"] == "DC" else "x_pu" + for j in range(sn.C.shape[1]): - for j in range(subnetwork.C.shape[1]): + cycle_is = sn.C[:,j].nonzero()[0] - cycle_is = subnetwork.C[:,j].nonzero()[0] + if len(cycle_is) == 0: continue - if len(cycle_is) == 0: continue + cycle_index.append((sn.name, j)) - cycle_index.append((subnetwork.name, j)) + branch_idxs = [ branches.index[cycle_i] for cycle_i in cycle_is ] + time_ind_params = [ -1 * branches.at[branch_idx,attribute]*(branches.at[branch_idx,"tap_ratio"] \ + if branch_idx[0] == "Transformer" else 1.)*sn.C[cycle_i, j] for branch_idx in branch_idxs] - branch_idxs = [ branches.index[cycle_i] for cycle_i in cycle_is ] - time_ind_params = [ branches.at[branch_idx,attribute]*(branches.at[branch_idx,"tap_ratio"] \ - if branch_idx[0] == "Transformer" else 1.)*subnetwork.C[cycle_i, j] for branch_idx in branch_idxs] - for snapshot in snapshots: - expression_list = [ (time_ind_param, + for snapshot in snapshots: + expression_list = [ (time_ind_param, network.model.passive_branch_p[branch_idx[0], branch_idx[1], snapshot]) for (branch_idx, time_ind_param) in zip(branch_idxs, time_ind_params)] - lhs = LExpression(expression_list) - cycle_constraints[subnetwork.name,j,snapshot] = LConstraint(lhs,"==",LExpression()) + lhs = LExpression(expression_list) + cycle_constraints[sn.name,j,snapshot] = LConstraint(lhs,"==",LExpression()) l_constraint(network.model, "cycle_constraints", cycle_constraints, cycle_index, snapshots) From 5def5631253b86889ce06c22902c9c6def6e8399 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Wed, 10 Jan 2018 19:14:46 +0000 Subject: [PATCH 044/135] fix for unsolvable --- pypsa/opf.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index a5c6f9031..c8dd5ff15 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -836,29 +836,39 @@ def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False cycle_index = [] cycle_constraints = {} - for sn in network.sub_networks.obj: + for subnetwork in network.sub_networks.obj: - branches = sn.branches() - attribute = "r_pu" if network.sub_networks.at[sn.name,"carrier"] == "DC" else "x_pu" + branches = subnetwork.branches() + attribute = "r_pu" if network.sub_networks.at[subnetwork.name,"carrier"] == "DC" else "x_pu" - for j in range(sn.C.shape[1]): + for j in range(subnetwork.C.shape[1]): - cycle_is = sn.C[:,j].nonzero()[0] + cycle_is = subnetwork.C[:,j].nonzero()[0] if len(cycle_is) == 0: continue - cycle_index.append((sn.name, j)) + cycle_index.append((subnetwork.name, j)) - branch_idxs = [ branches.index[cycle_i] for cycle_i in cycle_is ] - time_ind_params = [ -1 * branches.at[branch_idx,attribute]*(branches.at[branch_idx,"tap_ratio"] \ - if branch_idx[0] == "Transformer" else 1.)*sn.C[cycle_i, j] for branch_idx in branch_idxs] + + + branch_idxs, time_ind_params = zip( *[ ( + branches.index[cycle_i], + branches.at[branch_idx,attribute]*(branches.at[branch_idx,"tap_ratio"] \ + if branch_idx[0] == "Transformer" else 1.)*subnetwork.C[cycle_i, j] + ) for itertools.product( branch_idxs, cycle_is)] ) + + # branch_idxs = [ branches.index[cycle_i] for cycle_i in cycle_is ] + + # time_ind_params = [ -1 * branches.at[branch_idx,attribute]*(branches.at[branch_idx,"tap_ratio"] \ + # if branch_idx[0] == "Transformer" else 1.)*subnetwork.C[cycle_i, j] for ( + # branch_idx, cycle_i) in itertools.product( branch_idxs, cycle_is)] for snapshot in snapshots: expression_list = [ (time_ind_param, network.model.passive_branch_p[branch_idx[0], branch_idx[1], snapshot]) for (branch_idx, time_ind_param) in zip(branch_idxs, time_ind_params)] lhs = LExpression(expression_list) - cycle_constraints[sn.name,j,snapshot] = LConstraint(lhs,"==",LExpression()) + cycle_constraints[subnetwork.name,j,snapshot] = LConstraint(lhs,"==",LExpression()) l_constraint(network.model, "cycle_constraints", cycle_constraints, cycle_index, snapshots) From 32420daa7d0e2831337f556a7d8c9c265b598267 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Wed, 10 Jan 2018 20:06:11 +0000 Subject: [PATCH 045/135] fix creation of branch_idx and time_ind_params --- pypsa/opf.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index c8dd5ff15..88e8733f4 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -850,12 +850,13 @@ def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False cycle_index.append((subnetwork.name, j)) - - branch_idxs, time_ind_params = zip( *[ ( - branches.index[cycle_i], - branches.at[branch_idx,attribute]*(branches.at[branch_idx,"tap_ratio"] \ - if branch_idx[0] == "Transformer" else 1.)*subnetwork.C[cycle_i, j] - ) for itertools.product( branch_idxs, cycle_is)] ) + branch_idxs = [] + time_ind_params = [] + for cycle_i in cycle_is: + branch_idx = branches.index[cycle_i] + branch_idxs.append(branch_idx) + time_ind_params.append( branches.at[branch_idx,attribute]*(branches.at[branch_idx,"tap_ratio"] \ + if branch_idx[0] == "Transformer" else 1.)*subnetwork.C[cycle_i, j]) # branch_idxs = [ branches.index[cycle_i] for cycle_i in cycle_is ] From 375c8b7e5af173b1673fc8d8f94cef8565868e39 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Wed, 10 Jan 2018 16:53:26 -0500 Subject: [PATCH 046/135] constructing cycle constraints in separte function using coo iteration --- examples/build_model.py | 2 ++ pypsa/opf.py | 71 +++++++++++++++++++++++------------------ 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/examples/build_model.py b/examples/build_model.py index 7b720f535..2d4eca069 100644 --- a/examples/build_model.py +++ b/examples/build_model.py @@ -14,6 +14,8 @@ import logging import random +random.seed( 55) + parser = argparse.ArgumentParser() parser.add_argument( diff --git a/pypsa/opf.py b/pypsa/opf.py index 88e8733f4..edc0fde94 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -812,10 +812,42 @@ def using_tocoo_izip(x): for i,j,v in itertools.izip(cx.row, cx.col, cx.data): yield (i,j,v) +def define_sub_network_cycle_constraints( subnetwork, snapshots, passive_branch_p, attribute): + """ Constructs cycle_constraints for a particular subnetwork + + Uses CSC matrix structure for faster iteration + + Adapted from + https://stackoverflow.com/a/42625707/6753312 + """ + + sub_network_cycle_constraints = {} + sub_network_cycle_index = [] + + matrix = subnetwork.C.tocoo() + branches = subnetwork.branches() + + for cycle_i, col, matrix_data in itertools.izip( matrix.row, matrix.col, matrix.data): + branch_idx_attributes = [] + + branch_idx = branches.index[cycle_i] + attribute_value = branches.at[branch_idx,attribute]*(branches.at[branch_idx,"tap_ratio"] \ + if branch_idx[0] == "Transformer" else 1.) * matrix_data#matrix.data[cycle_i] + + branch_idx_attributes.append( (branch_idx, attribute_value)) + + for snapshot in snapshots: + expression_list = [ (attribute_value, + passive_branch_p[branch_idx[0], branch_idx[1], snapshot]) for (branch_idx, attribute_value) in branch_idx_attributes] + + lhs = LExpression(expression_list) + sub_network_cycle_constraints[subnetwork.name,col,snapshot] = LConstraint(lhs,"==",LExpression()) + + return( sub_network_cycle_index, sub_network_cycle_constraints) + def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False): """ define passive branch flows with the kirchoff method """ - print( "running kirchoff passive branch flows") for sub_network in network.sub_networks.obj: find_tree(sub_network) find_cycles(sub_network) @@ -825,9 +857,6 @@ def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False if len(sub_network.branches_i()) > 0: calculate_B_H(sub_network) - print( sub_network.T) - print( sub_network.C) - passive_branches = network.passive_branches() if not skip_vars: @@ -841,35 +870,15 @@ def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False branches = subnetwork.branches() attribute = "r_pu" if network.sub_networks.at[subnetwork.name,"carrier"] == "DC" else "x_pu" - for j in range(subnetwork.C.shape[1]): - - cycle_is = subnetwork.C[:,j].nonzero()[0] - - if len(cycle_is) == 0: continue - - cycle_index.append((subnetwork.name, j)) + csc_rep = subnetwork.C.tocoo() + #TODO fix the inputs here + sub_network_cycle_index, sub_network_cycle_constraints = define_sub_network_cycle_constraints( subnetwork, + snapshots, + network.model.passive_branch_p, attribute) - branch_idxs = [] - time_ind_params = [] - for cycle_i in cycle_is: - branch_idx = branches.index[cycle_i] - branch_idxs.append(branch_idx) - time_ind_params.append( branches.at[branch_idx,attribute]*(branches.at[branch_idx,"tap_ratio"] \ - if branch_idx[0] == "Transformer" else 1.)*subnetwork.C[cycle_i, j]) - - # branch_idxs = [ branches.index[cycle_i] for cycle_i in cycle_is ] - - # time_ind_params = [ -1 * branches.at[branch_idx,attribute]*(branches.at[branch_idx,"tap_ratio"] \ - # if branch_idx[0] == "Transformer" else 1.)*subnetwork.C[cycle_i, j] for ( - # branch_idx, cycle_i) in itertools.product( branch_idxs, cycle_is)] - - for snapshot in snapshots: - expression_list = [ (time_ind_param, - network.model.passive_branch_p[branch_idx[0], branch_idx[1], snapshot]) for (branch_idx, time_ind_param) in zip(branch_idxs, time_ind_params)] - - lhs = LExpression(expression_list) - cycle_constraints[subnetwork.name,j,snapshot] = LConstraint(lhs,"==",LExpression()) + cycle_index.extend( sub_network_cycle_index) + cycle_constraints.update( sub_network_cycle_constraints) l_constraint(network.model, "cycle_constraints", cycle_constraints, cycle_index, snapshots) From 7d82dc2c180fc9cf8c0111af8a5477e6497d01cf Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Wed, 10 Jan 2018 17:14:04 -0500 Subject: [PATCH 047/135] better naming and fixes --- pypsa/opf.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index edc0fde94..943348307 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -824,24 +824,33 @@ def define_sub_network_cycle_constraints( subnetwork, snapshots, passive_branch_ sub_network_cycle_constraints = {} sub_network_cycle_index = [] - matrix = subnetwork.C.tocoo() + matrix = subnetwork.C.tocsr() branches = subnetwork.branches() - for cycle_i, col, matrix_data in itertools.izip( matrix.row, matrix.col, matrix.data): +# for cycle_i, col, matrix_data in itertools.izip( matrix.row, matrix.col, matrix.data): + for col_j in range( matrix.shape[1] ): + cycle_is = matrix.getcol(col_j).nonzero()[0] + + if len(cycle_is) == 0: continue + + sub_network_cycle_index.append((subnetwork.name, col_j)) + + branch_idx_attributes = [] - branch_idx = branches.index[cycle_i] - attribute_value = branches.at[branch_idx,attribute]*(branches.at[branch_idx,"tap_ratio"] \ - if branch_idx[0] == "Transformer" else 1.) * matrix_data#matrix.data[cycle_i] + for cycle_i in cycle_is: + branch_idx = branches.index[cycle_i] + attribute_value = branches.at[branch_idx,attribute]*(branches.at[branch_idx,"tap_ratio"] \ + if branch_idx[0] == "Transformer" else 1.)*subnetwork.C[cycle_i, col_j] - branch_idx_attributes.append( (branch_idx, attribute_value)) + branch_idx_attributes.append( (branch_idx, attribute_value)) for snapshot in snapshots: expression_list = [ (attribute_value, passive_branch_p[branch_idx[0], branch_idx[1], snapshot]) for (branch_idx, attribute_value) in branch_idx_attributes] lhs = LExpression(expression_list) - sub_network_cycle_constraints[subnetwork.name,col,snapshot] = LConstraint(lhs,"==",LExpression()) + sub_network_cycle_constraints[subnetwork.name,col_j,snapshot] = LConstraint(lhs,"==",LExpression()) return( sub_network_cycle_index, sub_network_cycle_constraints) From 0704bca32608fe80ac6f60c6d8d2d5652430c48c Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Wed, 10 Jan 2018 17:25:41 -0500 Subject: [PATCH 048/135] removing wip for pr --- pypsa/opf.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index 943348307..214b6680f 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -28,7 +28,6 @@ __author__ = "Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)" __copyright__ = "Copyright 2015-2017 Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS), GNU GPL 3" -import itertools import numpy as np import pandas as pd from scipy.sparse.linalg import spsolve @@ -802,23 +801,8 @@ def define_passive_branch_flows_with_cycles(network,snapshots): list(passive_branches.index), snapshots) -def using_tocoo_izip(x): - """ from https://stackoverflow.com/questions/4319014/iterating-through-a-scipy-sparse-vector-or-matrix, - to more quickly iterate the dok matrix. - - Moved actual conversion into kirchhoff, to allow yield here - """ - - for i,j,v in itertools.izip(cx.row, cx.col, cx.data): - yield (i,j,v) - def define_sub_network_cycle_constraints( subnetwork, snapshots, passive_branch_p, attribute): """ Constructs cycle_constraints for a particular subnetwork - - Uses CSC matrix structure for faster iteration - - Adapted from - https://stackoverflow.com/a/42625707/6753312 """ sub_network_cycle_constraints = {} @@ -827,7 +811,6 @@ def define_sub_network_cycle_constraints( subnetwork, snapshots, passive_branch_ matrix = subnetwork.C.tocsr() branches = subnetwork.branches() -# for cycle_i, col, matrix_data in itertools.izip( matrix.row, matrix.col, matrix.data): for col_j in range( matrix.shape[1] ): cycle_is = matrix.getcol(col_j).nonzero()[0] @@ -881,7 +864,6 @@ def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False csc_rep = subnetwork.C.tocoo() - #TODO fix the inputs here sub_network_cycle_index, sub_network_cycle_constraints = define_sub_network_cycle_constraints( subnetwork, snapshots, network.model.passive_branch_p, attribute) From 76525cb41e4915e30db927d24c61b0d08ef2638f Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Wed, 10 Jan 2018 17:28:39 -0500 Subject: [PATCH 049/135] revert lopf example --- examples/minimal_example_lopf.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/minimal_example_lopf.py b/examples/minimal_example_lopf.py index df51678da..6288fa4ac 100644 --- a/examples/minimal_example_lopf.py +++ b/examples/minimal_example_lopf.py @@ -6,7 +6,6 @@ from __future__ import print_function, division import pypsa import numpy as np -import pypsa.opf as opf network = pypsa.Network() @@ -57,10 +56,7 @@ def my_f(network,snapshots): print(snapshots) -network.set_snapshots(range(10000)) - -opf.network_lopf_build_model( network, network.snapshots) -#network.lopf(extra_functionality=my_f) +network.lopf(extra_functionality=my_f) #Cheap generator 1 cannot be fully dispatched because of network constraints, #so expensive generator 0 also has to dispatch @@ -82,3 +78,4 @@ def my_f(network,snapshots): #it from expensive generator 0, also some dispatch from cheap generator 1 has to be substituted from generator0 #to avoid overloading line 1. print(network.buses_t.marginal_price) + From 7fc6b041b7800b2bd81b1272afafe847f2bef541 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Thu, 11 Jan 2018 10:01:00 -0500 Subject: [PATCH 050/135] update example to run over angles, cycles, and kirchhoff --- examples/build_model.py | 39 +++++---------------------------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/examples/build_model.py b/examples/build_model.py index 2d4eca069..da996ab25 100644 --- a/examples/build_model.py +++ b/examples/build_model.py @@ -13,6 +13,7 @@ import argparse import logging import random +import copy random.seed( 55) @@ -80,39 +81,9 @@ x = .2, r = .08) - print( nu.branches()) - - build_model( nu, nu.snapshots, formulation = "kirchhoff") + formulations = ["kirchhoff", "angles", "cycles"] #"ptdf" + for formulation in formulations: + nu_copy = copy.deepcopy(nu) + build_model( nu, nu.snapshots, formulation = formulation) # build_model( nu, nu.snapshots, formulation = "angles") # nu.lopf( nu.snapshots, formulation = "kirchhoff") - - nu.generators_t.status - - nu.generators_t.p - - ### Minimum up time demonstration - # - #Gas has minimum up time, forcing it to be online longer - - nu = pypsa.Network() - - nu.set_snapshots( snapshots) - nu.add("Bus","bus") - - nu.add("Generator","coal",bus="bus", - committable=True, - p_min_pu=0.3, - marginal_cost=20, - p_nom=10000) - - nu.add("Generator","gas",bus="bus", - committable=True, - marginal_cost=70, - p_min_pu=0.1, - initial_status=0, - min_up_time=3, - p_nom=1000) - - nu.add("Load","load",bus="bus",p_set= p_set )#[4000,800,5000,3000]) - - build_model( nu, nu.snapshots, formulation = "kirchhoff") From 4169642ed7ba83f7bd61bb36e94efe5b45d9c94f Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Thu, 11 Jan 2018 10:01:36 -0500 Subject: [PATCH 051/135] calculate effective x_pu/r_pu in calculate_dependent_values --- pypsa/pf.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pypsa/pf.py b/pypsa/pf.py index bdf070070..60a69e7af 100644 --- a/pypsa/pf.py +++ b/pypsa/pf.py @@ -519,12 +519,17 @@ def calculate_dependent_values(network): network.lines["r_pu"] = network.lines.r/(network.lines.v_nom**2) network.lines["b_pu"] = network.lines.b*network.lines.v_nom**2 network.lines["g_pu"] = network.lines.g*network.lines.v_nom**2 + network.lines["x_pu_eff"] = network.lines["x_pu"] + network.lines["r_pu_eff"] = network.lines["r_pu"] + #convert transformer impedances from base power s_nom to base = 1 MVA network.transformers["x_pu"] = network.transformers.x/network.transformers.s_nom network.transformers["r_pu"] = network.transformers.r/network.transformers.s_nom network.transformers["b_pu"] = network.transformers.b*network.transformers.s_nom network.transformers["g_pu"] = network.transformers.g*network.transformers.s_nom + network.lines["x_pu_eff"] = network.transformers["x_pu"]* network.transformers["tap_ratio"] + network.lines["r_pu_eff"] = network.transformers["r_pu"]* network.transformers["tap_ratio"] apply_transformer_t_model(network) From 1dca3831de7cdf371a4621643c733f309dcf5d09 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Thu, 11 Jan 2018 10:03:01 -0500 Subject: [PATCH 052/135] use define_sub_network_cycle_constraints for cycles formulation and use r_pu_eff/x_pu_eff --- pypsa/opf.py | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index 214b6680f..6fdf967c8 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -682,8 +682,8 @@ def define_passive_branch_flows_with_angles(network,snapshots): bt = branch[0] bn = branch[1] sub = passive_branches.at[branch,"sub_network"] - attribute = "r_pu" if network.sub_networks.at[sub,"carrier"] == "DC" else "x_pu" - y = 1/(passive_branches.at[branch,attribute]*(passive_branches.at[branch,"tap_ratio"] if bt == "Transformer" else 1.)) + attribute = "r_pu_eff" if network.sub_networks.at[sub,"carrier"] == "DC" else "x_pu_eff" + y = 1/ passive_branches.at[ branch, attribute] for sn in snapshots: lhs = LExpression([(y,network.model.voltage_angles[bus0,sn]), (-y,network.model.voltage_angles[bus1,sn]), @@ -750,22 +750,16 @@ def define_passive_branch_flows_with_cycles(network,snapshots): cycle_index = [] cycle_constraints = {} - for sn in network.sub_networks.obj: - - branches = sn.branches() - attribute = "r_pu" if network.sub_networks.at[sn.name,"carrier"] == "DC" else "x_pu" - - for j in range(sn.C.shape[1]): + for subnetwork in network.sub_networks.obj: + branches = subnetwork.branches() + attribute = "r_pu_eff" if network.sub_networks.at[subnetwork.name,"carrier"] == "DC" else "x_pu_eff" - cycle_is = sn.C[:,j].nonzero()[0] - cycle_index.append((sn.name, j)) + sub_network_cycle_index, sub_network_cycle_constraints = define_sub_network_cycle_constraints( subnetwork, + snapshots, + network.model.passive_branch_p, attribute) - for snapshot in snapshots: - lhs = LExpression([(branches.at[branches.index[i],attribute]* - (branches.at[branches.index[i],"tap_ratio"] if branches.index[i][0] == "Transformer" else 1.)*sn.C[i,j], - network.model.passive_branch_p[branches.index[i][0],branches.index[i][1],snapshot]) - for i in cycle_is]) - cycle_constraints[sn.name,j,snapshot] = LConstraint(lhs,"==",LExpression()) + cycle_index.extend( sub_network_cycle_index) + cycle_constraints.update( sub_network_cycle_constraints) l_constraint(network.model, "cycle_constraints", cycle_constraints, cycle_index, snapshots) @@ -775,22 +769,22 @@ def define_passive_branch_flows_with_cycles(network,snapshots): flows = {} - for sn in network.sub_networks.obj: - branches = sn.branches() - buses = sn.buses() + for subnetwork in network.sub_networks.obj: + branches = subnetwork.branches() + buses = subnetwork.buses() for i,branch in enumerate(branches.index): bt = branch[0] bn = branch[1] - cycle_is = sn.C[i,:].nonzero()[1] - tree_is = sn.T[i,:].nonzero()[1] + cycle_is = subnetwork.C[i,:].nonzero()[1] + tree_is = subnetwork.T[i,:].nonzero()[1] if len(cycle_is) + len(tree_is) == 0: logger.error("The cycle formulation does not support infinite impedances, yet.") for snapshot in snapshots: - expr = LExpression([(sn.C[i,j], network.model.cycles[sn.name,j,snapshot]) + expr = LExpression([(subnetwork.C[i,j], network.model.cycles[subnetwork.name,j,snapshot]) for j in cycle_is]) - lhs = expr + sum(sn.T[i,j]*network._p_balance[buses.index[j],snapshot] + lhs = expr + sum(subnetwork.T[i,j]*network._p_balance[buses.index[j],snapshot] for j in tree_is) rhs = LExpression([(1,network.model.passive_branch_p[bt,bn,snapshot])]) @@ -860,9 +854,7 @@ def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False for subnetwork in network.sub_networks.obj: branches = subnetwork.branches() - attribute = "r_pu" if network.sub_networks.at[subnetwork.name,"carrier"] == "DC" else "x_pu" - - csc_rep = subnetwork.C.tocoo() + attribute = "r_pu_eff" if network.sub_networks.at[subnetwork.name,"carrier"] == "DC" else "x_pu_eff" sub_network_cycle_index, sub_network_cycle_constraints = define_sub_network_cycle_constraints( subnetwork, snapshots, From e2abea7b464f02af315b1e11a9921b7d4c4464fd Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Thu, 11 Jan 2018 10:03:22 -0500 Subject: [PATCH 053/135] move function above for clarity --- pypsa/opf.py | 68 +++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index 6fdf967c8..04b73b1e2 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -729,6 +729,39 @@ def define_passive_branch_flows_with_PTDF(network,snapshots,ptdf_tolerance=0.): list(passive_branches.index), snapshots) +def define_sub_network_cycle_constraints( subnetwork, snapshots, passive_branch_p, attribute): + """ Constructs cycle_constraints for a particular subnetwork + """ + + sub_network_cycle_constraints = {} + sub_network_cycle_index = [] + + matrix = subnetwork.C.tocsr() + branches = subnetwork.branches() + + for col_j in range( matrix.shape[1] ): + cycle_is = matrix.getcol(col_j).nonzero()[0] + + if len(cycle_is) == 0: continue + + sub_network_cycle_index.append((subnetwork.name, col_j)) + + + branch_idx_attributes = [] + + for cycle_i in cycle_is: + branch_idx = branches.index[cycle_i] + attribute_value = branches.at[ branch_idx, attribute] *subnetwork.C[ cycle_i, col_j] + branch_idx_attributes.append( (branch_idx, attribute_value)) + + for snapshot in snapshots: + expression_list = [ (attribute_value, + passive_branch_p[branch_idx[0], branch_idx[1], snapshot]) for (branch_idx, attribute_value) in branch_idx_attributes] + + lhs = LExpression(expression_list) + sub_network_cycle_constraints[subnetwork.name,col_j,snapshot] = LConstraint(lhs,"==",LExpression()) + + return( sub_network_cycle_index, sub_network_cycle_constraints) def define_passive_branch_flows_with_cycles(network,snapshots): @@ -795,41 +828,6 @@ def define_passive_branch_flows_with_cycles(network,snapshots): list(passive_branches.index), snapshots) -def define_sub_network_cycle_constraints( subnetwork, snapshots, passive_branch_p, attribute): - """ Constructs cycle_constraints for a particular subnetwork - """ - - sub_network_cycle_constraints = {} - sub_network_cycle_index = [] - - matrix = subnetwork.C.tocsr() - branches = subnetwork.branches() - - for col_j in range( matrix.shape[1] ): - cycle_is = matrix.getcol(col_j).nonzero()[0] - - if len(cycle_is) == 0: continue - - sub_network_cycle_index.append((subnetwork.name, col_j)) - - - branch_idx_attributes = [] - - for cycle_i in cycle_is: - branch_idx = branches.index[cycle_i] - attribute_value = branches.at[branch_idx,attribute]*(branches.at[branch_idx,"tap_ratio"] \ - if branch_idx[0] == "Transformer" else 1.)*subnetwork.C[cycle_i, col_j] - - branch_idx_attributes.append( (branch_idx, attribute_value)) - - for snapshot in snapshots: - expression_list = [ (attribute_value, - passive_branch_p[branch_idx[0], branch_idx[1], snapshot]) for (branch_idx, attribute_value) in branch_idx_attributes] - - lhs = LExpression(expression_list) - sub_network_cycle_constraints[subnetwork.name,col_j,snapshot] = LConstraint(lhs,"==",LExpression()) - - return( sub_network_cycle_index, sub_network_cycle_constraints) def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False): """ define passive branch flows with the kirchoff method """ From 42d96b32cf97ac297e2a285978bf25f56c805a9c Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Thu, 11 Jan 2018 15:56:26 +0000 Subject: [PATCH 054/135] fix typo --- pypsa/pf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pypsa/pf.py b/pypsa/pf.py index 60a69e7af..7c477b588 100644 --- a/pypsa/pf.py +++ b/pypsa/pf.py @@ -528,8 +528,8 @@ def calculate_dependent_values(network): network.transformers["r_pu"] = network.transformers.r/network.transformers.s_nom network.transformers["b_pu"] = network.transformers.b*network.transformers.s_nom network.transformers["g_pu"] = network.transformers.g*network.transformers.s_nom - network.lines["x_pu_eff"] = network.transformers["x_pu"]* network.transformers["tap_ratio"] - network.lines["r_pu_eff"] = network.transformers["r_pu"]* network.transformers["tap_ratio"] + network.transformers["x_pu_eff"] = network.transformers["x_pu"]* network.transformers["tap_ratio"] + network.transformers["r_pu_eff"] = network.transformers["r_pu"]* network.transformers["tap_ratio"] apply_transformer_t_model(network) From 3286c720dd8760b04151b833b31269e054735177 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Thu, 11 Jan 2018 17:27:55 -0500 Subject: [PATCH 055/135] use r_pu_eff and x_pu_eff --- pypsa/pf.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pypsa/pf.py b/pypsa/pf.py index 7c477b588..3e4360d07 100644 --- a/pypsa/pf.py +++ b/pypsa/pf.py @@ -619,15 +619,14 @@ def calculate_B_H(sub_network,skip_pre=False): find_bus_controls(sub_network) if network.sub_networks.at[sub_network.name,"carrier"] == "DC": - attribute="r_pu" + attribute="r_pu_eff" else: - attribute="x_pu" + attribute="x_pu_eff" #following leans heavily on pypower.makeBdc #susceptances - b = 1./np.concatenate([(c.df.loc[c.ind, attribute]*c.df.loc[c.ind, "tap_ratio"]).values if c.name == "Transformer" - else c.df.loc[c.ind, attribute].values + b = 1./np.concatenate([(c.df.loc[c.ind, attribute]).values \ for c in sub_network.iterate_components(passive_branch_components)]) From 1f4ae38f616c0e9ed5730b7c8d30ea9363bf0903 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Thu, 11 Jan 2018 23:38:06 +0100 Subject: [PATCH 056/135] travis: Switch off coveralls on travis until coverage testing works properly --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7f6deed33..72ed83955 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ install: - conda env update -q -n pypsa --file=requirements.yml - conda env update -q -n pypsa --file=requirements_dev.yml - source activate pypsa - - conda install -q -c conda-forge python-coveralls # don't install on appveyor + # - conda install -q -c conda-forge python-coveralls # don't install on appveyor - pip install --no-cache-dir . # before_script: # configure a headless display to test plot generation @@ -40,5 +40,5 @@ install: script: "make test" -after_success: - - coveralls +# after_success: +# - coveralls From 437a9359aaeb603c8485f49c06c3e14839676950 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Fri, 12 Jan 2018 09:05:15 -0500 Subject: [PATCH 057/135] using csc instead of csr --- pypsa/opf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index 04b73b1e2..6ef07a5cb 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -736,7 +736,7 @@ def define_sub_network_cycle_constraints( subnetwork, snapshots, passive_branch_ sub_network_cycle_constraints = {} sub_network_cycle_index = [] - matrix = subnetwork.C.tocsr() + matrix = subnetwork.C.tocsc() branches = subnetwork.branches() for col_j in range( matrix.shape[1] ): From 2557211c92221d355ac9df1f7d8f8b7372879abc Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Wed, 17 Jan 2018 11:29:17 +0100 Subject: [PATCH 058/135] doc, website: add PyPSA paper reference, update year to 2018 --- README.rst | 17 ++++++++++------- doc/comparable_software.rst | 2 +- doc/introduction.rst | 15 +++++++-------- website/index.org | 12 ++++++------ website/publications/index.org | 15 +++++++++------ 5 files changed, 33 insertions(+), 28 deletions(-) diff --git a/README.rst b/README.rst index 7f4eefa45..571afd434 100644 --- a/README.rst +++ b/README.rst @@ -24,7 +24,7 @@ and solar generation, storage units, sector coupling and mixed alternating and direct current networks. PyPSA is designed to scale well with large networks and long time series. -As of 2017 PyPSA is under heavy development and therefore it is +As of 2018 PyPSA is under heavy development and therefore it is recommended to use caution when using it in a production environment. Some APIs may change - the changes in each PyPSA version are listed in the `doc/release_notes.rst `_. @@ -187,13 +187,16 @@ Citing PyPSA If you use PyPSA for your research, we would appreciate it if you -would cite the following preprint paper (which has been accepted to -the `Journal of Open Research Software -`_): +would cite the following paper: * T. Brown, J. Hörsch, D. Schlachtberger, `PyPSA: Python for Power - System Analysis `_, 2017, - `preprint arXiv:1707.09913 `_ + System Analysis `_, 2018, + `Journal of Open Research Software + `_, 6(1), + `arXiv:1707.09913 `_, + `DOI:10.5334/jors.188 `_ + + If you want to cite a specific PyPSA version, each release of PyPSA is stored on `Zenodo `_ with a release-specific DOI. @@ -206,7 +209,7 @@ This can be found linked from the overall PyPSA Zenodo DOI: Licence ======= -Copyright 2015-2017 Tom Brown (FIAS), Jonas Hörsch (FIAS), David +Copyright 2015-2018 Tom Brown (FIAS), Jonas Hörsch (FIAS), David Schlachtberger (FIAS) This program is free software: you can redistribute it and/or diff --git a/doc/comparable_software.rst b/doc/comparable_software.rst index 1429f7dd2..a46a2e319 100644 --- a/doc/comparable_software.rst +++ b/doc/comparable_software.rst @@ -2,7 +2,7 @@ Comparable Software ####################### -The `PyPSA preprint paper `_ +The `PyPSA paper `_ contains a discussion of PyPSA's features compared to other software tools and a functionality comparison in Table III. diff --git a/doc/introduction.rst b/doc/introduction.rst index ab65a644f..911ec5751 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -13,7 +13,7 @@ alternating and direct current networks. PyPSA is designed to scale well with large networks and long time series. -As of 2017 PyPSA is under heavy development and therefore it is +As of 2018 PyPSA is under heavy development and therefore it is recommended to use caution when using it in a production environment. Some APIs may change - the changes in each PyPSA version are listed in the :doc:`release_notes`. @@ -187,16 +187,15 @@ PyPSA has a Google Group `forum / mailing list Citing PyPSA ============ - - If you use PyPSA for your research, we would appreciate it if you -would cite the following preprint paper (which has been accepted to -the `Journal of Open Research Software -`_): +would cite the following paper: * T. Brown, J. Hörsch, D. Schlachtberger, `PyPSA: Python for Power - System Analysis `_, 2017, - `preprint arXiv:1707.09913 `_ + System Analysis `_, 2018, + `Journal of Open Research Software + `_, 6(1), + `arXiv:1707.09913 `_, + `DOI:10.5334/jors.188 `_ If you want to cite a specific PyPSA version, each release of PyPSA is diff --git a/website/index.org b/website/index.org index fd1c286a1..65c27e839 100644 --- a/website/index.org +++ b/website/index.org @@ -12,7 +12,7 @@ units, sector coupling and mixed alternating and direct current networks. PyPSA is designed to scale well with large networks and long time series. -As of 2017 PyPSA is under heavy development and therefore it is +As of 2018 PyPSA is under heavy development and therefore it is recommended to use caution when using it in a production environment. Some APIs may change - the changes in each PyPSA version are listed in the [[./doc/release_notes.html][release notes]]. @@ -176,13 +176,13 @@ PyPSA has a Google Group [[https://groups.google.com/group/pypsa][forum * Citing PyPSA -If you use PyPSA for your research, we would appreciate it if you -would cite the following preprint paper (which has been accepted to -the [[https://openresearchsoftware.metajnl.com/][Journal of Open Research Software]]): - -- T. Brown, J. H\ouml{}rsch, D. Schlachtberger, [[https://arxiv.org/abs/1707.09913][PyPSA: Python for Power System Analysis]], 2017, [[https://arxiv.org/abs/1707.09913][preprint arXiv:1707.09913]] +If you use PyPSA for your research, we would appreciate it if you +would cite the following paper: +- T. Brown, J. H\ouml{}rsch, D. Schlachtberger, [[https://arxiv.org/abs/1707.09913][PyPSA: Python for + Power System Analysis]], 2018, [[https://openresearchsoftware.metajnl.com/][Journal of Open Research Software]], 6(1), + [[https://arxiv.org/abs/1707.09913][arXiv:1707.09913]], [[https://doi.org/10.5334/jors.188][DOI: 10.5334/jors.188]] If you want to cite a specific PyPSA version, each release of PyPSA is diff --git a/website/publications/index.org b/website/publications/index.org index 133edca82..ff1d81b36 100644 --- a/website/publications/index.org +++ b/website/publications/index.org @@ -1,13 +1,15 @@ #+TITLE: Python for Power System Analysis: Publications #+OPTIONS: toc:nil no default TOC -There is a preprint paper describing PyPSA (which has been accepted -to the [[https://openresearchsoftware.metajnl.com/][Journal of Open Research Software]]): -- T. Brown, J. H\ouml{}rsch, D. Schlachtberger, [[https://arxiv.org/abs/1707.09913][PyPSA: Python for Power System Analysis]], 2017, [[https://arxiv.org/abs/1707.09913][preprint arXiv:1707.09913]] + If you use PyPSA for your research, we would appreciate it if you -would cite this preprint paper. +would cite the following paper: + +- T. Brown, J. H\ouml{}rsch, D. Schlachtberger, [[https://arxiv.org/abs/1707.09913][PyPSA: Python for + Power System Analysis]], 2018, [[https://openresearchsoftware.metajnl.com/][Journal of Open Research Software]], 6(1), + [[https://arxiv.org/abs/1707.09913][arXiv:1707.09913]], [[https://doi.org/10.5334/jors.188][DOI: 10.5334/jors.188]] If you want to cite a specific PyPSA version, each release of PyPSA is stored on [[https://zenodo.org/][Zenodo]] with a release-specific DOI. This can be found @@ -17,11 +19,12 @@ linked from the overall PyPSA Zenodo DOI: The following research papers have used PyPSA: +- T. Brown, D. Schlachtberger, A. Kies, S. Schramm, M. Greiner, [[https://arxiv.org/abs/1801.05290][Synergies of sector coupling and transmission extension in a cost-optimised, highly renewable European energy system]], 2017, [[https://arxiv.org/abs/1801.05290][preprint arXiv:1801.05290]], [[https://zenodo.org/record/1146665][all input data, code and output data on Zenodo]] - J. H\ouml{}rsch, Joanne Calitz, [[https://arxiv.org/abs/1710.11199][PyPSA-ZA: Investment and operation co-optimization of integrating wind and solar in South Africa at high spatial and temporal detail]], presented at the [[http://windac-africa.com/][WindAc Africa Conference]], 2017, [[https://arxiv.org/abs/1710.11199][preprint]], [[https://github.com/FRESNA/pypsa-za][code]] - Markus Groissb\ouml{}ck, Alexandre Gusmao, [[https://arxiv.org/abs/1709.03761][Impact of high renewable penetration scenarios on system reliability: two case studies in the Kingdom of Saudi Arabia]], 2017, [[https://arxiv.org/abs/1709.03761][preprint]] - J. H\ouml{}rsch, T. Brown, [[https://doi.org/10.1109/EEM.2017.7982024][The role of spatial scale in joint optimisations of generation and transmission for European highly renewable scenarios]], 2017, accepted to [[http://eem2017.com/][14th International Conference on the European Energy Market - EEM 2017]], [[https://arxiv.org/abs/1705.07617][preprint]] -- D. Schlachtberger, T. Brown, S. Schramm, M. Greiner, [[https://doi.org/10.1016/j.energy.2017.06.004][The Benefits of Cooperation in a Highly Renewable European Electricity Network]], Energy, Volume 134, 1 September 2017, Pages 469-481, [[https://arxiv.org/abs/1704.05492][preprint]], [[https://doi.org/10.5281/zenodo.804337][all input data, code, output data on Zenodo]] -- J. H\ouml{}rsch, H. Ronellenfitsch, D. Witthaut, T. Brown, [[https://arxiv.org/abs/1704.01881][Linear Optimal Power Flow Using Cycle Flows]], 2017, [[https://arxiv.org/abs/1704.01881][preprint]] +- D. Schlachtberger, T. Brown, S. Schramm, M. Greiner, [[https://doi.org/10.1016/j.energy.2017.06.004][The Benefits of Cooperation in a Highly Renewable European Electricity Network]], Energy, Volume 134, 1 September 2017, Pages 469-481, [[https://arxiv.org/abs/1704.05492][postprint]], [[https://doi.org/10.5281/zenodo.804337][all input data, code, output data on Zenodo]] +- J. H\ouml{}rsch, H. Ronellenfitsch, D. Witthaut, T. Brown, [[https://arxiv.org/abs/1704.01881][Linear Optimal Power Flow Using Cycle Flows]], 2017, [[https://arxiv.org/abs/1704.01881][preprint]], accepted to [[https://www.journals.elsevier.com/electric-power-systems-research][Electric Power Systems Research]] - Joao Gorenstein Dedecca, Rudi A. Hakvoort, Paulien M. Herder, [[https://doi.org/10.1016/j.energy.2017.02.111][Transmission expansion simulation for the European Northern Seas offshore grid]], Energy, Volume 125, 15 April 2017, Pages 805-824 If you have written a paper or report using PyPSA, please send us the From 5ea2f0e15da7f6f1194f0ec31f5324c2ceab1ac7 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Fri, 19 Jan 2018 10:44:28 +0100 Subject: [PATCH 059/135] doc, website: BibTeX for citation of PyPSA paper --- README.rst | 15 +++++++++++++++ doc/introduction.rst | 17 +++++++++++++++++ website/index.org | 17 +++++++++++++++++ website/publications/index.org | 18 ++++++++++++++++++ 4 files changed, 67 insertions(+) diff --git a/README.rst b/README.rst index 571afd434..d7deb3209 100644 --- a/README.rst +++ b/README.rst @@ -197,6 +197,21 @@ would cite the following paper: `DOI:10.5334/jors.188 `_ +Please use the following BibTeX: :: + + @article{PyPSA, + author = {T. Brown and J. H\"orsch and D. Schlachtberger}, + title = {{PyPSA: Python for Power System Analysis}}, + journal = {Journal of Open Research Software}, + volume = {6}, + issue = {1}, + number = {4}, + year = {2018}, + eprint = {1707.09913}, + url = {https://doi.org/10.5334/jors.188}, + doi = {10.5334/jors.188} + } + If you want to cite a specific PyPSA version, each release of PyPSA is stored on `Zenodo `_ with a release-specific DOI. diff --git a/doc/introduction.rst b/doc/introduction.rst index 911ec5751..d2bedbee5 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -197,6 +197,23 @@ would cite the following paper: `arXiv:1707.09913 `_, `DOI:10.5334/jors.188 `_ +Please use the following BibTeX: :: + + @article{PyPSA, + author = {T. Brown and J. H\"orsch and D. Schlachtberger}, + title = {{PyPSA: Python for Power System Analysis}}, + journal = {Journal of Open Research Software}, + volume = {6}, + issue = {1}, + number = {4}, + year = {2018}, + eprint = {1707.09913}, + url = {https://doi.org/10.5334/jors.188}, + doi = {10.5334/jors.188} + } + + + If you want to cite a specific PyPSA version, each release of PyPSA is stored on `Zenodo `_ with a release-specific DOI. diff --git a/website/index.org b/website/index.org index 65c27e839..306f53f67 100644 --- a/website/index.org +++ b/website/index.org @@ -185,6 +185,23 @@ would cite the following paper: [[https://arxiv.org/abs/1707.09913][arXiv:1707.09913]], [[https://doi.org/10.5334/jors.188][DOI: 10.5334/jors.188]] +Please use the following BibTeX: + +#+BEGIN_SRC + @article{PyPSA, + author = {T. Brown and J. H\"orsch and D. Schlachtberger}, + title = {{PyPSA: Python for Power System Analysis}}, + journal = {Journal of Open Research Software}, + volume = {6}, + issue = {1}, + number = {4}, + year = {2018}, + eprint = {1707.09913}, + url = {https://doi.org/10.5334/jors.188}, + doi = {10.5334/jors.188} + } +#+END_SRC + If you want to cite a specific PyPSA version, each release of PyPSA is stored on [[https://zenodo.org/][Zenodo]] with a release-specific DOI. This can be found linked from the overall PyPSA Zenodo DOI: diff --git a/website/publications/index.org b/website/publications/index.org index ff1d81b36..0bfcebade 100644 --- a/website/publications/index.org +++ b/website/publications/index.org @@ -11,6 +11,24 @@ would cite the following paper: Power System Analysis]], 2018, [[https://openresearchsoftware.metajnl.com/][Journal of Open Research Software]], 6(1), [[https://arxiv.org/abs/1707.09913][arXiv:1707.09913]], [[https://doi.org/10.5334/jors.188][DOI: 10.5334/jors.188]] +Please use the following BibTeX: + +#+BEGIN_SRC + @article{PyPSA, + author = {T. Brown and J. H\"orsch and D. Schlachtberger}, + title = {{PyPSA: Python for Power System Analysis}}, + journal = {Journal of Open Research Software}, + volume = {6}, + issue = {1}, + number = {4}, + year = {2018}, + eprint = {1707.09913}, + url = {https://doi.org/10.5334/jors.188}, + doi = {10.5334/jors.188} + } +#+END_SRC + + If you want to cite a specific PyPSA version, each release of PyPSA is stored on [[https://zenodo.org/][Zenodo]] with a release-specific DOI. This can be found linked from the overall PyPSA Zenodo DOI: From 2e1b05ee5bdc81254bae1c1f8d9efee2bea96e89 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Fri, 19 Jan 2018 12:16:08 +0100 Subject: [PATCH 060/135] components: Allow the addition of new types of component Pass DataFrame new_components to Network on __init__. Also store the sets of types of component on the network itself. --- pypsa/components.csv | 22 +++++++++++----------- pypsa/components.py | 34 +++++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/pypsa/components.csv b/pypsa/components.csv index d82aa4cdf..17b53b8c9 100644 --- a/pypsa/components.csv +++ b/pypsa/components.csv @@ -1,16 +1,16 @@ -component,list_name,description +component,list_name,description,type Network,networks,"Container for all components and functions which act upon the whole network." SubNetwork,sub_networks,"Subsets of buses and passive branches (i.e. lines and transformers) that are connected (i.e. synchronous areas)." Bus,buses,"Electrically fundamental node where x-port objects attach." Carrier,carriers,"Energy carrier, such as AC, DC, heat, wind, PV or coal. Buses have direct carriers and Generators indicate their primary energy carriers. The Carrier can track properties relevant for global constraints, such as CO2 emissions." GlobalConstraint,global_constraints,"Constraints for OPF that affect many components, such as CO2 emission constraints." -Line,lines,"Lines include distribution and transmission lines, overhead lines and cables." -LineType,line_types,"Standard line types with per length values for impedances." -Transformer,transformers,"2-winding transformer." -TransformerType,transformer_types,"Standard 2-winding transformer types." -Link,links,"Link between two buses with controllable active power - can be used for a transport power flow model OR as a simplified version of point-to-point DC connection OR as a lossy energy converter. NB: for a lossless bi-directional HVDC or transport link, set p_min_pu = -1 and efficiency = 1. NB: It is assumed that the links neither produce nor consume reactive power." -Load,loads,"PQ power consumer." -Generator,generators,"Power generator." -StorageUnit,storage_units,"Storage unit with fixed nominal-energy-to-nominal-power ratio." -Store,stores,"Generic store, whose capacity may be optimised." -ShuntImpedance,shunt_impedances,"Shunt y = g + jb." +Line,lines,"Lines include distribution and transmission lines, overhead lines and cables.",passive_branch +LineType,line_types,"Standard line types with per length values for impedances.",standard_type +Transformer,transformers,"2-winding transformer.",passive_branch +TransformerType,transformer_types,"Standard 2-winding transformer types.",standard_type +Link,links,"Link between two buses with controllable active power - can be used for a transport power flow model OR as a simplified version of point-to-point DC connection OR as a lossy energy converter. NB: for a lossless bi-directional HVDC or transport link, set p_min_pu = -1 and efficiency = 1. NB: It is assumed that the links neither produce nor consume reactive power.",controllable_branch +Load,loads,"PQ power consumer.",controllable_one_port +Generator,generators,"Power generator.",controllable_one_port +StorageUnit,storage_units,"Storage unit with fixed nominal-energy-to-nominal-power ratio.",controllable_one_port +Store,stores,"Generic store, whose capacity may be optimised.",controllable_one_port +ShuntImpedance,shunt_impedances,"Shunt y = g + jb.",passive_one_port diff --git a/pypsa/components.py b/pypsa/components.py index 8511c69c4..064705d0b 100644 --- a/pypsa/components.py +++ b/pypsa/components.py @@ -134,6 +134,8 @@ class Network(Basic): If True, do not read in PyPSA standard types into standard types DataFrames. csv_folder_name : string Name of folder from which to import CSVs of network data. Overrides import_name. + new_components : pandas.DataFrame + DataFrame with index of component name and columns of list_name and description kwargs Any remaining attributes to set @@ -197,7 +199,9 @@ class Network(Basic): adjacency_matrix = adjacency_matrix - def __init__(self, import_name=None, name="", ignore_standard_types=False, **kwargs): + def __init__(self, import_name=None, name="", ignore_standard_types=False, + new_components=None, + **kwargs): if 'csv_folder_name' in kwargs: logger.warning("The argument csv_folder_name for initiating Network() is deprecated, please use import_name instead.") @@ -220,14 +224,34 @@ def __init__(self, import_name=None, name="", ignore_standard_types=False, **kwa "components.csv"), index_col=0) + components = components.reindex(components.columns|["attrs"],axis=1) + + if new_components is not None: + components = pd.concat((components, new_components)) + + for c_type in ["passive_one_port", "controllable_one_port", + "passive_branch", "controllable_branch", "standard_type"]: + setattr(self, c_type + "_components", + set(components.index[components.type == c_type])) + + self.one_port_components = self.passive_one_port_components|self.controllable_one_port_components + + self.branch_components = self.passive_branch_components|self.controllable_branch_components + + #i.e. everything except "Network" + self.all_components = self.branch_components|self.one_port_components|self.standard_type_components|{"Bus", "SubNetwork", "Carrier", "GlobalConstraint"} + self.components = Dict(components.T.to_dict()) for component in self.components.keys(): - file_name = os.path.join(dir_name, - component_attrs_dir_name, - self.components[component]["list_name"] + ".csv") - attrs = pd.read_csv(file_name, index_col=0, na_values="n/a") + if type(self.components[component]["attrs"]) is pd.DataFrame: + attrs = self.components[component]["attrs"] + else: + file_name = os.path.join(dir_name, + component_attrs_dir_name, + self.components[component]["list_name"] + ".csv") + attrs = pd.read_csv(file_name, index_col=0, na_values="n/a") attrs['static'] = (attrs['type'] != 'series') attrs['varying'] = attrs['type'].isin({'series', 'static or series'}) From 364ea170ebaf7cc24c98696b7f481527b4ba062e Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Fri, 19 Jan 2018 12:57:47 +0100 Subject: [PATCH 061/135] everywhere: Move component type sets to Network object All sets pypsa.components.*_components are now found at network.*_components so they can be dynamically altered by the user. --- examples/ac-dc-meshed/ac-dc-lopf.py | 2 +- examples/ac-dc-meshed/ac-dc-lpf.py | 2 +- examples/opf-storage-hvdc/opf-storage.py | 2 +- pypsa/components.py | 64 +++++++++--------------- pypsa/contingency.py | 4 +- pypsa/graph.py | 12 ++--- pypsa/io.py | 6 +-- pypsa/networkclustering.py | 2 +- pypsa/opf.py | 13 ++--- pypsa/opt.py | 3 +- pypsa/pf.py | 30 ++++------- test/test_pf_against_pypower.py | 2 +- 12 files changed, 56 insertions(+), 86 deletions(-) diff --git a/examples/ac-dc-meshed/ac-dc-lopf.py b/examples/ac-dc-meshed/ac-dc-lopf.py index 10cc49521..4d27ac7d3 100644 --- a/examples/ac-dc-meshed/ac-dc-lopf.py +++ b/examples/ac-dc-meshed/ac-dc-lopf.py @@ -44,7 +44,7 @@ p0 = 0. p1 = 0. - for c in network.iterate_components(pypsa.components.branch_components): + for c in network.iterate_components(network.branch_components): bs = (c.df.bus0 == bus) diff --git a/examples/ac-dc-meshed/ac-dc-lpf.py b/examples/ac-dc-meshed/ac-dc-lpf.py index f20fdc71c..56a1f9df9 100644 --- a/examples/ac-dc-meshed/ac-dc-lpf.py +++ b/examples/ac-dc-meshed/ac-dc-lpf.py @@ -53,7 +53,7 @@ p0 = 0. p1 = 0. - for c in network.iterate_components(pypsa.components.branch_components): + for c in network.iterate_components(network.branch_components): bs = (c.df.bus0 == bus) diff --git a/examples/opf-storage-hvdc/opf-storage.py b/examples/opf-storage-hvdc/opf-storage.py index c842e4dcc..d045156c0 100644 --- a/examples/opf-storage-hvdc/opf-storage.py +++ b/examples/opf-storage-hvdc/opf-storage.py @@ -105,7 +105,7 @@ p0 = 0. p1 = 0. - for c in network.iterate_components(pypsa.components.branch_components): + for c in network.iterate_components(network.branch_components): bs = (c.df.bus0 == bus) diff --git a/pypsa/components.py b/pypsa/components.py index 064705d0b..77094e0c5 100644 --- a/pypsa/components.py +++ b/pypsa/components.py @@ -290,7 +290,7 @@ def __init__(self, import_name=None, name="", ignore_standard_types=False, def _build_dataframes(self): """Function called when network is created to build component pandas.DataFrames.""" - for component in all_components: + for component in self.all_components: attrs = self.components[component]["attrs"] @@ -314,7 +314,7 @@ def _build_dataframes(self): def read_in_default_standard_types(self): - for std_type in standard_types: + for std_type in self.standard_type_components: list_name = self.components[std_type]["list_name"] @@ -383,7 +383,7 @@ def set_snapshots(self,snapshots): if isinstance(snapshots, pd.DatetimeIndex) and _pd_version < '0.18.0': snapshots = pd.Index(snapshots.values) - for component in all_components: + for component in self.all_components: pnl = self.pnl(component) attrs = self.components[component]["attrs"] @@ -615,10 +615,10 @@ def copy(self, with_time=True, ignore_standard_types=False): network = self.__class__(ignore_standard_types=ignore_standard_types) - for component in self.iterate_components(["Bus", "Carrier"] + sorted(all_components - {"Bus","Carrier"})): + for component in self.iterate_components(["Bus", "Carrier"] + sorted(self.all_components - {"Bus","Carrier"})): df = component.df #drop the standard types to avoid them being read in twice - if not ignore_standard_types and component.name in standard_types: + if not ignore_standard_types and component.name in self.standard_type_components: df = component.df.drop(network.components[component.name]["standard_types"].index) import_components_from_dataframe(network, df, component.name) @@ -676,24 +676,24 @@ def __getitem__(self, key): ) buses_i = n.buses.index - rest_components = all_components - standard_types - one_port_components - branch_components + rest_components = self.all_components - self.standard_type_components - self.one_port_components - self.branch_components for c in rest_components - {"Bus", "SubNetwork"}: n.import_components_from_dataframe(pd.DataFrame(self.df(c)), c) - for c in standard_types: + for c in self.standard_type_components: df = self.df(c).drop(self.components[c]["standard_types"].index) n.import_components_from_dataframe(pd.DataFrame(df), c) - for c in one_port_components: + for c in self.one_port_components: df = self.df(c).loc[lambda df: df.bus.isin(buses_i)] n.import_components_from_dataframe(pd.DataFrame(df), c) - for c in branch_components: + for c in self.branch_components: df = self.df(c).loc[lambda df: df.bus0.isin(buses_i) & df.bus1.isin(buses_i)] n.import_components_from_dataframe(pd.DataFrame(df), c) n.set_snapshots(self.snapshots[time_i]) - for c in all_components: + for c in self.all_components: i = n.df(c).index try: npnl = n.pnl(c) @@ -716,23 +716,23 @@ def __getitem__(self, key): #beware, this turns bools like s_nom_extendable into objects because of #presence of links without s_nom_extendable def branches(self): - return pd.concat((self.df(c) for c in branch_components), - keys=branch_components) + return pd.concat((self.df(c) for c in self.branch_components), + keys=self.branch_components) def passive_branches(self): - return pd.concat((self.df(c) for c in passive_branch_components), - keys=passive_branch_components) + return pd.concat((self.df(c) for c in self.passive_branch_components), + keys=self.passive_branch_components) def controllable_branches(self): - return pd.concat((self.df(c) for c in controllable_branch_components), - keys=controllable_branch_components) + return pd.concat((self.df(c) for c in self.controllable_branch_components), + keys=self.controllable_branch_components) def determine_network_topology(self): """ Build sub_networks from topology. """ - adjacency_matrix = self.adjacency_matrix(passive_branch_components) + adjacency_matrix = self.adjacency_matrix(self.passive_branch_components) n_components, labels = csgraph.connected_components(adjacency_matrix, directed=False) # remove all old sub_networks @@ -761,12 +761,12 @@ def determine_network_topology(self): self.buses.loc[:, "sub_network"] = labels.astype(str) - for c in self.iterate_components(passive_branch_components): + for c in self.iterate_components(self.passive_branch_components): c.df["sub_network"] = c.df.bus0.map(self.buses["sub_network"]) def iterate_components(self, components=None, skip_empty=True): if components is None: - components = all_components + components = self.all_components return (Component(name=c, list_name=self.components[c]["list_name"], @@ -790,13 +790,13 @@ def consistency_check(self): """ - for c in self.iterate_components(one_port_components): + for c in self.iterate_components(self.one_port_components): missing = c.df.index[~c.df.bus.isin(self.buses.index)] if len(missing) > 0: logger.warning("The following %s have buses which are not defined:\n%s", c.list_name, missing) - for c in self.iterate_components(branch_components): + for c in self.iterate_components(self.branch_components): for attr in ["bus0","bus1"]: missing = c.df.index[~c.df[attr].isin(self.buses.index)] if len(missing) > 0: @@ -804,7 +804,7 @@ def consistency_check(self): c.list_name, attr, missing) - for c in self.iterate_components(passive_branch_components): + for c in self.iterate_components(self.passive_branch_components): for attr in ["x","r"]: bad = c.df.index[c.df[attr] == 0.] if len(bad) > 0: @@ -824,7 +824,7 @@ def consistency_check(self): c.list_name, bad) - for c in self.iterate_components(all_components): + for c in self.iterate_components(self.all_components): for attr in c.attrs.index[c.attrs.varying & c.attrs.static]: attr_df = c.pnl[attr] @@ -845,7 +845,7 @@ def consistency_check(self): static_attrs = ['p_nom', 's_nom', 'e_nom'] varying_attrs = ['p_max_pu', 'e_max_pu'] - for c in self.iterate_components(all_components - {'TransformerType'}): + for c in self.iterate_components(self.all_components - {'TransformerType'}): varying_attr = c.attrs.index[c.attrs.varying].intersection(varying_attrs) static_attr = c.attrs.index[c.attrs.static].intersection(static_attrs) @@ -958,7 +958,7 @@ def transformers_i(self): def branches_i(self): types = [] names = [] - for c in self.iterate_components(passive_branch_components): + for c in self.iterate_components(self.network.passive_branch_components): types += len(c.ind) * [c.name] names += list(c.ind) return pd.MultiIndex.from_arrays([types, names], names=('type', 'name')) @@ -1008,17 +1008,3 @@ def iterate_components(self, components=None, skip_empty=True): c = Component(*c[:-1], ind=getattr(self, c.list_name + '_i')()) if not (skip_empty and len(c.ind) == 0): yield c - - -standard_types = {"LineType", "TransformerType"} - -passive_one_port_components = {"ShuntImpedance"} -controllable_one_port_components = {"Load", "Generator", "StorageUnit", "Store"} -one_port_components = passive_one_port_components|controllable_one_port_components - -passive_branch_components = {"Line", "Transformer"} -controllable_branch_components = {"Link"} -branch_components = passive_branch_components|controllable_branch_components - -#i.e. everything except "Network" -all_components = branch_components|one_port_components|standard_types|{"Bus", "SubNetwork", "Carrier", "GlobalConstraint"} diff --git a/pypsa/contingency.py b/pypsa/contingency.py index d2198a984..ba688a11a 100644 --- a/pypsa/contingency.py +++ b/pypsa/contingency.py @@ -103,8 +103,6 @@ def network_lpf_contingency(network, snapshots=None, branch_outages=None): """ - from .components import passive_branch_components - if snapshots is None: snapshots = network.snapshots @@ -126,7 +124,7 @@ def network_lpf_contingency(network, snapshots=None, branch_outages=None): p0_base = pd.Series(index=passive_branches.index) - for c in passive_branch_components: + for c in network.passive_branch_components: pnl = network.pnl(c) p0_base[c] = pnl.p0.loc[snapshot] diff --git a/pypsa/graph.py b/pypsa/graph.py index d56d134c0..fe4d3baf6 100644 --- a/pypsa/graph.py +++ b/pypsa/graph.py @@ -29,11 +29,11 @@ def graph(network, branch_components=None, weight=None): if isinstance(network, components.Network): if branch_components is None: - branch_components = components.branch_components + branch_components = network.branch_components buses_i = network.buses.index elif isinstance(network, components.SubNetwork): if branch_components is None: - branch_components = components.passive_branch_components + branch_components = network.network.passive_branch_components buses_i = network.buses_i() else: raise TypeError("build_graph must be called with a Network or a SubNetwork") @@ -85,12 +85,12 @@ def adjacency_matrix(network, branch_components=None, busorder=None, weights=Non if isinstance(network, components.Network): if branch_components is None: - branch_components = components.branch_components + branch_components = network.branch_components if busorder is None: busorder = network.buses.index elif isinstance(network, components.SubNetwork): if branch_components is None: - branch_components = components.passive_branch_components + branch_components = network.network.passive_branch_components if busorder is None: busorder = network.buses_i() else: @@ -146,12 +146,12 @@ def incidence_matrix(network, branch_components=None, busorder=None): if isinstance(network, components.Network): if branch_components is None: - branch_components = components.branch_components + branch_components = network.branch_components if busorder is None: busorder = network.buses.index elif isinstance(network, components.SubNetwork): if branch_components is None: - branch_components = components.passive_branch_components + branch_components = network.network.passive_branch_components if busorder is None: busorder = network.buses_i() else: diff --git a/pypsa/io.py b/pypsa/io.py index 5ae00909a..a1f773124 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -288,7 +288,7 @@ def _export_to_exporter(network, exporter, basename, export_standard_types=False exporter.save_snapshots(snapshots) exported_components = [] - for component in pypsa.components.all_components - {"SubNetwork"}: + for component in network.all_components - {"SubNetwork"}: list_name = network.components[component]["list_name"] attrs = network.components[component]["attrs"] @@ -296,7 +296,7 @@ def _export_to_exporter(network, exporter, basename, export_standard_types=False df = network.df(component) pnl = network.pnl(component) - if not export_standard_types and component in pypsa.components.standard_types: + if not export_standard_types and component in network.standard_type_components: df = df.drop(network.components[component]["standard_types"].index) # first do static attributes @@ -538,7 +538,7 @@ def _import_from_importer(network, importer, basename, skip_time=False): imported_components = [] # now read in other components; make sure buses and carriers come first - for component in ["Bus", "Carrier"] + sorted(pypsa.components.all_components - {"Bus", "Carrier", "SubNetwork"}): + for component in ["Bus", "Carrier"] + sorted(network.all_components - {"Bus", "Carrier", "SubNetwork"}): list_name = network.components[component]["list_name"] df = importer.get_static(list_name) diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index 0131851ef..3cb784298 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -220,7 +220,7 @@ def get_clustering_from_busmap(network, busmap, with_time=True, line_length_fact if with_time: network_c.set_snapshots(network.snapshots) - one_port_components = components.one_port_components.copy() + one_port_components = network.one_port_components.copy() if aggregate_generators_weighted: one_port_components.remove("Generator") diff --git a/pypsa/opf.py b/pypsa/opf.py index 6ef07a5cb..ddf401993 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -866,14 +866,12 @@ def define_passive_branch_flows_with_kirchhoff(network,snapshots,skip_vars=False def define_passive_branch_constraints(network,snapshots): - from .components import passive_branch_components - passive_branches = network.passive_branches() extendable_branches = passive_branches[passive_branches.s_nom_extendable] fixed_branches = passive_branches[~ passive_branches.s_nom_extendable] s_max_pu = pd.concat({c : get_switchable_as_dense(network,c,'s_max_pu') - for c in passive_branch_components}, axis=1) + for c in network.passive_branch_components}, axis=1) flow_upper = {(b[0],b[1],sn) : [[(1,network.model.passive_branch_p[b[0],b[1],sn])], "<=", s_max_pu.at[sn,b]*fixed_branches.at[b,"s_nom"]] @@ -1123,9 +1121,6 @@ def define_linear_objective(network,snapshots): def extract_optimisation_results(network, snapshots, formulation="angles"): - from .components import \ - passive_branch_components, branch_components, controllable_one_port_components - if isinstance(snapshots, pd.DatetimeIndex) and _pd_version < '0.18.0': # Work around pandas bug #12050 (https://github.com/pydata/pandas/issues/12050) snapshots = pd.Index(snapshots.values) @@ -1181,14 +1176,14 @@ def set_from_series(df, series): pd.concat({c.name: c.pnl.p.loc[snapshots].multiply(c.df.sign, axis=1) .groupby(c.df.bus, axis=1).sum() - for c in network.iterate_components(controllable_one_port_components)}) \ + for c in network.iterate_components(network.controllable_one_port_components)}) \ .sum(level=1) \ .reindex(columns=network.buses_t.p.columns, fill_value=0.) # passive branches passive_branches = as_series(model.passive_branch_p) - for c in network.iterate_components(passive_branch_components): + for c in network.iterate_components(network.passive_branch_components): set_from_series(c.pnl.p0, passive_branches.loc[c.name]) c.pnl.p1.loc[snapshots] = - c.pnl.p0.loc[snapshots] @@ -1262,7 +1257,7 @@ def set_from_series(df, series): s_nom_extendable_passive_branches = as_series(model.passive_branch_s_nom) - for c in network.iterate_components(passive_branch_components): + for c in network.iterate_components(network.passive_branch_components): c.df['s_nom_opt'] = c.df.s_nom if c.df.s_nom_extendable.any(): c.df.loc[c.df.s_nom_extendable, 's_nom_opt'] = s_nom_extendable_passive_branches.loc[c.name] diff --git a/pypsa/opt.py b/pypsa/opt.py index 0fe9998b3..fb1a4cc97 100644 --- a/pypsa/opt.py +++ b/pypsa/opt.py @@ -334,10 +334,9 @@ def empty_model(model): @contextmanager def empty_network(network): logger.debug("Storing pypsa timeseries to disk") - from .components import all_components panels = {} - for c in all_components: + for c in network.all_components: attr = network.components[c]["list_name"] + "_t" panels[attr] = getattr(network, attr) setattr(network, attr, None) diff --git a/pypsa/pf.py b/pypsa/pf.py index 3e4360d07..fc2a4cf18 100644 --- a/pypsa/pf.py +++ b/pypsa/pf.py @@ -208,8 +208,6 @@ def sub_network_pf(sub_network, snapshots=None, skip_pre=False, x_tol=1e-6, use_ # _sub_network_prepare_pf(sub_network, snapshots, skip_pre, calculate_Y) network = sub_network.network - from .components import passive_branch_components, controllable_branch_components, controllable_one_port_components - if not skip_pre: calculate_dependent_values(network) find_bus_controls(sub_network) @@ -225,7 +223,7 @@ def sub_network_pf(sub_network, snapshots=None, skip_pre=False, x_tol=1e-6, use_ for n in ("q", "p"): # allow all one ports to dispatch as set - for c in sub_network.iterate_components(controllable_one_port_components): + for c in sub_network.iterate_components(network.controllable_one_port_components): c_n_set = get_switchable_as_dense(network, c.name, n + '_set', snapshots, c.ind) c.pnl[n].loc[snapshots, c.ind] = c_n_set @@ -234,13 +232,13 @@ def sub_network_pf(sub_network, snapshots=None, skip_pre=False, x_tol=1e-6, use_ sum([((c.pnl[n].loc[snapshots, c.ind] * c.df.loc[c.ind, 'sign']) .groupby(c.df.loc[c.ind, 'bus'], axis=1).sum() .reindex(columns=buses_o, fill_value=0.)) - for c in sub_network.iterate_components(controllable_one_port_components)]) + for c in sub_network.iterate_components(network.controllable_one_port_components)]) if n == "p": network.buses_t[n].loc[snapshots, buses_o] += sum( [(- c.pnl[n+str(i)].loc[snapshots].groupby(c.df["bus"+str(i)], axis=1).sum() .reindex(columns=buses_o, fill_value=0)) - for c in network.iterate_components(controllable_branch_components) + for c in network.iterate_components(network.controllable_branch_components) for i in [0,1]]) def f(guess): @@ -338,7 +336,7 @@ def dfdx(guess): #add voltages to branches buses_indexer = buses_o.get_indexer branch_bus0 = []; branch_bus1 = [] - for c in sub_network.iterate_components(passive_branch_components): + for c in sub_network.iterate_components(network.passive_branch_components): branch_bus0 += list(c.df.loc[c.ind, 'bus0']) branch_bus1 += list(c.df.loc[c.ind, 'bus1']) v0 = V[:,buses_indexer(branch_bus0)] @@ -352,7 +350,7 @@ def dfdx(guess): s0 = pd.DataFrame(v0*np.conj(i0), columns=branches_i, index=snapshots) s1 = pd.DataFrame(v1*np.conj(i1), columns=branches_i, index=snapshots) - for c in sub_network.iterate_components(passive_branch_components): + for c in sub_network.iterate_components(network.passive_branch_components): s0t = s0.loc[:,c.name] s1t = s1.loc[:,c.name] c.pnl.p0.loc[snapshots,s0t.columns] = s0t.values.real @@ -610,8 +608,6 @@ def find_bus_controls(sub_network): def calculate_B_H(sub_network,skip_pre=False): """Calculate B and H matrices for AC or DC sub-networks.""" - from .components import passive_branch_components - network = sub_network.network if not skip_pre: @@ -627,7 +623,7 @@ def calculate_B_H(sub_network,skip_pre=False): #susceptances b = 1./np.concatenate([(c.df.loc[c.ind, attribute]).values \ - for c in sub_network.iterate_components(passive_branch_components)]) + for c in sub_network.iterate_components(network.passive_branch_components)]) if np.isnan(b).any(): @@ -645,7 +641,7 @@ def calculate_B_H(sub_network,skip_pre=False): sub_network.p_branch_shift = -b*np.concatenate([(c.df.loc[c.ind, "phase_shift"]).values*np.pi/180. if c.name == "Transformer" else np.zeros((len(c.ind),)) - for c in sub_network.iterate_components(passive_branch_components)]) + for c in sub_network.iterate_components(network.passive_branch_components)]) sub_network.p_bus_shift = sub_network.K * sub_network.p_branch_shift @@ -916,10 +912,6 @@ def sub_network_lpf(sub_network, snapshots=None, skip_pre=False): logger.info("Performing linear load-flow on %s sub-network %s for snapshot(s) %s", sub_network.network.sub_networks.at[sub_network.name,"carrier"], sub_network, snapshots) - from .components import \ - one_port_components, controllable_one_port_components, \ - passive_branch_components, controllable_branch_components - network = sub_network.network @@ -939,7 +931,7 @@ def sub_network_lpf(sub_network, snapshots=None, skip_pre=False): network.shunt_impedances.g_pu.loc[shunt_impedances_i].values # allow all one ports to dispatch as set - for c in sub_network.iterate_components(controllable_one_port_components): + for c in sub_network.iterate_components(network.controllable_one_port_components): c_p_set = get_switchable_as_dense(network, c.name, 'p_set', snapshots, c.ind) c.pnl.p.loc[snapshots, c.ind] = c_p_set @@ -948,11 +940,11 @@ def sub_network_lpf(sub_network, snapshots=None, skip_pre=False): sum([((c.pnl.p.loc[snapshots, c.ind] * c.df.loc[c.ind, 'sign']) .groupby(c.df.loc[c.ind, 'bus'], axis=1).sum() .reindex(columns=buses_o, fill_value=0.)) - for c in sub_network.iterate_components(one_port_components)] + for c in sub_network.iterate_components(network.one_port_components)] + [(- c.pnl["p"+str(i)].loc[snapshots].groupby(c.df["bus"+str(i)], axis=1).sum() .reindex(columns=buses_o, fill_value=0)) - for c in network.iterate_components(controllable_branch_components) + for c in network.iterate_components(network.controllable_branch_components) for i in [0,1]]) if not skip_pre and len(branches_i) > 0: @@ -965,7 +957,7 @@ def sub_network_lpf(sub_network, snapshots=None, skip_pre=False): flows = pd.DataFrame(v_diff * sub_network.H.T, columns=branches_i, index=snapshots) + sub_network.p_branch_shift - for c in sub_network.iterate_components(passive_branch_components): + for c in sub_network.iterate_components(network.passive_branch_components): f = flows.loc[:, c.name] c.pnl.p0.loc[snapshots, f.columns] = f c.pnl.p1.loc[snapshots, f.columns] = -f diff --git a/test/test_pf_against_pypower.py b/test/test_pf_against_pypower.py index a92c9ca00..f6ebc32e2 100644 --- a/test/test_pf_against_pypower.py +++ b/test/test_pf_against_pypower.py @@ -55,7 +55,7 @@ def test_pypower_case(): network.pf() #compare branch flows - for c in network.iterate_components(pypsa.components.passive_branch_components): + for c in network.iterate_components(network.passive_branch_components): for si in ["p0","p1","q0","q1"]: si_pypsa = getattr(c.pnl,si).loc["now"].values si_pypower = results_df['branch'][si][c.df.original_index].values From 39e54183401bfabac1f6a11258ae8cdb52ca625f Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Fri, 19 Jan 2018 17:14:31 +0100 Subject: [PATCH 062/135] components: Establish component types based on those available --- pypsa/components.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pypsa/components.py b/pypsa/components.py index 77094e0c5..d992dc69a 100644 --- a/pypsa/components.py +++ b/pypsa/components.py @@ -229,8 +229,7 @@ def __init__(self, import_name=None, name="", ignore_standard_types=False, if new_components is not None: components = pd.concat((components, new_components)) - for c_type in ["passive_one_port", "controllable_one_port", - "passive_branch", "controllable_branch", "standard_type"]: + for c_type in set(components.type.unique()) - {np.nan}: setattr(self, c_type + "_components", set(components.index[components.type == c_type])) From fabb05c14de6c92740114d6148da644270ec2161 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Fri, 19 Jan 2018 17:37:47 +0100 Subject: [PATCH 063/135] examples: add examples for adding new components Also build network.all_components more directly. --- .../new_components/add_components_library.py | 23 ++++++++++ .../new_components/add_components_simple.py | 46 +++++++++++++++++++ examples/new_components/component_library.py | 44 ++++++++++++++++++ pypsa/components.py | 3 +- 4 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 examples/new_components/add_components_library.py create mode 100644 examples/new_components/add_components_simple.py create mode 100644 examples/new_components/component_library.py diff --git a/examples/new_components/add_components_library.py b/examples/new_components/add_components_library.py new file mode 100644 index 000000000..6845a8a0d --- /dev/null +++ b/examples/new_components/add_components_library.py @@ -0,0 +1,23 @@ + +#Use the component_library to add components. + +#NB: This only works with Python 3 because of super() in component_library + + +import component_library + +n = component_library.Network(csv_folder_name="../ac-dc-meshed/ac-dc-data/") + +n.add("Bus","Aarhus") + +n.add("CHP","My CHP",bus="Aarhus") + + +print("\nn.chps:\n") +print(n.chps) + +print("\nn.chps_t.p_set:\n") +print(n.chps_t.p_set) + + +n.lopf() diff --git a/examples/new_components/add_components_simple.py b/examples/new_components/add_components_simple.py new file mode 100644 index 000000000..e48ee04db --- /dev/null +++ b/examples/new_components/add_components_simple.py @@ -0,0 +1,46 @@ + + +#In this example we add a new component type "dynamically" by passing +#the pypsa.Network the required information. + +import pypsa, pandas as pd + +#create a pandas.DataFrame with the properties of the new components +#the format should be the same as pypsa/components.csv, except add a column "attrs" +new_components = pd.DataFrame(data = [["chps","Combined heat and power plant.","controllable_one_port",None], + ["methanations","Methanation plant.","controllable_one_port",None]], + index = ["CHP","Methanation"], + columns = ["list_name","description","type","attrs"]) +print("\nNew components:\n") +print(new_components) + +#now attach pandas.DataFrames for the attributes +#the format should be the same as pypsa/component_attrs/*.csv + +chp_attrs = pd.DataFrame(data = [["string","n/a","n/a","Unique name","Input (required)"], + ["string","n/a","n/a","name of bus to which generator is attached","Input (required)"], + ["static or series","MW",0.,"active power set point (for PF)","Input (optional)"]], + index = ["name","bus","p_set"], + columns = ["type","unit","default","description","status"]) + +print("\nComponent attributes:\n") +print(chp_attrs) + +new_components.at["CHP","attrs"] = chp_attrs +new_components.at["Methanation","attrs"] = chp_attrs + +#pass Network the information +n = pypsa.Network(new_components=new_components) + +n.set_snapshots(range(4)) + +n.add("Bus","Aarhus") +n.add("CHP","My CHP", + bus="Aarhus", + p_set=list(range(5,1,-1))) + +print("\nn.chps:\n") +print(n.chps) + +print("\nn.chps_t.p_set:\n") +print(n.chps_t.p_set) diff --git a/examples/new_components/component_library.py b/examples/new_components/component_library.py new file mode 100644 index 000000000..418006277 --- /dev/null +++ b/examples/new_components/component_library.py @@ -0,0 +1,44 @@ + +#Create a library with its own Network class that has new components +#and a new LOPF function + +#NB: This only works with Python 3 because of super() + +import pypsa, pandas as pd + + +new_components = pd.DataFrame(data = [["chps","Combined heat and power plant.","new",None], + ["methanations","Methanation plant.","new",None]], + index = ["CHP","Methanation"], + columns = ["list_name","description","type","attrs"]) + +chp_attrs = pd.DataFrame(data = [["string","n/a","n/a","Unique name","Input (required)"], + ["string","n/a","n/a","name of bus to which generator is attached","Input (required)"], + ["float","n/a",1.,"power sign","Input (optional)"], + ["static or series","MW",0.,"active power set point (for PF)","Input (optional)"], + ["series","MW",0.,"active power at bus (positive if net generation)","Output"]], + index = ["name","bus","sign","p_set","p"], + columns = ["type","unit","default","description","status"]) + + +new_components.at["CHP","attrs"] = chp_attrs +new_components.at["Methanation","attrs"] = chp_attrs + +class Network(pypsa.Network): + + def __init__(self,**kwargs): + super().__init__(new_components=new_components,**kwargs) + + def lopf(self,**kwargs): + + #at this point check that all the extra links are in place for the CHPs + + + #the following function should add to any extra_functionality in kwargs + def extra_func(network, snapshots): + print("Do something fancy") + + #at this point add the constraints for the CHPs + + + super().lopf(extra_functionality=extra_func,**kwargs) diff --git a/pypsa/components.py b/pypsa/components.py index d992dc69a..e31519ba6 100644 --- a/pypsa/components.py +++ b/pypsa/components.py @@ -237,8 +237,7 @@ def __init__(self, import_name=None, name="", ignore_standard_types=False, self.branch_components = self.passive_branch_components|self.controllable_branch_components - #i.e. everything except "Network" - self.all_components = self.branch_components|self.one_port_components|self.standard_type_components|{"Bus", "SubNetwork", "Carrier", "GlobalConstraint"} + self.all_components = set(components.index) - {"Network"} self.components = Dict(components.T.to_dict()) From d5425b54a01e72e62c3a33e2eff76002b52f1d0d Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Sat, 20 Jan 2018 12:36:02 +0100 Subject: [PATCH 064/135] io: Add the possibility to load from xarray dataset --- pypsa/io.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pypsa/io.py b/pypsa/io.py index 5ae00909a..2879c9fa9 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -183,6 +183,8 @@ def __init__(self, path): self.path = path if isinstance(path, string_types): self.ds = xr.open_dataset(path) + else: + self.ds = path def __enter__(self): if isinstance(self.path, string_types): From 929095366e9cc723785a8edb3414ce1d4c596ab0 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Tue, 23 Jan 2018 17:01:16 +0100 Subject: [PATCH 065/135] opf, pf: Allow links to end on multiple buses --- examples/ac-dc-meshed/ac-dc-lopf.py | 4 +--- pypsa/descriptors.py | 5 ++-- pypsa/opf.py | 36 +++++++++++++++++++++++++---- pypsa/pf.py | 15 +++++++++--- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/examples/ac-dc-meshed/ac-dc-lopf.py b/examples/ac-dc-meshed/ac-dc-lopf.py index 10cc49521..7ada7d1f4 100644 --- a/examples/ac-dc-meshed/ac-dc-lopf.py +++ b/examples/ac-dc-meshed/ac-dc-lopf.py @@ -29,9 +29,7 @@ now = network.snapshots[5] -print("\nCheck power balance at each branch:") - -branches = network.branches() +print("\nCheck power balance at each bus:") for bus in network.buses.index: print("\n"*3+bus) diff --git a/pypsa/descriptors.py b/pypsa/descriptors.py index 739ae44f5..6f65d4496 100644 --- a/pypsa/descriptors.py +++ b/pypsa/descriptors.py @@ -181,7 +181,8 @@ def get_switchable_as_dense(network, component, attr, snapshots=None, inds=None) pnl = network.pnl(component) index = df.index - varying_i = pnl[attr].columns + + varying_i = pnl[attr].columns if attr in pnl else pd.Index([]) fixed_i = df.index.difference(varying_i) if inds is not None: @@ -193,7 +194,7 @@ def get_switchable_as_dense(network, component, attr, snapshots=None, inds=None) return (pd.concat([ pd.DataFrame(np.repeat([df.loc[fixed_i, attr].values], len(snapshots), axis=0), index=snapshots, columns=fixed_i), - pnl[attr].loc[snapshots, varying_i] + pnl[attr].loc[snapshots, varying_i] if attr in pnl else pd.DataFrame() ], axis=1).reindex(columns=index)) def get_switchable_as_iter(network, component, attr, snapshots, inds=None): diff --git a/pypsa/opf.py b/pypsa/opf.py index 6ef07a5cb..4653d55f1 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -612,8 +612,8 @@ def define_link_flows(network,snapshots): fixed_links_i = network.links.index[~ network.links.p_nom_extendable] - p_max_pu = get_switchable_as_dense(network, 'Link', 'p_max_pu') - p_min_pu = get_switchable_as_dense(network, 'Link', 'p_min_pu') + p_max_pu = get_switchable_as_dense(network, 'Link', 'p_max_pu', snapshots) + p_min_pu = get_switchable_as_dense(network, 'Link', 'p_min_pu', snapshots) fixed_lower = p_min_pu.loc[:,fixed_links_i].multiply(network.links.loc[fixed_links_i, 'p_nom']) fixed_upper = p_max_pu.loc[:,fixed_links_i].multiply(network.links.loc[fixed_links_i, 'p_nom']) @@ -872,7 +872,7 @@ def define_passive_branch_constraints(network,snapshots): extendable_branches = passive_branches[passive_branches.s_nom_extendable] fixed_branches = passive_branches[~ passive_branches.s_nom_extendable] - s_max_pu = pd.concat({c : get_switchable_as_dense(network,c,'s_max_pu') + s_max_pu = pd.concat({c : get_switchable_as_dense(network, c, 's_max_pu', snapshots) for c in passive_branch_components}, axis=1) flow_upper = {(b[0],b[1],sn) : [[(1,network.model.passive_branch_p[b[0],b[1],sn])], @@ -913,7 +913,7 @@ def define_nodal_balances(network,snapshots): for bus in network.buses.index for sn in snapshots} - efficiency = get_switchable_as_dense(network, 'Link', 'efficiency') + efficiency = get_switchable_as_dense(network, 'Link', 'efficiency', snapshots) for cb in network.links.index: bus0 = network.links.at[cb,"bus0"] @@ -923,6 +923,15 @@ def define_nodal_balances(network,snapshots): network._p_balance[bus0,sn].variables.append((-1,network.model.link_p[cb,sn])) network._p_balance[bus1,sn].variables.append((efficiency.at[sn,cb],network.model.link_p[cb,sn])) + #Add any other buses to which the links are attached + i = 2 + while("bus{}".format(i) in network.links.columns): + efficiency = get_switchable_as_dense(network, 'Link', 'efficiency{}'.format(i), snapshots) + for cb in network.links.index[network.links["bus{}".format(i)] != ""]: + bus = network.links.at[cb, "bus{}".format(i)] + for sn in snapshots: + network._p_balance[bus,sn].variables.append((efficiency.at[sn,cb],network.model.link_p[cb,sn])) + i+=1 for gen in network.generators.index: bus = network.generators.at[gen,"bus"] @@ -930,7 +939,7 @@ def define_nodal_balances(network,snapshots): for sn in snapshots: network._p_balance[bus,sn].variables.append((sign,network.model.generator_p[gen,sn])) - load_p_set = get_switchable_as_dense(network, 'Load', 'p_set') + load_p_set = get_switchable_as_dense(network, 'Load', 'p_set', snapshots) for load in network.loads.index: bus = network.loads.at[load,"bus"] sign = network.loads.at[load,"sign"] @@ -1213,6 +1222,23 @@ def set_from_series(df, series): .groupby(network.links.bus1, axis=1).sum() .reindex(columns=network.buses_t.p.columns, fill_value=0.)) + #Add any other buses to which the links are attached + i = 2 + while("bus{}".format(i) in network.links.columns): + efficiency = get_switchable_as_dense(network, 'Link', 'efficiency{}'.format(i), snapshots) + p_name = "p{}".format(i) + #allocate missing outputs + if p_name not in network.links_t: + network.links_t[p_name] = pd.DataFrame(index=network.snapshots) + links = network.links.index[network.links["bus{}".format(i)] != ""] + network.links_t[p_name] = network.links_t[p_name].reindex(links, axis=1) + network.links_t[p_name].loc[snapshots] = - network.links_t.p0.loc[snapshots, links]*efficiency.loc[snapshots, links] + network.buses_t.p.loc[snapshots] -= (network.links_t[p_name].loc[snapshots, links] + .groupby(network.links["bus{}".format(i)], axis=1).sum() + .reindex(columns=network.buses_t.p.columns, fill_value=0.)) + i+=1 + + set_from_series(network.links_t.mu_lower, pd.Series(list(model.link_p_lower.values()), index=pd.MultiIndex.from_tuples(list(model.link_p_lower.keys()))).map(duals)) set_from_series(network.links_t.mu_upper, -pd.Series(list(model.link_p_upper.values()), diff --git a/pypsa/pf.py b/pypsa/pf.py index 3e4360d07..6d2f0d165 100644 --- a/pypsa/pf.py +++ b/pypsa/pf.py @@ -100,7 +100,16 @@ def _network_prepare_and_run_pf(network, snapshots, skip_pre, linear=False, **kw if not network.links.empty: p_set = get_switchable_as_dense(network, 'Link', 'p_set', snapshots) network.links_t.p0.loc[snapshots] = p_set.loc[snapshots] - network.links_t.p1.loc[snapshots] = -p_set.loc[snapshots].multiply(network.links.efficiency) + for i in [int(col[3:]) for col in network.links.columns if col[:3] == "bus" and col != "bus0"]: + eff_name = "efficiency" if i == 1 else "efficiency{}".format(i) + p_name = "p{}".format(i) + efficiency = get_switchable_as_dense(network, 'Link', eff_name, snapshots) + #allocate missing outputs + if p_name not in network.links_t: + network.links_t[p_name] = pd.DataFrame(index=network.snapshots) + links = network.links.index[network.links["bus{}".format(i)] != ""] + network.links_t['p{}'.format(i)] = network.links_t['p{}'.format(i)].reindex(links, axis=1) + network.links_t['p{}'.format(i)].loc[snapshots] = -network.links_t.p0.loc[snapshots, links]*efficiency.loc[snapshots, links] itdf = pd.DataFrame(index=snapshots, columns=network.sub_networks.index, dtype=int) difdf = pd.DataFrame(index=snapshots, columns=network.sub_networks.index) @@ -241,7 +250,7 @@ def sub_network_pf(sub_network, snapshots=None, skip_pre=False, x_tol=1e-6, use_ [(- c.pnl[n+str(i)].loc[snapshots].groupby(c.df["bus"+str(i)], axis=1).sum() .reindex(columns=buses_o, fill_value=0)) for c in network.iterate_components(controllable_branch_components) - for i in [0,1]]) + for i in [int(col[3:]) for col in c.df.columns if col[:3] == "bus"]]) def f(guess): network.buses_t.v_ang.loc[now,sub_network.pvpqs] = guess[:len(sub_network.pvpqs)] @@ -953,7 +962,7 @@ def sub_network_lpf(sub_network, snapshots=None, skip_pre=False): [(- c.pnl["p"+str(i)].loc[snapshots].groupby(c.df["bus"+str(i)], axis=1).sum() .reindex(columns=buses_o, fill_value=0)) for c in network.iterate_components(controllable_branch_components) - for i in [0,1]]) + for i in [int(col[3:]) for col in c.df.columns if col[:3] == "bus"]]) if not skip_pre and len(branches_i) > 0: calculate_B_H(sub_network, skip_pre=True) From ad4c184b90040148755e17e0aa20495a35a572b4 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Tue, 23 Jan 2018 18:23:17 +0100 Subject: [PATCH 066/135] doc, examples: Document link with multiple outputs --- doc/components.rst | 27 +++++++ doc/design.rst | 9 +-- doc/optimal_power_flow.rst | 12 ++- examples/ac-dc-meshed/ac-dc-lpf.py | 4 +- .../chp-fixed-heat-power-ratio.py | 75 +++++++++++++++++++ website/examples/index.org | 1 + 6 files changed, 117 insertions(+), 11 deletions(-) create mode 100644 examples/sector-coupling/chp-fixed-heat-power-ratio.py diff --git a/doc/components.rst b/doc/components.rst index f4f78fe53..88b3db74c 100644 --- a/doc/components.rst +++ b/doc/components.rst @@ -434,6 +434,33 @@ the ``Link`` with ``efficiency = 1``, ``marginal_cost = 0``, :file: ../pypsa/component_attrs/links.csv +.. _components-links-multiple-outputs: + +Link with multiple outputs or inputs +------------------------------------ + +Links can also be defined with multiple outputs in fixed ratio to the +power in the single input by defining new columns ``bus2``, ``bus3``, +etc. (always ascending without missing an integer) in +``network.links`` along with associated columns for the efficiencies +``efficiency2``, ``efficiency3``, etc. The different outputs are then +proportional to the input according to the efficiency; see +:ref:`opf-links` for how these are used in the LOPF and the `example +of a CHP with a fixed power-heat ratio +`_. + +If the column ``bus2`` exists, values in the column are not compulsory +for all links; if the link has no 2nd output, simply leave it empty +``network.links.at["my_link","bus2"] = ""``. + +For links with multiple inputs in fixed ratio to a single output, +simply reverse the flow in a link with one input and multiple outputs +by setting ``my_link.p_max_pu = 0`` and ``my_link.p_min_pu = -1``. + +For multiple inputs to multiple outputs, connect a multi-to-single +link to a single-to-multi link with an auxiliary bus in the middle. + + Groups of Components ==================== diff --git a/doc/design.rst b/doc/design.rst index a8a43ef0e..2c17c9461 100644 --- a/doc/design.rst +++ b/doc/design.rst @@ -14,8 +14,8 @@ Python 3.5. Network object is the overall container ======================================= -The ``pypsa.components.Network`` is an overall container for all -network components; components cannot exist without a network. +The ``pypsa.Network`` is an overall container for all network +components; components cannot exist without a network. It is also the object on which calculations, such as power flow and optimal power flow, are performed. @@ -24,9 +24,8 @@ optimal power flow, are performed. Buses are the fundamental nodes =============================== -The ``pypsa.components.Bus`` is the fundamental node to which all -loads, generators, storage units, lines, transformers and links -attach. +The bus is the fundamental node to which all loads, generators, +storage units, lines, transformers and links attach. You can have as many components attached to a bus as you want. diff --git a/doc/optimal_power_flow.rst b/doc/optimal_power_flow.rst index 1129112c0..78cca86d8 100644 --- a/doc/optimal_power_flow.rst +++ b/doc/optimal_power_flow.rst @@ -460,7 +460,7 @@ cases. The speedup is higher for larger networks with dispatchable generators at most nodes. - +.. _opf-links: Controllable branch flows: links --------------------------------- @@ -476,8 +476,14 @@ optimisation variable for each component which satisfies |f_{l,t}| \leq F_l If the link flow is positive :math:`f_{l,t} > 0` then it withdraws -:math:`f_{l,t}` from bus0 and feeds in :math:`\eta_l f_{l,t}` to bus1, -where :math:`\eta_l` is the link efficiency. +:math:`f_{l,t}` from ``bus0`` and feeds in :math:`\eta_l f_{l,t}` to +``bus1``, where :math:`\eta_l` is the link efficiency. + +If additional output buses ``busi`` for :math:`i=2,3,\dots` are +defined (i.e. ``bus2``, ``bus3``, etc) and their associated +efficiencies ``efficiencyi``, i.e. :math:`\eta_{i,l}`, then at +``busi`` the feed-in is :math:`\eta_{i,l} f_{l,t}`. See also +:ref:`components-links-multiple-outputs`. .. _nodal-power-balance: diff --git a/examples/ac-dc-meshed/ac-dc-lpf.py b/examples/ac-dc-meshed/ac-dc-lpf.py index f20fdc71c..08333c0e7 100644 --- a/examples/ac-dc-meshed/ac-dc-lpf.py +++ b/examples/ac-dc-meshed/ac-dc-lpf.py @@ -38,9 +38,7 @@ now = network.snapshots[5] -print("\nCheck power balance at each branch:") - -branches = network.branches() +print("\nCheck power balance at each bus:") for bus in network.buses.index: print("\n"*3+bus) diff --git a/examples/sector-coupling/chp-fixed-heat-power-ratio.py b/examples/sector-coupling/chp-fixed-heat-power-ratio.py new file mode 100644 index 000000000..33260883f --- /dev/null +++ b/examples/sector-coupling/chp-fixed-heat-power-ratio.py @@ -0,0 +1,75 @@ +## Demonstration of Link with multiple outputs: Combined-Heat-and-Power (CHP) with fixed heat-power ratio +# +#For a CHP with a more complicated heat-power feasible operational area, see https://www.pypsa.org/examples/power-to-gas-boiler-chp.html. +# +#This example demonstrates a Link component with more than one bus output ("bus2" in this case). In general links can have many output buses. +# +#In this example a CHP must be heat-following because there is no other supply of heat to the bus "Frankfurt heat". + +import pypsa + + +network = pypsa.Network() + +network.add("Bus", + "Frankfurt", + carrier="AC") + +network.add("Load", + "Frankfurt", + bus="Frankfurt", + p_set=5) + +network.add("Bus", + "Frankfurt heat", + carrier="heat") + +network.add("Load", + "Frankfurt heat", + bus="Frankfurt heat", + p_set=3) + +network.add("Bus", + "Frankfurt gas", + carrier="gas") + +network.add("Store", + "Frankfurt gas", + e_initial=1e6, + e_nom=1e6, + bus="Frankfurt gas") + +network.add("Link", + "OCGT", + bus0="Frankfurt gas", + bus1="Frankfurt", + p_nom_extendable=True, + capital_cost=600, + efficiency=0.4) + +network.add("Link", + "CHP", + bus0="Frankfurt gas", + bus1="Frankfurt", + p_nom_extendable=True, + capital_cost=1400, + efficiency=0.3) + +# Now add a second output bus for the CHP + +network.links["bus2"] = "" +network.links.at["CHP","bus2"] = "Frankfurt heat" + +network.links["efficiency2"] = 0. +network.links.at["CHP","efficiency2"] = 0.3 + +network.lopf() + +network.loads_t.p + +network.links_t.p0 + +network.links_t.p1 + +network.links_t.p2 + diff --git a/website/examples/index.org b/website/examples/index.org index 3cfb1036d..ab4df47ba 100644 --- a/website/examples/index.org +++ b/website/examples/index.org @@ -58,6 +58,7 @@ repository]]. buses are connected with transmission lines and there are electrical generators at two of the nodes. - [[./power-to-gas-boiler-chp.html][*Power-to-Gas with Gas Boiler and Combined-Heat-and-Power unit*]] + - [[./chp-fixed-heat-power-ratio.html][*Demonstration of Link with multiple outputs: Combined-Heat-and-Power (CHP) with fixed heat-power ratio*]] - [[./power-to-heat-water-tank.html][*Power-to-Heat with Water Tank*]] - [[./battery-electric-vehicle-charging.html][*Transport: Charging Battery Electric Vehicle with Solar Panel*]] - [[./chained-hydro-reservoirs.html][*Chained Hydroelectric Reservoirs*]] From 544de1e74a0287714dd27a340239c5d203fbd9ed Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Wed, 24 Jan 2018 14:13:10 +0100 Subject: [PATCH 067/135] io: Fix logging output of netcdf importer --- pypsa/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypsa/io.py b/pypsa/io.py index 2879c9fa9..dba712926 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -210,7 +210,7 @@ def get_static(self, list_name): for attr in iterkeys(self.ds.data_vars) if attr.startswith(t) and attr[i:i+2] != 't_'}) df.index.name = 'name' - return df + return df if not df.empty else None def get_series(self, list_name): t = list_name + '_t_' From 39b89165dd4084113ed89e9d03e7720ccd3c87a5 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Wed, 24 Jan 2018 17:43:29 +0100 Subject: [PATCH 068/135] io, doc: Tidy up and document export/import for netCDF files Deprecate export/import for HDF5 files since they do not support tables that have more than 1000 columns. --- doc/import_export.rst | 35 +++++++++++++++++++++++++++++++++-- doc/release_notes.rst | 14 ++++++++++++++ pypsa/io.py | 40 ++++++++++++++++++++++++++++++---------- 3 files changed, 77 insertions(+), 12 deletions(-) diff --git a/doc/import_export.rst b/doc/import_export.rst index 35cd504b2..640f87635 100644 --- a/doc/import_export.rst +++ b/doc/import_export.rst @@ -181,22 +181,53 @@ Following the previous example: "Generator", "p_max_pu") +Export to netCDF +================ + +Export network and components to a netCDF file. + +netCDF files take up less space than CSV files and are faster to load. + +Both static and series attributes of components are exported, but only +if they have non-default values. + +``network.export_to_netcdf(file.nc)`` + +If ``file.nc`` does not already exist, it is created. + + +Import from netCDF +================== + +Import network data from netCDF file ``file.nc``: + +``network.import_from_hdf5(file.nc)`` + + Export to HDF5 ============== +WARNING: This is now deprecated, because HDF5 fails for tables with +more than 1000 columns. Please use netCDF instead. + Export network and components to an HDF store. Both static and series attributes of components are exported, but only if they have non-default values. -If path does not already exist, it is created. +``network.export_to_hdf5(path)`` + + +If ``path`` does not already exist, it is created. -``network.export_to_hdf5(filename)`` Import from HDF5 ================ +WARNING: This is now deprecated, because HDF5 fails for tables with +more than 1000 columns. Please use netCDF instead. + Import network data from HDF5 store at ``path``: ``network.import_from_hdf5(path)`` diff --git a/doc/release_notes.rst b/doc/release_notes.rst index c4a76cdc5..c2b7fe22e 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -3,6 +3,20 @@ Release Notes ####################### +PyPSA 0.13.0 (2*th January 2018) +================================ + +This release contains new features, fixes for library dependencies and +some minor internal API changes. + +* Networks can now be exported to and imported from netCDF files with + ``network.export_to_netcdf()`` and + ``network.import_from_netcdf()``. This is faster than using CSV + files and the files take up less space. Import and export with HDF5 + files, introduced in PyPSA 0.12.0, is now deprecated. + + + PyPSA 0.12.0 (30th November 2017) ================================= diff --git a/pypsa/io.py b/pypsa/io.py index a1f773124..3927ff0ae 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -220,7 +220,7 @@ def get_series(self, list_name): yield attr[len(t):], df class ExporterNetCDF(Exporter): - def __init__(self, path, least_significant_digit=3): + def __init__(self, path, least_significant_digit=None): self.path = path self.least_significant_digit = least_significant_digit self.ds = xr.Dataset() @@ -244,7 +244,8 @@ def save_series(self, list_name, attr, df): df.columns.name = list_name + '_t_' + attr + '_i' self.ds[list_name + '_t_' + attr] = df if self.least_significant_digit is not None: - self.ds.encoding.upate({ + print(self.least_significant_digit) + self.ds.encoding.update({ 'zlib': True, 'least_significant_digit': self.least_significant_digit }) @@ -410,6 +411,10 @@ def import_from_hdf5(network, path, skip_time=False): Skip reading in time dependent attributes """ + logger.warning(dedent("""HDF5 file format for PyPSA is now deprecated, + because HDF5 fails for tables with more than 1000 columns. + Please use netCDF instead.""")) + basename = os.path.basename(path) with ImporterHDF5(path) as importer: _import_from_importer(network, importer, basename=basename, skip_time=skip_time) @@ -438,6 +443,10 @@ def export_to_hdf5(network, path, export_standard_types=False, **kwargs): >>> network.export_to_hdf5(filename) """ + logger.warning(dedent("""HDF5 file format for PyPSA is now deprecated, + because HDF5 fails for tables with more than 1000 columns. + Please use netCDF instead.""")) + kwargs.setdefault('complevel', 4) basename = os.path.basename(path) @@ -464,35 +473,46 @@ def import_from_netcdf(network, path, skip_time=False): _import_from_importer(network, importer, basename=basename, skip_time=skip_time) -def export_to_netcdf(network, path=None, export_standard_types=False): - """ - Export network and components to a netCDF file. +def export_to_netcdf(network, path=None, export_standard_types=False, + least_significant_digit=None): + """Export network and components to a netCDF file. Both static and series attributes of components are exported, but only if they have non-default values. If path does not already exist, it is created. + If no path is passed, no file is exported, but the xarray.Dataset + is still returned. + + Be aware that this cannot export boolean attributes on the Network + class, e.g. network.my_bool = False is not supported by netCDF. + Parameters ---------- path : string|None - Name of netCDF file to which to export (if it exists, it is overwritten) + Name of netCDF file to which to export (if it exists, it is overwritten); + if None is passed, no file is exported. + least_significant_digit + This is passed to the netCDF exporter, but currently makes no difference + to file size or float accuracy. We're working on improving this... Returns ------- - ds : xr.Dataset + ds : xarray.Dataset Examples -------- - >>> export_to_netcdf(network, filename) + >>> export_to_netcdf(network, "my_file.nc") OR - >>> network.export_to_netcdf(filename) + >>> network.export_to_netcdf("my_file.nc") + """ assert has_xarray, "xarray must be installed for netCDF support." basename = os.path.basename(path) if path is not None else None - with ExporterNetCDF(path=path) as exporter: + with ExporterNetCDF(path, least_significant_digit) as exporter: _export_to_exporter(network, exporter, basename=basename, export_standard_types=export_standard_types) return exporter.ds From 28d1d2544559f3b1930b382b3c55f544b70bc5cf Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Thu, 25 Jan 2018 12:30:46 +0100 Subject: [PATCH 069/135] components: Existing components can now be overridden This replaces behaviour where you can only add new components, thus allowing you also to modify existing components. Standard component properties now stored at pypsa.components.components and their attribute properties at pypsa.components.component_attrs. Examples and documentation have been updated to reflect this functionality. --- doc/components.rst | 32 ++++- doc/release_notes.rst | 18 ++- .../add_components_from_library.py | 79 ++++++++++++ .../new_components/add_components_library.py | 23 ---- .../new_components/add_components_simple.py | 58 ++++----- examples/new_components/component_library.py | 112 ++++++++++++++---- pypsa/components.py | 60 ++++++---- 7 files changed, 280 insertions(+), 102 deletions(-) create mode 100644 examples/new_components/add_components_from_library.py delete mode 100644 examples/new_components/add_components_library.py diff --git a/doc/components.rst b/doc/components.rst index f4f78fe53..35417dcb0 100644 --- a/doc/components.rst +++ b/doc/components.rst @@ -434,10 +434,14 @@ the ``Link`` with ``efficiency = 1``, ``marginal_cost = 0``, :file: ../pypsa/component_attrs/links.csv + + Groups of Components ==================== -In the code components are grouped according to their properties. +In the code components are grouped according to their properties in +sets such as ``network.one_port_components`` and +``network.branch_components``. One-ports share the property that they all connect to a single bus, i.e. generators, loads, storage units, etc.. They share the attributes @@ -453,3 +457,29 @@ nodal power imbalances, i.e. lines and transformers. Controllable branches are branches whose power flow can be controlled by e.g. the LOPF optimisation, i.e. links. + + +.. _custom_components: + +Custom Components +================= + +If you want to define your own components and override the standard +functionality of PyPSA, you can easily override the standard +components by passing pypsa.Network() the arguments +``override_components`` and ``override_component_attrs``. + +These will replace the standard definitions in +``pypsa.components.components`` and +``pypsa.components.component_attrs``, which correspond to the +repository CSV files ``pypsa/components.csv`` and +``pypsa/component_attrs/*.csv``. ``components`` is a pandas.DataFrame +with the component ``name``, ``list_name`` and +``description``. ``component_attrs`` is a pypsa.descriptors.Dict of +pandas.DataFrame with the attribute properties for each component. +Just follow the formatting for the standard components. + +There are examples for defining new components in the git repository +in ``examples/new_components/``, including an example of +overriding e.g. ``network.lopf()`` for functionality for +combined-heat-and-power (CHP) plants. diff --git a/doc/release_notes.rst b/doc/release_notes.rst index c2b7fe22e..0afce0581 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -3,18 +3,34 @@ Release Notes ####################### -PyPSA 0.13.0 (2*th January 2018) +PyPSA 0.13.0 (25th January 2018) ================================ This release contains new features, fixes for library dependencies and some minor internal API changes. +* If you want to define your own components and override the standard + functionality of PyPSA, you can now override the standard components + by passing pypsa.Network() the arguments ``override_components`` and + ``override_component_attrs``, see :ref:`custom_components`. There + are examples for defining new components in the git repository in + ``examples/new_components/``, including an example of overriding + e.g. ``network.lopf()`` for functionality for + combined-heat-and-power (CHP) plants. + + + * Networks can now be exported to and imported from netCDF files with ``network.export_to_netcdf()`` and ``network.import_from_netcdf()``. This is faster than using CSV files and the files take up less space. Import and export with HDF5 files, introduced in PyPSA 0.12.0, is now deprecated. +Thank Russell Smith. + +Acknowledge RE-Invest funding. + + PyPSA 0.12.0 (30th November 2017) diff --git a/examples/new_components/add_components_from_library.py b/examples/new_components/add_components_from_library.py new file mode 100644 index 000000000..43f3d730b --- /dev/null +++ b/examples/new_components/add_components_from_library.py @@ -0,0 +1,79 @@ + +#Use the component_library to add components. + +#NB: This only works with Python 3 because of super() in component_library + + +import component_library + +import pandas as pd + +n = component_library.Network() + +n.set_snapshots(list(range(4))) + +places = pd.Index(["Aarhus","Aalborg","Copenhagen"]) + + +n.madd("Bus", places) + +n.madd("Load", places, bus=places, p_set=10.5) + +n.madd("Bus",places + " heat") + +n.madd("Load", places+ " heat", bus=places+ " heat", p_set=4.2) + +n.loads_t.p_set["Aalborg heat"] = 3.7 + +n.madd("Bus",places + " gas") + +n.madd("Store",places + " gas", + bus=places + " gas", + e_min_pu=-1, + marginal_cost=50, + e_nom_extendable=True) + +#Some HVDC transmission lines +n.madd("Link", + [0,1], + bus0="Aarhus", + bus1=["Aalborg","Copenhagen"], + p_nom_extendable=True, + p_min_pu=-1, + capital_cost=1e2) + +n.add("Generator", + "wind", + p_nom_extendable=True, + capital_cost=1e2, + bus="Aalborg") + +n.madd("CHP", + places + " CHP", + bus_source=places + " gas", + bus_elec=places, + bus_heat=places + " heat", + p_nom_extendable=True, + capital_cost=1.4e4, + eta_elec=0.468, + c_v=0.15, + c_m=0.75, + p_nom_ratio=1.5) + + + +print("\nn.chps:\n") +print(n.chps) + +n.lopf() + + + +print("\nGenerator capacity:\n") +print(n.generators.p_nom_opt) + +print("\nLine capacities:\n") +print(n.links.loc[["0","1"],"p_nom_opt"]) + +print("\nCHP electrical output:\n") +print(n.links.loc[places + " CHP electric","p_nom_opt"]*n.links.loc[places + " CHP electric","efficiency"]) diff --git a/examples/new_components/add_components_library.py b/examples/new_components/add_components_library.py deleted file mode 100644 index 6845a8a0d..000000000 --- a/examples/new_components/add_components_library.py +++ /dev/null @@ -1,23 +0,0 @@ - -#Use the component_library to add components. - -#NB: This only works with Python 3 because of super() in component_library - - -import component_library - -n = component_library.Network(csv_folder_name="../ac-dc-meshed/ac-dc-data/") - -n.add("Bus","Aarhus") - -n.add("CHP","My CHP",bus="Aarhus") - - -print("\nn.chps:\n") -print(n.chps) - -print("\nn.chps_t.p_set:\n") -print(n.chps_t.p_set) - - -n.lopf() diff --git a/examples/new_components/add_components_simple.py b/examples/new_components/add_components_simple.py index e48ee04db..5869cfd5b 100644 --- a/examples/new_components/add_components_simple.py +++ b/examples/new_components/add_components_simple.py @@ -1,46 +1,40 @@ #In this example we add a new component type "dynamically" by passing -#the pypsa.Network the required information. +#the pypsa.Network the required information. Here we add a simple +#"ShadowPrice" component for storing the shadow prices of global +#constraints. -import pypsa, pandas as pd +import pypsa, pandas as pd, numpy as np -#create a pandas.DataFrame with the properties of the new components -#the format should be the same as pypsa/components.csv, except add a column "attrs" -new_components = pd.DataFrame(data = [["chps","Combined heat and power plant.","controllable_one_port",None], - ["methanations","Methanation plant.","controllable_one_port",None]], - index = ["CHP","Methanation"], - columns = ["list_name","description","type","attrs"]) -print("\nNew components:\n") -print(new_components) +from pypsa.descriptors import Dict -#now attach pandas.DataFrames for the attributes -#the format should be the same as pypsa/component_attrs/*.csv +#take a copy of the components pandas.DataFrame +override_components = pypsa.components.components.copy() + +#Pass it the list_name, description and component type. +override_components.loc["ShadowPrice"] = ["shadow_prices","Shadow price for a global constraint.",np.nan] -chp_attrs = pd.DataFrame(data = [["string","n/a","n/a","Unique name","Input (required)"], - ["string","n/a","n/a","name of bus to which generator is attached","Input (required)"], - ["static or series","MW",0.,"active power set point (for PF)","Input (optional)"]], - index = ["name","bus","p_set"], - columns = ["type","unit","default","description","status"]) +print("\nNew components table:\n") +print(override_components) -print("\nComponent attributes:\n") -print(chp_attrs) +#create a pandas.DataFrame with the properties of the new component attributes. +#the format should be the same as pypsa/component_attrs/*.csv +override_component_attrs = Dict({k : v.copy() for k,v in pypsa.components.component_attrs.items()}) +override_component_attrs["ShadowPrice"] = pd.DataFrame(columns = ["type","unit","default","description","status"]) +override_component_attrs["ShadowPrice"].loc["name"] = ["string","n/a","n/a","Unique name","Input (required)"] +override_component_attrs["ShadowPrice"].loc["value"] = ["float","n/a",0.,"shadow value","Output"] -new_components.at["CHP","attrs"] = chp_attrs -new_components.at["Methanation","attrs"] = chp_attrs +print("\nComponent attributes for ShadowPrice:\n") +print(override_component_attrs["ShadowPrice"]) #pass Network the information -n = pypsa.Network(new_components=new_components) - -n.set_snapshots(range(4)) +n = pypsa.Network(override_components=override_components, + override_component_attrs=override_component_attrs) -n.add("Bus","Aarhus") -n.add("CHP","My CHP", - bus="Aarhus", - p_set=list(range(5,1,-1))) +n.add("ShadowPrice","line_volume_constraint",value=4567.1) +n.add("ShadowPrice","co2_constraint",value=326.3) -print("\nn.chps:\n") -print(n.chps) +print("\nnetwork.shadow_prices:\n") -print("\nn.chps_t.p_set:\n") -print(n.chps_t.p_set) +print(n.shadow_prices) diff --git a/examples/new_components/component_library.py b/examples/new_components/component_library.py index 418006277..33cc9774f 100644 --- a/examples/new_components/component_library.py +++ b/examples/new_components/component_library.py @@ -1,44 +1,112 @@ -#Create a library with its own Network class that has new components -#and a new LOPF function +#Create a library with its own Network class that has a new CHP +#component and a new LOPF function for the CHP constraints #NB: This only works with Python 3 because of super() -import pypsa, pandas as pd +import pypsa, pandas as pd, numpy as np +from pypsa.descriptors import Dict -new_components = pd.DataFrame(data = [["chps","Combined heat and power plant.","new",None], - ["methanations","Methanation plant.","new",None]], - index = ["CHP","Methanation"], - columns = ["list_name","description","type","attrs"]) +from pyomo.environ import Constraint + + + +override_components = pypsa.components.components.copy() +override_components.loc["ShadowPrice"] = ["shadow_prices","Shadow price for a global constraint.",np.nan] +override_components.loc["CHP"] = ["chps","Combined heat and power plant.",np.nan] + + +override_component_attrs = Dict({k : v.copy() for k,v in pypsa.components.component_attrs.items()}) +override_component_attrs["ShadowPrice"] = pd.DataFrame(columns = ["type","unit","default","description","status"]) +override_component_attrs["ShadowPrice"].loc["name"] = ["string","n/a","n/a","Unique name","Input (required)"] +override_component_attrs["ShadowPrice"].loc["value"] = ["float","n/a",0.,"shadow value","Output"] + +override_component_attrs["CHP"] = pd.DataFrame(columns = ["type","unit","default","description","status"]) +override_component_attrs["CHP"].loc["name"] = ["string","n/a","n/a","Unique name","Input (required)"] +override_component_attrs["CHP"].loc["bus_fuel"] = ["string","n/a","n/a","Name of bus where fuel source is.","Input (required)"] +override_component_attrs["CHP"].loc["bus_elec"] = ["string","n/a","n/a","Name of bus where electricity is supplied.","Input (required)"] +override_component_attrs["CHP"].loc["bus_heat"] = ["string","n/a","n/a","Name of bus where heat is supplied.","Input (required)"] +override_component_attrs["CHP"].loc["p_nom_extendable"] = ["boolean","n/a",False,"","Input (optional)"] +override_component_attrs["CHP"].loc["capital_cost"] = ["float","EUR/MW",0.,"Capital cost per rating of electricity output.","Input (optional)"] +override_component_attrs["CHP"].loc["eta_elec"] = ["float","n/a",1.,"Electrical efficiency with no heat output, i.e. in condensing mode","Input (optional)"] +override_component_attrs["CHP"].loc["c_v"] = ["float","n/a",1.,"Loss of fuel for each addition of heat","Input (optional)"] +override_component_attrs["CHP"].loc["c_m"] = ["float","n/a",1.,"Backpressure ratio","Input (optional)"] +override_component_attrs["CHP"].loc["p_nom_ratio"] = ["float","n/a",1.,"Ratio of max heat output to max electrical output; max heat of 500 MWth and max electricity of 1000 MWth means p_nom_ratio is 0.5","Input (optional)"] -chp_attrs = pd.DataFrame(data = [["string","n/a","n/a","Unique name","Input (required)"], - ["string","n/a","n/a","name of bus to which generator is attached","Input (required)"], - ["float","n/a",1.,"power sign","Input (optional)"], - ["static or series","MW",0.,"active power set point (for PF)","Input (optional)"], - ["series","MW",0.,"active power at bus (positive if net generation)","Output"]], - index = ["name","bus","sign","p_set","p"], - columns = ["type","unit","default","description","status"]) -new_components.at["CHP","attrs"] = chp_attrs -new_components.at["Methanation","attrs"] = chp_attrs class Network(pypsa.Network): - def __init__(self,**kwargs): - super().__init__(new_components=new_components,**kwargs) + def __init__(self,*args,**kwargs): + kwargs["override_components"]=override_components + kwargs["override_component_attrs"]=override_component_attrs + super().__init__(*args,**kwargs) + - def lopf(self,**kwargs): + def lopf(self,*args,**kwargs): #at this point check that all the extra links are in place for the CHPs + if not self.chps.empty: + self.madd("Link", + self.chps.index + " electric", + bus0=self.chps.bus_source.values, + bus1=self.chps.bus_elec.values, + p_nom_extendable=self.chps.p_nom_extendable.values, + capital_cost=self.chps.capital_cost.values*self.chps.eta_elec.values, + efficiency=self.chps.eta_elec.values) + + self.madd("Link", + self.chps.index + " heat", + bus0=self.chps.bus_source.values, + bus1=self.chps.bus_heat.values, + p_nom_extendable=self.chps.p_nom_extendable.values, + efficiency=self.chps.eta_elec.values/self.chps.c_v.values) + + + if "extra_functionality" in kwargs: + user_extra_func = kwargs.pop('extra_functionality') + else: + user_extra_func = None #the following function should add to any extra_functionality in kwargs def extra_func(network, snapshots): - print("Do something fancy") - #at this point add the constraints for the CHPs + if not network.chps.empty: + print("Setting up CHPs:",network.chps.index) + + def chp_nom(model, chp): + return network.chps.at[chp,"eta_elec"]*network.chps.at[chp,'p_nom_ratio']*model.link_p_nom[chp + " electric"] == network.chps.at[chp,"eta_elec"]/network.chps.at[chp,"c_v"]*model.link_p_nom[chp + " heat"] + + + network.model.chp_nom = Constraint(list(network.chps.index),rule=chp_nom) + + + def backpressure(model,chp,snapshot): + return network.chps.at[chp,'c_m']*network.chps.at[chp,"eta_elec"]/network.chps.at[chp,"c_v"]*model.link_p[chp + " heat",snapshot] <= network.chps.at[chp,"eta_elec"]*model.link_p[chp + " electric",snapshot] + + network.model.backpressure = Constraint(list(network.chps.index),list(snapshots),rule=backpressure) + + + def top_iso_fuel_line(model,chp,snapshot): + return model.link_p[chp + " heat",snapshot] + model.link_p[chp + " electric",snapshot] <= model.link_p_nom[chp + " electric"] + + network.model.top_iso_fuel_line = Constraint(list(network.chps.index),list(snapshots),rule=top_iso_fuel_line) + + + + + + if user_extra_func is not None: + print("Now doing user defined extra functionality") + user_extra_func(network,snapshots) + + kwargs["extra_functionality"]=extra_func + + super().lopf(*args,**kwargs) + #Afterwards you can process the outputs, e.g. into network.chps_t.p_out - super().lopf(extra_functionality=extra_func,**kwargs) + #You could also delete the auxiliary links created above diff --git a/pypsa/components.py b/pypsa/components.py index e31519ba6..5a43c6e07 100644 --- a/pypsa/components.py +++ b/pypsa/components.py @@ -89,6 +89,19 @@ inf = float("inf") +components = pd.read_csv(os.path.join(dir_name, + "components.csv"), + index_col=0) + +component_attrs = Dict() + +for component in components.index: + file_name = os.path.join(dir_name, + component_attrs_dir_name, + components.at[component,"list_name"] + ".csv") + component_attrs[component] = pd.read_csv(file_name, index_col=0, na_values="n/a") + +del component class Basic(object): """Common to every object.""" @@ -134,8 +147,14 @@ class Network(Basic): If True, do not read in PyPSA standard types into standard types DataFrames. csv_folder_name : string Name of folder from which to import CSVs of network data. Overrides import_name. - new_components : pandas.DataFrame - DataFrame with index of component name and columns of list_name and description + override_components : pandas.DataFrame + If you want to override the standard PyPSA components in pypsa.components.components, + pass it a DataFrame with index of component name and columns of list_name and + description, following the format of pypsa.components.components. + See git repository examples/new_components/. + override_component_attrs : pypsa.descriptors.Dict of pandas.DataFrame + If you want to override pypsa.component_attrs, follow its format. + See git repository examples/new_components/. kwargs Any remaining attributes to set @@ -200,7 +219,7 @@ class Network(Basic): adjacency_matrix = adjacency_matrix def __init__(self, import_name=None, name="", ignore_standard_types=False, - new_components=None, + override_components=None, override_component_attrs=None, **kwargs): if 'csv_folder_name' in kwargs: @@ -220,36 +239,31 @@ def __init__(self, import_name=None, name="", ignore_standard_types=False, #corresponds to number of hours represented by each snapshot self.snapshot_weightings = pd.Series(index=self.snapshots,data=1.) - components = pd.read_csv(os.path.join(dir_name, - "components.csv"), - index_col=0) - - components = components.reindex(components.columns|["attrs"],axis=1) + if override_components is None: + self.components = components + else: + self.components = override_components - if new_components is not None: - components = pd.concat((components, new_components)) + if override_component_attrs is None: + self.component_attrs = component_attrs + else: + self.component_attrs = override_component_attrs - for c_type in set(components.type.unique()) - {np.nan}: + for c_type in set(self.components.type.unique()) - {np.nan}: setattr(self, c_type + "_components", - set(components.index[components.type == c_type])) + set(self.components.index[self.components.type == c_type])) self.one_port_components = self.passive_one_port_components|self.controllable_one_port_components self.branch_components = self.passive_branch_components|self.controllable_branch_components - self.all_components = set(components.index) - {"Network"} + self.all_components = set(self.components.index) - {"Network"} - self.components = Dict(components.T.to_dict()) + self.components = Dict(self.components.T.to_dict()) - for component in self.components.keys(): - - if type(self.components[component]["attrs"]) is pd.DataFrame: - attrs = self.components[component]["attrs"] - else: - file_name = os.path.join(dir_name, - component_attrs_dir_name, - self.components[component]["list_name"] + ".csv") - attrs = pd.read_csv(file_name, index_col=0, na_values="n/a") + for component in self.components: + #make copies to prevent unexpected sharing of variables + attrs = self.component_attrs[component].copy() attrs['static'] = (attrs['type'] != 'series') attrs['varying'] = attrs['type'].isin({'series', 'static or series'}) From 68c3374baafda5f75252573d40bc1746c3381ab1 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Thu, 25 Jan 2018 14:11:23 +0100 Subject: [PATCH 070/135] pf, opf: Use override_component_attrs for multilink attributes This is more explicit and ensures that import and export work properly. Adjust documentation and example appropriately. --- doc/components.rst | 21 ++++++++++----- doc/release_notes.rst | 18 +++++++++---- .../chp-fixed-heat-power-ratio.py | 26 +++++++++++-------- pypsa/__init__.py | 2 +- pypsa/descriptors.py | 4 +-- pypsa/opf.py | 18 +++++-------- pypsa/pf.py | 10 +++---- 7 files changed, 55 insertions(+), 44 deletions(-) diff --git a/doc/components.rst b/doc/components.rst index a28107e7e..9f98c203a 100644 --- a/doc/components.rst +++ b/doc/components.rst @@ -441,14 +441,23 @@ Link with multiple outputs or inputs Links can also be defined with multiple outputs in fixed ratio to the power in the single input by defining new columns ``bus2``, ``bus3``, -etc. (always ascending without missing an integer) in -``network.links`` along with associated columns for the efficiencies -``efficiency2``, ``efficiency3``, etc. The different outputs are then -proportional to the input according to the efficiency; see -:ref:`opf-links` for how these are used in the LOPF and the `example -of a CHP with a fixed power-heat ratio +etc. (``bus`` followed by an integer) in ``network.links`` along with +associated columns for the efficiencies ``efficiency2``, +``efficiency3``, etc. The different outputs are then proportional to +the input according to the efficiency; see :ref:`opf-links` for how +these are used in the LOPF and the `example of a CHP with a fixed +power-heat ratio `_. +To define the new columns ``bus2``, ``efficiency2``, ``bus3``, +``efficiency3``, etc. in ``network.links`` you need to override the +standard component attributes by passing ``pypsa.Network()`` an +``override_component_attrs`` argument. See the section +:ref:`custom_components` and the `example of a CHP with a fixed +power-heat ratio +`_. + + If the column ``bus2`` exists, values in the column are not compulsory for all links; if the link has no 2nd output, simply leave it empty ``network.links.at["my_link","bus2"] = ""``. diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 0afce0581..4d6f9f2f6 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -6,8 +6,9 @@ Release Notes PyPSA 0.13.0 (25th January 2018) ================================ -This release contains new features, fixes for library dependencies and -some minor internal API changes. +This release contains new features aimed at coupling to other energy +sectors, fixes for library dependencies and some minor internal API +changes. * If you want to define your own components and override the standard functionality of PyPSA, you can now override the standard components @@ -17,9 +18,16 @@ some minor internal API changes. ``examples/new_components/``, including an example of overriding e.g. ``network.lopf()`` for functionality for combined-heat-and-power (CHP) plants. - - - +* The ``Link`` component can now be defined with multiple outputs in + fixed ratio to the power in the single input by defining new columns + ``bus2``, ``bus3``, etc. (``bus`` followed by an integer) in + ``network.links`` along with associated columns for the efficiencies + ``efficiency2``, ``efficiency3``, etc. The different outputs are + then proportional to the input according to the efficiency; see + sections :ref:`components-links-multiple-outputs` and + :ref:`opf-links` and the `example of a CHP with a fixed power-heat + ratio + `_. * Networks can now be exported to and imported from netCDF files with ``network.export_to_netcdf()`` and ``network.import_from_netcdf()``. This is faster than using CSV diff --git a/examples/sector-coupling/chp-fixed-heat-power-ratio.py b/examples/sector-coupling/chp-fixed-heat-power-ratio.py index 33260883f..96912fc69 100644 --- a/examples/sector-coupling/chp-fixed-heat-power-ratio.py +++ b/examples/sector-coupling/chp-fixed-heat-power-ratio.py @@ -6,10 +6,19 @@ # #In this example a CHP must be heat-following because there is no other supply of heat to the bus "Frankfurt heat". -import pypsa +import pypsa, numpy as np +#First tell PyPSA that links will have a 2nd bus by +#overriding the component_attrs. This can be done for +#as many buses as you need with format busi for i = 2,3,4,5,.... -network = pypsa.Network() +override_component_attrs = pypsa.descriptors.Dict({k : v.copy() for k,v in pypsa.components.component_attrs.items()}) +override_component_attrs["Link"].loc["bus2"] = ["string",np.nan,np.nan,"2nd bus","Input (optional)"] +override_component_attrs["Link"].loc["efficiency2"] = ["static or series","per unit",1.,"2nd bus efficiency","Input (optional)"] +override_component_attrs["Link"].loc["p2"] = ["series","MW",0.,"2nd bus output","Output"] + + +network = pypsa.Network(override_component_attrs=override_component_attrs) network.add("Bus", "Frankfurt", @@ -47,21 +56,16 @@ capital_cost=600, efficiency=0.4) + network.add("Link", "CHP", bus0="Frankfurt gas", bus1="Frankfurt", + bus2="Frankfurt heat", p_nom_extendable=True, capital_cost=1400, - efficiency=0.3) - -# Now add a second output bus for the CHP - -network.links["bus2"] = "" -network.links.at["CHP","bus2"] = "Frankfurt heat" - -network.links["efficiency2"] = 0. -network.links.at["CHP","efficiency2"] = 0.3 + efficiency=0.3, + efficiency2=0.3) network.lopf() diff --git a/pypsa/__init__.py b/pypsa/__init__.py index b04dcfea1..ef3cb1220 100644 --- a/pypsa/__init__.py +++ b/pypsa/__init__.py @@ -25,7 +25,7 @@ from __future__ import absolute_import -from . import components +from . import components, descriptors from . import pf, opf, plot, networkclustering, io, contingency, geo from .components import Network, SubNetwork diff --git a/pypsa/descriptors.py b/pypsa/descriptors.py index 6f65d4496..347b6f070 100644 --- a/pypsa/descriptors.py +++ b/pypsa/descriptors.py @@ -182,7 +182,7 @@ def get_switchable_as_dense(network, component, attr, snapshots=None, inds=None) index = df.index - varying_i = pnl[attr].columns if attr in pnl else pd.Index([]) + varying_i = pnl[attr].columns fixed_i = df.index.difference(varying_i) if inds is not None: @@ -194,7 +194,7 @@ def get_switchable_as_dense(network, component, attr, snapshots=None, inds=None) return (pd.concat([ pd.DataFrame(np.repeat([df.loc[fixed_i, attr].values], len(snapshots), axis=0), index=snapshots, columns=fixed_i), - pnl[attr].loc[snapshots, varying_i] if attr in pnl else pd.DataFrame() + pnl[attr].loc[snapshots, varying_i] ], axis=1).reindex(columns=index)) def get_switchable_as_iter(network, component, attr, snapshots, inds=None): diff --git a/pypsa/opf.py b/pypsa/opf.py index 6d77e2886..cc318456e 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -923,14 +923,13 @@ def define_nodal_balances(network,snapshots): network._p_balance[bus1,sn].variables.append((efficiency.at[sn,cb],network.model.link_p[cb,sn])) #Add any other buses to which the links are attached - i = 2 - while("bus{}".format(i) in network.links.columns): + for i in [int(col[3:]) for col in network.links.columns if col[:3] == "bus" and col not in ["bus0","bus1"]]: efficiency = get_switchable_as_dense(network, 'Link', 'efficiency{}'.format(i), snapshots) for cb in network.links.index[network.links["bus{}".format(i)] != ""]: bus = network.links.at[cb, "bus{}".format(i)] for sn in snapshots: network._p_balance[bus,sn].variables.append((efficiency.at[sn,cb],network.model.link_p[cb,sn])) - i+=1 + for gen in network.generators.index: bus = network.generators.at[gen,"bus"] @@ -1142,7 +1141,8 @@ def extract_optimisation_results(network, snapshots, formulation="angles"): 'Bus': ['p', 'v_ang', 'v_mag_pu', 'marginal_price'], 'Line': ['p0', 'p1', 'mu_lower', 'mu_upper'], 'Transformer': ['p0', 'p1', 'mu_lower', 'mu_upper'], - 'Link': ['p0', 'p1', 'mu_lower', 'mu_upper']}) + 'Link': ["p"+col[3:] for col in network.links.columns if col[:3] == "bus"] + +['mu_lower', 'mu_upper']}) #get value of objective function network.objective = network.results["Problem"][0]["Upper bound"] @@ -1219,20 +1219,14 @@ def set_from_series(df, series): .reindex(columns=network.buses_t.p.columns, fill_value=0.)) #Add any other buses to which the links are attached - i = 2 - while("bus{}".format(i) in network.links.columns): + for i in [int(col[3:]) for col in network.links.columns if col[:3] == "bus" and col not in ["bus0","bus1"]]: efficiency = get_switchable_as_dense(network, 'Link', 'efficiency{}'.format(i), snapshots) p_name = "p{}".format(i) - #allocate missing outputs - if p_name not in network.links_t: - network.links_t[p_name] = pd.DataFrame(index=network.snapshots) links = network.links.index[network.links["bus{}".format(i)] != ""] - network.links_t[p_name] = network.links_t[p_name].reindex(links, axis=1) - network.links_t[p_name].loc[snapshots] = - network.links_t.p0.loc[snapshots, links]*efficiency.loc[snapshots, links] + network.links_t[p_name].loc[snapshots, links] = - network.links_t.p0.loc[snapshots, links]*efficiency.loc[snapshots, links] network.buses_t.p.loc[snapshots] -= (network.links_t[p_name].loc[snapshots, links] .groupby(network.links["bus{}".format(i)], axis=1).sum() .reindex(columns=network.buses_t.p.columns, fill_value=0.)) - i+=1 set_from_series(network.links_t.mu_lower, pd.Series(list(model.link_p_lower.values()), diff --git a/pypsa/pf.py b/pypsa/pf.py index 1fd13db5c..3a26560bc 100644 --- a/pypsa/pf.py +++ b/pypsa/pf.py @@ -67,7 +67,8 @@ def _allocate_pf_outputs(network, linear=False): 'Bus': ['p', 'v_ang', 'v_mag_pu'], 'Line': ['p0', 'p1'], 'Transformer': ['p0', 'p1'], - 'Link': ['p0', 'p1']} + 'Link': ["p"+col[3:] for col in network.links.columns if col[:3] == "bus"]} + if not linear: for component, attrs in to_allocate.items(): @@ -104,12 +105,8 @@ def _network_prepare_and_run_pf(network, snapshots, skip_pre, linear=False, **kw eff_name = "efficiency" if i == 1 else "efficiency{}".format(i) p_name = "p{}".format(i) efficiency = get_switchable_as_dense(network, 'Link', eff_name, snapshots) - #allocate missing outputs - if p_name not in network.links_t: - network.links_t[p_name] = pd.DataFrame(index=network.snapshots) links = network.links.index[network.links["bus{}".format(i)] != ""] - network.links_t['p{}'.format(i)] = network.links_t['p{}'.format(i)].reindex(links, axis=1) - network.links_t['p{}'.format(i)].loc[snapshots] = -network.links_t.p0.loc[snapshots, links]*efficiency.loc[snapshots, links] + network.links_t['p{}'.format(i)].loc[snapshots, links] = -network.links_t.p0.loc[snapshots, links]*efficiency.loc[snapshots, links] itdf = pd.DataFrame(index=snapshots, columns=network.sub_networks.index, dtype=int) difdf = pd.DataFrame(index=snapshots, columns=network.sub_networks.index) @@ -954,7 +951,6 @@ def sub_network_lpf(sub_network, snapshots=None, skip_pre=False): [(- c.pnl["p"+str(i)].loc[snapshots].groupby(c.df["bus"+str(i)], axis=1).sum() .reindex(columns=buses_o, fill_value=0)) for c in network.iterate_components(network.controllable_branch_components) - for i in [0,1]]) for i in [int(col[3:]) for col in c.df.columns if col[:3] == "bus"]]) if not skip_pre and len(branches_i) > 0: From d043e1dd2893431e18ed4bb2567aac8d4df5db55 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Thu, 25 Jan 2018 14:48:25 +0100 Subject: [PATCH 071/135] component_attrs: Add x_pu_eff to attributes for passive branches --- pypsa/component_attrs/lines.csv | 2 ++ pypsa/component_attrs/transformers.csv | 2 ++ 2 files changed, 4 insertions(+) diff --git a/pypsa/component_attrs/lines.csv b/pypsa/component_attrs/lines.csv index bea874737..d9c02d897 100644 --- a/pypsa/component_attrs/lines.csv +++ b/pypsa/component_attrs/lines.csv @@ -27,6 +27,8 @@ x_pu,float,per unit,0.,Per unit series reactance calculated by PyPSA from x and r_pu,float,per unit,0.,Per unit series resistance calculated by PyPSA from r and bus.v_nom,Output g_pu,float,per unit,0.,Per unit shunt conductivity calculated by PyPSA from g and bus.v_nom,Output b_pu,float,per unit,0.,Per unit shunt susceptance calculated by PyPSA from b and bus.v_nom,Output +x_pu_eff,float,per unit,0.,"Effective per unit series reactance for linear power flow, calculated by PyPSA from x, tap_ratio for transformers and bus.v_nom.",Output +r_pu_eff,float,per unit,0.,"Effective per unit series resistance for linear power flow, calculated by PyPSA from x, tap_ratio for transformers and bus.v_nom.",Output s_nom_opt,float,MVA,0.,Optimised capacity for apparent power.,Output mu_lower,series,currency/MVA,0.,Shadow price of lower s_nom limit -F \leq f. Always non-negative.,Output mu_upper,series,currency/MVA,0.,Shadow price of upper s_nom limit f \leq F. Always non-negative.,Output \ No newline at end of file diff --git a/pypsa/component_attrs/transformers.csv b/pypsa/component_attrs/transformers.csv index a7f821965..f5698268a 100644 --- a/pypsa/component_attrs/transformers.csv +++ b/pypsa/component_attrs/transformers.csv @@ -30,6 +30,8 @@ x_pu,float,per unit,0.,Per unit series reactance calculated by PyPSA from x and r_pu,float,per unit,0.,Per unit series resistance calculated by PyPSA from r and bus.v_nom,Output g_pu,float,per unit,0.,Per unit shunt conductivity calculated by PyPSA from g and bus.v_nom,Output b_pu,float,per unit,0.,Per unit shunt susceptance calculated by PyPSA from b and bus.v_nom,Output +x_pu_eff,float,per unit,0.,"Effective per unit series reactance for linear power flow, calculated by PyPSA from x, tap_ratio for transformers and bus.v_nom.",Output +r_pu_eff,float,per unit,0.,"Effective per unit series resistance for linear power flow, calculated by PyPSA from x, tap_ratio for transformers and bus.v_nom.",Output s_nom_opt,float,MVA,0.,Optimised capacity for apparent power.,Output mu_lower,series,currency/MVA,0.,Shadow price of lower s_nom limit -F \leq f. Always non-negative.,Output mu_upper,series,currency/MVA,0.,Shadow price of upper s_nom limit f \leq F. Always non-negative.,Output \ No newline at end of file From b74c4165fc590a2e6aa349333ce5eb3dbd33fd6d Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Thu, 25 Jan 2018 14:53:37 +0100 Subject: [PATCH 072/135] PyPSA Version 0.13.0 Hyperlinked release notes can be found here: https://www.pypsa.org/doc/release_notes.html#pypsa-0-13-0-25th-january-2018 This release contains new features aimed at coupling power networks to other energy sectors, fixes for library dependencies and some minor internal API changes. * If you want to define your own components and override the standard functionality of PyPSA, you can now override the standard components by passing pypsa.Network() the arguments override_components and override_component_attrs, see the section on Custom Components. There are examples for defining new components in the git repository in examples/new_components/, including an example of overriding network.lopf() for functionality for combined-heat-and-power (CHP) plants. * The Link component can now be defined with multiple outputs in fixed ratio to the power in the single input by defining new columns bus2, bus3, etc. (bus followed by an integer) in network.links along with associated columns for the efficiencies efficiency2, efficiency3, etc. The different outputs are then proportional to the input according to the efficiency; see sections Link with multiple outputs or inputs and Controllable branch flows: links and the example of a CHP with a fixed power-heat ratio. * Networks can now be exported to and imported from netCDF files with network.export_to_netcdf() and network.import_from_netcdf(). This is faster than using CSV files and the files take up less space. Import and export with HDF5 files, introduced in PyPSA 0.12.0, is now deprecated. * The export and import code has been refactored to be more general and abstract. This does not affect the API. * The internally-used sets such as pypsa.components.all_components and pypsa.one_port_components have been moved from pypsa.components to network, i.e. network.all_components and network.one_port_components, since these sets may change from network to network. * For linear power flow, PyPSA now pre-calculates the effective per unit reactance x_pu_eff for AC lines to take account of the transformer tap ratio, rather than doing it on the fly; this makes some code faster, particularly the kirchhoff formulation of the LOPF. * PyPSA is now compatible with networkx 2.0 and 2.1. * PyPSA now requires Pyomo version greater than 5.3. * PyPSA now uses the Travis CI continuous integration service to test every commit in the PyPSA GitHub repository. This will allow us to catch library dependency issues faster. We thank Russell Smith of Edison Energy for the pull request for the effective reactance that sped up the LOPF code and Tom Edwards for pointing out the Pyomo version dependency issue. For this release we also acknowledge funding to Tom Brown from the RE-InVEST project. --- doc/components.rst | 14 ++++++++------ doc/conf.py | 6 +++--- doc/release_notes.rst | 45 +++++++++++++++++++++++++++++++------------ pypsa/__init__.py | 4 ++-- pypsa/components.py | 2 +- pypsa/descriptors.py | 2 +- pypsa/opf.py | 2 +- pypsa/pf.py | 2 +- setup.py | 2 +- 9 files changed, 51 insertions(+), 28 deletions(-) diff --git a/doc/components.rst b/doc/components.rst index 9f98c203a..d3cd4766d 100644 --- a/doc/components.rst +++ b/doc/components.rst @@ -503,15 +503,17 @@ functionality of PyPSA, you can easily override the standard components by passing pypsa.Network() the arguments ``override_components`` and ``override_component_attrs``. -These will replace the standard definitions in +For this network, these will replace the standard definitions in ``pypsa.components.components`` and ``pypsa.components.component_attrs``, which correspond to the repository CSV files ``pypsa/components.csv`` and -``pypsa/component_attrs/*.csv``. ``components`` is a pandas.DataFrame -with the component ``name``, ``list_name`` and -``description``. ``component_attrs`` is a pypsa.descriptors.Dict of -pandas.DataFrame with the attribute properties for each component. -Just follow the formatting for the standard components. +``pypsa/component_attrs/*.csv``. + +``components`` is a pandas.DataFrame with the component ``name``, +``list_name`` and ``description``. ``component_attrs`` is a +pypsa.descriptors.Dict of pandas.DataFrame with the attribute +properties for each component. Just follow the formatting for the +standard components. There are examples for defining new components in the git repository in ``examples/new_components/``, including an example of diff --git a/doc/conf.py b/doc/conf.py index 46619e83e..c79597b4c 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -55,7 +55,7 @@ # General information about the project. project = u'PyPSA' -copyright = u'2016-2017 Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)' +copyright = u'2015-2018 Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)' author = u'Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)' # The version info for the project you're documenting, acts as replacement for @@ -63,9 +63,9 @@ # built documents. # # The short X.Y version. -version = u'0.12' +version = u'0.13' # The full version, including alpha/beta/rc tags. -release = u'0.12.0' +release = u'0.13.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 4d6f9f2f6..1c2ff3c76 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -6,18 +6,18 @@ Release Notes PyPSA 0.13.0 (25th January 2018) ================================ -This release contains new features aimed at coupling to other energy -sectors, fixes for library dependencies and some minor internal API -changes. +This release contains new features aimed at coupling power networks to +other energy sectors, fixes for library dependencies and some minor +internal API changes. * If you want to define your own components and override the standard functionality of PyPSA, you can now override the standard components by passing pypsa.Network() the arguments ``override_components`` and - ``override_component_attrs``, see :ref:`custom_components`. There - are examples for defining new components in the git repository in - ``examples/new_components/``, including an example of overriding - e.g. ``network.lopf()`` for functionality for - combined-heat-and-power (CHP) plants. + ``override_component_attrs``, see the section on + :ref:`custom_components`. There are examples for defining new + components in the git repository in ``examples/new_components/``, + including an example of overriding ``network.lopf()`` for + functionality for combined-heat-and-power (CHP) plants. * The ``Link`` component can now be defined with multiple outputs in fixed ratio to the power in the single input by defining new columns ``bus2``, ``bus3``, etc. (``bus`` followed by an integer) in @@ -33,10 +33,31 @@ changes. ``network.import_from_netcdf()``. This is faster than using CSV files and the files take up less space. Import and export with HDF5 files, introduced in PyPSA 0.12.0, is now deprecated. - -Thank Russell Smith. - -Acknowledge RE-Invest funding. +* The export and import code has been refactored to be more general + and abstract. This does not affect the API. +* The internally-used sets such as ``pypsa.components.all_components`` + and ``pypsa.one_port_components`` have been moved from + ``pypsa.components`` to ``network``, i.e. ``network.all_components`` + and ``network.one_port_components``, since these sets may change + from network to network. +* For linear power flow, PyPSA now pre-calculates the effective per + unit reactance ``x_pu_eff`` for AC lines to take account of the + transformer tap ratio, rather than doing it on the fly; this makes + some code faster, particularly the kirchhoff formulation of the + LOPF. +* PyPSA is now compatible with networkx 2.0 and 2.1. +* PyPSA now requires Pyomo version greater than 5.3. +* PyPSA now uses the `Travis CI `_ + continuous integration service to test every commit in the `PyPSA + GitHub repository `_. This will + allow us to catch library dependency issues faster. + +We thank Russell Smith of Edison Energy for the pull request for the +effective reactance that sped up the LOPF code and Tom Edwards for +pointing out the Pyomo version dependency issue. + +For this release we also acknowledge funding to Tom Brown from the +`RE-InVEST project `_. diff --git a/pypsa/__init__.py b/pypsa/__init__.py index ef3cb1220..e4f589288 100644 --- a/pypsa/__init__.py +++ b/pypsa/__init__.py @@ -33,6 +33,6 @@ import logging logging.basicConfig(level=logging.INFO) -__version__ = "0.12.0" +__version__ = "0.13.0" __author__ = "Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)" -__copyright__ = "Copyright 2015-2017 Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS), GNU GPL 3" +__copyright__ = "Copyright 2015-2018 Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS), GNU GPL 3" diff --git a/pypsa/components.py b/pypsa/components.py index 5a43c6e07..7cf8a6b32 100644 --- a/pypsa/components.py +++ b/pypsa/components.py @@ -1,6 +1,6 @@ -## Copyright 2015-2017 Tom Brown (FIAS), Jonas Hoersch (FIAS) +## Copyright 2015-2018 Tom Brown (FIAS), Jonas Hoersch (FIAS) ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as diff --git a/pypsa/descriptors.py b/pypsa/descriptors.py index 347b6f070..fbcd85d3f 100644 --- a/pypsa/descriptors.py +++ b/pypsa/descriptors.py @@ -1,6 +1,6 @@ -## Copyright 2015-2017 Tom Brown (FIAS), Jonas Hoersch (FIAS) +## Copyright 2015-2018 Tom Brown (FIAS), Jonas Hoersch (FIAS) ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as diff --git a/pypsa/opf.py b/pypsa/opf.py index cc318456e..9025a3ffe 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -1,6 +1,6 @@ -## Copyright 2015-2017 Tom Brown (FIAS), Jonas Hoersch (FIAS), David +## Copyright 2015-2018 Tom Brown (FIAS), Jonas Hoersch (FIAS), David ## Schlachtberger (FIAS) ## This program is free software; you can redistribute it and/or diff --git a/pypsa/pf.py b/pypsa/pf.py index 3a26560bc..f0062d23b 100644 --- a/pypsa/pf.py +++ b/pypsa/pf.py @@ -1,4 +1,4 @@ -## Copyright 2015-2017 Tom Brown (FIAS), Jonas Hoersch (FIAS) +## Copyright 2015-2018 Tom Brown (FIAS), Jonas Hoersch (FIAS) ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as diff --git a/setup.py b/setup.py index 9723fb62e..bcfd28d8b 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ setup( name='pypsa', - version='0.12.0', + version='0.13.0', author='Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)', author_email='brown@fias.uni-frankfurt.de', description='Python for Power Systems Analysis', From 7daaa7361984a2d7b1151481877ad993cec56fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Fri, 26 Jan 2018 15:26:53 +0100 Subject: [PATCH 073/135] networkclustering: Fix _consense error message --- pypsa/networkclustering.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index 3cb784298..338c298b6 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -50,7 +50,7 @@ def consense(x): v = x.iat[0] assert ((x == v).all() or x.isnull().all()), ( "In {} cluster {} the values of attribute {} do not agree:\n{}" - .format(component, attr, x.name, x) + .format(component, x.name, attr, x) ) return v return consense From c170705babf9f8aec190c476c3e33c563c0a7f00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Sat, 27 Jan 2018 00:34:30 +0100 Subject: [PATCH 074/135] networkclustering: simplify and extend busmap_by_stubs --- pypsa/networkclustering.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index 338c298b6..9a0ff5712 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -458,7 +458,7 @@ def rectangular_grid_clustering(network, divisions): ################ # Reduce stubs/dead-ends, i.e. nodes with valency 1, iteratively to remove tree-like structures -def busmap_by_stubs(network): +def busmap_by_stubs(network, matching_attrs=None): """Create a busmap by reducing stubs and stubby trees (i.e. sequentially reducing dead-ends). @@ -466,6 +466,9 @@ def busmap_by_stubs(network): ---------- network : pypsa.Network + matching_attrs : None|[str] + bus attributes clusters have to agree on + Returns ------- busmap : pandas.Series @@ -474,28 +477,26 @@ def busmap_by_stubs(network): """ - busmap = pd.Series(network.buses.index,network.buses.index) + busmap = pd.Series(network.buses.index, network.buses.index) - network = network.copy(with_time=False) + G = network.graph() - count = 0 + def attrs_match(u, v): + return (matching_attrs is None or + (network.buses.loc[u, matching_attrs] == + network.buses.loc[v, matching_attrs]).all()) while True: - old_count = count - logger.info("{} buses".format(len(network.buses))) - graph = network.graph() - for u in graph.node: - neighbours = list(graph.adj[u].keys()) + stubs = [] + for u in G.node: + neighbours = list(G.adj[u].keys()) if len(neighbours) == 1: - neighbour = neighbours[0] - count +=1 - lines = list(graph.adj[u][neighbour].keys()) - for line in lines: - network.remove(*line) - network.remove("Bus",u) - busmap[busmap==u] = neighbour - logger.info("{} deleted".format(count)) - if old_count == count: + v, = neighbours + if attrs_match(u, v): + busmap[busmap == u] = v + stubs.append(u) + G.remove_nodes_from(stubs) + if len(stubs) == 0: break return busmap From 76306f373fa5c4612b7e160d37ba1b734103f4f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Sat, 27 Jan 2018 00:28:59 +0100 Subject: [PATCH 075/135] networkclustering: Support line types (and tiny python3 compatibility import fix) --- pypsa/networkclustering.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index 9a0ff5712..c66bd8e5f 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -26,7 +26,7 @@ import networkx as nx from collections import OrderedDict, namedtuple from itertools import repeat -from six.moves import map, zip, range +from six.moves import map, zip, range, reduce from six import itervalues, iteritems import six @@ -167,6 +167,7 @@ def aggregatelinegroup(l): s_nom_min=l['s_nom_min'].sum(), s_nom_max=l['s_nom_max'].sum(), s_nom_extendable=l['s_nom_extendable'].any(), + num_parallel=l['num_parallel'].sum(), capital_cost=l['capital_cost'].sum(), length=length_s, sub_network=consense['sub_network'](l['sub_network']), From 0c89d3bca665213b458e497dc3c75b82e0442b47 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Tue, 30 Jan 2018 13:20:09 +0100 Subject: [PATCH 076/135] graph: Fix minor regression in adjacency_matrix --- pypsa/graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypsa/graph.py b/pypsa/graph.py index fe4d3baf6..84c99aa10 100644 --- a/pypsa/graph.py +++ b/pypsa/graph.py @@ -106,7 +106,7 @@ def adjacency_matrix(network, branch_components=None, busorder=None, weights=Non sel = slice(None) no_branches = len(c.df) else: - sel = t.ind + sel = c.ind no_branches = len(c.ind) bus0_inds.append(busorder.get_indexer(c.df.loc[sel, "bus0"])) bus1_inds.append(busorder.get_indexer(c.df.loc[sel, "bus1"])) From 6a256428c8424f9c8f0070540a161156e6a97e75 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Tue, 30 Jan 2018 13:22:48 +0100 Subject: [PATCH 077/135] plot: Add jitter argument to iplot --- pypsa/plot.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/pypsa/plot.py b/pypsa/plot.py index 6286aa62e..e13813299 100644 --- a/pypsa/plot.py +++ b/pypsa/plot.py @@ -271,7 +271,7 @@ def as_branch_series(ser): def iplot(network, fig=None, bus_colors='blue', bus_colorscale=None, bus_colorbar=None, bus_sizes=10, bus_text=None, line_colors='green', line_widths=2, line_text=None, title="", - branch_components=['Line', 'Link'], iplot=True): + branch_components=['Line', 'Link'], iplot=True, jitter=None): """ Plot the network buses and lines interactively using plotly. @@ -307,6 +307,9 @@ def iplot(network, fig=None, bus_colors='blue', Branch components to be plotted, defaults to Line and Link. iplot : bool, default True Automatically do an interactive plot of the figure. + jitter : None|float + Amount of random noise to add to bus positions to distinguish + overlapping buses Returns ------- @@ -325,8 +328,14 @@ def iplot(network, fig=None, bus_colors='blue', if bus_text is None: bus_text = 'Bus ' + network.buses.index - bus_trace = dict(x=network.buses.x, - y=network.buses.y, + x = network.buses.x + y = network.buses.y + + if jitter is not None: + x = x + np.random.uniform(low=-jitter, high=jitter, size=len(x)) + y = y + np.random.uniform(low=-jitter, high=jitter, size=len(y)) + + bus_trace = dict(x=x, y=y, text=bus_text, type="scatter", mode="markers", @@ -385,11 +394,14 @@ def as_branch_series(ser): else: l_colors.fillna(l_defaults['color'], inplace=True) - x0 = c.df.bus0.map(network.buses.x) - x1 = c.df.bus1.map(network.buses.x) + x = network.buses.x + y = network.buses.y + + x0 = c.df.bus0.map(x) + x1 = c.df.bus1.map(x) - y0 = c.df.bus0.map(network.buses.y) - y1 = c.df.bus1.map(network.buses.y) + y0 = c.df.bus0.map(y) + y1 = c.df.bus1.map(y) for line in c.df.index: shapes.append(dict(type='line', From b12a291385fb91302c8119286fa92717c9722f4a Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Tue, 30 Jan 2018 13:23:17 +0100 Subject: [PATCH 078/135] plot: Fix iplot for unspecified line colors and widths --- pypsa/plot.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pypsa/plot.py b/pypsa/plot.py index e13813299..38625d0e0 100644 --- a/pypsa/plot.py +++ b/pypsa/plot.py @@ -23,7 +23,7 @@ from __future__ import division from __future__ import absolute_import import six -from six import iteritems +from six import iteritems, string_types import pandas as pd import numpy as np @@ -379,8 +379,8 @@ def as_branch_series(ser): for c in network.iterate_components(branch_components): l_defaults = defaults_for_branches[c.name] l_widths = line_widths.get(c.name, l_defaults['width']) - l_nums = None l_colors = line_colors.get(c.name, l_defaults['color']) + l_nums = None if line_text is None: l_text = c.name + ' ' + c.df.index @@ -404,15 +404,16 @@ def as_branch_series(ser): y1 = c.df.bus1.map(y) for line in c.df.index: + color = l_colors if isinstance(l_colors, string_types) else l_colors[line] + width = l_widths if isinstance(l_widths, (int, float)) else l_widths[line] + shapes.append(dict(type='line', - x0=x0[line], - y0=y0[line], - x1=x1[line], - y1=y1[line], - opacity=0.7, - line=dict(color=l_colors[line], - width=l_widths[line]) - )) + x0=x0[line], + y0=y0[line], + x1=x1[line], + y1=y1[line], + opacity=0.7, + line=dict(color=color, width=width))) shape_traces.append(dict(x=0.5*(x0+x1), y=0.5*(y0+y1), @@ -420,8 +421,7 @@ def as_branch_series(ser): type="scatter", mode="markers", hoverinfo="text", - marker=dict(opacity=0.)) - ) + marker=dict(opacity=0.))) fig['data'].extend([bus_trace]+shape_traces) From fe15090d2cfb737bca3be945d02e0174288de7d6 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Tue, 30 Jan 2018 13:41:52 +0100 Subject: [PATCH 079/135] fixup! plot: Add jitter argument to iplot --- pypsa/plot.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pypsa/plot.py b/pypsa/plot.py index 38625d0e0..a552ed498 100644 --- a/pypsa/plot.py +++ b/pypsa/plot.py @@ -394,9 +394,6 @@ def as_branch_series(ser): else: l_colors.fillna(l_defaults['color'], inplace=True) - x = network.buses.x - y = network.buses.y - x0 = c.df.bus0.map(x) x1 = c.df.bus1.map(x) From 38ecca2647299b3adba1f6fcab3ed8bd8a894e97 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Wed, 31 Jan 2018 08:06:53 +0100 Subject: [PATCH 080/135] Initialise logging from Network.__init__ logging.basicConfig does only work once, as it checks whether any handlers have been set on the root logger beforehand. This means a user of pypsa could previously only configure logging BEFORE importing pypsa, because afterwards basicConfig turns into a NO-OP. --- pypsa/__init__.py | 3 --- pypsa/components.py | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pypsa/__init__.py b/pypsa/__init__.py index e4f589288..24a8ff988 100644 --- a/pypsa/__init__.py +++ b/pypsa/__init__.py @@ -30,9 +30,6 @@ from .components import Network, SubNetwork -import logging -logging.basicConfig(level=logging.INFO) - __version__ = "0.13.0" __author__ = "Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)" __copyright__ = "Copyright 2015-2018 Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS), GNU GPL 3" diff --git a/pypsa/components.py b/pypsa/components.py index 7cf8a6b32..ce9ec2f9e 100644 --- a/pypsa/components.py +++ b/pypsa/components.py @@ -226,6 +226,9 @@ def __init__(self, import_name=None, name="", ignore_standard_types=False, logger.warning("The argument csv_folder_name for initiating Network() is deprecated, please use import_name instead.") import_name = kwargs.pop('csv_folder_name') + # Initialise root logger and set its level, if this has not been done before + logging.basicConfig(level=logging.INFO) + from . import __version__ as pypsa_version Basic.__init__(self, name) From b6eac49a302437eefae8ac439fa2eba4e35efd13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Wed, 31 Jan 2018 12:54:17 +0100 Subject: [PATCH 081/135] graph, pf: Improve treatment of infinite impedances An infinite impedance arises naturally as a candidate line without any capacity, yet. By default, they are skipped when constructing the cycle basis (in which case they act just like a link in the opf equations) and given a high weight, when building the minimal spanning tree. --- pypsa/graph.py | 38 ++++++++++++++++++++++++++++++++++---- pypsa/pf.py | 4 ++-- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/pypsa/graph.py b/pypsa/graph.py index 84c99aa10..1193fc65f 100644 --- a/pypsa/graph.py +++ b/pypsa/graph.py @@ -23,8 +23,33 @@ from .descriptors import OrderedGraph -def graph(network, branch_components=None, weight=None): - """Build networkx graph.""" +def graph(network, branch_components=None, weight=None, inf_weight=False): + """ + Build NetworkX graph. + + Arguments + --------- + network : Network|SubNetwork + + branch_components : [str] + Components to use as branches. The default are + passive_branch_components in the case of a SubNetwork and + branch_components in the case of a Network. + + weight : str + Branch attribute to use as weight + + inf_weight : bool|float + How to treat infinite weights (default: False). True keeps the infinite + weight. False skips edges with infinite weight. If a float is given it + is used instead. + + Returns + ------- + graph : OrderedGraph + NetworkX graph + """ + from . import components if isinstance(network, components.Network): @@ -36,7 +61,7 @@ def graph(network, branch_components=None, weight=None): branch_components = network.network.passive_branch_components buses_i = network.buses_i() else: - raise TypeError("build_graph must be called with a Network or a SubNetwork") + raise TypeError("graph must be called with a Network or a SubNetwork") graph = OrderedGraph() @@ -52,7 +77,11 @@ def gen_edges(): data = {} else: data = dict(weight=getattr(branch, weight)) - if np.isinf(data['weight']): continue + if np.isinf(data['weight']) and inf_weight is not True: + if inf_weight is False: + continue + else: + data['weight'] = inf_weight yield (branch.bus0, branch.bus1, (c.name, branch.Index), data) @@ -81,6 +110,7 @@ def adjacency_matrix(network, branch_components=None, busorder=None, weights=Non adjacency_matrix : sp.sparse.coo_matrix Directed adjacency matrix """ + from . import components if isinstance(network, components.Network): diff --git a/pypsa/pf.py b/pypsa/pf.py index f0062d23b..1488537ca 100644 --- a/pypsa/pf.py +++ b/pypsa/pf.py @@ -827,7 +827,7 @@ def find_tree(sub_network, weight='x_pu'): branches_i = branches_bus0.index buses_i = sub_network.buses_i() - graph = sub_network.graph(weight=weight) + graph = sub_network.graph(weight=weight, inf_weight=1.) sub_network.tree = nx.minimum_spanning_tree(graph) #find bus with highest degree to use as slack @@ -862,7 +862,7 @@ def find_cycles(sub_network, weight='x_pu'): branches_i = branches_bus0.index #reduce to a non-multi-graph for cycles with > 2 edges - mgraph = sub_network.graph(weight=weight) + mgraph = sub_network.graph(weight=weight, inf_weight=False) graph = nx.OrderedGraph(mgraph) cycles = nx.cycle_basis(graph) From ddc84590150f1ec5620b49a526f5a539c15f5fed Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Wed, 31 Jan 2018 15:42:15 +0100 Subject: [PATCH 082/135] website: Update LOPF cycle flow paper with journal reference --- website/publications/index.org | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/website/publications/index.org b/website/publications/index.org index 0bfcebade..f0e087d65 100644 --- a/website/publications/index.org +++ b/website/publications/index.org @@ -9,7 +9,7 @@ would cite the following paper: - T. Brown, J. H\ouml{}rsch, D. Schlachtberger, [[https://arxiv.org/abs/1707.09913][PyPSA: Python for Power System Analysis]], 2018, [[https://openresearchsoftware.metajnl.com/][Journal of Open Research Software]], 6(1), - [[https://arxiv.org/abs/1707.09913][arXiv:1707.09913]], [[https://doi.org/10.5334/jors.188][DOI: 10.5334/jors.188]] + [[https://arxiv.org/abs/1707.09913][postprint arXiv:1707.09913]], [[https://doi.org/10.5334/jors.188][DOI: 10.5334/jors.188]] Please use the following BibTeX: @@ -38,12 +38,12 @@ linked from the overall PyPSA Zenodo DOI: The following research papers have used PyPSA: - T. Brown, D. Schlachtberger, A. Kies, S. Schramm, M. Greiner, [[https://arxiv.org/abs/1801.05290][Synergies of sector coupling and transmission extension in a cost-optimised, highly renewable European energy system]], 2017, [[https://arxiv.org/abs/1801.05290][preprint arXiv:1801.05290]], [[https://zenodo.org/record/1146665][all input data, code and output data on Zenodo]] -- J. H\ouml{}rsch, Joanne Calitz, [[https://arxiv.org/abs/1710.11199][PyPSA-ZA: Investment and operation co-optimization of integrating wind and solar in South Africa at high spatial and temporal detail]], presented at the [[http://windac-africa.com/][WindAc Africa Conference]], 2017, [[https://arxiv.org/abs/1710.11199][preprint]], [[https://github.com/FRESNA/pypsa-za][code]] -- Markus Groissb\ouml{}ck, Alexandre Gusmao, [[https://arxiv.org/abs/1709.03761][Impact of high renewable penetration scenarios on system reliability: two case studies in the Kingdom of Saudi Arabia]], 2017, [[https://arxiv.org/abs/1709.03761][preprint]] -- J. H\ouml{}rsch, T. Brown, [[https://doi.org/10.1109/EEM.2017.7982024][The role of spatial scale in joint optimisations of generation and transmission for European highly renewable scenarios]], 2017, accepted to [[http://eem2017.com/][14th International Conference on the European Energy Market - EEM 2017]], [[https://arxiv.org/abs/1705.07617][preprint]] -- D. Schlachtberger, T. Brown, S. Schramm, M. Greiner, [[https://doi.org/10.1016/j.energy.2017.06.004][The Benefits of Cooperation in a Highly Renewable European Electricity Network]], Energy, Volume 134, 1 September 2017, Pages 469-481, [[https://arxiv.org/abs/1704.05492][postprint]], [[https://doi.org/10.5281/zenodo.804337][all input data, code, output data on Zenodo]] -- J. H\ouml{}rsch, H. Ronellenfitsch, D. Witthaut, T. Brown, [[https://arxiv.org/abs/1704.01881][Linear Optimal Power Flow Using Cycle Flows]], 2017, [[https://arxiv.org/abs/1704.01881][preprint]], accepted to [[https://www.journals.elsevier.com/electric-power-systems-research][Electric Power Systems Research]] -- Joao Gorenstein Dedecca, Rudi A. Hakvoort, Paulien M. Herder, [[https://doi.org/10.1016/j.energy.2017.02.111][Transmission expansion simulation for the European Northern Seas offshore grid]], Energy, Volume 125, 15 April 2017, Pages 805-824 +- J. H\ouml{}rsch, Joanne Calitz, [[https://arxiv.org/abs/1710.11199][PyPSA-ZA: Investment and operation co-optimization of integrating wind and solar in South Africa at high spatial and temporal detail]], presented at the [[http://windac-africa.com/][WindAc Africa Conference]], 2017, [[https://arxiv.org/abs/1710.11199][preprint arXiv:1710.11199]], [[https://github.com/FRESNA/pypsa-za][code]] +- Markus Groissb\ouml{}ck, Alexandre Gusmao, [[https://arxiv.org/abs/1709.03761][Impact of high renewable penetration scenarios on system reliability: two case studies in the Kingdom of Saudi Arabia]], 2017, [[https://arxiv.org/abs/1709.03761][preprint arXiv:1709.03761]] +- J. H\ouml{}rsch, T. Brown, [[https://doi.org/10.1109/EEM.2017.7982024][The role of spatial scale in joint optimisations of generation and transmission for European highly renewable scenarios]], 2017, [[http://eem2017.com/][14th International Conference on the European Energy Market - EEM 2017]], [[https://arxiv.org/abs/1705.07617][preprint arXiv:1705.07617]], [[https://doi.org/10.1109/EEM.2017.7982024][DOI: 10.1109/EEM.2017.7982024]] +- D. Schlachtberger, T. Brown, S. Schramm, M. Greiner, [[https://doi.org/10.1016/j.energy.2017.06.004][The Benefits of Cooperation in a Highly Renewable European Electricity Network]], Energy, Volume 134, 1 September 2017, Pages 469-481, [[https://arxiv.org/abs/1704.05492][postprint arXiv:1704.05492]], [[https://doi.org/10.1016/j.energy.2017.06.004][DOI: 10.1016/j.energy.2017.06.004]], [[https://doi.org/10.5281/zenodo.804337][all input data, code, output data on Zenodo]] +- J. H\ouml{}rsch, H. Ronellenfitsch, D. Witthaut, T. Brown, [[https://arxiv.org/abs/1704.01881][Linear Optimal Power Flow Using Cycle Flows]], 2017, [[https://www.journals.elsevier.com/electric-power-systems-research][Electric Power Systems Research]], Volume 158, pages 126-135, [[https://arxiv.org/abs/1704.01881][postprint arXiv:1704.01881]], [[https://doi.org/10.1016/j.epsr.2017.12.034][DOI: 10.1016/j.epsr.2017.12.034]] +- Joao Gorenstein Dedecca, Rudi A. Hakvoort, Paulien M. Herder, [[https://doi.org/10.1016/j.energy.2017.02.111][Transmission expansion simulation for the European Northern Seas offshore grid]], Energy, Volume 125, 15 April 2017, Pages 805-824, [[https://doi.org/10.1016/j.energy.2017.02.111][DOI: 10.1016/j.energy.2017.02.111]] If you have written a paper or report using PyPSA, please send us the link so we can add it here by emailing Tom Brown (brown at From 18995704a93d6e5f98d9016960c8638cf0a19b88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Sat, 3 Feb 2018 13:54:24 +0100 Subject: [PATCH 083/135] opf: Clear pyomo model after extracting optimisation results --- pypsa/opf.py | 108 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 65 insertions(+), 43 deletions(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index 9025a3ffe..331133c1b 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -22,7 +22,7 @@ # make the code as Python 3 compatible as possible from __future__ import division, absolute_import -from six import iteritems, string_types +from six import iteritems, itervalues, string_types __author__ = "Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)" @@ -1128,7 +1128,7 @@ def define_linear_objective(network,snapshots): l_objective(model,objective) -def extract_optimisation_results(network, snapshots, formulation="angles"): +def extract_optimisation_results(network, snapshots, formulation="angles", free_pyomo=True): if isinstance(snapshots, pd.DatetimeIndex) and _pd_version < '0.18.0': # Work around pandas bug #12050 (https://github.com/pydata/pandas/issues/12050) @@ -1150,32 +1150,48 @@ def extract_optimisation_results(network, snapshots, formulation="angles"): model = network.model duals = pd.Series(list(model.dual.values()), index=pd.Index(list(model.dual.keys()))) + if free_pyomo: + model.dual.clear() - def as_series(indexedvar): - return pd.Series(indexedvar.get_values()) + def clear_indexedvar(indexedvar): + for v in itervalues(indexedvar._data): + v.clear() + + def get_values(indexedvar, free=free_pyomo): + s = pd.Series(indexedvar.get_values()) + if free: + clear_indexedvar(indexedvar) + return s def set_from_series(df, series): df.loc[snapshots] = series.unstack(0).reindex(columns=df.columns) + def get_shadows(constraint, multiind=True): + index = list(constraint.keys()) + if multiind: + index = pd.MultiIndex.from_tuples(index) + cdata = pd.Series(list(constraint.values()), index=index) + return cdata.map(duals) + if len(network.generators): - set_from_series(network.generators_t.p, as_series(model.generator_p)) + set_from_series(network.generators_t.p, get_values(model.generator_p)) if len(network.storage_units): set_from_series(network.storage_units_t.p, - as_series(model.storage_p_dispatch) - - as_series(model.storage_p_store)) + get_values(model.storage_p_dispatch) + - get_values(model.storage_p_store)) set_from_series(network.storage_units_t.state_of_charge, - as_series(model.state_of_charge)) + get_values(model.state_of_charge)) if (network.storage_units_t.inflow.max() > 0).any(): set_from_series(network.storage_units_t.spill, - as_series(model.storage_p_spill)) + get_values(model.storage_p_spill)) network.storage_units_t.spill.fillna(0, inplace=True) #p_spill doesn't exist if inflow=0 if len(network.stores): - set_from_series(network.stores_t.p, as_series(model.store_p)) - set_from_series(network.stores_t.e, as_series(model.store_e)) + set_from_series(network.stores_t.p, get_values(model.store_p)) + set_from_series(network.stores_t.e, get_values(model.store_e)) if len(network.loads): load_p_set = get_switchable_as_dense(network, 'Load', 'p_set', snapshots) @@ -1192,19 +1208,20 @@ def set_from_series(df, series): # passive branches - passive_branches = as_series(model.passive_branch_p) + passive_branches = get_values(model.passive_branch_p) + flow_lower = get_shadows(model.flow_lower) + flow_upper = get_shadows(model.flow_upper) for c in network.iterate_components(network.passive_branch_components): set_from_series(c.pnl.p0, passive_branches.loc[c.name]) c.pnl.p1.loc[snapshots] = - c.pnl.p0.loc[snapshots] - set_from_series(c.pnl.mu_lower, pd.Series(list(model.flow_lower.values()), - index=pd.MultiIndex.from_tuples(list(model.flow_lower.keys()))).map(duals)[c.name]) - set_from_series(c.pnl.mu_upper, -pd.Series(list(model.flow_upper.values()), - index=pd.MultiIndex.from_tuples(list(model.flow_upper.keys()))).map(duals)[c.name]) + set_from_series(c.pnl.mu_lower, flow_lower[c.name]) + set_from_series(c.pnl.mu_upper, -flow_upper[c.name]) + del flow_lower, flow_upper # active branches if len(network.links): - set_from_series(network.links_t.p0, as_series(model.link_p)) + set_from_series(network.links_t.p0, get_values(model.link_p)) efficiency = get_switchable_as_dense(network, 'Link', 'efficiency', snapshots) @@ -1229,11 +1246,8 @@ def set_from_series(df, series): .reindex(columns=network.buses_t.p.columns, fill_value=0.)) - set_from_series(network.links_t.mu_lower, pd.Series(list(model.link_p_lower.values()), - index=pd.MultiIndex.from_tuples(list(model.link_p_lower.keys()))).map(duals)) - set_from_series(network.links_t.mu_upper, -pd.Series(list(model.link_p_upper.values()), - index=pd.MultiIndex.from_tuples(list(model.link_p_upper.keys()))).map(duals)) - + set_from_series(network.links_t.mu_lower, get_shadows(model.link_p_lower)) + set_from_series(network.links_t.mu_upper, - get_shadows(model.link_p_upper)) if len(network.buses): if formulation in {'angles', 'kirchhoff'}: @@ -1247,7 +1261,7 @@ def set_from_series(df, series): if formulation == "angles": set_from_series(network.buses_t.v_ang, - as_series(model.voltage_angles)) + get_values(model.voltage_angles)) elif formulation in ["ptdf","cycles","kirchhoff"]: for sn in network.sub_networks.obj: network.buses_t.v_ang.loc[snapshots,sn.slack_bus] = 0. @@ -1264,20 +1278,20 @@ def set_from_series(df, series): network.generators.p_nom_opt = network.generators.p_nom network.generators.loc[network.generators.p_nom_extendable, 'p_nom_opt'] = \ - as_series(network.model.generator_p_nom) + get_values(network.model.generator_p_nom) network.storage_units.p_nom_opt = network.storage_units.p_nom network.storage_units.loc[network.storage_units.p_nom_extendable, 'p_nom_opt'] = \ - as_series(network.model.storage_p_nom) + get_values(network.model.storage_p_nom) network.stores.e_nom_opt = network.stores.e_nom network.stores.loc[network.stores.e_nom_extendable, 'e_nom_opt'] = \ - as_series(network.model.store_e_nom) + get_values(network.model.store_e_nom) - s_nom_extendable_passive_branches = as_series(model.passive_branch_s_nom) + s_nom_extendable_passive_branches = get_values(model.passive_branch_s_nom) for c in network.iterate_components(network.passive_branch_components): c.df['s_nom_opt'] = c.df.s_nom if c.df.s_nom_extendable.any(): @@ -1286,11 +1300,10 @@ def set_from_series(df, series): network.links.p_nom_opt = network.links.p_nom network.links.loc[network.links.p_nom_extendable, "p_nom_opt"] = \ - as_series(network.model.link_p_nom) + get_values(network.model.link_p_nom) try: - network.global_constraints.loc[:,"mu"] = -pd.Series(list(model.global_constraints.values()), - index=list(model.global_constraints.keys())).map(duals) + network.global_constraints.loc[:,"mu"] = - get_shadows(model.global_constraints, multiind=False) except (AttributeError, KeyError) as e: logger.warning("Could not read out global constraint shadow prices") @@ -1302,7 +1315,7 @@ def set_from_series(df, series): if len(fixed_committable_gens_i) > 0: network.generators_t.status.loc[snapshots,fixed_committable_gens_i] = \ - as_series(model.generator_status).unstack(0) + get_values(model.generator_status).unstack(0) def network_lopf_build_model(network, snapshots=None, skip_pre=False, formulation="angles", ptdf_tolerance=0.): @@ -1402,7 +1415,7 @@ def network_lopf_prepare_solver(network, solver_name="glpk", solver_io=None): return network.opt -def network_lopf_solve(network, snapshots=None, formulation="angles", solver_options={}, keep_files=False, free_memory={}): +def network_lopf_solve(network, snapshots=None, formulation="angles", solver_options={}, keep_files=False, free_memory={'pyomo'}): """ Solve linear optimal power flow for a group of snapshots and extract results. @@ -1421,9 +1434,10 @@ def network_lopf_solve(network, snapshots=None, formulation="angles", solver_opt keep_files : bool, default False Keep the files that pyomo constructs from OPF problem construction, e.g. .lp file - useful for debugging - free_memory : set, default {} - Any subset of {'pypsa'}. Stash time series data and/or pyomo model away - while the solver runs. + free_memory : set, default {'pyomo'} + Any subset of {'pypsa', 'pyomo'}. Allows to stash `pypsa` time-series + data away while the solver runs (as a pickle to disk) and/or free + `pyomo` data after the solution has been extracted. Returns ------- @@ -1456,13 +1470,15 @@ def network_lopf_solve(network, snapshots=None, formulation="angles", solver_opt if status == "ok" and termination_condition == "optimal": logger.info("Optimization successful") - extract_optimisation_results(network,snapshots,formulation) + extract_optimisation_results(network, snapshots, formulation, + free_pyomo='pyomo' in free_memory) elif status == "warning" and termination_condition == "other": logger.warn("WARNING! Optimization might be sub-optimal. Writing output anyway") - extract_optimisation_results(network,snapshots,formulation) + extract_optimisation_results(network, snapshots, formulation, + free_pyomo='pyomo' in free_memory) else: logger.error("Optimisation failed with status %s and terminal condition %s" - % (status,termination_condition)) + % (status, termination_condition)) return status, termination_condition @@ -1504,8 +1520,10 @@ def network_lopf(network, snapshots=None, solver_name="glpk", solver_io=None, one of ["angles","cycles","kirchhoff","ptdf"] ptdf_tolerance : float Value below which PTDF entries are ignored - free_memory : set, default {} - Any subset of {'pypsa'}. Stash time series data away. + free_memory : set, default {'pyomo'} + Any subset of {'pypsa', 'pyomo'}. Allows to stash `pypsa` time-series + data away while the solver runs (as a pickle to disk) and/or free + `pyomo` data after the solution has been extracted. Returns ------- @@ -1514,11 +1532,15 @@ def network_lopf(network, snapshots=None, solver_name="glpk", solver_io=None, snapshots = _as_snapshots(network, snapshots) - network_lopf_build_model(network, snapshots, skip_pre=skip_pre, formulation=formulation, ptdf_tolerance=ptdf_tolerance) + network_lopf_build_model(network, snapshots, skip_pre=skip_pre, + formulation=formulation, ptdf_tolerance=ptdf_tolerance) if extra_functionality is not None: extra_functionality(network,snapshots) - network_lopf_prepare_solver(network, solver_name=solver_name, solver_io=solver_io) + network_lopf_prepare_solver(network, solver_name=solver_name, + solver_io=solver_io) - return network_lopf_solve(network, snapshots, formulation=formulation, solver_options=solver_options, keep_files=keep_files, free_memory=free_memory) + return network_lopf_solve(network, snapshots, formulation=formulation, + solver_options=solver_options, + keep_files=keep_files, free_memory=free_memory) From ed827cfe909bd21aabdf87ec5e83326f488e7c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Sat, 3 Feb 2018 13:55:32 +0100 Subject: [PATCH 084/135] descriptors: Add function to clear pypsa output series data Meant to be run before solving lopf to temporarily free up data for the solver. --- pypsa/descriptors.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pypsa/descriptors.py b/pypsa/descriptors.py index fbcd85d3f..3f74e0e1d 100644 --- a/pypsa/descriptors.py +++ b/pypsa/descriptors.py @@ -284,6 +284,17 @@ def allocate_series_dataframes(network, series): pnl[attr] = pnl[attr].reindex(columns=df.index, fill_value=network.components[component]["attrs"].at[attr,"default"]) +def free_output_series_dataframes(network, components=None): + if components is None: + components = network.all_components + + for component in components: + attrs = network.components[component]['attrs'] + pnl = network.pnl(component) + + for attr in attrs.index[attrs['varying'] & (attrs['status'] == 'Output')]: + pnl[attr] = pd.DataFrame(index=network.snapshots, columns=[]) + def zsum(s, *args, **kwargs): """ pandas 0.21.0 changes sum() behavior so that the result of applying sum From 88d0cb9330fdfad1e7ce6c51f161919069667403 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Sun, 4 Feb 2018 03:22:22 +0100 Subject: [PATCH 085/135] opf: Fix get_shadows for no branches --- pypsa/opf.py | 2 ++ test/test_unit_commitment.py | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/pypsa/opf.py b/pypsa/opf.py index 331133c1b..54c6dac88 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -1167,6 +1167,8 @@ def set_from_series(df, series): df.loc[snapshots] = series.unstack(0).reindex(columns=df.columns) def get_shadows(constraint, multiind=True): + if len(constraint) == 0: return pd.Series() + index = list(constraint.keys()) if multiind: index = pd.MultiIndex.from_tuples(index) diff --git a/test/test_unit_commitment.py b/test/test_unit_commitment.py index d99d85725..cb10760a6 100644 --- a/test/test_unit_commitment.py +++ b/test/test_unit_commitment.py @@ -134,3 +134,8 @@ def test_minimum_down_time(): expected_dispatch = np.array([[3000,0,0,8000],[0,800,3000,0]],dtype=float).T np.testing.assert_array_almost_equal(nu.generators_t.p.values,expected_dispatch) + +if __name__ == "__main__": + test_minimum_down_time() + test_minimum_up_time() + test_part_load() From d90f05f6870c8ac239be98203f18295ff9d6070f Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Sun, 11 Feb 2018 14:49:26 +0100 Subject: [PATCH 086/135] examples: use multi-output link for chained reservoir example Allows water out of first turbine to feed second reservoir. Link between reservoirs is then for spillage. --- .../chained-hydro-reservoirs.py | 75 ++++++++++++------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/examples/sector-coupling/chained-hydro-reservoirs.py b/examples/sector-coupling/chained-hydro-reservoirs.py index f489f0fcb..4f7e43473 100644 --- a/examples/sector-coupling/chained-hydro-reservoirs.py +++ b/examples/sector-coupling/chained-hydro-reservoirs.py @@ -1,19 +1,36 @@ ## Two chained reservoirs with inflow in one supply two electric loads # -#Two disconnected electrical loads are fed from two reservoirs linked by a river; the first reservoir has inflow from a water basin. +#Two disconnected electrical loads are fed from two reservoirs linked by a river; the first reservoir has inflow from rain onto a water basin. # #Note that the two reservoirs are tightly coupled, meaning there is no time delay between the first one emptying and the second one filling, as there would be if there were a long stretch of river between the reservoirs. The reservoirs are essentially assumed to be close to each other. A time delay would require a "Link" element between different snapshots, which is not yet supported by PyPSA (but could be enabled by passing network.lopf() an extra_functionality function). import pypsa import pandas as pd +import numpy as np from pyomo.environ import Constraint +#First tell PyPSA that links will have a 2nd bus by +#overriding the component_attrs. This is needed so that +#water can both go through a turbine AND feed the next reservoir -network = pypsa.Network() +override_component_attrs = pypsa.descriptors.Dict({k : v.copy() for k,v in pypsa.components.component_attrs.items()}) +override_component_attrs["Link"].loc["bus2"] = ["string",np.nan,np.nan,"2nd bus","Input (optional)"] +override_component_attrs["Link"].loc["efficiency2"] = ["static or series","per unit",1.,"2nd bus efficiency","Input (optional)"] +override_component_attrs["Link"].loc["p2"] = ["series","MW",0.,"2nd bus output","Output"] + + +network = pypsa.Network(override_component_attrs=override_component_attrs) network.set_snapshots(pd.date_range("2016-01-01 00:00","2016-01-01 03:00",freq="H")) +network.add("Carrier", + "reservoir") + +network.add("Carrier", + "rain") + + network.add("Bus", "0", carrier="AC") @@ -22,20 +39,15 @@ "1", carrier="AC") + network.add("Bus", "0 reservoir", carrier="reservoir") - network.add("Bus", "1 reservoir", carrier="reservoir") -network.add("Carrier", - "reservoir") - -network.add("Carrier", - "rain") network.add("Generator", "rain", @@ -44,22 +56,38 @@ p_nom=1000, p_max_pu=[0.,0.2,0.7,0.4]) + network.add("Load", "0 load", bus="0", p_set=20.) - network.add("Load", "1 load", bus="1", p_set=30.) + +#The efficiency of a river is the relation between the gravitational potential +#energy of 1 m^3 of water in reservoir 0 relative to its turbine versus the +#potential energy of 1 m^3 of water in reservoir 1 relative to its turbine + +network.add("Link", + "spillage", + bus0="0 reservoir", + bus1="1 reservoir", + efficiency=0.5, + p_nom_extendable=True) + + +#water from turbine also goes into next reservoir network.add("Link", "0 turbine", bus0="0 reservoir", bus1="0", + bus2="1 reservoir", efficiency=0.9, + efficiency2=0.5, capital_cost=1000, p_nom_extendable=True) @@ -70,21 +98,7 @@ efficiency=0.9, capital_cost=1000, p_nom_extendable=True) - - - - -#The efficiency of a river is the relation between the gravitational potential -#energy of 1 m^3 of water in reservoir 0 relative to its turbine versus the -#potential energy of 1 m^3 of water in reservoir 1 relative to its turbine - -network.add("Link", - "river", - bus0="0 reservoir", - bus1="1 reservoir", - efficiency=0.5, - p_nom_extendable=True) - + network.add("Store", "0 reservoir", @@ -92,7 +106,6 @@ e_cyclic=True, e_nom_extendable=True) - network.add("Store", "1 reservoir", bus="1 reservoir", @@ -102,11 +115,15 @@ network.lopf(network.snapshots) print("Objective:",network.objective) -print(pd.DataFrame({attr: network.stores_t[attr]["0 reservoir"] for attr in ["p","e"]})) - -print(pd.DataFrame({attr: network.stores_t[attr]["1 reservoir"] for attr in ["p","e"]})) +print(network.generators_t.p) print(network.links_t.p0) -print(network.generators_t.p) +print(network.links_t.p1) + +print(network.links_t.p2) + +print(pd.DataFrame({attr: network.stores_t[attr]["0 reservoir"] for attr in ["p","e"]})) + +print(pd.DataFrame({attr: network.stores_t[attr]["1 reservoir"] for attr in ["p","e"]})) From 0c7dcc8b326ac2c3ab0e468a222dd7bd8af73258 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Sun, 11 Feb 2018 14:55:20 +0100 Subject: [PATCH 087/135] website, doc: Optimisation includes energy system as a whole Update CSS theme font. --- README.rst | 15 ++++++++------- doc/introduction.rst | 10 +++++----- website/index.org | 6 +++--- website/theme.css | 16 +++++++++------- 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/README.rst b/README.rst index d7deb3209..f25865b44 100644 --- a/README.rst +++ b/README.rst @@ -20,9 +20,9 @@ PyPSA is a `free software `_ toolbox for simulating and optimising modern power systems that include features such as conventional generators with unit commitment, variable wind -and solar generation, storage units, sector coupling and mixed -alternating and direct current networks. PyPSA is designed to scale -well with large networks and long time series. +and solar generation, storage units, coupling to other energy sectors, +and mixed alternating and direct current networks. PyPSA is designed +to scale well with large networks and long time series. As of 2018 PyPSA is under heavy development and therefore it is recommended to use caution when using it in a production environment. @@ -65,10 +65,11 @@ PyPSA can calculate: and storage dispatch within network constraints, using the linear network equations, over several snapshots) * security-constrained linear optimal power flow -* total electricity system least-cost investment optimisation (using - linear network equations, over several snapshots simultaneously for - optimisation of generation and storage dispatch and investment in - the capacities of generation, storage and transmission) +* total electricity/energy system least-cost investment optimisation + (using linear network equations, over several snapshots + simultaneously for optimisation of generation and storage dispatch + and investment in the capacities of generation, storage, + transmission and other infrastructure) It has models for: diff --git a/doc/introduction.rst b/doc/introduction.rst index d2bedbee5..a335b16bd 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -8,9 +8,9 @@ PyPSA is a `free software `_ toolbox for simulating and optimising modern power systems that include features such as conventional generators with unit commitment, variable wind -and solar generation, storage units, sector coupling and mixed -alternating and direct current networks. PyPSA is designed to scale -well with large networks and long time series. +and solar generation, storage units, coupling to other energy sectors, +and mixed alternating and direct current networks. PyPSA is designed +to scale well with large networks and long time series. As of 2018 PyPSA is under heavy development and therefore it is @@ -36,10 +36,10 @@ PyPSA can calculate: dispatch within network constraints, using the linear network equations, over several snapshots) * security-constrained linear optimal power flow -* total electricity system least-cost investment optimisation (using linear +* total electricity/energy system least-cost investment optimisation (using linear network equations, over several snapshots simultaneously for optimisation of generation and storage dispatch and investment in - the capacities of generation, storage and transmission) + the capacities of generation, storage, transmission and other infrastructure) It has models for: diff --git a/website/index.org b/website/index.org index 306f53f67..afaa1409d 100644 --- a/website/index.org +++ b/website/index.org @@ -8,7 +8,7 @@ PyPSA stands for "Python for Power System Analysis". It is pronounced PyPSA is a [[http://www.gnu.org/philosophy/free-sw.en.html][free software]] toolbox for simulating and optimising modern power systems that include features such as conventional generators with unit commitment, variable wind and solar generation, storage -units, sector coupling and mixed alternating and direct current +units, coupling to other energy sectors, and mixed alternating and direct current networks. PyPSA is designed to scale well with large networks and long time series. @@ -53,10 +53,10 @@ PyPSA can calculate: dispatch within network constraints, using the linear network equations, over several snapshots) - security-constrained linear optimal power flow -- total electricity system least-cost investment optimisation (using linear +- total electricity/energy system least-cost investment optimisation (using linear network equations, over several snapshots simultaneously for optimisation of generation and storage dispatch and investment in the - capacities of generation, storage and transmission) + capacities of generation, storage, transmission and other infrastructure) It has models for: diff --git a/website/theme.css b/website/theme.css index bc05db49f..0fb5d108d 100644 --- a/website/theme.css +++ b/website/theme.css @@ -1,12 +1,17 @@ +@import url(https://fonts.googleapis.com/css?family=Noto+Serif); + +body { + font-family: "Noto Serif", serif; /* wikitribune */ + font-size: 1.1rem; + line-height: 1.8rem; +} + #outer_box { width:760px; margin-left: auto; margin-right: auto; -font-size:120%; -text-align: justify; -line-height: 130%; } @@ -27,26 +32,23 @@ margin: 0 0 20px 40px; h1 { font-size:150%; -line-height:1.4em; } h2 { font-size:120%; -line-height:1.4em; } h3 { font-size:110%; -line-height:1.4em; } #submenu { padding: 0; margin: 0 0 0 0; height: 2em; /* Setting a height makes it act like a block */ - font-size:100%; + font-size:130%; } #submenu li { From b8691ffa759c1859b81203abc10c44938b82d173 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Wed, 21 Feb 2018 08:10:05 +0100 Subject: [PATCH 088/135] doc, website: Add references to PyPSA-animation --- README.rst | 7 ++++++- doc/img/pypsa-animation.png | Bin 0 -> 177198 bytes doc/introduction.rst | 11 +++++++++++ doc/release_notes.rst | 2 +- website/animations/index.org | 8 ++++++++ website/animations/pypsa-eur-30 | 1 + website/generate.py | 1 + website/index.org | 8 +++++++- 8 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 doc/img/pypsa-animation.png create mode 100644 website/animations/index.org create mode 120000 website/animations/pypsa-eur-30 diff --git a/README.rst b/README.rst index f25865b44..66173250e 100644 --- a/README.rst +++ b/README.rst @@ -122,7 +122,12 @@ available as Python scripts in `examples/ `_. Screenshots =========== -The showcase for PyPSA is the `SciGRID example +Results from a PyPSA simulation can be converted into an interactive +online animation using `PyPSA-animation +`_, see the `PyPSA-Eur-30 +example `_. + +Another showcase for PyPSA is the `SciGRID example `_ which demonstrates interactive plots generated with the `plotly `_ library. diff --git a/doc/img/pypsa-animation.png b/doc/img/pypsa-animation.png new file mode 100644 index 0000000000000000000000000000000000000000..ac94406711f8a83766fdb2f511600bc201cb3044 GIT binary patch literal 177198 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 Z{{d?A_$Y2VjiLYm002ovPDHLkV1i>NRPg`+ literal 0 HcmV?d00001 diff --git a/doc/introduction.rst b/doc/introduction.rst index a335b16bd..6a627aeb9 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -94,6 +94,17 @@ Screenshots =========== +Results from a PyPSA simulation can be converted into an interactive +online animation using `PyPSA-animation +`_, see the `PyPSA-Eur-30 +example `_. + +Another showcase for PyPSA is the `SciGRID example +`_ which +demonstrates interactive plots generated with the `plotly +`_ library. + + .. image:: img/line-loading.png .. image:: img/lmp.png diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 1c2ff3c76..064281005 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -36,7 +36,7 @@ internal API changes. * The export and import code has been refactored to be more general and abstract. This does not affect the API. * The internally-used sets such as ``pypsa.components.all_components`` - and ``pypsa.one_port_components`` have been moved from + and ``pypsa.components.one_port_components`` have been moved from ``pypsa.components`` to ``network``, i.e. ``network.all_components`` and ``network.one_port_components``, since these sets may change from network to network. diff --git a/website/animations/index.org b/website/animations/index.org new file mode 100644 index 000000000..6b2e86a15 --- /dev/null +++ b/website/animations/index.org @@ -0,0 +1,8 @@ +#+TITLE: Python for Power System Analysis: Animations +#+OPTIONS: toc:nil no default TOC + +PyPSA datasets can be animated with [[https://github.com/PyPSA/PyPSA-animation][PyPSA-animation]]. + +Here is a live example: + +- [[./pypsa-eur-30/][PyPSA-Eur-30]]: Europe with 95% renewable electricity diff --git a/website/animations/pypsa-eur-30 b/website/animations/pypsa-eur-30 new file mode 120000 index 000000000..abfe0fcfa --- /dev/null +++ b/website/animations/pypsa-eur-30 @@ -0,0 +1 @@ +../../../../../programming/html5/d3-network \ No newline at end of file diff --git a/website/generate.py b/website/generate.py index 9cb820fdf..0c836436b 100644 --- a/website/generate.py +++ b/website/generate.py @@ -9,6 +9,7 @@ ["examples/index.html","examples"], ["doc/index.html","documentation"], ["publications/index.html","publications"], + ["animations/index.html","animations"], ["forum/index.html","forum"], ] diff --git a/website/index.org b/website/index.org index afaa1409d..87c6ed089 100644 --- a/website/index.org +++ b/website/index.org @@ -106,7 +106,13 @@ There are [[./examples/index.html][extensive examples]] available as Jupyter not * Screenshots -The showcase for PyPSA is the [[https://pypsa.org/examples/scigrid-lopf-then-pf-plotly.html][SciGRID example]] which demonstrates + + +Results from a PyPSA simulation can be converted into an interactive +online animation using [[https://github.com/PyPSA/PyPSA-animation][PyPSA-animation]], see the [[https://www.pypsa.org/animations/pypsa-eur-30/][PyPSA-Eur-30 example]]. + + +Another showcase for PyPSA is the [[https://pypsa.org/examples/scigrid-lopf-then-pf-plotly.html][SciGRID example]] which demonstrates interactive plots generated with the [[https://plot.ly/python/][plotly]] library. From ad009b2b5fa5017577d50771588650a6f083d62e Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Thu, 22 Feb 2018 09:34:32 +0100 Subject: [PATCH 089/135] website: Update animation img to remove reservoir from consumers Fix year for sector coupling paper. --- doc/img/pypsa-animation.png | Bin 177198 -> 174645 bytes website/publications/index.org | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/img/pypsa-animation.png b/doc/img/pypsa-animation.png index ac94406711f8a83766fdb2f511600bc201cb3044..65ace04ea46f446f85e863700ae10005675fc1c3 100644 GIT binary patch literal 174645 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 z%0A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åGEIel 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 literal 177198 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 Z{{d?A_$Y2VjiLYm002ovPDHLkV1i>NRPg`+ diff --git a/website/publications/index.org b/website/publications/index.org index f0e087d65..4420c4639 100644 --- a/website/publications/index.org +++ b/website/publications/index.org @@ -37,7 +37,7 @@ linked from the overall PyPSA Zenodo DOI: The following research papers have used PyPSA: -- T. Brown, D. Schlachtberger, A. Kies, S. Schramm, M. Greiner, [[https://arxiv.org/abs/1801.05290][Synergies of sector coupling and transmission extension in a cost-optimised, highly renewable European energy system]], 2017, [[https://arxiv.org/abs/1801.05290][preprint arXiv:1801.05290]], [[https://zenodo.org/record/1146665][all input data, code and output data on Zenodo]] +- T. Brown, D. Schlachtberger, A. Kies, S. Schramm, M. Greiner, [[https://arxiv.org/abs/1801.05290][Synergies of sector coupling and transmission extension in a cost-optimised, highly renewable European energy system]], 2018, [[https://arxiv.org/abs/1801.05290][preprint arXiv:1801.05290]], [[https://zenodo.org/record/1146665][all input data, code and output data on Zenodo]] - J. H\ouml{}rsch, Joanne Calitz, [[https://arxiv.org/abs/1710.11199][PyPSA-ZA: Investment and operation co-optimization of integrating wind and solar in South Africa at high spatial and temporal detail]], presented at the [[http://windac-africa.com/][WindAc Africa Conference]], 2017, [[https://arxiv.org/abs/1710.11199][preprint arXiv:1710.11199]], [[https://github.com/FRESNA/pypsa-za][code]] - Markus Groissb\ouml{}ck, Alexandre Gusmao, [[https://arxiv.org/abs/1709.03761][Impact of high renewable penetration scenarios on system reliability: two case studies in the Kingdom of Saudi Arabia]], 2017, [[https://arxiv.org/abs/1709.03761][preprint arXiv:1709.03761]] - J. H\ouml{}rsch, T. Brown, [[https://doi.org/10.1109/EEM.2017.7982024][The role of spatial scale in joint optimisations of generation and transmission for European highly renewable scenarios]], 2017, [[http://eem2017.com/][14th International Conference on the European Energy Market - EEM 2017]], [[https://arxiv.org/abs/1705.07617][preprint arXiv:1705.07617]], [[https://doi.org/10.1109/EEM.2017.7982024][DOI: 10.1109/EEM.2017.7982024]] From d08da981990f17610f8174fdff052657a4d78076 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Thu, 1 Mar 2018 09:20:25 +0100 Subject: [PATCH 090/135] doc: Fix RE-INVEST link --- doc/release_notes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 064281005..f6dad024b 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -57,7 +57,7 @@ effective reactance that sped up the LOPF code and Tom Edwards for pointing out the Pyomo version dependency issue. For this release we also acknowledge funding to Tom Brown from the -`RE-InVEST project `_. +`RE-INVEST project `_. From ab36997dde539630cc2d84d527bff95a7b626879 Mon Sep 17 00:00:00 2001 From: ksyranid Date: Thu, 1 Mar 2018 16:20:33 +0100 Subject: [PATCH 091/135] Fix copying network with overridden components or component attributes. --- pypsa/components.py | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/pypsa/components.py b/pypsa/components.py index ce9ec2f9e..3f4ae26dd 100644 --- a/pypsa/components.py +++ b/pypsa/components.py @@ -226,9 +226,6 @@ def __init__(self, import_name=None, name="", ignore_standard_types=False, logger.warning("The argument csv_folder_name for initiating Network() is deprecated, please use import_name instead.") import_name = kwargs.pop('csv_folder_name') - # Initialise root logger and set its level, if this has not been done before - logging.basicConfig(level=logging.INFO) - from . import __version__ as pypsa_version Basic.__init__(self, name) @@ -605,6 +602,28 @@ def mremove(self, class_name, names): df.drop(df.columns.intersection(names), axis=1, inplace=True) + def _retrieve_overridden_components(self): + override_components = components.copy() + override_component_attrs = Dict({k: v.copy() for k, v in component_attrs.items()}) + + # add new components + new_components = {new_component: self.components[new_component] for new_component in + (set(self.components.keys()) - set(components.index))} + + for new_component in new_components.keys(): + override_components.loc[new_component] = [new_components[new_component]['list_name'], + new_components[new_component]['description'], + new_components[new_component]['type']] + override_component_attrs[new_component] = self.component_attrs[new_component] + + # override attributes of standard components + for component in self.all_components - set(new_components.keys()): + extra_attrs = self.component_attrs[component].index.difference(component_attrs[component].index) + if not extra_attrs.empty: + override_component_attrs[component] = self.component_attrs[component] + + return override_components, override_component_attrs + def copy(self, with_time=True, ignore_standard_types=False): """ @@ -628,7 +647,11 @@ def copy(self, with_time=True, ignore_standard_types=False): """ - network = self.__class__(ignore_standard_types=ignore_standard_types) + override_components, override_component_attrs = self._retrieve_overridden_components() + + network = self.__class__(ignore_standard_types=ignore_standard_types, + override_components=override_components, + override_component_attrs=override_component_attrs) for component in self.iterate_components(["Bus", "Carrier"] + sorted(self.all_components - {"Bus","Carrier"})): df = component.df @@ -684,7 +707,8 @@ def __getitem__(self, key): else: time_i = slice(None) - n = self.__class__() + override_components, override_component_attrs = self._retrieve_overridden_components() + n = self.__class__(override_components=override_components, override_component_attrs=override_component_attrs) n.import_components_from_dataframe( pd.DataFrame(self.buses.ix[key]).assign(sub_network=""), "Bus" From a5394a33e3032db01345078a34afbe88e3574a55 Mon Sep 17 00:00:00 2001 From: ksyranid Date: Thu, 1 Mar 2018 17:08:34 +0100 Subject: [PATCH 092/135] Revert deletion of logger initialization. --- pypsa/components.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pypsa/components.py b/pypsa/components.py index 3f4ae26dd..d7319f3f3 100644 --- a/pypsa/components.py +++ b/pypsa/components.py @@ -226,6 +226,9 @@ def __init__(self, import_name=None, name="", ignore_standard_types=False, logger.warning("The argument csv_folder_name for initiating Network() is deprecated, please use import_name instead.") import_name = kwargs.pop('csv_folder_name') + # Initialise root logger and set its level, if this has not been done before + logging.basicConfig(level=logging.INFO) + from . import __version__ as pypsa_version Basic.__init__(self, name) From 2aa833b02e3ee307afa1933decf2396690b097f0 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Mon, 26 Mar 2018 11:33:25 +0200 Subject: [PATCH 093/135] io: Fix NetCDF static im-/export with all-standard components --- pypsa/io.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/pypsa/io.py b/pypsa/io.py index da4d06091..e7f588140 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -201,16 +201,21 @@ def get_attributes(self): if attr.startswith('network_')} def get_snapshots(self): - return self.get_static('snapshots') + return self.get_static('snapshots', 'snapshots') - def get_static(self, list_name): + def get_static(self, list_name, index_name=None): t = list_name + '_' i = len(t) - df = pd.DataFrame.from_dict({attr[i:]: self.ds[attr].to_pandas() - for attr in iterkeys(self.ds.data_vars) - if attr.startswith(t) and attr[i:i+2] != 't_'}) - df.index.name = 'name' - return df if not df.empty else None + if index_name is None: + index_name = list_name + '_i' + if index_name not in self.ds.coords: + return None + index = self.ds.coords[index_name].to_index().rename('name') + df = pd.DataFrame(index=index) + for attr in iterkeys(self.ds.data_vars): + if attr.startswith(t) and attr[i:i+2] != 't_': + df[attr[i:]] = self.ds[attr].to_pandas() + return df def get_series(self, list_name): t = list_name + '_t_' @@ -238,6 +243,7 @@ def save_snapshots(self, snapshots): def save_static(self, list_name, df): df.index.name = list_name + '_i' + self.ds[list_name + '_i'] = df.index for attr in df.columns: self.ds[list_name + '_' + attr] = df[attr] From 8c99f8cf76d93ea1dc52665f450b8b086b0fe4ef Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Mon, 26 Mar 2018 23:10:42 +0200 Subject: [PATCH 094/135] io: Fix HDF5 im-/export with empty components and lift the 1000 buses limit --- pypsa/io.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/pypsa/io.py b/pypsa/io.py index e7f588140..05eec7b5b 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -141,6 +141,7 @@ def remove_series(self, list_name, attr): class ImporterHDF5(Importer): def __init__(self, path): self.ds = pd.HDFStore(path) + self.index = {} def get_attributes(self): return dict(self.ds["/network"].reset_index().iloc[0]) @@ -149,18 +150,25 @@ def get_snapshots(self): return self.ds["/snapshots"] if "/snapshots" in self.ds else None def get_static(self, list_name): - return (self.ds["/" + list_name] - if "/" + list_name in self.ds else None) + if "/" + list_name not in self.ds: + return None + + df = self.ds["/" + list_name].set_index('name') + self.index[list_name] = df.index + return df def get_series(self, list_name): for tab in self.ds: if tab.startswith('/' + list_name + '_t/'): attr = tab[len('/' + list_name + '_t/'):] - yield attr, self.ds[tab] + df = self.ds[tab] + df.columns = self.index[list_name][df.columns] + yield attr, df class ExporterHDF5(Exporter): def __init__(self, path, **kwargs): self.ds = pd.HDFStore(path, mode='w', **kwargs) + self.index = {} def save_attributes(self, attrs): name = attrs.pop('name') @@ -172,9 +180,13 @@ def save_snapshots(self, snapshots): self.ds.put('/snapshots', snapshots, format='table', index=False) def save_static(self, list_name, df): + df.index.name = 'name' + self.index[list_name] = df.index + df = df.reset_index() self.ds.put('/' + list_name, df, format='table', index=False) def save_series(self, list_name, attr, df): + df.columns = self.index[list_name].get_indexer(df.columns) self.ds.put('/' + list_name + '_t/' + attr, df, format='table', index=False) if has_xarray: @@ -234,7 +246,7 @@ def __init__(self, path, least_significant_digit=None): def save_attributes(self, attrs): self.ds.attrs.update(('network_' + attr, val) - for attr, val in iteritems(attrs)) + for attr, val in iteritems(attrs)) def save_snapshots(self, snapshots): snapshots.index.name = 'snapshots' @@ -419,10 +431,6 @@ def import_from_hdf5(network, path, skip_time=False): Skip reading in time dependent attributes """ - logger.warning(dedent("""HDF5 file format for PyPSA is now deprecated, - because HDF5 fails for tables with more than 1000 columns. - Please use netCDF instead.""")) - basename = os.path.basename(path) with ImporterHDF5(path) as importer: _import_from_importer(network, importer, basename=basename, skip_time=skip_time) @@ -451,10 +459,6 @@ def export_to_hdf5(network, path, export_standard_types=False, **kwargs): >>> network.export_to_hdf5(filename) """ - logger.warning(dedent("""HDF5 file format for PyPSA is now deprecated, - because HDF5 fails for tables with more than 1000 columns. - Please use netCDF instead.""")) - kwargs.setdefault('complevel', 4) basename = os.path.basename(path) From 8c0a3b08cebf02e505b7f88d765e041d827cebc0 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Tue, 27 Mar 2018 20:11:01 +0200 Subject: [PATCH 095/135] components: generalise _retrieve_overridden_components Now it also works more generally, including when components are removed during override, by making an exact copy of the components. This function is used in Network.copy() and Network.__getitem__. --- pypsa/components.py | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/pypsa/components.py b/pypsa/components.py index d7319f3f3..f2e1425ad 100644 --- a/pypsa/components.py +++ b/pypsa/components.py @@ -606,24 +606,16 @@ def mremove(self, class_name, names): def _retrieve_overridden_components(self): - override_components = components.copy() - override_component_attrs = Dict({k: v.copy() for k, v in component_attrs.items()}) - - # add new components - new_components = {new_component: self.components[new_component] for new_component in - (set(self.components.keys()) - set(components.index))} - - for new_component in new_components.keys(): - override_components.loc[new_component] = [new_components[new_component]['list_name'], - new_components[new_component]['description'], - new_components[new_component]['type']] - override_component_attrs[new_component] = self.component_attrs[new_component] - - # override attributes of standard components - for component in self.all_components - set(new_components.keys()): - extra_attrs = self.component_attrs[component].index.difference(component_attrs[component].index) - if not extra_attrs.empty: - override_component_attrs[component] = self.component_attrs[component] + + components_index = list(self.components.keys()) + + cols = ["list_name","description","type"] + + override_components = pd.DataFrame([[self.components[i][c] for c in cols] for i in components_index], + columns=cols, + index=components_index) + + override_component_attrs = Dict({i : self.component_attrs[i].copy() for i in components_index}) return override_components, override_component_attrs From cc743d1c9cbeaf3f04ef136c4aaec7cd73da0f44 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Tue, 27 Mar 2018 20:43:31 +0200 Subject: [PATCH 096/135] io: Work around to enable import of HDF5 exports from old PyPSA --- pypsa/io.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/pypsa/io.py b/pypsa/io.py index 05eec7b5b..1bc71a844 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -153,7 +153,11 @@ def get_static(self, list_name): if "/" + list_name not in self.ds: return None - df = self.ds["/" + list_name].set_index('name') + if self.pypsa_version is None or self.pypsa_version < [0, 13, 1]: + df = self.ds["/" + list_name] + else: + df = self.ds["/" + list_name].set_index('name') + self.index[list_name] = df.index return df @@ -162,7 +166,8 @@ def get_series(self, list_name): if tab.startswith('/' + list_name + '_t/'): attr = tab[len('/' + list_name + '_t/'):] df = self.ds[tab] - df.columns = self.index[list_name][df.columns] + if self.pypsa_version is not None and self.pypsa_version > [0, 13, 0]: + df.columns = self.index[list_name][df.columns] yield attr, df class ExporterHDF5(Exporter): @@ -540,25 +545,31 @@ def _import_from_importer(network, importer, basename, skip_time=False): """ attrs = importer.get_attributes() + + current_pypsa_version = [int(s) for s in network.pypsa_version.split(".")] + pypsa_version = None + if attrs is not None: network.name = attrs.pop('name') - ##https://docs.python.org/3/tutorial/datastructures.html#comparing-sequences-and-other-types - current_pypsa_version = [int(s) for s in network.pypsa_version.split(".")] try: pypsa_version = [int(s) for s in attrs.pop("pypsa_version").split(".")] except KeyError: pypsa_version = None - if pypsa_version is None or pypsa_version < current_pypsa_version: - logger.warning(dedent(""" + for attr, val in iteritems(attrs): + setattr(network, attr, val) + + ##https://docs.python.org/3/tutorial/datastructures.html#comparing-sequences-and-other-types + if pypsa_version is None or pypsa_version < current_pypsa_version: + logger.warning(dedent(""" Importing PyPSA from older version of PyPSA than current version {}. Please read the release notes at https://pypsa.org/doc/release_notes.html carefully to prepare your network for import. - """).format(network.pypsa_version)) + """).format(network.pypsa_version)) - for attr, val in iteritems(attrs): - setattr(network, attr, val) + importer.pypsa_version = pypsa_version + importer.current_pypsa_version = current_pypsa_version # if there is snapshots.csv, read in snapshot data df = importer.get_snapshots() From ccd1dcdee0804422d38f85b9b76bcc0564c34cb5 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Tue, 27 Mar 2018 20:56:11 +0200 Subject: [PATCH 097/135] doc: Remove warning from HDF5; explain why netCDF is better --- doc/import_export.rst | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/import_export.rst b/doc/import_export.rst index 640f87635..481f13c3c 100644 --- a/doc/import_export.rst +++ b/doc/import_export.rst @@ -188,6 +188,10 @@ Export network and components to a netCDF file. netCDF files take up less space than CSV files and are faster to load. +netCDF is also preferred over HDF5 because netCDF is structured more +cleanly, is easier to use from other programming languages, can limit +float precision to save space and supports lazy loading. + Both static and series attributes of components are exported, but only if they have non-default values. @@ -207,11 +211,13 @@ Import network data from netCDF file ``file.nc``: Export to HDF5 ============== -WARNING: This is now deprecated, because HDF5 fails for tables with -more than 1000 columns. Please use netCDF instead. - Export network and components to an HDF store. +NB: netCDF is preferred over HDF5 because netCDF is structured more +cleanly, is easier to use from other programming languages, can limit +float precision to save space and supports lazy loading. + + Both static and series attributes of components are exported, but only if they have non-default values. @@ -225,9 +231,6 @@ If ``path`` does not already exist, it is created. Import from HDF5 ================ -WARNING: This is now deprecated, because HDF5 fails for tables with -more than 1000 columns. Please use netCDF instead. - Import network data from HDF5 store at ``path``: ``network.import_from_hdf5(path)`` From 85bf33f1f9f1f8691d00910c4256aa8e96e3511e Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Tue, 27 Mar 2018 21:59:24 +0200 Subject: [PATCH 098/135] PyPSA Version 0.13.1 Hyperlinked release notes can be found here: https://www.pypsa.org/doc/release_notes.html#pypsa-0-13-1-27th-march-2018 This release contains bug fixes for the new features introduced in 0.13.0. * Export network to netCDF file bug fixed (components that were all standard except their name were ignored). * Import/export network to HDF5 file bug fixed and now works with more than 1000 columns; HDF5 format is no longer deprecated. * When networks are copied or sliced, overridden components (introduced in 0.13.0) are also copied. * Sundry other small fixes. We thank Tim Kittel for pointing out the first and second bugs. We thank Kostas Syranidis for not only pointing out the third issue with copying overridden components, but also submitting a fix as a pull request. For this release we acknowledge funding to Tom Brown from the RE-INVEST project. --- doc/conf.py | 2 +- doc/release_notes.rst | 23 +++++++++++++++++++++++ pypsa/__init__.py | 2 +- setup.py | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index c79597b4c..3c6f85d98 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -65,7 +65,7 @@ # The short X.Y version. version = u'0.13' # The full version, including alpha/beta/rc tags. -release = u'0.13.0' +release = u'0.13.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/release_notes.rst b/doc/release_notes.rst index f6dad024b..ea8de9745 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -2,6 +2,29 @@ Release Notes ####################### +PyPSA 0.13.1 (27th March 2018) +============================== + +This release contains bug fixes for the new features introduced in +0.13.0. + +* Export network to netCDF file bug fixed (components that were all + standard except their name were ignored). +* Import/export network to HDF5 file bug fixed and now works with more + than 1000 columns; HDF5 format is no longer deprecated. +* When networks are copied or sliced, overridden components + (introduced in 0.13.0) are also copied. +* Sundry other small fixes. + +We thank Tim Kittel for pointing out the first and second bugs. We +thank Kostas Syranidis for not only pointing out the third issue with +copying overridden components, but also submitting a fix as a pull +request. + +For this release we acknowledge funding to Tom Brown from the +`RE-INVEST project `_. + + PyPSA 0.13.0 (25th January 2018) ================================ diff --git a/pypsa/__init__.py b/pypsa/__init__.py index 24a8ff988..87121001c 100644 --- a/pypsa/__init__.py +++ b/pypsa/__init__.py @@ -30,6 +30,6 @@ from .components import Network, SubNetwork -__version__ = "0.13.0" +__version__ = "0.13.1" __author__ = "Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)" __copyright__ = "Copyright 2015-2018 Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS), GNU GPL 3" diff --git a/setup.py b/setup.py index bcfd28d8b..024b8433a 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ setup( name='pypsa', - version='0.13.0', + version='0.13.1', author='Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)', author_email='brown@fias.uni-frankfurt.de', description='Python for Power Systems Analysis', From b17530d599be6a553832cba067c48effc3bc049d Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Wed, 28 Mar 2018 08:30:11 +0200 Subject: [PATCH 099/135] website: Add benefits of cooperation sensitivity paper --- website/publications/index.org | 1 + 1 file changed, 1 insertion(+) diff --git a/website/publications/index.org b/website/publications/index.org index 4420c4639..28949c167 100644 --- a/website/publications/index.org +++ b/website/publications/index.org @@ -37,6 +37,7 @@ linked from the overall PyPSA Zenodo DOI: The following research papers have used PyPSA: +- D. Schlachtberger, T. Brown, M. Schäfer, S. Schramm, M. Greiner, [[https://arxiv.org/abs/1803.09711][Cost optimal scenarios of a future highly renewable European electricity system: Exploring the influence of weather data, cost parameters and policy constraints]], 2018, [[https://arxiv.org/abs/1803.09711][preprint arXiv:1803.09711]] - T. Brown, D. Schlachtberger, A. Kies, S. Schramm, M. Greiner, [[https://arxiv.org/abs/1801.05290][Synergies of sector coupling and transmission extension in a cost-optimised, highly renewable European energy system]], 2018, [[https://arxiv.org/abs/1801.05290][preprint arXiv:1801.05290]], [[https://zenodo.org/record/1146665][all input data, code and output data on Zenodo]] - J. H\ouml{}rsch, Joanne Calitz, [[https://arxiv.org/abs/1710.11199][PyPSA-ZA: Investment and operation co-optimization of integrating wind and solar in South Africa at high spatial and temporal detail]], presented at the [[http://windac-africa.com/][WindAc Africa Conference]], 2017, [[https://arxiv.org/abs/1710.11199][preprint arXiv:1710.11199]], [[https://github.com/FRESNA/pypsa-za][code]] - Markus Groissb\ouml{}ck, Alexandre Gusmao, [[https://arxiv.org/abs/1709.03761][Impact of high renewable penetration scenarios on system reliability: two case studies in the Kingdom of Saudi Arabia]], 2017, [[https://arxiv.org/abs/1709.03761][preprint arXiv:1709.03761]] From 6ef502a263694cfe0bb5772bcbf056da4a3d7bd3 Mon Sep 17 00:00:00 2001 From: jankaeh <33579567+jankaeh@users.noreply.github.com> Date: Thu, 19 Apr 2018 17:57:38 +0200 Subject: [PATCH 100/135] Update plot.py If radius is 0, ratios are NaN because of division by 0. --- pypsa/plot.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pypsa/plot.py b/pypsa/plot.py index a552ed498..d7f60024d 100644 --- a/pypsa/plot.py +++ b/pypsa/plot.py @@ -165,7 +165,10 @@ def compute_bbox_with_margins(margin, x, y): for b_i in bus_sizes.index.levels[0]: s = bus_sizes.loc[b_i] radius = s.sum()**0.5 - ratios = s/s.sum() + if radius == 0.0: + ratios = s + else: + ratios = s/s.sum() start = 0.25 for i, ratio in ratios.iteritems(): From 26aaf85e7f5f465c663078b2c72d830400fe723c Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 14 May 2018 18:24:49 +0200 Subject: [PATCH 101/135] website: Add Joao paper on North Sea offshore grid --- website/publications/index.org | 1 + 1 file changed, 1 insertion(+) diff --git a/website/publications/index.org b/website/publications/index.org index 28949c167..0e19bb19d 100644 --- a/website/publications/index.org +++ b/website/publications/index.org @@ -37,6 +37,7 @@ linked from the overall PyPSA Zenodo DOI: The following research papers have used PyPSA: +- Joao Gorenstein Dedeccaa, Sara Lumbreras, Andres Ramos, Rudi A. Hakvoort Paulien M. Herder, [[https://doi.org/10.1016/j.eneco.2018.04.037][Expansion planning of the North Sea offshore grid: Simulation of integrated governance constraints]], Energy Economics, Volume 72, May 2018, Pages 376-392, [[https://doi.org/10.1016/j.eneco.2018.04.037][DOI: 10.1016/j.eneco.2018.04.037]] - D. Schlachtberger, T. Brown, M. Schäfer, S. Schramm, M. Greiner, [[https://arxiv.org/abs/1803.09711][Cost optimal scenarios of a future highly renewable European electricity system: Exploring the influence of weather data, cost parameters and policy constraints]], 2018, [[https://arxiv.org/abs/1803.09711][preprint arXiv:1803.09711]] - T. Brown, D. Schlachtberger, A. Kies, S. Schramm, M. Greiner, [[https://arxiv.org/abs/1801.05290][Synergies of sector coupling and transmission extension in a cost-optimised, highly renewable European energy system]], 2018, [[https://arxiv.org/abs/1801.05290][preprint arXiv:1801.05290]], [[https://zenodo.org/record/1146665][all input data, code and output data on Zenodo]] - J. H\ouml{}rsch, Joanne Calitz, [[https://arxiv.org/abs/1710.11199][PyPSA-ZA: Investment and operation co-optimization of integrating wind and solar in South Africa at high spatial and temporal detail]], presented at the [[http://windac-africa.com/][WindAc Africa Conference]], 2017, [[https://arxiv.org/abs/1710.11199][preprint arXiv:1710.11199]], [[https://github.com/FRESNA/pypsa-za][code]] From b4af4fdc795a09147827bef757cbffe914c6df65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Tue, 30 Jan 2018 13:27:29 +0100 Subject: [PATCH 102/135] components: Fail properly in Network.add --- pypsa/components.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/pypsa/components.py b/pypsa/components.py index f2e1425ad..f92558b2d 100644 --- a/pypsa/components.py +++ b/pypsa/components.py @@ -430,19 +430,14 @@ def add(self, class_name, name, **kwargs): """ - if class_name not in self.components: - logger.error("Component class {} not found".format(class_name)) - return None + assert class_name in self.components, "Component class {} not found".format(class_name) cls_df = self.df(class_name) cls_pnl = self.pnl(class_name) name = str(name) - if name in cls_df.index: - logger.error("Failed to add {} component {} because there is already an object with this name in {}".format(class_name, name, self.components[class_name]["list_name"])) - return - + assert name not in cls_df.index, "Failed to add {} component {} because there is already an object with this name in {}".format(class_name, name, self.components[class_name]["list_name"]) attrs = self.components[class_name]["attrs"] From 31d837d4e5f2e689795d98228f7e7fbbec5f6308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Thu, 1 Mar 2018 10:42:11 +0100 Subject: [PATCH 103/135] networkclustering: Allow aggregating only a subset of generators --- pypsa/networkclustering.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index c66bd8e5f..f33c02ddc 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -60,9 +60,14 @@ def _haversine(coords): a = np.sin((lat[1]-lat[0])/2.)**2 + np.cos(lat[0]) * np.cos(lat[1]) * np.sin((lon[0] - lon[1])/2.)**2 return 6371.000 * 2 * np.arctan2( np.sqrt(a), np.sqrt(1-a) ) -def aggregategenerators(network, busmap, with_time=True): +def aggregategenerators(network, busmap, with_time=True, carriers=None): + if carriers is None: + carriers = network.generators.carrier.unique() + + gens_agg_b = network.generators.carrier.isin(carriers) attrs = network.components["Generator"]["attrs"] - generators = network.generators.assign(bus=lambda df: df.bus.map(busmap)) + generators = (network.generators.loc[gens_agg_b] + .assign(bus=lambda df: df.bus.map(busmap))) columns = (set(attrs.index[attrs.static & attrs.status.str.startswith('Input')]) | {'weight'}) & set(generators.columns) grouper = [generators.bus, generators.carrier] @@ -74,15 +79,21 @@ def aggregategenerators(network, busmap, with_time=True): new_df = generators.groupby(grouper, axis=0).agg(strategies) new_df.index = _flatten_multiindex(new_df.index).rename("name") + new_df = pd.concat([new_df, + network.generators.loc[~gens_agg_b] + .assign(bus=lambda df: df.bus.map(busmap))], axis=0) + new_pnl = dict() if with_time: for attr, df in iteritems(network.generators_t): - if not df.empty: + pnl_gens_agg_b = df.columns.to_series().map(gens_agg_b) + df_agg = df.loc[:, pnl_gens_agg_b] + if not df_agg.empty: if attr == 'p_max_pu': - df = df.multiply(weighting.loc[df.columns], axis=1) - pnl_df = df.groupby(grouper, axis=1).sum() + df_agg = df_agg.multiply(weighting.loc[df_agg.columns], axis=1) + pnl_df = df_agg.groupby(grouper, axis=1).sum() pnl_df.columns = _flatten_multiindex(pnl_df.columns).rename("name") - new_pnl[attr] = pnl_df + new_pnl[attr] = pd.concat([df.loc[:, ~pnl_gens_agg_b], pnl_df], axis=1) return new_df, new_pnl @@ -209,6 +220,7 @@ def get_buses_linemap_and_lines(network, busmap, line_length_factor=1.0, bus_str def get_clustering_from_busmap(network, busmap, with_time=True, line_length_factor=1.0, aggregate_generators_weighted=False, aggregate_one_ports={}, + aggregate_generators_carriers=None, bus_strategies=dict()): buses, linemap, linemap_p, linemap_n, lines = get_buses_linemap_and_lines(network, busmap, line_length_factor, bus_strategies) @@ -225,7 +237,8 @@ def get_clustering_from_busmap(network, busmap, with_time=True, line_length_fact if aggregate_generators_weighted: one_port_components.remove("Generator") - generators, generators_pnl = aggregategenerators(network, busmap, with_time=with_time) + generators, generators_pnl = aggregategenerators(network, busmap, with_time=with_time, + carriers=aggregate_generators_carriers) io.import_components_from_dataframe(network_c, generators, "Generator") if with_time: for attr, df in iteritems(generators_pnl): From cea63aa1aad256c25aadf4e5d9110161e277f2c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Mon, 4 Jun 2018 17:36:10 +0200 Subject: [PATCH 104/135] plot: Fix regression when choosing bus_colors ax.scatter needs the edgecolor='face' parameter to use s and c parameters, simultaneously --- pypsa/plot.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pypsa/plot.py b/pypsa/plot.py index a552ed498..fbfe3dd76 100644 --- a/pypsa/plot.py +++ b/pypsa/plot.py @@ -177,11 +177,8 @@ def compute_bbox_with_margins(margin, x, y): ax.add_collection(bus_collection) else: c = pd.Series(bus_colors, index=network.buses.index) - if c.dtype == np.dtype('O'): - c.fillna("b", inplace=True) - c = list(c.values) s = pd.Series(bus_sizes, index=network.buses.index, dtype="float").fillna(10) - bus_collection = ax.scatter(x, y, c=c, s=s, cmap=bus_cmap) + bus_collection = ax.scatter(x, y, c=c, s=s, cmap=bus_cmap, edgecolor='face') def as_branch_series(ser): if isinstance(ser, dict) and set(ser).issubset(branch_components): From d21c6355c274580abd44bf38c86a9aa620000e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Thu, 21 Jun 2018 02:17:31 +0200 Subject: [PATCH 105/135] opt: Extend pyomo hack to pyomo 5.6+ Thanks to Soner Candas for reporting the problem and the providing the fix. --- pypsa/opt.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/pypsa/opt.py b/pypsa/opt.py index fb1a4cc97..d866aef86 100644 --- a/pypsa/opt.py +++ b/pypsa/opt.py @@ -145,6 +145,25 @@ def __init__(self,lhs=None,sense="==",rhs=None): def __repr__(self): return "{} {} {}".format(self.lhs, self.sense, self.rhs) +try: + from pyomo.core.base import expr_coopr3 + + def _build_sum_expression(variables, constant=0.): + expr = expr_coopr3._SumExpression() + expr._args = [item[1] for item in variables] + expr._coef = [item[0] for item in variables] + expr._const = constant + return expr +except ImportError: + from pyomo.core.expr import expr_pyomo5 + + def _build_sum_expression(variables, constant=0.): + expr = expr_pyomo5.LinearExpression() + expr.linear_vars = [item[1] for item in variables] + expr.linear_coefs = [item[0] for item in variables] + expr.constant = constant + return expr + def l_constraint(model,name,constraints,*args): """A replacement for pyomo's Constraint that quickly builds linear @@ -201,10 +220,8 @@ def l_constraint(model,name,constraints,*args): constant = c[2] v._data[i] = pyomo.core.base.constraint._GeneralConstraintData(None,v) - v._data[i]._body = pyomo.core.base.expr_coopr3._SumExpression() - v._data[i]._body._args = [item[1] for item in variables] - v._data[i]._body._coef = [item[0] for item in variables] - v._data[i]._body._const = 0. + v._data[i]._body = _build_sum_expression(variables) + if sense == "==": v._data[i]._equality = True v._data[i]._lower = pyomo.core.base.numvalue.NumericConstant(constant) @@ -254,11 +271,7 @@ def l_objective(model,objective=None): #initialise with a dummy model.objective = Objective(expr = 0.) - - model.objective._expr = pyomo.core.base.expr_coopr3._SumExpression() - model.objective._expr._args = [item[1] for item in objective.variables] - model.objective._expr._coef = [item[0] for item in objective.variables] - model.objective._expr._const = objective.constant + model.objective._expr = _build_sum_expression(objective.variables, constant=objective.constant) def free_pyomo_initializers(obj): obj.construct() From 1595d5cbf08e7919fde76e21b49735ba7b6942d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Thu, 21 Jun 2018 15:42:50 +0200 Subject: [PATCH 106/135] Skip tests against pypower PyPOWER 5.0.0 is broken with recent numpy and unmaintained since Aug 2017. --- test/test_lpf_against_pypower.py | 7 +++++++ test/test_pf_against_pypower.py | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/test/test_lpf_against_pypower.py b/test/test_lpf_against_pypower.py index 0546c1312..e313c2a86 100644 --- a/test/test_lpf_against_pypower.py +++ b/test/test_lpf_against_pypower.py @@ -8,13 +8,20 @@ from pypower.api import ppoption, runpf, case30 as case +from pypower.ppver import ppver +from distutils.version import StrictVersion +pypower_version = StrictVersion(ppver()['Version']) + import pandas as pd import numpy as np +import pytest +@pytest.mark.skipif(pypower_version <= '5.0.0', + reason="PyPOWER 5.0.0 is broken with recent numpy and unmaintained since Aug 2017.") def test_pypower_case(): #ppopt is a dictionary with the details of the optimization routine to run diff --git a/test/test_pf_against_pypower.py b/test/test_pf_against_pypower.py index f6ebc32e2..6413fe949 100644 --- a/test/test_pf_against_pypower.py +++ b/test/test_pf_against_pypower.py @@ -5,14 +5,20 @@ from pypower.api import ppoption, runpf, case118 as case +from pypower.ppver import ppver +from distutils.version import StrictVersion +pypower_version = StrictVersion(ppver()['Version']) import pandas as pd import numpy as np +import pytest +@pytest.mark.skipif(pypower_version <= '5.0.0', + reason="PyPOWER 5.0.0 is broken with recent numpy and unmaintained since Aug 2017.") def test_pypower_case(): #ppopt is a dictionary with the details of the optimization routine to run From 3fdeb43a34264965e79f8153dd9b3131c427526b Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Wed, 25 Jul 2018 16:37:24 +0200 Subject: [PATCH 107/135] website, doc: Add latest publications, add latest affiliations --- README.rst | 10 +++++++++- doc/introduction.rst | 9 ++++++++- website/examples/index.org | 2 +- website/index.org | 18 ++++++++++-------- website/publications/index.org | 20 ++++++++++++-------- 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/README.rst b/README.rst index 66173250e..4d4a879ff 100644 --- a/README.rst +++ b/README.rst @@ -31,7 +31,15 @@ the `doc/release_notes.rst `_. -PyPSA was initially developed by the `Renewable Energy Group + +This project is maintained by the `Energy System Modelling +group `_ at the `Institute for +Automation and Applied +Informatics `_ at the +`Karlsruhe Institute of +Technology `_. The group is funded by the +`Helmholtz Association `_ until 2024. +Previous versions were developed by the `Renewable Energy Group `_ at `FIAS `_ to carry out simulations for the `CoNDyNet project `_, financed by the diff --git a/doc/introduction.rst b/doc/introduction.rst index 6a627aeb9..04576d32a 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -18,7 +18,14 @@ recommended to use caution when using it in a production environment. Some APIs may change - the changes in each PyPSA version are listed in the :doc:`release_notes`. -PyPSA was initially developed by the `Renewable Energy Group +This project is maintained by the `Energy System Modelling +group `_ at the `Institute for +Automation and Applied +Informatics `_ at the +`Karlsruhe Institute of +Technology `_. The group is funded by the +`Helmholtz Association `_ until 2024. +Previous versions were developed by the `Renewable Energy Group `_ at `FIAS `_ to carry out simulations for the `CoNDyNet project `_, financed by the diff --git a/website/examples/index.org b/website/examples/index.org index ab4df47ba..677f2810f 100644 --- a/website/examples/index.org +++ b/website/examples/index.org @@ -75,4 +75,4 @@ repository]]. If you have a nice example of using PyPSA, send your Jupyter notebook to -Tom Brown (brown at fias.uni-frankfurt.de). +Tom Brown (firstname.lastname@kit.edu). diff --git a/website/index.org b/website/index.org index 87c6ed089..7b1606f2b 100644 --- a/website/index.org +++ b/website/index.org @@ -17,14 +17,16 @@ recommended to use caution when using it in a production environment. Some APIs may change - the changes in each PyPSA version are listed in the [[./doc/release_notes.html][release notes]]. -PyPSA was initially developed by the -[[https://fias.uni-frankfurt.de/physics/schramm/renewable-energy-system-and-network-analysis/][Renewable -Energy Group]] at [[https://fias.uni-frankfurt.de/][FIAS]] to carry out -simulations for the [[http://condynet.de/][CoNDyNet project]], financed -by the [[https://www.bmbf.de/en/index.html][German Federal Ministry for -Education and Research (BMBF)]] as part of the -[[http://forschung-stromnetze.info/projekte/grundlagen-und-konzepte-fuer-effiziente-dezentrale-stromnetze/][Stromnetze -Research Initiative]]. + + +This project is maintained by the [[https://www.iai.kit.edu/english/2338.php][Energy System Modelling group]] at the +[[https://www.iai.kit.edu/english/index.php][Institute for Automation and Applied Informatics]] at the [[http://www.kit.edu/english/index.php][Karlsruhe +Institute of Technology]]. The group is funded by the [[https://www.helmholtz.de/en/][Helmholtz +Association]] until 2024. Previous versions were developed by the +[[https://fias.uni-frankfurt.de/physics/schramm/renewable-energy-system-and-network-analysis/][Renewable Energy Group]] at [[https://fias.uni-frankfurt.de/][FIAS]] to carry out simulations for the +[[http://condynet.de/][CoNDyNet project]], financed by the [[https://www.bmbf.de/en/index.html][German Federal Ministry for +Education and Research (BMBF)]] as part of the [[http://forschung-stromnetze.info/projekte/grundlagen-und-konzepte-fuer-effiziente-dezentrale-stromnetze/][Stromnetze Research +Initiative]]. * Download diff --git a/website/publications/index.org b/website/publications/index.org index 0e19bb19d..d14560cb7 100644 --- a/website/publications/index.org +++ b/website/publications/index.org @@ -37,16 +37,20 @@ linked from the overall PyPSA Zenodo DOI: The following research papers have used PyPSA: -- Joao Gorenstein Dedeccaa, Sara Lumbreras, Andres Ramos, Rudi A. Hakvoort Paulien M. Herder, [[https://doi.org/10.1016/j.eneco.2018.04.037][Expansion planning of the North Sea offshore grid: Simulation of integrated governance constraints]], Energy Economics, Volume 72, May 2018, Pages 376-392, [[https://doi.org/10.1016/j.eneco.2018.04.037][DOI: 10.1016/j.eneco.2018.04.037]] -- D. Schlachtberger, T. Brown, M. Schäfer, S. Schramm, M. Greiner, [[https://arxiv.org/abs/1803.09711][Cost optimal scenarios of a future highly renewable European electricity system: Exploring the influence of weather data, cost parameters and policy constraints]], 2018, [[https://arxiv.org/abs/1803.09711][preprint arXiv:1803.09711]] -- T. Brown, D. Schlachtberger, A. Kies, S. Schramm, M. Greiner, [[https://arxiv.org/abs/1801.05290][Synergies of sector coupling and transmission extension in a cost-optimised, highly renewable European energy system]], 2018, [[https://arxiv.org/abs/1801.05290][preprint arXiv:1801.05290]], [[https://zenodo.org/record/1146665][all input data, code and output data on Zenodo]] +- T. Brown, D. Schlachtberger, A. Kies, S. Schramm, M. Greiner, [[https://arxiv.org/abs/1801.05290][Synergies of sector coupling and transmission extension in a cost-optimised, highly renewable European energy system]], *Energy*, Volume 160, Pages 720-739, 2018, [[https://arxiv.org/abs/1801.05290][postprint arXiv:1801.05290]], [[https://doi.org/10.1016/j.energy.2018.06.222][DOI: 10.1016/j.energy.2018.06.222]], [[https://zenodo.org/record/1146665][all input data, code and output data on Zenodo]] +- Joao Gorenstein Dedecca, Sara Lumbreras, Andres Ramos, Rudi A. Hakvoort Paulien M. Herder, [[https://doi.org/10.1016/j.eneco.2018.04.037][Expansion planning of the North Sea offshore grid: Simulation of integrated governance constraints]], *Energy Economics*, Volume 72, May 2018, Pages 376-392, [[https://doi.org/10.1016/j.eneco.2018.04.037][DOI: 10.1016/j.eneco.2018.04.037]] +- Tommaso Nesti, Alessandro Zocca, Bert Zwart, [[https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.120.258301][Emergent Failures and Cascades in Power Grids: A Statistical Physics Perspective]], *Physical Review Letters*, Volume 120, 2018, [[https://doi.org/10.1103/PhysRevLett.120.258301][DOI: 10.1103/PhysRevLett.120.258301]] +- Fabian Hofmann, Mirko Sch\auml{}fer, Tom Brown, Jonas H\ouml{}rsch, Stefan Schramm, Martin Greiner, [[https://arxiv.org/abs/1807.07771][Principal flow patterns across renewable electricity networks]], 2018, [[https://arxiv.org/abs/1807.07771][preprint arXiv:1807.07771]] +- Bo Tranberg, Mirko Sch\auml{}fer, Tom Brown, Jonas H\ouml{}rsch, Martin Greiner, [[https://arxiv.org/abs/1806.02549][Flow-based analysis of storage usage in a low-carbon European electricity scenario]], 2018, [[https://arxiv.org/abs/1806.02549][preprint arXiv:1806.02549]] +- Jonas H\ouml{}rsch, Fabian Hofmann, David Schlachtberger, Tom Brown [[https://arxiv.org/abs/1806.01613][PyPSA-Eur: An Open Optimisation Model of the European Transmission System]], 2018, [[https://arxiv.org/abs/1806.01613][preprint arXiv:1806.01613]] +- Markus Schlott, Alexander Kies, Tom Brown, Stefan Schramm, Martin Greiner, [[https://arxiv.org/abs/1805.11673][The Impact of Climate Change on a Cost-Optimal Highly Renewable European Electricity Network]], 2018, [[https://arxiv.org/abs/1805.11673][preprint arXiv:1805.11673]] +- D. Schlachtberger, T. Brown, M. Sch\auml{}fer, S. Schramm, M. Greiner, [[https://arxiv.org/abs/1803.09711][Cost optimal scenarios of a future highly renewable European electricity system: Exploring the influence of weather data, cost parameters and policy constraints]], 2018, [[https://arxiv.org/abs/1803.09711][preprint arXiv:1803.09711]] - J. H\ouml{}rsch, Joanne Calitz, [[https://arxiv.org/abs/1710.11199][PyPSA-ZA: Investment and operation co-optimization of integrating wind and solar in South Africa at high spatial and temporal detail]], presented at the [[http://windac-africa.com/][WindAc Africa Conference]], 2017, [[https://arxiv.org/abs/1710.11199][preprint arXiv:1710.11199]], [[https://github.com/FRESNA/pypsa-za][code]] - Markus Groissb\ouml{}ck, Alexandre Gusmao, [[https://arxiv.org/abs/1709.03761][Impact of high renewable penetration scenarios on system reliability: two case studies in the Kingdom of Saudi Arabia]], 2017, [[https://arxiv.org/abs/1709.03761][preprint arXiv:1709.03761]] - J. H\ouml{}rsch, T. Brown, [[https://doi.org/10.1109/EEM.2017.7982024][The role of spatial scale in joint optimisations of generation and transmission for European highly renewable scenarios]], 2017, [[http://eem2017.com/][14th International Conference on the European Energy Market - EEM 2017]], [[https://arxiv.org/abs/1705.07617][preprint arXiv:1705.07617]], [[https://doi.org/10.1109/EEM.2017.7982024][DOI: 10.1109/EEM.2017.7982024]] -- D. Schlachtberger, T. Brown, S. Schramm, M. Greiner, [[https://doi.org/10.1016/j.energy.2017.06.004][The Benefits of Cooperation in a Highly Renewable European Electricity Network]], Energy, Volume 134, 1 September 2017, Pages 469-481, [[https://arxiv.org/abs/1704.05492][postprint arXiv:1704.05492]], [[https://doi.org/10.1016/j.energy.2017.06.004][DOI: 10.1016/j.energy.2017.06.004]], [[https://doi.org/10.5281/zenodo.804337][all input data, code, output data on Zenodo]] -- J. H\ouml{}rsch, H. Ronellenfitsch, D. Witthaut, T. Brown, [[https://arxiv.org/abs/1704.01881][Linear Optimal Power Flow Using Cycle Flows]], 2017, [[https://www.journals.elsevier.com/electric-power-systems-research][Electric Power Systems Research]], Volume 158, pages 126-135, [[https://arxiv.org/abs/1704.01881][postprint arXiv:1704.01881]], [[https://doi.org/10.1016/j.epsr.2017.12.034][DOI: 10.1016/j.epsr.2017.12.034]] -- Joao Gorenstein Dedecca, Rudi A. Hakvoort, Paulien M. Herder, [[https://doi.org/10.1016/j.energy.2017.02.111][Transmission expansion simulation for the European Northern Seas offshore grid]], Energy, Volume 125, 15 April 2017, Pages 805-824, [[https://doi.org/10.1016/j.energy.2017.02.111][DOI: 10.1016/j.energy.2017.02.111]] +- D. Schlachtberger, T. Brown, S. Schramm, M. Greiner, [[https://doi.org/10.1016/j.energy.2017.06.004][The Benefits of Cooperation in a Highly Renewable European Electricity Network]], *Energy*, Volume 134, 1 September 2017, Pages 469-481, [[https://arxiv.org/abs/1704.05492][postprint arXiv:1704.05492]], [[https://doi.org/10.1016/j.energy.2017.06.004][DOI: 10.1016/j.energy.2017.06.004]], [[https://doi.org/10.5281/zenodo.804337][all input data, code, output data on Zenodo]] +- J. H\ouml{}rsch, H. Ronellenfitsch, D. Witthaut, T. Brown, [[https://arxiv.org/abs/1704.01881][Linear Optimal Power Flow Using Cycle Flows]], 2017, [[https://www.journals.elsevier.com/electric-power-systems-research][*Electric Power Systems Research*]], Volume 158, pages 126-135, [[https://arxiv.org/abs/1704.01881][postprint arXiv:1704.01881]], [[https://doi.org/10.1016/j.epsr.2017.12.034][DOI: 10.1016/j.epsr.2017.12.034]] +- Joao Gorenstein Dedecca, Rudi A. Hakvoort, Paulien M. Herder, [[https://doi.org/10.1016/j.energy.2017.02.111][Transmission expansion simulation for the European Northern Seas offshore grid]], *Energy*, Volume 125, 15 April 2017, Pages 805-824, [[https://doi.org/10.1016/j.energy.2017.02.111][DOI: 10.1016/j.energy.2017.02.111]] If you have written a paper or report using PyPSA, please send us the -link so we can add it here by emailing Tom Brown (brown at -fias.uni-frankfurt.de). +link so we can add it here by emailing Tom Brown (firstname.lastname@kit.edu). From b310303661ddb564de5f3d886553c17f8beb1c2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Thu, 16 Aug 2018 14:51:42 +0200 Subject: [PATCH 108/135] networkclustering: Recalculate capital_cost of aggregated lines --- pypsa/networkclustering.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index f33c02ddc..947217806 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -160,6 +160,12 @@ def aggregatelines(network, buses, interlines, line_length_factor=1.0): } def aggregatelinegroup(l): + def normed(s): + tot = s.sum() + if tot == 0: + return 1. + else: + return s/tot # l.name is a tuple of the groupby index (bus0_s, bus1_s) length_s = _haversine(buses.loc[list(l.name),['x', 'y']])*line_length_factor @@ -179,7 +185,7 @@ def aggregatelinegroup(l): s_nom_max=l['s_nom_max'].sum(), s_nom_extendable=l['s_nom_extendable'].any(), num_parallel=l['num_parallel'].sum(), - capital_cost=l['capital_cost'].sum(), + capital_cost=(length_factor * normed(l['s_nom']) * l['capital_cost']).mean(), length=length_s, sub_network=consense['sub_network'](l['sub_network']), v_ang_min=l['v_ang_min'].max(), From ca2198b7c60eb9257af7ee4a1dda93019084c4a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Thu, 23 Aug 2018 14:08:19 +0200 Subject: [PATCH 109/135] Fix pandas deprecation warnings --- pypsa/components.py | 6 +++--- pypsa/descriptors.py | 2 +- pypsa/io.py | 12 ++++++------ pypsa/networkclustering.py | 8 ++++---- pypsa/opf.py | 5 +++-- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/pypsa/components.py b/pypsa/components.py index f92558b2d..4e0084359 100644 --- a/pypsa/components.py +++ b/pypsa/components.py @@ -746,15 +746,15 @@ def __getitem__(self, key): #presence of links without s_nom_extendable def branches(self): return pd.concat((self.df(c) for c in self.branch_components), - keys=self.branch_components) + keys=self.branch_components, sort=False) def passive_branches(self): return pd.concat((self.df(c) for c in self.passive_branch_components), - keys=self.passive_branch_components) + keys=self.passive_branch_components, sort=False) def controllable_branches(self): return pd.concat((self.df(c) for c in self.controllable_branch_components), - keys=self.controllable_branch_components) + keys=self.controllable_branch_components, sort=False) def determine_network_topology(self): """ diff --git a/pypsa/descriptors.py b/pypsa/descriptors.py index 3f74e0e1d..fe7dc9cc4 100644 --- a/pypsa/descriptors.py +++ b/pypsa/descriptors.py @@ -195,7 +195,7 @@ def get_switchable_as_dense(network, component, attr, snapshots=None, inds=None) pd.DataFrame(np.repeat([df.loc[fixed_i, attr].values], len(snapshots), axis=0), index=snapshots, columns=fixed_i), pnl[attr].loc[snapshots, varying_i] - ], axis=1).reindex(columns=index)) + ], axis=1, sort=False).reindex(columns=index)) def get_switchable_as_iter(network, component, attr, snapshots, inds=None): """ diff --git a/pypsa/io.py b/pypsa/io.py index 1bc71a844..e4434a428 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -666,7 +666,7 @@ def import_components_from_dataframe(network, dataframe, cls_name): old_df = network.df(cls_name) new_df = dataframe.drop(non_static_attrs_in_df, axis=1) if not old_df.empty: - new_df = pd.concat((old_df, new_df)) + new_df = pd.concat((old_df, new_df), sort=False) if not new_df.index.is_unique: logger.error("Error, new components for {} are not unique".format(cls_name)) @@ -712,14 +712,14 @@ def import_series_from_dataframe(network, dataframe, cls_name, attr): if len(diff) > 0: logger.warning("Components {} for attribute {} of {} are not in main components dataframe {}".format(diff,attr,cls_name,list_name)) - diff = network.snapshots.difference(dataframe.index) - if len(diff): - logger.warning("Snapshots {} are missing from {} of {}".format(diff,attr,cls_name)) - - attr_series = network.components[cls_name]["attrs"].loc[attr] columns = dataframe.columns + diff = network.snapshots.difference(dataframe.index) + if len(diff): + logger.warning("Snapshots {} are missing from {} of {}. Filling with default value '{}'".format(diff,attr,cls_name,attr_series["default"])) + dataframe = dataframe.reindex(network.snapshots, fill_value=attr_series["default"]) + if not attr_series.static: pnl[attr] = pnl[attr].reindex(columns=df.index|columns, fill_value=attr_series.default) else: diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index 947217806..5f741e8e2 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -81,7 +81,7 @@ def aggregategenerators(network, busmap, with_time=True, carriers=None): new_df = pd.concat([new_df, network.generators.loc[~gens_agg_b] - .assign(bus=lambda df: df.bus.map(busmap))], axis=0) + .assign(bus=lambda df: df.bus.map(busmap))], axis=0, sort=False) new_pnl = dict() if with_time: @@ -93,7 +93,7 @@ def aggregategenerators(network, busmap, with_time=True, carriers=None): df_agg = df_agg.multiply(weighting.loc[df_agg.columns], axis=1) pnl_df = df_agg.groupby(grouper, axis=1).sum() pnl_df.columns = _flatten_multiindex(pnl_df.columns).rename("name") - new_pnl[attr] = pd.concat([df.loc[:, ~pnl_gens_agg_b], pnl_df], axis=1) + new_pnl[attr] = pd.concat([df.loc[:, ~pnl_gens_agg_b], pnl_df], axis=1, sort=False) return new_df, new_pnl @@ -145,7 +145,7 @@ def aggregatelines(network, buses, interlines, line_length_factor=1.0): positive_order = interlines.bus0_s < interlines.bus1_s interlines_p = interlines[positive_order] interlines_n = interlines[~ positive_order].rename(columns={"bus0_s":"bus1_s", "bus1_s":"bus0_s"}) - interlines_c = pd.concat((interlines_p,interlines_n)) + interlines_c = pd.concat((interlines_p,interlines_n), sort=False) attrs = network.components["Line"]["attrs"] columns = set(attrs.index[attrs.static & attrs.status.str.startswith('Input')]).difference(('name', 'bus0', 'bus1')) @@ -199,7 +199,7 @@ def normed(s): linemap_p = interlines_p.join(lines['name'], on=['bus0_s', 'bus1_s'])['name'] linemap_n = interlines_n.join(lines['name'], on=['bus0_s', 'bus1_s'])['name'] - linemap = pd.concat((linemap_p,linemap_n)) + linemap = pd.concat((linemap_p,linemap_n), sort=False) return lines, linemap_p, linemap_n, linemap diff --git a/pypsa/opf.py b/pypsa/opf.py index 54c6dac88..75664190d 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -872,7 +872,7 @@ def define_passive_branch_constraints(network,snapshots): s_max_pu = pd.concat({c : get_switchable_as_dense(network, c, 's_max_pu', snapshots) - for c in network.passive_branch_components}, axis=1) + for c in network.passive_branch_components}, axis=1, sort=False) flow_upper = {(b[0],b[1],sn) : [[(1,network.model.passive_branch_p[b[0],b[1],sn])], "<=", s_max_pu.at[sn,b]*fixed_branches.at[b,"s_nom"]] @@ -1204,7 +1204,8 @@ def get_shadows(constraint, multiind=True): pd.concat({c.name: c.pnl.p.loc[snapshots].multiply(c.df.sign, axis=1) .groupby(c.df.bus, axis=1).sum() - for c in network.iterate_components(network.controllable_one_port_components)}) \ + for c in network.iterate_components(network.controllable_one_port_components)}, + sort=False) \ .sum(level=1) \ .reindex(columns=network.buses_t.p.columns, fill_value=0.) From 8ce34404ff0688a379325b183b284ec0ee8001c3 Mon Sep 17 00:00:00 2001 From: Fabian Neumann Date: Tue, 28 Aug 2018 16:02:08 +0200 Subject: [PATCH 110/135] Correct instructions for import from netCDF Previously provided command for hdf5 import. --- doc/import_export.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/import_export.rst b/doc/import_export.rst index 481f13c3c..bc28edc01 100644 --- a/doc/import_export.rst +++ b/doc/import_export.rst @@ -205,7 +205,7 @@ Import from netCDF Import network data from netCDF file ``file.nc``: -``network.import_from_hdf5(file.nc)`` +``network.import_from_netcdf(file.nc)`` Export to HDF5 From 16e820e882fcdc8c4e8889f88830c37d0ae08761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Tue, 4 Sep 2018 17:56:43 +0200 Subject: [PATCH 111/135] opf: Use pyomo's special treatment of logfiles Necessary for cplex. --- pypsa/opf.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index 75664190d..8d6a37292 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -1418,7 +1418,7 @@ def network_lopf_prepare_solver(network, solver_name="glpk", solver_io=None): return network.opt -def network_lopf_solve(network, snapshots=None, formulation="angles", solver_options={}, keep_files=False, free_memory={'pyomo'}): +def network_lopf_solve(network, snapshots=None, formulation="angles", solver_options={}, solver_logfile=None, keep_files=False, free_memory={'pyomo'}): """ Solve linear optimal power flow for a group of snapshots and extract results. @@ -1434,6 +1434,8 @@ def network_lopf_solve(network, snapshots=None, formulation="angles", solver_opt solver_options : dictionary A dictionary with additional options that get passed to the solver. (e.g. {'threads':2} tells gurobi to use only 2 cpus) + solver_logfile : None|string + If not None, sets the logfile option of the solver. keep_files : bool, default False Keep the files that pyomo constructs from OPF problem construction, e.g. .lp file - useful for debugging @@ -1461,9 +1463,9 @@ def network_lopf_solve(network, snapshots=None, formulation="angles", solver_opt if 'pypsa' in free_memory: with empty_network(network): - network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, options=solver_options) + network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options) else: - network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, options=solver_options) + network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options) if logger.isEnabledFor(logging.INFO): network.results.write() @@ -1512,6 +1514,8 @@ def network_lopf(network, snapshots=None, solver_name="glpk", solver_io=None, the model building is complete, but before it is sent to the solver. It allows the user to add/change constraints and add/change the objective function. + solver_logfile : None|string + If not None, sets the logfile option of the solver. solver_options : dictionary A dictionary with additional options that get passed to the solver. (e.g. {'threads':2} tells gurobi to use only 2 cpus) @@ -1545,5 +1549,5 @@ def network_lopf(network, snapshots=None, solver_name="glpk", solver_io=None, solver_io=solver_io) return network_lopf_solve(network, snapshots, formulation=formulation, - solver_options=solver_options, + solver_logfile=solver_logfile, solver_options=solver_options, keep_files=keep_files, free_memory=free_memory) From 629f256446bb856c1f8da1bb65285e0d9f9f6576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Tue, 4 Sep 2018 18:12:33 +0200 Subject: [PATCH 112/135] opf: Fix solver_logfile option --- pypsa/opf.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index 8d6a37292..ad256bf8c 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -1488,9 +1488,9 @@ def network_lopf_solve(network, snapshots=None, formulation="angles", solver_opt return status, termination_condition def network_lopf(network, snapshots=None, solver_name="glpk", solver_io=None, - skip_pre=False, extra_functionality=None, solver_options={}, - keep_files=False, formulation="angles", ptdf_tolerance=0., - free_memory={}): + skip_pre=False, extra_functionality=None, solver_logfile=None, + solver_options={}, keep_files=False, formulation="angles", + ptdf_tolerance=0., free_memory={}): """ Linear optimal power flow for a group of snapshots. From a1a91a6800a27b6a7ba999d0608037da350870ef Mon Sep 17 00:00:00 2001 From: Michel Lavoie Date: Tue, 18 Sep 2018 13:10:53 -0400 Subject: [PATCH 113/135] Corrected an error in "Upgrading PyPSA" It says "pip install -U pandas" instead of "... pypsa" --- doc/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/installation.rst b/doc/installation.rst index e6ee35a47..d706757e7 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -136,7 +136,7 @@ We recommend always keeping your PyPSA installation up-to-date, since bugs get fixed and new features are added. To upgrade PyPSA with pip, do at the command line:: - pip install -U pandas + pip install -U pypsa Don't forget to read the :doc:`release_notes` regarding API changes that might require you to update your code. From 04cc02c173f1567d512da7fd62ac3d89a6e6e754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Tue, 2 Oct 2018 14:05:18 +0200 Subject: [PATCH 114/135] Fix deprecation warnings about logger.warn and missing sort=False Fix pandas deprecation warnings --- pypsa/components.py | 2 +- pypsa/io.py | 4 ++-- pypsa/opf.py | 2 +- pypsa/pf.py | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pypsa/components.py b/pypsa/components.py index 4e0084359..8a19d46fc 100644 --- a/pypsa/components.py +++ b/pypsa/components.py @@ -445,7 +445,7 @@ def add(self, class_name, name, **kwargs): #This guarantees that the correct attribute type is maintained obj_df = pd.DataFrame(data=[static_attrs.default],index=[name],columns=static_attrs.index) - new_df = cls_df.append(obj_df) + new_df = cls_df.append(obj_df, sort=False) setattr(self, self.components[class_name]["list_name"], new_df) diff --git a/pypsa/io.py b/pypsa/io.py index e4434a428..3cf03471d 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -929,13 +929,13 @@ def import_from_pandapower_net(network, net): "q_set" : -(net.sgen.scaling*net.sgen.q_kvar).values/1e3, "bus" : net.bus.name.loc[net.sgen.bus].values, "control" : "PQ"}, - index=net.sgen.name))) + index=net.sgen.name)), sort=False) d["Generator"] = pd.concat((d["Generator"],pd.DataFrame({"control" : "Slack", "p_set" : 0., "q_set" : 0., "bus" : net.bus.name.loc[net.ext_grid.bus].values}, - index=net.ext_grid.name.fillna("External Grid")))) + index=net.ext_grid.name.fillna("External Grid"))), sort=False) d["Bus"].loc[net.bus.name.loc[net.ext_grid.bus].values,"v_mag_pu_set"] = net.ext_grid.vm_pu.values diff --git a/pypsa/opf.py b/pypsa/opf.py index ad256bf8c..702893ff0 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -1478,7 +1478,7 @@ def network_lopf_solve(network, snapshots=None, formulation="angles", solver_opt extract_optimisation_results(network, snapshots, formulation, free_pyomo='pyomo' in free_memory) elif status == "warning" and termination_condition == "other": - logger.warn("WARNING! Optimization might be sub-optimal. Writing output anyway") + logger.warning("WARNING! Optimization might be sub-optimal. Writing output anyway") extract_optimisation_results(network, snapshots, formulation, free_pyomo='pyomo' in free_memory) else: diff --git a/pypsa/pf.py b/pypsa/pf.py index 1488537ca..565ffddcc 100644 --- a/pypsa/pf.py +++ b/pypsa/pf.py @@ -178,7 +178,7 @@ def newton_raphson_sparse(f, guess, dfdx, x_tol=1e-10, lim_iter=100): logger.debug("Error at iteration %d: %f", n_iter, diff) if diff > x_tol: - logger.warn("Warning, we didn't reach the required tolerance within %d iterations, error is at %f. See the section \"Troubleshooting\" in the documentation for tips to fix this. ", n_iter, diff) + logger.warning("Warning, we didn't reach the required tolerance within %d iterations, error is at %f. See the section \"Troubleshooting\" in the documentation for tips to fix this. ", n_iter, diff) elif not np.isnan(diff): converged = True @@ -548,7 +548,7 @@ def find_slack_bus(sub_network): gens = sub_network.generators() if len(gens) == 0: - logger.warn("No generators in sub-network {}, better hope power is already balanced".format(sub_network.name)) + logger.warning("No generators in sub-network {}, better hope power is already balanced".format(sub_network.name)) sub_network.slack_generator = None sub_network.slack_bus = sub_network.buses_i()[0] @@ -633,7 +633,7 @@ def calculate_B_H(sub_network,skip_pre=False): if np.isnan(b).any(): - logger.warn("Warning! Some series impedances are zero - this will cause a singularity in LPF!") + logger.warning("Warning! Some series impedances are zero - this will cause a singularity in LPF!") b_diag = csr_matrix((b, (r_[:len(b)], r_[:len(b)]))) #incidence matrix @@ -699,7 +699,7 @@ def calculate_Y(sub_network,skip_pre=False): calculate_dependent_values(sub_network.network) if sub_network.network.sub_networks.at[sub_network.name,"carrier"] != "AC": - logger.warn("Non-AC networks not supported for Y!") + logger.warning("Non-AC networks not supported for Y!") return branches = sub_network.branches() From 54f212ab54aa7711a0b8c64e249d8a5b19cfaae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Fri, 26 Oct 2018 15:21:27 +0200 Subject: [PATCH 115/135] plot: Use ImportError instead of catch all for basemap and matplotlib imports --- pypsa/plot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pypsa/plot.py b/pypsa/plot.py index 4a7939904..17d1ff908 100644 --- a/pypsa/plot.py +++ b/pypsa/plot.py @@ -41,20 +41,20 @@ import matplotlib.pyplot as plt from matplotlib.patches import Wedge from matplotlib.collections import LineCollection, PatchCollection -except: +except ImportError: plt_present = False basemap_present = True try: from mpl_toolkits.basemap import Basemap -except: +except ImportError: basemap_present = False pltly_present = True try: import plotly.offline as pltly -except: +except ImportError: pltly_present = False From 70511927529c9c78c3f93bb74474b01b90f7b1d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Fri, 26 Oct 2018 15:22:38 +0200 Subject: [PATCH 116/135] graph: Generation of weighted nx graph falls back to a weight of 0 Especially useful for using the lengths as weight, since the length is not defined for transformers. --- pypsa/graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypsa/graph.py b/pypsa/graph.py index 1193fc65f..938c1a1fb 100644 --- a/pypsa/graph.py +++ b/pypsa/graph.py @@ -76,7 +76,7 @@ def gen_edges(): if weight is None: data = {} else: - data = dict(weight=getattr(branch, weight)) + data = dict(weight=getattr(branch, weight, 0)) if np.isinf(data['weight']) and inf_weight is not True: if inf_weight is False: continue From 05f6fd53a997777a45fc09e2c66313f6db928c2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Fri, 26 Oct 2018 15:27:34 +0200 Subject: [PATCH 117/135] opf: Rescale cycle constraints in the kirchhoff formulation The constant factor 1e5 improves the numerical stability of the interior point algorithm. --- pypsa/opf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index 702893ff0..7097a839c 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -751,7 +751,7 @@ def define_sub_network_cycle_constraints( subnetwork, snapshots, passive_branch_ for cycle_i in cycle_is: branch_idx = branches.index[cycle_i] - attribute_value = branches.at[ branch_idx, attribute] *subnetwork.C[ cycle_i, col_j] + attribute_value = 1e5 * branches.at[ branch_idx, attribute] * subnetwork.C[ cycle_i, col_j] branch_idx_attributes.append( (branch_idx, attribute_value)) for snapshot in snapshots: From 032f655cd1cf7dea77daf7f46e092785208eee32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Fri, 26 Oct 2018 17:59:15 +0200 Subject: [PATCH 118/135] requirements_dev.yml: Remove currently broken anaconda channel --- requirements_dev.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements_dev.yml b/requirements_dev.yml index 01fb867ae..ab86172f2 100644 --- a/requirements_dev.yml +++ b/requirements_dev.yml @@ -2,7 +2,6 @@ name: pypsa channels: - conda-forge - - anaconda dependencies: - pytest From 9ec406252add1b64530c0ee6dc4628aa7fefae31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Sat, 27 Oct 2018 03:44:08 +0200 Subject: [PATCH 119/135] networkclustering: Add strategy for weighted clustering of capital_cost --- pypsa/networkclustering.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index 5f741e8e2..0a0456b89 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -73,7 +73,8 @@ def aggregategenerators(network, busmap, with_time=True, carriers=None): weighting = generators.weight.groupby(grouper, axis=0).transform(lambda x: (x/x.sum()).fillna(1.)) generators['p_nom_max'] /= weighting - strategies = {'p_nom_max': np.min, 'weight': np.sum, 'p_nom': np.sum} + generators['capital_cost'] *= weighting + strategies = {'p_nom_max': np.min, 'weight': np.sum, 'p_nom': np.sum, 'capital_cost': np.sum} strategies.update((attr, _make_consense('Generator', attr)) for attr in columns.difference(strategies)) new_df = generators.groupby(grouper, axis=0).agg(strategies) From fe1f98df7d0824ebd0c67b3bef5e9f04f1f64c5c Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Tue, 30 Oct 2018 19:34:24 +0100 Subject: [PATCH 120/135] opf: Allow extra_postprocessing function for reading shadow prices It is called after solving and results are extracted. It can be used to get the values of shadow prices for constraints not normally extracted. --- pypsa/opf.py | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index 54c6dac88..80b2ada69 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -1128,7 +1128,8 @@ def define_linear_objective(network,snapshots): l_objective(model,objective) -def extract_optimisation_results(network, snapshots, formulation="angles", free_pyomo=True): +def extract_optimisation_results(network, snapshots, formulation="angles", free_pyomo=True, + extra_postprocessing=None): if isinstance(snapshots, pd.DatetimeIndex) and _pd_version < '0.18.0': # Work around pandas bug #12050 (https://github.com/pydata/pandas/issues/12050) @@ -1150,6 +1151,7 @@ def extract_optimisation_results(network, snapshots, formulation="angles", free_ model = network.model duals = pd.Series(list(model.dual.values()), index=pd.Index(list(model.dual.keys()))) + if free_pyomo: model.dual.clear() @@ -1319,6 +1321,10 @@ def get_shadows(constraint, multiind=True): network.generators_t.status.loc[snapshots,fixed_committable_gens_i] = \ get_values(model.generator_status).unstack(0) + if extra_postprocessing is not None: + extra_postprocessing(network, snapshots, duals) + + def network_lopf_build_model(network, snapshots=None, skip_pre=False, formulation="angles", ptdf_tolerance=0.): """ @@ -1417,7 +1423,8 @@ def network_lopf_prepare_solver(network, solver_name="glpk", solver_io=None): return network.opt -def network_lopf_solve(network, snapshots=None, formulation="angles", solver_options={}, keep_files=False, free_memory={'pyomo'}): +def network_lopf_solve(network, snapshots=None, formulation="angles", solver_options={}, keep_files=False, + free_memory={'pyomo'},extra_postprocessing=None): """ Solve linear optimal power flow for a group of snapshots and extract results. @@ -1440,6 +1447,11 @@ def network_lopf_solve(network, snapshots=None, formulation="angles", solver_opt Any subset of {'pypsa', 'pyomo'}. Allows to stash `pypsa` time-series data away while the solver runs (as a pickle to disk) and/or free `pyomo` data after the solution has been extracted. + extra_postprocessing : callable function + This function must take three arguments + `extra_postprocessing(network,snapshots,duals)` and is called after + the model has solved and the results are extracted. It allows the user to + extract further information about the solution, such as additional shadow prices. Returns ------- @@ -1473,11 +1485,13 @@ def network_lopf_solve(network, snapshots=None, formulation="angles", solver_opt if status == "ok" and termination_condition == "optimal": logger.info("Optimization successful") extract_optimisation_results(network, snapshots, formulation, - free_pyomo='pyomo' in free_memory) + free_pyomo='pyomo' in free_memory, + extra_postprocessing=extra_postprocessing) elif status == "warning" and termination_condition == "other": logger.warn("WARNING! Optimization might be sub-optimal. Writing output anyway") extract_optimisation_results(network, snapshots, formulation, - free_pyomo='pyomo' in free_memory) + free_pyomo='pyomo' in free_memory, + extra_postprocessing=extra_postprocessing) else: logger.error("Optimisation failed with status %s and terminal condition %s" % (status, termination_condition)) @@ -1487,7 +1501,7 @@ def network_lopf_solve(network, snapshots=None, formulation="angles", solver_opt def network_lopf(network, snapshots=None, solver_name="glpk", solver_io=None, skip_pre=False, extra_functionality=None, solver_options={}, keep_files=False, formulation="angles", ptdf_tolerance=0., - free_memory={}): + free_memory={},extra_postprocessing=None): """ Linear optimal power flow for a group of snapshots. @@ -1526,6 +1540,11 @@ def network_lopf(network, snapshots=None, solver_name="glpk", solver_io=None, Any subset of {'pypsa', 'pyomo'}. Allows to stash `pypsa` time-series data away while the solver runs (as a pickle to disk) and/or free `pyomo` data after the solution has been extracted. + extra_postprocessing : callable function + This function must take three arguments + `extra_postprocessing(network,snapshots,duals)` and is called after + the model has solved and the results are extracted. It allows the user to + extract further information about the solution, such as additional shadow prices. Returns ------- @@ -1545,4 +1564,5 @@ def network_lopf(network, snapshots=None, solver_name="glpk", solver_io=None, return network_lopf_solve(network, snapshots, formulation=formulation, solver_options=solver_options, - keep_files=keep_files, free_memory=free_memory) + keep_files=keep_files, free_memory=free_memory, + extra_postprocessing=extra_postprocessing) From 0da56c4ec0294475ea009dd4e29cbb13811a2be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Tue, 6 Nov 2018 14:52:58 +0100 Subject: [PATCH 121/135] fixup! networkclustering: Add strategy for weighted clustering of capital_cost --- pypsa/networkclustering.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index 0a0456b89..48baf3445 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -156,8 +156,7 @@ def aggregatelines(network, buses, interlines, line_length_factor=1.0): for attr in (columns | {'sub_network'} - {'r', 'x', 'g', 'b', 'terrain_factor', 's_nom', 's_nom_min', 's_nom_max', 's_nom_extendable', - 'capital_cost', 'length', 'v_ang_min', - 'v_ang_max'}) + 'length', 'v_ang_min', 'v_ang_max'}) } def aggregatelinegroup(l): @@ -186,7 +185,7 @@ def normed(s): s_nom_max=l['s_nom_max'].sum(), s_nom_extendable=l['s_nom_extendable'].any(), num_parallel=l['num_parallel'].sum(), - capital_cost=(length_factor * normed(l['s_nom']) * l['capital_cost']).mean(), + capital_cost=(length_factor * normed(l['s_nom']) * l['capital_cost']).sum(), length=length_s, sub_network=consense['sub_network'](l['sub_network']), v_ang_min=l['v_ang_min'].max(), From 680ccd52f8e6f965bd79855cb4fbd926e5615d20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Mon, 12 Nov 2018 21:36:53 +0100 Subject: [PATCH 122/135] networkclustering: Aggregate max_hours in proportion to p_nom and enable customization of strategies --- pypsa/networkclustering.py | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index 48baf3445..a9a9f45ae 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -40,6 +40,13 @@ from . import components, io +def _normed(s): + tot = s.sum() + if tot == 0: + return 1. + else: + return s/tot + def _flatten_multiindex(m, join=' '): if m.nlevels <= 1: return m levels = map(m.get_level_values, range(m.nlevels)) @@ -98,17 +105,24 @@ def aggregategenerators(network, busmap, with_time=True, carriers=None): return new_df, new_pnl -def aggregateoneport(network, busmap, component, with_time=True): +def aggregateoneport(network, busmap, component, with_time=True, custom_strategies=dict()): attrs = network.components[component]["attrs"] old_df = getattr(network, network.components[component]["list_name"]).assign(bus=lambda df: df.bus.map(busmap)) columns = set(attrs.index[attrs.static & attrs.status.str.startswith('Input')]) & set(old_df.columns) grouper = old_df.bus if 'carrier' not in columns else [old_df.bus, old_df.carrier] - strategies = {attr: (np.sum - if attr in {'p', 'q', 'p_set', 'q_set', - 'p_nom', 'p_nom_max', 'p_nom_min'} - else _make_consense(component, attr)) + def aggregate_max_hours(max_hours): + if (max_hours == max_hours.iloc[0]).all(): + return max_hours.iloc[0] + else: + return (max_hours * _normed(old_df.p_nom.reindex(max_hours.index))).sum() + + default_strategies = dict(p=np.sum, q=np.sum, p_set=np.sum, q_set=np.sum, + p_nom=np.sum, p_nom_max=np.sum, p_nom_min=np.sum, + max_hours=aggregate_max_hours) + strategies = {attr: default_strategies.get(attr, _make_consense(component, attr)) for attr in columns} + strategies.update(custom_strategies) new_df = old_df.groupby(grouper).agg(strategies) new_df.index = _flatten_multiindex(new_df.index).rename("name") @@ -160,12 +174,6 @@ def aggregatelines(network, buses, interlines, line_length_factor=1.0): } def aggregatelinegroup(l): - def normed(s): - tot = s.sum() - if tot == 0: - return 1. - else: - return s/tot # l.name is a tuple of the groupby index (bus0_s, bus1_s) length_s = _haversine(buses.loc[list(l.name),['x', 'y']])*line_length_factor @@ -185,7 +193,7 @@ def normed(s): s_nom_max=l['s_nom_max'].sum(), s_nom_extendable=l['s_nom_extendable'].any(), num_parallel=l['num_parallel'].sum(), - capital_cost=(length_factor * normed(l['s_nom']) * l['capital_cost']).sum(), + capital_cost=(length_factor * _normed(l['s_nom']) * l['capital_cost']).sum(), length=length_s, sub_network=consense['sub_network'](l['sub_network']), v_ang_min=l['v_ang_min'].max(), @@ -227,7 +235,7 @@ def get_buses_linemap_and_lines(network, busmap, line_length_factor=1.0, bus_str def get_clustering_from_busmap(network, busmap, with_time=True, line_length_factor=1.0, aggregate_generators_weighted=False, aggregate_one_ports={}, aggregate_generators_carriers=None, - bus_strategies=dict()): + bus_strategies=dict(), one_port_strategies=dict()): buses, linemap, linemap_p, linemap_n, lines = get_buses_linemap_and_lines(network, busmap, line_length_factor, bus_strategies) @@ -253,7 +261,8 @@ def get_clustering_from_busmap(network, busmap, with_time=True, line_length_fact for one_port in aggregate_one_ports: one_port_components.remove(one_port) - new_df, new_pnl = aggregateoneport(network, busmap, component=one_port, with_time=with_time) + new_df, new_pnl = aggregateoneport(network, busmap, component=one_port, with_time=with_time, + custom_strategies=one_port_strategies.get(one_port, {})) io.import_components_from_dataframe(network_c, new_df, one_port) for attr, df in iteritems(new_pnl): io.import_series_from_dataframe(network_c, df, one_port, attr) From 990e88897168ab089aad7489ecc9f9af3912e5ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Wed, 14 Nov 2018 12:04:53 +0100 Subject: [PATCH 123/135] io: Fix saving of network names unicode is not the same as str in Python 2.7 --- pypsa/io.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pypsa/io.py b/pypsa/io.py index 3cf03471d..6ae75ed9a 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -299,13 +299,13 @@ def _export_to_exporter(network, exporter, basename, export_standard_types=False #exportable component types #what about None???? - nan is float? - allowed_types = [float,int,str,bool] + list(np.typeDict.values()) + allowed_types = (float,int,bool) + string_types + tuple(np.typeDict.values()) #first export network properties attrs = dict((attr, getattr(network, attr)) for attr in dir(network) - if (attr[:2] != "__" and - type(getattr(network,attr)) in allowed_types)) + if (not attr.startswith("__") and + isinstance(getattr(network,attr), allowed_types))) exporter.save_attributes(attrs) #now export snapshots From 50d1f2d30a3ab04dd8c50b5cc0b26449be7aea34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=B6rsch?= Date: Wed, 21 Nov 2018 12:33:17 +0100 Subject: [PATCH 124/135] io: Don't touch HDF5 files on import --- pypsa/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypsa/io.py b/pypsa/io.py index 6ae75ed9a..93486caf0 100644 --- a/pypsa/io.py +++ b/pypsa/io.py @@ -140,7 +140,7 @@ def remove_series(self, list_name, attr): class ImporterHDF5(Importer): def __init__(self, path): - self.ds = pd.HDFStore(path) + self.ds = pd.HDFStore(path, mode='r') self.index = {} def get_attributes(self): From 8179db91a96d368d122350e4861094fbab6577ff Mon Sep 17 00:00:00 2001 From: vs2788 Date: Mon, 26 Nov 2018 16:26:39 +0100 Subject: [PATCH 125/135] fix louvain and spectral clustering. louvain lost 'level' argument --- pypsa/networkclustering.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index a9a9f45ae..22de720a4 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -330,11 +330,12 @@ def length_clustering(network, length): from sklearn.cluster import spectral_clustering as sk_spectral_clustering def busmap_by_spectral_clustering(network, n_clusters, **kwds): - lines = network.lines.loc[:,['bus0', 'bus1']].assign(weight=1./network.lines.x).set_index(['bus0','bus1']) - G = OrderedGraph() + lines = network.lines.loc[:,['bus0', 'bus1']].assign(weight=network.lines.num_parallel).set_index(['bus0','bus1']) + lines.weight+=0.1 + G = nx.Graph() G.add_nodes_from(network.buses.index) G.add_edges_from((u,v,dict(weight=w)) for (u,v),w in lines.itertuples()) - return pd.Series(sk_spectral_clustering(nx.adjacency_matrix(G), n_clusters, **kwds) + 1, + return pd.Series(list(map(str,sk_spectral_clustering(nx.adjacency_matrix(G), n_clusters, **kwds) + 1)), index=network.buses.index) def spectral_clustering(network, n_clusters=8, **kwds): @@ -351,19 +352,20 @@ def spectral_clustering(network, n_clusters=8, **kwds): # available using pip as python-louvain import community - def busmap_by_louvain(network, level=-1): - lines = network.lines.loc[:,['bus0', 'bus1']].assign(weight=1./network.lines.x).set_index(['bus0','bus1']) + def busmap_by_louvain(network): + lines = network.lines.loc[:,['bus0', 'bus1']].assign(weight=network.lines.num_parallel).set_index(['bus0','bus1']) + lines.weight+=0.1 G = nx.Graph() G.add_nodes_from(network.buses.index) G.add_edges_from((u,v,dict(weight=w)) for (u,v),w in lines.itertuples()) - dendrogram = community.generate_dendrogram(G) - if level < 0: - level += len(dendrogram) - return pd.Series(community.partition_at_level(dendrogram, level=level), - index=network.buses.index) - - def louvain_clustering(network, level=-1, **kwds): - busmap = busmap_by_louvain(network, level=level) + b=community.best_partition(G) + list_cluster=[] + for i in b: + list_cluster.append(str(b[i])) + return pd.Series(list_cluster,index=network.buses.index) + + def louvain_clustering(network, **kwds): + busmap = busmap_by_louvain(network) return get_clustering_from_busmap(network, busmap) except ImportError: From 6cb091a93f15a85caf30d584307a2bed0353f719 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 3 Dec 2018 17:20:02 +0100 Subject: [PATCH 126/135] Add carbon management / biomass example Also add other examples to website examples list. --- ...omass-synthetic-fuels-carbon-management.py | 202 ++++++++++++++++++ website/examples/index.org | 7 +- 2 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 examples/sector-coupling/biomass-synthetic-fuels-carbon-management.py diff --git a/examples/sector-coupling/biomass-synthetic-fuels-carbon-management.py b/examples/sector-coupling/biomass-synthetic-fuels-carbon-management.py new file mode 100644 index 000000000..a22eeccc7 --- /dev/null +++ b/examples/sector-coupling/biomass-synthetic-fuels-carbon-management.py @@ -0,0 +1,202 @@ +## Biomass, synthetic fuels and carbon management +# +#In this example we show how to manage different biomass stocks with different potentials and costs, carbon dioxide hydrogenation from biogas, direct air capture (DAC) and carbon capture and usage/sequestration/cycling (CCU/S/C). +# +#Demand for electricity and diesel transport have to be met from various biomass sources, natural gas with possibility for carbon capture, electrolysis for hydrogen production, direct air capture of CO2, and diesel synthesis via Fischer-Tropsch. +# +#The system has to reach a target of net negative emissions over the period. +# +#All numbers/costs/efficiencies are fictitious to allow easy analysis. +# +# +#This Jupyter Notebook is also available to download at: http://www.pypsa.org/examples/biomass-synthetic-fuels-carbon-management.ipynb. +# +#It demonstrates features of the energy system modelling tool PyPSA : https://github.com/PyPSA/PyPSA. + +import pypsa +import numpy as np + +#First tell PyPSA that links can have multiple outputs by +#overriding the component_attrs. This can be done for +#as many buses as you need with format busi for i = 2,3,4,5,.... +#See https://pypsa.org/doc/components.html#link-with-multiple-outputs-or-inputs + +override_component_attrs = pypsa.descriptors.Dict({k : v.copy() for k,v in pypsa.components.component_attrs.items()}) +override_component_attrs["Link"].loc["bus2"] = ["string",np.nan,np.nan,"2nd bus","Input (optional)"] +override_component_attrs["Link"].loc["bus3"] = ["string",np.nan,np.nan,"3rd bus","Input (optional)"] +override_component_attrs["Link"].loc["efficiency2"] = ["static or series","per unit",1.,"2nd bus efficiency","Input (optional)"] +override_component_attrs["Link"].loc["efficiency3"] = ["static or series","per unit",1.,"3rd bus efficiency","Input (optional)"] +override_component_attrs["Link"].loc["p2"] = ["series","MW",0.,"2nd bus output","Output"] +override_component_attrs["Link"].loc["p3"] = ["series","MW",0.,"3rd bus output","Output"] + +n = pypsa.Network(override_component_attrs=override_component_attrs) + +n.set_snapshots(range(10)) + +#add a constant electrical load +n.add("Bus","bus") +n.add("Load","load",bus="bus", + p_set=1.) + +#add a constant demand for transport +n.add("Bus","transport") +n.add("Load","transport",bus="transport", + p_set=1.) + + +n.add("Bus","diesel") + + +n.add("Store","diesel",bus="diesel", + e_cyclic=True, + e_nom=1000.) + +n.add("Bus","hydrogen") + +n.add("Store","hydrogen",bus="hydrogen", + e_cyclic=True, + e_nom=1000.) + +#n.add("Load","hydrogen", +# bus="hydrogen", +# p_set=1.) + + +n.add("Link","electrolysis", + p_nom=2., + efficiency=0.8, + bus0="bus", + bus1="hydrogen") + + +#Allow production of diesel from H2 and CO2 using Fischer-Tropsch +n.add("Link","FT", + p_nom=4, + bus0="hydrogen", + bus1="diesel", + bus2="co2 stored", + efficiency=1., + efficiency2=-1) + + +#minus sign because opposite to how fossil fuels used: +#CH4 burning puts CH4 down, atmosphere up +n.add("Carrier","co2", + co2_emissions=-1.) + +#this tracks CO2 in the atmosphere +n.add("Bus","co2 atmosphere", + carrier="co2") + +#NB: can also be negative +n.add("Store","co2 atmosphere", + e_nom=1000, + e_min_pu=-1, + bus="co2 atmosphere") + +#this tracks CO2 stored, e.g. underground +n.add("Bus","co2 stored") + +#NB: can also be negative +n.add("Store","co2 stored", + e_nom = 1000, + e_min_pu=-1, + bus="co2 stored") + +#direct air capture consumes electricity to take CO2 from the air to the underground store +n.add("Link","DAC", + bus0="bus", + bus1="co2 stored", + bus2 = "co2 atmosphere", + efficiency=1, + efficiency2=-1, + p_nom=5.) + + +#meet transport with diesel +n.add("Link","diesel car", + bus0="diesel", + bus1="transport", + bus2="co2 atmosphere", + efficiency=1., + efficiency2=1., + p_nom=2.) + +n.add("Bus","gas") + +n.add("Store","gas", + e_initial=50, + e_nom=50, + marginal_cost=20, + bus="gas") + +n.add("Link","OCGT", + bus0 = "gas", + bus1 = "bus", + bus2 = "co2 atmosphere", + p_nom_extendable=True, + efficiency = 0.5, + efficiency2 = 1) + + +n.add("Link","OCGT+CCS", + bus0 = "gas", + bus1 = "bus", + bus2 = "co2 stored", + bus3 = "co2 atmosphere", + p_nom_extendable=True, + efficiency = 0.4, + efficiency2 = 0.9, + efficiency3 = 0.1) + + + +#Cheap and expensive biomass +biomass_marginal_cost = [20.,50.] +biomass_stored = [40.,15.] + +for i in range(2): + n.add("Bus","biomass"+str(i)) + + n.add("Store","biomass"+str(i), + bus="biomass"+str(i), + e_nom_extendable=True, + marginal_cost=biomass_marginal_cost[i], + e_nom=biomass_stored[i], + e_initial=biomass_stored[i]) + + #simultaneously empties and refills co2 atmosphere + n.add("Link","biomass"+str(i), + bus0 = "biomass"+str(i), + bus1 = "bus", + p_nom_extendable=True, + efficiency = 0.5) + + n.add("Link","biomass+CCS"+str(i), + bus0 = "biomass"+str(i), + bus1 = "bus", + bus2 = "co2 stored", + bus3 = "co2 atmosphere", + p_nom_extendable=True, + efficiency = 0.4, + efficiency2 = 1., + efficiency3 = -1) + + +#can go to -50, but at some point can't generate enough electricity for DAC and demand +target = -50 + +n.add("GlobalConstraint","co2_limit", + sense="<=", + carrier_attribute="co2_emissions", + constant=target) + +n.lopf() + +n.stores_t.e.plot() + +n.links_t.p0[["biomass+CCS0","biomass+CCS1","OCGT+CCS","DAC"]].plot() + +#at all times, the amount of carbon is constant +n.stores_t.e[["co2 stored","co2 atmosphere","gas","diesel"]].sum(axis=1) + diff --git a/website/examples/index.org b/website/examples/index.org index 677f2810f..d969a534b 100644 --- a/website/examples/index.org +++ b/website/examples/index.org @@ -62,13 +62,18 @@ repository]]. - [[./power-to-heat-water-tank.html][*Power-to-Heat with Water Tank*]] - [[./battery-electric-vehicle-charging.html][*Transport: Charging Battery Electric Vehicle with Solar Panel*]] - [[./chained-hydro-reservoirs.html][*Chained Hydroelectric Reservoirs*]] + - [[./biomass-synthetic-fuels-carbon-management.html][*Biomass, synthetic fuels and carbon management*]] - [[./replace-generator-storage-units-with-store.html][*Replacing Generators and Storage Units with Fundamental Stores and Links*]] - This notebook demonstrates how generators and storage units can be replaced by more fundamental components (Stores and Links), and how their parameters map to each other. -- [[./logging-demo.html][Logging]] - How to control the level of logging that PyPSA reports +- [[./logging-demo.html][*Logging*]] - How to control the level of logging that PyPSA reports back, e.g. error/warning/info/debug messages. +- [[https://github.com/PyPSA/pypsa-eur][*PyPSA-Eur*]]: An Open Optimisation Model of the European Transmission System +- [[https://github.com/PyPSA/pypsa-za][*PyPSA-ZA*]]: PyPSA Model of the South African Energy System +- [[https://zenodo.org/record/1146665][*Sector-Coupled European Dataset*]] - Full dataset and code from [[https://arxiv.org/abs/1801.05290][Synergies of sector coupling and transmission extension in a cost-optimised, highly renewable European energy system]], Energy, Volume 160, Pages 720-739, 2018, [[https://arxiv.org/abs/1801.05290][postprint arXiv:1801.05290]], DOI: [[https://doi.org/10.1016/j.energy.2018.06.222][10.1016/j.energy.2018.06.222]]. - [[https://zenodo.org/record/804337][*European One-Node-Per-Country Dataset*]] - Full dataset and code from [[https://doi.org/10.1016/j.energy.2017.06.004][The Benefits of Cooperation in a Highly Renewable European Electricity Network]], Energy, Volume 134, 1 September 2017, Pages 469-481, [[https://arxiv.org/abs/1704.05492][preprint]]. +- [[https://github.com/PyPSA/WHOBS][*WHOBS*]]: Optimal Wind+Hydrogen+Other+Battery+Solar (WHOBS) electricity systems for European countries - [[https://github.com/openego][*open-Ego project*]] - The project open-eGo aims to develop a transparent, inter-grid-level operating grid planning tool to investigate economic viable grid expansion scenarios considering alternative flexibility options such as storages or redispatch. It uses PyPSA. From 867c69e88d89b9f29eeca61a20f86e8c1504b339 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Mon, 3 Dec 2018 17:36:42 +0100 Subject: [PATCH 127/135] Update landing page comments, features and comparable software For features that are unlikely to be developed in PyPSA directly, we have linked to comparable software with this functionality. Scare warning about heavy development has been removed. --- README.rst | 38 ++++++++++++++------------------------ doc/introduction.rst | 37 ++++++++++++++----------------------- website/index.org | 42 ++++++++++++++++-------------------------- 3 files changed, 44 insertions(+), 73 deletions(-) diff --git a/README.rst b/README.rst index 4d4a879ff..b9bf5d8ce 100644 --- a/README.rst +++ b/README.rst @@ -24,14 +24,6 @@ and solar generation, storage units, coupling to other energy sectors, and mixed alternating and direct current networks. PyPSA is designed to scale well with large networks and long time series. -As of 2018 PyPSA is under heavy development and therefore it is -recommended to use caution when using it in a production environment. -Some APIs may change - the changes in each PyPSA version are listed in -the `doc/release_notes.rst `_. - - - - This project is maintained by the `Energy System Modelling group `_ at the `Institute for Automation and Applied @@ -93,31 +85,29 @@ It has models for: * basic components out of which more complicated assets can be built, such as Combined Heat and Power (CHP) units, heat pumps, resistive Power-to-Heat (P2H), Power-to-Gas (P2G), battery electric vehicles - (BEVs), etc.; each of these is demonstrated in the `examples + (BEVs), Fischer-Tropsch, direct air capture (DAC), etc.; each of + these is demonstrated in the `examples `_ -Functionality that will definitely be added soon: +Functionality that may be added in the future: * Multi-year investment optimisation -* Simple RMS simulations with the swing equation * Distributed active power slack -* Non-linear power flow solution using `analytic continuation - `_ - in the complex plane following `GridCal - `_ - -Functionality that may be added in the future: - -* Short-circuit current calculations -* Dynamic RMS simulations -* Small signal stability analysis * Interactive web-based GUI with SVG * OPF with the full non-linear network equations -* Dynamic EMT simulations -* Unbalanced load flow * Port to `Julia `_ +Other complementary libraries: + +* `pandapower `_ for more + detailed modelling of distribution grids, short-circuit + calculations, unbalanced load flow and more +* `PowerDynamics.jl + `_ for dynamic + modelling of power grids at time scales where differential equations are relevant + + Example scripts as Jupyter notebooks ==================================== @@ -165,7 +155,7 @@ What PyPSA uses under the hood =============================== PyPSA is written and tested to be compatible with both Python 2.7 and -Python 3.5. +Python 3.6. It leans heavily on the following Python packages: diff --git a/doc/introduction.rst b/doc/introduction.rst index 04576d32a..233fe4265 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -12,12 +12,6 @@ and solar generation, storage units, coupling to other energy sectors, and mixed alternating and direct current networks. PyPSA is designed to scale well with large networks and long time series. - -As of 2018 PyPSA is under heavy development and therefore it is -recommended to use caution when using it in a production -environment. Some APIs may change - the changes in each PyPSA version -are listed in the :doc:`release_notes`. - This project is maintained by the `Energy System Modelling group `_ at the `Institute for Automation and Applied @@ -62,31 +56,28 @@ It has models for: * basic components out of which more complicated assets can be built, such as Combined Heat and Power (CHP) units, heat pumps, resistive Power-to-Heat (P2H), Power-to-Gas (P2G), battery electric vehicles - (BEVs), etc.; each of these is demonstrated in the `examples + (BEVs), Fischer-Tropsch, direct air capture (DAC), etc.; each of + these is demonstrated in the `examples `_ -Functionality that will definitely be added soon: +Functionality that may be added in the future: * Multi-year investment optimisation -* Simple RMS simulations with the swing equation * Distributed active power slack -* Non-linear power flow solution using `analytic continuation - `_ - in the complex plane following `GridCal - `_ - -Functionality that may be added in the future: - -* Short-circuit current calculations -* Dynamic RMS simulations -* Small signal stability analysis * Interactive web-based GUI with SVG * OPF with the full non-linear network equations -* Dynamic EMT simulations -* Unbalanced load flow * Port to `Julia `_ +Other complementary libraries: + +* `pandapower `_ for more + detailed modelling of distribution grids, short-circuit + calculations, unbalanced load flow and more +* `PowerDynamics.jl + `_ for dynamic + modelling of power grids at time scales where differential equations are relevant + Example scripts as Jupyter notebooks @@ -171,8 +162,8 @@ and DC. PyPSA uses some of the sparse-matrix constructs from PYPOWER. What PyPSA uses under the hood =============================== -PyPSA is written and tested to be compatible with Python 2.7 and -Python 3.5. +PyPSA is written and tested to be compatible with both Python 2.7 and +Python 3.6. It leans heavily on the following Python packages: diff --git a/website/index.org b/website/index.org index 7b1606f2b..f58a0ce18 100644 --- a/website/index.org +++ b/website/index.org @@ -12,12 +12,6 @@ units, coupling to other energy sectors, and mixed alternating and direct curren networks. PyPSA is designed to scale well with large networks and long time series. -As of 2018 PyPSA is under heavy development and therefore it is -recommended to use caution when using it in a production environment. -Some APIs may change - the changes in each PyPSA version are listed in -the [[./doc/release_notes.html][release notes]]. - - This project is maintained by the [[https://www.iai.kit.edu/english/2338.php][Energy System Modelling group]] at the [[https://www.iai.kit.edu/english/index.php][Institute for Automation and Applied Informatics]] at the [[http://www.kit.edu/english/index.php][Karlsruhe @@ -76,29 +70,25 @@ It has models for: - basic components out of which more complicated assets can be built, such as Combined Heat and Power (CHP) units, heat pumps, resistive Power-to-Heat (P2H), Power-to-Gas (P2G), battery electric vehicles - (BEVs), etc.; each of these is demonstrated in the - [[./examples/index.html][examples]] - -Functionality that will definitely be added soon: + (BEVs), Fischer-Tropsch, direct air capture (DAC), etc.; each of + these is demonstrated in the [[./examples/index.html][examples]] -- Multi-year investment optimisation -- Simple RMS simulations with the swing equation -- Distributed active power slack -- Non-linear power flow solution using - [[https://en.wikipedia.org/wiki/Holomorphic_embedding_load_flow_method][analytic - continuation]] in the complex plane following - [[https://github.com/SanPen/GridCal][GridCal]] Functionality that may be added in the future: -- Short-circuit current calculations -- Dynamic RMS simulations -- Small signal stability analysis -- Interactive web-based GUI with SVG -- OPF with the full non-linear network equations -- Dynamic EMT simulations -- Unbalanced load flow -- Port to [[http://julialang.org/][Julia]] +- Multi-year investment optimisation +- Distributed active power slack +- Interactive web-based GUI with SVG +- OPF with the full non-linear network equations +- Port to [[http://julialang.org/][Julia]] + +Other complementary libraries: + +- [[https://github.com/e2nIEE/pandapower][pandapower]] for more detailed modelling of distribution grids, + short-circuit calculations, unbalanced load flow and more +- [[https://github.com/JuliaEnergy/PowerDynamics.jl][PowerDynamics.jl]] for dynamic modelling of power grids at time scales + where differential equations are relevant + * Example scripts as Jupyter notebooks @@ -153,7 +143,7 @@ interactive plots generated with the [[https://plot.ly/python/][plotly]] library * What PyPSA uses under the hood PyPSA is written and tested to be compatible with both Python 2.7 and -Python 3.5. +Python 3.6. It leans heavily on the following Python packages: From 86f20555dc56648ce79184480963543741ee0960 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Mon, 31 Dec 2018 15:00:50 +0100 Subject: [PATCH 128/135] networkclustering: Implement custom generator_strategies for aggregategenerators p_nom_max has special treatment, in that if it is set to np.min, p_nom_max is divided by the weighting first. --- pypsa/networkclustering.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index 22de720a4..6e053dc6e 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -67,7 +67,7 @@ def _haversine(coords): a = np.sin((lat[1]-lat[0])/2.)**2 + np.cos(lat[0]) * np.cos(lat[1]) * np.sin((lon[0] - lon[1])/2.)**2 return 6371.000 * 2 * np.arctan2( np.sqrt(a), np.sqrt(1-a) ) -def aggregategenerators(network, busmap, with_time=True, carriers=None): +def aggregategenerators(network, busmap, with_time=True, carriers=None, custom_strategies=dict()): if carriers is None: carriers = network.generators.carrier.unique() @@ -79,9 +79,12 @@ def aggregategenerators(network, busmap, with_time=True, carriers=None): grouper = [generators.bus, generators.carrier] weighting = generators.weight.groupby(grouper, axis=0).transform(lambda x: (x/x.sum()).fillna(1.)) - generators['p_nom_max'] /= weighting generators['capital_cost'] *= weighting strategies = {'p_nom_max': np.min, 'weight': np.sum, 'p_nom': np.sum, 'capital_cost': np.sum} + strategies.update(custom_strategies) + if strategies['p_nom_max'] is np.min: + generators['p_nom_max'] /= weighting + strategies.update((attr, _make_consense('Generator', attr)) for attr in columns.difference(strategies)) new_df = generators.groupby(grouper, axis=0).agg(strategies) @@ -235,7 +238,8 @@ def get_buses_linemap_and_lines(network, busmap, line_length_factor=1.0, bus_str def get_clustering_from_busmap(network, busmap, with_time=True, line_length_factor=1.0, aggregate_generators_weighted=False, aggregate_one_ports={}, aggregate_generators_carriers=None, - bus_strategies=dict(), one_port_strategies=dict()): + bus_strategies=dict(), one_port_strategies=dict(), + generator_strategies=dict()): buses, linemap, linemap_p, linemap_n, lines = get_buses_linemap_and_lines(network, busmap, line_length_factor, bus_strategies) @@ -252,7 +256,8 @@ def get_clustering_from_busmap(network, busmap, with_time=True, line_length_fact if aggregate_generators_weighted: one_port_components.remove("Generator") generators, generators_pnl = aggregategenerators(network, busmap, with_time=with_time, - carriers=aggregate_generators_carriers) + carriers=aggregate_generators_carriers, + custom_strategies=generator_strategies) io.import_components_from_dataframe(network_c, generators, "Generator") if with_time: for attr, df in iteritems(generators_pnl): From 1fe5b73fcdfb6db39e6150795ad4e2161a2cf131 Mon Sep 17 00:00:00 2001 From: Tom Brown Date: Thu, 10 Jan 2019 18:53:55 +0100 Subject: [PATCH 129/135] PyPSA Version 0.13.2 Hyperlinked release notes can be found here: https://pypsa.org/doc/release_notes.html#pypsa-0-13-2-10th-january-2019 This minor release contains small new features and fixes. * Optimisation now works with Pyomo >= 5.6 (there was a Pyomo update that affected the opt.py LConstraint object). * New functional argument can be passed to Network.lopf: extra_postprocessing(network,snapshots,duals), which is called after solving and results are extracted. It can be used to get the values of shadow prices for constraints that are not normally extracted by PyPSA. * In the lopf kirchhoff formulation, the cycle constraint is rescaled by a factor 1e5, which improves the numerical stability of the interior point algorithm (since the coefficients in the constraint matrix were very small). * Updates and fixes to networkclustering, io, plot. We thank Soner Candas of TUM for reporting the problem with the most recent version of Pyomo and providing the fix. --- README.rst | 4 ++-- doc/conf.py | 6 +++--- doc/optimal_power_flow.rst | 21 ++++++++++++++++----- doc/release_notes.rst | 23 +++++++++++++++++++++++ pypsa/__init__.py | 10 +++++----- setup.py | 2 +- website/animations/pypsa-eur-30 | 2 +- 7 files changed, 51 insertions(+), 17 deletions(-) diff --git a/README.rst b/README.rst index b9bf5d8ce..f8e74fa2e 100644 --- a/README.rst +++ b/README.rst @@ -228,8 +228,8 @@ This can be found linked from the overall PyPSA Zenodo DOI: Licence ======= -Copyright 2015-2018 Tom Brown (FIAS), Jonas Hörsch (FIAS), David -Schlachtberger (FIAS) +Copyright 2015-2019 Tom Brown (KIT, FIAS), Jonas Hörsch (KIT, FIAS), +David Schlachtberger (FIAS) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/doc/conf.py b/doc/conf.py index 3c6f85d98..760b2dca3 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -55,8 +55,8 @@ # General information about the project. project = u'PyPSA' -copyright = u'2015-2018 Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)' -author = u'Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)' +copyright = u'2015-2019 Tom Brown (KIT, FIAS), Jonas Hoersch (KIT, FIAS), David Schlachtberger (FIAS)' +author = u'Tom Brown (KIT, FIAS), Jonas Hoersch (KIT, FIAS), David Schlachtberger (FIAS)' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -65,7 +65,7 @@ # The short X.Y version. version = u'0.13' # The full version, including alpha/beta/rc tags. -release = u'0.13.1' +release = u'0.13.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/optimal_power_flow.rst b/doc/optimal_power_flow.rst index 78cca86d8..8bc34a43f 100644 --- a/doc/optimal_power_flow.rst +++ b/doc/optimal_power_flow.rst @@ -28,15 +28,20 @@ Overview Execute: -``network.lopf(snapshots, solver_name="glpk", solver_io=None, extra_functionality=None, solver_options={}, keep_files=False, formulation="angles")`` +``network.lopf(snapshots, solver_name="glpk", solver_io=None, +extra_functionality=None, solver_options={}, keep_files=False, +formulation="angles",extra_postprocessing=None)`` where ``snapshots`` is an iterable of snapshots, ``solver_name`` is a string, e.g. "gurobi" or "glpk", ``solver_io`` is a string, ``extra_functionality`` is a function of network and snapshots that is -called before the solver (see below), ``solver_options`` is a -dictionary of flags to pass to the solver, ``keep_files`` means that -the ``.lp`` file is saved and ``formulation`` is a string in -``["angles","cycles","kirchhoff","ptdf"]`` (see :ref:`formulations` for more details). +called before the solver (see below), ``extra_postprocessing`` is a +function of network, snapshots and duals that is called after solving +(see below), ``solver_options`` is a dictionary of flags to pass to +the solver, ``keep_files`` means that the ``.lp`` file is saved and +``formulation`` is a string in +``["angles","cycles","kirchhoff","ptdf"]`` (see :ref:`formulations` +for more details). The linear OPF module can optimises the dispatch of generation and storage and the capacities of generation, storage and transmission. @@ -566,6 +571,12 @@ and stores both pass an ``extra_functionality`` argument to the LOPF to add functionality. +The function ``extra_postprocessing`` is called after the model has +solved and the results are extracted. This function must take three +arguments `extra_postprocessing(network,snapshots,duals)`. It allows +the user to extract further information about the solution, such as +additional shadow prices for constraints. + Inputs ------ diff --git a/doc/release_notes.rst b/doc/release_notes.rst index ea8de9745..0e8c53676 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -2,6 +2,29 @@ Release Notes ####################### + +PyPSA 0.13.2 (10th January 2019) +================================ + +This minor release contains small new features and fixes. + +* Optimisation now works with Pyomo >= 5.6 (there was a Pyomo update + that affected the opt.py LConstraint object). +* New functional argument can be passed to Network.lopf: + extra_postprocessing(network,snapshots,duals), which is called after + solving and results are extracted. It can be used to get the values + of shadow prices for constraints that are not normally extracted by + PyPSA. +* In the lopf kirchhoff formulation, the cycle constraint is rescaled + by a factor 1e5, which improves the numerical stability of the + interior point algorithm (since the coefficients in the constraint + matrix were very small). +* Updates and fixes to networkclustering, io, plot. + +We thank Soner Candas of TUM for reporting the problem with the most +recent version of Pyomo and providing the fix. + + PyPSA 0.13.1 (27th March 2018) ============================== diff --git a/pypsa/__init__.py b/pypsa/__init__.py index 87121001c..4a1892632 100644 --- a/pypsa/__init__.py +++ b/pypsa/__init__.py @@ -1,7 +1,7 @@ -## Copyright 2015-2017 Tom Brown (FIAS), Jonas Hoersch (FIAS), David -## Schlachtberger (FIAS) +## Copyright 2015-2019 Tom Brown (KIT, FIAS), Jonas Hoersch (KIT, +## FIAS), David Schlachtberger (FIAS) ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as @@ -30,6 +30,6 @@ from .components import Network, SubNetwork -__version__ = "0.13.1" -__author__ = "Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)" -__copyright__ = "Copyright 2015-2018 Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS), GNU GPL 3" +__version__ = "0.13.2" +__author__ = "Tom Brown (KIT, FIAS), Jonas Hoersch (KIT, FIAS), David Schlachtberger (FIAS)" +__copyright__ = "Copyright 2015-2019 Tom Brown (KIT, FIAS), Jonas Hoersch (KIT, FIAS), David Schlachtberger (FIAS), GNU GPL 3" diff --git a/setup.py b/setup.py index 024b8433a..52f9472da 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ setup( name='pypsa', - version='0.13.1', + version='0.13.2', author='Tom Brown (FIAS), Jonas Hoersch (FIAS), David Schlachtberger (FIAS)', author_email='brown@fias.uni-frankfurt.de', description='Python for Power Systems Analysis', diff --git a/website/animations/pypsa-eur-30 b/website/animations/pypsa-eur-30 index abfe0fcfa..bb23ff027 120000 --- a/website/animations/pypsa-eur-30 +++ b/website/animations/pypsa-eur-30 @@ -1 +1 @@ -../../../../../programming/html5/d3-network \ No newline at end of file +/home/tom/energy/playground/pypsa-animation/ \ No newline at end of file From 13a0fb667fb29015e7e81b151133f70d1638aac4 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Tue, 22 Jan 2019 11:18:50 +0100 Subject: [PATCH 130/135] networkclustering: Fix busmap_by_kmeans docstring to include buses_i argument --- pypsa/networkclustering.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index 6e053dc6e..344528c03 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -397,8 +397,11 @@ def busmap_by_kmeans(network, bus_weightings, n_clusters, buses_i=None, ** kwarg Series of integer weights for buses, indexed by bus names. n_clusters : int Final number of clusters desired. + buses_i : None|pandas.Index + If not None (default), subset of buses to cluster. kwargs - Any remaining arguments to be passed to KMeans (e.g. n_init, n_jobs) + Any remaining arguments to be passed to KMeans (e.g. n_init, + n_jobs). Returns ------- From d044ed36312ce50f1c033ec2e2e1c72f15e68bc8 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Mon, 4 Feb 2019 11:49:06 +0100 Subject: [PATCH 131/135] Update Networkclustering --- pypsa/networkclustering.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index 344528c03..03180ae86 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -80,7 +80,8 @@ def aggregategenerators(network, busmap, with_time=True, carriers=None, custom_s weighting = generators.weight.groupby(grouper, axis=0).transform(lambda x: (x/x.sum()).fillna(1.)) generators['capital_cost'] *= weighting - strategies = {'p_nom_max': np.min, 'weight': np.sum, 'p_nom': np.sum, 'capital_cost': np.sum} + strategies = {'p_nom_min':np.min,'p_nom_max': np.min, 'weight': np.sum, 'p_nom': np.sum, 'p_nom_opt': np.sum, + 'marginal_cost': np.mean, 'capital_cost': np.mean} strategies.update(custom_strategies) if strategies['p_nom_max'] is np.min: generators['p_nom_max'] /= weighting @@ -126,6 +127,10 @@ def aggregate_max_hours(max_hours): strategies = {attr: default_strategies.get(attr, _make_consense(component, attr)) for attr in columns} strategies.update(custom_strategies) + if 'efficiency_dispatch' in columns: + strategies.update({'efficiency_dispatch':np.mean, 'standing_loss':np.mean, 'efficiency_store':np.mean}) + if 'capital_cost' in columns: + strategies.update({'capital_cost':np.mean}) new_df = old_df.groupby(grouper).agg(strategies) new_df.index = _flatten_multiindex(new_df.index).rename("name") @@ -384,7 +389,7 @@ def louvain_clustering(network, **kwds): # available using pip as scikit-learn from sklearn.cluster import KMeans - def busmap_by_kmeans(network, bus_weightings, n_clusters, buses_i=None, ** kwargs): + def busmap_by_kmeans(network, bus_weightings, n_clusters, buses_i=None,load_cluster=False, n_init=10, max_iter=300, tol=1e-6, n_jobs=1, ** kwargs): """ Create a bus map from the clustering of buses in space with a weighting. @@ -418,10 +423,16 @@ def busmap_by_kmeans(network, bus_weightings, n_clusters, buses_i=None, ** kwarg # same position points = (network.buses.loc[buses_i, ["x","y"]].values .repeat(bus_weightings.reindex(buses_i).astype(int), axis=0)) + #optional load of cluster coordinates + if load_cluster != False: + busmap_array = np.loadtxt(load_cluster) + kmeans = KMeans(init=busmap_array, n_clusters=n_clusters, n_init=n_init, max_iter=max_iter, tol=tol, n_jobs=n_jobs, ** kwargs) + kmeans.fit(points) - kmeans = KMeans(init='k-means++', n_clusters=n_clusters, ** kwargs) + else: + kmeans = KMeans(init='k-means++', n_clusters=n_clusters, ** kwargs) - kmeans.fit(points) + kmeans.fit(points) busmap = pd.Series(data=kmeans.predict(network.buses.loc[buses_i, ["x","y"]]), index=buses_i).astype(str) From 2912d9b4c3f4f65fd34b1150795c9a098ffaecd4 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Wed, 6 Feb 2019 11:33:23 +0100 Subject: [PATCH 132/135] Implement clustering of storage-units from pypsa0.11.0fork --- pypsa/networkclustering.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index abbca2287..e0b3a00e4 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -113,7 +113,13 @@ def aggregateoneport(network, busmap, component, with_time=True, custom_strategi attrs = network.components[component]["attrs"] old_df = getattr(network, network.components[component]["list_name"]).assign(bus=lambda df: df.bus.map(busmap)) columns = set(attrs.index[attrs.static & attrs.status.str.startswith('Input')]) & set(old_df.columns) - grouper = old_df.bus if 'carrier' not in columns else [old_df.bus, old_df.carrier] + + if ('carrier' in columns and 'max_hours' in columns): + grouper = [old_df.bus, old_df.carrier, old_df.max_hours] + elif ('carrier' in columns and not 'max_hours' in columns): + grouper = [old_df.bus, old_df.carrier] + else: + grouper = old_df.bus def aggregate_max_hours(max_hours): if (max_hours == max_hours.iloc[0]).all(): @@ -122,8 +128,8 @@ def aggregate_max_hours(max_hours): return (max_hours * _normed(old_df.p_nom.reindex(max_hours.index))).sum() default_strategies = dict(p=np.sum, q=np.sum, p_set=np.sum, q_set=np.sum, - p_nom=np.sum, p_nom_max=np.sum, p_nom_min=np.sum, - max_hours=aggregate_max_hours) + p_nom=np.sum, p_nom_max=np.sum, p_nom_min=np.sum) + #max_hours=aggregate_max_hours) strategies = {attr: default_strategies.get(attr, _make_consense(component, attr)) for attr in columns} strategies.update(custom_strategies) @@ -132,14 +138,27 @@ def aggregate_max_hours(max_hours): if 'capital_cost' in columns: strategies.update({'capital_cost':np.mean}) new_df = old_df.groupby(grouper).agg(strategies) - new_df.index = _flatten_multiindex(new_df.index).rename("name") + if 'max_hours' in columns: + # index level max_hours is of type numeric.Float64Index, that cannot + # be handled by _flatten_multiindex, other indices are of type + # base.Index, thus we have to convert the datatype of max_hours. There + # might be a better way to do it inplace instead of reconstructing the + # Index. + new_df.reset_index(drop=True, inplace=True) + new_df.max_hours = new_df.max_hours.astype('str') + new_df.set_index(['bus', 'carrier', 'max_hours']) + + new_df.index = _flatten_multiindex(new_df.index).rename("name") new_pnl = dict() if with_time: old_pnl = network.pnl(component) for attr, df in iteritems(old_pnl): if not df.empty: pnl_df = df.groupby(grouper, axis=1).sum() + if 'max_hours' in columns: + pnl_df.columns.set_levels(pnl_df.columns.levels[2].astype(object), level=2, inplace=True) + pnl_df.columns.set_levels(pnl_df.columns.levels[2].astype(str), level=2, inplace=True) pnl_df.columns = _flatten_multiindex(pnl_df.columns).rename("name") new_pnl[attr] = pnl_df From 9c6ef5cc09822850f4b267ee7a17ee22c5bb0084 Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Wed, 6 Feb 2019 11:34:18 +0100 Subject: [PATCH 133/135] Changes for snapshots-clustering --- pypsa/opf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypsa/opf.py b/pypsa/opf.py index 0a9cbf5cf..d7fb2a7eb 100644 --- a/pypsa/opf.py +++ b/pypsa/opf.py @@ -449,7 +449,7 @@ def su_p_lower(model,su_name,snapshot): soc[su,sn] = [[],"==",0.] - elapsed_hours = network.snapshot_weightings[sn] + elapsed_hours = 1# network.snapshot_weightings[sn] if i == 0 and not sus.at[su,"cyclic_state_of_charge"]: previous_state_of_charge = sus.at[su,"state_of_charge_initial"] From cf98c18f775f18b624fa5a63793b96670f3c345f Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 14 Feb 2019 16:07:00 +0100 Subject: [PATCH 134/135] Add s_nom_total for lines --- pypsa/component_attrs/lines.csv | 1 + pypsa/networkclustering.py | 1 + 2 files changed, 2 insertions(+) diff --git a/pypsa/component_attrs/lines.csv b/pypsa/component_attrs/lines.csv index 044c9305f..44fbc3274 100644 --- a/pypsa/component_attrs/lines.csv +++ b/pypsa/component_attrs/lines.csv @@ -12,6 +12,7 @@ s_nom_extendable,boolean,n/a,False,Switch to allow capacity s_nom to be extended s_nom_min,float,MVA,0.,"If s_nom is extendable in OPF, set its minimum value.",Input (optional) s_nom_max,float,MVA,inf,"If s_nom is extendable in OPF, set its maximum value (e.g. limited by potential).",Input (optional) s_max_pu,static or series,per unit,1.,"The maximum allowed absolute flow per unit of s_nom for the OPF (e.g. can be set <1 to approximate n-1 factor, or can be time-varying to represent weather-dependent dynamic line rating for overhead lines).",Input (optional) +s_nom_total,float,MVA,inf,If s_nom is reduced by a branch-capacity factor, this shows its total installed capacity. capital_cost,float,currency/MVA,0.,"Capital cost of extending s_nom by 1 MVA.",Input (optional) length,float,km,0.,"Length of line used when ""type"" is set, also useful for calculating the capital cost.",Input (optional) terrain_factor,float,per unit,1.,"Terrain factor for increasing capital cost.",Input (optional) diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index e0b3a00e4..87d678dc0 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -226,6 +226,7 @@ def aggregatelinegroup(l): s_nom_min=l['s_nom_min'].sum(), s_nom_max=l['s_nom_max'].sum(), s_nom_extendable=l['s_nom_extendable'].any(), + s_nom_total=l['s_nom_total'].sum(), num_parallel=l['num_parallel'].sum(), capital_cost=costs, length=length_s, From bf07f98aab280217b5211596a1916310987994ad Mon Sep 17 00:00:00 2001 From: ClaraBuettner Date: Thu, 7 Mar 2019 14:19:20 +0100 Subject: [PATCH 135/135] Save load-cluster file --- pypsa/networkclustering.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypsa/networkclustering.py b/pypsa/networkclustering.py index 87d678dc0..2e58c35c3 100644 --- a/pypsa/networkclustering.py +++ b/pypsa/networkclustering.py @@ -460,8 +460,8 @@ def busmap_by_kmeans(network, bus_weightings, n_clusters, buses_i=None, load_clu else: kmeans = KMeans(init='k-means++', n_clusters=n_clusters, ** kwargs) - kmeans.fit(points) + np.savetxt("cluster_coord_k_%i_result" % (n_clusters), kmeans.cluster_centers_) busmap = pd.Series(data=kmeans.predict(network.buses.loc[buses_i, ["x","y"]]), index=buses_i).astype(str)