Experiment implementations (experiments package)¶
Module contents¶
The scripts/files in this directory contain the core logic for each type of spectroscopic experiment.
All scripts conform to a similar structure, provide a common interface and rely on a number of calling conventions as well as parameters available from other parts of the software, in particular the main GUI.
The documentation specific to each individual experiment type is contained in the respective script.
The experiment modules are intended to be called from the main GUI (technically this could also be called via __main__). The classes within each module are generally multiprocessed or multithreaded with the Python built-in modules or Qts multithreading. Multithreading instead of multiprocessing has to be used in the situations where objects that are not picklable (e.g. ADC) need to be passed. To use multiprocessing all objects that are passed to the class need to be picklable. As already mentioned, this is generally not the case for objects that interface with hardware.
The structure of the experiments (and beyond) was chosen to fulfill the following requirements in descending order of priority:
Efficiency: The ratio of laser time in relation to down time/waiting time needs to be as high as possible to acquire the largest amount of data in the smallest possible time frame. – Running the laser costs money and more importantly human time.
Stability and safety: The algorithm running the experiment needs to be stable and if a bug in the software occurs that is not relevant to the acquisition and the subsequent saving of the data to the hard drive the experiment should not halt. – Having a meticulously aligned setup go to waste because the software crashed overnight is devastating.
Simplicity: The code should be as easy to understand as possible. – The testing of ideas is only possible in a framework that makes it easy to implement changes and that avoids having to reinvent the metaphorical wheel every time.
Ease of use: The software should be as versatile and intuitive to use for the experimenter as possible. Plots and graphs should be self explanatory without outside input. - Having to transfer the least possible knowledge to new experimentors regarding the use of the software makes time to hand down know-how concerning the rest of the setup.
These requirements lead to the following logical conclusion:
The acquisition of data needs to be separated from the processing so that the processing does not slow down the acquisition. The processing of data to the point where it can be saved to the hard drive needs to be separated from the rest of the processing steps, that can happen afterwards. This separation into different independent units of operation can be achieved by leveraging python modules multiprocessing, multithreading.
Simplicity can be achieved by moving the processing steps into the data_processing module and calling the according functions from the experiment module. This cleans up and shortens the total length of the code and also makes it easier to document: The functions in data_processing have their own docstrings which automatically organizes the code into logical blocks. Another advantage of this is that changing the same processing step for several experiments is less tedious. The change has to only be implemented in one position.
Making the software more user-friendly generally implies more work on the developers’ side and is not limited to the experiment modules. The work that needs to be put in the experiments mainly concerns plotting. The minimum requirement for this would be that all plots should have clear titles and both axes should be properly labeled including units. This evidently includes the prerequisite that all plots that are necessary for troubleshooting purposes when the experiment is not working are displayed on the GUI. Although a lot of work has been already put into data visualization it only grazes the surface of what can be easily implemented in PyQtGraph.
Note
The developer also needs to make sure that when the experiment is being set up from the main GUI thread (widget_main_window) that undefined user input (e.g. missing delay files or mismatching delay files for VIPER experiments) can *not* be used to start the experiment. The checking for possible exceptions was deliberately excluded from the experiments to make the underlying algorithm easier to grasp and thus needs to be implemented before the experiment is even called. If an undefined user input in the last iteration of a scan is used to start an experiment the software might run for hours before it crashes.
Structure of the modules in the experiment package¶
Experiment class which instantiates the four classes Acquisition, Primary Processing, Secondary Processing and Pyqt Plotting. As already mentioned, the way this is built is to optimize the data collection (trying to collect as many laser shots as possible without throwing any away due to dead-time with processing etc.). This is achieved since the acquisition can continue acquiring new data directly and does not have to wait for other processes (except for waiting for the acquisition itself).
Acquisition class (python multithreaded). This is where the actual experiment is conducted. This class is used to control the hardware devices required for the experiment. The sole purpose of it is to collect the data according to the parameters specified for the experiment by moving the delay stages, opening and closing shutters etc. The collected data is then passed to primary processing.
Primary processing class (python multiprocess). This class’ purpose is to process the raw data to a state where it can be saved onto the hard drive as npy (binary) files. The last step of this process is to write the relevant data to the hard drive. Even if the subsequent processes, the data is secured and can be analysed in post processing. The primary processed data is handed over to the secondary processing process.
Secondary processing class (python multiprocess). This class is used to process the primary processed data such that it can be displayed on the GUI within plots and lineEdits. For example: calculate the difference signal etc. This data is handed over to the Pyqt Plotting process.
Pyqt Plotting class (QT multithreaded). This class is necessary for displaying plots on the GUI. Generally the plots are set up first (type of plot, layout, title etc.). On the first run, the plots are drawn for the first time. Then the plots are updated every iteration. We update the same plot references every time to make it more efficient.
- Note:
Initially, the intention was to use the Matplotlib engine to display graphs. As it turns out, Matplotlib is too slow for our purposes and the move towards PyQtGraph was made. To switch to PyQtGraph was a good decision for other reasons, too. The whole engine generally offers more interactive features. Nevertheless, in some experiments a fully functioning plotting class using Matplotlib can be found. We leave it there in case it is ever needed.
There are two types of experiments currently implemented in the software. The first type is the standard type of experiment that is run to collect data that is later evaluated. The second type of experiment is used to tune and optimize different parts of the setup. These are, for instance, the show spectrum or show signal experiment. These types of adjustment experiments offer the same saving functionality as the first type of experiment but the checkboxes for saving on the GUI are unchecked by default. The naming convention for these is to prepend the word “show” to their actual name. Please note that this does not mean that the first type of experiment is not used to fine tune parameters. A good example of this is the Broadband VIPER experiment which can be used to find the optimum delay between the IR pump and UV/VIS pump pulse.
While it is an issue that needs to be addressed in the GUI logic of the software, it is worth mentioning here that while for the “show” experiments the majority of the GUI elements are activated and accessible to the user, the opposite is the case for the standard type of experiments. Most of the GUI, e.g.: delay stages, spectrometer, micrometer screw etc. are disabled during an actual measurement. The rationale behind this is to prevent to user from changing any parameters during a measurement, for instance moving the central wavelength of the spectrometer. On the other hand, during the setup and fine tuning phase the experimenter needs to be able to change parameters while being able to see the changes that he is inducing. Please note that some devices need to be disabled even during “show” experiments. The two prime examples for this is the ADC and the Fabry Perot. Changing the number of samples to acquire of the ADC while the ADC is acquiring data will lead to a crash. The Fabry Perot needs access to the ADC (and other devices) when it is tuning to a new wavelength. As the center hub of every experiment, the ADC cannot be used at two places at the same time. So the Fabry Perot (except for the functionality of setting the voltage directly) needs to be disabled on the GUI for all experiments.
Submodules¶
- experiments.show_spectrum module
- experiments.show_signal module
- experiments.show_signal_wobbler module
- experiments.scan_t_zero_interleaves module
- experiments.scan_t_zero_wobbler module
- experiments.vis_pump_ir_probe module
- experiments.vis_pump_ir_probe_split_sample module
- experiments.bb_ir_pump_ir_probe_interleaves module
- experiments.fabry_perot_2d_ir_interleaves module
- experiments.ft_2d_ir module
- experiments.show_viper module
- experiments.show_viper_wobbler module
- experiments.bb_viper_interleaves module
- experiments.bb_viper_wobbler module
- experiments.fabry_perot_viper_interleaves module
- experiments.ft_viper module
- experiments.show_wobbler_states module