forked from AmbaPant/mantid
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSaveYDA.py
321 lines (240 loc) · 9.64 KB
/
SaveYDA.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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
# 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 PythonAlgorithm, AlgorithmFactory, MatrixWorkspaceProperty, WorkspaceUnitValidator, \
InstrumentValidator, FileProperty, FileAction
from mantid.kernel import Direction, CompositeValidator
from mantid.dataobjects import Workspace2D
import yaml
from yaml import Dumper
from collections import OrderedDict
import math
class SaveYDA(PythonAlgorithm):
""" Save data in yaml/frida 2.0 format from a Workspace2D.
"""
def category(self):
"""Return category
"""
return "DataHandling\\Text"
def name(self):
"""Return name
"""
return "SaveYDA"
def summary(self):
"""Return summary
"""
return "Save Workspace to a Frida 2.0 yaml format"
def PyInit(self):
"""Declare properties
"""
wsValidators = CompositeValidator()
# X axis must be a NumericAxis in energy transfer units.
wsValidators.add(WorkspaceUnitValidator("DeltaE"))
# Workspace must have an Instrument
wsValidators.add(InstrumentValidator())
self.declareProperty(MatrixWorkspaceProperty(name="InputWorkspace", defaultValue="", direction=Direction.Input,
validator=wsValidators), doc="Workspace name for input")
self.declareProperty(FileProperty(name="Filename", defaultValue="", action=FileAction.Save, extensions=""),
doc="The name to use when writing the file")
def validateInputs(self):
"""Basic validation for inputs.
:return: issues with not valid Inputs in dictionary
"""
issues = dict()
# Only MomentumTransfer is allowed
allowUn = "MomentumTransfer"
ws = self.getProperty("InputWorkspace").value
# Y axis must be either a SpectrumAxis or a NumericAxis in q units.
# workspace must be a Workspace2D
if ws:
ax = ws.getAxis(1)
if not ax.isSpectra() and ax.getUnit().unitID() != allowUn:
issues["InputWorkspace"] = "Y axis is not 'Spectrum Axis' or 'Momentum Transfer'"
if not isinstance(ws, Workspace2D):
issues["InputWorkspace"] = "Input Workspace is not a Workspace2D"
return issues
def PyExec(self):
""" Main execution body
"""
# Properties
ws = self.getProperty("InputWorkspace").value
filename = self.getProperty("Filename").value
run = ws.getRun()
ax = ws.getAxis(1)
nHist = ws.getNumberHistograms()
# check sample logs exists
if len(run.getLogData()) == 0:
raise NotImplementedError("No sample log data exist in workspace: "
+ self.getPropertyValue("InputWorkspace"))
# save sample log data in lists, commented sequences an commented maps
# commented sequences and maps are used to keep Data in the order they get inserted
# if a log does not exist a warning is written on the log and the data is not saved in the file
metadata = OrderedDict()
metadata["format"] = "yaml/frida 2.0"
metadata["type"] = "generic tabular data"
hist = []
if run.hasProperty("proposal_number"):
propn = "Proposal number " + run.getLogData("proposal_number").value
hist.append(propn)
else:
self.log().warning("no proposal number found")
if run.hasProperty("proposal_title"):
propt = run.getLogData("proposal_title").value
hist.append(propt)
else:
self.log().warning("no proposal title found")
if run.hasProperty("experiment_team"):
expt = run.getLogData("experiment_team").value
hist.append(expt)
else:
self.log().warning("no experiment team found")
hist.append("data reduced with mantid")
rpar = []
if run.hasProperty("temperature"):
temperature = float(run.getLogData("temperature").value)
temp = OrderedDict()
temp["name"] = "T"
temp["unit"] = "K"
temp["val"] = round(temperature, 14)
temp["stdv"] = 0
rpar.append(temp)
else:
self.log().warning("no temperature found")
if run.hasProperty("Ei"):
eimeV = float(run.getLogData("Ei").value)
ei = OrderedDict()
ei["name"] = "Ei"
ei["unit"] = "meV"
ei["val"] = round(eimeV, 14)
ei["stdv"] = 0
rpar.append(ei)
else:
self.log().warning("no Ei found")
coord = OrderedDict()
x = FlowOrderedDict()
x["name"] = "w"
x["unit"] = "meV"
coord["x"] = x
y = FlowOrderedDict()
y["name"] = "S(q,w)"
y["unit"] = "meV-1"
coord["y"] = y
z = FlowOrderedDict()
if ax.isSpectra():
zname = "2th"
zunit = "deg"
else:
zname = "q"
zunit = "A-1"
z["name"] = zname
z["unit"] = zunit
coord["z"] = FlowList()
coord["z"].append(z)
slices = []
bin = []
# if y axis is SpectrumAxis
if ax.isSpectra:
samplePos = ws.getInstrument().getSample().getPos()
sourcePos = ws.getInstrument().getSource().getPos()
beamPos = samplePos - sourcePos
for i in range(nHist):
detector = ws.getDetector(i)
# convert radians to degrees
twoTheta = detector.getTwoTheta(samplePos, beamPos)*180/math.pi
twoTheta = round(twoTheta, 14)
bin.append(twoTheta)
elif ax.length() == nHist:
# if y axis contains bin centers
for i in range(ax.length()):
xval = round(ax.getValue(), 14)
bin.append(xval)
else:
# get the bin centers not the bin edges
bin = self._get_bin_centers(ax)
for i in range(nHist):
slicethis = OrderedDict()
# add j to slices, j = counts
slicethis["j"] = i
# save in list and commented Map to keep format
val = FlowOrderedDict()
val["val"] = bin[i]
# z is bin centers of y axis, SpectrumAxis or NumericAxis in q units
slicethis["z"] = FlowList()
slicethis["z"].append(val)
xax = ws.readX(i)
# get the bin centers not the bin edges
xcenters = self._get_bin_centers(xax)
# x axis is NumericAxis in energy transfer units
xx = [float(j) for j in xcenters]
slicethis["x"] = FlowList(xx)
ys = ws.dataY(i)
# y is dataY of the workspace
yy = [float(round(j, 14)) for j in ys]
slicethis["y"] = FlowList(yy)
slices.append(slicethis)
data = OrderedDict()
data["Meta"] = metadata
data["History"] = hist
data["Coord"] = coord
data["RPar"] = rpar
data["Slices"] = slices
data["Slices"] = slices
# create yaml file
try:
with open(filename, "w") as outfile:
yaml.dump(data, outfile, default_flow_style=False, canonical=False, Dumper=MyDumper)
outfile.close()
except:
raise RuntimeError("Can't write in File" + filename)
def _get_bin_centers(self, ax):
""" calculates the bin centers from the bin edges
:param ax: bin center axis
:return: list of bin centers
"""
bin = []
for i in range(1, ax.size):
axval = round((ax[i]+ax[i-1])/2, 14)
bin.append(axval)
return bin
class MyDumper(Dumper):
""" regulates the indent for yaml Dumper
"""
def increase_indent(self, flow=False, indentless=False):
return super(MyDumper, self).increase_indent(flow, False)
class FlowOrderedDict(OrderedDict):
""" Helper class to switch between flow style and no flow style
Equal to OrderedDict class but other yaml representer
"""
pass
class FlowList(list):
""" Helper class to switch between flow style and no flow style
Equal to list class but other yaml representer
"""
pass
def _flow_list_rep(dumper, data):
"""Yaml representer for list in flow style
"""
return dumper.represent_sequence(u'tag:yaml.org,2002:seq', data, flow_style=True)
def _flow_ord_dic_rep(dumper, data):
"""Yaml representer for OrderedDict in flow style
"""
return dumper.represent_mapping(u'tag:yaml.org,2002:map', data, flow_style=True)
def _represent_ordered_dict(dumper, data):
"""Yaml representer for OrderedDict
regulates dumping for class OrderedDict
"""
value = []
for item_key, item_value in data.items():
node_key = dumper.represent_data(item_key)
node_value = dumper.represent_data(item_value)
value.append((node_key, node_value))
return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', value)
# Adding representers to yaml
yaml.add_representer(OrderedDict, _represent_ordered_dict)
yaml.add_representer(FlowList, _flow_list_rep)
yaml.add_representer(FlowOrderedDict, _flow_ord_dic_rep)
#---------------------------------------------------------------------------------------------------------------------#
AlgorithmFactory.subscribe(SaveYDA)