Source code for exosim.tasks.radiometric.estimateSpectralBinning

import astropy.units as u
import numpy as np
from astropy.table import QTable
from scipy.interpolate import interp1d

import exosim.utils.grids as grids
from exosim.tasks.task import Task


[docs]class EstimateSpectralBinning(Task): """ It computes spectral binning useful to produce the radiometric tables Returns -------- astropy.table.QTable: wavelength bins table Raises -------- TypeError: if the output is not :class:`~astropy.table.QTable` KeyError: if a column is missing in the output :class:`~astropy.table.QTable` Notes ----- This is a default class with standardised inputs and outputs. The user can load this class and overwrite the "model" method to implement a custom Task to replace this. """ def __init__(self): """ Parameters __________ parameters: dict dictionary contained the channel parameters. This is usually parsed from :class:`~exosim.tasks.load.loadOptions.LoadOptions` wl_grid: :class:`~astropy.units.Quantity` (optional) focal plane wavelength grid. Useful for spectrometers. Default is ``None``. """ self.add_task_param("parameters", "channel parameters dict") self.add_task_param("wl_grid", "focal plane spectral grid", None)
[docs] def execute(self): self.debug("spectral binning estimation started") parameters = self.get_task_param("parameters") wl_grid = self.get_task_param("wl_grid") table = self.model(wl_grid, parameters) if not isinstance(table, QTable): self.error("wrong output format") raise TypeError("wrong output format") key_list = [ "ch_name", "Wavelength", "bandwidth", "left_bin_edge", "right_bin_edge", ] for key in key_list: if key not in table.keys(): self.error("missing keyword: {}".format(key)) raise KeyError("missing keyword: {}".format(key)) self.set_output(table)
[docs] def model(self, wl_grid, parameters): """ It runs a dedicated method if the channel is a spectrometer or a photometer. Parameters ---------- wl_grid: :class:`~astropy.units.Quantity` (optional) focal plane wavelength grid. Useful for spectrometers. Default is ``None``. parameters: dict dictionary contained the channel parameters. This is usually parsed from :class:`~exosim.tasks.load.loadOptions.LoadOptions` Returns -------- astropy.table.QTable: wavelength bins table Raises -------- AttributeError: if the channel type is not `spectrometer` or `photometer` """ if parameters["type"].lower() == "spectrometer": table = self.wavelength_table_spectrometer(parameters, wl_grid) elif parameters["type"].lower() == "photometer": table = self.wavelength_table_photometer(parameters) else: self.error( "unsupported channel type: {}", format(parameters["type"]) ) raise AttributeError( "unsupported channel type: {}", format(parameters["type"]) ) return table
[docs] def wavelength_table_spectrometer(self, description, wl_grid): """ It returns the wavelength table for a spectrometer. The wavelength grid can be estimated in 2 modes: - `native` mode. If `targetR` is set to `native` the wavelength grid computed is the pixel level wavelength grid, where each bin is of the size of a pixel; - `fixed R` mode. If targetR` is set to a constant value, the wavelength grid is estimated using :func:`~exosim.utils.grids.wl_grid`. Parameters ---------- description: dict dictionary contained the channel parameters. wl_grid: :class:`~astropy.units.Quantity` wavelength grid. Returns ------- astropy.table.QTable: wavelength bins table Raises --------- KeyError Channel targetR format unsupported """ table = QTable() # check if R is defined if "targetR" not in description: self.warning("Channel targetR missing: native R is assumed") description["targetR"] = "native" # native R if description["targetR"] == "native": wl_bin_c = wl_grid wl_sol = interp1d( wl_grid, np.arange(0, wl_grid.size), fill_value="extrapolate" ) wl_bin_width = ( wl_sol(np.arange(0, wl_grid.size) + 0.5) - wl_sol(np.arange(0, wl_grid.size) - 0.5) ) * u.um # fixed R elif isinstance(description["targetR"], int): wl_bin_c, wl_bin_width = grids.wl_grid( description["wl_min"], description["wl_max"], description["targetR"], return_bin_width=True, ) else: self.error("Channel targetR format unsupported.") raise KeyError("Channel targetR format unsupported.") # preparing the table table["ch_name"] = [description["value"]] * wl_bin_c.size table["Wavelength"] = wl_bin_c table["bandwidth"] = wl_bin_width table["left_bin_edge"] = table["Wavelength"] - 0.5 * table["bandwidth"] table["right_bin_edge"] = ( table["Wavelength"] + 0.5 * table["bandwidth"] ) self.debug("wavelength table: \n{}".format(table)) return table
[docs] def wavelength_table_photometer(self, description): """ It returns the wavelength table for a photometer. It is estimated as the central wavelength of the photometer with a bin width equal to the wavelength band. Parameters ---------- description: dict dictionary contained the channel parameters. Returns ------- astropy.table.QTable: wavelength bins table """ table = QTable() wl_c = 0.5 * (description["wl_min"] + description["wl_max"]) bandwidth = description["wl_max"] - description["wl_min"] left_bin_edge = wl_c - 0.5 * bandwidth right_bin_edge = wl_c + 0.5 * bandwidth table["ch_name"] = [description["value"]] table["Wavelength"] = [wl_c.value] * wl_c.unit table["bandwidth"] = [bandwidth.value] * bandwidth.unit table["left_bin_edge"] = [left_bin_edge.value] * left_bin_edge.unit table["right_bin_edge"] = [right_bin_edge.value] * right_bin_edge.unit self.debug("wavelength table: \n{}".format(table)) return table