Module stixdcpy.transmission_backup

Expand source code
from collections import OrderedDict
from functools import partial

import astropy.units as u
import numpy as np
from astropy.table.table import Table
from roentgen.absorption.material import Compound, MassAttenuationCoefficient, Material

#from stixcore.config.reader import read_energy_channels

__all__ = ['Transmission']

MIL_SI = 0.0254 * u.mm

# TODO move to configuration files
COMPONENTS = OrderedDict([
    ('front_window', [('solarblack', 0.005 * u.mm), ('be', 2 * u.mm)]),
    ('rear_window', [('be', 1 * u.mm)]),
    ('grid_covers', [('kapton', 4 * 2 * MIL_SI)]),
    ('dem', [('kapton', 2 * 3 * MIL_SI)]),
    ('attenuator', [('al', 0.6 * u.mm)]),
    ('mli', [('al', 1000 * u.angstrom), ('kapton', 3 * MIL_SI),
             ('al', 40 * 1000 * u.angstrom), ('mylar', 20 * 0.25 * MIL_SI),
             ('pet', 21 * 0.005 * u.mm), ('kapton', 3 * MIL_SI),
             ('al', 1000 * u.angstrom)]),
    ('calibration_foil', [('al', 4 * 1000 * u.angstrom),
                          ('kapton', 4 * 2 * MIL_SI)]),
    ('dead_layer', [('te_o2', 392 * u.nm)]),
])

MATERIALS = OrderedDict([
    ('al', ({
        'Al': 1.0
    }, 2.7 * u.g / u.cm**3)),
    ('be', ({
        'Be': 1.0
    }, 1.85 * u.g / u.cm**3)),
    ('kapton', ({
        'H': 0.026362,
        'C': 0.691133,
        'N': 0.073270,
        'O': 0.209235
    }, 1.43 * u.g / u.cm**3)),
    ('mylar', ({
        'H': 0.041959,
        'C': 0.625017,
        'O': 0.333025
    }, 1.38 * u.g / u.cm**3)),
    ('pet', ({
        'H': 0.041960,
        'C': 0.625016,
        'O': 0.333024
    }, 1.370 * u.g / u.cm**3)),
    ('solarblack_oxygen', ({
        'H': 0.002,
        'O': 0.415,
        'Ca': 0.396,
        'P': 0.187
    }, 3.2 * u.g / u.cm**3)),
    ('solarblack_carbon', ({
        'C': 0.301,
        'Ca': 0.503,
        'P': 0.195
    }, 3.2 * u.g / u.cm**3)),
    ('te_o2', ({
        'Te': 0.7995088158691722,
        'O': 0.20049124678825841
    }, 5.670 * u.g / u.cm**3)),
])

