From 3e51130ba72280b2d2b41ef79cfceac8ced398ef Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sun, 17 Mar 2024 23:57:01 -0600 Subject: [PATCH] Added `esis.flights.f1.optics.gratings.materials.multilayer_fit()` factory function. --- .../optics/gratings/materials/_materials.py | 129 ++++++++++++++++++ .../gratings/materials/_materials_test.py | 5 + 2 files changed, 134 insertions(+) diff --git a/esis/flights/f1/optics/gratings/materials/_materials.py b/esis/flights/f1/optics/gratings/materials/_materials.py index 3d28e1a..cc60fc4 100644 --- a/esis/flights/f1/optics/gratings/materials/_materials.py +++ b/esis/flights/f1/optics/gratings/materials/_materials.py @@ -10,6 +10,7 @@ "multilayer_design", "multilayer_witness_measured", "multilayer_witness_fit", + "multilayer_fit", ] @@ -432,3 +433,131 @@ def _func(x: np.ndarray): ) return _multilayer(*fit.x) + + +def multilayer_fit() -> optika.materials.MultilayerMirror: + """ + A multilayer coating determined by modifying :func:`multilayer_witness_fit` + to have a glass substrate with the appropriate roughness. + + Examples + -------- + + Plot the theoretical reflectivity of this multilayer stack vs. the + theoretical reflectivity of :func:`multilayer_witness_fit`. + + .. jupyter-execute:: + + import numpy as np + import matplotlib.pyplot as plt + import astropy.units as u + import named_arrays as na + import optika + from esis.flights.f1.optics import gratings + + # Define a grid of wavelength samples + wavelength = na.geomspace(100, 1000, axis="wavelength", num=1001) * u.AA + + # Define a grid of incidence angles + angle = 4 * u.deg + + # Define the light rays incident on the multilayer stack + rays = optika.rays.RayVectorArray( + wavelength=wavelength, + direction=na.Cartesian3dVectorArray( + x=np.sin(angle), + y=0, + z=np.cos(angle), + ), + ) + + # Initialize the multilayer stacks + multilayer_witness_fit = gratings.materials.multilayer_witness_fit() + multilayer_fit = gratings.materials.multilayer_fit() + + # Define the vector normal to the multilayer stack + normal = na.Cartesian3dVectorArray(0, 0, -1) + + # Compute the reflectivity of the multilayer for the given incident rays + reflectivity_witness = multilayer_witness_fit.efficiency(rays, normal) + reflectivity_fit = multilayer_fit.efficiency(rays, normal) + + # Plot the reflectivities as a function of wavelength + fig, ax = plt.subplots(constrained_layout=True) + na.plt.plot( + wavelength, + reflectivity_witness, + ax=ax, + axis="wavelength", + label="witness fit", + ); + na.plt.plot( + wavelength, + reflectivity_fit, + ax=ax, + axis="wavelength", + label="grating fit", + ); + ax.set_xlabel(f"wavelength ({wavelength.unit:latex_inline})"); + ax.set_ylabel("reflectivity"); + ax.legend(); + + | + + Plot the reflectivity of this multilayer stack as a function of incidence angle. + + .. jupyter-execute:: + + import numpy as np + import matplotlib.pyplot as plt + import astropy.units as u + import named_arrays as na + import optika + from esis.flights.f1.optics import gratings + + # Define a grid of wavelength samples + wavelength = na.geomspace(100, 1000, axis="wavelength", num=1001) * u.AA + + # Define a grid of incidence angles + angle = na.linspace(0, 20, axis="angle", num=3) * u.deg + + # Define the light rays incident on the multilayer stack + rays = optika.rays.RayVectorArray( + wavelength=wavelength, + direction=na.Cartesian3dVectorArray( + x=np.sin(angle), + y=0, + z=np.cos(angle), + ), + ) + + # Initialize the multilayer stack + multilayer = gratings.materials.multilayer_fit() + + # Compute the reflectivity of the multilayer for the given incident rays + reflectivity = multilayer.efficiency( + rays=rays, + normal=na.Cartesian3dVectorArray(0, 0, -1), + ) + + # Plot the reflectivity of the multilayer as a function of wavelength + fig, ax = plt.subplots(constrained_layout=True) + na.plt.plot( + wavelength, + reflectivity, + ax=ax, + axis="wavelength", + label=angle, + ); + ax.set_xlabel(f"wavelength ({wavelength.unit:latex_inline})"); + ax.set_ylabel("reflectivity"); + ax.legend(); + """ + result = multilayer_design() + witness = multilayer_witness_fit() + + result.layers = witness.layers + + result.substrate.interface.width = 0.5 * u.nm + + return result diff --git a/esis/flights/f1/optics/gratings/materials/_materials_test.py b/esis/flights/f1/optics/gratings/materials/_materials_test.py index b0a7db8..f888eb6 100644 --- a/esis/flights/f1/optics/gratings/materials/_materials_test.py +++ b/esis/flights/f1/optics/gratings/materials/_materials_test.py @@ -15,3 +15,8 @@ def test_multilayer_witness_measured(): def test_multilayer_witness_fit(): r = esis.flights.f1.optics.gratings.materials.multilayer_witness_fit() assert isinstance(r, optika.materials.MultilayerMirror) + + +def test_multilayer_fit(): + r = esis.flights.f1.optics.gratings.materials.multilayer_fit() + assert isinstance(r, optika.materials.MultilayerMirror)