Skip to content

Commit 57087b8

Browse files
author
dpscience
committed
new functionalities added
- Gaussian fit of the retrieved distiributions - results of the Gaussian fits are shown - IRF consisting of a superposition of Gaussian functions can be used - mono-decay spectrum is not anymore required
1 parent db844b5 commit 57087b8

2 files changed

Lines changed: 207 additions & 36 deletions

File tree

pyDCONTINPALS/pyDCONTINPALS.py

Lines changed: 191 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@
2828
from pathlib import Path
2929
import matplotlib.pyplot as plt
3030
import numpy as np
31+
from scipy.optimize import curve_fit
32+
from scipy import interpolate
3133

3234
import pyDCONTINPALSSpecSimulator as specSimulator
3335
import pyDCONTINPALSInput as userInput
3436

3537
def __information__():
36-
print("#********************* pyDCONTINPALS 1.01 (20.01.2021) *********************")
38+
print("#********************* pyDCONTINPALS 1.02 (16.03.2021) *********************")
3739
print("#**")
3840
print("#** Copyright (C) 2020-2021 Danny Petschke")
3941
print("#**")
@@ -63,6 +65,53 @@ def __licence__():
6365
print("#**")
6466
print("#*************************************************************************************************")
6567

68+
def gaussian(x=[],amplitude=1.,loc=0.,scale=1.):
69+
return amplitude*np.exp(-0.5*((x-loc)/scale)**2)
70+
71+
def detectPeaks(x=[],y=[]):
72+
peak_list = []
73+
peak_list_y = []
74+
75+
der_1st = []
76+
for i in range(0,len(y)-1):
77+
der_1st.append((y[i+1]-y[i])/(x[i+1]-x[i]))
78+
79+
#plt.plot(der_1st,'ro')
80+
#plt.show()
81+
82+
for i in range(0,len(der_1st)-1):
83+
p1=der_1st[i]
84+
p2=der_1st[i+1]
85+
86+
if np.abs(p1-p2)<=1e-5:
87+
continue
88+
89+
if (p1 > 0 and p2 < 0) or (p2 > 0 and p1 < 0):
90+
peak_list.append(x[i+1])
91+
peak_list_y.append(y[i+1])
92+
93+
return peak_list,peak_list_y
94+
95+
def multiPeakFit(x=[],y=[]):
96+
peak_list_x,peak_list_y = detectPeaks(x,y)
97+
98+
results = []
99+
results_uncertainties = []
100+
results_curve = []
101+
results_area = []
102+
103+
for i in range(0,len(peak_list_x)):
104+
init_vals = [peak_list_y[i],peak_list_x[i],50.]
105+
106+
best_vals,covar = curve_fit(gaussian,x,y,p0=init_vals)
107+
108+
results.append(best_vals)
109+
results_uncertainties.append(np.sqrt(np.diag(covar)))
110+
results_curve.append(gaussian(x,amplitude=best_vals[0],loc=best_vals[1],scale=best_vals[2]))
111+
results_area.append(sum(gaussian(x,amplitude=best_vals[0],loc=best_vals[1],scale=best_vals[2])))
112+
113+
return results,results_uncertainties,results_curve,results_area
114+
66115
if __name__ == '__main__':
67116
__information__()
68117

@@ -75,15 +124,16 @@ def __licence__():
75124

76125
monoDecayTau = userInput.__tau_monoDecaySpec_in_ps
77126
binWidth_in_ps = userInput.__channelResolutionInPs
78-
tauGrid_in_ps = [userInput.__gridTau_start, userInput.__gridTau_stop]
127+
tauGrid_in_ps = [userInput.__gridTau_start,userInput.__gridTau_stop]
79128
gridPoints = userInput.__gridPoints
129+
binFac = userInput.__binFactor
80130

81131
# running demo mode ?
82132
if userInput.__demoMode:
83133
numberOfBins = userInput.__bkgrd_startIndex + userInput.__bkgrd_count + 10 # set to the maximum for demonstration purposes
84134

85-
charactLifetimes_in_ps = [160.0, 400.0, 2000.0]
86-
contributionOfLifetimes = [0.90, 0.095, 0.005]
135+
charactLifetimes_in_ps = [180.0, 400.0, 1600.0]
136+
contributionOfLifetimes = [0.30, 0.50, 0.20]
87137