#    'Channel Number', 'Channel Edge', 'Energy Edge ', 'Elower', 'Eupper ',
#    'BinWidth', 'dE/E', 'QL channel'
default_energy_bins = [['0', '0', '0', '0', '4', '4', '2', 'n/a'],
                       ['1', '1', '4', '4', '5', '1', '0.222', '0'],
                       ['2', '2', '5', '5', '6', '1', '0.182', '0'],
                       ['3', '3', '6', '6', '7', '1', '0.154', '0'],
                       ['4', '4', '7', '7', '8', '1', '0.133', '0'],
                       ['5', '5', '8', '8', '9', '1', '0.118', '0'],
                       ['6', '6', '9', '9', '10', '1', '0.105', '0'],
                       ['7', '7', '10', '10', '11', '1', '0.095', '1'],
                       ['8', '8', '11', '11', '12', '1', '0.087', '1'],
                       ['9', '9', '12', '12', '13', '1', '0.08', '1'],
                       ['10', '10', '13', '13', '14', '1', '0.074', '1'],
                       ['11', '11', '14', '14', '15', '1', '0.069', '1'],
                       ['12', '12', '15', '15', '16', '1', '0.065', '2'],
                       ['13', '13', '16', '16', '17', '1', '0.061', '2'],
                       ['14', '14', '18', '18', '20', '2', '0.105', '2'],
                       ['15', '15', '20', '20', '22', '2', '0.095', '2'],
                       ['16', '16', '22', '22', '25', '3', '0.128', '2'],
                       ['17', '17', '25', '25', '28', '3', '0.113', '3'],
                       ['18', '18', '28', '28', '32', '4', '0.133', '3'],
                       ['19', '19', '32', '32', '36', '4', '0.118', '3'],
                       ['20', '20', '36', '36', '40', '4', '0.105', '3'],
                       ['21', '21', '40', '40', '45', '5', '0.118', '3'],
                       ['22', '22', '45', '45', '50', '5', '0.105', '3'],
                       ['23', '23', '50', '50', '56', '6', '0.113', '4'],
                       ['24', '24', '56', '56', '63', '7', '0.118', '4'],
                       ['25', '25', '63', '63', '70', '7', '0.105', '4'],
                       ['26', '26', '70', '70', '76', '6', '0.082', '4'],
                       ['27', '27', '76', '76', '84', '8', '0.1', '4'],
                       ['28', '28', '84', '84', '100', '16', '0.174', '4'],
                       ['29', '29', '100', '100', '120', '20', '0.182', '4'],
                       ['30', '30', '120', '120', '150', '30', '0.222', '4'],
                       [
                           '31', '31', '150', '150', 'maxADC', 'n/a', 'n/a',
                           'n/a'
                       ], ['', '32', 'max ADC', '', '', '', '', '']]


def float_def(value, default=np.inf):
    """Parse the value into a float or return the default value.

    Parameters
    ----------
    value : `str`
        the value to parse
    default : `double`, optional
        default value to return in case of pasring errors, by default numpy.inf

    Returns
    -------
    `double`
        the parsed value
    """
    try:
        return float(value)
    except ValueError:
        return default


def int_def(value, default=0):
    """Parse the value into a int or return the default value.

    Parameters
    ----------
    value : `str`
        the value to parse
    default : `int`, optional
        default value to return in case of pasring errors, by default 0

    Returns
    -------
    `int`
        the parsed value
    """
    try:
        return int(value)
    except ValueError:
        return default


# TODO get file from config
'''
class EnergyChannel:
    """Represent a STIX energy channel.

    Attributes
    ----------
    channel_edge : ´int´
        chanel idx
    energy_edge : ´int´
        edge on energy axis
    e_lower : ´float´
        start of the energy band in keV
    e_upper : ´float´
        end of the energy band in keV
    bin_width : ´float´
        width of the energy band in keV
    dE_E : ´float´
        TODO
    """

    def __init__(self, *, channel_edge, energy_edge, e_lower, e_upper, bin_width, dE_E):
        self.channel_edge = channel_edge
        self.energy_edge = energy_edge
        self.e_lower = e_lower
        self.e_upper = e_upper
        self.bin_width = bin_width
        self.dE_E = dE_E

    def __repr__(self):
        return f'{self.__class__.__name__}(channel_edge={self.channel_edge}, ' +\
               f'energy_edge={self.energy_edge}, e_lower={self.e_lower}, e_upper={self.e_upper},' +\
               f' bin_width={self.bin_width}, dE_E={self.dE_E})'
def read_energy_channels(energy_bin_config=default_energy_bins):
    """Read the energy channels from the configuration file.

    Parameters
    ----------
    path : `pathlib.Path`
        path to the config file

    Returns
    -------
    `dict`
        set of `EnergyChannel` accessible by index
    """
    energy_channels = dict()
    if energy_bin_config:
        for row in energy_bin_config:
        
            idx = int_def(row[0], -1)
            if idx == -1:
                continue
            energy_channels[idx] = EnergyChannel(
                channel_edge=int_def(row[1]),
                energy_edge=int_def(row[2]),
                e_lower=float_def(row[3]),
                e_upper=float_def(row[4]),
                bin_width=float_def(row[5]),
                dE_E=float_def(row[6])
            )

    return energy_channels

#ENERGY_CHANNELS = read_energy_channels(default_energy_bins)
'''


