Nektar++
serial.py
Go to the documentation of this file.
1# Andrew Gloster
2# Summer 2016
3# Python Version of Communication Model Implementation
4# Functions relating to serial computations and fitting of serial element of the model
5
6#------------------------------------
7# Import relevant modules
8#------------------------------------
9
10from scipy.optimize import fmin
11import matplotlib.pyplot as plt
12
13#------------------------------------
14# Import relevant functions and classes
15#------------------------------------
16
17import math as m
18import numpy as np
19from scipy.optimize import minimize
20
21#------------------------------------
22# New Function
23#------------------------------------
24
25# Compute length of one timestep for given inputs (Model must be fitted prior to this function)
26def Serial_Computation(P, Num_Elements, Num_Modes, N_P, N_V_1, N_V_2, N_V_3, Num_Constants, constants, Scheme):
27
28# Operation Counts as per the thesis
29 O_A_1 = 9 * Num_Elements * (P + 1) ** 2 * Num_Modes * m.log(Num_Modes, 2)
30 O_A_2 = Num_Elements * Num_Modes * (P + 1) ** 2
31 O_A_3 = 6 * Num_Elements * (P + 1) ** 4 * Num_Modes
32 O_A_4 = 15 * Num_Elements * (P + 1) ** 2 * Num_Modes
33
34 T_A = O_A_1 + O_A_2 + O_A_3 + O_A_4
35
36 if (Scheme == 'IterativeFull'):
37 O_E_1 = 8 * Num_Elements * (P + 1) ** 2
38 O_E_2 = Num_Elements * (P + 1) ** 2
39 O_E_3 = Num_Elements * ((4 * P ** 3) + (18 * P ** 2) + (26 * P) + 12)
40 O_E_4 = 6 * Num_Elements * (P + 1) ** 2
41
42 t_e = O_E_1 + O_E_2 + O_E_3 + O_E_4
43
44 T_E = (N_P + N_V_1 + N_V_2 + N_V_3) * t_e
45
46 if (Scheme == 'IterativeStaticCond'):
47 O_E_1 = 8 * ((P - 1) ** 2) * 4 * P * Num_Elements * Num_Modes
48 O_E_2 = (N_P + N_V_1 + N_V_2 + N_V_3) * 2 * 16 * (P ** 2) * Num_Elements
49 O_E_3 = (8 * 4 * ((P - 1) ** 2)) + (4 * ((P - 1) ** 2)) * Num_Elements * Num_Modes
50 O_E_4 = 8 * ((((P - 1) ** 2)) ** 2) * Num_Elements * Num_Modes
51
52 T_E = O_E_1 + O_E_2 + O_E_3 + O_E_4
53
54# Divide by operations per second.
55
56 if (Num_Constants == 1):
57 T_A = T_A / constants
58 T_E = T_E / constants
59
60 if (Num_Constants == 2):
61 T_A = T_A / constants[0]
62 T_E = T_E / constants[1]
63
64 Time = T_A + T_E
65
66 return(Time)
67
68#------------------------------------
69# New Function
70#------------------------------------
71
72def Operation_Count(P, Num_Elements, Num_Modes, N_P, N_V_1, N_V_2, N_V_3, Scheme):
73
74# Operation Counts as per the thesis
75 O_A_1 = 9 * Num_Elements * (P + 1) ** 2 * Num_Modes * m.log(Num_Modes, 2)
76 O_A_2 = Num_Elements * Num_Modes * (P + 1) ** 2
77 O_A_3 = 6 * Num_Elements * (P + 1) ** 4 * Num_Modes
78 O_A_4 = 15 * Num_Elements * (P + 1) ** 2 * Num_Modes
79
80 T_A = O_A_1 + O_A_2 + O_A_3 + O_A_4
81
82 if (Scheme == 'IterativeFull'):
83 O_E_1 = 8 * Num_Elements * (P + 1) ** 2
84 O_E_2 = Num_Elements * (P + 1) ** 2
85 O_E_3 = Num_Elements * ((4 * P ** 3) + (18 * P ** 2) + (26 * P) + 12)
86 O_E_4 = 6 * Num_Elements * (P + 1) ** 2
87
88 t_e = O_E_1 + O_E_2 + O_E_3 + O_E_4
89
90 T_E = (N_P + N_V_1 + N_V_2 + N_V_3) * t_e
91
92 if (Scheme == 'IterativeStaticCond'):
93 O_E_1 = 8 * ((P - 1) ** 2) * 4 * P * Num_Elements * Num_Modes
94 O_E_2 = (N_P + N_V_1 + N_V_2 + N_V_3) * 2 * 16 * (P ** 2) * Num_Elements
95 O_E_3 = (8 * 4 * ((P - 1) ** 2)) + (4 * ((P - 1) ** 2)) * Num_Elements * Num_Modes
96 O_E_4 = 8 * ((((P - 1) ** 2)) ** 2) * Num_Elements * Num_Modes
97
98 T_E = O_E_1 + O_E_2 + O_E_3 + O_E_4
99
100 return(T_A, T_E)
101
102#------------------------------------
103# New Function
104#------------------------------------
105
106# Find the L_2 norm of the difference between the data and model, used for fitting.
107def compare_data(constants, Num_Constants, Data, T_A, T_E):
108
109 # Lemons make nice Lemonade (Needed a list to hold results)
110 lemons = []
111
112 # Loop over data calculating difference between data and model, this will be minimised by the constants.
113 for i in range(0, len(Data)):
114
115 if (Num_Constants == 1):
116 lemons.append(Data[i] - (T_A[i] + T_E[i])/constants)
117
118 if (Num_Constants == 2):
119 lemons.append(Data[i] - (T_A[i] / constants[0] + T_E[i] / constants[1]))
120
121 # Calculate the L_2 norm of the difference, this is the quantity to be minimised by the optimisation step
122 L_2_norm = np.linalg.norm(lemons, 2)
123
124 return(L_2_norm)
125
126#------------------------------------
127# New Function
128#------------------------------------
129
130# Function to run the fmin optimisation algorithm to fit the model to the data.
131def Fit_Model(Num_Constants, Data, T_A, T_E):
132
133 # Numpy arrays to hold the results and data.
134 Data = np.array(Data)
135 T_A = np.array(T_A)
136 T_E = np.array(T_E)
137
138 # Set the initial values depending on how many constants are required
139 if (Num_Constants == 1):
140 inital = 1e6
141
142 if (Num_Constants == 2):
143 inital = np.array([1e6, 1e06])
144
145 # Run the optimisation algorithm
146 Fit = fmin(compare_data, inital, args=(Num_Constants, Data, T_A, T_E), xtol=0.0001, ftol=1, maxiter=1e04, maxfun=1e09)
147
148 # Return the fitted constants.
149 return(Fit)
150
151#------------------------------------
152# New Function
153#------------------------------------
154
155# Function to run all the steps in order to fit the serial model to serially produced data.
156def Run_Serial_Fit(Compare_Serial, Consider_Modes, Num_Constants, P, Num_Elements, Nektar_Modes, Timings, Pressure, Velocity_1, Velocity_2, Velocity_3, Scheme):
157
158 # List to hold data from Nektar simulation
159 Data = []
160
161 # Loop over modes to be considered in calibrating the model
162 for i in range(0, len(Consider_Modes)):
163 Data.append(np.mean(Timings[str(Consider_Modes[i])])/10)
164
165 # Lists to hold the operation counts to be fed into the optimisation
166 T_A = []
167 T_E = []
168
169 # Loop over modes to be used in the model
170 for i in range(0, len(Consider_Modes)):
171
172 # Initialise CG counters to 0
173 N_P = 0
174 N_V_1 = 0
175 N_V_2 = 0
176 N_V_3 = 0
177
178 # Loop over all the modes up to the mode being considered adding the CG iterations from each mode to the counter
179 for j in range(1, Consider_Modes[i] + 1):
180
181 # Skip the 2nd mode as we do not get any data about it from Nektar
182 if (j == 2):
183 continue
184
185 # We don't necessarily have data for all the CG iterations hence we use try
186 # The except case has to be included to keep python happy hence we pay homage to Alan Turing
187 try:
188 N_P += Pressure[str(j)][0]
189 except:
190 Turing = 'King of Computers'
191
192 try:
193 N_V_1 += Velocity_1[str(j)][0]
194 except:
195 Turing = 'King of Computers'
196
197 try:
198 N_V_2 += Velocity_2[str(j)][0]
199 except:
200 Turing = 'King of Computers'
201
202 try:
203 N_V_3 += Velocity_3[str(j)][0]
204 except:
205 Turing = 'King of Computers'
206
207 # Perform the operation counts for each mode and store these to be fed into the optimisation step
208 (t_a, t_e) = Operation_Count(P, Num_Elements, Consider_Modes[i], N_P, N_V_1, N_V_2, N_V_3, Scheme)
209 T_A.append(t_a)
210 T_E.append(t_e)
211
212 # Now fit the model to the data
213 Fit = Fit_Model(Num_Constants, Data, T_A, T_E)
214
215 # Print the fitted FLOPs
216 print(Fit)
217
218 # Output the comparison the results if the user desires
219 if Compare_Serial is True:
220
221 # Pharse the Nektar data as before
222 Data = []
223
224 for i in range(1, len(Nektar_Modes)):
225 Data.append(np.mean(Timings[str(Nektar_Modes[i])])/10)
226
227 Time = []
228
229 for i in range(1, len(Nektar_Modes)):
230 N_P = 0
231 N_V_1 = 0
232 N_V_2 = 0
233 N_V_3 = 0
234
235 for j in range(1, Nektar_Modes[i] + 1):
236
237 if (j == 2):
238 continue
239
240 try:
241 N_P += Pressure[str(j)][0]
242 except:
243 Turing = 'King of Computers'
244
245 try:
246 N_V_1 += Velocity_1[str(j)][0]
247 except:
248 Turing = 'King of Computers'
249
250 try:
251 N_V_2 += Velocity_2[str(j)][0]
252 except:
253 Turing = 'King of Computers'
254
255 try:
256 N_V_3 += Velocity_3[str(j)][0]
257 except:
258 Turing = 'King of Computers'
259
260 Time.append(Serial_Computation(P, Num_Elements, Nektar_Modes[i], N_P, N_V_1, N_V_2, N_V_3, Num_Constants, Fit, Scheme))
261
262 Nektar_Modes = list(Nektar_Modes)
263 Nektar_Modes.pop(0)
264
265 # Calculate the mean, standard deviation and variance of the difference between the data and the fitted model
266 difference = []
267
268 for i in range(0, len(Nektar_Modes)):
269 difference.append(abs(Data[i] - Time[i]))
270
271 mean_diff = np.mean(difference)
272 std_dev_diff = np.std(difference)
273 var_diff = np.var(difference)
274
275 # Print these results for the user to see
276 print('The mean of the differences between the Data and the Model is ' + str(mean_diff))
277 print('The standard deviation of the differences between the Data and the Model is ' + str(std_dev_diff))
278 print('The variance of the differences between the Data and the Model is ' + str(var_diff))
279
280 # Plot the two data sets together for a visual comparison
281 fig, ax = plt.subplots()
282 ax.plot(Nektar_Modes, Data, label = 'Data')
283 ax.errorbar(Nektar_Modes, Time, label = 'Model')
284 ax.set_xlabel('$ N_Z $')
285 ax.set_ylabel('Timestep (s)')
286 ax.set_title('Length of Single Timestep: Model vs Data')
287 plt.legend(loc=4)
288 fig.savefig("Output/Figures/Model_vs_Data.png")
289
290 return(Fit)
291
292#------------------------------------
293# End of Functions
294#------------------------------------
def Fit_Model(Num_Constants, Data, T_A, T_E)
Definition: serial.py:131
def Run_Serial_Fit(Compare_Serial, Consider_Modes, Num_Constants, P, Num_Elements, Nektar_Modes, Timings, Pressure, Velocity_1, Velocity_2, Velocity_3, Scheme)
Definition: serial.py:156
def Serial_Computation(P, Num_Elements, Num_Modes, N_P, N_V_1, N_V_2, N_V_3, Num_Constants, constants, Scheme)
Definition: serial.py:26
def Operation_Count(P, Num_Elements, Num_Modes, N_P, N_V_1, N_V_2, N_V_3, Scheme)
Definition: serial.py:72
def compare_data(constants, Num_Constants, Data, T_A, T_E)
Definition: serial.py:107
scalarT< T > abs(scalarT< T > in)
Definition: scalar.hpp:298