Skip to content

Commit

Permalink
Merge pull request #4 from sun-data/feature/sensors
Browse files Browse the repository at this point in the history
Add `optika.sensors` module.
  • Loading branch information
byrdie authored Dec 21, 2023
2 parents d0983b6 + 131c610 commit 0ba6f70
Show file tree
Hide file tree
Showing 7 changed files with 503 additions and 3 deletions.
25 changes: 25 additions & 0 deletions docs/refs.bib
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,28 @@ @incollection{Born1980
url = {https://www.sciencedirect.com/science/article/pii/B9780080264820500086},
author = {Max Born and Emil Wolf},
}
@book{Janesick2001
author = {James R. Janesick},
title = {Scientific Charge-Coupled Devices},
publisher = {The International Society for Optical Engineering},
address = {Bellingham, Washington},
year = {2001},
isbn = {0-8194-3698-4},
doi = {10.1117/3.374903},
url = {https://www.spiedigitallibrary.org/ebooks/PM/Scientific-Charge-Coupled-Devices/eISBN-9780819480392/10.1117/3.374903},
}
@article{Stern1994,
author = {Robert A. Stern and Lawrence Shing and Morley M. Blouke},
journal = {Appl. Opt.},
keywords = {Absorption coefficient; CCD cameras; Charge-coupled devices; Interference filters; Optical constants; Solar cells},
number = {13},
pages = {2521--2533},
publisher = {Optica Publishing Group},
title = {Quantum efficiency measurements and modeling of ion-implanted, laser-annealed charge-coupled devices: x-ray, extreme-ultraviolet, ultraviolet, and optical data},
volume = {33},
month = {May},
year = {1994},
url = {https://opg.optica.org/ao/abstract.cfm?URI=ao-33-13-2521},
doi = {10.1364/AO.33.002521},
abstract = {We report quantum efficiency measurements of backilluminated, ion-implanted, laser-annealed CCD's in the wavelength range 13--10,000 {\AA}. The equivalent quantum efficiency (the equivalent photons detected per incident photon) ranges from a minimum of 5\% at 1216 {\AA} to a maximum of 87\% at 135 {\AA}. Using a simple relationship for the charge-collection efficiency of the CCD pixels as a function of depth, we present a semiempirical model with few parameters that reproduces our measurements with a fair degree of accuracy. The advantage of this model is that it can be used to predict CCD quantum efficiency performance for shallow backside implanted devices without a detailed solution of a system of differential equations, as in conventional approaches, and it yields a simple analytic form for the charge-collection efficiency that is adequate for detector calibration purposes. Making detailed assumptions about the dopant profile, we also solve the current density and continuity equations in order to relate our semiempirical model parameters to surface and bulk device properties. The latter procedure helps to better establish device processing parameters for a given level of CCD quantum efficiency performance.},
}
1 change: 1 addition & 0 deletions optika/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
from . import rulings
from . import propagators
from . import surfaces
from . import sensors
from . import systems
78 changes: 78 additions & 0 deletions optika/_tests/test_sensors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import pytest
import numpy as np
import astropy.units as u
import named_arrays as na
import optika


@pytest.mark.parametrize(
argnames="wavelength,result_expected",
argvalues=[
(1.0 * u.eV, 0),
(2.0 * u.eV, 1),
(2 * optika.sensors.energy_electron_hole, 2),
],
)
def test_quantum_yield_ideal(
wavelength: u.Quantity | na.AbstractScalar, result_expected: na.AbstractScalar
):
result = optika.sensors.quantum_yield_ideal(wavelength)
assert np.all(result == result_expected)


@pytest.mark.parametrize(
argnames="wavelength",
argvalues=[
304 * u.AA,
na.linspace(100, 200, axis="wavelength", num=4) * u.AA,
],
)
@pytest.mark.parametrize(
argnames="direction",
argvalues=[
na.Cartesian3dVectorArray(0, 0, 1),
],
)
@pytest.mark.parametrize(
argnames="thickness_oxide",
argvalues=[
10 * u.AA,
],
)
@pytest.mark.parametrize(
argnames="thickness_implant",
argvalues=[
1000 * u.AA,
],
)
@pytest.mark.parametrize(
argnames="thickness_substrate",
argvalues=[
1 * u.um,
],
)
@pytest.mark.parametrize(
argnames="cce_backsurface",
argvalues=[
0.2,
1,
],
)
def test_quantum_efficiency_effective(
wavelength: u.Quantity | na.AbstractScalar,
direction: na.AbstractCartesian3dVectorArray,
thickness_oxide: u.Quantity | na.AbstractScalar,
thickness_implant: u.Quantity | na.AbstractScalar,
thickness_substrate: u.Quantity | na.AbstractScalar,
cce_backsurface: u.Quantity | na.AbstractScalar,
):
result = optika.sensors.quantum_efficiency_effective(
wavelength=wavelength,
direction=direction,
thickness_oxide=thickness_oxide,
thickness_implant=thickness_implant,
thickness_substrate=thickness_substrate,
cce_backsurface=cce_backsurface,
)
assert np.all(result >= 0)
assert np.all(result <= 1)
4 changes: 2 additions & 2 deletions optika/chemicals/_chemicals.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ class Chemical(
na.plt.plot(n_si.inputs, n_si.outputs, label="silicon");
na.plt.plot(n_sio2.inputs, n_sio2.outputs, label="silicon dioxide");
ax.set_xscale("log");
ax.set_xlabel(f"wavelength ({n_si.inputs.unit:latex_inline}");
ax.set_xlabel(f"wavelength ({n_si.inputs.unit:latex_inline})");
ax.set_ylabel("index of refraction");
ax.legend();
Expand All @@ -187,7 +187,7 @@ class Chemical(
na.plt.plot(k_si.inputs, k_si.outputs, label="silicon");
na.plt.plot(k_sio2.inputs, k_sio2.outputs, label="silicon dioxide");
ax.set_xscale("log");
ax.set_xlabel(f"wavelength ({k_si.inputs.unit:latex_inline}");
ax.set_xlabel(f"wavelength ({k_si.inputs.unit:latex_inline})");
ax.set_ylabel("wavenumber");
ax.legend();
"""
Expand Down
4 changes: 4 additions & 0 deletions optika/materials/_multilayers.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def multilayer_efficiency(
stack.
normal
A vector perpendicular to the interface between successive layers.
If :obj:`None`, the normal vector is assumed to be :math:`-\hat{z}`
profile_interface
An optional profile for modeling the roughness and/or
diffusiveness of the interface between successive layers.
Expand Down Expand Up @@ -358,6 +359,9 @@ def multilayer_efficiency(

wavelength = wavelength_ambient

if normal is None:
normal = na.Cartesian3dVectorArray(0, 0, -1)

direction_substrate = snells_law(
wavelength=wavelength,
direction=direction_ambient,
Expand Down
2 changes: 1 addition & 1 deletion optika/materials/_tests/test_multilayers.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
@pytest.mark.parametrize(
argnames="normal",
argvalues=[
na.Cartesian3dVectorArray(0, 0, -1),
None,
],
)
@pytest.mark.parametrize(
Expand Down
Loading

0 comments on commit 0ba6f70

Please sign in to comment.