Using Mathematica for Simultaneous
Instrument Control, Data Archiving, and Analysis

W. V. Meyer, C. Saltiel, and A. E. Smart

NCSER/NASA GRC and Scattering Solutions, Inc.

Abstract

We have designed and written Mathematica programs to run hardware, archive data, retrieve data files, and perform data analysis.  For surface light scattering we generate a preview of what a data set will look like based on the instrument settings.  This allows us to optimize experiment settings. For volume light scattering, we have written a more elaborate Mathematica program that has a "Standard User Mode" and an "Advanced User Mode" to exploit the power of Mathematica’s sophisticated constrained nonlinear regression routines and plotting capabilities.

Our requirements are to improve the speed and accuracy of complex instrument control and real-time data processing.  Our Surface Light Scattering Spectroscopy (SLSS) instrument measures the thermally induced ‘ripplons’ at a fluid interface to determine surface tension, viscosity and more sophisticated tensor properties of interfacial membranes, with applications ranging from the biophysics of lipid bilayers to oil recovery from tar sands.  Analogously, our Dynamic Light Scattering (DLS) instrument measures the hydrodynamic radius of nano- to micron-scale particles suspended in a transparent liquid.

Figure 1 (on-line) shows a window of our Surface Response Function modeling program.  Figures 2-6 (on-line) show windows of the Correlation Data Program, with its Data Summarized tab selected.  A plot of the raw data is shown as it accumulates, with instrument control settings for the graphical control panel also visible.  Data may be retrieved from any of several sources, including a real time raw data stream from the experimental, compressed or post-processed data from hardware or software correlators, data synthesized with Mathematica, and previously archived data.  Data from any of these sources can be analyzed by proprietary algorithms, nonlinear regression fitting routines, and/or combinations of other high-level functions available in Mathematica 7.  We have created and refined graphical tools to allow the optimization of external constraints by easily visualized real-time representations of their effects.  For advanced analyses, comparisons can be made by simultaneous comparison of different fitting routines, input parameter settings, and constraints.  Data sets from different sources, including the current data set (taken in real-time), may also be simultaneously compared.

Additional capabilities will be demonstrated during the presentation, with emphasis on coding techniques for the many levels and components that work together efficiently in this program.

We wish to thank and acknowledge the Wolfram Partnerships Group, Technical Support Staff, and Developers at Wolfram Research, whose support has made this development possible.

Surface Response Function Program - Previewing Experimental Results [Figure 1]

Correlation Data Program - Data Summarized (Measurement Mode) [Figure 2]

Correlation Data Program - Data Summarized (Standard User Mode) [Figure 3]

Correlation Data Program - Changing Experiment Parameters (Standard User Mode) [Figure 4]

Correlation Data Program - Data Summarized (Advanced User Mode) [Figure 5]

Correlation Data Program - Changing Experiment Parameters (Advanced User Mode) [Figure 6]

Using Setter and Pick within Grid to change the layout of a GUI

Using Setter and Pick within Grid (simple example with one graphic)

Dynamic[
Grid[{{Grid[{{Checkbox[Dynamic[gridLayout01],{True,False}],"Select Layout"}}]},
{Grid[{Pick[{,},{gridLayout01,Not[gridLayout01]}]}]
}}]]

Using Setter and Pick within Grid (simple example with two graphics)

Dynamic[
Grid[{{Grid[{{Checkbox[Dynamic[gridLayout01],{True,False}],"Select Layout",SpanFromLeft}}]},
{Grid[{{,
Grid[{Pick[{,},{gridLayout01,Not[gridLayout01]}]}]
}}]}}]]

Using Setter and Pick within Grid (simple example with selectable vertical and horizontal graphics)

Dynamic[
Grid[{{Grid[{{Checkbox[Dynamic[gridLayout01],{True,False}],"Select Layout"}}],SpanFromLeft},
{Grid[Pick[{{Grid[{{,
Grid[{Pick[{,},{gridLayout01,Not[gridLayout01]}]}]
}}]},{,SpanFromLeft}},{True,Not[gridLayout01]}]]}}]]

