Source code for scopesim.optics.surface_utils
import numpy as np
from astropy import units as u
from synphot import SourceSpectrum, BlackBody1D, Empirical1D
from ..utils import (quantify, extract_type_from_unit, extract_base_from_unit,
get_logger)
logger = get_logger(__name__)
[docs]def make_emission_from_emissivity(temp, emiss_src_spec):
"""
Create an emission SourceSpectrum using blackbody and emissivity curves.
Parameters
----------
temp : float, Quantity
[Kelvin] If float, then must be in Kelvin
emiss_src_spec : synphot.SpectralElement
An emissivity response curve in the range [0..1]
Returns
-------
flux : synphot.SourceSpectrum
"""
if isinstance(temp, u.Quantity):
temp = temp.to(u.Kelvin, equivalencies=u.temperature()).value
if emiss_src_spec is None:
logger.warning("Either emission or emissivity must be set")
flux = None
else:
flux = SourceSpectrum(BlackBody1D, temperature=temp)
flux.meta["solid_angle"] = u.sr**-1
flux = flux * emiss_src_spec
flux.meta["history"] = ["Created from Blackbody curve. Units are to be"
" understood as per steradian"]
return flux
[docs]def make_emission_from_array(flux, wave, meta):
"""
Create an emission SourceSpectrum using an array.
Takes care of bins and solid angles. The solid_angle is kept in the
returned SourceSpectrum meta dictionary under self.meta["solid_angle"].
Parameters
----------
flux : array-like, Quantity
if flux is not an array, the ``emission_unit`` must be in meta dict
wave : array-like, Quantity
if flux is not an array, the ``wavelength_unit`` must be in meta dict
meta : dict
Returns
-------
flux : synphot.SourceSpectrum
"""
if not isinstance(flux, u.Quantity):
if "emission_unit" in meta:
flux = quantify(flux, meta["emission_unit"])
else:
logger.warning("emission_unit must be set in self.meta, "
"or emission must be an astropy.Quantity")
flux = None
if isinstance(wave, u.Quantity) and isinstance(flux, u.Quantity):
flux_unit, angle = extract_type_from_unit(flux.unit, "solid angle")
flux = flux / angle
if is_flux_binned(flux.unit):
flux = normalise_binned_flux(flux, wave)
orig_unit = flux.unit
flux = SourceSpectrum(Empirical1D, points=wave,
lookup_table=flux)
flux.meta["solid_angle"] = angle
flux.meta["history"] = [("Created from emission array with units "
f"{orig_unit}")]
else:
logger.warning("wavelength and emission must be "
"astropy.Quantity py_objects")
flux = None
return flux
[docs]def normalise_binned_flux(flux, wave):
"""
Convert a binned flux Quantity array back into flux density.
The flux density normalising unit is taken from the wavelength Quantity
unit.
Parameters
----------
flux : array-like Quantity
flux unit must include ``bin``, e.g. ``ph s-1 m-2 bin-1``
wave : array-like Quantity
Returns
-------
flux : array-like Quantity
"""
bins = np.zeros(len(wave)) * wave.unit
bins[:-1] = 0.5 * np.diff(wave)
bins[1:] += 0.5 * np.diff(wave)
# bins[0] *= 2. # edge bins only have half the flux of other bins
# bins[-1] *= 2.
bin_unit = extract_base_from_unit(flux.unit, u.bin)[1]
flux = flux / bins / bin_unit
return flux
[docs]def is_flux_binned(unit):
"""
Check if the (flux) unit is a binned unit.
Parameters
----------
unit : Unit
Returns
-------
flag : bool
"""
unit = unit**1
# unit.physical_type is a string in astropy<=4.2 and a PhysicalType
# class in astropy==4.3 and thus has to be cast to a string first.
return (u.bin in unit._bases or "flux density" not in str(unit.physical_type))