class Transmission:
    """
    Calculate the energy dependant transmission of X-ray through the instrument
    """
    def __init__(self, solarblack='solarblack_carbon'):
        """
        Create a new instance of the transmission with the given solar black composition.

        Parameters
        ----------

        solarblack : `str` optional
            The SolarBlack composition to use.
        """
        if solarblack not in ['solarblack_oxygen', 'solarblack_carbon']:
            raise ValueError(
                "solarblack must be either 'solarblack_oxygen' or "
                "'solarblack_carbon'.")

        self.solarblack = solarblack
        self.materials = MATERIALS
        self.components = COMPONENTS
        self.components = dict()

        for name, layers in COMPONENTS.items():
            parts = []
            for material, thickness in layers:
                if material == 'solarblack':
                    material = self.solarblack
                mass_frac, den = MATERIALS[material]
                if material == 'al':
                    thickness = thickness * 0.8
                parts.append(
                    self.create_material(name=material,
                                         fractional_masses=mass_frac,
                                         thickness=thickness,
                                         density=den))
            self.components[name] = Compound(parts)

    def get_transmission(self, energies, attenuator=False):
        """
        Get the transmission for each detector at the center of the given energy bins.

        If energies are not supplied will evaluate at standard science energy channels

        Parameters
        ----------
        energies : `astropy.units.Quantity`, optional
            The energies to evaluate the transmission
        attenuator : `bool`, optional
            True for attenuator in X-ray path, False for attenuator not in X-ray path

        Returns
        -------
        `astropy.table.Table`
            Table containing the transmission values for each energy and detector
        """
        base_comps = [
            self.components[name] for name in [
                'front_window', 'rear_window', 'dem', 'mli',
                'calibration_foil', 'dead_layer'
            ]
        ]

        #if energies is None:
        energies = energies * u.keV
        #    self.energies = [ENERGY_CHANNELS[i].e_lower for i in range(1, 32)] * u.keV

        if attenuator:
            base_comps.append(self.components['attenuator'])

        base = Compound(base_comps)
        base_trans = base.transmission(energies)

        fine = Compound(base_comps + [self.components['grid_covers']])
        fine_trans = fine.transmission(energies)

        # TODO need to move to configuration db
        fine_grids = np.array([11, 13, 18, 12, 19, 17]) - 1
        detector_transmission = Table()
        # transmission['sci_channel'] = range(1, 31)
        detector_transmission['energies'] = energies
        for i in range(32):
            name = f'det-{i}'
            if np.isin(i, fine_grids):
                detector_transmission[name] = fine_trans
            else:
                detector_transmission[name] = base_trans
        return detector_transmission

    def get_detector_transmission(self,
                                  detector_id,
                                  energy_bins,
                                  attenuator=False):
        '''
            get transmission for detector
            Arguments
            ---------
            detector:  ranges from 0...31 
            energy_bins:   32 x 2 array like [[e_bin_0_low, ebin_0_up], ...[]]
            Returns
            --------
            transmission for the energy bins
            
        '''
        base_comps = [
            self.components[name] for name in [
                'front_window', 'rear_window', 'dem', 'mli',
                'calibration_foil', 'dead_layer'
            ]
        ]

        if attenuator:
            base_comps.append(self.components['attenuator'])

        fine_grids = [10, 12, 17, 11, 18, 16]

        if detector_id in fine_grids:
            comp = Compound(base_comps + [self.components['grid_covers']])
        else:
            comp = Compound(base_comps)

        ebins_1d = energy_bins.reshape(-1) * u.keV
        #convert energy bin ranges to 1d
        det_trans = comp.transmission(ebins_1d)
        mean_trans = np.mean(det_trans.reshape((-1, 2)), axis=1)
        #calculate mean energy transmission factors for energy bins
        return mean_trans

    def get_transmission_by_component(self):
        """
        Get the contributions to the total transmission by broken down by component.

        Returns
        -------
        `dict`
            Entries are Compounds for each component
        """
        return self.components

    def get_transmission_by_material(self):
        """
        Get the contribution to the transmission by total thickness for each material.

        Layers of the same materials are combined to return one instance with the total thickness.

        Returns
        -------
        `dict`
            Entries are meterials with the total thickness for that material.
        """
        material_thickness = dict()
        for name, layers in COMPONENTS.items():
            for material_name, thickness in layers:
                if material_name == 'solarblack':
                    material_name = self.solarblack
                if material_name in material_thickness.keys():
                    material_thickness[material_name] += thickness.to('mm')
                else:
                    material_thickness[material_name] = thickness.to('mm')
        res = {}
        for name, thickness in material_thickness.items():
            frac_mass, density = self.materials[name]
            mat = self.create_material(name=name,
                                       fractional_masses=frac_mass,
                                       density=density,
                                       thickness=thickness)
            res[name] = mat

        return res

    @classmethod
    def create_material(cls,
                        name=None,
                        fractional_masses=None,
                        thickness=None,
                        density=None):
        """
        Create a new material given the composition and fractional masses.

        Parameters
        ----------
        name : `str`
            Name of the meterial
        fractional_masses : `dict`
            The element and fractional masses of the material e.g. `{'H': 0.031, 'O': 0.969}`
        thickness : `astropy.units.Quantity`
            Thickness of the material
        density : `astropy.units.Quantity`
            Density of the material

        Returns
        -------
        `roentgen.absorption.material.Material`
            The material
        """
        material = Material('h', thickness, density)
        material.name = name
        # probably don't need this
        material.density = density

        # TODO remove in favour of upstream fix when completed
        #  see https://github.com/ehsteve/roentgen/issues/26
        def func(composition, e):
            return sum([
                MassAttenuationCoefficient(element).func(e) * frac_mass
                for element, frac_mass in composition.items()
            ])

        material_func = partial(func, fractional_masses)

        material.mass_attenuation_coefficient.func = material_func
        return material


