Source code for optika.sensors.materials.depletion._depletion

import abc
import dataclasses
import numpy as np
import scipy.optimize
import astropy.units as u
import named_arrays as na
import optika

__all__ = [
    "AbstractDepletionModel",
    "JanesickDepletionModel",
]


[docs] @dataclasses.dataclass(eq=False, repr=False) class AbstractDepletionModel( optika.mixins.Printable, optika.mixins.Shaped, ): """ An arbitrary model of the depletion region of a semiconducting imaging sensor. """ @property @abc.abstractmethod def thickness(self) -> u.Quantity: """The thickness of the depletion region."""
[docs] @dataclasses.dataclass(eq=False, repr=False) class JanesickDepletionModel( AbstractDepletionModel, ): """ A depletion model that relies on :cite:t:`Janesick2001`'s model of charge diffusion to measure the thickness of the field-free region of an imaging sensor and infer the thickness of the depletion region. """ thickness: u.Quantity | na.AbstractScalar = dataclasses.MISSING """The thickness of the depletion region of this sensor.""" thickness_substrate: u.Quantity | na.AbstractScalar = dataclasses.MISSING """The thickness of the light-sensitive region of this sensor.""" chemical_substrate: optika.chemicals.AbstractChemical = dataclasses.MISSING """A model of the optical properties of the substrate material.""" width_pixel: u.Quantity | na.AbstractScalar = dataclasses.MISSING """The physical size of a pixel on this sensor.""" mcc_measured: None | na.FunctionArray = None """ An optional measurement of the mean charge capture that can be used to compute residuals. """
[docs] @classmethod def fit_mcc( cls, thickness_substrate: u.Quantity | na.AbstractScalar, chemical_substrate: optika.chemicals.AbstractChemical, width_pixel: u.Quantity | na.AbstractScalar, mcc_measured: None | na.FunctionArray = None, ): """ Given a measured mean charge capture, find the thickness of the depletion region which best matches the measurements. Parameters ---------- thickness_substrate The thickness of the light-sensitive region of this sensor. chemical_substrate A model of the optical properties of the substrate material. width_pixel The physical size of a pixel on this sensor. mcc_measured The measured mean charge capture that will be fit by the function :func:`optika.sensors.mean_charge_capture`. """ absorption = chemical_substrate.absorption(mcc_measured.inputs) unit = u.um def objective(thickness_depletion: float) -> float: width_diffusion = optika.sensors.charge_diffusion( absorption=absorption, thickness_substrate=thickness_substrate, thickness_depletion=thickness_depletion * unit, ) mcc = optika.sensors.mean_charge_capture( width_diffusion=width_diffusion, width_pixel=width_pixel, ) diff = mcc_measured.outputs - mcc result = np.sqrt(np.mean(np.square(diff))).ndarray return result fit = scipy.optimize.minimize_scalar( fun=objective, bounds=( 0, thickness_substrate.to_value(unit), ), ) thickness = fit.x * unit return cls( thickness=thickness, thickness_substrate=thickness_substrate, chemical_substrate=chemical_substrate, width_pixel=width_pixel, mcc_measured=mcc_measured, )
[docs] def mean_charge_capture( self, wavelength: u.Quantity | na.AbstractScalar, ) -> na.AbstractScalar: """ The mean charge capture of this sensor for the given wavelength computed using :func:`optika.sensors.mean_charge_capture` Parameters ---------- wavelength The wavelengths at which to evaluate the mean charge capture. """ return optika.sensors.mean_charge_capture( width_diffusion=optika.sensors.charge_diffusion( absorption=self.chemical_substrate.absorption(wavelength), thickness_substrate=self.thickness_substrate, thickness_depletion=self.thickness, ), width_pixel=self.width_pixel, )
@property def shape(self) -> dict[str, int]: return na.broadcast_shapes( na.shape(self.thickness), na.shape(self.thickness_substrate), na.shape(self.width_pixel), na.shape(self.mcc_measured), )