optixstuff.detector
===================

.. py:module:: optixstuff.detector

.. autoapi-nested-parse::

   Detector abstractions and concrete implementations.



Classes
-------

.. autoapisummary::

   optixstuff.detector.AbstractDetector
   optixstuff.detector.IdealDetector
   optixstuff.detector.Detector


Functions
---------

.. autoapisummary::

   optixstuff.detector.dark_current
   optixstuff.detector.clock_induced_charge
   optixstuff.detector.read_noise


Module Contents
---------------

.. py:class:: AbstractDetector

   Bases: :py:obj:`equinox.Module`


   Abstract interface for a focal-plane detector.

   Provides both scalar noise rates (for ETC use) and stochastic noise
   realization (for image simulation).  All concrete implementations
   must define the hardware parameters listed as ``AbstractVar`` fields.


   .. py:attribute:: pixel_scale_arcsec
      :type:  equinox.AbstractVar[float]

      Detector plate scale in arcsec/pixel.



   .. py:attribute:: quantum_efficiency
      :type:  equinox.AbstractVar[float]

      Baseline quantum efficiency as a fraction in [0, 1].



   .. py:attribute:: dark_current_rate_e_per_s
      :type:  equinox.AbstractVar[float]

      Dark current rate in electrons/pixel/second.



   .. py:attribute:: read_noise_e
      :type:  equinox.AbstractVar[float]

      Read noise in electrons RMS per pixel per read.



   .. py:attribute:: clock_induced_charge_rate_e_per_frame
      :type:  equinox.AbstractVar[float]

      Clock-induced charge in electrons/pixel/frame.



   .. py:attribute:: frame_time_s
      :type:  equinox.AbstractVar[float]

      Integration time per frame/read in seconds.



   .. py:attribute:: read_time_s
      :type:  equinox.AbstractVar[float]

      Time per read cycle in seconds (for RN^2/t_read in ETC).



   .. py:attribute:: dqe
      :type:  equinox.AbstractVar[float]

      QE degradation factor (multiplicative correction over mission life).



   .. py:attribute:: shape
      :type:  equinox.AbstractVar[tuple[int, int]]

      Detector dimensions (ny, nx) in pixels.



   .. py:method:: get_qe(wavelength_nm)
      :abstractmethod:


      Quantum efficiency at a given wavelength.

      Args:
          wavelength_nm: Wavelength in nanometres.

      Returns:
          QE as a fraction in [0, 1].



   .. py:method:: scalar_noise_rate(n_pix, t_photon)
      :abstractmethod:


      Total scalar noise variance rate for the ETC.

      Returns the combined noise variance per unit time (electrons^2/s)
      for a photometric aperture of n_pix pixels.

      Args:
          n_pix: Number of pixels in the photometric aperture.
          t_photon: Photon counting integration time in seconds.

      Returns:
          Noise variance rate in electrons^2/second.



   .. py:method:: readout(image_rate, exposure_time_s, prng_key)
      :abstractmethod:


      Apply stochastic noise realization to a photon rate image.

      Converts photon rates to detected electrons including QE,
      dark current, CIC, and read noise.

      Args:
          image_rate: Incident photon rate array in ph/s/pixel.
          exposure_time_s: ExposureConfig time in seconds.
          prng_key: JAX PRNG key (required, no default).

      Returns:
          Detected electrons array, same shape as image_rate.



   .. py:method:: readout_source_electrons(image_rate, exposure_time_s, prng_key)

      Poisson-sample incident photons and convert to electrons via QE.

      This is the source-dependent half of detector readout: photon
      arrival statistics and quantum-efficiency selection. Dark current,
      CIC, and read noise are handled separately by
      :meth:`readout_noise_electrons` so that multi-source exposures do not
      double-count the source-independent noise floor.

      Args:
          image_rate: Incident photon rate array in ph/s/pixel.
          exposure_time_s: ExposureConfig time in seconds.
          prng_key: JAX PRNG key.

      Returns:
          Photo-electron counts, same shape as image_rate.



   .. py:method:: readout_source_electrons_thinned(image_rate, exposure_time_s, prng_key)

      Fast equivalent of :meth:`readout_source_electrons` via Poisson thinning.

      Distributionally identical to the explicit Poisson-then-Binomial
      chain (Poisson thinning theorem: ``Binomial(Poisson(L), p) ~
      Poisson(L * p)``), but ~3x faster because it skips the Binomial
      draw and never materialises the intermediate photon count. Use in
      performance-critical paths (animation rendering, yield runs)
      when the photon count is not needed downstream.

      The marginal distribution of returned electrons is identical to
      :meth:`readout_source_electrons`. The two methods produce
      different specific realisations even with the same key.

      Args:
          image_rate: Incident photon rate array in ph/s/pixel.
          exposure_time_s: ExposureConfig time in seconds.
          prng_key: JAX PRNG key.

      Returns:
          Photo-electron counts, same shape as image_rate.



   .. py:method:: noise_variance(image_rate, exposure_time_s)

      Deterministic per-pixel total noise variance in electrons^2.

      The expected variance at each pixel for an incident photon-rate image
      -- the deterministic companion to :meth:`readout`. Combines source shot
      noise (Poisson on detected electrons) with the source-independent floor
      (dark current, clock-induced charge, read noise)::

          N = QE * rate * t
              + dark_rate * t
              + CIC_rate * n_frames
              + read_noise^2 * n_frames

      with ``n_frames = ceil(t / frame_time_s)``. Use ``1 / noise_variance(...)``
      as inverse-variance weights for least-squares spectral extraction or its
      GLS covariance, where the shot term carries the wavelength dependence.

      Args:
          image_rate: Incident photon rate [ph/s/pixel], any shape.
          exposure_time_s: Exposure time in seconds.

      Returns:
          Per-pixel noise variance [electrons^2], same shape as image_rate.



   .. py:method:: readout_noise_electrons(exposure_time_s, prng_key)
      :abstractmethod:


      Source-independent detector noise (dark + CIC + read).

      Each call draws a fresh noise realization of shape ``self.shape``;
      consumers add exactly one such draw per exposure regardless of
      how many sources were co-added via :meth:`readout_source_electrons`.

      Args:
          exposure_time_s: ExposureConfig time in seconds.
          prng_key: JAX PRNG key.

      Returns:
          Noise-electron array of shape ``self.shape``.