"""
def generate_transmission_tables():
    from datetime import datetime
    cur_date = datetime.now().strftime('%Y%m%d')
    datetime.now().strftime('%Y%m%d')
    trans = Transmission()
    norm_sci_energies = trans.get_transmission()
    norm_sci_energies.write(f'stix_transmission_sci_energies_{cur_date}.csv')
    norm_high_res = trans.get_transmission(energies=energies)
    norm_high_res.write(f'stix_transmission_highres_{cur_date}.csv')

    comps = trans.get_transmission_by_component()

    comps_sci_energies = Table([c.transmission(trans.energies) for c in comps.values()],
                               names=[k for k in comps.keys()])
    comps_sci_energies['energy'] = trans.energies
    comps_sci_energies.write(f'stix_transmission_by_component_sci_energies_{cur_date}.csv')

    comps_highres = Table([c.transmission(energies) for c in comps.values()],
                          names=[k for k in comps.keys()])
    comps_highres['energy'] = energies
    comps_highres.write(f'stix_transmission_by_component_highres_{cur_date}.csv')

generate_transmission_tables()
"""


def get_detector_absorption(energies=None):
    mass_fraction = {'Te': 0.531644, 'Cd': 0.4683554}
    desity = 5.85 * u.g / u.cm**3
    thickness = 1 * u.mm
    material = Transmission.create_material(name='cdte',
                                            fractional_masses=mass_fraction,
                                            thickness=thickness,
                                            density=density)
    if energies is None:
        energies = np.linspace(2, 150, 1001)
        energies = energies * u.keV
    absorption = material.absorption(energies)
    print(absorption)


#def get_transmission(energies):
#    return trans.get_transmission(energies)
if __name__ == '__main__':
    energies = np.linspace(2, 150, 1001)
    tr = Transmission()
    t = tr.get_transmission(energies)
    t.pprint()
    print(t['det-0'][0])

Classes

class Transmission (solarblack='solarblack_carbon')

Calculate the energy dependant transmission of X-ray through the instrument

Create a new instance of the transmission with the given solar black composition.

Parameters

