diff --git a/libs/utils/analysis/frequency_analysis.py b/libs/utils/analysis/frequency_analysis.py index 1a2972c12b167b77eecbe5bf4056e535611b73ed..6220db22910e795f70c9906f38452c06cb42b75c 100644 --- a/libs/utils/analysis/frequency_analysis.py +++ b/libs/utils/analysis/frequency_analysis.py @@ -383,7 +383,6 @@ class FrequencyAnalysis(AnalysisModule): return cluster_active - @memoized def _getClusterFrequencyResidency(self, cluster): """ Get a DataFrame with per cluster frequency residency, i.e. amount of @@ -427,6 +426,7 @@ class FrequencyAnalysis(AnalysisModule): 'cannot compute residency!') return None cluster_freqs = freq_df[freq_df.cpu == _cluster[0]].frequency + cluster_freqs = self._trace._cropToXTimeRange(cluster_freqs) # Compute TOTAL Time time_intervals = cluster_freqs.index[1:] - cluster_freqs.index[:-1] @@ -438,6 +438,7 @@ class FrequencyAnalysis(AnalysisModule): # Compute ACTIVE Time cluster_active = self._getClusterActiveSignal(_cluster) + cluster_active = self._trace._cropToXTimeRange(cluster_active) # In order to compute the active time spent at each frequency we # multiply 2 square waves: diff --git a/libs/utils/trace.py b/libs/utils/trace.py index 209f60a93778381d4adaed9796da70bda4590c0d..b9c41f80fd459c99dcededac70d66456b7ee5b55 100644 --- a/libs/utils/trace.py +++ b/libs/utils/trace.py @@ -128,14 +128,13 @@ class Trace(object): trace_format) self.__computeTimeSpan() - # Minimum and Maximum x_time to use for all plots - self.x_min = 0 - self.x_max = self.time_range - # Reset x axis time range to full scale - t_min = self.window[0] - t_max = self.window[1] - self.setXTimeRange(t_min, t_max) + self.x_min = self.window[0] + if self.window[0] is None: + self.x_min = 0 + self.x_max = self.window[1] + if self.window[1] is None: + self.x_max = self.time_range self.data_frame = TraceData() self._registerDataFrameGetters(self) @@ -169,16 +168,37 @@ class Trace(object): :param t_max: upper bound :type t_max: int or float """ - if t_min is None: + self.x_min = self.window[0] + if self.window[0] is None: self.x_min = 0 - else: - self.x_min = t_min - if t_max is None: + self.x_max = self.window[1] + if self.window[1] is None: self.x_max = self.time_range - else: - self.x_max = t_max + + if t_min is not None: + if t_min < self.x_min: + logging.warning('t_min out of range: ' + 'capping to trace minimum %.6f [s]', + self.x_min) + elif t_min > self.x_max: + raise ValueError('t_min out of range: ' + 'trace boundaries are (%.6f, %.6f) [s]', + self.x_min, self.x_max) + else: + self.x_min = t_min + if t_max is not None: + if t_max > self.x_max: + logging.warning('t_max out of range: ' + 'capping to trace maximum %.6f [s]', + self.x_max) + elif t_max < self.x_min: + raise ValueError('t_max out of range: ' + 'trace boundaries are (%.6f, %.6f) [s]', + self.window[0], self.x_max) + else: + self.x_max = t_max logging.info('Set plots time range to (%.6f, %.6f)[s]', - self.x_min, self.x_max) + self.x_min, self.x_max) def __registerTraceEvents(self, events): """ @@ -728,6 +748,43 @@ class Trace(object): return len(self._functions_stats_df) > 0 + def _cropToXTimeRange(self, data): + """ + Crop a dataframe containing FTrace events to the X time range specified + by the user through setXTimeRange(). + + When cropping a series it might be necessary to include a first and + last element with timestamps equal to the boundaries of the X time + range. + + :param data: series to be cropped + :type data: :mod:`pandas.Series` + """ + if not isinstance(data, pd.Series): + msg = 'Cropping supported only for pandas.Series objects!' + raise ValueError(msg) + + first = None + # Avoid duplicate indexes in case the user specifies an x_min that + # corresponds to the time stamp of an existing event. + if self.x_min not in data.index: + # data[data.index < self.x_min] may return an empty series if x_min + # is lower than the lowest index of the data series. This will + # raise an exception that can be caught and leave first to None, as + # in this case a first element is not needed + try: + first = pd.Series([data[data.index < self.x_min].iloc[-1]], + index=[self.x_min]) + except: pass + last = None + if self.x_max not in data.index: + try: + last = pd.Series([data[data.index <= self.x_max].iloc[-1]], + index=[self.x_max]) + except: pass + data = data.loc[self.x_min:self.x_max] + return pd.concat([first, data, last]) + class TraceData: """ A DataFrame collector exposed to Trace's clients """