from nbtemplate import display_header, get_path
display_header('Channel_offset.ipynb', status='Channel positioning for Arcus 2023')
Last revision in version control:
This document is git version controlled. The repository is available at https://github.com/hamogu/arcus. See git commit log for full revision history.
Code was last run with:
import numpy as np
import astropy.units as u
import matplotlib.pyplot as plt
import matplotlib.patches as patches
%matplotlib inline
from transforms3d.affines import decompose
from marxs.missions.arcus.arcus import DetCamera, defaultconf
from marxs.missions.arcus.ralfgrating import load_table
cam = DetCamera(defaultconf)
gratings = load_table('gratings', 'facets')
I suggest to stagger Arcus channels 5 mm apart. To do that, one camera is offset by 10 mm with respect to the other (or in other words: each is offset 5 mm with respect to some nominal camera position), and each of the two channels with the same "nominal optical axes" is offset by +/- 2.5 mm with respect to that common, nominal optical axis.
Arcus has four different channels, two dispersing in positive x direction and two dispersing in negative x direction. These channels are offset from each other in y (cross-dispersion) direction so that the traces can be identified separately on the CCDs. Each channel has gaps in its effective area when the photons hit a chip gap. For a fully symmetric layout, the four channels would have chip gaps at the same wavelengths, leading to gaps in wavelength coverage. For a steady pointing, this could lead to gaps in wavelength coverage. To remedy this problem, the CCDS are not placed symmetrically. One camera of 8 CCDs is placed a few mm further away from the geometric center of the focal plane. This is possible without a loss of resolving power, if the cameras are not exact copies, but the are CCDs located at slightly different heights in each camera to account for their different position on the Rowland torus. This is the arrangement we proposed in 2018.
With this arrangement, only two channels have identical chip gaps. In this notebook, we look at moving the two channels that disperse in the same direction with respect to each other, so that chip gaps are distributed such that two channels never have a chip gap at the same position.
The widest gap from pixel edge to pixel edge occurs for chip gaps that are placed below the frame of the optical blocking filters. These filters are only as wide as 3 CCDs, and thus three frames are needed to cover the 8 CCDs of one camera. The gap here is 4 mm wide. Adding 0.5 mm margin on each side because the calibration is less accurate when part of the line spread function falls in a chip gap, channels need to be staggered by 5 mm. Note that displacing channels this way leads to a reduction in resolving power, because no longer are the channels using the same optical axis that is used to place the CCDs on the Rowland circle. However, a distance of a few mm is far below the tolerances.
Dithering between observations is another way to mitigate the impact of chip gaps. If the instrument dithers enough, it might not matter that the chip gaps are at the same locations for boresight pointing for two channels. A gap of 5 mm in the focal plane corresponds to the following dither on the sky:
(np.sin(5/12000) * u.rad).to(u.arcmin)
That's much larger than the "natural dither" that we expect from the aspect control. So, for dither to smooth out chip gaps, Arcus would have to implement some deliberate dithering strategy.
At short wavelength, each wavelength is observed in more than one order, e.g. for 20 Å, Arcus will see order -6 and -7. When order -7 hits a chip gap, order -6 is still available. However, much of the signal is concentrated in just one order (-7 for 20 Å) and above 40 Å we see only one order anyway.
There are two different ways to look at the focal plane. One is in physical space in mm, the other in wavelength (or more precisely $m\lambda$) space. To look at the latter, I imagine a grating on-axis and calculate which dispersion angles $\phi$ hit chips in the focal plan and which do not. Since the grating equation is $m\lambda = d\;sin(\phi)$ where $d$ is the grating constant, it natural to plot $m\lambda$. In this metric, the position of the chip gaps is a the same for all orders.
def _plot_cam_geom(ax, camera, conf, xlim):
for e in camera.elements:
trans, rot, zoom, shear = decompose(e.pos4d)
det = patches.Rectangle(trans[[0, 1]] - zoom[[1,2]], zoom[1] * 2, zoom[2] * 2,
linewidth=1, edgecolor='orange', facecolor='orange', label='detector')
ax.add_patch(det)
for channame, chan in conf['pos_opt_ax'].items():
x, y = chan[:2]
if 'm' in channame:
ax.annotate("", xy=(xlim[0], y + 0), xycoords='data',
xytext=(min(x, xlim[1]), y), textcoords='data',
arrowprops=dict(arrowstyle="->",
connectionstyle="arc3",
ls=':', lw=3, ec='0.5'),
)
else:
ax.annotate("", xy=(xlim[1], y + 0), xycoords='data',
xytext=(max(x, xlim[0]), y), textcoords='data',
arrowprops=dict(arrowstyle="->",
connectionstyle="arc3",
ls=':', lw=3, ec='0.5'),
)
if (x > xlim[0]) and (x < xlim[1]):
ax.annotate("",
xy=(min(x + 0.1 * (xlim[1] - xlim[0]), xlim[1]), y + 0),
xycoords='data',
xytext=(x, y), textcoords='data',
arrowprops=dict(arrowstyle="simple",
connectionstyle="arc3", ec='g', fc='g'),
)
ax.annotate("",
xy=(x + 0, y + 20),
xycoords='data',
xytext=(x, y), textcoords='data',
arrowprops=dict(arrowstyle="simple",
connectionstyle="arc3", ec='b'),
)
optax = ax.plot([x], [y], 'bp', label='opt. axis', ms=10, zorder=10)
ax.set_ylim(-15, 30)
ax.set_xlim(xlim)
ax.set_xlabel('distance [mm]')
ax.set_ylabel('distance [mm]')
def mlam_boundaries(camera, opt_ax_x, opt_ax_f):
phi = np.zeros((len(camera.elements), 2))
for i, e in enumerate(camera.elements):
for j, fac in enumerate([-1, 1]):
camedge = e.geometry['center'] + fac * e.geometry['v_y']
phi[i, j] = np.arctan2(opt_ax_x + camedge[0], opt_ax_f + camedge[2])
return (np.sin(phi) * gratings['period'].mean() * gratings['period'].unit).to(u.Angstrom)
def _plot_mlam(ax, camera, conf):
labels = list(conf['pos_opt_ax'].keys())
for i, k in enumerate(conf['pos_opt_ax'].values()):
out = mlam_boundaries(camera, k[0], conf['f'])
pos_width = out.value.copy()
pos_width[:, 1] = pos_width[:, 1] - pos_width[:, 0]
# turn around everything for channels that disperse the other way
if 'm' in labels[i]:
pos_width = -pos_width
ax.broken_barh(pos_width, (i, .8), color='y')
ax.set_xlabel('m$\lambda [\AA]$')
ax.set_label('channel')
ax.set_yticks([.5, 1.5, 2.5, 3.5])
ax.set_yticklabels(labels)
def plot_mlam(camera, conf):
fig, axes = plt.subplots(nrows=2, figsize=(10, 5), constrained_layout=True)
for ax in axes:
_plot_mlam(ax, camera, conf)
axes[1].set_xlim(-160, -80)
axes[1].set_title('Blaze peak')
axes[0].set_xlim(-40, 40)
axes[0].set_title('Zero order and low orders')
axes[0].axvline(x=0, color='b')
return fig, axes
def plot_camgeom(camera, conf):
fig, axes = plt.subplots(nrows=2, figsize=(10, 5), constrained_layout=True,
subplot_kw={'aspect': 'equal'})
for ax, xlim in zip(axes, [[-400, -220], [220, 400]]):
_plot_cam_geom(ax, camera, conf, xlim)
axes[0].set_title('camera 1')
axes[1].set_title('camera 2')
return fig, axes
arcus = {'f': defaultconf['f'],
'pos_opt_ax': {'1': [-377.5, -7.5, 0. , 1. ],
'1m': [371.5, -2.5, 0. , 1. ],
'2': [-371.5, 2.5, 0. , 1. ],
'2m': [377.5, 7.5, 0. , 1. ]},
}
fig, axes = plt.subplots(figsize=(10, 2), constrained_layout=True)
_plot_cam_geom(axes, cam, defaultconf, [-700, 700])
The figure above shows the physical layout of the CCDs (orange) in the focal plane. Given the resolution of the figure, most chip gaps are not visible. Also note, that the x and the y axis are scaled differently. The purpose if this figure is only to give a high-level overview of the geometry. Two cameras with eight CCDs each are located in the focal plane. In this plot, 0 is the geometric center of all the Rowland tori involved. Note that the cameras are intentionally not symmetric to 0.
The position of the four optical axes for the four channels is marked with a blue pentagon. The $x$-axes (green arrows) and the $y$-axes (blue arrows) are parallel to each other and point in the same direction. This means that the $x$ value in optical channel coordinate systems (OCCS) 1 and 2 (origin on the left) increases along dispersed spectrum (gray dashed arrow) while the $x$ values in OCCS 1m and 2m (origin on the right) decreases because the direction of dispersion is opposite to that of channel 1 and 2. All coordinate systems are right-handed and thus both $z$-axes point out of the plane of the drawing.
In the following, instead of showing the whole focal plane, I zoom in to the two regions close to the optical axes in the two cameras.
fig, axes = plot_camgeom(cam, arcus)
The plot shows the proposed layout of the focal plane. We have a 10 mm offset between the two cameras (note that the position of the gaps between camera 1 and 2 in the plot differs by 10 mm) and we offset the two channels that have their zeroth orders on the same chip by +/- 2.5 mm, such that they are spaced 5 mm apart. With this layout, all zeroth orders are still at a comfortable distance from the chip edges and thus a slight (less than a few armin in $x$ direction) error in the target acquisition will still place all zeroth orders on a CCD.
fig, axes = plot_mlam(cam, arcus)
The $m\lambda$ plots shows that in this layout, chip gaps do not overlap.
I suggest to stagger Arcus channels 5 mm apart. To do that, one camera is offset by 10 mm with respect to the other (or in other words: each is offset 5 mm with respect to some nominal camera position), and each of the two channels with the same "nominal optical axes" is offset by +/- 2.5 mm with respect to that common, nominal optical axis.