gratinglab 0.3.4__tar.gz → 0.3.5__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.
Files changed (214) hide show
  1. {gratinglab-0.3.4/src/gratinglab.egg-info → gratinglab-0.3.5}/PKG-INFO +1 -1
  2. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/__init__.py +1 -1
  3. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/mathtext.py +83 -2
  4. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/qt/setup_tab.py +2 -3
  5. gratinglab-0.3.5/src/gratinglab/gui/qt/theory_viewer.py +210 -0
  6. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/richtext.py +50 -4
  7. gratinglab-0.3.5/src/gratinglab/io/__init__.py +17 -0
  8. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/io/ggp.py +64 -44
  9. gratinglab-0.3.5/src/gratinglab/metrology/_analyzer_summary.py +385 -0
  10. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/analyzer.py +17 -368
  11. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/wiki/reading-outputs.md +13 -6
  12. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/profiles/_shapes.py +1 -1
  13. gratinglab-0.3.5/src/gratinglab/solvers/_scalar_groove.py +556 -0
  14. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/integral/__init__.py +1 -1
  15. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/scalar.py +17 -517
  16. {gratinglab-0.3.4 → gratinglab-0.3.5/src/gratinglab.egg-info}/PKG-INFO +1 -1
  17. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab.egg-info/SOURCES.txt +2 -0
  18. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_ggp.py +37 -29
  19. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_mathtext.py +56 -0
  20. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_richtext.py +25 -0
  21. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_scalar.py +3 -2
  22. gratinglab-0.3.4/src/gratinglab/gui/qt/theory_viewer.py +0 -99
  23. gratinglab-0.3.4/src/gratinglab/io/__init__.py +0 -0
  24. {gratinglab-0.3.4 → gratinglab-0.3.5}/LICENSE +0 -0
  25. {gratinglab-0.3.4 → gratinglab-0.3.5}/README.md +0 -0
  26. {gratinglab-0.3.4 → gratinglab-0.3.5}/pyproject.toml +0 -0
  27. {gratinglab-0.3.4 → gratinglab-0.3.5}/setup.cfg +0 -0
  28. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/checks.py +0 -0
  29. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/compare.py +0 -0
  30. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/convergence.py +0 -0
  31. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/geometry.py +0 -0
  32. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/__init__.py +0 -0
  33. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/app.py +0 -0
  34. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/cmethod_options.py +0 -0
  35. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/comparison.py +0 -0
  36. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/diagram.py +0 -0
  37. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/diagram3d.py +0 -0
  38. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/docs.py +0 -0
  39. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/efficiency_plot.py +0 -0
  40. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/icon.py +0 -0
  41. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/integral_options.py +0 -0
  42. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/launcher.py +0 -0
  43. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/orders.py +0 -0
  44. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/provenance.py +0 -0
  45. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/qt/__init__.py +0 -0
  46. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/qt/cmethod_tab.py +0 -0
  47. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/qt/compare_tab.py +0 -0
  48. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/qt/geometry_panel.py +0 -0
  49. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/qt/geometry_tab.py +0 -0
  50. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/qt/integral_tab.py +0 -0
  51. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/qt/launcher.py +0 -0
  52. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/qt/main_window.py +0 -0
  53. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/qt/profile_plot_panel.py +0 -0
  54. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/qt/rcwa_tab.py +0 -0
  55. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/qt/scalar_tab.py +0 -0
  56. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/qt/scene3d_panel.py +0 -0
  57. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/qt/sizing.py +0 -0
  58. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/qt/theming.py +0 -0
  59. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/qt/worker.py +0 -0
  60. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/rcwa_options.py +0 -0
  61. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/scalar_options.py +0 -0
  62. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/state.py +0 -0
  63. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/gui/theme.py +0 -0
  64. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/illumination.py +0 -0
  65. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/io/boundary.py +0 -0
  66. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/io/efficiency_table.py +0 -0
  67. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/io/txl.py +0 -0
  68. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/materials/__init__.py +0 -0
  69. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/materials/data/SOURCES.md +0 -0
  70. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/materials/fresnel.py +0 -0
  71. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/materials/optical.py +0 -0
  72. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/materials/refractiveindex.py +0 -0
  73. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/materials/typical.py +0 -0
  74. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/measured.py +0 -0
  75. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/__init__.py +0 -0
  76. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/boundary/__init__.py +0 -0
  77. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/boundary/average.py +0 -0
  78. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/boundary/cross_section.py +0 -0
  79. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/boundary/pipeline.py +0 -0
  80. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/boundary/swaths.py +0 -0
  81. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/cli.py +0 -0
  82. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/config.py +0 -0
  83. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/core/__init__.py +0 -0
  84. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/core/analysis.py +0 -0
  85. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/core/image_flatten.py +0 -0
  86. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/core/orientation.py +0 -0
  87. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/core/processing.py +0 -0
  88. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/core/region_mask.py +0 -0
  89. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/core/scars.py +0 -0
  90. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/core/tip.py +0 -0
  91. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/data/__init__.py +0 -0
  92. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/data/aggregation.py +0 -0
  93. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/gui/__init__.py +0 -0
  94. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/gui/icon.py +0 -0
  95. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/gui/qt/__init__.py +0 -0
  96. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/gui/qt/app.py +0 -0
  97. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/gui/qt/boundary_view.py +0 -0
  98. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/gui/qt/canvas.py +0 -0
  99. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/gui/qt/efficiency_dialog.py +0 -0
  100. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/gui/qt/import_view.py +0 -0
  101. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/gui/qt/layout.py +0 -0
  102. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/gui/qt/main_window.py +0 -0
  103. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/gui/qt/swath_dialog.py +0 -0
  104. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/gui/qt/wiki_view.py +0 -0
  105. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/gui/qt/worker.py +0 -0
  106. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/gui/state.py +0 -0
  107. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/io/__init__.py +0 -0
  108. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/io/file_io.py +0 -0
  109. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/io/ibw.py +0 -0
  110. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/io/metrics.py +0 -0
  111. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/io/sem.py +0 -0
  112. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/io/spm.py +0 -0
  113. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/settings.py +0 -0
  114. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/stats/__init__.py +0 -0
  115. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/stats/analysis.py +0 -0
  116. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/stats/icc.py +0 -0
  117. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/visualization/__init__.py +0 -0
  118. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/visualization/diagnostics.py +0 -0
  119. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/visualization/profiles.py +0 -0
  120. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/visualization/statistics.py +0 -0
  121. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/wiki/__init__.py +0 -0
  122. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/wiki/facet-fitting.md +0 -0
  123. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/wiki/file-formats.md +0 -0
  124. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/wiki/flattening.md +0 -0
  125. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/wiki/groove-orientation.md +0 -0
  126. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/wiki/icc-correction.md +0 -0
  127. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/wiki/overview.md +0 -0
  128. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/wiki/row-groups.md +0 -0
  129. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/wiki/scars-and-masking.md +0 -0
  130. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/wiki/swath-statistics.md +0 -0
  131. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/wiki/tip-correction.md +0 -0
  132. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/metrology/workflows.py +0 -0
  133. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/problem.py +0 -0
  134. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/profiles/__init__.py +0 -0
  135. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/profiles/_base.py +0 -0
  136. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/profiles/_polyline.py +0 -0
  137. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/profiles/_trapezoid.py +0 -0
  138. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/profiles/_types.py +0 -0
  139. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/resolution.py +0 -0
  140. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/result.py +0 -0
  141. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/__init__.py +0 -0
  142. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/_conditioning.py +0 -0
  143. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/_execution.py +0 -0
  144. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/base.py +0 -0
  145. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/cmethod/__init__.py +0 -0
  146. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/cmethod/_core.py +0 -0
  147. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/cmethod/_series.py +0 -0
  148. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/integral/_boundary.py +0 -0
  149. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/integral/_core.py +0 -0
  150. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/integral/_finite.py +0 -0
  151. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/integral/_greens.py +0 -0
  152. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/integral/_kernels.py +0 -0
  153. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/integral/_nystrom.py +0 -0
  154. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/rcwa/__init__.py +0 -0
  155. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/rcwa/_core.py +0 -0
  156. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab/solvers/rcwa/_stack.py +0 -0
  157. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab.egg-info/dependency_links.txt +0 -0
  158. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab.egg-info/entry_points.txt +0 -0
  159. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab.egg-info/requires.txt +0 -0
  160. {gratinglab-0.3.4 → gratinglab-0.3.5}/src/gratinglab.egg-info/top_level.txt +0 -0
  161. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_changelog.py +0 -0
  162. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_checks.py +0 -0
  163. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_cmethod.py +0 -0
  164. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_compare.py +0 -0
  165. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_conditioning.py +0 -0
  166. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_convergence.py +0 -0
  167. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_cross_method.py +0 -0
  168. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_efficiency_table.py +0 -0
  169. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_four_way.py +0 -0
  170. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_fresnel.py +0 -0
  171. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_geometry.py +0 -0
  172. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_cmethod_options.py +0 -0
  173. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_cmethod_tab.py +0 -0
  174. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_compare_tab.py +0 -0
  175. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_diagram.py +0 -0
  176. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_diagram3d.py +0 -0
  177. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_docs.py +0 -0
  178. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_integral_options.py +0 -0
  179. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_launcher.py +0 -0
  180. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_orders.py +0 -0
  181. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_provenance.py +0 -0
  182. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_qt.py +0 -0
  183. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_rcwa_options.py +0 -0
  184. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_scalar_options.py +0 -0
  185. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_state.py +0 -0
  186. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_gui_theme.py +0 -0
  187. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_illumination.py +0 -0
  188. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_integral_core.py +0 -0
  189. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_integral_corpus.py +0 -0
  190. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_integral_finite.py +0 -0
  191. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_integral_graded.py +0 -0
  192. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_integral_greens.py +0 -0
  193. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_integral_kernels.py +0 -0
  194. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_integral_measured_polygon.py +0 -0
  195. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_integral_operators.py +0 -0
  196. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_integral_solver.py +0 -0
  197. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_io_boundary.py +0 -0
  198. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_littrow.py +0 -0
  199. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_materials.py +0 -0
  200. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_measured.py +0 -0
  201. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_perturbation.py +0 -0
  202. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_profiles.py +0 -0
  203. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_rayleigh_anomaly.py +0 -0
  204. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_rcwa.py +0 -0
  205. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_rcwa_benchmarks.py +0 -0
  206. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_refractiveindex.py +0 -0
  207. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_resolution.py +0 -0
  208. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_result.py +0 -0
  209. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_solver_conformance.py +0 -0
  210. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_solver_execution.py +0 -0
  211. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_solver_progress.py +0 -0
  212. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_trapezoidal_solvers.py +0 -0
  213. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_txl.py +0 -0
  214. {gratinglab-0.3.4 → gratinglab-0.3.5}/tests/test_typical.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gratinglab
