diff --git a/src/gui_qt/data/container.py b/src/gui_qt/data/container.py index 93d2f12..d696e61 100644 --- a/src/gui_qt/data/container.py +++ b/src/gui_qt/data/container.py @@ -438,7 +438,7 @@ class ExperimentContainer(QtCore.QObject): if (i is None) and (j is None): prefix = '' else: - prefix = 'g[%i].s[%i].' % (i, j) + prefix = f'g[{i}].s[{j}].' namespace = {prefix + 'x': (self.x, 'x values'), prefix + 'y': [self.y, 'y values'], @@ -459,16 +459,33 @@ class ExperimentContainer(QtCore.QObject): return namespace - def eval_expression(self, cmds, namespace): - namespace.update({'x': self._data.x, 'y': self._data.y, 'y_err': self._data.y_err, 'value': self.value}) + def eval_expression(self, cmds, namespace, i=None, j=None): + if i is not None: + namespace['i'] = i - if len(self._fits) == 1: - namespace.update({"fit['%s']" % (convert(pname, old='tex', new='str')): pvalue.value - for (pname, pvalue) in self._manager[self._fits[0]].parameter.items()}) + if j is not None: + namespace['j'] = j + + namespace.update({'x': self._data.x, 'y': self._data.y, 'y_err': self._data.y_err, 'value': self.value}) + namespace['fit'] = {} + + if isinstance(self, FitContainer): + namespace['fit'].update({ + '%s' % convert(pname, old='latex', new='plain'): pvalue.value + for (pname, pvalue) in self._data.parameter.items() + }) + + elif len(self._fits) == 1: + namespace['fit'].update({ + '%s' % convert(pname, old='tex', new='str'): pvalue.value + for (pname, pvalue) in self._manager[self._fits[0]].parameter.items() + }) else: for k, f in enumerate(self._fits): - namespace.update({"fit['%s_%i']" % (convert(pname, old='tex', new='str'), k): pvalue.value - for (pname, pvalue) in self._manager[f].parameter.items()}) + namespace['fit'].update({ + "%s_%i" % (convert(pname, old='tex', new='str'), k): pvalue.value + for (pname, pvalue) in self._manager[f].parameter.items() + }) new_data = self.copy() for c in cmds: diff --git a/src/gui_qt/lib/namespace.py b/src/gui_qt/lib/namespace.py index a5eb34f..e6ce2eb 100644 --- a/src/gui_qt/lib/namespace.py +++ b/src/gui_qt/lib/namespace.py @@ -21,36 +21,53 @@ class Namespace: self.top_levels = {} if basic: - self.add_namespace({'x': (None, 'x values'), 'y': (None, 'x values'), 'y_err': (None, 'y error values'), - 'fit': (None, 'dictionary of fit parameter', 'fit["PIKA"]'), 'np': (np, 'numpy module')}, - parents=('Basic', 'General')) + self.add_namespace( + {'x': (None, 'x values'), + 'y': (None, 'x values'), + 'y_err': (None, 'y error values'), + 'fit': (None, 'dictionary of fit parameter', 'fit["PIKA"]'), + 'np': (np, 'numpy module'), + }, + parents=('Basic', 'General'), + ) - self.add_namespace({'sin': (np.sin, 'Sine', 'sin(PIKA)'), 'cos': (np.cos, 'Cosine', 'cos(PIKA)'), - 'tan': (np.tan, 'Tangens', 'tan(PIKA)'), 'ln': (np.log, 'Natural Logarithm', 'ln(PIKA)'), - 'log': (np.log10, 'Logarithm (base 10)', 'log(PIKA)'), - 'exp': (np.exp, 'Exponential', 'exp(PIKA)'), 'sqrt': (np.sqrt, 'Root', 'sqrt(PIKA)'), - 'lin_range': (np.linspace, 'N evenly spaced over interval [start, stop]', - 'lin_range(start, stop, N)'), - 'log_range': (np.geomspace, 'N evenly spaced (log-scale) over interval [start, stop]', - 'lin_range(start, stop, N)')}, - parents=('Basic', 'Functions')) - - self.add_namespace({'max': (np.max, 'Maximum value', 'max(PIKA)'), - 'min': (np.min, 'Minimum value', 'min(PIKA)'), - 'argmax': (np.argmax, 'Index of maximum value', 'argmax(PIKA)'), - 'argmin': (np.argmax, 'Index of minimum value', 'argmin(PIKA)')}, - parents=('Basic', 'Values')) + self.add_namespace( + {'sin': (np.sin, 'Sine', 'sin(PIKA)'), + 'cos': (np.cos, 'Cosine', 'cos(PIKA)'), + 'tan': (np.tan, 'Tangens', 'tan(PIKA)'), + 'ln': (np.log, 'Natural Logarithm', 'ln(PIKA)'), + 'log': (np.log10, 'Logarithm (base 10)', 'log(PIKA)'), + 'exp': (np.exp, 'Exponential', 'exp(PIKA)'), + 'sqrt': (np.sqrt, 'Root', 'sqrt(PIKA)'), + 'lin_range': (np.linspace, 'N evenly spaced over interval [start, stop]', 'lin_range(start, stop, N)'), + 'log_range': (np.geomspace, 'N evenly spaced (log-scale) over interval [start, stop]', 'lin_range(start, stop, N)'), + }, + parents=('Basic', 'Functions')) + + self.add_namespace( + {'max': (np.max, 'Maximum value', 'max(PIKA)'), + 'min': (np.min, 'Minimum value', 'min(PIKA)'), + 'argmax': (np.argmax, 'Index of maximum value', 'argmax(PIKA)'), + 'argmin': (np.argmax, 'Index of minimum value', 'argmin(PIKA)'), + }, + parents=('Basic', 'Values')), if const: - self.add_namespace({'e': (constants.e, 'e / As'), 'eps0': (constants.epsilon0, 'epsilon0 / As/Vm'), - 'Eu': (constants.Eu,), 'h': (constants.h, 'h / eVs'), - 'hbar': (constants.hbar, 'hbar / eVs'), 'kB': (constants.kB, 'kB / eV/K'), - 'mu0': (constants.mu0, 'mu0 / Vs/Am'), 'NA': (constants.NA, 'NA / 1/mol'), - 'pi': (constants.pi,), 'R': (constants.R, 'R / eV')}, - parents=('Constants', 'Maybe useful')) + self.add_namespace( + {'e': (constants.e, 'e / As'), + 'eps0': (constants.epsilon0, 'epsilon0 / As/Vm'), + 'Eu': (constants.Eu,), 'h': (constants.h, 'h / eVs'), + 'hbar': (constants.hbar, 'hbar / eVs'), 'kB': (constants.kB, 'kB / eV/K'), + 'mu0': (constants.mu0, 'mu0 / Vs/Am'), 'NA': (constants.NA, 'NA / 1/mol'), + 'pi': (constants.pi,), 'R': (constants.R, 'R / eV'), + }, + parents=('Constants', 'Maybe useful'), + ) - self.add_namespace({f'gamma["{k}"]': (v, k, f'gamma["{k}"]') for k, v in constants.gamma.items()}, - parents=('Constants', 'Magnetogyric ratios (in 1/(sT))')) + self.add_namespace( + {f'gamma["{k}"]': (v, k, f'gamma["{k}"]') for k, v in constants.gamma.items()}, + parents=('Constants', 'Magnetogyric ratios (in 1/(sT))') + ) if fitfuncs: self.make_dict_from_fitmodule(models) @@ -104,9 +121,18 @@ class Namespace: graph = namedtuple('graphs', ['s']) sets = namedtuple('sets', ['x', 'y', 'y_err', 'value', 'fit'], defaults=(None,)) + gamma = {} + gs = re.compile(r'g\[(\d+)].s\[(\d+)].(x|y(?:_err)*|value|fit)') + gamma_re = re.compile(r'gamma\["(\w+)"\]') for k, v in self.namespace.items(): + m = gamma_re.match(k) + if m: + gamma[m.group(1)] = v[0] + + continue + m = gs.match(k) if m: if 'g' not in ret_dic: @@ -118,7 +144,7 @@ class Namespace: gg = ret_dic['g'][int(g_num)] if int(s_num) not in gg.s: - gg.s[int(s_num)] = sets('', '', '', '') + gg.s[int(s_num)] = sets('', '', '', '', {}) ss = gg.s[int(s_num)] if pos == 'fit': @@ -130,8 +156,11 @@ class Namespace: else: ret_dic['g'][int(g_num)].s[int(s_num)] = ss._replace(**{pos: v[0]}) - else: - ret_dic[k] = v[0] + continue + + ret_dic[k] = v[0] + + ret_dic['gamma'] = gamma return ret_dic diff --git a/src/gui_qt/main/management.py b/src/gui_qt/main/management.py index de40a3a..cfc0f26 100644 --- a/src/gui_qt/main/management.py +++ b/src/gui_qt/main/management.py @@ -939,21 +939,26 @@ class UpperManagement(QtCore.QObject): self.undostack.beginMacro('Evaluate expression') failures = [] - for sid in set_ids: - data_i = self.data[sid] - try: - # use a copy of original namespace - new_data = data_i.eval_expression(cmds, dict(ns)) - if overwrite: - cmd = EvalCommand(self.data, sid, new_data, 'Evaluate expression') - self.undostack.push(cmd) - else: - new_id = self.copy_sets(sets=[sid]) - self.data[new_id[0]].data = new_data - except Exception as e: - failures.append((data_i, e)) - logger.warning(str(data_i) + ' failed with Exception: ' + ''.join(e.args)) - continue + for i, g in enumerate(self.graphs.values()): + for j, sid in enumerate(g.sets): + + if sid not in set_ids: + continue + + data_i = self.data[sid] + try: + # use a copy of original namespace + new_data = data_i.eval_expression(cmds, dict(ns), i=i, j=j) + if overwrite: + cmd = EvalCommand(self.data, sid, new_data, 'Evaluate expression') + self.undostack.push(cmd) + else: + new_id = self.copy_sets(sets=[sid]) + self.data[new_id[0]].data = new_data + except Exception as e: + failures.append((data_i, e)) + logger.warning(str(data_i) + ' failed with Exception: ' + ''.join(e.args)) + continue if overwrite: self.undostack.endMacro()