def set_meas(dep, fast_indep, slow_indep=None, setup=setup, cleanup=cleanup):
    """
       Regestration of the parameters and setting up before/after run procedure
        
       args:
            dep: InstParameter to be regestered as dependent var 
            fast_indep: InstParameter to be regestered as fast independent var 
            slow_indep: InstParameter to be regestered as second independent var (if any) 
            setup: Procedure to be done before run
            cleanup: Procedure to be done after run
            
        returns:
        meas: Measurement() object
           
    """

    meas = Measurement()
    meas.register_parameter(
        fast_indep)  # register the fast independent parameter

    if slow_indep is not None:
        meas.register_parameter(
            slow_indep)  # register the first independent parameter
        meas.register_parameter(dep, setpoints=(slow_indep, fast_indep))
    else:
        meas.register_parameter(dep, setpoints=(fast_indep, ))

    meas.add_before_run(setup, args=())
    meas.add_after_run(cleanup, args=())
    meas.write_period = 2

    return meas
Example #2
0
def test_setting_write_period(empty_temp_db, wp):
    new_experiment('firstexp', sample_name='no sample')
    meas = Measurement()

    if isinstance(wp, str):
        with pytest.raises(ValueError):
            meas.write_period = wp
    elif wp < 1e-3:
        with pytest.raises(ValueError):
            meas.write_period = wp
    else:
        meas.write_period = wp
        assert meas._write_period == wp

        with meas.run() as datasaver:
            assert datasaver.write_period == wp
Example #3
0
def test_datasaver_scalars(experiment, DAC, DMM, set_values, get_values,
                           breakpoint, write_period):

    no_of_runs = len(experiment)

    station = qc.Station(DAC, DMM)

    meas = Measurement(station=station)
    meas.write_period = write_period

    assert meas.write_period == write_period

    meas.register_parameter(DAC.ch1)
    meas.register_parameter(DMM.v1, setpoints=(DAC.ch1,))

    with meas.run() as datasaver:
        for set_v, get_v in zip(set_values[:breakpoint], get_values[:breakpoint]):
            datasaver.add_result((DAC.ch1, set_v), (DMM.v1, get_v))

        assert datasaver._dataset.number_of_results == 0
        sleep(write_period)
        datasaver.add_result((DAC.ch1, set_values[breakpoint]),
                             (DMM.v1, get_values[breakpoint]))
        assert datasaver.points_written == breakpoint + 1

    assert datasaver.run_id == no_of_runs + 1

    with meas.run() as datasaver:
        with pytest.raises(ValueError):
            datasaver.add_result((DAC.ch2, 1), (DAC.ch2, 2))
        with pytest.raises(ValueError):
            datasaver.add_result((DMM.v1, 0))

    # important cleanup, else the following tests will fail
    qc.Station.default = None
Example #4
0
def test_subscriptions(experiment, DAC, DMM):
    def subscriber1(results, length, state):
        """
        A dict of all results
        """
        state[length] = results

    def subscriber2(results, length, state):
        """
        A list of all parameter values larger than 7
        """
        for res in results:
            state += [pres for pres in res if pres > 7]

    meas = Measurement(exp=experiment)
    meas.register_parameter(DAC.ch1)
    meas.register_parameter(DMM.v1, setpoints=(DAC.ch1, ))

    res_dict = {}
    lt7s = []

    meas.add_subscriber(subscriber1, state=res_dict)
    assert len(meas.subscribers) == 1
    meas.add_subscriber(subscriber2, state=lt7s)
    assert len(meas.subscribers) == 2

    meas.write_period = 0.2

    expected_list = []

    with meas.run() as datasaver:

        assert len(datasaver._dataset.subscribers) == 2
        assert res_dict == {}
        assert lt7s == []

        as_and_bs = list(zip(range(5), range(3, 8)))

        for num in range(5):

            (a, b) = as_and_bs[num]
            expected_list += [c for c in (a, b) if c > 7]
            sleep(1.2 * meas.write_period)
            datasaver.add_result((DAC.ch1, a), (DMM.v1, b))
            assert lt7s == expected_list
            assert list(res_dict.keys()) == [n for n in range(1, num + 2)]

    assert len(datasaver._dataset.subscribers) == 0
