diff --git a/src/damaris/data/DataPool.py b/src/damaris/data/DataPool.py index ac3a4d8..c454dd4 100644 --- a/src/damaris/data/DataPool.py +++ b/src/damaris/data/DataPool.py @@ -365,7 +365,13 @@ class DataPool(collections.abc.MutableMapping): obj = read_from_hdf(node) if obj is not None: self[full_path] = obj - + + elif damaris_type == "TemperatureResult": + from damaris.data.Temperature import read_from_hdf + obj = read_from_hdf(node) + if obj is not None: + self[full_path] = obj + # Skip further processing of this group since we handled it continue diff --git a/src/damaris/data/Temperature.py b/src/damaris/data/Temperature.py index 9842340..85e174a 100644 --- a/src/damaris/data/Temperature.py +++ b/src/damaris/data/Temperature.py @@ -4,6 +4,7 @@ from .Resultable import Resultable from .Drawable import Drawable import numpy import threading +import tables ############################################################################# # # @@ -141,6 +142,100 @@ class TemperatureResult(Resultable, Drawable): return ydata[mask].min() return 0 + def write_to_hdf(self, hdffile, where, name, title, complib=None, complevel=None): + """ + Write the temperature result to an HDF5 group. + + Stores x (timestamps), y (temperature readings), and setpoint arrays + as an HDF5 table, with metadata saved as group attributes. + """ + h5_table_format = { + "x": tables.Float64Col(), + "y": tables.Float64Col(), + "setpoint": tables.Float64Col(), + } + filter = None + if complib is not None: + if complevel is None: + complevel = 9 + filter = tables.Filters(complevel=complevel, complib=complib, shuffle=1) + + with self.__listener_lock: + xdata = numpy.array(self.x, dtype=float) if self.x else numpy.array([], dtype=float) + ydata = numpy.array(self.y, dtype=float) if self.y else numpy.array([], dtype=float) + spdata = numpy.array(self.setpoint, dtype=float) if self.setpoint else numpy.array([], dtype=float) + + # Use the length of the longest array; shorter ones are padded with 0 + n = max(len(xdata), len(ydata), 1) + if len(xdata) < n: + xdata = numpy.pad(xdata, (0, n - len(xdata)), constant_values=0.0) + if len(ydata) < n: + ydata = numpy.pad(ydata, (0, n - len(ydata)), constant_values=0.0) + if len(spdata) < n: + spdata = numpy.pad(spdata, (0, n - len(spdata)), constant_values=0.0) + + temp_table = hdffile.create_table( + where=where, name=name, + description=h5_table_format, + title=title, + filters=filter, + expectedrows=n, + ) + temp_table.flavor = "numpy" + temp_table.attrs.damaris_type = "TemperatureResult" + temp_table.attrs.xlabel = self.xlabel + temp_table.attrs.ylabel = self.ylabel + + if hasattr(self, "job_id") and self.job_id is not None: + temp_table.attrs.job_id = self.job_id + if hasattr(self, "job_date") and self.job_date is not None: + temp_table.attrs.time = "%04d%02d%02d %02d:%02d:%02d.%03d" % ( + self.job_date.year, + self.job_date.month, + self.job_date.day, + self.job_date.hour, + self.job_date.minute, + self.job_date.second, + self.job_date.microsecond // 1000, + ) + if hasattr(self, "description") and self.description is not None: + for (key, value) in self.description.items(): + temp_table.attrs.__setattr__("description_" + key, str(value)) + + row = temp_table.row + for i in range(n): + row["x"] = xdata[i] + row["y"] = ydata[i] + row["setpoint"] = spdata[i] + row.append() + + temp_table.flush() + def __repr__(self): """String representation of the temperature result.""" return f"TemperatureResult(points={len(self)}, job_id={self.job_id})" + + +def read_from_hdf(hdf_node): + """ + Read a TemperatureResult object from an HDF5 table node. + Returns None if the node is not suitable. + """ + if not isinstance(hdf_node, tables.Table): + return None + + if hdf_node._v_attrs.damaris_type != "TemperatureResult": + return None + + tr = TemperatureResult() + tr.xlabel = getattr(hdf_node._v_attrs, "xlabel", "Time (s)") + tr.ylabel = getattr(hdf_node._v_attrs, "ylabel", "Temperature (C)") + + for r in hdf_node.iterrows(): + tr.x.append(r["x"]) + tr.y.append(r["y"]) + sp = r["setpoint"] + if sp != 0.0 or len(tr.setpoint) > 0: + tr.setpoint.append(sp) + + return tr