import numpy as np
from scipy.interpolate import InterpolatedUnivariateSpline
import os
path_and_file = os.path.realpath(__file__)
path = path_and_file[:-12]
[docs]class Materials:
"""Compute the absorption, scattering, and extinction spectra of a sphere using Mie theory"""
def _create_test_multilayer(self, central_wavelength):
"""
Simple method to create a 3-entry array of wavelengths as follows:
[central_wavelength-1e-9 m, central_wavelength m, central_wavelength+1e-9 m]
and dummy _refractive_index_array that can be filled in
with actual materials at the wavelength arrays.
This is simply meant to enable unit testing for desired wavelengths of the
various materials methods
"""
self.wavelength_array = np.array(
[central_wavelength - 1e-9, central_wavelength, central_wavelength + 1e-9]
)
self.number_of_wavelengths = 3
self.number_of_layers = 3
self._refractive_index_array = np.reshape(
np.tile(np.array([1 + 0j, 1 + 0j, 1 + 0j]), self.number_of_wavelengths),
(self.number_of_wavelengths, self.number_of_layers),
)
def _find_unique_ri_file_data(self, wl_array):
"""
A simple method to eliminate redundant or numerically indistinguishable
data from refractive index files.
Arguments
---------
wl_array : numpy array of floats
the array of wavelengths read from a refractive index file
Returns
-------
unique_index_array : array of ints
the array of the indices of the unique elements of the
refractive index file
"""
wl_val = wl_array[0]
unique_index_array = [0]
for i in range(1, len(wl_array)):
new_wl_val = wl_array[i]
# skip over redundant values
if new_wl_val<=wl_val:
wl_val = new_wl_val
else:
unique_index_array.append(i)
wl_val = new_wl_val
return unique_index_array
def material_H2O(self, layer_number):
""" defines the refractive index layer of layer_number to be water
assuming static refractive index of n = 1.33 + 0j
"""
self._refractive_index_array[:, layer_number] = (
np.ones(len(self.wavelength_array), dtype=complex) * 1.33
)
def insert_layer(self, layer_number):
""" insert an air layer between layer_number-1 and layer_number
e.g. if you have a structure that is Air/SiO2/HfO2/Ag/Air
and you issue insert_layer(1), the new structure will be
Air/Air/SiO2/HfO2/Ag/Air
if you issue insert_layer(2), the new structure will be
Air/SiO2/Air/HfO2/Ag/Air
"""
_nwl = len(self._refractive_index_array[:,0])
_nl = len(self._refractive_index_array[0,:])
_temp_ri_array = np.copy(self._refractive_index_array)
_new_ri_array = np.zeros((_nwl, _nl+1), dtype=complex)
_new_air_layer = np.ones(_nwl, dtype=complex) * 1.0
_new_ri_array[:,:layer_number] = _temp_ri_array[:,:layer_number]
_new_ri_array[:,layer_number] = _new_air_layer
_new_ri_array[:,layer_number+1:] = _temp_ri_array[:,layer_number:]
self._refractive_index_array = np.copy(_new_ri_array)
def material_Air(self, layer_number):
""" defines the refractive index layer of layer_number to be air
assuming static refractive index of n = 1.0 + 0j
"""
self._refractive_index_array[:, layer_number] = (
np.ones(len(self.wavelength_array), dtype=complex) * 1.0
)
def material_from_file(self, layer_number, file_name):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be
from a test file with name "file_name" where the text file is ordered:
column 1: wavelength in meters, increasing order
column 2: real part of refractive index corresponding to wavelengths in col 1
column 3: imaginary part of refractive index corresponding to wavelengths in col 1
the file is expected to be in the directory $wpspecdir/wpspecdev/data
where $wpspecdir is the full path to the directory where you have wpspecdev installed
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as SiO2
file_name : str
the full file-name of the data file containing your refractive index information
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_from_file(1, "SiO2_ir.txt") -> layer 1 will be SiO2 from the SiO2_ir
data set good from visible to 50 microns (0.21-50)
"""
# get path to the sio2 data file
file_path = path + "data/" + file_name
# now read SiO2 data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
# now read Ag data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
# sometimes there are duplicate wavelength, n, and k entries
# in a data set; we want only the unique elements
idx = self._find_unique_ri_file_data(file_data[:,0])
n_spline = InterpolatedUnivariateSpline(file_data[idx,0], file_data[idx, 1], k=1)
k_spline = InterpolatedUnivariateSpline(file_data[idx,0], file_data[idx, 2], k=1)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_SiO2(self, layer_number):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be SiO2
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as SiO2
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_SiO2(1, wavelength_range="visible") -> layer 1 will be SiO2 from the SiO2_ir
data set good from visible to 50 microns (0.21-50)
"""
# get path to the sio2 data file
file_path = path + "data/SiO2_ir.txt"
# now read SiO2 data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_TiO2(self, layer_number):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be TiO2
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as TiO2
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_TiO2(1, wavelength_range="visible") -> layer 1 will be TiO2 from the Siefke data set good from visible to 125.123 microns (0.12-125.123)
"""
# get path to the TiO2 data file
file_path = path + "data/TiO2_Siefke.txt"
# now read TiO2 data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_Ta2O5(self, layer_number, wavelength_range="visible", override="true"):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be Ta2O5
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as Ta2O5
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_Ta2O5(1, wavelength_range="visible") -> layer 1 will be Ta2O5 from the Rodriguez data set good from visible to 1.5 microns (0.02-1.5)
>>> material_Ta2O5(2, wavelength_range="ir") -> layer 2 will be Ta2O5 from the Bright data set good until 1000 microns (0.5-1000)
"""
# dictionary specific to Ta2O5 with wavelength range information corresponding to different
# data sets
data1 = {
"file": "data/Ta2O5_Rodriguez.txt",
"lower_wavelength": 2.9494e-08,
"upper_wavelength": 1.5143e-06,
}
data2 = {
"file": "data/Ta2O5_Bright.txt",
"lower_wavelength": 5.0000e-07,
"upper_wavelength": 1.0000e-03,
}
shortest_wavelength = self.wavelength_array[0]
longest_wavelength = self.wavelength_array[self.number_of_wavelengths - 1]
if (
shortest_wavelength >= data1["lower_wavelength"]
and longest_wavelength <= data1["upper_wavelength"]
):
file_path = path + data1["file"]
elif (
shortest_wavelength >= data2["lower_wavelength"]
and longest_wavelength <= data2["upper_wavelength"]
):
file_path = path + data2["file"]
else:
file_path = path + data1["file"]
if override == "false":
# make sure the wavelength_range string is all lowercase
wavelength_range = wavelength_range.lower()
if (
wavelength_range == "visible"
or wavelength_range == "short"
or wavelength_range == "vis"
):
file_path = path + "data/Ta2O5_Rodriguez.txt"
elif wavelength_range == "ir" or wavelength_range == "long":
file_path = path + "data/Ta2O5_Bright.txt"
print("read from ", file_path)
# now read Ta2O5 data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_TiN(self, layer_number):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be Tin
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as Tin
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_Tin(1, wavelength_range="visible") -> layer 1 will be Tin from the Tin_ellipsometry data set good from visible to 7 microns (0.4-7)
"""
# get path to the tin data file
file_path = path + "data/TiN_ellipsometry_data.txt"
# now read Tin data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_static_refractive_index(self, layer_number, refractive_index):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
self._refractive_index_array[:, layer_number] = (
np.ones(len(self.wavelength_array), dtype=complex) * refractive_index
)
def material_Al(self, layer_number):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be Al
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as Al
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_Al(1, wavelength_range="visible") -> layer 1 will be Al from the Al_Rakic data set good from visible to 200 microns (0.00012399-200)
"""
# get path to the Al data file
file_path = path + "data/Al_Rakic.txt"
# now read Al data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_Pt(self, layer_number):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be Pt
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as Pt
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_Pt(1, wavelength_range="visible") -> layer 1 will be Pt from the Pt_Rakic data set good from visible to 12.398 microns (0.24797-12.398)
"""
self._refractive_index_array[:, layer_number] = (
np.ones(len(self.wavelength_array), dtype=complex) * 2.4
)
# get path to the Platinum data file
file_path = path + "data/Pt_Rakic.txt"
# now read Platinum data into a numpy array
file_data = np.loadtxt(file_path)
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_HfO2(self, layer_number):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be HfO2
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as HfO2
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_HfO2(1, wavelength_range="visible") -> layer 1 will be HfO2 from the HfO2_Al-Kuhaili data set good from visible to 2 microns (0.2-2)
"""
self._refractive_index_array[:, layer_number] = (
np.ones(len(self.wavelength_array), dtype=complex) * 2.4
)
# get path to the HfO2 data file
file_path = path + "data/HfO2_Al-Kuhaili.txt"
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_Au(self, layer_number, wavelength_range="visible", override="true"):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be Au
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as Au
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_Au(1, wavelength_range="visible") -> layer 1 will be Au from the JC_RI_f data set from 0.2 to 1.000025 microns
>>> material_Au(2, wavelength_range="ir") -> layer 2 will be Au from the Au_IR data set from 0.3 to 24.93 microns
"""
# dictionary specific to Au with wavelength range information corresponding to different
# data sets
data1 = {
"file": "data/Au_JC_RI_f.txt",
"lower_wavelength": 2e-07,
"upper_wavelength": 1.00025e-06,
}
data2 = {
"file": "data/Au_IR.txt",
"lower_wavelength": 3.000000e-07,
"upper_wavelength": 2.493000e-05,
}
shortest_wavelength = self.wavelength_array[0]
longest_wavelength = self.wavelength_array[self.number_of_wavelengths - 1]
if (
shortest_wavelength >= data1["lower_wavelength"]
and longest_wavelength <= data1["upper_wavelength"]
):
file_path = path + data1["file"]
elif (
shortest_wavelength >= data2["lower_wavelength"]
and longest_wavelength <= data2["upper_wavelength"]
):
file_path = path + data2["file"]
else:
file_path = path + data1["file"]
if override == "false":
# make sure the wavelength_range string is all lowercase
wavelength_range = wavelength_range.lower()
if (
wavelength_range == "visible"
or wavelength_range == "short"
or wavelength_range == "vis"
):
file_path = path + "data/Au_JC_RI_f.txt.txt"
elif wavelength_range == "ir" or wavelength_range == "long":
file_path = path + "data/Au_IR.txt"
print("read from ", file_path)
# now read Au data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_Rh(self, layer_number):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be Rh
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as Rh
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_Rh(1, wavelength_range="visible") -> layer 1 will be Rh from the Rh_Weaver data set good from visible to 12.4 microns (0.2-12.4)
"""
self._refractive_index_array[:, layer_number] = (
np.ones(len(self.wavelength_array), dtype=complex) * 2.4
)
# get path to the Rh data file
file_path = path + "data/Rh_Weaver.txt"
# now read Rh data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_Al2O3(self, layer_number):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be Al2O3
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as Al2O3
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_Al2O3(1, wavelength_range="visible") -> layer 1 will be Al2O3 from the Al2O3_ri.txt data set good from visible to 2.5 microns (0.4-2.5)
"""
self._refractive_index_array[:, layer_number] = (
np.ones(len(self.wavelength_array), dtype=complex) * 2.4
)
# get path to the Al2O3 data file
file_path = path + "data/Al2O3_ri.txt"
# now read Au data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_Ru(self, layer_number):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be Ru
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as Ru
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_Ru(1, wavelength_range="visible") -> layer 1 will be Ru from the Ru.txt data set good from visible to 6.0 microns (0.4-6.0)
"""
self._refractive_index_array[:, layer_number] = (
np.ones(len(self.wavelength_array), dtype=complex) * 2.4
)
# get path to the Ru data file
file_path = path + "data/Ru.txt"
# now read Ru data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_polystyrene(self, layer_number):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be polystyrene
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as polystyrene
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_polystyrene(1, wavelength_range="visible") -> layer 1 will be polystyrene from the polystyrene.txt data set good from visible to 19.942 microns (0.4-19.942)
"""
self._refractive_index_array[:, layer_number] = (
np.ones(len(self.wavelength_array), dtype=complex) * 2.4
)
# get path to the polystyrene data file
file_path = path + "data/Polystyrene.txt"
# now read Au data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_AlN(self, layer_number, wavelength_range="visible", override="true"):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be AlN
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as AlN
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_AlN(1, wavelength_range="visible") -> layer 1 will be AlN from the AlN_Pastrnak data set from 0.22 to 5.0 microns
>>> material_AlN(2, wavelength_range="ir") -> layer 2 will be AlN from the AlN_Kischkat data set from 1.53846 to 14.2857 microns
"""
# dictionary specific to AlN with wavelength range information corresponding to different
# data sets
data1 = {
"file": "data/AlN_Pastrnak.txt",
"lower_wavelength": 0.22e-6,
"upper_wavelength": 5.00e-6,
}
data2 = {
"file": "data/AlN_Kischkat.txt",
"lower_wavelength": 1.53846e-06,
"upper_wavelength": 14.2857e-06,
}
shortest_wavelength = self.wavelength_array[0]
longest_wavelength = self.wavelength_array[self.number_of_wavelengths - 1]
if (
shortest_wavelength >= data1["lower_wavelength"]
and longest_wavelength <= data1["upper_wavelength"]
):
file_path = path + data1["file"]
print("1")
elif (
shortest_wavelength >= data2["lower_wavelength"]
and longest_wavelength <= data2["upper_wavelength"]
):
file_path = path + data2["file"]
print("2")
else:
file_path = path + data1["file"]
if override == "false":
# make sure the wavelength_range string is all lowercase
wavelength_range = wavelength_range.lower()
if (
wavelength_range == "visible"
or wavelength_range == "short"
or wavelength_range == "vis"
):
file_path = path + "data/AlN_Pastrnak.txt"
elif wavelength_range == "ir" or wavelength_range == "long":
file_path = path + "data/AlN_Kischkat.txt"
print("read from ", file_path)
# now read AlN data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_W(self, layer_number, wavelength_range="visible", override="true"):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be W
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as W
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_W(1, wavelength_range="visible") -> layer 1 will be W from the W_Rakic data set good from visible to 12.398 microns (0.24797-12.398)
>>> material_W(2, wavelength_range="ir") -> layer 2 will be W from the Ordal data set good until 200 microns (0.667-200)
"""
# dictionary specific to W with wavelength range information corresponding to different
# data sets
data1 = {
"file": "data/W_Rakic.txt",
"lower_wavelength": 2.4797e-07,
"upper_wavelength": 1.2398e-05,
}
data2 = {
"file": "data/W_Ordal.txt",
"lower_wavelength": 6.67000e-07,
"upper_wavelength": 2.00000e-04,
}
shortest_wavelength = self.wavelength_array[0]
longest_wavelength = self.wavelength_array[self.number_of_wavelengths - 1]
if (
shortest_wavelength >= data1["lower_wavelength"]
and longest_wavelength <= data1["upper_wavelength"]
):
file_path = path + data1["file"]
elif (
shortest_wavelength >= data2["lower_wavelength"]
and longest_wavelength <= data2["upper_wavelength"]
):
file_path = path + data2["file"]
else:
file_path = path + data1["file"]
if override == "false":
# make sure the wavelength_range string is all lowercase
wavelength_range = wavelength_range.lower()
if (
wavelength_range == "visible"
or wavelength_range == "short"
or wavelength_range == "vis"
):
file_path = path + "data/W_Rakic.txt"
elif wavelength_range == "ir" or wavelength_range == "long":
file_path = path + "data/W_Ordal.txt"
print("read from ", file_path)
# now read W data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_Si(self, layer_number, wavelength_range="visible", override="true"):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be Si
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as Si
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_si(1, wavelength_range="visible") -> layer 1 will be Si from the Schinke data set good from visible to 1.5 microns (0.25-1.45)
>>> material_si(2, wavelength_range="ir") -> layer 2 will be Si from the Shkondin data set good until 1000 microns (2-20)
"""
# dictionary specific to W with wavelength range information corresponding to different
# data sets
data1 = {
"file": "data/Si_Schinke.txt",
"lower_wavelength": 0.000000250,
"upper_wavelength": 0.000001450,
}
data2 = {
"file": "data/Si_Shkondin.txt",
"lower_wavelength": 0.00000200000,
"upper_wavelength": 0.00002000000,
}
shortest_wavelength = self.wavelength_array[0]
longest_wavelength = self.wavelength_array[self.number_of_wavelengths - 1]
if (
shortest_wavelength >= data1["lower_wavelength"]
and longest_wavelength <= data1["upper_wavelength"]
):
file_path = path + data1["file"]
elif (
shortest_wavelength >= data2["lower_wavelength"]
and longest_wavelength <= data2["upper_wavelength"]
):
file_path = path + data2["file"]
else:
file_path = path + data1["file"]
if override == "false":
# make sure the wavelength_range string is all lowercase
wavelength_range = wavelength_range.lower()
if (
wavelength_range == "visible"
or wavelength_range == "short"
or wavelength_range == "vis"
):
file_path = path + "data/Si_Schinke.txt"
elif wavelength_range == "ir" or wavelength_range == "long":
file_path = path + "data/Si_Shkondin.txt"
# now read Si data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_Re(self, layer_number, wavelength_range="visible", override="true"):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be Re
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as Re
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_Re(1, wavelength_range="visible") -> layer 1 will be Re from the Re_Windt data set good from visible to 1.5 microns (0.00236-0.12157)
>>> material_Re(2, wavelength_range="ir") -> layer 2 will be Re from the Re_Palik data set good until 6 microns (0.4-6)
"""
# dictionary specific to W with wavelength range information corresponding to different
# data sets
data1 = {
"file": "data/Re_Windt.txt",
"lower_wavelength": 2.36e-09,
"upper_wavelength": 1.2157e-07,
}
data2 = {
"file": "data/Re_Palik.txt",
"lower_wavelength": 0.0000004000,
"upper_wavelength": 0.0000060000,
}
shortest_wavelength = self.wavelength_array[0]
longest_wavelength = self.wavelength_array[self.number_of_wavelengths - 1]
if (
shortest_wavelength >= data1["lower_wavelength"]
and longest_wavelength <= data1["upper_wavelength"]
):
file_path = path + data1["file"]
elif (
shortest_wavelength >= data2["lower_wavelength"]
and longest_wavelength <= data2["upper_wavelength"]
):
file_path = path + data2["file"]
else:
file_path = path + data1["file"]
if override == "false":
# make sure the wavelength_range string is all lowercase
wavelength_range = wavelength_range.lower()
if (
wavelength_range == "visible"
or wavelength_range == "short"
or wavelength_range == "vis"
):
file_path = path + "data/Re_Windt.txt"
elif wavelength_range == "ir" or wavelength_range == "long":
file_path = path + "data/Re_Palik.txt"
# now read Re data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_Ag(self, layer_number, wavelength_range="visible", override="true"):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be Ag
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as Ag
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_Ag(1, wavelength_range="visible") -> layer 1 will be Ag from the Ag_JC data set good from 0.1879 to 1.937
>>> material_Ag(2, wavelength_range="ir") -> layer 2 will be Ag from the Yang data set good until 24.92 microns (0.27-24.92)
"""
# dictionary specific to W with wavelength range information corresponding to different
# data sets
data1 = {
"file": "data/Ag_JC.txt",
"lower_wavelength": 1.87900e-07,
"upper_wavelength": 1.93700e-06,
}
data2 = {
"file": "data/Ag_Yang.txt",
"lower_wavelength": 2.70000e-07,
"upper_wavelength": 2.49200e-05,
}
shortest_wavelength = self.wavelength_array[0]
longest_wavelength = self.wavelength_array[self.number_of_wavelengths - 1]
if (
shortest_wavelength >= data1["lower_wavelength"]
and longest_wavelength <= data1["upper_wavelength"]
):
file_path = path + data1["file"]
elif (
shortest_wavelength >= data2["lower_wavelength"]
and longest_wavelength <= data2["upper_wavelength"]
):
file_path = path + data2["file"]
else:
file_path = path + data1["file"]
if override == "false":
# make sure the wavelength_range string is all lowercase
wavelength_range = wavelength_range.lower()
if (
wavelength_range == "visible"
or wavelength_range == "short"
or wavelength_range == "vis"
):
file_path = path + "data/Ag_JC.txt"
elif wavelength_range == "ir" or wavelength_range == "long":
file_path = path + "data/Ag_Yang.txt"
# now read Ag data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
# sometimes there are duplicate wavelength, n, and k entries
# in a data set; we want only the unique elements
idx = self._find_unique_ri_file_data(file_data[:,0])
n_spline = InterpolatedUnivariateSpline(file_data[idx,0], file_data[idx, 1], k=1)
k_spline = InterpolatedUnivariateSpline(file_data[idx,0], file_data[idx, 2], k=1)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def material_Pb(self, layer_number, wavelength_range="visible", override="true"):
if layer_number > 0 and layer_number < (self.number_of_layers - 1):
"""defines the refractive index of layer layer_number to be Pb
Arguments
----------
layer_number : int
specifies the layer of the stack that will be modelled as Pb
wavelength_range (optional) : str
specifies wavelength regime that is desired for modelling the material
Attributes
----------
_refractive_index_array : 1 x number_of_wavelengths numpy array of complex floats
Returns
-------
None
Examples
--------
>>> material_pb(1, wavelength_range="visible") -> layer 1 will be Pb from the Werner data set good from visible to 2.47 microns
>>> material_pb(2, wavelength_range="ir") -> layer 2 will be Pb from the ordal data set good until 667 microns
"""
# dictionary specific to W with wavelength range information corresponding to different
# data sets
data1 = {
"file": "data/Pb_Werner.txt",
"lower_wavelength": 1.758600000E-08,
"upper_wavelength": 2.479684000E-06,
}
data2 = {
"file": "data/Pb_Ordal.txt",
"lower_wavelength": 0.00000066700000,
"upper_wavelength": 0.00066700000000,
}
shortest_wavelength = self.wavelength_array[0]
longest_wavelength = self.wavelength_array[self.number_of_wavelengths - 1]
if (
shortest_wavelength >= data1["lower_wavelength"]
and longest_wavelength <= data1["upper_wavelength"]
):
file_path = path + data1["file"]
elif (
shortest_wavelength >= data2["lower_wavelength"]
and longest_wavelength <= data2["upper_wavelength"]
):
file_path = path + data2["file"]
else:
file_path = path + data1["file"]
if override == "false":
# make sure the wavelength_range string is all lowercase
wavelength_range = wavelength_range.lower()
if (
wavelength_range == "visible"
or wavelength_range == "short"
or wavelength_range == "vis"
):
file_path = path + "data/Pb_Werner.txt"
elif wavelength_range == "ir" or wavelength_range == "long":
file_path = path + "data/Pb_Ordal.txt"
print("read from ", file_path)
# now read Pb data into a numpy array
file_data = np.loadtxt(file_path)
# file_path[:,0] -> wavelengths in meters
# file_path[:,1] -> real part of the refractive index
# file_path[:,2] -> imaginary part of the refractive index
n_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
k_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 2], k=1
)
self._refractive_index_array[:, layer_number] = n_spline(
self.wavelength_array
) + 1j * k_spline(self.wavelength_array)
def _read_CIE(self):
""" Reads CIE data and stores as attributes self.cie_cr, self.cie_cg, self.cie_cb
Arguments
----------
None
References
----------
Equations 29-32 of https://github.com/FoleyLab/wptherml/blob/master/docs/Equations.pdf
Attributes
----------
_cie_cr : 1 x wavelength array of floats
data corresponding to red cone response function in integrand of Eq. 29
_cie_cg : 1 x wavelength array of floats
data corresponding to the green cone response function in integrand of Eq. 30
_cie_cb : 1 x wavelength array of floats
data corresponding to the blue cone response function in integrand of Eq. 31
Returns
-------
None
"""
# initialize cie arrays?
self._cie_cr = np.zeros_like(self.wavelength_array)
self._cie_cg = np.zeros_like(self.wavelength_array)
self._cie_cb = np.zeros_like(self.wavelength_array)
#)
# get path to the cie data
file_path = path + "data/cie_cmf.txt"
# now read Rh data into a numpy array
file_data = np.loadtxt(file_path)
# file_data[:,0] -> wavelengths in nm
# file_data[:,1] -> cr response function
# file_data[:,2] -> cg response function
# file_data[:,3] -> cb resposne function
_cr_spline = InterpolatedUnivariateSpline(
file_data[:, 0] * 1e-9, file_data[:, 1], k=1
)
_cg_spline = InterpolatedUnivariateSpline(
file_data[:, 0] * 1e-9, file_data[:, 2], k=1
)
_cb_spline = InterpolatedUnivariateSpline(
file_data[:, 0] * 1e-9, file_data[:, 3], k=1
)
# values of data file at 500 nm
expected_values = np.array([0.0049, 0.3230, 0.2720])
spline_values = np.array([_cr_spline(500e-9), _cg_spline(500e-9), _cb_spline(500e-9)])
assert np.allclose(expected_values, spline_values)
self._cie_cr[:] = _cr_spline(self.wavelength_array)
self._cie_cg[:] = _cg_spline(self.wavelength_array)
self._cie_cb[:] = _cb_spline(self.wavelength_array)
def _read_AM(self):
""" Reads AM1.5 data and returns an array of the AM1.5 data evaluated at each value of
self.wavelength_array
Arguments
----------
None
References
----------
add
Attributes
----------
None
Returns
-------
1 x number_of_wavelengths array of floats
Data corresponding the AM1.5 spectrum evaluated at each value of self.wavelength_array
"""
# get path to the AM data
file_path = path + "data/scaled_AM_1_5.txt"
# now read Rh data into a numpy array
file_data = np.loadtxt(file_path)
# file_data[:,0] -> wavelengths in m
# file_data[:,1] -> solar spectrum in W / m / m^2 / sr
_solar_spline = InterpolatedUnivariateSpline(
file_data[:, 0], file_data[:, 1], k=1
)
# values of data file at 615 nm
# 0.000000615000000 1325400000.0000000000000000000000
_expected_value = 1325400000.
_spline_value = _solar_spline(615e-9)
assert np.isclose(_expected_value, _spline_value)
return _solar_spline(self.wavelength_array)
def _read_Atmospheric_Transmissivity(self):
""" Reads atmospherical transmissivity data and returns
an array of this data evaluated at each value of self.wavelength_array
Arguments
----------
None
References
----------
add
Attributes
----------
None
Returns
-------
1 x number_of_wavelengths array of floats
atmospheric transmissivity evaluated at each value of self.wavelength_array
"""
# get path to the AM data
file_path = path + "data/Atmospheric_transmissivity.txt"
# now read Rh data into a numpy array
file_data = np.loadtxt(file_path)
# file_data[:,0] -> wavelengths in m
# file_data[:,1] -> atmospheric transmissivity
# get indices of unique elements
idx = self._find_unique_ri_file_data(file_data[:,0])
_atrans_spline = InterpolatedUnivariateSpline(
file_data[idx, 0], file_data[idx, 1], k=1
)
# values of data file at 7.1034e-06 meters (7.1034 microns) -> T = 0.561289
_expected_value = 0.561289
_spline_value = _atrans_spline(7.1034e-6)
assert np.isclose(_expected_value, _spline_value)
return _atrans_spline(self.wavelength_array)