-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsimulation.py
128 lines (101 loc) · 4.85 KB
/
simulation.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import os
import numpy as np
import pandas as pd
from network import Network
class Simulation:
def __init__(self, data_folder, t1, t2):
self.data_folder = data_folder
self.t1 = t1
self.t2 = t2
self.net = Network(data_folder)
self.time_range = np.arange(t1, t2+1, 1)
self.T = len(self.time_range)
self.elec = pd.read_csv(os.path.join(data_folder, 'tariffs.csv'))
self.demands = pd.read_csv(os.path.join(data_folder, 'demands.csv'))
self.data = self.build()
self.n_comb_facilities = len(self.net.fsp['name'].unique())
def build(self):
df = pd.DataFrame(index=self.time_range)
df['hour'] = df.index.to_series() % 24
df = pd.merge(df, self.elec, left_on='hour', right_on='time', how='inner').drop('time', axis=1)
df = pd.merge(df, self.demands, left_on='hour', right_on='time', how='inner').drop('time', axis=1)
df.index = self.time_range
return df
def get_total_max_inflow(self, tank_idx):
fsp_inflows = self.net.fsp.loc[self.net.fsp['in'] == tank_idx, :]
max_fsp_inflow = fsp_inflows[['name', 'flow']].groupby('name').max()
total_max_fsp_inflow = max_fsp_inflow['flow'].sum()
vsp_inflows = self.net.vsp.loc[self.net.vsp['in'] == tank_idx, :]
max_vsp_inflow = vsp_inflows[['name', 'max_flow']].groupby('name').max()
total_max_vsp_inflow = max_vsp_inflow['max_flow'].sum()
return total_max_fsp_inflow + total_max_vsp_inflow
def get_tank_demand(self, tank_idx):
tank_consumer = self.net.tanks.loc[tank_idx, "demand"]
return self.data[tank_consumer]
def get_min_vol_vector(self, tank_idx, is_dynamic=True):
static_min_vol = self.net.tanks.loc[tank_idx, "min_vol"]
final_vol = self.net.tanks.loc[tank_idx, "final_vol"]
if not is_dynamic:
min_vol = static_min_vol * np.ones(self.T, 1)
else:
q_max = self.get_total_max_inflow(tank_idx)
demand = self.get_tank_demand(tank_idx)
dynamic_min = [final_vol]
for i, t in enumerate(self.time_range[::-1]):
dynamic_min = [max(static_min_vol, dynamic_min[0] + demand.loc[t] - q_max)] + dynamic_min
min_vol = dynamic_min[1:]
return min_vol
def get_tank_vol(self, tank_idx, x_fsp, x_vsp):
mat = np.tril(np.ones((self.T, self.T)))
tank_consumer = self.net.tanks.loc[tank_idx, 'demand']
cum_tank_demand = self.data[tank_consumer].values.cumsum()
lhs = self.net.tanks.loc[tank_idx, "init_vol"]
for fsp_idx, row_fsp in self.net.fsp.iterrows():
if row_fsp['in'] == tank_idx:
lhs += row_fsp['flow'] * mat @ x_fsp[fsp_idx, :].T
elif row_fsp['out'] == tank_idx:
lhs += -1 * row_fsp['flow'] * mat @ x_fsp[fsp_idx, :].T
else:
continue
for vsp_idx, row_vsp in self.net.vsp.iterrows():
if row_vsp['in'] == tank_idx:
lhs += mat @ x_vsp[vsp_idx, :].T
elif row_vsp['out'] == tank_idx:
lhs += -1 * mat @ x_vsp[vsp_idx, :].T
else:
continue
vol = lhs - cum_tank_demand
return vol
def get_all_tanks_vol(self, x_fsp, x_vsp):
df = pd.DataFrame()
for tank_idx, row in self.net.tanks.iterrows():
vol = self.get_tank_vol(tank_idx, x_fsp, x_vsp)
df = pd.concat([df, pd.DataFrame({tank_idx: vol}, index=self.time_range)], axis=1)
return df
def get_facilities_flows(self, x_fsp, x_vsp):
df = pd.DataFrame()
for facility in self.net.fsp['name'].unique():
facility_idx = self.net.fsp.loc[self.net.fsp['name'] == facility].index
flows = self.net.fsp.loc[facility_idx, 'flow'].values
facility_flow = (x_fsp[facility_idx, :] * flows[:, np.newaxis]).sum(axis=0)
df = pd.concat([df, pd.DataFrame({facility: facility_flow}, index=self.time_range)], axis=1)
for i, row in self.net.vsp.iterrows():
df = pd.concat([df, pd.DataFrame({row['name']: x_vsp[i, :]}, index=self.time_range)], axis=1)
return df
def get_cost(self, x_fsp):
"""
Cost is calculated based on Fixed Speed Pumps alone
VSP have no additional costs in the examples of this paper
All modules are built such that vsp costs can be added easily in the future
The function returns a vector of the hourly cost
"""
power = self.net.fsp.loc[:, "power"].values
power = power @ x_fsp
cost = power * self.data.loc[:, "tariff"].values
return cost
def get_nominal_demands(self, flatten=False):
consumers = self.net.tanks['demand']
demands = self.data[consumers].values
if flatten:
demands = demands.flatten("F")
return demands