forked from AmbaPant/mantid
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSetSampleFromLogs.py
182 lines (150 loc) · 8.66 KB
/
SetSampleFromLogs.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# Mantid Repository : https://github.com/mantidproject/mantid
#
# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
# NScD Oak Ridge National Laboratory, European Spallation Source,
# Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
# SPDX - License - Identifier: GPL - 3.0 +
from mantid.api import (AlgorithmFactory, DistributedDataProcessorAlgorithm)
from mantid.kernel import (ConfigService)
from mantid.simpleapi import (SetSample)
PROPS_FOR_SETSAMPLE = ["InputWorkspace", "Geometry", "Material", "Environment", "ContainerGeometry", "ContainerMaterial"]
def _findKey(dictlike, *args):
for name in args:
if (name in dictlike) and bool(dictlike[name]):
return name
return '' # indicates failure
def _hasValue(dictLike, key): # TODO refactor into *args and return the one that exists or empty string
return (key in dictLike) and bool(dictLike[key])
def _getLogValue(propertyManager, key):
if not _hasValue(propertyManager, key):
raise ValueError('Failed to find "{}" in logs'.format(key))
return propertyManager[key].lastValue()
class SetSampleFromLogs(DistributedDataProcessorAlgorithm):
def category(self):
return "Sample"
def seeAlso(self):
return ["SetSample"]
def name(self):
return "SetSampleFromLogs"
def summary(self):
return "This algorithm looks through the logs to automatically determine the sample geometry and material"
def PyInit(self):
self.copyProperties("SetSample", PROPS_FOR_SETSAMPLE)
self.declareProperty("FindGeometry", True,
"Whether to look for the 'Height' parameter in the logs")
self.declareProperty("FindSample", True,
"Whether to look for the sample material in the logs")
self.declareProperty("FindEnvironment", True,
"Whether to look for the sample container in the logs")
def _createMaterial(self, runObject):
'''Create the sample material by combining the supplied material information with things found in the logs'''
# grab the starting information from the algorithm property
material = self.getProperty("Material").value
# only look in the logs if the user asks for it
if self.getProperty("FindSample").value: # SetSample calls it the material
if (not _hasValue(material, 'ChemicalFormula')) and _hasValue(runObject, "SampleFormula"):
self.log().information("Looking for 'SampleFormula' in logs")
material['ChemicalFormula'] = _getLogValue(runObject, 'SampleFormula').strip()
if (not _hasValue(material, "SampleMassDensity")) and _hasValue(runObject, "SampleDensity"):
self.log().information("Looking for 'SampleDensity', 'SampleMass', and 'SampleMassDensity' in logs")
value = _getLogValue(runObject, 'SampleDensity')
if value == 1.0 or value == 0.0:
material['Mass'] = _getLogValue(runObject, 'SampleMass')
elif _hasValue(runObject, "SampleMass"):
material['SampleMassDensity'] = value
# log the results and return
self.log().information('MATERIAL: ' + str(material))
return material
def _createGeometry(self, runObject, instrEnum):
# get height of sample from the logs
# this assumes the shape has a "Height" property
geometry = self.getProperty("Geometry").value
if self.getProperty("FindGeometry").value:
if not _hasValue(geometry, "Height"):
heightInContainerNames = ['HeightInContainer']
heightInContainerUnitsNames = ['HeightInContainerUnits']
# determine "special" logs for SNS instruments
if instrEnum.facility().name() == 'SNS':
beamline = ''
# TODO this would benefit from the beamline exposed through python
if instrEnum.name() == 'NOMAD':
beamline = 'BL1B'
elif instrEnum.name() == 'POWGEN':
beamline = 'BL11A'
else:
warningMsg = 'Do not know how to create lognames for "{}"'.format(instrEnum.name())
self.log().warning(warningMsg)
if beamline:
# "internal" log names at SNS are templated from the beamline number
heightInContainerUnitsNames.append('{}:CS:ITEMS:HeightInContainerUnits'.format(beamline))
heightInContainerNames.append('{}:CS:ITEMS:HeightInContainer'.format(beamline))
self.log().information("Looking for sample height from {} and units from {}".format(heightInContainerNames,
heightInContainerUnitsNames))
# Check units - SetSample expects cm
unitsKey = _findKey(runObject, *heightInContainerUnitsNames)
units = ''
if unitsKey:
units = _getLogValue(runObject, unitsKey)
# not finding units will generate a warning below
# create conversion factor into cm
conversion = 1.0 # don't do any conversion
if units == "cm":
conversion = 1.0
elif units == "mm":
conversion = 0.1
else:
warningMsg = "HeightInContainerUnits expects cm or mm;" + \
" specified units not recognized: {:s};".format(units) + \
" we will reply on user input for sample density information."
self.log().warning(warningMsg)
# set the height
heightKey = _findKey(runObject, *heightInContainerNames)
if heightKey:
geometry['Height'] = conversion * _getLogValue(runObject, heightKey)
else:
warningMsg = "No valid height found in sample logs;" + \
"we will rely on user input for sample density information."
self.log().warning(warningMsg)
# log the results and return
self.log().information('GEOMETRY (in cm): ' + str(geometry))
return geometry
def _createEnvironment(self, runObject, instrEnum):
'''Create the sample material by combining the supplied environment information with things found in the logs'''
# grab the starting information from the algorithm property
environment = self.getProperty("Environment").value
# get the container from the logs
if self.getProperty("FindEnvironment").value:
if (not _hasValue(environment, "Container")) and _hasValue(runObject, "SampleContainer"):
self.log().information('Looking for "SampleContainer" in logs')
environment['Container'] = runObject['SampleContainer'].lastValue().replace(" ", "")
if _hasValue(environment, "Container") and (not _hasValue(environment, "Name")):
# set a default environment
if instrEnum.facility().name() == 'SNS':
environment['Name'] = 'InAir'
self.log().information('ENVIRONMENT: ' + str(environment))
return environment
def PyExec(self):
wksp = self.getProperty("InputWorkspace").value
# these items are not grabbed from the logs
geometryContainer = self.getProperty("ContainerGeometry").value
materialContainer = self.getProperty("ContainerMaterial").value
# get a convenient handle to the logs
runObject = wksp.run()
# this is used for determining some of the log names
instrEnum = ConfigService.getInstrument(wksp.getInstrument().getFullName())
# get information from the logs
material = self._createMaterial(runObject)
geometry = self._createGeometry(runObject, instrEnum)
environment = self._createEnvironment(runObject, instrEnum)
# let SetSample generate errors if anything is wrong
SetSample(InputWorkspace=wksp,
Material=material,
Geometry=geometry,
Environment=environment,
ContainerGeometry=geometryContainer,
ContainerMaterial=materialContainer)
# validate that sample shape was set to something with volume!=0
if (wksp.sample().getShape().volume() == 0):
raise RuntimeError("Resulting sample shape has volume of 0")
# Register algorithm with Mantid.
AlgorithmFactory.subscribe(SetSampleFromLogs)