nxs-analysis-tools 0.0.47__py3-none-any.whl → 0.1.0__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 nxs-analysis-tools might be problematic. Click here for more details.
- _meta/__init__.py +10 -10
- nxs_analysis_tools/__init__.py +14 -13
- nxs_analysis_tools/chess.py +703 -697
- nxs_analysis_tools/datareduction.py +1360 -1187
- nxs_analysis_tools/fitting.py +259 -259
- nxs_analysis_tools/pairdistribution.py +1650 -1650
- {nxs_analysis_tools-0.0.47.dist-info → nxs_analysis_tools-0.1.0.dist-info}/METADATA +95 -89
- nxs_analysis_tools-0.1.0.dist-info/RECORD +11 -0
- {nxs_analysis_tools-0.0.47.dist-info → nxs_analysis_tools-0.1.0.dist-info}/licenses/LICENSE +21 -21
- nxs_analysis_tools-0.0.47.dist-info/RECORD +0 -11
- {nxs_analysis_tools-0.0.47.dist-info → nxs_analysis_tools-0.1.0.dist-info}/WHEEL +0 -0
- {nxs_analysis_tools-0.0.47.dist-info → nxs_analysis_tools-0.1.0.dist-info}/top_level.txt +0 -0
nxs_analysis_tools/chess.py
CHANGED
|
@@ -1,697 +1,703 @@
|
|
|
1
|
-
"""
|
|
2
|
-
This module provides classes and functions for analyzing scattering datasets collected at CHESS
|
|
3
|
-
(ID4B) with temperature dependence. It includes functions for loading data, cutting data, and
|
|
4
|
-
plotting linecuts.
|
|
5
|
-
"""
|
|
6
|
-
import os
|
|
7
|
-
import re
|
|
8
|
-
|
|
9
|
-
import matplotlib.pyplot as plt
|
|
10
|
-
import matplotlib as mpl
|
|
11
|
-
import pandas as pd
|
|
12
|
-
import numpy as np
|
|
13
|
-
from IPython.display import display, Markdown
|
|
14
|
-
from nxs_analysis_tools import load_data, Scissors
|
|
15
|
-
from nxs_analysis_tools.fitting import LinecutModel
|
|
16
|
-
from nxs_analysis_tools.datareduction import load_transform, reciprocal_lattice_params
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class TempDependence:
|
|
20
|
-
"""
|
|
21
|
-
A class for analyzing temperature-dependent scattering datasets collected at CHESS (ID4B).
|
|
22
|
-
|
|
23
|
-
The `TempDependence` class facilitates the loading, processing, and analysis of scattering
|
|
24
|
-
data across different temperatures. It includes methods for handling datasets, setting
|
|
25
|
-
lattice parameters, performing linecuts, modeling the data, and visualizing the results.
|
|
26
|
-
|
|
27
|
-
Attributes
|
|
28
|
-
----------
|
|
29
|
-
sample_directory : str
|
|
30
|
-
Path to the directory containing the datasets.
|
|
31
|
-
xlabel : str
|
|
32
|
-
Label for the x-axis of plots, determined by the axis of the linecuts.
|
|
33
|
-
datasets : dict
|
|
34
|
-
Dictionary storing datasets keyed by temperature.
|
|
35
|
-
temperatures : list of str
|
|
36
|
-
List of temperatures for which data is available.
|
|
37
|
-
scissors : dict
|
|
38
|
-
Dictionary of Scissors objects, one for each temperature, used for data manipulation and
|
|
39
|
-
linecut operations.
|
|
40
|
-
linecuts : dict
|
|
41
|
-
Dictionary storing the linecut data for each temperature.
|
|
42
|
-
linecutmodels : dict
|
|
43
|
-
Dictionary of LinecutModel objects, one for each temperature, used for fitting the linecuts.
|
|
44
|
-
a, b, c, al, be, ga : float or None
|
|
45
|
-
Lattice parameters (a, b, c, alpha, beta, gamma) of the crystal.
|
|
46
|
-
a_star, b_star, c_star, al_star, be_star, ga_star : float or None
|
|
47
|
-
Reciprocal lattice parameters (a*, b*, c*, alpha*, beta*, gamma*).
|
|
48
|
-
|
|
49
|
-
Methods
|
|
50
|
-
-------
|
|
51
|
-
set_temperatures(temperatures):
|
|
52
|
-
Set the list of temperatures for the datasets.
|
|
53
|
-
find_temperatures():
|
|
54
|
-
Set the list of temperatures by automatically scanning the sample directory.
|
|
55
|
-
set_sample_directory(path):
|
|
56
|
-
Set the directory path where the datasets are located.
|
|
57
|
-
initialize():
|
|
58
|
-
Initialize Scissors and LinecutModel objects for each temperature.
|
|
59
|
-
set_data(temperature, data):
|
|
60
|
-
Set the dataset for a specific temperature.
|
|
61
|
-
load_transforms(temperatures_list=None, print_tree=True):
|
|
62
|
-
Load transform datasets (from nxrefine) based on temperature.
|
|
63
|
-
load_datasets(file_ending='hkli.nxs', temperatures_list=None, print_tree=True):
|
|
64
|
-
Load datasets (CHESS format) from the specified folder.
|
|
65
|
-
get_sample_directory():
|
|
66
|
-
Get the folder path where the datasets are located.
|
|
67
|
-
clear_datasets():
|
|
68
|
-
Clear the datasets stored in the TempDependence instance.
|
|
69
|
-
set_Lattice_params(lattice_params):
|
|
70
|
-
Set lattice parameters and calculate reciprocal lattice parameters.
|
|
71
|
-
set_window(window, verbose=False):
|
|
72
|
-
Set the extents of the integration window for each temperature.
|
|
73
|
-
set_center(center):
|
|
74
|
-
Set the central coordinate for the linecut for each temperature.
|
|
75
|
-
cut_data(center=None, window=None, axis=None, verbose=False):
|
|
76
|
-
Perform data cutting for each temperature dataset.
|
|
77
|
-
plot_linecuts(vertical_offset=0, **kwargs):
|
|
78
|
-
Plot the linecuts obtained from data cutting.
|
|
79
|
-
plot_linecuts_heatmap(ax=None, **kwargs):
|
|
80
|
-
Plot a heatmap of the linecuts obtained from data cutting.
|
|
81
|
-
highlight_integration_window(temperature=None, **kwargs):
|
|
82
|
-
Display the integration window plot for a specific temperature.
|
|
83
|
-
plot_integration_window(temperature=None, **kwargs):
|
|
84
|
-
Plot the integration window cross-sections for a specific temperature.
|
|
85
|
-
set_model_components(model_components):
|
|
86
|
-
Set the model components for all line cut models.
|
|
87
|
-
set_param_hint(*args, **kwargs):
|
|
88
|
-
Set parameter hints for all line cut models.
|
|
89
|
-
make_params():
|
|
90
|
-
Create parameters for all line cut models.
|
|
91
|
-
guess():
|
|
92
|
-
Make initial parameter guesses for all line cut models.
|
|
93
|
-
print_initial_params():
|
|
94
|
-
Print the initial parameter values for all line cut models.
|
|
95
|
-
plot_initial_guess():
|
|
96
|
-
Plot the initial guess for all line cut models.
|
|
97
|
-
fit(verbose=False):
|
|
98
|
-
Fit the line cut models for each temperature.
|
|
99
|
-
plot_fit(mdheadings=False, **kwargs):
|
|
100
|
-
Plot the fit results for each temperature.
|
|
101
|
-
plot_order_parameter(self):
|
|
102
|
-
Plot the temperature dependence of the peakheight parameter.
|
|
103
|
-
print_fit_report():
|
|
104
|
-
Print the fit report for each temperature.
|
|
105
|
-
"""
|
|
106
|
-
|
|
107
|
-
def __init__(self):
|
|
108
|
-
"""
|
|
109
|
-
Initialize the TempDependence class with default values.
|
|
110
|
-
"""
|
|
111
|
-
|
|
112
|
-
self.sample_directory = None
|
|
113
|
-
self.xlabel = ''
|
|
114
|
-
self.datasets = {}
|
|
115
|
-
self.temperatures = []
|
|
116
|
-
self.scissors = {}
|
|
117
|
-
self.linecuts = {}
|
|
118
|
-
self.linecutmodels = {}
|
|
119
|
-
self.a, self.b, self.c, self.al, self.be, self.ga, \
|
|
120
|
-
self.a_star, self.b_star, self.c_star, self.al_star, self.be_star, self.ga_star \
|
|
121
|
-
= [None] * 12
|
|
122
|
-
|
|
123
|
-
def set_temperatures(self, temperatures):
|
|
124
|
-
"""
|
|
125
|
-
Set the list of temperatures for the datasets.
|
|
126
|
-
|
|
127
|
-
Parameters
|
|
128
|
-
----------
|
|
129
|
-
temperatures : list
|
|
130
|
-
List of temperatures to set.
|
|
131
|
-
"""
|
|
132
|
-
self.temperatures = temperatures
|
|
133
|
-
|
|
134
|
-
def find_temperatures(self):
|
|
135
|
-
"""
|
|
136
|
-
Set the list of temperatures by automatically scanning the sample directory.
|
|
137
|
-
"""
|
|
138
|
-
|
|
139
|
-
# Assert that self.sample_directory must exist
|
|
140
|
-
if self.sample_directory is None:
|
|
141
|
-
raise ValueError("Sample directory is not set. Use set_sample_directory(path) first.")
|
|
142
|
-
|
|
143
|
-
# Clear existing temperatures
|
|
144
|
-
self.temperatures = []
|
|
145
|
-
|
|
146
|
-
# Search for nxrefine .nxs files
|
|
147
|
-
for item in os.listdir(self.sample_directory):
|
|
148
|
-
pattern = r'_(\d+)\.nxs'
|
|
149
|
-
match = re.search(pattern, item)
|
|
150
|
-
if match:
|
|
151
|
-
# Identify temperature
|
|
152
|
-
temperature = match.group(1)
|
|
153
|
-
self.temperatures.append(temperature)
|
|
154
|
-
# Convert all temperatures to int temporarily to sort temperatures list
|
|
155
|
-
self.temperatures = [int(t) for t in self.temperatures]
|
|
156
|
-
self.temperatures.sort()
|
|
157
|
-
self.temperatures = [str(t) for t in self.temperatures]
|
|
158
|
-
|
|
159
|
-
def set_sample_directory(self, path):
|
|
160
|
-
"""
|
|
161
|
-
Set the directory path where the datasets are located.
|
|
162
|
-
|
|
163
|
-
Parameters
|
|
164
|
-
----------
|
|
165
|
-
path : str
|
|
166
|
-
Path to the sample directory.
|
|
167
|
-
"""
|
|
168
|
-
self.sample_directory = os.path.normpath(path)
|
|
169
|
-
|
|
170
|
-
def initialize(self):
|
|
171
|
-
"""
|
|
172
|
-
Initialize Scissors and LinecutModel objects for each temperature.
|
|
173
|
-
"""
|
|
174
|
-
for temperature in self.temperatures:
|
|
175
|
-
self.scissors[temperature] = Scissors()
|
|
176
|
-
self.scissors[temperature] = LinecutModel()
|
|
177
|
-
|
|
178
|
-
def set_data(self, temperature, data):
|
|
179
|
-
"""
|
|
180
|
-
Set the dataset for a specific temperature.
|
|
181
|
-
|
|
182
|
-
Parameters
|
|
183
|
-
----------
|
|
184
|
-
temperature : str
|
|
185
|
-
Temperature for which to set the data.
|
|
186
|
-
data : object
|
|
187
|
-
The dataset to be set.
|
|
188
|
-
"""
|
|
189
|
-
self.datasets[temperature] = data
|
|
190
|
-
|
|
191
|
-
def load_transforms(self, temperatures_list=None, print_tree=True):
|
|
192
|
-
"""
|
|
193
|
-
Load transform datasets (from nxrefine) based on temperature.
|
|
194
|
-
|
|
195
|
-
Parameters
|
|
196
|
-
----------
|
|
197
|
-
temperatures_list : list of int or None, optional
|
|
198
|
-
List of temperatures to load. If None, all available temperatures are loaded.
|
|
199
|
-
print_tree : bool, optional
|
|
200
|
-
Whether to print the data tree upon loading. Default True.
|
|
201
|
-
"""
|
|
202
|
-
# Convert all temperatures to strings
|
|
203
|
-
if temperatures_list:
|
|
204
|
-
temperatures_list = [str(t) for t in temperatures_list]
|
|
205
|
-
|
|
206
|
-
# Clear existing temperatures before loading files
|
|
207
|
-
self.temperatures = []
|
|
208
|
-
|
|
209
|
-
# Identify files to load
|
|
210
|
-
items_to_load = []
|
|
211
|
-
# Search for nxrefine .nxs files
|
|
212
|
-
for item in os.listdir(self.sample_directory):
|
|
213
|
-
pattern = r'_(\d+)\.nxs'
|
|
214
|
-
match = re.search(pattern, item)
|
|
215
|
-
if match:
|
|
216
|
-
# Identify temperature
|
|
217
|
-
temperature = match.group(1)
|
|
218
|
-
# print(f'Temperature = {temperature}')
|
|
219
|
-
if (temperatures_list is None) or (temperature in temperatures_list):
|
|
220
|
-
# Prepare file to be loaded
|
|
221
|
-
self.temperatures.append(temperature)
|
|
222
|
-
items_to_load.append(item)
|
|
223
|
-
# print(f'Preparing to load {temperature} K data: {item}')
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
loading_template =
|
|
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
|
-
temperature_folders
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
self.
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
"""
|
|
311
|
-
self.
|
|
312
|
-
|
|
313
|
-
def
|
|
314
|
-
"""
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
"""
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
The
|
|
369
|
-
Defaults to the
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
# Convert list of
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
#
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
#
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
"""
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
"""
|
|
603
|
-
for
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
#
|
|
677
|
-
for T in self.temperatures
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
1
|
+
"""
|
|
2
|
+
This module provides classes and functions for analyzing scattering datasets collected at CHESS
|
|
3
|
+
(ID4B) with temperature dependence. It includes functions for loading data, cutting data, and
|
|
4
|
+
plotting linecuts.
|
|
5
|
+
"""
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
10
|
+
import matplotlib as mpl
|
|
11
|
+
import pandas as pd
|
|
12
|
+
import numpy as np
|
|
13
|
+
from IPython.display import display, Markdown
|
|
14
|
+
from nxs_analysis_tools import load_data, Scissors
|
|
15
|
+
from nxs_analysis_tools.fitting import LinecutModel
|
|
16
|
+
from nxs_analysis_tools.datareduction import load_transform, reciprocal_lattice_params
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TempDependence:
|
|
20
|
+
"""
|
|
21
|
+
A class for analyzing temperature-dependent scattering datasets collected at CHESS (ID4B).
|
|
22
|
+
|
|
23
|
+
The `TempDependence` class facilitates the loading, processing, and analysis of scattering
|
|
24
|
+
data across different temperatures. It includes methods for handling datasets, setting
|
|
25
|
+
lattice parameters, performing linecuts, modeling the data, and visualizing the results.
|
|
26
|
+
|
|
27
|
+
Attributes
|
|
28
|
+
----------
|
|
29
|
+
sample_directory : str
|
|
30
|
+
Path to the directory containing the datasets.
|
|
31
|
+
xlabel : str
|
|
32
|
+
Label for the x-axis of plots, determined by the axis of the linecuts.
|
|
33
|
+
datasets : dict
|
|
34
|
+
Dictionary storing datasets keyed by temperature.
|
|
35
|
+
temperatures : list of str
|
|
36
|
+
List of temperatures for which data is available.
|
|
37
|
+
scissors : dict
|
|
38
|
+
Dictionary of Scissors objects, one for each temperature, used for data manipulation and
|
|
39
|
+
linecut operations.
|
|
40
|
+
linecuts : dict
|
|
41
|
+
Dictionary storing the linecut data for each temperature.
|
|
42
|
+
linecutmodels : dict
|
|
43
|
+
Dictionary of LinecutModel objects, one for each temperature, used for fitting the linecuts.
|
|
44
|
+
a, b, c, al, be, ga : float or None
|
|
45
|
+
Lattice parameters (a, b, c, alpha, beta, gamma) of the crystal.
|
|
46
|
+
a_star, b_star, c_star, al_star, be_star, ga_star : float or None
|
|
47
|
+
Reciprocal lattice parameters (a*, b*, c*, alpha*, beta*, gamma*).
|
|
48
|
+
|
|
49
|
+
Methods
|
|
50
|
+
-------
|
|
51
|
+
set_temperatures(temperatures):
|
|
52
|
+
Set the list of temperatures for the datasets.
|
|
53
|
+
find_temperatures():
|
|
54
|
+
Set the list of temperatures by automatically scanning the sample directory.
|
|
55
|
+
set_sample_directory(path):
|
|
56
|
+
Set the directory path where the datasets are located.
|
|
57
|
+
initialize():
|
|
58
|
+
Initialize Scissors and LinecutModel objects for each temperature.
|
|
59
|
+
set_data(temperature, data):
|
|
60
|
+
Set the dataset for a specific temperature.
|
|
61
|
+
load_transforms(temperatures_list=None, print_tree=True):
|
|
62
|
+
Load transform datasets (from nxrefine) based on temperature.
|
|
63
|
+
load_datasets(file_ending='hkli.nxs', temperatures_list=None, print_tree=True):
|
|
64
|
+
Load datasets (CHESS format) from the specified folder.
|
|
65
|
+
get_sample_directory():
|
|
66
|
+
Get the folder path where the datasets are located.
|
|
67
|
+
clear_datasets():
|
|
68
|
+
Clear the datasets stored in the TempDependence instance.
|
|
69
|
+
set_Lattice_params(lattice_params):
|
|
70
|
+
Set lattice parameters and calculate reciprocal lattice parameters.
|
|
71
|
+
set_window(window, verbose=False):
|
|
72
|
+
Set the extents of the integration window for each temperature.
|
|
73
|
+
set_center(center):
|
|
74
|
+
Set the central coordinate for the linecut for each temperature.
|
|
75
|
+
cut_data(center=None, window=None, axis=None, verbose=False):
|
|
76
|
+
Perform data cutting for each temperature dataset.
|
|
77
|
+
plot_linecuts(vertical_offset=0, **kwargs):
|
|
78
|
+
Plot the linecuts obtained from data cutting.
|
|
79
|
+
plot_linecuts_heatmap(ax=None, **kwargs):
|
|
80
|
+
Plot a heatmap of the linecuts obtained from data cutting.
|
|
81
|
+
highlight_integration_window(temperature=None, **kwargs):
|
|
82
|
+
Display the integration window plot for a specific temperature.
|
|
83
|
+
plot_integration_window(temperature=None, **kwargs):
|
|
84
|
+
Plot the integration window cross-sections for a specific temperature.
|
|
85
|
+
set_model_components(model_components):
|
|
86
|
+
Set the model components for all line cut models.
|
|
87
|
+
set_param_hint(*args, **kwargs):
|
|
88
|
+
Set parameter hints for all line cut models.
|
|
89
|
+
make_params():
|
|
90
|
+
Create parameters for all line cut models.
|
|
91
|
+
guess():
|
|
92
|
+
Make initial parameter guesses for all line cut models.
|
|
93
|
+
print_initial_params():
|
|
94
|
+
Print the initial parameter values for all line cut models.
|
|
95
|
+
plot_initial_guess():
|
|
96
|
+
Plot the initial guess for all line cut models.
|
|
97
|
+
fit(verbose=False):
|
|
98
|
+
Fit the line cut models for each temperature.
|
|
99
|
+
plot_fit(mdheadings=False, **kwargs):
|
|
100
|
+
Plot the fit results for each temperature.
|
|
101
|
+
plot_order_parameter(self):
|
|
102
|
+
Plot the temperature dependence of the peakheight parameter.
|
|
103
|
+
print_fit_report():
|
|
104
|
+
Print the fit report for each temperature.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def __init__(self):
|
|
108
|
+
"""
|
|
109
|
+
Initialize the TempDependence class with default values.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
self.sample_directory = None
|
|
113
|
+
self.xlabel = ''
|
|
114
|
+
self.datasets = {}
|
|
115
|
+
self.temperatures = []
|
|
116
|
+
self.scissors = {}
|
|
117
|
+
self.linecuts = {}
|
|
118
|
+
self.linecutmodels = {}
|
|
119
|
+
self.a, self.b, self.c, self.al, self.be, self.ga, \
|
|
120
|
+
self.a_star, self.b_star, self.c_star, self.al_star, self.be_star, self.ga_star \
|
|
121
|
+
= [None] * 12
|
|
122
|
+
|
|
123
|
+
def set_temperatures(self, temperatures):
|
|
124
|
+
"""
|
|
125
|
+
Set the list of temperatures for the datasets.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
temperatures : list
|
|
130
|
+
List of temperatures to set.
|
|
131
|
+
"""
|
|
132
|
+
self.temperatures = temperatures
|
|
133
|
+
|
|
134
|
+
def find_temperatures(self):
|
|
135
|
+
"""
|
|
136
|
+
Set the list of temperatures by automatically scanning the sample directory.
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
# Assert that self.sample_directory must exist
|
|
140
|
+
if self.sample_directory is None:
|
|
141
|
+
raise ValueError("Sample directory is not set. Use set_sample_directory(path) first.")
|
|
142
|
+
|
|
143
|
+
# Clear existing temperatures
|
|
144
|
+
self.temperatures = []
|
|
145
|
+
|
|
146
|
+
# Search for nxrefine .nxs files
|
|
147
|
+
for item in os.listdir(self.sample_directory):
|
|
148
|
+
pattern = r'_(\d+)\.nxs'
|
|
149
|
+
match = re.search(pattern, item)
|
|
150
|
+
if match:
|
|
151
|
+
# Identify temperature
|
|
152
|
+
temperature = match.group(1)
|
|
153
|
+
self.temperatures.append(temperature)
|
|
154
|
+
# Convert all temperatures to int temporarily to sort temperatures list
|
|
155
|
+
self.temperatures = [int(t) for t in self.temperatures]
|
|
156
|
+
self.temperatures.sort()
|
|
157
|
+
self.temperatures = [str(t) for t in self.temperatures]
|
|
158
|
+
|
|
159
|
+
def set_sample_directory(self, path):
|
|
160
|
+
"""
|
|
161
|
+
Set the directory path where the datasets are located.
|
|
162
|
+
|
|
163
|
+
Parameters
|
|
164
|
+
----------
|
|
165
|
+
path : str
|
|
166
|
+
Path to the sample directory.
|
|
167
|
+
"""
|
|
168
|
+
self.sample_directory = os.path.normpath(path)
|
|
169
|
+
|
|
170
|
+
def initialize(self):
|
|
171
|
+
"""
|
|
172
|
+
Initialize Scissors and LinecutModel objects for each temperature.
|
|
173
|
+
"""
|
|
174
|
+
for temperature in self.temperatures:
|
|
175
|
+
self.scissors[temperature] = Scissors()
|
|
176
|
+
self.scissors[temperature] = LinecutModel()
|
|
177
|
+
|
|
178
|
+
def set_data(self, temperature, data):
|
|
179
|
+
"""
|
|
180
|
+
Set the dataset for a specific temperature.
|
|
181
|
+
|
|
182
|
+
Parameters
|
|
183
|
+
----------
|
|
184
|
+
temperature : str
|
|
185
|
+
Temperature for which to set the data.
|
|
186
|
+
data : object
|
|
187
|
+
The dataset to be set.
|
|
188
|
+
"""
|
|
189
|
+
self.datasets[temperature] = data
|
|
190
|
+
|
|
191
|
+
def load_transforms(self, temperatures_list=None, print_tree=True):
|
|
192
|
+
"""
|
|
193
|
+
Load transform datasets (from nxrefine) based on temperature.
|
|
194
|
+
|
|
195
|
+
Parameters
|
|
196
|
+
----------
|
|
197
|
+
temperatures_list : list of int or None, optional
|
|
198
|
+
List of temperatures to load. If None, all available temperatures are loaded.
|
|
199
|
+
print_tree : bool, optional
|
|
200
|
+
Whether to print the data tree upon loading. Default True.
|
|
201
|
+
"""
|
|
202
|
+
# Convert all temperatures to strings
|
|
203
|
+
if temperatures_list:
|
|
204
|
+
temperatures_list = [str(t) for t in temperatures_list]
|
|
205
|
+
|
|
206
|
+
# Clear existing temperatures before loading files
|
|
207
|
+
self.temperatures = []
|
|
208
|
+
|
|
209
|
+
# Identify files to load
|
|
210
|
+
items_to_load = []
|
|
211
|
+
# Search for nxrefine .nxs files
|
|
212
|
+
for item in os.listdir(self.sample_directory):
|
|
213
|
+
pattern = r'_(\d+)\.nxs'
|
|
214
|
+
match = re.search(pattern, item)
|
|
215
|
+
if match:
|
|
216
|
+
# Identify temperature
|
|
217
|
+
temperature = match.group(1)
|
|
218
|
+
# print(f'Temperature = {temperature}')
|
|
219
|
+
if (temperatures_list is None) or (temperature in temperatures_list):
|
|
220
|
+
# Prepare file to be loaded
|
|
221
|
+
self.temperatures.append(temperature)
|
|
222
|
+
items_to_load.append(item)
|
|
223
|
+
# print(f'Preparing to load {temperature} K data: {item}')
|
|
224
|
+
|
|
225
|
+
# Convert all temperatures to int temporarily to sort temperatures list before loading
|
|
226
|
+
self.temperatures = [int(t) for t in self.temperatures]
|
|
227
|
+
|
|
228
|
+
loading_template = pd.DataFrame({'temperature': self.temperatures,
|
|
229
|
+
'filename': items_to_load})
|
|
230
|
+
loading_template = loading_template.sort_values(by='temperature')
|
|
231
|
+
self.temperatures = loading_template['temperature']
|
|
232
|
+
self.temperatures = [str(t) for t in self.temperatures]
|
|
233
|
+
items_to_load = loading_template['filename'].to_list()
|
|
234
|
+
|
|
235
|
+
for i, item in enumerate(items_to_load):
|
|
236
|
+
path = os.path.join(self.sample_directory, item)
|
|
237
|
+
|
|
238
|
+
# Ensure path is a string before using it
|
|
239
|
+
path = str(path)
|
|
240
|
+
|
|
241
|
+
# Save dataset
|
|
242
|
+
try:
|
|
243
|
+
self.datasets[self.temperatures[i]] = load_transform(path, print_tree)
|
|
244
|
+
except Exception as e:
|
|
245
|
+
# Report temperature that was unable to load, then raise exception.
|
|
246
|
+
temp_failed = self.temperatures[i]
|
|
247
|
+
print(f"Failed to load data for temperature {temp_failed} K from file {item}."
|
|
248
|
+
f" Error: {e}")
|
|
249
|
+
raise # Re-raise the exception
|
|
250
|
+
|
|
251
|
+
# Initialize scissors object
|
|
252
|
+
self.scissors[self.temperatures[i]] = Scissors()
|
|
253
|
+
self.scissors[self.temperatures[i]].set_data(self.datasets[self.temperatures[i]])
|
|
254
|
+
|
|
255
|
+
# Initialize linecutmodel object
|
|
256
|
+
self.linecutmodels[self.temperatures[i]] = LinecutModel()
|
|
257
|
+
|
|
258
|
+
def load_datasets(self, file_ending='hkli.nxs', temperatures_list=None, print_tree=True):
|
|
259
|
+
"""
|
|
260
|
+
Load datasets (CHESS format) from the specified folder.
|
|
261
|
+
|
|
262
|
+
Parameters
|
|
263
|
+
----------
|
|
264
|
+
file_ending : str, optional
|
|
265
|
+
The file extension of the datasets to be loaded. The default is 'hkli.nxs'.
|
|
266
|
+
temperatures_list : list of int or None, optional
|
|
267
|
+
The list of specific temperatures to load. If None, all available temperatures are
|
|
268
|
+
loaded. The default is None.
|
|
269
|
+
print_tree : bool, optional
|
|
270
|
+
Whether to print the data tree upon loading. Default True.
|
|
271
|
+
"""
|
|
272
|
+
temperature_folders = [] # Empty list to store temperature folder names
|
|
273
|
+
for item in os.listdir(self.sample_directory):
|
|
274
|
+
try:
|
|
275
|
+
temperature_folders.append(int(item)) # If folder name can be int, add it
|
|
276
|
+
except ValueError:
|
|
277
|
+
pass # Otherwise don't add it
|
|
278
|
+
temperature_folders.sort() # Sort from low to high T
|
|
279
|
+
temperature_folders = [str(i) for i in temperature_folders] # Convert to strings
|
|
280
|
+
|
|
281
|
+
self.temperatures = temperature_folders
|
|
282
|
+
|
|
283
|
+
if temperatures_list is not None:
|
|
284
|
+
self.temperatures = [str(t) for t in temperatures_list]
|
|
285
|
+
|
|
286
|
+
# Load .nxs files
|
|
287
|
+
for T in self.temperatures:
|
|
288
|
+
for file in os.listdir(os.path.join(self.sample_directory, T)):
|
|
289
|
+
if file.endswith(file_ending):
|
|
290
|
+
filepath = os.path.join(self.sample_directory, T, file)
|
|
291
|
+
|
|
292
|
+
# Load dataset at each temperature
|
|
293
|
+
self.datasets[T] = load_data(filepath, print_tree)
|
|
294
|
+
|
|
295
|
+
# Initialize scissors object at each temperature
|
|
296
|
+
self.scissors[T] = Scissors()
|
|
297
|
+
self.scissors[T].set_data(self.datasets[T])
|
|
298
|
+
|
|
299
|
+
# Initialize linecutmodel object at each temperature
|
|
300
|
+
self.linecutmodels[T] = LinecutModel()
|
|
301
|
+
|
|
302
|
+
def get_sample_directory(self):
|
|
303
|
+
"""
|
|
304
|
+
Get the folder path where the datasets are located.
|
|
305
|
+
|
|
306
|
+
Returns
|
|
307
|
+
-------
|
|
308
|
+
str
|
|
309
|
+
The folder path.
|
|
310
|
+
"""
|
|
311
|
+
return self.sample_directory
|
|
312
|
+
|
|
313
|
+
def clear_datasets(self):
|
|
314
|
+
"""
|
|
315
|
+
Clear the datasets stored in the TempDependence instance.
|
|
316
|
+
"""
|
|
317
|
+
self.datasets = {}
|
|
318
|
+
|
|
319
|
+
def set_lattice_params(self, lattice_params):
|
|
320
|
+
"""
|
|
321
|
+
Set lattice parameters and calculate reciprocal lattice parameters.
|
|
322
|
+
|
|
323
|
+
Parameters
|
|
324
|
+
----------
|
|
325
|
+
lattice_params : tuple
|
|
326
|
+
Tuple containing lattice parameters (a, b, c, al, be, ga).
|
|
327
|
+
"""
|
|
328
|
+
self.a, self.b, self.c, self.al, self.be, self.ga = lattice_params
|
|
329
|
+
self.a_star, self.b_star, self.c_star, \
|
|
330
|
+
self.al_star, self.be_star, self.ga_star = reciprocal_lattice_params(lattice_params)
|
|
331
|
+
|
|
332
|
+
def set_window(self, window, verbose=False):
|
|
333
|
+
"""
|
|
334
|
+
Set the extents of the integration window for each temperature.
|
|
335
|
+
|
|
336
|
+
Parameters
|
|
337
|
+
----------
|
|
338
|
+
window : tuple
|
|
339
|
+
Extents of the window for integration along each axis.
|
|
340
|
+
verbose : bool, optional
|
|
341
|
+
Enables printout of linecut axis and integrated axes. Default is False.
|
|
342
|
+
"""
|
|
343
|
+
for T in self.temperatures:
|
|
344
|
+
if verbose:
|
|
345
|
+
print("----------------------------------")
|
|
346
|
+
print("T = " + T + " K")
|
|
347
|
+
self.scissors[T].set_window(window, verbose)
|
|
348
|
+
|
|
349
|
+
def set_center(self, center):
|
|
350
|
+
"""
|
|
351
|
+
Set the central coordinate for the linecut for each temperature.
|
|
352
|
+
|
|
353
|
+
Parameters
|
|
354
|
+
----------
|
|
355
|
+
center : tuple
|
|
356
|
+
Central coordinate around which to perform the linecut.
|
|
357
|
+
"""
|
|
358
|
+
for T in self.temperatures:
|
|
359
|
+
self.scissors[T].set_center(center)
|
|
360
|
+
|
|
361
|
+
def cut_data(self, center=None, window=None, axis=None, verbose=False):
|
|
362
|
+
"""
|
|
363
|
+
Perform data cutting for each temperature dataset.
|
|
364
|
+
|
|
365
|
+
Parameters
|
|
366
|
+
----------
|
|
367
|
+
center : tuple, optional
|
|
368
|
+
The center point for cutting the data.
|
|
369
|
+
Defaults to the first temperature's center if None.
|
|
370
|
+
window : tuple, optional
|
|
371
|
+
The window size for cutting the data.
|
|
372
|
+
Defaults to the first temperature's window if None.
|
|
373
|
+
axis : int or None, optional
|
|
374
|
+
The axis along which to perform the cutting.
|
|
375
|
+
Defaults to the longest axis in `window` if None.
|
|
376
|
+
verbose : bool, optional
|
|
377
|
+
Enables printout of linecut progress. Default is False.
|
|
378
|
+
|
|
379
|
+
Returns
|
|
380
|
+
-------
|
|
381
|
+
dict
|
|
382
|
+
A dictionary of linecuts obtained from the cutting operation.
|
|
383
|
+
"""
|
|
384
|
+
|
|
385
|
+
for T in self.temperatures:
|
|
386
|
+
if verbose:
|
|
387
|
+
print("-------------------------------")
|
|
388
|
+
print("Cutting T = " + T + " K data...")
|
|
389
|
+
self.scissors[T].set_center(center)
|
|
390
|
+
self.scissors[T].set_window(window)
|
|
391
|
+
self.scissors[T].cut_data(axis=axis, verbose=verbose)
|
|
392
|
+
self.linecuts[T] = self.scissors[T].linecut
|
|
393
|
+
self.linecutmodels[T].set_data(self.linecuts[T])
|
|
394
|
+
|
|
395
|
+
xlabel_components = [self.linecuts[self.temperatures[0]].axes
|
|
396
|
+
if i == self.scissors[self.temperatures[0]].axis
|
|
397
|
+
else str(c) for i, c in
|
|
398
|
+
enumerate(self.scissors[self.temperatures[0]].center)]
|
|
399
|
+
self.xlabel = ' '.join(xlabel_components)
|
|
400
|
+
|
|
401
|
+
return self.linecuts
|
|
402
|
+
|
|
403
|
+
def plot_linecuts(self, vertical_offset=0, **kwargs):
|
|
404
|
+
"""
|
|
405
|
+
Plot the linecuts obtained from data cutting.
|
|
406
|
+
|
|
407
|
+
Parameters
|
|
408
|
+
----------
|
|
409
|
+
vertical_offset : float, optional
|
|
410
|
+
The vertical offset between linecuts on the plot. The default is 0.
|
|
411
|
+
**kwargs
|
|
412
|
+
Additional keyword arguments to be passed to the plot function.
|
|
413
|
+
"""
|
|
414
|
+
fig, ax = plt.subplots()
|
|
415
|
+
|
|
416
|
+
# Get the Viridis colormap
|
|
417
|
+
cmap = mpl.colormaps.get_cmap('viridis')
|
|
418
|
+
|
|
419
|
+
for i, linecut in enumerate(self.linecuts.values()):
|
|
420
|
+
x_data = linecut[linecut.axes].nxdata
|
|
421
|
+
y_data = linecut[linecut.signal].nxdata + i * vertical_offset
|
|
422
|
+
ax.plot(x_data, y_data, color=cmap(i / len(self.linecuts)), label=self.temperatures[i],
|
|
423
|
+
**kwargs)
|
|
424
|
+
|
|
425
|
+
ax.set(xlabel=self.xlabel,
|
|
426
|
+
ylabel=self.linecuts[self.temperatures[0]].signal)
|
|
427
|
+
|
|
428
|
+
# Get the current legend handles and labels
|
|
429
|
+
handles, labels = plt.gca().get_legend_handles_labels()
|
|
430
|
+
|
|
431
|
+
# Reverse the order of handles and labels
|
|
432
|
+
handles = handles[::-1]
|
|
433
|
+
labels = labels[::-1]
|
|
434
|
+
|
|
435
|
+
# Create a new legend with reversed order
|
|
436
|
+
plt.legend(handles, labels)
|
|
437
|
+
|
|
438
|
+
return fig, ax
|
|
439
|
+
|
|
440
|
+
def plot_linecuts_heatmap(self, ax=None, **kwargs):
|
|
441
|
+
"""
|
|
442
|
+
Plot the linecuts obtained from data cutting.
|
|
443
|
+
|
|
444
|
+
Parameters
|
|
445
|
+
----------
|
|
446
|
+
ax : matplotlib.axes.Axes, optional
|
|
447
|
+
The axes on which to plot the heatmap. If None, a new figure and axes
|
|
448
|
+
are created. The default is None.
|
|
449
|
+
**kwargs
|
|
450
|
+
Additional keyword arguments to be passed to the `pcolormesh` function.
|
|
451
|
+
|
|
452
|
+
Returns
|
|
453
|
+
-------
|
|
454
|
+
QuadMesh
|
|
455
|
+
The plotted heatmap object.
|
|
456
|
+
"""
|
|
457
|
+
|
|
458
|
+
# Retrieve linecut data for the first temperature and extract x-axis data
|
|
459
|
+
cut = self.linecuts[self.temperatures[0]]
|
|
460
|
+
x = cut[cut.axes].nxdata
|
|
461
|
+
|
|
462
|
+
# Convert the list of temperatures to a NumPy array for the y-axis
|
|
463
|
+
y = np.array([int(t) for t in self.temperatures])
|
|
464
|
+
|
|
465
|
+
# Collect counts from each temperature and ensure they are numpy arrays
|
|
466
|
+
v = [self.linecuts[T].counts.nxdata for T in self.temperatures]
|
|
467
|
+
|
|
468
|
+
# Convert list of arrays to a 2D array for the heatmap
|
|
469
|
+
v_2d = np.array(v)
|
|
470
|
+
|
|
471
|
+
# Create the grid for the heatmap
|
|
472
|
+
X, Y = np.meshgrid(x, y)
|
|
473
|
+
|
|
474
|
+
# Plot using pcolormesh
|
|
475
|
+
if ax is None:
|
|
476
|
+
_, ax = plt.subplots()
|
|
477
|
+
p = ax.pcolormesh(X, Y, v_2d, **kwargs)
|
|
478
|
+
plt.colorbar(p, label='counts')
|
|
479
|
+
ax.set(xlabel=self.xlabel, ylabel=r'$T$ (K)')
|
|
480
|
+
|
|
481
|
+
return p
|
|
482
|
+
|
|
483
|
+
def highlight_integration_window(self, temperature=None, **kwargs):
|
|
484
|
+
"""
|
|
485
|
+
Displays the integration window plot for a specific temperature,
|
|
486
|
+
or for the first temperature if none is provided.
|
|
487
|
+
|
|
488
|
+
Parameters
|
|
489
|
+
----------
|
|
490
|
+
temperature : str, optional
|
|
491
|
+
The temperature at which to display the integration window plot. If provided, the plot
|
|
492
|
+
will be generated using the dataset corresponding to the specified temperature. If not
|
|
493
|
+
provided, the integration window plots will be generated for the first temperature.
|
|
494
|
+
**kwargs : keyword arguments, optional
|
|
495
|
+
Additional keyword arguments to customize the plot.
|
|
496
|
+
"""
|
|
497
|
+
|
|
498
|
+
if temperature is not None:
|
|
499
|
+
p = self.scissors[
|
|
500
|
+
self.temperatures[0]].highlight_integration_window(
|
|
501
|
+
data=self.datasets[temperature], **kwargs
|
|
502
|
+
)
|
|
503
|
+
else:
|
|
504
|
+
p = self.scissors[self.temperatures[0]].highlight_integration_window(
|
|
505
|
+
data=self.datasets[self.temperatures[0]], **kwargs
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
return p
|
|
509
|
+
|
|
510
|
+
def plot_integration_window(self, temperature=None, **kwargs):
|
|
511
|
+
"""
|
|
512
|
+
Plots the three principal cross-sections of the integration volume on
|
|
513
|
+
a single figure for a specific temperature, or for the first temperature
|
|
514
|
+
if none is provided.
|
|
515
|
+
|
|
516
|
+
Parameters
|
|
517
|
+
----------
|
|
518
|
+
temperature : str, optional
|
|
519
|
+
The temperature at which to plot the integration volume. If provided,
|
|
520
|
+
the plot will be generated using the dataset corresponding to the
|
|
521
|
+
specified temperature. If not provided, the integration window plots
|
|
522
|
+
will be generated for the first temperature.
|
|
523
|
+
|
|
524
|
+
**kwargs : keyword arguments, optional
|
|
525
|
+
Additional keyword arguments to customize the plot.
|
|
526
|
+
"""
|
|
527
|
+
|
|
528
|
+
if temperature is not None:
|
|
529
|
+
p = self.scissors[self.temperatures[0]].plot_integration_window(**kwargs)
|
|
530
|
+
else:
|
|
531
|
+
p = self.scissors[self.temperatures[0]].plot_integration_window(**kwargs)
|
|
532
|
+
|
|
533
|
+
return p
|
|
534
|
+
|
|
535
|
+
def set_model_components(self, model_components):
|
|
536
|
+
"""
|
|
537
|
+
Set the model components for all line cut models.
|
|
538
|
+
|
|
539
|
+
This method sets the same model components for all line cut models in the
|
|
540
|
+
analysis. It iterates over each line cut model and calls their respective
|
|
541
|
+
`set_model_components` method with the provided `model_components`.
|
|
542
|
+
|
|
543
|
+
Parameters
|
|
544
|
+
----------
|
|
545
|
+
model_components : Model or iterable of Model
|
|
546
|
+
The model components to set for all line cut models.
|
|
547
|
+
|
|
548
|
+
"""
|
|
549
|
+
[linecutmodel.set_model_components(model_components) for
|
|
550
|
+
linecutmodel in self.linecutmodels.values()]
|
|
551
|
+
|
|
552
|
+
def set_param_hint(self, *args, **kwargs):
|
|
553
|
+
"""
|
|
554
|
+
Set parameter hints for all line cut models.
|
|
555
|
+
|
|
556
|
+
This method sets the parameter hints for all line cut models in the analysis.
|
|
557
|
+
It iterates over each line cut model and calls their respective `set_param_hint` method
|
|
558
|
+
with the provided arguments and keyword arguments.
|
|
559
|
+
|
|
560
|
+
Parameters
|
|
561
|
+
----------
|
|
562
|
+
*args
|
|
563
|
+
Variable length argument list.
|
|
564
|
+
**kwargs
|
|
565
|
+
Arbitrary keyword arguments.
|
|
566
|
+
|
|
567
|
+
"""
|
|
568
|
+
[linecutmodel.set_param_hint(*args, **kwargs)
|
|
569
|
+
for linecutmodel in self.linecutmodels.values()]
|
|
570
|
+
|
|
571
|
+
def make_params(self):
|
|
572
|
+
"""
|
|
573
|
+
Make parameters for all line cut models.
|
|
574
|
+
|
|
575
|
+
This method creates the parameters for all line cut models in the analysis.
|
|
576
|
+
It iterates over each line cut model and calls their respective `make_params` method.
|
|
577
|
+
"""
|
|
578
|
+
[linecutmodel.make_params() for linecutmodel in self.linecutmodels.values()]
|
|
579
|
+
|
|
580
|
+
def guess(self):
|
|
581
|
+
"""
|
|
582
|
+
Make initial parameter guesses for all line cut models.
|
|
583
|
+
|
|
584
|
+
This method generates initial parameter guesses for all line cut models in the analysis.
|
|
585
|
+
It iterates over each line cut model and calls their respective `guess` method.
|
|
586
|
+
|
|
587
|
+
"""
|
|
588
|
+
[linecutmodel.guess() for linecutmodel in self.linecutmodels.values()]
|
|
589
|
+
|
|
590
|
+
def print_initial_params(self):
|
|
591
|
+
"""
|
|
592
|
+
Print the initial parameter values for all line cut models.
|
|
593
|
+
|
|
594
|
+
This method prints the initial parameter values for all line cut models
|
|
595
|
+
in the analysis. It iterates over each line cut model and calls their
|
|
596
|
+
respective `print_initial_params` method.
|
|
597
|
+
|
|
598
|
+
"""
|
|
599
|
+
[linecutmodel.print_initial_params() for linecutmodel in self.linecutmodels.values()]
|
|
600
|
+
|
|
601
|
+
def plot_initial_guess(self):
|
|
602
|
+
"""
|
|
603
|
+
Plot the initial guess for all line cut models.
|
|
604
|
+
|
|
605
|
+
This method plots the initial guess for all line cut models in the analysis.
|
|
606
|
+
It iterates over each line cut model and calls their respective `plot_initial_guess` method.
|
|
607
|
+
|
|
608
|
+
"""
|
|
609
|
+
for T, linecutmodel in self.linecutmodels.items():
|
|
610
|
+
_, ax = plt.subplots()
|
|
611
|
+
ax.set(title=T + ' K')
|
|
612
|
+
linecutmodel.plot_initial_guess()
|
|
613
|
+
|
|
614
|
+
def fit(self, verbose=False):
|
|
615
|
+
"""
|
|
616
|
+
Fit the line cut models.
|
|
617
|
+
|
|
618
|
+
This method fits the line cut models for each temperature in the analysis.
|
|
619
|
+
It iterates over each line cut model, performs the fit, and prints the fitting progress.
|
|
620
|
+
|
|
621
|
+
Parameters
|
|
622
|
+
----------
|
|
623
|
+
verbose : bool, optional
|
|
624
|
+
Enables printout of fitting progress. Default False.
|
|
625
|
+
|
|
626
|
+
"""
|
|
627
|
+
for T, linecutmodel in self.linecutmodels.items():
|
|
628
|
+
if verbose:
|
|
629
|
+
print(f"Fitting {T} K data...")
|
|
630
|
+
linecutmodel.fit()
|
|
631
|
+
if verbose:
|
|
632
|
+
print("Done.")
|
|
633
|
+
print("Fits completed.")
|
|
634
|
+
|
|
635
|
+
def plot_fit(self, mdheadings=False, **kwargs):
|
|
636
|
+
"""
|
|
637
|
+
Plot the fit results.
|
|
638
|
+
|
|
639
|
+
This method plots the fit results for each temperature in the analysis.
|
|
640
|
+
It iterates over each line cut model, calls their respective `plot_fit` method,
|
|
641
|
+
and sets the xlabel, ylabel, and title for the plot.
|
|
642
|
+
|
|
643
|
+
"""
|
|
644
|
+
for T, linecutmodel in self.linecutmodels.items():
|
|
645
|
+
# Create a markdown heading for the plot
|
|
646
|
+
if mdheadings:
|
|
647
|
+
display(Markdown(f"### {T} K Fit Results"))
|
|
648
|
+
# Plot fit
|
|
649
|
+
linecutmodel.plot_fit(xlabel=self.xlabel,
|
|
650
|
+
ylabel=self.datasets[self.temperatures[0]].signal,
|
|
651
|
+
title=f"{T} K",
|
|
652
|
+
**kwargs)
|
|
653
|
+
|
|
654
|
+
def plot_order_parameter(self):
|
|
655
|
+
"""
|
|
656
|
+
Plot the temperature dependence of the peak height (order parameter).
|
|
657
|
+
|
|
658
|
+
This method extracts the peak height from each temperature-dependent
|
|
659
|
+
line cut fit stored in `linecutmodels` and plots it as a function
|
|
660
|
+
of temperature using matplotlib.
|
|
661
|
+
|
|
662
|
+
Returns
|
|
663
|
+
-------
|
|
664
|
+
Figure
|
|
665
|
+
Matplotlib Figure object containing the peak height vs. temperature plot.
|
|
666
|
+
Axes
|
|
667
|
+
Matplotlib Axes object associated with the figure.
|
|
668
|
+
|
|
669
|
+
Notes
|
|
670
|
+
-----
|
|
671
|
+
- Temperature values are converted to integers for plotting.
|
|
672
|
+
- Peak heights are extracted from the 'peakheight' parameter in the model results.
|
|
673
|
+
- The plot uses standard axes labels with temperature in Kelvin.
|
|
674
|
+
"""
|
|
675
|
+
|
|
676
|
+
# Create an array of temperature values
|
|
677
|
+
temperatures = [int(T) for T in self.temperatures]
|
|
678
|
+
|
|
679
|
+
# Create an empty list for the peak heights
|
|
680
|
+
peakheights = []
|
|
681
|
+
|
|
682
|
+
# Extract the peakheight at every temperature
|
|
683
|
+
for T in self.temperatures:
|
|
684
|
+
peakheights.append(self.linecutmodels[T].modelresult.params['peakheight'].value)
|
|
685
|
+
|
|
686
|
+
# Plot the peakheights vs. temperature
|
|
687
|
+
fig, ax = plt.subplots()
|
|
688
|
+
ax.plot(temperatures, peakheights)
|
|
689
|
+
ax.set(xlabel='$T$ (K)', ylabel='peakheight')
|
|
690
|
+
return fig, ax
|
|
691
|
+
|
|
692
|
+
def print_fit_report(self):
|
|
693
|
+
"""
|
|
694
|
+
Plot the fit results.
|
|
695
|
+
|
|
696
|
+
This method plots the fit results for each temperature in the analysis.
|
|
697
|
+
It iterates over each line cut model, calls their respective `plot_fit` method,
|
|
698
|
+
and sets the xlabel, ylabel, and title for the plot.
|
|
699
|
+
|
|
700
|
+
"""
|
|
701
|
+
for T, linecutmodel in self.linecutmodels.items():
|
|
702
|
+
print(f"[[[{T} K Fit Report]]]")
|
|
703
|
+
linecutmodel.print_fit_report()
|