Example #5
0
def _set_write_period(meas: Measurement,
                      write_period: Optional[float] = None) -> None:
    if write_period is not None:
        meas.write_period = write_period
Example #6
0
def linear2d(param_set1,
             start1,
             stop1,
             num_points1,
             delay1,
             param_set2,
             start2,
             stop2,
             num_points2,
             delay2,
             *param_meas,
             win=None,
             append=False,
             plot_params=None,
             atstart=None,
             ateachcol=None,
             ateach=None,
             atend=None,
             setback=False,
             write_period=120):
    """
    Run a sweep of a single parameter, between start and stop, with a delay after settings
    the point given by delay.

    Args:
        param_set1 (Parameter): The parameter to be stepped on the x-axis

        start1 (Union[int, float]): Starting point of the x-axis parameter

        stop1 (Union[int, float]): End point of the x-axis parameter

        num_points1 (int): Number of points to take between start and stop (inclusive) on the x-axis

        delay1 (Union[int, float]): The delay after setting the parameter on the x-axis

        param_set2 (Parameter): The parameter to be swept on the y-axis

        start2 (Union[int, float]): Starting point of the y-axis parameter

        stop2 (Union[int, float]): End point of the y-axis parameter

        num_points2 (int): Number of points to take between start and stop (inclusive) on the y-axis

        delay2 (Union[int, float]): The delay after setting the parameter on the y-axis

        *param_meas (Iterable[Parameter]): A list of the parameters to be measured at each of the
        set points. These must be single valued for live plotting to work

        win (Optional[PlotWindow]): The plot window to add plots to. If this value is None, the sweep
        will not be live plotted.

        append (bool): If this parameter is true, the trace will be appended to an existing window.

        plot_params (Optional[Iterable[Parameter]]): A list of measured parameters to live plot. If no
        value is given, then all parameters will be live-plotted

        atstart (Optional[Union[Callable,Iterable[Callable]]]): A function or list of functions
        to be run before the measurement is started. The functions will be run BEFORE the parameters
        are inserted into the measurement, hence if some parameters require setup before they are run,
        they can be inserted here.

        ateachcol (Optional[Union[Callable,Iterable[Callable]]]): A function or list of functions
        to be run after each column of data is complete, useful for example for doing more advanced
        wall control. These functions are run AFTER the delay.

        ateach (Optional[Union[Callable,Iterable[Callable]]]): A function or list of functions
        to be run after each time the sweep parameter is set. These functions will be run AFTER
        the delay, and so is suitable if an instrument requires a call to capture a trace before
        the parameter can be read.

        atend (Optional[Union[Callable,Iterable[Callable]]]): A function or list of functions
        to be run at the end of a trace. This is run AFTER the data is saved into the database,
        and after parameters are set back to their starting points (if setback is True), and
        can therefore be used to read the data that was taken and potentially do some post analysis.

        setback (Optional[bool]): If this is True, the setpoint parameter is returned to its starting
        value at the end of the sweep.

        write_period (Optional[int]): The time inbetween which data is written to the database.
        Irrespective of what this is set to, data will be saved when the week finishes, and will attempt
        to save in the case the sweep is interrupted.

    Returns:
        (id, win): ID is the trace id of the saved wave, win is a handle to the plot window that was created
        for the purposes of liveplotting.

    """
    _flush_buffers(*param_meas)

    # Register setpoints
    meas = Measurement()
    # Step Axis
    meas.register_parameter(param_set1)
    param_set1.post_delay = delay1
    set_points1 = np.linspace(start1, stop1, num_points1)
    # Sweep Axis
    meas.register_parameter(param_set2)
    param_set2.post_delay = delay2
    set_points2 = np.linspace(start2, stop2, num_points2)

    # Keep track of data and plots
    if plot_params is None:
        plot_params = param_meas
    output = []
    plots = {}

    # Run @start functions
    _run_functions(atstart)

    # Register each parameter
    for p, parameter in enumerate(param_meas):
        meas.register_parameter(parameter, setpoints=(param_set1, param_set2))
        output.append([parameter, None])

        # Add Plot item
        if win is not None and parameter in plot_params:
            if append:
                plotitem = win.items[0]
                plotdata = plotitem.plot(setpoint_x=set_points1,
                                         setpoint_y=set_points2)
            else:
                plotitem = win.addPlot(
                    name=parameter.full_name,
                    title="%s (%s) v.<br>%s (%s)" %
                    (param_set1.full_name, param_set1.label,
                     param_set2.full_name, param_set2.label))
                plotdata = plotitem.plot(setpoint_x=set_points1,
                                         setpoint_y=set_points2)
                plotitem.update_axes(param_set1, param_set2)
                plotdata.update_histogram_axis(parameter)
            plots[parameter] = LivePlotDataItem(
                plotitem, plotdata, np.ndarray((num_points1, num_points2)))

    meas.write_period = write_period
    pbar = None
    try:
        with meas.run() as datasaver:
            # Update plot titles
            win.run_id = datasaver.run_id
            win.win_title += "{} ".format(datasaver.run_id)
            for plotitem in plots.values():
                plotitem.plot.plot_title += " (id: %d)" % datasaver.run_id

            pbar = tqdm(total=num_points1, unit="col", position=0, leave=True)
            for i, set_point1 in enumerate(set_points1):
                param_set2.set(start2)
                param_set1.set(set_point1)
                _run_functions(ateachcol,
                               param_vals=(Setpoint(param_set1, i,
                                                    set_point1), ))
                for j, set_point2 in enumerate(set_points2):
                    param_set2.set(set_point2)
                    _run_functions(
                        ateach,
                        param_vals=(Setpoint(param_set1, i, set_point1),
                                    Setpoint(param_set2, j, set_point2)))
                    for p, parameter in enumerate(param_meas):
                        output[p][1] = parameter.get()

                        if win is not None and parameter in plots:
                            fdata = plots[parameter].data
                            fdata[i, j] = output[p][1]
                            if i == 0:
                                # Calculate z-range of data, and remove NaN's from first column
                                # This sets zero point for rest of data
                                z_range = (np.nanmin(fdata[i, :j + 1]),
                                           np.nanmax(fdata[i, :j + 1]))
                                fdata[0,
                                      j + 1:] = (z_range[0] + z_range[1]) / 2
                                fdata[1:, :] = (z_range[0] + z_range[1]) / 2

                            # Update plot items, and update range every 10 points
                            if (num_points1 * num_points2) < 1000 or (j %
                                                                      20) == 0:
                                plots[parameter].plotdata.update(fdata, True)

                    # Save data
                    datasaver.add_result((param_set1, set_point1),
                                         (param_set2, set_point2), *output)
                pbar.update(1)

            # At the end, do one last update to make sure that all data is displayed.
            if win is not None:
                for pd in plots.values():
                    pd.plotdata.update(pd.data, True)
    finally:
        # Set paramters back to start
        if setback:
            param_set1.set(start1)
            param_set2.set(start2)

        # Close progress bar
        if pbar is not None:
            pbar.close()

        _run_functions(atend)

    return datasaver.run_id
