"""Classes for describing channels on Hinode/XRT."""
from pathlib import Path
import numpy as np
import sunpy.io.special
import sunpy.time
from astropy import units as u
__all__ = [
"Geometry",
"EntranceFilter",
"Mirror",
"Filter",
"CCD",
"Channel",
"resolve_filter_name",
]
_channel_name_to_index_mapping = {
"Al-mesh": 0,
"Al-poly": 1,
"C-poly": 2,
"Ti-poly": 3,
"Be-thin": 4,
"Be-med": 5,
"Al-med": 6,
"Al-thick": 7,
"Be-thick": 8,
"Al-poly/Al-mesh": 9,
"Al-poly/Ti-poly": 10,
"Al-poly/Al-thick": 11,
"Al-poly/Be-thick": 12,
"C-poly/Ti-poly": 13,
}
_genx_file = sunpy.io.special.genx.read_genx(
Path(__file__).parent.absolute() / "data" / "xrt_channels_v0017.genx"
)["SAVEGEN0"]
[docs]
def resolve_filter_name(name):
"""
Formats the user's filter name to match the expected format.
Parameters
----------
name : str
The filter name provided by the user.
Returns
-------
str
The formatted filter name.
Raises
------
TypeError
If the provided name is not a string.
"""
if not isinstance(name, str):
raise TypeError("name must be a string")
name = name.replace("_", "-")
parts: list = name.split("/")
new_parts: list = [part.capitalize() for part in parts]
name: str = "/".join(new_parts)
return name
[docs]
class Geometry:
"""
The physical geometric parameters of the X-Ray Telescope (XRT) on board the Hinode spacecraft.
Parameters
----------
index : int
The index of the channel in the GENX file.
Attributes
----------
Channel.Geometry.geometry_name : str
Hinode/XRT flight model geometry.
Channel.Geometry.geometry_focal_len : astropy.units.Quantity
XRT flight model geometry focal length in cm.
Channel.Geometry.geometry_aperture_area : astropy.units.Quantity
XRT flight model geometry aperture area in cm^2.
"""
_genx_file = _genx_file
def __init__(self, index):
self._channel_index = index
self._geom_data = self._genx_file[self._channel_index]["GEOM"]
@property
def geometry_name(
self,
) -> str:
"""Hinode/XRT flight model geometry."""
return self._geom_data["LONG_NAME"]
@property
@u.quantity_input
def geometry_focal_len(self) -> u.cm:
"""XRT flight model geometry focal length."""
return u.Quantity(self._geom_data["FOC_LEN"], u.cm)
@property
@u.quantity_input
def geometry_aperture_area(self) -> u.cm**2:
"""XRT flight model geometry aperture area."""
return u.Quantity(self._geom_data["APERTURE_AREA"], u.cm**2)
[docs]
class EntranceFilter:
"""
Represents the entrance filter of the X-Ray Telescope (XRT) on the Hinode spacecraft.
The entrance filter covers the annular entrance aperture of the XRT, serving to reduce
both visible light and heat load entering the instrument.
Parameters
----------
index : int
The index of the channel within the GENX file that contains data specific to this filter.
Attributes
----------
Channel.EntranceFilter.entrancefilter_density : astropy.units.Quantity
The density of the entrance filter material in g/cm³.
Channel.EntranceFilter.entrancefilter_material : str
The material composition of the entrance filter.
Channel.EntranceFilter.entrancefilter_mesh_transmission : float
The percentage transmission of the mesh part of the filter.
Channel.EntranceFilter.entrancefilter_name : str
The descriptive name of the entrance filter.
Channel.EntranceFilter.entrancefilter_substrate : str
The substrate material of the entrance filter.
Channel.EntranceFilter.entrancefilter_thickness : astropy.units.Quantity
The thickness of the entrance filter material measured in Angstroms.
Channel.EntranceFilter.entrancefilter_transmission : numpy.ndarray
The transmission efficiency of the entrance filter across different wavelengths.
Channel.EntranceFilter.entrancefilter_wavelength : astropy.units.Quantity
The wavelengths at which the transmission data of the filter are measured, in Angstroms.
"""
_genx_file = _genx_file
def __init__(self, index):
self._channel_index = index
self._en_filter_data = self._genx_file[self._channel_index]["EN_FILTER"]
@property
def entrancefilter_density(self) -> u.g * u.cm**-3:
r"""XRT entrance filter material density in g/cm\ :sup:`3`\ ."""
return u.Quantity(self._en_filter_data["DENS"], u.g * u.cm**-3)
@property
def entrancefilter_material(self) -> str:
"""XRT entrance filter material."""
return self._en_filter_data["MATERIAL"]
@property
def entrancefilter_mesh_transmission(self):
"""Transmission of mesh filter substrate."""
return self._en_filter_data["MESH_TRANS"]
@property
def entrancefilter_name(self) -> str:
"""Entrance filter name."""
return self._en_filter_data["LONG_NAME"]
@property
def number_of_wavelengths(self):
"""Data number length."""
return self._en_filter_data["LENGTH"]
@property
def entrancefilter_substrate(self) -> str:
"""XRT entrance filter substrate."""
return self._en_filter_data["SUBSTRATE"]
@property
@u.quantity_input
def entrancefilter_wavelength(self) -> u.angstrom:
"""Array of wavelengths for entrance filter transmission in angstroms."""
return u.Quantity(self._en_filter_data["WAVE"], u.angstrom)[
: self.number_of_wavelengths
]
@property
@u.quantity_input
def entrancefilter_thickness(self) -> u.angstrom:
"""XRT entrance filter material thickness in angstroms."""
return u.Quantity(self._en_filter_data["THICK"], u.angstrom)
@property
def entrancefilter_transmission(self):
"""Entrance filter transmission."""
return self._en_filter_data["TRANS"][: self.number_of_wavelengths]
[docs]
class Mirror:
"""
Defines a grazing incidence mirror used in the X-Ray Telescope (XRT) for imaging in soft X-rays.
Parameters
----------
index : int
Index of the channel within the GENX data file.
mirror_number : int
Specifies whether this is the first or second mirror (1 or 2).
Attributes
----------
Channel.Mirror.mirror_density : astropy.units.Quantity
The mass density of the mirror in g/cm³.
Channel.Mirror.mirror_graze_angle : astropy.units.Quantity
The graze angle of the mirror during operation, measured in degrees.
Channel.Mirror.mirror_material : str
The material composition of the mirror.
Channel.Mirror.mirror_name : str
The name or identifier of the mirror.
Channel.Mirror.mirror_reflection : numpy.ndarray
The reflectance values of the mirror at different wavelengths.
Channel.Mirror.mirror_wavelength : astropy.units.Quantity
The wavelengths at which the mirror's reflectance is measured, in Angstroms.
"""
_genx_file = _genx_file
def __init__(self, index, mirror_number):
self._channel_index = index
self._mirror_data = self._genx_file[self._channel_index][
f"MIRROR{mirror_number}"
]
@property
@u.quantity_input
def mirror_density(self) -> u.g * u.cm**-3:
"""Mirror mass density."""
return u.Quantity(self._mirror_data["DENS"], u.g * u.cm**-3)
@property
@u.quantity_input
def mirror_graze_angle(self) -> u.deg:
"""Mirror graze angle in units of degrees."""
return u.Quantity(self._mirror_data["GRAZE_ANGLE"], u.deg)
@property
def mirror_name(self) -> str:
"""Hinode/XRT flight model mirror."""
return self._mirror_data["LONG_NAME"]
@property
def mirror_material(self) -> str:
"""XRT flight model mirror material."""
return self._mirror_data["MATERIAL"]
@property
@u.quantity_input
def mirror_reflection(self) -> u.angstrom:
"""Reflection of a mirror."""
return u.Quantity(self._mirror_data["REFL"], u.angstrom)[
: self.number_of_wavelengths
]
@property
@u.quantity_input
def mirror_wavelength(self) -> u.angstrom:
"""Array of wavelengths for mirror reflectance."""
return u.Quantity(self._mirror_data["WAVE"], u.angstrom)[
: self.number_of_wavelengths
]
@property
def number_of_wavelengths(self):
"""Data number length."""
return self._mirror_data["LENGTH"]
[docs]
class Filter:
"""
Represents one of the focal plane filters of the X-Ray Telescope (XRT) on the Hinode spacecraft,
which are mounted on two filter wheels.
Parameters
----------
index : int
Index of the channel within the GENX data file.
filter_number : int
Specifies the filter wheel position (1 or 2).
Attributes
----------
Channel.Filter.filter_density : astropy.units.Quantity
The density of the filter material in g/cm³.
Channel.Filter.filter_material : str
The material composition of the filter.
Channel.Filter.filter_mesh_transmission : float
The transmission efficiency of the filter's mesh.
Channel.Filter.filter_name : str
The descriptive name of the filter.
Channel.Filter.filter_substrate : str
The substrate material of the filter.
Channel.Filter.filter_thickness : astropy.units.Quantity
The thickness of the filter material, measured in Angstroms.
Channel.Filter.filter_transmission : numpy.ndarray
The transmission efficiency of the filter across different wavelengths.
Channel.Filter.filter_wavelength : astropy.units.Quantity
The wavelengths at which the filter's transmission data are measured, in Angstroms.
"""
_genx_file = _genx_file
def __init__(self, index, filter_number):
self._channel_index = index
self._fp_filter_data = self._genx_file[self._channel_index][
f"FP_FILTER{filter_number}"
]
@property
@u.quantity_input
def filter_density(self) -> u.g * u.cm**-3:
"""XRT filter density."""
return u.Quantity(self._fp_filter_data["DENS"], u.g * u.cm**-3)
@property
def filter_material(self) -> str:
"""XRT filter material."""
return self._fp_filter_data["MATERIAL"]
@property
def filter_mesh_transmission(self):
"""Mesh transmission for the focal plane filter."""
return self._fp_filter_data["MESH_TRANS"]
@property
def filter_name(self) -> str:
"""XRT filter name."""
return self._fp_filter_data["LONG_NAME"]
@property
def number_of_wavelengths(self):
"""Data number length."""
return self._fp_filter_data["LENGTH"]
@property
def filter_substrate(self) -> str:
"""XRT filter substrate."""
return self._fp_filter_data["SUBSTRATE"]
@property
@u.quantity_input
def filter_thickness(self) -> u.angstrom:
"""Filter thickness."""
return u.Quantity(self._fp_filter_data["THICK"], u.angstrom)
@property
def filter_transmission(self):
"""Filter transmission."""
return self._fp_filter_data["TRANS"][: self.number_of_wavelengths]
@property
@u.quantity_input
def filter_wavelength(self) -> u.angstrom:
"""XRT filter wavelength in angstroms."""
return u.Quantity(self._fp_filter_data["WAVE"], u.angstrom)[
: self.number_of_wavelengths
]
[docs]
class CCD:
"""
Describes the Charge-Coupled Device (CCD) used in the X-Ray Telescope (XRT) on the Hinode spacecraft.
The CCD is a crucial component of the XRT, responsible for capturing X-ray images. This class provides
various properties and characteristics of the CCD, including its gain, quantum efficiency, and physical
dimensions.
Parameters
----------
index : int
Index of the channel within the GENX data file containing CCD-specific properties.
Attributes
----------
Channel.CCD.ccd_energy_per_electron : astropy.units.Quantity
The energy required to dislodge a single electron, measured in electron-volts per electron.
Channel.CCD.ccd_full_well : astropy.units.Quantity
The full well capacity of the CCD in terms of the maximum number of electrons it can hold.
Channel.CCD.ccd_gain_left : astropy.units.Quantity
The gain of the CCD when reading from the left port, measured in electrons per digital number.
Channel.CCD.ccd_gain_right : astropy.units.Quantity
The gain of the CCD when reading from the right port, measured in electrons per digital number.
Channel.CCD.ccd_name : str
The name or identifier of the CCD.
Channel.CCD.ccd_pixel_size : astropy.units.Quantity
The size of individual pixels on the CCD, measured in micrometers.
Channel.CCD.ccd_quantum_efficiency : numpy.ndarray
The quantum efficiency of the CCD, representing the efficiency of photon-to-electron conversion.
Channel.CCD.ccd_wavelength : astropy.units.Quantity
The wavelengths at which the CCD's quantum efficiency is measured, in Angstroms.
"""
_genx_file = _genx_file
def __init__(self, index):
self._channel_index = index
self._ccd_data = self._genx_file[self._channel_index]["CCD"]
@property
@u.quantity_input
def ccd_energy_per_electron(self) -> u.eV / u.electron:
"""The energy necessary to dislodge one electron."""
return u.Quantity(self._ccd_data["EV_PER_EL"], u.eV / u.electron)
@property
@u.quantity_input
def ccd_full_well(self) -> u.electron:
"""Number of electrons for a CCD full well."""
return u.Quantity(self._ccd_data["FULL_WELL"], u.electron)
@property
@u.quantity_input
def ccd_gain_left(self) -> u.electron / u.DN:
"""Gain when reading the left port of the CCD."""
return u.Quantity(self._ccd_data["GAIN_L"], u.electron / u.DN)
@property
@u.quantity_input
def ccd_gain_right(self) -> u.electron / u.DN:
"""Gain when reading the right port of the CCD."""
return u.Quantity(self._ccd_data["GAIN_R"], u.electron / u.DN)
@property
def ccd_name(self) -> str:
"""Hinode/XRT flight model CCD."""
return self._ccd_data["LONG_NAME"]
@property
def number_of_wavelengths(self):
"""Data number length."""
return self._ccd_data["LENGTH"]
@property
@u.quantity_input
def ccd_pixel_size(self) -> u.micron:
"""CCD pixel size in micrometers."""
return u.Quantity(self._ccd_data["PIXEL_SIZE"], u.micron)
@property
def ccd_quantum_efficiency(self):
"""Quantum efficiency of the CCD."""
return self._ccd_data["QE"][: self.number_of_wavelengths]
@property
@u.quantity_input
def ccd_wavelength(self) -> u.angstrom:
"""Array of wavelengths for the CCD quantum efficiency in angstroms."""
return u.Quantity(self._ccd_data["WAVE"], u.angstrom)[
: self.number_of_wavelengths
]
[docs]
class Channel:
"""
Represents an XRT channel on the Hinode spacecraft.
Available channels: "Al-mesh", "Al-poly", "C-poly", "Ti-poly", "Be-thin", "Be-med",
"Al-med", "Al-thick", "Be-thick", "Al-poly/Al-mesh", "Al-poly/Ti-poly", "Al-poly/Al-thick",
"Al-poly/Be-thick", "C-poly/Ti-poly".
Parameters
----------
name : str
The name of the filter for the XRT channel.
Attributes
----------
Channel.Geometry
The geometric parameters of the XRT channel.
Channel.EntranceFilter
The entrance filter properties.
Channel.Mirror.mirror_1
Properties of the first mirror.
Channel.Mirror.mirror_2
Properties of the second mirror.
Channel.Filter.filter_1
Properties of the first filter.
Channel.Filter.filter_2
Properties of the second filter.
Channel.CCD
Properties of the CCD.
Name : str
Name of XRT X-Ray channel.
Wavelength : astropy.units.Quantity
Array of wavelengths for every X-ray channel in angstroms.
Transmission : numpy.ndarray
Transmission of the channel.
Number_of_wavelengths : int
Length of the data.
Observatory : str
Name of the spacecraft.
Instrument : str
Name of the instrument (X-Ray Telescope -XRT).
"""
_genx_file = _genx_file
def __init__(self, name):
name = resolve_filter_name(name)
if name in _channel_name_to_index_mapping:
self._channel_index = _channel_name_to_index_mapping[name]
self._channel_data = _genx_file[self._channel_index]
self._geometry = Geometry(self._channel_index)
self._entrancefilter = EntranceFilter(self._channel_index)
self._mirror_1 = Mirror(self._channel_index, 1)
self._mirror_2 = Mirror(self._channel_index, 2)
self._filter_1 = Filter(self._channel_index, 1)
self._filter_2 = Filter(self._channel_index, 2)
self._ccd = CCD(self._channel_index)
elif name.lower() == "open": # Complete by adding remaining indices
self._sample_channel_data = _genx_file[1]
self._geometry = Geometry(1)
self._channel_data = {
"WAVE": self._sample_channel_data["WAVE"],
"TRANS": np.ones_like(self._sample_channel_data["TRANS"]),
"LENGTH": self._sample_channel_data["LENGTH"],
}
else:
raise ValueError(
f"{name} is not a valid channel. The available channels are: {list(_channel_name_to_index_mapping.keys())}"
)
@property
def geometry(self) -> Geometry:
"""
Geometric parameters of the XRT channel.
"""
return self._geometry
@property
def entrancefilter(self) -> EntranceFilter:
"""
Entrance filter properties.
"""
return self._entrancefilter
@property
def mirror_1(self) -> Mirror:
"""
Properties of the first mirror.
"""
return self._mirror_1
@property
def mirror_2(self) -> Mirror:
"""
Properties of the second mirror.
"""
return self._mirror_2
@property
def filter_1(self) -> Filter:
"""
Properties of the first filter.
"""
return self._filter_1
@property
def filter_2(self) -> Filter:
"""
Properties of the second filter.
"""
return self._filter_2
@property
def ccd(self) -> CCD:
"""
Properties of the CCD.
"""
return self._ccd
def __str__(self):
"""Readable printout."""
return f"XRT Channel for {self.name}"
def __repr__(self):
"""Code representation."""
return f"Channel({self.name!r})"
@property
def name(self) -> str:
"""
Name of XRT X-Ray channel.
"""
return self._channel_data["NAME"]
@property
@u.quantity_input
def wavelength(self) -> u.angstrom:
"""
Array of wavelengths for every X-ray channel in angstroms.
"""
return u.Quantity(self._channel_data["WAVE"], u.angstrom)[
: self.number_of_wavelengths
]
@property
def transmission(self):
"""
Channel transmission.
"""
return self._channel_data["TRANS"][: self.number_of_wavelengths]
@property
def number_of_wavelengths(self):
"""
Data number length.
"""
return self._channel_data["LENGTH"]
@property
def observatory(self) -> str:
"""
The spacecraft name - Hinode.
"""
return self._channel_data["OBSERVATORY"]
@property
def instrument(self) -> str:
"""
X-Ray Telescope -XRT.
"""
return self._channel_data["INSTRUMENT"]