Nektar++
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
class_topology.py
Go to the documentation of this file.
1 # Andrew Gloster
2 # Summer 2016
3 # Python Version of Communication Model Implementation
4 # Topology Class
5 
6 #------------------------------------
7 # Import relevant modules
8 #------------------------------------
9 
10 import os
11 
12 #------------------------------------
13 # Import relevant functions
14 #------------------------------------
15 
16 from serial import Serial_Computation
17 
18 #------------------------------------
19 # Class Definitions
20 #------------------------------------
21 
22 class Topology:
23 
24 #------------------------------------
25 # New Function
26 #------------------------------------
27 
28  # Initialisation of the Class
29  def __init__(self, PROC_Z, PROC_XY, Num_Core_Per_Socket, Num_Sock_Per_Node, Scheme):
30 
31  # Store read in data
32  self.PROC_Z = PROC_Z
33  self.PROC_XY = PROC_XY
34  self.Num_Core_Per_Socket = Num_Core_Per_Socket
35  self.Num_Sock_Per_Node = Num_Sock_Per_Node
36  self.Scheme = Scheme
37 
38  # Calculate PROC_TOT
39  self.PROC_TOT = self.PROC_XY * self.PROC_Z
40 
41  # Initialise storage lists
42  self.Core = []
43  self.Socket = []
44  self.Node = []
45 
46  # Begin counters used in for loops
47  count_core = 0
48  count_socket_old = 0
49  count_socket_new = 0
50  count_node = 0
51 
52  # Iterate over cores assigning core, socket and node number
53  for i in range(0, self.PROC_Z):
54  for j in range(0, self.PROC_XY):
55  self.Core.append(count_core)
56  self.Socket.append(count_socket_new)
57  self.Node.append(count_node)
58 
59  count_core += 1
60 
61  # Use modulo to find when to iterate socker number based on bumber of cores assigned
62  if(count_core % Num_Core_Per_Socket == 0):
63  count_socket_new = count_socket_new + 1
64 
65  # Use modulo to find when to iterate node number based on bumber of sockets assigned
66  if(count_socket_old != count_socket_new):
67  if(count_socket_new % Num_Sock_Per_Node == 0):
68  count_node = count_node + 1;
69 
70  # Update socket counter
71  count_socket_old = count_socket_new;
72 
73  # Flags to track operations that have occured, default to False
74  self.Elements_Disributed = False
75  self.Modes_Disributed = False
76  self.Data_Size_Input = False
77  self.Data_Communication_Input = False
78 
79 
80 #------------------------------------
81 # New Function
82 #------------------------------------
83 
84  # Print out hardware layout to user when desired, also used for error checking
85  def Print_Hardware(self):
86 
87  # Check that there is a file to put outputs into, if not make one
88  output_path = 'Output'
89  if not os.path.exists(output_path):
90  os.mkdir(output_path)
91 
92  # Open new file and write out configurations
93  f = open('Output/Hardware_Topology.txt', "w")
94 
95  f.write("Core Distribution")
96  f.write("\n")
97 
98  for i in range(0, self.PROC_Z):
99  for j in range(0, self.PROC_XY):
100  f.write(repr(self.Core[i*self.PROC_XY + j]).rjust(5))
101  f.write("\n")
102 
103 
104  f.write("\n")
105 
106 
107  f.write("Socket Distribution")
108  f.write("\n")
109  for i in range(0, self.PROC_Z):
110  for j in range(0, self.PROC_XY):
111  f.write(repr(self.Socket[i*self.PROC_XY + j]).rjust(5))
112  f.write("\n")
113 
114 
115  f.write("\n")
116 
117 
118  f.write("Node Distribution")
119  f.write("\n")
120  for i in range(0, self.PROC_Z):
121  for j in range(0, self.PROC_XY):
122  f.write(repr(self.Node[i*self.PROC_XY + j]).rjust(5))
123  f.write("\n")
124 
125  # Close the file
126  f.close()
127 
128 #------------------------------------
129 # New Function
130 #------------------------------------
131 
132  # Distribute the elements as specified over the computational domain
133  def Distribute_Elements(self, Num_Element_Msg, Num_Elements):
134  self.Num_Elements = []
135  for i in range(0, self.PROC_Z):
136  self.Num_Elements.append([])
137  for j in range(0, self.PROC_XY):
138  self.Num_Elements[i].append(Num_Elements[j])
139 
140  self.Num_Element_Msg = Num_Element_Msg
141 
142  self.Elements_Disributed = True
143 
144 #------------------------------------
145 # New Function
146 #------------------------------------
147 
148  # Distribute the modes as specified over the computational domain
149  def Distribute_Modes(self, Num_Modes):
150  self.Num_Modes = Num_Modes
151  self.N_Z = Num_Modes * 2
152  self.Split = self.Num_Modes / self.PROC_Z
153  self.Modes = []
154  self.Planes = []
155  self.Plane_Num = []
156  for i in range(0, self.PROC_TOT):
157  self.Modes.append(self.Split)
158  self.Planes.append(self.Split * 2)
159 
160  for i in range(0, self.PROC_Z):
161  self.Plane_Num.append([])
162  for j in range(0, self.Split * 2):
163  self.Plane_Num[i].append(i * (self.Split * 2) + j)
164 
165  self.Modes_Disributed = True
166 
167 #------------------------------------
168 # New Function
169 #------------------------------------
170 
171  # Read in the fitted constants from the serial model
172  def Hardware_Constant(self, Num_Constants, constants):
173  self.Num_Constants = Num_Constants
174  self.constants = constants
175 
176 
177 #------------------------------------
178 # New Function
179 #------------------------------------
180 
181  # Print the element distribution to file, also used for error checking
182  def Print_Elements(self, Index):
183 
184  if (self.Elements_Disributed is True):
185 
186  # Check that there is a file to put outputs into, if not make one
187  output_path = 'Output'
188  if not os.path.exists(output_path):
189  os.mkdir(output_path)
190 
191  # Open new file and write out configurations
192  f = open('Output/Element_Distribution_' + str(Index) + '.txt', "w")
193 
194  f.write("Core Distribution")
195  f.write("\n")
196 
197  for i in range(0, self.PROC_Z):
198  for j in range(0, self.PROC_XY):
199  f.write(repr(self.Core[i * self.PROC_XY + j]).rjust(5))
200  f.write("\n")
201 
202  f.write("\n")
203 
204  f.write("Element Distribution")
205  f.write("\n")
206 
207  for i in range(0, self.PROC_Z):
208  for j in range(0, self.PROC_XY):
209  f.write(repr(self.Num_Elements[i][j]).rjust(5))
210  f.write("\n")
211 
212  else:
213  # Check that there is a file to put outputs into, if not make one
214  output_path = 'Output'
215  if not os.path.exists(output_path):
216  os.mkdir(output_path)
217 
218  # Open new file and write out configurations
219  f = open('Output/Element_Distribution_' + str(Index) + '.txt', "w")
220 
221  f.write('Elements have no yet been distributed by the program.')
222 
223  f.close()
224 
225 
226 #------------------------------------
227 # New Function
228 #------------------------------------
229 
230  # Print the mode distribution to file, also used for error checking
231  def Print_Modes(self, Index):
232 
233  if (self.Modes_Disributed is True):
234 
235  # Check that there is a file to put outputs into, if not make one
236  output_path = 'Output'
237  if not os.path.exists(output_path):
238  os.mkdir(output_path)
239 
240  # Open new file and write out configurations
241  f = open('Output/Mode_Distribution.txt', "w")
242 
243  f.write("Core Distribution")
244  f.write("\n")
245 
246  for i in range(0, self.PROC_Z):
247  for j in range(0, self.PROC_XY):
248  f.write(repr(self.Core[i*self.PROC_XY + j]).rjust(5))
249  f.write("\n")
250 
251  f.write("\n")
252 
253  f.write("Mode Distribution")
254  f.write("\n")
255  for i in range(0, self.PROC_Z):
256  for j in range(0, self.PROC_XY):
257  f.write(repr(self.Modes[i*self.PROC_XY + j]).rjust(5))
258  f.write("\n")
259 
260  else:
261  # Check that there is a file to put outputs into, if not make one
262  output_path = 'Output'
263  if not os.path.exists(output_path):
264  os.mkdir(output_path)
265 
266  # Open new file and write out configurations
267  f = open('Output/Mode_Distribution_' + str(Index) + '.txt', "w")
268 
269  f.write('Modes have no yet been distributed by the program.')
270 
271  f.close()
272 #------------------------------------
273 # New Function
274 #------------------------------------
275 
276  # Read in the CG iterations for the planes
277  def CG_Iterations(self, Pressure, Velocity_1, Velocity_2, Velocity_3):
278  self.Pressure = Pressure
279  self.Velocity_1 = Velocity_1
280  self.Velocity_2 = Velocity_2
281  self.Velocity_3 = Velocity_3
282 
283 #------------------------------------
284 # New Function
285 #------------------------------------
286 
287  # Calulate data size in bytes for each element and mode, elements and modes assumed to have homogeneous P
288  # 8 bytes per double
289  def Data_Size(self, P):
290  self.P = P
291  self.Data_Element = (P + 1) * 8
292  self.Data_Mode = 8 * ((P+1) ** 2)
293 
294  # Update tracker flag
295  self.Data_Size_Input = True
296 
297 #------------------------------------
298 # New Function
299 #------------------------------------
300 
301  # Communication inputs as parsed from MPI Benchmarking
302  def Input_Communication(self, BW_Node_To_Node, LAT_Node_To_Node, BW_Socket_To_Socket, LAT_Socket_To_Socket, BW_Core_To_Core, LAT_Core_To_Core):
303  self.BW_Node_To_Node = BW_Node_To_Node
304  self.BW_Socket_To_Socket = BW_Socket_To_Socket
305  self.BW_Core_To_Core = BW_Core_To_Core
306  self.LAT_Node_To_Node = LAT_Node_To_Node
307  self.LAT_Socket_To_Socket = LAT_Socket_To_Socket
308  self.LAT_Core_To_Core = LAT_Core_To_Core
309 
310  # Update tracker flag
311  self.Data_Communication_Input = True
312 
313 #------------------------------------
314 # New Function
315 #------------------------------------
316 
317  # Check the relation between any two cores input to the function, whether they are on the same node/socket etc.
318  def Check_Neighbour(self, core_1, core_2):
319  if(core_1 == core_2):
320  return 0
321 
322  if(self.Socket[core_1] == self.Socket[core_2]):
323  return 1
324 
325  if(self.Node[core_1] == self.Node[core_2]):
326  return 2;
327 
328  else:
329  return 3;
330 
331 #------------------------------------
332 # New Function
333 #------------------------------------
334 
335  # Model of the exchange of element data pairwise, using Alltoall style
337  comm_max = 0.0
338 
339  for i in range(0, self.PROC_Z):
340 
341  current_row = i
342 
343  CG_Iter = 0
344 
345  for j in range(0, len(self.Plane_Num[current_row])):
346  try:
347  Plane = self.Plane_Num[current_row][j] + 1
348  CG_Iter += self.Pressure[str(Plane)][0]
349  CG_Iter += self.Velocity_1[str(Plane)][0]
350  CG_Iter += self.Velocity_2[str(Plane)][0]
351  CG_Iter += self.Velocity_3[str(Plane)][0]
352  except:
353  continue
354 
355  for j in range(0, self.PROC_XY):
356  comm_count = 0.0
357  current = i * self.PROC_XY + j
358 
359  for k in range(0, self.PROC_XY):
360  send_loc = i * self.PROC_XY + k
361 
362  relation = self.Check_Neighbour(current, send_loc)
363 
364  if (relation == 1):
365  comm_count += self.LAT_Core_To_Core + (self.Num_Element_Msg[j][k] * self.Data_Element)/(self.BW_Core_To_Core)
366 
367  if (relation == 2):
368  comm_count += self.LAT_Socket_To_Socket + (self.Num_Element_Msg[j][k] * self.Data_Element)/(self.BW_Socket_To_Socket)
369 
370  if (relation == 3):
371  comm_count += self.LAT_Node_To_Node + (self.Num_Element_Msg[j][k] * self.Data_Element)/(self.BW_Node_To_Node)
372 
373  comm_count = comm_count * CG_Iter
374 
375  if (comm_count >= comm_max):
376  comm_max = comm_count
377 
378  return(comm_max)
379 
380 #------------------------------------
381 # New Function
382 #------------------------------------
383 
384  # Model to describe MPI_AlltoALL for mode communcation
386 
387  # We count the number of
388  comm_max = 0.0
389 
390  # Set max relation size tracking to False
391  relation_1 = False
392  relation_2 = False
393  relation_3 = False
394 
395  for i in range(0, self.PROC_Z):
396  current = self.Core[i * self.PROC_XY]
397 
398  current_row = i
399 
400  CG_Iter = 0
401  for j in range(0, len(self.Plane_Num[current_row])):
402  try:
403  Plane = self.Plane_Num[current_row][j] + 1
404  CG_Iter += self.Pressure[str(Plane)][0]
405  CG_Iter += self.Velocity_1[str(Plane)][0]
406  CG_Iter += self.Velocity_2[str(Plane)][0]
407  CG_Iter += self.Velocity_3[str(Plane)][0]
408  except:
409  continue
410 
411  comm_count = 0.0
412 
413  for j in range(0, self.PROC_XY - 1):
414  current_right = current + 1
415 
416  relation = self.Check_Neighbour(current, current_right)
417 
418  if (relation == 1):
419  comm_count += self.LAT_Core_To_Core + (3 * 8)/(self.BW_Core_To_Core)
420  relation_1 = True
421 
422  if (relation == 2):
423  comm_count += self.LAT_Socket_To_Socket + (3 * 8)/(self.BW_Socket_To_Socket)
424  relation_2 = True
425 
426  if (relation == 3):
427  comm_count += self.LAT_Node_To_Node + (3 * 8)/(self.BW_Node_To_Node)
428  relation_3 = True
429 
430  current += 1
431 
432  comm_count = comm_count * CG_Iter
433  if (comm_count >= comm_max):
434  comm_max = comm_count
435  CG_MAX = CG_Iter
436 
437  if (relation_1 is True and relation_2 is False and relation_3 is False):
438  comm_max += (self.LAT_Core_To_Core + (3 * 8)/(self.BW_Core_To_Core)) * CG_MAX
439 
440  if (relation_1 is True and relation_2 is True and relation_3 is False):
441  comm_max += (self.LAT_Socket_To_Socket + (3 * 8)/(self.BW_Socket_To_Socket)) * CG_MAX
442 
443  if (relation_1 is True and relation_2 is True and relation_3 is True):
444  comm_max += (self.LAT_Node_To_Node + (3 * 8)/(self.BW_Node_To_Node)) * CG_MAX
445 
446  return(comm_max)
447 
448 #------------------------------------
449 # New Function
450 #------------------------------------
451 
452  # Model to describe MPI_AlltoALL for mode communcation
454 
455  # Find how long each core requires to send its information to each level, core, socker or node.
456  MSG_Node_Mode = []
457  MSG_Socket_Mode = []
458  MSG_Core_Mode = []
459 
460  # Divide by PROC_Z as Alltoall involves this data size
461  for i in range(0, self.PROC_Z):
462  for j in range(0, self.PROC_XY):
463  MSG_Node_Mode.append(self.LAT_Node_To_Node + (self.Planes[i] * self.Num_Elements[i][j] * self.Data_Mode)/((self.BW_Node_To_Node) * self.PROC_Z))
464  MSG_Socket_Mode.append(self.LAT_Socket_To_Socket + (self.Planes[i] * self.Num_Elements[i][j] * self.Data_Mode)/((self.BW_Socket_To_Socket) * self.PROC_Z))
465  MSG_Core_Mode.append(self.LAT_Core_To_Core + (self.Planes[i] * self.Num_Elements[i][j] * self.Data_Mode)/((self.BW_Core_To_Core) * self.PROC_Z))
466 
467  # We are looking for the core that has the most communication to do in this Alltoall set up.
468  # Each core performs PROC_Z - 1 sends (A send contains the recieve in this model)
469  comm_max = 0.0
470 
471  for i in range(0, self.PROC_Z):
472  for j in range(0, self.PROC_XY):
473 
474  # Counter for the communication for this the current core
475  comm_count = 0.0
476 
477  # Current column and core
478  current = self.Core[i*self.PROC_XY + j]
479  current_column = current % self.PROC_XY
480 
481  # Loop looking for neighbours and counting the communication
482  for k in range(0, self.PROC_TOT):
483  if (k % self.PROC_XY == current_column):
484  relation = self.Check_Neighbour(current, k)
485 
486  if (relation == 0):
487  continue
488 
489  if (relation == 1):
490  comm_count += MSG_Core_Mode[current]
491  continue
492 
493  if (relation == 2):
494  comm_count += MSG_Socket_Mode[current]
495  continue
496 
497  if (relation == 3):
498  comm_count += MSG_Node_Mode[current]
499  continue
500 
501  # Update the largest communication cost
502  if (comm_count >= comm_max):
503  comm_max = comm_count
504 
505  # Return the result
506  return(18 * comm_max)
507 
508 #------------------------------------
509 # New Function
510 #------------------------------------
511 
512  def Serial_Compute(self):
513 
514  serial_max = 0.0
515 
516  for i in range(0, self.PROC_Z):
517  for j in range(0, self.PROC_XY):
518 
519  serial_count = 0.0
520 
521  current = self.PROC_XY * i + j
522 
523  N_P = 0
524  N_V_1 = 0
525  N_V_2 = 0
526  N_V_3 = 0
527 
528  try:
529  length = len(self.Plane_Num[i])
530  except:
531  length = 1
532 
533  for k in range(0, length):
534 
535  Current_Plane = str(1 + self.Plane_Num[i][k])
536 
537  try:
538  N_P += self.Pressure[Current_Plane][0]
539  except:
540  Turing = 'King of Computers'
541 
542  try:
543  N_V_1 += self.Velocity_1[Current_Plane][0]
544  except:
545  Turing = 'King of Computers'
546 
547  try:
548  N_V_2 += self.Velocity_2[Current_Plane][0]
549  except:
550  Turing = 'King of Computers'
551 
552  try:
553  N_V_3 += self.Velocity_3[Current_Plane][0]
554  except:
555  Turing = 'King of Computers'
556 
557  serial_count = Serial_Computation(self.P, self.Num_Elements[i][j], self.Planes[current], N_P, N_V_1, N_V_2, N_V_3, self.Num_Constants, self.constants, self.Scheme)
558 
559  if (serial_count >= serial_max):
560  serial_max = serial_count
561 
562  return(serial_max)
563 
564 
565 #------------------------------------
566 # End of Class File
567 #------------------------------------
def Serial_Computation
Definition: serial.py:26
def Communication_Pairwise_Exchange