Example #7
0
def linear1d(param_set,
             start,
             stop,
             num_points,
             delay,
             *param_meas,
             win=None,
             append=False,
             plot_params=None,
             atstart=None,
             ateach=None,
             atend=None,
             setback=False,
             write_period=120):
    """
    Run a sweep of a single parameter, between start and stop, with a delay after settings
    the point given by delay.

    Args:
        param_set (Parameter): The parameter to be swept

        start (Union[int, float]): Starting point of the parameter

        stop (Union[int, float]): End point of the parameter

        num_points (int): Number of points to take between start and stop (inclusive)

        delay (Union[int, float]): The delay after setting the parameter

        *param_meas (Iterable[Parameter]): A list of the parameters to be measured at each of the
        set points. If any of the parameters given are ArrayParameters then a 2D sweep will be
        taken on that parameter, using the setpoints given in that ArrayParamter.
        Note: At the current time, there is an assumption that the setpoints do NOT change during
        a measurement, and that points are uniformly distributed for the purposes of plotting.
        If the points are not uniformly distributed, data is correctly saved, however the live
        plot will be distorted.

        win (Optional[PlotWindow]): The plot window to add plots to. If this value is None, the sweep
        will not be live plotted.

        append (bool): If this parameter is true, the trace will be appended to an existing window.

        plot_params (Optional[Iterable[Parameter]]): A list of measured parameters to live plot. If no
        value is given, then all parameters will be live-plotted

        atstart (Optional[Union[Callable,Iterable[Callable]]]): A function or list of functions
        to be run before the measurement is started. The functions will be run BEFORE the parameters
        are inserted into the measurement, hence if some parameters require setup before they are run,
        they can be inserted here.

        ateach (Optional[Union[Callable,Iterable[Callable]]]): A function or list of functions
        to be run after each time the sweep parameter is set. These functions will be run AFTER
        the delay, and so is suitable if an instrument requires a call to capture a trace before
        the parameter can be read.

        atend (Optional[Union[Callable,Iterable[Callable]]]): A function or list of functions
        to be run at the end of a trace. This is run AFTER the data is saved into the database,
        and after parameters are set back to their starting points (if setback is True), and
        can therefore be used to read the data that was taken and potentially do some post analysis.

        setback (Optional[bool]): If this is True, the setpoint parameter is returned to its starting
        value at the end of the sweep.

        write_period (Optional[int]): The time inbetween which data is written to the database.
        Irrespective of what this is set to, data will be saved when the week finishes, and will attempt
        to save in the case the sweep is interrupted.

    Returns:
        (id, win): ID is the trace id of the saved wave, win is a handle to the plot window that was created
        for the purposes of liveplotting.

    """
    _flush_buffers(*param_meas)

    # Register setpoints
    meas = Measurement()
    meas.register_parameter(param_set)
    param_set.post_delay = delay
    set_points = np.linspace(start, stop, num_points)

    # Keep track of data and plots
    if plot_params is None:
        plot_params = param_meas
    output = []
    plots = {}

    # Run @start functions
    _run_functions(atstart)

    # Register each of the sweep parameters and set up a plot window for them
    for p, parameter in enumerate(param_meas):
        meas.register_parameter(parameter, setpoints=(param_set, ))
        output.append([parameter, None])

        if win is not None and parameter in plot_params:
            # Create plot window
            if append:
                plotitem = win.items[0]
            else:
                plotitem = win.addPlot(name=parameter.full_name,
                                       title="%s (%s) v.<br>%s (%s)" %
                                       (param_set.full_name, param_set.label,
                                        parameter.full_name, parameter.label))

            # Figure out if we have 1d or 2d data
            shape = getattr(parameter, 'shape', None)
            if shape is not None and shape != tuple():
                set_points_y = parameter.setpoints[0]
                data = np.ndarray((num_points, parameter.shape[0]))
            else:
                set_points_y = None
                data = np.full(num_points, np.nan)

            # Add data into the plot window
            plotdata = plotitem.plot(setpoint_x=set_points,
                                     setpoint_y=set_points_y,
                                     pen=(255, 0, 0),
                                     name=parameter.name)
            plots[parameter] = LivePlotDataItem(plotitem, plotdata, data)

            # Update axes
            if set_points_y is not None:
                plotitem.update_axes(param_set,
                                     parameter,
                                     param_y_setpoint=True)
                plotdata.update_histogram_axis(parameter)
            else:
                plotitem.update_axes(param_set, parameter)

    # Run the sweep
    meas.write_period = write_period
    pbar = None
    try:
        with meas.run() as datasaver:
            if win is not None:
                # Update plot titles to include the ID
                win.run_id = datasaver.run_id
                win.win_title += "{} ".format(datasaver.run_id)
                for plotitem in plots.values():
                    plotitem.plot.plot_title += " (id: %d)" % datasaver.run_id

            # Then, run the actual sweep
            pbar = tqdm(total=num_points, unit="pt", position=0, leave=True)
            for i, set_point in enumerate(set_points):
                param_set.set(set_point)
                _run_functions(ateach,
                               param_vals=(Setpoint(param_set, i,
                                                    set_point), ))
                # Read out each parameter
                for p, parameter in enumerate(param_meas):
                    output[p][1] = parameter.get()
                    shape = getattr(parameter, 'shape', None)
                    if win is not None and parameter in plots:
                        if shape is not None and shape != tuple():
                            plots[parameter].data[i, :] = output[p][
                                1]  # Update 2D data
                            # For a 2D trace, figure out the value for data not yet set if this is the
                            # first column
                            if i == 0:
                                plots[parameter].data[1:] = (np.min(
                                    output[p][1]) + np.max(output[p][1])) / 2
                            # Update live plots
                            plots[parameter].plotdata.update(
                                plots[parameter].data)
                        else:
                            plots[parameter].data[i] = output[p][
                                1]  # Update 1D data
                            plots[parameter].plotdata.setData(
                                set_points[:i], plots[parameter].data[:i])

                # Save data
                datasaver.add_result((param_set, set_point), *output)
                pbar.update(1)
    finally:
        # Set back to start at the end of the measurement
        if setback:
            param_set.set(start)

        # Close the progress bar
        if pbar is not None:
            pbar.close()

        _run_functions(atend)  # Run functions at the end

    # Return the dataid
    return datasaver.run_id  # can use plot_by_id(dataid)
