coronagraphoto.simulation
=========================

.. py:module:: coronagraphoto.simulation

.. autoapi-nested-parse::

   Functions for running full simulations and processing sources.

   Public API conventions:

   - ``<source>_rate(source, optical_path, *, ...)`` returns the noiseless
     per-pixel photo-electron rate on the detector for one source.
   - ``<source>_readout(source, optical_path, prng_key, *, ...)`` returns a
     noisy detector readout (photon Poisson + QE binomial) for one source.
   - ``system_rate(scene, optical_path, *, ...)`` sums every per-source rate
     map for a scene (the differentiable forward model).
   - ``system_readout(scene, optical_path, prng_key, *, ...)`` sums every
     per-source Poisson-realised readout for a scene.

   All observation parameters (``start_time_jd``, ``exposure_time_s``,
   ``wavelength_nm``, ``bin_width_nm``, ``telescope_pa_deg``,
   ``ecliptic_lat_deg``, ``solar_lon_deg``) are kwarg-only. The convention
   keeps signatures discoverable when more parameters land later (IFS,
   multi-roll observations).



Functions
---------

.. autoapisummary::

   coronagraphoto.simulation.pre_coro_bin_processing
   coronagraphoto.simulation._resample_to_detector
   coronagraphoto.simulation.post_coro_bin_processing
   coronagraphoto.simulation.star_rate
   coronagraphoto.simulation.star_readout
   coronagraphoto.simulation.planet_rate
   coronagraphoto.simulation.planet_readout
   coronagraphoto.simulation._convolve_quadrants
   coronagraphoto.simulation.disk_rate
   coronagraphoto.simulation.disk_readout
   coronagraphoto.simulation.zodi_rate
   coronagraphoto.simulation.zodi_readout
   coronagraphoto.simulation.system_rate
   coronagraphoto.simulation.system_readout


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

.. py:function:: pre_coro_bin_processing(flux, bin_center_nm, bin_width_nm, optical_path)

   Process a bin through the pre-coro elements of the optical path.


.. py:function:: _resample_to_detector(image_rate_coro, bin_center_nm, optical_path)

   Resample a coronagraph-plane image onto the detector pixel grid.

   Pipeline geometry, not detector hardware: needs the coronagraph's
   plate scale (lambda/D / px), the detector's plate scale (arcsec/px),
   the wavelength, and the primary diameter to convert lambda/D to
   arcsec.


.. py:function:: post_coro_bin_processing(image_rate_coro, bin_center_nm, optical_path)

   Process a bin through the post-coro elements of the optical path.


.. py:function:: star_rate(star, optical_path, *, start_time_jd, wavelength_nm, bin_width_nm)

   Generate the star count rate on the detector.


.. py:function:: star_readout(star, optical_path, prng_key, *, start_time_jd, exposure_time_s, wavelength_nm, bin_width_nm)

   Process a star through the provided optical path.


.. py:function:: planet_rate(planet, optical_path, *, start_time_jd, wavelength_nm, bin_width_nm, telescope_pa_deg, star, trig_solver)

   Generate the per-batch planet count rate on the detector.

   Operates on a single ``skyscapes.scene.Planet`` (which internally
   batches K planets sharing the same atmosphere class). The Python
   loop over a heterogeneous ``System.planets`` tuple lives in
   :func:`system_readout`; this function stays inside the per-Planet-type
   JIT cache boundary (see ``brain/Planet Loop Architecture.md``).


.. py:function:: planet_readout(planet, optical_path, prng_key, *, start_time_jd, exposure_time_s, wavelength_nm, bin_width_nm, telescope_pa_deg, star, trig_solver)

   Process a per-batch Planet through the optical path.


.. py:function:: _convolve_quadrants(flux, psf_datacube)

   Convolve flux with a quarter-symmetric PSF datacube via fold-and-sum.

   Handles padding dynamically to ensure all quadrants match the shape of the
   first quadrant (which defines the PSF datacube shape).


.. py:function:: disk_rate(disk, optical_path, *, start_time_jd, wavelength_nm, bin_width_nm, telescope_pa_deg, star, incl_deg, pa_deg)

   Generate the disk count rate on the detector.

   Disks return CONTRAST (dimensionless flux ratio relative to the host
   star); we multiply by ``star.spec_flux_density`` here to convert to
   photon flux density per pixel before resampling and PSF convolution.

   ``incl_deg`` / ``pa_deg`` are the disk's intrinsic orientation in the
   sky frame; ``telescope_pa_deg`` is the telescope's roll. The disk is
   rendered at its intrinsic geometry, then resample_flux rotates the
   rendered image by ``-telescope_pa_deg`` into the detector frame.

   Raises:
       ValueError: if ``optical_path.coronagraph.psf_datacube`` is
           ``None``.


.. py:function:: disk_readout(disk, optical_path, prng_key, *, start_time_jd, exposure_time_s, wavelength_nm, bin_width_nm, telescope_pa_deg, star, incl_deg, pa_deg)

   Process a disk through the provided optical path.

   ``incl_deg`` / ``pa_deg`` are the disk's intrinsic sky-frame
   orientation; ``system_readout`` pulls them from
   ``scene.system.midplane_inc_deg`` / ``midplane_pa_deg`` so every
   disk component in the System renders at the same midplane.


.. py:function:: zodi_rate(zodi, optical_path, *, start_time_jd, wavelength_nm, bin_width_nm, ecliptic_lat_deg, solar_lon_deg)

   Generate the zodi count rate on the detector.

   Treats zodi as a spatially uniform surface-brightness source. The
   coronagraph's sky transmission map sets the per-pixel attenuation;
   no PSF convolution is needed (a flat field convolved with any
   normalised PSF returns itself).


.. py:function:: zodi_readout(zodi, optical_path, prng_key, *, start_time_jd, exposure_time_s, wavelength_nm, bin_width_nm, ecliptic_lat_deg, solar_lon_deg)

   Process a zodi source through the provided optical path.


.. py:function:: system_rate(scene, optical_path, *, start_time_jd, wavelength_nm, bin_width_nm, telescope_pa_deg, ecliptic_lat_deg, solar_lon_deg)

   Sum of deterministic per-source count rates for a :class:`~skyscapes.Scene`.

   The differentiable companion to :func:`system_readout`. Returns the
   total rate map (electrons/s/pixel, no Poisson noise, no QE multiply)
   summing star, every planet, the optional disk, and the optional zodi.
   Use this for likelihood evaluation, retrievals, or any inference loop
   that needs gradients through the full forward model.


.. py:function:: system_readout(scene, optical_path, prng_key, *, start_time_jd, exposure_time_s, wavelength_nm, bin_width_nm, telescope_pa_deg, ecliptic_lat_deg, solar_lon_deg)

   Simulate a full :class:`~skyscapes.Scene` through the optical path.

   Sums per-source detector readouts. Each source consumes its own
   independent PRNG subkey (see :mod:`jax.random` best practices).

   The Python loop over ``scene.system.planets`` is intentionally
   unjitted -- it orchestrates JIT-cached per-Planet-type kernels (see
   ``brain/Planet Loop Architecture.md``). The expensive math is inside
   each ``planet_readout`` call, not the loop.


