ChessAnalysisPipeline 0.0.11__py3-none-any.whl → 0.0.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ChessAnalysisPipeline might be problematic. Click here for more details.
- CHAP/__init__.py +2 -0
- CHAP/common/__init__.py +6 -2
- CHAP/common/models/map.py +217 -70
- CHAP/common/processor.py +249 -155
- CHAP/common/reader.py +175 -130
- CHAP/common/writer.py +150 -94
- CHAP/edd/models.py +458 -262
- CHAP/edd/processor.py +614 -354
- CHAP/edd/utils.py +746 -235
- CHAP/tomo/models.py +22 -18
- CHAP/tomo/processor.py +1215 -892
- CHAP/utils/fit.py +211 -127
- CHAP/utils/general.py +789 -610
- CHAP/utils/parfile.py +1 -9
- CHAP/utils/scanparsers.py +101 -52
- {ChessAnalysisPipeline-0.0.11.dist-info → ChessAnalysisPipeline-0.0.13.dist-info}/METADATA +1 -1
- {ChessAnalysisPipeline-0.0.11.dist-info → ChessAnalysisPipeline-0.0.13.dist-info}/RECORD +21 -21
- {ChessAnalysisPipeline-0.0.11.dist-info → ChessAnalysisPipeline-0.0.13.dist-info}/WHEEL +1 -1
- {ChessAnalysisPipeline-0.0.11.dist-info → ChessAnalysisPipeline-0.0.13.dist-info}/LICENSE +0 -0
- {ChessAnalysisPipeline-0.0.11.dist-info → ChessAnalysisPipeline-0.0.13.dist-info}/entry_points.txt +0 -0
- {ChessAnalysisPipeline-0.0.11.dist-info → ChessAnalysisPipeline-0.0.13.dist-info}/top_level.txt +0 -0
CHAP/edd/processor.py
CHANGED
|
@@ -7,14 +7,15 @@ Author : Keara Soloway, Rolf Verberg
|
|
|
7
7
|
Description: Module for Processors used only by EDD experiments
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
#
|
|
10
|
+
# System modules
|
|
11
|
+
from copy import deepcopy
|
|
11
12
|
from json import dumps
|
|
12
13
|
import os
|
|
13
14
|
|
|
14
|
-
#
|
|
15
|
+
# Third party modules
|
|
15
16
|
import numpy as np
|
|
16
17
|
|
|
17
|
-
#
|
|
18
|
+
# Local modules
|
|
18
19
|
from CHAP.processor import Processor
|
|
19
20
|
|
|
20
21
|
|
|
@@ -30,29 +31,30 @@ class DiffractionVolumeLengthProcessor(Processor):
|
|
|
30
31
|
outputdir='.',
|
|
31
32
|
inputdir='.',
|
|
32
33
|
interactive=False):
|
|
33
|
-
"""Return calculated value of the DV length.
|
|
34
|
+
"""Return the calculated value of the DV length.
|
|
34
35
|
|
|
35
|
-
:param data:
|
|
36
|
+
:param data: Input configuration for the raw scan data & DVL
|
|
36
37
|
calculation procedure.
|
|
37
38
|
:type data: list[PipelineData]
|
|
38
|
-
:param config:
|
|
39
|
+
:param config: Initialization parameters for an instance of
|
|
39
40
|
CHAP.edd.models.DiffractionVolumeLengthConfig, defaults to
|
|
40
|
-
None
|
|
41
|
+
`None`.
|
|
41
42
|
:type config: dict, optional
|
|
42
|
-
:param save_figures:
|
|
43
|
-
outputs of this Processor, defaults to False
|
|
43
|
+
:param save_figures: Save .pngs of plots for checking inputs &
|
|
44
|
+
outputs of this Processor, defaults to False.
|
|
44
45
|
:type save_figures: bool, optional
|
|
45
|
-
:param outputdir:
|
|
46
|
+
:param outputdir: Directory to which any output figures will
|
|
46
47
|
be saved, defaults to '.'
|
|
47
48
|
:type outputdir: str, optional
|
|
48
|
-
:param inputdir:
|
|
49
|
-
input configuration are not absolute paths
|
|
50
|
-
|
|
49
|
+
:param inputdir: Input directory, used only if files in the
|
|
50
|
+
input configuration are not absolute paths,
|
|
51
|
+
defaults to '.'.
|
|
51
52
|
:type inputdir: str, optional
|
|
52
|
-
:param interactive:
|
|
53
|
-
False
|
|
53
|
+
:param interactive: Allows for user interactions, defaults to
|
|
54
|
+
False.
|
|
54
55
|
:type interactive: bool, optional
|
|
55
|
-
:
|
|
56
|
+
:raises RuntimeError: Unable to get a valid DVL configuration.
|
|
57
|
+
:return: Complete DVL configuraiton dictionary.
|
|
56
58
|
:rtype: dict
|
|
57
59
|
"""
|
|
58
60
|
|
|
@@ -64,7 +66,9 @@ class DiffractionVolumeLengthProcessor(Processor):
|
|
|
64
66
|
self.logger.info('No valid DVL config in input pipeline data, '
|
|
65
67
|
+ 'using config parameter instead.')
|
|
66
68
|
try:
|
|
69
|
+
# Local modules
|
|
67
70
|
from CHAP.edd.models import DiffractionVolumeLengthConfig
|
|
71
|
+
|
|
68
72
|
dvl_config = DiffractionVolumeLengthConfig(
|
|
69
73
|
**config, inputdir=inputdir)
|
|
70
74
|
except Exception as dict_exc:
|
|
@@ -93,53 +97,60 @@ class DiffractionVolumeLengthProcessor(Processor):
|
|
|
93
97
|
computed diffraction volume length is approximately equal to
|
|
94
98
|
the standard deviation of the fitted peak.
|
|
95
99
|
|
|
96
|
-
:param dvl_config:
|
|
97
|
-
procedure
|
|
98
|
-
:type dvl_config: DiffractionVolumeLengthConfig
|
|
99
|
-
:param detector: A single MCA detector element configuration
|
|
100
|
-
:type detector:
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
:param dvl_config: Configuration for the DVL calculation
|
|
101
|
+
procedure.
|
|
102
|
+
:type dvl_config: CHAP.edd.models.DiffractionVolumeLengthConfig
|
|
103
|
+
:param detector: A single MCA detector element configuration.
|
|
104
|
+
:type detector:
|
|
105
|
+
CHAP.edd.models.MCAElementDiffractionVolumeLengthConfig
|
|
106
|
+
:param save_figures: Save .pngs of plots for checking inputs &
|
|
107
|
+
outputs of this Processor, defaults to False.
|
|
103
108
|
:type save_figures: bool, optional
|
|
104
|
-
:param outputdir:
|
|
105
|
-
be saved, defaults to '.'
|
|
109
|
+
:param outputdir: Directory to which any output figures will
|
|
110
|
+
be saved, defaults to '.'.
|
|
106
111
|
:type outputdir: str, optional
|
|
107
|
-
:param interactive:
|
|
108
|
-
False
|
|
112
|
+
:param interactive: Allows for user interactions, defaults to
|
|
113
|
+
False.
|
|
109
114
|
:type interactive: bool, optional
|
|
110
|
-
:
|
|
115
|
+
:raises ValueError: No value provided for included bin ranges
|
|
116
|
+
for the MCA detector element.
|
|
117
|
+
:return: Calculated diffraction volume length.
|
|
111
118
|
:rtype: float
|
|
112
119
|
"""
|
|
113
|
-
|
|
120
|
+
# Local modules
|
|
114
121
|
from CHAP.utils.fit import Fit
|
|
115
|
-
from CHAP.utils.general import
|
|
122
|
+
from CHAP.utils.general import (
|
|
123
|
+
index_nearest,
|
|
124
|
+
select_mask_1d,
|
|
125
|
+
)
|
|
116
126
|
|
|
117
127
|
# Get raw MCA data from raster scan
|
|
118
128
|
mca_data = dvl_config.mca_data(detector)
|
|
119
129
|
|
|
120
130
|
# Interactively set mask, if needed & possible.
|
|
121
131
|
if interactive or save_figures:
|
|
132
|
+
# Third party modules
|
|
133
|
+
import matplotlib.pyplot as plt
|
|
134
|
+
|
|
122
135
|
self.logger.info(
|
|
123
136
|
'Interactively select a mask in the matplotlib figure')
|
|
124
|
-
mask, include_bin_ranges
|
|
137
|
+
fig, mask, include_bin_ranges = select_mask_1d(
|
|
125
138
|
np.sum(mca_data, axis=0),
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
title='Click and drag to select
|
|
130
|
-
|
|
139
|
+
x = np.arange(detector.num_bins),
|
|
140
|
+
label='Sum of MCA spectra over all scan points',
|
|
141
|
+
preselected_index_ranges=detector.include_bin_ranges,
|
|
142
|
+
title='Click and drag to select data range to include when '
|
|
143
|
+
'measuring diffraction volume length',
|
|
131
144
|
xlabel='MCA channel (index)',
|
|
132
145
|
ylabel='MCA intensity (counts)',
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
)
|
|
146
|
+
min_num_index_ranges=1,
|
|
147
|
+
interactive=interactive)
|
|
136
148
|
detector.include_bin_ranges = include_bin_ranges
|
|
137
149
|
self.logger.debug('Mask selected. Including detector bin ranges: '
|
|
138
150
|
+ str(detector.include_bin_ranges))
|
|
139
151
|
if save_figures:
|
|
140
|
-
|
|
152
|
+
fig.savefig(os.path.join(
|
|
141
153
|
outputdir, f'{detector.detector_name}_dvl_mask.png'))
|
|
142
|
-
import matplotlib.pyplot as plt
|
|
143
154
|
plt.close()
|
|
144
155
|
if detector.include_bin_ranges is None:
|
|
145
156
|
raise ValueError(
|
|
@@ -154,10 +165,7 @@ class DiffractionVolumeLengthProcessor(Processor):
|
|
|
154
165
|
# 3) sum of intensities in detector bins after mask is applied
|
|
155
166
|
unmasked_sum = np.sum(mca_data, axis=1)
|
|
156
167
|
mask = detector.mca_mask()
|
|
157
|
-
masked_mca_data =
|
|
158
|
-
(mca_data.shape[0], *mca_data[0][mask].shape))
|
|
159
|
-
for i in range(mca_data.shape[0]):
|
|
160
|
-
masked_mca_data[i] = mca_data[i][mask]
|
|
168
|
+
masked_mca_data = mca_data[:,mask]
|
|
161
169
|
masked_max = np.amax(masked_mca_data, axis=1)
|
|
162
170
|
masked_sum = np.sum(masked_mca_data, axis=1)
|
|
163
171
|
|
|
@@ -167,31 +175,38 @@ class DiffractionVolumeLengthProcessor(Processor):
|
|
|
167
175
|
scan_center = np.sum(scanned_vals * masked_sum) / np.sum(masked_sum)
|
|
168
176
|
x = scanned_vals - scan_center
|
|
169
177
|
|
|
170
|
-
#
|
|
171
|
-
|
|
172
|
-
|
|
178
|
+
# Normalize the data
|
|
179
|
+
unmasked_sum = unmasked_sum / max(unmasked_sum)
|
|
180
|
+
masked_max = masked_max / max(masked_max)
|
|
181
|
+
masked_sum = masked_sum / max(masked_sum)
|
|
182
|
+
|
|
183
|
+
# Fit the masked summed data with a gaussian
|
|
184
|
+
fit = Fit.fit_data(masked_sum, ('constant', 'gaussian'), x=x)
|
|
173
185
|
|
|
174
186
|
# Calculate / manually select diffraction volume length
|
|
175
187
|
dvl = fit.best_values['sigma'] * detector.sigma_to_dvl_factor
|
|
176
188
|
if detector.measurement_mode == 'manual':
|
|
177
189
|
if interactive:
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
label='
|
|
181
|
-
ref_data=[
|
|
182
|
-
((x, fit.best_fit),
|
|
183
|
-
{'label': 'gaussian fit (to total)'}),
|
|
184
|
-
((x, masked_max
|
|
185
|
-
{'label': 'maximum (masked)'}),
|
|
186
|
-
((x, unmasked_sum
|
|
187
|
-
{'label': 'total (unmasked)'})
|
|
188
|
-
],
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
190
|
+
_, _, dvl_bounds = select_mask_1d(
|
|
191
|
+
masked_sum, x=x,
|
|
192
|
+
label='Total (masked & normalized)',
|
|
193
|
+
#RV TODO ref_data=[
|
|
194
|
+
# ((x, fit.best_fit),
|
|
195
|
+
# {'label': 'gaussian fit (to total)'}),
|
|
196
|
+
# ((x, masked_max),
|
|
197
|
+
# {'label': 'maximum (masked)'}),
|
|
198
|
+
# ((x, unmasked_sum),
|
|
199
|
+
# {'label': 'total (unmasked)'})
|
|
200
|
+
# ],
|
|
201
|
+
preselected_index_ranges=[
|
|
202
|
+
(index_nearest(x, -dvl/2), index_nearest(x, dvl/2))],
|
|
203
|
+
title=('Click and drag to indicate the boundary '
|
|
204
|
+
'of the diffraction volume'),
|
|
192
205
|
xlabel=(dvl_config.scanned_dim_lbl
|
|
193
206
|
+ ' (offset from scan "center")'),
|
|
194
|
-
ylabel='MCA intensity (normalized)'
|
|
207
|
+
ylabel='MCA intensity (normalized)',
|
|
208
|
+
min_num_index_ranges=1,
|
|
209
|
+
max_num_index_ranges=1)
|
|
195
210
|
dvl_bounds = dvl_bounds[0]
|
|
196
211
|
dvl = abs(x[dvl_bounds[1]] - x[dvl_bounds[0]])
|
|
197
212
|
else:
|
|
@@ -201,24 +216,29 @@ class DiffractionVolumeLengthProcessor(Processor):
|
|
|
201
216
|
+ 'Using default DVL calcluation instead.')
|
|
202
217
|
|
|
203
218
|
if interactive or save_figures:
|
|
219
|
+
# Third party modules
|
|
204
220
|
import matplotlib.pyplot as plt
|
|
221
|
+
|
|
205
222
|
fig, ax = plt.subplots()
|
|
206
223
|
ax.set_title(f'Diffraction Volume ({detector.detector_name})')
|
|
207
224
|
ax.set_xlabel(dvl_config.scanned_dim_lbl \
|
|
208
225
|
+ ' (offset from scan "center")')
|
|
209
226
|
ax.set_ylabel('MCA intensity (normalized)')
|
|
210
|
-
ax.plot(x,
|
|
227
|
+
ax.plot(x, masked_sum, label='total (masked & normalized)')
|
|
211
228
|
ax.plot(x, fit.best_fit, label='gaussian fit (to total)')
|
|
212
|
-
ax.plot(x, masked_max
|
|
213
|
-
|
|
214
|
-
ax.plot(x, unmasked_sum / max(unmasked_sum),
|
|
215
|
-
label='total (unmasked)')
|
|
229
|
+
ax.plot(x, masked_max, label='maximum (masked)')
|
|
230
|
+
ax.plot(x, unmasked_sum, label='total (unmasked)')
|
|
216
231
|
ax.axvspan(-dvl / 2., dvl / 2.,
|
|
217
232
|
color='gray', alpha=0.5,
|
|
218
233
|
label='diffraction volume'
|
|
219
234
|
+ f' ({detector.measurement_mode})')
|
|
220
235
|
ax.legend()
|
|
221
|
-
|
|
236
|
+
ax.text(
|
|
237
|
+
0, 1,
|
|
238
|
+
f'Diffraction volume length: {dvl:.2f}',
|
|
239
|
+
ha='left', va='top',
|
|
240
|
+
#transform=ax.get_xaxis_transform())
|
|
241
|
+
transform=ax.transAxes)
|
|
222
242
|
if save_figures:
|
|
223
243
|
figfile = os.path.join(outputdir,
|
|
224
244
|
f'{detector.detector_name}_dvl.png')
|
|
@@ -245,40 +265,42 @@ class MCACeriaCalibrationProcessor(Processor):
|
|
|
245
265
|
"""Return tuned values for 2&theta and linear correction
|
|
246
266
|
parameters for the MCA channel energies.
|
|
247
267
|
|
|
248
|
-
:param data:
|
|
249
|
-
procedure
|
|
250
|
-
:type data: list[
|
|
251
|
-
:param config:
|
|
268
|
+
:param data: Input configuration for the raw data & tuning
|
|
269
|
+
procedure.
|
|
270
|
+
:type data: list[PipelineData]
|
|
271
|
+
:param config: Initialization parameters for an instance of
|
|
252
272
|
CHAP.edd.models.MCACeriaCalibrationConfig, defaults to
|
|
253
|
-
None
|
|
273
|
+
None.
|
|
254
274
|
:type config: dict, optional
|
|
255
|
-
:param save_figures:
|
|
256
|
-
outputs of this Processor, defaults to False
|
|
275
|
+
:param save_figures: Save .pngs of plots for checking inputs &
|
|
276
|
+
outputs of this Processor, defaults to False.
|
|
257
277
|
:type save_figures: bool, optional
|
|
258
|
-
:param outputdir:
|
|
259
|
-
be saved, defaults to '.'
|
|
278
|
+
:param outputdir: Directory to which any output figures will
|
|
279
|
+
be saved, defaults to '.'.
|
|
260
280
|
:type outputdir: str, optional
|
|
261
|
-
:param inputdir:
|
|
262
|
-
input configuration are not absolute paths
|
|
263
|
-
|
|
281
|
+
:param inputdir: Input directory, used only if files in the
|
|
282
|
+
input configuration are not absolute paths,
|
|
283
|
+
defaults to '.'.
|
|
264
284
|
:type inputdir: str, optional
|
|
265
|
-
:param interactive:
|
|
266
|
-
False
|
|
285
|
+
:param interactive: Allows for user interactions, defaults to
|
|
286
|
+
False.
|
|
267
287
|
:type interactive: bool, optional
|
|
268
|
-
:
|
|
269
|
-
|
|
288
|
+
:raises RuntimeError: Invalid or missing input configuration.
|
|
289
|
+
:return: Original configuration with the tuned values for
|
|
290
|
+
2&theta and the linear correction parameters added.
|
|
270
291
|
:rtype: dict[str,float]
|
|
271
292
|
"""
|
|
272
|
-
|
|
273
293
|
try:
|
|
274
294
|
calibration_config = self.get_config(
|
|
275
295
|
data, 'edd.models.MCACeriaCalibrationConfig',
|
|
276
296
|
inputdir=inputdir)
|
|
277
297
|
except Exception as data_exc:
|
|
278
298
|
self.logger.info('No valid calibration config in input pipeline '
|
|
279
|
-
|
|
299
|
+
'data, using config parameter instead.')
|
|
280
300
|
try:
|
|
301
|
+
# Local modules
|
|
281
302
|
from CHAP.edd.models import MCACeriaCalibrationConfig
|
|
303
|
+
|
|
282
304
|
calibration_config = MCACeriaCalibrationConfig(
|
|
283
305
|
**config, inputdir=inputdir)
|
|
284
306
|
except Exception as dict_exc:
|
|
@@ -286,10 +308,8 @@ class MCACeriaCalibrationProcessor(Processor):
|
|
|
286
308
|
|
|
287
309
|
for detector in calibration_config.detectors:
|
|
288
310
|
tth, slope, intercept = self.calibrate(
|
|
289
|
-
calibration_config, detector,
|
|
290
|
-
save_figures=save_figures,
|
|
311
|
+
calibration_config, detector, save_figures=save_figures,
|
|
291
312
|
interactive=interactive, outputdir=outputdir)
|
|
292
|
-
|
|
293
313
|
detector.tth_calibrated = tth
|
|
294
314
|
detector.slope_calibrated = slope
|
|
295
315
|
detector.intercept_calibrated = intercept
|
|
@@ -305,63 +325,91 @@ class MCACeriaCalibrationProcessor(Processor):
|
|
|
305
325
|
"""Iteratively calibrate 2&theta by fitting selected peaks of
|
|
306
326
|
an MCA spectrum until the computed strain is sufficiently
|
|
307
327
|
small. Use the fitted peak locations to determine linear
|
|
308
|
-
correction parameters for the MCA
|
|
328
|
+
correction parameters for the MCA channel energies.
|
|
309
329
|
|
|
310
|
-
:param calibration_config:
|
|
311
|
-
calibration procedure
|
|
312
|
-
:type calibration_config:
|
|
313
|
-
|
|
330
|
+
:param calibration_config: Object configuring the CeO2
|
|
331
|
+
calibration procedure for an MCA detector.
|
|
332
|
+
:type calibration_config:
|
|
333
|
+
CHAP.edd.models.MCACeriaCalibrationConfig
|
|
334
|
+
:param detector: A single MCA detector element configuration.
|
|
314
335
|
:type detector: CHAP.edd.models.MCAElementCalibrationConfig
|
|
315
|
-
:param save_figures:
|
|
316
|
-
outputs of this Processor, defaults to False
|
|
336
|
+
:param save_figures: Save .pngs of plots for checking inputs &
|
|
337
|
+
outputs of this Processor, defaults to False.
|
|
317
338
|
:type save_figures: bool, optional
|
|
318
|
-
:param outputdir:
|
|
319
|
-
be saved, defaults to '.'
|
|
339
|
+
:param outputdir: Directory to which any output figures will
|
|
340
|
+
be saved, defaults to '.'.
|
|
320
341
|
:type outputdir: str, optional
|
|
321
|
-
:param interactive:
|
|
322
|
-
False
|
|
342
|
+
:param interactive: Allows for user interactions, defaults to
|
|
343
|
+
False.
|
|
323
344
|
:type interactive: bool, optional
|
|
324
|
-
:
|
|
325
|
-
|
|
326
|
-
|
|
345
|
+
:raises ValueError: No value provided for included bin ranges
|
|
346
|
+
or the fitted HKLs for the MCA detector element.
|
|
347
|
+
:return: Calibrated values of 2&theta and the linear correction
|
|
348
|
+
parameters for MCA channel energies: tth, slope, intercept.
|
|
327
349
|
:rtype: float, float, float
|
|
328
350
|
"""
|
|
329
|
-
|
|
351
|
+
# Local modules
|
|
352
|
+
from CHAP.edd.utils import get_peak_locations
|
|
330
353
|
from CHAP.utils.fit import Fit
|
|
331
354
|
|
|
355
|
+
# Get the unique HKLs and lattice spacings for the calibration
|
|
356
|
+
# material
|
|
357
|
+
hkls, ds = calibration_config.material.unique_hkls_ds(
|
|
358
|
+
tth_tol=detector.hkl_tth_tol, tth_max=detector.tth_max)
|
|
359
|
+
|
|
332
360
|
# Collect raw MCA data of interest
|
|
361
|
+
mca_bin_energies = np.linspace(
|
|
362
|
+
0, detector.max_energy_kev, detector.num_bins)
|
|
333
363
|
mca_data = calibration_config.mca_data(detector)
|
|
334
|
-
mca_bin_energies = np.arange(0, detector.num_bins) \
|
|
335
|
-
* (detector.max_energy_kev / detector.num_bins)
|
|
336
|
-
|
|
337
|
-
if interactive:
|
|
338
|
-
# Interactively adjust initial tth guess
|
|
339
|
-
from CHAP.edd.utils import select_tth_initial_guess
|
|
340
|
-
select_tth_initial_guess(detector, calibration_config.material,
|
|
341
|
-
mca_data, mca_bin_energies)
|
|
342
|
-
self.logger.debug(f'tth_initial_guess = {detector.tth_initial_guess}')
|
|
343
364
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
365
|
+
if interactive or save_figures:
|
|
366
|
+
# Third party modules
|
|
367
|
+
import matplotlib.pyplot as plt
|
|
368
|
+
|
|
369
|
+
# Local modules
|
|
370
|
+
from CHAP.edd.utils import (
|
|
371
|
+
select_tth_initial_guess,
|
|
372
|
+
select_mask_and_hkls,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
# Adjust initial tth guess
|
|
376
|
+
fig, detector.tth_initial_guess = select_tth_initial_guess(
|
|
377
|
+
mca_bin_energies, mca_data, hkls, ds,
|
|
378
|
+
detector.tth_initial_guess, interactive)
|
|
379
|
+
if save_figures:
|
|
380
|
+
fig.savefig(os.path.join(
|
|
381
|
+
outputdir,
|
|
382
|
+
f'{detector.detector_name}_calibration_'
|
|
383
|
+
'tth_initial_guess.png'))
|
|
384
|
+
plt.close()
|
|
385
|
+
|
|
386
|
+
# Select mask & HKLs for fitting
|
|
387
|
+
fig, include_bin_ranges, hkl_indices = select_mask_and_hkls(
|
|
388
|
+
mca_bin_energies, mca_data, hkls, ds,
|
|
389
|
+
detector.tth_initial_guess, detector.include_bin_ranges,
|
|
390
|
+
detector.hkl_indices, detector.detector_name,
|
|
391
|
+
flux_energy_range=calibration_config.flux_file_energy_range,
|
|
392
|
+
interactive=interactive)
|
|
357
393
|
detector.include_bin_ranges = include_bin_ranges
|
|
358
|
-
|
|
359
|
-
|
|
394
|
+
detector.hkl_indices = hkl_indices
|
|
395
|
+
if save_figures:
|
|
396
|
+
fig.savefig(os.path.join(
|
|
397
|
+
outputdir,
|
|
398
|
+
f'{detector.detector_name}_calibration_fit_mask_hkls.png'))
|
|
399
|
+
plt.close()
|
|
400
|
+
self.logger.debug(f'tth_initial_guess = {detector.tth_initial_guess}')
|
|
401
|
+
self.logger.debug(
|
|
402
|
+
f'include_bin_ranges = {detector.include_bin_ranges}')
|
|
360
403
|
if detector.include_bin_ranges is None:
|
|
361
404
|
raise ValueError(
|
|
362
405
|
'No value provided for include_bin_ranges. '
|
|
363
406
|
'Provide them in the MCA Ceria Calibration Configuration, '
|
|
364
407
|
'or re-run the pipeline with the --interactive flag.')
|
|
408
|
+
if detector.hkl_indices is None:
|
|
409
|
+
raise ValueError(
|
|
410
|
+
'No value provided for hkl_indices. Provide them in '
|
|
411
|
+
'the detector\'s MCA Ceria Calibration Configuration, or'
|
|
412
|
+
' re-run the pipeline with the --interactive flag.')
|
|
365
413
|
mca_mask = detector.mca_mask()
|
|
366
414
|
fit_mca_energies = mca_bin_energies[mca_mask]
|
|
367
415
|
fit_mca_intensities = mca_data[mca_mask]
|
|
@@ -374,26 +422,10 @@ class MCACeriaCalibrationProcessor(Processor):
|
|
|
374
422
|
|
|
375
423
|
# Get the HKLs and lattice spacings that will be used for
|
|
376
424
|
# fitting
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
import matplotlib.pyplot as plt
|
|
380
|
-
from CHAP.edd.utils import select_hkls
|
|
381
|
-
fig = select_hkls(detector, [calibration_config.material], tth,
|
|
382
|
-
mca_data, mca_bin_energies, interactive)
|
|
383
|
-
if save_figures:
|
|
384
|
-
fig.savefig(os.path.join(
|
|
385
|
-
outputdir,
|
|
386
|
-
f'{detector.detector_name}_calibration_hkls.png'))
|
|
387
|
-
plt.close()
|
|
388
|
-
self.logger.debug(f'HKLs selected: {detector.fit_hkls}')
|
|
389
|
-
if detector.fit_hkls is None:
|
|
390
|
-
raise ValueError(
|
|
391
|
-
'No value provided for fit_hkls. Provide them in '
|
|
392
|
-
'the detector\'s MCA Ceria Calibration Configuration, or'
|
|
393
|
-
' re-run the pipeline with the --interactive flag.')
|
|
394
|
-
fit_hkls, fit_ds = detector.fit_ds(calibration_config.material)
|
|
425
|
+
fit_hkls = np.asarray([hkls[i] for i in detector.hkl_indices])
|
|
426
|
+
fit_ds = np.asarray([ds[i] for i in detector.hkl_indices])
|
|
395
427
|
c_1 = fit_hkls[:,0]**2 + fit_hkls[:,1]**2 + fit_hkls[:,2]**2
|
|
396
|
-
|
|
428
|
+
tth = detector.tth_initial_guess
|
|
397
429
|
for iter_i in range(calibration_config.max_iter):
|
|
398
430
|
self.logger.debug(f'Tuning tth: iteration no. {iter_i}, '
|
|
399
431
|
+ f'starting tth value = {tth} ')
|
|
@@ -402,19 +434,20 @@ class MCACeriaCalibrationProcessor(Processor):
|
|
|
402
434
|
|
|
403
435
|
# Get expected peak energy locations for this iteration's
|
|
404
436
|
# starting value of tth
|
|
405
|
-
|
|
406
|
-
fit_E0 = hc / fit_lambda
|
|
437
|
+
fit_E0 = get_peak_locations(fit_ds, tth)
|
|
407
438
|
|
|
408
439
|
# Run the uniform fit
|
|
409
440
|
uniform_fit = Fit(fit_mca_intensities, x=fit_mca_energies)
|
|
410
|
-
uniform_fit.create_multipeak_model(
|
|
441
|
+
uniform_fit.create_multipeak_model(
|
|
442
|
+
fit_E0, fit_type='uniform')
|
|
443
|
+
#fit_E0, fit_type='uniform', background='constant')
|
|
411
444
|
uniform_fit.fit()
|
|
412
445
|
|
|
413
446
|
# Extract values of interest from the best values for the
|
|
414
447
|
# uniform fit parameters
|
|
415
448
|
uniform_fit_centers = [
|
|
416
449
|
uniform_fit.best_values[f'peak{i+1}_center']
|
|
417
|
-
for i in range(len(
|
|
450
|
+
for i in range(len(fit_hkls))]
|
|
418
451
|
uniform_a = uniform_fit.best_values['scale_factor']
|
|
419
452
|
uniform_strain = np.log(
|
|
420
453
|
(uniform_a
|
|
@@ -427,16 +460,17 @@ class MCACeriaCalibrationProcessor(Processor):
|
|
|
427
460
|
# fit
|
|
428
461
|
unconstrained_fit = Fit(fit_mca_intensities, x=fit_mca_energies)
|
|
429
462
|
unconstrained_fit.create_multipeak_model(
|
|
430
|
-
uniform_fit_centers, fit_type='unconstrained'
|
|
463
|
+
uniform_fit_centers, fit_type='unconstrained',
|
|
464
|
+
)#background='constant')
|
|
431
465
|
unconstrained_fit.fit()
|
|
432
466
|
|
|
433
467
|
# Extract values of interest from the best values for the
|
|
434
468
|
# unconstrained fit parameters
|
|
435
469
|
unconstrained_fit_centers = np.array(
|
|
436
470
|
[unconstrained_fit.best_values[f'peak{i+1}_center']
|
|
437
|
-
for i in range(len(
|
|
438
|
-
unconstrained_a =
|
|
439
|
-
|
|
471
|
+
for i in range(len(fit_hkls))])
|
|
472
|
+
unconstrained_a = np.sqrt(c_1)*abs(get_peak_locations(
|
|
473
|
+
unconstrained_fit_centers, tth))
|
|
440
474
|
unconstrained_strains = np.log(
|
|
441
475
|
(unconstrained_a
|
|
442
476
|
/ calibration_config.material.lattice_parameters))
|
|
@@ -455,14 +489,14 @@ class MCACeriaCalibrationProcessor(Processor):
|
|
|
455
489
|
# Fit line to expected / computed peak locations from the last
|
|
456
490
|
# unconstrained fit.
|
|
457
491
|
fit = Fit.fit_data(
|
|
458
|
-
fit_E0,
|
|
459
|
-
'linear',
|
|
460
|
-
x=unconstrained_fit_centers,
|
|
461
|
-
nan_policy='omit')
|
|
492
|
+
fit_E0, 'linear', x=unconstrained_fit_centers, nan_policy='omit')
|
|
462
493
|
slope = fit.best_values['slope']
|
|
463
494
|
intercept = fit.best_values['intercept']
|
|
464
495
|
|
|
465
496
|
if interactive or save_figures:
|
|
497
|
+
# Third party modules
|
|
498
|
+
import matplotlib.pyplot as plt
|
|
499
|
+
|
|
466
500
|
fig, axs = plt.subplots(2, 2, sharex='all', figsize=(11, 8.5))
|
|
467
501
|
|
|
468
502
|
# Upper left axes: Input data & best fits
|
|
@@ -541,22 +575,30 @@ class MCADataProcessor(Processor):
|
|
|
541
575
|
transformed according to the results of a ceria calibration.
|
|
542
576
|
"""
|
|
543
577
|
|
|
544
|
-
def process(self,
|
|
578
|
+
def process(self,
|
|
579
|
+
data,
|
|
580
|
+
config=None,
|
|
581
|
+
save_figures=False,
|
|
582
|
+
outputdir='.',
|
|
583
|
+
inputdir='.',
|
|
584
|
+
interactive=False):
|
|
545
585
|
"""Process configurations for a map and MCA detector(s), and
|
|
546
586
|
return the calibrated MCA data collected over the map.
|
|
547
587
|
|
|
548
|
-
:param data:
|
|
549
|
-
calibration
|
|
588
|
+
:param data: Input map configuration and results of ceria
|
|
589
|
+
calibration.
|
|
550
590
|
:type data: list[dict[str,object]]
|
|
551
|
-
:return:
|
|
591
|
+
:return: Calibrated and flux-corrected MCA data.
|
|
552
592
|
:rtype: nexusformat.nexus.NXentry
|
|
553
593
|
"""
|
|
554
594
|
|
|
595
|
+
print(f'data:\n{data}')
|
|
596
|
+
exit('Done Here')
|
|
555
597
|
map_config = self.get_config(
|
|
556
|
-
data, 'common.models.map.MapConfig')
|
|
557
|
-
|
|
558
|
-
data, 'edd.models.MCACeriaCalibrationConfig')
|
|
559
|
-
nxroot = self.get_nxroot(map_config,
|
|
598
|
+
data, 'common.models.map.MapConfig', inputdir=inputdir)
|
|
599
|
+
ceria_calibration_config = self.get_config(
|
|
600
|
+
data, 'edd.models.MCACeriaCalibrationConfig', inputdir=inputdir)
|
|
601
|
+
nxroot = self.get_nxroot(map_config, ceria_calibration_config)
|
|
560
602
|
|
|
561
603
|
return nxroot
|
|
562
604
|
|
|
@@ -567,20 +609,23 @@ class MCADataProcessor(Processor):
|
|
|
567
609
|
`calibration_config`. The data will be returned along with
|
|
568
610
|
relevant metadata in the form of a NeXus structure.
|
|
569
611
|
|
|
570
|
-
:param map_config:
|
|
571
|
-
:type map_config: MapConfig
|
|
572
|
-
:param calibration_config:
|
|
573
|
-
:type calibration_config:
|
|
574
|
-
|
|
612
|
+
:param map_config: The map configuration.
|
|
613
|
+
:type map_config: CHAP.common.models.MapConfig.
|
|
614
|
+
:param calibration_config: The calibration configuration.
|
|
615
|
+
:type calibration_config:
|
|
616
|
+
CHAP.edd.models.MCACeriaCalibrationConfig
|
|
617
|
+
:return: A map of the calibrated and flux-corrected MCA data.
|
|
575
618
|
:rtype: nexusformat.nexus.NXroot
|
|
576
619
|
"""
|
|
577
|
-
#
|
|
578
|
-
from nexusformat.nexus import (
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
620
|
+
# Third party modules
|
|
621
|
+
from nexusformat.nexus import (
|
|
622
|
+
NXdata,
|
|
623
|
+
NXdetector,
|
|
624
|
+
NXinstrument,
|
|
625
|
+
NXroot,
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
# Local modules
|
|
584
629
|
from CHAP.common import MapProcessor
|
|
585
630
|
|
|
586
631
|
nxroot = NXroot()
|
|
@@ -600,8 +645,7 @@ class MCADataProcessor(Processor):
|
|
|
600
645
|
detector.num_bins))
|
|
601
646
|
nxdata.raw.attrs['units'] = 'counts'
|
|
602
647
|
nxdata.channel_energy = detector.slope_calibrated \
|
|
603
|
-
* np.
|
|
604
|
-
* (detector.max_energy_kev / detector.num_bins) \
|
|
648
|
+
* np.linspace(0, detector.max_energy_kev, detector.num_bins) \
|
|
605
649
|
+ detector.intercept_calibrated
|
|
606
650
|
nxdata.channel_energy.attrs['units'] = 'keV'
|
|
607
651
|
|
|
@@ -642,27 +686,29 @@ class StrainAnalysisProcessor(Processor):
|
|
|
642
686
|
interactive=False):
|
|
643
687
|
"""Return strain analysis maps & associated metadata in an NXprocess.
|
|
644
688
|
|
|
645
|
-
:param data:
|
|
689
|
+
:param data: Input data containing configurations for a map,
|
|
646
690
|
completed ceria calibration, and parameters for strain
|
|
647
691
|
analysis
|
|
648
692
|
:type data: list[PipelineData]
|
|
649
|
-
:param config:
|
|
693
|
+
:param config: Initialization parameters for an instance of
|
|
650
694
|
CHAP.edd.models.StrainAnalysisConfig, defaults to
|
|
651
|
-
None
|
|
695
|
+
None.
|
|
652
696
|
:type config: dict, optional
|
|
653
|
-
:param save_figures:
|
|
654
|
-
outputs of this Processor, defaults to False
|
|
697
|
+
:param save_figures: Save .pngs of plots for checking inputs &
|
|
698
|
+
outputs of this Processor, defaults to False.
|
|
655
699
|
:type save_figures: bool, optional
|
|
656
|
-
:param outputdir:
|
|
657
|
-
be saved, defaults to '.'
|
|
700
|
+
:param outputdir: Directory to which any output figures will
|
|
701
|
+
be saved, defaults to '.'.
|
|
658
702
|
:type outputdir: str, optional
|
|
659
|
-
:param inputdir:
|
|
660
|
-
input configuration are not absolute paths
|
|
661
|
-
|
|
703
|
+
:param inputdir: Input directory, used only if files in the
|
|
704
|
+
input configuration are not absolute paths,
|
|
705
|
+
defaults to '.'.
|
|
662
706
|
:type inputdir: str, optional
|
|
663
|
-
:param interactive:
|
|
664
|
-
False
|
|
707
|
+
:param interactive: Allows for user interactions, defaults to
|
|
708
|
+
False.
|
|
665
709
|
:type interactive: bool, optional
|
|
710
|
+
:raises RuntimeError: Unable to get a valid strain analysis
|
|
711
|
+
configuration.
|
|
666
712
|
:return: NXprocess containing metadata about strain analysis
|
|
667
713
|
processing parameters and empty datasets for strain maps
|
|
668
714
|
to be filled in later.
|
|
@@ -670,17 +716,17 @@ class StrainAnalysisProcessor(Processor):
|
|
|
670
716
|
|
|
671
717
|
"""
|
|
672
718
|
# Get required configuration models from input data
|
|
673
|
-
# map_config = self.get_config(
|
|
674
|
-
# data, 'common.models.map.MapConfig')
|
|
675
719
|
ceria_calibration_config = self.get_config(
|
|
676
720
|
data, 'edd.models.MCACeriaCalibrationConfig', inputdir=inputdir)
|
|
677
721
|
try:
|
|
678
722
|
strain_analysis_config = self.get_config(
|
|
679
|
-
data, 'edd.models.StrainAnalysisConfig')
|
|
723
|
+
data, 'edd.models.StrainAnalysisConfig', inputdir=inputdir)
|
|
680
724
|
except Exception as data_exc:
|
|
725
|
+
# Local modules
|
|
726
|
+
from CHAP.edd.models import StrainAnalysisConfig
|
|
727
|
+
|
|
681
728
|
self.logger.info('No valid strain analysis config in input '
|
|
682
729
|
+ 'pipeline data, using config parameter instead')
|
|
683
|
-
from CHAP.edd.models import StrainAnalysisConfig
|
|
684
730
|
try:
|
|
685
731
|
strain_analysis_config = StrainAnalysisConfig(
|
|
686
732
|
**config, inputdir=inputdir)
|
|
@@ -688,7 +734,6 @@ class StrainAnalysisProcessor(Processor):
|
|
|
688
734
|
raise RuntimeError from dict_exc
|
|
689
735
|
|
|
690
736
|
nxroot = self.get_nxroot(
|
|
691
|
-
#map_config,
|
|
692
737
|
strain_analysis_config.map_config,
|
|
693
738
|
ceria_calibration_config,
|
|
694
739
|
strain_analysis_config,
|
|
@@ -696,8 +741,8 @@ class StrainAnalysisProcessor(Processor):
|
|
|
696
741
|
outputdir=outputdir,
|
|
697
742
|
interactive=interactive)
|
|
698
743
|
self.logger.debug(nxroot.tree)
|
|
699
|
-
return nxroot
|
|
700
744
|
|
|
745
|
+
return nxroot
|
|
701
746
|
|
|
702
747
|
def get_nxroot(self,
|
|
703
748
|
map_config,
|
|
@@ -709,38 +754,70 @@ class StrainAnalysisProcessor(Processor):
|
|
|
709
754
|
"""Return NXroot containing strain maps.
|
|
710
755
|
|
|
711
756
|
|
|
712
|
-
:param map_config:
|
|
757
|
+
:param map_config: The map configuration.
|
|
713
758
|
:type map_config: CHAP.common.models.map.MapConfig
|
|
714
|
-
:param ceria_calibration_config:
|
|
759
|
+
:param ceria_calibration_config: The calibration configuration.
|
|
715
760
|
:type ceria_calibration_config:
|
|
716
761
|
'CHAP.edd.models.MCACeriaCalibrationConfig'
|
|
717
762
|
:param strain_analysis_config: Strain analysis processing
|
|
718
|
-
configuration
|
|
719
|
-
:type strain_analysis_config:
|
|
720
|
-
|
|
721
|
-
|
|
763
|
+
configuration.
|
|
764
|
+
:type strain_analysis_config:
|
|
765
|
+
CHAP.edd.models.StrainAnalysisConfig
|
|
766
|
+
:param save_figures: Save .pngs of plots for checking inputs &
|
|
767
|
+
outputs of this Processor, defaults to False.
|
|
722
768
|
:type save_figures: bool, optional
|
|
723
|
-
:param outputdir:
|
|
724
|
-
be saved, defaults to '.'
|
|
769
|
+
:param outputdir: Directory to which any output figures will
|
|
770
|
+
be saved, defaults to '.'.
|
|
725
771
|
:type outputdir: str, optional
|
|
726
|
-
:param interactive:
|
|
727
|
-
False
|
|
772
|
+
:param interactive: Allows for user interactions, defaults to
|
|
773
|
+
False.
|
|
728
774
|
:type interactive: bool, optional
|
|
729
|
-
:return: NXroot containing strain maps
|
|
775
|
+
:return: NXroot containing strain maps.
|
|
730
776
|
:rtype: nexusformat.nexus.NXroot
|
|
731
777
|
"""
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
778
|
+
# Third party modules
|
|
779
|
+
from nexusformat.nexus import (
|
|
780
|
+
NXcollection,
|
|
781
|
+
NXdata,
|
|
782
|
+
NXdetector,
|
|
783
|
+
NXfield,
|
|
784
|
+
NXparameters,
|
|
785
|
+
NXprocess,
|
|
786
|
+
NXroot,
|
|
787
|
+
)
|
|
788
|
+
|
|
789
|
+
# Local modules
|
|
740
790
|
from CHAP.common import MapProcessor
|
|
741
|
-
from CHAP.edd.utils import
|
|
791
|
+
from CHAP.edd.utils import (
|
|
792
|
+
get_peak_locations,
|
|
793
|
+
get_unique_hkls_ds,
|
|
794
|
+
)
|
|
742
795
|
from CHAP.utils.fit import FitMap
|
|
743
796
|
|
|
797
|
+
def linkdims(nxgroup, field_dims=[]):
|
|
798
|
+
if isinstance(field_dims, dict):
|
|
799
|
+
field_dims = [field_dims]
|
|
800
|
+
if map_config.map_type == 'structured':
|
|
801
|
+
axes = deepcopy(map_config.dims)
|
|
802
|
+
for dims in field_dims:
|
|
803
|
+
axes.append(dims['axes'])
|
|
804
|
+
nxgroup.attrs['axes'] = axes
|
|
805
|
+
else:
|
|
806
|
+
axes = ['map_index']
|
|
807
|
+
for dims in field_dims:
|
|
808
|
+
axes.append(dims['axes'])
|
|
809
|
+
nxgroup.attrs['axes'] = axes
|
|
810
|
+
nxgroup.attrs[f'map_index_indices'] = 0
|
|
811
|
+
for dim in map_config.dims:
|
|
812
|
+
nxgroup.makelink(nxentry.data[dim])
|
|
813
|
+
if f'{dim}_indices' in nxentry.data.attrs:
|
|
814
|
+
nxgroup.attrs[f'{dim}_indices'] = \
|
|
815
|
+
nxentry.data.attrs[f'{dim}_indices']
|
|
816
|
+
for dims in field_dims:
|
|
817
|
+
nxgroup.attrs[f'{dims["axes"]}_indices'] = dims['index']
|
|
818
|
+
|
|
819
|
+
if len(strain_analysis_config.detectors) != 1:
|
|
820
|
+
raise RuntimeError('Multiple detectors not tested')
|
|
744
821
|
for detector in strain_analysis_config.detectors:
|
|
745
822
|
calibration = [
|
|
746
823
|
d for d in ceria_calibration_config.detectors \
|
|
@@ -758,73 +835,86 @@ class StrainAnalysisProcessor(Processor):
|
|
|
758
835
|
nxprocess.data = NXdata()
|
|
759
836
|
nxprocess.default = 'data'
|
|
760
837
|
nxdata = nxprocess.data
|
|
761
|
-
nxdata.attrs['axes'] = map_config.dims
|
|
762
|
-
def linkdims(nxgroup):
|
|
763
|
-
for dim in map_config.dims:
|
|
764
|
-
nxgroup.makelink(nxentry.data[dim])
|
|
765
|
-
nxgroup.attrs[f'{dim}_indices'] = \
|
|
766
|
-
nxentry.data.attrs[f'{dim}_indices']
|
|
767
838
|
linkdims(nxdata)
|
|
768
839
|
|
|
840
|
+
# Collect raw MCA data of interest
|
|
841
|
+
mca_bin_energies = []
|
|
842
|
+
for i, detector in enumerate(strain_analysis_config.detectors):
|
|
843
|
+
mca_bin_energies.append(
|
|
844
|
+
detector.slope_calibrated
|
|
845
|
+
* np.linspace(0, detector.max_energy_kev, detector.num_bins)
|
|
846
|
+
+ detector.intercept_calibrated)
|
|
847
|
+
mca_data = strain_analysis_config.mca_data()
|
|
848
|
+
|
|
769
849
|
# Select interactive params / save figures
|
|
770
|
-
if
|
|
850
|
+
if interactive or save_figures:
|
|
851
|
+
# Third party modules
|
|
771
852
|
import matplotlib.pyplot as plt
|
|
772
|
-
from CHAP.edd.utils import select_hkls
|
|
773
|
-
from CHAP.utils.general import draw_mask_1d
|
|
774
|
-
for detector in strain_analysis_config.detectors:
|
|
775
|
-
x = np.linspace(detector.intercept_calibrated,
|
|
776
|
-
detector.max_energy_kev \
|
|
777
|
-
* detector.slope_calibrated,
|
|
778
|
-
detector.num_bins)
|
|
779
|
-
y = strain_analysis_config.mca_data(
|
|
780
|
-
detector,
|
|
781
|
-
(0,) * len(strain_analysis_config.map_config.shape))
|
|
782
|
-
fig = select_hkls(detector,
|
|
783
|
-
strain_analysis_config.materials,
|
|
784
|
-
detector.tth_calibrated,
|
|
785
|
-
y, x, interactive)
|
|
786
|
-
if save_figures:
|
|
787
|
-
fig.savefig(os.path.join(
|
|
788
|
-
outputdir,
|
|
789
|
-
f'{detector.detector_name}_strainanalysis_hkls.png'))
|
|
790
|
-
plt.close()
|
|
791
853
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
854
|
+
# Local modules
|
|
855
|
+
from CHAP.edd.utils import (
|
|
856
|
+
select_material_params,
|
|
857
|
+
select_mask_and_hkls,
|
|
858
|
+
)
|
|
859
|
+
|
|
860
|
+
# Mask during calibration
|
|
861
|
+
if len(ceria_calibration_config.detectors) != 1:
|
|
862
|
+
raise RuntimeError('Multiple detectors not implemented')
|
|
863
|
+
for detector in ceria_calibration_config.detectors:
|
|
864
|
+
# calibration_mask = detector.mca_mask()
|
|
865
|
+
calibration_bin_ranges = detector.include_bin_ranges
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
tth = strain_analysis_config.detectors[0].tth_calibrated
|
|
869
|
+
fig, strain_analysis_config.materials = select_material_params(
|
|
870
|
+
mca_bin_energies[0], mca_data[0][0], tth,
|
|
871
|
+
materials=strain_analysis_config.materials,
|
|
872
|
+
interactive=interactive)
|
|
873
|
+
self.logger.debug(
|
|
874
|
+
f'materials: {strain_analysis_config.materials}')
|
|
875
|
+
if save_figures:
|
|
876
|
+
fig.savefig(os.path.join(
|
|
877
|
+
outputdir,
|
|
878
|
+
f'{detector.detector_name}_strainanalysis_'
|
|
879
|
+
'material_config.png'))
|
|
880
|
+
plt.close()
|
|
881
|
+
|
|
882
|
+
# ASK: can we assume same hkl_tth_tol and tth_max for
|
|
883
|
+
# every detector in this part?
|
|
884
|
+
hkls, ds = get_unique_hkls_ds(
|
|
885
|
+
strain_analysis_config.materials,
|
|
886
|
+
tth_tol=strain_analysis_config.detectors[0].hkl_tth_tol,
|
|
887
|
+
tth_max=strain_analysis_config.detectors[0].tth_max)
|
|
888
|
+
for i, detector in enumerate(strain_analysis_config.detectors):
|
|
889
|
+
fig, include_bin_ranges, hkl_indices = \
|
|
890
|
+
select_mask_and_hkls(
|
|
891
|
+
mca_bin_energies[i], mca_data[i][0], hkls, ds,
|
|
892
|
+
detector.tth_calibrated,
|
|
893
|
+
detector.include_bin_ranges, detector.hkl_indices,
|
|
894
|
+
detector.detector_name, mca_data[i],
|
|
895
|
+
# calibration_mask=calibration_mask,
|
|
896
|
+
calibration_bin_ranges=calibration_bin_ranges,
|
|
897
|
+
interactive=interactive)
|
|
806
898
|
detector.include_bin_ranges = include_bin_ranges
|
|
899
|
+
detector.hkl_indices = hkl_indices
|
|
807
900
|
if save_figures:
|
|
808
|
-
|
|
901
|
+
fig.savefig(os.path.join(
|
|
809
902
|
outputdir,
|
|
810
|
-
f'{detector.detector_name}
|
|
903
|
+
f'{detector.detector_name}_strainanalysis_'
|
|
904
|
+
'fit_mask_hkls.png'))
|
|
811
905
|
plt.close()
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
strain_analysis_config.materials = select_material_params(
|
|
825
|
-
x, y, tth, materials=strain_analysis_config.materials)
|
|
826
|
-
|
|
827
|
-
for detector in strain_analysis_config.detectors:
|
|
906
|
+
else:
|
|
907
|
+
# ASK: can we assume same hkl_tth_tol and tth_max for
|
|
908
|
+
# every detector in this part?
|
|
909
|
+
# Get the unique HKLs and lattice spacings for the strain
|
|
910
|
+
# analysis materials (assume hkl_tth_tol and tth_max are the
|
|
911
|
+
# same for each detector)
|
|
912
|
+
hkls, ds = get_unique_hkls_ds(
|
|
913
|
+
strain_analysis_config.materials,
|
|
914
|
+
tth_tol=strain_analysis_config.detectors[0].hkl_tth_tol,
|
|
915
|
+
tth_max=strain_analysis_config.detectors[0].tth_max)
|
|
916
|
+
|
|
917
|
+
for i, detector in enumerate(strain_analysis_config.detectors):
|
|
828
918
|
# Setup NXdata group
|
|
829
919
|
self.logger.debug(
|
|
830
920
|
f'Setting up NXdata group for {detector.detector_name}')
|
|
@@ -834,21 +924,21 @@ class StrainAnalysisProcessor(Processor):
|
|
|
834
924
|
nxdetector.detector_config = dumps(detector.dict())
|
|
835
925
|
nxdetector.data = NXdata()
|
|
836
926
|
det_nxdata = nxdetector.data
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
all_energies = np.arange(0, detector.num_bins) \
|
|
840
|
-
* (detector.max_energy_kev / detector.num_bins) \
|
|
841
|
-
* detector.slope_calibrated \
|
|
842
|
-
+ detector.intercept_calibrated
|
|
927
|
+
linkdims(
|
|
928
|
+
det_nxdata, {'axes': 'energy', 'index': len(map_config.shape)})
|
|
843
929
|
mask = detector.mca_mask()
|
|
844
|
-
energies =
|
|
930
|
+
energies = mca_bin_energies[i][mask]
|
|
845
931
|
det_nxdata.energy = NXfield(value=energies,
|
|
846
932
|
attrs={'units': 'keV'})
|
|
847
|
-
det_nxdata.attrs['energy_indices'] = len(map_config.dims)
|
|
848
933
|
det_nxdata.intensity = NXfield(
|
|
849
934
|
dtype='uint16',
|
|
850
935
|
shape=(*map_config.shape, len(energies)),
|
|
851
936
|
attrs={'units': 'counts'})
|
|
937
|
+
det_nxdata.tth = NXfield(
|
|
938
|
+
dtype='float64',
|
|
939
|
+
shape=map_config.shape,
|
|
940
|
+
attrs={'units':'degrees', 'long_name': '2\u03B8 (degrees)'}
|
|
941
|
+
)
|
|
852
942
|
det_nxdata.microstrain = NXfield(
|
|
853
943
|
dtype='float64',
|
|
854
944
|
shape=map_config.shape,
|
|
@@ -857,32 +947,27 @@ class StrainAnalysisProcessor(Processor):
|
|
|
857
947
|
# Gather detector data
|
|
858
948
|
self.logger.debug(
|
|
859
949
|
f'Gathering detector data for {detector.detector_name}')
|
|
860
|
-
for map_index in np.ndindex(map_config.shape):
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
map_config.get_scan_step_index(map_index)
|
|
864
|
-
except:
|
|
865
|
-
continue
|
|
866
|
-
scanparser = scans.get_scanparser(scan_number)
|
|
867
|
-
intensity = scanparser.get_detector_data(
|
|
868
|
-
detector.detector_name, scan_step_index)\
|
|
869
|
-
.astype('uint16')[mask]
|
|
870
|
-
det_nxdata.intensity[map_index] = intensity
|
|
950
|
+
for j, map_index in enumerate(np.ndindex(map_config.shape)):
|
|
951
|
+
det_nxdata.intensity[map_index] = \
|
|
952
|
+
mca_data[i][j].astype('uint16')[mask]
|
|
871
953
|
det_nxdata.summed_intensity = det_nxdata.intensity.sum(axis=-1)
|
|
872
954
|
|
|
873
955
|
# Perform strain analysis
|
|
874
956
|
self.logger.debug(
|
|
875
957
|
f'Beginning strain analysis for {detector.detector_name}')
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
958
|
+
|
|
959
|
+
# Get the HKLs and lattice spacings that will be used for
|
|
960
|
+
# fitting
|
|
961
|
+
fit_hkls = np.asarray([hkls[i] for i in detector.hkl_indices])
|
|
962
|
+
fit_ds = np.asarray([ds[i] for i in detector.hkl_indices])
|
|
963
|
+
peak_locations = get_peak_locations(
|
|
964
|
+
fit_ds, detector.tth_calibrated)
|
|
965
|
+
num_peak = len(peak_locations)
|
|
880
966
|
# KLS: Use the below def of peak_locations when
|
|
881
967
|
# FitMap.create_multipeak_model can accept a list of maps
|
|
882
968
|
# for centers.
|
|
883
969
|
# tth = np.radians(detector.map_tth(map_config))
|
|
884
|
-
# peak_locations = [
|
|
885
|
-
# for d0 in fit_ds]
|
|
970
|
+
# peak_locations = [get_peak_locations(d0, tth) for d0 in fit_ds]
|
|
886
971
|
|
|
887
972
|
# Perform initial fit: assume uniform strain for all HKLs
|
|
888
973
|
self.logger.debug('Performing uniform fit')
|
|
@@ -891,118 +976,293 @@ class StrainAnalysisProcessor(Processor):
|
|
|
891
976
|
peak_locations,
|
|
892
977
|
fit_type='uniform',
|
|
893
978
|
peak_models=detector.peak_models,
|
|
894
|
-
background=detector.background
|
|
979
|
+
background=detector.background,
|
|
980
|
+
fwhm_min=detector.fwhm_min,
|
|
981
|
+
fwhm_max=detector.fwhm_max)
|
|
895
982
|
fit.fit()
|
|
896
983
|
uniform_fit_centers = [
|
|
897
984
|
fit.best_values[
|
|
898
985
|
fit.best_parameters().index(f'peak{i+1}_center')]
|
|
899
|
-
for i in range(
|
|
900
|
-
|
|
986
|
+
for i in range(num_peak)]
|
|
987
|
+
uniform_fit_centers_errors = [
|
|
901
988
|
fit.best_errors[
|
|
902
989
|
fit.best_parameters().index(f'peak{i+1}_center')]
|
|
903
|
-
for i in range(
|
|
990
|
+
for i in range(num_peak)]
|
|
991
|
+
uniform_fit_amplitudes = [
|
|
992
|
+
fit.best_values[
|
|
993
|
+
fit.best_parameters().index(f'peak{i+1}_amplitude')]
|
|
994
|
+
for i in range(num_peak)]
|
|
995
|
+
uniform_fit_amplitudes_errors = [
|
|
996
|
+
fit.best_errors[
|
|
997
|
+
fit.best_parameters().index(f'peak{i+1}_amplitude')]
|
|
998
|
+
for i in range(num_peak)]
|
|
999
|
+
uniform_fit_sigmas = [
|
|
1000
|
+
fit.best_values[
|
|
1001
|
+
fit.best_parameters().index(f'peak{i+1}_sigma')]
|
|
1002
|
+
for i in range(num_peak)]
|
|
1003
|
+
uniform_fit_sigmas_errors = [
|
|
1004
|
+
fit.best_errors[
|
|
1005
|
+
fit.best_parameters().index(f'peak{i+1}_sigma')]
|
|
1006
|
+
for i in range(num_peak)]
|
|
904
1007
|
|
|
905
1008
|
# Add uniform fit results to the NeXus structure
|
|
906
1009
|
nxdetector.uniform_fit = NXcollection()
|
|
907
1010
|
fit_nxgroup = nxdetector.uniform_fit
|
|
1011
|
+
|
|
908
1012
|
# Full map of results
|
|
909
1013
|
fit_nxgroup.results = NXdata()
|
|
910
1014
|
fit_nxdata = fit_nxgroup.results
|
|
911
|
-
|
|
912
|
-
|
|
1015
|
+
linkdims(
|
|
1016
|
+
fit_nxdata, {'axes': 'energy', 'index': len(map_config.shape)})
|
|
913
1017
|
fit_nxdata.makelink(det_nxdata.energy)
|
|
914
|
-
fit_nxdata.
|
|
915
|
-
for d in fit.best_results:
|
|
916
|
-
if d.endswith('_fit'):
|
|
917
|
-
fit_nxdata.fits = fit.best_results[d]
|
|
1018
|
+
fit_nxdata.best_fit= fit.best_fit
|
|
918
1019
|
fit_nxdata.residuals = fit.residual
|
|
1020
|
+
fit_nxdata.redchi = fit.redchi
|
|
1021
|
+
fit_nxdata.success = fit.success
|
|
919
1022
|
|
|
920
1023
|
# Peak-by-peak results
|
|
921
|
-
fit_nxgroup.fit_hkl_centers = NXdata()
|
|
922
|
-
fit_nxdata = fit_nxgroup.fit_hkl_centers
|
|
923
|
-
fit_nxdata
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1024
|
+
# fit_nxgroup.fit_hkl_centers = NXdata()
|
|
1025
|
+
# fit_nxdata = fit_nxgroup.fit_hkl_centers
|
|
1026
|
+
# linkdims(fit_nxdata)
|
|
1027
|
+
for (hkl, center_guess, centers_fit, centers_error,
|
|
1028
|
+
amplitudes_fit, amplitudes_error, sigmas_fit,
|
|
1029
|
+
sigmas_error) in zip(
|
|
1030
|
+
fit_hkls, peak_locations,
|
|
1031
|
+
uniform_fit_centers, uniform_fit_centers_errors,
|
|
1032
|
+
uniform_fit_amplitudes, uniform_fit_amplitudes_errors,
|
|
1033
|
+
uniform_fit_sigmas, uniform_fit_sigmas_errors):
|
|
928
1034
|
hkl_name = '_'.join(str(hkl)[1:-1].split(' '))
|
|
929
1035
|
fit_nxgroup[hkl_name] = NXparameters()
|
|
930
|
-
|
|
931
|
-
fit_nxgroup[hkl_name].
|
|
1036
|
+
# Report initial HKL peak centers
|
|
1037
|
+
fit_nxgroup[hkl_name].center_initial_guess = center_guess
|
|
1038
|
+
fit_nxgroup[hkl_name].center_initial_guess.attrs['units'] = \
|
|
1039
|
+
'keV'
|
|
1040
|
+
# Report HKL peak centers
|
|
932
1041
|
fit_nxgroup[hkl_name].centers = NXdata()
|
|
933
|
-
fit_nxgroup[hkl_name].centers.attrs['axes'] = map_config.dims
|
|
934
1042
|
linkdims(fit_nxgroup[hkl_name].centers)
|
|
935
1043
|
fit_nxgroup[hkl_name].centers.values = NXfield(
|
|
936
1044
|
value=centers_fit, attrs={'units': 'keV'})
|
|
937
1045
|
fit_nxgroup[hkl_name].centers.errors = NXfield(
|
|
938
|
-
value=
|
|
939
|
-
|
|
940
|
-
|
|
1046
|
+
value=centers_error)
|
|
1047
|
+
fit_nxgroup[hkl_name].centers.attrs['signal'] = 'values'
|
|
1048
|
+
# fit_nxdata.makelink(
|
|
1049
|
+
# fit_nxgroup[f'{hkl_name}/centers/values'], name=hkl_name)
|
|
1050
|
+
# Report HKL peak amplitudes
|
|
1051
|
+
fit_nxgroup[hkl_name].amplitudes = NXdata()
|
|
1052
|
+
linkdims(fit_nxgroup[hkl_name].amplitudes)
|
|
1053
|
+
fit_nxgroup[hkl_name].amplitudes.values = NXfield(
|
|
1054
|
+
value=amplitudes_fit, attrs={'units': 'counts'})
|
|
1055
|
+
fit_nxgroup[hkl_name].amplitudes.errors = NXfield(
|
|
1056
|
+
value=amplitudes_error)
|
|
1057
|
+
fit_nxgroup[hkl_name].amplitudes.attrs['signal'] = 'values'
|
|
1058
|
+
# Report HKL peak FWHM
|
|
1059
|
+
fit_nxgroup[hkl_name].sigmas = NXdata()
|
|
1060
|
+
linkdims(fit_nxgroup[hkl_name].sigmas)
|
|
1061
|
+
fit_nxgroup[hkl_name].sigmas.values = NXfield(
|
|
1062
|
+
value=sigmas_fit, attrs={'units': 'keV'})
|
|
1063
|
+
fit_nxgroup[hkl_name].sigmas.errors = NXfield(
|
|
1064
|
+
value=sigmas_error)
|
|
1065
|
+
fit_nxgroup[hkl_name].sigmas.attrs['signal'] = 'values'
|
|
941
1066
|
|
|
942
1067
|
# Perform second fit: do not assume uniform strain for all
|
|
943
1068
|
# HKLs, and use the fit peak centers from the uniform fit
|
|
944
1069
|
# as inital guesses
|
|
945
1070
|
self.logger.debug('Performing unconstrained fit')
|
|
946
1071
|
fit.create_multipeak_model(fit_type='unconstrained')
|
|
947
|
-
fit.fit()
|
|
1072
|
+
fit.fit(rel_amplitude_cutoff=detector.rel_amplitude_cutoff)
|
|
948
1073
|
unconstrained_fit_centers = np.array(
|
|
949
1074
|
[fit.best_values[
|
|
950
1075
|
fit.best_parameters()\
|
|
951
1076
|
.index(f'peak{i+1}_center')]
|
|
952
|
-
for i in range(
|
|
953
|
-
|
|
1077
|
+
for i in range(num_peak)])
|
|
1078
|
+
unconstrained_fit_centers_errors = np.array(
|
|
954
1079
|
[fit.best_errors[
|
|
955
1080
|
fit.best_parameters()\
|
|
956
1081
|
.index(f'peak{i+1}_center')]
|
|
957
|
-
for i in range(
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1082
|
+
for i in range(num_peak)])
|
|
1083
|
+
unconstrained_fit_amplitudes = [
|
|
1084
|
+
fit.best_values[
|
|
1085
|
+
fit.best_parameters().index(f'peak{i+1}_amplitude')]
|
|
1086
|
+
for i in range(num_peak)]
|
|
1087
|
+
unconstrained_fit_amplitudes_errors = [
|
|
1088
|
+
fit.best_errors[
|
|
1089
|
+
fit.best_parameters().index(f'peak{i+1}_amplitude')]
|
|
1090
|
+
for i in range(num_peak)]
|
|
1091
|
+
unconstrained_fit_sigmas = [
|
|
1092
|
+
fit.best_values[
|
|
1093
|
+
fit.best_parameters().index(f'peak{i+1}_sigma')]
|
|
1094
|
+
for i in range(num_peak)]
|
|
1095
|
+
unconstrained_fit_sigmas_errors = [
|
|
1096
|
+
fit.best_errors[
|
|
1097
|
+
fit.best_parameters().index(f'peak{i+1}_sigma')]
|
|
1098
|
+
for i in range(num_peak)]
|
|
1099
|
+
|
|
1100
|
+
if interactive or save_figures:
|
|
1101
|
+
# Third party modules
|
|
1102
|
+
import matplotlib.animation as animation
|
|
1103
|
+
|
|
1104
|
+
if save_figures:
|
|
1105
|
+
path = os.path.join(
|
|
1106
|
+
outputdir, f'{detector.detector_name}_strainanalysis_'
|
|
1107
|
+
'unconstrained_fits')
|
|
1108
|
+
if not os.path.isdir(path):
|
|
1109
|
+
os.mkdir(path)
|
|
1110
|
+
|
|
1111
|
+
def animate(i):
|
|
1112
|
+
map_index = np.unravel_index(i, map_config.shape)
|
|
1113
|
+
intensity.set_ydata(
|
|
1114
|
+
det_nxdata.intensity.nxdata[map_index]
|
|
1115
|
+
/ det_nxdata.intensity.nxdata[map_index].max())
|
|
1116
|
+
best_fit.set_ydata(fit.best_fit[map_index]
|
|
1117
|
+
/ fit.best_fit[map_index].max())
|
|
1118
|
+
# residual.set_ydata(fit.residual[map_index])
|
|
1119
|
+
index.set_text('\n'.join(f'{k}[{i}] = {v}'
|
|
1120
|
+
for k, v in map_config.get_coords(map_index).items()))
|
|
1121
|
+
if save_figures:
|
|
1122
|
+
plt.savefig(os.path.join(path, f'frame_{i}.png'))
|
|
1123
|
+
#return intensity, best_fit, residual, index
|
|
1124
|
+
return intensity, best_fit, index
|
|
1125
|
+
|
|
1126
|
+
fig, ax = plt.subplots()
|
|
1127
|
+
map_index = np.unravel_index(0, map_config.shape)
|
|
1128
|
+
data_normalized = (
|
|
1129
|
+
det_nxdata.intensity.nxdata[map_index]
|
|
1130
|
+
/ det_nxdata.intensity.nxdata[map_index].max())
|
|
1131
|
+
intensity, = ax.plot(
|
|
1132
|
+
energies, data_normalized, 'b.', label='data')
|
|
1133
|
+
fit_normalized = (fit.best_fit[map_index]
|
|
1134
|
+
/ fit.best_fit[map_index].max())
|
|
1135
|
+
best_fit, = ax.plot(
|
|
1136
|
+
energies, fit_normalized, 'k-', label='fit')
|
|
1137
|
+
# residual, = ax.plot(
|
|
1138
|
+
# energies, fit.residual[map_index], 'r-',
|
|
1139
|
+
# label='residual')
|
|
1140
|
+
ax.set(
|
|
1141
|
+
title='Unconstrained fits',
|
|
1142
|
+
xlabel='Energy (keV)',
|
|
1143
|
+
ylabel='Normalized Intensity (-)')
|
|
1144
|
+
ax.legend(loc='upper right')
|
|
1145
|
+
index = ax.text(
|
|
1146
|
+
0.05, 0.95, '', transform=ax.transAxes, va='top')
|
|
1147
|
+
|
|
1148
|
+
num_frames = int(det_nxdata.intensity.nxdata.size
|
|
1149
|
+
/ det_nxdata.intensity.nxdata.shape[-1])
|
|
1150
|
+
if not save_figures:
|
|
1151
|
+
ani = animation.FuncAnimation(
|
|
1152
|
+
fig, animate,
|
|
1153
|
+
frames=int(det_nxdata.intensity.nxdata.size
|
|
1154
|
+
/ det_nxdata.intensity.nxdata.shape[-1]),
|
|
1155
|
+
interval=1000, blit=True, repeat=False)
|
|
1156
|
+
else:
|
|
1157
|
+
for i in range(num_frames):
|
|
1158
|
+
animate(i)
|
|
1159
|
+
|
|
1160
|
+
plt.close()
|
|
1161
|
+
plt.subplots_adjust(top=1, bottom=0, left=0, right=1)
|
|
1162
|
+
|
|
1163
|
+
frames = []
|
|
1164
|
+
for i in range(num_frames):
|
|
1165
|
+
frame = plt.imread(
|
|
1166
|
+
os.path.join(path, f'frame_{i}.png'))
|
|
1167
|
+
im = plt.imshow(frame, animated=True)
|
|
1168
|
+
if not i:
|
|
1169
|
+
plt.imshow(frame)
|
|
1170
|
+
frames.append([im])
|
|
1171
|
+
|
|
1172
|
+
ani = animation.ArtistAnimation(
|
|
1173
|
+
plt.gcf(), frames, interval=1000, blit=True,
|
|
1174
|
+
repeat=False)
|
|
1175
|
+
|
|
1176
|
+
if interactive:
|
|
1177
|
+
plt.show()
|
|
1178
|
+
|
|
1179
|
+
if save_figures:
|
|
1180
|
+
path = os.path.join(
|
|
1181
|
+
outputdir,
|
|
1182
|
+
f'{detector.detector_name}_strainanalysis_'
|
|
1183
|
+
'unconstrained_fits.mp4')
|
|
1184
|
+
ani.save(path)
|
|
1185
|
+
plt.close()
|
|
1186
|
+
|
|
1187
|
+
tth_map = detector.get_tth_map(map_config)
|
|
1188
|
+
det_nxdata.tth.nxdata = tth_map
|
|
1189
|
+
nominal_centers = np.asarray(
|
|
1190
|
+
[get_peak_locations(d0, tth_map) for d0 in fit_ds])
|
|
1191
|
+
unconstrained_strains = np.log(
|
|
1192
|
+
nominal_centers / unconstrained_fit_centers)
|
|
962
1193
|
unconstrained_strain = np.mean(unconstrained_strains, axis=0)
|
|
963
1194
|
det_nxdata.microstrain.nxdata = unconstrained_strain * 1e6
|
|
964
1195
|
|
|
965
1196
|
# Add unconstrained fit results to the NeXus structure
|
|
966
1197
|
nxdetector.unconstrained_fit = NXcollection()
|
|
967
1198
|
fit_nxgroup = nxdetector.unconstrained_fit
|
|
1199
|
+
|
|
968
1200
|
# Full map of results
|
|
969
|
-
fit_nxgroup.
|
|
970
|
-
fit_nxdata = fit_nxgroup.
|
|
971
|
-
|
|
972
|
-
|
|
1201
|
+
fit_nxgroup.results = NXdata()
|
|
1202
|
+
fit_nxdata = fit_nxgroup.results
|
|
1203
|
+
linkdims(
|
|
1204
|
+
fit_nxdata, {'axes': 'energy', 'index': len(map_config.shape)})
|
|
973
1205
|
fit_nxdata.makelink(det_nxdata.energy)
|
|
974
|
-
fit_nxdata.
|
|
975
|
-
for d in fit.best_results:
|
|
976
|
-
if d.endswith('_fit'):
|
|
977
|
-
fit_nxdata.fits = fit.best_results[d]
|
|
1206
|
+
fit_nxdata.best_fit= fit.best_fit
|
|
978
1207
|
fit_nxdata.residuals = fit.residual
|
|
1208
|
+
fit_nxdata.redchi = fit.redchi
|
|
1209
|
+
fit_nxdata.success = fit.success
|
|
1210
|
+
|
|
979
1211
|
# Peak-by-peak results
|
|
980
1212
|
fit_nxgroup.fit_hkl_centers = NXdata()
|
|
981
1213
|
fit_nxdata = fit_nxgroup.fit_hkl_centers
|
|
982
|
-
fit_nxdata.attrs['axes'] = map_config.dims
|
|
983
1214
|
linkdims(fit_nxdata)
|
|
984
|
-
for (hkl,
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1215
|
+
for (hkl, center_guesses, centers_fit, centers_error,
|
|
1216
|
+
amplitudes_fit, amplitudes_error, sigmas_fit,
|
|
1217
|
+
sigmas_error) in zip(
|
|
1218
|
+
fit_hkls, uniform_fit_centers,
|
|
1219
|
+
unconstrained_fit_centers,
|
|
1220
|
+
unconstrained_fit_centers_errors,
|
|
1221
|
+
unconstrained_fit_amplitudes,
|
|
1222
|
+
unconstrained_fit_amplitudes_errors,
|
|
1223
|
+
unconstrained_fit_sigmas, unconstrained_fit_sigmas_errors):
|
|
988
1224
|
hkl_name = '_'.join(str(hkl)[1:-1].split(' '))
|
|
989
1225
|
fit_nxgroup[hkl_name] = NXparameters()
|
|
990
|
-
|
|
991
|
-
fit_nxgroup[hkl_name].
|
|
1226
|
+
# Report initial guesses HKL peak centers
|
|
1227
|
+
fit_nxgroup[hkl_name].center_initial_guess = NXdata()
|
|
1228
|
+
linkdims(fit_nxgroup[hkl_name].center_initial_guess)
|
|
1229
|
+
fit_nxgroup[hkl_name].center_initial_guess.makelink(
|
|
1230
|
+
nxdetector.uniform_fit[f'{hkl_name}/centers/values'],
|
|
1231
|
+
name='values')
|
|
1232
|
+
fit_nxgroup[hkl_name].center_initial_guess.attrs['signal'] = \
|
|
1233
|
+
'values'
|
|
1234
|
+
# Report HKL peak centers
|
|
992
1235
|
fit_nxgroup[hkl_name].centers = NXdata()
|
|
993
|
-
fit_nxgroup[hkl_name].centers.attrs['axes'] = map_config.dims
|
|
994
1236
|
linkdims(fit_nxgroup[hkl_name].centers)
|
|
995
1237
|
fit_nxgroup[hkl_name].centers.values = NXfield(
|
|
996
1238
|
value=centers_fit, attrs={'units': 'keV'})
|
|
997
1239
|
fit_nxgroup[hkl_name].centers.errors = NXfield(
|
|
998
|
-
value=
|
|
999
|
-
fit_nxdata.makelink(fit_nxgroup[f'{hkl_name}/centers/values'],
|
|
1000
|
-
name=hkl_name)
|
|
1240
|
+
value=centers_error)
|
|
1241
|
+
# fit_nxdata.makelink(fit_nxgroup[f'{hkl_name}/centers/values'],
|
|
1242
|
+
# name=hkl_name)
|
|
1243
|
+
fit_nxgroup[hkl_name].centers.attrs['signal'] = 'values'
|
|
1244
|
+
# Report HKL peak amplitudes
|
|
1245
|
+
fit_nxgroup[hkl_name].amplitudes = NXdata()
|
|
1246
|
+
linkdims(fit_nxgroup[hkl_name].amplitudes)
|
|
1247
|
+
fit_nxgroup[hkl_name].amplitudes.values = NXfield(
|
|
1248
|
+
value=amplitudes_fit, attrs={'units': 'counts'})
|
|
1249
|
+
fit_nxgroup[hkl_name].amplitudes.errors = NXfield(
|
|
1250
|
+
value=amplitudes_error)
|
|
1251
|
+
fit_nxgroup[hkl_name].amplitudes.attrs['signal'] = 'values'
|
|
1252
|
+
# Report HKL peak sigmas
|
|
1253
|
+
fit_nxgroup[hkl_name].sigmas = NXdata()
|
|
1254
|
+
linkdims(fit_nxgroup[hkl_name].sigmas)
|
|
1255
|
+
fit_nxgroup[hkl_name].sigmas.values = NXfield(
|
|
1256
|
+
value=sigmas_fit, attrs={'units': 'keV'})
|
|
1257
|
+
fit_nxgroup[hkl_name].sigmas.errors = NXfield(
|
|
1258
|
+
value=sigmas_error)
|
|
1259
|
+
fit_nxgroup[hkl_name].sigmas.attrs['signal'] = 'values'
|
|
1260
|
+
|
|
1001
1261
|
return nxroot
|
|
1002
1262
|
|
|
1003
1263
|
|
|
1004
1264
|
if __name__ == '__main__':
|
|
1005
|
-
#
|
|
1265
|
+
# Local modules
|
|
1006
1266
|
from CHAP.processor import main
|
|
1007
1267
|
|
|
1008
1268
|
main()
|