From 494fe6a8af50d7c09921915e989dbe4ed907a290 Mon Sep 17 00:00:00 2001 From: Prasanna Pakkiam Date: Fri, 10 Jan 2025 11:50:22 +1000 Subject: [PATCH] Added ability to perform automatic reverse sweeps in experiments. --- UnitTests/UTestExperimentConfiguration.yaml | 2 + UnitTests/testLaboratory.py | 68 +++++++++++- docs/User/Exp_Sweep.md | 108 ++++++++++++++++++++ sqdtoolz/Drivers/dummySMU.py | 100 ++++++++++++++++++ sqdtoolz/Experiment.py | 53 ++++++++-- sqdtoolz/Utilities/FileIO.py | 2 + 6 files changed, 322 insertions(+), 11 deletions(-) create mode 100644 sqdtoolz/Drivers/dummySMU.py diff --git a/UnitTests/UTestExperimentConfiguration.yaml b/UnitTests/UTestExperimentConfiguration.yaml index c278d03..21d71cf 100644 --- a/UnitTests/UTestExperimentConfiguration.yaml +++ b/UnitTests/UTestExperimentConfiguration.yaml @@ -13,3 +13,5 @@ instruments: type: sqdtoolz.Drivers.dummyGENmwSource.DummyGENmwSrc virMWS2: type: sqdtoolz.Drivers.dummyGENmwSource.DummyGENmwSrc + virSMU: + type: sqdtoolz.Drivers.dummySMU.DummySMU diff --git a/UnitTests/testLaboratory.py b/UnitTests/testLaboratory.py index ae18d72..294893e 100644 --- a/UnitTests/testLaboratory.py +++ b/UnitTests/testLaboratory.py @@ -624,6 +624,7 @@ def initialise(self): self.lab.load_instrument('virAWG') self.lab.load_instrument('virMWS') self.lab.load_instrument('virMWS2') + self.lab.load_instrument('virSMU') #Initialise test-modules hal_acq = ACQ("dum_acq", self.lab, 'virACQ') @@ -631,6 +632,7 @@ def initialise(self): awg_wfm = WaveformAWG("Wfm1", self.lab, [('virAWG', 'CH1'), ('virAWG', 'CH2')], 1e9) hal_mw = GENmwSource("MW-Src", self.lab, 'virMWS', 'CH1') hal_mw2 = GENmwSource("MW-Src2", self.lab, 'virMWS2', 'CH1') + hal_smu = GENsmu('SMU', self.lab, 'virSMU') #Reinitialise the waveform read_segs = [] @@ -792,6 +794,68 @@ def test_Exp(self): shutil.rmtree('test_save_dir') self.cleanup() + + def test_ExpReverseSweep(self): + self.initialise() + + #Check basic parameter storage with 1D reverse sweep... + exp = Experiment("test", self.lab.CONFIG('testConf')) + self.lab.VAR('myFreq').Value = 5 + res = self.lab.run_single(exp, [(self.lab.VAR("testAmpl"), np.arange(0,3,1))], delay=0.1, reverse_index=0, rec_params=[self.lab.VAR('myFreq'), self.lab.VAR('testAmpl')]) + assert hasattr(exp, 'last_rec_params'), "Running an experiment with rec_params did not create an attribute \'last_rec_params\'." + assert self.arr_equality(np.array(exp.last_rec_params.get_numpy_array().shape), np.array([3,4])), "Reverse sweeping experiment in 1D did not produce the right number of dependent parameters." + assert self.arr_equality(np.array(exp.last_rec_params.get_numpy_array()), np.array([[5,0]*2,[5,1]*2,[5,2]*2])), "Reverse sweeping experiment in 1D did not store the correct data." + assert self.arr_equality(np.array(exp.last_rec_params.param_vals[0]), np.arange(0,3,1)), "Reverse sweeping experiment in 1D did not produce the right parameter values." + # + #Check basic parameter storage with 1D reverse sweep... + exp = Experiment("test", self.lab.CONFIG('testConf')) + self.lab.VAR('myFreq').Value = 2 + self.lab.UpdateStateEnabled = False + self.lab.HAL('SMU').Voltage = 0 + res = self.lab.run_single(exp, [(self.lab.VAR("testAmpl"), np.arange(0,3,1))], delay=0.1, reverse_index=0, rec_params=[self.lab.VAR('myFreq'), self.lab.VAR('testAmpl'), (self.lab.HAL('SMU'), 'Voltage')]) + assert hasattr(exp, 'last_rec_params'), "Running an experiment with rec_params did not create an attribute \'last_rec_params\'." + arr = exp.last_rec_params.get_numpy_array() + assert self.arr_equality(np.array(arr.shape), np.array([3,6])), "Reverse sweeping experiment in 1D did not produce the right number of dependent parameters." + assert self.arr_equality(np.array(arr[:,0:2]), np.array([[2,0],[2,1],[2,2]])), "Reverse sweeping experiment in 1D did not store the correct data." + assert self.arr_equality(np.array(arr[:,3:5]), np.array([[2,0],[2,1],[2,2]])), "Reverse sweeping experiment in 1D did not store the correct data." + assert self.arr_equality(np.diff(np.concatenate([arr[:,2], arr[:,5][::-1]])), np.ones(5)), "Reverse sweeping experiment in 1D did not store the data in correct order." + assert self.arr_equality(np.array(exp.last_rec_params.param_vals[0]), np.arange(0,3,1)), "Reverse sweeping experiment in 1D did not produce the right parameter values." + # + #Check basic parameter storage with 2D reverse sweep... + exp = Experiment("test", self.lab.CONFIG('testConf')) + self.lab.VAR('myFreq').Value = 2 + self.lab.UpdateStateEnabled = False + self.lab.HAL('SMU').Voltage = 0 + res = self.lab.run_single(exp, [(self.lab.VAR("testAmpl"), np.arange(0,3,1)), (self.lab.VAR("myFreq"), np.arange(1,5,1))], delay=0.1, reverse_index=0, rec_params=[self.lab.VAR('myFreq'), self.lab.VAR('testAmpl'), (self.lab.HAL('SMU'), 'Voltage')]) + assert hasattr(exp, 'last_rec_params'), "Running an experiment with rec_params did not create an attribute \'last_rec_params\'." + arr = exp.last_rec_params.get_numpy_array() + assert self.arr_equality(np.array(arr.shape), np.array([3,4,6])), "Reverse sweeping experiment in 1D did not produce the right number of dependent parameters." + assert self.arr_equality(np.array(arr[:,:,0:2]), np.array([[[1,0],[2,0],[3,0],[4,0]], [[1,1],[2,1],[3,1],[4,1]], [[1,2],[2,2],[3,2],[4,2]]])), "Reverse sweeping experiment in 1D did not store the correct data." + assert self.arr_equality(np.array(arr[:,:,3:5]), np.array([[[1,0],[2,0],[3,0],[4,0]], [[1,1],[2,1],[3,1],[4,1]], [[1,2],[2,2],[3,2],[4,2]]])), "Reverse sweeping experiment in 1D did not store the correct data." + assert self.arr_equality(np.diff(np.concatenate([np.ndarray.flatten(arr[:,:,2]), np.ndarray.flatten(arr[:,:,5][::-1])])), np.ones(23)), "Reverse sweeping experiment in 1D did not store the data in correct order." + assert self.arr_equality(np.array(exp.last_rec_params.param_vals[0]), np.arange(0,3,1)), "Reverse sweeping experiment in 1D did not produce the right parameter values." + assert self.arr_equality(np.array(exp.last_rec_params.param_vals[1]), np.arange(1,5,1)), "Reverse sweeping experiment in 1D did not produce the right parameter values." + # + #Check basic parameter storage with 2D reverse sweep... + exp = Experiment("test", self.lab.CONFIG('testConf')) + self.lab.VAR('myFreq').Value = 2 + self.lab.UpdateStateEnabled = False + self.lab.HAL('SMU').Voltage = 0 + res = self.lab.run_single(exp, [(self.lab.VAR("testAmpl"), np.arange(0,3,1)), (self.lab.VAR("myFreq"), np.arange(1,5,1))], delay=0.1, reverse_index=1, rec_params=[self.lab.VAR('myFreq'), self.lab.VAR('testAmpl'), (self.lab.HAL('SMU'), 'Voltage')]) + assert hasattr(exp, 'last_rec_params'), "Running an experiment with rec_params did not create an attribute \'last_rec_params\'." + arr = exp.last_rec_params.get_numpy_array() + assert self.arr_equality(np.array(arr.shape), np.array([3,4,6])), "Reverse sweeping experiment in 1D did not produce the right number of dependent parameters." + assert self.arr_equality(np.array(arr[:,:,0:2]), np.array([[[1,0],[2,0],[3,0],[4,0]], [[1,1],[2,1],[3,1],[4,1]], [[1,2],[2,2],[3,2],[4,2]]])), "Reverse sweeping experiment in 1D did not store the correct data." + assert self.arr_equality(np.array(arr[:,:,3:5]), np.array([[[1,0],[2,0],[3,0],[4,0]], [[1,1],[2,1],[3,1],[4,1]], [[1,2],[2,2],[3,2],[4,2]]])), "Reverse sweeping experiment in 1D did not store the correct data." + assert self.arr_equality(np.diff(np.ndarray.flatten(np.concatenate([(arr[x,:,2], arr[x,:,5][::-1]) for x in range(3)]))), np.ones(23)), "Reverse sweeping experiment in 1D did not store the data in correct order." + assert self.arr_equality(np.array(exp.last_rec_params.param_vals[0]), np.arange(0,3,1)), "Reverse sweeping experiment in 1D did not produce the right parameter values." + assert self.arr_equality(np.array(exp.last_rec_params.param_vals[1]), np.arange(1,5,1)), "Reverse sweeping experiment in 1D did not produce the right parameter values." + + + shutil.rmtree('test_save_dir') + self.cleanup() + + def test_ExpSweepAndFullColdReload(self): self.initialise() @@ -1283,6 +1347,6 @@ def test_SnakeExp(self): self.cleanup() if __name__ == '__main__': - temp = TestColdReload() - temp.test_VARs() + temp = TestSweeps() + temp.test_ExpReverseSweep() unittest.main() \ No newline at end of file diff --git a/docs/User/Exp_Sweep.md b/docs/User/Exp_Sweep.md index 97f6af3..3dd9ade 100644 --- a/docs/User/Exp_Sweep.md +++ b/docs/User/Exp_Sweep.md @@ -9,6 +9,7 @@ This article covers: - [Basic Sweeps](#basic-sweeps) - [One-Many Sweeps](#one-many-sweeps) - [Changing the sampled order in sweeps](#changing-the-sampled-order-in-sweeps) +- [Reverse sweeps](#reverse-sweeps) ## Basic sweeps @@ -201,3 +202,110 @@ Note that **the order of entries in the HDF5 data file is the same, only the sam - The `sweep_orders` is given as a list of `ExSwp*` objects where it shuffles the order with the listed functions from left to right. That is, each shuffling transformatio is applied in ascending order. - See the [other article](Exp_SweepPerm.md) for a list of available `ExpSwp*` classes. + + +## Reverse sweeps + +When measuring hysteretic devices, the data will be different when sweeping a given parameter in the forward and backward directions. When setting up a parametric sweep, one may automatically take the reverse sweep. The subsequent data is added to a different channel with the suffix `'_reverse'`. Simply add the `reverse_index` parameter to highlight the index (of the sweeping parameter in the list) to reverse when running the experiment: + + +```python +#Assuming that exp is an Experiment object and lab is a Laboratory object +lab.run_single(exp, [(lab.VAR('volt'), np.arange(-1, 1, 0.1)), (lab.VAR('flux'), np.arange(-20,20,0.1))], reverse_index=1) +``` + + +```python +lab.VAR("volt").Value = -1 +lab.VAR("flux").Value = -20 +# --- Get Data --- # store to normal channel +lab.VAR("flux").Value = -19.9 +# --- Get Data --- # store to normal channel +... +lab.VAR("flux").Value = 19.8 +# --- Get Data --- # store to normal channel +lab.VAR("flux").Value = 19.9 +# --- Get Data --- # store to normal channel +lab.VAR("flux").Value = 19.9 +# --- Get Data --- # store to reverse channel +lab.VAR("flux").Value = 19.8 +# --- Get Data --- # store to reverse channel +... +lab.VAR("flux").Value = -20 +# --- Get Data --- # store to reverse channel + +lab.VAR("volt").Value = -0.9 +lab.VAR("flux").Value = -20 +# --- Get Data --- # store to normal channel +lab.VAR("flux").Value = -19.9 +# --- Get Data --- # store to normal channel +... +lab.VAR("flux").Value = 19.8 +# --- Get Data --- # store to normal channel +lab.VAR("flux").Value = 19.9 +# --- Get Data --- # store to normal channel +lab.VAR("flux").Value = 19.9 +# --- Get Data --- # store to reverse channel +lab.VAR("flux").Value = 19.8 +# --- Get Data --- # store to reverse channel +... +lab.VAR("flux").Value = -20 +# --- Get Data --- # store to reverse channel + +... +``` + +However, if `reverse_index=0`: + +```python +lab.VAR("volt").Value = -1 +lab.VAR("flux").Value = -20 +# --- Get Data --- # store to normal channel +lab.VAR("flux").Value = -19.9 +# --- Get Data --- # store to normal channel +... +lab.VAR("flux").Value = 19.8 +# --- Get Data --- # store to normal channel + +lab.VAR("volt").Value = -0.9 +lab.VAR("flux").Value = -20 +# --- Get Data --- # store to normal channel +lab.VAR("flux").Value = -19.9 +# --- Get Data --- # store to normal channel +... +lab.VAR("flux").Value = 19.8 +# --- Get Data --- # store to normal channel + +... + +lab.VAR("volt").Value = 0.9 +lab.VAR("flux").Value = -20 +# --- Get Data --- # store to normal channel +lab.VAR("flux").Value = -19.9 +# --- Get Data --- # store to normal channel +... +lab.VAR("flux").Value = 19.8 +# --- Get Data --- # store to normal channel + +lab.VAR("volt").Value = 0.9 +lab.VAR("flux").Value = -20 +# --- Get Data --- # store to reverse channel +lab.VAR("flux").Value = -19.9 +# --- Get Data --- # store to reverse channel +... +lab.VAR("flux").Value = 19.8 +# --- Get Data --- # store to reverse channel + +lab.VAR("volt").Value = 0.8 +lab.VAR("flux").Value = -20 +# --- Get Data --- # store to reverse channel +lab.VAR("flux").Value = -19.9 +# --- Get Data --- # store to reverse channel +... +lab.VAR("flux").Value = 19.8 +# --- Get Data --- # store to reverse channel + +... +``` + +Optionally, if the suffix for the reverse channels is to be changed, one may provide it in the `reverse_variable_suffix` argument (default value is `'_reverse'`) in `run_single`. diff --git a/sqdtoolz/Drivers/dummySMU.py b/sqdtoolz/Drivers/dummySMU.py new file mode 100644 index 0000000..ba4a67a --- /dev/null +++ b/sqdtoolz/Drivers/dummySMU.py @@ -0,0 +1,100 @@ +from qcodes import Instrument, InstrumentChannel, VisaInstrument, validators as vals + +class DummySMU(Instrument): + ''' + Dummy driver to emulate a SMU instrument. + ''' + def __init__(self, name, **kwargs): + super().__init__(name, **kwargs) + self.increment_voltage = kwargs.get('increment_voltage', True) + self.increment_voltage_step = kwargs.get('increment_voltage_step', 1) + self._voltage = kwargs.get('increment_voltage_start', 0) - self.increment_voltage_step + self._current = 0 + self._output = True + self._comp_current = 1 + self._comp_voltage = 1 + self._mode = 'SrcI_MeasV' + + @property + def Voltage(self): + self._voltage += self.increment_voltage_step + return self._voltage + @Voltage.setter + def Voltage(self, val): + self._voltage = val + + @property + def Current(self): + return self._current + @Current.setter + def Current(self, val): + self._current = val + + @property + def SenseVoltage(self): + return self._voltage + + @property + def SenseCurrent(self): + return self._current + + @property + def Output(self): + return self._output + @Output.setter + def Output(self, val): + self._output = val + + @property + def ComplianceCurrent(self): + return self._comp_current + @ComplianceCurrent.setter + def ComplianceCurrent(self, val): + self._comp_current = val + + @property + def ComplianceVoltage(self): + return self._comp_voltage + @ComplianceVoltage.setter + def ComplianceVoltage(self, val): + self._comp_voltage = val + + @property + def Mode(self): + return self._mode + @Mode.setter + def Mode(self, val): + self._mode = val + + @property + def SupportsSweeping(self): + return False + + @property + def RampRateVoltage(self): + return 1 + @RampRateVoltage.setter + def RampRateVoltage(self, val): + pass + + @property + def RampRateCurrent(self): + return 1 + @RampRateCurrent.setter + def RampRateCurrent(self, val): + pass + + @property + def ProbeType(self): + return 'TwoWire' + @ProbeType.setter + def ProbeType(self, val): + pass #Can't set this one... + + def get_idn(self): + return { + "vendor": "QCoDeS", + "model": str(self.__class__), + "seral": "NA", + "firmware": "NA", + } diff --git a/sqdtoolz/Experiment.py b/sqdtoolz/Experiment.py index d970c88..af28378 100644 --- a/sqdtoolz/Experiment.py +++ b/sqdtoolz/Experiment.py @@ -27,14 +27,14 @@ def Name(self): def ConfigName(self): return self._expt_config.Name - def _init_data_file(self, filename): + def _init_data_file(self, filename, fileio_options): if self._data_file_index >= 0: data_file_name = f'{filename}{self._data_file_index}' else: data_file_name = filename if data_file_name in self._cur_filewriters: return - data_file = FileIOWriter(self._file_path + data_file_name + '.h5', store_timestamps=self._store_timestamps) + data_file = FileIOWriter(self._file_path + data_file_name + '.h5', store_timestamps=self._store_timestamps, **fileio_options) self._cur_filewriters[data_file_name] = data_file return data_file, data_file_name + '.h5' @@ -90,12 +90,20 @@ def _run(self, file_path, sweep_vars=[], **kwargs): self._data_file_index = kwargs.get('data_file_index', -1) self._store_timestamps = kwargs.get('store_timestamps', True) - data_file, data_file_name = self._init_data_file('data') + init_data_file_options = {} + rev_ind = kwargs.get('reverse_index', -1) + rev_suffix = kwargs.get('reverse_variable_suffix', '_reverse') + assert not (len(sweep_vars) == 0 and rev_ind >= 0), "Cannot reverse indices when no sweeping variables are given." #Although the one below covers this, it's nicer to have a more specific error message... + assert rev_ind < len(sweep_vars), "Index for reversing variable does not fall within the list of given sweeping variables." + if rev_ind >= 0: + init_data_file_options['add_reverse_channels'] = True + init_data_file_options['reverse_channel_suffix'] = rev_suffix + data_file, data_file_name = self._init_data_file('data', init_data_file_options) rec_params = kwargs.get('rec_params') rec_params_extra = self._init_extra_rec_params() if len(rec_params) + len(rec_params_extra) > 0: - rec_data_file, rec_param_file_name = self._init_data_file('rec_params') + rec_data_file, rec_param_file_name = self._init_data_file('rec_params', init_data_file_options) self._init_aux_datafiles() @@ -130,6 +138,7 @@ def _run(self, file_path, sweep_vars=[], **kwargs): else: sweep_vars2 = [] sweepEx = {} + assert rev_ind == -1 or rev_ind >= 0, "The parameter reverse_index must be supplied as a non-negative indexing integer." for ind_var, cur_var in enumerate(sweep_vars): assert isinstance(cur_var, tuple) or isinstance(cur_var, list), "Sweeping variables must be given as a LIST of TUPLEs: [(VAR1, range1), (VAR2, range2), ...]" if len(cur_var) == 2: @@ -149,7 +158,9 @@ def _run(self, file_path, sweep_vars=[], **kwargs): if not kill_signal(): sweep_arrays = [x[1] for x in sweep_vars2] - self._sweep_shape = [x[1].size for x in sweep_vars2] + if rev_ind >= 0: + sweep_arrays[rev_ind] = np.concatenate([sweep_arrays[rev_ind], sweep_arrays[rev_ind][::-1]]) + self._sweep_shape = [x.size for x in sweep_arrays] self._sweep_grids = np.meshgrid(*sweep_arrays) self._sweep_grids = np.array(self._sweep_grids) axes = np.arange(len(self._sweep_grids.shape)) @@ -162,6 +173,7 @@ def _run(self, file_path, sweep_vars=[], **kwargs): #Setup permutations on the sweeping orders: sweep_orders = kwargs.get('sweep_orders', []) + assert not (len(sweep_orders) > 0 and rev_ind >= 0), "Cannot supply reverse_index and sweep_orders simultaneously." swp_order = np.arange(self._sweep_grids.shape[0]) for cur_order in sweep_orders: assert isinstance(cur_order, ExperimentSweepBase), "The argument sweep_orders must be specified as a list of ExpSwp* (i.e. ExperimentSweepBase) objects." @@ -192,11 +204,16 @@ def _run(self, file_path, sweep_vars=[], **kwargs): cur_raw_data = self._expt_config.get_data() self._data = cur_raw_data.pop('data') for x in cur_raw_data: - self._init_data_file(x) - self._cur_filewriters[x].push_datapkt(cur_raw_data, sweep_vars, dset_ind=ind_coord) #TODO: Update documentation on ACQ Data Format - i.e. for auxiliary pieces... - data_file.push_datapkt(self._data, sweep_vars2, sweepEx, dset_ind=ind_coord) + self._init_data_file(x, init_data_file_options) + store_pkt, store_ind = self._reverse_datapkt(cur_raw_data[x], sweep_vars2, rev_ind, ind_coord) + self._cur_filewriters[x].push_datapkt(store_pkt, sweep_vars2, dset_ind=store_ind) #TODO: Update documentation on ACQ Data Format - i.e. for auxiliary pieces... + # + store_pkt, store_ind = self._reverse_datapkt(self._data, sweep_vars2, rev_ind, ind_coord) + data_file.push_datapkt(store_pkt, sweep_vars2, sweepEx, dset_ind=store_ind) + # if len(rec_params) > 0: - rec_data_file.push_datapkt(self._prepare_rec_params(rec_params, rec_params_extra), sweep_vars2, sweepEx, dset_ind=ind_coord) + store_pkt, store_ind = self._reverse_datapkt(self._prepare_rec_params(rec_params, rec_params_extra), sweep_vars2, rev_ind, ind_coord) + rec_data_file.push_datapkt(store_pkt, sweep_vars2, sweepEx, dset_ind=store_ind) self._sweep_vars = sweep_vars2 self._mid_process() self._data = None @@ -215,6 +232,24 @@ def _run(self, file_path, sweep_vars=[], **kwargs): return FileIOReader(file_path + data_file_name) + def _reverse_datapkt(self, datapkt, sweep_vars, rev_ind, ind_coord): + if rev_ind < 0: + return datapkt, ind_coord + sweep_inds = list(np.unravel_index(ind_coord, self._sweep_shape)) + store_shape = [x[1].size for x in sweep_vars] + num_swp_pts_on_reverse_var = sweep_vars[rev_ind][1].size + if sweep_inds[rev_ind] < num_swp_pts_on_reverse_var: + store_ind = np.ravel_multi_index(sweep_inds, store_shape) + return datapkt, store_ind #MISTAKE HERE NEED TO SUBTRACT OFFSET + # + le_channels = [x for x in datapkt['data']] + for cur_ch in le_channels: + datapkt['data'][cur_ch + '_reverse'] = datapkt['data'].pop(cur_ch) + sweep_inds[rev_ind] = self._sweep_shape[rev_ind]-1 - sweep_inds[rev_ind] + store_ind = np.ravel_multi_index(sweep_inds, store_shape) + return datapkt, store_ind + + def _prepare_rec_params(self, rec_params, rec_params_extra): ret_data = { 'parameters' : [], diff --git a/sqdtoolz/Utilities/FileIO.py b/sqdtoolz/Utilities/FileIO.py index 0787df9..2a4d042 100644 --- a/sqdtoolz/Utilities/FileIO.py +++ b/sqdtoolz/Utilities/FileIO.py @@ -132,9 +132,11 @@ def push_datapkt(self, data_pkt, sweep_vars, sweepEx = {}, dset_ind = -1): #Doing the columns individually - e.g. as required when using reverse for example as only some channels get filled/populated at a time... for x in data_pkt['data']: cur_data = data_pkt['data'][x].flatten() + assert x in self._meas_chs, f"The channel {x} was not present when initialising the FileIOWriter object. Cannot write this data as the storage has not been properly initialised." self._dset[cur_dset_ind*self._datapkt_size : (cur_dset_ind+1)*self._datapkt_size, self._meas_chs.index(x)] = cur_data # if self.store_timestamps: + #TODO: When reverse-sweeping, the time-stamps are just overwritten as they don't go to the granularity of dependent variables? Fix this with some changes? #Trick taken from here: https://stackoverflow.com/questions/68443753/datetime-storing-in-hd5-database utc_strs = np.repeat(np.datetime_as_string(np.datetime64(datetime.now()),timezone='UTC').encode('utf-8'), cur_data.shape[0]) self._dsetTS[cur_dset_ind*self._datapkt_size : (cur_dset_ind+1)*self._datapkt_size] = utc_strs