CustomFunction

From SweepMe! Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

version: 2022-08-17 or later

The CustomFunction module is designed to allow users to run their own python functions. The main purpose is to hand over measurement data from multiple modules to a function which takes care about calculating further parameters and about data processing. It can be used for many different purposes and is one basic opportunity to run individual python code.

The position of the CustomFunction module in the Sequencer is essential. It determines the amount of data that is handed over to the CustomFunction function.

A simplified rule is: Whenever a function of the CustomFunction module is called, all values generated since the last call of this function are handed over.

Adding a new function

  1. Add a CustomFunction module to the sequencer
  2. Go to the tab of the CustomFunction module
  3. Press "Copy selected function" and enter the name of the new function, it will be a copy of the example function.
  4. You can modify your new function using 'Open/Modify'
  5. Your new function can be found in the public folder '.\DataModules\CustomFunction\Functions'. This folder can also be found by using the button "Open folder".
  6. Edit the arguments of the function 'main' to add or remove further GUI items that are displayed to the user.
  7. Change the lists 'variables' and 'units' and return the corresponding values at the end of the function 'main'.
  8. Use the button 'Reload' to refresh the CustomFunction module after you did changed to your function.

Editing a function

A CustomFunction script is basically a python file that must contain a class 'Main' with a function 'main'.

Each class muss contain the static variables 'variables' and 'units'

  • variables: a list of strings defining the names of the values that are returned by the function 'main'
  • units: a list of strings defining the names of the units corresponding to variables
  • variables and units need to have the same length as the number of returned values of your function 'main'
  • A function 'main' must be added that returns as many values as defined by the length of variables and units.


There are two ways to define arguments that are processed by the function 'main':

  • New style: The arguments are defined by the static variable 'arguments' using a dictionary
  • Old style: The arguments of the function 'main' are automatically interpreted to create the Arguments box in the user interface of the CustomFunction module


This is a minimal working example for the new style:

class Main():
    """ here you can add some description of your script by using html. 
    This description will be shown in the description box of the CustomFunction module 
    """

    variables = ["Variable1", "Variable2"]  # add as many return variables as you like as strings
    units = ["unit1", "unit2"]  # add units as strings in the same number as you have defined variables
    arguments = {"Argument 1": "some text",  # a string
                 "Argument 2": 42,  # an integer
                 "Argument 3": 3.14,  # a float
        }

    def main(self, **kwargs):

         print(kwargs)  # Now you can dow some processing with the arguments. kwargs is a dictionary containing the value for each argument name.

         var1 = 0
         var2 = "another text"
         return variable1, variable2  # make sure you return an as many values as you have defined variables

The arguments are defined by a static attribute 'arguments' whose keys are the argument names as presented to the user and the values are the default values after loading the script. The arguments are received by the function 'main' using **kwargs that can be used to receive an arbitrary number of keyword arguments as a dictionary. The advantage of the new style is that the arguments can have friendly names because space characters are allowed. Furthermore, the number of arguments can be dynamically adjusted.


This is a minimal working example of the old style to define arguments:

class Main():
    """ here you can add some description of your script by using html. 
    This description will be shown in the description box of the CustomFunction module 
    """

    variables = ["Variable1", "Variable2"]  # add as many return variables as you like as strings
    units = ["unit1", "unit2"]  # add units as strings in the same number as you have defined variables

    def main(self, argument1="some text", argument2=42, argument3=3.14):

         print("Arguments:", argument1, argument2, argument3)

         var1 = 0
         var2 = "another text"
         return variable1, variable2  # make sure you return an as many values as you have defined variables

Arguments are automatically defined by the arguments of the function 'main'. Whenever 'main' is called by SweepMe!, the current argument values are handed over and can be processed then.


If you need to know the values of the arguments very early at the beginning of a run even before the 'main' function is called, you can use the variable 'self.main_arguments' that is handed over to the class instance of the script during "initialize"

def initialize(self):
   print(self.main_arguments)  # prints the dictionary with the argument names being the keys and the values being the user selection

Don't forget to use the 'Reload' button to update changes to user interface if you have changed the number or the names of the arguments.

Arguments/Input parameters

  • Data from SweepMe!: tuple -> () Please note: data is always handed over as numpy array, independent whether is is just a single value or a list. Please make sure you process the data correctly before you return it.
  • Integer: int -> any integer number which will be the preset value
  • Float: float -> any float value which will be the preset value
  • String: str -> any string which will be the preset value
  • List: list -> any list of strings which will be presented in ComboBox for selection by the user
  • Bool: bool -> set to True or False; the user can later select using a CheckBox
  • Directory: pathlib.Path(<directory>) -> an empty pathlib.Path object or a non-empty pathlib.Path object with a given directory <directory>. The user will see a QFileDialog to choose a folder.
  • File: pathlib.Path(<file>) -> a non-empty pathlib.Path object with a given file <file>. The user will see a QFileDialog to choose a file.