3
- Version: 0.3.4
3
+ Version: 0.3.5
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
@@ -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.3.4"
25
+ __version__ = "0.3.5"
26
26
 
27
27
  __all__ = [
28
28
  "Illumination",
@@ -173,7 +173,12 @@ def strip_boxed(latex: str) -> str:
173
173
 
174
174
 
175
175
  def render_math_png(
176
- latex: str, *, dpi: int = 140, color: str = "black"
176
+ latex: str,
177
+ *,
178
+ dpi: int = 140,
179
+ color: str = "black",
180
+ axis_pt: float | None = None,
181
+ min_height_pt: float = 0.0,
177
182
  ) -> bytes | None:
178
183
  r"""Rasterize a LaTeX string (no `$` delimiters) to PNG bytes.
179
184
 
@@ -192,6 +197,20 @@ def render_math_png(
192
197
  confirmed that mathtext's parser does not tolerate an embedded ``\n``. Any
193
198
  run of whitespace is therefore collapsed to a single space before parsing,
194
199
  which matches what plain LaTeX would do with the same source.
200
+
201
+ ``axis_pt`` is for an equation set inside a line of text. Qt places an
202
+ image aligned ``vertical-align: middle`` with its centre a quarter of the
203
+ font's x-height above the baseline, so given that height in points of the
204
+ 10 pt render, space is added above or below until the image's centre lands
205
+ there exactly when the equation's own baseline sits on the text's. Without
206
+ it the image's bottom edge sits on the baseline, and every symbol with a
207
+ descender rides above the line.
208
+
209
+ That rule holds only for an image at least about three x-heights tall;
210
+ Qt sets a shorter one higher, by an amount that depends on its height
211
+ (measured with PySide6 6.11 on 2026-11-12, by scanning solid images from
212
+ 8 to 32 px against a 14 px font). ``min_height_pt`` pads the image evenly
213
+ above and below up to that height, which keeps the centre where it was.
195
214
  """
196
215
  import io
197
216
  import re
@@ -205,7 +224,69 @@ def render_math_png(
205
224
 
206
225
  buffer = io.BytesIO()
207
226
  try:
208
- math_to_image(f"${normalized}$", buffer, dpi=dpi, format="png", color=color)
227
+ if axis_pt is None:
228
+ math_to_image(f"${normalized}$", buffer, dpi=dpi, format="png", color=color)
229
+ return buffer.getvalue()
230
+ return _inline_png(normalized, dpi, color, axis_pt, min_height_pt)
209
231
  except ValueError:
210
232
  return None
233
+
234
+
235
+ def _inline_png(
236
+ latex: str, dpi: int, color: str, axis_pt: float, min_height_pt: float
237
+ ) -> bytes:
238
+ """An inline equation as a transparent PNG, padded about its baseline.
239
+
240
+ The baseline is measured rather than taken from mathtext's box depth,
241
+ which is the layout box and not where the glyphs land: rasterised at
242
+ 101 dpi the two differ by a pixel in either direction depending on the
243
+ expression. So the expression is rendered a second time with an upright
244
+ ``x`` after it, which sits on the baseline, and the bottom of that ``x``
245
+ is read off. The two renders share one vertical frame whenever the ``x``
246
+ is no taller and no deeper than the expression, which is any expression
247
+ holding a letter or digit; otherwise the box depth is used.
248
+ """
249
+ import io
250
+
251
+ import numpy as np
252
+ from matplotlib.colors import to_rgb
253
+ from matplotlib.image import imsave
254
+ from matplotlib.mathtext import MathTextParser
255
+
256
+ parser = MathTextParser("agg")
257
+ ink = np.asarray(parser.parse(f"${latex}$", dpi=dpi).image)
258
+ probe = parser.parse(f"${latex}" + r"\mathrm{x}$", dpi=dpi)
259
+ probe_ink = np.asarray(probe.image)
260
+ rows = ink.shape[0]
261
+ if probe_ink.shape[0] == rows:
262
+ # The rightmost couple of points of the x: its right leg ends on
263
+ # the baseline, and nothing of the expression reaches that far.
264
+ columns = np.flatnonzero(probe_ink.max(axis=0))
265
+ leg = probe_ink[:, columns[-1] - max(1, round(2 * dpi / 72)) + 1 : columns[-1] + 1]
266
+ baseline = int(np.flatnonzero(leg.max(axis=1) > 100)[-1]) + 1
267
+ else:
268
+ rendered = parser.parse(f"${latex}$", dpi=dpi)
269
+ baseline = int(rendered.height - rendered.depth) - 1
270
+
271
+ # Centre on the axis: (above - below) / 2 must equal the axis height.
272
+ scale = dpi / 72.0
273
+ above, below = float(baseline), float(rows - baseline)
274
+ excess = (above - below) - 2 * axis_pt * scale
275
+ if excess > 0:
276
+ below += excess
277
+ else:
278
+ above -= excess
279
+ short = min_height_pt * scale - (above + below)
280
+ if short > 0:
281
+ above += short / 2
282
+ below += short / 2
283
+ top = round(above) - baseline
284
+ bottom = round(below) - (rows - baseline)
285
+
286
+ alpha = np.pad(ink, ((top, bottom), (0, 0))).astype(float) / 255.0
287
+ rgba = np.empty(alpha.shape + (4,))
288
+ rgba[..., :3] = to_rgb(color)
289
+ rgba[..., 3] = alpha
290
+ buffer = io.BytesIO()
291
+ imsave(buffer, rgba, format="png")
211
292
  return buffer.getvalue()
@@ -36,13 +36,12 @@ from PySide6.QtWidgets import (
36
36
  QLabel,
37
37
  QMessageBox,
38
38
  QPushButton,
39
- QTextBrowser,
40
39
  QVBoxLayout,
41
40
  QWidget,
42
41
  )
43
42
 
44
43
  from ..docs import TheoryPage
45
- from .theory_viewer import render_into
44
+ from .theory_viewer import ReadingBrowser, render_into
46
45
 
47
46
  #: What each reader's ``source`` string starts with, and what to cite for it.
48
47
  _CITATIONS = {
@@ -242,7 +241,7 @@ class SetupTab(QWidget):
242
241
 
243
242
  def __init__(self, parent: QWidget | None = None) -> None:
244
243
  super().__init__(parent)
245
- self._browser = QTextBrowser()
244
+ self._browser = ReadingBrowser()
246
245
  self._browser.setOpenExternalLinks(False)
247
246
  self._browser.setOpenLinks(False)
248
247
  # Links leave for the system browser; the page itself never navigates.
@@ -0,0 +1,210 @@
1
+ """The read-only viewer for one theory or reference page.
2
+
3
+ The viewer is thin by design. :mod:`gratinglab.gui.richtext` turns the page
4
+ into HTML and a list of rasterised equations, and this class only passes both
5
+ to a `QTextBrowser`. Everything that could be wrong in a non-trivial way
6
+ (table structure, escaping, math placement, the approximate-method banner) is
7
+ decided and tested in that module, without a window.
8
+
9
+ Images are registered with `QTextDocument.addResource` instead of being
10
+ embedded as `data:` URIs, because `loadResource` does not handle `data:` by
11
+ default. This also removes the least elegant detail of the Tk viewer: a list
12
+ monkey-patched onto the window solely to keep `PhotoImage` objects alive
13
+ against garbage collection while Tk was still displaying them.
14
+
15
+ :class:`ReadingBrowser` is the page itself: a slightly larger font than the
16
+ widgets around it, and a text column kept to a readable width and centred,
17
+ so a wide window adds margin rather than longer lines. The math is sized
18
+ from that font, so an equation in a sentence matches the words beside it.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from PySide6.QtCore import Qt, QUrl
24
+ from PySide6.QtGui import (
25
+ QFontMetricsF,
26
+ QImage,
27
+ QTextBlockFormat,
28
+ QTextCursor,
29
+ QTextDocument,
30
+ QTextImageFormat,
31
+ )
32
+ from PySide6.QtWidgets import QTextBrowser, QVBoxLayout, QWidget
33
+
34
+ from .. import richtext
35
+
36
+ __all__ = ["ReadingBrowser", "TheoryViewer", "render_into"]
37
+
38
+ #: Body text relative to the application font. The pages are long prose, and
39
+ #: the 9 pt that suits a button is small for reading paragraphs of it.
40
+ READING_SCALE = 1.15
41
+
42
+ #: Widest the text column grows, in ems of the reading font: about 100
43
+ #: characters of prose a line. A display equation wider than that widens the
44
+ #: column to fit rather than scrolling sideways.
45
+ READING_WIDTH_EMS = 50
46
+
47
+ #: Space either side of the column, in pixels, when the window is narrower
48
+ #: than the column.
49
+ READING_MARGIN = 24
50
+
51
+ #: Space around tables, which Qt otherwise sets flush against the paragraph
52
+ #: above them.
53
+ _STYLE = "table { margin-top: 6px; margin-bottom: 10px; } th { text-align: left; }"
54
+
55
+ #: Extra space between lines, as a fraction of the font's pixel size. Added
56
+ #: as a fixed distance rather than CSS ``line-height: 135%``, which scales
57
+ #: each line's own height and so opens a wide gap around every line holding
58
+ #: an equation.
59
+ LEADING = 0.3
60
+
61
+
62
+ class ReadingBrowser(QTextBrowser):
63
+ """A `QTextBrowser` laid out for reading a page rather than a log."""
64
+
65
+ def __init__(self, parent: QWidget | None = None) -> None:
66
+ super().__init__(parent)
67
+ font = self.font()
68
+ font.setPointSizeF(font.pointSizeF() * READING_SCALE)
69
+ self.setFont(font)
70
+ self.document().setDefaultFont(font)
71
+ self.document().setDefaultStyleSheet(_STYLE)
72
+ self._widest_image = 0.0
73
+
74
+ def resizeEvent(self, event) -> None: # noqa: N802 - Qt's spelling
75
+ super().resizeEvent(event)
76
+ self._centre_column()
77
+
78
+ def pixel_size(self) -> float:
79
+ """The reading font's size in pixels.
80
+
81
+ From the point size and the screen's DPI rather than `QFontInfo`,
82
+ which reports the matched font and so reads near zero where no system
83
+ fonts are installed, as on the offscreen CI runs.
84
+ """
85
+ return self.font().pointSizeF() * self.logicalDpiY() / 72.0
86
+
87
+ def _centre_column(self) -> None:
88
+ column = max(self.pixel_size() * READING_WIDTH_EMS, self._widest_image)
89
+ spare = self.viewport().width() - column
90
+ side = max(READING_MARGIN, spare / 2)
91
+ root = self.document().rootFrame()
92
+ frame = root.frameFormat()
93
+ if frame.leftMargin() == side and frame.rightMargin() == side:
94
+ return
95
+ frame.setLeftMargin(side)
96
+ frame.setRightMargin(side)
97
+ frame.setTopMargin(READING_MARGIN / 2)
98
+ frame.setBottomMargin(READING_MARGIN)
99
+ root.setFrameFormat(frame)
100
+
101
+ def setHtml(self, text: str) -> None: # noqa: N802 - Qt's spelling
102
+ # setHtml replaces the root frame's format along with the content.
103
+ super().setHtml(text)
104
+ leading = QTextBlockFormat()
105
+ leading.setLineHeight(
106
+ round(self.pixel_size() * LEADING),
107
+ QTextBlockFormat.LineHeightTypes.LineDistanceHeight.value,
108
+ )
109
+ cursor = QTextCursor(self.document())
110
+ cursor.select(QTextCursor.SelectionType.Document)
111
+ cursor.mergeBlockFormat(leading)
112
+ self._widest_image = _widest_image(self.document())
113
+ self._centre_column()
114
+
115
+
116
+ def _widest_image(document: QTextDocument) -> float:
117
+ """The widest image in a document, in pixels, tables included."""
118
+ widest = 0.0
119
+ block = document.begin()
120
+ while block.isValid():
121
+ fragments = block.begin()
122
+ while not fragments.atEnd():
123
+ fmt = fragments.fragment().charFormat()
124
+ if fmt.isImageFormat():
125
+ widest = max(widest, QTextImageFormat(fmt).width())
126
+ fragments += 1
127
+ block = block.next()
128
+ return widest
129
+
130
+
131
+ def render_into(
132
+ browser: QTextBrowser, page, *, device_pixel_ratio: float, text_color: str
133
+ ) -> None:
134
+ """Render a page into an existing `QTextBrowser`, images and all.
135
+
136
+ It is shared by :class:`TheoryViewer` and :class:`~.setup_tab.SetupTab`,
137
+ both of which show "one markdown-ish page, typeset, in a browser," and the
138
+ second use is what justified factoring this function out instead of
139
+ copying it.
140
+ """
141
+ font = browser.document().defaultFont()
142
+ pixels = font.pointSizeF() * browser.logicalDpiY() / 72.0
143
+ rendered = richtext.to_html(
144
+ page,
145
+ device_pixel_ratio=device_pixel_ratio,
146
+ color=text_color,
147
+ inline_dpi=richtext.math_dpi(pixels),
148
+ display_dpi=richtext.math_dpi(pixels, display=True),
149
+ **richtext.inline_metrics(pixels, QFontMetricsF(font).xHeight()),
150
+ )
151
+ document = browser.document()
152
+ for image in rendered.images:
153
+ document.addResource(
154
+ QTextDocument.ResourceType.ImageResource,
155
+ QUrl(image.key),
156
+ QImage.fromData(image.png, "PNG"),
157
+ )
158
+ browser.setHtml(rendered.html)
159
+
160
+
161
+ class TheoryViewer(QWidget):
162
+ """One page, typeset."""
163
+
164
+ def __init__(self, page, parent: QWidget | None = None) -> None:
165
+ # A window in its own right rather than a modal dialog: reading the
166
+ # theory while adjusting the grating is the point.
167
+ super().__init__(parent)
168
+ self.setWindowFlag(Qt.WindowType.Window)
169
+ self.setWindowTitle(page.title)
170
+ self.resize(860, 720)
171
+
172
+ self._browser = ReadingBrowser()
173
+ self._browser.setOpenExternalLinks(False)
174
+ # Relative links between doc pages point at files on disk that this
175
+ # viewer has no route to open. Following them would empty the window.
176
+ self._browser.setOpenLinks(False)
177
+
178
+ layout = QVBoxLayout(self)
179
+ layout.setContentsMargins(0, 0, 0, 0)
180
+ layout.addWidget(self._browser)
181
+
182
+ self.set_page(page)
183
+ from . import theming
184
+
185
+ # Equations are images rasterised in the text colour, so a theme
186
+ # change re-renders the page rather than only repainting it.
187
+ theming.subscribe(self._on_theme)
188
+
189
+ def _on_theme(self, _theme) -> None:
190
+ # A bound method so Qt disconnects it with the window (see SetupTab).
191
+ self.set_page(self._page)
192
+
193
+ def set_page(self, page) -> None:
194
+ """Render a page into the browser, images and all."""
195
+ self._page = page
196
+ render_into(
197
+ self._browser,
198
+ page,
199
+ device_pixel_ratio=self.devicePixelRatioF(),
200
+ text_color=self._text_color(),
201
+ )
202
+
203
+ def _text_color(self) -> str:
204
+ """Rasterise math in the palette's own text colour.
205
+
206
+ Equations are images, so they do not follow a stylesheet the way the
207
+ surrounding prose does. Black math on a dark theme would be unreadable
208
+ only because its colour had been fixed at render time.
209
+ """
210
+ return self.palette().windowText().color().name()
@@ -60,6 +60,7 @@ __all__ = [
60
60
  "to_html",
61
61
  "png_size",
62
62
  "math_dpi",
63
+ "inline_metrics",
63
64
  "menu_label",
64
65
  "INLINE_DPI",
65
66
  "DISPLAY_DPI",
@@ -130,13 +131,23 @@ class _Collector:
130
131
  device_pixel_ratio: float
131
132
  color: str
132
133
  render: Callable[..., bytes | None]
134
+ axis_pt: float | None = None
135
+ min_height_pt: float = 0.0
133
136
  images: list[MathImage] = field(default_factory=list)
134
137
 
135
138
  def add(self, latex: str, *, display: bool) -> str:
136
139
  """Render one math span; return the HTML that stands for it."""
137
140
  base = self.display_dpi if display else self.inline_dpi
138
141
  dpi = max(1, round(base * self.device_pixel_ratio))
139
- png = self.render(latex, dpi=dpi, color=self.color)
142
+ # Only inline math is set against a line of text. A display
143
+ # equation sits on its own line, so there is no baseline to meet.
144
+ centred = not display and self.axis_pt is not None
145
+ extra = (
146
+ {"axis_pt": self.axis_pt, "min_height_pt": self.min_height_pt}
147
+ if centred
148
+ else {}
149
+ )
150
+ png = self.render(latex, dpi=dpi, color=self.color, **extra)
140
151
 
141
152
  if png is None:
142
153
  delimiter = "$$" if display else "$"
@@ -156,7 +167,11 @@ class _Collector:
156
167
  height=max(1, round(height / self.device_pixel_ratio)),
157
168
  )
158
169
  )
159
- image = f'<img src="{key}" width="{self.images[-1].width}" height="{self.images[-1].height}">'
170
+ align = ' style="vertical-align:middle"' if centred else ""
171
+ image = (
172
+ f'<img src="{key}" width="{self.images[-1].width}" '
173
+ f'height="{self.images[-1].height}"{align}>'
174
+ )
160
175
  return f"<br>{image}<br>" if display else image
161
176
 
162
177
 
@@ -168,6 +183,8 @@ def render_markdown(
168
183
  device_pixel_ratio: float = 1.0,
169
184
  color: str = "black",
170
185
  render: Callable[..., bytes | None] | None = None,
186
+ axis_pt: float | None = None,
187
+ min_height_pt: float = 0.0,
171
188
  ) -> RichPage:
172
189
  """Convert markdown with `$...$` math to Qt-displayable HTML.
173
190
 
@@ -176,6 +193,11 @@ def render_markdown(
176
193
  ``None``. It defaults to a cached
177
194
  :func:`~gratinglab.gui.mathtext.render_math_png`. A page carries roughly
178
195
  forty equations, and the cache allows the viewer to reopen immediately.
196
+
197
+ ``axis_pt`` and ``min_height_pt`` put inline math on the text's
198
+ baseline; see :func:`~gratinglab.gui.mathtext.render_math_png` and
199
+ :func:`inline_metrics`. Left ``None``, an inline equation's bottom edge
200
+ sits on the baseline instead.
179
201
  """
180
202
  collector = _Collector(
181
203
  inline_dpi=inline_dpi,
@@ -183,6 +205,8 @@ def render_markdown(
183
205
  device_pixel_ratio=device_pixel_ratio,
184
206
  color=color,
185
207
  render=render if render is not None else _cached_render,
208
+ axis_pt=axis_pt,
209
+ min_height_pt=min_height_pt,
186
210
  )
187
211
  parts = [_block_html(block, collector) for block in _blocks(markdown)]
188
212
  return RichPage(html="\n".join(p for p in parts if p), images=tuple(collector.images))
@@ -439,6 +463,19 @@ def png_size(data: bytes) -> tuple[int, int] | None:
439
463
  )
440
464
 
441
465
 
466
+ def inline_metrics(font_pixel_size: float, x_height: float) -> dict[str, float]:
467
+ """``axis_pt`` and ``min_height_pt`` for inline math beside this font.
468
+
469
+ Qt puts a middle-aligned image's centre a quarter of the x-height above
470
+ the baseline, provided the image is at least three x-heights tall. Both
471
+ are returned in points of the math render: mathtext renders at 10 pt and
472
+ :func:`math_dpi` scales that to the font's pixel size, so a pixel of the
473
+ text is ``10 / font_pixel_size`` points.
474
+ """
475
+ points = 10.0 / font_pixel_size
476
+ return {"axis_pt": x_height / 4.0 * points, "min_height_pt": 3.0 * x_height * points}
477
+
478
+
442
479
  def math_dpi(font_pixel_size: float, *, display: bool = False) -> int:
443
480
  """DPI at which mathtext output matches text of the given size.
444
481
 
@@ -464,9 +501,18 @@ def menu_label(title: str, suffix: str = "") -> str:
464
501
 
465
502
 
466
503
  @lru_cache(maxsize=512)
467
- def _cached_render(latex: str, *, dpi: int, color: str) -> bytes | None:
504
+ def _cached_render(
505
+ latex: str,
506
+ *,
507
+ dpi: int,
508
+ color: str,
509
+ axis_pt: float | None = None,
510
+ min_height_pt: float = 0.0,
511
+ ) -> bytes | None:
468
512
  """Rasterise with caching.
469
513
 
470
514
  Without the cache, reopening the viewer would re-render every equation on
471
515
  the page, roughly forty of them, and more at 2x."""
472
- return mathtext.render_math_png(latex, dpi=dpi, color=color)
516
+ return mathtext.render_math_png(
517
+ latex, dpi=dpi, color=color, axis_pt=axis_pt, min_height_pt=min_height_pt
518
+ )
@@ -0,0 +1,17 @@
1
+ """File formats other programs read or write.
2
+
3
+ Each module handles one format, and nothing is imported here, so a caller
4
+ names the module it wants (``from gratinglab.io.ggp import read_ggp``):
5
+
6
+ ``ggp``
7
+ PCGrate's ``.ggp`` groove profiles, read in all five border types and
8
+ written in the polygonal one.
9
+ ``boundary``
10
+ A measured groove profile saved with its period and provenance, as
11
+ gratinglab's own ``.json``, which reads back, or a write-only ``.csv``.
12
+ A ``.ggp`` cannot carry the period, which is why this exists beside it.
13
+ ``efficiency_table``
14
+ Efficiency tables exported by PCGrate-SX, read for comparison.
15
+ ``txl``
16
+ Raith ``.txl`` layouts of a grating, written for electron-beam writing.
17
+ """