Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add slot overlappogram configuration #31

Merged
merged 7 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading