-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added
optika.sensors.E2VCCD97Material
class. (#6)
Added `optika.sensors.E2VCCD97Material` class.
- Loading branch information
Showing
9 changed files
with
454 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
from ._materials import * | ||
from ._e2v_ccd97 import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from ._e2v_ccd97 import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
import functools | ||
import pathlib | ||
import numpy as np | ||
import scipy.optimize | ||
import astropy.units as u | ||
import named_arrays as na | ||
from .._materials import quantum_efficiency_effective | ||
from .._materials import AbstractBackilluminatedCCDMaterial | ||
|
||
__all__ = [ | ||
"E2VCCD97Material", | ||
] | ||
|
||
|
||
class E2VCCD97Material( | ||
AbstractBackilluminatedCCDMaterial, | ||
): | ||
""" | ||
A model of the light-sensitive material of an e2v CCD90 sensor based on | ||
measurements by :cite:t:`Moody2017`. | ||
Examples | ||
-------- | ||
Plot the measured E2VCCD97 quantum efficiency vs the fitted | ||
quantum efficiency calculated using the method of :cite:t:`Stern1994`. | ||
.. jupyter-execute:: | ||
import matplotlib.pyplot as plt | ||
import astropy.units as u | ||
import astropy.visualization | ||
import named_arrays as na | ||
import optika | ||
# Create a new instance of the e2v CCD97 light-sensitive material | ||
material_ccd97 = optika.sensors.E2VCCD97Material() | ||
# Store the wavelengths at which the QE was measured | ||
wavelength_measured = material_ccd97.quantum_efficiency_measured.inputs | ||
# Store the QE measurements | ||
qe_measured = material_ccd97.quantum_efficiency_measured.outputs | ||
# Define a grid of wavelengths with which to evaluate the fitted QE | ||
wavelength_fit = na.geomspace(5, 10000, axis="wavelength", num=1001) * u.AA | ||
# Evaluate the fitted QE using the given wavelengths | ||
qe_fit = material_ccd97.quantum_efficiency_effective( | ||
rays=optika.rays.RayVectorArray( | ||
wavelength=wavelength_fit, | ||
direction=na.Cartesian3dVectorArray(0, 0, 1), | ||
), | ||
normal=na.Cartesian3dVectorArray(0, 0, -1), | ||
) | ||
# Plot the measured QE vs the fitted QE | ||
with astropy.visualization.quantity_support(): | ||
fig, ax = plt.subplots(constrained_layout=True) | ||
na.plt.scatter( | ||
wavelength_measured, | ||
qe_measured, | ||
label="measured", | ||
) | ||
na.plt.plot( | ||
wavelength_fit, | ||
qe_fit, | ||
label="fit", | ||
) | ||
ax.set_xscale("log") | ||
ax.set_xlabel(f"wavelength ({wavelength_fit.unit:latex_inline})") | ||
ax.set_ylabel("quantum efficiency") | ||
ax.legend() | ||
The thickness of the oxide layer found by the fit is | ||
.. jupyter-execute:: | ||
material_ccd97.thickness_oxide | ||
The thickness of the implant layer found by the fit is | ||
.. jupyter-execute:: | ||
material_ccd97.thickness_implant | ||
The thickness of the substrate found by the fit is | ||
.. jupyter-execute:: | ||
material_ccd97.thickness_substrate | ||
And the differential charge collection efficiency at the backsurface | ||
found by the fit is | ||
.. jupyter-execute:: | ||
material_ccd97.cce_backsurface | ||
""" | ||
|
||
@property | ||
def quantum_efficiency_measured(self) -> na.FunctionArray: | ||
directory = pathlib.Path(__file__).parent | ||
energy, qe = np.genfromtxt( | ||
fname=directory / "e2v_ccd97_qe_moody2017.csv", | ||
delimiter=", ", | ||
unpack=True, | ||
) | ||
energy = energy << u.eV | ||
wavelength = energy.to(u.AA, equivalencies=u.spectral()) | ||
return na.FunctionArray( | ||
inputs=na.ScalarArray(wavelength, axes="wavelength"), | ||
outputs=na.ScalarArray(qe, axes="wavelength"), | ||
) | ||
|
||
@functools.cached_property | ||
def _quantum_efficiency_fit(self) -> dict[str, float | u.Quantity]: | ||
qe_measured = self.quantum_efficiency_measured | ||
|
||
unit_thickness_oxide = u.AA | ||
unit_thickness_implant = u.AA | ||
unit_thickness_substrate = u.um | ||
|
||
def eqe_rms_difference(x: tuple[float, float, float, float]): | ||
( | ||
thickness_oxide, | ||
thickness_implant, | ||
thickness_substrate, | ||
cce_backsurface, | ||
) = x | ||
qe_fit = quantum_efficiency_effective( | ||
wavelength=qe_measured.inputs, | ||
direction=na.Cartesian3dVectorArray(0, 0, 1), | ||
thickness_oxide=thickness_oxide << unit_thickness_oxide, | ||
thickness_implant=thickness_implant << unit_thickness_implant, | ||
thickness_substrate=thickness_substrate << unit_thickness_substrate, | ||
cce_backsurface=cce_backsurface, | ||
) | ||
|
||
return np.sqrt(np.mean(np.square(qe_measured.outputs - qe_fit))).ndarray | ||
|
||
thickness_oxide_guess = 50 * u.AA | ||
thickness_implant_guess = 2317 * u.AA | ||
thickness_substrate_guess = 7 * u.um | ||
cce_backsurface_guess = 0.21 | ||
|
||
fit = scipy.optimize.minimize( | ||
fun=eqe_rms_difference, | ||
x0=[ | ||
thickness_oxide_guess.to_value(unit_thickness_oxide), | ||
thickness_implant_guess.to_value(unit_thickness_implant), | ||
thickness_substrate_guess.to_value(unit_thickness_substrate), | ||
cce_backsurface_guess, | ||
], | ||
method="nelder-mead", | ||
) | ||
|
||
thickness_oxide, thickness_implant, thickness_substrate, cce_backsurface = fit.x | ||
thickness_oxide = thickness_oxide << unit_thickness_oxide | ||
thickness_implant = thickness_implant << unit_thickness_implant | ||
thickness_substrate = thickness_substrate << unit_thickness_substrate | ||
|
||
return dict( | ||
thickness_oxide=thickness_oxide, | ||
thickness_implant=thickness_implant, | ||
thickness_substrate=thickness_substrate, | ||
cce_backsurface=cce_backsurface, | ||
) | ||
|
||
@property | ||
def thickness_oxide(self) -> u.Quantity: | ||
return self._quantum_efficiency_fit["thickness_oxide"] | ||
|
||
@property | ||
def thickness_implant(self) -> u.Quantity: | ||
return self._quantum_efficiency_fit["thickness_implant"] | ||
|
||
@property | ||
def thickness_substrate(self) -> u.Quantity: | ||
return self._quantum_efficiency_fit["thickness_substrate"] | ||
|
||
@property | ||
def cce_backsurface(self) -> float: | ||
return self._quantum_efficiency_fit["cce_backsurface"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import pytest | ||
import optika | ||
from .._materials_test import AbstractTestAbstractBackilluminatedCCDMaterial | ||
|
||
|
||
@pytest.mark.parametrize( | ||
argnames="a", | ||
argvalues=[ | ||
optika.sensors.E2VCCD97Material(), | ||
], | ||
) | ||
class TestE2VCCD97Material( | ||
AbstractTestAbstractBackilluminatedCCDMaterial, | ||
): | ||
pass |
33 changes: 33 additions & 0 deletions
33
optika/sensors/_materials/_e2v_ccd97/e2v_ccd97_qe_moody2017.csv
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
39.91891990062515, 0.6788079470198677 | ||
50.05075223132972, 0.7324503311258279 | ||
59.97856496789139, 0.8178807947019869 | ||
64.77313122091705, 0.8258278145695366 | ||
69.95096548922383, 0.8695364238410598 | ||
74.86231645365851, 0.8059602649006624 | ||
79.75688300572139, 0.8317880794701988 | ||
89.71162491843556, 0.851655629139073 | ||
97.76351712475721, 0.8576158940397353 | ||
109.96573146388018, 0.5317880794701989 | ||
130.00166645736314, 0.45033112582781476 | ||
149.57280983041403, 0.4860927152317882 | ||
199.79719692884856, 0.5635761589403975 | ||
249.37710648876907, 0.6529801324503313 | ||
309.8554473373301, 0.7225165562913909 | ||
399.1891990062509, 0.8357615894039737 | ||
500.50752231329665, 0.9052980132450333 | ||
547.9025729451029, 0.9211920529801326 | ||
568.0943005681785, 0.9271523178807949 | ||
599.7856496789133, 0.9291390728476823 | ||
647.7313122091703, 0.960927152317881 | ||
748.6231645365851, 0.9807947019867551 | ||
901.183765633706, 0.9986754966887419 | ||
1198.354915639953, 0.9907284768211922 | ||
1397.6006827213541, 0.8894039735099339 | ||
1593.5201660325251, 0.7741721854304637 | ||
1697.7059683717916, 0.6907284768211922 | ||
1800.5399029139885, 0.6569536423841061 | ||
1825.142004033248, 0.7463576158940399 | ||
1833.4171803334832, 0.8735099337748347 | ||
1833.4171803334832, 0.9172185430463577 | ||
1900.9836823956464, 0.947019867549669 | ||
1850.0802617566994, 0.9748344370860929 |
Oops, something went wrong.