solprop.processing.spectra#
Spectral post-processing: instrument resolution and explicit bandpass windows.
Two operations that belong to the spectrum rather than to the accessor that fetched it: smoothing a spectral energy density to the resolution of a real instrument, and building the Planck-tapered window of a bandpass.
Neither is ever applied on its own. A window is a keyword: a wavelength range, a
Passband on any spectral axis, or a ready-made array, and the array it produces
is returned to the caller to multiply; no window adjusts its own limits as a side
channel.
Module Attributes
A wavelength range, a |
|
Widest default bandpass edge, in frequency bins (Luna's |
|
Widest default bandpass edge as a fraction of the pass band's angular width. |
Functions
|
Return a Planck-tapered bandpass window on the stored frequency axis. |
|
Smooth a spectral density to an instrument resolution on a uniform axis. |
Classes
|
A bandpass given on any spectral axis, for an accessor's |
- solprop.processing.spectra.Bandpass = tuple[float, float] | solprop.processing.spectra.Passband | jaxtyping.Float[ndarray, 'n_omega']#
A wavelength range, a
Passbandon any axis, or a ready-made window.
- solprop.processing.spectra.DEFAULT_BANDPASS_BINS = 64#
Widest default bandpass edge, in frequency bins (Luna’s
winwidth = :auto).
- solprop.processing.spectra.DEFAULT_BANDPASS_FRACTION = 0.25#
Widest default bandpass edge as a fraction of the pass band’s angular width.
Each Planck edge is antisymmetric about its midpoint, so it transmits half its width: a window with edges
ftimes the band’s width has an integrated width1 + ftimes the band’s. A quarter keeps that excess to 25 % while each edge still spans a quarter of the band, so the window stays smooth on the band’s own scale; and it leaves every band wider than64 / 0.25 = 256bins on the 64-bin edge it always had.
- class solprop.processing.spectra.Passband(limits, axis='wavelength', width=None)[source]#
Bases:
objectA bandpass given on any spectral axis, for an accessor’s
bandpasskeyword.A bare pair of numbers is always a wavelength range; this says which axis its limits are on, so that a band can be written in hertz or in rad/s, and a low-pass band can start at zero frequency, which no wavelength can say.
- Attributes:
- limitstuple of float
The pass band, in m, Hz or rad/s as
axissays, in either order.- axis{‘wavelength’, ‘f’, ‘omega’}
The axis of
limits.- widthfloat or None
Width of each taper edge, rad/s, whatever the axis: the taper is drawn on the angular-frequency grid.
Nonegives the default ofbandpass().
- Raises:
- solprop.exceptions.ProcessingError
If the axis is unknown, a wavelength limit is not positive, or a frequency limit is negative.
Examples
>>> from solprop.processing._examples import example_output >>> from solprop.processing.spectra import Passband >>> from solprop.processing.time import energy >>> red = energy(example_output(), plane=0, bandpass=Passband((0.0, 375e12), "f")) >>> 0.0 < float(red) < float(energy(example_output(), plane=0)) True
- solprop.processing.spectra.bandpass(grid, limits, *, axis='wavelength', width=None)[source]#
Return a Planck-tapered bandpass window on the stored frequency axis.
The window is one inside the range and zero outside it, joined on each side by a Planck taper of the given width in angular frequency. The default edge is the narrower of
DEFAULT_BANDPASS_BINS(64) bin spacings andDEFAULT_BANDPASS_FRACTION(a quarter) of the pass band’s angular width, so a narrow band on a short time window is not swamped by edges wider than itself. It is returned rather than applied, because nothing insolprop.processingwindows a spectrum unless it was asked to.- Parameters:
- gridsolprop.grid.TimeGrid
The grid whose
omegaaxis the window is built on.- limitstuple of float
The pass band, in m, Hz or rad/s as
axissays, in either order.- axis{‘wavelength’, ‘f’, ‘omega’}
The axis of
limits; wavelength by default.- widthfloat or None
Width of each taper edge, rad/s;
Nonegives the smaller of 64 bin spacings and a quarter of the pass band’s angular width.
- Returns:
- numpy.ndarray
Window values in
[0, 1], shape(n_omega,), in storage order.
- Raises:
- solprop.exceptions.ProcessingError
If the axis is unknown, a wavelength limit is not positive, a frequency limit is negative, or the width is not positive – including the default width of an empty pass band (equal limits).
Examples
>>> import numpy as np >>> from solprop.processing._examples import example_grid >>> from solprop.processing.spectra import bandpass >>> grid = example_grid() >>> window = bandpass(grid, (700e-9, 900e-9)) >>> float(window.max()), float(window.min()) (1.0, 0.0) >>> from solprop.constants import C >>> same = bandpass(grid, (C / 900e-9, C / 700e-9), axis="f") >>> bool(np.allclose(window, same)) True
- solprop.processing.spectra.specres(x, density, resolution, *, weights=None, spec_range=None, samples=10)[source]#
Smooth a spectral density to an instrument resolution on a uniform axis.
The output axis is uniform with
samplespoints per resolution element, and each input bin is spread over it by a unit-area Gaussian whose full width at half maximum isresolution, weighted by the width of the bin it came from:S_out(x_j) = sum_k S(x_k) w_k G(x_j - x_k).Because the kernel integrates to one, the integral of the density – the energy – is preserved to round-off wherever the signal is well inside the range. The input axis need not be uniform, which is what makes this work directly on a wavelength axis derived from a uniform frequency grid: pass the bin widths as
weights.Non-positive output values are not replaced with the smallest positive one to keep a logarithmic plot away from zero: that would change the data.
- Parameters:
- xnumpy.ndarray
Input abscissa, shape
(n_x,), strictly increasing.- densitynumpy.ndarray
Spectral density on that abscissa, shape
(..., n_x), per unit ofx.- resolutionfloat
Instrument resolution as a full width at half maximum, in the units of
x.- weightsnumpy.ndarray or None
Integration measure of each input bin, in the units of
x;Noneuses the central differences ofx.- spec_rangetuple of float or None
Limits of the output axis, clipped to the range of
x;Noneuses all of it.- samplesint
Output points per resolution element, at least one.
- Returns:
- x_outnumpy.ndarray
Uniform output abscissa, shape
(n_out,).- density_outnumpy.ndarray
The smoothed density, shape
(..., n_out).
- Raises:
- solprop.exceptions.ProcessingError
If the resolution or the sampling is not positive, if
xdoes not increase, or if the requested range does not overlap the data.
Examples
>>> import numpy as np >>> from solprop.processing.spectra import specres >>> x = np.linspace(400e-9, 1200e-9, 2001) >>> density = np.exp(-0.5 * ((x - 800e-9) / 20e-9) ** 2) >>> smooth_x, smooth = specres(x, density, 50e-9) >>> bool(smooth.max() < density.max()) True