virgil.spectra
Wavelength-dependent fluxes. A component's flux can be a number or a
spectrum; inside a System each component is weighted by its spectrum at each
sample's wavelength, as in SPARCO.
Wavelength-dependent fluxes for source components.
A component's flux is either a number (achromatic) or a spectrum from this
module, which gives its weight at each wavelength. Inside a
System the visibility is then
V(λ) = Σ f_i(λ) V_i / Σ f_i(λ), as in SPARCO (Kluska et al. 2014):
star = UniformDisk(0.5, flux=PowerLaw(1.0, index=-4.0, wavel0=1.65e-6))
ring = GaussianDisk(5.0, flux=BlackBody(0.3, temperature=1200.0))
wind = GaussianDisk(
2.0,
flux=Sum(
continuum=PowerLaw(0.3, wavel0=2.15e-6),
brg=GaussianLine(0.5, line_wavel=2.1661e-6, fwhm=1.0e-9),
),
)
free = PointSource(flux=Nodes(values, channel_wavelengths))
Spectrum parameters are reached by path like any other, e.g.
"ring.flux.temperature" or "wind.flux.brg.amplitude". Flux ratios are
relative: component i's fraction of the total at the reference
wavelength is f_i / Σ f. Every spectrum has a reference wavelength
wavel0, and its reference flux (spectrum(), used when rendering
images) is its value there. Only the evaluated flux must be non-negative:
a part of a Sum may be negative (absorption) as long
as the total is not, which
is_physical checks at wavel0,
the nodes and the line centres.
BlackBody
Bases: Spectrum
Planck spectrum ratio * B_λ(T, λ) / B_λ(T, wavel0).
A blackbody at temperature in F_λ, normalized to ratio at
wavel0, as SPARCO uses for dust and companions (Hillen et al. 2016,
https://arxiv.org/abs/1603.03023). At long wavelengths it tends to the
Rayleigh-Jeans PowerLaw with index -4.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ratio
|
float or array - like
|
Flux at the reference wavelength, relative to the other components. |
required |
temperature
|
float or array - like
|
Temperature in kelvin. |
required |
wavel0
|
float or array - like
|
Reference wavelength in metres (default 1.65e-6, H band). |
1.65e-06
|
Examples:
>>> round(float(BlackBody(0.2, 1500.0)(1.65e-6)), 6)
0.2
ratio = np.asarray(ratio, dtype=float)
instance-attribute
temperature = np.asarray(temperature, dtype=float)
instance-attribute
wavel0 = np.asarray(wavel0, dtype=float)
instance-attribute
__init__(ratio, temperature, wavel0=1.65e-06)
__call__(wavel=None)
is_physical()
__check_init__()
GaussianLine
Bases: _Line
A Gaussian emission or absorption line.
amplitude * exp(-4 ln 2 ((λ - line_wavel) / fwhm)²), with the full width
at half maximum fwhm. On its own a line is the whole flux of a
component, so amplitude must be non-negative; inside a
Sum with a continuum a negative amplitude is an
absorption line.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
amplitude
|
float
|
Flux at the line centre, relative to the other components. |
required |
line_wavel
|
float
|
Line centre in metres (shifted by any velocity: λ₀(1 + v/c)). |
required |
fwhm
|
float
|
Full width at half maximum in metres. |
required |
wavel0
|
float
|
Reference wavelength in metres; the line centre by default. |
None
|
Examples:
>>> line = GaussianLine(0.4, line_wavel=2.1661e-6, fwhm=1.0e-9)
>>> round(float(line(2.1661e-6 + 0.5e-9)), 4)
0.2
LorentzianLine
Bases: _Line
A Lorentzian emission or absorption line.
amplitude / (1 + 4 ((λ - line_wavel) / fwhm)²). Its wings fall off
slowly, so over a wide band it adds a near-constant pedestal. Parameters are
as for GaussianLine.
Examples:
>>> line = LorentzianLine(0.4, line_wavel=2.1661e-6, fwhm=1.0e-9)
>>> round(float(line(2.1661e-6 + 0.5e-9)), 4)
0.2
Nodes
Bases: Spectrum
A spectrum through free values at fixed wavelengths.
Linear or natural-cubic interpolation between the nodes. Beyond the end
nodes the flux is held at the end values (outside="constant") or a fixed
number (e.g. outside=0.0 for an excess that lives only in a line window,
on top of a continuum in a Sum; give the end nodes
the value 0 to keep the spectrum continuous). Fit the values with a
prior of their shape; a smooth spectrum is a prior on them, not a feature
of this class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values
|
(array - like, shape(n))
|
Flux at each node, relative to the other components. They may be negative (an absorption excess) as long as a component's total flux is not. |
required |
wavel
|
(array - like, shape(n))
|
Node wavelengths in metres: finite, positive and strictly increasing. |
required |
kind
|
(linear, cubic)
|
Interpolation; |
"linear"
|
outside
|
constant or float
|
The flux beyond the end nodes (default |
'constant'
|
wavel0
|
float
|
Reference wavelength in metres; the first node by default. |
None
|
Examples:
>>> spectrum = Nodes([0.2, 0.4], [2.0e-6, 2.2e-6])
>>> round(float(spectrum(2.1e-6)), 6)
0.3
>>> excess = Nodes([0.0, 0.5, 0.0], [2.16e-6, 2.166e-6, 2.172e-6], outside=0.0)
>>> float(excess(2.0e-6))
0.0
wavel0
instance-attribute
values = np.asarray(values, dtype=float)
instance-attribute
wavel = np.asarray(wavel, dtype=float)
instance-attribute
kind = kind
class-attribute
instance-attribute
outside = outside if outside == 'constant' else float(outside)
class-attribute
instance-attribute
__init__(values, wavel, kind='linear', outside='constant', wavel0=None)
__check_init__()
PowerLaw
Bases: Spectrum
Power-law spectrum ratio * (λ / wavel0) ** index.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ratio
|
float or array - like
|
Flux at the reference wavelength, relative to the other components. |
required |
index
|
float or array - like
|
Spectral index (default 0, i.e. achromatic). A star in the Rayleigh-Jeans regime of F_λ has index -4. |
0.0
|
wavel0
|
float or array - like
|
Reference wavelength in metres (default 1.65e-6, H band). |
1.65e-06
|
Examples:
>>> round(float(PowerLaw(0.2, index=-4.0, wavel0=1.6e-6)(3.2e-6)), 6)
0.0125
ratio = np.asarray(ratio, dtype=float)
instance-attribute
index = np.asarray(index, dtype=float)
instance-attribute
wavel0 = np.asarray(wavel0, dtype=float)
instance-attribute
__init__(ratio, index=0.0, wavel0=1.65e-06)
__call__(wavel=None)
is_physical()
__check_init__()
Scaled
Bases: Spectrum
A fixed spectral shape with a free reference flux, ratio at wavel0.
The value is ratio * template(λ) / template(). Any spectrum, e.g. a
library SED or a Sum with lines, can then be a
companion in the grids, limits and detection statistics, whose flux axis is
comp.flux.ratio. The template's parameters are held fixed: no gradient
flows to them, so do not give priors to paths such as
comp.flux.template.index. Only ratio is free.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
template
|
Spectrum
|
The shape; only its shape matters, and its normalization must be positive. |
required |
ratio
|
float or array - like
|
The flux at the template's |
1.0
|
Examples:
>>> template = PowerLaw(0.5, index=-2.0, wavel0=2.0e-6)
>>> round(float(Scaled(template, 0.1)(4.0e-6)), 6)
0.025
>>> round(float(Scaled(template, 0.1)()), 6)
0.1
template = template
instance-attribute
ratio = np.asarray(ratio, dtype=float)
instance-attribute
wavel0
property
__init__(template, ratio=1.0)
__call__(wavel=None)
__check_init__()
Spectrum
Bases: Base
Base class for component spectra.
Subclasses implement _at(wavel), the flux at wavel (metres, any
shape), and have a reference wavelength wavel0; calling the spectrum
with no wavelength gives the flux there. _params_valid checks the
parameters' domains and _check_wavel lists the wavelengths where the
flux must be non-negative.
__call__(wavel=None)
is_physical()
Whether the spectrum is valid, as a (traceable) boolean.
Its parameters are in their domains and its flux is non-negative at
its characteristic wavelengths (wavel0, nodes, line centres).
Sum
Bases: Spectrum
The sum of named spectra, e.g. a continuum plus lines.
Parts are reached by name, like the components of a
System: "star.flux.continuum.index". Only the
total must be non-negative, so a part may be an absorption line or a
negative node excess.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parts
|
dict
|
|
None
|
wavel0
|
float
|
Reference wavelength in metres, where the reference flux is the sum of
the parts; the first part's |
None
|
Examples:
>>> flux = Sum(
... continuum=PowerLaw(1.0, wavel0=2.2e-6),
... brg=GaussianLine(-0.3, line_wavel=2.1661e-6, fwhm=1.0e-9),
... )
>>> round(float(flux(2.1661e-6)), 4) # continuum minus the absorption
0.7
>>> round(float(flux()), 4) # the reference flux, at 2.2 µm
1.0
names = tuple(parts)
class-attribute
instance-attribute
parts = tuple(parts.values())
instance-attribute
wavel0 = np.asarray(wavel0, dtype=float)
instance-attribute
components
property
The parts as a {name: spectrum} dictionary, in order.
__init__(parts=None, /, *, wavel0=None, **named)
__getattr__(name)
flux_at(flux, wavel=None)
Evaluate a number or a spectrum at wavel (None = reference).
reference_flux(flux)
The reference flux of a number or a spectrum.
For a spectrum this is flux(None), its value at wavel0 (the
ratio for PowerLaw and BlackBody).