Source code for ctis._arange

"""
Evenly-spaced sequences for building CTIS coordinate grids.
"""

import numpy as np
import astropy.units as u
import named_arrays as na

__all__ = [
    "arange",
]


[docs] def arange( start: "float | u.Quantity | na.AbstractVectorArray", stop: "float | u.Quantity | na.AbstractVectorArray", axis: "str | na.AbstractVectorArray", step: "float | u.Quantity | na.AbstractVectorArray" = 1, ) -> "na.AbstractArray": """ Return evenly-spaced values over a range, centered within it. This is the centered counterpart of :func:`named_arrays.arange`, which starts exactly at `start` and leaves the remainder of the range as a gap on the right. :func:`ctis.arange` fits as many samples spaced by `step` as possible into ``[start, stop]`` and centers them, splitting the leftover evenly between the two ends. This is convenient for building a coordinate grid with a fixed pitch that is centered on a field of view rather than biased toward one edge. Unlike :func:`named_arrays.arange`, this is built on :func:`named_arrays.linspace`, so it works correctly when the arguments are :class:`astropy.units.Quantity` instances. If `start`, `stop`, and `step` are instances of :class:`named_arrays.AbstractVectorArray`, each component is centered independently along its own axis (given by the matching component of `axis`), producing an outer-product grid. Parameters ---------- start The lower bound of the range. stop The upper bound of the range. The samples are centered within ``[start, stop]``, so `start` and `stop` are samples only when `step` divides the range evenly. axis The name of the new logical axis of the result. If `start`, `stop`, and `step` are vectors, this should be a vector of axis names, one per component. step The spacing between adjacent samples. Examples -------- :func:`named_arrays.arange` starts at `start`, leaving the remainder of the range as a gap on the right. .. jupyter-execute:: import named_arrays as na import ctis na.arange(0, 10, axis="x", step=3) :func:`ctis.arange` uses the same samples and step, but splits that remainder evenly between the two ends of the range. .. jupyter-execute:: ctis.arange(0, 10, axis="x", step=3) Vector arguments produce a centered grid, with each component sampled independently along its own axis. .. jupyter-execute:: import astropy.units as u ctis.arange( start=na.Cartesian2dVectorArray(-10, -8) * u.arcsec, stop=na.Cartesian2dVectorArray(10, 8) * u.arcsec, axis=na.Cartesian2dVectorArray("x", "y"), step=na.Cartesian2dVectorArray(6, 5) * u.arcsec, ) """ # the number of samples spaced by `step` that fit within the range, # computed per-component so vector arguments produce a grid. num = na.as_named_array((stop - start) / step) num = (np.floor(num + 1e-10) + 1).astype(int) if not isinstance(num, na.AbstractVectorArray): # na.linspace requires a Python int for the count of a scalar axis num = int(num.ndarray) # center the samples by splitting the leftover evenly between both ends span = (num - 1) * step offset = (stop - start - span) / 2 return na.linspace(start + offset, stop - offset, axis=axis, num=num)