.. py:function:: dark_current(dark_current_rate_e_per_s, exposure_time_s, shape, prng_key)

   Draw dark current electrons from a Poisson distribution.

   Args:
       dark_current_rate_e_per_s: Dark current rate in electrons/s/pixel.
       exposure_time_s: ExposureConfig time in seconds.
       shape: Detector shape (ny, nx).
       prng_key: PRNG key.

   Returns:
       Dark current electrons, shape (ny, nx).


.. py:function:: clock_induced_charge(clock_induced_charge_rate_e_per_frame, num_frames, shape, prng_key)

   Draw clock-induced charge electrons from a Poisson distribution.

   Args:
       clock_induced_charge_rate_e_per_frame: CIC rate in electrons/pixel/frame.
       num_frames: Number of frames (kept as float for JIT safety).
       shape: Detector shape (ny, nx).
       prng_key: PRNG key.

   Returns:
       CIC electrons, shape (ny, nx).


.. py:function:: read_noise(read_noise_e, num_frames, shape, prng_key)

   Draw read noise from a Gaussian distribution.

   Total read noise sigma = sqrt(num_frames) * read_noise_per_read.

   Args:
       read_noise_e: Read noise in electrons/pixel/read.
       num_frames: Number of frames.
       shape: Detector shape (ny, nx).
       prng_key: PRNG key.

   Returns:
       Read noise electrons, shape (ny, nx).