88138
specdata_sample = specSimulator.generateCompleteLTSpectrum(numberOfComponents=len(charactLifetimes_in_ps),
89139
binWidth_in_ps=binWidth_in_ps,
@@ -92,49 +142,110 @@ def __licence__():
92142
numberOfBins=numberOfBins,
93143
charactLifetimes_in_ps=charactLifetimes_in_ps,
94144
contributionOfLifetimes=contributionOfLifetimes,
95-
irf_tZero_in_ps=3500.0,
145+
irf_tZero_in_ps=userInput.__t_zero*userInput.__channelResolutionInPs,
96146
irf_fwhm_in_ps=230.0,
97147
noise=True,
98148
noiseLevel=1.0)
99149

100-
specdata_ref = specSimulator.generateCompleteLTSpectrum(numberOfComponents=1,
150+
specdata_ref = specSimulator.generateCompleteLTSpectrum(numberOfComponents=3,
101151
binWidth_in_ps=binWidth_in_ps,
102152
integralCounts=5000000,
103153
constBkgrdCounts=5,
104154
numberOfBins=numberOfBins,
105-
charactLifetimes_in_ps=[monoDecayTau],
106-
contributionOfLifetimes=[1.0],
107-
irf_tZero_in_ps=3500.0,
155+
charactLifetimes_in_ps=[userInput.__tau_monoDecaySpec_in_ps],
156+
contributionOfLifetimes=[1.],
157+
irf_tZero_in_ps=userInput.__t_zero*userInput.__channelResolutionInPs,
108158
irf_fwhm_in_ps=230.0,
109159
noise=True,
110160
noiseLevel=1.0)
111161
else:
112-
__ ,specdata_sample = np.loadtxt(userInput.__filePathSpec, delimiter=userInput.__specDataDelimiter, skiprows=userInput.__skipRows, unpack=True, dtype='float');
113-
__ ,specdata_ref = np.loadtxt(userInput.__filePathRefOrIRFSpec, delimiter=userInput.__refDataDelimiter, skiprows=userInput.__skipRows, unpack=True, dtype='float');
114-
115-
numberOfBins = len(specdata_sample)
162+
specdata_sample = np.loadtxt(userInput.__filePathSpec, delimiter=userInput.__specDataDelimiter, skiprows=userInput.__skipRows, unpack=True, dtype='float')
163+
164+
if userInput.__usingRefSpectrum:
165+
specdata_ref = np.loadtxt(userInput.__filePathRefOrIRFSpec, delimiter=userInput.__refDataDelimiter, skiprows=userInput.__skipRows, unpack=True, dtype='float')
166+
else:
167+
specdata_ref = np.zeros(len(specdata_sample))
168+
169+
spec_rebinned = np.zeros(int(np.ceil(len(specdata_sample)/binFac)))
170+
171+
i_bin = 0
172+
sum_bins = 0.
173+
for i in range(0,len(specdata_sample)):
174+
sum_bins += specdata_sample[i]
175+
if not i%binFac:
176+
spec_rebinned[i_bin] = sum_bins
177+
i_bin += 1
178+
sum_bins = 0.
179+
180+
specdata_sample = spec_rebinned
181+
182+
if not userInput.__usingRefSpectrum:
183+
for i in range(len(specdata_ref)):
184+
t = (i-userInput.__t_zero)*binWidth_in_ps
185+
186+
a = 0
187+
for ii in range(len(userInput.__irf_fwhm)):
188+
a += userInput.__irf_intensity[ii]*np.exp(-0.5*((t-userInput.__irf_t0[ii])/(userInput.__irf_fwhm[ii]/2.3548))**2)
189+
190+
specdata_ref[i] = a
191+
192+
irf_rebinned = np.zeros(int(np.ceil(len(specdata_ref)/binFac)))
193+
194+
i_bin = 0
195+
sum_bins = 0.
196+
for i in range(0,len(specdata_ref)):
197+
sum_bins += specdata_ref[i]
198+
if not i%binFac:
199+
irf_rebinned[i_bin] = sum_bins
200+
i_bin += 1
201+
sum_bins = 0.
202+
203+
specdata_ref = irf_rebinned
204+
205+
# adjust values to rebinned data ...
206+
binWidth_in_ps *= binFac
207+
208+
roi_start = int(np.ceil(userInput.__roi_start/binFac))
209+
roi_end = int(np.ceil(userInput.__roi_end/binFac))
210+
211+
bkgrd_startIndex = int(np.ceil(userInput.__bkgrd_startIndex/binFac))
212+
bkgrd_count = int(np.ceil(userInput.__bkgrd_count/binFac))
213+
214+
bkgrd_startIndex -= roi_start+1
215+
216+
t_zero_chn = int(np.ceil(userInput.__t_zero/binFac))
217+
218+
spec_data_roi = specdata_sample[roi_start:roi_end]
219+
irf_data_roi = specdata_ref[roi_start:roi_end]
220+
221+
numberOfBins = len(spec_data_roi)
116222

117223
# catch general limitations given by CONTIN-PALS
118224
assert numberOfBins <= 4000 and numberOfBins >= 10
119225
assert gridPoints >= 10 and gridPoints <= 100
120226
assert binWidth_in_ps >= 10.0
121227

122-
if monoDecayTau == 0.0:
228+
if not userInput.__usingRefSpectrum:
123229
monoDecayTau = 1E-6
124230

125231
# show the data
126232
fig, ax = plt.subplots()
127-
plt.semilogy(specdata_sample, 'bo', specdata_ref, 'ro')
128-
ax.set_ylabel('counts [a.u.]')
129-
ax.set_xlabel('channel [a.u.]')
233+
234+
if userInput.__usingRefSpectrum:
235+
plt.semilogy(spec_data_roi/sum(spec_data_roi), 'bo', irf_data_roi/sum(irf_data_roi), 'r-')
236+
else:
237+
plt.semilogy(spec_data_roi/sum(spec_data_roi), 'bo')
238+
239+
ax.set_ylabel('area normalized counts [a.u.]')
240+
ax.set_xlabel('channels [{} ps]'.format(binWidth_in_ps))
130241
plt.show()
131242

132243
specSamp = (ctypes.c_int*numberOfBins)()
133244
specRef = (ctypes.c_int*numberOfBins)()
134245

135246
for i in range(numberOfBins):
136-
specSamp[i] = int(specdata_sample[i])
137-
specRef[i] = int(specdata_ref[i])
247+
specSamp[i] = int(spec_data_roi[i])
248+
specRef[i] = int(irf_data_roi[i])
138249

139250
program = __dllPtr.analyseData
140251
program.restype = ctypes.c_int
@@ -148,8 +259,8 @@ def __licence__():
148259
ctypes.c_double(tauGrid_in_ps[0]),
149260
ctypes.c_double(tauGrid_in_ps[1]),
150261
ctypes.c_int(gridPoints),
151-
ctypes.c_int(userInput.__bkgrd_startIndex),
152-
ctypes.c_int(userInput.__bkgrd_count))
262+
ctypes.c_int(bkgrd_startIndex),
263+
ctypes.c_int(bkgrd_count))
153264

