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