3from fileinput
import filename
12 def __init__(self,file_name,solver_name="",verbose=False):
16 self._number_of_time_steps: Int
17 Includes the empty time steps (warm-up phase) if you have
18 adaptive time stepping.
23 This is a mandatory field, as it tells us which file to parse.
26 In theory, you could live without this one if you have only one
27 solver. However, ExaHyPE2 supports multiple solvers within one
28 grid and therefore needs a dedicated solver name to know where
29 to look in the file. Leave it empty if there's only one solver.
59 The code tries to parse the machine format first. This format however is only
60 used by the command line logger. If you use the other loggers, they tend to
61 dump their data in human readable format; which is less accurate. I thought
62 about giving the user the option which format to use or to require her to use
63 the command line logger. But that's all too complicated: Why not try to parse
64 the most accurate format first. If that fails, we can still use the human
65 readable one. See parse_machine_time_format.
77 Hand in a string in nanoseconds and return double as s
81 return result / 1000 / 1000 / 1000
87 Hand in a string in the format hh:mm:ss
90 match = re.findall(
r"\d\d\:\d\d\:\d\d", data)
92 hours = match[0].split(
":")[0]
93 minutes = match[0].split(
":")[1]
94 seconds = match[0].split(
":")[2]
95 return float(hours)*60*60 + float(minutes)*60 + float(seconds)
99 return "(ranks=" + str(self.
_ranks) +
",threads=" + str(self.
_threads) + \
104 ",valid=" + str(self.
valid) + \
114 print(
"Warning: Have not found machine-readable log format. Use command line logger for this one. Will continue with human readable format.")
124 file = open(self.
_file_name,
"r", encoding=
"unicode_escape")
131 if "manually reset number of used cores to" in line:
132 new_thread_count =
int( line.split(
"manually reset number of used cores to" )[1] )
134 print(
"WARNING: Number of manually set cores ({}) does not match system settings of {}. Use manual value".format(new_thread_count,self.
_threads) )
142 if "no threading" in line:
145 elif "tbb" in line
or "omp" in line:
146 threads_parsed=line.split(
"threads" )[0].split(
"(")[-1]
147 if not threads_parsed.replace(
" ",
"").isdigit():
148 print(
'Warning: failed to find a number of threads in:', threads_parsed)
149 print(
'Assuming threads=1')
155 print(
"file has been written with {} cores/threads per node".format(self.
_threads) )
161 self.
_ranks =
int( line.split(
"ranks)" )[0].split(
"(")[-1] )
163 print(
"file has been written with {} ranks".format(self.
_ranks) )
165 predicate =
"rank:0" in line
and "Abstract" + self.
_solver_name in line
170 print(
"started new time step at " + str(time_stamp) +
"s" )
182 if predicate
and re.search(
r"#updates\s*=", line)!=
None and self.
_updates[-1]<0:
183 self.
_updates[-1] = float( line.split(
"=")[-1].split(
"(")[0] )
184 if predicate
and re.search(
r"dt_{min,this-step}\s*=", line)!=
None and self.
_time_step_size_min[-1]<0:
186 elif predicate
and re.search(
r"dt_{max,this-step}\s*=", line)!=
None and self.
_time_step_size_max[-1]<0:
189 if "not yet known" in line:
203 if "step()" in line
and "PlotSolution" in line
and "rank:0" in line:
206 print(
"triggered plot at " + str(time_stamp) +
"s" )
209 if "terminated successfully" in line
and "rank:0" in line:
212 print(
"terminated simulation at " + str(time_stamp) +
"s" )
215 if "initial grid construction:" in line:
217 match = re.findall(
r"measurements=\d+", line)
222 if "time stepping:" in line
and not "#measurements=0" in line:
224 match = re.findall(
r"measurements=\d+", line)
230 if "plotting:" in line
and not "#measurements=0" in line:
232 match = re.findall(
r"measurements=\d+", line)
237 except Exception
as ex:
238 print(
"parsing failed: " + str(ex))
239 print(traceback.format_exc())
243 print(
"** Warning: number of time steps could not be found **" )
245 print(
"Setting number of time steps = "
248 print(
"file " + self.
_file_name +
" is invalid as number of time steps equals zero" )
256 Returns a whole array of times per time step, so you can plot the evoluation
257 of the cost per step over time.
269 Returns a whole array of times per time step, so you can plot the evoluation
270 of the cost per step over time.
280 return result/len(data)
290 Returns a series of real time stamps (in seconds) snapshotted
294 This is not a mere copy, as the last entry in the local set is the end
295 of the simulation. So we remove this one. At the same time, the very
296 first entry is the start of the simulation or first time step where
297 nothing happens yet (if we have to analyse the eigenvalue first).
309 Returns a sequence of time stamps that are the simulated time
310 stamps (where has the simulation been at a certain point) per
313 This is not a mere copy, as the last entry in the local set is the end
320 result = result[0][1:], result[1][1:]
329 if result[0][0]<=0.0:
330 result = result[0][1:], result[1][1:]
339 raise Exception(
"incompatible time step sizes" )
341 result |= (lr[0]!=lr[1])
348 Should maybe eliminate the time steps that are not really steps
352 raise Exception(
"incompatible time step sizes" )
359 Time of last time step normalised (multiplied) with h^d
369 Remove the first count entries from the dataset. Usually, count is one and
370 anticipates that the solver requires one ``warm up'' sweep to determine h
371 and the eigenvalue, e.g.
388 Returns a tuple of arrays to be plotted
394 for point
in performance_data_points:
395 x_value = point._threads + (point._ranks-1) * point._cores_per_node
396 insert_at_position = 0
397 while insert_at_position<len(x_data)
and x_data[insert_at_position]<x_value:
398 insert_at_position += 1
399 x_data.insert( insert_at_position, x_value )
400 y_data.insert( insert_at_position, point._start_time_stepping )
402 return (x_data,y_data)
409 for point
in performance_data_points:
411 print(
"study " + str(point) +
" with " + str(point.total_time_stepping_steps) +
" time step(s)" )
412 if point.total_time_stepping_steps>0:
414 if max_cores_per_rank>0:
415 x_value = point._ranks + 0.5*point._threads/max_cores_per_rank
416 if max_cores_per_rank==0:
417 x_value = point._ranks
418 if max_cores_per_rank<0:
419 x_value = point._threads
421 print(
"experiment results from " + str(x_value) +
" cores/ranks" )
422 insert_at_position = 0
423 while insert_at_position<len(x_data)
and x_data[insert_at_position]<x_value:
424 insert_at_position += 1
425 x_data.insert( insert_at_position, x_value )
426 raw_data = point.total_time_stepping_time
427 y_data.insert( insert_at_position, raw_data )
429 return (x_data,y_data)
435 Returns a tuple of arrays to be plotted
437 This is a rather simple routine. Its big USP is that it accepts an unsorted list of
438 measurements and returns properly sorted measurements.
440 performance_data_points: [exahype2.postprocessing.PerformanceData]
443 max_cores_per_rank: Integer
444 Should be set to -1 if you analyse single node data. Should be set to 0 if you have only
445 one core count measurement per rank count. Should be set to something bigger than 0 if
446 you scale over both the mpi rank count and the core count.
452 for point
in performance_data_points:
454 print(
"study " + str(point) +
" with " + str(point.total_time_stepping_steps) +
" time step(s)" )
455 if point.total_time_stepping_steps>0:
457 if max_cores_per_rank>0:
458 x_value = point._ranks + 0.5*point._threads/max_cores_per_rank
459 if max_cores_per_rank==0:
460 x_value = point._ranks
461 if max_cores_per_rank<0:
462 x_value = point._threads
464 print(
"experiment results from " + str(x_value) +
" cores/ranks" )
465 insert_at_position = 0
466 while insert_at_position<len(x_data)
and x_data[insert_at_position]<x_value:
467 insert_at_position += 1
468 x_data.insert( insert_at_position, x_value )
469 raw_data = point.time_per_time_step()
470 y_data.insert( insert_at_position, raw_data )
472 return (x_data,y_data)
478 Load all files that start with file_prefix and merge them into one
483 filtered_files = glob.glob( file_prefix +
"*" + file_postfix )
484 for filtered_file
in filtered_files:
486 if new_dataset.valid
and result!=
None:
487 result.append( new_dataset )
489 print(
"ERROR: file " + filtered_file +
" was not a valid Peano 4 output file" )
get_time_step_simulated_time_stamps(self)
Returns a sequence of time stamps that are the simulated time stamps (where has the simulation been a...
get_time_per_time_step(self)
Returns a whole array of times per time step, so you can plot the evoluation of the cost per step ove...
__str__(self, *args, **kwargs)
__convert_human_readable_timestamp_to_seconds(self, data)
Hand in a string in the format hh:mm:ss.
uses_local_timestepping(self)
__init__(self, file_name, solver_name="", verbose=False)
:: Attributes
get_average_time_per_time_step(self)
Returns a whole array of times per time step, so you can plot the evoluation of the cost per step ove...
_parse_machine_time_format
time_per_time_step(self)
Time of last time step normalised (multiplied) with h^d.
remove_first_n_entries(self, count)
Remove the first count entries from the dataset.
__extract_time_stamp_from_run_call(self, line)
timesteps(self)
Should maybe eliminate the time steps that are not really steps.
_simulated_time_stamp_max
__convert_machine_readable_timestamp_to_seconds(self, data)
Hand in a string in nanoseconds and return double as s.
total_time_stepping_steps
get_time_step_real_time_stamps(self)
Returns a series of real time stamps (in seconds) snapshotted per time step.
_simulated_time_stamp_min
get_time_step_time_step_size(self)
extract_total_time_stepping_times(performance_data_points, max_cores_per_rank=0, verbose=False)
extract_times_per_step(performance_data_points, max_cores_per_rank=0, verbose=False)
Returns a tuple of arrays to be plotted.
extract_grid_construction_times(performance_data_points)
Returns a tuple of arrays to be plotted.
load_file_sequence(file_prefix, file_postfix="", solver_name="", verbose=False)
Load all files that start with file_prefix and merge them into one instance of Dataset.