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