forked from AmbaPant/mantid
-
Notifications
You must be signed in to change notification settings - Fork 1
/
SaveGSSCW.py
230 lines (186 loc) · 7.67 KB
/
SaveGSSCW.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
# 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 +
#pylint: disable=no-init,invalid-name
import mantid
import os
import numpy as np
from mantid.simpleapi import ConvertToPointData
class SaveGSSCW(mantid.api.PythonAlgorithm):
def category(self):
return "DataHandling\\Nexus"
def seeAlso(self):
return ["SaveGSSCW"]
def name(self):
return "SaveGSSCW"
def summary(self):
return "Save constant wavelength powder diffraction data to a GSAS file in FXYE format"
def PyInit(self):
self.declareProperty(mantid.api.WorkspaceProperty('InputWorkspace',
'',
mantid.kernel.Direction.Input),
"Workspace to save")
self.declareProperty(mantid.api.FileProperty('OutputFilename',
'',
action=mantid.api.FileAction.Save,
extensions=['.gss']),
doc='Name of the GSAS file to save to')
def validateInputs(self):
"""Validate input properties
(virtual method)
"""
issues = dict()
wkspParamName = 'InputWorkspace'
# FIXME - need to find out the unit of the workspace
allowedUnits = ['Degrees']
# check for units
wksp = self.getProperty(wkspParamName).value
units = wksp.getAxis(0).getUnit().unitID()
if units not in allowedUnits:
allowedUnits = ["'%s'" % unit for unit in allowedUnits]
allowedUnits = ', '.join(allowedUnits)
issues[wkspParamName] = "Only support units %s" % allowedUnits
print(f'[ERROR] Workspace {wksp.name()} with unit {units} is not supported.'
f'Allowed units are {allowedUnits}')
# check output file name: whether user can access and write files
output_file_name_property = 'OutputFilename'
gsas_name = self.getProperty(output_file_name_property).value
try:
temp_file = open(gsas_name, 'w')
except PermissionError as per_err:
# error message
issues[output_file_name_property] = f'User is not allowed to write file {gsas_name} due to ' \
f'{per_err}'
else:
# delete the temp file
temp_file.close()
os.remove(gsas_name)
return issues
def PyExec(self):
"""
Main method to execute the algorithm
"""
# Get input
wksp = self.getProperty('InputWorkspace').value
assert wksp, 'Input workspace cannot be None'
# process workspace to make it a PointData workspace
if wksp.isHistogramData():
wksp_name = wksp.name()
wksp = ConvertToPointData(InputWorkspace=wksp_name, OutputWorkspace=wksp_name)
gsas_content = self._write_gsas_fxye(wksp)
# Get output and write file
gsas_name = self.getProperty('OutputFilename').value
with open(gsas_name, 'w') as gsas_file:
gsas_file.write(gsas_content)
def _write_gsas_fxye(self, workspace):
"""
Write GSAS file from a workspace to FXYE format
Example:
BANK 1 2438 488 CONST 1000.000 5.000 0 0 ESD
287.51 16.96 281.38 13.52 279.80 11.83 282.77 11.89 279.73 13.43
270.69 16.45 270.27 13.23 271.90 11.66 275.57 11.74 286.05 13.66
303.35 17.42 295.64 13.86 292.17 12.09 292.92 12.10 288.03 13.63
277.50 16.66 278.01 13.42 274.65 11.72 267.41 11.56 266.29 13.15
271.27 16.47 273.06 13.29 272.29 11.67 268.96 11.60 260.08 12.93
245.63 15.67 250.87 12.73 258.06 11.36 267.18 11.56 272.39 13.29
Returns
-------
str
GSAS file content
"""
# generate header
header = self._create_gsas_header(workspace)
# generate body
body = self._create_gsas_body(workspace)
# construct the file
gsas_content = self.empty_line(80) + '\n' + header + body
return gsas_content
@staticmethod
def empty_line(width):
"""Create an empty line
Parameters
----------
width: int
line width
Returns
-------
str
A line with space only
"""
space = ''
line = f'{space:{width}s}'
return line
@staticmethod
def _create_gsas_header(workspace):
""" Create GSAS header
Example:
BANK 1 2438 488 CONST 1000.000 5.000 0 0 ESD
Returns
-------
str
header
"""
# data points
num_data_points = len(workspace.readY(0))
# number of lines
num_lines = num_data_points // 5
if num_data_points % 5 > 0:
num_lines += 1
# min 2theta and 2theta step as centi-degree
min_2theta = workspace.readX(0)[0] * 100
delta_2theta = np.mean(workspace.readX(0)[1:] - workspace.readX(0)[:-1]) * 100
# check whether the workspace's 2theta has constant step
delta_2theta_std = np.std(workspace.readX(0)[1:] - workspace.readX(0)[:-1])
if delta_2theta_std > 1E-5:
raise RuntimeError(f'2theta steps are not constant')
min_2theta_str = '%9s' % f'{min_2theta:6.3f}'
delta_2theta_str = '%9s' % f'{delta_2theta:6.3f}'
header = f'BANK 1{num_data_points:5}{num_lines:4} CONST{min_2theta_str}{delta_2theta_str} 0 0 ESD'
# enforce to 80 character with line change
header = '{:80s}\n'.format(header)
return header
@staticmethod
def _create_gsas_body(workspace):
"""Create GSAS file's body containing data only
Returns
-------
str
data body
"""
# Get Y and E
vec_y = workspace.readY(0)
vec_e = workspace.readE(0)
num_data_points = len(vec_y)
# Write
gsas_data = ''
gsas_line = ''
for item_index in range(num_data_points):
# check whether any value exceeds the space list, i.e., larger than 99999.99
if vec_y[item_index] > 99999.99 or vec_e[item_index] > 99999.99:
raise RuntimeError(f'{item_index}-th data point: Y = {vec_y[item_index]}, E = {vec_e[item_index]}'
f'. Exceeding limit 99999.99 as GSAS F5.2 format limit.')
# format Y_i and E_i
y_value = '%8s' % f'{vec_y[item_index]:5.2f}'
e_value = '%8s' % f'{vec_e[item_index]:5.2f}'
gsas_line += f'{y_value}{e_value}'
# new line?
if (item_index + 1) % 5 == 0:
# check line width
assert len(gsas_line) == 80
# append
gsas_data += f'{gsas_line}\n'
# reset gsas line
gsas_line = ''
# remove end of line if it is the last character
if gsas_line == '':
# reset gsas line, then the last character must be '\n'
gsas_data = gsas_data[:-1]
else:
# in this case, gsas line cannot empty
gsas_data += '{:80s}'.format(gsas_line)
return gsas_data
# Register the algorithm
mantid.api.AlgorithmFactory.subscribe(SaveGSSCW)