solarblack : str</code> optional
The SolarBlack composition to use.
Expand source code
class Transmission:
    """
    Calculate the energy dependant transmission of X-ray through the instrument
    """
    def __init__(self, solarblack='solarblack_carbon'):
        """
        Create a new instance of the transmission with the given solar black composition.

        Parameters
        ----------

        solarblack : `str` optional
            The SolarBlack composition to use.
        """
        if solarblack not in ['solarblack_oxygen', 'solarblack_carbon']:
            raise ValueError(
                "solarblack must be either 'solarblack_oxygen' or "
                "'solarblack_carbon'.")

        self.solarblack = solarblack
        self.materials = MATERIALS
        self.components = COMPONENTS
        self.components = dict()

        for name, layers in COMPONENTS.items():
            parts = []
            for material, thickness in layers:
                if material == 'solarblack':
                    material = self.solarblack
                mass_frac, den = MATERIALS[material]
                if material == 'al':
                    thickness = thickness * 0.8
                parts.append(
                    self.create_material(name=material,
                                         fractional_masses=mass_frac,
                                         thickness=thickness,
                                         density=den))
            self.components[name] = Compound(parts)

    def get_transmission(self, energies, attenuator=False):
        """
        Get the transmission for each detector at the center of the given energy bins.

        If energies are not supplied will evaluate at standard science energy channels

        Parameters
        ----------
        energies : `astropy.units.Quantity`, optional
            The energies to evaluate the transmission
        attenuator : `bool`, optional
            True for attenuator in X-ray path, False for attenuator not in X-ray path

        Returns
        -------
        `astropy.table.Table`
            Table containing the transmission values for each energy and detector
        """
        base_comps = [
            self.components[name] for name in [
                'front_window', 'rear_window', 'dem', 'mli',
                'calibration_foil', 'dead_layer'
            ]
        ]

        #if energies is None:
        energies = energies * u.keV
        #    self.energies = [ENERGY_CHANNELS[i].e_lower for i in range(1, 32)] * u.keV

        if attenuator:
            base_comps.append(self.components['attenuator'])

        base = Compound(base_comps)
        base_trans = base.transmission(energies)

        fine = Compound(base_comps + [self.components['grid_covers']])
        fine_trans = fine.transmission(energies)

        # TODO need to move to configuration db
        fine_grids = np.array([11, 13, 18, 12, 19, 17]) - 1
        detector_transmission = Table()
        # transmission['sci_channel'] = range(1, 31)
        detector_transmission['energies'] = energies
        for i in range(32):
            name = f'det-{i}'
            if np.isin(i, fine_grids):
                detector_transmission[name] = fine_trans
            else:
                detector_transmission[name] = base_trans
        return detector_transmission

    def get_detector_transmission(self,
                                  detector_id,
                                  energy_bins,
                                  attenuator=False):
        '''
            get transmission for detector
            Arguments
            ---------
            detector:  ranges from 0...31 
            energy_bins:   32 x 2 array like [[e_bin_0_low, ebin_0_up], ...[]]
            Returns
            --------
            transmission for the energy bins
            
        '''
        base_comps = [
            self.components[name] for name in [
                'front_window', 'rear_window', 'dem', 'mli',
                'calibration_foil', 'dead_layer'
            ]
        ]

        if attenuator:
            base_comps.append(self.components['attenuator'])

        fine_grids = [10, 12, 17, 11, 18, 16]

        if detector_id in fine_grids:
            comp = Compound(base_comps + [self.components['grid_covers']])
        else:
            comp = Compound(base_comps)

        ebins_1d = energy_bins.reshape(-1) * u.keV
        #convert energy bin ranges to 1d
        det_trans = comp.transmission(ebins_1d)
        mean_trans = np.mean(det_trans.reshape((-1, 2)), axis=1)
        #calculate mean energy transmission factors for energy bins
        return mean_trans

    def get_transmission_by_component(self):
        """
        Get the contributions to the total transmission by broken down by component.

        Returns
        -------
        `dict`
            Entries are Compounds for each component
        """
        return self.components

    def get_transmission_by_material(self):
        """
        Get the contribution to the transmission by total thickness for each material.

        Layers of the same materials are combined to return one instance with the total thickness.

        Returns
        -------
        `dict`
            Entries are meterials with the total thickness for that material.
        """
        material_thickness = dict()
        for name, layers in COMPONENTS.items():
            for material_name, thickness in layers:
                if material_name == 'solarblack':
                    material_name = self.solarblack
                if material_name in material_thickness.keys():
                    material_thickness[material_name] += thickness.to('mm')
                else:
                    material_thickness[material_name] = thickness.to('mm')
        res = {}
        for name, thickness in material_thickness.items():
            frac_mass, density = self.materials[name]
            mat = self.create_material(name=name,
                                       fractional_masses=frac_mass,
                                       density=density,
                                       thickness=thickness)
            res[name] = mat

        return res

    @classmethod
    def create_material(cls,
                        name=None,
                        fractional_masses=None,
                        thickness=None,
                        density=None):
        """
        Create a new material given the composition and fractional masses.

        Parameters
        ----------
        name : `str`
            Name of the meterial
        fractional_masses : `dict`
            The element and fractional masses of the material e.g. `{'H': 0.031, 'O': 0.969}`
        thickness : `astropy.units.Quantity`
            Thickness of the material
        density : `astropy.units.Quantity`
            Density of the material

        Returns
        -------
        `roentgen.absorption.material.Material`
            The material
        """
        material = Material('h', thickness, density)
        material.name = name
        # probably don't need this
        material.density = density

        # TODO remove in favour of upstream fix when completed
        #  see https://github.com/ehsteve/roentgen/issues/26
        def func(composition, e):
            return sum([
                MassAttenuationCoefficient(element).func(e) * frac_mass
                for element, frac_mass in composition.items()
            ])

        material_func = partial(func, fractional_masses)

        material.mass_attenuation_coefficient.func = material_func
        return material