154265
if not result == 1:
155266
if result == 0:
@@ -187,40 +298,89 @@ def __licence__():
187298
m_yerr.restype = ctypes.c_double
188299
m_res.restype = ctypes.c_double
189300

190-
x = np.zeros(gridPoints)
191-
y = np.zeros(gridPoints)
192-
yerr = np.zeros(gridPoints)
301+
x = np.zeros(gridPoints)
302+
y = np.zeros(gridPoints)
303+
yerr = np.zeros(gridPoints)
193304

194-
res = np.zeros(numberOfBins)
305+
res = np.zeros(numberOfBins)
195306

196307
# visualize results
197308
for i in range(0, gridPoints):
198309
x[i] = m_x(ctypes.c_int(i))
199310
y[i] = m_y(ctypes.c_int(i))
200311
yerr[i] = m_yerr(ctypes.c_int(i))
201312

313+
# fit results to retrieve information ...
314+
results,uncertainties,fitData,intensities = multiPeakFit(x,y)
315+
202316
for i in range(0, numberOfBins):
203317
res[i] = m_res(ctypes.c_int(i))
204318

205319
fig, ax = plt.subplots()
206-
plt.errorbar(x, y, yerr=yerr)
320+
plt.errorbar(x,y,yerr=yerr,marker='s',mfc='red',mec='blue', ms=2, mew=4,label="CONTIN-PALS results")
321+
322+
for i in range(0,len(fitData)):
323+
plt.plot(x,fitData[i],'r--',lw=1,label='Gaussian fit at {} ps'.format(results[i][1]))
324+
325+
x_v = [results[i][1],results[i][1]]
326+
y_v = [0,results[i][0]]
327+
328+
plt.plot(x_v,y_v,'r-',lw=2)
207329

208330
# running demo mode ?
209331
if userInput.__demoMode:
210332
for xc in charactLifetimes_in_ps:
211333
plt.axvline(x=xc, color='k', linestyle='--')
212334

213-
ax.set_ylabel('intensity [a.u.]')
335+
ax.set_ylabel('intensity pdf [a.u.]')
214336
ax.set_xlabel('characteristic lifetimes [ps]')
337+
plt.legend(loc='best')
215338
plt.show()
216339

