-
Notifications
You must be signed in to change notification settings - Fork 0
/
calculation.py
285 lines (242 loc) · 9.15 KB
/
calculation.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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# -*- coding: utf-8 -*-
# License information
# Pandas - BSD 3 - https://github.com/pandas-dev/pandas/blob/main/LICENSE
# Numpy - BDS - https://github.com/numpy/numpy/blob/main/LICENSE.txt
import pandas as pd
from numpy import arange as np_arrange
### Les inn fil
def read(file):
df = pd.read_csv(file, delimiter=",", decimal=",")
try:
df['Timestamp'] = pd.to_datetime(df['Timestamp'], format="%d/%m/%Y %H:%M:%S")
except:
pass
df.set_index(['Timestamp'], inplace=True)
return df
# Copy a step to a new dataFrame
def find_step(df, step, name="SB401"):
start_step = step[0]
end_step = step[1]
for i in range(1, len(df)):
if df.iloc[-i][name] == start_step:
if df.iloc[-(i + 1)][name] == end_step:
df = df.drop(df.tail(i - 5).index)
break
for i in range(6, len(df)):
if df.iloc[-i][name] != end_step:
df = df.tail(i - 1)
break
return df
class step_analytics:
# TODO - Make setters for "response start" and "max_value"
# The setters should recalculate everything
def __init__(self, df, sampling_time, step_from_to, factor=0.1):
self.step_df = df # dataframe with one step
self._step_from = step_from_to[0] # Step from value
self._step_to = step_from_to[1] # Step to value
self._factor = factor # The factor for when we say a change in value represent the first responce
self._sampling_time = sampling_time
# SYSTEM VALUES
self.theta = 0
self.tau = 0
self.k = 0
self.k_m = 0
self.k_c = 0
self.t_i = 0
self.positive_step = True
self.gain = "Name of gain var" # Navn på pådrags organ
self.measured_value = "Name of response var" # Navn på måleverdien
self.prosent63 = []
self.band = False
# Find and return the position in the DF where the step happens
def find_step(self):
"""
Find where the step is done in the pandas dataframe
:return: index place of the step
"""
i = 1
while self.step_df.iloc[-i][self.gain] == self._step_from:
i += 1
return -i + 1
# Change the index of the DF to be == with sampling time
# since you can argue that timestamps isnt to important for the test
def change_index(self):
""""
Set df[-1] (the end of dataframe) = 0
Each row is calculated row_nr*sampling_time
:returns True on success:
"""
try:
self.step_df["Time"] = (np_arrange(len(self.step_df)) * self._sampling_time)[::-1]
ret = True
except:
print("Error in changing index")
ret = False
return ret
# [::-1] -> Start the df at max time and decrease (Reversing the array)
# This is done since the dataframe starts at the end
def calculate(self, band=False):
"""
Run calculations
band : If set true, max value will be calculated out from 5% of observed max
:returns: string on sucsess, false on error
"""
self.use_band = band
# try:
self.find_parameters()
return self.__str__()
def re_calculate(self, max, theta):
try:
self.use_band = False
self.theta = theta
self.max_val = max
self.find_delta()
self.prosent63 = self.find_precent(0.63)
self.find_tau()
# Find tau value
self.find_response_for_plot()
self.calc_skogestad()
return self.__str__()
except:
return "error"
def find_tau(self):
"""
Calculate tau
Time from response to 63%
:return:
"""
self.tau = self.step_df.iloc[self.prosent63[0]]['Time'] - self.theta - self.step_df.iloc[self.start[0]]["Time"]
def find_theta(self):
"""
Find theta
From step start to response
:return:
"""
self.theta = self.step_df.iloc[self.response]["Time"] - self.step_df.iloc[self.start[0]]["Time"]
def find_precent(self, prct=0.63, band=0.05):
"""
Find value and index for 63% of step
:return:
"""
percent = []
# Find prct value and keep index and value
dy_band = self.dY - self.dY * band
percent_value = dy_band * prct + self.start[1]
# Loop the df
for i in range(1, len(self.step_df)):
# If the value for 63% is found
if percent_value <= self.step_df.iloc[-i][self.measured_value]:
if (abs(self.step_df.iloc[-i][self.measured_value] - percent_value) > abs(
self.step_df.iloc[-i + 1][self.measured_value] - percent_value)):
i = i + 1
# Return index and value
percent = [-i, (self.step_df.iloc[-i][self.measured_value])]
return percent
def find_max(self, use_factor=False):
"""
Find the maxima value for the step
:return:
"""
# Add posibility to find it with a bond +/- 5% feks
# Since SMIC say find max when t-> inf, we can use max/min functions since the step is issolated
self.max_val = self.step_df[self.measured_value].max()
self.max_val_id = self.step_df[self.measured_value].idxmax()
def find_delta(self):
"""
Find:
dY = max-start
dU = step start value - step stop value
k = dY / dU
:return:
"""
self.dY = abs((self.max_val - self.start[1]))
# # dU = 60-40 # Use this if you got more then one stepresponse in the dataset
self.dU = abs(self._step_to - self._step_from)
#
self.k = self.dY / self.dU
def find_factor(self, calculate_factor=True):
"""
Set the factor to 5% of dY
:return:
"""
if calculate_factor:
self._factor = self.dY * 0.05
else:
pass
def find_response(self):
for i in range(-self.start[0], len(self.step_df)):
if abs(self.start[1] - self.step_df.iloc[-i][self.measured_value]) >= self._factor:
self.response = -i
break
def find_response_for_plot(self):
self.response = -int((self.theta / self._sampling_time) - self.start[0])
def find_parameters(self):
# Change index from timestamp to sampling time
self.change_index()
step_start = self.find_step()
# Add it to list start to be able to access it easy later on
self.start = [step_start, self.step_df.iloc[step_start][self.measured_value]]
# Find maxima value
self.find_max(use_factor=False)
# Find dY, dU and k
self.find_delta() # Find dY/du
self.find_factor(self.use_band) # Find the factor we would use as a % of dY/dU
# Find where we get the first sign of an response
self.find_response()
# Copy index to a column, to make it easier to do math on it
self.step_df.index = self.step_df["Time"]
self.find_theta()
# self.find_63()
self.prosent63 = self.find_precent(0.63)
# All values aquired, and you know what that means...
# MATH TIME
self.calc_skogestad()
def calc_skogestad(self):
# 63% = (dY * 0.63) + start_val
# Theta = response time - start time
# Tau = 63% time - response time
# Find tau value
self.find_tau()
self.k_m = self.k / self.tau
# 4 x tau -> 98% of response
tc = self.theta
# # firstOrder
self.k_c = (1 / self.k) * self.tau / (tc + self.theta)
self.t_i = min(self.tau, 4 * (tc + self.theta))
# Setters and getters for important vars
# Doing it this way, we can automagicly re-calculate when vars are changed
def __str__(self):
return (
f'Step from {self._step_from} to {self._step_to} \nTheta: {self.theta}\nTau: {self.tau}\nK : {self.k:.2}\nK* : {self.k_m:.2}\n'
f'Kc : {self.k_c:.3}\n Ti : {self.t_i}'
f'\nMax = {self.max_val}\n63% value = {self.prosent63[1]}\n'
f'Start value= {self.start[1]}\n63% time : {self.step_df.iloc[self.prosent63[0]]["Time"]}'
f'\nResponse value = {self.step_df.iloc[self.response][self.measured_value]} \nResponse time = {-(self.response - self.start[0]) * self._sampling_time}')
def get_vars(self):
var = {
"from": self._step_from,
"to": self._step_to,
"theta": self.theta,
"tau": self.tau,
"kc": self.k_c,
"ti": self.t_i,
"k": self.k,
"max": self.max_val,
"63%val": self.prosent63[1],
"gain" :self.gain,
"resp":self.measured_value,
}
return var
@property
def factor(self):
return self._factor
@factor.setter
def factor(self, factor):
self._factor = factor
self.calculate()
@property
def sampling_time(self):
return self._sampling_time
@sampling_time.setter
def sampling_time(self, sampling_time):
self._sampling_time = sampling_time