Skip to content

Commit

Permalink
Add slot overlappogram configuration (#31)
Browse files Browse the repository at this point in the history
* Adding method to plot the detector layout

* adding short slot instrument config

* Adding 3rd dimension to moxsi_slot ref_pix and mergin with main.

* Fixing slot aperture to be a circle at either end rather than a rectangle

* separate out slot config

* refactor detector layout plot function

---------

Co-authored-by: Will Barnes <will.t.barnes@gmail.com>
  • Loading branch information
jacobdparker and wtbarnes authored Jan 23, 2024
1 parent c737917 commit fc57e1e
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 10 deletions.
1 change: 1 addition & 0 deletions mocksipipeline/instrument/configuration/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from .moxsi_short import *
from .moxsi_slot import *
1 change: 1 addition & 0 deletions mocksipipeline/instrument/configuration/moxsi_short.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
'moxsi_short_spectrogram',
]


# Build needed filters
table = 'Chantler'
be_thin = ThinFilmFilter('Be', thickness=8 * u.micron, xrt_table=table)
Expand Down
108 changes: 108 additions & 0 deletions mocksipipeline/instrument/configuration/moxsi_slot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""
Configurations for MOXSI with slot and pinhole overlappogram
"""
import astropy.units as u
import numpy as np

from mocksipipeline.instrument.design import InstrumentDesign
from mocksipipeline.instrument.optics.aperture import (CircularAperture,
SlotAperture)
from mocksipipeline.instrument.optics.configuration import short_design
from mocksipipeline.instrument.optics.filter import ThinFilmFilter
from mocksipipeline.instrument.optics.response import Channel

__all__ = [
'moxsi_slot',
'moxsi_slot_filtergrams',
'moxsi_slot_spectrogram_pinhole',
'moxsi_slot_spectrogram_slot',
]

# Build needed filters
table = 'Chantler'
be_thin = ThinFilmFilter('Be', thickness=8 * u.micron, xrt_table=table)
be_med = ThinFilmFilter('Be', thickness=30 * u.micron, xrt_table=table)
be_thick = ThinFilmFilter('Be', thickness=350 * u.micron, xrt_table=table)
polymide = ThinFilmFilter(elements=['C', 'H', 'N', 'O'],
quantities=[22, 10, 2, 5],
density=1.43 * u.g / u.cm ** 3,
thickness=1 * u.micron,
xrt_table=table)
al_thin = ThinFilmFilter(elements='Al', thickness=142.5 * u.nm, xrt_table=table)
al_oxide = ThinFilmFilter(elements=['Al', 'O'], quantities=[2, 3], thickness=7.5 * u.nm, xrt_table=table)

# Set up apertures
pinhole = CircularAperture(44*u.micron)
slot = SlotAperture(diameter=pinhole.diameter,
center_to_center_distance=9*np.pi*pinhole.diameter/4)

# Set up reference pixels
detector_center = np.array(short_design.detector_shape)[::-1] / 2
# Values pulled from mechanical drawing 1/11/24
filtergram_y = 3.51 * u.mm
filtergram_x = (np.arange(4) * 3.32 - 1.5 * 3.32) * u.mm
slot_y = 3.43 * u.mm
filtergram_ref_pix = [
tuple(np.array([x / short_design.pixel_size_x,
filtergram_y / short_design.pixel_size_y]) + detector_center)
for x in filtergram_x
]
pinhole_ref_pix = tuple(detector_center)
slot_ref_pix = tuple(detector_center - [0, slot_y / short_design.pixel_size_y])

# Set up filtergrams
filtergram_1 = Channel(name='filtergram_1',
order=0,
filters=be_thin,
reference_pixel=(filtergram_ref_pix[0]+(0,))*u.pix,
design=short_design,
aperture=pinhole)
filtergram_2 = Channel(name='filtergram_2',
order=0,
filters=be_med,
reference_pixel=(filtergram_ref_pix[1]+(0,))*u.pix,
design=short_design,
aperture=pinhole)
filtergram_3 = Channel(name='filtergram_3',
order=0,
filters=be_thick,
reference_pixel=(filtergram_ref_pix[2]+(0,))*u.pix,
design=short_design,
aperture=pinhole)
filtergram_4 = Channel(name='filtergram_4',
order=0,
filters=[al_oxide, al_thin, polymide],
reference_pixel=(filtergram_ref_pix[3]+(0,))*u.pix,
design=short_design,
aperture=pinhole)

# Set up spectrograms
orders = [-4, -3, -2, -1, 0, 1, 2, 3, 4]
spectrograms_pinhole = []
spectrograms_slot = []
for order in orders:
spectrograms_pinhole.append(
Channel(name='spectrogram_pinhole',
order=order,
filters=[al_thin, al_oxide],
reference_pixel=(pinhole_ref_pix+(0,))*u.pix,
design=short_design,
aperture=pinhole)
)
spectrograms_slot.append(
Channel(name='spectrogram_slot',
order=order,
filters=[al_thin, al_oxide],
reference_pixel=(slot_ref_pix+(0,))*u.pix,
design=short_design,
aperture=slot)
)

# Build instrument configurations
moxsi_slot_filtergrams = InstrumentDesign(
'slot-filtergrams',
[filtergram_1, filtergram_2, filtergram_3, filtergram_4]
)
moxsi_slot_spectrogram_pinhole = InstrumentDesign('slot-dispersed-pinhole', spectrograms_pinhole)
moxsi_slot_spectrogram_slot = InstrumentDesign('slot-dispersed-slot', spectrograms_slot)
moxsi_slot = moxsi_slot_filtergrams + moxsi_slot_spectrogram_pinhole + moxsi_slot_spectrogram_slot
26 changes: 26 additions & 0 deletions mocksipipeline/instrument/design.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
import dataclasses
import pprint

import astropy.units as u
import matplotlib.pyplot as plt
from astropy.coordinates import SkyCoord
from sunpy.coordinates import get_earth
from sunpy.coordinates.utils import get_limb_coordinates

from mocksipipeline.instrument.optics.response import Channel


Expand All @@ -12,6 +18,26 @@ class InstrumentDesign:
name: str
channel_list: list[Channel]

def plot_detector_layout(self, observer=None, wavelength=0*u.AA, color=None):
if observer is None:
observer = get_earth('2020-01-01')
limb_coord = get_limb_coordinates(observer, resolution=100)
origin = SkyCoord(Tx=0*u.arcsec, Ty=0*u.arcsec, frame='helioprojective', observer=observer)

fig = plt.figure()
ax = fig.add_subplot()
colors = {i: f'C{i}' for i in range(5)}
for channel in self.channel_list:
_wcs = channel.get_wcs(observer)
px, py, _ = _wcs.world_to_pixel(limb_coord, wavelength)
_color=colors[abs(channel.spectral_order)] if color is None else color
ax.plot(px, py, ls='-', color=_color)
px, py, _ = _wcs.world_to_pixel(origin, wavelength)
ax.plot(px, py, ls='', marker='x', color=_color)
ax.set_xlim(0, self.optical_design.detector_shape[1])
ax.set_ylim(0, self.optical_design.detector_shape[0])
plt.show()

def __post_init__(self):
# Each channel must have the same optical design
if not all([c.design==self.channel_list[0].design for c in self.channel_list]):
Expand Down
23 changes: 13 additions & 10 deletions mocksipipeline/instrument/optics/aperture.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,35 @@ class AbstractAperture(abc.ABC):

@abc.abstractproperty
@u.quantity_input
def area(self) -> u.cm**2:
def area(self) -> u.cm ** 2:
...


class SlotAperture(AbstractAperture):
"""
Rectangular MOXSI aperture
Slot MOXSI aperture
Parameters
----------
dimensions: `~astropy.units.Quantity`
diameter: `~astropy.units.Quantity`
center_to_center_distance: `~astropy.units.Quantity`
"""

@u.quantity_input
def __init__(self, dimensions: u.cm):
self.dimensions = dimensions
def __init__(self, diameter: u.cm, center_to_center_distance: u.cm):
self.diameter = diameter
self.center_to_center_distance = center_to_center_distance

@property
@u.quantity_input
def area(self) -> u.cm**2:
return self.dimensions[0]*self.dimensions[1]
def area(self) -> u.cm ** 2:
return np.pi * (self.diameter/2) ** 2 + self.diameter * self.center_to_center_distance

def __repr__(self):
return f"""Rectangular Slot Aperture
return f"""Slot Aperture
-----------------------------
dimensions: {self.dimensions}
diameter: {self.diameter}
center to center distance: {self.center_to_center_distance}
area: {self.area}
"""

Expand All @@ -61,7 +64,7 @@ def __init__(self, diameter: u.cm):

@property
@u.quantity_input
def area(self) -> u.cm**2:
def area(self) -> u.cm ** 2:
return np.pi * (self.diameter / 2) ** 2

def __repr__(self):
Expand Down

0 comments on commit fc57e1e

Please sign in to comment.