gratinglab 0.2.6__tar.gz → 0.2.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.2.6/src/gratinglab.egg-info → gratinglab-0.2.7}/PKG-INFO +25 -3
- {gratinglab-0.2.6 → gratinglab-0.2.7}/README.md +24 -2
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/__init__.py +1 -1
- gratinglab-0.2.7/src/gratinglab/gui/comparison.py +361 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/integral_options.py +4 -2
- gratinglab-0.2.7/src/gratinglab/gui/qt/compare_tab.py +473 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/qt/integral_tab.py +6 -4
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/qt/main_window.py +99 -5
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/qt/worker.py +84 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/integral/__init__.py +17 -10
- {gratinglab-0.2.6 → gratinglab-0.2.7/src/gratinglab.egg-info}/PKG-INFO +25 -3
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab.egg-info/SOURCES.txt +3 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_conditioning.py +7 -1
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_cross_method.py +3 -3
- gratinglab-0.2.7/tests/test_gui_compare_tab.py +514 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_gui_integral_options.py +35 -11
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_gui_qt.py +20 -9
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_integral_graded.py +10 -4
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_integral_solver.py +3 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/LICENSE +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/pyproject.toml +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/setup.cfg +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/checks.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/compare.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/convergence.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/geometry.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/app.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/cmethod_options.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/diagram.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/diagram3d.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/docs.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/efficiency_plot.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/icon.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/launcher.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/mathtext.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/orders.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/provenance.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/qt/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/qt/cmethod_tab.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/qt/geometry_panel.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/qt/geometry_tab.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/qt/launcher.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/qt/profile_plot_panel.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/qt/rcwa_tab.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/qt/scalar_tab.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/qt/scene3d_panel.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/qt/setup_tab.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/qt/sizing.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/qt/theming.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/qt/theory_viewer.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/rcwa_options.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/richtext.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/scalar_options.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/state.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/gui/theme.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/illumination.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/io/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/io/boundary.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/io/efficiency_table.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/io/ggp.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/materials/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/materials/data/SOURCES.md +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/materials/fresnel.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/materials/optical.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/materials/refractiveindex.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/measured.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/analyzer.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/boundary/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/boundary/average.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/boundary/pipeline.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/boundary/swaths.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/cli.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/config.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/core/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/core/analysis.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/core/image_flatten.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/core/orientation.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/core/processing.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/core/region_mask.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/core/scars.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/core/tip.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/data/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/data/aggregation.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/gui/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/gui/icon.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/gui/qt/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/gui/qt/app.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/gui/qt/boundary_view.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/gui/qt/canvas.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/gui/qt/efficiency_dialog.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/gui/qt/import_view.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/gui/qt/layout.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/gui/qt/main_window.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/gui/qt/swath_dialog.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/gui/qt/wiki_view.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/gui/qt/worker.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/gui/state.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/io/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/io/file_io.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/io/ibw.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/io/metrics.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/io/spm.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/settings.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/stats/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/stats/analysis.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/stats/icc.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/visualization/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/visualization/diagnostics.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/visualization/profiles.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/visualization/statistics.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/wiki/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/wiki/facet-fitting.md +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/wiki/file-formats.md +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/wiki/flattening.md +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/wiki/groove-orientation.md +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/wiki/icc-correction.md +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/wiki/overview.md +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/wiki/reading-outputs.md +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/wiki/row-groups.md +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/wiki/scars-and-masking.md +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/wiki/swath-statistics.md +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/wiki/tip-correction.md +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/metrology/workflows.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/problem.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/profiles.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/resolution.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/result.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/_conditioning.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/_execution.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/base.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/cmethod/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/cmethod/_core.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/cmethod/_series.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/integral/_boundary.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/integral/_core.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/integral/_finite.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/integral/_greens.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/integral/_kernels.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/integral/_nystrom.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/rcwa/__init__.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/rcwa/_core.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/rcwa/_stack.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab/solvers/scalar.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab.egg-info/dependency_links.txt +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab.egg-info/entry_points.txt +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab.egg-info/requires.txt +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/src/gratinglab.egg-info/top_level.txt +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_changelog.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_checks.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_cmethod.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_compare.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_convergence.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_efficiency_table.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_four_way.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_fresnel.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_geometry.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_ggp.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_gui_cmethod_options.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_gui_cmethod_tab.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_gui_diagram.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_gui_diagram3d.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_gui_docs.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_gui_launcher.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_gui_mathtext.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_gui_orders.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_gui_provenance.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_gui_rcwa_options.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_gui_richtext.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_gui_scalar_options.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_gui_state.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_gui_theme.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_illumination.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_integral_core.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_integral_corpus.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_integral_finite.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_integral_greens.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_integral_kernels.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_integral_operators.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_io_boundary.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_materials.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_measured.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_perturbation.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_profiles.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_rayleigh_anomaly.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_rcwa.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_rcwa_benchmarks.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_refractiveindex.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_resolution.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_result.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_scalar.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_solver_conformance.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_solver_execution.py +0 -0
- {gratinglab-0.2.6 → gratinglab-0.2.7}/tests/test_solver_progress.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gratinglab
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: Open platform for diffraction grating groove metrology and efficiency analysis
|
|
5
5
|
Author: Jake McCoy, Aditi Gangadharan
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -115,6 +115,15 @@ beside them, both written in-house and both experimental: a rigorous coupled-wav
|
|
|
115
115
|
analysis (RCWA), which also models coated gratings through conformal layers, and
|
|
116
116
|
the C-method (Chandezon). Each has a tab in the efficiency window since 0.2.1.
|
|
117
117
|
|
|
118
|
+
**Every modelling method here still needs verification**, the rigorous ones
|
|
119
|
+
included. The checks in the table below show that each method reproduces the
|
|
120
|
+
cases it was checked on; they do not show that it is right on yours. Nor does
|
|
121
|
+
agreement between the solvers: they share the problem specification, the optical
|
|
122
|
+
constant tables and the angle conventions, so a mistake in any of those moves all
|
|
123
|
+
of them together and no comparison between them can see it. A number from any
|
|
124
|
+
solver is evidence to be checked against measurement or a published benchmark
|
|
125
|
+
before it is relied on.
|
|
126
|
+
|
|
118
127
|
## Scope
|
|
119
128
|
|
|
120
129
|
The metrology (`gratinglab.metrology`) reads AFM scans: Bruker/Veeco Nanoscope `.spm`
|
|
@@ -147,7 +156,7 @@ The method backends, in order of development:
|
|
|
147
156
|
| Method | Status |
|
|
148
157
|
|---|---|
|
|
149
158
|
| Scalar (Kirchhoff, x-ray blaze form) | experimental; checked against closed-form results |
|
|
150
|
-
| Integral method (boundary integral, conical, undercut-capable) | experimental; perfect and finite conductivity (`conductivity="tabulated"`, Goray & Schmidt 2010), absolute efficiencies with the absorbed fraction recorded; a corner-graded mesh for profiles with corners (`corner_grading=4`), under either boundary condition; checked against published benchmark tables (Maystre & Popov; Goray & Schmidt), energy balance and reciprocity |
|
|
159
|
+
| Integral method (boundary integral, conical, undercut-capable) | experimental; perfect and finite conductivity (`conductivity="tabulated"`, Goray & Schmidt 2010), absolute efficiencies with the absorbed fraction recorded; a corner-graded mesh for profiles with corners (`corner_grading=4`, the default since 0.2.7; `0` is the equal-arc mesh), under either boundary condition; checked against published benchmark tables (Maystre & Popov; Goray & Schmidt), energy balance and reciprocity |
|
|
151
160
|
| RCWA (in-plane, conical, off-plane; Li factorization, S-matrix, layer stacks) | experimental, since 0.2.0; lamellar gratings exact and other profiles staircased, transmitted orders, and coatings through conformal layers (`Problem.layers`); checked against Fresnel, the Airy film formula, reciprocity, the integral solver, and published lamellar values (Granet; Lalanne & Hugonin; Gralak; Goray & Schmidt's conical tables). Not yet checked: staircase accuracy in metallic TM, and layers of a second material against an independent code |
|
|
152
161
|
| C-method (Chandezon) | experimental, since 0.2.0; perfect conductor in any mount, and one interface into an absorbing or lossless material, conical and coupled; checked against Goray & Schmidt's Table 3, Fresnel and the integral solver. Refuses vertical facets and undercuts; converges slowly on sawtooth corners, and loses precision at high truncation on profiles deeper than about half the period, both reported in provenance. No coatings |
|
|
153
162
|
|
|
@@ -310,8 +319,21 @@ hides it), and the window has one tab for each task:
|
|
|
310
319
|
own boundary-condition control (perfect conductivity or tabulated optical
|
|
311
320
|
constants) and a **Corner mesh** control: off, or a grading order that clusters
|
|
312
321
|
the boundary nodes at the profile's corners, which a cornered profile needs to
|
|
313
|
-
converge quickly (4
|
|
322
|
+
converge quickly (4, the default). It takes minutes where the scalar solver takes milliseconds, so the
|
|
314
323
|
window opens on the scalar solver and this tab computes only when asked to.
|
|
324
|
+
- **Compare:** the geometry solved by several solvers at once, each with the
|
|
325
|
+
settings on its own tab. Efficiency is drawn with order by colour and solver by
|
|
326
|
+
line style, above each solver's difference from a chosen reference, on a linear
|
|
327
|
+
or log scale. A solver that cannot model the problem, such as the C-method on a
|
|
328
|
+
lamellar groove, is named with its reason and the others still run.
|
|
329
|
+
|
|
330
|
+

|
|
331
|
+
|
|
332
|
+
*Four solvers on one problem: a 40 nm gold sinusoid, 5% deep, at 80° incidence,
|
|
333
|
+
unpolarized. The lower panel is each solver's difference from the C-method on a
|
|
334
|
+
log scale: the integral method and RCWA agree with it to within 2e-6, and the
|
|
335
|
+
scalar one is off by up to 0.018. That agreement is a consistency check between
|
|
336
|
+
methods, not a verification of any of them; see Status.*
|
|
315
337
|
- **Setup:** the material library, showing which optical constants are installed, the
|
|
316
338
|
range each covers, where each came from, and how to add others. The coating itself is
|
|
317
339
|
chosen in the geometry dock, because it is a `Problem` field, like the period, and the
|
|
@@ -70,6 +70,15 @@ beside them, both written in-house and both experimental: a rigorous coupled-wav
|
|
|
70
70
|
analysis (RCWA), which also models coated gratings through conformal layers, and
|
|
71
71
|
the C-method (Chandezon). Each has a tab in the efficiency window since 0.2.1.
|
|
72
72
|
|
|
73
|
+
**Every modelling method here still needs verification**, the rigorous ones
|
|
74
|
+
included. The checks in the table below show that each method reproduces the
|
|
75
|
+
cases it was checked on; they do not show that it is right on yours. Nor does
|
|
76
|
+
agreement between the solvers: they share the problem specification, the optical
|
|
77
|
+
constant tables and the angle conventions, so a mistake in any of those moves all
|
|
78
|
+
of them together and no comparison between them can see it. A number from any
|
|
79
|
+
solver is evidence to be checked against measurement or a published benchmark
|
|
80
|
+
before it is relied on.
|
|
81
|
+
|
|
73
82
|
## Scope
|
|
74
83
|
|
|
75
84
|
The metrology (`gratinglab.metrology`) reads AFM scans: Bruker/Veeco Nanoscope `.spm`
|
|
@@ -102,7 +111,7 @@ The method backends, in order of development:
|
|
|
102
111
|
| Method | Status |
|
|
103
112
|
|---|---|
|
|
104
113
|
| Scalar (Kirchhoff, x-ray blaze form) | experimental; checked against closed-form results |
|
|
105
|
-
| Integral method (boundary integral, conical, undercut-capable) | experimental; perfect and finite conductivity (`conductivity="tabulated"`, Goray & Schmidt 2010), absolute efficiencies with the absorbed fraction recorded; a corner-graded mesh for profiles with corners (`corner_grading=4`), under either boundary condition; checked against published benchmark tables (Maystre & Popov; Goray & Schmidt), energy balance and reciprocity |
|
|
114
|
+
| Integral method (boundary integral, conical, undercut-capable) | experimental; perfect and finite conductivity (`conductivity="tabulated"`, Goray & Schmidt 2010), absolute efficiencies with the absorbed fraction recorded; a corner-graded mesh for profiles with corners (`corner_grading=4`, the default since 0.2.7; `0` is the equal-arc mesh), under either boundary condition; checked against published benchmark tables (Maystre & Popov; Goray & Schmidt), energy balance and reciprocity |
|
|
106
115
|
| RCWA (in-plane, conical, off-plane; Li factorization, S-matrix, layer stacks) | experimental, since 0.2.0; lamellar gratings exact and other profiles staircased, transmitted orders, and coatings through conformal layers (`Problem.layers`); checked against Fresnel, the Airy film formula, reciprocity, the integral solver, and published lamellar values (Granet; Lalanne & Hugonin; Gralak; Goray & Schmidt's conical tables). Not yet checked: staircase accuracy in metallic TM, and layers of a second material against an independent code |
|
|
107
116
|
| C-method (Chandezon) | experimental, since 0.2.0; perfect conductor in any mount, and one interface into an absorbing or lossless material, conical and coupled; checked against Goray & Schmidt's Table 3, Fresnel and the integral solver. Refuses vertical facets and undercuts; converges slowly on sawtooth corners, and loses precision at high truncation on profiles deeper than about half the period, both reported in provenance. No coatings |
|
|
108
117
|
|
|
@@ -265,8 +274,21 @@ hides it), and the window has one tab for each task:
|
|
|
265
274
|
own boundary-condition control (perfect conductivity or tabulated optical
|
|
266
275
|
constants) and a **Corner mesh** control: off, or a grading order that clusters
|
|
267
276
|
the boundary nodes at the profile's corners, which a cornered profile needs to
|
|
268
|
-
converge quickly (4
|
|
277
|
+
converge quickly (4, the default). It takes minutes where the scalar solver takes milliseconds, so the
|
|
269
278
|
window opens on the scalar solver and this tab computes only when asked to.
|
|
279
|
+
- **Compare:** the geometry solved by several solvers at once, each with the
|
|
280
|
+
settings on its own tab. Efficiency is drawn with order by colour and solver by
|
|
281
|
+
line style, above each solver's difference from a chosen reference, on a linear
|
|
282
|
+
or log scale. A solver that cannot model the problem, such as the C-method on a
|
|
283
|
+
lamellar groove, is named with its reason and the others still run.
|
|
284
|
+
|
|
285
|
+

|
|
286
|
+
|
|
287
|
+
*Four solvers on one problem: a 40 nm gold sinusoid, 5% deep, at 80° incidence,
|
|
288
|
+
unpolarized. The lower panel is each solver's difference from the C-method on a
|
|
289
|
+
log scale: the integral method and RCWA agree with it to within 2e-6, and the
|
|
290
|
+
scalar one is off by up to 0.018. That agreement is a consistency check between
|
|
291
|
+
methods, not a verification of any of them; see Status.*
|
|
270
292
|
- **Setup:** the material library, showing which optical constants are installed, the
|
|
271
293
|
range each covers, where each came from, and how to add others. The coating itself is
|
|
272
294
|
chosen in the geometry dock, because it is a `Problem` field, like the period, and the
|
|
@@ -22,7 +22,7 @@ from .measured import MeasuredEfficiency, MeasurementProvenance
|
|
|
22
22
|
from .resolution import ResolvingPower, resolving_power
|
|
23
23
|
from .result import EfficiencyScan, OrderEfficiency, Provenance
|
|
24
24
|
|
|
25
|
-
__version__ = "0.2.
|
|
25
|
+
__version__ = "0.2.7"
|
|
26
26
|
|
|
27
27
|
__all__ = [
|
|
28
28
|
"Illumination",
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
"""What the Compare tab draws and says, without a GUI toolkit.
|
|
2
|
+
|
|
3
|
+
The Compare tab runs one geometry through several solvers and puts the
|
|
4
|
+
answers on one pair of axes: efficiency above, the difference from a chosen
|
|
5
|
+
reference solver below. Like :mod:`gratinglab.gui.provenance` and
|
|
6
|
+
:mod:`gratinglab.gui.efficiency_plot`, the decisions live here, so they can be
|
|
7
|
+
tested without a window and the widget only paints them.
|
|
8
|
+
|
|
9
|
+
Colour means the order and line style means the solver, never the other way
|
|
10
|
+
round. The order colours are the ones every solver tab already uses
|
|
11
|
+
(:func:`~gratinglab.gui.theme.order_colors`), so m = +1 is the same orange on
|
|
12
|
+
the Compare tab as on the Scalar tab. A solver keeps its line style whichever
|
|
13
|
+
is the reference and whichever others are ticked, because the style is fixed
|
|
14
|
+
by the solver's name rather than by its position in the run.
|
|
15
|
+
|
|
16
|
+
A solver that refused the problem, or failed on it, is not dropped from the
|
|
17
|
+
picture without a word. :class:`ComparisonOutcome` carries every solver that
|
|
18
|
+
was asked for, and :func:`comparison_lines` names each one that produced
|
|
19
|
+
nothing, with the reason it gave.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from collections.abc import Collection, Sequence
|
|
25
|
+
from dataclasses import dataclass, field
|
|
26
|
+
from typing import TYPE_CHECKING
|
|
27
|
+
|
|
28
|
+
import numpy as np
|
|
29
|
+
|
|
30
|
+
from .provenance import Line
|
|
31
|
+
from .theme import Theme, order_colors, set_titles, style_axes
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
34
|
+
from ..checks import EnergyReport
|
|
35
|
+
from ..compare import Comparison
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"VERIFICATION_NOTE",
|
|
39
|
+
"ComparisonOutcome",
|
|
40
|
+
"line_style",
|
|
41
|
+
"pick_reference",
|
|
42
|
+
"draw_comparison",
|
|
43
|
+
"comparison_lines",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
#: Line styles in the order solvers are registered. Four solvers, four
|
|
47
|
+
#: styles: solid, dashed, dash-dot and dotted all stay distinct at 1.3 pt,
|
|
48
|
+
#: which no fifth matplotlib style does reliably.
|
|
49
|
+
_STYLES = ("-", "--", "-.", ":")
|
|
50
|
+
|
|
51
|
+
#: Closes every comparison the panel reports. Two solvers agreeing is evidence
|
|
52
|
+
#: that neither has a bug the other lacks, not that either is right: they can
|
|
53
|
+
#: share an assumption, a material table or a geometry convention. Every
|
|
54
|
+
#: method here is experimental, so the panel says so where agreement is shown,
|
|
55
|
+
#: dimmed because it is a fact about the methods, not a fault in this run.
|
|
56
|
+
VERIFICATION_NOTE = (
|
|
57
|
+
"\nagreement between solvers is not verification: every method here is "
|
|
58
|
+
"experimental and needs checking against measurement or a published "
|
|
59
|
+
"benchmark\n"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
#: More legend entries than this and the order legend takes a second column,
|
|
63
|
+
#: as the solver tabs' legend does.
|
|
64
|
+
_LEGEND_ROWS = 20
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass(frozen=True)
|
|
68
|
+
class ComparisonOutcome:
|
|
69
|
+
"""Everything one Compare run produced, across the thread boundary.
|
|
70
|
+
|
|
71
|
+
``requested`` is every solver that was ticked, in run order. Each of them
|
|
72
|
+
ends up in exactly one of ``comparison.methods``, ``refused`` or
|
|
73
|
+
``failed``. ``comparison`` is ``None`` when no solver produced a scan.
|
|
74
|
+
``refused`` holds solvers whose capabilities declined the problem
|
|
75
|
+
(an ``UnsupportedConfiguration``, or a form error on that solver's own
|
|
76
|
+
tab), ``failed`` those that raised anything else; both map a solver name
|
|
77
|
+
to the reason it gave.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
requested: tuple[str, ...]
|
|
81
|
+
comparison: "Comparison | None"
|
|
82
|
+
energies: dict[str, "EnergyReport"] = field(default_factory=dict)
|
|
83
|
+
refused: dict[str, str] = field(default_factory=dict)
|
|
84
|
+
failed: dict[str, str] = field(default_factory=dict)
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def solved(self) -> tuple[str, ...]:
|
|
88
|
+
return () if self.comparison is None else self.comparison.methods
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def line_style(name: str, solvers: Sequence[str]) -> str:
|
|
92
|
+
"""The line style of solver ``name``, fixed by its place in ``solvers``.
|
|
93
|
+
|
|
94
|
+
Pass every registered solver, not only the ticked ones, so that unticking
|
|
95
|
+
one never restyles the rest.
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
index = list(solvers).index(name)
|
|
99
|
+
except ValueError:
|
|
100
|
+
index = len(solvers)
|
|
101
|
+
return _STYLES[index % len(_STYLES)]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def pick_reference(wanted: str | None, solved: Sequence[str]) -> str | None:
|
|
105
|
+
"""The reference actually used: ``wanted`` if it solved, else the first
|
|
106
|
+
rigorous solver that did, else the first that did.
|
|
107
|
+
|
|
108
|
+
A reference that refused cannot be subtracted from anything, and falling
|
|
109
|
+
back silently would make the lower panel mean something other than its
|
|
110
|
+
title says, so the caller reports the substitution (see
|
|
111
|
+
:func:`comparison_lines`).
|
|
112
|
+
"""
|
|
113
|
+
if not solved:
|
|
114
|
+
return None
|
|
115
|
+
if wanted in solved:
|
|
116
|
+
return wanted
|
|
117
|
+
from ..solvers.base import get_solver
|
|
118
|
+
|
|
119
|
+
for name in solved:
|
|
120
|
+
try:
|
|
121
|
+
if get_solver(name).capabilities.rigorous:
|
|
122
|
+
return name
|
|
123
|
+
except KeyError:
|
|
124
|
+
continue
|
|
125
|
+
return solved[0]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _title(name: str) -> str:
|
|
129
|
+
from .docs import short_title
|
|
130
|
+
|
|
131
|
+
return short_title(name)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def draw_comparison(
|
|
135
|
+
efficiency_axes,
|
|
136
|
+
difference_axes,
|
|
137
|
+
comparison: "Comparison",
|
|
138
|
+
reference: str,
|
|
139
|
+
visible: Collection[int],
|
|
140
|
+
theme: Theme,
|
|
141
|
+
*,
|
|
142
|
+
solvers: Sequence[str],
|
|
143
|
+
log_difference: bool = False,
|
|
144
|
+
) -> dict[int, str]:
|
|
145
|
+
"""Draw ``comparison`` into the two axes; return the colour of each order.
|
|
146
|
+
|
|
147
|
+
The upper axes get each solver's efficiency in every visible order. The
|
|
148
|
+
lower get ``solver - reference`` for every other solver, or ``|solver -
|
|
149
|
+
reference|`` on a log scale when ``log_difference`` is set, which is what
|
|
150
|
+
makes a 1e-7 agreement between rigorous methods visible beside a 0.1
|
|
151
|
+
disagreement with the scalar one. Exact zeros are left off a log axis
|
|
152
|
+
rather than drawn at an invented floor.
|
|
153
|
+
"""
|
|
154
|
+
colors = order_colors((int(m) for m in comparison.orders), theme)
|
|
155
|
+
wavelengths = comparison.wavelengths
|
|
156
|
+
shown = [
|
|
157
|
+
(column, int(order))
|
|
158
|
+
for column, order in enumerate(comparison.orders)
|
|
159
|
+
if int(order) in visible
|
|
160
|
+
]
|
|
161
|
+
ref_index = comparison.methods.index(reference)
|
|
162
|
+
|
|
163
|
+
for axes in (efficiency_axes, difference_axes):
|
|
164
|
+
axes.clear()
|
|
165
|
+
style_axes(axes, theme)
|
|
166
|
+
axes.grid(alpha=0.35, color=theme.grid)
|
|
167
|
+
|
|
168
|
+
for index, name in enumerate(comparison.methods):
|
|
169
|
+
style = line_style(name, solvers)
|
|
170
|
+
for column, order in shown:
|
|
171
|
+
efficiency_axes.plot(
|
|
172
|
+
wavelengths, comparison.efficiency[index, :, column],
|
|
173
|
+
color=colors[order], ls=style, lw=1.3,
|
|
174
|
+
)
|
|
175
|
+
if index == ref_index:
|
|
176
|
+
continue
|
|
177
|
+
delta = comparison.efficiency[index] - comparison.efficiency[ref_index]
|
|
178
|
+
for column, order in shown:
|
|
179
|
+
values = delta[:, column]
|
|
180
|
+
if log_difference:
|
|
181
|
+
values = np.abs(values)
|
|
182
|
+
values = np.where(values > 0.0, values, np.nan)
|
|
183
|
+
difference_axes.plot(
|
|
184
|
+
wavelengths, values, color=colors[order], ls=style, lw=1.3,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
efficiency_axes.axhline(1.0, color=theme.bad, lw=0.8, ls=":", alpha=0.8)
|
|
188
|
+
efficiency_axes.set_ylabel("efficiency")
|
|
189
|
+
efficiency_axes.set_ylim(bottom=0)
|
|
190
|
+
count = len(comparison.methods)
|
|
191
|
+
set_titles(
|
|
192
|
+
efficiency_axes, "Efficiency by solver",
|
|
193
|
+
f"{count} solver{'s' if count != 1 else ''}, "
|
|
194
|
+
f"{len(shown)} of {len(comparison.orders)} orders shown",
|
|
195
|
+
theme,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
if log_difference:
|
|
199
|
+
difference_axes.set_yscale("log")
|
|
200
|
+
difference_axes.set_ylabel("|Δ efficiency|")
|
|
201
|
+
else:
|
|
202
|
+
difference_axes.set_yscale("linear")
|
|
203
|
+
difference_axes.axhline(0.0, color=theme.ink, lw=0.8, alpha=0.6)
|
|
204
|
+
difference_axes.set_ylabel("Δ efficiency")
|
|
205
|
+
difference_axes.set_xlabel("wavelength (nm)")
|
|
206
|
+
set_titles(
|
|
207
|
+
difference_axes, f"Difference from {_title(reference)}",
|
|
208
|
+
_worst_detail(comparison, reference, shown), theme,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
_legends(
|
|
212
|
+
efficiency_axes, difference_axes, comparison, shown, colors, theme,
|
|
213
|
+
solvers, reference,
|
|
214
|
+
)
|
|
215
|
+
return colors
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _worst_detail(comparison, reference: str, shown) -> str:
|
|
219
|
+
"""The largest disagreement over the drawn orders, for the title's right."""
|
|
220
|
+
others = [m for m in comparison.methods if m != reference]
|
|
221
|
+
if not others:
|
|
222
|
+
return "nothing to compare with"
|
|
223
|
+
if not shown:
|
|
224
|
+
return "no orders shown"
|
|
225
|
+
columns = [c for c, _ in shown]
|
|
226
|
+
ref = comparison.efficiency[comparison.methods.index(reference)][:, columns]
|
|
227
|
+
worst_name, worst = None, -1.0
|
|
228
|
+
for name in others:
|
|
229
|
+
delta = comparison.efficiency[comparison.methods.index(name)][:, columns] - ref
|
|
230
|
+
value = float(np.abs(delta).max())
|
|
231
|
+
if value > worst:
|
|
232
|
+
worst_name, worst = name, value
|
|
233
|
+
return f"largest |Δ| {worst:.2g} ({_title(worst_name)})"
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def _legends(axes, difference_axes, comparison, shown, colors, theme, solvers, reference) -> None:
|
|
237
|
+
"""Two keys outside the axes: orders by colour above, solvers by style below.
|
|
238
|
+
|
|
239
|
+
Two keys rather than one entry per curve, because the curves are the
|
|
240
|
+
product of the two and a 4 x 10 legend would be forty entries saying the
|
|
241
|
+
same thing forty times. One key per axes, rather than both on one, so the
|
|
242
|
+
figure's constrained layout makes room for each: a second legend added
|
|
243
|
+
to the same axes is invisible to the layout and was clipped.
|
|
244
|
+
"""
|
|
245
|
+
from matplotlib.lines import Line2D
|
|
246
|
+
|
|
247
|
+
keys = []
|
|
248
|
+
if shown:
|
|
249
|
+
keys.append(axes.legend(
|
|
250
|
+
[Line2D([], [], color=colors[m], lw=2.0) for _, m in shown],
|
|
251
|
+
[f"m={m:+d}" for _, m in shown],
|
|
252
|
+
fontsize=7, loc="upper left", bbox_to_anchor=(1.01, 1.0),
|
|
253
|
+
borderaxespad=0.0, frameon=False,
|
|
254
|
+
ncol=1 if len(shown) <= _LEGEND_ROWS else 2,
|
|
255
|
+
))
|
|
256
|
+
keys.append(difference_axes.legend(
|
|
257
|
+
[Line2D([], [], color=theme.ink, ls=line_style(name, solvers), lw=1.3)
|
|
258
|
+
for name in comparison.methods],
|
|
259
|
+
[_title(name) + (" (ref)" if name == reference else "")
|
|
260
|
+
for name in comparison.methods],
|
|
261
|
+
fontsize=7, loc="upper left", bbox_to_anchor=(1.01, 1.0),
|
|
262
|
+
borderaxespad=0.0, frameon=False, handlelength=3.0,
|
|
263
|
+
))
|
|
264
|
+
for legend in keys:
|
|
265
|
+
for text in legend.get_texts():
|
|
266
|
+
text.set_color(theme.ink)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def comparison_lines(
|
|
270
|
+
outcome: ComparisonOutcome,
|
|
271
|
+
wanted_reference: str | None,
|
|
272
|
+
*,
|
|
273
|
+
cancelled: bool = False,
|
|
274
|
+
) -> tuple[Line, ...]:
|
|
275
|
+
"""What the Compare tab's panel says about a run, solver by solver.
|
|
276
|
+
|
|
277
|
+
One line per requested solver, in run order: the time it took and its
|
|
278
|
+
energy verdict if it solved, its reason if it refused or failed. Then the
|
|
279
|
+
headline numbers against the reference, over every order, so the panel
|
|
280
|
+
states the disagreement the plot shows. A refusal is reported in the
|
|
281
|
+
``warn`` colour, because it is the solver declining a problem it cannot
|
|
282
|
+
model, which is correct behaviour and not a fault; a failure is ``bad``.
|
|
283
|
+
"""
|
|
284
|
+
lines: list[Line] = []
|
|
285
|
+
if cancelled:
|
|
286
|
+
lines.append(Line(
|
|
287
|
+
"cancelled: the comparison stopped; showing the previous one\n", "dim",
|
|
288
|
+
))
|
|
289
|
+
for name in outcome.requested:
|
|
290
|
+
lines.append(Line(f"{_title(name)}: ", None))
|
|
291
|
+
if name in outcome.refused:
|
|
292
|
+
lines.append(Line(f"refused: {outcome.refused[name]}\n", "warn"))
|
|
293
|
+
elif name in outcome.failed:
|
|
294
|
+
lines.append(Line(f"failed: {outcome.failed[name]}\n", "bad"))
|
|
295
|
+
elif name in outcome.solved:
|
|
296
|
+
lines.extend(_solved_line(name, outcome))
|
|
297
|
+
else:
|
|
298
|
+
lines.append(Line("not run\n", "dim"))
|
|
299
|
+
|
|
300
|
+
reference = pick_reference(wanted_reference, outcome.solved)
|
|
301
|
+
if reference is None:
|
|
302
|
+
lines.append(Line("\nno solver produced a result to compare\n", "bad"))
|
|
303
|
+
return tuple(lines)
|
|
304
|
+
if wanted_reference is not None and reference != wanted_reference:
|
|
305
|
+
lines.append(Line(
|
|
306
|
+
f"\nreference: {_title(wanted_reference)} did not solve, so the "
|
|
307
|
+
f"differences are from {_title(reference)}\n", "warn",
|
|
308
|
+
))
|
|
309
|
+
else:
|
|
310
|
+
lines.append(Line(f"\nreference: {_title(reference)}\n", "dim"))
|
|
311
|
+
|
|
312
|
+
comparison = outcome.comparison
|
|
313
|
+
for name in comparison.methods:
|
|
314
|
+
if name == reference:
|
|
315
|
+
continue
|
|
316
|
+
summary = comparison.summary(name, reference)
|
|
317
|
+
lines.append(Line(
|
|
318
|
+
f" {_title(name)} − {_title(reference)}: max |Δ| "
|
|
319
|
+
f"{summary['max_abs_difference']:.3g} at "
|
|
320
|
+
f"{summary['at_wavelength']:.4g} nm, m={summary['at_order']:+d}; "
|
|
321
|
+
f"rms {summary['rms_difference']:.3g}\n", None,
|
|
322
|
+
))
|
|
323
|
+
lines.append(Line(VERIFICATION_NOTE, "dim"))
|
|
324
|
+
return tuple(lines)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def _solved_line(name: str, outcome: ComparisonOutcome) -> list[Line]:
|
|
328
|
+
scan = outcome.comparison.scans[outcome.comparison.methods.index(name)]
|
|
329
|
+
provenance = scan.provenance
|
|
330
|
+
wall = provenance.wall_time_s
|
|
331
|
+
timing = "time not recorded" if wall is None else f"{wall * 1e3:.0f} ms"
|
|
332
|
+
parts = [Line(f"solved, {timing}", "dim")]
|
|
333
|
+
if not _rigorous(name):
|
|
334
|
+
parts.append(Line(", approximate method", "dim"))
|
|
335
|
+
energy = outcome.energies.get(name)
|
|
336
|
+
if energy is not None:
|
|
337
|
+
parts.append(Line(", energy balance ", "dim"))
|
|
338
|
+
if energy.passed:
|
|
339
|
+
parts.append(Line("ok", "ok"))
|
|
340
|
+
elif energy.lossless:
|
|
341
|
+
deviation = float(np.abs(energy.total - 1.0).max())
|
|
342
|
+
parts.append(Line(f"off unity by up to {deviation:.2g}", "bad"))
|
|
343
|
+
else:
|
|
344
|
+
parts.append(Line(f"EXCEEDS UNITY by {energy.max_excess:.2g}", "bad"))
|
|
345
|
+
parts.append(Line("\n"))
|
|
346
|
+
# The solver's own warnings, word for word and in the solver tabs' form.
|
|
347
|
+
# A verdict alone says that something is wrong; the warning is where the
|
|
348
|
+
# solver says why and what to change. Without it, the integral solver on
|
|
349
|
+
# an equal-arc mesh read "EXCEEDS UNITY by 2.3" here while its own tab
|
|
350
|
+
# said "pass corner_grading=4".
|
|
351
|
+
parts.extend(Line(f" ⚠ {warning}\n", "warn") for warning in provenance.warnings)
|
|
352
|
+
return parts
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def _rigorous(name: str) -> bool:
|
|
356
|
+
from ..solvers.base import get_solver
|
|
357
|
+
|
|
358
|
+
try:
|
|
359
|
+
return get_solver(name).capabilities.rigorous
|
|
360
|
+
except KeyError:
|
|
361
|
+
return True
|
|
@@ -81,7 +81,9 @@ CONDUCTIVITY_NOTES = {
|
|
|
81
81
|
|
|
82
82
|
|
|
83
83
|
#: The corner-mesh choices as the form shows them: ``off`` is the equal-arc
|
|
84
|
-
#: mesh
|
|
84
|
+
#: mesh and a number is the grading order. The solver's default, ``4``, is
|
|
85
|
+
#: the form's too (`IntegralOptionsState`), for the reason `CONDUCTIVITY_MODES`
|
|
86
|
+
#: gives; ``off`` is listed first only because the list reads in order.
|
|
85
87
|
GRADING_CHOICES = ("off",) + tuple(str(p) for p in range(2, _MAX_GRADING + 1))
|
|
86
88
|
|
|
87
89
|
#: What the choice means, shown beneath the selector, for the reason
|
|
@@ -107,7 +109,7 @@ class IntegralOptionsState:
|
|
|
107
109
|
|
|
108
110
|
boundary_points: str = "400"
|
|
109
111
|
conductivity: str = CONDUCTIVITY_MODES[0]
|
|
110
|
-
corner_grading: str =
|
|
112
|
+
corner_grading: str = "4"
|
|
111
113
|
|
|
112
114
|
def with_field(self, name: str, value: Any) -> "IntegralOptionsState":
|
|
113
115
|
"""Return a copy with one field changed."""
|