init
This commit is contained in:
commit
30a9eef102
2999 changed files with 1898721 additions and 0 deletions
0
optifik/__init__.py
Normal file
0
optifik/__init__.py
Normal file
133
optifik/analysis.py
Normal file
133
optifik/analysis.py
Normal file
|
@ -0,0 +1,133 @@
|
|||
|
||||
import pandas as pd
|
||||
|
||||
from scipy.signal import savgol_filter
|
||||
from scipy.signal import find_peaks
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
plt.rc('text', usetex=True)
|
||||
plt.rcParams.update({
|
||||
'axes.labelsize': 26,
|
||||
'xtick.labelsize': 32,
|
||||
'ytick.labelsize': 32,
|
||||
'legend.fontsize': 23,
|
||||
})
|
||||
|
||||
from .io import load_spectrum
|
||||
from .fft import *
|
||||
from .scheludko import *
|
||||
from .minmax import *
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def plot_xy(file_path, plot=True):
|
||||
try:
|
||||
# Lecture du fichier .xy en utilisant pandas
|
||||
data = pd.read_csv(file_path, delimiter=',', header=None, names=["x", "y"])
|
||||
|
||||
# Extraction des colonnes
|
||||
x = data["x"]
|
||||
y = data["y"]
|
||||
|
||||
# Tracer la deuxième colonne en fonction de la première
|
||||
plt.figure(figsize=(10, 6),dpi = 600)
|
||||
plt.plot(x, y, 'o-', markersize=2, label="Raw data")
|
||||
|
||||
# Ajout des labels et du titre
|
||||
plt.xlabel(r'$\lambda$ (nm)')
|
||||
plt.ylabel(r'$I^*$')
|
||||
plt.legend()
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"Erreur : le fichier '{file_path}' est introuvable.")
|
||||
except Exception as e:
|
||||
print(f"Une erreur est survenue : {e}")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def finds_peak(filename, min_peak_prominence, min_peak_distance=10, plot=None):
|
||||
"""
|
||||
Charge un fichier .xy et affiche les données avec les extrema détectés (minima et maxima).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : str
|
||||
Chemin vers le fichier .xy (2 colonnes : lambda et intensité).
|
||||
min_peak_prominence : float
|
||||
Importance minimale des pics.
|
||||
min_peak_distance : float
|
||||
Distance minimale entre les pics.
|
||||
"""
|
||||
# Charger et lisser les données
|
||||
lambdas, intensities = load_spectrum(filename, lambda_min=450)
|
||||
WIN_SIZE = 11
|
||||
smoothed_intensities = savgol_filter(intensities, WIN_SIZE, 3)
|
||||
# Trouver les maxima et minima sur le signal lissé
|
||||
peaks_max, _ = find_peaks(smoothed_intensities, prominence=min_peak_prominence, distance=min_peak_distance)
|
||||
peaks_min, _ = find_peaks(-smoothed_intensities, prominence=min_peak_prominence, distance=min_peak_distance)
|
||||
|
||||
if plot:
|
||||
plt.figure(figsize=(10, 6),dpi =600)
|
||||
plt.plot(lambdas, smoothed_intensities, 'o-', markersize=2, label="Smoothed data")
|
||||
plt.plot(lambdas[peaks_max], smoothed_intensities[peaks_max], 'ro')
|
||||
plt.plot(lambdas[peaks_min], smoothed_intensities[peaks_min], 'ro')
|
||||
plt.xlabel(r'$\lambda$ (nm)')
|
||||
plt.ylabel(r'$I^*$')
|
||||
plt.legend()
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
# Nombre total d’extremums
|
||||
total_extrema = len(peaks_max) + len(peaks_min)
|
||||
if total_extrema >=15:
|
||||
print('Number of extrema', total_extrema,'.')
|
||||
print('FFT method')
|
||||
|
||||
if total_extrema <=15 and total_extrema >4:
|
||||
print('Number of extrema', total_extrema,'.')
|
||||
print('OOSpectro method')
|
||||
|
||||
if total_extrema <=4:
|
||||
print('Number of extrema', total_extrema,'.')
|
||||
print('Scheludko method')
|
||||
|
||||
return total_extrema, smoothed_intensities, intensities, lambdas, peaks_min, peaks_max
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def Data_Smoothed(filename):
|
||||
"""
|
||||
Charge un fichier .xy et affiche les données avec les extrema détectés (minima et maxima).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : str
|
||||
Chemin vers le fichier .xy (2 colonnes : lambda et intensité).
|
||||
min_peak_prominence : float
|
||||
Importance minimale des pics.
|
||||
min_peak_distance : float
|
||||
Distance minimale entre les pics.
|
||||
"""
|
||||
# Charger et lisser les données
|
||||
lambdas, intensities = load_spectrum(filename, lambda_min=450)
|
||||
WIN_SIZE = 11
|
||||
smoothed_intensities = savgol_filter(intensities, WIN_SIZE, 3)
|
||||
|
||||
return smoothed_intensities, intensities, lambdas
|
||||
|
||||
|
||||
|
||||
|
||||
|
82
optifik/auto.py
Normal file
82
optifik/auto.py
Normal file
|
@ -0,0 +1,82 @@
|
|||
import os.path
|
||||
|
||||
from .analysis import *
|
||||
from .io import load_spectrum
|
||||
|
||||
def auto(DATA_FOLDER, FILE_NAME, plot=None):
|
||||
|
||||
|
||||
spectre_file = os.path.join(DATA_FOLDER, FILE_NAME)
|
||||
|
||||
##### Affichage du spectre brut et récupération des Intesités brutes#####
|
||||
|
||||
raw_intensities = load_spectrum(spectre_file)
|
||||
|
||||
##### Affichage du spectre lissé #####
|
||||
|
||||
smoothed_intensities, intensities, lambdas = Data_Smoothed(spectre_file)
|
||||
|
||||
##### Indice Optique en fonction de Lambda #####
|
||||
|
||||
indice = 1.324188 + 3102.060378 / (lambdas**2)
|
||||
|
||||
##### Determination de la prominence associé #####
|
||||
|
||||
prominence = Prominence_from_fft(lambdas=lambdas,
|
||||
intensities=smoothed_intensities,
|
||||
refractive_index=indice,
|
||||
plot=plot)
|
||||
|
||||
prominence = 0.03
|
||||
##### Find Peak #####
|
||||
|
||||
total_extrema, smoothed_intensities, raw_intensities, lambdas, peaks_min, peaks_max = finds_peak(spectre_file,
|
||||
min_peak_prominence=prominence,
|
||||
plot=plot)
|
||||
|
||||
##### Epaisseur selon la methode #####
|
||||
|
||||
#thickness_FFT = thickness_from_fft(lambdas,smoothed_intensities,refractive_index=1.33)
|
||||
|
||||
if total_extrema > 15 and total_extrema > 4:
|
||||
print('Apply method FFT')
|
||||
thickness_FFT = thickness_from_fft(lambdas,smoothed_intensities,
|
||||
refractive_index=indice,
|
||||
plot=plot)
|
||||
thickness = thickness_FFT.thickness
|
||||
print(f'thickness: {thickness:.2f} nm')
|
||||
|
||||
|
||||
if total_extrema <= 15 and total_extrema > 4:
|
||||
print('Apply method minmax')
|
||||
thickness_minmax = thickness_from_minmax(lambdas,smoothed_intensities,
|
||||
refractive_index=indice,
|
||||
min_peak_prominence=prominence,
|
||||
plot=plot)
|
||||
thickness = thickness_minmax.thickness
|
||||
print(f'thickness: {thickness:.2f} nm')
|
||||
|
||||
if total_extrema <= 4 and total_extrema >= 2: #& 2peak minimum:
|
||||
print('Apply method Scheludko')
|
||||
thickness = thickness_from_scheludko(lambdas, raw_intensities, smoothed_intensities,
|
||||
peaks_min, peaks_max,
|
||||
refractive_index=indice,
|
||||
plot=plot)
|
||||
print(f'thickness: {thickness:.2f} nm')
|
||||
|
||||
if total_extrema <= 4 and len(peaks_max) == 1 and len(peaks_min) == 0 : #dans l'ordre zéro !
|
||||
print('Apply method ordre0')
|
||||
|
||||
thickness = thickness_for_order0(lambdas, raw_intensities, smoothed_intensities,
|
||||
peaks_min, peaks_max,
|
||||
refractive_index=indice,
|
||||
plot=plot)
|
||||
|
||||
print(f'thickness: {thickness:.2f} nm')
|
||||
|
||||
if total_extrema <= 4 and len(peaks_max) == 0 and (len(peaks_min) == 1 or len(peaks_min) == 0):
|
||||
#& 1peak min ou zéro:
|
||||
thickness = None
|
||||
print('Zone Ombre')
|
||||
|
||||
|
152
optifik/fft.py
Normal file
152
optifik/fft.py
Normal file
|
@ -0,0 +1,152 @@
|
|||
import numpy as np
|
||||
from scipy.interpolate import interp1d
|
||||
from scipy.fftpack import fft, ifft, fftfreq
|
||||
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
plt.rc('text', usetex=True)
|
||||
plt.rcParams.update({
|
||||
'axes.labelsize': 26,
|
||||
'xtick.labelsize': 32,
|
||||
'ytick.labelsize': 32,
|
||||
'legend.fontsize': 23,
|
||||
})
|
||||
|
||||
|
||||
from .utils import OptimizeResult
|
||||
|
||||
|
||||
|
||||
def thickness_from_fft(lambdas, intensities,
|
||||
refractive_index,
|
||||
num_half_space=None,
|
||||
plot=None):
|
||||
"""
|
||||
Determine the tickness by Fast Fourier Transform.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lambdas : array
|
||||
Wavelength values in nm.
|
||||
intensities : array
|
||||
Intensity values.
|
||||
refractive_index : scalar, optional
|
||||
Value of the refractive index of the medium.
|
||||
num_half_space : scalar, optional
|
||||
Number of points to compute FFT's half space.
|
||||
If `None`, default corresponds to `10*len(lambdas)`.
|
||||
debug : boolean, optional
|
||||
Show plot of the transformed signal and the peak detection.
|
||||
|
||||
Returns
|
||||
-------
|
||||
results : Instance of `OptimizeResult` class.
|
||||
The attribute `thickness` gives the thickness value in nm.
|
||||
"""
|
||||
if num_half_space is None:
|
||||
num_half_space = 10 * len(lambdas)
|
||||
|
||||
# FFT requires evenly spaced data.
|
||||
# So, we interpolate the signal.
|
||||
# Interpolate to get a linear increase of 1 / lambdas.
|
||||
inverse_lambdas_times_n = refractive_index / lambdas
|
||||
f = interp1d(inverse_lambdas_times_n, intensities)
|
||||
|
||||
inverse_lambdas_linspace = np.linspace(inverse_lambdas_times_n[0],
|
||||
inverse_lambdas_times_n[-1],
|
||||
2*num_half_space)
|
||||
intensities_linspace = f(inverse_lambdas_linspace)
|
||||
|
||||
|
||||
# Perform FFT
|
||||
density = (inverse_lambdas_times_n[-1]-inverse_lambdas_times_n[0]) / (2*num_half_space)
|
||||
inverse_lambdas_fft = fftfreq(2*num_half_space, density)
|
||||
intensities_fft = fft(intensities_linspace)
|
||||
|
||||
# The FFT is symetrical over [0:N] and [N:2N].
|
||||
# Keep over [N:2N], ie for positive freq.
|
||||
intensities_fft = intensities_fft[num_half_space:2*num_half_space]
|
||||
inverse_lambdas_fft = inverse_lambdas_fft[num_half_space:2*num_half_space]
|
||||
|
||||
idx_max_fft = np.argmax(abs(intensities_fft))
|
||||
freq_max = inverse_lambdas_fft[idx_max_fft]
|
||||
|
||||
|
||||
thickness_fft = freq_max / 2.
|
||||
|
||||
|
||||
plt.figure(figsize=(10, 6),dpi =600)
|
||||
if plot:
|
||||
plt.loglog(inverse_lambdas_fft, np.abs(intensities_fft))
|
||||
plt.loglog(freq_max, np.abs(intensities_fft[idx_max_fft]), 'o')
|
||||
plt.xlabel('Frequency')
|
||||
plt.ylabel(r'FFT($I^*$)')
|
||||
plt.title(f'Thickness={thickness_fft:.2f}')
|
||||
|
||||
return OptimizeResult(thickness=thickness_fft,)
|
||||
|
||||
|
||||
def Prominence_from_fft(lambdas, intensities, refractive_index, num_half_space=None, plot=True):
|
||||
if num_half_space is None:
|
||||
num_half_space = 10 * len(lambdas)
|
||||
|
||||
# Interpolation pour que les données soient uniformément espacées
|
||||
inverse_lambdas_times_n = refractive_index / lambdas
|
||||
f = interp1d(inverse_lambdas_times_n, intensities)
|
||||
|
||||
inverse_lambdas_linspace = np.linspace(inverse_lambdas_times_n[0],
|
||||
inverse_lambdas_times_n[-1],
|
||||
2*num_half_space)
|
||||
intensities_linspace = f(inverse_lambdas_linspace)
|
||||
|
||||
# FFT
|
||||
density = (inverse_lambdas_times_n[-1] - inverse_lambdas_times_n[0]) / (2*num_half_space)
|
||||
freqs = fftfreq(2*num_half_space, density)
|
||||
fft_vals = fft(intensities_linspace)
|
||||
|
||||
# On conserve uniquement les fréquences positives
|
||||
freqs = freqs[num_half_space:]
|
||||
fft_vals = fft_vals[num_half_space:]
|
||||
|
||||
# Trouver le pic principal
|
||||
abs_fft = np.abs(fft_vals)
|
||||
idx_max = np.argmax(abs_fft)
|
||||
F_max = freqs[idx_max]
|
||||
|
||||
if plot:
|
||||
print(f"F_max detected at: {F_max:.4f}")
|
||||
plt.figure(figsize=(10, 6),dpi = 600)
|
||||
plt.plot(freqs, abs_fft, label='|FFT|')
|
||||
plt.axvline(F_max, color='r', linestyle='--', label='F_max')
|
||||
plt.xlabel('Fréquence')
|
||||
plt.ylabel('Amplitude FFT')
|
||||
plt.yscale('log')
|
||||
plt.xscale('log')
|
||||
plt.legend()
|
||||
plt.show()
|
||||
|
||||
# Filtrage : on garde les composantes au-dessus de 10 * F_max
|
||||
cutoff = 10 * F_max
|
||||
mask = freqs >= cutoff
|
||||
fft_filtered = np.zeros_like(fft_vals)
|
||||
fft_filtered[mask] = fft_vals[mask]
|
||||
|
||||
fft_full = np.zeros(2 * num_half_space, dtype=complex)
|
||||
fft_full[num_half_space:] = fft_filtered # fréquences positives
|
||||
fft_full[:num_half_space] = np.conj(fft_filtered[::-1])
|
||||
# IFFT
|
||||
signal_filtered = np.real(ifft(fft_full))
|
||||
|
||||
# Max amplitude après filtrage
|
||||
max_amplitude = np.max(np.abs(signal_filtered))
|
||||
|
||||
if plot:
|
||||
plt.figure(figsize=(10, 6),dpi = 600)
|
||||
plt.plot(signal_filtered, label='Signal filtered')
|
||||
plt.xlabel('Échantillons')
|
||||
plt.ylabel('Amplitude')
|
||||
plt.legend()
|
||||
plt.show()
|
||||
print(f"Amplitude Mal filtered : {max_amplitude:.4f}")
|
||||
|
||||
return max_amplitude
|
29
optifik/io.py
Normal file
29
optifik/io.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
import numpy as np
|
||||
|
||||
|
||||
def load_spectrum(spectrum_path, lambda_min=0, lambda_max=np.inf,
|
||||
delimiter=','):
|
||||
"""
|
||||
Load a spectrum file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
spectrum_path : string
|
||||
File path.
|
||||
lambda_min : scalar, optional
|
||||
Cut the data at this minimum wavelength in nm.
|
||||
lambda_max : scalar, optional
|
||||
Cut the data at this maximum wavelength in nm.
|
||||
delimiter : string, optional
|
||||
Delimiter between columns in the datafile.
|
||||
|
||||
Returns
|
||||
-------
|
||||
values : arrays
|
||||
(lamdbas, intensities)
|
||||
"""
|
||||
data = np.loadtxt(spectrum_path, delimiter=delimiter)
|
||||
lambdas, intensities = np.column_stack(data)
|
||||
|
||||
mask = (lambdas > lambda_min) & (lambdas < lambda_max)
|
||||
return lambdas[mask], intensities[mask]
|
151
optifik/minmax.py
Normal file
151
optifik/minmax.py
Normal file
|
@ -0,0 +1,151 @@
|
|||
import numpy as np
|
||||
|
||||
from scipy import stats
|
||||
from skimage.measure import ransac, LineModelND
|
||||
from scipy.signal import find_peaks
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
plt.rc('text', usetex=True)
|
||||
plt.rcParams.update({
|
||||
'axes.labelsize': 26,
|
||||
'xtick.labelsize': 32,
|
||||
'ytick.labelsize': 32,
|
||||
'legend.fontsize': 23,
|
||||
})
|
||||
|
||||
from .utils import OptimizeResult
|
||||
|
||||
|
||||
def thickness_from_minmax(lambdas,
|
||||
intensities,
|
||||
refractive_index,
|
||||
min_peak_prominence,
|
||||
min_peak_distance=10,
|
||||
method='linreg',
|
||||
plot=None):
|
||||
|
||||
"""
|
||||
Return the thickness from a min-max detection.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lambdas : array
|
||||
Wavelength values in nm.
|
||||
intensities : array
|
||||
Intensity values.
|
||||
refractive_index : scalar, optional
|
||||
Value of the refractive index of the medium.
|
||||
min_peak_prominence : scalar, optional
|
||||
Required prominence of peaks.
|
||||
min_peak_distance : scalar, optional
|
||||
Minimum distance between peaks.
|
||||
method : string, optional
|
||||
Either 'linreg' for linear regression or 'ransac'
|
||||
for Randon Sampling Consensus.
|
||||
debug : boolean, optional
|
||||
Show plots of peak detection and lin regression.
|
||||
|
||||
Returns
|
||||
-------
|
||||
results : Instance of `OptimizeResult` class.
|
||||
The attribute `thickness` gives the thickness value in nm.
|
||||
|
||||
Notes
|
||||
-----
|
||||
For more details about `min_peak_prominence` and `min_peak_distance`,
|
||||
see the documentation of `scipy.signal.find_peaks`. This function
|
||||
is used to find extrema.
|
||||
"""
|
||||
|
||||
peaks_max, _ = find_peaks(intensities, prominence=min_peak_prominence, distance=min_peak_distance)
|
||||
peaks_min, _ = find_peaks(-intensities, prominence=min_peak_prominence, distance=min_peak_distance)
|
||||
peaks = np.concatenate((peaks_min, peaks_max))
|
||||
peaks.sort()
|
||||
|
||||
k_values = np.arange(len(peaks))
|
||||
n_over_lambda = refractive_index[peaks][::-1] / lambdas[peaks][::-1]
|
||||
|
||||
if k_values.size < 2:
|
||||
# Can't fit if less than two points.
|
||||
return np.nan
|
||||
|
||||
if isinstance(refractive_index, np.ndarray):
|
||||
refractive_index = refractive_index[peaks][::-1]
|
||||
|
||||
if method.lower() == 'ransac':
|
||||
residual_threshold = 4e-5
|
||||
min_samples = 2
|
||||
# Scikit-image
|
||||
data = np.column_stack([k_values, n_over_lambda])
|
||||
model_robust, inliers = ransac(data, LineModelND,
|
||||
min_samples=min_samples,
|
||||
residual_threshold=residual_threshold,
|
||||
max_trials=100)
|
||||
slope = model_robust.params[1][1]
|
||||
thickness_minmax = 1 / slope / 4
|
||||
|
||||
# Scikit-learn
|
||||
#X, y = k_values.reshape(-1, 1), 1/lambdas[peaks][::-1]
|
||||
|
||||
## Fit line using all data
|
||||
#lr = linear_model.LinearRegression()
|
||||
#lr.fit(X, y)
|
||||
|
||||
#slransac = linear_model.RANSACRegressor(min_samples=min_samples,
|
||||
# residual_threshold=residual_threshold)
|
||||
#slransac.fit(X, y)
|
||||
#inlier_mask = slransac.inlier_mask_
|
||||
#outlier_mask = np.logical_not(inlier_mask)
|
||||
|
||||
## Predict data of estimated models
|
||||
#line_X = np.arange(X.min(), X.max())[:, np.newaxis]
|
||||
#line_y = lr.predict(line_X)
|
||||
#line_y_ransac = slransac.predict(line_X)
|
||||
|
||||
#slope = slransac.estimator_.coef_[0]
|
||||
|
||||
if plot:
|
||||
fig, ax = plt.subplots(figsize=(10, 6), dpi=600)
|
||||
|
||||
ax.set_xlabel('extremum n°')
|
||||
ax.set_ylabel('$n$($\lambda$) / $\lambda$')
|
||||
ax.plot(data[inliers, 0], data[inliers, 1], 'xb', alpha=0.6, label='Inliers')
|
||||
ax.plot(data[~inliers, 0], data[~inliers, 1], '+r', alpha=0.6, label='Outliers')
|
||||
ax.plot(k_values, model_robust.predict_y(k_values), '-g', label='Fit')
|
||||
|
||||
ax.legend()
|
||||
ax.set_title(f'Thickness = {thickness_minmax:.2f} nm')
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
return OptimizeResult(thickness=thickness_minmax,
|
||||
num_inliers=inliers.sum(),
|
||||
num_outliers=(~inliers).sum(),
|
||||
peaks_max=peaks_max,
|
||||
peaks_min=peaks_min)
|
||||
|
||||
elif method.lower() == 'linreg':
|
||||
slope, intercept, r_value, p_value, std_err = stats.linregress(k_values, n_over_lambda)
|
||||
#mean_n = np.mean(refractive_index)
|
||||
thickness_minmax = 1 / slope / 4
|
||||
|
||||
if plot:
|
||||
fig, ax = plt.subplots(figsize=(8, 6))
|
||||
|
||||
ax.set_xlabel('extremum n°')
|
||||
ax.set_ylabel('1 / $\lambda$')
|
||||
ax.plot(k_values, n_over_lambda, 's', label='Extrema')
|
||||
ax.plot(k_values, intercept + k_values * slope, label='Fit')
|
||||
|
||||
ax.legend()
|
||||
ax.set_title(f'Thickness = {thickness_minmax:.2f} nm')
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
return OptimizeResult(thickness=thickness_minmax,
|
||||
peaks_max=peaks_max,
|
||||
peaks_min=peaks_min,
|
||||
stderr=std_err)
|
||||
|
||||
else:
|
||||
raise ValueError('Wrong method')
|
288
optifik/scheludko.py
Normal file
288
optifik/scheludko.py
Normal file
|
@ -0,0 +1,288 @@
|
|||
import numpy as np
|
||||
|
||||
from .io import load_spectrum
|
||||
from scipy.optimize import curve_fit
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
plt.rc('text', usetex=True)
|
||||
plt.rcParams.update({
|
||||
'axes.labelsize': 26,
|
||||
'xtick.labelsize': 32,
|
||||
'ytick.labelsize': 32,
|
||||
'legend.fontsize': 23,
|
||||
})
|
||||
|
||||
|
||||
from .utils import OptimizeResult
|
||||
|
||||
def thickness_scheludko_at_order(wavelengths,
|
||||
intensity,
|
||||
interference_order,
|
||||
refractive_index,
|
||||
Imin=None):
|
||||
"""
|
||||
Compute the film thickness for a given interference order.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
wavelengths : TYPE
|
||||
DESCRIPTION.
|
||||
intensity : TYPE
|
||||
DESCRIPTION.
|
||||
interference_order: TYPE
|
||||
DESCRIPTION.
|
||||
refractive_index : TYPE
|
||||
DESCRIPTION.
|
||||
Imin : TYPE, optional
|
||||
DESCRIPTION. The default is None.
|
||||
|
||||
Returns
|
||||
-------
|
||||
thickness : TYPE
|
||||
DESCRIPTION.
|
||||
|
||||
"""
|
||||
if Imin is None:
|
||||
Imin = np.min(intensity)
|
||||
|
||||
n = refractive_index
|
||||
m = interference_order
|
||||
I = (np.asarray(intensity) - Imin) / (np.max(intensity) - Imin)
|
||||
|
||||
|
||||
prefactor = wavelengths / (2 * np.pi * n)
|
||||
argument = np.sqrt(I / (1 + (1 - I) * (n**2 - 1)**2 / (4 * n**2)))
|
||||
|
||||
if m % 2 == 0:
|
||||
term1 = (m / 2) * np.pi
|
||||
else:
|
||||
term1 = ((m+1) / 2) * np.pi
|
||||
|
||||
term2 = (-1)**m * np.arcsin(argument)
|
||||
|
||||
return prefactor * (term1 + term2)
|
||||
|
||||
|
||||
def Delta(wavelengths, thickness, interference_order, refractive_index):
|
||||
"""
|
||||
Calculates the Delta value for arrays of wavelengths, thicknesses h and r_indexs n.
|
||||
|
||||
Parameters:
|
||||
- wavelengths: array_like (or float), wavelengths λ
|
||||
- thickness : array_like (or float), thicknesses h
|
||||
- interference_order : int, interference order
|
||||
- refractive_index : array_like (or float), refractive r_indexs n
|
||||
|
||||
Returns:
|
||||
- delta: ndarray of corresponding Δ values
|
||||
"""
|
||||
|
||||
# ensure that the entries are numpy arrays
|
||||
wavelengths = np.asarray(wavelengths)
|
||||
h = np.asarray(thickness)
|
||||
n = np.asarray(refractive_index)
|
||||
m = interference_order
|
||||
|
||||
# Calculation of p as a function of the parity of m
|
||||
if m % 2 == 0:
|
||||
p = m / 2
|
||||
else:
|
||||
p = (m + 1) / 2
|
||||
|
||||
# Calculation of alpha
|
||||
alpha = ((n**2 - 1)**2) / (4 * n**2)
|
||||
|
||||
# Argument of sinus
|
||||
angle = (2 * np.pi * n * h / wavelengths) - p * np.pi
|
||||
|
||||
# A = sin²(argument)
|
||||
A = np.sin(angle)**2
|
||||
|
||||
# Final calcuation of Delta
|
||||
return (A * (1 + alpha)) / (1 + A * alpha)
|
||||
|
||||
|
||||
def Delta_fit(xdata, thickness, interference_order):
|
||||
"""
|
||||
Wrapper pour curve_fit : on fixe m, et lambda & n seront passés comme "xdata"
|
||||
"""
|
||||
lambd, n = xdata
|
||||
return Delta(lambd, thickness, interference_order, n)
|
||||
|
||||
|
||||
|
||||
|
||||
def thickness_from_scheludko(lambdas,
|
||||
raw_intensities,
|
||||
smoothed_intensities,
|
||||
peaks_min,
|
||||
peaks_max,
|
||||
refractive_index,
|
||||
plot=None):
|
||||
"""
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lambdas : TYPE
|
||||
DESCRIPTION.
|
||||
raw_intensities : TYPE
|
||||
DESCRIPTION.
|
||||
smoothed_intensities : TYPE
|
||||
DESCRIPTION.
|
||||
peaks_min : TYPE
|
||||
DESCRIPTION.
|
||||
peaks_max : TYPE
|
||||
DESCRIPTION.
|
||||
refractive_index : TYPE
|
||||
DESCRIPTION.
|
||||
plot : TYPE, optional
|
||||
DESCRIPTION. The default is None.
|
||||
|
||||
Returns
|
||||
-------
|
||||
thickness : TYPE
|
||||
DESCRIPTION.
|
||||
|
||||
"""
|
||||
|
||||
r_index = refractive_index
|
||||
|
||||
|
||||
lambda_min = lambdas[peaks_min[-1]]
|
||||
lambda_max = lambdas[peaks_max[-1]]
|
||||
|
||||
# On s'assure que lambda_min < lambda_max
|
||||
lambda_start = min(lambda_min, lambda_max)
|
||||
lambda_end = max(lambda_min, lambda_max)
|
||||
|
||||
# On crée le masque pour ne garder que les lambdas entre les deux extrema
|
||||
mask = (lambdas >= lambda_start) & (lambdas <= lambda_end)
|
||||
lambdas_masked = lambdas[mask]
|
||||
r_index_masked = r_index[mask]
|
||||
intensities_masked = smoothed_intensities[mask]
|
||||
intensities_raw_masked = raw_intensities[mask]
|
||||
min_ecart = np.inf
|
||||
best_m = None
|
||||
meilleure_h = None
|
||||
|
||||
if plot:
|
||||
plt.figure(figsize=(10, 6),dpi =600)
|
||||
plt.ylabel(r'$h$ ($\mathrm{{nm}}$)')
|
||||
plt.xlabel(r'$\lambda$ ($ \mathrm{nm} $)')
|
||||
|
||||
for m in range(0, 9):
|
||||
h_values = thickness_scheludko_at_order(lambdas_masked, intensities_masked, m, r_index_masked)
|
||||
|
||||
if plot:
|
||||
plt.plot(lambdas_masked, h_values,'.', markersize =3, label=f"Épaisseur du film (Scheludko, m={m})")
|
||||
ecart = np.max(h_values)-np.min(h_values)
|
||||
|
||||
print(f"Écart pour m={m} : {ecart:.3f} nm")
|
||||
|
||||
if ecart < min_ecart:
|
||||
min_ecart = ecart
|
||||
best_m = m
|
||||
meilleure_h = h_values
|
||||
|
||||
if plot:
|
||||
plt.figure(figsize=(10, 6), dpi=600)
|
||||
|
||||
DeltaVrai = (intensities_masked -np.min(intensities_masked))/(np.max(intensities_masked) -np.min(intensities_masked))
|
||||
#DeltaVrai = (intensities_raw_masked -np.min(intensities_raw_masked))/(np.max(intensities_raw_masked) -np.min(intensities_raw_masked))
|
||||
|
||||
DeltaScheludko = Delta(lambdas_masked, np.mean(meilleure_h), best_m, r_index_masked)
|
||||
#print(np.mean(meilleure_h),np.std(meilleure_h))
|
||||
if plot:
|
||||
plt.plot(lambdas_masked,DeltaVrai,'bo-', markersize=2,label=r'$\mathrm{{Smoothed}}\ \mathrm{{Data}}$')
|
||||
plt.plot(lambdas_masked,DeltaScheludko,'go-', markersize=2,label = rf'$\mathrm{{Scheludko}}\ (h = {np.mean(meilleure_h):.1f} \pm {np.std(meilleure_h):.1f}\ \mathrm{{nm}})$')
|
||||
|
||||
|
||||
xdata = (lambdas_masked, r_index_masked)
|
||||
popt, pcov = curve_fit(lambda x, h: Delta_fit(x, h, m), xdata, DeltaVrai, p0=[np.mean(meilleure_h)])
|
||||
fitted_h = popt[0]
|
||||
|
||||
|
||||
if plot:
|
||||
plt.plot(lambdas_masked, Delta(lambdas_masked, fitted_h, best_m, r_index_masked ), 'ro-',markersize=2, label=rf'$\mathrm{{Fit}}\ (h = {fitted_h:.1f}\pm {np.sqrt(pcov[0][0]):.1f} \ \mathrm{{nm}})$')
|
||||
plt.legend()
|
||||
plt.ylabel(r'$\Delta$')
|
||||
plt.xlabel(r'$\lambda$ ($ \mathrm{nm} $)')
|
||||
|
||||
|
||||
return OptimizeResult(thickness=fitted_h ,)
|
||||
|
||||
|
||||
def thickness_for_order0(lambdas,
|
||||
raw_intensities,
|
||||
smoothed_intensities,
|
||||
peaks_min,
|
||||
peaks_max,
|
||||
refractive_index,
|
||||
plot=None):
|
||||
|
||||
|
||||
File_I_min = 'tests/spectre_trou/000043641.xy'
|
||||
r_index = refractive_index
|
||||
|
||||
|
||||
lambdas_I_min, intensities_I_min = load_spectrum(File_I_min, lambda_min=450)
|
||||
|
||||
lambda_unique = lambdas[peaks_max[0]]
|
||||
|
||||
|
||||
# On crée le masque pour ne garder que les lambdas superieures a lambdas unique
|
||||
mask = lambdas >= lambda_unique
|
||||
lambdas_masked = lambdas[mask]
|
||||
r_index_masked = r_index[mask]
|
||||
intensities_masked = smoothed_intensities[mask]
|
||||
intensities_raw_masked = raw_intensities[mask]
|
||||
intensities_I_min_masked =intensities_I_min[mask]
|
||||
|
||||
min_ecart = np.inf
|
||||
best_m = None
|
||||
meilleure_h = None
|
||||
|
||||
|
||||
m = 0
|
||||
h_values = thickness_scheludko_at_order(lambdas_masked,
|
||||
intensities_masked,
|
||||
0,
|
||||
r_index_masked,
|
||||
Imin=intensities_I_min_masked)
|
||||
|
||||
if plot:
|
||||
plt.figure(figsize=(10, 6), dpi=600)
|
||||
plt.plot(lambdas_masked, h_values, label=r"Épaisseur du film (Scheludko, m=0)")
|
||||
|
||||
ecart = np.max(h_values) - np.min(h_values)
|
||||
best_m = m
|
||||
meilleure_h = h_values
|
||||
|
||||
|
||||
|
||||
DeltaVrai = (intensities_masked -np.min(intensities_I_min_masked))/(np.max(intensities_masked) -np.min(intensities_I_min_masked))
|
||||
|
||||
#DeltaVrai = (intensities_masked -np.min(intensities_masked))/(np.max(intensities_masked) -np.min(intensities_masked))
|
||||
|
||||
DeltaScheludko = Delta(lambdas_masked, np.mean(meilleure_h), best_m, r_index_masked)
|
||||
#print(np.mean(meilleure_h),np.std(meilleure_h))
|
||||
|
||||
|
||||
if plot:
|
||||
plt.figure(figsize=(10, 6), dpi=600)
|
||||
plt.plot(lambdas_masked,DeltaVrai,'bo-', markersize=2,label=r'$\mathrm{{Raw}}\ \mathrm{{Data}}$')
|
||||
plt.plot(lambdas_masked,DeltaScheludko,'ro-', markersize=2,label = rf'$\mathrm{{Scheludko}}\ (h = {np.mean(meilleure_h):.1f} \pm {np.std(meilleure_h):.1f}\ \mathrm{{nm}})$')
|
||||
|
||||
|
||||
xdata = (lambdas_masked, r_index_masked)
|
||||
popt, pcov = curve_fit(lambda x, h: Delta_fit(x, h, m), xdata, DeltaVrai, p0=[np.mean(meilleure_h)])
|
||||
fitted_h = popt[0]
|
||||
|
||||
if plot:
|
||||
plt.plot(lambdas_masked, Delta(lambdas_masked, fitted_h, best_m, r_index_masked ), 'go-',markersize=2, label=rf'$\mathrm{{Fit}}\ (h = {fitted_h:.1f}\pm {np.sqrt(pcov[0][0]):.1f} \ \mathrm{{nm}})$')
|
||||
plt.legend()
|
||||
plt.ylabel(r'$\Delta$')
|
||||
plt.xlabel(r'$\lambda$ (nm)')
|
||||
|
||||
return OptimizeResult(thickness=fitted_h ,)
|
27
optifik/utils.py
Normal file
27
optifik/utils.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
class OptimizeResult(dict):
|
||||
""" Represents the optimization result.
|
||||
|
||||
Notes
|
||||
-----
|
||||
This class has been copied from scipy.optimize
|
||||
|
||||
"""
|
||||
def __getattr__(self, name):
|
||||
try:
|
||||
return self[name]
|
||||
except KeyError:
|
||||
raise AttributeError(name)
|
||||
|
||||
__setattr__ = dict.__setitem__
|
||||
__delattr__ = dict.__delitem__
|
||||
|
||||
def __repr__(self):
|
||||
if self.keys():
|
||||
m = max(map(len, list(self.keys()))) + 1
|
||||
return '\n'.join([k.rjust(m) + ': ' + repr(v)
|
||||
for k, v in sorted(self.items())])
|
||||
else:
|
||||
return self.__class__.__name__ + "()"
|
||||
|
||||
def __dir__(self):
|
||||
return list(self.keys())
|
Loading…
Add table
Add a link
Reference in a new issue