PyFiberModes 0.13.2__tar.gz → 0.14.1__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 (166) hide show
  1. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/.github/workflows/quality.yml +1 -0
  2. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/.zenodo.json +1 -1
  3. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/CHANGELOG.md +20 -1
  4. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/Makefile +1 -0
  5. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PKG-INFO +17 -5
  6. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/__init__.py +38 -3
  7. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/_version.py +2 -2
  8. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/analysis.py +22 -12
  9. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/factory.py +0 -6
  10. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber.py +11 -2
  11. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fundamentals.py +83 -28
  12. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/loader.py +37 -14
  13. pyfibermodes-0.14.1/PyFiberModes/materials.py +118 -0
  14. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/plotting.py +0 -3
  15. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/services.py +2 -10
  16. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/solver/__init__.py +6 -6
  17. pyfibermodes-0.14.1/PyFiberModes/solver/multilayer/__init__.py +5 -0
  18. pyfibermodes-0.13.2/PyFiberModes/solver/mlsif/neff.py → pyfibermodes-0.14.1/PyFiberModes/solver/multilayer/effective_index.py +162 -51
  19. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/solver/protocols.py +16 -1
  20. {pyfibermodes-0.13.2/PyFiberModes/solver/tlsif → pyfibermodes-0.14.1/PyFiberModes/solver/three_layer}/__init__.py +2 -0
  21. {pyfibermodes-0.13.2/PyFiberModes/solver/tlsif → pyfibermodes-0.14.1/PyFiberModes/solver/three_layer}/cutoff.py +14 -16
  22. pyfibermodes-0.14.1/PyFiberModes/solver/two_layer/__init__.py +6 -0
  23. {pyfibermodes-0.13.2/PyFiberModes/solver/ssif → pyfibermodes-0.14.1/PyFiberModes/solver/two_layer}/cutoff.py +1 -6
  24. pyfibermodes-0.13.2/PyFiberModes/solver/ssif/neff.py → pyfibermodes-0.14.1/PyFiberModes/solver/two_layer/effective_index.py +54 -16
  25. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes.egg-info/PKG-INFO +17 -5
  26. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes.egg-info/SOURCES.txt +25 -11
  27. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes.egg-info/requires.txt +2 -1
  28. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/README.rst +14 -3
  29. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/conda.recipe/meta.yaml +1 -2
  30. pyfibermodes-0.14.1/docs/examples/analysis/plot_guided_mode_discovery.py +22 -0
  31. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/basic/plot_mode_field_1.py +1 -1
  32. pyfibermodes-0.14.1/docs/examples/benchmarks/plot_component_batching_speedup.py +33 -0
  33. pyfibermodes-0.14.1/docs/examples/benchmarks/plot_modal_cache_speedup.py +28 -0
  34. pyfibermodes-0.14.1/docs/examples/fields/plot_confinement_by_radius.py +28 -0
  35. pyfibermodes-0.14.1/docs/examples/fields/plot_polarization_rotation.py +27 -0
  36. pyfibermodes-0.14.1/docs/examples/specialty_fibers/README.rst +7 -0
  37. pyfibermodes-0.14.1/docs/examples/specialty_fibers/plot_refractive_index_profile.py +26 -0
  38. pyfibermodes-0.14.1/docs/examples/specialty_fibers/plot_specialty_fiber_catalog.py +33 -0
  39. pyfibermodes-0.14.1/docs/examples/specialty_fibers/plot_specialty_mode_indices.py +25 -0
  40. pyfibermodes-0.14.1/docs/examples/validation/plot_field_area_convergence.py +26 -0
  41. pyfibermodes-0.14.1/docs/examples/validation/plot_normalized_effective_index.py +31 -0
  42. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/_static/default.css +7 -0
  43. pyfibermodes-0.14.1/docs/source/api_policy.rst +59 -0
  44. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/architecture.rst +5 -0
  45. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/code/analysis.rst +12 -0
  46. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/code.rst +3 -0
  47. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/conf.py +1 -1
  48. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/examples.rst +2 -2
  49. pyfibermodes-0.14.1/docs/source/index.rst +25 -0
  50. pyfibermodes-0.14.1/docs/source/migration_guide.rst +69 -0
  51. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/pyproject.toml +13 -4
  52. pyfibermodes-0.14.1/tests/api_snapshot.json +54 -0
  53. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tests/test_architecture.py +34 -6
  54. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tests/test_architecture_enhancements.py +2 -2
  55. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tests/test_file_loading.py +15 -3
  56. pyfibermodes-0.14.1/tests/test_public_api.py +44 -0
  57. pyfibermodes-0.14.1/tests/test_three_layer_solver.py +42 -0
  58. pyfibermodes-0.13.2/tests/test_ssif.py → pyfibermodes-0.14.1/tests/test_two_layer_solver.py +1 -1
  59. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tests/test_validation.py +1 -1
  60. pyfibermodes-0.13.2/PyFiberModes/solver/mlsif/__init__.py +0 -3
  61. pyfibermodes-0.13.2/PyFiberModes/solver/ssif/__init__.py +0 -4
  62. pyfibermodes-0.13.2/docs/examples/DCF/README.rst +0 -6
  63. pyfibermodes-0.13.2/docs/examples/DCF/plot_DCF_fields.py +0 -35
  64. pyfibermodes-0.13.2/docs/source/index.rst +0 -23
  65. pyfibermodes-0.13.2/tests/test_tlsif.py +0 -20
  66. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/.coveragerc +0 -0
  67. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/.github/dependabot.yml +0 -0
  68. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/.github/workflows/deploy_PyPi.yml +0 -0
  69. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/.github/workflows/deploy_anaconda.yml +0 -0
  70. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/.github/workflows/deploy_coverage.yml +0 -0
  71. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/.github/workflows/deploy_documentation.yml +0 -0
  72. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/.github/workflows/tests.yml +0 -0
  73. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/.gitignore +0 -0
  74. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/CITATION.cff +0 -0
  75. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/CONTRIBUTING.md +0 -0
  76. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/LICENSE +0 -0
  77. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/MANIFEST.in +0 -0
  78. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/coordinates.py +0 -0
  79. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/directories.py +0 -0
  80. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/exceptions.py +0 -0
  81. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/1550BHP.yaml +0 -0
  82. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/DCF13.yaml +0 -0
  83. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/DCF1300S_20.yaml +0 -0
  84. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/DCF1300S_26.yaml +0 -0
  85. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/DCF1300S_33.yaml +0 -0
  86. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/DCF1300S_42.yaml +0 -0
  87. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/F2028M12.yaml +0 -0
  88. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/F2028M21.yaml +0 -0
  89. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/F2028M24.yaml +0 -0
  90. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/F2058G1.yaml +0 -0
  91. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/F2058L1.yaml +0 -0
  92. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/HI1060.yaml +0 -0
  93. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/HP630.yaml +0 -0
  94. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/SM1950.yaml +0 -0
  95. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/SMF28.yaml +0 -0
  96. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/fluorine_doped_1%_capillary.yaml +0 -0
  97. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/fluorine_doped_2%_capillary.yaml +0 -0
  98. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/test_fiber.yaml +0 -0
  99. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/fiber_files/test_multimode_fiber.yaml +0 -0
  100. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/field.py +0 -0
  101. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/mode.py +0 -0
  102. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/mode_instances.py +0 -0
  103. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/models.py +0 -0
  104. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/optimization.py +0 -0
  105. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/propagation.py +0 -0
  106. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/solver/base_solver.py +0 -0
  107. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/solver/results.py +0 -0
  108. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/source.py +0 -0
  109. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes/stepindex.py +0 -0
  110. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes.egg-info/dependency_links.txt +0 -0
  111. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/PyFiberModes.egg-info/top_level.txt +0 -0
  112. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/benchmarks/README.md +0 -0
  113. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/benchmarks/test_field_performance.py +0 -0
  114. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/Makefile +0 -0
  115. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/README.rst +0 -0
  116. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/SMF28/README.rst +0 -0
  117. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/SMF28/plot_smf28.py +0 -0
  118. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/SMF28/plot_smf28_dispersion_vs_wavelength.py +0 -0
  119. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/SMF28/plot_smf28_group_index_vs_wavelength.py +0 -0
  120. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/analysis/README.rst +0 -0
  121. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/analysis/plot_coupled_mode_exchange.py +0 -0
  122. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/analysis/plot_structured_wavelength_sweep.py +0 -0
  123. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/basic/README.rst +0 -0
  124. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/basic/plot_U_vs_V.py +0 -0
  125. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/basic/plot_comparison_solvers.py +0 -0
  126. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/basic/plot_dispersion.py +0 -0
  127. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/basic/plot_effective_index.py +0 -0
  128. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/basic/plot_fiber_LP_modes.py +0 -0
  129. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/basic/plot_group_index.py +0 -0
  130. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/basic/plot_mode_field.py +0 -0
  131. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/basic/plot_mode_field_2.py +0 -0
  132. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/basic/plot_neff_tapered_fiber.py +0 -0
  133. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/benchmarks/README.rst +0 -0
  134. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/benchmarks/plot_field_grid_scaling.py +0 -0
  135. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/fields/README.rst +0 -0
  136. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/fields/plot_vector_components.py +0 -0
  137. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/validation/README.rst +0 -0
  138. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/examples/validation/plot_effective_index_bounds.py +0 -0
  139. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/make.bat +0 -0
  140. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/_static/favicon.png +0 -0
  141. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/_static/logo.png +0 -0
  142. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/_static/thumbnail.png +0 -0
  143. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/code/fibers.rst +0 -0
  144. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/code/fields.rst +0 -0
  145. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/code/fundamentals.rst +0 -0
  146. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/getting_started.rst +0 -0
  147. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/performance.rst +0 -0
  148. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/references.rst +0 -0
  149. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/theory.rst +0 -0
  150. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/troubleshooting.rst +0 -0
  151. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/docs/source/workflows.rst +0 -0
  152. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/pytest.ini +0 -0
  153. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/setup.cfg +0 -0
  154. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tests/__init__.py +0 -0
  155. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tests/helpers.py +0 -0
  156. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tests/test_advanced_features.py +0 -0
  157. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tests/test_api.py +0 -0
  158. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tests/test_coordinates.py +0 -0
  159. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tests/test_docstrings.py +0 -0
  160. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tests/test_fiber.py +0 -0
  161. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tests/test_field.py +0 -0
  162. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tests/test_physics_regression.py +0 -0
  163. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tests/test_release_tools.py +0 -0
  164. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tools/check_release.py +0 -0
  165. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tools/next_release_version.py +0 -0
  166. {pyfibermodes-0.13.2 → pyfibermodes-0.14.1}/tools/release_tag.py +0 -0
