update pshell explorer and data import, misc. improvements

FEATURES

- pshell: convert scienta data to true counts
- pre-process: add gauss2_reduction data reduction function
- anglescan: add set_contrast and normalize_strip_phi functions
- explorer: show info about multi-region scans
- documentation: add detailed instructions for angle-scan processing

BUGFIXES

- explorer: fix attributes notebook
- pshell: fix progress bar
- elog: increase the number of accepted attachments
This commit is contained in:
2017-09-21 12:36:30 +02:00
parent 0a436db00b
commit cf1399e59c
137 changed files with 1824 additions and 853 deletions

View File

@ -1,12 +1,12 @@
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma version = 1.7
#pragma version = 1.8
#pragma IgorVersion = 6.2
#pragma ModuleName = PearlAnglescanProcess
#include "pearl-vector-operations"
#include "pearl-polar-coordinates"
#include <New Polar Graphs>
// copyright (c) 2013-16 Paul Scherrer Institut
// copyright (c) 2013-17 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.
@ -64,13 +64,13 @@
///
/// @author matthias muntwiler, matthias.muntwiler@psi.ch
///
/// @copyright 2013-16 Paul Scherrer Institut @n
/// @copyright 2013-17 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
///
/// @version 1.6
/// @version 1.8
/// canonical orientation of spherical coordinate system.
///
@ -241,6 +241,84 @@ function normalize_strip_x(strip, [smooth_method, smooth_factor, check])
endif
end
/// divide the strip by a sine function in phi (wobble correction).
///
/// the sine function is a curve fit to the intensity integrated over detector angle
/// with a period of 360&degree;.
///
/// this normalization may be useful if the intensity varies with a 360&degree; periodicity in the azimuthal angle,
/// e.g. due to misalignment of the surface normal and the azimuthal rotation axis of the manipulator (wobble).
/// note, however, that this function does not correct other effects of wobble such as angle shifts.
///
/// the strip is normalized in place, previous data is overwritten.
///
/// @param[in,out] strip 2D data, X-axis = analyser angle, Y-axis = arbitrary manipulator scan
/// @param[in] theta polar manipulator angle.
/// @param[in] phi azimuthal manipulator angle, arbitrary offset.
/// @param[in] theta_offset theta value corresponding to normal emission (default 0).
/// @param[in] theta_range maximum (offset corrected) theta to consider in the sine fit (default 10).
///
/// @param check enable output of intermediate results
/// @arg 0 (default) don't create additional waves
/// @arg 1 create check waves in the current folder
/// @arg 2 calculate check waves only, do not modify strip
///
/// @return if check waves are enabled, the following waves are created (overwritten if existing):
/// @arg check_dist average theta distribution
/// @arg check_smoo smoothed distribution used to normalize the strip
///
function normalize_strip_phi(strip, theta, phi, [theta_offset, theta_range, check])
wave strip
wave theta
wave phi
variable theta_offset
variable theta_range
variable check
if (ParamIsDefault(check))
check = 0
endif
if (ParamIsDefault(theta_offset))
theta_offset = 0
endif
if (ParamIsDefault(theta_range))
theta_offset = 10
endif
// average over analyser angles
wave dist = ad_profile_y(strip, -inf, inf, "")
// smooth distribution function
duplicate /free dist, dist_smoo
duplicate /free theta, theta_int
theta_int = theta - theta_offset
duplicate /free phi, phi_int
setscale /p x phi_int[0], phi_int[1] - phi_int[0], waveunits(phi, -1), dist, dist_smoo
extract /free /indx dist, red_idx, theta_int < theta_range
duplicate /free red_idx, red_dist, red_phi
red_dist = dist[red_idx]
red_phi = phi_int[red_idx]
variable wavg = mean(red_dist)
make /n=4 /d /free coef
coef[0] = {wavg, wavg/100, pi/180, 0}
CurveFit /q /h="0010" /g /w=2 sin, kwcWave=coef, red_dist /x=red_phi
dist_smoo = coef[0] + coef[1] * sin(coef[2] * phi_int[p] + coef[3])
// divide
if (check != 2)
strip = strip / dist_smoo[q] * coef[0]
endif
// check
if (check)
duplicate /o dist, check_dist
duplicate /o dist_smoo, check_smoo
setscale /p x dimoffset(dist,0), dimdelta(dist,0), waveunits(dist,0), check_dist, check_smoo
endif
end
/// divide the strip by the average polar distribution.
///
/// this is a simple way to remove the polar angle dependence.
@ -2843,3 +2921,112 @@ function /wave hemi_azi_cut(nickname, pol)
return $""
endif
end
static function check_contrast(values, pcmin, pcmax, vmin, vmax)
wave values
variable pcmin
variable pcmax
variable &vmin
variable &vmax
dfref save_df = GetDataFolderDFR()
dfref dfr = NewFreeDataFolder()
setdatafolder dfr
StatsQuantiles /inan /iw /q /z values
wave index = w_quantilesindex
variable imin = round(numpnts(index) * pcmin / 100)
variable imax = round(numpnts(index) * (100 - pcmax) / 100)
vmin = values[index[imin]]
vmax = values[index[imax]]
KillDataFolder dfr
setdatafolder save_df
end
/// set the pseudocolor contrast by percentile.
///
/// set the minimum and maximum values of the pseudocolor scale
/// such that a specified percentile of the distribution lies outside the limits.
///
/// the new contrast is applied to traces and images of the selected graph
/// that have pseudocolor tables.
///
/// the function is not specific to angle scans.
/// it can be used for any pseudocolor trace or image plots except contour plots.
///
/// @param pcmin percentile below the minimum color (0-100).
/// @param pcmax percentile above the maximum color (0-100).
/// @param graphname name of graph. default: top graph.
/// @param colortable name of new colortable. default: keep current table.
///
function set_contrast(pcmin, pcmax, [graphname, colortable])
variable pcmin
variable pcmax
string graphname
string colortable
if (ParamIsDefault(graphname))
graphname = ""
endif
if (ParamIsDefault(colortable))
colortable = ""
endif
dfref save_df = GetDataFolderDFR()
string objname
string info
string wname
string ctab
variable rev
variable n
variable i
variable vmin
variable vmax
string traces = TraceNameList(graphname, ";", 1+4)
n = ItemsInList(traces, ";")
for (i = 0; i < n; i += 1)
objname = StringFromList(i, traces, ";")
info = TraceInfo(graphname, objname, 0)
if (strlen(info) > 0)
info = StringByKey("RECREATION", info, ":", ";")
info = StringByKey("zColor(x)", info, "=", ";")
if (strlen(info) > 2)
info = info[1,strlen(info)-2]
wname = StringFromList(0, info, ",")
wave w = $wname
ctab = StringFromList(3, info, ",")
rev = str2num("0" + StringFromList(4, info, ","))
if (strlen(colortable) > 0)
ctab = colortable
endif
check_contrast(w, pcmin, pcmax, vmin, vmax)
ModifyGraph /w=$graphname zColor($objname)={w, vmin, vmax, $ctab, rev}
endif
endif
endfor
string images = ImageNameList(graphname, ";")
n = ItemsInList(images, ";")
for (i = 0; i < n; i += 1)
objname = StringFromList(i, images, ";")
wave w = ImageNameToWaveRef(graphname, objname)
info = ImageInfo(graphname, objname, 0)
if (strlen(info) > 0)
info = StringByKey("RECREATION", info, ":", ";")
info = StringByKey("ctab", info, "=", ";")
if (strlen(info) > 2)
info = info[1,strlen(info)-2]
ctab = StringFromList(2, info, ",")
rev = str2num("0" + StringFromList(3, info, ","))
if (strlen(colortable) > 0)
ctab = colortable
endif
check_contrast(w, pcmin, pcmax, vmin, vmax)
ModifyImage /w=$graphname $objname ctab={vmin, vmax, $ctab, rev}
endif
endif
endfor
setdatafolder save_df
end