Using Setter and Pick within Grid (Hold Pick elements:  one way to optimize Dynamic when using large blocks of Dynamic code)

(* A simple implementation of Pick sweeps and evealuates all Pick selections when wrapped in Dynamic.
Wrapping Hold around each element in Pick, while wrapping ReleaseHold around Pick addresses this 
possible concern if when using Pick to limit the amount of code that Dynamic is allowed to sweep and
evaluate at any one time. *)
Module[{aPickContent,bPickContent,bTermSelected},
aPickContent=.;bPickContent=.;
Dynamic[Grid[{{Checkbox[Dynamic[bTermSelected],{False,True}]},
{Pick[{aPickContent=1,bPickContent=2},{True,bTermSelected}]},
{Grid[{{aPickContent,bPickContent}}]}},Alignment->{Left,Center}]]]

Module[{aPickContent,bPickContent,bTermSelected},
aPickContent=.;bPickContent=.;
Dynamic[Grid[{{Checkbox[Dynamic[bTermSelected],{False,True}]},
{ReleaseHold[Pick[{Hold[aPickContent=1],Hold[bPickContent=2]},{True,bTermSelected}]]},
{Grid[{{aPickContent,bPickContent}}]}},Alignment->{Left,Center}]]]

Using Tab to modularize code

Using Tab (standard example, which does not optimize Dynamic code)

(* A simple implementation of Tab sweeps through and evelauates the contents of all Tab Windows.
This software design allows the Tab windows to display a window that is big enough to display the contents 
of any Tab window.  But this slows the response of a large Dynamic program because it now contains a lot of code to monitor for changes; that is, unless the contents of each Tab window can be made to be independent and a conditional check 
is placed at the beginning of each Tab to see if that particular Tab has been selected before 
allowing Dynamic to check its contents. *)
Module[{aTabContent,bTabContent,cTabContent},
aTabContent=.;bTabContent=.;cTabContent=.;
TabSelection=2;
Dynamic[
Grid[{{TabView[{
10->(aTabContent=),
20->(bTabContent=),
30->(cTabContent=)},
TabSelection (* ,ImageSize->{122,150} *)]},
{Grid[{{aTabContent,bTabContent,cTabContent}}]}},Alignment->{Left,Center}]]]

Using Tab (use conditional to block access to each Tab window:  one way to optimize GUI responsiveness when using large blocks of Dynamic code)

Module[{aTabContent,bTabContent,cTabContent},
aTabContent=.;bTabContent=.;cTabContent=.;
tabSelectionNumber=2;
Dynamic[
Grid[{{
TabView[{
(EventHandler[10,{"MouseUp":>(tabSelectionNumber=1)}]->Dynamic[If[(tabSelectionNumber==1),(aTabContent=)]]),
(EventHandler[20,{"MouseUp":>(tabSelectionNumber=2)}]->Dynamic[If[(tabSelectionNumber==2),(bTabContent=)]]),
(EventHandler[30,{"MouseUp":>(tabSelectionNumber=3)}]->Dynamic[If[(tabSelectionNumber==3),(cTabContent=)]])},
Dynamic[tabSelectionNumber],ImageSize->{120,150}]},
{Grid[{{aTabContent,bTabContent,cTabContent}}]}},Alignment->{Left,Center}]]]

Using SetDelay and a pull-down menu to choose which of multiple (simultaneous) input values to display

Use pure function inside Pull - down menus.  Only computes when a DIFFERENT selection is chosen.  Only sees and executes code when a new selection is made.
Using groups of SetDelay ":=" to select results from multiple inputs for display.

