Public Access
FitResult is a new Drawable in damaris.data with x-values independent of the measured points, so fits can be sampled on a dense grid. It is stored as its own data pool entry (e.g. "measurement/fit"), written to and read from the HDF5 data pool (damaris_type "FitResult", params as attributes), and drawn by the GUI as overlay on its measurement, including a fit parameter text box. Fits get no entry in the display tree, so measurement rows stay directly selectable. Result scripts create them via FitResult(x, y, title, source, params). Additionally, HDF5 files that experiment or result scripts left open (e.g. after a script failure) are closed when the experiment stops. pytables keeps such handles registered process-wide, which blocked reopening the dump file and the scripts' own files on the next run. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
128 lines
4.5 KiB
Python
128 lines
4.5 KiB
Python
# fit curve with x-values independent of the measurement data points
|
|
|
|
import numpy
|
|
import tables
|
|
from . import Drawable
|
|
|
|
|
|
class FitResult(Drawable.Drawable):
|
|
"""
|
|
plottable fit curve belonging to a MeasurementResult
|
|
|
|
In contrast to MeasurementResult, the x-values are a plain array
|
|
and are independent of the measured data points, so a fit can be
|
|
sampled on a much denser x-grid for a smooth plot. The fit is
|
|
stored in the data pool under its own key, e.g. as a child of the
|
|
measurement ("Magnetization(tau)/fit"), and is drawn by the GUI
|
|
as an overlay on the measurement it belongs to.
|
|
"""
|
|
|
|
def __init__(self, x, y, title=None, source=None, y_err=None, params=None):
|
|
"""
|
|
x: array-like of x-values (independent of the measurement)
|
|
y: array-like of y-values, same length as x
|
|
title: plot title of the fit
|
|
source: data pool key of the MeasurementResult this fit belongs to
|
|
y_err: optional array-like of y errors, same length as x
|
|
params: optional dictionary of fit parameters, shown in the plot
|
|
"""
|
|
Drawable.Drawable.__init__(self)
|
|
self.x = numpy.array(x, dtype="float64").reshape(-1)
|
|
self.y = [numpy.array(y, dtype="float64").reshape(-1)]
|
|
if y_err is not None:
|
|
self.y_err = numpy.array(y_err, dtype="float64").reshape(-1)
|
|
else:
|
|
self.y_err = None
|
|
self.title = title
|
|
self.source = source
|
|
self.params = dict(params) if params is not None else {}
|
|
|
|
def get_lineplotdata(self):
|
|
"""
|
|
x and y data for plotting the fit curve
|
|
"""
|
|
return [self.x, self.y[0]]
|
|
|
|
def uses_statistics(self):
|
|
"""
|
|
drawable interface method returns False
|
|
"""
|
|
return False
|
|
|
|
def write_to_hdf(self, hdffile, where, name, title, complib=None, complevel=None):
|
|
h5_table_format = {
|
|
"x": tables.Float64Col(),
|
|
"y": tables.Float64Col()
|
|
}
|
|
if self.y_err is not None:
|
|
h5_table_format["y_err"] = tables.Float64Col()
|
|
filter = None
|
|
if complib is not None:
|
|
if complevel is None:
|
|
complevel = 9
|
|
filter = tables.Filters(complevel=complevel, complib=complib, shuffle=1)
|
|
|
|
fit_table = hdffile.create_table(where=where, name=name,
|
|
description=h5_table_format,
|
|
title=title,
|
|
filters=filter,
|
|
expectedrows=self.x.shape[0])
|
|
fit_table.flavor = "numpy"
|
|
fit_table.attrs.damaris_type = "FitResult"
|
|
fit_table.attrs.quantity_name = self.get_title()
|
|
if self.xlabel is not None:
|
|
fit_table.attrs.xlabel = self.xlabel
|
|
if self.ylabel is not None:
|
|
fit_table.attrs.ylabel = self.ylabel
|
|
if self.source is not None:
|
|
fit_table.attrs.source = self.source
|
|
for (key, value) in self.params.items():
|
|
if isinstance(value, (int, float)) and not isinstance(value, bool):
|
|
fit_table.attrs.__setattr__("param_" + str(key), float(value))
|
|
else:
|
|
fit_table.attrs.__setattr__("param_" + str(key), str(value))
|
|
|
|
row = fit_table.row
|
|
has_y_err = self.y_err is not None
|
|
for i in range(self.x.shape[0]):
|
|
row["x"] = self.x[i]
|
|
row["y"] = self.y[0][i]
|
|
if has_y_err:
|
|
row["y_err"] = self.y_err[i]
|
|
row.append()
|
|
fit_table.flush()
|
|
|
|
|
|
def read_from_hdf(hdf_node):
|
|
"""
|
|
reads a FitResult object from the hdf_node
|
|
or None if the node is not suitable
|
|
"""
|
|
if not isinstance(hdf_node, tables.Table):
|
|
return None
|
|
|
|
if hdf_node._v_attrs.damaris_type != "FitResult":
|
|
return None
|
|
|
|
xdata = hdf_node.col("x")
|
|
ydata = hdf_node.col("y")
|
|
if "y_err" in hdf_node.colnames:
|
|
y_err = hdf_node.col("y_err")
|
|
else:
|
|
y_err = None
|
|
|
|
fr = FitResult(xdata, ydata,
|
|
title=getattr(hdf_node._v_attrs, "quantity_name", None),
|
|
source=getattr(hdf_node._v_attrs, "source", None),
|
|
y_err=y_err)
|
|
fr.xlabel = getattr(hdf_node._v_attrs, "xlabel", None)
|
|
fr.ylabel = getattr(hdf_node._v_attrs, "ylabel", None)
|
|
|
|
params = {}
|
|
for attr_name in hdf_node._v_attrs._f_list("all"):
|
|
if attr_name.startswith("param_"):
|
|
params[attr_name[6:]] = getattr(hdf_node._v_attrs, attr_name)
|
|
fr.params = params
|
|
|
|
return fr
|