Static methods

def create_material(name=None, fractional_masses=None, thickness=None, density=None)

Create a new material given the composition and fractional masses.

Parameters

name : str
Name of the meterial
fractional_masses : dict
The element and fractional masses of the material e.g. {'H': 0.031, 'O': 0.969}
thickness : astropy.units.Quantity
Thickness of the material
density : astropy.units.Quantity
Density of the material

Returns

roentgen.absorption.material.Material The material

Expand source code
@classmethod
def create_material(cls,
                    name=None,
                    fractional_masses=None,
                    thickness=None,
                    density=None):
    """
    Create a new material given the composition and fractional masses.

    Parameters
    ----------
    name : `str`
        Name of the meterial
    fractional_masses : `dict`
        The element and fractional masses of the material e.g. `{'H': 0.031, 'O': 0.969}`
    thickness : `astropy.units.Quantity`
        Thickness of the material
    density : `astropy.units.Quantity`
        Density of the material

    Returns
    -------
    `roentgen.absorption.material.Material`
        The material
    """
    material = Material('h', thickness, density)
    material.name = name
    # probably don't need this
    material.density = density

    # TODO remove in favour of upstream fix when completed
    #  see https://github.com/ehsteve/roentgen/issues/26
    def func(composition, e):
        return sum([
            MassAttenuationCoefficient(element).func(e) * frac_mass
            for element, frac_mass in composition.items()
        ])

    material_func = partial(func, fractional_masses)

    material.mass_attenuation_coefficient.func = material_func
    return material

Methods

def get_detector_transmission(self, detector_id, energy_bins, attenuator=False)

get transmission for detector Arguments


detector :  ranges from 0…31
 
energy_bins :  32 x 2 array like [[e_bin_0_low, ebin_0_up], …[]]
 

Returns

transmission for the energy bins
 