Procedure

Whenever CustomFunction is part of an iteration in a branch of the sequencer, the defined function 'main' is called with the given parameters and the defined variables must be returned. SweepMe! measurement values that are requested by using a tuple () as argument are automatically handed over as an array of the values that have been acquired since the last call CustomFunction. For example to get a single number from this array, one can take the item at the first index, e.g 'val[0]' assuming that 'val' is the list that has been handed over. Please also take about care changing to the correct type beforehand.

Function calls

main

The function 'main' can define the arguments (old style) and returns as many values as defined by the static attributes 'variables' and 'units'. It is called in process() during the Sequencer procedure.

It is possible to advance the call of the 'main' function to the start() of the Sequencer procedure by adding the 'execution' property to the CustomFunction class:

class Main():
    execution = "start"

This can be used to calculate set values which shall be used for other modules in the sequencer.

prepare_run / prepare_stop

You can add prepare_run and prepare_stop to your script. These functions have no arguments and return no values. They are called before and after the measurement inside the GUI thread.

initialize / deinitialize

You can add initialize and deinitialize to your script. These functions have no arguments and return no values. They are called according to the Sequencer procedure at the start and at the end of the run, respectively.

configure / unconfigure

You can add configure and unconfigure to your script. These functions have no arguments and return no values. They are called according to the Sequencer procedure when the module is in the new branch or not anymore in the next branch, respectively.

signin / signout

You can add signin and signout to your script. These functions have no arguments and return no values. They are called according to the Sequencer procedure when a new setvalue in a module above your CustomFunction module is started or finished.

GUI mode

Creating GUI widgets, e.g. by using PySide2 package, leads to a program crash as CustomFunction is running in the measurement thread of SweepMe! while GUI widgets must run in the main GUI thread of the program. For that reason, CustomFunction provides a GUI mode that can be enabled by using the function 'renew_widget' that takes care about exchanging Qwidget objects with the CustomFunction module. The function must be defined in class 'Main'. Please, use the function 'Example_GUI' to see how to use it.

def renew_widget(self, widget = None):
    """ gets the widget from the module and returns the same widget or creates a new one"""
    
    if widget is None:
        # if the widget has not been created so far, we create it now and store it as self.widget
        self.widget = Widget()
    else:
        # the second time a run is started, we can use the widget, that is handed over to renwe_widget, to store is as self.widget
        self.widget = widget
            
    # return the actual widget to inform the module which one has to be inserted into the DockWidget of the Dashboard    
    return self.widget

As shown in the code snippet above, 'renew_widget' expects an argument 'widget' that is None at the very first call. Thus, one has to create a new QWidget-type object called here 'Widget()' that is set to 'self.widget' and returned at the end of the function. The second time 'renew_widget' is called, e.g. when a measurement is started again, the module CustomFunction hands over the last known Qwidget to 'renew_widget' and the widget can be set to 'self.widget' in order to make it available to other function of the class 'Main'.

Qwidget objects that are returned by 'renew_widget' are automatically placed into a QDockWidget that can be placed by the user into the dashboard. 'renew_widget' is called before every measurement and whenever the selected function is changed. Thus, it is also possible to store the layout of the dock widget to the setting. However, values that are entered in such custom widgets are not automatically saved to the setting file.

Importing packages

Importing external python packages that are not shipped with SweepMe! just works like for Driver Programming. You can find a guide here External libraries and dependencies.

Applications

  • Data smoothing
  • Repetitive curve fitting and extraction of fit parameters
  • Parameter extraction
  • Specializing a measurement setup to certain needs
  • pre-evaluation of the data
  • using SweepMe! as a frontend for simulations
  • Control loops, e.g. a PID controller
  • creating output GUI widgets to display data
  • creating input GUI widgets to let the user change a parameter
  • Manipulating or Copying data files after a run

Examples

  • Characteriztion of field-effect transistors: Transfer characteristics are measured in the linear regime. Current and voltage measurement data is handed over to the Evaluation module where charge carrier mobility and threshold voltage is extracted.
  • Characterization of LEDs: Spectra and current-voltage characteristics can be handed over to the Evaluation module to calculate several device efficiency parameters.
  • Calculation of peak values, zeroing, derivation, integral, mean, standard deviation, ...
  • Creating a custom GUI widgets including a number of slider and input fields to control a certain process.
  • Displaying several values in human-machine-interface (HMI) like way.