new features: data reduction, angle scan panel

- new data reduction interface for more efficient multi-peak fitting.
  the new interface breaks compatibility with pre-2.0 data reduction
  functions. user-defined functions must be adapted to the new
  interface.
- new angle scan processing panel for interactive data analysis.
This commit is contained in:
2018-02-06 11:39:57 +01:00
parent e1f1aa9636
commit fda49c3195
171 changed files with 7539 additions and 2554 deletions

View File

@ -1,11 +1,10 @@
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma IgorVersion = 6.2
#pragma ModuleName = PearlAreaImport
#pragma version = 1.06
#include <HDF5 Browser>
#include "pearl-gui-tools"
// copyright (c) 2013-16 Paul Scherrer Institut
// copyright (c) 2013-18 Paul Scherrer Institut
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -23,6 +22,14 @@
/// as of Igor 6.3, Igor can open datasets of up to rank 4.
/// i.e. the extra dimension Y of the file plugin cannot be used.
/// the extra dimensions N and X are supported.
///
/// @author matthias muntwiler, matthias.muntwiler@psi.ch
///
/// @copyright 2013-18 Paul Scherrer Institut @n
/// Licensed under the Apache License, Version 2.0 (the "License"); @n
/// you may not use this file except in compliance with the License. @n
/// You may obtain a copy of the License at
/// http://www.apache.org/licenses/LICENSE-2.0
/// @namespace PearlAreaImport
/// @brief HDF5 file import from EPICS area detectors
@ -1031,7 +1038,7 @@ end
/// it may thus include functions which are not suitable as reduction functions.
///
function /s adh5_list_reduction_funcs()
string all_funcs = FunctionList("*", ";", "KIND:6,NPARAMS:4,VALTYPE:1")
string all_funcs = FunctionList("*", ";", "KIND:6,NPARAMS:2,VALTYPE:8")
string result = ""
variable ii
@ -1045,16 +1052,14 @@ function /s adh5_list_reduction_funcs()
for (ii = 0; ii < nn; ii += 1)
funcname = StringFromList(ii, all_funcs, ";")
info = FunctionInfo(funcname)
accept = (NumberByKey("RETURNTYPE", info, ":", ";") == 0x0004)
accept = (NumberByKey("RETURNTYPE", info, ":", ";") == 0x4000)
accept = accept && (cmpstr(StringByKey("THREADSAFE", info, ":", ";"), "yes") == 0)
accept = accept && (NumberByKey("N_PARAMS", info, ":", ";") == 4)
accept = accept && (NumberByKey("N_PARAMS", info, ":", ";") == 2)
accept = accept && (NumberByKey("N_OPT_PARAMS", info, ":", ";") == 0)
if (accept)
// 3 numeric waves and one pass-by-reference string
// one numeric wave and one pass-by-reference string
accept = accept && (NumberByKey("PARAM_0_TYPE", info, ":", ";") == 0x4002)
accept = accept && (NumberByKey("PARAM_1_TYPE", info, ":", ";") == 0x4002)
accept = accept && (NumberByKey("PARAM_2_TYPE", info, ":", ";") == 0x4002)
accept = accept && (NumberByKey("PARAM_3_TYPE", info, ":", ";") == 0x3000)
accept = accept && (NumberByKey("PARAM_1_TYPE", info, ":", ";") == 0x3000)
endif
if (accept)
result = AddListItem(funcname, result, ";")
@ -1067,9 +1072,18 @@ end
/// function prototype for adh5_load_reduced_detector
///
/// derived functions reduce a two-dimensional dataset to a one-dimensional dataset,
/// e.g. by ROI-integration, curve fitting, etc.
// the resulting wave must have the same size as either dimension of the source image.
/// this is a prototype of custom functions that convert (reduce) a two-dimensional detector image
/// into one or more one-dimensional waves.
/// data processing can be tuned with a set of parameters.
///
/// reduction functions have a fixed signature (function arguments) so that the file import functions
/// can call them efficiently on a series of detector images.
/// pearl procedures comes with a number of pre-defined reduction functions
/// but you may as well implement your own functions.
/// if you write your own function, you must use the same declaration and arguments
/// as this function except for the function name.
/// you can do many things in a reduction function,
/// e.g. integration over a region of interest, curve fitting, etc.
///
/// each destination wave is a one-dimensional intensity distribution.
/// the function must redimension each of these waves to one of the image dimensions
@ -1077,32 +1091,38 @@ end
/// this function will also copy the scale information and dimension labels,
/// which is important for the proper scaling of the result.
///
/// the meaning of the data in dest1 and dest2 is up to the particular function,
/// the meaning of the data in the result waves is up to the particular function,
/// e.g. dest1 could hold the mean value and dest2 the one-sigma error,
/// or dest1 could hold the X-profile, and dest2 the Y-profile.
///
/// @param source source wave
/// two-dimensional intensity distribution (image)
/// @param dest1, dest2 destination waves
/// @param param string with optional parameters, shared between calls.
/// @param source source wave.
/// two-dimensional intensity distribution (image).
/// the scales are carried over to the result waves.
///
/// @param param string with optional parameters, shared between calls.
/// this is a pass-by-reference argument,
/// the function may modify the string
/// the function may modify the string.
///
/// @return zero if successful, non-zero if an error occurs.
/// @return a free wave containing references of the result waves.
/// the result waves should as well be free waves.
/// if an error occurred, the reference wave is empty.
///
threadsafe function adh5_default_reduction(source, dest1, dest2, param)
threadsafe function /wave adh5_default_reduction(source, param)
wave source
wave dest1, dest2
string &param
// demo code
// integrate along the dimensions
// integrate along the dimensions
make /n=0 /free dest1, dest2
adh5_setup_profile(source, dest1, 0)
ad_profile_x_w(source, 0, -1, dest1)
adh5_setup_profile(source, dest2, 1)
ad_profile_y_w(source, 0, -1, dest2)
return 0
make /n=2 /free /wave results
results[0] = dest1
results[1] = dest2
return results
end
/// set up a one-dimensional wave for a line profile based on a 2D original wave.
@ -1123,20 +1143,66 @@ end
/// wrapper function for testing reduction functions from the command line.
///
/// Igor does not allow global variables as pass-by-reference parameter for reduction_param.
/// reduction functions cannot be used on the command line because they require
/// a pass-by-reference argument and return free waves.
/// this function expects the reduction parameters in a normal string
/// and copies the results into the current data folder.
/// the prefix of the result names can be specified.
///
function /s adh5_test_reduction_func(source, dest1, dest2, reduction_func, reduction_param)
/// @param source source wave.
/// two-dimensional intensity distribution (image).
/// the scales are carried over to the result waves.
///
/// @param reduction_func name of the reduction function to apply to the source data.
///
/// @param reduction_param string with reduction parameters as required by the specific reduction function.
///
/// @param result_prefix name prefix of result waves.
/// a numeric index is appended to distinguish the results.
/// the index starts at 1. existing waves are overwritten.
///
/// @return a copy of the reduction_param string, possibly modified by the reduction function.
///
function /s adh5_test_reduction_func(source, reduction_func, reduction_param, result_prefix)
wave source
wave dest1
wave dest2
funcref adh5_default_reduction reduction_func
string reduction_param
string result_prefix
reduction_func(source, dest1, dest2, reduction_param)
wave /wave results = reduction_func(source, reduction_param)
adh5_get_result_waves(results, result_prefix, 1)
return reduction_param
end
/// copy waves from wave reference wave into current data folder
///
/// this function copies waves that are referenced in a wave reference wave into the current data folder.
/// the destination waves get new names consisting of a prefix and a numeric index.
/// the index is the array index of the wave in results plus a chosen offset.
///
/// @param results a wave reference wave pointing to result waves from data reduction.
/// the waves can be free or regular waves.
/// results can be a free or regular wave.
///
/// @param result_prefix name prefix of the copied waves.
///
/// @param start_index start index (offset) of the copied waves.
///
threadsafe function adh5_get_result_waves(results, result_prefix, start_index)
wave /wave results
string result_prefix
variable start_index
variable nw = numpnts(results)
variable iw
string sw
for (iw = 0; iw < nw; iw += 1)
sw = result_prefix + num2str(iw + start_index)
duplicate /o results[iw], $sw
endfor
end
/// load a reduced detector dataset from the open HDF5 file.
///
/// the function loads the dataset image by image using the hyperslab option
@ -1266,6 +1332,10 @@ function adh5_load_reduced_detector(fileID, detectorpath, reduction_func, reduct
variable iz, it
string dfname
variable iw, nw
string sw
make /n=0 /free /wave result_waves
izt = 0
for (iz = 0; iz < nz; iz += 1)
for (it = 0; it < nt; it += 1)
@ -1287,11 +1357,11 @@ function adh5_load_reduced_detector(fileID, detectorpath, reduction_func, reduct
ThreadGroupPutDF threadGroupID, :
else
processing_folders[izt] = GetDataFolderDFR()
make /n=1/d profile1, profile2
wave slabdata
variable /g func_result
func_result = reduce_slab_image(slabdata, image, profile1, profile2, reduction_func, func_param)
WaveClear slabdata, image, profile1, profile2
wave /wave reduced_waves = reduce_slab_image(slabdata, image, reduction_func, func_param)
variable /g func_result = numpnts(reduced_waves)
adh5_get_result_waves(reduced_waves, "redw_", 0)
WaveClear slabdata, image, reduced_waves
setdatafolder ::
endif
@ -1343,26 +1413,32 @@ function adh5_load_reduced_detector(fileID, detectorpath, reduction_func, reduct
nvar rr = dfr:r_index
nvar ss = dfr:s_index
nvar func_result = dfr:func_result
wave profile1 = dfr:profile1
wave profile2 = dfr:profile2
if (func_result == 0)
if (izt == 0)
make /n=(dimsize(profile1, 0), nz, nt)/d/o data1
make /n=(dimsize(profile2, 0), nz, nt)/d/o data2
setdimlabel 0, -1, $getdimlabel(profile1, 0, -1), data1
setdimlabel 0, -1, $getdimlabel(profile2, 0, -1), data2
setscale /p x dimoffset(profile1, 0), dimdelta(profile1, 0), waveunits(profile1, 0), data1
setscale /p x dimoffset(profile2, 0), dimdelta(profile2, 0), waveunits(profile2, 0), data2
setscale d 0, 0, waveunits(profile1, -1), data1
setscale d 0, 0, waveunits(profile2, -1), data2
endif
data1[][rr][ss] = profile1[p]
data2[][rr][ss] = profile2[p]
else
if (func_result < 1)
result = -3 // dimension reduction error
break
endif
if (numpnts(result_waves) == 0)
redimension /n=(func_result) result_waves
for (iw = 0; iw < func_result; iw += 1)
sw = "redw_" + num2str(iw)
wave profile = dfr:$sw
sw = "ReducedData" + num2str(iw+1)
make /n=(dimsize(profile, 0), nz, nt) /d /o $sw
wave data = $sw
setdimlabel 0, -1, $getdimlabel(profile, 0, -1), data
setscale /p x dimoffset(profile, 0), dimdelta(profile, 0), waveunits(profile, 0), data
setscale d 0, 0, waveunits(profile, -1), data
result_waves[iw] = data
endfor
endif
for (iw = 0; iw < func_result; iw += 1)
sw = "redw_" + num2str(iw)
wave profile = dfr:$sw
wave data = result_waves[iw]
data[][rr][ss] = profile[p]
endfor
endfor
if (nthreads > 0)
@ -1377,20 +1453,19 @@ function adh5_load_reduced_detector(fileID, detectorpath, reduction_func, reduct
endif
if (result == 0)
if (nz == 1)
redimension /n=(dimsize(data1,0)) data1
redimension /n=(dimsize(data2,0)) data2
elseif (nt == 1)
redimension /n=(dimsize(data1,0),nz) data1
redimension /n=(dimsize(data2,0),nz) data2
setdimlabel 1, -1, AD_DimN, data1
setdimlabel 1, -1, AD_DimN, data2
else
setdimlabel 1, -1, AD_DimN, data1
setdimlabel 1, -1, AD_DimN, data2
setdimlabel 2, -1, AD_DimX, data1
setdimlabel 2, -1, AD_DimX, data2
endif
nw = numpnts(result_waves)
for (iw = 0; iw < nw; iw += 1)
wave data = result_waves[iw]
if (nz == 1)
redimension /n=(dimsize(data, 0)) data
elseif (nt == 1)
redimension /n=(dimsize(data, 0),nz) data
setdimlabel 1, -1, AD_DimN, data
else
setdimlabel 1, -1, AD_DimN, data
setdimlabel 2, -1, AD_DimX, data
endif
endfor
endif
if (progress)
kill_progress_panel()
@ -1426,14 +1501,14 @@ threadsafe static function reduce_slab_worker(reduction_func)
// do the work
newdatafolder /s outDF
make /n=1/d profile1, profile2
variable /g r_index = rr
variable /g s_index = ss
variable /g func_result
func_result = reduce_slab_image(slabdata, image, profile1, profile2, reduction_func, func_param)
wave /wave reduced_waves = reduce_slab_image(slabdata, image, reduction_func, func_param)
variable /g func_result = numpnts(reduced_waves)
// send output to queue and clean up
WaveClear slabdata, image, profile1, profile2
adh5_get_result_waves(reduced_waves, "redw_", 0)
WaveClear slabdata, image, reduced_waves
ThreadGroupPutDF 0, :
KillDataFolder dfr
while (1)
@ -1441,11 +1516,9 @@ threadsafe static function reduce_slab_worker(reduction_func)
return 0
end
threadsafe static function reduce_slab_image(slabdata, image, profile1, profile2, reduction_func, reduction_param)
threadsafe static function /wave reduce_slab_image(slabdata, image, reduction_func, reduction_param)
wave slabdata
wave image
wave profile1
wave profile2
funcref adh5_default_reduction reduction_func
string reduction_param
@ -1461,7 +1534,7 @@ threadsafe static function reduce_slab_image(slabdata, image, profile1, profile2
break
endswitch
return reduction_func(image, profile1, profile2, reduction_param)
return reduction_func(image, reduction_param)
end
/// load an NDAttributes group from an open HDF5 file into the current data folder.
@ -1667,12 +1740,12 @@ function adh5_scale_scienta(data)
case 1: // Angular45
ALow = -45/2
AHigh = +45/2
AUnit = "°"
AUnit = "<EFBFBD>"
break
case 2: // Angular60
ALow = -60/2
AHigh = +60/2
AUnit = "°"
AUnit = "<EFBFBD>"
break
endswitch
endif