Expand source code
def get_detector_transmission(self,
                              detector_id,
                              energy_bins,
                              attenuator=False):
    '''
        get transmission for detector
        Arguments
        ---------
        detector:  ranges from 0...31 
        energy_bins:   32 x 2 array like [[e_bin_0_low, ebin_0_up], ...[]]
        Returns
        --------
        transmission for the energy bins
        
    '''
    base_comps = [
        self.components[name] for name in [
            'front_window', 'rear_window', 'dem', 'mli',
            'calibration_foil', 'dead_layer'
        ]
    ]

    if attenuator:
        base_comps.append(self.components['attenuator'])

    fine_grids = [10, 12, 17, 11, 18, 16]

    if detector_id in fine_grids:
        comp = Compound(base_comps + [self.components['grid_covers']])
    else:
        comp = Compound(base_comps)

    ebins_1d = energy_bins.reshape(-1) * u.keV
    #convert energy bin ranges to 1d
    det_trans = comp.transmission(ebins_1d)
    mean_trans = np.mean(det_trans.reshape((-1, 2)), axis=1)
    #calculate mean energy transmission factors for energy bins
    return mean_trans
def get_transmission(self, energies, attenuator=False)

Get the transmission for each detector at the center of the given energy bins.

If energies are not supplied will evaluate at standard science energy channels

Parameters

energies : astropy.units.Quantity, optional
The energies to evaluate the transmission
attenuator : bool, optional
True for attenuator in X-ray path, False for attenuator not in X-ray path

Returns

astropy.table.Table Table containing the transmission values for each energy and detector

Expand source code
def get_transmission(self, energies, attenuator=False):
    """
    Get the transmission for each detector at the center of the given energy bins.

    If energies are not supplied will evaluate at standard science energy channels

    Parameters
    ----------
    energies : `astropy.units.Quantity`, optional
        The energies to evaluate the transmission
    attenuator : `bool`, optional
        True for attenuator in X-ray path, False for attenuator not in X-ray path

    Returns
    -------
    `astropy.table.Table`
        Table containing the transmission values for each energy and detector
    """
    base_comps = [
        self.components[name] for name in [
            'front_window', 'rear_window', 'dem', 'mli',
            'calibration_foil', 'dead_layer'
        ]
    ]

    #if energies is None:
    energies = energies * u.keV
    #    self.energies = [ENERGY_CHANNELS[i].e_lower for i in range(1, 32)] * u.keV

    if attenuator:
        base_comps.append(self.components['attenuator'])

    base = Compound(base_comps)
    base_trans = base.transmission(energies)

    fine = Compound(base_comps + [self.components['grid_covers']])
    fine_trans = fine.transmission(energies)

    # TODO need to move to configuration db
    fine_grids = np.array([11, 13, 18, 12, 19, 17]) - 1
    detector_transmission = Table()
    # transmission['sci_channel'] = range(1, 31)
    detector_transmission['energies'] = energies
    for i in range(32):
        name = f'det-{i}'
        if np.isin(i, fine_grids):
            detector_transmission[name] = fine_trans
        else:
            detector_transmission[name] = base_trans
    return detector_transmission
def get_transmission_by_component(self)

Get the contributions to the total transmission by broken down by component.

Returns

dict Entries are Compounds for each component

Expand source code
def get_transmission_by_component(self):
    """
    Get the contributions to the total transmission by broken down by component.

    Returns
    -------
    `dict`
        Entries are Compounds for each component
    """
    return self.components
def get_transmission_by_material(self)

Get the contribution to the transmission by total thickness for each material.

Layers of the same materials are combined to return one instance with the total thickness.

Returns

dict Entries are meterials with the total thickness for that material.

Expand source code
def get_transmission_by_material(self):
    """
    Get the contribution to the transmission by total thickness for each material.

    Layers of the same materials are combined to return one instance with the total thickness.

    Returns
    -------
    `dict`
        Entries are meterials with the total thickness for that material.
    """
    material_thickness = dict()
    for name, layers in COMPONENTS.items():
        for material_name, thickness in layers:
            if material_name == 'solarblack':
                material_name = self.solarblack
            if material_name in material_thickness.keys():
                material_thickness[material_name] += thickness.to('mm')
            else:
                material_thickness[material_name] = thickness.to('mm')
    res = {}
    for name, thickness in material_thickness.items():
        frac_mass, density = self.materials[name]
        mat = self.create_material(name=name,
                                   fractional_masses=frac_mass,
                                   density=density,
                                   thickness=thickness)
        res[name] = mat

    return res