Skip to content

Commit

Permalink
new tests and suggestions integrated
Browse files Browse the repository at this point in the history
Updated method to include additional inputs suggested by Fabian and error thrown if snapshots/snapshot weightings in the networks do not agree. Also added warning if components with IDs matching component in existing network are added - this warning does not apply to components of type LineType or TransformerType but duplicate components will still not be added in this case.

Updated static test to reflect these changes and added new tests for the case where the method returns a new network (inplace=false), and the case where time-varying data is added (with_time=True_
  • Loading branch information
jessLryan committed Nov 29, 2023
1 parent 18360da commit 4cd5fe8
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 31 deletions.
70 changes: 46 additions & 24 deletions pypsa/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,13 +560,13 @@ def set_snapshots(
lambda self: self._snapshots, set_snapshots, doc="Time steps of the network"
)

def add_network(self, network, components_to_skip=None):
def add_network(self, other, components_to_skip=None, inplace=False, with_time=True):
"""
Function: Add all components from a new network into this network.
Function: Add components from network "other" into this network.
Notes:
If two components in different networks have the same index value then the component in
the new network will not be added to this network.
If two components in different networks have the same ID value then the component in
the new network will not be added into this network.
For static data:
If a component in the new network does not have values for attributes present in
Expand All @@ -575,37 +575,59 @@ def add_network(self, network, components_to_skip=None):
final network.
For time-varying data:
When adding time-varying data (as pandas DataFrames), if the new data is missing time snapshots
compared to the current network, default values will be set. If the new data has additional time
snapshots then the attributes for these snapshots will not be present in the final network.
If time-varying data is added, the new data must have identical snapshots and snapshot weightings.
Parameters
----------
n : pypsa.Network
*args : list
List of names of components in new network which are not to be merged
e.g. "Buses"
other : pypsa.Network
network whose components are to be added to this network
components_to_skip : list-like, default None
list of names of components which are not to be added e.g. "Bus"
inplace : boolean, default False
if True, new components are added into current network in-place, otherwise
a new network containing the components of both networks is returned
with_time : bool, default True
if False, only static data is merged
Returns
-------
None.
new_network : pypsa.Network
network containing components of both input networks, or None if inplace=True
"""
to_skip = ["Network"]
if components_to_skip:
for component_name in components_to_skip:
to_skip.append(component_name)
if network.srid != self.srid:
to_skip.update(components_to_skip)
if other.srid != self.srid:
logger.warning(
f"Spatial Reference System Indentifier {network.srid} for new network not equal to value {self.srid} of existing network. Original value will be used."
f"Warning: spatial Reference System Indentifier {other.srid} for new network not equal to value {self.srid} of existing network. Original value will be used."
)
for component in network.iterate_components(
network.components.keys() - to_skip
):
# import static data for component
self.import_components_from_dataframe(component.df, component.name)
# import time series data for component
for k, v in component.pnl.items():
self.import_series_from_dataframe(v, component.name, k)
if with_time:
snapshots_aligned = self.snapshots.equals(other.snapshots)
weightings_aligned = self.snapshot_weightings.equals(other.snapshot_weightings)
assert snapshots_aligned and weightings_aligned, (
"Error, snapshots or snapshot weightings do not agree, cannot add network with time-varying attributes.")

new_network = self if inplace else self.copy()

for component in other.iterate_components(other.components.keys() - to_skip):
#we do not add components whose ID is present in this network
index_list = list(set(component.df.index)-set(self.df(component.name).index))
if set(index_list) != set(component.df.index) and component.name not in ["LineType", "TransformerType"]:
logger.warning(
f"Warning: components of type {component.name} in new network have duplicate IDs in existing network. These components will not be added")
#import static data for component
df_to_add = component.df[component.df.index.isin(index_list)]
new_network.import_components_from_dataframe(df_to_add, component.name)
if with_time:
#import time series data for component
for attr, df in component.pnl.items():
df_to_add = df.loc[:, df.columns.isin(index_list)]
new_network.import_series_from_dataframe(df, component.name, attr)
return None if inplace else new_network

@property
def snapshot_weightings(self):
Expand Down
71 changes: 64 additions & 7 deletions test/test_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,19 +235,76 @@ def test_copy_no_snapshot(ac_dc_network):
assert snapshot not in copied_network.snapshots


def test_add_network_static(ac_dc_network, empty_network_5_buses):
def test_add_network_static_inplace(ac_dc_network, empty_network_5_buses):
"""
GIVEN the AC DC exemplary pypsa network and an empty PyPSA network with 5
buses.
buses
WHEN the second network is added to the first
THEN the first network should now contain its original buses and
also the buses in the second network
also the buses in the second network
"""
ac_dc_network.add_network(empty_network_5_buses,inplace=True,with_time=False)
buses_now = ac_dc_network.buses.index
buses_added_network = empty_network_5_buses.buses.index

assert set(buses_added_network).issubset(set(buses_now))

ac_dc_network.add_network(empty_network_5_buses)

busesNow = ac_dc_network.buses.index
busesAddedNetwork = empty_network_5_buses.buses.index
assert set(busesAddedNetwork).issubset(set(busesNow))

def test_add_network_static_not_inplace(ac_dc_network, empty_network_5_buses):
"""
GIVEN the AC DC exemplary pypsa network and an empty PyPSA network with 5
buses.
WHEN the second network is added to the first with inplace=False
THEN we obtain a new network which should contain the buses of the first
network and the second network
"""

new_network = ac_dc_network.add_network(empty_network_5_buses,inplace=False,with_time=False)

buses_now = new_network.buses.index
buses_added_network = empty_network_5_buses.buses.index
assert set(buses_added_network).issubset(set(buses_now))


def test_add_network_with_time_inplace():
"""
GIVEN two networks containing three buses each with time-varying attributes.
WHEN the second network is added to the first with with_time=True
THEN the first network should now contain the time-varying attributes of the
buses in the second network
"""

snapshots = list()
for year in ["2015","2016","2017"]:
snapshots.append(f"{year}-01-01 00:00")

#intialise "first" network
network_added_to = components.Network()
network_added_to.set_snapshots(snapshots)
buses_added_to = set()

#intialise "second" network
network_to_add = components.Network()
network_to_add.set_snapshots(snapshots)
buses_to_add = set()

#add buses with time-varying attributes to both networks
for i in range(3):
buses_added_to.add(f"bus_to_add_{i}")
network_to_add.add("Bus", f"bus_to_add_{i}",p=[1,2,3])
buses_added_to.add(f"bus_added_to_{i}")
network_added_to.add("Bus", f"bus_added_to_{i}",p=[1,2,3])

network_added_to.add_network(network_to_add,inplace=True,with_time=True)

buses_t_now = set(network_added_to.buses_t['p'].columns.values)
#check that the first network now contains time-varying data
#for buses in both networks
assert(buses_t_now == buses_added_to.union(buses_to_add))

0 comments on commit 4cd5fe8

Please sign in to comment.