View File

@ -1495,6 +1495,7 @@ function ad_profiles_set_slice(brick, dim, value)
return 0
end
/// set slice coordinate (slider procedure).
static function slp_slice_position(sa) : SliderControl
STRUCT WMSliderAction &sa
@ -1508,12 +1509,16 @@ static function slp_slice_position(sa) : SliderControl
string control_datafolder = GetUserData(sa.win, "", "control_datafolder")
setdatafolder control_datafolder
string brick_path = GetUserData(sa.win, "", "brick_path")
wave brick = $brick_path
string axis = StringFromList(1, sa.ctrlName, "_")
variable dim = char2num(axis[0]) - char2num("x")
ad_gizmo_set_plane(brick, dim, sa.curval)
ad_profiles_set_slice(brick, dim, sa.curval)
wave /z brick = $brick_path
if (WaveExists(brick))
ad_gizmo_set_plane(brick, dim, sa.curval)
ad_profiles_set_slice(brick, dim, sa.curval)
else
Abort "can't find original wave " + brick_path
endif
endif
break
endswitch
@ -1535,12 +1540,16 @@ static function svp_slice_position(sva) : SetVariableControl
string control_datafolder = GetUserData(sva.win, "", "control_datafolder")
setdatafolder control_datafolder
string brick_path = GetUserData(sva.win, "", "brick_path")
wave brick = $brick_path
string axis = StringFromList(1, sva.ctrlName, "_")
variable dim = char2num(axis[0]) - char2num("x")
ad_gizmo_set_plane(brick, dim, sva.dval)
ad_profiles_set_slice(brick, dim, sva.dval)
wave /z brick = $brick_path
if (WaveExists(brick))
ad_gizmo_set_plane(brick, dim, sva.dval)
ad_profiles_set_slice(brick, dim, sva.dval)
else
Abort "can't find original wave " + brick_path
endif
break
case -1: // control being killed
break
@ -1561,29 +1570,33 @@ static function bp_move_slice(ba) : ButtonControl
string control_datafolder = GetUserData(ba.win, "", "control_datafolder")
setdatafolder control_datafolder
string brick_path = GetUserData(ba.win, "", "brick_path")
wave brick = $brick_path
string axis = StringFromList(1, ba.ctrlName, "_")
string cmd = StringFromList(2, ba.ctrlName, "_")
variable dim = char2num(axis[0]) - char2num("x")
string posvariable = getdatafolder(1) + axis[0] + "_slice_pos"
nvar pos = $(posvariable)
strswitch (cmd)
case "forward":
ad_slicer_start_bg(brick, dim, posvariable, dimdelta(brick, dim))
break
case "back":
ad_slicer_start_bg(brick, dim, posvariable, -dimdelta(brick, dim))
break
case "center":
ad_slicer_stop_bg(posvariable)
bp_move_slice_center(brick, dim, posvariable)
break
case "stop":
ad_slicer_stop_bg(posvariable)
break
endswitch
wave /z brick = $brick_path
if (WaveExists(brick))
strswitch (cmd)
case "forward":
ad_slicer_start_bg(brick, dim, posvariable, dimdelta(brick, dim))
break
case "back":
ad_slicer_start_bg(brick, dim, posvariable, -dimdelta(brick, dim))
break
case "center":
ad_slicer_stop_bg(posvariable)
bp_move_slice_center(brick, dim, posvariable)
break
case "stop":
ad_slicer_stop_bg(posvariable)
break
endswitch
else
ad_slicer_stop_bg(posvariable)
Abort "can't find original wave " + brick_path
endif
break
case -1: // control being killed
break