Example #8
0
def test_subscriptions(experiment, DAC, DMM):
    """
    Test that subscribers are called at the moment that data is flushed to database

    Note that for the purpose of this test, flush_data_to_database method is called explicitly instead of waiting for
    the data to be flushed automatically after the write_period passes after a add_result call.

    Args:
        experiment (qcodes.dataset.experiment_container.Experiment) : qcodes experiment object
        DAC (qcodes.instrument.base.Instrument) : dummy instrument object
        DMM (qcodes.instrument.base.Instrument) : another dummy instrument object
    """
    def subscriber1(results, length, state):
        """
        A dict of all results
        """
        state[length] = results

    def subscriber2(results, length, state):
        """
        A list of all parameter values larger than 7
        """
        for res in results:
            state += [pres for pres in res if pres > 7]

    meas = Measurement(exp=experiment)
    meas.register_parameter(DAC.ch1)
    meas.register_parameter(DMM.v1, setpoints=(DAC.ch1, ))

    res_dict = {}
    lt7s = []

    meas.add_subscriber(subscriber1, state=res_dict)
    assert len(meas.subscribers) == 1
    meas.add_subscriber(subscriber2, state=lt7s)
    assert len(meas.subscribers) == 2

    meas.write_period = 0.2

    expected_list = []

    with meas.run() as datasaver:

        assert len(datasaver._dataset.subscribers) == 2
        assert res_dict == {}
        assert lt7s == []

        as_and_bs = list(zip(range(5), range(3, 8)))

        for num in range(5):

            (a, b) = as_and_bs[num]
            expected_list += [c for c in (a, b) if c > 7]

            datasaver.add_result((DAC.ch1, a), (DMM.v1, b))
            datasaver.flush_data_to_database()

            assert lt7s == expected_list
            assert list(res_dict.keys()) == [n for n in range(1, num + 2)]

    assert len(datasaver._dataset.subscribers) == 0
