Source code for across.tools.core.schemas.bandpass

from typing import Any, Literal

from astropy import units as u  # type: ignore[import-untyped]

from ..enums import EnergyUnit, FrequencyUnit, WavelengthUnit
from .base import BaseSchema
from .exceptions import BandwidthValueError, MinMaxValueError


[docs] class BaseBandpass(BaseSchema): """ A base class for defining bandpass filters with a specified range. Attributes ---------- filter_name : str or None The name of the filter, if provided. min : float or None The minimum value of the bandpass range. max : float or None The maximum value of the bandpass range. """
[docs] filter_name: str | None = None
[docs] min: float | None = None
[docs] max: float | None = None
[docs] class WavelengthBandpass(BaseBandpass): """ A class representing a bandpass filter defined in terms of wavelength. Inherits from `BaseBandpass`, this class specializes the filter to operate in the wavelength domain and provides additional functionality for wavelength-based filters. Attributes ---------- type : Literal['WAVELENGTH'] A constant string indicating the type of the bandpass filter. central_wavelength : float or None The central wavelength of the filter. It is defined as the midpoint between the min and max. bandwidth : float or None The bandwidth of the filter. It is defined as half the difference between the max and min. unit : WavelengthUnit The unit of measurement for the wavelength. """
[docs] type: Literal["WAVELENGTH"] = "WAVELENGTH"
[docs] central_wavelength: float | None = None
[docs] peak_wavelength: float | None = None
[docs] bandwidth: float | None = None
[docs] unit: WavelengthUnit
[docs] def model_post_init(self, __context: Any) -> None: """ Validates the min and max values of the wavelength bandpass and calculates the central wavelength and bandwidth if they are not provided. Also ensures the values are positive and that the max wavelength is greater than the min wavelength. Lastly, it converts the units to angstroms. Raises ------ ValueError If the min/max values are invalid or if the calculated values for central wavelength or bandwidth are non-positive. """ if (self.min and not self.max) or (self.max and not self.min): raise MinMaxValueError("Both min and max must be defined.") if self.min and self.max: if self.max < self.min: raise MinMaxValueError("Max wavelength cannot be less than min wavelength.") if not all([self.min > 0, self.max > 0]): raise MinMaxValueError("Wavelength values must be positive.") self.bandwidth = 0.5 * (self.max - self.min) self.central_wavelength = self.min + self.bandwidth if not (self.central_wavelength and self.bandwidth): raise BandwidthValueError("Both central wavelength and bandwidth must be defined.") if not all([self.central_wavelength > 0, self.bandwidth > 0]): raise BandwidthValueError("Central wavelength and bandwidth must be positive.") if self.bandwidth >= self.central_wavelength: raise BandwidthValueError("Bandwidth must be less than the central wavelength.") self.bandwidth = float( (self.bandwidth * u.Unit(self.unit.value)).to(u.Unit(WavelengthUnit.ANGSTROM.value)).value ) self.central_wavelength = float( (self.central_wavelength * u.Unit(self.unit.value)) .to(u.Unit(WavelengthUnit.ANGSTROM.value)) .value ) self.min = self.central_wavelength - self.bandwidth self.max = self.central_wavelength + self.bandwidth self.unit = WavelengthUnit.ANGSTROM
[docs] class EnergyBandpass(BaseBandpass): """ A class representing a bandpass filter defined in terms of energy. Inherits from `BaseBandpass`, this class specializes the filter to operate in the energy domain. Attributes ---------- type : Literal['ENERGY'] A constant string indicating the type of the bandpass filter. unit : EnergyUnit The unit of measurement for the energy. """
[docs] type: Literal["ENERGY"] = "ENERGY"
[docs] unit: EnergyUnit
[docs] def model_post_init(self, __context: Any) -> None: """ Validates that the min and max energy values are positive. Raises ------ ValueError If the min or max energy values are not defined, are non-positive, and max is greater than min. """ if not (self.min and self.max): raise MinMaxValueError("Both min and max energy values must be defined.") if not all([self.min > 0, self.max > 0]): raise MinMaxValueError("Energy values must be positive.") if self.max < self.min: raise MinMaxValueError("Max wavelength cannot be less than min wavelength.")
[docs] class FrequencyBandpass(BaseBandpass): """ A class representing a bandpass filter defined in terms of frequency. Inherits from `BaseBandpass`, this class specializes the filter to operate in the frequency domain. Attributes ---------- type : Literal['FREQUENCY'] A constant string indicating the type of the bandpass filter. unit : FrequencyUnit The unit of measurement for the frequency. """
[docs] type: Literal["FREQUENCY"] = "FREQUENCY"
[docs] unit: FrequencyUnit
[docs] def model_post_init(self, __context: Any) -> None: """ Validates that the min and max frequency values are positive. Raises ------ ValueError If the min or max energy values are not defined, are non-positive, and max is greater than min. """ if not (self.min and self.max): raise MinMaxValueError("Both min and max frequency values must be defined.") if not all([self.min > 0, self.max > 0]): raise MinMaxValueError("Frequency values must be positive.") if self.max < self.min: raise MinMaxValueError("Max wavelength cannot be less than min wavelength.")
[docs] def convert_to_wave(bandpass: EnergyBandpass | FrequencyBandpass) -> WavelengthBandpass: """ Converts a given EnergyBandpass or FrequencyBandpass to a WavelengthBandPass. Parameters ---------- bandpass : EnergyBandpass or FrequencyBandpass The bandpass filter in energy or frequency domain. Returns ------- WavelengthBandpass The corresponding bandpass filter in the wavelength domain. Notes ----- When converting from Energy/Frequency to wavelength, the min/max values are inverted in relation to their corresponding wavelengths. Thus, the min/max values are switched during conversion. """ bandpass_min_angstrom = ( (bandpass.max * u.Unit(bandpass.unit.value)) .to(u.Unit(WavelengthUnit.ANGSTROM.value), equivalencies=u.spectral()) .value ) bandpass_max_angstrom = ( (bandpass.min * u.Unit(bandpass.unit.value)) .to(u.Unit(WavelengthUnit.ANGSTROM.value), equivalencies=u.spectral()) .value ) return WavelengthBandpass( min=bandpass_min_angstrom, max=bandpass_max_angstrom, unit=WavelengthUnit.ANGSTROM, filter_name=bandpass.filter_name, )