View File

@ -308,31 +308,44 @@ static function /wave preview_pshell_file(filename)
string filename
dfref saveDF = GetDataFolderDFR()
setdatafolder $package_path
dfref previewDF = GetDataFolderDFR()
svar s_preview_file
svar s_preview_source
psh5_load_preview("preview_image", "pearl_explorer_filepath", filename)
s_preview_file = filename
s_preview_source = ""
wave /z preview_image
svar /z s_file_info
if (! svar_exists(s_file_info))
string /g s_file_info
endif
dfref tempDF = NewFreeDataFolder()
setdatafolder tempDF
string dataname
dataname = psh5_load_preview("pearl_explorer_filepath", filename)
s_preview_file = filename
s_preview_source = ""
wave /z data = $dataname
if (waveexists(data))
duplicate /o data, previewDF:preview_image
else
print "no data found in file " + filename
endif
if (strlen(s_preview_file) > 0)
s_file_info = psh5_load_info("pearl_explorer_filepath", filename)
else
s_file_info = ""
endif
if (DataFolderExists("attr"))
setdatafolder attr
preview_attributes(GetDataFolderDFR())
setdatafolder ::
dfref attrDF = tempDF:attr
if (DataFolderRefStatus(attrDF))
preview_attributes(attrDF)
endif
setdatafolder saveDF
wave /z /sdfr=previewDF preview_image
return preview_image
end
@ -654,7 +667,6 @@ static function preview_attributes(attr_folder, [dest_folder, attr_filter, inclu
attr_names = ""
attr_values = ""
string /g s_attr_folder = GetDataFolder(1, attr_folder)
setdatafolder attr_folder
wave /t /z IN
wave /t /z ID

View File

@ -1,10 +1,10 @@
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma version = 1.40
#pragma version = 1.41
#pragma IgorVersion = 6.2
#pragma ModuleName = PearlElog
// author: matthias.muntwiler@psi.ch
// Copyright (c) 2013-16 Paul Scherrer Institut
// Copyright (c) 2013-17 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.
@ -71,7 +71,7 @@
///
/// @author matthias muntwiler, matthias.muntwiler@psi.ch
///
/// @copyright 2013-16 Paul Scherrer Institut @n
/// @copyright 2013-17 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
@ -247,7 +247,7 @@ static function init_package([clean])
variable /g port = 0 // 0 = unspecified (default)
variable /g ssl = 0 // 0 = plain text (incl. passwords), 1 = secure connection
string /g subdir = ""
variable /g loglevel = 3
variable /g loglevel = 4
setdatafolder savedf
return 0
@ -748,23 +748,27 @@ function elog_create_entry(logbook, attributes, message, [encoding, graphs, repl
string messagefile = create_message_file(message)
if (strlen(messagefile) > 0)
cmd += " -m \"" + messagefile + "\""
cmd += " > \"" + get_log_path() + "\""
cmd += " > elog.log"
if (loglevel >= 5)
print cmd
endif
string cmd_file_path = create_cmd_file(cmd)
ExecuteScriptText cmd_file_path
variable id = parse_result()
if (id > 0)
msg_id = id
if (loglevel >= 4)
print "ELOG: sent message " + num2str(id)
if (strlen(cmd_file_path) > 0)
ExecuteScriptText cmd_file_path
variable id = parse_result()
if (id > 0)
msg_id = id
if (loglevel >= 4)
print "ELOG: sent message " + num2str(id)
endif
else
if (loglevel >= 2)
print "ELOG: sending message failed."
endif
result = -4
endif
else
if (loglevel >= 2)
print "ELOG: sending message failed."
endif
result = -4
result = -2
endif
else
if (loglevel >= 2)
@ -814,20 +818,24 @@ function elog_add_attachment(logbook, id, graphs)
if (result == 0)
cmd += " " + cmd_graphs
cmd += " > \"" + get_log_path() + "\""
cmd += " > elog.log"
string cmd_file_path = create_cmd_file(cmd)
ExecuteScriptText cmd_file_path
id = parse_result()
if (id > 0)
msg_id = id
if (loglevel >= 4)
print "ELOG: attached graphs to message " + num2str(id)
if (strlen(cmd_file_path) > 0)
ExecuteScriptText cmd_file_path
id = parse_result()
if (id > 0)
msg_id = id
if (loglevel >= 4)
print "ELOG: attached graphs to message " + num2str(id)
endif
else
if (loglevel >= 2)
print "ELOG: failed to attach graphs."
endif
result = -4 // error: elog returned error
endif
else
if (loglevel >= 2)
print "ELOG: failed to attach graphs."
endif
result = -4 // error: elog returned error
result = -2 // error: invalid command line
endif
endif
@ -923,7 +931,8 @@ end
///
/// prepares the attachment files from Igor graph windows
/// and returns the arguments to the elog command to attach the files.
/// the result string does not include leading or trailing space
/// file names are returned without path.
/// the result string does not include leading or trailing space.
///
/// @param graphs names of graph windows to be added as attachments, semicolon separated
///
@ -954,25 +963,63 @@ static function /s get_timestamp(sep)
return dat + sep + tim
end
/// save the message to a temporary text file
///
/// the file is saved to the Temporary directory returned by igor's SpecialDirPath function
/// under the file name "elog_temp_message.txt".
/// the function returns the name of the file (excluding path!)
///
/// @note percent characters (%) cannot be passed to elog.
/// they are removed silently from the message.
///
/// @param message text message to save to the file.
/// @return (string) name of the created file.
/// empty string if unsuccessful.
///
/// @version 1.41 the return value has changed from full path to file name only
/// due to the limited length of the command line (1024 bytes).
///
static function /s create_message_file(message)
string message
message = ReplaceString("%", message, "")
string path = SpecialDirPath("Temporary", 0, 1, 0)
variable len = strlen(path)
string filename
if (numtype(len) == 0)
path += "elog_temp_message.txt"
filename = "elog_temp_message.txt"
path += filename
variable f1
Open f1 as path
fprintf f1, message
Close f1
else
path = ""
filename = ""
endif
return path
return filename
end
/// save a graph to a temporary graphics file
///
/// the file is saved to the Temporary directory returned by igor's SpecialDirPath function.
/// the file name contains a time stamp and the specified file index to make it unique.
/// the function returns the name of the file (excluding path!)
///
/// the full path is added to the temp_graph_files global list.
/// a hook function will delete the files listed there when igor quits.
///
/// @param graphname object name of the graph to save.
/// @param fileindex incrememtal index of the file within one submission.
/// the file name is made unique by a time stamp and this file index.
/// submissions within the same second must have a unique file index.
/// @return (string) name of the created file.
/// empty string if unsuccessful.
///
/// @version 1.41 the return value has changed from full path to file name only
/// due to the limited length of the command line (1024 bytes).
///
static function /s create_graph_file(graphname, fileindex)
string graphname
variable fileindex
@ -983,37 +1030,66 @@ static function /s create_graph_file(graphname, fileindex)
string path = SpecialDirPath("Temporary", 0, 1, 0)
string ts = get_timestamp("_")
variable len = strlen(path)
string filename
if (numtype(len) == 0)
path += "elog_" + ts + "_" + num2str(fileindex) + ".png"
filename = "elog_" + ts + "_" + num2str(fileindex) + ".png"
path += filename
SavePICT /B=72 /E=-5 /M /O /W=(0,0,8,6) /WIN=$graphname /Z as path
if (v_flag == 0)
temp_graph_files = AddListItem(path, temp_graph_files, ";", inf)
else
path = ""
filename = ""
endif
else
path = ""
filename = ""
endif
return path
return filename
end
/// write the command line to a file.
///
/// the command script changes the working directory to the Temporary directory.
/// it also deletes a previous elog.log file.
///
/// @note somewhere the command line (even inside command files) is limited to 1024 bytes.
/// for this reason all files should now be in the Temporary directory assigned by igor.
///
static function /s create_cmd_file(cmd)
string cmd
string path = SpecialDirPath("Temporary", 0, 1, 0)
variable len = strlen(path)
dfref df_general = get_elog_df("", kdfPersistent)
nvar /sdfr=df_general loglevel
if (strlen(cmd) >= 1024)
if (loglevel >= 2)
print "ELOG: command line too long (add fewer attachments)."
endif
return ""
endif
string work_path = SpecialDirPath("Temporary", 0, 1, 0)
variable len = strlen(work_path)
if (numtype(len) == 0)
path += "elog_temp_cmd.bat"
string cmdx
string cmd_path = work_path + "elog_temp_cmd.bat"
variable f1
Open f1 as path
Open f1 as cmd_path
cmdx = "c:\r\n"
fprintf f1, cmdx
cmdx = "cd \"" + work_path + "\"\r\n"
fprintf f1, cmdx
cmdx = "del elog.log"
fprintf f1, cmdx + "\r\n"
fprintf f1, cmd
Close f1
else
path = ""
cmd_path = ""
endif
return path
return cmd_path
end
static function /s get_log_path()

View File

@ -75,6 +75,9 @@ strconstant kScientaScalingDatasets = "LensMode;ScientaChannelBegin;ScientaChann
/// List of datasets that should be transposed upon loading
strconstant kTransposedDatasets = "ScientaImage;"
/// multiply scienta detector intensity by this value to get actual counts.
constant kDetectorSensitivity = 4
/// open a HDF5 file created by the PShell data acquisition program and prepare the data folder.
///
/// the function opens a specified or interactively selected HDF5 file,
@ -216,11 +219,6 @@ end
/// the data wave is loaded into the current data folder.
/// attributes are loaded into the attr subfolder. existing waves in attr are deleted.
///
/// @warning EXPERIMENTAL
/// this function uses the root:pearl_area:preview data folder. existing data there may be deleted!
///
/// @param ANickName destination wave name. the wave is created in the current data folder.
///
/// @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
@ -242,8 +240,7 @@ end
///
/// @return name of loaded preview wave.
///
function /s psh5_load_preview(ANickName, APathName, AFileName, [load_data, load_attr, pref_scans, pref_datasets])
string ANickName
function /s psh5_load_preview(APathName, AFileName, [load_data, load_attr, pref_scans, pref_datasets])
string APathName
string AFileName
variable load_data
@ -265,9 +262,6 @@ function /s psh5_load_preview(ANickName, APathName, AFileName, [load_data, load_
endif
dfref saveDF = GetDataFolderDFR()
setdatafolder root:
newdatafolder /o/s pearl_area
newdatafolder /o/s preview
variable fileID
string scanpaths = ""
@ -319,16 +313,6 @@ function /s psh5_load_preview(ANickName, APathName, AFileName, [load_data, load_
setdatafolder fileDF
dataname = psh5_load_scan_preview(fileID, sg, set_scale=load_attr, pref_datasets=pref_datasets)
wave /z data = $dataname
string destpath = GetDataFolder(1, saveDF) + ANickName
if (waveexists(data))
duplicate /o data, $destpath
wave /z data = $destpath
else
print "no data found in file " + AFileName
endif
else
print "no scans found in file " + AFileName
endif
@ -903,7 +887,6 @@ function /s psh5_load_scan_preview(fileID, scanpath, [set_scale, pref_datasets])
setdatafolder dataDF
newdatafolder /o/s attr
killwaves /a/z
psh5_load_scan_attrs(fileID, scanpath, attr_sets=2)
setdatafolder dataDF
ps_scale_dataset(data)
@ -999,7 +982,7 @@ function /s psh5_load_scan_section(fileID, scanpath, dim, [set_scale, pref_datas
ny = di.dims[idy]
nz = di.dims[idz]
HDF5MakeHyperslabWave(GetDataFolder(1) + "slab", max(di.ndims, 4))
HDF5MakeHyperslabWave("slab", max(di.ndims, 4))
wave slab
slab[][%Start] = 0
slab[][%Stride] = 1
@ -1197,7 +1180,7 @@ function /s psh5_load_dataset_slabs(fileID, datapath, datasetname, [progress])
endif
// load data image by image
HDF5MakeHyperslabWave(GetDataFolder(1) + "slab", max(di.ndims, 4))
HDF5MakeHyperslabWave("slab", max(di.ndims, 4))
wave slab
slab[][%Start] = 0
slab[][%Stride] = 1
@ -1319,7 +1302,7 @@ function /s psh5_load_dataset_slab(fileID, datapath, datasetname, dim2start, dim
wave data = $datawavename
data = 0
HDF5MakeHyperslabWave(GetDataFolder(1) + "slab", max(di.ndims, 4))
HDF5MakeHyperslabWave("slab", max(di.ndims, 4))
wave slab
slab[][%Start] = 0
slab[][%Stride] = 1
@ -1794,6 +1777,16 @@ function ps_scale_dataset_2(data, ax, lo, hi, un)
string data_label = ax[%$kDataDimLabel]
if (cmpstr(data_unit, "arb.") == 0)
strswitch(NameOfWave(data))
case "ScientaImage":
case "ImageAngleDistribution":
case "ScientaAngleDistribution":
case "ScientaSpectrum":
case "ImageEnergyDistribution":
case "ScientaEnergyDistribution":
data *= kDetectorSensitivity
data_unit = "counts"
data_label = "intensity"
break
case "SampleCurrent":
case "RefCurrent":
case "AuxCurrent":
@ -1948,8 +1941,9 @@ end
/// @param scanpath path to scan group in the HDF5 file.
///
/// @param datasetname name of the dataset.
/// this must currently be "ScientaImage", other data is not supported.
/// the name of the loaded wave is a cleaned up version of the dataset name.
/// the name can include the region name as a relative path, e.g. "region1/ScientaSpectrum".
/// 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
@ -2050,7 +2044,7 @@ function /s psh5_load_dataset_reduced(fileID, scanpath, datasetname, reduction_f
nzt = nz * nt
// load data image by image
HDF5MakeHyperslabWave(GetDataFolder(1) + "slab", max(di.ndims, 4))
HDF5MakeHyperslabWave("slab", max(di.ndims, 4))
wave slab
slab[][%Start] = 0
slab[][%Stride] = 1
@ -2076,11 +2070,12 @@ function /s psh5_load_dataset_reduced(fileID, scanpath, datasetname, reduction_f
if (progress)
display_progress_panel("HDF5 Import", "Loading data (step 1 of 2)...", nzt)
endif
make /n=(nx,ny) /d /o image_template
setdimlabel 0, -1, $kEnergyDimLabel, image_template
setdimlabel 1, -1, $kAngleDimLabel, image_template
ps_scale_dataset(image_template)
// create a template wave with the correct scales and labels
make /n=(nx,ny) /d /o $datawavename
wave template = $datawavename
ps_set_dimlabels2(template, datawavename)
ps_scale_dataset(template)
variable iz, it, izt
string dfname
@ -2093,9 +2088,9 @@ function /s psh5_load_dataset_reduced(fileID, scanpath, datasetname, reduction_f
dfname = "processing_" + num2str(izt)
newdatafolder /s $dfname
HDF5LoadData /O /Q /Z /SLAB=slab /N=slabdata fileID, datasetpath
// send to processing queue
duplicate image_template, image
duplicate template, image
variable /g r_index = iz
variable /g s_index = it
string /g func_param = reduction_param
@ -2125,7 +2120,7 @@ function /s psh5_load_dataset_reduced(fileID, scanpath, datasetname, reduction_f
endfor
endfor
killwaves /z slab, slabdata, image_template
killwaves /z slab, slabdata, template
if (progress)
update_progress_panel(0, message="Processing data (step 2 of 2)...")
endif
@ -2134,10 +2129,6 @@ function /s psh5_load_dataset_reduced(fileID, scanpath, datasetname, reduction_f
for (izt = 0; (izt < nzt) && (result == 0); izt += 1)
if (nthreads > 0)
do
dfr = ThreadGroupGetDFR(threadGroupID, 1000)
if (DatafolderRefStatus(dfr) != 0)
break
endif
if (progress)
if (update_progress_panel(izt))
print "user abort"
@ -2145,9 +2136,12 @@ function /s psh5_load_dataset_reduced(fileID, scanpath, datasetname, reduction_f
break
endif
endif
dfr = ThreadGroupGetDFR(threadGroupID, 1000)
if (DatafolderRefStatus(dfr) != 0)
break
endif
while (1)
else
dfr = processing_folders[izt]
if (progress)
if (update_progress_panel(izt))
print "user abort"
@ -2155,6 +2149,7 @@ function /s psh5_load_dataset_reduced(fileID, scanpath, datasetname, reduction_f
break
endif
endif
dfr = processing_folders[izt]
endif
if (result != 0)
@ -2175,6 +2170,8 @@ function /s psh5_load_dataset_reduced(fileID, scanpath, datasetname, reduction_f
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
@ -2210,8 +2207,6 @@ function /s psh5_load_dataset_reduced(fileID, scanpath, datasetname, reduction_f
redimension /n=(-1, nz, 0) ReducedData2
endif
wavenames = "ReducedData1;ReducedData2;"
ps_scale_dataset(ReducedData1)
ps_scale_dataset(ReducedData2)
endif
if (progress)
kill_progress_panel()
@ -2268,7 +2263,8 @@ threadsafe static function reduce_slab_image(slabdata, image, profile1, profile2
funcref adh5_default_reduction reduction_func
string reduction_param
image = slabdata[q][p][0][0]
// 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)
end
@ -2325,7 +2321,8 @@ end
/// the info string contains up to three lines which are made up of the following information:
/// - number of scan positions.
/// - dataset names of scan positioners.
/// - dataset names of detectors.
/// - dataset names of detectors (without region names).
/// - region names
///
/// @param fileID ID of open HDF5 file from psh5_open_file().
///
@ -2340,9 +2337,12 @@ function /s psh5_load_scan_info(fileID, scanpath)
string info = ""
string positions = ""
string positioners = ""
string readables = ""
string detectors = ""
string regions = ""
psh5_load_scan_meta(fileID, scanpath)
wave /z ScanDimensions
wave /t /z ScanWritables
wave /t /z ScanReadables
@ -2357,10 +2357,31 @@ function /s psh5_load_scan_info(fileID, scanpath)
positioners = "positioners = " + twave2list(ScanWritables, ",")
info = AddListItem(positioners, info, "\r", inf)
endif
variable i, m, n
string s
if (WaveExists(ScanReadables) && (numpnts(ScanReadables) >= 1))
detectors = "detectors = " + twave2list(ScanReadables, ",")
readables = twave2list(ScanReadables, ",")
n = ItemsInList(readables, ",")
for (i = 0; i < n; i += 1)
s = StringFromList(i, readables, ",")
m = ItemsInList(s, "/")
if (m > 1)
s = StringFromList(m - 1, s, "/")
endif
if (WhichListItem(s, detectors, ",") < 0)
detectors = AddListItem(s, detectors, ",", inf)
endif
endfor
detectors = "detectors = " + detectors
info = AddListItem(detectors, info, "\r", inf)
endif
regions = psh5_list_scan_regions(fileID, scanpath)
if (strlen(regions) > 0)
regions = "regions = " + regions
info = AddListItem(regions, info, "\r", inf)
endif
return info
end

View File

@ -1,11 +1,12 @@
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma IgorVersion = 6.1
#pragma ModuleName = PearlScientaPreprocess
#pragma version = 1.02
#pragma version = 1.03
#include "mm-fitfuncs"
// $Id$
// author: matthias.muntwiler@psi.ch
// Copyright (c) 2013-14 Paul Scherrer Institut
// Copyright (c) 2013-17 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.
@ -21,7 +22,7 @@
///
/// @author matthias muntwiler, matthias.muntwiler@psi.ch
///
/// @copyright 2013-15 Paul Scherrer Institut @n
/// @copyright 2013-17 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
@ -799,3 +800,198 @@ threadsafe function redim_linbg_reduction(source, dest1, dest2, param)
return int_linbg_reduction(source_redim, dest1, dest2, param)
end
// DblGaussLinBG function fit reduction
/// apply the gauss2_reduction function to a single image
///
/// useful for testing or manual processing.
/// to debug, (temporarily) remove the threadsafe attribute from the gauss2_reduction function.
///
function test_gauss2_reduction(image)
wave image
string param = ""
param = ReplaceNumberByKey("rngl", param, -inf, "=", ";")
param = ReplaceNumberByKey("rngh", param, inf, "=", ";")
param = ReplaceNumberByKey("pos1", param, 113.70, "=", ";")
param = ReplaceNumberByKey("wid1", param, 1.46, "=", ";")
param = ReplaceNumberByKey("pos2", param, 126.53, "=", ";")
param = ReplaceNumberByKey("wid2", param, 1.63, "=", ";")
param = ReplaceNumberByKey("ybox", param, 1, "=", ";")
param = ReplaceStringByKey("return", param, "amp1", "=", ";")
make /o test1
make /o test2
gauss2_reduction(image, test1, test2, param)
end
/// prompt for the gauss2_reduction parameters
///
/// useful for testing or manual processing.
/// to debug, (temporarily) remove the threadsafe attribute from the gauss2_reduction function.
///
function prompt_gauss2_reduction(param)
string &param
variable rngl = NumberByKey("rngl", param, "=", ";")
variable rngh = NumberByKey("rngh", param, "=", ";")
variable pos1 = NumberByKey("pos1", param, "=", ";")
variable wid1 = NumberByKey("wid1", param, "=", ";")
variable pos2 = NumberByKey("pos2", param, "=", ";")
variable wid2 = NumberByKey("wid2", param, "=", ";")
variable ybox = NumberByKey("ybox", param, "=", ";")
string retpar = StringByKey("return", param, "=", ";")
prompt rngl, "range low"
prompt rngh, "range high"
prompt pos1, "position 1"
prompt wid1, "width 1"
prompt pos2, "position 2"
prompt wid2, "width 2"
prompt ybox, "ybox (1 or 3)"
prompt retpar, "return", popup "amp1;amp2;"
doprompt "gauss2_reduction reduction parameters", rngl, rngh, pos1, wid1, pos2, wid2, ybox, retpar
if (v_flag == 0)
param = ReplaceNumberByKey("rngl", param, rngl, "=", ";")
param = ReplaceNumberByKey("rngh", param, rngh, "=", ";")
param = ReplaceNumberByKey("pos1", param, pos1, "=", ";")
param = ReplaceNumberByKey("wid1", param, wid1, "=", ";")
param = ReplaceNumberByKey("pos2", param, pos2, "=", ";")
param = ReplaceNumberByKey("wid2", param, wid2, "=", ";")
param = ReplaceStringByKey("return", param, retpar, "=", ";")
param = ReplaceNumberByKey("ybox", param, ybox, "=", ";")
endif
return v_flag
end
/// fit horizontal cuts of an image with two gaussian peaks on a linear background
///
/// the function fits each horizontal profile (EDC) with two gaussian peaks on a linear background.
/// the position and width of the peaks is kept fixed according to input parameters.
/// the peak amplitude is constrained to positive value.
///
/// the width parameter is defined as in Igor's gauss curve fit function
/// (standard deviation divided by the square root of two).
/// the return value in dest1 is the integrated peak either of peak 1 or peak 2.
/// dest2 returns the corresponding error estimate.
///
/// @param source source wave.
/// two-dimensional distribution of counts.
/// for correct weighting and error estimation it is important
/// that the source wave contains actual counts (Poisson statistics).
/// @param dest1 (out) peak area
/// @param dest2 (out) error estimate of peak area
/// @param param (in, out) semicolon-separated key=value list of processing parameters.
/// this is a pass-by-reference argument.
/// the following parameters are required.
/// position, width and limit parameters are on the x (energy) scale.
/// @arg rngl low limit of fit interval
/// @arg rngh high limit of fit interval
/// @arg pos1 position of peak 1
/// @arg wid1 width of peak 1
/// @arg pos2 position of peak 2
/// @arg wid2 width of peak 2
/// @arg return select result to return, either "int1" or "int2".
/// @arg ybox box size of averaging in y direction, must be 1 or 3.
/// other values lead to corrupt data.
///
/// @return zero if successful, non-zero if an error occurs.
///
threadsafe function gauss2_reduction(source, dest1, dest2, param)
wave source
wave dest1, dest2
string &param
variable nx = dimsize(source, 0)
variable ny = dimsize(source, 1)
// prepare output
adh5_setup_profile(source, dest1, 1)
adh5_setup_profile(source, dest2, 1)
dest1 = 0
dest2 = 0
// read parameters
variable rngl = NumberByKey("rngl", param, "=", ";")
variable rngh = NumberByKey("rngh", param, "=", ";")
variable pos1 = NumberByKey("pos1", param, "=", ";")
variable wid1 = NumberByKey("wid1", param, "=", ";")
variable pos2 = NumberByKey("pos2", param, "=", ";")
variable wid2 = NumberByKey("wid2", param, "=", ";")
variable ybox = NumberByKey("ybox", param, "=", ";")
string retpar = StringByKey("return", param, "=", ";")
variable idestcoef
if (cmpstr(retpar, "amp2") == 0)
idestcoef = 3
else
idestcoef = 0
endif
// prepare curve fit
make /free xprof
adh5_setup_profile(source, xprof, 0)
duplicate /free xprof, xprof_sig
make /free /d /n=8 w_coef, W_sigma
w_coef[0] = {1, pos1, wid1, 1, pos2, wid2, 0, 0}
variable pl = max(x2pnt(xprof, rngl), 0)
variable ph = min(x2pnt(xprof, rngh), numpnts(xprof) - 1)
// text constraints cannot be used in threadsafe functions.
// the following matrix-vector forumlation is equivalent to:
// make /free /T /N=4 constraints
// constraints[0] = {"K0 >= 0", "K3 >= 0", "K6 <= 0", "K7 => 0"}
make /free /n=(4,8) cmat
make /free /n=4 cvec
cmat = 0
cmat[0][0] = -1
cmat[1][3] = -1
cmat[2][6] = 1
cmat[3][7] = -1
cvec = 0
// loop over angle scale
variable p0 = 0
variable p1 = numpnts(dest1) - 1
variable pp
variable wmin
variable wmax
if (ybox > 1)
p0 += ceil((ybox - 1) / 2)
p1 -= ceil((ybox - 1) / 2)
endif
variable V_FitNumIters
for (pp = p0; pp <= p1; pp += 1)
xprof = source[p][pp]
if (ybox > 1)
xprof += source[p][pp-1] + source[p][pp+1]
endif
xprof_sig = max(sqrt(xprof), 1)
xprof /= ybox
xprof_sig /= ybox
wmin = wavemin(xprof)
wmax = wavemax(xprof)
w_coef[0] = wmax - wmin
w_coef[3] = w_coef[0]
w_coef[6] = 0
w_coef[7] = wmin
FuncFit /H="01101100" /Q /NTHR=1 /N /W=2 DblGaussLinBG w_coef xprof[pl,ph] /C={cmat, cvec} /I=1 /W=xprof_sig[pl,ph]
wave w_sigma
if (V_FitNumIters < 40)
dest1[pp] = max(w_coef[idestcoef], 0)
dest2[pp] = max(w_sigma[idestcoef], 0)
endif
endfor
dest1 *= w_coef[idestcoef+2] * sqrt(pi)
dest2 *= w_coef[idestcoef+2] * sqrt(pi)
return 0
end

View File

@ -0,0 +1,32 @@
IGOR
WAVES/T attr_filter_summary
BEGIN
"MonoEnergy"
"MonoGrating"
"FrontendHSize"
"FrontendVSize"
"ExitSlit"
"ManipulatorTheta"
"CenterEnergy"
"ManipulatorTilt"
"ManipulatorPhi"
"AcquisitionMode"
"ChannelBegin"
"ChannelEnd"
"CenterEnergy"
"PassEnergy"
"NumChannels"
"NumFrames"
"NumIterations"
"StepSize"
"NumIterations"
"AnalyserSlit"
"SampleCurrent"
"LensMode"
END
X SetScale/P x 0,1,"", attr_filter_summary; SetScale y 0,0,"", attr_filter_summary
WAVES/T attr_filter
BEGIN
END
X SetScale d 0,0,"", attr_filter