def test_subscriptions(experiment, DAC, DMM):
    """
    Test that subscribers are called at the moment the data is flushed to database

    Note that for the purpose of this test, flush_data_to_database method is
    called explicitly instead of waiting for the data to be flushed
    automatically after the write_period passes after a add_result call.
    """
    def collect_all_results(results, length, state):
        """
        Updates the *state* to contain all the *results* acquired
        during the experiment run
        """
        # Due to the fact that by default subscribers only hold 1 data value
        # in their internal queue, this assignment should work (i.e. not
        # overwrite values in the "state" object) assuming that at the start
        # of the experiment both the dataset and the *state* objects have
        # the same length.
        state[length] = results

    def collect_values_larger_than_7(results, length, state):
        """
        Appends to the *state* only the values from *results*
        that are larger than 7
        """
        for result_tuple in results:
            state += [value for value in result_tuple if value > 7]

    meas = Measurement(exp=experiment)
    meas.register_parameter(DAC.ch1)
    meas.register_parameter(DMM.v1, setpoints=(DAC.ch1, ))

    # key is the number of the result tuple,
    # value is the result tuple itself
    all_results_dict = {}
    values_larger_than_7 = []

    meas.add_subscriber(collect_all_results, state=all_results_dict)
    assert len(meas.subscribers) == 1
    meas.add_subscriber(collect_values_larger_than_7,
                        state=values_larger_than_7)
    assert len(meas.subscribers) == 2

    meas.write_period = 0.2

    with meas.run() as datasaver:

        # Assert that the measurement, runner, and datasaver
        # have added subscribers to the dataset
        assert len(datasaver._dataset.subscribers) == 2

        assert all_results_dict == {}
        assert values_larger_than_7 == []

        dac_vals_and_dmm_vals = list(zip(range(5), range(3, 8)))
        values_larger_than_7__expected = []

        for num in range(5):
            (dac_val, dmm_val) = dac_vals_and_dmm_vals[num]
            values_larger_than_7__expected += \
                [val for val in (dac_val, dmm_val) if val > 7]

            datasaver.add_result((DAC.ch1, dac_val), (DMM.v1, dmm_val))

            # Ensure that data is flushed to the database despite the write
            # period, so that the database triggers are executed, which in turn
            # add data to the queues within the subscribers
            datasaver.flush_data_to_database()

            # In order to make this test deterministic, we need to ensure that
            # just enough time has passed between the moment the data is flushed
            # to database and the "state" object (that is passed to subscriber
            # constructor) has been updated by the corresponding subscriber's
            # callback function. At the moment, there is no robust way to ensure
            # this. The reason is that the subscribers have internal queue which
            # is populated via a trigger call from the SQL database, hence from
            # this "main" thread it is difficult to say whether the queue is
            # empty because the subscriber callbacks have already been executed
            # or because the triggers of the SQL database has not been executed
            # yet.
            #
            # In order to overcome this problem, a special decorator is used
            # to wrap the assertions. This is going to ensure that some time
            # is given to the Subscriber threads to finish exhausting the queue.
            @retry_until_does_not_throw(
                exception_class_to_expect=AssertionError, delay=0.5, tries=10)
            def assert_states_updated_from_callbacks():
                assert values_larger_than_7 == values_larger_than_7__expected
                assert list(all_results_dict.keys()) == \
                    [result_index for result_index in range(1, num + 1 + 1)]

            assert_states_updated_from_callbacks()

    # Ensure that after exiting the "run()" context,
    # all subscribers get unsubscribed from the dataset
    assert len(datasaver._dataset.subscribers) == 0

    # Ensure that the triggers for each subscriber
    # have been removed from the database
    get_triggers_sql = "SELECT * FROM sqlite_master WHERE TYPE = 'trigger';"
    triggers = atomic_transaction(datasaver._dataset.conn,
                                  get_triggers_sql).fetchall()
    assert len(triggers) == 0
