##########################################################################
########################## Useful functions ##########################
##########################################################################
# Version 0.1
# (c) Larry Lueer, Vincent M. Le Corre, i-MEET 2021-2023
# Import libraries
import numpy as np
from copy import deepcopy
from functools import partial
from typing import Callable, Any
[docs]
def get_unique_X(X,xaxis,X_dimensions):
"""Get the unique values of the independent variable (X) in the dataset
Parameters
----------
X : ndarray
the experimental dimensions
xaxis : str, optional
the name of the independent variable
X_dimensions : list, optional
names of the X columns
Returns
-------
X_unique : ndarray
the unique values of the independent variable
X_dimensions_uni : list
the names of the columns of X_unique
Raises
------
ValueError
if xaxis is not in X_dimensions
"""
X_unique = deepcopy(X)
idx_x = None
if xaxis in X_dimensions:
idx_x = X_dimensions.index(xaxis)
else:
raise ValueError(xaxis + ' not in X_dimensions, please add it to X_dimensions')
X_unique = np.delete(X_unique,idx_x,axis=1)
X_dimensions_uni = [x for x in X_dimensions if x != xaxis]
# get index of unique values
unique,idxuni = np.unique(X_unique,axis=0,return_index=True)
X_unique = X_unique[np.sort(idxuni),:] # resort X_unique
return X_unique,X_dimensions_uni
[docs]
def get_unique_X_and_xaxis_values(X,xaxis,X_dimensions):
"""Get the values of the independent variable (X) in the dataset for each unique value of the other dimensions
Parameters
----------
X : ndarray
the experimental dimensions
xaxis : str, optional
the name of the independent variable
X_dimensions : list, optional
the names of the columns of X
Returns
-------
xs : list of ndarrays
the values of the independent variable for each unique value of the other dimensions
"""
X_unique, X_dimensions_uni = get_unique_X(X,xaxis,X_dimensions) # get unique X values and their dimensions
idx_x = int(X_dimensions.index(xaxis))
xs = []
for uni in X_unique:
X_dum = deepcopy(X)
# drop the xaxis column
X_dum = np.delete(X_dum,X_dimensions.index(xaxis),axis=1)
# find indexes where the other columns are equal to the unique values
idxs = np.where(np.all(X_dum==uni,axis=1))[0]
# get the values of the xaxis for these indexes
xs.append(X[idxs,idx_x])
return X_unique, X_dimensions_uni, xs
[docs]
def callable_name(any_callable: Callable[..., Any]) -> str:
"""Returns the name of a callable object
Parameters
----------
any_callable : Callable[..., Any]
Callable object
Returns
-------
str
Name of the callable object
"""
if isinstance(any_callable, partial):
return any_callable.func.__name__
else:
try:
return any_callable.__name__
except AttributeError:
return str(any_callable)
[docs]
def gaussian_pulse_norm(t, tpulse, width):
"""Returns a gaussian pulse
Parameters
----------
t : 1-D sequence of floats
t time axis (unit: s)
tpulse : float
tpulse center of the pulse (unit: s)
width : float
width of the pulse (unit: s)
Returns
-------
1-D sequence of floats
Vector containing the gaussian pulse
"""
return np.exp(-np.power(t - tpulse, 2.) / (2 * np.power(width, 2.)))
[docs]
def polynom(x,a,gamma):
return a*(x**gamma)
[docs]
def sigmoid(x,a,b,xc):
return a/2*(1+np.tanh((x-xc)/(2*b)))
[docs]
def gauss(x,a,b,xc):
return a/(b*np.sqrt(2*np.pi)) * np.exp(-1/2*((x-xc)/b)**2) # b is the standard deviation of a Normal distribution
[docs]
def gauss_sk(x,a,b,c,s=0.0001): # Gauss function VERIFIED 11 June 2020
# a= amps, b=width, c=center, s=skewness parameter
S=a/(b*np.sqrt(2*np.pi))*np.exp(-(np.log(1+np.sqrt(2)/2*s*((x-c)/b))/s)**2)
S[np.isnan(S)]=0
return S
[docs]
def sci_notation(number, sig_fig=2):
"""Make proper scientific notation for graphs
Parameters
----------
number : float
Number to put in scientific notation.
sig_fig : int, optional
Number of significant digits (Defaults = 2).
Returns
-------
output : str
String containing the number in scientific notation
"""
if sig_fig != -1:
if number == 0:
output = '0'
else:
ret_string = "{0:.{1:d}e}".format(number, sig_fig)
a,b = ret_string.split("e")
if int(b) >= 0:
b = int(b) #removed leading "+" and strips leading zeros too.
c = ''
else:
b = abs(int(b))
c = u"\u207B" # superscript minus sign
SUP = str.maketrans("0123456789", "⁰¹²³⁴⁵⁶⁷⁸⁹")
b = str(b).translate(SUP)
output =a + ' x 10' + c + b
else:
if number == 0:
output = '0'
else:
ret_string = "{0:.{1:d}e}".format(number, 0)
a,b = ret_string.split("e")
b = int(b) #removed leading "+" and strips leading zeros too.
if int(b) >= 0:
b = int(b) #removed leading "+" and strips leading zeros too.
c = ''
else:
b = abs(int(b))
c = u"\u207B" # superscript minus sign
SUP = str.maketrans("0123456789", "⁰¹²³⁴⁵⁶⁷⁸⁹")
#SUB = str.maketrans("0123456789", "₀₁₂₃₄₅₆₇₈₉")
b = str(b).translate(SUP)
output = '10' + c + b
return output
[docs]
def get_flux_density(P,wl,nu,A,alpha):
"""
From the measured power and reprate and area,
get photons/cm2 and approximate photons/cm3 per pulse
Args:
P (float): total CW power of pulse in W
wl (float): excitation wavelength in nm
nu (float): repetition rate in s-1
A (float): effective pump area in cm2
alpha (float): penetration depth in cm
Returns:
flux (float): flux in photons per cm2
density (float): average volume density in photons/cm3
"""
E = 1e7/wl/8065 * 1.603e-19 # convert wavelength to J for a single photon
Epu = P/nu # energy in J of a single pulse
Nph = Epu/E # Photon number in pulse
flux = Nph/A # flux in photons / cm2
density = flux/alpha # average absorbed density in photons/cm3
return flux,density