gratinglab 0.1.6__tar.gz → 0.1.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gratinglab-0.1.7/PKG-INFO +372 -0
- gratinglab-0.1.7/README.md +333 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/__init__.py +2 -2
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/checks.py +20 -20
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/compare.py +28 -26
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/convergence.py +93 -90
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/geometry.py +108 -102
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/app.py +19 -18
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/diagram.py +78 -71
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/diagram3d.py +68 -62
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/docs.py +33 -32
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/icon.py +8 -7
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/integral_options.py +23 -22
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/mathtext.py +52 -50
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/orders.py +39 -35
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/provenance.py +42 -41
- gratinglab-0.1.7/src/gratinglab/gui/qt/__init__.py +22 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/qt/gc_guard.py +1 -1
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/qt/geometry_panel.py +20 -19
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/qt/geometry_tab.py +50 -44
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/qt/integral_tab.py +12 -10
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/qt/main_window.py +88 -83
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/qt/profile_plot_panel.py +7 -7
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/qt/scalar_tab.py +29 -26
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/qt/scene3d_panel.py +46 -43
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/qt/setup_tab.py +16 -16
- gratinglab-0.1.7/src/gratinglab/gui/qt/sizing.py +26 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/qt/theory_viewer.py +16 -16
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/qt/worker.py +57 -55
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/richtext.py +63 -54
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/scalar_options.py +19 -18
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/state.py +31 -32
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/illumination.py +9 -9
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/io/boundary.py +12 -10
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/io/efficiency_table.py +16 -16
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/io/ggp.py +24 -22
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/materials/__init__.py +26 -23
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/materials/fresnel.py +71 -69
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/materials/optical.py +60 -58
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/materials/refractiveindex.py +4 -4
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/__init__.py +17 -16
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/analyzer.py +28 -24
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/boundary/__init__.py +4 -4
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/boundary/average.py +7 -6
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/boundary/pipeline.py +35 -33
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/boundary/swaths.py +13 -12
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/core/__init__.py +3 -2
- gratinglab-0.1.7/src/gratinglab/metrology/core/image_flatten.py +136 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/core/processing.py +11 -10
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/core/tip.py +58 -56
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/data/__init__.py +3 -2
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/data/aggregation.py +3 -2
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/gui/__init__.py +16 -14
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/gui/icon.py +20 -18
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/gui/qt/__init__.py +6 -6
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/gui/qt/boundary_view.py +21 -18
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/gui/qt/efficiency_dialog.py +19 -16
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/gui/qt/import_view.py +9 -8
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/gui/qt/main_window.py +5 -4
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/gui/qt/wiki_view.py +9 -7
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/gui/qt/worker.py +13 -12
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/gui/state.py +9 -8
- gratinglab-0.1.7/src/gratinglab/metrology/io/__init__.py +9 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/io/file_io.py +6 -5
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/io/metrics.py +5 -5
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/io/spm.py +23 -21
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/settings.py +7 -6
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/stats/__init__.py +3 -2
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/stats/analysis.py +10 -8
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/stats/icc.py +4 -4
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/visualization/__init__.py +3 -2
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/visualization/diagnostics.py +3 -2
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/visualization/profiles.py +7 -6
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/visualization/statistics.py +17 -17
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/wiki/__init__.py +13 -11
- gratinglab-0.1.7/src/gratinglab/metrology/wiki/facet-fitting.md +99 -0
- gratinglab-0.1.7/src/gratinglab/metrology/wiki/file-formats.md +122 -0
- gratinglab-0.1.7/src/gratinglab/metrology/wiki/flattening.md +109 -0
- gratinglab-0.1.7/src/gratinglab/metrology/wiki/icc-correction.md +112 -0
- gratinglab-0.1.7/src/gratinglab/metrology/wiki/overview.md +55 -0
- gratinglab-0.1.7/src/gratinglab/metrology/wiki/reading-outputs.md +182 -0
- gratinglab-0.1.7/src/gratinglab/metrology/wiki/row-groups.md +78 -0
- gratinglab-0.1.7/src/gratinglab/metrology/wiki/swath-statistics.md +66 -0
- gratinglab-0.1.7/src/gratinglab/metrology/wiki/tip-correction.md +180 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/workflows.py +14 -11
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/problem.py +8 -7
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/profiles.py +36 -34
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/resolution.py +37 -34
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/result.py +17 -17
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/solvers/base.py +56 -53
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/solvers/integral/__init__.py +40 -38
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/solvers/integral/_boundary.py +5 -5
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/solvers/integral/_core.py +12 -10
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/solvers/integral/_finite.py +53 -48
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/solvers/integral/_greens.py +25 -22
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/solvers/integral/_kernels.py +66 -58
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/solvers/integral/_nystrom.py +89 -80
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/solvers/scalar.py +209 -195
- gratinglab-0.1.7/src/gratinglab.egg-info/PKG-INFO +372 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_cross_method.py +2 -2
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_integral_corpus.py +7 -5
- gratinglab-0.1.6/PKG-INFO +0 -348
- gratinglab-0.1.6/README.md +0 -309
- gratinglab-0.1.6/src/gratinglab/gui/qt/__init__.py +0 -20
- gratinglab-0.1.6/src/gratinglab/gui/qt/sizing.py +0 -25
- gratinglab-0.1.6/src/gratinglab/metrology/core/image_flatten.py +0 -131
- gratinglab-0.1.6/src/gratinglab/metrology/io/__init__.py +0 -8
- gratinglab-0.1.6/src/gratinglab/metrology/wiki/facet-fitting.md +0 -95
- gratinglab-0.1.6/src/gratinglab/metrology/wiki/file-formats.md +0 -116
- gratinglab-0.1.6/src/gratinglab/metrology/wiki/flattening.md +0 -105
- gratinglab-0.1.6/src/gratinglab/metrology/wiki/icc-correction.md +0 -111
- gratinglab-0.1.6/src/gratinglab/metrology/wiki/overview.md +0 -51
- gratinglab-0.1.6/src/gratinglab/metrology/wiki/reading-outputs.md +0 -172
- gratinglab-0.1.6/src/gratinglab/metrology/wiki/row-groups.md +0 -75
- gratinglab-0.1.6/src/gratinglab/metrology/wiki/swath-statistics.md +0 -61
- gratinglab-0.1.6/src/gratinglab/metrology/wiki/tip-correction.md +0 -173
- gratinglab-0.1.6/src/gratinglab.egg-info/PKG-INFO +0 -348
- {gratinglab-0.1.6 → gratinglab-0.1.7}/LICENSE +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/pyproject.toml +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/setup.cfg +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/gui/__init__.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/io/__init__.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/materials/data/SOURCES.md +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/cli.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/config.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/core/analysis.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/gui/qt/app.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/gui/qt/canvas.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/metrology/gui/qt/swath_dialog.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab/solvers/__init__.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab.egg-info/SOURCES.txt +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab.egg-info/dependency_links.txt +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab.egg-info/entry_points.txt +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab.egg-info/requires.txt +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/src/gratinglab.egg-info/top_level.txt +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_checks.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_compare.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_convergence.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_efficiency_table.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_fresnel.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_geometry.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_ggp.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_gui_diagram.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_gui_diagram3d.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_gui_docs.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_gui_integral_options.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_gui_mathtext.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_gui_orders.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_gui_provenance.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_gui_qt.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_gui_richtext.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_gui_scalar_options.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_gui_state.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_illumination.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_integral_core.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_integral_finite.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_integral_greens.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_integral_kernels.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_integral_operators.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_integral_solver.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_io_boundary.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_materials.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_perturbation.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_profiles.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_refractiveindex.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_resolution.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_result.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_scalar.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_solver_conformance.py +0 -0
- {gratinglab-0.1.6 → gratinglab-0.1.7}/tests/test_solver_progress.py +0 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gratinglab
|
|
3
|
+
Version: 0.1.7
|
|
4
|
+
Summary: Open platform for diffraction grating groove metrology and efficiency analysis
|
|
5
|
+
Author: Jake McCoy
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/jamccoy/gratinglab
|
|
8
|
+
Keywords: diffraction,grating,efficiency,rcwa,x-ray,spectroscopy,afm,metrology,blaze,pcgrate
|
|
9
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: numpy>=1.26
|
|
19
|
+
Requires-Dist: scipy>=1.11
|
|
20
|
+
Requires-Dist: pydantic>=2.6
|
|
21
|
+
Requires-Dist: endstation>=0.2.2
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
24
|
+
Requires-Dist: pytest-qt>=4.5; extra == "dev"
|
|
25
|
+
Requires-Dist: matplotlib>=3.8; extra == "dev"
|
|
26
|
+
Requires-Dist: pandas>=2.1; extra == "dev"
|
|
27
|
+
Requires-Dist: ruff>=0.16; extra == "dev"
|
|
28
|
+
Provides-Extra: gui
|
|
29
|
+
Requires-Dist: matplotlib>=3.8; extra == "gui"
|
|
30
|
+
Requires-Dist: PySide6-Essentials>=6.7; extra == "gui"
|
|
31
|
+
Requires-Dist: endstation[qt]>=0.2.2; extra == "gui"
|
|
32
|
+
Provides-Extra: metrology
|
|
33
|
+
Requires-Dist: matplotlib>=3.9; extra == "metrology"
|
|
34
|
+
Provides-Extra: refractiveindex
|
|
35
|
+
Requires-Dist: pyyaml>=6; extra == "refractiveindex"
|
|
36
|
+
Provides-Extra: mut
|
|
37
|
+
Requires-Dist: mutmut<3.3,>=3.2; extra == "mut"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# gratinglab
|
|
41
|
+
|
|
42
|
+
[](https://pypi.org/project/gratinglab/)
|
|
43
|
+
[](https://pypi.org/project/gratinglab/)
|
|
44
|
+
[](https://github.com/jamccoy/gratinglab/actions/workflows/test.yml)
|
|
45
|
+
[](https://github.com/jamccoy/gratinglab/blob/master/LICENSE)
|
|
46
|
+
|
|
47
|
+
**[PyPI](https://pypi.org/project/gratinglab/)** · **[Documentation](https://github.com/jamccoy/gratinglab/wiki)** · **[Releases](https://github.com/jamccoy/gratinglab/releases)** · **[Issues](https://github.com/jamccoy/gratinglab/issues)**
|
|
48
|
+
|
|
49
|
+
gratinglab is a Python package for measuring the groove profiles of diffraction
|
|
50
|
+
gratings from atomic force microscope (AFM) scans and for computing the diffraction
|
|
51
|
+
efficiency of a grating from such a profile.
|
|
52
|
+
|
|
53
|
+
The AFM metrology is the part of the package in regular use. It measures blaze angles
|
|
54
|
+
from a scan, averages the grooves into a single profile, reports how much that profile
|
|
55
|
+
varies down the scan, corrects for the shape of the AFM tip where the data allow it,
|
|
56
|
+
and exports the averaged groove as a boundary profile. The efficiency solvers are
|
|
57
|
+
**experimental**: they pass their test suite but have not yet been used for real
|
|
58
|
+
analysis, and their results should be treated as provisional.
|
|
59
|
+
|
|
60
|
+
Rigorous methods for computing grating efficiency are well established, and open codes
|
|
61
|
+
exist for several of them, although each carries its own conventions and
|
|
62
|
+
normalizations. gratinglab is intended to fill two gaps that remain: running the same
|
|
63
|
+
physical problem through several methods so that their results can be compared
|
|
64
|
+
directly, and starting that problem from a measured surface rather than an idealised
|
|
65
|
+
one. To this end, the physical problem and the numerical method are kept separate. A
|
|
66
|
+
`Problem` describes the grating and can be saved to a file, and the same `Problem` is
|
|
67
|
+
passed to every solver, so any difference between their results is attributable to
|
|
68
|
+
the methods themselves.
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
AFM scan -> BoundaryProfile (gratinglab.metrology)
|
|
72
|
+
| measured period, averaged groove, facet fit
|
|
73
|
+
v
|
|
74
|
+
Problem (period + profile + materials; no solver fields)
|
|
75
|
+
Illumination (wavelength + direction + polarization)
|
|
76
|
+
|
|
|
77
|
+
v
|
|
78
|
+
Solver (plugin) -> Result (+ provenance)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The metrology and the solvers are distributed as one package so that no information is
|
|
82
|
+
lost between them. A plain boundary file records the groove shape but not the grating
|
|
83
|
+
period, which must then be entered again by hand. `BoundaryProfile.to_problem()`
|
|
84
|
+
carries the period measured from the scan, together with the fitted facet angle. A
|
|
85
|
+
profile can be saved with that information intact in gratinglab's own `.json` format
|
|
86
|
+
(`gratinglab.io.boundary.write_boundary_json`); reading it back gives the same
|
|
87
|
+
problem, and "Load boundary profile" in the efficiency window takes the period from
|
|
88
|
+
it. The Boundary tab of the metrology window exports `.json`, `.csv`, or a `.ggp`
|
|
89
|
+
boundary file for PCGrate, and its "Send to efficiency window" button opens the groove
|
|
90
|
+
in the efficiency window directly.
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from gratinglab.metrology.boundary import build_boundary_profile
|
|
94
|
+
|
|
95
|
+
profile = build_boundary_profile(data, scan_x_size, settings)
|
|
96
|
+
problem = profile.to_problem(coating="Au", blaze_angle=29.5)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Status
|
|
100
|
+
|
|
101
|
+
The project is at a pre-alpha stage. The AFM metrology is the more mature of its two
|
|
102
|
+
parts. On the efficiency side, the scalar and integral solvers, the comparison and
|
|
103
|
+
convergence harnesses and the materials layer work end to end. They are checked
|
|
104
|
+
against closed-form results, published benchmark tables, energy balance and
|
|
105
|
+
reciprocity, but remain experimental until they have been used on real problems.
|
|
106
|
+
Efficiencies are absolute when a coating is named, using an optical constant table
|
|
107
|
+
supplied by the user (see Install). A rigorous coupled-wave analysis (RCWA) backend
|
|
108
|
+
has been contributed but is not yet integrated.
|
|
109
|
+
|
|
110
|
+
## Scope
|
|
111
|
+
|
|
112
|
+
The metrology (`gratinglab.metrology`) reads AFM scans, either Nanoscope `.spm` files
|
|
113
|
+
or Gwyddion text exports. It produces blaze angles for each groove with statistics
|
|
114
|
+
over groups of scan rows and a correction for the correlation between those groups,
|
|
115
|
+
an averaged groove normalised to one period, and statistics describing how that
|
|
116
|
+
groove varies between horizontal swaths of the scan. It can also undo the broadening
|
|
117
|
+
introduced by the AFM tip where the tip reached the surface, and it marks the regions
|
|
118
|
+
the tip could not reach instead of estimating them.
|
|
119
|
+
|
|
120
|
+
The efficiency side comprises the problem specification, the solvers, the comparison
|
|
121
|
+
and convergence harnesses, the materials layer, and a calculation of the resolving
|
|
122
|
+
power implied by the number of grooves measured.
|
|
123
|
+
|
|
124
|
+
Two mounting geometries are supported:
|
|
125
|
+
|
|
126
|
+
- **In-plane (classical):** ultraviolet to near-infrared optical engineering.
|
|
127
|
+
- **Extreme off-plane (conical) at grazing incidence:** soft x-ray spectroscopy, where
|
|
128
|
+
the integral method is the established approach and general-purpose RCWA codes are
|
|
129
|
+
least reliable.
|
|
130
|
+
|
|
131
|
+
The method backends, in order of development:
|
|
132
|
+
|
|
133
|
+
| Method | Status |
|
|
134
|
+
|---|---|
|
|
135
|
+
| Scalar (Kirchhoff, x-ray blaze form) | experimental; checked against closed-form results |
|
|
136
|
+
| Integral method (boundary integral, conical, undercut-capable) | experimental; perfect and finite conductivity (`conductivity="tabulated"`, Goray & Schmidt 2010), absolute efficiencies with the absorbed fraction recorded; checked against published benchmark tables (Maystre & Popov; Goray & Schmidt), energy balance and reciprocity |
|
|
137
|
+
| RCWA (in-plane, conical, off-plane; Li factorization, S-matrix) | contributed, not yet integrated |
|
|
138
|
+
| C-method (Chandezon) | planned |
|
|
139
|
+
|
|
140
|
+
The comparison harness also accepts an efficiency table already computed by an
|
|
141
|
+
external program as one of its methods, so that comparison plots can be made without
|
|
142
|
+
a licence for the program that produced the reference data:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
reference = read_scan("run.txt", method="integral")
|
|
146
|
+
align(sweep(problem, illumination, wavelengths, ["scalar", reference]))
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Because the file does not record the physics behind it, the caller states which method
|
|
150
|
+
produced an imported table. Comparison legends then name the method, for example
|
|
151
|
+
`scalar vs integral`, while `Provenance.version` records the program and version that
|
|
152
|
+
generated the file.
|
|
153
|
+
|
|
154
|
+
### Resolving power from a measured surface
|
|
155
|
+
|
|
156
|
+
A scan records the number of grooves it covers, so resolving power can be computed
|
|
157
|
+
without entering anything by hand:
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
problem = profile.to_problem(coating="Au") # n_grooves = the grooves averaged
|
|
161
|
+
resolving_power(problem, illumination, wavelength=2.0, order=3).r
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The resolving power follows from the Rayleigh criterion as `R = |m|·N`, and
|
|
165
|
+
`line_profile` gives the line shape for a finite number of grooves from which that
|
|
166
|
+
result is derived. A problem whose `n_grooves` is not set is refused rather than
|
|
167
|
+
treated as having infinitely many grooves. Resolving power degraded by errors in groove
|
|
168
|
+
placement is planned; until it is implemented, the ideal formula is not used as an
|
|
169
|
+
approximation for it.
|
|
170
|
+
|
|
171
|
+
### Correcting for the AFM tip
|
|
172
|
+
|
|
173
|
+
An AFM image is the true surface dilated by the shape of the tip.
|
|
174
|
+
`tip_correction='erosion'` reverses this dilation where it can be reversed (Villarrubia
|
|
175
|
+
1997) and reports a certainty map showing where it cannot. The tip is modelled as a
|
|
176
|
+
spherical apex on two flanks whose angles may differ, so a pyramidal probe such as
|
|
177
|
+
Bruker's ScanAsyst-Air can be represented. No method can recover the bottom of a
|
|
178
|
+
trough the tip did not enter, and the correction leaves such regions as upper bounds
|
|
179
|
+
instead of filling them in.
|
|
180
|
+
|
|
181
|
+
On one real scan, the groove depth and the fitted facet angle disagree by 7.6°.
|
|
182
|
+
Erosion with the probe at its nominal radius recovers none of that difference, and
|
|
183
|
+
reproducing it by simulation requires an apex worn to about 80 nm, which excludes the
|
|
184
|
+
nominal tip as the cause
|
|
185
|
+
([findings](https://github.com/jamccoy/gratinglab/blob/master/docs/findings.md)).
|
|
186
|
+
|
|
187
|
+
## Install
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
pip install gratinglab # solvers only: numpy, scipy, pydantic
|
|
191
|
+
pip install "gratinglab[metrology]" # + AFM readers and the boundary pipeline
|
|
192
|
+
pip install "gratinglab[gui]" # + the Qt windows
|
|
193
|
+
pip install "gratinglab[refractiveindex]" # + refractiveindex.info tables
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
With [uv](https://docs.astral.sh/uv/), the same extras work:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
uv pip install "gratinglab[refractiveindex]" # into the active environment
|
|
200
|
+
uv add gratinglab # into a uv project
|
|
201
|
+
uv tool install "gratinglab[gui]" # gratinglab-gui as a standalone command
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
From a checkout, use `pip install -e ".[dev,gui,metrology,refractiveindex]"` instead.
|
|
205
|
+
|
|
206
|
+
No optical constant tables are distributed with the package, so absolute efficiencies
|
|
207
|
+
require tables supplied by the user. A library is a directory of such tables, one
|
|
208
|
+
material per file, each named after its material:
|
|
209
|
+
|
|
210
|
+
| File | From | Range |
|
|
211
|
+
|---|---|---|
|
|
212
|
+
| `Pt.txt` | a [CXRO](https://henke.lbl.gov/optical_constants/) export | x-ray |
|
|
213
|
+
| `Pt.yml` | a [refractiveindex.info](https://refractiveindex.info) file, renamed from its author's name | ultraviolet to infrared |
|
|
214
|
+
| `Pt.ari` | a PCGrate table | whatever it covers |
|
|
215
|
+
|
|
216
|
+
Saving one as `~/.gratinglab/materials/Pt.yml` makes `coating="Pt"` available.
|
|
217
|
+
`GRATINGLAB_MATERIALS` points to a different directory, and
|
|
218
|
+
`materials.from_cxro_file(path)` or `from_refractiveindex_file(path)` reads a single
|
|
219
|
+
file without a library. The YAML files require the `refractiveindex` extra.
|
|
220
|
+
[`SOURCES.md`](https://github.com/jamccoy/gratinglab/blob/master/src/gratinglab/materials/data/SOURCES.md)
|
|
221
|
+
explains why no tables are distributed.
|
|
222
|
+
|
|
223
|
+
The core install does not include a plotting stack, so a user who only runs the solvers
|
|
224
|
+
does not install one. A subpackage that needs an optional extra names it when it is
|
|
225
|
+
missing, instead of failing with a bare `ModuleNotFoundError`.
|
|
226
|
+
|
|
227
|
+
Apart from numpy, scipy and pydantic, the only dependency is
|
|
228
|
+
[endstation](https://github.com/jamccoy/endstation), a library of shared components for
|
|
229
|
+
desktop scientific applications by the same author. gratinglab uses its guards for
|
|
230
|
+
optional extras (the messages described above), form validation, Qt layout sizing,
|
|
231
|
+
test helpers, and the tools that build the icon and the macOS application and publish
|
|
232
|
+
the wiki. The core of endstation has no dependencies of its own, so it adds nothing to
|
|
233
|
+
a solver-only install; its Qt components are installed only with the `gui` extra.
|
|
234
|
+
|
|
235
|
+
### Where the measurement data lives
|
|
236
|
+
|
|
237
|
+
AFM scans are not included in the repository, because they are research data. The
|
|
238
|
+
same applies to the private reference corpus of efficiency tables:
|
|
239
|
+
|
|
240
|
+
| Data | Environment variable | Default |
|
|
241
|
+
|---|---|---|
|
|
242
|
+
| AFM scans | `GRATINGLAB_AFM_DIR` | `~/Documents/afm_scans` |
|
|
243
|
+
| Reference efficiency tables | `GRATINGLAB_REF_DIR` | `~/Documents/diffraction_efficiency` |
|
|
244
|
+
|
|
245
|
+
Tests that need either are skipped when it is absent, so the suite passes on a fresh
|
|
246
|
+
clone. Most of the metrology tests do not need it: they run against a committed
|
|
247
|
+
synthetic scan of a grating with known geometry
|
|
248
|
+
(`tools/metrology/make_synthetic_scan.py`), which also allows a test to check that the
|
|
249
|
+
pipeline recovers the period and blaze angle used to generate it.
|
|
250
|
+
|
|
251
|
+
## GUI
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
.venv/bin/python -m pip install -e ".[gui,metrology]"
|
|
255
|
+
gratinglab-gui # geometry, solvers, comparison
|
|
256
|
+
gratinglab-metrology-gui # AFM import, blaze angles, boundary profiles
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
The windows are built with Qt (PySide6), so `pip install "gratinglab[gui]"` needs no
|
|
260
|
+
system toolkit to be installed first. PySide6 is licensed under the LGPLv3 and is an
|
|
261
|
+
optional, dynamically linked extra, so this package remains under the BSD-3 licence.
|
|
262
|
+
|
|
263
|
+
The grating geometry is set in a dock on the left of the efficiency window (Ctrl+G
|
|
264
|
+
hides it), and the window has one tab for each task:
|
|
265
|
+
|
|
266
|
+
- **Grating Geometry:** a rotatable 3D view of the diffraction cone, in which every wave
|
|
267
|
+
vector is drawn as an exact unit vector. In the reference off-plane geometry the
|
|
268
|
+
orders separate in azimuth rather than in polar angle (93.8° of azimuthal spread at
|
|
269
|
+
0.0° of polar spread), so the view looking down the cone axis, combined with a uniform
|
|
270
|
+
27× zoom, makes them distinguishable at true scale. The grazing angle γ is not
|
|
271
|
+
exaggerated: a uniform zoom preserves every angle in the view, and the magnification
|
|
272
|
+
is stated on the canvas. Alongside it are the cross-section in the dispersion plane, a
|
|
273
|
+
`sin β` diagram that includes the evanescent orders, and the groove profile. The view
|
|
274
|
+
updates as values are typed.
|
|
275
|
+
- **Scalar (Kirchhoff):** the scalar solution, the efficiency of each order with
|
|
276
|
+
per-order visibility, and a provenance panel that lists every validity warning, the
|
|
277
|
+
summed-efficiency check, and whether convergence was demonstrated. A result that
|
|
278
|
+
violates energy conservation is flagged. "Check convergence…" runs a convergence
|
|
279
|
+
sweep and reports the coarsest setting that meets the criterion, with a progress bar
|
|
280
|
+
and a Cancel button. Because solvers report progress for each wavelength, Cancel
|
|
281
|
+
stops the computation itself.
|
|
282
|
+
- **Integral method:** the rigorous backend, with the same panel and provenance and its
|
|
283
|
+
own boundary-condition control (perfect conductivity or tabulated optical
|
|
284
|
+
constants). It takes minutes where the scalar solver takes milliseconds, so the
|
|
285
|
+
window opens on the scalar solver and this tab computes only when asked to.
|
|
286
|
+
- **Setup:** the material library, showing which optical constants are installed, the
|
|
287
|
+
range each covers, where each came from, and how to add others. The coating itself is
|
|
288
|
+
chosen in the geometry dock, because it is a `Problem` field, like the period, and the
|
|
289
|
+
same `Problem` is used by every tab.
|
|
290
|
+
|
|
291
|
+
Each registered solver receives a tab generated from the solver registry, so a
|
|
292
|
+
contributed backend appears in the window once it is registered.
|
|
293
|
+
|
|
294
|
+
The Help menu contains the theory pages, rendered with typeset equations and tables.
|
|
295
|
+
|
|
296
|
+
A macOS `.app` can be built with `python tools/make_app.py` (macOS 13 or later, the
|
|
297
|
+
minimum supported by the PySide6 wheels), and the icon is regenerated with
|
|
298
|
+
`python tools/make_icon.py`.
|
|
299
|
+
|
|
300
|
+
## Documentation
|
|
301
|
+
|
|
302
|
+
| Document | Contents |
|
|
303
|
+
|---|---|
|
|
304
|
+
| [`docs/roadmap.md`](https://github.com/jamccoy/gratinglab/blob/master/docs/roadmap.md) | The current state of the project, planned work and open questions; the place to start |
|
|
305
|
+
| [`docs/conventions.md`](https://github.com/jamccoy/gratinglab/blob/master/docs/conventions.md) | The normative definitions: signs, units, normalizations, and errata in the references |
|
|
306
|
+
| [`docs/theory/scalar.md`](https://github.com/jamccoy/gratinglab/blob/master/docs/theory/scalar.md) | The derivation of the scalar solver, its closed-form results and its limits |
|
|
307
|
+
| [`docs/theory/integral.md`](https://github.com/jamccoy/gratinglab/blob/master/docs/theory/integral.md) | The boundary-integral solver: conical formulation, perfect and finite conductivity |
|
|
308
|
+
| [`docs/theory/metrology.md`](https://github.com/jamccoy/gratinglab/blob/master/docs/theory/metrology.md) | The assumptions of the AFM pipeline, what it measures and what it does not, including what tip erosion can recover |
|
|
309
|
+
| [`docs/findings.md`](https://github.com/jamccoy/gratinglab/blob/master/docs/findings.md) | Empirical results, each with the evidence for it |
|
|
310
|
+
| [`docs/references.md`](https://github.com/jamccoy/gratinglab/blob/master/docs/references.md) | The literature each part of the package is based on |
|
|
311
|
+
| [`docs/mutation-testing.md`](https://github.com/jamccoy/gratinglab/blob/master/docs/mutation-testing.md) | What is mutated, what is not, and how to interpret a surviving mutant |
|
|
312
|
+
| [`benchmarks/corpus.toml`](https://github.com/jamccoy/gratinglab/blob/master/benchmarks/corpus.toml) | Geometry for the reference corpus, most of it recovered from the data |
|
|
313
|
+
|
|
314
|
+
`conventions.md` should be read before writing a solver or reporting a bug involving a
|
|
315
|
+
sign. In brief: the time convention is `exp(-iωt)`, the complex refractive index is
|
|
316
|
+
*n* = n′ + ik, the grating equation is `sin α + sin β_m = mλ/(p sin γ)`, efficiencies
|
|
317
|
+
are absolute, lengths are in nm, and angles are in degrees at the API boundary.
|
|
318
|
+
|
|
319
|
+
Each solver has a theory page stating its assumptions, its derivation in the notation
|
|
320
|
+
of the code, and the conditions under which its results cease to be reliable.
|
|
321
|
+
|
|
322
|
+
The [wiki](https://github.com/jamccoy/gratinglab/wiki) is a published copy of `docs/`
|
|
323
|
+
and of the metrology window's own help pages, which appear there with an "AFM" prefix.
|
|
324
|
+
Changes are made to the sources in the repository and then republished:
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
git clone https://github.com/jamccoy/gratinglab.wiki.git ../gratinglab.wiki
|
|
328
|
+
.venv/bin/python tools/publish_wiki.py --wiki ../gratinglab.wiki # then review and push
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Development
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
.venv/bin/python -m pip install -e ".[dev,gui,metrology,refractiveindex]"
|
|
335
|
+
.venv/bin/python -m pytest
|
|
336
|
+
.venv/bin/ruff check .
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
The test suite relies on three practices in particular:
|
|
340
|
+
|
|
341
|
+
- **Physical self-checks.** `checks.check_reciprocity` and `checks.check_energy_balance`
|
|
342
|
+
test properties that any correct model must satisfy, independently of how the
|
|
343
|
+
solver computes its result. They require no reference data and apply to every
|
|
344
|
+
backend.
|
|
345
|
+
- **Demonstrated convergence.** `convergence.check_convergence` varies whatever a
|
|
346
|
+
solver declares as its accuracy parameter and reports the coarsest setting that
|
|
347
|
+
meets the criterion, or reports that none did. It requires three consecutive values
|
|
348
|
+
to agree, because the quadrature error of a blazed profile does not decrease
|
|
349
|
+
monotonically, so a single agreement can be coincidental
|
|
350
|
+
([findings](https://github.com/jamccoy/gratinglab/blob/master/docs/findings.md#quadrature-error-is-not-monotone-in-the-number-of-points)).
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
report = check_convergence(scalar, problem, illumination, wavelengths)
|
|
354
|
+
report.converged_at # 4096 -- what a production run should use
|
|
355
|
+
report.scan.provenance.converged # True
|
|
356
|
+
```
|
|
357
|
+
- **Mutation testing.** The suite is run against deliberately altered copies of the
|
|
358
|
+
physics code to check that the changes are detected. Coverage shows that a line was
|
|
359
|
+
executed; mutation testing shows whether any test would fail if that line were wrong.
|
|
360
|
+
Both runs so far have found real gaps in the tests; see
|
|
361
|
+
[findings](https://github.com/jamccoy/gratinglab/blob/master/docs/findings.md#mutation-testing-found-a-real-gap)
|
|
362
|
+
and [docs/mutation-testing.md](https://github.com/jamccoy/gratinglab/blob/master/docs/mutation-testing.md)
|
|
363
|
+
for how to run it.
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
.venv/bin/python -m pip install -e ".[mut]"
|
|
367
|
+
.venv/bin/mutmut run
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## License
|
|
371
|
+
|
|
372
|
+
BSD-3-Clause.
|