---
# System prepended metadata

title: xrtpy/aiapy Common API Discussion

---

# xrtpy/aiapy Common API Discussion

PyHC Meeting, 5/18/2023


From previous discussion: https://docs.google.com/presentation/d/1WQyYBwFqu4FGfcRTr11sNQj2O5PiYuJJTak45QjiPKk/edit?usp=sharing

Nick's previous pr: https://github.com/sunpy/sunkit-instruments/pull/98/files

## Theory

We want to compute temperature response for an instrument with some wavelength bandpass $c$,

$$
K_c(T) = \int\mathrm{d}\lambda S(\lambda,T)R_c(\lambda),\quad[\mathrm{DN}\,\mathrm{cm}^5\,\mathrm{pix}^{-1}\,\mathrm{s}^{-1}]
$$

where,

- $S(\lambda,T)$ spectra [photon cm$^3$ s$^{-1}$ sr$^{-1}$ Å$^{-1}$]
- $R_c(\lambda)$ wavelength response function for channel $c$ [cm$^2$ DN sr photon$^{-1}$ pixel$^{-1}$]

We need to come up with a common API for computing $S$ and $R_c$.
Both `aiapy` and `xrtpy` already have a conception for $R_c$ but it is slightly different.
Coming up with $S$ is a bit more challenging because there are more possible variations and it is not yet clear how to parameterize it.

## Joe's conception

```python=
class dumb_channel(object):

    def __init__(self, waves, ea, time=0.0):
        self.waves=waves
        self.ea=ea
        self.time=time
        
    def just_give_me_the_dumb_ea(self):
        return self.waves,self.ea
        
class dumb_tresp(object):
    
    def __init__(self, waves, temp, iso_spectra):
        self.waves=waves
        self.temp=temp
        self.ispec = iso_spectra
        self.n_atoms = iso_spectra.shape[0]
        self.nt = iso_spectra.shape[1]
        self.nw = iso_spectra.shape[2]
        self.dw = waves[1]-waves[0]
        
    def compute(self, channel, abundances=None):
        if(abundances is None): abundances = np.ones(self.n_atoms)
        tresp = np.zeros(self.nt)
        ea_waves, ea = channel.just_give_me_the_dumb_ea()
        ea_interp = np.interp(self.waves,ea_waves,ea)
        for i in range(0,self.nt):
            for j in range(0,self.n_atoms):
                tresp[i] += np.sum(ea_interp*self.ispec[j,i]*self.dw*abundances[j])
            
        return self.temp, tresp
```

## More general/abstract interface

```python=
class AbstractChannel:
    def ccd_gain_left(self) -> u.electron / u.DN:...
    
    def effective_area(self):  # ?????

class AbstractSpectralModel:
    def __init__(self, wavelength, flux, temperature): ...
    #Different variations of abundance
    
    def 

class AbstractTemperatureResponse:  # do we need this?
    
    def __init__(self, obs_date, spectral_model: AbstractSpectralModel): ...
        # will need to define what spectral_model means
    
    def wavelength(self) -> u.Angstrom: ...
        
    def effective_area(self) -> u.cm ** 2: ...
        
    def temperature_response(self) -> u.DN * u.cm**5 / (u.s * u.pix): ...    
        # units from xrtpy 
        
def temperature_response(
    spectral_model: AbstractSpectralModel, 
    channel: AbstractChannel, 
    temperature: Optional[u.Quantity[u.K]] = None,
):
    ...
```

```pycon=
>>> channel = Channel(...)
>>> spectral_model = SpectralModel(...)
>>> tr = TemperatureResponse(channel, spectral_model)
>>> temperature_response = tr.temperature_response()  # or
>>> temperature_response = temperature_response(spectral_model, channel)
```

## Open Questions

- how should the temperature response be calculated
    - object? Jon: makes sense to have temperature response separate since it combines the effective area and spectra
    - method on channel?
    - or separate function?
- how do we deal with time dependence?
    - input to Channel object
    - or input to the `effective_area` method?

## Notes

- Adding unit annotations will help disambiguate between the different definitions for effective area, etc.
    - Aside: Follow up with definitionns for `PhysicalType` objects in `astropy.units` to sort of standardize this?
- `ElementalAbundances` in `plasmapy.particles`? Already in `IonizationStateCollection`.
- Will's thought: could a `temperature_response` function (or object) live upstream? Is there any reason to implement it in each package? If the respective classses expose the right bits of data, it is just a matter of computing the integral above

## Vocabulary

- *effective area*: function of wavelength, must be in units of area; does not include any time-dependent corrections [cm$^2$]
- *gain*: function of wavelength, conversion between DN and photons [DN photon$^{-1}$]
- *plate scale*: steradians per pixel [sr pixel$^{-1}$]
- *wavelength response*: product of effective area, gain, and plate scale that may also include some time-dependent correction; essentially this should include every instrument dependent parameter in the integrand in the above integral [cm$^2$ DN sr photon$^{-1}$ pixel$^{-1}$]

## Conclusions

- It is helpful to have a `Channel` class
- Proposed that `xrtpy` can move `EffectiveAreaFundamental` to a method of `Channel` rather than a standalone object
- Proposed that `xrtpy` can move `TemperatureresponseFundamental` to a standalone function
- It is helpful to have a `SpectralModel` class
- Implement `temperature_response` in `sunkit-instruments` directly? If the interfaces are well-defined, then it could be the same across instruments.
- 

