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,12 +1,11 @@
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma IgorVersion = 6.36
#pragma ModuleName = PearlPShellImport
#pragma version = 1.03
#include <HDF5 Browser>
#include "pearl-gui-tools"
#include "pearl-area-import"
// 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.
@ -47,6 +46,14 @@
/// - psh5_list_scan_datasets()
/// - psh5_load_scan_meta()
/// - psh5_load_scan_attrs()
///
/// @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 PearlPShellImport
/// @brief import data from PShell
@ -1728,6 +1735,12 @@ end
/// @arg `lo[%%scan]` scan dimension.
/// @arg `lo[%%data]` data dimension.
///
/// if the data dimension labels and units are at their defaults ("value" and "arb.", respectively),
/// the function tries to read them from the existing wave note ("AxisLabelD" and "AxisUnitD"),
/// or based on the wave name if the name is one of the known measurement variables:
/// "ScientaImage", "ImageAngleDistribution", "ScientaAngleDistribution", "ScientaSpectrum", "ImageEnergyDistribution", "ScientaEnergyDistribution",
/// "SampleCurrent", "RefCurrent", "AuxCurrent", "MachineCurrent".
///
/// @param data data wave to be scaled.
/// dimension labels (index -1) must be set to match the limit waves.
///
@ -1754,28 +1767,45 @@ function ps_scale_dataset_2(data, ax, lo, hi, un)
wave hi
wave /t un
string snote = note(data)
string sdim
sdim = GetDimLabel(data, 0, -1)
if (strlen(sdim))
setscale /i x lo[%$sdim], hi[%$sdim], un[%$sdim], data
Note data, "AxisLabelX=" + ax[%$sdim]
snote = ReplaceStringByKey("AxisLabelX", snote, ax[%$sdim], "=", "\r")
endif
sdim = GetDimLabel(data, 1, -1)
if (strlen(sdim))
setscale /i y lo[%$sdim], hi[%$sdim], un[%$sdim], data
Note data, "AxisLabelY=" + ax[%$sdim]
snote = ReplaceStringByKey("AxisLabelY", snote, ax[%$sdim], "=", "\r")
endif
sdim = GetDimLabel(data, 2, -1)
if (strlen(sdim))
setscale /i z lo[%$sdim], hi[%$sdim], un[%$sdim], data
Note data, "AxisLabelZ=" + ax[%$sdim]
snote = ReplaceStringByKey("AxisLabelZ", snote, ax[%$sdim], "=", "\r")
endif
string data_unit = un[%$kDataDimLabel]
string data_label = ax[%$kDataDimLabel]
if (cmpstr(data_unit, "arb.") == 0)
string s
variable def = (cmpstr(data_unit, "arb.") == 0) && (cmpstr(data_label, "value") == 0)
if (def)
s = StringByKey("AxisLabelD", snote, "=", "\r")
if (strlen(s) > 0)
data_label = s
def = 0
endif
s = StringByKey("AxisUnitD", snote, "=", "\r")
if (strlen(s) > 0)
data_unit = s
def = 0
endif
endif
if (def)
strswitch(NameOfWave(data))
case "ScientaImage":
case "ImageAngleDistribution":
@ -1786,28 +1816,35 @@ function ps_scale_dataset_2(data, ax, lo, hi, un)
data *= kDetectorSensitivity
data_unit = "counts"
data_label = "intensity"
def = 0
break
case "SampleCurrent":
case "RefCurrent":
case "AuxCurrent":
data_unit = "A"
data_label = "current"
def = 0
break
case "MachineCurrent":
data_unit = "mA"
data_label = "current"
def = 0
break
endswitch
endif
setscale d 0, 0, data_unit, data
Note data, "AxisLabelD=" + data_label
Note data, "Dataset=" + NameOfWave(data)
snote = ReplaceStringByKey("AxisLabelD", snote, data_label, "=", "\r")
snote = ReplaceStringByKey("AxisUnitD", snote, data_unit, "=", "\r")
snote = ReplaceStringByKey("Dataset", snote, NameOfWave(data), "=", "\r")
note /k data, snote
end
/// load and reduce the ScientaImage dataset of the first scan of a PShell data file.
///
/// the resulting dataset is reduced in one image dimension by a user-defined reduction function,
/// e.g. by region-of-interest integration, curve fitting, etc.
/// cf. @ref adh5_default_reduction for further details.
///
/// the function loads the dataset image by image using the hyperslab option
/// and applies a custom reduction function to each image.
@ -1817,14 +1854,21 @@ end
/// if the data is from the electron analyser driver and some special attributes are included,
/// the function will set the scales of the image dimensions.
///
/// by default, the reduction function is called in separate threads to reduce the total loading time.
/// (see the global variable psh5_perf_secs which reports the total run time of the function.)
/// the effect varies depending on the balance between file loading (image size)
/// and data processing (complexity of the reduction function).
/// for debugging the reduction function, multi-threading can be disabled.
///
/// @param ANickName destination folder name (top level under root).
///
/// @param APathName igor symbolic path name. can be empty if the path is specified in FileName or a dialog box should be displayed.
///
/// @param AFileName if empty a dialog box shows up.
///
/// @param reduction_func custom reduction function
/// (any user-defined function which has the same parameters as adh5_default_reduction())
/// @param reduction_func custom data reduction function.
/// this can be any user-defined function which has the same parameters as @ref adh5_default_reduction.
/// some reduction functions are predefined in the @ref PearlScientaPreprocess module.
///
/// @param reduction_param parameter string for the reduction function.
///
@ -1832,8 +1876,13 @@ end
/// @arg 1 (default) show progress window
/// @arg 0 do not show progress window
///
/// @return semicolon-separated list of the loaded waves,
/// `ReducedData1` and `ReducedData2` if successful.
/// @param nthreads
/// @arg -1 (default) use as many threads as there are processor cores (in addition to main thread).
/// @arg 0 use main thread only (for debugging and profiling).
/// @arg >= 1 use a fixed number of (additional) threads.
///
/// @return semicolon-separated list of the loaded dataset `ReducedData1`, `ReducedData2`, etc. if successful.
/// auxiliary waves, scan positions, attributes are loaded but not listed in the string.
/// empty string if an error occurred.
/// error messages are printed to the history.
///
@ -1841,20 +1890,22 @@ end
///
/// @return global string s_scanpaths in new data folder contains a list of scan groups inside the file.
///
/// @todo load scan positions.
///
function /s psh5_load_reduced(ANickName, APathName, AFileName, reduction_func, reduction_param, [progress])
function /s psh5_load_reduced(ANickName, APathName, AFileName, reduction_func, reduction_param, [progress, nthreads])
string ANickName
string APathName
string AFileName
funcref adh5_default_reduction reduction_func
string reduction_param
variable progress
variable nthreads
if (ParamIsDefault(progress))
progress = 1
endif
if (ParamIsDefault(nthreads))
nthreads = -1
endif
dfref saveDF = GetDataFolderDFR()
// performance monitoring
@ -1904,7 +1955,7 @@ function /s psh5_load_reduced(ANickName, APathName, AFileName, reduction_func, r
setdatafolder dataDF
string datasets = psh5_list_scan_datasets(fileID, scanpath, include_regions=1)
string dataset = select_dataset(datasets, "ScientaImage")
wavenames = psh5_load_dataset_reduced(fileID, scanpath, dataset, reduction_func, reduction_param, progress=progress)
wavenames = psh5_load_dataset_reduced(fileID, scanpath, dataset, reduction_func, reduction_param, progress=progress, nthreads=nthreads)
psh5_close_file(fileID)
endif
@ -1922,7 +1973,7 @@ end
///
/// the function loads the dataset image by image using the hyperslab option
/// and applies a custom reduction function to each image.
/// the results from the reduction function are written to the `ReducedData1` and `ReducedData2` waves.
/// the results from the reduction function are written to the `ReducedData1`, `ReducedData2`, etc. waves.
/// the raw data are discarded.
///
/// by default, the reduction function is called in separate threads to reduce the total loading time.
@ -1946,8 +1997,9 @@ end
/// the name can include the region name as a relative path, e.g. "region1/ScientaImage".
/// in this case, the dataset is loaded into a sub-folder named "region1".
///
/// @param reduction_func custom reduction function
/// (any user-defined function which has the same parameters as adh5_default_reduction()).
/// @param reduction_func custom data reduction function.
/// this can be any user-defined function which has the same parameters as @ref adh5_default_reduction.
/// some reduction functions are predefined in the @ref PearlScientaPreprocess module.
///
/// @param reduction_param parameter string for the reduction function.
///
@ -1957,11 +2009,11 @@ end
///
/// @param nthreads
/// @arg -1 (default) use as many threads as there are processor cores (in addition to main thread).
/// @arg 0 use main thread only (e.g. for debugging the reduction function).
/// @arg 0 use main thread only (for debugging and profiling).
/// @arg >= 1 use a fixed number of (additional) threads.
///
/// @return semicolon-separated list of the loaded waves,
/// `ReducedData1` and `ReducedData2` if successful.
/// @return semicolon-separated list of the loaded dataset `ReducedData1`, `ReducedData2`, etc. if successful.
/// auxiliary waves, scan positions, attributes are loaded but not listed in the string.
/// empty string if an error occurred.
/// error messages are printed to the history.
///
@ -2079,6 +2131,10 @@ function /s psh5_load_dataset_reduced(fileID, scanpath, datasetname, reduction_f
variable iz, it, izt
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)
@ -2102,9 +2158,10 @@ function /s psh5_load_dataset_reduced(fileID, scanpath, datasetname, reduction_f
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
@ -2159,31 +2216,36 @@ function /s psh5_load_dataset_reduced(fileID, scanpath, datasetname, reduction_f
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 ReducedData1
make /n=(dimsize(profile2, 0), nz, nt) /d /o ReducedData2
setdimlabel 0, -1, $getdimlabel(profile1, 0, -1), ReducedData1
setdimlabel 0, -1, $getdimlabel(profile2, 0, -1), ReducedData2
setdimlabel 1, -1, $kScanDimLabel, ReducedData1
setdimlabel 1, -1, $kScanDimLabel, ReducedData2
ps_scale_dataset(ReducedData1)
ps_scale_dataset(ReducedData2)
setscale /p x dimoffset(profile1, 0), dimdelta(profile1, 0), waveunits(profile1, 0), ReducedData1
setscale /p x dimoffset(profile2, 0), dimdelta(profile2, 0), waveunits(profile2, 0), ReducedData2
setscale d 0, 0, waveunits(profile1, -1), ReducedData1
setscale d 0, 0, waveunits(profile2, -1), ReducedData2
endif
ReducedData1[][rr][ss] = profile1[p]
ReducedData2[][rr][ss] = profile2[p]
else
if (func_result < 1)
print "error during data reduction."
result = -3
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
setdimlabel 1, -1, $kScanDimLabel, data
note data, note(profile)
ps_scale_dataset(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)
@ -2199,14 +2261,17 @@ function /s psh5_load_dataset_reduced(fileID, scanpath, datasetname, reduction_f
endif
if (result == 0)
if (nz == 1)
redimension /n=(-1, 0, 0) ReducedData1
redimension /n=(-1, 0, 0) ReducedData2
elseif (nt == 1)
redimension /n=(-1, nz, 0) ReducedData1
redimension /n=(-1, nz, 0) ReducedData2
endif
wavenames = "ReducedData1;ReducedData2;"
nw = numpnts(result_waves)
wavenames = ""
for (iw = 0; iw < nw; iw += 1)
wave data = result_waves[iw]
if (nz == 1)
redimension /n=(-1, 0, 0) data
elseif (nt == 1)
redimension /n=(-1, nz, 0) data
endif
wavenames += nameofwave(data) + ";"
endfor
endif
if (progress)
kill_progress_panel()
@ -2240,14 +2305,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)
@ -2255,18 +2320,16 @@ 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
// the multiplication by detector sensitivity assumes that we are loading a ScientaImage.
image = slabdata[q][p][0][0] * kDetectorSensitivity
return reduction_func(image, profile1, profile2, reduction_param)
return reduction_func(image, reduction_param)
end
/// load descriptive info from a PShell data file.