Example #10
0
def do2d(param_set1: _BaseParameter,
         start1: number,
         stop1: number,
         num_points1: int,
         delay1: number,
         param_set2: _BaseParameter,
         start2: number,
         stop2: number,
         num_points2: int,
         delay2: number,
         *param_meas: Union[_BaseParameter, Callable[[], None]],
         set_before_sweep: Optional[bool] = False,
         enter_actions: Sequence[Callable[[], None]] = (),
         exit_actions: Sequence[Callable[[], None]] = (),
         before_inner_actions: Sequence[Callable[[], None]] = (),
         after_inner_actions: Sequence[Callable[[], None]] = (),
         write_period: Optional[float] = None,
         flush_columns: bool = False,
         do_plot: bool = True,
         conDuct: Instrument = None) -> AxesTupleListWithRunId:
    """
    adapted for logging settings by felix 17.04.2020
		-added argument do2buf
		-added _export_settings functionality

	adapted for live plotting of conductance by felix 17.04.2020
		-added argument conDuct
		-conDuct is a virtual parameter who has to be called as an optional argument in do1D.
		 conDuct has a function calcG() which allows to calculate the division of two given 
		 parameters or one parameter and a float number. See init file for more info.

    Perform a 1D scan of ``param_set1`` from ``start1`` to ``stop1`` in
    ``num_points1`` and ``param_set2`` from ``start2`` to ``stop2`` in
    ``num_points2`` measuring param_meas at each step.

    Args:
        param_set1: The QCoDeS parameter to sweep over in the outer loop
        start1: Starting point of sweep in outer loop
        stop1: End point of sweep in the outer loop
        num_points1: Number of points to measure in the outer loop
        delay1: Delay after setting parameter in the outer loop
        param_set2: The QCoDeS parameter to sweep over in the inner loop
        start2: Starting point of sweep in inner loop
        stop2: End point of sweep in the inner loop
        num_points2: Number of points to measure in the inner loop
        delay2: Delay after setting paramter before measurement is performed
        *param_meas: Parameter(s) to measure at each step or functions that
          will be called at each step. The function should take no arguments.
          The parameters and functions are called in the order they are
          supplied.
        set_before_sweep: if True the outer parameter is set to its first value
            before the inner parameter is swept to its next value.
        enter_actions: A list of functions taking no arguments that will be
            called before the measurements start
        exit_actions: A list of functions taking no arguments that will be
            called after the measurements ends
        before_inner_actions: Actions executed before each run of the inner loop
        after_inner_actions: Actions executed after each run of the inner loop
        do_plot: should png and pdf versions of the images be saved after the
            run.

    Returns:
        The run_id of the DataSet created
    """

    meas = Measurement()
    if write_period:
        meas.write_period = write_period
    meas.register_parameter(param_set1)
    param_set1.post_delay = delay1
    meas.register_parameter(param_set2)
    param_set2.post_delay = delay2
    interrupted = False
    for action in enter_actions:
        # this omits the possibility of passing
        # argument to enter and exit actions.
        # Do we want that?
        meas.add_before_run(action, ())

    for action in exit_actions:
        meas.add_after_run(action, ())

    for parameter in param_meas:
        if isinstance(parameter, _BaseParameter):
            meas.register_parameter(parameter,
                                    setpoints=(param_set1, param_set2))
        if conDuct != None:
            meas.register_parameter(conDuct.G,
                                    setpoints=(param_set1, param_set2))

    try:
        with meas.run() as datasaver:
            start_time = time.perf_counter()
            os.makedirs(datapath + '{}'.format(datasaver.run_id))
            for set_point1 in np.linspace(start1, stop1, num_points1):
                if set_before_sweep:
                    param_set2.set(start2)

                param_set1.set(set_point1)
                for action in before_inner_actions:
                    action()
                for set_point2 in np.linspace(start2, stop2, num_points2):
                    # skip first inner set point if `set_before_sweep`
                    if set_point2 == start2 and set_before_sweep:
                        pass
                    else:
                        param_set2.set(set_point2)
                    output = []
                    for parameter in param_meas:
                        if isinstance(parameter, _BaseParameter):
                            output.append((parameter, parameter.get()))
                        elif callable(parameter):
                            parameter()

                        if conDuct != None:
                            output.append((conDuct.G, conDuct.calcG(output)))

                    datasaver.add_result((param_set1, set_point1),
                                         (param_set2, set_point2), *output)
                for action in after_inner_actions:
                    action()
                if flush_columns:
                    datasaver.flush_data_to_database()

        stop_time = time.perf_counter()
    except KeyboardInterrupt:
        interrupted = True

    dataid = datasaver.run_id

    if interrupted:
        inst = list(meas.parameters.values())
        exportpath = datapath + '{}'.format(
            datasaver.run_id) + '/{}_set_{}_set.dat'.format(
                inst[0].name, inst[1].name)
        exportsnapshot = datapath + '{}'.format(
            datasaver.run_id) + '/snapshot.txt'
        #export_by_id(dataid,exportpath)
        export_by_id_pd(dataid, exportpath)
        export_snapshot_by_id(dataid, exportsnapshot)
        #added by felix 05.03.2020
        _export_settings(datasaver.run_id, inst)

        stop_time = time.perf_counter()
        print("Acquisition took:  %s seconds " % (stop_time - start_time))
        raise KeyboardInterrupt

    inst = list(meas.parameters.values())
    exportpath = datapath + '{}'.format(
        datasaver.run_id) + '/{}_set_{}_set.dat'.format(
            inst[0].name, inst[1].name)
    exportsnapshot = datapath + '{}'.format(datasaver.run_id) + '/snapshot.txt'
    #export_by_id(dataid,exportpath)
    export_by_id_pd(dataid, exportpath)
    export_snapshot_by_id(dataid, exportsnapshot)
    # _export_settings(datasaver.run_id,inst)

    if do_plot is True:
        ax, cbs = _save_image(datasaver, inst)
    else:
        ax = None,
        cbs = None

    print("Acquisition took:  %s seconds " % (stop_time - start_time))

    return dataid, ax, cbs