rubycond_cb 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rubycond_CB/example/Neon_data.npy +0 -0
- rubycond_CB/model/CB_Model.py +277 -0
- rubycond_CB/model/Neon_Astrosurf.csv +25 -0
- rubycond_CB/model/Neon_PI.csv +20 -0
- rubycond_CB/rubycond_CB.py +549 -0
- rubycond_CB/view/CB_View.py +992 -0
- rubycond_CB/view/CB_about.py +237 -0
- rubycond_CB/view/CB_calib_controls.py +278 -0
- rubycond_CB/view/logo_CP_Scaled.png +0 -0
- rubycond_CB/view/logo_IMPMC_Scaled.png +0 -0
- rubycond_CB/view/manual.pdf +0 -0
- rubycond_cb-0.2.0.dist-info/METADATA +85 -0
- rubycond_cb-0.2.0.dist-info/RECORD +15 -0
- rubycond_cb-0.2.0.dist-info/WHEEL +4 -0
- rubycond_cb-0.2.0.dist-info/entry_points.txt +3 -0
|
Binary file
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
|
|
4
|
+
Title: Rubycond_CB: tool for calibrating the wavelength of the spectrometer
|
|
5
|
+
|
|
6
|
+
This file is part of Rubycond: Pressure by Ruby Luminescence (PRL) software to determine pressure in diamond anvil cell experiments.
|
|
7
|
+
|
|
8
|
+
Version 0.2.0
|
|
9
|
+
Release 260301
|
|
10
|
+
|
|
11
|
+
Author:
|
|
12
|
+
|
|
13
|
+
Yiuri Garino
|
|
14
|
+
yiuri.garino@cnrs.fr
|
|
15
|
+
|
|
16
|
+
Copyright (c) 2023-2026 Yiuri Garino
|
|
17
|
+
|
|
18
|
+
Download:
|
|
19
|
+
https://github.com/CelluleProjet/Rubycond_calc
|
|
20
|
+
|
|
21
|
+
License: GPLv3
|
|
22
|
+
|
|
23
|
+
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
24
|
+
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
25
|
+
You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def reset():
|
|
30
|
+
import sys
|
|
31
|
+
|
|
32
|
+
if hasattr(sys, 'ps1'):
|
|
33
|
+
|
|
34
|
+
#clean Console and Memory
|
|
35
|
+
from IPython import get_ipython
|
|
36
|
+
get_ipython().run_line_magic('clear','/')
|
|
37
|
+
get_ipython().run_line_magic('reset','-sf')
|
|
38
|
+
print("Running interactively")
|
|
39
|
+
print()
|
|
40
|
+
terminal = False
|
|
41
|
+
else:
|
|
42
|
+
print("Running in terminal")
|
|
43
|
+
print()
|
|
44
|
+
terminal = True
|
|
45
|
+
|
|
46
|
+
if __name__ == '__main__':
|
|
47
|
+
reset()
|
|
48
|
+
|
|
49
|
+
import configparser as cp
|
|
50
|
+
from pathlib import Path
|
|
51
|
+
import numpy as np
|
|
52
|
+
import matplotlib.pyplot as plt
|
|
53
|
+
import os
|
|
54
|
+
from datetime import datetime
|
|
55
|
+
from lmfit.models import LinearModel, GaussianModel, PolynomialModel
|
|
56
|
+
from PyQt5 import QtWidgets, QtCore, QtGui
|
|
57
|
+
from scipy.special import wofz
|
|
58
|
+
from scipy.interpolate import Akima1DInterpolator
|
|
59
|
+
from pathlib import Path
|
|
60
|
+
|
|
61
|
+
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
62
|
+
|
|
63
|
+
class my_model():
|
|
64
|
+
def __init__(self, debug = False):
|
|
65
|
+
self.debug = debug
|
|
66
|
+
self.version = 'RC_Model_11.py'
|
|
67
|
+
if self.debug: print('\nModel\n')
|
|
68
|
+
try:
|
|
69
|
+
#file_NEON = Path("Neon_Astrosurf.csv")
|
|
70
|
+
file_NEON = Path("Neon_PI.csv")
|
|
71
|
+
path_NEON = Path(__file__).parent.resolve() / file_NEON
|
|
72
|
+
except:
|
|
73
|
+
path_NEON = file_NEON
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
self.Neon_lines = np.loadtxt(path_NEON, delimiter=',')
|
|
77
|
+
self.peaks_data_x = np.zeros((2,2)) #Data from open file
|
|
78
|
+
self.peaks_data_y = np.zeros((2,2)) #Data from open file
|
|
79
|
+
self.peaks_data_x_simple_calib = np.zeros((2,2))
|
|
80
|
+
|
|
81
|
+
self.fit_delta_pixel = .6
|
|
82
|
+
|
|
83
|
+
self.Last_peaks_pixel_clicked = 0
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
self.delta = 20
|
|
87
|
+
self.calib_cn = [0,0,0,0] #max poly degree = 3
|
|
88
|
+
|
|
89
|
+
#
|
|
90
|
+
self.statusbar_message_ref = [print] #List of messages method (print, label, statusbar, etc)
|
|
91
|
+
|
|
92
|
+
def statusbar_message_add(self, method):
|
|
93
|
+
#print(method)
|
|
94
|
+
self.statusbar_message_ref.append(method)
|
|
95
|
+
|
|
96
|
+
def nearest_neon(self, value):
|
|
97
|
+
if value is not None:
|
|
98
|
+
i = abs(self.Neon_lines[:,0]-value).argmin()
|
|
99
|
+
data = self.Neon_lines[i]
|
|
100
|
+
#print(self.Neon_lines)
|
|
101
|
+
#print(abs(self.Neon_lines-value))
|
|
102
|
+
if self.debug :
|
|
103
|
+
print(f'input {i}: {value}')
|
|
104
|
+
print(f'output : {data}')
|
|
105
|
+
else:
|
|
106
|
+
data = 0
|
|
107
|
+
return data
|
|
108
|
+
|
|
109
|
+
def peaks_data_pixel(self, value):
|
|
110
|
+
if value is not None:
|
|
111
|
+
data = abs(self.peaks_data_x-value).argmin()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
#print(abs(self.Neon_lines-value))
|
|
115
|
+
if self.debug :
|
|
116
|
+
print(self.peaks_data_x)
|
|
117
|
+
print(f'input : {value}')
|
|
118
|
+
print(f'output : {data}')
|
|
119
|
+
else:
|
|
120
|
+
data = 0
|
|
121
|
+
return data
|
|
122
|
+
|
|
123
|
+
def button_calc_simple_calib(self):
|
|
124
|
+
y1 = self.Neon_Left_reference
|
|
125
|
+
y2 = self.Neon_Right_reference
|
|
126
|
+
x1 = self.Peaks_Left_reference
|
|
127
|
+
x2 = self.Peaks_Right_reference
|
|
128
|
+
s = (y2 - y1)/(x2 - x1)
|
|
129
|
+
i = y1 - s*x1
|
|
130
|
+
size = len(self.peaks_data_x)
|
|
131
|
+
pixels = np.arange(0,size,1)
|
|
132
|
+
if self.debug :
|
|
133
|
+
print(f'slope : {s}')
|
|
134
|
+
print(f'intercept : {i}')
|
|
135
|
+
self.peaks_data_x_simple_calib = i + s*pixels
|
|
136
|
+
|
|
137
|
+
def format_fit_results(self, degree, res):
|
|
138
|
+
message = 'Fit Output\n'
|
|
139
|
+
message+= '\n'
|
|
140
|
+
# message+= 'Wavelengths (nm) = c0 + c1 * pixels + c2 * pixels\u00B2 + c3 * pixels\u00B3\n'
|
|
141
|
+
# message+= '\n\n'
|
|
142
|
+
for i in range(degree+1):
|
|
143
|
+
|
|
144
|
+
message+= f'c{i} = '
|
|
145
|
+
message+= f"{res.params[f'line_c{i}'].value:.5e}"
|
|
146
|
+
message+= '\n'
|
|
147
|
+
self.calib_cn[i] = res.params[f'line_c{i}'].value
|
|
148
|
+
for j in range(3 - degree):
|
|
149
|
+
message+= f'c{j + degree +1} = 0'
|
|
150
|
+
message+= '\n'
|
|
151
|
+
self.calib_cn[j + degree +1] = 0
|
|
152
|
+
print(message)
|
|
153
|
+
print(self.calib_cn)
|
|
154
|
+
self.print_message_output = message
|
|
155
|
+
|
|
156
|
+
def calib_poly_3(self, data_x, data_y):
|
|
157
|
+
degree = min(len(data_x)-1, 3)
|
|
158
|
+
model = PolynomialModel(degree=degree, prefix='line_')
|
|
159
|
+
pars = model.guess(data_y, data_x)
|
|
160
|
+
res = model.fit(data_y, pars, x = data_x)
|
|
161
|
+
if self.debug: pars.pretty_print()
|
|
162
|
+
#if self.debug: res.fit_report()#TODO restore in next version
|
|
163
|
+
print(res.fit_report()) #TODO cut in next version
|
|
164
|
+
size = len(self.peaks_data_x)
|
|
165
|
+
pixels = np.arange(0,size,1)
|
|
166
|
+
|
|
167
|
+
self.peaks_data_x_simple_calib = model.eval(res.params, x = pixels)
|
|
168
|
+
|
|
169
|
+
#prepare message to be used in layout_Output
|
|
170
|
+
self.format_fit_results(degree, res)
|
|
171
|
+
|
|
172
|
+
def fit_peak_range(self):
|
|
173
|
+
try:
|
|
174
|
+
center = self.Last_peaks_pixel_clicked
|
|
175
|
+
delta = self.delta
|
|
176
|
+
data_x_graph = self.peaks_data_x[center - delta: center + delta + 1]
|
|
177
|
+
data_x_pixel = np.linspace(center - delta, center + delta, delta*2+1)
|
|
178
|
+
data_y = self.peaks_data_y[center - delta: center + delta + 1]
|
|
179
|
+
data_x, data_y, init, fit_x, fit_y, res = self.fit_peak(data_x_pixel, data_y)
|
|
180
|
+
|
|
181
|
+
spl = Akima1DInterpolator(data_x_pixel, data_x_graph)
|
|
182
|
+
|
|
183
|
+
data_x = spl(data_x)
|
|
184
|
+
fit_x = spl(fit_x)
|
|
185
|
+
center_pixel = res.params['center'].value
|
|
186
|
+
center_graph = spl(center_pixel)
|
|
187
|
+
return data_x, data_y, init, fit_x, fit_y, center_graph, center_pixel
|
|
188
|
+
except:
|
|
189
|
+
return None
|
|
190
|
+
|
|
191
|
+
def fit_peak(self, data_x, data_y):
|
|
192
|
+
model = LinearModel() + GaussianModel()
|
|
193
|
+
pars = model.make_params()
|
|
194
|
+
|
|
195
|
+
y1 = data_y[0]
|
|
196
|
+
y2 = data_y[-1]
|
|
197
|
+
x1 = data_x[0]
|
|
198
|
+
x2 = data_x[-1]
|
|
199
|
+
slope = (y2 - y1)/(x2 - x1)
|
|
200
|
+
intercept = y1 - slope*x1
|
|
201
|
+
pars['slope'].value = slope
|
|
202
|
+
pars['intercept'].value = intercept
|
|
203
|
+
center = data_x[data_y.argmax()]
|
|
204
|
+
pars['center'].value = center
|
|
205
|
+
sigma = 5 * abs(data_x[1] - data_x[0])
|
|
206
|
+
pars['sigma'].value = sigma
|
|
207
|
+
height = data_y.max() - (intercept + slope * center)
|
|
208
|
+
pars['amplitude'].value = self.Gauss_amplitude(height, sigma)
|
|
209
|
+
|
|
210
|
+
init = model.eval(pars, x = data_x)
|
|
211
|
+
if self.debug: pars.pretty_print()
|
|
212
|
+
res = model.fit(data_y, pars, x = data_x)
|
|
213
|
+
|
|
214
|
+
if self.debug: print(res.fit_report())
|
|
215
|
+
size = len(self.peaks_data_x)
|
|
216
|
+
fit_x = np.linspace(data_x.min(),data_x.max(),500)
|
|
217
|
+
fit_y = model.eval(res.params, x = fit_x)
|
|
218
|
+
|
|
219
|
+
return data_x, data_y, init, fit_x, fit_y, res
|
|
220
|
+
|
|
221
|
+
def x_2_nm(self, x, i, c1, c2, c3):
|
|
222
|
+
return i +c1*x + c2*x**2 +c3*x**3
|
|
223
|
+
|
|
224
|
+
def Voigt(self, x, c0, c1, c2, c3, amplitude, center, sigma, gamma):
|
|
225
|
+
"""
|
|
226
|
+
Return the Voigt line shape at x with Lorentzian component HWHM gamma
|
|
227
|
+
and Gaussian component sigma.
|
|
228
|
+
sigma = HWHM / sqrt(2 * log(2))
|
|
229
|
+
|
|
230
|
+
"""
|
|
231
|
+
nm = c0 +c1*x + c2*x**2 +c3*x**3
|
|
232
|
+
return amplitude*np.real(wofz((nm - center + 1j*gamma)/sigma/np.sqrt(2))) / sigma /np.sqrt(2*np.pi)
|
|
233
|
+
|
|
234
|
+
def Voigt_amplitude(self, height, gamma, sigma):
|
|
235
|
+
|
|
236
|
+
return height*(sigma*np.sqrt(2*np.pi))/wofz((1j*gamma)/(sigma*np.sqrt(2))).real
|
|
237
|
+
|
|
238
|
+
def Gauss(self, x, c0, c1, c2, c3, amplitude, center, sigma):
|
|
239
|
+
#x pixel
|
|
240
|
+
nm = c0 +c1*x + c2*x**2 +c3*x**3
|
|
241
|
+
cm = 1e7/nm
|
|
242
|
+
center_cm = 1e7/center
|
|
243
|
+
return amplitude / sigma / np.sqrt(2*np.pi) * np.exp( - (cm -center_cm) **2 / 2 / sigma**2 )
|
|
244
|
+
|
|
245
|
+
def Gauss_amplitude(self, height, sigma):
|
|
246
|
+
return sigma * np.sqrt(2*np.pi) * height
|
|
247
|
+
|
|
248
|
+
#
|
|
249
|
+
def statusbar_message(self, message):
|
|
250
|
+
now = datetime.now()
|
|
251
|
+
text = now.strftime("%H:%M:%S : ") + message
|
|
252
|
+
for method in self.statusbar_message_ref:
|
|
253
|
+
method(text)
|
|
254
|
+
|
|
255
|
+
def error_box(self, error):
|
|
256
|
+
msgBox = QtWidgets.QMessageBox()
|
|
257
|
+
msgBox.setIcon(QtWidgets.QMessageBox.Critical)
|
|
258
|
+
msgBox.setWindowTitle("Script Error")
|
|
259
|
+
msgBox.setText(str(error))
|
|
260
|
+
msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
|
261
|
+
msgBox.exec()
|
|
262
|
+
'''
|
|
263
|
+
#%%
|
|
264
|
+
data_x = np.array((300,400,500,600))
|
|
265
|
+
data_y = np.array((300,400,500,600))
|
|
266
|
+
model = PolynomialModel(degree=2, prefix='line_')
|
|
267
|
+
pars = model.guess(data_y, data_x)
|
|
268
|
+
res = model.fit(data_y, pars, x = data_x)
|
|
269
|
+
pars.pretty_print()
|
|
270
|
+
res.params.pretty_print()
|
|
271
|
+
res.fit_report()
|
|
272
|
+
|
|
273
|
+
Fit = self.model.eval(self.out.params, x = x_fit_lmfit)
|
|
274
|
+
self.center_pk = self.out.params['pk_center'].value
|
|
275
|
+
self.out = self.model.fit(y_fit, self.pars, x = x, max_nfev = self.Fit_iter_lim.get())
|
|
276
|
+
comps = self.out.eval_components(x = x_fit_lmfit)
|
|
277
|
+
'''
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
585.0936329588015, 0.9920106524633822
|
|
2
|
+
588.1179775280899, 0.1238348868175766
|
|
3
|
+
594.3258426966293, 0.23435419440745675
|
|
4
|
+
597.3501872659176, 0.0679094540612517
|
|
5
|
+
602.9213483146068, 0.06924101198402122
|
|
6
|
+
607.059925093633, 0.18375499334221035
|
|
7
|
+
609.2883895131087, 0.2583222370173103
|
|
8
|
+
614.0636704119851, 0.4194407456724367
|
|
9
|
+
616.1329588014981, 0.14647137150466039
|
|
10
|
+
621.3857677902622, 0.118508655126498
|
|
11
|
+
626.3202247191011, 0.2250332889480693
|
|
12
|
+
630.1404494382023, 0.07589880159786955
|
|
13
|
+
632.8464419475656, 0.18242343541944084
|
|
14
|
+
637.7808988764045, 0.2663115845539281
|
|
15
|
+
639.8501872659176, 0.29693741677762975
|
|
16
|
+
650.3558052434457, 0.16644474034620504
|
|
17
|
+
652.9026217228464, 0.07589880159786955
|
|
18
|
+
659.5880149812734, 0.0732356857523303
|
|
19
|
+
667.5468164794008, 0.11584553928095864
|
|
20
|
+
671.5262172284645, 0.1131824234354194
|
|
21
|
+
692.6966292134832, 0.09986684420772307
|
|
22
|
+
703.0430711610487, 0.2809587217043942
|
|
23
|
+
717.368913857678, 0.011984021304926706
|
|
24
|
+
724.5318352059926, 0.08655126498002663
|
|
25
|
+
744.1104868913858, 0.01464713715046595
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
607.43377, 0.18375499334221035
|
|
2
|
+
609.61631, 0.2583222370173103
|
|
3
|
+
614.30626, 0.4194407456724367
|
|
4
|
+
616.35939, 0.14647137150466039
|
|
5
|
+
621.72812, 0.118508655126498
|
|
6
|
+
626.6495, 0.2250332889480693
|
|
7
|
+
630.4789, 0.07589880159786955
|
|
8
|
+
633.44278, 0.18242343541944084
|
|
9
|
+
638.29917, 0.2663115845539281
|
|
10
|
+
640.2246, 0.29693741677762975
|
|
11
|
+
650.65281, 0.16644474034620504
|
|
12
|
+
653.28822, 0.07589880159786955
|
|
13
|
+
659.89529, 0.0732356857523303
|
|
14
|
+
667.82764, 0.11584553928095864
|
|
15
|
+
671.7043, 0.1131824234354194
|
|
16
|
+
692.94673, 0.09986684420772307
|
|
17
|
+
703.24131, 0.2809587217043942
|
|
18
|
+
717.39381, 0.011984021304926706
|
|
19
|
+
724.51666, 0.08655126498002663
|
|
20
|
+
743.8899, 0.01464713715046595
|