.. py:class:: IdealDetector(pixel_scale_arcsec, shape, quantum_efficiency = 1.0, dark_current_rate_e_per_s = 0.0, read_noise_e = 0.0, clock_induced_charge_rate_e_per_frame = 0.0, frame_time_s = 1.0, read_time_s = 0.05, dqe = 0.0)

   Bases: :py:obj:`AbstractDetector`


   Detector with constant QE and minimal noise sources.

   Suitable for broadband imager studies where wavelength-dependent
   QE variation is not important and CIC/read noise are negligible.


   .. py:attribute:: pixel_scale_arcsec
      :type:  float

      Detector plate scale in arcsec/pixel.



   .. py:attribute:: quantum_efficiency
      :type:  float

      Baseline quantum efficiency as a fraction in [0, 1].



   .. py:attribute:: dark_current_rate_e_per_s
      :type:  float

      Dark current rate in electrons/pixel/second.



   .. py:attribute:: read_noise_e
      :type:  float

      Read noise in electrons RMS per pixel per read.



   .. py:attribute:: clock_induced_charge_rate_e_per_frame
      :type:  float

      Clock-induced charge in electrons/pixel/frame.



   .. py:attribute:: frame_time_s
      :type:  float

      Integration time per frame/read in seconds.



   .. py:attribute:: read_time_s
      :type:  float

      Time per read cycle in seconds (for RN^2/t_read in ETC).



   .. py:attribute:: dqe
      :type:  float

      QE degradation factor (multiplicative correction over mission life).



   .. py:attribute:: shape
      :type:  tuple[int, int]

      Detector dimensions (ny, nx) in pixels.



   .. py:method:: get_qe(wavelength_nm)

      Return constant QE, ignoring wavelength.



   .. py:method:: scalar_noise_rate(n_pix, t_photon)

      Combined dark + CIC noise variance rate.

      Read noise is not included here as it scales per-read, not per-second.
      Callers add (read_noise_e^2 * n_reads) / t_exp separately.



   .. py:method:: readout_noise_electrons(exposure_time_s, prng_key)

      Dark current only -- IdealDetector has no CIC or read noise.



   .. py:method:: readout(image_rate, exposure_time_s, prng_key)

      Full detector readout: source electrons + dark current.



   .. py:method:: __repr__()

      One-line summary of shape, plate scale, QE, and dark current.



.. py:class:: Detector(pixel_scale_arcsec, shape, quantum_efficiency = 1.0, dark_current_rate_e_per_s = 0.0, read_noise_e = 0.0, clock_induced_charge_rate_e_per_frame = 0.0, frame_time_s = 1.0, read_time_s = 0.05, dqe = 0.0)

   Bases: :py:obj:`AbstractDetector`


   Full detector model with dark current, CIC, and read noise.

   Suitable for detailed noise simulations where all detector noise
   sources matter.  Uses Poisson statistics for dark/CIC and Gaussian
   for read noise, matching the coronagraphoto convention.

   Warning: ``num_frames = jnp.ceil(exposure_time_s / frame_time_s)`` is
   kept as a float. Never cast it to int inside JIT -- that triggers a
   ConcretizationTypeError when exposure_time_s is traced.


   .. py:attribute:: pixel_scale_arcsec
      :type:  float

      Detector plate scale in arcsec/pixel.



   .. py:attribute:: quantum_efficiency
      :type:  float

      Baseline quantum efficiency as a fraction in [0, 1].



   .. py:attribute:: dark_current_rate_e_per_s
      :type:  float

      Dark current rate in electrons/pixel/second.



   .. py:attribute:: read_noise_e
      :type:  float

      Read noise in electrons RMS per pixel per read.



   .. py:attribute:: clock_induced_charge_rate_e_per_frame
      :type:  float

      Clock-induced charge in electrons/pixel/frame.



   .. py:attribute:: frame_time_s
      :type:  float

      Integration time per frame/read in seconds.



   .. py:attribute:: read_time_s
      :type:  float

      Time per read cycle in seconds (for RN^2/t_read in ETC).



   .. py:attribute:: dqe
      :type:  float

      QE degradation factor (multiplicative correction over mission life).



   .. py:attribute:: shape
      :type:  tuple[int, int]

      Detector dimensions (ny, nx) in pixels.



   .. py:method:: get_qe(wavelength_nm)

      Return constant QE, ignoring wavelength.



   .. py:method:: scalar_noise_rate(n_pix, t_photon)

      Combined dark + CIC noise variance rate.



   .. py:method:: readout_noise_electrons(exposure_time_s, prng_key)

      Dark current + CIC + read noise (source-independent).



   .. py:method:: readout(image_rate, exposure_time_s, prng_key)

      Full detector readout: source electrons + all noise sources.



   .. py:method:: __repr__()

      One-line summary of shape, plate scale, QE, and noise sources.



