mmanalysis 0.0.1__tar.gz
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.
- mmanalysis-0.0.1/MANIFEST.in +1 -0
- mmanalysis-0.0.1/PKG-INFO +92 -0
- mmanalysis-0.0.1/README.md +67 -0
- mmanalysis-0.0.1/mmanalysis/__init__.py +12 -0
- mmanalysis-0.0.1/mmanalysis/cli/__init__.py +0 -0
- mmanalysis-0.0.1/mmanalysis/cli/mmanalysis_cli.py +25 -0
- mmanalysis-0.0.1/mmanalysis/core/__init__.py +0 -0
- mmanalysis-0.0.1/mmanalysis/core/fits.py +419 -0
- mmanalysis-0.0.1/mmanalysis/core/settings.py +36 -0
- mmanalysis-0.0.1/mmanalysis/gui/__init__.py +0 -0
- mmanalysis-0.0.1/mmanalysis/gui/mma_gui.py +139 -0
- mmanalysis-0.0.1/mmanalysis/io/__init__.py +0 -0
- mmanalysis-0.0.1/mmanalysis/io/importing.py +245 -0
- mmanalysis-0.0.1/mmanalysis/main_analysis.py +192 -0
- mmanalysis-0.0.1/mmanalysis/mmanalysis.py +414 -0
- mmanalysis-0.0.1/mmanalysis/visualization/__init__.py +0 -0
- mmanalysis-0.0.1/mmanalysis/visualization/plots.py +486 -0
- mmanalysis-0.0.1/mmanalysis.egg-info/PKG-INFO +92 -0
- mmanalysis-0.0.1/mmanalysis.egg-info/SOURCES.txt +26 -0
- mmanalysis-0.0.1/mmanalysis.egg-info/dependency_links.txt +1 -0
- mmanalysis-0.0.1/mmanalysis.egg-info/entry_points.txt +2 -0
- mmanalysis-0.0.1/mmanalysis.egg-info/requires.txt +9 -0
- mmanalysis-0.0.1/mmanalysis.egg-info/top_level.txt +1 -0
- mmanalysis-0.0.1/pyproject.toml +3 -0
- mmanalysis-0.0.1/requirements.txt +9 -0
- mmanalysis-0.0.1/setup.cfg +31 -0
- mmanalysis-0.0.1/setup.py +11 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include requirements.txt
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: mmanalysis
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: MultiModalAnalysis is a package to easily postprocess GIWAXS data.
|
|
5
|
+
Home-page: https://github.com/sutterfellalab/MultiModalAnalysis
|
|
6
|
+
Author: attr:mmanalysis.__author__
|
|
7
|
+
Author-email: TimKodalle@lbl.gov
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Topic :: Software Development
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering
|
|
14
|
+
Requires-Python: >=3.6
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: numpy
|
|
17
|
+
Requires-Dist: scipy
|
|
18
|
+
Requires-Dist: pandas
|
|
19
|
+
Requires-Dist: dill
|
|
20
|
+
Requires-Dist: matplotlib
|
|
21
|
+
Requires-Dist: tk
|
|
22
|
+
Requires-Dist: lmfit
|
|
23
|
+
Requires-Dist: bokeh
|
|
24
|
+
Requires-Dist: tqdm
|
|
25
|
+
|
|
26
|
+
# Multi Modal Analysis
|
|
27
|
+
|
|
28
|
+
This repository contains a Python script designed for analysis tasks related to MMAnalysis. The script performs various data processing tasks including logging data selection, GIWAXS data selection, and PL data selection. It also allows for peak fitting and generates stacked plots.
|
|
29
|
+
|
|
30
|
+
## Requirements
|
|
31
|
+
|
|
32
|
+
Check the file [requirements.txt](requirements.txt) to see which packages are needed. Installing the package using `pip` should already take care of all dependencies.
|
|
33
|
+
|
|
34
|
+
## Installation instructions
|
|
35
|
+
|
|
36
|
+
### Create a new virtual environment
|
|
37
|
+
|
|
38
|
+
Create a new Python environment. (You can also do it in a pre-existing environment, but make sure you don't break something):
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
conda create -n mmanalysis python=3.11
|
|
42
|
+
conda activate mmanalysis
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Note that you may need to initialize your shell within conda, e.g., using conda init bash. You will know if the conda environment has been activated when you see that your shell prompt is modified with (`mmanalysis`).
|
|
46
|
+
|
|
47
|
+
After activating your new (or existing) environment, follow the next steps.
|
|
48
|
+
|
|
49
|
+
### Install using `pip`
|
|
50
|
+
|
|
51
|
+
You can simply install the latest release of the package and all dependencies using:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install mmanalysis
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Install directly the source code
|
|
58
|
+
|
|
59
|
+
Alternatively you can obtain `mmanalysis` directly from the repository by following those steps:
|
|
60
|
+
|
|
61
|
+
Clone the repository in the desired location:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
git clone https://github.com/sutterfellalab/MultiModalAnalysis.git
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Install the required packages:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
cd MultiModalAnalysis
|
|
71
|
+
conda install -c conda-forge --file requirements.txt
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Install the package with pip:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install .
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Features
|
|
81
|
+
|
|
82
|
+
- **Logging Data Selection**: Automatically suggests start times and plots raw and post-processed log data.
|
|
83
|
+
- **GIWAXS Data Selection**: Automatically finds start times, plots raw and post-processed GIWAXS data, and performs peak fitting.
|
|
84
|
+
- **PL Data Selection**: Plots raw and post-processed PL data, optimizes data for plotting, and performs peak fitting.
|
|
85
|
+
- **Stacked Plots**: Generates stacked plots for combined GIWAXS, PL, and logging data.
|
|
86
|
+
|
|
87
|
+
## Contact
|
|
88
|
+
|
|
89
|
+
Feel free to create Merge Requests and Issues on our GitHub page: [https://github.com/sutterfellalab/MultiModalAnalysis](https://github.com/sutterfellalab/MultiModalAnalysis).
|
|
90
|
+
|
|
91
|
+
If you want to contact the authors, please write to T. Kodalle at <TimKodalle@lbl.gov>.
|
|
92
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Multi Modal Analysis
|
|
2
|
+
|
|
3
|
+
This repository contains a Python script designed for analysis tasks related to MMAnalysis. The script performs various data processing tasks including logging data selection, GIWAXS data selection, and PL data selection. It also allows for peak fitting and generates stacked plots.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
Check the file [requirements.txt](requirements.txt) to see which packages are needed. Installing the package using `pip` should already take care of all dependencies.
|
|
8
|
+
|
|
9
|
+
## Installation instructions
|
|
10
|
+
|
|
11
|
+
### Create a new virtual environment
|
|
12
|
+
|
|
13
|
+
Create a new Python environment. (You can also do it in a pre-existing environment, but make sure you don't break something):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
conda create -n mmanalysis python=3.11
|
|
17
|
+
conda activate mmanalysis
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Note that you may need to initialize your shell within conda, e.g., using conda init bash. You will know if the conda environment has been activated when you see that your shell prompt is modified with (`mmanalysis`).
|
|
21
|
+
|
|
22
|
+
After activating your new (or existing) environment, follow the next steps.
|
|
23
|
+
|
|
24
|
+
### Install using `pip`
|
|
25
|
+
|
|
26
|
+
You can simply install the latest release of the package and all dependencies using:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install mmanalysis
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Install directly the source code
|
|
33
|
+
|
|
34
|
+
Alternatively you can obtain `mmanalysis` directly from the repository by following those steps:
|
|
35
|
+
|
|
36
|
+
Clone the repository in the desired location:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
git clone https://github.com/sutterfellalab/MultiModalAnalysis.git
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Install the required packages:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
cd MultiModalAnalysis
|
|
46
|
+
conda install -c conda-forge --file requirements.txt
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Install the package with pip:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install .
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
- **Logging Data Selection**: Automatically suggests start times and plots raw and post-processed log data.
|
|
58
|
+
- **GIWAXS Data Selection**: Automatically finds start times, plots raw and post-processed GIWAXS data, and performs peak fitting.
|
|
59
|
+
- **PL Data Selection**: Plots raw and post-processed PL data, optimizes data for plotting, and performs peak fitting.
|
|
60
|
+
- **Stacked Plots**: Generates stacked plots for combined GIWAXS, PL, and logging data.
|
|
61
|
+
|
|
62
|
+
## Contact
|
|
63
|
+
|
|
64
|
+
Feel free to create Merge Requests and Issues on our GitHub page: [https://github.com/sutterfellalab/MultiModalAnalysis](https://github.com/sutterfellalab/MultiModalAnalysis).
|
|
65
|
+
|
|
66
|
+
If you want to contact the authors, please write to T. Kodalle at <TimKodalle@lbl.gov>.
|
|
67
|
+
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created on Fri Nov 29 17:52:31 2024
|
|
5
|
+
|
|
6
|
+
@author: roncofaber
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
|
|
11
|
+
import mmanalysis
|
|
12
|
+
import mmanalysis.main_analysis
|
|
13
|
+
|
|
14
|
+
#%%
|
|
15
|
+
|
|
16
|
+
def main():
|
|
17
|
+
parser = argparse.ArgumentParser(description="Run MMAnalysis with specified parameters.")
|
|
18
|
+
parser.add_argument('-f', '--folder', type=str, default=None, help="Path to the folder to analyze.")
|
|
19
|
+
|
|
20
|
+
args = parser.parse_args()
|
|
21
|
+
|
|
22
|
+
mmanalysis.main_analysis.main(folder=args.folder)
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Created on Wed Dec 21 16:54:01 2022
|
|
4
|
+
|
|
5
|
+
@author: Tim Kodalle
|
|
6
|
+
"""
|
|
7
|
+
import numpy as np
|
|
8
|
+
import os
|
|
9
|
+
import pandas as pd
|
|
10
|
+
import matplotlib.pyplot as plt
|
|
11
|
+
from matplotlib import ticker
|
|
12
|
+
from scipy import signal
|
|
13
|
+
from tqdm import tqdm
|
|
14
|
+
from lmfit.models import LinearModel, PseudoVoigtModel
|
|
15
|
+
from scipy.optimize import curve_fit
|
|
16
|
+
import scipy.integrate as integrate
|
|
17
|
+
import traceback
|
|
18
|
+
|
|
19
|
+
#%%
|
|
20
|
+
#GIWAXS-Fitting
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def fit_single_frame(lowQ, highQ, q, intensity, frame_index, frames_to_plot, sampleName, outputPath):
|
|
24
|
+
|
|
25
|
+
x = q[lowQ:highQ]
|
|
26
|
+
y = intensity[frame_index, lowQ:highQ]
|
|
27
|
+
|
|
28
|
+
init_params = { # initial guess parameters
|
|
29
|
+
'amplitude' : max(y)/40, # default: 2
|
|
30
|
+
'center' : x[np.argmax(y)], # 1 (in angstrom-1)
|
|
31
|
+
'sigma' : 0.01, # 0.01
|
|
32
|
+
'fraction' : 0.5, # 0.5
|
|
33
|
+
'slope' : y[-1] - y[0],
|
|
34
|
+
'intercept' : 0 # 700
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# init_params = { # initial guess parameters
|
|
38
|
+
# 'amplitude' : max(y)/2, # default: 2
|
|
39
|
+
# 'center' : x[np.argmax(y)], # 1 (in angstrom-1)
|
|
40
|
+
# 'sigma' : 0.3, # 0.01
|
|
41
|
+
# 'fraction' : 0.5, # 0.5
|
|
42
|
+
# 'slope' : (y[-1] - y[0])/(x[-1] - x[0]),
|
|
43
|
+
# 'intercept' : y[0] - (y[-1] - y[0])/(x[-1] - x[0])*x[0] # 700
|
|
44
|
+
# }
|
|
45
|
+
|
|
46
|
+
# define fitting models (so far, one peak and a background function)
|
|
47
|
+
peak = PseudoVoigtModel()
|
|
48
|
+
background = LinearModel()
|
|
49
|
+
mod = peak + background
|
|
50
|
+
# initial values
|
|
51
|
+
pars = mod.make_params(amplitude = init_params['amplitude'],
|
|
52
|
+
center = init_params['center'],
|
|
53
|
+
sigma = init_params['sigma'],
|
|
54
|
+
fraction = init_params['fraction'],
|
|
55
|
+
slope = init_params['slope'],
|
|
56
|
+
intercept = init_params['intercept'])
|
|
57
|
+
# bounds
|
|
58
|
+
pars.add('center', value=init_params['center'], min=q[lowQ], max=q[highQ])
|
|
59
|
+
pars.add('amplitude', value=init_params['amplitude'])
|
|
60
|
+
mod.set_param_hint('amplitude', min=0)
|
|
61
|
+
mod.set_param_hint('center', min=q[lowQ], max=q[highQ])
|
|
62
|
+
mod.set_param_hint('sigma', max=0.01)
|
|
63
|
+
|
|
64
|
+
# determine if peak in data, promninence of 190 is chosen by hand, doesn't
|
|
65
|
+
# need to be ideal for every sample
|
|
66
|
+
peak_in_frame = False #initially false
|
|
67
|
+
peaks = signal.find_peaks(y)[0]
|
|
68
|
+
if len(peaks) > 0:
|
|
69
|
+
|
|
70
|
+
peak_in_frame = True
|
|
71
|
+
|
|
72
|
+
# fitting call
|
|
73
|
+
result = mod.fit(y, pars, x=x)
|
|
74
|
+
|
|
75
|
+
redchi = result.redchi
|
|
76
|
+
dely = result.eval_uncertainty(sigma=3)
|
|
77
|
+
params = []
|
|
78
|
+
std_error = []
|
|
79
|
+
|
|
80
|
+
for name, param in result.params.items():
|
|
81
|
+
params.append(param.value)
|
|
82
|
+
std_error.append(param.stderr)
|
|
83
|
+
if frame_index in frames_to_plot:
|
|
84
|
+
plt.figure(figsize=(7, 5))
|
|
85
|
+
plt.plot(x, y, 'o', label='intensity')
|
|
86
|
+
plt.plot(x[peaks], y[peaks], 'r.', label='found peak')
|
|
87
|
+
plt.plot(x, result.init_fit, '--', label='initial guess')
|
|
88
|
+
plt.plot(x, result.best_fit, '-', label='best fit')
|
|
89
|
+
plt.fill_between(x, result.best_fit-dely, result.best_fit+dely,
|
|
90
|
+
color='#ABABAB', label='3$\sigma$ - uncertainty band')
|
|
91
|
+
|
|
92
|
+
plt.xlabel(r'q $(\AA)$')
|
|
93
|
+
plt.ylabel(r'Intensity (au)')
|
|
94
|
+
# result.plot(data_kws={'markersize': 1})
|
|
95
|
+
plt.legend()
|
|
96
|
+
plt.title('Frame: ' + str(frame_index))
|
|
97
|
+
plt.savefig(os.path.join(outputPath + '/fits/', str(sampleName) + '_GIWAXS-fit_Frame_' + str(frame_index) + '.png'), format = 'png')
|
|
98
|
+
plt.show(block=False)
|
|
99
|
+
plt.pause(1)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
elif len(peaks) == 0:
|
|
103
|
+
if frame_index in frames_to_plot:
|
|
104
|
+
plt.figure(figsize=(7, 5))
|
|
105
|
+
plt.plot(x, y, 'o', label='intensity')
|
|
106
|
+
plt.xlabel(r'q $(\AA)$')
|
|
107
|
+
plt.ylabel(r'Intensity (au)')
|
|
108
|
+
# result.plot(data_kws={'markersize': 1})
|
|
109
|
+
plt.legend()
|
|
110
|
+
plt.title('Frame: ' + str(frame_index))
|
|
111
|
+
#plt.show()
|
|
112
|
+
params = [None]*6
|
|
113
|
+
std_error = [None]*3
|
|
114
|
+
redchi = [None]
|
|
115
|
+
print("No Peak Found")
|
|
116
|
+
|
|
117
|
+
return (params, std_error, redchi, peak_in_frame)
|
|
118
|
+
|
|
119
|
+
def fit_several_frames(q, time, intensity, show_every, lowQ, highQ, sampleName, outputPath, hkl):
|
|
120
|
+
|
|
121
|
+
amplitude, unc_a = [], []
|
|
122
|
+
center, unc_c = [], []
|
|
123
|
+
sigma, unc_s = [], []
|
|
124
|
+
fraction = []
|
|
125
|
+
slope = []
|
|
126
|
+
|
|
127
|
+
intercept = []
|
|
128
|
+
red_chi = []
|
|
129
|
+
all_params = [amplitude, center, sigma, fraction, slope, intercept]
|
|
130
|
+
peak_unc = [unc_a, unc_c, unc_s]
|
|
131
|
+
frames = range(0, len(time))
|
|
132
|
+
frames_to_plot = [i for i in frames if i % show_every == 0]
|
|
133
|
+
for frame in tqdm(frames, desc='Fitting frames'):
|
|
134
|
+
params, std_error, redchi, peak_in_frame = fit_single_frame(lowQ, highQ, q, intensity,
|
|
135
|
+
frame, frames_to_plot, sampleName, outputPath)
|
|
136
|
+
|
|
137
|
+
red_chi.append(redchi)
|
|
138
|
+
|
|
139
|
+
for index, param in enumerate(all_params):
|
|
140
|
+
param.append(params[index])
|
|
141
|
+
for index, unc in enumerate(peak_unc):
|
|
142
|
+
unc.append(std_error[index])
|
|
143
|
+
# =============================================================================
|
|
144
|
+
# if peak_in_frame:
|
|
145
|
+
# if std_error[0] != None:
|
|
146
|
+
# if std_error[0] < 1:
|
|
147
|
+
# # for higher efficiency, the init_params are now changed to the
|
|
148
|
+
# # fit values for next scan. However, if the initial frame is
|
|
149
|
+
# # wrongly identified to contain a peak, this might lead to problems
|
|
150
|
+
# init_params['amplitude'] = params[0]
|
|
151
|
+
# init_params['center'] = params[1]
|
|
152
|
+
# init_params['sigma'] = params[2]
|
|
153
|
+
# init_params['fraction'] = params[3]
|
|
154
|
+
# init_params['slope'] = params[4]
|
|
155
|
+
# init_params['intercept'] = params[5]
|
|
156
|
+
# =============================================================================
|
|
157
|
+
|
|
158
|
+
fig, ax1 = plt.subplots(figsize=(7, 5))
|
|
159
|
+
plot1, = ax1.plot(frames, center, label='center')
|
|
160
|
+
ax2 = ax1.twinx()
|
|
161
|
+
plot2, = ax2.plot(frames, sigma, 'g', label='$\sigma$')
|
|
162
|
+
ax1.set_xlabel('Frame #')
|
|
163
|
+
ax1.set_ylabel(r'q ($\AA^{-1}$)')
|
|
164
|
+
ax2.set_ylabel(r' $\sigma$ ($\AA^{-1}$)')
|
|
165
|
+
# Create your ticker object with M ticks
|
|
166
|
+
yticks = ticker.MaxNLocator(5)
|
|
167
|
+
ax1.yaxis.set_major_locator(yticks)
|
|
168
|
+
fig.suptitle('Fit Results ' + sampleName, fontsize=14)
|
|
169
|
+
fig.legend()
|
|
170
|
+
plt.pause(1)
|
|
171
|
+
|
|
172
|
+
# saving peak fit params in separate csv files
|
|
173
|
+
params_to_save = {sampleName + '_' + hkl + '_time (s)' : time,
|
|
174
|
+
sampleName + '_' + hkl + '_amplitude (au)' : amplitude,
|
|
175
|
+
sampleName + '_' + hkl + '_center ($\AA$)' : center,
|
|
176
|
+
sampleName + '_' + hkl + '_sigma ($\AA$)' : sigma,
|
|
177
|
+
sampleName + '_' + hkl + '_std error amplitude (au)' : unc_a,
|
|
178
|
+
sampleName + '_' + hkl + '_std error center ($\AA$)' : unc_c,
|
|
179
|
+
sampleName + '_' + hkl + '_std error sigma ($\AA$)' : unc_s}
|
|
180
|
+
|
|
181
|
+
df = pd.DataFrame(params_to_save)
|
|
182
|
+
df = df.replace(np.nan, 'NaN')
|
|
183
|
+
|
|
184
|
+
df.to_csv(os.path.join(outputPath, str(hkl) + '_peak_fit_results_' + sampleName + '.csv'), index=None)
|
|
185
|
+
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
#%%
|
|
189
|
+
#PL-Fitting
|
|
190
|
+
|
|
191
|
+
def sum_of_Voigts(x, *params):
|
|
192
|
+
|
|
193
|
+
if isinstance(x, float):
|
|
194
|
+
x = np.array([x])
|
|
195
|
+
|
|
196
|
+
params = np.array(params)
|
|
197
|
+
n = (len(params)-2) // 4
|
|
198
|
+
|
|
199
|
+
# divide parameters
|
|
200
|
+
amps = params[:n]
|
|
201
|
+
mus = params[n:2*n]
|
|
202
|
+
sigmas = params[2*n:3*n]
|
|
203
|
+
alphas = params[3*n:4*n]
|
|
204
|
+
|
|
205
|
+
gaussians = amps*np.exp(-(x[:, np.newaxis] - mus)**2 / sigmas)
|
|
206
|
+
lorentian = np.log(2) * (2/np.pi)**0.5 * (amps*sigmas / ((x[:, np.newaxis] - mus)**2 + sigmas*np.log(2)))
|
|
207
|
+
background = params[-2]*x + params[-1]
|
|
208
|
+
|
|
209
|
+
return np.dot(gaussians, 1-alphas) + np.dot(lorentian, alphas) + background
|
|
210
|
+
|
|
211
|
+
def background(x, y0, y1):
|
|
212
|
+
return y0*x + y1
|
|
213
|
+
|
|
214
|
+
def fWHM_Voigt(x, center, maxValue, params):
|
|
215
|
+
|
|
216
|
+
x1 = np.linspace(x[0], center, 5001)
|
|
217
|
+
x2 = np.linspace(center, x[-1], 5001)
|
|
218
|
+
|
|
219
|
+
y1 = sum_of_Voigts(x1, *params)
|
|
220
|
+
y2 = sum_of_Voigts(x2, *params)
|
|
221
|
+
|
|
222
|
+
root1 = np.interp(maxValue/2,y1,x1)
|
|
223
|
+
root2 = np.interp(maxValue/2,y2[::-1],x2[::-1])
|
|
224
|
+
|
|
225
|
+
return root2 - root1
|
|
226
|
+
|
|
227
|
+
def plFitting(plParams, df_yCut, df_xCutFit, df_fit, show_every, numGauss, peakLowerTH, inputDict, peakUpperTH, estPeakWidth, minPeakWidth, maxPeakWidth, name_d, name):
|
|
228
|
+
|
|
229
|
+
estPositions = inputDict["PLFits_CenterGuesses"]
|
|
230
|
+
|
|
231
|
+
frames = range(0, len(df_xCutFit))
|
|
232
|
+
frames_to_plot = [i for i in frames if i % show_every == 0]
|
|
233
|
+
|
|
234
|
+
yVals = np.copy(df_fit)
|
|
235
|
+
popt = np.array([[np.nan, np.nan, np.nan, np.nan]*int(numGauss) + [np.nan, np.nan]] * np.shape(df_fit)[1])
|
|
236
|
+
peakFWHM = np.array([[np.nan]*int(numGauss)] * np.shape(df_fit)[1])
|
|
237
|
+
peakArea = np.array([[np.nan]*int(numGauss)] * np.shape(df_fit)[1])
|
|
238
|
+
|
|
239
|
+
# The next block is to convert the estimated peak positions and ranges into indexes
|
|
240
|
+
idxLowerTH = [0.0]*int(numGauss)
|
|
241
|
+
idxUpperTH = [0.0]*int(numGauss)
|
|
242
|
+
|
|
243
|
+
for i in range(0, int(numGauss)):
|
|
244
|
+
idxLowerTH[i] = next(xStart for xStart, valStart in enumerate(df_yCut) if valStart > peakLowerTH[i])
|
|
245
|
+
idxUpperTH[i] = next(xEnd for xEnd, valEnd in enumerate(df_yCut) if valEnd > peakUpperTH[i])
|
|
246
|
+
|
|
247
|
+
firstSpectrum = True
|
|
248
|
+
for i in range(0, np.shape(df_fit)[1]):
|
|
249
|
+
|
|
250
|
+
# get y values
|
|
251
|
+
yVals[:, i] = np.where(yVals[:, i] == float('inf'), 5, yVals[:, i])
|
|
252
|
+
|
|
253
|
+
idx = np.argmax(yVals[0:idxUpperTH[0], i])
|
|
254
|
+
yVals[idx, i] = yVals[idx - 1, i]
|
|
255
|
+
|
|
256
|
+
# find peaks
|
|
257
|
+
peaks = signal.find_peaks(yVals[:, i])[0]
|
|
258
|
+
|
|
259
|
+
# array initialization
|
|
260
|
+
estAmplitudes = [0.0]*int(numGauss)
|
|
261
|
+
minAmplitudes = [0.0]*int(numGauss)
|
|
262
|
+
maxAmplitudes = [0.0]*int(numGauss)
|
|
263
|
+
estAlphas = [0.24]*int(numGauss)
|
|
264
|
+
minAlphas = [0.0]*int(numGauss)
|
|
265
|
+
maxAlphas = [1.0]*int(numGauss)
|
|
266
|
+
minLinBkg = 0.0
|
|
267
|
+
estLinBkg = 0.0
|
|
268
|
+
maxLinBkg = 1000.0
|
|
269
|
+
minConstBkg = 0.0
|
|
270
|
+
estConstBkg = 0.0
|
|
271
|
+
maxConstBkg = 1000.0
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
# no peak, skip
|
|
275
|
+
if len(peaks) == 0:
|
|
276
|
+
print("Time:")
|
|
277
|
+
print(df_xCutFit[i])
|
|
278
|
+
print("No Peak Found")
|
|
279
|
+
continue
|
|
280
|
+
|
|
281
|
+
if firstSpectrum:
|
|
282
|
+
firstSpectrum = False
|
|
283
|
+
firstFitIdx = i
|
|
284
|
+
|
|
285
|
+
# find initial parameters and bounds for peak amplitudes, having free peaks start more prominent than propagating ones
|
|
286
|
+
for ii in range(0,int(numGauss)):
|
|
287
|
+
if float(inputDict["PLFits_Propagate?"][ii]):
|
|
288
|
+
estAmplitudes[ii] = max(yVals[idxLowerTH[ii]:idxUpperTH[ii], i]) / 5
|
|
289
|
+
minAmplitudes[ii] = 0
|
|
290
|
+
maxAmplitudes[ii] = max(yVals[idxLowerTH[ii]:idxUpperTH[ii], i]) / 1.5
|
|
291
|
+
else:
|
|
292
|
+
estAmplitudes[ii] = max(yVals[idxLowerTH[ii]:idxUpperTH[ii], i])
|
|
293
|
+
minAmplitudes[ii] = estAmplitudes[ii] / 10
|
|
294
|
+
maxAmplitudes[ii] = np.inf
|
|
295
|
+
|
|
296
|
+
# collecting fit parameters
|
|
297
|
+
estParams = estAmplitudes + estPositions + estPeakWidth + estAlphas + [estLinBkg, estConstBkg]
|
|
298
|
+
lowerBounds = minAmplitudes + peakLowerTH + minPeakWidth + minAlphas + [minLinBkg, minConstBkg]
|
|
299
|
+
upperBounds = maxAmplitudes + peakUpperTH + maxPeakWidth + maxAlphas + [maxLinBkg, maxConstBkg]
|
|
300
|
+
|
|
301
|
+
else:
|
|
302
|
+
# update initial parameters and bounds. Propagating peaks have their position and width linked to the first one
|
|
303
|
+
for ii in range(0,int(numGauss)):
|
|
304
|
+
# estAlphas[ii] = popt[firstFitIdx, 3*int(numGauss)+ii]
|
|
305
|
+
# minAlphas[ii] = estAlphas[ii] / 1.05
|
|
306
|
+
# maxAlphas[ii] = estAlphas[ii] * 1.05
|
|
307
|
+
|
|
308
|
+
if float(inputDict["PLFits_Propagate?"][ii]):
|
|
309
|
+
estAmplitudes[ii] = max(yVals[idxLowerTH[ii]:idxUpperTH[ii], i]) / 10
|
|
310
|
+
minAmplitudes[ii] = 0
|
|
311
|
+
maxAmplitudes[ii] = np.inf
|
|
312
|
+
estPositions[ii] = popt[firstFitIdx,int(numGauss)+ii]
|
|
313
|
+
peakLowerTH[ii] = estPositions[ii]
|
|
314
|
+
peakUpperTH[ii] = estPositions[ii] * 1.001
|
|
315
|
+
estPeakWidth[ii] = popt[firstFitIdx][2*int(numGauss)+ii]
|
|
316
|
+
minPeakWidth[ii] = estPeakWidth[ii] / 1.01
|
|
317
|
+
maxPeakWidth[ii] = estPeakWidth[ii] * 1.01
|
|
318
|
+
|
|
319
|
+
else:
|
|
320
|
+
estAmplitudes[ii] = max(yVals[idxLowerTH[ii]:idxUpperTH[ii], i])
|
|
321
|
+
minAmplitudes[ii] = 0
|
|
322
|
+
maxAmplitudes[ii] = np.inf
|
|
323
|
+
|
|
324
|
+
# # if previously converged, keep position from optimized (didn't improve fit much but makes it slower)
|
|
325
|
+
# if not np.isnan(popt[i-1, int(numGauss)+ii]):
|
|
326
|
+
# estPositions[ii] = popt[i-1,int(numGauss)+ii]
|
|
327
|
+
# peakLowerTH[ii] = estPositions[ii] - 0.1
|
|
328
|
+
# peakUpperTH[ii] = estPositions[ii] + 0.1
|
|
329
|
+
|
|
330
|
+
# collecting fit parameters
|
|
331
|
+
estParams = estAmplitudes + estPositions + estPeakWidth + estAlphas + [estConstBkg, estLinBkg]
|
|
332
|
+
lowerBounds = minAmplitudes + peakLowerTH + minPeakWidth + minAlphas + [0.0, 0.0]
|
|
333
|
+
upperBounds = maxAmplitudes + peakUpperTH + maxPeakWidth + maxAlphas + [1000.0, 1000.0]
|
|
334
|
+
|
|
335
|
+
# try fitting
|
|
336
|
+
try:
|
|
337
|
+
popt[i], pcov = curve_fit(sum_of_Voigts,
|
|
338
|
+
df_yCut,
|
|
339
|
+
yVals[:, i],
|
|
340
|
+
p0 = estParams,
|
|
341
|
+
bounds = (lowerBounds, upperBounds)
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
except Exception:
|
|
346
|
+
print("Time:")
|
|
347
|
+
print(df_xCutFit[i])
|
|
348
|
+
traceback.print_exc()
|
|
349
|
+
pass
|
|
350
|
+
|
|
351
|
+
for ii in range(0,int(numGauss)):
|
|
352
|
+
parameters = [popt[i,ii], popt[i,int(numGauss)+ii], popt[i,2*int(numGauss)+ii], popt[i,3*int(numGauss)+ii], 0, 0]
|
|
353
|
+
peakFWHM[i,ii] = fWHM_Voigt(df_yCut, popt[i,int(numGauss)+ii], sum_of_Voigts(popt[i,int(numGauss)+ii], *parameters), parameters)
|
|
354
|
+
peakArea[i,ii] = integrate.quad(lambda x: sum_of_Voigts(x, *parameters), -np.inf,np.inf)[0]
|
|
355
|
+
|
|
356
|
+
# plotting fit results for pre-selected frames
|
|
357
|
+
if i in frames_to_plot:
|
|
358
|
+
|
|
359
|
+
plt.figure(figsize=(6, 5))
|
|
360
|
+
plt.plot(df_yCut, yVals[:, i], 'o', label='data')
|
|
361
|
+
plt.plot(df_yCut, sum_of_Voigts(df_yCut, *popt[i,:]), 'r-', label='fit')
|
|
362
|
+
|
|
363
|
+
for ii in range(0, int(numGauss)):
|
|
364
|
+
plt.plot(df_yCut, sum_of_Voigts(df_yCut, *[popt[i,ii], popt[i,int(numGauss)+ii], popt[i,2*int(numGauss)+ii], popt[i,3*int(numGauss)+ii], 0, 0]), '--', label='Peak ' + str(ii+1))
|
|
365
|
+
plt.plot(df_yCut, background(df_yCut, *[popt[i,-2], popt[i,-1]]), 'k--', label='Background')
|
|
366
|
+
plt.legend()
|
|
367
|
+
plt.xlabel('Energy (eV)')
|
|
368
|
+
plt.ylabel('Intensity (a.u.)')
|
|
369
|
+
plt.title('Time: ' + str(df_xCutFit[i]))
|
|
370
|
+
plt.savefig(os.path.join(name + '/fits/', str(name_d) + '_PL-fit_' + str(int(df_xCutFit[i])) + '_s.png'), format = 'png')
|
|
371
|
+
plt.show(block=False)
|
|
372
|
+
plt.pause(1)
|
|
373
|
+
|
|
374
|
+
if plParams['logplots']:
|
|
375
|
+
plt.figure(figsize=(6, 5))
|
|
376
|
+
plt.plot(df_yCut, np.log(yVals[:, i]), 'o', label='data')
|
|
377
|
+
plt.plot(df_yCut, np.log(sum_of_Voigts(df_yCut, *popt[i,:])), 'r-', label='fit')
|
|
378
|
+
plt.legend()
|
|
379
|
+
plt.xlabel('Energy (eV)')
|
|
380
|
+
plt.ylabel('Log-Intensity (a.u.)')
|
|
381
|
+
plt.title('Time: ' + str(df_xCutFit[i]))
|
|
382
|
+
plt.savefig(os.path.join(name + '/fits/', str(name_d) + '_PL-fit_Log_' + str(int(df_xCutFit[i])) + '_s.png'), format = 'png')
|
|
383
|
+
plt.show(block=False)
|
|
384
|
+
plt.pause(1)
|
|
385
|
+
|
|
386
|
+
# Plotting the time-evolution of the peak-positions and intensities
|
|
387
|
+
for i in range(0, int(numGauss)):
|
|
388
|
+
fig, ax1 = plt.subplots(figsize=(6, 5))
|
|
389
|
+
plot1, = ax1.plot(df_xCutFit, popt[:,int(numGauss)+i], label = 'Peak Position')
|
|
390
|
+
ax2 = ax1.twinx()
|
|
391
|
+
plot2, = ax2.plot(df_xCutFit, popt[:,i], 'g', label = 'Peak Intensity')
|
|
392
|
+
ax1.set_xlabel('Time (s)')
|
|
393
|
+
ax1.set_ylabel(r'PL Position (eV)')
|
|
394
|
+
ax2.set_ylabel(r'PL Intensity (a.u.)')
|
|
395
|
+
# Create your ticker object with M ticks
|
|
396
|
+
yticks = ticker.MaxNLocator(5)
|
|
397
|
+
ax1.yaxis.set_major_locator(yticks)
|
|
398
|
+
fig.suptitle('Fit Results Peak ' + str(i+1) + ' ' + name_d, fontsize=14)
|
|
399
|
+
fig.legend()
|
|
400
|
+
|
|
401
|
+
# collecting the fit results in a dataframe
|
|
402
|
+
dfPeaks = pd.DataFrame()
|
|
403
|
+
dfPeaks['Fit-Time_' + name_d] = df_xCutFit
|
|
404
|
+
for i in range(0,int(numGauss)):
|
|
405
|
+
colPos = 'Peak' + str(i+1) + 'Pos_' + name_d
|
|
406
|
+
colArea = 'Peak' + str(i+1) + 'Area_' + name_d
|
|
407
|
+
colFWHM = 'Peak' + str(i+1) + 'FWHM_' + name_d
|
|
408
|
+
colAlphas = 'Peak' + str(i+1) + 'Alpha_' + name_d
|
|
409
|
+
data = np.array([peakArea[:,i], popt[:,int(numGauss)+i], peakFWHM[:,i], popt[:,3*int(numGauss)+i]])
|
|
410
|
+
dfTemp = pd.DataFrame(
|
|
411
|
+
data.T,
|
|
412
|
+
columns=[colArea, colPos, colFWHM, colAlphas])
|
|
413
|
+
dfPeaks = pd.concat([dfPeaks, dfTemp], axis=1)
|
|
414
|
+
dfPeaks = dfPeaks.fillna('nan')
|
|
415
|
+
|
|
416
|
+
# saving the data:
|
|
417
|
+
dfPeaks.to_csv(str(name) + '/PL_FitResults.csv', index=False)
|
|
418
|
+
|
|
419
|
+
return
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Created on Thu Dec 22 11:56:04 2022
|
|
4
|
+
|
|
5
|
+
@author: Tim Kodalle
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
#%%PL-Settings:
|
|
9
|
+
|
|
10
|
+
def generalParameters():
|
|
11
|
+
|
|
12
|
+
genParams = {
|
|
13
|
+
'GIWAXS' : True,
|
|
14
|
+
'PL' : True,
|
|
15
|
+
'Logging': True,
|
|
16
|
+
'TempOld' : False,
|
|
17
|
+
|
|
18
|
+
'LabviewPL' : True, # BL PL via Labview
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return genParams
|
|
22
|
+
|
|
23
|
+
def plParameters():
|
|
24
|
+
|
|
25
|
+
plParams = {
|
|
26
|
+
'Thorlabs' : False, # If Thorlabs software is used instead of OceanView
|
|
27
|
+
'smoothing' : False, # Smoothing of the data to reduce noise
|
|
28
|
+
'Labview' : True, # BL PL via Labview
|
|
29
|
+
'sFactor' : 3, # Parameter for smoothing with a SavGol-Filter
|
|
30
|
+
'bkgCorr' : False, # Enable linear background removal. If True, the program will ask for two ranges for the removal. I recommend setting one of them at higher and the other at lower energy compared to the peaks of interest.
|
|
31
|
+
'bkgCorrPoly' : 1, # This parameter determines the order of the polynomial fit used for background correction (0=const, 1=linear, etc.)
|
|
32
|
+
'binning' : 0, # 0: no binning, >0: Binning of n spectra into one, i.e. reducing the time resolution for increased signal to noise ratio
|
|
33
|
+
'logplots': 0,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return plParams
|
|
File without changes
|