@@ -19,4 +19,5 @@ jobs:
19
19
  python-version: "3.11"
20
20
  - run: python -m pip install --upgrade pip && python -m pip install -e ".[dev]"
21
21
  - run: python -m ruff check PyFiberModes tests tools
22
+ - run: python -m mypy
22
23
  - run: python tools/check_release.py
@@ -9,7 +9,7 @@
9
9
  "license": "MIT",
10
10
  "upload_type": "software",
11
11
  "access_right": "open",
12
- "version": "0.13.2",
12
+ "version": "0.14.1",
13
13
  "publication_date": "2026-09-23",
14
14
  "keywords": [
15
15
  "optical fibers",
@@ -6,6 +6,23 @@ use [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.14.1] - 2026-09-23
10
+
11
+ ## [0.14.0] - 2026-09-23
12
+
13
+ ### Added
14
+
15
+ - Expanded the specialty-fiber, field, analysis, validation, and performance
16
+ galleries so every documentation series contains multiple examples.
17
+ - Defined a stable root-level public API, compatibility and deprecation policy,
18
+ API snapshot regression test, and migration guide.
19
+
20
+ ### Changed
21
+
22
+ - Replaced the opaque `ssif`, `tlsif`, `mlsif`, and `NeffSolver` names with
23
+ descriptive two-layer, three-layer, multilayer, and effective-index solver
24
+ namespaces and class names.
25
+
9
26
  ## [0.13.2] - 2026-09-23
10
27
 
11
28
  ### Changed
@@ -86,7 +103,9 @@ use [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
86
103
 
87
104
  - Established the current tagged baseline for subsequent documented releases.
88
105
 
89
- [Unreleased]: https://github.com/MartinPdeS/PyFiberModes/compare/v0.13.2...HEAD
106
+ [Unreleased]: https://github.com/MartinPdeS/PyFiberModes/compare/v0.14.1...HEAD
107
+ [0.14.1]: https://github.com/MartinPdeS/PyFiberModes/releases/tag/v0.14.1
108
+ [0.14.0]: https://github.com/MartinPdeS/PyFiberModes/releases/tag/v0.14.0
90
109
  [0.13.2]: https://github.com/MartinPdeS/PyFiberModes/releases/tag/v0.13.2
91
110
  [0.13.1]: https://github.com/MartinPdeS/PyFiberModes/releases/tag/v0.13.1
92
111
  [0.13.0]: https://github.com/MartinPdeS/PyFiberModes/releases/tag/v0.13.0
@@ -31,6 +31,7 @@ test:
31
31
 
32
32
  quality:
33
33
  $(PYTHON) -m ruff check PyFiberModes tests tools
34
+ $(PYTHON) -m mypy
34
35
 
35
36
  release-check:
36
37
  $(PYTHON) tools/check_release.py $(if $(VERSION),--version $(VERSION),)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyFiberModes
3
- Version: 0.13.2
3
+ Version: 0.14.1
4
4
  Summary: Guided-mode analysis and design for circular optical fibers.
5
5
  Author-email: Martin Poinsinet de Sivry-Houle <martin.poinsinet.de.sivry@gmail.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -697,7 +697,6 @@ Classifier: Intended Audience :: Science/Research
697
697
  Requires-Python: >=3.11
698
698
  Description-Content-Type: text/x-rst
699
699
  License-File: LICENSE
700
- Requires-Dist: PyOptik<3,>=2
701
700
  Requires-Dist: PyFinitDiff
702
701
  Requires-Dist: pyyaml
703
702
  Requires-Dist: numpy
@@ -709,6 +708,7 @@ Requires-Dist: pytest-json-report~=1.5; extra == "testing"
709
708
  Requires-Dist: coverage~=7.6; extra == "testing"
710
709
  Requires-Dist: ruff<1,>=0.8; extra == "testing"
711
710
  Requires-Dist: matplotlib>=3.8; extra == "testing"
711
+ Requires-Dist: mypy<2,>=1.15; extra == "testing"
712
712
  Provides-Extra: plotting
713
713
  Requires-Dist: matplotlib>=3.8; extra == "plotting"
714
714
  Provides-Extra: benchmarking
@@ -723,6 +723,7 @@ Requires-Dist: sphinx-rtd-theme==3.1.0; extra == "documentation"
723
723
  Requires-Dist: pydata-sphinx-theme==0.17.1; extra == "documentation"
724
724
  Provides-Extra: dev
725
725
  Requires-Dist: ruff<1,>=0.8; extra == "dev"
726
+ Requires-Dist: mypy<2,>=1.15; extra == "dev"
726
727
  Dynamic: license-file
727
728
 
728
729
  .. image:: https://raw.githubusercontent.com/MartinPdeS/PyFiberModes/master/docs/source/_static/logo.png
@@ -813,9 +814,9 @@ simulations:
813
814
 
814
815
  python -c "import PyFiberModes; print(PyFiberModes.__version__)"
815
816
 
816
- PyFiberModes currently uses the ``MaterialBank`` interface provided by
817
- PyOptik 2.x. Released dependency metadata selects the compatible
818
- ``PyOptik>=2,<3`` range automatically.
817
+ Fiber material names are resolved through a lightweight, pluggable registry.
818
+ PyOptik is not required; applications can register their own dispersive model
819
+ objects or simple wavelength-to-index callables.
819
820
 
820
821
  First mode calculation
821
822
  ----------------------
@@ -1012,6 +1013,17 @@ contains the API reference and executable example gallery, including:
1012
1013
  * double-clad fiber field analysis; and
1013
1014
  * comparisons between supported analytical solvers.
1014
1015
 
1016
+ API stability
1017
+ -------------
1018
+
1019
+ Supported imports come from the package root, for example
1020
+ ``from PyFiberModes import Fiber, LP01``. The
1021
+ `public API policy <https://martinpdes.github.io/PyFiberModes/latest/api_policy.html>`_
1022
+ defines compatibility guarantees and the deprecation period. Review the
1023
+ `migration guide <https://martinpdes.github.io/PyFiberModes/latest/migration_guide.html>`_
1024
+ when upgrading across minor or major versions. Internal solver modules are not
1025
+ covered by the compatibility guarantee.
1026
+
1015
1027
  Development and testing
1016
1028
  -----------------------
1017
1029
 
@@ -1,7 +1,27 @@
1
1
  """Public API for circular optical-fiber mode analysis."""
2
2
 
3
- from PyFiberModes.mode import Mode, Family # noqa: F401
4
- from PyFiberModes.mode_instances import * # noqa: F403
3
+ from PyFiberModes.mode import Family, Mode
4
+ from PyFiberModes.mode_instances import (
5
+ EH11,
6
+ HE11,
7
+ HE12,
8
+ HE21,
9
+ HE22,
10
+ HE31,
11
+ HE32,
12
+ LP01,
13
+ LP02,
14
+ LP03,
15
+ LP11,
16
+ LP12,
17
+ LP21,
18
+ LP22,
19
+ LP31,
20
+ LP32,
21
+ LP41,
22
+ TE01,
23
+ TM01,
24
+ )
5
25
  from PyFiberModes.factory import FiberFactory
6
26
  from PyFiberModes.fiber import Fiber, load_fiber
7
27
  from PyFiberModes.field import Field
@@ -12,6 +32,13 @@ from PyFiberModes.solver.results import SolverResult
12
32
  from PyFiberModes.analysis import ModeSweepResult, find_modes, sweep_modes
13
33
  from PyFiberModes.optimization import DesignParameter, DesignResult, optimize_fiber
14
34
  from PyFiberModes.propagation import CoupledModeSystem, PropagationResult, coupling_matrix, overlap
35
+ from PyFiberModes.materials import (
36
+ CallableIndex,
37
+ ConstantIndex,
38
+ FusedSilica,
39
+ MaterialRegistry,
40
+ RefractiveIndexModel,
41
+ )
15
42
 
16
43
 
17
44
  try:
@@ -21,7 +48,7 @@ except ImportError:
21
48
  __version__ = "0.0.0"
22
49
 
23
50
 
24
- __all__ = [ # noqa: F405
51
+ __all__ = [
25
52
  'Mode',
26
53
  'Family',
27
54
  'Fiber',
@@ -34,10 +61,17 @@ __all__ = [ # noqa: F405
34
61
  'ModeSweepResult', 'find_modes', 'sweep_modes',
35
62
  'DesignParameter', 'DesignResult', 'optimize_fiber',
36
63
  'CoupledModeSystem', 'PropagationResult', 'coupling_matrix', 'overlap',
64
+ 'RefractiveIndexModel', 'ConstantIndex', 'CallableIndex', 'FusedSilica',
65
+ 'MaterialRegistry',
37
66
  'HE11',
38
67
  'HE12',
68
+ 'HE21',
39
69
  'HE22',
40
70
  'HE31',
71
+ 'HE32',
72
+ 'EH11',
73
+ 'TE01',
74
+ 'TM01',
41
75
  'LP01',
42
76
  'LP11',
43
77
  'LP21',
@@ -47,4 +81,5 @@ __all__ = [ # noqa: F405
47
81
  'LP22',
48
82
  'LP03',
49
83
  'LP32',
84
+ 'LP41',
50
85
  ]
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.13.2'
22
- __version_tuple__ = version_tuple = (0, 13, 2)
21
+ __version__ = version = '0.14.1'
22
+ __version_tuple__ = version_tuple = (0, 14, 1)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -64,7 +64,7 @@ class ModeSweepResult:
64
64
  index = self.modes.index(mode)
65
65
  return {name: value[:, index] for name, value in self.values.items()}
66
66
 
67
- def as_records(self) -> list[dict]:
67
+ def as_records(self) -> list[dict[str, float | Mode]]:
68
68
  """Convert the dense result into row-oriented records.
69
69
 
70
70
  Returns
@@ -133,9 +133,13 @@ def find_modes(fiber, families=("LP",), max_nu=6, max_m=6) -> tuple[Mode, ...]:
133
133
  found = []
134
134
  for mode in candidate_modes(families, max_nu, max_m):
135
135
  try:
136
- neff = fiber.get_effective_index(mode)
137
- guided = fiber.last_layer.refractive_index < neff <= fiber.maximum_index
138
- if np.isfinite(neff) and guided:
136
+ effective_index = fiber.get_effective_index(mode)
137
+ guided = (
138
+ fiber.last_layer.refractive_index
139
+ < effective_index
140
+ <= fiber.maximum_index
141
+ )
142
+ if np.isfinite(effective_index) and guided:
139
143
  found.append(mode)
140
144
  except (ArithmeticError, AssertionError, RuntimeError, ValueError):
141
145
  continue
@@ -191,7 +195,6 @@ def sweep_modes(
191
195
  parameters = np.asarray(tuple(parameters), dtype=float)
192
196
  if parameters.ndim != 1 or not len(parameters):
193
197
  raise ValueError("parameters must be a non-empty one-dimensional sequence")
194
- working_fiber = deepcopy(fiber)
195
198
  if setter is None:
196
199
  if parameter_name != "wavelength":
197
200
  raise ValueError("a setter is required for non-wavelength sweeps")
@@ -202,19 +205,26 @@ def sweep_modes(
202
205
  if modes is None:
203
206
  discovered = []
204
207
  for parameter in parameters:
205
- setter(working_fiber, float(parameter))
206
- for mode in find_modes(working_fiber, families, max_nu, max_m):
208
+ parameter_fiber = deepcopy(fiber)
209
+ setter(parameter_fiber, float(parameter))
210
+ for mode in find_modes(parameter_fiber, families, max_nu, max_m):
207
211
  if mode not in discovered:
208
212
  discovered.append(mode)
209
213
  modes = discovered
210
214
  modes = tuple(modes)
211
215
  values = {metric: np.full((len(parameters), len(modes)), np.nan) for metric in metrics}
212
- for i, parameter in enumerate(parameters):
213
- setter(working_fiber, float(parameter))
214
- for j, mode in enumerate(modes):
215
- for metric in metrics:
216
+ parameter_fibers = tuple(deepcopy(fiber) for _ in parameters)
217
+ for parameter_fiber, parameter in zip(parameter_fibers, parameters, strict=True):
218
+ setter(parameter_fiber, float(parameter))
219
+
220
+ for i, parameter_fiber in enumerate(parameter_fibers):
221
+ metric_functions = {
222
+ metric: getattr(parameter_fiber, f"get_{metric}") for metric in metrics
223
+ }
224
+ for metric, metric_function in metric_functions.items():
225
+ for j, mode in enumerate(modes):
216
226
  try:
217
- value = getattr(working_fiber, f"get_{metric}")(mode)
227
+ value = metric_function(mode)
218
228
  values[metric][i, j] = value if np.isfinite(value) else np.nan
219
229
  except (ArithmeticError, AssertionError, RuntimeError, ValueError):
220
230
  pass
@@ -55,10 +55,6 @@ class FiberFactory:
55
55
  ----------
56
56
  layers_list : list[ProxyLayer]
57
57
  List of ProxyLayer objects representing the layers of the fiber.
58
- neff_solver : None
59
- Placeholder for a solver object (if required).
60
- cutoff_solver : None
61
- Placeholder for a cutoff solver object (if required).
62
58
  wavelength : float
63
59
  Wavelength used in simulations.
64
60
  """
@@ -72,8 +68,6 @@ class FiberFactory:
72
68
  Vacuum wavelength in meters for generated fibers.
73
69
  """
74
70
  self.layers_list = []
75
- self.neff_solver = None
76
- self.cutoff_solver = None
77
71
  self.wavelength = wavelength
78
72
 
79
73
  def add_layer(self, index: float, name: str = "", radius: float = 0.0):
@@ -22,6 +22,7 @@ from PyFiberModes.fundamentals import (
22
22
  from PyFiberModes import loader
23
23
  from PyFiberModes.coordinates import CylindricalCoordinates
24
24
  from PyFiberModes.exceptions import ValidationError
25
+ from PyFiberModes.materials import DEFAULT_MATERIALS, MaterialRegistry
25
26
  from PyFiberModes.services import FieldAnalysis, ModalAnalysis
26
27
 
27
28
 
@@ -1004,7 +1005,12 @@ def get_fiber_from_delta_and_V0(delta: float, V0: float, wavelength: float) -> F
1004
1005
  return fiber
1005
1006
 
1006
1007
 
1007
- def load_fiber(fiber_name: str, wavelength: float = None, add_air_layer: bool = False) -> Fiber:
1008
+ def load_fiber(
1009
+ fiber_name: str,
1010
+ wavelength: float | None = None,
1011
+ add_air_layer: bool = False,
1012
+ materials: MaterialRegistry = DEFAULT_MATERIALS,
1013
+ ) -> Fiber:
1008
1014
  """Load a named YAML fiber definition.
1009
1015
 
1010
1016
  Parameters
@@ -1015,6 +1021,8 @@ def load_fiber(fiber_name: str, wavelength: float = None, add_air_layer: bool =
1015
1021
  Vacuum wavelength in meters used to evaluate material indices.
1016
1022
  add_air_layer : bool, optional
1017
1023
  Add an outer air layer when true.
1024
+ materials : MaterialRegistry, optional
1025
+ Registry used to resolve material names in the YAML definition.
1018
1026
 
1019
1027
  Returns
1020
1028
  -------
@@ -1024,7 +1032,8 @@ def load_fiber(fiber_name: str, wavelength: float = None, add_air_layer: bool =
1024
1032
  fiber_dict = loader.load_fiber_as_dict(
1025
1033
  fiber_name=fiber_name,
1026
1034
  wavelength=wavelength,
1027
- order='out-to-in'
1035
+ order='out-to-in',
1036
+ materials=materials,
1028
1037
  )
1029
1038
 
1030
1039
  fiber = Fiber(wavelength=wavelength)
@@ -1,13 +1,19 @@
1
1
  """Fundamental normalized-frequency and propagation relations."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import numpy
4
6
  import numpy as np
7
+ from typing import TYPE_CHECKING
5
8
  from PyFiberModes.mode import Mode
6
9
  from PyFiberModes.mode_instances import HE11, LP01
7
10
  from scipy.constants import c
8
11
 
9
12
  from PyFiberModes.coordinates import CylindricalCoordinates
10
13
 
14
+ if TYPE_CHECKING:
15
+ from PyFiberModes.solver.results import SolverResult
16
+
11
17
 
12
18
  def get_delta_from_fiber(fiber) -> float:
13
19
  r"""
@@ -31,7 +37,7 @@ def get_delta_from_fiber(fiber) -> float:
31
37
  return 0.5 * (1 - n_ratio)
32
38
 
33
39
 
34
- def get_wavelength_from_V0(fiber: object, V0: float) -> float:
40
+ def get_wavelength_from_V0(fiber: object, normalized_frequency: float) -> float:
35
41
  r"""
36
42
  Compute the wavelength corresponding to a given V-number, \(V_0\).
37
43
 
@@ -46,7 +52,7 @@ def get_wavelength_from_V0(fiber: object, V0: float) -> float:
46
52
  ----------
47
53
  fiber : Fiber
48
54
  The fiber object.
49
- V0 : float
55
+ normalized_frequency : float
50
56
  The V-number.
51
57
 
52
58
  Returns
@@ -56,7 +62,7 @@ def get_wavelength_from_V0(fiber: object, V0: float) -> float:
56
62
  """
57
63
  NA = fiber.get_NA()
58
64
  last_layer = fiber.last_layer
59
- wavelength = 2 * np.pi / V0 * last_layer.radius_in * NA
65
+ wavelength = 2 * np.pi / normalized_frequency * last_layer.radius_in * NA
60
66
  return wavelength
61
67
 
62
68
 
@@ -95,11 +101,14 @@ def get_propagation_constant_from_omega(
95
101
 
96
102
  from PyFiberModes import solver
97
103
 
98
- neff_solver = solver.ssif.NeffSolver(fiber=fiber, wavelength=wavelength) if fiber.n_layer == 2 \
99
- else solver.mlsif.NeffSolver(fiber=fiber, wavelength=wavelength)
104
+ effective_index_solver = (
105
+ solver.two_layer.EffectiveIndexSolver(fiber=fiber, wavelength=wavelength)
106
+ if fiber.n_layer == 2
107
+ else solver.multilayer.EffectiveIndexSolver(fiber=fiber, wavelength=wavelength)
108
+ )
100
109
 
101
- neff = neff_solver.solve(mode=mode, delta_neff=delta_neff)
102
- return neff * (2 * numpy.pi / wavelength)
110
+ effective_index = effective_index_solver.solve(mode=mode, delta_neff=delta_neff)
111
+ return effective_index * (2 * numpy.pi / wavelength)
103
112
 
104
113
 
105
114
  def get_U_parameter(
@@ -132,9 +141,11 @@ def get_U_parameter(
132
141
  from PyFiberModes import solver
133
142
  assert fiber.n_layer == 2, "U-parameter can only be calculated for two-layer fibers."
134
143
 
135
- neff_solver = solver.ssif.NeffSolver(fiber=fiber, wavelength=wavelength)
136
- neff = neff_solver.solve(mode=mode, delta_neff=delta_neff)
137
- U, _, _ = neff_solver.get_U_W_V_parameter(neff=neff)
144
+ effective_index_solver = solver.two_layer.EffectiveIndexSolver(
145
+ fiber=fiber, wavelength=wavelength
146
+ )
147
+ effective_index = effective_index_solver.solve(mode=mode, delta_neff=delta_neff)
148
+ U, _, _ = effective_index_solver.get_U_W_V_parameter(neff=effective_index)
138
149
  return U
139
150
 
140
151
 
@@ -165,11 +176,49 @@ def get_effective_index(
165
176
  from PyFiberModes import solver
166
177
 
167
178
  if fiber.n_layer == 2:
168
- neff_solver = solver.ssif.NeffSolver(fiber=fiber, wavelength=wavelength)
179
+ effective_index_solver = solver.two_layer.EffectiveIndexSolver(
180
+ fiber=fiber, wavelength=wavelength
181
+ )
169
182
  else:
170
- neff_solver = solver.mlsif.NeffSolver(fiber=fiber, wavelength=wavelength)
183
+ effective_index_solver = solver.multilayer.EffectiveIndexSolver(
184
+ fiber=fiber, wavelength=wavelength
185
+ )
171
186
 
172
- return neff_solver.solve(mode=mode, delta_neff=delta_neff)
187
+ return effective_index_solver.solve(mode=mode, delta_neff=delta_neff)
188
+
189
+
190
+ def get_effective_index_result(
191
+ fiber,
192
+ wavelength: float,
193
+ mode: Mode,
194
+ delta_neff: float = 1e-6) -> "SolverResult[float]":
195
+ """Compute an effective index with structured convergence diagnostics.
196
+
197
+ Parameters
198
+ ----------
199
+ fiber : Fiber
200
+ Fiber to solve.
201
+ wavelength : float
202
+ Vacuum wavelength in meters.
203
+ mode : Mode
204
+ Mode to solve.
205
+ delta_neff : float, optional
206
+ Maximum effective-index search step.
207
+
208
+ Returns
209
+ -------
210
+ SolverResult[float]
211
+ Effective index, residual, bracket, iteration count, and explanation.
212
+ """
213
+ from PyFiberModes import solver
214
+
215
+ solver_class = (
216
+ solver.two_layer.EffectiveIndexSolver
217
+ if fiber.n_layer == 2
218
+ else solver.multilayer.EffectiveIndexSolver
219
+ )
220
+ effective_index_solver = solver_class(fiber=fiber, wavelength=wavelength)
221
+ return effective_index_solver.solve_result(mode=mode, delta_neff=delta_neff)
173
222
 
174
223
 
175
224
  def get_mode_cutoff_v0(
@@ -199,12 +248,14 @@ def get_mode_cutoff_v0(
199
248
  return 0
200
249
 
201
250
  match fiber.n_layer:
202
- case 2: # Standard Step-Index Fiber [SSIF|
203
- cutoff_solver = solver.ssif.CutoffSolver(fiber=fiber, wavelength=wavelength)
204
- case 3: # Three-Layer Step-Index Fiber [TLSIF]
205
- cutoff_solver = solver.tlsif.CutoffSolver(fiber=fiber, wavelength=wavelength)
206
- case _: # Multi-Layer Step-Index Fiber [MLSIF]
207
- cutoff_solver = solver.solver.FiberSolver(fiber=fiber, wavelength=wavelength)
251
+ case 2:
252
+ cutoff_solver = solver.two_layer.CutoffSolver(fiber=fiber, wavelength=wavelength)
253
+ case 3:
254
+ cutoff_solver = solver.three_layer.CutoffSolver(fiber=fiber, wavelength=wavelength)
255
+ case _:
256
+ raise NotImplementedError(
257
+ "Cutoff calculations are currently available for two- and three-layer fibers"
258
+ )
208
259
 
209
260
  cutoff = cutoff_solver.solve(mode=mode)
210
261
 
@@ -241,10 +292,14 @@ def get_radial_field(
241
292
  """
242
293
  from PyFiberModes import solver
243
294
 
244
- if fiber.n_layer == 2: # Standard Step-Index Fiber [SSIF]
245
- neff_solver = solver.ssif.NeffSolver(fiber=fiber, wavelength=wavelength)
246
- else: # Multi-Layer Step-Index Fiber [MLSIF]
247
- neff_solver = solver.mlsif.NeffSolver(fiber=fiber, wavelength=wavelength)
295
+ if fiber.n_layer == 2:
296
+ effective_index_solver = solver.two_layer.EffectiveIndexSolver(
297
+ fiber=fiber, wavelength=wavelength
298
+ )
299
+ else:
300
+ effective_index_solver = solver.multilayer.EffectiveIndexSolver(
301
+ fiber=fiber, wavelength=wavelength
302
+ )
248
303
 
249
304
  neff = get_effective_index(
250
305
  fiber=fiber,
@@ -260,15 +315,15 @@ def get_radial_field(
260
315
 
261
316
  match mode.family:
262
317
  case 'LP':
263
- (er, ephi, ez), (hr, hphi, hz) = neff_solver.get_LP_field(**kwargs)
318
+ (er, ephi, ez), (hr, hphi, hz) = effective_index_solver.get_LP_field(**kwargs)
264
319
  case 'TE':
265
- (er, ephi, ez), (hr, hphi, hz) = neff_solver.get_TE_field(**kwargs)
320
+ (er, ephi, ez), (hr, hphi, hz) = effective_index_solver.get_TE_field(**kwargs)
266
321
  case 'TM':
267
- (er, ephi, ez), (hr, hphi, hz) = neff_solver.get_TM_field(**kwargs)
322
+ (er, ephi, ez), (hr, hphi, hz) = effective_index_solver.get_TM_field(**kwargs)
268
323
  case 'EH':
269
- (er, ephi, ez), (hr, hphi, hz) = neff_solver.get_EH_field(**kwargs)
324
+ (er, ephi, ez), (hr, hphi, hz) = effective_index_solver.get_EH_field(**kwargs)
270
325
  case 'HE':
271
- (er, ephi, ez), (hr, hphi, hz) = neff_solver.get_HE_field(**kwargs)
326
+ (er, ephi, ez), (hr, hphi, hz) = effective_index_solver.get_HE_field(**kwargs)
272
327
 
273
328
  e_field = CylindricalCoordinates(rho=er, phi=ephi, z=ez)
274
329
  h_field = CylindricalCoordinates(rho=hr, phi=hphi, z=hz)
@@ -1,9 +1,15 @@
1
1
  """Load and normalize YAML fiber definitions."""
2
2
 
3
- import yaml
4
- import numpy as np
5
3
  from pathlib import Path
6
- from PyOptik import MaterialBank
4
+ from typing import Any, TypeAlias
5
+
6
+ import numpy as np
7
+ import yaml
8
+
9
+ from PyFiberModes.materials import DEFAULT_MATERIALS, MaterialRegistry
10
+
11
+ LayerData: TypeAlias = dict[str, Any]
12
+ LayerCollection: TypeAlias = dict[str, LayerData]
7
13
 
8
14
 
9
15
  def get_fiber_file_path(fiber_name: str) -> Path:
@@ -23,7 +29,7 @@ def get_fiber_file_path(fiber_name: str) -> Path:
23
29
  return Path(__file__).parent / 'fiber_files' / f'{fiber_name}.yaml'
24
30
 
25
31
 
26
- def load_yaml_configuration(file_path: Path) -> dict:
32
+ def load_yaml_configuration(file_path: Path) -> dict[str, Any]:
27
33
  """
28
34
  Load and parse a YAML configuration file.
29
35
 
@@ -44,7 +50,12 @@ def load_yaml_configuration(file_path: Path) -> dict:
44
50
  return yaml.safe_load(file)
45
51
 
46
52
 
47
- def calculate_layer_index(layer: dict, wavelength: float, outer_layer: dict = None) -> float:
53
+ def calculate_layer_index(
54
+ layer: LayerData,
55
+ wavelength: float | None,
56
+ outer_layer: LayerData | None = None,
57
+ materials: MaterialRegistry = DEFAULT_MATERIALS,
58
+ ) -> float:
48
59
  """
49
60
  Calculate the refractive index for a layer.
50
61
 
@@ -62,14 +73,21 @@ def calculate_layer_index(layer: dict, wavelength: float, outer_layer: dict = No
62
73
  float
63
74
  Computed refractive index for the layer.
64
75
  """
65
- if 'material' in layer and wavelength:
66
- return getattr(MaterialBank, layer['material']).compute_refractive_index(wavelength)
76
+ if 'material' in layer and wavelength is not None:
77
+ return materials.refractive_index(str(layer['material']), wavelength)
67
78
  elif 'NA' in layer and outer_layer:
68
79
  return np.sqrt(layer['NA']**2 + outer_layer['index']**2)
69
- return layer.get('index')
80
+ index = layer.get('index')
81
+ if index is None:
82
+ raise ValueError("layer must define material, numerical aperture, or index")
83
+ return float(index)
70
84
 
71
85
 
72
- def process_layers(layers: dict, wavelength: float = None) -> dict:
86
+ def process_layers(
87
+ layers: LayerCollection,
88
+ wavelength: float | None = None,
89
+ materials: MaterialRegistry = DEFAULT_MATERIALS,
90
+ ) -> LayerCollection:
73
91
  """
74
92
  Process and calculate refractive indices for all layers.
75
93
 
@@ -89,14 +107,14 @@ def process_layers(layers: dict, wavelength: float = None) -> dict:
89
107
  outer_layer = None
90
108
 
91
109
  for idx, layer in layers.items():
92
- layer_index = calculate_layer_index(layer, wavelength, outer_layer)
110
+ layer_index = calculate_layer_index(layer, wavelength, outer_layer, materials)
93
111
  processed_layers[idx] = {**layer, 'index': layer_index}
94
112
  outer_layer = processed_layers[idx]
95
113
 
96
114
  return processed_layers
97
115
 
98
116
 
99
- def cleanup_layers(layers: dict) -> dict:
117
+ def cleanup_layers(layers: LayerCollection) -> LayerCollection:
100
118
  """
101
119
  Remove unnecessary keys from layer dictionaries.
102
120
 
@@ -116,7 +134,7 @@ def cleanup_layers(layers: dict) -> dict:
116
134
  return layers
117
135
 
118
136
 
119
- def reorder_layers(layers: dict, order: str) -> dict:
137
+ def reorder_layers(layers: LayerCollection, order: str) -> LayerCollection:
120
138
  """
121
139
  Reorder layers based on the specified order.
122
140
 
@@ -137,7 +155,12 @@ def reorder_layers(layers: dict, order: str) -> dict:
137
155
  return layers
138
156
 
139
157
 
140
- def load_fiber_as_dict(fiber_name: str, wavelength: float = None, order: str = 'in-to-out') -> dict:
158
+ def load_fiber_as_dict(
159
+ fiber_name: str,
160
+ wavelength: float | None = None,
161
+ order: str = 'in-to-out',
162
+ materials: MaterialRegistry = DEFAULT_MATERIALS,
163
+ ) -> dict[str, LayerCollection]:
141
164
  """
142
165
  Load and process a fiber configuration file.
143
166
 
@@ -167,7 +190,7 @@ def load_fiber_as_dict(fiber_name: str, wavelength: float = None, order: str = '
167
190
  config = load_yaml_configuration(file_path)
168
191
  layers = config.get('layers', {})
169
192
 
170
- processed_layers = process_layers(layers, wavelength)
193
+ processed_layers = process_layers(layers, wavelength, materials)
171
194
  cleaned_layers = cleanup_layers(processed_layers)
172
195
  reordered_layers = reorder_layers(cleaned_layers, order)
173
196