Skip to content

Commit bf15bd2

Browse files
committed
Added esis.flights.f1.optics.gratings.materials.multilayer_witness_fit() function.
1 parent 39baa18 commit bf15bd2

File tree

5 files changed

+283
-10
lines changed

5 files changed

+283
-10
lines changed

docs/conf.py

+5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
'sphinx.ext.autosummary',
3737
'sphinx.ext.intersphinx',
3838
'sphinx.ext.inheritance_diagram',
39+
'sphinxcontrib.bibtex',
3940
'jupyter_sphinx',
4041
'sphinx_favicon'
4142
]
@@ -106,6 +107,10 @@
106107
# https://github.com/readthedocs/readthedocs.org/issues/2569
107108
master_doc = 'index'
108109

110+
bibtex_bibfiles = ['refs.bib']
111+
bibtex_default_style = 'plain'
112+
bibtex_reference_style = 'author_year'
113+
109114
intersphinx_mapping = {
110115
'python': ('https://docs.python.org/3', None),
111116
'numpy': ('https://numpy.org/doc/stable/', None),

docs/index.rst

+7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ launched from White Sands Missile Range on September 30th, 2019.
1111

1212
esis
1313

14+
References
15+
==========
16+
17+
.. bibliography::
18+
19+
|
20+
1421
Indices and tables
1522
==================
1623

docs/refs.bib

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
@article{Kortright1988,
2+
title={Amorphous silicon carbide coatings for extreme ultraviolet optics.},
3+
author={Jeffrey B Kortright and David L Windt},
4+
journal={Applied optics},
5+
year={1988},
6+
volume={27 14},
7+
pages={
8+
2841-6
9+
},
10+
url={https://api.semanticscholar.org/CorpusID:39248487}
11+
}
12+
13+
@article{VidalDasilva2010,
14+
author = {Vidal-Dasilva, Manuela and Aquila, Andrew L. and Gullikson, Eric M. and Salmassi, Farhad and Larruquert, Juan I.},
15+
title = "{Optical constants of magnetron-sputtered magnesium films in the 25–1300 eV energy range}",
16+
journal = {Journal of Applied Physics},
17+
volume = {108},
18+
number = {6},
19+
pages = {063517},
20+
year = {2010},
21+
month = {09},
22+
abstract = "{The transmittance of dc magnetron-sputtered Mg thin films was measured in the 25–1300 eV spectral range. Freestanding Mg films protected with Al layers were characterized ex situ. Transmittance measurements were used to obtain the extinction coefficient k of Mg films. The obtained k values along with the data available in the literature, and with interpolations and extrapolations for the rest of the spectrum, were used to obtain the real part of the index of refraction n by the Kramers–Krönig analysis. Sum-rule tests indicated a good consistency of the data.}",
23+
issn = {0021-8979},
24+
doi = {10.1063/1.3481457},
25+
url = {https://doi.org/10.1063/1.3481457},
26+
eprint = {https://pubs.aip.org/aip/jap/article-pdf/doi/10.1063/1.3481457/13212872/063517\_1\_online.pdf},
27+
}
28+
29+
@inproceedings{Rebellato2018,
30+
author = {Jennifer Rebellato and Evgueni Meltchakov and Regina Soufli and S{\'e}bastien De Rossi and Xueyan Zhang and Fr{\'e}d{\'e}ric Auch{\`e}re and Franck Delmotte},
31+
title = {{Analyses of tabulated optical constants for thin films in the EUV range and application to solar physics multilayer coatings}},
32+
volume = {10691},
33+
booktitle = {Advances in Optical Thin Films VI},
34+
editor = {Michel Lequime and H. Angus Macleod and Detlev Ristau},
35+
organization = {International Society for Optics and Photonics},
36+
publisher = {SPIE},
37+
pages = {106911U},
38+
keywords = {optical constants, soft x-rays, extreme ultraviolet, EUV, multilayer coatings, solar physics, thin films, materials},
39+
year = {2018},
40+
doi = {10.1117/12.2313346},
41+
URL = {https://doi.org/10.1117/12.2313346}
42+
}
43+

esis/flights/f1/optics/gratings/materials/_materials.py

+223-10
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
import dataclasses
22
import pathlib
33
import numpy as np
4+
import scipy
45
import astropy.units as u
56
import named_arrays as na
67
import optika
78

89
__all__ = [
910
"multilayer_design",
1011
"multilayer_witness_measured",
12+
"multilayer_witness_fit",
1113
]
1214

1315

1416
def multilayer_design() -> optika.materials.MultilayerMirror:
1517
"""
1618
The as-designed multilayer coating for the ESIS diffraction gratings.
1719
20+
Based on the analysis of :cite:t:`Rebellato2018`, this model uses
21+
:cite:t:`Kortright1988` for the silicon carbide optical constants, and
22+
:cite:t:`VidalDasilva2010` for the magnesium optical constants.
23+
1824
Examples
1925
--------
2026
@@ -66,8 +72,22 @@ def multilayer_design() -> optika.materials.MultilayerMirror:
6672
ax.set_axis_off()
6773
"""
6874

75+
layer_oxide = optika.materials.Layer(
76+
chemical="SiO2",
77+
thickness=1 * u.nm,
78+
interface=optika.materials.profiles.ErfInterfaceProfile(1 * u.nm),
79+
kwargs_plot=dict(
80+
color="gray",
81+
),
82+
x_label=1.1,
83+
)
84+
6985
layer_sic = optika.materials.Layer(
70-
chemical="SiC",
86+
chemical=optika.chemicals.Chemical(
87+
formula="SiC",
88+
is_amorphous=True,
89+
table="kortright",
90+
),
7191
thickness=10 * u.nm,
7292
interface=optika.materials.profiles.ErfInterfaceProfile(1 * u.nm),
7393
kwargs_plot=dict(
@@ -86,15 +106,18 @@ def multilayer_design() -> optika.materials.MultilayerMirror:
86106
)
87107

88108
layer_mg = optika.materials.Layer(
89-
chemical="Mg",
109+
chemical=optika.chemicals.Chemical(
110+
formula="Mg",
111+
table="fernandez_perea",
112+
),
90113
thickness=30 * u.nm,
91114
interface=optika.materials.profiles.ErfInterfaceProfile(1 * u.nm),
92115
kwargs_plot=dict(
93116
color="pink",
94117
),
95118
)
96119

97-
layer_sio2 = optika.materials.Layer(
120+
layer_substrate = optika.materials.Layer(
98121
chemical="SiO2",
99122
thickness=10 * u.mm,
100123
interface=optika.materials.profiles.ErfInterfaceProfile(1 * u.nm),
@@ -105,13 +128,13 @@ def multilayer_design() -> optika.materials.MultilayerMirror:
105128

106129
return optika.materials.MultilayerMirror(
107130
layers=[
131+
layer_oxide,
132+
layer_sic,
108133
dataclasses.replace(
109-
layer_sio2,
110-
thickness=1 * u.nm,
111-
x_label=1.1,
134+
layer_al,
135+
thickness=4 * u.nm,
136+
interface=optika.materials.profiles.ErfInterfaceProfile(1 * u.nm),
112137
),
113-
layer_sic,
114-
dataclasses.replace(layer_al, thickness=4 * u.nm),
115138
layer_mg,
116139
optika.materials.PeriodicLayerSequence(
117140
layers=[
@@ -121,9 +144,13 @@ def multilayer_design() -> optika.materials.MultilayerMirror:
121144
],
122145
num_periods=3,
123146
),
124-
dataclasses.replace(layer_al, thickness=10 * u.nm),
147+
dataclasses.replace(
148+
layer_al,
149+
thickness=10 * u.nm,
150+
interface=optika.materials.profiles.ErfInterfaceProfile(1 * u.nm),
151+
),
125152
],
126-
substrate=layer_sio2,
153+
substrate=layer_substrate,
127154
)
128155

129156

@@ -219,3 +246,189 @@ def multilayer_witness_measured() -> optika.materials.MeasuredMirror:
219246
)
220247

221248
return result
249+
250+
251+
def multilayer_witness_fit() -> optika.materials.MultilayerMirror:
252+
r"""
253+
A multilayer stack fitted to the witness sample measurements given by
254+
:func:`multilayer_witness_measured`.
255+
256+
This fit has five free parameters: the ratio of the thicknesses of
257+
:math:`\text{Mg}`, :math:`\text{Al}`, and the :math:`\text{SiC}` to their
258+
as-designed thickness, the roughness of the substrate, and a single
259+
roughness parameter for all the layers in the multilayer stack.
260+
261+
Examples
262+
--------
263+
264+
Plot the fitted vs. measured reflectivity of the grating witness samples.
265+
266+
.. jupyter-execute::
267+
268+
import numpy as np
269+
import matplotlib.pyplot as plt
270+
import astropy.units as u
271+
import astropy.visualization
272+
import named_arrays as na
273+
import optika
274+
from esis.flights.f1.optics import gratings
275+
276+
# Load the measured reflectivity of the witness samples
277+
multilayer_measured = gratings.materials.multilayer_witness_measured()
278+
measurement = multilayer_measured.efficiency_measured
279+
280+
# Isolate the angle of incidence of the measurement
281+
angle_incidence = measurement.inputs.direction
282+
283+
# Fit a multilayer stack to the measured reflectivity
284+
multilayer = gratings.materials.multilayer_witness_fit()
285+
286+
# Define the rays incident on the multilayer stack that will be used to
287+
# compute the reflectivity
288+
rays = optika.rays.RayVectorArray(
289+
wavelength=na.geomspace(250, 950, axis="wavelength", num=1001) * u.AA,
290+
direction=na.Cartesian3dVectorArray(
291+
x=np.sin(angle_incidence),
292+
y=0,
293+
z=np.cos(angle_incidence),
294+
),
295+
)
296+
297+
# Compute the reflectivity of the fitted multilayer stack
298+
reflectivity_fit = multilayer.efficiency(
299+
rays=rays,
300+
normal=na.Cartesian3dVectorArray(0, 0, -1),
301+
)
302+
303+
# Plot the fitted vs. measured reflectivity
304+
fig, ax = plt.subplots(constrained_layout=True)
305+
na.plt.scatter(
306+
measurement.inputs.wavelength,
307+
measurement.outputs,
308+
ax=ax,
309+
label="measurement"
310+
);
311+
na.plt.plot(
312+
rays.wavelength,
313+
reflectivity_fit,
314+
ax=ax,
315+
axis="wavelength",
316+
label="fit",
317+
color="tab:orange",
318+
);
319+
ax.set_xlabel(f"wavelength ({rays.wavelength.unit:latex_inline})")
320+
ax.set_ylabel("reflectivity")
321+
ax.legend();
322+
323+
# Print the fitted multilayer stack
324+
multilayer
325+
326+
Plot a diagram of the fitted multilayer stack
327+
328+
.. jupyter-execute::
329+
330+
with astropy.visualization.quantity_support():
331+
fig, ax = plt.subplots()
332+
multilayer.plot_layers(
333+
ax=ax,
334+
thickness_substrate=20 * u.nm,
335+
)
336+
ax.set_axis_off()
337+
338+
"""
339+
340+
design = multilayer_design()
341+
342+
measurement = multilayer_witness_measured()
343+
unit = u.nm
344+
345+
reflectivity = measurement.efficiency_measured.outputs
346+
angle_incidence = measurement.efficiency_measured.inputs.direction
347+
348+
rays = optika.rays.RayVectorArray(
349+
wavelength=measurement.efficiency_measured.inputs.wavelength,
350+
direction=na.Cartesian3dVectorArray(
351+
x=np.sin(angle_incidence),
352+
y=0,
353+
z=np.cos(angle_incidence),
354+
),
355+
)
356+
357+
normal = na.Cartesian3dVectorArray(0, 0, -1)
358+
359+
def _multilayer(
360+
ratio_SiC: float,
361+
ratio_Al: float,
362+
ratio_Mg: float,
363+
width_layers: float,
364+
width_substrate: float,
365+
):
366+
367+
width_layers = width_layers * unit
368+
width_substrate = width_substrate * unit
369+
370+
result = multilayer_design()
371+
372+
result.layers[1].thickness = ratio_SiC * design.layers[1].thickness
373+
result.layers[1].interface.width = width_layers
374+
375+
result.layers[2].thickness = ratio_Al * design.layers[2].thickness
376+
result.layers[2].interface.width = width_layers
377+
378+
result.layers[3].thickness = ratio_Mg * design.layers[3].thickness
379+
result.layers[3].interface.width = width_layers
380+
381+
thickness_Al = ratio_Al * design.layers[~1].layers[0].thickness
382+
result.layers[~1].layers[0].thickness = thickness_Al
383+
result.layers[~1].layers[0].interface.width = width_layers
384+
385+
thickness_SiC = ratio_SiC * design.layers[~1].layers[~1].thickness
386+
result.layers[~1].layers[~1].thickness = thickness_SiC
387+
result.layers[~1].layers[~1].interface.width = width_layers
388+
389+
thickness_Mg = ratio_Mg * design.layers[~1].layers[~0].thickness
390+
result.layers[~1].layers[~0].thickness = thickness_Mg
391+
result.layers[~1].layers[~0].interface.width = width_layers
392+
393+
result.layers[~0].thickness = ratio_Al * design.layers[~0].thickness
394+
result.layers[~0].interface.width = width_layers
395+
396+
result.layers.pop(0)
397+
398+
result.substrate.interface.width = width_substrate
399+
result.substrate.chemical = "Si"
400+
401+
return result
402+
403+
def _func(x: np.ndarray):
404+
405+
multilayer = _multilayer(*x)
406+
407+
reflectivity_fit = multilayer.efficiency(
408+
rays=rays,
409+
normal=normal,
410+
)
411+
412+
result = np.sqrt(np.mean(np.square(reflectivity_fit - reflectivity)))
413+
414+
return result.ndarray.value
415+
416+
x0 = u.Quantity(
417+
[
418+
1,
419+
1,
420+
1,
421+
1,
422+
1,
423+
]
424+
)
425+
426+
bounds = [(0, None)] * len(x0)
427+
428+
fit = scipy.optimize.minimize(
429+
fun=_func,
430+
x0=x0,
431+
bounds=bounds,
432+
)
433+
434+
return _multilayer(*fit.x)

esis/flights/f1/optics/gratings/materials/_materials_test.py

+5
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,8 @@ def test_multilayer_design():
1010
def test_multilayer_witness_measured():
1111
r = esis.flights.f1.optics.gratings.materials.multilayer_witness_measured()
1212
assert isinstance(r, optika.materials.MeasuredMirror)
13+
14+
15+
def test_multilayer_witness_fit():
16+
r = esis.flights.f1.optics.gratings.materials.multilayer_witness_fit()
17+
assert isinstance(r, optika.materials.MultilayerMirror)

0 commit comments

Comments
 (0)