Author: Yannick Copin y.copin@ipnl.in2p3.fr
We apply the naive spectrograph model (i.e. including a grism in a collimated beam) to the NISP instrument.
In [1]:
# Technical stuff related to the Jupyter notebook
%load_ext autoreload
%autoreload 2
%matplotlib inline
import mpld3
mpld3.enable_notebook()
import warnings
warnings.filterwarnings("ignore")
In [2]:
import numpy as N
from matplotlib import pyplot as P
from spectrogrism import spectrogrism as S
from spectrogrism import nisp
In [3]:
datapath = "../spectrogrism/data/"
simulations = S.Configuration([
("name", "Zemax"),
(1, datapath + "run_190315.dat"), # 1st-order dispersed simulation
(0, datapath + "run_011115_conf2_o0.dat"), # 0th-order dispersed simulation
(2, datapath + "run_161115_conf2_o2.dat"), # 2nd-order dispersed simulation
('J', datapath + "run_071215_conf6_J.dat"), # J-band undispersed simulation
])
print(simulations)
In [4]:
zmx_pos = nisp.ZemaxPositions(simulations)
print(zmx_pos)
Plot input sources:
In [5]:
ax = zmx_pos.plot_input()
In [6]:
simcfg = zmx_pos.get_simcfg() # Simulation configuration
print(simcfg)
In [7]:
# Optical modeling
optcfg = nisp.NISP_R # Optical configuration (default NISP)
print(optcfg)
In [8]:
spectro = S.Spectrograph(optcfg,
telescope=S.Telescope(optcfg))
print(spectro)
In [9]:
print(" Spectrograph round-trip test ".center(70, '-'))
for mode in simcfg.get('modes', (1, 0, 2)):
if not spectro.test(simcfg.get_wavelengths(optcfg), mode=mode, verbose=False):
warnings.warn("Order #{}: backward modeling does not match.".format(mode))
else:
print("{}: OK".format(S.str_mode(mode)))
In [10]:
spe_pos = spectro.predict_positions(simcfg, orders=zmx_pos.orders)
spe_pos.test_compatibility(zmx_pos) # Would raise IndexError if incompatible
In [11]:
subsampling = 3 # Subsample output plot
kwargs = dict(s=20, edgecolor='k', linewidths=1) # Outlined symbols
ax = zmx_pos.plot_output(modes=zmx_pos.orders, subsampling=subsampling, **kwargs)
kwargs = {} # Default
for order in zmx_pos.orders:
# Compute RMS on spectra positions
rms = zmx_pos.compute_rms(spe_pos, mode=order)
print("Order #{} RMS = {:.4f} mm = {:.2f} px".format(order, rms / 1e-3, rms / spectro.detector.pxsize))
spe_pos.plot(ax=ax, zorder=0, # Draw below Zemax
modes=(order,),
subsampling=subsampling,
label="{} #{} (RMS={:.1f} px)".format(spe_pos.name, order, rms / spectro.detector.pxsize),
**kwargs)
ax.axis([-100, +100, -100, +100]) # [mm]
ax.set_aspect('equal', adjustable='datalim')
ax.legend(fontsize='small', frameon=True, framealpha=0.5, title='')
ax.figure.set_size_inches(12, 10)
In [12]:
fig, axs = P.subplots(1, 3)
# Position offset quiver plots
for ax, order in zip(axs.ravel(), zmx_pos.orders):
zmx_pos.plot_offsets(spe_pos, ax=ax, mode=order)
ax.set_aspect('equal', adjustable='box')
ax.set_title("Order #{}".format(order))
fig.set_size_inches(12, 5)
In [13]:
phot_pos = spectro.predict_positions(simcfg, modes=zmx_pos.bands)
phot_pos.test_compatibility(zmx_pos)
kwargs = dict(s=20, edgecolor='k', linewidths=1) # Outlined symbols
ax = zmx_pos.plot_output(modes=zmx_pos.bands, **kwargs)
kwargs = {} # Default
for band in zmx_pos.bands:
# Compute RMS on positions
rms = zmx_pos.compute_rms(phot_pos, mode=band)
print("Band {} RMS = {:.4f} mm = {:.2f} px".format(
band, rms / 1e-3, rms / spectro.detector.pxsize))
phot_pos.plot(ax=ax, zorder=0, # Draw below Zemax
modes=(band,),
label="{} {} (RMS={:.1f} px)".format(
phot_pos.name, band, rms / spectro.detector.pxsize),
**kwargs)
ax.axis([-100, +100, -100, +100]) # [mm]
ax.set_aspect('equal', adjustable='datalim')
ax.legend(fontsize='small', frameon=True, framealpha=0.5, title='')
ax.figure.set_size_inches(12, 10)