fit_distortion_scan#

esis.optics.fit_distortion_scan(instrument, scene, observation, grids, parameters=None, pupil=None, axis_wavelength=None, axis_field=None, axis_channel=None, coherent=False, smoothing=None, sigma_psf=1.0, seed=0, tolerance=None, num_repeat=8, directory=None)[source]#

Fit the distortion parameters of an instrument by scanning the merit.

A derivative-free coordinate search: for each round in grids, every listed parameter is scanned along the given offsets while the others are held at their current best, and the parameter is moved to the peak of the sampled correlation curve, refined with a three-point parabola. Rounds are typically coarse-to-fine, so early rounds capture the solution and later rounds polish it.

If axis_channel is given, the correlation of each channel is recorded separately during every scan, and each channel’s peak is read off its own curve. Because a channel’s correlation depends only on that channel’s parameters, one scan pass fits all channels simultaneously, and the fitted parameters gain an axis_channel axis.

This is the production fitting engine: unlike scipy.optimize.least_squares() driving a DistortionResidual, which was found to under-converge on the locally-flat correlation surface of realistic scenes, the scans read the shape of the merit basin directly and are robust as long as the true solution lies within the coarsest scan range (measured to be at least \(\pm 10''\) in pointing for the ESIS-I flight data).

Parameters:
  • instrument (AbstractInstrument) – The instrument model to fit.

  • scene (FunctionArray) – The spectral radiance of the scene as a function of wavelength and field position, imaged through the instrument on every evaluation.

  • observation (AbstractScalar) – The observed image that the modeled images are compared against. Any axes of the modeled image which are not present in this array are summed over before comparing.

  • grids (Sequence[dict[str, Quantity]]) – The scan schedule: a sequence of rounds, each a mapping from a field name of DistortionParameters to a one-dimensional Quantity of offsets (relative to the current best) to scan, for example [dict(pitch=np.linspace(-10, 10, 21) * u.arcsec)]. Strongly-coupled fields can be scanned jointly by using a tuple of field names as the key and a matching tuple of offset grids as the value, for example {("pitch_grating", "pitch"): (grid_a, grid_b)}, which scans the full outer product and reads the joint peak — independent scans of such pairs converge to a compensating local optimum instead of the true solution.

  • parameters (None | DistortionParameters) – The starting point of the fit. If None, the current parameters of instrument are used.

  • pupil (None | AbstractCartesian2dVectorArray) – The vertices of the pupil grid used to image the scene.

  • axis_wavelength (None | str) – The logical axis of the scene corresponding to changing wavelength.

  • axis_field (None | tuple[str, str]) – The logical axes of the scene corresponding to changing field position.

  • axis_channel (None | str) – The logical axis of the observation corresponding to changing camera channel. If given, each channel is standardized independently and each channel’s peak is read from its own correlation curve.

  • coherent (bool) – If True, the per-channel correlation curves are averaged before locating the peak, so that every channel receives the same offset (a rigid-payload model). Requires axis_channel, which controls the per-channel standardization of the merit.

  • smoothing (None | int) – The width, in detector pixels, of a box filter applied to both images before comparing.

  • sigma_psf (None | float) – The standard deviation, in detector pixels, of a Gaussian point-spread function convolved with the modeled image only. See DistortionResidual.sigma_psf.

  • seed (int) – The seed used to make each evaluation deterministic. See DistortionResidual.seed.

  • tolerance (None | float) – If given, the last round of grids is repeated until the mean correlation improves by less than this amount, at most num_repeat extra rounds.

  • num_repeat (int) – The maximum number of extra polish rounds appended when tolerance is given.

  • directory (None | Path) – A directory where the scan curves and convergence history are written as scan.json and scan.log. If None, the fit is not logged.

Raises:

ValueError – If a joint entry of grids has a different number of fields and offset grids.

Return type:

DistortionParameters

Examples

Fit the per-frame payload pointing of the ESIS flight-1 model to a Level-1 frame.

import numpy as np
import astropy.units as u
import esis

obs = esis.flights.f1.data.level_1()[dict(time=0)]
scene = ...  # an AIA scene resampled to the frame's timestamp

instrument = esis.flights.f1.optics.distortion_fit(num_distribution=0)

fitted = esis.optics.fit_distortion_scan(
    instrument=instrument,
    scene=scene,
    observation=obs.outputs.value,
    grids=[
        dict(
            pitch=np.linspace(-10, 10, 21) * u.arcsec,
            yaw=np.linspace(-10, 10, 21) * u.arcsec,
            roll=np.linspace(-0.4, 0.4, 11) * u.deg,
        ),
        dict(
            pitch=np.linspace(-1, 1, 11) * u.arcsec,
            yaw=np.linspace(-1, 1, 11) * u.arcsec,
            roll=np.linspace(-0.05, 0.05, 11) * u.deg,
        ),
    ],
    axis_wavelength="velocity",
    axis_field=("detector_x", "detector_y"),
    sigma_psf=1.0,
)