From 7984544ff69b180795c645e4807e3aaacca3732f Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sat, 20 Jul 2024 02:02:54 -0600 Subject: [PATCH] Added `esis.flights.f1.optics.gratings.rulings.ruling_measurement()`. (#6) --- .../f1/optics/gratings/rulings/_rulings.py | 143 +++++++++++++++++- .../optics/gratings/rulings/_rulings_test.py | 5 + 2 files changed, 147 insertions(+), 1 deletion(-) diff --git a/esis/flights/f1/optics/gratings/rulings/_rulings.py b/esis/flights/f1/optics/gratings/rulings/_rulings.py index 637c026..2174bad 100644 --- a/esis/flights/f1/optics/gratings/rulings/_rulings.py +++ b/esis/flights/f1/optics/gratings/rulings/_rulings.py @@ -1,9 +1,12 @@ -import named_arrays as na +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", ] @@ -13,6 +16,11 @@ def ruling_design( """ The as-designed rulings for the ESIS diffraction gratings. + Parameters + ---------- + num_distribution + The number of Monte Carlo samples to draw when computing uncertainties. + Examples -------- @@ -87,10 +95,143 @@ def ruling_design( 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, ) + + +def ruling_measurement( + num_distribution: int = 11, +): + """ + A model of the rulings where the efficiency has been calculated using the + ratio of the total efficiency of the gratings to the efficiency of the + multilayer coatings. + + 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()`. + + 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 + 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 ({wavelength.unit:latex_inline})"); + 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, + ) diff --git a/esis/flights/f1/optics/gratings/rulings/_rulings_test.py b/esis/flights/f1/optics/gratings/rulings/_rulings_test.py index bca16de..ecdeffd 100644 --- a/esis/flights/f1/optics/gratings/rulings/_rulings_test.py +++ b/esis/flights/f1/optics/gratings/rulings/_rulings_test.py @@ -5,3 +5,8 @@ def test_ruling_design(): r = esis.flights.f1.optics.gratings.rulings.ruling_design() assert isinstance(r, optika.rulings.AbstractRulings) + + +def test_ruling_measurement(): + r = esis.flights.f1.optics.gratings.rulings.ruling_measurement() + assert isinstance(r, optika.rulings.AbstractRulings)