217340
fig, ax = plt.subplots()
218-
plt.plot(res, 'ro')
341+
plt.plot(res,'ro',label="error normalized residuals")
342+
plt.legend(loc='best')
219343

220-
hlines = [-2, 0 , 2]
344+
hlines = [-4,-2,0,2,4]
221345
for yc in hlines:
222346
plt.axhline(y=yc, color='k', linestyle='--')
223347

224348
ax.set_ylabel('confidence level [sigma]')
225-
ax.set_xlabel('channel [a.u.]')
226-
plt.show()
349+
ax.set_xlabel('channels [{} ps]'.format(binWidth_in_ps))
350+
ax.set_ylim([-6,6])
351+
plt.show()
352+
353+
print('')
354+
print('channel-width: {} ps (= {} x {} ps)'.format(binWidth_in_ps,binFac,userInput.__channelResolutionInPs))
355+
print('')
356+
print('ROI: [{} : {}]'.format(roi_start,roi_end))
357+
358+
if not userInput.__usingRefSpectrum:
359+
print('t-zero channel: {}'.format(t_zero_chn))
360+
361+
print('background: [{} : {}]'.format(bkgrd_startIndex,bkgrd_startIndex+bkgrd_count))
362+
print('')
363+
364+
if not userInput.__usingRefSpectrum:
365+
for i in range(len(userInput.__irf_fwhm)):
366+
print('----- fixed Gaussian IRF components ({}/{}) -----'.format(i+1,len(userInput.__irf_fwhm)))
367+
print('')
368+
print('intensity: {} %'.format(100.*userInput.__irf_intensity[i]))
369+
print('t: {} ps'.format(userInput.__irf_t0[i]))
370+
print('FWHM: {} ps'.format(userInput.__irf_fwhm[i]))
371+
372+
print('')
373+
374+
for i in range(0,len(results)):
375+
print('------------ found component ({}/{}) ------------'.format(i+1,len(results)))
376+
print('')
377+
print('tau-mean: ({} +/- {}) ps'.format(results[i][1],uncertainties[i][1]))
378+
print('tau-sigma: ({} +/- {}) ps'.format(results[i][2],uncertainties[i][2]))
379+
print('')
380+
print('intensity: {} %'.format(100.*intensities[i]/sum(intensities)))
381+
print('')
382+
383+
384+
385+
386+

pyDCONTINPALS/pyDCONTINPALSInput.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@
2525

2626
# NOTE: spectrum and IRF (or mono-exponential decay spectrum) data vectors require equal length!
2727

28+
__roi_start = 0
29+
__roi_end = 7400
30+
2831
# file path (and name) to the SPECTRUM data:
32+
__usingRefSpectrum = True # if FALSE the '__irfXXX' related parameters are considered
33+
2934
__filePathSpec = 'testData/spectrum_10ps.dat'
3035
__specDataDelimiter = '\t'
3136

@@ -38,16 +43,22 @@
3843

3944
# fixed mono-decay component in units of picoseconds [ps] (1/lambda = tau):
4045
# Note: set to something like 1E-6 if you provide numerical IRF data as input
41-
__tau_monoDecaySpec_in_ps = 182.0 #[ps]
46+
__tau_monoDecaySpec_in_ps = 182. #[ps]
47+
48+
__t_zero = 2000
49+
__irf_fwhm = [270.04,498.63]
50+
__irf_intensity = [0.9382,0.0618]
51+
__irf_t0 = [0.,6.6]
4252

4353
# grid of characteristic lifetimes with equally distributed grid points defining the resulting intensity spectrum
44-
__gridTau_start = 50.0 # [ps]
54+
__gridTau_start = 10.0 # [ps]
4555
__gridTau_stop = 3000.0 # [ps]
4656
__gridPoints = 100 # 10 ... 100 Note: this value is internally limited to 100 by CONTIN
4757

4858
# channel/bin resolution [ps]
49-
__channelResolutionInPs = 50.0 # >= 10 ... Note: this value is internally limited by CONTIN
59+
__channelResolutionInPs = 5. # >= 10 ... Note: this value is internally limited by CONTIN
60+
__binFactor = 1
5061

5162
# background estimation:
52-
__bkgrd_startIndex = 800;
53-
__bkgrd_count = 190; # number of channels with respect to the 'startIndex'
63+
__bkgrd_startIndex = 6500;
64+
__bkgrd_count = 900; # number of channels with respect to the 'startIndex'

0 commit comments

Comments
 (0)