import numpy as np
import astropy.units as u
import named_arrays as na
import optika
from esis.flights.f1.optics import gratings
__all__ = [
"ruling_design",
"ruling_measurement",
]
[docs]
def ruling_design(
num_distribution: int = 11,
) -> optika.rulings.AbstractRulings:
"""
Load a model of the as-designed rulings for the diffraction gratings.
Parameters
----------
num_distribution
The number of Monte Carlo samples to draw when computing uncertainties.
Examples
--------
Plot the efficiency of the rulings over the EUV wavelength range.
.. jupyter-execute::
import numpy as np
import matplotlib.pyplot as plt
import astropy.units as u
import astropy.visualization
import named_arrays as na
import optika
from esis.flights.f1.optics import gratings
# Define an array of wavelengths with which to sample the efficiency
wavelength = na.geomspace(500, 700, axis="wavelength", num=1001) * u.AA
# Define the incidence angle to be the same as the Horiba technical proposal
angle = 1.3 * u.deg
# Define the incident rays from the wavelength array
rays = optika.rays.RayVectorArray(
wavelength=wavelength,
position=0 * u.mm,
direction=na.Cartesian3dVectorArray(
x=np.sin(angle),
y=0,
z=np.cos(angle),
),
)
# Initialize the ESIS diffraction grating ruling model
rulings = gratings.rulings.ruling_design()
# Compute the efficiency of the grating rulings
efficiency = rulings.efficiency(
rays=rays,
normal=na.Cartesian3dVectorArray(0, 0, -1),
)
# Plot the efficiency vs wavelength
with astropy.visualization.quantity_support():
fig, ax = plt.subplots(constrained_layout=True)
na.plt.plot(wavelength, efficiency, ax=ax);
ax.set_xlabel(f"wavelength ({ax.get_xlabel()})");
ax.set_ylabel("efficiency");
"""
density = na.UniformUncertainScalarArray(
nominal=(2.586608603456000 / u.um).to(1 / u.mm),
width=1 / u.mm,
num_distribution=num_distribution,
)
return optika.rulings.RectangularRulings(
spacing=optika.rulings.Polynomial1dRulingSpacing(
coefficients={
0: 1 / density,
1: na.UniformUncertainScalarArray(
nominal=-3.3849e-5 * (u.um / u.mm),
width=0.0512e-5 * (u.um / u.mm),
num_distribution=num_distribution,
),
2: na.UniformUncertainScalarArray(
nominal=-1.3625e-7 * (u.um / u.mm**2),
width=0.08558e-7 * (u.um / u.mm**2),
num_distribution=num_distribution,
),
},
normal=na.Cartesian3dVectorArray(1, 0, 0),
),
depth=na.UniformUncertainScalarArray(
nominal=15 * u.nm,
width=2 * u.nm,
num_distribution=num_distribution,
),
ratio_duty=na.UniformUncertainScalarArray(
nominal=0.5,
width=0.1,
num_distribution=num_distribution,
),
diffraction_order=-1,
)
[docs]
def ruling_measurement(
num_distribution: int = 11,
):
"""
Load a model of the rulings based on the total efficiency of the gratings.
The total efficiency of the gratings is given by
:func:`~esis.flights.f1.optics.gratings.efficiencies.efficiency_vs_wavelength()`,
and the efficiency of the multilayer coating is given by
:func:`~esis.flights.f1.optics.gratings.materials.multilayer_fit()`.
This function takes the ratio of those two functions to estimate the
efficiency of the rulings.
Parameters
----------
num_distribution
The number of Monte Carlo samples to draw when computing uncertainties.
Examples
--------
Compare the as-designed efficiency of the rulings to the measured efficiency
of the rulings.
.. jupyter-execute::
import numpy as np
import matplotlib.pyplot as plt
import astropy.units as u
import astropy.visualization
import named_arrays as na
import optika
from esis.flights.f1.optics import gratings
# Define an array of wavelengths with which to sample the efficiency
wavelength = na.geomspace(500, 700, axis="wavelength", num=1001) * u.AA
# Define the incidence angle to be the same as the Horiba technical proposal
angle = 1.3 * u.deg
# Define the incident rays from the wavelength array
rays = optika.rays.RayVectorArray(
wavelength=wavelength,
position=0 * u.mm,
direction=na.Cartesian3dVectorArray(
x=np.sin(angle),
y=0,
z=np.cos(angle),
),
)
# Define the surface normal
normal = na.Cartesian3dVectorArray(0, 0, -1)
# Initialize the ESIS diffraction grating ruling model
ruling_design = gratings.rulings.ruling_design(num_distribution=0)
ruling_measurement = gratings.rulings.ruling_measurement(num_distribution=0)
# Compute the efficiency of the grating rulings
efficiency_design = ruling_design.efficiency(
rays=rays,
normal=normal,
)
efficiency_measurement = ruling_measurement.efficiency(
rays=rays,
normal=normal,
)
# Plot the efficiency vs wavelength
with astropy.visualization.quantity_support():
fig, ax = plt.subplots(constrained_layout=True)
na.plt.plot(
wavelength,
efficiency_design,
ax=ax,
color="tab:blue",
label="design",
);
na.plt.plot(
wavelength,
efficiency_measurement,
ax=ax,
color="tab:orange",
label="measurement",
);
ax.set_xlabel(f"wavelength ({ax.get_xlabel()})");
ax.set_ylabel("efficiency");
ax.legend();
"""
design = ruling_design(num_distribution=num_distribution)
density = na.UniformUncertainScalarArray(
nominal=2585.5 / u.mm,
width=1 / u.mm,
num_distribution=num_distribution,
)
spacing = design.spacing
spacing.coefficients[0] = 1 / density
efficiency_total = gratings.efficiencies.efficiency_vs_wavelength()
wavelength = efficiency_total.inputs.wavelength
angle = efficiency_total.inputs.direction
coating = gratings.materials.multilayer_fit()
efficiency_coating = coating.efficiency(
rays=optika.rays.RayVectorArray(
wavelength=wavelength,
direction=na.Cartesian3dVectorArray(
x=np.sin(angle),
y=0,
z=np.cos(angle),
),
),
normal=na.Cartesian3dVectorArray(0, 0, -1),
)
efficiency_rulings = na.FunctionArray(
inputs=efficiency_total.inputs,
outputs=efficiency_total.outputs / efficiency_coating,
)
return optika.rulings.MeasuredRulings(
spacing=spacing,
diffraction_order=design.diffraction_order,
efficiency_measured=efficiency_rulings,
)