Skip to content

Commit

Permalink
Merge pull request mantidproject#33766 from mantidproject/33764_sansi…
Browse files Browse the repository at this point in the history
…ll_more_ascii_exports

SANSILL: Input information metadata
  • Loading branch information
gemmaguest authored Jul 1, 2022
2 parents 9b65522 + 3edce0f commit 53ae806
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,12 @@
WorkspaceGroupProperty, FileAction, WorkspaceGroup
from mantid.kernel import Direction, FloatBoundedValidator, FloatArrayProperty, IntBoundedValidator
from mantid.simpleapi import *
import SANSILLCommon as common
import numpy as np
from os import path

EMPTY_TOKEN = '000000'


def get_run_number(value):
"""
Extracts the run number from the first run out of the string value of a
multiple file property of numors
"""
return path.splitext(path.basename(value.split(',')[0].split('+')[0]))[0]


def needs_processing(property_value, process_reduction_type):
"""
Checks whether a given unary reduction needs processing or is already cached
Expand All @@ -33,7 +25,7 @@ def needs_processing(property_value, process_reduction_type):
do_process = False
ws_name = ''
if property_value:
run_number = get_run_number(property_value)
run_number = common.get_run_number(property_value)
ws_name = run_number + '_' + process_reduction_type
if mtd.doesExist(ws_name):
if isinstance(mtd[ws_name], WorkspaceGroup):
Expand Down Expand Up @@ -462,10 +454,14 @@ def PyExec(self):
# try to stitch automatically
if len(outputSamples) > 1 and self.getPropertyValue('OutputType') == 'I(Q)':
try:
stitched = self.output + "_stitched"
stitched = f'{self.output}_stitched'
stitch_params_ws = f'{self.output}_stitch_scale_factors'
Stitch(InputWorkspaces=outputSamples,
OutputWorkspace=stitched,
ReferenceWorkspace=outputSamples[self.stitch_reference_index])
ReferenceWorkspace=outputSamples[self.stitch_reference_index],
OutputScaleFactorsWorkspace=stitch_params_ws)
mtd[stitched].getRun().addProperty('stitch_scale_factors', list(mtd[stitch_params_ws].readY(0)), True)
DeleteWorkspace(stitch_params_ws)
outputSamples.append(stitched)
except RuntimeError as re:
self.log().warning("Unable to stitch automatically, consider "
Expand Down Expand Up @@ -923,6 +919,11 @@ def processSample(self, i, flux_name, sample_transmission_names, beam_name,
Wavelength=self.getProperty('Wavelength').value,
Version=1)

common.add_correction_numors(ws=sample_name, stransmission=sample_transmission_name,
container=container_name, absorber=absorber_name, beam=beam_name,
flux=flux_name, solvent=solv_input, reference=ref_input,
sensitivity=sens_input)

output_sample = self.output + '_#' + str(i + 1)

output_panels = ''
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from enum import IntEnum
from math import fabs
from os import path
import re


class AcqMode(IntEnum):
Expand Down Expand Up @@ -38,6 +39,87 @@ class AcqMode(IntEnum):
# as the sample measurement (so 1 if mono, >1 if kinetic mono).


def return_numors_from_path(run_list):
"""Returns numors used to create a given workspace, including binary operations performed.
Args:
run_list: (str) containing full paths of all inputs, including binary operations on nexus files
"""
regex_all = r'(\+)'
p = re.compile(regex_all)
list_entries = []
binary_op = []
prev_pos = 0
for obj in p.finditer(run_list):
list_entries.append(run_list[prev_pos:obj.span()[0]])
prev_pos = obj.span()[1]
binary_op.append(obj.group())
list_entries.append(run_list[prev_pos:]) # add the last remaining file
list_entries = [path.split(entry)[1] for entry in list_entries]
binary_op.append('') # there is one fewer binary operator than there are numors
list_entries = [entry + operation for entry, operation in zip(list_entries, binary_op)]
return ''.join(list_entries)


def _return_numors_from_ws(ws_name):
"""Returns numor lists from the provided workspace name.
Args:
ws_name: (str) workspace name to extract numor_list from
"""
numors = str()
if ws_name in mtd:
try:
numors = mtd[ws_name].getRun().getLogData('numor_list').value
except RuntimeError: # numor list not set
numors = ''
return numors


def add_correction_information(ws, parameters):
"""Adds information regarding corrections and inputs to the provided workspace using the parameters dictionary.
Args:
ws: (str) workspace name to which information is to be added
parameters: (dict) dictionary containing parameter name to be added and its value
"""
for param in parameters:
mtd[ws].getRun().addProperty(param, parameters[param], True)


def add_correction_numors(ws, stransmission, container, absorber, beam, flux, solvent, reference,
sensitivity):
"""Adds numors used for corrections and inputs to the provided workspace.
Args:
ws: (str) workspace name to which information is to be added
stransmission: (str) workspace name with sample transmission
container: (str) workspace name with container
absorber: (str) workspace name with absorber
beam: (str) workspace name with beam
flux: (str) workspace name with flux
solvent: (str) workspace name with solvent
reference: (str) workspace name with reference
sensitivity: (str) workspace name with sensitivity
"""
mtd[ws].getRun().addProperty('sample_transmission_numors',
_return_numors_from_ws(stransmission), True)
mtd[ws].getRun().addProperty('container_numors',
_return_numors_from_ws(container), True)
mtd[ws].getRun().addProperty('absorber_numors',
_return_numors_from_ws(absorber), True)
mtd[ws].getRun().addProperty('beam_numors',
_return_numors_from_ws(beam), True)
mtd[ws].getRun().addProperty('flux_numors',
_return_numors_from_ws(flux), True)
mtd[ws].getRun().addProperty('solvent_numors',
_return_numors_from_ws(solvent), True)
mtd[ws].getRun().addProperty('reference_numors',
_return_numors_from_ws(reference), True)
mtd[ws].getRun().addProperty('sensitivity_numors',
_return_numors_from_ws(sensitivity), True)


def get_run_number(value):
"""
Extracts the run number from the first run out of the string value of a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -773,9 +773,9 @@ def process_sample(self, d, dark_current_ws, empty_beam_ws, empty_can_ws, flux_w
if self.getProperty('ProduceSensitivity').value:
process = 'Water'
sens_out = sample_ws + '_Sens'
uesr_thickness = self.getProperty('SampleThickness').value
user_thickness = self.getProperty('SampleThickness').value
thickness_from = self.getPropertyValue('SampleThicknessFrom')
thickness_to_use = uesr_thickness if thickness_from == 'User' else [-1]
thickness_to_use = user_thickness if thickness_from == 'User' else [-1]
SANSILLReduction(Runs=runs,
ProcessAs=process,
DarkCurrentWorkspace=dark_current_ws,
Expand All @@ -796,6 +796,9 @@ def process_sample(self, d, dark_current_ws, empty_beam_ws, empty_can_ws, flux_w
OutputSensitivityWorkspace=sens_out,
startProgress=(self.lambda_rank+d)*self.n_samples/self.n_reports,
endProgress=(self.lambda_rank+d+1)*self.n_samples/self.n_reports)
add_correction_numors(ws=sample_ws, stransmission=sample_tr_ws,
container=empty_can_ws, absorber=dark_current_ws, beam=empty_beam_ws,
flux=flux_ws, solvent=solvent_ws, reference='', sensitivity='')
return [sample_ws, sens_out]
else:
return []
Expand Down Expand Up @@ -948,6 +951,7 @@ def do_stitch(self, inputs, output, output_scale_factors):
OutputScaleFactorsWorkspace=output_scale_factors,
ReferenceWorkspace=inputs[self.getProperty('StitchReferenceIndex').value],
**kwargs)
mtd[output].getRun().addProperty('stitch_scale_factors', list(mtd[output_scale_factors].readY(0)), True)
return [output, output_scale_factors]
except RuntimeError as e:
self.log().error('Unable to stitch, consider stitching manually: '+str(e))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
from mantid.kernel import Direction, EnabledWhenProperty, FloatBoundedValidator, LogicOperator, PropertyCriterion, \
StringListValidator
from mantid.simpleapi import *
import SANSILLCommon as common
from math import fabs
import numpy as np
import os
import re


class SANSILLReduction(PythonAlgorithm):
Expand Down Expand Up @@ -97,23 +97,6 @@ def _mask(ws, masked_ws, check_if_masked_detectors=True):
if not check_if_masked_detectors or masked_ws.detectorInfo().hasMaskedDetectors():
MaskDetectors(Workspace=ws, MaskedWorkspace=masked_ws)

@staticmethod
def _return_numors(paths):
regex_all = r'(\+)'
p = re.compile(regex_all)
list_entries = []
binary_op = []
prev_pos = 0
for obj in p.finditer(paths):
list_entries.append(paths[prev_pos:obj.span()[0]])
prev_pos = obj.span()[1]
binary_op.append(obj.group())
list_entries.append(paths[prev_pos:]) # add the last remaining file
list_entries = [os.path.split(entry)[1] for entry in list_entries]
binary_op.append('') # there is one fewer binary operator than there are numors
list_entries = [entry + operation for entry, operation in zip(list_entries, binary_op)]
return ''.join(list_entries)

def PyInit(self):

self.declareProperty(MultipleFileProperty('Run', action=FileAction.OptionalLoad,
Expand Down Expand Up @@ -605,13 +588,15 @@ def _apply_transmission(self, ws, transmission_ws):
ApplyTransmissionCorrection(InputWorkspace=ws, TransmissionValue=transmission,
TransmissionError=transmission_err, ThetaDependent=theta_dependent,
OutputWorkspace=ws)
mtd[ws].getRun().addProperty('sample.transmission', float(transmission), True)
else:
# wavelength dependent transmission, need to rebin
transmission_rebinned = ws + '_tr_rebinned'
RebinToWorkspace(WorkspaceToRebin=transmission_ws, WorkspaceToMatch=ws,
OutputWorkspace=transmission_rebinned)
ApplyTransmissionCorrection(InputWorkspace=ws, TransmissionWorkspace=transmission_rebinned,
ThetaDependent=theta_dependent, OutputWorkspace=ws)
mtd[ws].getRun().addProperty('sample.transmission', list(mtd[transmission_rebinned].readY(0)), True)
DeleteWorkspace(transmission_rebinned)

def _apply_container(self, ws, container_ws):
Expand Down Expand Up @@ -692,12 +677,35 @@ def _finalize(self, ws, process):
ReplaceSpecialValues(InputWorkspace=ws, OutputWorkspace=ws, NaNValue=0,
NaNError=0, InfinityValue=0, InfinityError=0)
mtd[ws].getRun().addProperty('ProcessedAs', process, True)
mtd[ws].getRun().addProperty('numor_list', self._return_numors(self.getPropertyValue('Run')), True)
mtd[ws].getRun().addProperty('sample_transmission_numors',
self._return_numors(self.getPropertyValue('TransmissionInputWorkspace')), True)
self._add_correction_information(ws)
RenameWorkspace(InputWorkspace=ws, OutputWorkspace=ws[2:])
self.setProperty('OutputWorkspace', mtd[ws[2:]])

def _add_correction_information(self, ws):
"""Adds information regarding corrections and inputs to the provided workspace.
Args:
ws: (str) workspace name to which information is to be added
"""
# first, let's create the dictionary containing all parameters that should be added to the metadata
parameters = dict()
parameters['numor_list'] = common.return_numors_from_path(self.getPropertyValue('Run'))
parameters['sample_transmission_ws'] = \
common.return_numors_from_path(self.getPropertyValue('TransmissionInputWorkspace'))
parameters['container_ws'] = \
common.return_numors_from_path(self.getPropertyValue('ContainerInputWorkspace'))
parameters['absorber_ws'] = \
common.return_numors_from_path(self.getPropertyValue('AbsorberInputWorkspace'))
parameters['beam_ws'] = \
common.return_numors_from_path(self.getPropertyValue('BeamInputWorkspace'))
parameters['flux_ws'] = common.return_numors_from_path(self.getPropertyValue('FluxInputWorkspace'))
parameters['reference_ws'] = common.return_numors_from_path(self.getPropertyValue('ReferenceInputWorkspace'))
parameters['sensitivity_ws'] = \
common.return_numors_from_path(self.getPropertyValue('SensitivityInputWorkspace'))
parameters['mask_ws'] = common.return_numors_from_path(self.getPropertyValue('MaskedInputWorkspace'))
# when all is set, a common function can set them all
common.add_correction_information(ws, parameters)

def _apply_masks(self, ws):
# apply the default mask, e.g. the bad detector edges
default_mask_ws = self.getProperty('DefaultMaskedInputWorkspace').value
Expand Down Expand Up @@ -922,9 +930,8 @@ def _process_kinetic_sample(self, ws):
MaskDetectorsIf(InputWorkspace=ws, OutputWorkspace=ws, Operator='NotFinite')

mtd[ws].getRun().addProperty('ProcessedAs', 'Sample', True)
mtd[ws].getRun().addProperty('numor_list', self._return_numors(self.getPropertyValue('Run')), True)
mtd[ws].getRun().addProperty('sample_transmission_numors',
self._return_numors(self.getPropertyValue('TransmissionInputWorkspace')), True)
self._add_correction_information(ws)

RenameWorkspace(InputWorkspace=ws, OutputWorkspace=ws[2:])

GroupWorkspaces(InputWorkspaces=frames_to_group, OutputWorkspace=group_name[2:])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
# SPDX - License - Identifier: GPL - 3.0 +
from SANSILLCommon import *
import SANSILLCommon as common
from mantid.api import PythonAlgorithm, MatrixWorkspace, MatrixWorkspaceProperty, WorkspaceProperty, \
MultipleFileProperty, PropertyMode, Progress, WorkspaceGroup, FileAction
from mantid.dataobjects import SpecialWorkspace2D
Expand Down Expand Up @@ -229,6 +230,30 @@ def PyInit(self):

self.setPropertySettings('OutputFluxWorkspace', beam)

def _add_correction_information(self, ws):
"""Adds information regarding corrections and inputs to the provided workspace.
Args:
ws: (str) workspace name to which information is to be added
"""
# first, let's create the dictionary containing all parameters that should be added to the metadata
parameters = dict()
parameters['numor_list'] = common.return_numors_from_path(self.getPropertyValue('Runs'))
parameters['sample_transmission_ws'] = \
common.return_numors_from_path(self.getPropertyValue('TransmissionWorkspace'))
parameters['container_ws'] = \
common.return_numors_from_path(self.getPropertyValue('EmptyContainerWorkspace'))
parameters['absorber_ws'] = \
common.return_numors_from_path(self.getPropertyValue('DarkCurrentWorkspace'))
parameters['beam_ws'] = \
common.return_numors_from_path(self.getPropertyValue('EmptyBeamWorkspace'))
parameters['flux_ws'] = common.return_numors_from_path(self.getPropertyValue('FluxWorkspace'))
parameters['sensitivity_ws'] = \
common.return_numors_from_path(self.getPropertyValue('SensitivityWorkspace'))
parameters['mask_ws'] = common.return_numors_from_path(self.getPropertyValue('MaskWorkspace'))
# when all is set, a common function can set them all
common.add_correction_information(ws, parameters)

def reset(self):
'''Resets the class member variables'''
self.instrument = None
Expand Down Expand Up @@ -357,6 +382,7 @@ def apply_direct_beam(self, ws):
beam_y = run['BeamCenterY'].value
AddSampleLog(Workspace=ws, LogName='BeamCenterX', LogText=str(beam_x), LogType='Number')
AddSampleLog(Workspace=ws, LogName='BeamCenterY', LogText=str(beam_y), LogType='Number')
AddSampleLog(Workspace=ws, LogName='beam_ws', LogText=beam_ws, LogType='String')
self.apply_multipanel_beam_center_corr(ws, beam_x, beam_y)
if 'BeamWidthX' in run:
AddSampleLog(Workspace=ws, LogName='BeamWidthX', LogText=str(run['BeamWidthX'].value),
Expand Down Expand Up @@ -388,6 +414,7 @@ def apply_flux(self, ws):
else:
Divide(LHSWorkspace=ws, RHSWorkspace=flux_ws, OutputWorkspace=ws)
AddSampleLog(Workspace=ws, LogText='True', LogType='String', LogName='NormalisedByFlux')
AddSampleLog(Workspace=ws, LogName='flux_ws', LogText=flux_ws, LogType='String')

def apply_transmission(self, ws):
'''Applies transmission correction'''
Expand All @@ -402,6 +429,7 @@ def apply_transmission(self, ws):
OutputWorkspace=tr_ws_rebin)
ApplyTransmissionCorrection(InputWorkspace=ws, TransmissionWorkspace=tr_ws_rebin,
ThetaDependent=theta_dependent, OutputWorkspace=ws)
mtd[ws].getRun().addProperty('sample.transmission', list(mtd[tr_ws_rebin].readY(0)), True)
DeleteWorkspace(tr_ws_rebin)
else:
check_wavelengths_match(mtd[tr_ws], mtd[ws])
Expand All @@ -416,6 +444,7 @@ def apply_transmission(self, ws):
TransmissionWorkspace=tr_to_apply,
ThetaDependent=theta_dependent,
OutputWorkspace=ws)
mtd[ws].getRun().addProperty('sample.transmission', list(mtd[tr_to_apply].readY(0)), True)
if needs_broadcasting:
DeleteWorkspace(tr_to_apply)
if theta_dependent and self.instrument == 'D16' and 75 < mtd[ws].getRun()['Gamma.value'].value < 105:
Expand Down Expand Up @@ -960,6 +989,7 @@ def reduce(self):
if self.process != 'Solvent':
self.apply_solvent(ws)
self.progress.report()
self._add_correction_information(ws)
self.setProperty('OutputWorkspace', ws)

def PyExec(self):
Expand Down
1 change: 1 addition & 0 deletions docs/source/release/v6.5.0/SANS/New_features/33764.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- The ILL SANS workflow now adds more metadata to the reduced sample workspace. The sample logs now include information about empty container, empty beam, absorber, etc. workspaces names and files that were used to create them. In addition, sample transmission is now overwritten after transmission correction is done as sample.transmission, and stitching factors are added to the sample logs of the stitching output workspace as stitch_scaling_factors.
Loading

0 comments on commit 53ae806

Please sign in to comment.