DiadFit 0.0.54__py3-none-any.whl → 0.0.57__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.
@@ -1,1431 +0,0 @@
1
- import pandas as pd
2
- import numpy as np
3
- import matplotlib.pyplot as plt
4
- from matplotlib import patches
5
- import lmfit
6
- from lmfit.models import GaussianModel, VoigtModel, LinearModel, ConstantModel, PseudoVoigtModel
7
- from scipy.signal import find_peaks
8
- import os
9
- import re
10
- from os import listdir
11
- from os.path import isfile, join
12
- from DiadFit.importing_data_files import *
13
- from typing import Tuple, Optional
14
- from dataclasses import dataclass
15
- import matplotlib.patches as patches
16
- from tqdm import tqdm
17
-
18
- ## Test this
19
-
20
-
21
- encode="ISO-8859-1"
22
-
23
- ## Plotting Ne lines, returns peak position
24
- def find_closest(df, line1_shift):
25
- dist = (df['Raman_shift (cm-1)'] - line1_shift).abs()
26
- return df.loc[dist.idxmin()]
27
-
28
-
29
-
30
- def calculate_Ne_splitting(wavelength=532.05, line1_shift=1117, line2_shift=1447, cut_off_intensity=2000):
31
- """
32
- Calculates ideal splitting in air between lines closest to user-specified line shift
33
- """
34
-
35
- df_Ne=calculate_Ne_line_positions(wavelength=wavelength, cut_off_intensity=cut_off_intensity)
36
-
37
- closest1=find_closest(df_Ne, line1_shift).loc['Raman_shift (cm-1)']
38
- closest2=find_closest(df_Ne, line2_shift).loc['Raman_shift (cm-1)']
39
-
40
- diff=abs(closest1-closest2)
41
-
42
- df=pd.DataFrame(data={'Ne_Split': diff,
43
- 'Line_1': closest1,
44
- 'Line_2': closest2,
45
- 'Entered Pos Line 1': line1_shift,
46
- 'Entered Pos Line 2': line2_shift}, index=[0])
47
-
48
-
49
- return df
50
-
51
- def calculate_Ne_line_positions(wavelength=532.05, cut_off_intensity=2000):
52
- """
53
- Calculates Ne line positions using the theoretical lines from NIST for the user inputted wavelenth
54
- """
55
-
56
- Ne_emission_line_air=np.array([
57
- 556.244160,
58
- 556.276620,
59
- 556.305310,
60
- 557.603940,
61
- 558.590500,
62
- 558.934720,
63
- 559.115000,
64
- 565.256640,
65
- 565.602580,
66
- 565.665880,
67
- 566.220000,
68
- 566.254890,
69
- 568.464700,
70
- 568.981630,
71
- 571.534090,
72
- 571.887980,
73
- 571.922480,
74
- 571.953000,
75
- 574.243700,
76
- 574.829850,
77
- 574.864460,
78
- 576.058850,
79
- 576.405250,
80
- 576.441880,
81
- 577.030670,
82
- 580.409000,
83
- 580.444960,
84
- 581.140660,
85
- 581.662190,
86
- 582.015580,
87
- 582.890630
88
-
89
-
90
-
91
- ])
92
-
93
- Intensity=np.array([1500.00,
94
- 5000.00,
95
- 750.00,
96
- 350.00,
97
- 50.00,
98
- 500.00,
99
- 80.00,
100
- 750.00,
101
- 750.00,
102
- 5000.00,
103
- 40.00,
104
- 750.00,
105
- 250.00,
106
- 1500.00,
107
- 350.00,
108
- 1500.00,
109
- 5000.00,
110
- 750.00,
111
- 80.00,
112
- 5000.00,
113
- 700.00,
114
- 700.00,
115
- 30.00,
116
- 7000.00,
117
- 500.00,
118
- 750.00,
119
- 5000.00,
120
- 3000.00,
121
- 500.00,
122
- 5000.00,
123
- 750.00])
124
-
125
-
126
-
127
- Raman_shift=(10**7/wavelength)-(10**7/Ne_emission_line_air)
128
-
129
- df_Ne=pd.DataFrame(data={'Raman_shift (cm-1)': Raman_shift,
130
- 'Intensity': Intensity,
131
- 'Ne emission line in air': Ne_emission_line_air})
132
-
133
- df_Ne_r=df_Ne.loc[df_Ne['Intensity']>cut_off_intensity]
134
- return df_Ne_r
135
-
136
-
137
- @dataclass
138
- class Neon_id_config:
139
- # Exclude a range, e.g. cosmic rays
140
- exclude_range_1: Optional [Tuple[float, float]] = None
141
- exclude_range_2: Optional [Tuple[float, float]] = None
142
-
143
- # Things for Scipy find peaks
144
- height: tuple = 10
145
- distance: float= 1
146
- prominence: float = 10
147
- width: float = 1
148
- threshold: float = 0.6
149
- peak1_cent: float= 1117
150
- peak2_cent: float=1447
151
-
152
- # Number of peaks to look for
153
- n_peaks: float = 6
154
-
155
-
156
-
157
-
158
-
159
-
160
- def identify_Ne_lines(*, config: Neon_id_config=Neon_id_config(), path=None, filename=None, filetype=None, plot_figure=True, print_df=False,
161
- Ne_array=None):
162
-
163
- """
164
- Loads Ne line, uses scipy find peaks to identify peaks, overlays these,
165
- and returns peak positions to feed into fitting algorithms
166
-
167
- Parameters
168
- -----------
169
-
170
- path: str
171
- Folder user wishes to read data from
172
-
173
- filename: str
174
- Specific file being read
175
-
176
- filetype: str
177
- Identifies type of file
178
- Witec_ASCII: Datafile from WITEC with metadata for first few lines
179
- headless_txt: Txt file with no headers, just data with wavenumber in 1st col, int 2nd
180
- HORIBA_txt: Datafile from newer HORIBA machines with metadata in first rows
181
- Renishaw_txt: Datafile from renishaw with column headings.
182
-
183
- n_peaks: int
184
- Number of peaks to return values for
185
-
186
- peak1_cent: int or float, default 1118
187
- Position to look for 1st peak in, finds peaks within +/- 5 of this
188
-
189
- peak2_cent: int or float, default 1447
190
- Position to look for 2nd peak in, finds peaks within +/- 5 of this
191
-
192
-
193
- height, threshold, distance, prominence, width: int
194
- parameters for scipy find peaks
195
-
196
- exclude_range_1: None or list
197
- users can enter a range (e.g [1100, 1112]) to exclude a part of their spectrum,
198
- perhaps to remove cosmic rays
199
-
200
- exclude_range_2: None or list
201
- users can enter a range (e.g [1100, 1112]) to exclude a part of their spectrum,
202
- perhaps to remove cosmic rays
203
-
204
-
205
-
206
- """
207
-
208
- # This bit extracts the data, unless you already fed it in as an array
209
- if filename is not None and path is not None and filetype is not None:
210
- Ne_in=get_data(path=path, filename=filename, filetype=filetype)
211
- if Ne_array is not None:
212
- Ne_in=Ne_array
213
-
214
-
215
- # This gets parameters from config file
216
- exclude_range_1=config.exclude_range_1
217
- exclude_range_2=config.exclude_range_2
218
- height=config.height
219
- distance=config.distance
220
- prominence=config.prominence
221
- width=config.width
222
- threshold=config.threshold
223
- peak1_cent=config.peak1_cent
224
- peak2_cent=config.peak2_cent
225
- n_peaks=config.n_peaks
226
-
227
-
228
- # This bit filters the spectra if you want to exclude a range or 2
229
- if exclude_range_1 is None and exclude_range_2 is None:
230
- Ne=Ne_in
231
- if exclude_range_1 is not None:
232
- Ne=Ne_in[ (Ne_in[:, 0]<exclude_range_1[0]) | (Ne_in[:, 0]>exclude_range_1[1]) ]
233
-
234
- if exclude_range_2 is not None:
235
- Ne=Ne_in[ (Ne_in[:, 0]<exclude_range_2[0]) | (Ne_in[:, 0]>exclude_range_2[1]) ]
236
-
237
- # Get X and Y coords.
238
- y=Ne[:, 1]
239
- x=Ne[:, 0]
240
- spec_res=np.abs(x[1]-x[0])
241
-
242
-
243
- # Apply Scipy Find peaks using the parameters in the config file.
244
- peaks = find_peaks(y,height = height, threshold = threshold, distance = distance, prominence=prominence, width=width)
245
-
246
- # This gets a list of peak heights
247
- height = peaks[1]['peak_heights']
248
- # This gets a list of peak positions
249
- peak_pos = x[peaks[0]]
250
-
251
- # Lets combine them in a dataframe
252
- df=pd.DataFrame(data={'pos': peak_pos,
253
- 'height': height})
254
-
255
-
256
- # Find bigest peaks, and take up to n peaks
257
- df_sort_Ne=df.sort_values('height', axis=0, ascending=False)
258
- df_sort_Ne_trim=df_sort_Ne[0:n_peaks]
259
-
260
- if print_df is True:
261
- print('Biggest 6 peaks:')
262
- display(df_sort_Ne_trim)
263
-
264
- # Get peak within +-5
265
- df_pk1=df_sort_Ne.loc[df['pos'].between(peak1_cent-10*spec_res, peak1_cent+10*spec_res)]
266
- df_pk2=df_sort_Ne.loc[df['pos'].between(peak2_cent-10*spec_res, peak2_cent+10*spec_res)]
267
-
268
- df_pk1_trim=df_pk1[0:1]
269
- df_pk2_trim=df_pk2[0:1]
270
-
271
- # Lets extract spectra 10 spectral units either side of the asked for peak positions
272
- Neon1_region=(x<(peak1_cent+10*spec_res)) & (x>(peak1_cent-10*spec_res))
273
- Neon1_trim_y=y[Neon1_region]
274
- Neon1_trim_x=x[Neon1_region]
275
- Neon2_region=(x<(peak1_cent+10*spec_res)) & (x>(peak1_cent-10*spec_res))
276
- Neon2_trim_y=y[Neon1_region]
277
- Neon2_trim_x=x[Neon1_region]
278
-
279
- # Take 25th quantile as representative of the background position
280
- Baseline_Neon1=np.quantile(Neon1_trim_y, 0.1)
281
- Baseline_Neon2=np.quantile(Neon2_trim_y, 0.1)
282
-
283
- if len(df_pk1_trim)==1:
284
-
285
- df_fit_params=pd.DataFrame(data={'Peak1_cent': df_pk1_trim['pos'],
286
- 'Peak1_height': df_pk1_trim['height']})
287
- elif len(df_pk1_trim)>1:
288
- df_pk1_max=df_pk1_trim.loc[df_pk1_trim['height']==np.max(df_pk1_trim['height'])]
289
- df_fit_params=pd.DataFrame(data={'Peak1_cent': df_pk1_max['pos'],
290
- 'Peak1_height': df_pk1_max['height']})
291
- elif len(df_pk1_trim)==0:
292
-
293
-
294
- Max_y_Neon1=np.max(Neon1_trim_y)
295
- x_Neon_1=x[y==Max_y_Neon1][0]
296
- df_fit_params=pd.DataFrame(data={'Peak1_cent': x_Neon_1,
297
- <<<<<<< HEAD
298
- 'Peak1_height': Max_y_Neon1}, index=[0])
299
- =======
300
- 'Peak1_height': Max_y_Neon1},index=[0])
301
- >>>>>>> 321e668c4492e7deb287a9d4e3f3b5173494efa8
302
-
303
-
304
- if len(df_pk2_trim)==1:
305
- df_fit_params['Peak2_cent']=df_pk2_trim['pos'].iloc[0]
306
- df_fit_params['Peak2_height']=df_pk2_trim['height'].iloc[0]
307
- elif len(df_pk2_trim)>1:
308
- df_pk2_max=df_pk2_trim.loc[df_pk2_trim['height']==np.max(df_pk2['height'])]
309
-
310
- df_fit_params['Peak2_cent']=df_pk2_max['pos'].iloc[0]
311
- df_fit_params['Peak2_height']=df_pk2_max['height'].iloc[0]
312
-
313
- elif len(df_pk2_trim)==0:
314
-
315
- Max_y_Neon2=np.max(Neon2_trim_y)
316
- x_Neon_2=x[y==Max_y_Neon2][0]
317
- df_fit_params['Peak2_cent']= x_Neon_2
318
- df_fit_params['Peak2_height']=Max_y_Neon2
319
-
320
-
321
- if plot_figure is True:
322
- fig, (ax0, ax1, ax2) = plt.subplots(1, 3, figsize=(11, 3.5))
323
- ax0.plot(x, y, '-r')
324
- miny=np.min(y)
325
- maxy=np.max(y)
326
-
327
-
328
- ax0.set_ylabel('Amplitude (counts)')
329
- ax0.set_xlabel('Wavenumber (cm$^{-1}$)')
330
- ax1.plot(Ne_in[:, 0], Ne_in[:, 1], '-c')
331
- ax1.plot(Ne_in[:, 0], Ne_in[:, 1], '.c')
332
- ax1.plot(Ne[:, 0], Ne[:, 1], '.r')
333
- ax1.plot(x, y, '-r', label='filtered')
334
- ax1.plot(df_sort_Ne_trim['pos'], df_sort_Ne_trim['height'], '*c', label='all pks IDed')
335
-
336
- ax1.plot([peak1_cent-20, peak1_cent+20], [Baseline_Neon1, Baseline_Neon1], '-g', label='approx bck' )
337
- #ax1.plot(peak2_cent, df_pk2_trim['height'], '*k')
338
-
339
- pos_pk1=str(np.round(df_fit_params['Peak1_cent'].iloc[0], 2))
340
-
341
- nearest_pk1=peak1_cent
342
-
343
-
344
- ax1.annotate('peak=' + pos_pk1, xy=(df_fit_params['Peak1_cent'].iloc[0]+3,
345
- Baseline_Neon1*1.5+700), xycoords="data", fontsize=10, rotation=90)
346
-
347
-
348
- ax1.plot(df_fit_params['Peak1_cent'], df_fit_params['Peak1_height'], '*k', mfc='yellow', ms=8, label='selected peak')
349
-
350
- ax1.legend(loc='upper center', ncol=2, fontsize=8)
351
-
352
-
353
- pos_pk2=str(np.round(df_fit_params['Peak2_cent'].iloc[0], 2))
354
- ax2.plot([peak2_cent-20, peak2_cent+20], [Baseline_Neon2, Baseline_Neon2], '-g')
355
-
356
- ax2.annotate('peak=' + pos_pk2, xy=(df_fit_params['Peak2_cent'].iloc[0]-5,
357
- Baseline_Neon1*2+700), xycoords="data", fontsize=10, rotation=90)
358
-
359
-
360
-
361
- ax1.set_xlim([peak1_cent-15, peak1_cent+15])
362
-
363
- ax1.set_xlim([peak1_cent-10, peak1_cent+10])
364
- ax2.plot(Ne[:, 0], Ne[:, 1], '.r', label='input')
365
- ax2.plot(x, y, '-r')
366
- ax2.plot(df_sort_Ne_trim['pos'], df_sort_Ne_trim['height'], '*k', mfc='yellow', ms=8)
367
- #print(df_pk1)
368
-
369
-
370
- ax2.set_xlim([peak2_cent-15, peak2_cent+15])
371
-
372
- ax1.set_xlabel('Wavenumber (cm$^{-1}$)')
373
- ax2.set_xlabel('Wavenumber (cm$^{-1}$)')
374
-
375
- ax1.set_ylim([0, 1.5*df_fit_params['Peak1_height'].iloc[0]+100])
376
- ax2.set_ylim([0, 1.5*df_fit_params['Peak2_height'].iloc[0]+100])
377
- fig.tight_layout()
378
-
379
- df_fit_params['Peak1_prom']=df_fit_params['Peak1_height']-Baseline_Neon1
380
- df_fit_params['Peak2_prom']=df_fit_params['Peak2_height']-Baseline_Neon2
381
-
382
-
383
- return Ne, df_fit_params
384
-
385
-
386
-
387
- ## Ne baselines
388
- def remove_Ne_baseline_pk1(Ne, N_poly_pk1_baseline=None, Ne_center_1=None,
389
- lower_bck=None, upper_bck1=None, upper_bck2=None, sigma_baseline=None):
390
- """ This function uses a defined range of values to fit a baseline of Nth degree polynomial to the baseline
391
- around the 1117 peak
392
-
393
- Parameters
394
- -----------
395
-
396
- Ne: np.array
397
- np.array of x and y coordinates from the spectra
398
-
399
- N_poly_pk1_baseline: int
400
- Degree of polynomial used to fit the background
401
-
402
- Ne_center_1: float
403
- Center position for Ne line being fitted
404
-
405
- lower_bck: list (length 2). default [-50, -20]
406
- position used for lower background relative to peak, so =[-50, -20] takes a
407
- background -50 and -20 from the peak center
408
-
409
- upper_bck1: list (length 2). default [8, 15]
410
- position used for 1st upper background relative to peak, so =[8, 15] takes a
411
- background +8 and +15 from the peak center
412
-
413
- upper_bck2: list (length 2). default [30, 50]
414
- position used for 2nd upper background relative to peak, so =[30, 50] takes a
415
- background +30 and +50 from the peak center
416
- """
417
-
418
- lower_0baseline_pk1=Ne_center_1+lower_bck[0]
419
- upper_0baseline_pk1=Ne_center_1+lower_bck[1]
420
- lower_1baseline_pk1=Ne_center_1+upper_bck1[0]
421
- upper_1baseline_pk1=Ne_center_1+upper_bck1[1]
422
- lower_2baseline_pk1=Ne_center_1+upper_bck2[0]
423
- upper_2baseline_pk1=Ne_center_1+upper_bck2[1]
424
-
425
- # Trim for entire range
426
- Ne_short=Ne[ (Ne[:,0]>lower_0baseline_pk1) & (Ne[:,0]<upper_2baseline_pk1) ]
427
-
428
- # Get actual baseline
429
- Baseline_with_outl=Ne_short[
430
- ((Ne_short[:, 0]<=upper_0baseline_pk1) &(Ne_short[:, 0]>=lower_0baseline_pk1))
431
- |
432
- ((Ne_short[:, 0]<=upper_1baseline_pk1) &(Ne_short[:, 0]>=lower_1baseline_pk1))
433
- |
434
- ((Ne_short[:, 0]<=upper_2baseline_pk1) &(Ne_short[:, 0]>=lower_2baseline_pk1))]
435
-
436
- # Calculates the median for the baseline and the standard deviation
437
- Median_Baseline=np.median(Baseline_with_outl[:, 1])
438
- Std_Baseline=np.std(Baseline_with_outl[:, 1])
439
-
440
- # Removes any points in the baseline outside of 2 sigma (helps remove cosmic rays etc).
441
- Baseline=Baseline_with_outl[(Baseline_with_outl[:, 1]<Median_Baseline+sigma_baseline*Std_Baseline)
442
- &
443
- (Baseline_with_outl[:, 1]>Median_Baseline-sigma_baseline*Std_Baseline)
444
- ]
445
-
446
- # Fits a polynomial to the baseline of degree
447
- Pf_baseline = np.poly1d(np.polyfit(Baseline[:, 0], Baseline[:, 1], N_poly_pk1_baseline))
448
- Py_base =Pf_baseline(Ne_short[:, 0])
449
- Baseline_ysub=Pf_baseline(Baseline_with_outl[:, 0])
450
- Baseline_y=Baseline[:, 1]
451
- Baseline_x= Baseline[:, 0]#Baseline[:, 0]
452
- y_corr= Ne_short[:, 1]- Py_base
453
- x=Ne_short[:, 0]
454
-
455
-
456
- return y_corr, Py_base, x, Ne_short, Py_base, Baseline_y, Baseline_x
457
-
458
- def remove_Ne_baseline_pk2(Ne, N_poly_pk2_baseline=None, Ne_center_2=None, sigma_baseline=None,
459
- lower_bck=None, upper_bck1=None, upper_bck2=None):
460
-
461
- """ This function uses a defined range of values to fit a baseline of Nth degree polynomial to the baseline
462
- around the 1447 peak
463
-
464
- Parameters
465
- -----------
466
-
467
- Ne: np.array
468
- np.array of x and y coordinates from the spectra
469
-
470
- N_poly_pk1_baseline: int
471
- Degree of polynomial used to fit the background
472
-
473
- Ne_center_1: float
474
- Center position for Ne line being fitted
475
-
476
- lower_bck: list (length 2) Default [-44.2, -22]
477
- position used for lower background relative to peak, so =[-50, -20] takes a
478
- background -50 and -20 from the peak center
479
-
480
- upper_bck1: list (length 2). Default [15, 50]
481
- position used for 1st upper background relative to peak, so =[8, 15] takes a
482
- background +8 and +15 from the peak center
483
-
484
- upper_bck2: list (length 2) Default [50, 51]
485
- position used for 2nd upper background relative to peak, so =[30, 50] takes a
486
- background +30 and +50 from the peak center
487
- """
488
-
489
-
490
-
491
- lower_0baseline_pk2=Ne_center_2+lower_bck[0]
492
- upper_0baseline_pk2=Ne_center_2+lower_bck[1]
493
- lower_1baseline_pk2=Ne_center_2+upper_bck1[0]
494
- upper_1baseline_pk2=Ne_center_2+upper_bck1[1]
495
- lower_2baseline_pk2=Ne_center_2+upper_bck2[0]
496
- upper_2baseline_pk2=Ne_center_2+upper_bck2[1]
497
-
498
- # Trim for entire range
499
- Ne_short=Ne[ (Ne[:,0]>lower_0baseline_pk2) & (Ne[:,0]<upper_2baseline_pk2) ]
500
-
501
- # Get actual baseline
502
- Baseline_with_outl=Ne_short[
503
- ((Ne_short[:, 0]<upper_0baseline_pk2) &(Ne_short[:, 0]>lower_0baseline_pk2))
504
- |
505
- ((Ne_short[:, 0]<upper_1baseline_pk2) &(Ne_short[:, 0]>lower_1baseline_pk2))
506
- |
507
- ((Ne_short[:, 0]<upper_2baseline_pk2) &(Ne_short[:, 0]>lower_2baseline_pk2))]
508
-
509
- # Calculates the median for the baseline and the standard deviation
510
- Median_Baseline=np.median(Baseline_with_outl[:, 1])
511
- Std_Baseline=np.std(Baseline_with_outl[:, 1])
512
-
513
- # Removes any points in the baseline outside of 2 sigma (helps remove cosmic rays etc).
514
- Baseline=Baseline_with_outl[(Baseline_with_outl[:, 1]<Median_Baseline+sigma_baseline*Std_Baseline)
515
- &
516
- (Baseline_with_outl[:, 1]>Median_Baseline-sigma_baseline*Std_Baseline)
517
- ]
518
-
519
- # Fits a polynomial to the baseline of degree
520
- Pf_baseline = np.poly1d(np.polyfit(Baseline[:, 0], Baseline[:, 1], N_poly_pk2_baseline))
521
- Py_base =Pf_baseline(Ne_short[:, 0])
522
- Baseline_ysub=Pf_baseline(Baseline[:, 0])
523
- Baseline_x=Baseline[:, 0]
524
- Baseline_y=Baseline[:, 1]
525
- y_corr= Ne_short[:, 1]- Py_base
526
- x=Ne_short[:, 0]
527
-
528
-
529
- return y_corr, Py_base, x, Ne_short, Py_base, Baseline_y, Baseline_x
530
-
531
-
532
-
533
-
534
-
535
- def fit_pk1(x, y_corr, x_span=[-10, 8], Ne_center=1117.1, amplitude=98, pk1_sigma=0.28,
536
- LH_offset_mini=[1.5, 3], peaks_pk1=2, model_name='PseudoVoigtModel', block_print=True,
537
- const_params=True, spec_res=0.4) :
538
- """ This function fits the 1117 Ne line as 1 or two voigt peaks
539
-
540
- Parameters
541
- -----------
542
-
543
- x: np.array
544
- x coordinate (wavenumber)
545
-
546
- y: np.array
547
- Background corrected intensiy
548
-
549
- x_span: list length 2. Default [-10, 8]
550
- Span either side of peak center used for fitting,
551
- e.g. by default, fits to 10 wavenumbers below peak, 8 above.
552
-
553
- Ne_center: float (default=1117.1)
554
- Center position for Ne line being fitted
555
-
556
- amplitude: integer (default = 98)
557
- peak amplitude
558
-
559
- sigma: float (default =0.28)
560
- sigma of the voigt peak
561
-
562
- peaks_pk1: integer
563
- number of peaks to fit, e.g. 1, single voigt, 2 to get shoulder peak
564
-
565
- LH_offset_mini: list length 2
566
- Forces second peak to be within -1.5 to -3 from the main peak position.
567
-
568
- print_report: bool
569
- if True, prints fit report.
570
-
571
-
572
- """
573
- if const_params is True:
574
- min_off=0.8
575
- max_off=1.2
576
- if const_params is False:
577
- min_off=0
578
- max_off=100
579
-
580
- # Flatten x and y if needed
581
- xdat=x.flatten()
582
- ydat=y_corr.flatten()
583
-
584
- # This defines the range you want to fit (e.g. how big the tails are)
585
- lower_pk1=Ne_center+x_span[0]
586
- upper_pk1=Ne_center+x_span[1]
587
-
588
- # This segments into the x and y variable, and variables to plot, which are a bit bigger.
589
- Ne_pk1_reg_x=x[(x>lower_pk1)&(x<upper_pk1)]
590
- Ne_pk1_reg_y=y_corr[(x>lower_pk1)&(x<upper_pk1)]
591
- Ne_pk1_reg_x_plot=x[(x>(lower_pk1-3))&(x<(upper_pk1+3))]
592
- Ne_pk1_reg_y_plot=y_corr[(x>(lower_pk1-3))&(x<(upper_pk1+3))]
593
-
594
- if peaks_pk1>1:
595
-
596
- # Setting up lmfit
597
- if model_name == 'PseudoVoigtModel':
598
- model0 = PseudoVoigtModel(prefix='p0_')#+ ConstantModel(prefix='c0')
599
- if model_name=="VoigtModel":
600
- model0 = VoigtModel(prefix='p0_')#+ ConstantModel(prefix='c0')
601
- pars0 = model0.make_params(p0_center=Ne_center, p0_amplitude=amplitude)
602
- init0 = model0.eval(pars0, x=xdat)
603
- result0 = model0.fit(ydat, pars0, x=xdat)
604
- Center_p0=result0.best_values.get('p0_center')
605
- if block_print is False:
606
- print('first iteration, peak Center='+str(np.round(Center_p0, 4)))
607
-
608
- Center_p0_error=result0.params.get('p0_center')
609
- Amp_p0=result0.params.get('p0_amplitude')
610
- if block_print is False:
611
- print('first iteration, peak Amplitude='+str(np.round(Amp_p0, 4)))
612
- fwhm_p0=result0.params.get('p0_fwhm')
613
- Center_p0_errorval=float(str(Center_p0_error).split()[4].replace(",", ""))
614
-
615
- #Ne_center=Ne_center
616
- #rough_peak_positions=Ne_center-2
617
- if model_name == 'PseudoVoigtModel':
618
- model1 = PseudoVoigtModel(prefix='p1_')#+ ConstantModel(prefix='c0')
619
- if model_name=="VoigtModel":
620
- model1 = VoigtModel(prefix='p1_')#+ ConstantModel(prefix='c0')
621
- pars1 = model1.make_params()
622
- pars1['p1_'+ 'amplitude'].set(Amp_p0, min=min_off*Amp_p0, max=max_off*Amp_p0)
623
- pars1['p1_'+ 'center'].set(Center_p0, min=Center_p0-0.2, max=Center_p0+0.2)
624
- pars1['p1_'+ 'sigma'].set(pk1_sigma, min=pk1_sigma*min_off, max=pk1_sigma*max_off)
625
-
626
-
627
- # Second wee peak
628
- prefix='p2_'
629
- if model_name == 'PseudoVoigtModel':
630
- peak = PseudoVoigtModel(prefix='p2_')#+ ConstantModel(prefix='c0')
631
- if model_name=="VoigtModel":
632
- peak = VoigtModel(prefix='p2_')#+ ConstantModel(prefix='c0')
633
-
634
-
635
- pars = peak.make_params()
636
- minp2=Center_p0-LH_offset_mini[1]
637
- maxp2=Center_p0-LH_offset_mini[0]
638
- if block_print is False:
639
- print('Trying to place second peak between '+str(np.round(minp2, 2))+'and'+ str(np.round(maxp2, 2)))
640
- pars[prefix + 'center'].set(Center_p0, min=minp2,
641
- max=maxp2)
642
-
643
- pars['p2_'+ 'fwhm'].set(fwhm_p0/2, min=0.001, max=fwhm_p0*5)
644
-
645
-
646
- pars[prefix + 'amplitude'].set(Amp_p0/5, min=0, max=Amp_p0/2)
647
- pars[prefix + 'sigma'].set(0.2, min=0)
648
-
649
- model_combo=model1+peak
650
- pars1.update(pars)
651
-
652
-
653
- if peaks_pk1==1:
654
-
655
- if model_name == 'PseudoVoigtModel':
656
- model_combo = PseudoVoigtModel(prefix='p1_')#+ ConstantModel(prefix='c0')
657
- if model_name=="VoigtModel":
658
- model_combo= VoigtModel(prefix='p1_')#+ ConstantModel(prefix='c0')
659
-
660
-
661
-
662
- # create parameters with initial values
663
- pars1 = model_combo.make_params(amplitude=amplitude)
664
- pars1['p1_' + 'center'].set(Ne_center, min=Ne_center-2*spec_res,max=Ne_center+2*spec_res)
665
- pars1['p1_'+ 'sigma'].set(pk1_sigma, min=pk1_sigma*min_off, max=pk1_sigma*max_off)
666
-
667
-
668
-
669
-
670
-
671
-
672
-
673
- init = model_combo.eval(pars1, x=xdat)
674
- result = model_combo.fit(ydat, pars1, x=xdat)
675
- # Need to check errors output
676
- Error_bars=result.errorbars
677
-
678
-
679
- # Get center value
680
- Center_p1=result.best_values.get('p1_center')
681
- Center_p1_error=result.params.get('p1_center')
682
-
683
- # Get mix of lorenz
684
- Peak1_Prop_Lor=result.best_values.get('p1_fraction')
685
-
686
-
687
- if peaks_pk1==1:
688
- Center_pk1=Center_p1
689
- if Error_bars is False:
690
- if block_print is False:
691
- print('Error bars not determined by function')
692
- error_pk1=np.nan
693
- else:
694
- error_pk1 = float(str(Center_p1_error).split()[4].replace(",", ""))
695
-
696
-
697
- if peaks_pk1>1:
698
- Center_p2=result.best_values.get('p2_center')
699
- Center_p2_error=result.params.get('p2_center')
700
-
701
-
702
- if Error_bars is False:
703
- if block_print is False:
704
- print('Error bars not determined by function')
705
- Center_p1_errorval=np.nan
706
- if peaks_pk1>1:
707
- Center_p2_errorval=np.nan
708
- else:
709
- Center_p1_errorval=float(str(Center_p1_error).split()[4].replace(",", ""))
710
- if peaks_pk1>1:
711
- Center_p2_errorval=float(str(Center_p2_error).split()[4].replace(",", ""))
712
-
713
- # Check if nonsense, e.g. if center 2 miles away, just use center 0
714
- if Center_p2 is not None:
715
- if Center_p2>Center_p0 or Center_p2<1112:
716
- Center_pk1=Center_p0
717
- error_pk1=Center_p0_errorval
718
- if block_print is False:
719
- print('No meaningful second peak found')
720
-
721
- elif Center_p1 is None and Center_p2 is None:
722
- if block_print is False:
723
- print('No peaks found')
724
- elif Center_p1 is None and Center_p2>0:
725
- Center_pk1=Center_p2
726
- error_pk1=Center_p2_errorval
727
- elif Center_p2 is None and Center_p1>0:
728
- Center_pk1=Center_p1
729
- error_pk1=Center_p1_errorval
730
- elif Center_p1>Center_p2:
731
- Center_pk1=Center_p1
732
- error_pk1=Center_p1_errorval
733
- elif Center_p1<Center_p2:
734
- Center_pk1=Center_p2
735
- error_pk1=Center_p2_errorval
736
-
737
- Area_pk1=result.best_values.get('p1_amplitude')
738
- sigma_pk1=result.best_values.get('p1_sigma')
739
- gamma_pk1=result.best_values.get('p1_gamma')
740
-
741
-
742
- # Evaluate the peak at 100 values for pretty plotting
743
- xx_pk1=np.linspace(lower_pk1, upper_pk1, 2000)
744
-
745
- result_pk1=result.eval(x=xx_pk1)
746
- comps=result.eval_components(x=xx_pk1)
747
-
748
-
749
- result_pk1_origx=result.eval(x=Ne_pk1_reg_x)
750
-
751
-
752
-
753
- return Center_pk1, Area_pk1, sigma_pk1, gamma_pk1, Ne_pk1_reg_x_plot, Ne_pk1_reg_y_plot, Ne_pk1_reg_x, Ne_pk1_reg_y, xx_pk1, result_pk1, error_pk1, result_pk1_origx, comps, Peak1_Prop_Lor
754
-
755
-
756
- def fit_pk2(x, y_corr, x_span=[-5, 5], Ne_center=1447.5, amplitude=1000, pk2_sigma=0.4,
757
- model_name='PseudoVoigtModel', print_report=False, const_params=True) :
758
- """ This function fits the 1447 Ne line as a single Voigt
759
-
760
- Parameters
761
- -----------
762
-
763
- x: np.array
764
- x coordinate (wavenumber)
765
-
766
- y: np.array
767
- Background corrected intensiy
768
-
769
- x_span: list length 2. Default [-5, 5]
770
- Span either side of peak center used for fitting,
771
- e.g. by default, fits to 5 wavenumbers below peak, 5 above.
772
-
773
- Ne_center: float (default=1447.5)
774
- Center position for Ne line being fitted
775
-
776
- amplitude: integer (default = 1000)
777
- peak amplitude
778
-
779
- sigma: float (default =0.28)
780
- sigma of the voigt peak
781
-
782
-
783
- print_report: bool
784
- if True, prints fit report.
785
-
786
-
787
- """
788
- if const_params is True:
789
- min_off=0.8
790
- max_off=1.2
791
- if const_params is False:
792
- min_off=0
793
- max_off=100
794
-
795
-
796
- # This defines the range you want to fit (e.g. how big the tails are)
797
- lower_pk2=Ne_center+x_span[0]
798
- upper_pk2=Ne_center+x_span[1]
799
-
800
- # This segments into the x and y variable, and variables to plot, which are a bit bigger.
801
- Ne_pk2_reg_x=x[(x>lower_pk2)&(x<upper_pk2)]
802
- Ne_pk2_reg_y=y_corr[(x>lower_pk2)&(x<upper_pk2)]
803
- Ne_pk2_reg_x_plot=x[(x>(lower_pk2-3))&(x<(upper_pk2+3))]
804
- Ne_pk2_reg_y_plot=y_corr[(x>(lower_pk2-3))&(x<(upper_pk2+3))]
805
-
806
- if model_name == 'PseudoVoigtModel':
807
- model = PseudoVoigtModel()#+ ConstantModel(prefix='c0')
808
- if model_name=="VoigtModel":
809
- model = VoigtModel()#+ ConstantModel(prefix='c0')
810
-
811
-
812
-
813
- # create parameters with initial values
814
- params = model.make_params(center=Ne_center, amplitude=amplitude, sigma=pk2_sigma)
815
-
816
- # Place bounds on center allowed
817
- params['center'].min = Ne_center+x_span[0]
818
- params['center'].max = Ne_center+x_span[1]
819
- params['sigma'].max = pk2_sigma*max_off
820
- params['sigma'].min = pk2_sigma*min_off
821
- params['amplitude'].max = amplitude*max_off
822
- params['amplitude'].min = amplitude*min_off
823
- result = model.fit(Ne_pk2_reg_y.flatten(), params, x=Ne_pk2_reg_x.flatten())
824
-
825
- # Get center value
826
- Center_pk2=result.best_values.get('center')
827
- Center_pk2_error=result.params.get('center')
828
-
829
- Peak2_Prop_Lor=result.best_values.get('fraction')
830
-
831
- #print(result.best_values)
832
-
833
- Area_pk2=result.best_values.get('amplitude')
834
- sigma_pk2=result.best_values.get('sigma')
835
- gamma_pk2=result.best_values.get('gamma')
836
- # Have to strip away the rest of the string, as center + error
837
- # print('debug:')
838
- # print(Center_pk2_error)
839
- # Center_pk2_errorval=float(str(Center_pk2_error).split()[4].replace(",", ""))
840
- # error_pk2=Center_pk2_errorval
841
- error_pk2=1
842
-
843
- # Evaluate the peak at 100 values for pretty plotting
844
- xx_pk2=np.linspace(lower_pk2, upper_pk2, 2000)
845
-
846
- result_pk2=result.eval(x=xx_pk2)
847
- result_pk2_origx=result.eval(x=Ne_pk2_reg_x)
848
-
849
- if print_report is True:
850
- print(result.fit_report(min_correl=0.5))
851
-
852
- return Center_pk2,Area_pk2, sigma_pk2, gamma_pk2, Ne_pk2_reg_x_plot, Ne_pk2_reg_y_plot, Ne_pk2_reg_x, Ne_pk2_reg_y, xx_pk2, result_pk2, error_pk2, result_pk2_origx, Peak2_Prop_Lor
853
-
854
- ## Setting default Ne fitting parameters
855
- @dataclass
856
- class Ne_peak_config:
857
-
858
- model_name: str = 'PseudoVoigtModel'
859
- # Things for the background positioning and fit
860
- N_poly_pk1_baseline: float = 1 #Degree of polynomial to fit to the baseline
861
- N_poly_pk2_baseline: float = 1 #Degree of polynomial to fit to the baseline
862
- sigma_baseline=3 # Discard things outside of this sigma on the baseline
863
- lower_bck_pk1: Tuple[float, float] = (-50, -25) # Background position Pk1
864
- upper_bck1_pk1: Tuple[float, float] = (8, 15) # Background position Pk1
865
- upper_bck2_pk1: Tuple[float, float] = (30, 50) # Background position Pk1
866
- lower_bck_pk2: Tuple[float, float] = (-44.2, -22) # Background position Pk2
867
- upper_bck1_pk2: Tuple[float, float] = (15, 50) # Background position Pk2
868
- upper_bck2_pk2: Tuple[float, float] = (50, 51) # Background position Pk1
869
-
870
- # Whether you want a secondary peak
871
- peaks_1: float=2
872
-
873
- # SPlitting
874
- DeltaNe_ideal: float= 330.477634
875
-
876
- # Things for plotting the baseline
877
- x_range_baseline_pk1: float=20 # How many units outside your selected background it shows on the baseline plot
878
- y_range_baseline_pk1: float= 200 # Where the y axis is cut off above the minimum baseline measurement
879
- x_range_baseline_pk2: float=20 # How many units outside your selected background it shows on the baseline plot
880
- y_range_baseline_pk2: float= 200 # Where the y axis is cut off above the minimum baseline measurement
881
-
882
-
883
- # Sigma for peaks
884
- pk1_sigma: float = 0.4
885
- pk2_sigma: float = 0.4
886
-
887
- # Things for plotting the primary peak
888
- x_range_peak: float=15 # How many units to each side are shown when plotting the peak fits
889
-
890
- # Things for plotting the residual
891
- x_range_residual: float=7 # Shows how many x units to left and right is shown on residual plot
892
-
893
- # Things for fitting a secondary peak on 1117
894
- LH_offset_mini: Tuple[float, float] = (1.5, 3)
895
-
896
- # Optional, by default, fits to the points inside the baseline. Can also specify as values to make a smaller peak fit.
897
- x_span_pk1: Optional [Tuple[float, float]] = None # Tuple[float, float] = (-10, 8)
898
- x_span_pk2: Optional [Tuple[float, float]] = None # Tuple[float, float] = (-5, 5)
899
-
900
-
901
-
902
- def fit_Ne_lines(*, config: Ne_peak_config=Ne_peak_config(),
903
- Ne_center_1=1117.1, Ne_center_2=1147, Ne_prom_1=100, Ne_prom_2=200,
904
- Ne=None, filename=None, path=None, prefix=True,
905
- plot_figure=True, loop=True,
906
- save_clipboard=False,
907
- close_figure=False, const_params=True):
908
-
909
-
910
-
911
-
912
-
913
-
914
- """ This function reads in a user file, fits the Ne lines, and if required, saves an image
915
- into a new sub folder
916
-
917
- Parameters
918
- -----------
919
-
920
- Ne: np.array
921
- x coordinate (wavenumber) and y coordinate (intensity)
922
-
923
- filename and path: str
924
- used to save filename in datatable, and to make a new folder.
925
-
926
- filetype: str
927
- Identifies type of file
928
- Witec_ASCII: Datafile from WITEC with metadata for first few lines
929
- headless_txt: Txt file with no headers, just data with wavenumber in 1st col, int 2nd
930
- HORIBA_txt: Datafile from newer HORIBA machines with metadata in first rows
931
- Renishaw_txt: Datafile from renishaw with column headings.
932
-
933
- amplitude: int or float
934
- first guess of peak amplitude
935
-
936
- plot_figure: bool
937
- if True, saves figure of fit in a new folder
938
-
939
- Loop: bool
940
- If True, only returns df.
941
-
942
- x_range_baseline: flt, int
943
- How much x range outside selected baseline the baseline selection plot shows.
944
-
945
- y_range_baseline: flt, int
946
- How much above the baseline position is shown on the y axis.
947
-
948
- x_range_peak: flt, int, or None
949
- How much to either side of the peak to show on the final peak fitting plot
950
-
951
-
952
- DeltaNe_ideal: float
953
- Theoretical distance between the two peaks you have selected. Default is 330.477634 for
954
- the 1117 and 1447 Neon for the Cornell Raman. You can calculate this using the calculate_Ne_line_positions
955
-
956
-
957
- Things for Neon 1 (~1117):
958
-
959
- N_poly_pk1_baseline: int
960
- Degree of polynomial used to fit the background
961
-
962
- Ne_center_1: float
963
- Center position for Ne line being fitted
964
-
965
- lower_bck_1, upper_bck1, upper_bck1: 3 lists of length 2:
966
- Positions used for background relative to peak.[-50, -20] takes a
967
- background -50 and -20 from the peak center
968
-
969
- x_span_pk1: list length 2. Default [-10, 8]
970
- Span either side of peak center used for fitting,
971
- e.g. by default, fits to 10 wavenumbers below peak, 8 above.
972
-
973
-
974
- peaks_1: int
975
- How many peaks to fit to the 1117 Neon, if 2, tries to put a shoulder peak
976
-
977
- LH_offset_mini: list
978
- If peaks>1, puts second peak within this range left of the main peak
979
-
980
-
981
-
982
-
983
- Things for Neon 2 (~1447):
984
- N_poly_pk2_baseline: int
985
- Degree of polynomial used to fit the background
986
-
987
- Ne_center_2: float
988
- Center position for Ne line being fitted
989
-
990
- lower_bck_2, upper_bck2, upper_bck2: 3 lists of length 2:
991
- Positions used for background relative to peak.[-50, -20] takes a
992
- background -50 and -20 from the peak center
993
-
994
- x_span_pk2: list length 2. Default [-10, 8]
995
- Span either side of peak center used for fitting,
996
- e.g. by default, fits to 10 wavenumbers below peak, 8 above.
997
- """
998
-
999
- x=Ne[:, 0]
1000
- spec_res=np.abs(x[1]-x[0])
1001
- # Getting things from config file
1002
- peaks_1=config.peaks_1
1003
- DeltaNe_ideal=config.DeltaNe_ideal
1004
-
1005
- # Estimate amplitude from prominence and sigma you entered
1006
- Pk1_Amp=((config.pk1_sigma)*(Ne_prom_1))/0.3939
1007
- Pk2_Amp=((config.pk2_sigma)*(Ne_prom_2))/0.3939
1008
-
1009
-
1010
- #Remove the baselines
1011
- y_corr_pk1, Py_base_pk1, x_pk1, Ne_short_pk1, Py_base_pk1, Baseline_ysub_pk1, Baseline_x_pk1=remove_Ne_baseline_pk1(Ne,
1012
- N_poly_pk1_baseline=config.N_poly_pk1_baseline, Ne_center_1=Ne_center_1, sigma_baseline=config.sigma_baseline,
1013
- lower_bck=config.lower_bck_pk1, upper_bck1=config.upper_bck1_pk1, upper_bck2=config.upper_bck2_pk1)
1014
-
1015
- y_corr_pk2, Py_base_pk2, x_pk2, Ne_short_pk2, Py_base_pk2, Baseline_ysub_pk2, Baseline_x_pk2=remove_Ne_baseline_pk2(Ne, Ne_center_2=Ne_center_2, N_poly_pk2_baseline=config.N_poly_pk2_baseline, sigma_baseline=config.sigma_baseline,
1016
- lower_bck=config.lower_bck_pk2, upper_bck1=config.upper_bck1_pk2, upper_bck2=config.upper_bck2_pk2)
1017
-
1018
-
1019
- # Have the option to override the xspan here from default. Else, trims
1020
- if config.x_span_pk1 is None:
1021
-
1022
- x_span_pk1=[config.lower_bck_pk1[1], config.upper_bck1_pk1[0]]
1023
- x_span_pk1_dist=abs(config.lower_bck_pk1[1]-config.upper_bck1_pk1[0])
1024
- else:
1025
- x_span_pk1=config.x_span_pk1
1026
- x_span_pk1_dist=abs(config.x_span_pk1[1]-config.x_span_pk1[0])
1027
-
1028
- if config.x_span_pk2 is None:
1029
- x_span_pk2=[config.lower_bck_pk2[1], config.upper_bck1_pk2[0]]
1030
- x_span_pk2_dist=abs(config.lower_bck_pk2[1]-config.upper_bck1_pk2[0])
1031
- else:
1032
- x_span_pk2=config.x_span_pk2
1033
- x_span_pk2_dist=abs(config.x_span_pk2[1]-config.x_span_pk2[0])
1034
-
1035
- # Fit the 1117 peak
1036
- cent_pk1, Area_pk1, sigma_pk1, gamma_pk1, Ne_pk1_reg_x_plot, Ne_pk1_reg_y_plot, Ne_pk1_reg_x, Ne_pk1_reg_y, xx_pk1, result_pk1, error_pk1, result_pk1_origx, comps, Peak1_Prop_Lor = fit_pk1(x_pk1, y_corr_pk1, x_span=x_span_pk1, Ne_center=Ne_center_1,model_name=config.model_name, LH_offset_mini=config.LH_offset_mini, peaks_pk1=peaks_1, amplitude=Pk1_Amp, pk1_sigma=config.pk1_sigma,
1037
- const_params=const_params, spec_res=spec_res)
1038
-
1039
-
1040
-
1041
- # Fit the 1447 peak
1042
- cent_pk2,Area_pk2, sigma_pk2, gamma_pk2, Ne_pk2_reg_x_plot, Ne_pk2_reg_y_plot, Ne_pk2_reg_x, Ne_pk2_reg_y, xx_pk2, result_pk2, error_pk2, result_pk2_origx, Peak2_Prop_Lor = fit_pk2( x_pk2, y_corr_pk2, x_span=x_span_pk2, Ne_center=Ne_center_2, model_name=config.model_name, amplitude=Pk2_Amp, pk2_sigma=config.pk2_sigma, const_params=const_params)
1043
-
1044
-
1045
- # Calculate difference between peak centers, and Delta Ne
1046
- DeltaNe=cent_pk2-cent_pk1
1047
-
1048
- Ne_Corr=DeltaNe_ideal/DeltaNe
1049
-
1050
- # Calculate maximum splitting (+1 sigma)
1051
- DeltaNe_max=(cent_pk2+error_pk2)-(cent_pk1-error_pk1)
1052
- DeltaNe_min=(cent_pk2-error_pk2)-(cent_pk1+error_pk1)
1053
- Ne_Corr_max=DeltaNe_ideal/DeltaNe_min
1054
- Ne_Corr_min=DeltaNe_ideal/DeltaNe_max
1055
-
1056
- # Calculating least square residual
1057
- residual_pk1=np.sum(((Ne_pk1_reg_y-result_pk1_origx)**2)**0.5)/(len(Ne_pk1_reg_y))
1058
- residual_pk2=np.sum(((Ne_pk2_reg_y-result_pk2_origx)**2)**0.5)/(len(Ne_pk2_reg_y))
1059
-
1060
- if plot_figure is True:
1061
- # Make a summary figure of the backgrounds and fits
1062
- fig, ((ax3, ax2), (ax5, ax4), (ax1, ax0)) = plt.subplots(3,2, figsize = (12,15)) # adjust dimensions of figure here
1063
- fig.suptitle(filename, fontsize=16)
1064
-
1065
- # Setting y limits of axis
1066
- ymin_ax1=min(Ne_short_pk1[:,1])-10
1067
- ymax_ax1=min(Ne_short_pk1[:,1])+config.y_range_baseline_pk1
1068
- ymin_ax0=min(Ne_short_pk2[:,1])-10
1069
- ymax_ax0=min(Ne_short_pk2[:,1])+config.y_range_baseline_pk2
1070
- ax1.set_ylim([ymin_ax1, ymax_ax1])
1071
- ax0.set_ylim([ymin_ax0, ymax_ax0])
1072
-
1073
- # Setting x limits of axis
1074
-
1075
- ax1_xmin=min(Ne_short_pk1[:,0])-config.x_range_baseline_pk1
1076
- ax1_xmax=max(Ne_short_pk1[:,0])+config.x_range_baseline_pk1
1077
- ax0_xmin=min(Ne_short_pk2[:,0])-config.x_range_baseline_pk2
1078
- ax0_xmax=max(Ne_short_pk2[:,0])+config.x_range_baseline_pk2
1079
- ax0.set_xlim([ax0_xmin, ax0_xmax])
1080
- ax1.set_xlim([ax1_xmin, ax1_xmax])
1081
-
1082
- # Adding background positions as colored bars on pk1
1083
- ax1cop=ax1.twiny()
1084
- #ax1cop.set_zorder(ax1.get_zorder())
1085
- ax1cop_xmax=ax1_xmax-Ne_center_1
1086
- ax1cop_xmin=ax1_xmin-Ne_center_1
1087
- ax1cop.set_xlim([ax1cop_xmin, ax1cop_xmax])
1088
- rect_pk1_b1=patches.Rectangle((config.lower_bck_pk1[0],ymin_ax1),config.lower_bck_pk1[1]-config.lower_bck_pk1[0],ymax_ax1-ymin_ax1,
1089
- linewidth=1,edgecolor='none',facecolor='cyan', label='bck_pk1', alpha=0.3, zorder=0)
1090
- ax1cop.add_patch(rect_pk1_b1)
1091
- rect_pk1_b2=patches.Rectangle((config.upper_bck1_pk1[0],ymin_ax1),config.upper_bck1_pk1[1]-config.upper_bck1_pk1[0],ymax_ax1-ymin_ax1,
1092
- linewidth=1,edgecolor='none',facecolor='cyan', label='bck_pk2', alpha=0.3, zorder=0)
1093
- ax1cop.add_patch(rect_pk1_b2)
1094
- rect_pk1_b3=patches.Rectangle((config.upper_bck2_pk1[0],ymin_ax1),config.upper_bck2_pk1[1]-config.upper_bck2_pk1[0],ymax_ax1-ymin_ax1,
1095
- linewidth=1,edgecolor='none',facecolor='cyan', label='bck_pk3', alpha=0.3, zorder=0)
1096
- ax1cop.add_patch(rect_pk1_b3)
1097
-
1098
- # Adding background positions as colored bars on pk2
1099
- ax0cop=ax0.twiny()
1100
- ax0cop_xmax=ax0_xmax-Ne_center_2
1101
- ax0cop_xmin=ax0_xmin-Ne_center_2
1102
- ax0cop.set_xlim([ax0cop_xmin, ax0cop_xmax])
1103
- rect_pk2_b1=patches.Rectangle((config.lower_bck_pk2[0],ymin_ax0),config.lower_bck_pk2[1]-config.lower_bck_pk2[0],ymax_ax0-ymin_ax0,
1104
- linewidth=1,edgecolor='none',facecolor='cyan', label='bck_pk2', alpha=0.3)
1105
- ax0cop.add_patch(rect_pk2_b1)
1106
- rect_pk2_b2=patches.Rectangle((config.upper_bck1_pk2[0],ymin_ax0),config.upper_bck1_pk2[1]-config.upper_bck1_pk2[0],ymax_ax0-ymin_ax0,
1107
- linewidth=1,edgecolor='none',facecolor='cyan', label='bck_pk2', alpha=0.3)
1108
- ax0cop.add_patch(rect_pk2_b2)
1109
- rect_pk2_b3=patches.Rectangle((config.upper_bck2_pk2[0],ymin_ax0),config.upper_bck2_pk2[1]-config.upper_bck2_pk2[0],ymax_ax0-ymin_ax0,
1110
- linewidth=1,edgecolor='none',facecolor='cyan', label='bck_pk3', alpha=0.3)
1111
- ax0cop.add_patch(rect_pk2_b3)
1112
-
1113
- # Plotting trimmed data and background
1114
- ax0.plot(Ne_short_pk2[:,0], Py_base_pk2, '-k', label='Fit. Bck')
1115
- ax0.plot(Ne_short_pk2[:,0], Ne_short_pk2[:,1], '-r')
1116
-
1117
- ax1.plot(Ne_short_pk1[:,0], Py_base_pk1, '-k', label='Fit. Bck')
1118
- ax1.plot(Ne_short_pk1[:,0], Ne_short_pk1[:,1], '-r')
1119
-
1120
- # Plotting data actually used in the background, after sigma exclusion
1121
- ax1.plot(Baseline_x_pk1, Baseline_ysub_pk1, '.b', ms=6, label='Bck')
1122
- ax0.plot(Baseline_x_pk2, Baseline_ysub_pk2, '.b', ms=5, label='Bck')
1123
-
1124
-
1125
- mean_baseline=np.mean(Py_base_pk2)
1126
- std_baseline=np.std(Py_base_pk2)
1127
-
1128
- std_baseline=np.std(Py_base_pk1)
1129
-
1130
-
1131
- ax1.plot([Ne_center_1, Ne_center_1], [ymin_ax1, ymax_ax1], ':k', label='Peak')
1132
- ax0.plot([Ne_center_2, Ne_center_2], [ymin_ax0, ymax_ax0], ':k', label='Peak')
1133
-
1134
- ax1.set_title('%.0f' %Ne_center_1+': background fitting')
1135
- ax1.set_xlabel('Wavenumber')
1136
- ax1.set_ylabel('Intensity')
1137
- ax0.set_title('%.0f' %Ne_center_2+ ': background fitting')
1138
- ax0.set_xlabel('Wavenumber')
1139
- ax0.set_ylabel('Intensity')
1140
- ax1cop.set_xlabel('Offset from Pk estimate')
1141
- ax0cop.set_xlabel('Offset from Pk estimate')
1142
-
1143
- #Showing all data, not just stuff fit
1144
-
1145
-
1146
- ax0.plot(Ne[:,0], Ne[:,1], '-', color='grey', zorder=0)
1147
- ax1.plot(Ne[:,0], Ne[:,1], '-', color='grey', zorder=0)
1148
-
1149
-
1150
-
1151
- # ax1.legend()
1152
- # ax0.legend()
1153
- ax0.legend(loc='lower right', bbox_to_anchor= (-0.08, 0.8), ncol=1,
1154
- borderaxespad=0, frameon=True, facecolor='white')
1155
-
1156
- # Actual peak fits.
1157
- ax2.plot(Ne_pk2_reg_x_plot, Ne_pk2_reg_y_plot, 'xb', label='all data')
1158
- ax2.plot(Ne_pk2_reg_x, Ne_pk2_reg_y, 'ok', label='data fitted')
1159
- ax2.plot(xx_pk2, result_pk2, 'r-', label='interpolated fit')
1160
- ax2.set_title('%.0f' %Ne_center_2+' peak fitting')
1161
- ax2.set_xlabel('Wavenumber')
1162
- ax2.set_ylabel('Intensity')
1163
- if config.x_range_peak is None:
1164
- ax2.set_xlim([cent_pk2-x_span_pk2_dist/2, cent_pk2+x_span_pk2_dist/2])
1165
- else:
1166
- ax2.set_xlim([cent_pk2-config.x_range_peak, cent_pk2+config.x_range_peak])
1167
-
1168
-
1169
- ax3.plot(Ne_pk1_reg_x_plot, Ne_pk1_reg_y_plot, 'xb', label='all data')
1170
- ax3.plot(Ne_pk1_reg_x, Ne_pk1_reg_y, 'ok', label='data fitted')
1171
-
1172
- ax3.set_title('%.0f' %Ne_center_1+' peak fitting')
1173
- ax3.set_xlabel('Wavenumber')
1174
- ax3.set_ylabel('Intensity')
1175
- ax3.plot(xx_pk1, comps.get('p1_'), '-r', label='p1')
1176
- if peaks_1>1:
1177
- ax3.plot(xx_pk1, comps.get('p2_'), '-c', label='p2')
1178
- ax3.plot(xx_pk1, result_pk1, 'g-', label='best fit')
1179
- ax3.legend()
1180
- if config.x_range_peak is None:
1181
- ax3.set_xlim([cent_pk1-x_span_pk1_dist/2, cent_pk1+x_span_pk1_dist/2])
1182
- else:
1183
- ax3.set_xlim([cent_pk1-config.x_range_peak, cent_pk1+config.x_range_peak ])
1184
-
1185
- # Residuals for peak fits.
1186
- ax4.plot(Ne_pk2_reg_x, Ne_pk2_reg_y-result_pk2_origx, '-r', label='residual')
1187
- ax5.plot(Ne_pk1_reg_x, Ne_pk1_reg_y-result_pk1_origx, '-r', label='residual')
1188
- ax4.plot(Ne_pk2_reg_x, Ne_pk2_reg_y-result_pk2_origx, 'ok', mfc='r', label='residual')
1189
- ax5.plot(Ne_pk1_reg_x, Ne_pk1_reg_y-result_pk1_origx, 'ok', mfc='r', label='residual')
1190
- ax4.set_ylabel('Residual (Intensity units)')
1191
- ax4.set_xlabel('Wavenumber')
1192
- ax5.set_ylabel('Residual (Intensity units)')
1193
- ax5.set_xlabel('Wavenumber')
1194
- Residual_pk2=Ne_pk2_reg_y-result_pk2_origx
1195
- Residual_pk1=Ne_pk1_reg_y-result_pk1_origx
1196
-
1197
- Local_Residual_pk2=Residual_pk2[(Ne_pk2_reg_x>cent_pk2-config.x_range_residual)&(Ne_pk2_reg_x<cent_pk2+config.x_range_residual)]
1198
- Local_Residual_pk1=Residual_pk1[(Ne_pk1_reg_x>cent_pk1-config.x_range_residual)&(Ne_pk1_reg_x<cent_pk1+config.x_range_residual)]
1199
- ax5.set_xlim([cent_pk1-config.x_range_residual, cent_pk1+config.x_range_residual])
1200
- ax4.set_xlim([cent_pk2-config.x_range_residual, cent_pk2+config.x_range_residual])
1201
- ax5.plot([cent_pk1, cent_pk1 ], [np.min(Local_Residual_pk1)-10, np.max(Local_Residual_pk1)+10], ':k')
1202
- ax4.plot([cent_pk2, cent_pk2 ], [np.min(Local_Residual_pk2)-10, np.max(Local_Residual_pk2)+10], ':k')
1203
- ax5.set_ylim([np.min(Local_Residual_pk1)-10, np.max(Local_Residual_pk1)+10])
1204
- ax4.set_ylim([np.min(Local_Residual_pk2)-10, np.max(Local_Residual_pk2)+10])
1205
- fig.tight_layout()
1206
-
1207
- # Save figure
1208
- path3=path+'/'+'Ne_fit_images'
1209
- if os.path.exists(path3):
1210
- out='path exists'
1211
- else:
1212
- os.makedirs(path+'/'+ 'Ne_fit_images', exist_ok=False)
1213
-
1214
- figure_str=path+'/'+ 'Ne_fit_images'+ '/'+ filename+str('_Ne_Line_Fit')+str('.png')
1215
-
1216
- fig.savefig(figure_str, dpi=200)
1217
- if close_figure is True:
1218
- plt.close(fig)
1219
-
1220
- if prefix is True:
1221
-
1222
- filename=filename.split(' ')[1:][0]
1223
- df=pd.DataFrame(data={'filename': filename,
1224
- 'pk2_peak_cent':cent_pk2,
1225
- 'pk2_amplitude': Area_pk2,
1226
- 'pk2_sigma': sigma_pk2,
1227
- 'pk2_gamma': gamma_pk2,
1228
- 'error_pk2': error_pk2,
1229
- 'Peak2_Prop_Lor': Peak2_Prop_Lor,
1230
- 'pk1_peak_cent':cent_pk1,
1231
- 'pk1_amplitude': Area_pk1,
1232
- 'pk1_sigma': sigma_pk1,
1233
- 'pk1_gamma': gamma_pk1,
1234
- 'error_pk1': error_pk1,
1235
- 'Peak1_Prop_Lor': Peak1_Prop_Lor,
1236
-
1237
- 'deltaNe': DeltaNe,
1238
- 'Ne_Corr': Ne_Corr,
1239
- 'Ne_Corr_min':Ne_Corr_min,
1240
- 'Ne_Corr_max': Ne_Corr_max,
1241
- 'residual_pk2':residual_pk2,
1242
- 'residual_pk1': residual_pk1,
1243
- 'residual_pk1+pk2':residual_pk1+residual_pk2,
1244
- }, index=[0])
1245
- if save_clipboard is True:
1246
- df.to_clipboard(excel=True, header=False, index=False)
1247
-
1248
- if loop is False:
1249
- return df, Ne_pk1_reg_x_plot, Ne_pk1_reg_y_plot
1250
- if loop is True:
1251
- return df
1252
-
1253
-
1254
-
1255
- ## Plot to help inspect which Ne lines to discard
1256
- def plot_Ne_corrections(df=None, x_axis=None, x_label='index', marker='o', mec='k',
1257
- mfc='r'):
1258
- if x_axis is not None:
1259
- x=x_axis
1260
- else:
1261
- x=df.index
1262
- fig, ((ax5, ax6), (ax1, ax2), (ax3, ax4), ) = plt.subplots(3, 2, figsize=(10, 12))
1263
- ax1.plot(x, df['Ne_Corr'], marker, mec='k', mfc='grey')
1264
- ax1.set_ylabel('Ne Correction factor')
1265
- ax1.set_xlabel(x_label)
1266
-
1267
- ax5.plot(x, df['pk1_peak_cent'], marker, mec='k', mfc='b')
1268
- ax6.plot(x, df['pk2_peak_cent'], marker, mec='k', mfc='r')
1269
- ax5.set_xlabel(x_label)
1270
- ax6.set_xlabel(x_label)
1271
- ax5.set_ylabel('Peak 1 center')
1272
- ax6.set_ylabel('Peak 2 center')
1273
-
1274
-
1275
- ax2.plot( df['residual_pk2']+df['residual_pk1'], df['Ne_Corr'], marker, mec='k', mfc='r')
1276
- ax2.set_xlabel('Sum of pk1 and pk2 residual')
1277
- ax2.set_ylabel('Ne Correction factor')
1278
-
1279
- ax3.plot(df['Ne_Corr'], df['pk2_peak_cent'],marker, mec='k', mfc='r')
1280
- ax3.set_xlabel('Ne Correction factor')
1281
- ax3.set_ylabel('Peak 2 center')
1282
-
1283
- ax4.plot(df['Ne_Corr'], df['pk1_peak_cent'], marker, mec='k', mfc='b')
1284
- ax4.set_xlabel('Ne Correction factor')
1285
- ax4.set_ylabel('Peak 1 center')
1286
-
1287
- plt.setp(ax3.get_xticklabels(), rotation=30, horizontalalignment='right')
1288
- plt.setp(ax4.get_xticklabels(), rotation=30, horizontalalignment='right')
1289
- ax1.ticklabel_format(useOffset=False)
1290
- ax5.ticklabel_format(useOffset=False)
1291
- ax6.ticklabel_format(useOffset=False)
1292
- ax2.ticklabel_format(useOffset=False)
1293
- ax3.ticklabel_format(useOffset=False)
1294
- ax4.ticklabel_format(useOffset=False)
1295
- fig.tight_layout()
1296
- return fig
1297
-
1298
- ## Looping Ne lines
1299
- def loop_Ne_lines(*, files, spectra_path, filetype,
1300
- config, config_ID_peaks, df_fit_params=None, prefix=False, print_df=False,
1301
- plot_figure=True, single_acq=False):
1302
-
1303
- df = pd.DataFrame([])
1304
- # This is for repeated acquisition of Ne lines
1305
- if single_acq is True:
1306
-
1307
- for i in tqdm(range(0, np.shape(files)[1]-2)):
1308
- Ne=np.column_stack((files[:, 0], files[:, i+1]))
1309
- filename=str(i)
1310
-
1311
- Ne, df_fit_params=identify_Ne_lines(Ne_array=Ne,
1312
- config=config_ID_peaks, print_df=False, plot_figure=False)
1313
-
1314
-
1315
- data=fit_Ne_lines(Ne=Ne, path=spectra_path,
1316
- config=config, prefix=prefix,
1317
- Ne_center_1=df_fit_params['Peak1_cent'].iloc[0],
1318
- Ne_center_2=df_fit_params['Peak2_cent'].iloc[0],
1319
- Ne_prom_1=df_fit_params['Peak1_prom'].iloc[0],
1320
- Ne_prom_2=df_fit_params['Peak2_prom'].iloc[0],
1321
- plot_figure=plot_figure)
1322
- df = pd.concat([df, data], axis=0)
1323
-
1324
- else:
1325
- for i in tqdm(range(0, len(files))):
1326
- filename=files[i]
1327
-
1328
- Ne, df_fit_params=identify_Ne_lines(path=spectra_path,
1329
- filename=filename, filetype=filetype,
1330
- config=config_ID_peaks, print_df=False, plot_figure=False)
1331
-
1332
- data=fit_Ne_lines(Ne=Ne, filename=filename,
1333
- path=spectra_path, prefix=prefix,
1334
- config=config,
1335
- Ne_center_1=df_fit_params['Peak1_cent'].iloc[0],
1336
- Ne_center_2=df_fit_params['Peak2_cent'].iloc[0],
1337
- Ne_prom_1=df_fit_params['Peak1_prom'].iloc[0],
1338
- Ne_prom_2=df_fit_params['Peak2_prom'].iloc[0],
1339
- plot_figure=plot_figure)
1340
- df = pd.concat([df, data], axis=0)
1341
-
1342
-
1343
-
1344
-
1345
-
1346
- #print('working on ' + str(files[i]))
1347
-
1348
-
1349
- df2=df.reset_index(drop=True)
1350
-
1351
- # Now lets reorder some columns
1352
-
1353
- cols_to_move = ['filename', 'Ne_Corr', 'deltaNe', 'pk2_peak_cent', 'pk1_peak_cent', 'pk2_amplitude', 'pk1_amplitude', 'residual_pk2', 'residual_pk1']
1354
- df2 = df2[cols_to_move + [
1355
- col for col in df2.columns if col not in cols_to_move]]
1356
-
1357
-
1358
- return df2
1359
-
1360
- ## Regressing Ne lines against time
1361
- from scipy.interpolate import interp1d
1362
- def reg_Ne_lines_time(df, fit='poly', N_poly=None, spline_fit=None):
1363
- """
1364
- Parameters
1365
- -----------
1366
- df: pd.DataFrame
1367
- dataframe of stitched Ne fits and metadata information from WITEC,
1368
- must have columns 'sec since midnight' and 'Ne_Corr'
1369
-
1370
- fit: float 'poly', or 'spline'
1371
- If 'poly':
1372
- N_poly: int, degree of polynomial to fit (1 if linear)
1373
- if 'spline':
1374
- spline_fit: The string has to be one of:
1375
- ‘linear’, ‘nearest’, ‘nearest-up’, ‘zero’, ‘slinear’,
1376
- ‘quadratic’, ‘cubic’, ‘previous’. Look up documentation for interpld
1377
-
1378
- Returns
1379
- -----------
1380
- figure of fit and data used to make it
1381
- Pf: fit model, can be used to evaluate unknown data (only within x range of df for spline fits).
1382
-
1383
-
1384
-
1385
-
1386
- """
1387
- Px=np.linspace(np.min(df['sec since midnight']), np.max(df['sec since midnight']),
1388
- 101)
1389
- if fit=='poly':
1390
- Pf = np.poly1d(np.polyfit(df['sec since midnight'], df['Ne_Corr'],
1391
- N_poly))
1392
-
1393
- if fit == 'spline':
1394
- Pf = interp1d(df['sec since midnight'], df['Ne_Corr'], kind=spline_fit)
1395
-
1396
- Py=Pf(Px)
1397
-
1398
- fig, (ax1) = plt.subplots(1, 1, figsize=(6, 3))
1399
- ax1.plot(df['sec since midnight'], df['Ne_Corr'], 'xk')
1400
- ax1.plot(Px, Py, '-r')
1401
- ax1.set_xlabel('Seconds since midnight')
1402
- ax1.set_ylabel('Ne Correction Factor')
1403
-
1404
- ax1.ticklabel_format(useOffset=False)
1405
-
1406
-
1407
- return Pf, fig
1408
-
1409
-
1410
- def filter_Ne_Line_neighbours(Corr_factor, number_av=6, offset=0.00005):
1411
- Corr_factor_Filt=np.empty(len(Corr_factor), dtype=float)
1412
- median_loop=np.empty(len(Corr_factor), dtype=float)
1413
-
1414
- for i in range(0, len(Corr_factor)):
1415
- if i<len(Corr_factor)/2: # For first half, do 5 after
1416
- median_loop[i]=np.nanmedian(Corr_factor[i:i+number_av])
1417
- if i>=len(Corr_factor)/2: # For first half, do 5 after
1418
- median_loop[i]=np.nanmedian(Corr_factor[i-number_av:i])
1419
- if Corr_factor[i]>(median_loop[i]+offset) or Corr_factor[i]<(median_loop[i]-offset) :
1420
- Corr_factor_Filt[i]=np.nan
1421
- else:
1422
- Corr_factor_Filt[i]=Corr_factor[i]
1423
- ds=pd.Series(Corr_factor_Filt)
1424
-
1425
-
1426
-
1427
- return ds
1428
-
1429
-
1430
-
1431
-