Dynamic[
Grid[{{PopupMenu[Dynamic[dataSelectionDatasetAIOX,
(dataSelectionDatasetAIOXTemp=#;
Which[
(#1=="plotValues01" (* Type 1 input. *)),
(rawDataPlottedDatasetA00:=rawDataPlottedDatasetA01;
fitDataPlottedDatasetA00:=fitDataPlottedDatasetA01;
residualDataPlottedDatasetA00:=residualDataPlottedDatasetA01
(* ... etc ... *)),
(#2=="plotValues02" (* Type 2 input. *)),
(rawDataPlottedDatasetA00:=rawDataPlottedDatasetA02;
fitDataPlottedDatasetA00:=fitDataPlottedDatasetA02;
residualDataPlottedDatasetA00:=residualDataPlottedDatasetA02
(* ... etc ... *))
(* ... etc ... *)])&,SynchronousUpdating->True],
{((* 
1 -> "Load saved correlatation data for data set A ( *.cdp.xls, *.dat, or *.sin format)",
2 -> "Run correlator for data set A",
3 -> "Import and correlate raw data for data set A ( *.bin format)",
4 -> "Synthesize data for data set A",
5 -> "Load saved correlatation data for data set B ( *.cdp.xls, *.dat, or *.sin format)",
6 -> "Run correlator for data set B",
7 -> "Import and correlate raw data for data set B ( *.bin format)",
8 -> "Synthesize data for data set B",
9 -> "Load and run script ( *.scrp format)" *)
{1 -> "Load saved correlatation data for data set A",
2 -> "Run correlator for data set A"})}
]}}];

ListLinePlot[{rawDataPlottedDatasetA00,fitDataPlottedDatasetA00,residualDataPlottedDatasetA00},PlotRange->All]]

Using DynamicWrapper to compartmentalize calls of code in external modules and avoid waiting while they finish their computations

When calling an external Module (Package) to do calculations from within a Dynamic block of code, wrapping that call in DynamicWrapper enables the function being called to run on its own and keeps the software that is calling it from waiting  (hanging)  until the calculation has finished.  So, creating a responsive GUI interface is much easier with the DynamicWrapper command.

DynamicWrapper[CDPCumulantsAnalysisDatasetA`CumulantsAnalysisDatasetA[valuesPassed01,valuesPassed02 (* ,etc *)]];

Passing back variable values when using ParallelTable (instead of Table)

If[(TrueQ[parallelizationUsed]),
(DistributeDefinitions[{...}])];

DistributeDefinitions[{...}] is NOT bi-directional.
You may pass parameters to external modules, but they are NOT passed back in the same way.
Instead, you need to create a varialbe list and assign the resulting values from the computation as shown below.

CDPCumulantsAnalysisDatasetA`CumulantsAnalysisDatasetA[valuesPassed01_,valuesPassed02_,etc_]:=
((* non-parallel calculations followed by ... *),
variableList=Grid[{{
ParallelTable[(* parallel calculations *)]
}}];

If[(TrueQ[parallelizationUsed]),
((* Note that definitions are distributed at the time DistributeDefinitions is called, but DistributeDefinitions is not bi-directional, it only sends definitions out to subkernels.  In this case, we update the values below once they have been calculated above,if calulated in parallel by asigning the appropriate part of variableList to an appropriate global variable name, so that they will be passed back to the master kernel. When not running parallel kernels, this procedure in not needed, for the variable asignments above remain global and are not localized by the ParallelTable command.). *)
{corrIntRelaxTimeDatasetAFitResultDecay01C1=variableList[[1,1,1,1,1,1,1,1,1,1]],
corrIntRelaxTimeFitDatasetA01AsymptoticSEC1=variableList[[1,1,1,1,1,1,1,1,1,2]],
corrIntRelaxTimeDatasetAFitResultIntensity01C1=variableList[[1,1,1,1,1,1,1,1,1,3]],
etc})]
);

Summary of techniques discussed above for optimizing large blocks of Dynamic code
(ways to remove lag and sluggishness from your Dynamic GUI)

Acknowledgements

Demonstrate working versions of Mathematica programs discussed above