From f433a9fa3a464d611e75b26fcb540dd835f693d7 Mon Sep 17 00:00:00 2001 From: Daniel Friesel Date: Fri, 25 Oct 2019 15:40:13 +0200 Subject: dfatool: More error handling, support a posteriori arguments --- lib/dfatool.py | 79 +++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 12 deletions(-) (limited to 'lib/dfatool.py') diff --git a/lib/dfatool.py b/lib/dfatool.py index dd9ed61..2f5fa29 100644 --- a/lib/dfatool.py +++ b/lib/dfatool.py @@ -330,9 +330,26 @@ class CrossValidator: def _preprocess_measurement(measurement): setup = measurement['setup'] mim = MIMOSA(float(setup['mimosa_voltage']), int(setup['mimosa_shunt'])) - charges, triggers = mim.load_data(measurement['content']) - trigidx = mim.trigger_edges(triggers) - triggers = [] + try: + charges, triggers = mim.load_data(measurement['content']) + trigidx = mim.trigger_edges(triggers) + except EOFError as e: + mim.is_error = True + mim.errors.append('MIMOSA logfile error: {}'.format(e)) + trigidx = list() + + if len(trigidx) == 0: + mim.is_error = True + mim.errors.append('MIMOSA log has no triggers') + return { + 'fileno' : measurement['fileno'], + 'info' : measurement['info'], + 'has_mimosa_error' : mim.is_error, + 'mimosa_errors' : mim.errors, + 'expected_trace' : measurement['expected_trace'], + 'repeat_id' : measurement['repeat_id'], + } + cal_edges = mim.calibration_edges(running_mean(mim.currents_nocal(charges[0:trigidx[0]]), 10)) calfunc, caldata = mim.calibration_function(charges, cal_edges) vcalfunc = np.vectorize(calfunc, otypes=[np.float64]) @@ -348,8 +365,9 @@ def _preprocess_measurement(measurement): 'mimosa_errors' : mim.errors, } - if 'expected_trace' in measurement: - processed_data['expected_trace'] = measurement['expected_trace'] + for key in ['expected_trace', 'repeat_id']: + if key in measurement: + processed_data[key] = measurement[key] return processed_data @@ -489,6 +507,7 @@ class RawData: self.version = 0 self.preprocessed = False self._parameter_names = None + self.ignore_clipping = False with tarfile.open(filenames[0]) as tf: for member in tf.getmembers(): @@ -621,7 +640,7 @@ class RawData: # Clipping in UNINITIALIZED (offline_idx == 0) can happen during # calibration and is handled by MIMOSA - if offline_idx != 0 and offline_trace_part['clip_rate'] != 0: + if offline_idx != 0 and offline_trace_part['clip_rate'] != 0 and not self.ignore_clipping: processed_data['error'] = 'Offline #{off_idx:d} (online {on_name:s} @ {on_idx:d}/{on_sub:d}) was clipping {clip:f}% of the time'.format( off_idx = offline_idx, on_idx = online_run_idx, on_sub = online_trace_part_idx, @@ -679,13 +698,20 @@ class RawData: online_trace_part['offline'].append(offline_trace_part) paramkeys = sorted(online_trace_part['parameter'].keys()) - paramvalue = [soft_cast_int(online_trace_part['parameter'][x]) for x in paramkeys] + + paramvalues = list() + + for paramkey in paramkeys: + if type(online_trace_part['parameter'][paramkey]) is list: + paramvalues.append(soft_cast_int(online_trace_part['parameter'][paramkey][measurement['repeat_id']])) + else: + paramvalues.append(soft_cast_int(online_trace_part['parameter'][paramkey])) # NB: Unscheduled transitions do not have an 'args' field set. # However, they should only be caused by interrupts, and # interrupts don't have args anyways. if arg_support_enabled and 'args' in online_trace_part: - paramvalue.extend(map(soft_cast_int, online_trace_part['args'])) + paramvalues.extend(map(soft_cast_int, online_trace_part['args'])) if not 'offline_aggregates' in online_trace_part: online_trace_part['offline_attributes'] = ['power', 'duration', 'energy'] @@ -715,7 +741,7 @@ class RawData: online_trace_part['offline_aggregates']['energy'].append( offline_trace_part['uW_mean'] * (offline_trace_part['us'] - 20)) online_trace_part['offline_aggregates']['paramkeys'].append(paramkeys) - online_trace_part['offline_aggregates']['param'].append(paramvalue) + online_trace_part['offline_aggregates']['param'].append(paramvalues) if online_trace_part['isa'] == 'transition': online_trace_part['offline_aggregates']['rel_energy_prev'].append( offline_trace_part['uW_mean_delta_prev'] * (offline_trace_part['us'] - 20)) @@ -838,13 +864,14 @@ class RawData: 'mimosa_shunt' : ptalog['configs'][j]['shunt'], 'state_duration' : ptalog['opt']['sleep'], }) - for mim_file in ptalog['files'][j]: + for repeat_id, mim_file in enumerate(ptalog['files'][j]): member = tf.getmember(mim_file) mim_files.append({ 'content' : tf.extractfile(member).read(), 'fileno' : j, 'info' : member, 'setup' : self.setup_by_fileno[j], + 'repeat_id' : repeat_id, 'expected_trace' : ptalog['traces'][j], }) self.filenames = new_filenames @@ -856,6 +883,13 @@ class RawData: valid_traces = list() for measurement in measurements: + if not 'energy_trace' in measurement: + vprint(self.verbose, '[W] Skipping {ar:s}/{m:s}: {e:s}'.format( + ar = self.filenames[measurement['fileno']], + m = measurement['info'].name, + e = '; '.join(measurement['mimosa_errors']))) + continue + if version == 0: # Strip the last state (it is not part of the scheduled measurement) measurement['energy_trace'].pop() @@ -1763,12 +1797,27 @@ class PTAModel: 'by_name' : detailed_results } - def assess_states(self, model_function, model_attribute = 'power'): + def assess_states(self, model_function, model_attribute = 'power', distribution: dict = None): """ Calculate overall model error assuming equal distribution of states """ + # TODO calculate mean power draw for distribution and use it to + # calculate relative error from MAE combination model_quality = self.assess(model_function) - total_error = np.sqrt(sum(map(lambda x: np.square(model_quality['by_name'][x][model_attribute]['mae']), self.states()))) + num_states = len(self.states()) + if distribution is None: + distribution = dict(map(lambda x: [x, 1/num_states], self.states())) + + if not np.isclose(sum(distribution.values()), 1): + raise ValueError('distribution must be a probability distribution with sum 1') + + total_value = None + try: + total_value = sum(map(lambda x: model_function(x, model_attribute) * distribution[x], self.states())) + except KeyError: + pass + + total_error = np.sqrt(sum(map(lambda x: np.square(model_quality['by_name'][x][model_attribute]['mae'] * distribution[x]), self.states()))) return total_error @@ -1961,6 +2010,12 @@ class MIMOSA: :returns: list of int (trigger indices, e.g. [2000000, ...] means the first trigger appears in charges/currents interval 2000000 -> 20s after start of measurements. Keep in mind that each interval is 10µs long, not 1µs, so index values are not µs timestamps) """ trigidx = [] + + if len(triggers) < 1000000: + self.is_error = True + self.errors.append('MIMOSA log is too short') + return trigidx + prevtrig = triggers[999999] # if the first trigger is high (i.e., trigger/buzzer pin is active before the benchmark starts), -- cgit v1.2.3